InlineHook的原理与做法

InlineHook翻译为内联钩子 内联也就是我们的内联汇编 钩子就是修改目标的执行流程或代码

cpp 复制代码
#include<iostream>
using namespace std;
#include<Windows.h>

DWORD OldPro = 0;      //老的保护权限
char OldCode[9] = { 0 };  //hook前的汇编代码
DWORD RetData = 0;     //内联汇编结束需要返回的地址

DWORD RetAddr = 0;     //原本被hook函数的返回地址
DWORD Num1 = 0;        //传入的参数1
DWORD Num2 = 0;        //传入的参数2
int Add(int a, int b)
{
	return a + b;
}

_declspec(naked) void HookFun()
{
	__asm     //第一步备份原本的寄存器和Eflag
	{
		PUSHAD
		PUSHFD
	}
	__asm
	{
		MOV EAX, [ESP+0x24]     //原本是esp保存返回地址但是由于前面写了备份寄存器和eflag共0x24个字节
		MOV RetAddr,EAX

		MOV EAX, [ESP+0x28]
		MOV Num1, EAX

		MOV EAX, [ESP+0x2C]
		MOV Num2, EAX
	}
	cout << RetAddr << Num1 << Num2;

	__asm
	{
		POPFD
		POPAD
	}

	__asm   //提高栈顶
	{
		push        ebp
		mov         ebp, esp
		sub         esp, 0xC0
	}

	__asm
	{
		jmp RetData
	}


}

VOID SetInlineHook(DWORD OldAddr, DWORD DeAddr)
{
	//第一步设置内存属性记得恢复属性
	BOOL bRet=VirtualProtect((LPVOID)OldAddr, 9, PAGE_EXECUTE_READWRITE, &OldPro);
	if (!bRet)
	{
		return;
	}
	//第二步保存原本的指令
	memcpy(OldCode, (LPVOID)OldAddr, 9);
	//第三步 设置NOP
	memset((LPVOID)OldAddr, 0x90, 9);
	//第三步 修改汇编
	DWORD JumpAddr = DeAddr - OldAddr - 5;  //计算跳转地址
	char JumpCode[5] = { 0 };
	JumpCode[0] = 0xE9;                    //0x90对应jmp
	*(PCHAR)(JumpCode + 1) = JumpAddr;     //将地址写入后四个位置
	memcpy((LPVOID)OldAddr, JumpCode, 5);
	//恢复属性
	RetData = OldAddr + 5;       //记录返回地址
	VirtualProtect((LPVOID)OldAddr, 9, PAGE_EXECUTE_READ, &OldPro);
}


VOID UnsetInlineHook(DWORD HookAddr)
{
	//第一步修改属性
	DWORD OldProtection = 0;
	BOOL bVpro=VirtualProtect((LPVOID)HookAddr, 9, PAGE_EXECUTE_READWRITE, &OldProtection);
	if (!bVpro)
	{
		return;
	}
	//写入之前保存的代码
	memcpy((LPVOID)HookAddr, OldCode, 9);
	//恢复属性
	VirtualProtect((LPVOID)HookAddr, 9, OldProtection,NULL);
}



int main()
{
	SetInlineHook((DWORD)Add,(DWORD)HookFun);
	Add(1, 2);
	UnsetInlineHook((DWORD)Add);
}

我们对简单的Add函数进行hook

首先写hook函数

cpp 复制代码
VOID SetInlineHook(DWORD OldAddr, DWORD DeAddr)
{
	//第一步设置内存属性记得恢复属性
	BOOL bRet=VirtualProtect((LPVOID)OldAddr, 9, PAGE_EXECUTE_READWRITE, &OldPro);
	if (!bRet)
	{
		return;
	}
	//第二步保存原本的指令
	memcpy(OldCode, (LPVOID)OldAddr, 9);
	//第三步 设置NOP
	memset((LPVOID)OldAddr, 0x90, 9);
	//第三步 修改汇编
	DWORD JumpAddr = DeAddr - OldAddr - 5;  //计算跳转地址
	char JumpCode[5] = { 0 };
	JumpCode[0] = 0xE9;                    //0x90对应jmp
	*(PCHAR)(JumpCode + 1) = JumpAddr;     //将地址写入后四个位置
	memcpy((LPVOID)OldAddr, JumpCode, 5);
	//恢复属性
	RetData = OldAddr + 5;       //记录返回地址
	VirtualProtect((LPVOID)OldAddr, 9, PAGE_EXECUTE_READ, &OldPro);
}

第一步对要进行hook的函数进行修改内存权限

cpp 复制代码
BOOL bRet=VirtualProtect((LPVOID)OldAddr, 9, PAGE_EXECUTE_READWRITE, &OldPro);
	if (!bRet)
	{
		return;
	}

第二步保存hook前目标函数的指令

cpp 复制代码
memcpy(OldCode, (LPVOID)OldAddr, 9);

第三步将函数开头三个指令全部设置为NOP //便于jmp NOP的硬编码为0x90

cpp 复制代码
memset((LPVOID)OldAddr, 0x90, 9);

第四步开始修改汇编 将刚刚nop掉的改为jmp 并记录返回地址

