Inline Hook研究

  1. 实验
  2. DetOurs库的使用
  3. 参考链接

Inline Hook是在程序调用某个API函数之前,在其上方设置指令跳转到自己写的的函数

实验

Hoo kMessageBox测试
x86思路如下:

1.调用 GetModuleHandle 来获取到模块的基址(user32.dll)
2.调用 GetProcAddress 获取到MessageBoxA弹窗的基址
3.调用 VirtualProtect 来修改MsgBox前5个字节内存属性
4.计算 Dest - MsgBox - 5 重定位跳转地址,并Jmp跳转
5.计算 Dest + Offset + 5 = MsgBox +5 跳转回来

疑问:为什么是5个字节?,jmp指令的机器码刚好5字节

E9 87 FE FF FF

而在调用MessageBoxA API之前的保存栈堆新开堆栈时刚好够5字节(机器码共5个字节,这种情况适用于大部分API但并非全部API都适用

8B FF mov edi,pdi
55 push ebp //新栈顶
8B EC mov ebp,esp //保留堆栈

Hook后这些指令就变成了一条jmp指令

最终在OD类似如下

x86内嵌asm hook实现如下:

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
typedef int (WINAPI
        *MessageBox_type) (
               __in_opt HWND hWnd,
               __in_opt LPCSTR lpText,
               __in_opt LPCSTR lpCaption,
               __in UINT uType);
MessageBox_type RealMessageBox = MessageBoxA;
//我们自己的MessageBox,每调用MessageBox都要跳到myMessageBox来处理
_declspec(naked)  void WINAPI
myMessageBox(
        __in_opt HWND hWnd,
        __in_opt LPCSTR lpText,
        __in_opt LPCSTR lpCaption,
        __in UINT uType)
{
        __asm
        {
               PUSH ebp
               mov ebp, esp
               /*
               vs2010 debug 编译后的代码由于要cmp esi esp来比较堆栈。
               所以这里在调用非__asm函数前push一下esi
               */
               push esi
        }

        //下面打印MessageBox参数
        printf("hwnd:%8X lpText:%s lpCaption:%s,uType:%8X", hWnd, lpText, lpCaption,  uType);
        __asm
        {
               /*
               vs2010 debug 编译后的代码由于要cmp esi esp来比较堆栈。
               所以这里在调用非__asm函数前push一下esi
               */
               pop esi
               mov ebx, RealMessageBox
               add ebx, 5 //地址恢复避免重复hook
               jmp ebx
        }
}
#pragma pack(1)
typedef struct _JMPCODE
{
        BYTE jmp;
        DWORD addr;
}JMPCODE, *PJMPCODE;
VOID HookMessageBoxA()
{
        JMPCODE jcode;
        jcode.jmp = 0xe9;//jmp
        jcode.addr = (DWORD)myMessageBox - (DWORD)RealMessageBox - 5; //自己的函数地址-MessageBox基址-jmp指令大小
        RealMessageBox = MessageBoxA;
        ::WriteProcessMemory(GetCurrentProcess(), MessageBoxA, &jcode, sizeof(JMPCODE),  NULL);
}
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                                       )
{
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
               HookMessageBoxA();  //hook操作
               MessageBoxA(NULL, "HOOK", "HOOKKKKKK", MB_OK);
               break;
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
               break;
        }
        return TRUE;
}

x64如下
1.备份原来的指令
2.8个0x90替换为要跳转的函数地址
3.自己的函数中要执行的内容
4.恢复原来的指令

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
BYTE oldcode[12] = { 0x00 };
BYTE HookCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF,   0xE0 };
void HookFunction(LPVOID lpFunction) {
        DWORD_PTR FuncAddress = (UINT64)GetProcAddress(GetModuleHandleA("user32.dll"),  "MessageBoxA");
        DWORD offset = 0;
        if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &offset)) {
               memcpy(oldcode, (LPVOID)FuncAddress, 12); // 拷贝原始机器码指令到oldcode
               *(PINT64)(HookCode + 2) = (UINT64)lpFunction; //填充90为指定跳转地址    (0x90修改为要跳转的地址)
        }
        memcpy((LPVOID)FuncAddress, &HookCode, sizeof(HookCode)); //拷贝hook指令
        VirtualProtect((LPVOID)FuncAddress, 12, offset, &offset); //还原页面保护属性
}
void UnHookFunction() {
        DWORD offset = 0;
        DWORD_PTR FuncAddress = (UINT64)GetProcAddress(GetModuleHandleA("user32.dll"),  "MessageBoxA");
        if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &offset)) {
               memcpy((LPVOID)FuncAddress, oldcode, sizeof(oldcode)); //恢复原来的机器指令
        }
        VirtualProtect((LPVOID)FuncAddress, 12, offset, &offset);//还原页面保护属性
}
int WINAPI TestMessageBoxA(HWND hwnd, LPCSTR lpText, LPCSTR lpcaption, UINT utype) {
        UnHookFunction(); //取消hook
        int ret = MessageBoxA(0, "HOOK", "title", MB_OK);
        HookFunction((PROC)TestMessageBoxA); //继续hook
        return ret;
}
BOOL APIENTRY DllMain(HMODULE hModule,
        DWORD  ul_reason_for_call,
        LPVOID lpReserved
)
{
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
               HookFunction((PROC)TestMessageBoxA);
               break;
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
               break;
        }
        return TRUE;
}

