前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >红队 | InlineHook技术实现

红队 | InlineHook技术实现

作者头像
HACK学习
发布2021-05-14 15:25:25
6010
发布2021-05-14 15:25:25
举报
文章被收录于专栏:HACK学习

前言

IATHOOK局限性较大,当我们想HOOK一个普通函数,并不是API,或者IAT表里并没有这个API函数(有可能他自己LoadLibrary,自己加载的),那我们根本就从导入表中找不到这个函数,自然也就在IAT表中无法找到,InlineHook算是对IATHOOK一个升级版吧

大体思路

用JMP改变函数入口,JMP到我们自己的函数,然后又JMP回去执行刚刚的没执行完的函数。

过程无论怎么变,一定要让堆栈平衡和保留原来的寄存器,这是Hook是否成功的关键.

具体实现

创建钩子

代码语言:javascript
复制
 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,这里要算一下.

卸载钩子

代码语言:javascript
复制
 DWORD UnInlineHook(DWORD num)
 {
     if (!dw_ifHOOK)
     {
         printf("还没hook呢");
         return 0;
     }
     memcpy((LPVOID)GlobleHookAddr, Buffer, num);

     Buffer = NULL;
     dw_ifHOOK = 0;
     return 1;
 }

这里把我们在创建钩子的时候定义的全局变量Buffer的值重新写回来就行了

钩子函数

代码语言:javascript
复制
 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;
     }
 }

上来先把寄存器的值保存下来,后面我们要还原现场.这里我先创建了一个结构体用于接收寄存器的值,等会方便打印

代码语言:javascript
复制
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的函数

代码语言:javascript
复制
DWORD Test(int x, int y, int z)
{
return x + y + z;
}

测试

代码语言:javascript
复制
 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

测试结果

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

完整代码

代码语言:javascript
复制
// 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, 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);   //先全部nop
//计算跳到我们自己函数的硬编码,这里用E8方便平衡堆栈,E8后面的值 = 要跳转的地址 - E8的地址 - 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;
}


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;
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 HACK学习呀 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档