HooK原理簡析

通過反彙編代碼的跟進,瞭解Hook的工作原理(使用Detours庫實現Hook)

示例代碼

#include "stdafx.h"
#include <windows.h>
#include <detours.h>
#pragma  comment(lib,"detours.lib")

//1.拿到需要Hook的地址 操作系統裏面MessageboxA裏面的地址
static int (WINAPI* OldMessageBoxA)( HWND, LPCSTR, LPCSTR, UINT) = MessageBoxA;

//2.需要跳至的地方
int WINAPI NewMessageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
	if (strcmp(lpText, "This is zy") != 0)
	{
		return OldMessageBoxA(hWnd, "You are ugly!", "Sorry", MB_OK);
	}
	return OldMessageBoxA(hWnd, "This is zy", "Everyone", MB_OK);
}

//3.開始進行Hook地址
bool Hook() 
{
	// 相關的初始化信息
	DetourTransactionBegin();
	// 更新線程信息
	DetourUpdateThread(GetCurrentThread());
	// 掛載我們的hook函數(NeeMessagwBoxA)到MessageBoxA函數的地址上
	DetourAttach(&(PVOID&)OldMessageBoxA, NewMessageBoxA);
	return NO_ERROR == DetourTransactionCommit();
}

// 卸載Hook
bool UnHook()
{
	DetourTransactionBegin();
	DetourUpdateThread(GetCurrentThread());
	DetourDetach(&(PVOID&)OldMessageBoxA, NewMessageBoxA);
	return NO_ERROR == DetourTransactionCommit();
}

int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd )
{
	//跨進程改不了
	//因爲我們當前的這個Hook就是我們本進程
	//如果想跨進程,在Dll中Hook,然後注入 全局Hook
	//Hook在哪個地方 
	//檢測Hook CRC、
	//Hook後如何讓保證系統的穩定,這個做的Hook實在R3層,R0也能Hook
	MessageBoxA(NULL, "This is Hook", "Hook", MB_OK);
	Hook();
	MessageBoxA(NULL, "This is Hook", "Hook", MB_OK);
	UnHook();
	MessageBoxA(NULL, "This is Hook", "Hook", MB_OK);

//  加載Hook的Dll文件
// 	HMODULE hModule = LoadLibraryA("HooKDll.dll");
// 	if (hModule == NULL)
// 	{
// 		printf("LoadLibraryA faild!\n");
// 	}
// 	MessageBoxA(NULL, "This is Hook", "Hook", MB_OK);
// 	FreeLibrary(hModule);
    return 0;
}

運行結果:
這裏寫圖片描述


反彙編解析

接下來我們進入到反彙編格式查看下實現
第一句MessageBoxA(NULL, “This is Hook”, “Hook”, MB_OK);

// 壓棧數據,調用MessageBoxA該API
0124280E  mov         esi,esp  
01242810  push        0  
01242812  push        offset string "Hook" (0124AC64h)  
01242817  push        offset string "This is Hook" (0124BD88h)  
0124281C  push        0  
0124281E  call        dword ptr [__imp__MessageBoxA@16 (0124F0C8h)] 

// 查看MessageBox函數的首地址
76EEFDAE  mov         edi,edi  
76EEFDB0  push        ebp  
76EEFDB1  mov         ebp,esp  
76EEFDB3  push        0  
76EEFDB5  push        dword ptr [ebp+14h]  
76EEFDB8  push        dword ptr [ebp+10h]  
76EEFDBB  push        dword ptr [ebp+0Ch]  
76EEFDBE  push        dword ptr [ebp+8]  
76EEFDC1  call        76EEFD66  
76EEFDC6  pop         ebp  
76EEFDC7  ret         10h  

具體指令的作用就不一一解釋了,這不是重點。
可以看到MessageBox的函數首地址爲76EEFDAE,該處的彙編指令爲
mov edi,edi->不難看出,該指令的作用就是沒有作用!只是爲了方面擴展


經過Hook後
第二句MessageBoxA(NULL, “This is Hook”, “Hook”, MB_OK);

// 1、壓棧數據,調用MessageBoxA該API
01242830  mov         esi,esp  
01242832  push        0  
01242834  push        offset string "Hook" (0124AC64h)  
01242839  push        offset string "This is Hook" (0124BD88h)  
0124283E  push        0  
01242840  call        dword ptr [__imp__MessageBoxA@16 (0124F0C8h)]  

// 2、跟進查看MessageBox函數的首地址,此處原本應該是 mov edi,edi
76EEFDAE  jmp         NewMessageBoxA (01241424h)  
76EEFDB3  push        0  
76EEFDB5  push        dword ptr [ebp+14h]  
76EEFDB8  push        dword ptr [ebp+10h]  
76EEFDBB  push        dword ptr [ebp+0Ch]  
76EEFDBE  push        dword ptr [ebp+8]  
76EEFDC1  call        76EEFD66  
76EEFDC6  pop         ebp  
76EEFDC7  ret         10h  

