作者:oHuangKeo
时间:2019-04-05
有些时候,我们需要hook一些函数,达到我们预想的一些效果。
普通的windows api就不说了,hook的资料还是很多的。
但是普通的call和function相关资料还是很少的。
最近反编译一个项目是,需要这样的功能,这里整理了一下资料,做了个小DEMO演示一下。
某个call调用一个function,原本返回的0,这里修改返回1
代码中有些细节有更好的解决方案,请忽略,不同的项目中有不同的适合解决方案,这里只做一个演示。
@2019-04-05 11:14:53 +0800
// hook模版
// ... 这里可以增加一些预处理代码,例如修改参数的数据
call 0x00000000 // call function
// ... 这里可以增加一些后置代码,例如修改某些数据
mov eax, 1 // 将返回值改为1
jmp 0x00000000 // 返回代码写一个code
call 0x00413CC0
mov eax, 1
jmp 0x0041812A // 0x00418125+0x5
VirtualProtect // PAGE_EXECUTE_READWRITE
*(BYTE*)fromAddr = 0xE9; // JMP
*(int*)(fromAddr + 1) = fix3; // JMP x
VirtualProtect // 恢复
#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;
}
output:
call address: 0x00413CC0
address: 0x00418125
return: 0 => 1
作者:oHuangKeo
时间:2019-04-05
@2019-04-05 11:14:53 +0800
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!