lnlineHook即内联钩子,不直接修改地址表的函数跳转地址,lnlinehook通过修改函数指令来实现拦截函数
5字节
JMP指令分为短跳转E8:2个机器码,长跳转E9:5个机器码,做hook时,长跳转E9才能跳转内存地址的需要
完整代码如下,该示例运行在x86平台
#include <windows.h>
// 存储替换前的5字节
char originalBytes[5];
FARPROC hookedAddress;
// hook后跳转到的自定义函数
int __stdcall myFunc(LPCSTR lpCmdLine, UINT uCmdShow) {
// 恢复原来的5字节数据
WriteProcessMemory(GetCurrentProcess(), (LPVOID)hookedAddress, originalBytes, 5, NULL);
// 自定义内容
return WinExec("calc", uCmdShow);
}
// hook函数
void setMySuperHook() {
HINSTANCE hLib;
VOID* myFuncAddress;
DWORD* rOffset;
DWORD src;
DWORD dst;
CHAR patch[5] = { 0 };
// 提取WinExec函数
hLib = LoadLibraryA("kernel32.dll");
hookedAddress = GetProcAddress(hLib, "WinExec");
// 备份原来5个字节
ReadProcessMemory(GetCurrentProcess(), (LPCVOID)hookedAddress, originalBytes, 5, NULL);
// 指向自定义函数
myFuncAddress = &myFunc;
// 计算偏移
//“+5”在x86汇编中,JMP指令需要5个字节,而要在被hook的函数指令前面插入一个JMP指令,需要向后移动5个字节,才能腾出空间给新的指令。
src = (DWORD)hookedAddress + 5;
dst = (DWORD)myFuncAddress;
// 转移偏移量公式:目的地址 - 起始地址 - 跳转指令自身长度
rOffset = (DWORD*)(dst - src);
// 将JMP指令和相对偏移量打包成一个5字节的二进制代码序列,机器码占用1字节,跳转到的地址偏移占用4字节
memcpy(patch, "\xE9", 1);
memcpy(patch + 1, &rOffset, 4);
// 写入5字节
WriteProcessMemory(GetCurrentProcess(), (LPVOID)hookedAddress, patch, 5, NULL);
}
int main() {
WinExec("notepad", SW_SHOWDEFAULT);
// hook
setMySuperHook();
WinExec("notepad", SW_SHOWDEFAULT);
}
其中一个notepad被hook到了calc
7字节
inline hook是非线程稳定的,在多线程的程序中,传统5字节在调用API时需频繁的进行脱钩和挂钩过程,有可能会导致程序崩溃,不稳定,为了完成安全的hook机制,微软预留热补丁的机制
如下图
很多函数地址上方会留空五个或五个以上的无意义字节
因此,我们在创建hook时,可以尝试把这类两个字节的地址改成两个字节的跳转指令E8 xx,再将此地址之上的5个字节的位置写入跳转函数位置,即所谓的7字节hook = 2字节的短跳转指令+5字节的长指令
这个帖子给出了很详细的例子和说明:https://bbs.kanxue.com/thread-252074.htm#msg_header_h3_3