注意跳转地址的计算方式是 跳转到目标地址减去要进行跳转函数的地址再减5(减5的原因是jmp指令长度为5)

0xE9对应的是jmp指令这两步将JumpCode填充完毕然后写入到被hook函数的开头即可

cpp 复制代码
JumpCode[0] = 0xE9;                    //0xE9对应jmp
	*(PCHAR)(JumpCode + 1) = JumpAddr;     //将地址写入后四个位置
	memcpy((LPVOID)OldAddr, JumpCode, 5);
cpp 复制代码
DWORD JumpAddr = DeAddr - OldAddr - 5;  //计算跳转地址
	char JumpCode[5] = { 0 };
	JumpCode[0] = 0xE9;                    //0xE9对应jmp
	*(PCHAR)(JumpCode + 1) = JumpAddr;     //将地址写入后四个位置
	memcpy((LPVOID)OldAddr, JumpCode, 5);

最后记录返回地址(加5是因为当前地址的指令长度为5)并恢复属性

cpp 复制代码
RetData = OldAddr + 5;       //记录返回地址
VirtualProtect((LPVOID)OldAddr, 9, PAGE_EXECUTE_READ, &OldPro);

到这里就完成了hook的准备操作 下面来看hook的过程

cpp 复制代码
_declspec(naked) void HookFun()
{
	__asm     //第一步备份原本的寄存器和Eflag
	{
		PUSHAD
		PUSHFD
	}
	__asm
	{
		MOV EAX, [ESP+0x24]     //原本是esp保存返回地址但是由于前面写了备份寄存器和eflag共0x24个字节
		MOV RetAddr,EAX

		MOV EAX, [ESP+0x28]
		MOV Num1, EAX

		MOV EAX, [ESP+0x2C]
		MOV Num2, EAX
	}
	cout << RetAddr << Num1 << Num2;

	__asm   //到这里我们的操作代码结束
	{
		POPFD
		POPAD
	}

	__asm   //提高栈顶 恢复原本的指令
	{
		push        ebp
		mov         ebp, esp
		sub         esp, 0xC0
	}

	__asm
	{
		jmp RetData
	}


}

hook的代码是裸函数进行内联汇编(Inline)

第一步先保存所有的寄存器和Eflag 然后这里我们拿到我们想要的数据 比如函数的参数和函数返回地址 然后将保存的寄存器和eflag出栈 然后模拟提升栈顶的操作因为原本函数的这个操作被我们nop掉了 然后使用jmp跳转到我们记录的地址即可

最后取消掉我们的钩子避免出问题

cpp 复制代码
VOID UnsetInlineHook(DWORD HookAddr)
{
	//第一步修改属性
	DWORD OldProtection = 0;
	BOOL bVpro=VirtualProtect((LPVOID)HookAddr, 9, PAGE_EXECUTE_READWRITE, &OldProtection);
	if (!bVpro)
	{
		return;
	}
	//写入之前保存的代码
	memcpy((LPVOID)HookAddr, OldCode, 9);
	//恢复属性
	VirtualProtect((LPVOID)HookAddr, 9, OldProtection,NULL);
}

重点就是将原本保存的指令重新写入到函数的起始位置

cpp 复制代码
memcpy((LPVOID)HookAddr, OldCode, 9);

简单来说InlineHook流程 就是使用jmp跳转到我们自己写的代码区域执行我们自己写的代码

注意的是每个步骤需要按照严格的顺序进行书写不然会出现很多问题

相关推荐
XINVRY-FPGA1 小时前
XCZU4EV-1FBVB900E Xilinx FPGA AMD Zynq UltraScale+ MPSoC EV(Embedded Vision)
arm开发·嵌入式硬件·计算机视觉·fpga开发·硬件架构·硬件工程·fpga
猫猫的小茶馆1 小时前
【STM32】FreeRTOS 任务的删除(三)
java·linux·stm32·单片机·嵌入式硬件·mcu·51单片机
学不动CV了1 小时前
单片机ADC采集机理层面详细分析(二)
c语言·arm开发·stm32·单片机·嵌入式硬件·开源·51单片机
学不动CV了2 小时前
51核和ARM核单片机OTA实战解析(二)
c语言·arm开发·stm32·单片机·嵌入式硬件·51单片机
Yuroo zhou2 小时前
IMU的精度对无人机姿态控制意味着什么?
单片机·嵌入式硬件·算法·无人机·嵌入式实时数据库
嵌入式小白牙4 小时前
ARM-I2C硬实现
arm开发·单片机·嵌入式硬件
knight_20247 小时前
嵌入式学习日志————对射式红外传感器计次
stm32·单片机·嵌入式硬件·学习
深圳安凯星单片机开发方案公司7 小时前
用单片机怎么控制转速
单片机·51单片机
忆和熙7 小时前
【模电笔记】—— 波形发生电路(波形振荡器)
嵌入式硬件·模电笔记·波形发生电路
文火冰糖的硅基工坊7 小时前
[硬件电路-97]:模拟器件 - 如何通过外部的闭环负反馈,让运算放大器从“暴脾气”、“愣头青”、情绪容易失控者变成“沉着”、“冷静”的精密调控者的?
嵌入式硬件·架构·电路·跨学科融合