HOOK API入門之Hook自己程式的MessageBoxW
原文地址:http://blog.csdn.net/friendan/article/details/12222651
說到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:
- int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType)
- {
- //定義假API時,具體的函式體程式碼暫時可不寫...
- return 0;
- }
至於我們如何知道真實API的原型定義呢?很簡單,檢視MSDN即可,或者你也可以在VS2008開發環境中,
寫下該真實API,然後選中該API,再點滑鼠右鍵,選擇【轉到定義】,就可以看到其原型宣告瞭。
...........................................................................................................................................................................
2.定義API函式型別
因為我們要動態獲取原API函式的地址,獲取到後,我們要將其儲存起來,儲存在哪裡呢?
這就是定義API函式型別的原因了,有了API函式型別的定義後,我們就可以用其定義一個變數來 儲存獲取到的
真實API函式的地址了,例如,我定義的MessageBoxW函式型別的語句如下:
- //原函式型別定義
- typedef int (WINAPI* MsgBoxW)(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);
上面就是定義了API函式型別MsgBoxW,這跟我們常見的int、char、float等型別,用法是一樣的,
實際上,我們完全可以把MsgBoxW當成int型別來使用,這樣一說,API函式型別也不過如此嘛。
有了API函式型別MsgBoxW,我們就可以用其來定義變數,從而為儲存原API地址做準備了。
如:
- MsgBoxW OldMsgBoxW=NULL;//指向原函式的指標
有了API函式型別後,我們還需要一個遠指標型別的變數,因為系統API是在dll檔案中的,更多關於
遠指標的資訊,我現在也不是很清楚,有興趣的盆友,不妨百度一下。系統已經給我們定義好了
遠指標型別:FARPROC,我們直接拿來用即可,如:
- FARPROC pfOldMsgBoxW; //指向函式的遠指標
..................................................................................................................................................................................................................
3.獲取API函式入口有了儲存原API函式地址的變數OldMsgBox和指向原API函式的遠指標,我們就可以獲取真實API的地址了。
如:
- //獲取原API入口地址
- HMODULE hmod=::LoadLibrary(_T("User32.dll"));
- OldMsgBoxW=(MsgBoxW)::GetProcAddress(hmod,"MessageBoxW");
- pfOldMsgBoxW=(FARPROC)OldMsgBoxW;
注意,上面我們將原API地址OldMsgBoxW強制轉換成了遠指標pfOldMsgBoxW,至於為什麼要強制轉換,
我現在也不是很清楚,想知道的童鞋,可以百度一下。
................................................................................................................................................................................................................
4.儲存原API入口的前5個位元組
儲存的目的是為了恢復用的,畢竟我們HOOK了API後,還需要呼叫真實的API嘛,
如何儲存一個函式入口的前5個位元組呢?這裡用到了彙編程式碼,至於不用匯編可以嗎?我想是可以的。
有空時,我再試一下,不用匯編是否能儲存一個API入口的前5個位元組,這裡就先用別人寫的彙編程式碼吧,
程式碼如下:
- // 將原API的入口前5個位元組程式碼儲存到OldCode[]
- BYTE OldCode[5];
- _asm
- {
- lea edi,OldCode //獲取OldCode陣列的地址,放到edi
- mov esi,pfOldMsgBoxW //獲取原API入口地址,放到esi
- cld //方向標誌位,為以下兩條指令做準備
- movsd //複製原API入口前4個位元組到OldCode陣列
- movsb //複製原API入口第5個位元組到OldCode陣列
- }
....................................................................................................................................................................................................................
5.獲取新入口的前5個位元組
因為我們修改真實API入口的前5個位元組,使其跳轉到我們假API函式的入口地址,即改成Jmp xxxxxxxx,其中xxxxxxxx就是我們
假API的入口地址,那麼我們如何得到該地址呢?
前人已經總結出了一條公式:
int nAddr= UserFunAddr – SysFunAddr - (我們定製的這條指令的大小);
Jmp nAddr; //我們要獲取的就是nAddr的值
既然已經有了公式,我們拿來用即可,UserFunAddr相當於我們的假API,SysFunAddr相當於真實API,這裡指令的大小為5,
實現以上公式的彙編程式碼如下:
- //獲取MyMessageBoxW的相對地址,為Jmp做準備
- //int nAddr= UserFunAddr – SysFunAddr - (我們定製的這條指令的大小);
- //Jmp nAddr;
- //(我們定製的這條指令的大小), 這裡是5,5個位元組嘛
- BYTE NewCode[5];
- _asm
- {
- lea eax,MyMessageBoxW //獲取我們的MyMessageBoxW函式地址
- mov ebx,pfOldMsgBoxW //原系統API函式地址
- sub eax,ebx //int nAddr= UserFunAddr – SysFunAddr
- sub eax,5 //nAddr=nAddr-5
- mov dword ptr [NewCode+1],eax //將算出的地址nAddr儲存到NewCode後面4個位元組
- //注:一個函式地址佔4個位元組
- }
......................................................................................................................................................................................................................
6.修改真實API入口的前5個位元組
前面我們已經儲存了真實API入口的前5個位元組,也已經計算出了新入口的前5個位元組,
可謂萬事俱備,只欠東風,現在可以修改真實API入口的前5個位元組了。
程式碼如下:
- //填充完畢,現在NewCode[]裡的指令相當於Jmp MyMessageBoxW
- //既然已經獲取到了Jmp MyMessageBoxW
- //現在該是將Jmp MyMessageBoxW寫入原API入口前5個位元組的時候了
- //知道為什麼是5個位元組嗎?
- //Jmp指令相當於0xe9,佔一個位元組的記憶體空間
- //MyMessageBoxW是一個地址,其實是一個整數,佔4個位元組的記憶體空間
- //int n=0x123; n佔4個位元組和MyMessageBoxW佔4個位元組是一樣的
- //1+4=5,知道為什麼是5個位元組了吧
- HookOn();
修改API入口前5個位元組的HookOn()函式具體程式碼如下:
- //開啟鉤子的函式
- void HookOn()
- {
- ASSERT(hProcess!=NULL);
- DWORD dwTemp=0;
- DWORD dwOldProtect;
- //修改API函式入口前5個位元組為jmp xxxxxx
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);
- WriteProcessMemory(hProcess,pfOldMsgBoxW,NewCode,5,0);
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);
- }
以上修改API函式入口前5個位元組,用到了兩個系統API函式,百度百科裡面對它們有詳解,在此就不說了。
注:hProcess是程式控制程式碼,我們可以這樣獲取它:
DWORD dwPid=::GetCurrentProcessId();
HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);
..........................................................................................................................................................................................................
7.恢復API函式入口前5個位元組
有修改,就有恢復嘛,恢復函式程式碼如下:
- //關閉鉤子的函式
- void HookOff()
- {
- ASSERT(hProcess!=NULL);
- DWORD dwTemp=0;
- DWORD dwOldProtect;
- //恢復API函式入口前5個位元組
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);
- WriteProcessMemory(hProcess,pfOldMsgBoxW,OldCode,5,0);
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);
- }
注:hProcess是程式控制程式碼,我們可以這樣獲取它:
DWORD dwPid=::GetCurrentProcessId();
HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);
以上就是HOOK API的大致流程了,看不明白的盆友,不妨再看下我寫的原始碼,文章結合原始碼,
希望有助於你們的理解。
原始碼下載地址:Hook自己程式的MessageBoxW.zip
學會了HOOK自己的程式,下篇我們再接再厲,HOOK系統所有程式的MessageBoxA和MessageBoxW
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
下面貼一下以上這個程式的主要程式碼,算是有個整體印象吧:
- //原函式型別定義
- typedef int (WINAPI* MsgBoxW)(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);
- MsgBoxW OldMsgBoxW=NULL;//指向原函式的指標
- FARPROC pfOldMsgBoxW; //指向函式的遠指標
- BYTE OldCode[5]; //原系統API入口程式碼
- BYTE NewCode[5]; //原系統API新的入口程式碼 (jmp xxxxxxxx)
- HANDLE hProcess=NULL;//本程式程式控制程式碼
- HINSTANCE hInst=NULL;//API所在的dll檔案控制程式碼
- void HookOn();
- void HookOff();
- int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType)
- {
- TRACE(lpText);
- HookOff();//呼叫原函式之前,記得先恢復HOOK呀,不然是呼叫不到的
- //如果不恢復HOOK,就呼叫原函式,會造成死迴圈
- //畢竟呼叫的還是我們的函式,從而造成堆疊溢位,程式崩潰。
- int nRet=::MessageBoxW(hWnd,_T("哈哈,MessageBoxW被HOOK了"),lpCaption,uType);
- HookOn();//呼叫完原函式後,記得繼續開啟HOOK,不然下次會HOOK不到。
- return nRet;
- }
- //開啟鉤子的函式
- void HookOn()
- {
- ASSERT(hProcess!=NULL);
- DWORD dwTemp=0;
- DWORD dwOldProtect;
- //修改API函式入口前5個位元組為jmp xxxxxx
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);
- WriteProcessMemory(hProcess,pfOldMsgBoxW,NewCode,5,0);
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);
- }
- //關閉鉤子的函式
- void HookOff()
- {
- ASSERT(hProcess!=NULL);
- DWORD dwTemp=0;
- DWORD dwOldProtect;
- //恢復API函式入口前5個位元組
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);
- WriteProcessMemory(hProcess,pfOldMsgBoxW,OldCode,5,0);
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);
- }
- //獲取API函式入口前5個位元組
- //舊入口前5個位元組儲存在前面定義的位元組陣列BYTE OldCode[5]
- //新入口前5個位元組儲存在前面定義的位元組陣列BYTE NewCode[5]
- void GetApiEntrance()
- {
- //獲取原API入口地址
- HMODULE hmod=::LoadLibrary(_T("User32.dll"));
- OldMsgBoxW=(MsgBoxW)::GetProcAddress(hmod,"MessageBoxW");
- pfOldMsgBoxW=(FARPROC)OldMsgBoxW;
- if (pfOldMsgBoxW==NULL)
- {
- MessageBox(NULL,_T("獲取原API入口地址出錯"),_T("error!"),0);
- return;
- }
- // 將原API的入口前5個位元組程式碼儲存到OldCode[]
- _asm
- {
- lea edi,OldCode //獲取OldCode陣列的地址,放到edi
- mov esi,pfOldMsgBoxW //獲取原API入口地址,放到esi
- cld //方向標誌位,為以下兩條指令做準備
- movsd //複製原API入口前4個位元組到OldCode陣列
- movsb //複製原API入口第5個位元組到OldCode陣列
- }
- NewCode[0]=0xe9;//實際上0xe9就相當於jmp指令
- //獲取MyMessageBoxW的相對地址,為Jmp做準備
- //int nAddr= UserFunAddr – SysFunAddr - (我們定製的這條指令的大小);
- //Jmp nAddr;
- //(我們定製的這條指令的大小), 這裡是5,5個位元組嘛
- BYTE NewCode[5];
- _asm
- {
- lea eax,MyMessageBoxW //獲取我們的MyMessageBoxW函式地址
- mov ebx,pfOldMsgBoxW //原系統API函式地址
- sub eax,ebx //int nAddr= UserFunAddr – SysFunAddr
- sub eax,5 //nAddr=nAddr-5
- mov dword ptr [NewCode+1],eax //將算出的地址nAddr儲存到NewCode後面4個位元組
- //注:一個函式地址佔4個位元組
- }
- //填充完畢,現在NewCode[]裡的指令相當於Jmp MyMessageBoxW
- //既然已經獲取到了Jmp MyMessageBoxW
- //現在該是將Jmp MyMessageBoxW寫入原API入口前5個位元組的時候了
- //知道為什麼是5個位元組嗎?
- //Jmp指令相當於0xe9,佔一個位元組的記憶體空間
- //MyMessageBoxW是一個地址,其實是一個整數,佔4個位元組的記憶體空間
- //int n=0x123; n佔4個位元組和MyMessageBoxW佔4個位元組是一樣的
- //1+4=5,知道為什麼是5個位元組了吧
- HookOn();
- }
- //開始Hook MessageBoxW
- void CHookMsgBoxDlg::OnBnClickedBtnStartHook()
- {
- DWORD dwPid=::GetCurrentProcessId();
- hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);
- GetApiEntrance();
- SetDlgItemText(IDC_STATIC_INFO,_T("Hook已啟動"));
- }
- //呼叫MessageBoxW
- void CHookMsgBoxDlg::OnBnClickedBtnCallMsgBox()
- {
- ::MessageBoxW(m_hWnd,_T("這是正常的MessageBoxW"),_T("Hello"),0);
- }
- //停止Hook MessageBoxW
- void CHookMsgBoxDlg::OnBnClickedBtnStopHook()
- {
- HookOff();
- SetDlgItemText(IDC_STATIC_INFO,_T("Hook未啟動"));
- }
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
相關文章
- React Hook快速入門ReactHook
- React Hook 入門使用ReactHook
- Hook技術之Hook ActivityHook
- hook初識之inline hookHookinline
- React學習之HookReactHook
- Windows Dll Injection、Process Injection、API Hook、DLL後門/惡意程式入侵技術WindowsAPIHook
- hook!Hook
- Android Hook 神器——XPosed入門(登陸劫持演示)AndroidHook
- JS HOOK 程式碼段JSHook
- 淺談API HOOK技術(一) (轉)APIHook
- 淺談API HOOK技術(二) (轉)APIHook
- Android外掛化原理解析——Hook機制之Binder HookAndroidHook
- IAT HookHook
- exit hookHook
- react hookReactHook
- Hook原理Hook
- hook zwcreateprocessexHook
- Javascript HookJavaScriptHook
- Android 外掛化原理解析(3):Hook 機制之 Binder HookAndroidHook
- iOS逆向 程式碼注入+HookiOSHook
- Windows核心程式設計_HookWindows程式設計Hook
- 全屏API及vue3 hook封裝APIVueHook封裝
- 安卓黑科技之HOOK詳解安卓Hook
- api-hook,更輕量的介面測試工具APIHook
- 以pytorch的forward hook為例探究hook機制PyTorchForwardHook
- Hook踩坑記:React Hook react-unity-webglHookReactUnityWeb
- hook 系統api啟動未註冊ActivityHookAPI
- 企業微信PC版hook原始碼api介面Hook原始碼API
- 非越獄下 iOS程式碼注入&HOOK微信登入iOSHook
- 使用Hook寫ReduxHookRedux
- An example about git hookGitHook
- gitlab web hookGitlabWebHook
- 關於Hook CreateMutexHookMutex
- Hook簡介 (轉)Hook
- PReact10.5.13原始碼理解之hookReact原始碼Hook
- WINDOWS下的各類HOOKWindowsHook
- PC微信機器人介面api之od找登陸二維碼hook地址機器人APIHook
- React Hook 提高程式碼複用性ReactHook