前言
IATHOOK局限性较大,当我们想HOOK一个普通函数,并不是API,或者IAT表里并没有这个API函数(有可能他自己LoadLibrary,自己加载的),那我们根本就从导入表中找不到这个函数,自然也就在IAT表中无法找到,InlineHook算是对IATHOOK一个升级版吧
大体思路
用JMP改变函数入口,JMP到我们自己的函数,然后又JMP回去执行刚刚的没执行完的函数。
过程无论怎么变,一定要让堆栈平衡和保留原来的寄存器,这是Hook是否成功的关键.
具体实现
创建钩子
DWORD SetInlineHook(LPBYTE HookAddr,LPVOID HookProc,DWORD num) //要挂钩子的地址,钩子函数(如何处理),要改多少个的硬编码
{
if (HookAddr == NULL || HookProc == NULL)
{
printf("地址填错了");
return 0;
}
if (num
{
printf("HOOK不了");
return 0;
}
//改变修改地址为可写属性
DWORD OldProtect = 0;
DWORD bret = VirtualProtect((LPBYTE)HookAddr,num, PAGE_EXECUTE_READWRITE,&OldProtect);
if (bret == 0)
{
printf("修改可写属性失败");
return 0;
}
Buffer = malloc(num * sizeof(char));
memcpy(Buffer, HookAddr, num); //存起来把原来的值
memset(HookAddr,0x90,num); //先全部nop
//计算跳到我们自己函数的硬编码,E9后面的值 = 要跳转的地址 - E9的地址 - 5
DWORD JmpAddr = (DWORD)HookProc - (DWORD)HookAddr - 5;
*(LPBYTE)HookAddr = 0xE9;
*(PDWORD)((LPBYTE)HookAddr + 1) = JmpAddr;
GlobleHookAddr = (DWORD)HookAddr;
RetGlobleHookAddr = (DWORD)HookAddr + num; //等会的返回地址
dw_ifHOOK = 1;
}
这里别忘了改属性,然后就是有个公式,JMP后面的值并不是我们真正想要去的地址
E9后面的值 = 要跳转的地址 - E9的地址 - 5,这里要算一下.
卸载钩子
DWORD UnInlineHook(DWORD num)
{
if (!dw_ifHOOK)
{
printf("还没hook呢");
return 0;
}
memcpy((LPVOID)GlobleHookAddr, Buffer, num);
Buffer = NULL;
dw_ifHOOK = 0;
return 1;
}
这里把我们在创建钩子的时候定义的全局变量Buffer的值重新写回来就行了
钩子函数
extern "C" _declspec(naked) void HookProc() //裸函数,编译器不帮我们平衡堆栈
{
//先把现场保留了
_asm
{
pushad //保留寄存器
pushfd //保留标志寄存器
}
_asm
{
mov reg.EAX, eax
mov reg.EBX, ebx
mov reg.ECX, ecx
mov reg.EDX, edx
mov reg.EDI, edi
mov reg.ESI, esi
mov reg.ESP, esp
mov reg.EBP, ebp
}
_asm
{
mov eax, DWORD PTR ss : [esp + 0x28]
mov x, eax
mov eax, DWORD PTR ss : [esp + 0x2c]
mov y, eax
mov eax, DWORD PTR ss : [esp + 0x30]
mov z, eax
}
printf("EAX:%x EBX:%x ECX:%x EDX:%x EDI:%x ESI:%x ESP:%x EBP:%x \n", reg.EAX, reg.EBX, reg.ECX, reg.EDX, reg.EDI, reg.ESI, reg.ESP, reg.EBP);
printf("参数:%d %d %d\n", x, y, z);
_asm
{
popfd
popad
}
_asm
{
push ebp
mov ebp, esp
sub esp, 0C0h
}
_asm
{
jmp RetGlobleHookAddr;
}
}
上来先把寄存器的值保存下来,后面我们要还原现场.这里我先创建了一个结构体用于接收寄存器的值,等会方便打印
typedef struct _regeist
{
DWORD EAX;
DWORD EBX;
DWORD ECX;
DWORD EDX;
DWORD EBP;
DWORD ESP;
DWORD ESI;
DWORD EDI;
}regeist;
regeist reg = { 0 };
然后第22~28行的值由于pushad和pushfd了,偏移不能是+4 +8 +c了,这里要算一下,40~45行,我们将原来没执行的代码执行一下,不然堆栈出问题了,最后跳转到原函数的下一个位置,继续执行原函数
被HOOK的函数
DWORD Test(int x, int y, int z)
{
return x + y + z;
}
测试
DWORD TestInlineHook()
{
PAddr = (BYTE*)Test + 1;
PAddr += *(DWORD*)PAddr+ 4;
SetInlineHook((LPBYTE)Test, HookProc,9);
Test(1, 2, 3);
UnInlineHook(9);
Test(1, 2, 3);
return 0;
}
这里有一个小的细节,我们用函数名Test传参的话,传进去的这个参数的值并不是真正的函数地址,而是一个间接地址,间接地址里面的值是JMP到真正的函数地址。也就是我们平时看反汇编时,如果我们F11一个CALL我们会发现他先到一个地址,然后再F11,才会到真正的函数地址。用函数名传参的话得到的并不是真正的函数地址,但其实也是个间接地址嘛,JMP + 真正函数地址经过运算后的地址,我们还是用“E9后面的值 = 要跳转的地址 - E9的地址 - 5”这个公式算一下。
这里我找到一篇文章说明为什么有这种机制:
https://blog.csdn.net/x_iya/article/details/13161937
测试结果
我调用了两次函数,但只有一次输出说明卸载也成功了
完整代码
#include
#include
LPVOID Buffer;
typedef struct _regeist
{
DWORD EAX;
DWORD EBX;
DWORD ECX;
DWORD EDX;
DWORD EBP;
DWORD ESP;
DWORD ESI;
DWORD EDI;
}regeist;
regeist reg = { 0 };
DWORD x;
DWORD y;
DWORD z;
DWORD GlobleHookAddr;
DWORD RetGlobleHookAddr;
DWORD dw_ifHOOK = 0;
DWORD Test(int x, int y, int z);
PBYTE PAddr;
extern "C" _declspec(naked) void HookProc()
{
_asm
{
pushad
pushfd
}
_asm
{
mov reg.EAX, eax
mov reg.EBX, ebx
mov reg.ECX, ecx
mov reg.EDX, edx
mov reg.EDI, edi
mov reg.ESI, esi
mov reg.ESP, esp
mov reg.EBP, ebp
}
_asm
{
mov eax, DWORD PTR ss : [esp + 0x28]
mov x, eax
mov eax, DWORD PTR ss : [esp + 0x2c]
mov y, eax
mov eax, DWORD PTR ss : [esp + 0x30]
mov z, eax
}
printf("EAX:%x EBX:%x ECX:%x EDX:%x EDI:%x ESI:%x ESP:%x EBP:%x \n", reg.EAX, reg.EBX, reg.ECX, reg.EDX, reg.EDI, reg.ESI, reg.ESP, reg.EBP);
printf("参数:%d %d %d\n", x, y, z);
_asm
{
popfd
popad
}
_asm
{
push ebp
mov ebp, esp
sub esp, 0C0h
}
_asm
{
jmp RetGlobleHookAddr;
}
}
DWORD SetInlineHook(LPBYTE HookAddr,LPVOID HookProc,DWORD num)
{
if (HookAddr == NULL || HookProc == NULL)
{
printf("地址填错了");
return 0;
}
if (num < 5)
{
printf("HOOK不了");
return 0;
}
DWORD OldProtect = 0;
DWORD bret = VirtualProtect((LPBYTE)HookAddr,num, PAGE_EXECUTE_READWRITE,&OldProtect);
if (bret == 0)
{
printf("修改可写属性失败");
return 0;
}
Buffer = malloc(num * sizeof(char));
memcpy(Buffer, HookAddr, num);
memset(HookAddr,0x90,num);
DWORD JmpAddr = (DWORD)HookProc - (DWORD)HookAddr - 5;
*(LPBYTE)HookAddr = 0xE9;
*(PDWORD)((LPBYTE)HookAddr + 1) = JmpAddr;
GlobleHookAddr = (DWORD)HookAddr;
RetGlobleHookAddr = (DWORD)HookAddr + num;
dw_ifHOOK = 1;
}
DWORD UnInlineHook(DWORD num)
{
if (!dw_ifHOOK)
{
printf("还没hook呢");
return 0;
}
memcpy((LPVOID)GlobleHookAddr, Buffer, num);
Buffer = NULL;
dw_ifHOOK = 0;
return 1;
}
DWORD Test(int x, int y, int z)
{
return x + y + z;
}
DWORD TestInlineHook()
{
PAddr = (BYTE*)Test + 1;
PAddr += *(DWORD*)PAddr + 4;
SetInlineHook((LPBYTE)PAddr, HookProc,9);
Test(1, 2, 3);
UnInlineHook(9);
Test(1, 2, 3);
return 0;
}
int main()
{
TestInlineHook();
return 1;
}
推荐阅读:
远程线程注入Dll,突破Session 0
红队 | IAT Hook技术实现
本月报名可以参加抽奖送暗夜精灵6Pro笔记本电脑的优惠活动
点赞,转发,在看
原创投稿作者:Buffer