HOOK API入門之Hook自己程序的MessageBoxW(簡單入門)


說到HOOK,我看了很多的資料和教程,無奈就是學不會HOOK,不懂是我的理解能力差,還是你們說的

不夠明白,直到我看了以下這篇文章,終於學會了HOOK:

http://blog.sina.com.cn/s/blog_628821950100xmuc.html    //感謝文章作者的分享,讓我學會了HOOK

文章出處,好像是這篇:http://blog.csdn.net/glliuxueke/article/details/2702608     //后來才看到

----------------------------------------------------------------------------------------------------------------------------------------------

既然窩已經入門了HOOK,窩會寫幾篇關於HOOK的文章,讓同樣想入門HOOK,卻難以入門的童鞋

有個參考,這篇是第一篇,希望幫助到有此需要的盆友,我測試的環境都是:Win7+VS2008+MFC

--------------------------------------------------------------------------------------------------------------------------------------------------

第一篇說的是HOOK自己程序的MessageBoxW,誠然HOOK自己程序用到的API在實際應用中沒有什么

大的用處,不過我認為對於我們理解HOOK卻有莫大的幫助,因此我的HOOK文章就從HOOK自己的程序

開始,文章后面附有本例子程序的VS2008源碼下載地址。

---------------------------------------------------------------------------------------------------------------------------------------------------

//先看下我寫的例子程序及運行效果截圖,后面再對其進行分析

//未鈎MessageBoxW前

//鈎MessageBoxW后

---------------------------------------------------------------------------------------------------------------------

根據我的理解,先說一下HOOK API的一般思路和步驟。

HOOK API的思路就是修改原API的入口,使其跳轉到我們的假API入口,

然后執行我們的假API函數,為什么說是假API函數呢?

因為我們的假API,除了函數名稱和真實API的名稱不一樣之外,其它都是相同的,即

它們的函數參數和返回值和調用形式都是一樣的。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

HOOK API的一般步驟:

1.定義假API函數

       注意假API函數,除了函數名稱和真實API不一樣之外,其它的都要跟真實API的定義相同,如參數類型和返回值、調用形式等。

如我們可以這樣定義假的MessageBoxW:

[cpp]  view plain  copy
 
  1. int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType)  
  2. {  
  3.     //定義假API時,具體的函數體代碼暫時可不寫...  
  4.     return 0;  
  5. }  

至於我們如何知道真實API的原型定義呢?很簡單,查看MSDN即可,或者你也可以在VS2008開發環境中,

寫下該真實API,然后選中該API,再點鼠標右鍵,選擇【轉到定義】,就可以看到其原型聲明了。

...........................................................................................................................................................................

2.定義API函數類型

       因為我們要動態獲取原API函數的地址,獲取到后,我們要將其保存起來,保存在哪里呢?

這就是定義API函數類型的原因了,有了API函數類型的定義后,我們就可以用其定義一個變量來 保存獲取到的

真實API函數的地址了,例如,我定義的MessageBoxW函數類型的語句如下:

 

[cpp]  view plain  copy
 
  1. //原函數類型定義  
  2. typedef int (WINAPI* MsgBoxW)(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);  

注意,原函數類型定義中的函數返回值和參數類型要跟MessageBoxW的定義相同。

 

上面就是定義了API函數類型MsgBoxW,這跟我們常見的int、char、float等類型,用法是一樣的,

實際上,我們完全可以把MsgBoxW當成int類型來使用,這樣一說,API函數類型也不過如此嘛。

有了API函數類型MsgBoxW,我們就可以用其來定義變量,從而為保存原API地址做准備了。

如:

 

[cpp]  view plain  copy
 
  1. MsgBoxW OldMsgBoxW=NULL;//指向原函數的指針  


有了API函數類型后,我們還需要一個遠指針類型的變量,因為系統API是在dll文件中的,更多關於

 

遠指針的信息,我現在也不是很清楚,有興趣的盆友,不妨百度一下。系統已經給我們定義好了

遠指針類型:FARPROC,我們直接拿來用即可,如:

 

[cpp]  view plain  copy
 
  1. FARPROC pfOldMsgBoxW;  //指向函數的遠指針  

 

..................................................................................................................................................................................................................

3.獲取API函數入口

         有了保存原API函數地址的變量OldMsgBox和指向原API函數的遠指針,我們就可以獲取真實API的地址了。

如:

 

