InlineHook实践

2023-03-23 09:16:37 浏览数 (1)

InlineHook介绍

  InlineHook是一种指令级的函数hook方式,通过直接修改运行时内存的方式替换指令,完全手工的完成hook及跳回操作,理论上可以实现任意位置的hook。平时使用Frida进行Native hook实际上也是用的InlineHook的原理。

  InlineHook的大概实现流程是:

  1. 获取target函数地址
  2. 读取target函数开头的指令,并保存为opcode_src
  3. 修改内存,将target函数开头的指令改为跳转指令opcode_jmp,使其跳转到自己编写的my_target函数的开头地址,实现函数替换
  4. 如果在my_target函数需要调用target函数,先把target函数的指令修改回去opcode_src,然后调用,调用完成后重新修改为opcode_jmp

  理论部分大概就这些,接下来讲下实践。这篇文章讲会介绍在Windows平台下,使用DLL注入的方式来实现C 语言的inlinehook的实现。

Target程序编写

  随便写一个简单的程序用来hook测试

代码语言:javascript复制
#include <stdio.h>
#include <Windows.h>

int targetFunction(){
    return 666;
}

int main(){
    printf("Calling taget function....n");
    int result = targetFunction();
    printf("result: %dn",result);

    system("pausen");

    printf("Calling taget function second time....n");
    result = targetFunction();
    printf("result: %dn",result);

    return 0;
    
}

  这段程序运行后会调用targetFunction,然后暂停,方便我们进行hook,最后再调用一次targetFunction

DLL编写

  我们要编写的DLL它的功能是能够在自己被加载时自动地对targetFunction进行Hook,需要用到DllMain函数,这个函数在Dll中相当于正常程序中的main函数,在自己被加载或者被解除加载时会被调用。声明方式为bool DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)fdwReason为调用的理由,当其为DLL_PROCESS_ATTACH时代表被加载,当前为DLL_PROCESS_DETACH时代表被释放。这边有个坑是这个函数名称为DllMain而不是DLLMain,要看清楚否则加载时不会调用。

  然后在这边稍微提一下,虽然Hook时用不到但是还是讲下,在写DLL的导出函数时,根据编译器的不同导出函数的声明方式也不一样,网上的教程都是让你写__declspec(dllexport)然而编译报错,因为我用的是g ,所以其实要写成__attribute__((visibility("default")))。编译时输入g -shared ./mydll.cpp -o mydll.dll即可。

  最后按照开头介绍的流程编写hook的具体实现即可,目标函数的具体地址可以把编译好的EXE拖到IDA中看一下,跳转指令的汇编网上查一下就差不多了,具体代码如下:

代码语言:javascript复制
#if defined(_MSC_VER)
    //  Microsoft 
    #define EXPORT __declspec(dllexport)
    #define IMPORT __declspec(dllimport)
#elif defined(__GNUC__)
    //  GCC
    #define EXPORT __attribute__((visibility("default")))
    #define IMPORT
#else
    //  do nothing and hope for the best?
    #define EXPORT
    #define IMPORT
    #pragma warning Unknown dynamic link import/export semantics.
#endif

#include <Windows.h>
#include <stdio.h>

extern "C" EXPORT int add(int a, int b);
typedef int(*targetFunc)();

targetFunc fun = (targetFunc)0x401550;

BYTE sourceOpCode[5] = {0x00};
BYTE jmpOpCode[5] = {0xE9};

int add(int a, int b)
{
    return a   b;
}

void EnableHook(BOOL Enable = TRUE)
{
    DWORD OldProtect = 0;
    VirtualProtect((LPVOID)fun, 5, PAGE_EXECUTE_READWRITE, &OldProtect);
    memcpy((LPVOID)fun, Enable ? jmpOpCode : sourceOpCode, 5);
    VirtualProtect((LPVOID)fun, 5, OldProtect, &OldProtect);
}

int myTargetFunc(){
    printf("Before calling targetFuncn");
    EnableHook(FALSE);
    int result = fun();
    EnableHook(TRUE);
    printf("After calling targetFuncn");
    printf("Replace result to 999n");
    return 999;
}

bool init()
{
    memcpy(sourceOpCode, (LPVOID)fun, 5);
    DWORD64 jmpOffset = (DWORD64)myTargetFunc-(DWORD64)fun-5;
    *(DWORD64*)&jmpOpCode[1] = jmpOffset;
    return true;
}


bool DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    switch (fdwReason){
        case DLL_PROCESS_ATTACH:
            printf("DLL loadedn");
            if(init()){
                EnableHook(TRUE);
            }
            break;
        case DLL_PROCESS_DETACH:
            printf("DLL unloadedn");
            break;
    }
    return true;
}

DLL注入

  Windows平台的DLL注入比安卓平台的SO注入更简单,只需要知道目标进程的PID即可注入,无需管理员权限。主要使用CreateRemoteThread来创建远程线程从而加载DLL,代码如下:

代码语言:javascript复制
#include <stdio.h>
#include <Windows.h>

typedef int(*addFunc)(int,int);

int main()
{

    DWORD Pid = 7788;
    SIZE_T Size = 0;
    const char* DllName = "D:\Coding\cplus\learn\mydll.dll";
    HANDLE Handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
    LPVOID Addr = VirtualAllocEx(Handle, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    WriteProcessMemory(Handle, Addr, DllName, strlen(DllName)   1, &Size);
    CreateRemoteThread(Handle, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryA, Addr, NULL, NULL);

    return 0;
}

  通过这段代码我们可以指定PID为7788的进程去加载mydll.dll,从而触发mydll中的DllMain函数来完成一些函数调用,因此我们可以在mydll中编写hook函数。

效果

  就不放截图了,自己这边测试通过了,先打开target.exe会输出666,在打开main.exe实现注入后会打印999。

Reference

PE基础7-HOOK练习

0 人点赞