通過反彙編代碼的跟進,瞭解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下載地址
本文難免有所錯誤,如有問題歡迎留言