[cpp]  view plain  copy
 
  1. //獲取原API入口地址  
  2.   HMODULE hmod=::LoadLibrary(_T("User32.dll"));  
  3.   OldMsgBoxW=(MsgBoxW)::GetProcAddress(hmod,"MessageBoxW");  
  4.   pfOldMsgBoxW=(FARPROC)OldMsgBoxW;  

 

注意,上面我們將原API地址OldMsgBoxW強制轉換成了遠指針pfOldMsgBoxW,至於為什么要強制轉換,

我現在也不是很清楚,想知道的童鞋,可以百度一下。

................................................................................................................................................................................................................

4.保存原API入口的前5個字節

           保存的目的是為了恢復用的,畢竟我們HOOK了API后,還需要調用真實的API嘛,

如何保存一個函數入口的前5個字節呢?這里用到了匯編代碼,至於不用匯編可以嗎?我想是可以的。

有空時,我再試一下,不用匯編是否能保存一個API入口的前5個字節,這里就先用別人寫的匯編代碼吧,

代碼如下:

 

[cpp]  view plain  copy
 
  1. // 將原API的入口前5個字節代碼保存到OldCode[]  
  2.   BYTE OldCode[5];  
  3.   _asm   
  4.   {   
  5.    lea edi,OldCode      //獲取OldCode數組的地址,放到edi  
  6.    mov esi,pfOldMsgBoxW //獲取原API入口地址,放到esi  
  7.    cld    //方向標志位,為以下兩條指令做准備  
  8.    movsd //復制原API入口前4個字節到OldCode數組  
  9.    movsb //復制原API入口第5個字節到OldCode數組  
  10.   }  


....................................................................................................................................................................................................................

 

5.獲取新入口的前5個字節

          因為我們修改真實API入口的前5個字節,使其跳轉到我們假API函數的入口地址,即改成Jmp xxxxxxxx,其中xxxxxxxx就是我們

假API的入口地址,那么我們如何得到該地址呢?

前人已經總結出了一條公式:

int nAddr= UserFunAddr – SysFunAddr - (我們定制的這條指令的大小);

Jmp nAddr; //我們要獲取的就是nAddr的值

既然已經有了公式,我們拿來用即可,UserFunAddr相當於我們的假API,SysFunAddr相當於真實API,這里指令的大小為5,

實現以上公式的匯編代碼如下:

 

[cpp]  view plain  copy
 
  1. //獲取MyMessageBoxW的相對地址,為Jmp做准備  
  2.   //int nAddr= UserFunAddr – SysFunAddr - (我們定制的這條指令的大小);  
  3.   //Jmp nAddr;  
  4.   //(我們定制的這條指令的大小), 這里是5,5個字節嘛  
  5.   BYTE NewCode[5];  
  6.   _asm   
  7.   {   
  8.    lea eax,MyMessageBoxW //獲取我們的MyMessageBoxW函數地址  
  9.    mov ebx,pfOldMsgBoxW  //原系統API函數地址  
  10.    sub eax,ebx           //int nAddr= UserFunAddr – SysFunAddr  
  11.    sub eax,5             //nAddr=nAddr-5  
  12.    mov dword ptr [NewCode+1],eax //將算出的地址nAddr保存到NewCode后面4個字節  
  13.                                  //注:一個函數地址占4個字節  
  14.   }   


......................................................................................................................................................................................................................

 

6.修改真實API入口的前5個字節

       前面我們已經保存了真實API入口的前5個字節,也已經計算出了新入口的前5個字節,

可謂萬事俱備,只欠東風,現在可以修改真實API入口的前5個字節了。

代碼如下:

 

[cpp]  view plain  copy
 
  1. //填充完畢,現在NewCode[]里的指令相當於Jmp MyMessageBoxW  
  2.   //既然已經獲取到了Jmp MyMessageBoxW  
  3.   //現在該是將Jmp MyMessageBoxW寫入原API入口前5個字節的時候了  
  4.   //知道為什么是5個字節嗎?  
  5.   //Jmp指令相當於0xe9,占一個字節的內存空間  
  6.   //MyMessageBoxW是一個地址,其實是一個整數,占4個字節的內存空間  
  7.   //int n=0x123;   n占4個字節和MyMessageBoxW占4個字節是一樣的  
  8.   //1+4=5,知道為什么是5個字節了吧  
  9.   HookOn();   


修改API入口前5個字節的HookOn()函數具體代碼如下:

 

 

