inline hook 實際上就是指 通過改變目標函數頭部的代碼來使改變后的代碼跳轉到我們自己設置的一個函數里,產生hook。
今天就拿MessageBoxA這個api函數來做實驗。功能就是當程序調用MessageBoxA 時,我們打印出MessageBoxA的參數
大概代碼結構應該是這樣
typedef int (WINAPI *MessageBox_type) ( __in_opt HWND hWnd, __in_opt LPCSTR lpText, __in_opt LPCSTR lpCaption, __in UINT uType) ; MessageBox_type RealMessageBox; //我們自己的MessageBox,每調用MessageBox都要跳到myMessageBox來處理 int WINAPI myMessageBox( __in_opt HWND hWnd, __in_opt LPCSTR lpText, __in_opt LPCSTR lpCaption, __in UINT uType) { //下面打印MessageBox參數 printf("hwnd:%8X lpText:%s lpCaption:%s,uType:%8X",hWnd,lpText,lpCaption,uType); return RealMessageBox(hWnd,lpText,lpCaption,uType); //現在開始調用真正的MessageBox } VOID HookMessageBoxA() { } int _tmain(int argc, _TCHAR* argv[]) { HookMessageBoxA(); //hook操作 ::MessageBoxA(NULL,"hook test","tip",MB_OK); //執行api MessageBox return 0; }
我們先看看匯編是怎樣調用MessageBoxA的
首先看到,MessageBoxA里面
mov edi,edi
mov ebp
mov ebp,esp
剛好是5個字節,5個字節可以做一個遠jmp
直接匯編改成我們自己的jmp
改后結果如下
單步執行發現hook成功。但程序崩潰。原因主要是由於
我們破壞了真正的MessageBox使們想要調用真正的MessageBox時也會調用失敗了,所以我們要調用真正的MessageBox時要加上頭部被我們換掉的部分,我們要內聯匯編,里面不能含有編譯器自動添加的代碼,所以在myMessageBox頭部要加上 _declspec(naked)
vs2010的debug版本每執行一個函數都要 cmp esi,esp 來驗證堆棧的。所以還要加一句push esi 和pop esi
//我們自己的MessageBox,每調用MessageBox都要跳到myMessageBox來處理 _declspec(naked) void WINAPI myMessageBox( __in_opt HWND hWnd, __in_opt LPCSTR lpText, __in_opt LPCSTR lpCaption, __in UINT uType) { __asm { PUSH ebp mov ebp,esp /* vs2010 debug 編譯后的代碼由於要cmp esi esp來比較堆棧。 所以這里在調用非__asm函數前push一下esi */ push esi } //下面打印MessageBox參數 printf("hwnd:%8X lpText:%s lpCaption:%s,uType:%8X",hWnd,lpText,lpCaption,uType); __asm { /* vs2010 debug 編譯后的代碼由於要cmp esi esp來比較堆棧。 所以這里在調用非__asm函數前push一下esi */ pop esi mov ebx,RealMessageBox add ebx,5 jmp ebx } }
下面說一下用代碼來寫MessageBoxA着呢5個字節
首先要懂得 JMP指令轉換公式推導 不懂的話 看 http://www.cnblogs.com/zhangdongsheng/archive/2012/12/06/2804234.html
先聲明一個JMP結構體。注意前面加 #pragma pack(1)來避免內存對齊的一些規則
#pragma pack(1) typedef struct _JMPCODE { BYTE jmp; DWORD addr; }JMPCODE,*PJMPCODE;
接下來寫hook函數
VOID HookMessageBoxA() { JMPCODE jcode; jcode.jmp = 0xe9;//jmp jcode.addr = (DWORD)myMessageBox - (DWORD)RealMessageBox - 5; RealMessageBox = MessageBoxA; ::WriteProcessMemory(GetCurrentProcess(),MessageBoxA,&jcode,sizeof(JMPCODE),NULL); }
現在測試成功。
完整源代碼如下:
// hook_blog_writer.cpp : 定義控制台應用程序的入口點。 // #include "stdafx.h" #include <windows.h> #include <stdio.h> typedef int (WINAPI *MessageBox_type) ( __in_opt HWND hWnd, __in_opt LPCSTR lpText, __in_opt LPCSTR lpCaption, __in UINT uType) ; MessageBox_type RealMessageBox = MessageBoxA; //我們自己的MessageBox,每調用MessageBox都要跳到myMessageBox來處理 _declspec(naked) void WINAPI myMessageBox( __in_opt HWND hWnd, __in_opt LPCSTR lpText, __in_opt LPCSTR lpCaption, __in UINT uType) { __asm { PUSH ebp mov ebp,esp /* vs2010 debug 編譯后的代碼由於要cmp esi esp來比較堆棧。 所以這里在調用非__asm函數前push一下esi */ push esi } //下面打印MessageBox參數 printf("hwnd:%8X lpText:%s lpCaption:%s,uType:%8X",hWnd,lpText,lpCaption,uType); __asm { /* vs2010 debug 編譯后的代碼由於要cmp esi esp來比較堆棧。 所以這里在調用非__asm函數前push一下esi */ pop esi mov ebx,RealMessageBox add ebx,5 jmp ebx } } #pragma pack(1) typedef struct _JMPCODE { BYTE jmp; DWORD addr; }JMPCODE,*PJMPCODE; VOID HookMessageBoxA() { JMPCODE jcode; jcode.jmp = 0xe9;//jmp jcode.addr = (DWORD)myMessageBox - (DWORD)RealMessageBox - 5; RealMessageBox = MessageBoxA; ::WriteProcessMemory(GetCurrentProcess(),MessageBoxA,&jcode,sizeof(JMPCODE),NULL); } int _tmain(int argc, _TCHAR* argv[]) { HookMessageBoxA(); //hook操作 ::MessageBoxA(NULL,"hook test","tip",MB_OK); //執行api MessageBox return 0; }