// 3、在01241424h地址處,進行跳轉到012426C0h(我們真正的函數首地址)
01241424h  jmp         NewMwssageBoxA (012426C0h) 
int WINAPI NewMwssageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
012426C0  push        ebp  
012426C1  mov         ebp,esp  
012426C3  sub         esp,0C0h  
012426C9  push        ebx  
012426CA  push        esi  
012426CB  push        edi  
012426CC  lea         edi,[ebp-0C0h]  
012426D2  mov         ecx,30h  
012426D7  mov         eax,0CCCCCCCCh  
012426DC  rep stos    dword ptr es:[edi]  
	if (strcmp(lpText, "This is zy") != 0)
012426DE  push        offset string "This is zy" (0124AC30h)  
012426E3  mov         eax,dword ptr [lpText]  
012426E6  push        eax  
012426E7  call        _strcmp (01241136h)  
012426EC  add         esp,8  
012426EF  test        eax,eax  
012426F1  je          NewMwssageBoxA+54h (01242714h)  
	{
		return OldMwssageBoxA(hWnd, "You are ugly!", "Sorry", MB_OK);
		
//進行數據的壓棧
012426F3  mov         esi,esp  
012426F5  push        0  
012426F7  push        offset string "Sorry" (0124AC40h)  
012426FC  push        offset string "You are ugly!" (0124AC48h)  
01242701  mov         eax,dword ptr [hWnd]  
01242704  push        eax  

// 我們的程序將從此處跳轉
01242705  call        dword ptr [OldMwssageBoxA (0124E2D0h)] 

// 進行 RTC 運行時檢錯
0124270B  cmp         esi,esp  
0124270D  call        __RTC_CheckEsp (012411AEh)  
01242712  jmp         NewMwssageBoxA+73h (01242733h)  
	}
	return OldMwssageBoxA(hWnd, "This is zy", "Everyone", MB_OK);
01242714  mov         esi,esp  
01242716  push        0  
01242718  push        offset string "Everyone" (0124AC58h)  
0124271D  push        offset string "This is zy" (0124AC30h)  
01242722  mov         eax,dword ptr [hWnd]  
01242725  push        eax  
01242726  call        dword ptr [OldMwssageBoxA (0124E2D0h)]  
0124272C  cmp         esi,esp  
0124272E  call        __RTC_CheckEsp (012411AEh)  
}
01242733  pop         edi  
01242734  pop         esi  
01242735  pop         ebx  
01242736  add         esp,0C0h  
0124273C  cmp         ebp,esp  
0124273E  call        __RTC_CheckEsp (012411AEh)  
01242743  mov         esp,ebp  
01242745  pop         ebp  
01242746  ret         10h

// 4、經過在NewMwssageBoxA中的跳轉,進入到36ED00D8地址處
36ED00D8  mov         edi,edi  
36ED00DA  push        ebp  
36ED00DB  mov         ebp,esp  
36ED00DD  jmp         76EEFDB3 

// 5、很明顯,此時我們又跳轉回來了(此處和第二點是同一位置)
76EEFDB3  push        0  
76EEFDB5  push        dword ptr [ebp+14h]  
76EEFDB8  push        dword ptr [ebp+10h]  
76EEFDBB  push        dword ptr [ebp+0Ch]  
76EEFDBE  push        dword ptr [ebp+8]  
76EEFDC1  call        76EEFD66  
76EEFDC6  pop         ebp  
76EEFDC7  ret         10h  

通過該彙編指令的理解,可以發現:經過Hook後,改變了原來的函數首地址的指令mov edi,edi,使得該地址先跳轉到我們Hook後的地址上,先實現我們的函數,完成之後再跳轉到原來的地址上執行(當然,你也可以不用再跳回到原來的地址中執行),這也是Hook的原理

注意的是,該Hook僅僅是在本進程進行的,而且是Hook的ring3層


經過UnHook後,將會將我們的地址改變爲原來的mov edi,edi,和第三句
MessageBoxA(NULL, “This is Hook”, “Hook”, MB_OK);一樣

// 壓棧數據,調用MessageBoxA該API
01242852  mov         esi,esp  
01242854  push        0  
01242858  push        offset string "Hook" (0124AC64h)  
0124285D  push        offset string "This is Hook" (0124BD88h)  
01242863  push        0  
01242865  call        dword ptr [__imp__MessageBoxA@16 (0124F0C8h)] 

// 查看MessageBox函數的首地址
76EEFDAE  mov         edi,edi  
76EEFDB0  push        ebp  
76EEFDB1  mov         ebp,esp  
76EEFDB3  push        0  
76EEFDB5  push        dword ptr [ebp+14h]  
76EEFDB8  push        dword ptr [ebp+10h]  
76EEFDBB  push        dword ptr [ebp+0Ch]  
76EEFDBE  push        dword ptr [ebp+8]  
76EEFDC1  call        76EEFD66  
76EEFDC6  pop         ebp  
76EEFDC7  ret         10h  

總結下(Hook前後的對比):
這裏寫圖片描述

示例代碼需要Detours庫的支持,Detours下載地址
本文難免有所錯誤,如有問題歡迎留言

發佈了54 篇原創文章 · 獲贊 38 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章