[cpp]  view plain  copy
 
  1. //開啟鈎子的函數  
  2. void HookOn()   
  3. {   
  4.  ASSERT(hProcess!=NULL);  
  5.   
  6.  DWORD dwTemp=0;  
  7.  DWORD dwOldProtect;  
  8.    
  9.  //修改API函數入口前5個字節為jmp xxxxxx  
  10.  VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);   
  11.  WriteProcessMemory(hProcess,pfOldMsgBoxW,NewCode,5,0);  
  12.  VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);  
  13.   
  14. }  


以上修改API函數入口前5個字節,用到了兩個系統API函數,百度百科里面對它們有詳解,在此就不說了。

 

注:hProcess是進程句柄,我們可以這樣獲取它:

DWORD dwPid=::GetCurrentProcessId();
HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);

..........................................................................................................................................................................................................


7.恢復API函數入口前5個字節

          有修改,就有恢復嘛,恢復函數代碼如下:

 

[cpp]  view plain  copy
 
  1. //關閉鈎子的函數  
  2. void HookOff()  
  3. {   
  4.  ASSERT(hProcess!=NULL);  
  5.   
  6.  DWORD dwTemp=0;  
  7.  DWORD dwOldProtect;  
  8.   
  9.  //恢復API函數入口前5個字節  
  10.  VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);   
  11.  WriteProcessMemory(hProcess,pfOldMsgBoxW,OldCode,5,0);   
  12.  VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);    
  13. }  

 

 

注:hProcess是進程句柄,我們可以這樣獲取它:

DWORD dwPid=::GetCurrentProcessId();
HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
以上就是HOOK API的大致流程了,看不明白的盆友,不妨再看下我寫的源碼,文章結合源碼,

希望有助於你們的理解。

源碼下載地址:Hook自己程序的MessageBoxW.zip

學會了HOOK自己的程序,下篇我們再接再厲,HOOK系統所有程序的MessageBoxA和MessageBoxW

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

下面貼一下以上這個程序的主要代碼,算是有個整體印象吧:

 

