主要思想:
1、构造跳转指令。
2、在内存中找到欲HOOK函数地址,并保存欲HOOK位置处的原本字节。
3、将构造的跳转指令写入需HOOK的位置处。
4、当被HOOK位置被执行时会转到我们的流程执行。
5、如果要执行原来的流程,那么恢复HOOK,也就是还原被修改的字节。
6、执行函数原来的流程。
这部分其实参考了网上很多文章,自己感觉这些文章光写文字还是缺少一些对比,所以自己思考然后patch出来一些代码,自己理解与展示的更方便
众所周知,ms的库函数是从一条MOV EDI, EDI指令开始的,这是因为在运行时,如果需要一个填充字节,则会填充一条NOP指令,如果需要两个字节来填充,则会填充一条MOV EDI, EDI指令。
这样当hook时就有了五个字节给我们进行操作,可以对MessageBoxW的前五个字节(固定的mov edi,edi push ebp mov ebp,esp)进行修改,将其替换为一个五字节的近距离地址跳转的jmp指令跳转到我们自行构造的函数中。
至于修改字节,有几种方式,《加密与解密》中记录了jmp xxxxxxxx;push xxxxxxxx/retn;mov eax,xxxxxxxx/jmp eax;这几种方式。
代码实现:
#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <tchar.h>
//修改API入口为 jmp xxx 使得程序能跳转到自己的函数
BYTE __NewCode[7] = { 0xE9, 0x0, 0x0, 0x0, 0x0 };
BYTE __OldCode[7] = { 0 };
FARPROC Messagebox_FARPROC;
int WINAPI MyMessageBoxA(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
);
void InlineHook();
void main()
{
InlineHook();
//调用MessageBoxA
MessageBoxA(NULL, "Hello World", "Title", MB_OK);
}
void InlineHook()
{
DWORD dwOldProtect = 0;
//首先获取user32.dll的模块句柄
HMODULE user32_HMODULE = LoadLibraryA("user32.dll");
//利用GetProcAddress获取MessageBoxA导出函数的地址
Messagebox_FARPROC = GetProcAddress(user32_HMODULE, "MessageBoxA");
//利用ReadProcessMemory读取内存中MessageBoxA的前5字节
if (!ReadProcessMemory(GetCurrentProcess(), Messagebox_FARPROC, __OldCode, 6, NULL)) {
printf("[!] ReadProcessMemory Failed");
}
printf("%x%x%x%x%x\n", __OldCode[0], __OldCode[1], __OldCode[2], __OldCode[3], __OldCode[4]);
//E9跳转为jmp地址到目标地址的间隔,地址需要进行计算,计算出来后赋值给newcode的后4位
*((ULONG*)(__NewCode + 1)) = (ULONG)&MyMessageBoxA - ((ULONG)Messagebox_FARPROC + 5);
//memcpy(&__NewCode[1], &JmpAddress, 4);
printf("JmpAddress : %x\n", *((ULONG*)(__NewCode + 1)));
//将权限替换为可读写执行
VirtualProtect(Messagebox_FARPROC, 6, PAGE_EXECUTE_READWRITE, &dwOldProtect);
//直接写入内存
WriteProcessMemory(GetCurrentProcess(), Messagebox_FARPROC, __NewCode, 5, NULL);
//恢复之前的权限
VirtualProtect(Messagebox_FARPROC, 6, dwOldProtect, &dwOldProtect);
}
int WINAPI MyMessageBoxA(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
)
{
printf("Hook Successful\n");
//恢复原本的MessageBoxA,否则不能调用
WriteProcessMemory(GetCurrentProcess(), Messagebox_FARPROC,__OldCode, 5, NULL);
//可以使用参数进行调用
MessageBoxA(NULL, (const char*)lpText, (const char*)lpCaption, MB_OK);
MessageBoxA(NULL, "Xunflash", "Hook successful", MB_OK);
//恢复hook
WriteProcessMemory(GetCurrentProcess(), Messagebox_FARPROC, __NewCode, 5, NULL);
return 0;
}
假如在call跳转之前直接将程序hook的地址替换了那就更加方便了,可以直接将call跳转的地址替换成我们自己的函数地址,从而实现hook效果
感觉缺点也很明显,假如有很多次调用的话,需要反复修改不同位置的call(也可能是我理解错了)
利用函数上方的空间,将mov edi,edi和前面的nop/int 3进行替换,先使用一个向上的小跳转替换mov edi,edi。再用一个大跳转替换掉函数前面留空的nop/int 3,从而实现热补丁hook
这种方式可以避免多次hook后恢复原程序流程的时候的反复的卸载hook,提高效率。要恢复原流程只需要重新跳转到短跳转下方的push ebp即可
本文作者:Xunflash
本文链接:http://xunflash.top/index.php/archives/Inline_Hook.html
最后修改时间:2023-03-09 19:37:56
本站未注明转载的文章均为原创,并采用 CC BY-NC-SA 4.0 授权协议,转载请注明来源,谢谢!