结果如下:

HookCode刨析:

x86话的HookCode为E9 00 00 00 00 (另外个执行例子)还需要减去5字节的便宜地址

x86非内嵌asm,x86要将自己函数处理的地址-被Hook函数+5得出的偏移地址 (至于为什么我也不知道,原文写的不清不楚)

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
BYTE oldcode[5] = { 0x00 };
BYTE HookCode[5] = { 0xe9, 0x0, 0x0, 0x0, 0x0 };
void HookFunction(LPVOID lpFunction) {
        DWORD_PTR FuncAddress = (UINT32)GetProcAddress(GetModuleHandleA("user32.dll"),  "MessageBoxA");
        DWORD offset = 0;
        DWORD dfset = 0;
        dfset = (DWORD)lpFunction - ((DWORD)FuncAddress + 5); //偏移地址计算
        if (VirtualProtect((LPVOID)FuncAddress, 5, PAGE_EXECUTE_READWRITE, &offset)) {
               memcpy(oldcode, (LPVOID)FuncAddress, 5); // 拷贝原始机器码指令到oldcode
               *(PINT32)(HookCode + 1) = (UINT32)dfset; //填充90为指定跳转地址    (0x90修改为要跳转的地址)
        }
        memcpy((LPVOID)FuncAddress, &HookCode, sizeof(HookCode)); //拷贝hook指令
        VirtualProtect((LPVOID)FuncAddress, 5, offset, &offset); //还原页面保护属性
}
void UnHookFunction() {
        DWORD offset = 0;
        DWORD_PTR FuncAddress = (UINT32)GetProcAddress(GetModuleHandleA("user32.dll"),  "MessageBoxA");
        if (VirtualProtect((LPVOID)FuncAddress, 5, PAGE_EXECUTE_READWRITE, &offset)) {
               memcpy((LPVOID)FuncAddress, oldcode, sizeof(oldcode)); //恢复原来的机器指令
        }
        VirtualProtect((LPVOID)FuncAddress, 5, offset, &offset);//还原页面保护属性
}
int WINAPI TestMessageBoxA(HWND hwnd, LPCSTR lpText, LPCSTR lpcaption, UINT utype) {
        UnHookFunction(); //取消hook
        int ret = MessageBoxA(0, "HOOK", "title", MB_OK);
        HookFunction((PROC)TestMessageBoxA); //继续hook
        return ret;
}
BOOL APIENTRY DllMain(HMODULE hModule,
        DWORD  ul_reason_for_call,
        LPVOID lpReserved
)
{
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
               HookFunction((PROC)TestMessageBoxA);
               break;
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
               break;
        }
        return TRUE;
}

其他例子SetConsoleTitleW Hook

Hook如下,一执行命令cmd的标题就会被更改

总的来说x86用内嵌asm,x64用memcpy插机器码

DetOurs库的使用

这玩意x64是商业版(
github地址:https://github.com/Microsoft/Detours
下完进入到目录,用vs命令行的nmake编译。然后将lib和include添加到项目

Hook MessageBoxA

#include "stdafx.h"
#include <detours.h>
#pragma comment(lib,"detours.lib")
static INT(WINAPI *OldMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT) = MessageBoxA; //定义被Hook的API函数原型
INT WINAPI MyMessAgeBoxA(HWND, LPCSTR, LPCSTR, UINT) {
        OldMessageBoxA(NULL, "Hook", "Title", MB_OK); //注意调用的被hook的函数时要使用自己定义的,否则会崩溃
        return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                                       )
{
        int ts;
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
               ts = DetourTransactionBegin();
               if (ts == NO_ERROR) {
                       DetourUpdateThread(GetCurrentThread());
                       DetourAttach(&(PVOID&)OldMessageBoxA, MyMessAgeBoxA);
                       DetourTransactionCommit();
               }
               break;
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
               break;
        }
        return TRUE;
}

参考链接

https://www.cnblogs.com/LyShark/p/11692436.html
https://developer.aliyun.com/article/568432
https://www.write-bug.com/article/1851.html


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。

文章标题:Inline Hook研究

本文作者:九世

发布时间:2021-08-19, 01:31:25

最后更新:2021-08-19, 01:44:49

原始链接:http://jiushill.github.io/posts/3d0703bd.html

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录