1. 程式人生 > 其它 >Windows下32位程式的Inline Hook

Windows下32位程式的Inline Hook

話不多說,首先貼程式碼,程式碼主體參考自一本關於黑客技術的書,書名給忘了。

當時只是敲出來跟著用,也沒有深入理解,最近再看,才發現一些原理。

Inline Hook的好處就是可以獲取被Hook函式的引數,我們可以自行處理這些資料,再呼叫本來的Hook函式。

#include <stdio.h>
#include <windows.h>
#include<iostream>
using namespace std;
class MyHook
{
public:
    MyHook()
    {
        funcAddr = NULL;
        ZeroMemory(oldBytes,
5); ZeroMemory(newBytes,5); } ~MyHook() { UnHook(); funcAddr = NULL; ZeroMemory(oldBytes,5); ZeroMemory(newBytes,5); } /* *Hook的模組名稱,Hook的API函式名稱,鉤子函式地址 */ WINBOOL Hook(LPSTR ModuleName, LPSTR FuncName, PROC HookFunc) { BOOL bRet
= FALSE; funcAddr = (PROC)GetProcAddress(GetModuleHandleA(ModuleName),FuncName); cout<<funcAddr<<endl; if(funcAddr!=NULL) { DWORD num = 0; ReadProcessMemory(GetCurrentProcess(),(void*)funcAddr,oldBytes,5,&num); newBytes[
0] = 0xe9; *(DWORD *)(newBytes + 1) = (DWORD)HookFunc - (DWORD)funcAddr - 5; WriteProcessMemory(GetCurrentProcess(),(void*)funcAddr,newBytes,5,&num); bRet = TRUE; } return bRet; } void UnHook() { if(funcAddr!=0) { DWORD num = 0; WriteProcessMemory(GetCurrentProcess(),(void*)funcAddr,oldBytes,5,&num); } } WINBOOL ReHook() { BOOL ret = FALSE; if(funcAddr!=0) { DWORD num; WriteProcessMemory(GetCurrentProcess(),(void*)funcAddr,newBytes,5,&num); ret = TRUE; } return ret; } private: PROC funcAddr; BYTE oldBytes[5]; BYTE newBytes[5]; }; MyHook hook; int WINAPI MyMessageBoxA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT type) { hook.UnHook(); MessageBox(hWnd,"Hook流程",lpCaption,type); MessageBox(hWnd,lpText,lpCaption,type); hook.ReHook(); return 0; } int main() { MessageBox(NULL,"正常流程1","test",MB_OK); hook.Hook((LPSTR)"User32.dll",(LPSTR)"MessageBoxA",(PROC)&MyMessageBoxA); MessageBox(NULL,"被Hook了1","test",MB_OK); // MessageBox(NULL,"被Hook了2","test",MB_OK); hook.UnHook(); // MessageBox(NULL,"正常流程2","test",MB_OK); }

既不解釋程式碼含義,也不解釋Win32 API。需要注意的就是以下兩點。

首先來看圖

為什麼是0xe9?
0xe9代表jmp指令。
但是圖上不是EB嗎?
因為平時使用OD這樣的除錯工具,大多數情況下修改跳轉,把jne之類的修改成jmp,
都是修改成了EB,久而久之,就忘了,jmp有好幾種跳轉方式,這種修改應該是屬於直接跳轉。 Jmp
short.(說是short,實際上是一個帶符號的位元組範圍 -128~127)。 如圖所示,地址0x00401200 和0x00401216,有著0x14個位元組的指令,這就是直接跳轉。
1個位元組,肯定不夠我們跳轉的,所以這裡要用的是第2種跳轉方式。再看另一副圖
從地址0x0041c3c1跳轉到0x41c2ae,用的是E9 e8feffff
因為32位下jmp遠跳一共用了5位元組的記憶體。
E9 是jmp ,對應程式碼中的第一處
剩下4位元組儲存的是 目的地址-起始地址-指令長度,對應程式碼中的第二處
所以才有 0x41c2ae-0x41c3c1-5 = FFFFFEE8 。因為記憶體裡面是小端,所以顯示出來就是E8FEFFFF。

一種比較好的測試方案,就是使用OD除錯以上程式碼,ctrl+g跳轉到MessageBoxA的地址處,看看隨著程式碼的執行,該地址處的指令如何變化。