WIN32 Inline HOOK

Inline hook 和CE里面的代码注入很像(应该是一个东西),这个技术的原理是,修改汇编指令,让某个指令跳转到我们定义的函数,然后在函数内完成被替换的指令,然后再跳转回去。在我们定义的函数内,我们可以实时获取寄存器的值,从而可以完成对函数行为的监视和修改。

在我们定义的函数内,需要注意,寄存器的值和堆栈状态,执行前后必须完全一致,否则程序会出错。为了实现这个目的,我们需要用到裸函数。

下面是代码,要注意代码中使用的地址仅在我的机器上有效,如果你要编译这个程序,第一次运行会失败,请根据生成的汇编代码,找到Plus的地址,然后对SetInlineHook的参数1作相应修改。

另外补充一点,如果被替换的代码不含修改堆栈的指令(如push),则可以用call和ret的方式来实现跳转,这样写起来更方便些。

// InlineHook.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <STDIO.H>

int Plus(int x, int y);
void HookAddress();
void SetInlineHook(DWORD originalCodeAddr, DWORD originalSize, DWORD newCodeAddr);
void UnsetInlineHook(DWORD originalCodeAddr);

BYTE g_bOriginCode[64]; // 原始代码,卸载HOOK时用到
DWORD g_dwOriginCodeSize; // 原始代码的大小
DWORD g_ret; // HOOK函数内跳转到原函数的地址


int main(int argc, char* argv[])
{
	// 00401080 55                   push        ebp
	// 00401081 8B EC                mov         ebp,esp
	// 00401083 83 EC 40             sub         esp,40h
	// 00401080 是Plus的第一行汇编地址,CALL/JMP最少需要5字节,所以要替换这3条指令
	SetInlineHook(0x00401080,6,(DWORD)HookAddress);
	printf("%d\n",Plus(1,2));
	UnsetInlineHook(0x00401080);
	printf("%d\n",Plus(3,4));
	return 0;
}

// 一个普通的函数,将对它设置HOOK
int Plus(int x, int y)
{
	return x+y;
}

// HOOK跳转的地址
void __declspec(naked)HookAddress()
{
	// 执行被替换的代码
	__asm
	{
		push ebp
		mov ebp,esp
		sub esp,40h			
	}
	// 保存8个常用寄存器和标志寄存器
	__asm
	{
		pushad
		pushfd
	}

	// 我的代码,功能是查看寄存器状态
	// 这段代码有BUG,不能在裸函数里直接定义变量,而应该使用全局变量
	// 因为这些变量使用的是上层函数的堆栈
	// 我懒得改了
	DWORD _eax,_ecx,_edx,_ebx;
	__asm
	{
		mov _eax,eax
		mov _ecx,ecx
		mov _edx,edx
		mov _ebx,ebx
	}

	printf("eax: %x\n", _eax);
	printf("ecx: %x\n", _ecx);
	printf("edx: %x\n", _edx);
	printf("ebx: %x\n", _ebx);

	// 恢复寄存器,然后返回
	__asm
	{
		popfd
		popad
		jmp g_ret
	}	
}

// 设置HOOK的函数
void SetInlineHook(DWORD originalCodeAddr, DWORD originalSize, DWORD newCodeAddr)
{
	if (originalCodeAddr==0||originalSize<5||newCodeAddr==0)
	{
		printf("参数错误\n");
		return;
	}
	// 设置内存写权限
	DWORD dwOldProtectFlag;
	BOOL bRet = VirtualProtectEx(GetCurrentProcess(),(LPVOID)originalCodeAddr,originalSize,
		PAGE_EXECUTE_READWRITE,&dwOldProtectFlag);
	if (!bRet)
	{
		printf("修改内存属性失败\n");
		return;
	}
	// 存储原始硬编码,卸载的时候要把原始代码贴回去	
	memcpy(g_bOriginCode,(LPVOID)originalCodeAddr,originalSize);
	g_dwOriginCodeSize = originalSize;
	// 计算E8 CALL后面的4字节 = 要跳转的地址 - CALL的下一条指令的地址
	DWORD dwJmpCode = newCodeAddr - (originalCodeAddr + 5);
	// 将要替换的代码区域全部初始化为NOP
	memset((LPVOID)originalCodeAddr,0x90,originalSize);
	// HOOK
	*(PBYTE)originalCodeAddr = 0xE9; // JMP
	*PDWORD(originalCodeAddr+1) = dwJmpCode;
	// 设置返回地址
	g_ret = originalCodeAddr + originalSize;
	// 恢复内存属性
	VirtualProtectEx(GetCurrentProcess(),(LPVOID)originalCodeAddr,originalSize,dwOldProtectFlag,NULL);
}

// 卸载HOOK的函数
void UnsetInlineHook(DWORD originalCodeAddr)
{
	VirtualProtectEx(GetCurrentProcess(),(LPVOID)originalCodeAddr,g_dwOriginCodeSize,PAGE_EXECUTE_READWRITE,NULL);
	memcpy((LPVOID)originalCodeAddr,g_bOriginCode,g_dwOriginCodeSize);
}



执行结果:
在这里插入图片描述

该程序对我们定义的Plus函数设置HOOK,然后跳转到我们定义的裸函数内部,在裸函数里获取并打印了寄存器的内容,然后执行被替代的汇编指令,最后返回原处执行。

然后我们卸载HOOK之后又调用了一次Plus函数,验证卸载成功。


---------------------
作者:hambaga
来源:CSDN
原文:https://blog.csdn.net/Kwansy/article/details/108002252
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

猜你喜欢

转载自blog.csdn.net/weixin_41875267/article/details/108467129