hook原理

2024/7/5 15:47:10

常说的hook一般指的是inline hook。windows系统的分几个环,inline hook是用的最广泛的在r3中。

目标:
参数:在干嘛
call正常流程代码

我们的代码:
拦截内容
修改内容
还原进来时的代码
原封不动的返回

可以在od中进行测试,比如定位到我们需要拦截指令,对需要重写的那几行指令选中,右键-标签-用nop填充。在空出来的字节中,添加指令jmp到自定义的函数。自定义的函数在jmp回拦截指令后续需要执行的指令。

代码实现

...
8d4d 0c | lea ecx,dword ptr ss:[ebp+0xc]
e8 xxx	|  call xxx  //hook这里
xxxx    | xxxx
....

相当于我们要将hook对应的机器码替换调。这里的e8对应的call, e8后面的xxx=要跳转的地址-hook的地址-(这行机器码对应的字节),例如这个的xxx=d34d2900, 那么字节就应该是5=xxx的字节(4)+e8的字节(1)。

//核心代码,hookA要hook的地址偏移,funAdd自定义函数的地址
void startHook(DWORD hookA, LPVOID funAdd){
	DWORD winAdd = ...;//模块地址
	DWORD hookAdd = winAdd+hookA;//通过偏移计算出实际地址
	//组装指令
	BYTE jmpCode[5]={0};
	jmpCode[0]=0xe9;//e9对应jmp
	*(DWORD*)&jmpCode[1]=(DWORD)funAdd-hookAdd-5;
	//获取当前的进程句柄,因为使用inline hook,那么这里就是运行程序的句柄
	HANDLE hWHND = OpenProcess(...);
	//备份hook地址的数据,就是要将替换前的指令保存
	ReadProcessMemory(...,backCode,...);
	//写入组好的指令
	WriteProcessMemory(...,jmpCode,...);
}

DWORD pEax=0;
...
//需要将该函数声明为裸函数,不然调用这个函数时会多许多其他操作
void __declspec(naked) test(){
	//备份寄存器
	__asm{
		mov pEax, eax
		...
	}
	//自定义的操作,注意这里的操作是不能声明变量的
	//恢复寄存器
	__asm{
		mov eax,pEax
		...
		jmp 返回地址(e8 xxx后续需要执行的指令地址)
	}
}
调用
startHook(xxx, test);