红队 | InlineHook技术实现

  • A+
所属分类:安全文章

前言


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 < 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);   //先全部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 %dn", 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


测试结果

红队 | InlineHook技术实现

 

 我调用了两次函数,但只有一次输出说明卸载也成功了


完整代码

// InlineHook.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。//

#include <iostream>#include <windows.h>

//保留原来的硬编码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;//typedef DWORD(*MyTest)(int x, int y, int z);//MyTest pAddr =Test;extern "C" _declspec(naked) void HookProc() //裸函数,编译器不帮我们平衡堆栈{//先把现场保留了_asm{pushad //保留寄存器pushfd //保留标志寄存器}_asm{mov reg.EAX, eaxmov reg.EBX, ebxmov reg.ECX, ecxmov reg.EDX, edxmov reg.EDI, edimov reg.ESI, esimov reg.ESP, espmov reg.EBP, ebp}_asm{mov eax, DWORD PTR ss : [esp + 0x28]mov x, eaxmov eax, DWORD PTR ss : [esp + 0x2c]mov y, eaxmov 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 %dn", x, y, z);

_asm{popfdpopad}

_asm{push ebpmov ebp, espsub 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); //先全部nop//计算跳到我们自己函数的硬编码,这里用E8方便平衡堆栈,E8后面的值 = 要跳转的地址 - E8的地址 - 5DWORD 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();//Test(1, 2, 3);return 1;}

红队 | InlineHook技术实现


推荐阅读:


远程线程注入Dll,突破Session 0


红队 | IAT Hook技术实现


本月报名可以参加抽奖送暗夜精灵6Pro笔记本电脑的优惠活动


红队 | InlineHook技术实现


点赞,转发,在看


原创投稿作者:Buffer

红队 | InlineHook技术实现


本文始发于微信公众号(HACK学习呀):红队 | InlineHook技术实现

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: