【逆向学习】Inline Hook学习

· 2023-03-09 · 1037 人浏览

inline hook

主要思想:

1、构造跳转指令。

2、在内存中找到欲HOOK函数地址,并保存欲HOOK位置处的原本字节。

3、将构造的跳转指令写入需HOOK的位置处。

4、当被HOOK位置被执行时会转到我们的流程执行。

5、如果要执行原来的流程,那么恢复HOOK,也就是还原被修改的字节。

6、执行函数原来的流程。

hook方式

这部分其实参考了网上很多文章,自己感觉这些文章光写文字还是缺少一些对比,所以自己思考然后patch出来一些代码,自己理解与展示的更方便

1、call跳转后修改系统函数

众所周知,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;这几种方式。

image-20230308190458298

image-20230308190545893

代码实现:

#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;
}

2、call跳转前替换函数地址

假如在call跳转之前直接将程序hook的地址替换了那就更加方便了,可以直接将call跳转的地址替换成我们自己的函数地址,从而实现hook效果

感觉缺点也很明显,假如有很多次调用的话,需要反复修改不同位置的call(也可能是我理解错了)

image-20230308190710851

image-20230308190645876

3、热补丁

利用函数上方的空间,将mov edi,edi和前面的nop/int 3进行替换,先使用一个向上的小跳转替换mov edi,edi。再用一个大跳转替换掉函数前面留空的nop/int 3,从而实现热补丁hook

这种方式可以避免多次hook后恢复原程序流程的时候的反复的卸载hook,提高效率。要恢复原流程只需要重新跳转到短跳转下方的push ebp即可

image-20230308185637484

image-20230308190009834

Theme Jasmine by Kent Liao