首页
社区
课程
招聘
[原创]hook函数返回1
2019-4-5 11:47 4990

[原创]hook函数返回1

2019-4-5 11:47
4990

作者签名

作者:oHuangKeo
时间:2019-04-05

应用场景

有些时候,我们需要hook一些函数,达到我们预想的一些效果。
普通的windows api就不说了,hook的资料还是很多的。
但是普通的call和function相关资料还是很少的。
最近反编译一个项目是,需要这样的功能,这里整理了一下资料,做了个小DEMO演示一下。

模拟效果

某个call调用一个function,原本返回的0,这里修改返回1

忽略

代码中有些细节有更好的解决方案,请忽略,不同的项目中有不同的适合解决方案,这里只做一个演示。

划重点,要考的,呵呵

  • hook模版
  • 查找call/function
  • 计算修改call/jmp的偏移值

流程、逻辑

  • 查找function的内存地址,例如:0x00413CC0
  • 搜索代码相关代码段,查找调用function的call,例如:0x00418125 call 0x00413CC0
  • 申请一个小内存,将hook模版写入。
      // hook模版
      // ... 这里可以增加一些预处理代码,例如修改参数的数据
      call 0x00000000 // call function
      // ... 这里可以增加一些后置代码,例如修改某些数据
      mov eax, 1      // 将返回值改为1
      jmp 0x00000000  // 返回代码写一个code
    
  • 写入正确的function和call的偏移地址。
      call 0x00413CC0
      mov eax, 1
      jmp 0x0041812A // 0x00418125+0x5
    
  • 修改code内存页权限,修改原call代码为jmp,改回内存页权限
      VirtualProtect                // PAGE_EXECUTE_READWRITE
      *(BYTE*)fromAddr = 0xE9;      // JMP
      *(int*)(fromAddr + 1) = fix3; // JMP x
      VirtualProtect                // 恢复
    
  • 恢复执行,完美搞定

完整DEMO代码

#include <string>
#include <windows.h>
using namespace std;

// 一个正常的普通函数,最后返回0/1,这里模拟返回0
static __declspec(naked) bool dosomething() {
    __asm {
        // ... code ...
        mov eax, 1; // B8 01 00 00 00
        // ... code ...
        mov eax, 2; // B8 02 00 00 00
        // ... code ...
        mov eax, 3; // B8 03 00 00 00
        // ... code ...
        mov eax, 4; // B8 04 00 00 00
        // return false
        xor eax, eax; // 33 C0
        ret;
    }
}

// hook 代码
static __declspec(naked) void return1() {
    __asm {
        // call x // 5bit
        _emit 0xE8;
        _emit 0;
        _emit 0;
        _emit 0;
        _emit 0;

        mov eax, 1; // 5bit

        // jmp far:x
        __emit 0xE9; // 5bit
        _emit 0;
        _emit 0;
        _emit 0;
        _emit 0;
    }
}

// 设置hook
void* setReturn1(int toAddr, int fromAddr) {

    int isize = 5 + 5 + 5;
    char *p1 = (char*)VirtualAlloc(0, isize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memset(p1, 0, isize);
    char *p = (char*)&return1;
    // 改用汇编,不然Release编译时会出错
    // memcpy(p1, p, isize);
    __asm {
        pushad;
        mov esi, p;
        mov edi, p1;
        mov ecx, isize;
        rep movsb;
        popad;
    }

    // 目标地址 - (当前地址 + 指令长度)
    int fix1 = toAddr - ((int)p1 + 5);
    int fix2 = (fromAddr + 5) - ((int)p1 + 0xA + 5); // ret addr
    int fix3 = (int)p1 - (fromAddr + 5);
    *(int*)((int)p1 + 1) = fix1;
    *(int*)((int)p1 + 0xB) = fix2;

    // hook
    DWORD flOldProtect;
    VirtualProtect((LPVOID)fromAddr, 5, PAGE_EXECUTE_READWRITE, &flOldProtect);
    *(BYTE*)fromAddr = 0xE9; // JMP x
    *(int*)(fromAddr + 1) = fix3; // JMP x
    VirtualProtect((LPVOID)fromAddr, 5, flOldProtect, &flOldProtect);

    return p1;
}

// 查找方法地址
int findCallAddr(char *code, int codeSize, char *bin, int binSize) {
    string s(code, codeSize);
    int p = s.find(bin, 0, binSize);
    if (p < 0) return 0;
    return (int)code + p;
}

// 查找调用方法的call
int findCall(char *start, char *end, int callAddr) {
    for (int p = (int)start; p < (int)end; p++) {
        if (p < 0x417E00) continue;
        if (*(BYTE*)p != 0xE8) continue; // 不是call,继续找
        int addr = *(int*)(p + 1); // 操作数
        if (callAddr - p - 0x5 != addr) continue; // 不是call addr
        return p;
    }
    return 0;
}

int main()
{
    // 要查找代码的起始地址
    char *base = (char*)GetModuleHandle(NULL) + 0x10000;
    // 搜索代码的总长度
    int codeSize = 0x10000;
    // 查找call的特征sig
    char codeSig[] = { 0xB8,0x01,0x00,0x00,0x00,0xB8,0x02,0x00,0x00,0x00 };

    // 查找call地址
    int call = findCallAddr(base, codeSize, codeSig, sizeof(codeSig));
    if (call == 0) {
        printf("not found call\n");
        return 1;
    }
    printf("call address: 0x%08X\n", call);

    // 查找调用call的地址
    int p = findCall(base, base + codeSize, call);
    if (p != 0) {
        printf("address: 0x%08X\n", p);

        // hook
        setReturn1(call, p);
    }

    // 由原来的返回0,改为返回1
    printf("return: 0 => %d\n", dosomething());

    getchar();
    return 0;
}

DEMO输出

output:

call address: 0x00413CC0
address: 0x00418125
return: 0 => 1

@2019-04-05 11:14:53 +0800


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
免费 2
打赏
分享
最新回复 (1)
雪    币: 1186
活跃值: (1192)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
cmputer 2019-4-5 19:14
2
0
支持
最后于 2019-4-5 19:18 被cmputer编辑 ,原因:
游客
登录 | 注册 方可回帖
返回