[cpp]  view plain  copy
 
  1. //原函數類型定義  
  2. typedef int (WINAPI* MsgBoxW)(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);  
  3. MsgBoxW OldMsgBoxW=NULL;//指向原函數的指針  
  4. FARPROC pfOldMsgBoxW;  //指向函數的遠指針  
  5. BYTE OldCode[5]; //原系統API入口代碼  
  6. BYTE NewCode[5]; //原系統API新的入口代碼 (jmp xxxxxxxx)  
  7.   
  8. HANDLE hProcess=NULL;//本程序進程句柄  
  9. HINSTANCE hInst=NULL;//API所在的dll文件句柄  
  10.   
  11. void HookOn();  
  12. void HookOff();  
  13. int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType)  
  14. {  
  15.     TRACE(lpText);  
  16.     HookOff();//調用原函數之前,記得先恢復HOOK呀,不然是調用不到的  
  17.               //如果不恢復HOOK,就調用原函數,會造成死循環  
  18.               //畢竟調用的還是我們的函數,從而造成堆棧溢出,程序崩潰。  
  19.   
  20.     int nRet=::MessageBoxW(hWnd,_T("哈哈,MessageBoxW被HOOK了"),lpCaption,uType);  
  21.   
  22.     HookOn();//調用完原函數后,記得繼續開啟HOOK,不然下次會HOOK不到。   
  23.   
  24.     return nRet;  
  25. }  
  26.   
  27.   
  28.   
  29.   
  30. //開啟鈎子的函數  
  31. void HookOn()   
  32. {   
  33.  ASSERT(hProcess!=NULL);  
  34.   
  35.  DWORD dwTemp=0;  
  36.  DWORD dwOldProtect;  
  37.    
  38.  //修改API函數入口前5個字節為jmp xxxxxx  
  39.  VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);   
  40.  WriteProcessMemory(hProcess,pfOldMsgBoxW,NewCode,5,0);  
  41.  VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);  
  42.   
  43. }  
  44.   
  45. //關閉鈎子的函數  
  46. void HookOff()  
  47. {   
  48.  ASSERT(hProcess!=NULL);  
  49.   
  50.  DWORD dwTemp=0;  
  51.  DWORD dwOldProtect;  
  52.   
  53.  //恢復API函數入口前5個字節  
  54.  VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);   
  55.  WriteProcessMemory(hProcess,pfOldMsgBoxW,OldCode,5,0);   
  56.  VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);    
  57. }  
  58.   
  59. //獲取API函數入口前5個字節  
  60. //舊入口前5個字節保存在前面定義的字節數組BYTE OldCode[5]  
  61. //新入口前5個字節保存在前面定義的字節數組BYTE NewCode[5]  
  62. void GetApiEntrance()  
  63. {  
  64.    
  65.   //獲取原API入口地址  
  66.   HMODULE hmod=::LoadLibrary(_T("User32.dll"));  
  67.   OldMsgBoxW=(MsgBoxW)::GetProcAddress(hmod,"MessageBoxW");  
  68.   pfOldMsgBoxW=(FARPROC)OldMsgBoxW;  
  69.     
  70.   if (pfOldMsgBoxW==NULL)  
  71.   {  
  72.     MessageBox(NULL,_T("獲取原API入口地址出錯"),_T("error!"),0);  
  73.     return;  
  74.   }  
  75.   
  76.   // 將原API的入口前5個字節代碼保存到OldCode[]  
  77.   _asm   
  78.   {   
  79.    lea edi,OldCode      //獲取OldCode數組的地址,放到edi  
  80.    mov esi,pfOldMsgBoxW //獲取原API入口地址,放到esi  
  81.    cld    //方向標志位,為以下兩條指令做准備  
  82.    movsd //復制原API入口前4個字節到OldCode數組  
  83.    movsb //復制原API入口第5個字節到OldCode數組  
  84.   }  
  85.   
  86.   
  87.   NewCode[0]=0xe9;//實際上0xe9就相當於jmp指令  
  88.   
  89.   //獲取MyMessageBoxW的相對地址,為Jmp做准備  
  90.   //int nAddr= UserFunAddr – SysFunAddr - (我們定制的這條指令的大小);  
  91.   //Jmp nAddr;  
  92.   //(我們定制的這條指令的大小), 這里是5,5個字節嘛  
  93.   BYTE NewCode[5];  
  94.   _asm   
  95.   {   
  96.    lea eax,MyMessageBoxW //獲取我們的MyMessageBoxW函數地址  
  97.    mov ebx,pfOldMsgBoxW  //原系統API函數地址  
  98.    sub eax,ebx           //int nAddr= UserFunAddr – SysFunAddr  
  99.    sub eax,5             //nAddr=nAddr-5  
  100.    mov dword ptr [NewCode+1],eax //將算出的地址nAddr保存到NewCode后面4個字節  
  101.                                  //注:一個函數地址占4個字節  
  102.   }   
  103.    
  104.   
  105.   //填充完畢,現在NewCode[]里的指令相當於Jmp MyMessageBoxW  
  106.   //既然已經獲取到了Jmp MyMessageBoxW  
  107.   //現在該是將Jmp MyMessageBoxW寫入原API入口前5個字節的時候了  
  108.   //知道為什么是5個字節嗎?  
  109.   //Jmp指令相當於0xe9,占一個字節的內存空間  
  110.   //MyMessageBoxW是一個地址,其實是一個整數,占4個字節的內存空間  
  111.   //int n=0x123;   n占4個字節和MyMessageBoxW占4個字節是一樣的  
  112.   //1+4=5,知道為什么是5個字節了吧  
  113.   HookOn();   
  114. }  
  115.   
  116.   
  117.   
  118. //開始Hook MessageBoxW  
  119. void CHookMsgBoxDlg::OnBnClickedBtnStartHook()  
  120. {  
  121.     DWORD dwPid=::GetCurrentProcessId();  
  122.     hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);   
  123.   
  124.     GetApiEntrance();  
  125.     SetDlgItemText(IDC_STATIC_INFO,_T("Hook已啟動"));  
  126. }  
  127.   
  128. //調用MessageBoxW  
  129. void CHookMsgBoxDlg::OnBnClickedBtnCallMsgBox()  
  130. {  
  131.     ::MessageBoxW(m_hWnd,_T("這是正常的MessageBoxW"),_T("Hello"),0);  
  132. }  
  133.   
  134. //停止Hook MessageBoxW  
  135. void CHookMsgBoxDlg::OnBnClickedBtnStopHook()  
  136. {  
  137.     HookOff();  
  138.     SetDlgItemText(IDC_STATIC_INFO,_T("Hook未啟動"));  
  139. }  



 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

您的十分滿意是我追求的宗旨。

您的一點建議是我后續的動力。

 

https://blog.csdn.net/friendan/article/details/12222651


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM