lnlineHooK两种类型

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

image-20230305194740803

7字节

inline hook是非线程稳定的,在多线程的程序中,传统5字节在调用API时需频繁的进行脱钩和挂钩过程,有可能会导致程序崩溃,不稳定,为了完成安全的hook机制,微软预留热补丁的机制

如下图

image-20230305215119824

很多函数地址上方会留空五个或五个以上的无意义字节

因此,我们在创建hook时,可以尝试把这类两个字节的地址改成两个字节的跳转指令E8 xx,再将此地址之上的5个字节的位置写入跳转函数位置,即所谓的7字节hook = 2字节的短跳转指令+5字节的长指令

这个帖子给出了很详细的例子和说明:https://bbs.kanxue.com/thread-252074.htm#msg_header_h3_3

发表评论