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博客文章一键转载插件