前言

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

大体思路

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

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

具体实现

创建钩子

 1 DWORD SetInlineHook(LPBYTE HookAddr,LPVOID HookProc,DWORD num)  //要挂钩子的地址,钩子函数(如何处理),要改多少个的硬编码
2 {
3 if (HookAddr == NULL || HookProc == NULL)
4 {
5 printf("地址填错了");
6 return 0;
7 }
8 if (num < 5)
9 {
10 printf("HOOK不了");
11 return 0;
12 }
13 //改变修改地址为可写属性
14 DWORD OldProtect = 0;
15 DWORD bret = VirtualProtect((LPBYTE)HookAddr,num, PAGE_EXECUTE_READWRITE,&OldProtect);
16 if (bret == 0)
17 {
18 printf("修改可写属性失败");
19 return 0;
20 }
21 Buffer = malloc(num * sizeof(char));
22
23 memcpy(Buffer, HookAddr, num); //存起来把原来的值
24
25 memset(HookAddr,0x90,num); //先全部nop
26 //计算跳到我们自己函数的硬编码,E9后面的值 = 要跳转的地址 - E9的地址 - 5
27 DWORD JmpAddr = (DWORD)HookProc - (DWORD)HookAddr - 5;
28
29 *(LPBYTE)HookAddr = 0xE9;
30 *(PDWORD)((LPBYTE)HookAddr + 1) = JmpAddr;
31
32 GlobleHookAddr = (DWORD)HookAddr;
33 RetGlobleHookAddr = (DWORD)HookAddr + num; //等会的返回地址
34 dw_ifHOOK = 1;
35 }

这里别忘了改属性,然后就是有个公式,JMP后面的值并不是我们真正想要去的地址

E9后面的值 = 要跳转的地址 - E9的地址 - 5,这里要算一下.

卸载钩子

 1 DWORD UnInlineHook(DWORD num)
2 {
3 if (!dw_ifHOOK)
4 {
5 printf("还没hook呢");
6 return 0;
7 }
8 memcpy((LPVOID)GlobleHookAddr, Buffer, num);
9
10 Buffer = NULL;
11 dw_ifHOOK = 0;
12 return 1;
13 }

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

钩子函数

 1 extern "C" _declspec(naked) void HookProc()   //裸函数,编译器不帮我们平衡堆栈
2 {
3 //先把现场保留了
4 _asm
5 {
6 pushad //保留寄存器
7 pushfd //保留标志寄存器
8 }
9 _asm
10 {
11 mov reg.EAX, eax
12 mov reg.EBX, ebx
13 mov reg.ECX, ecx
14 mov reg.EDX, edx
15 mov reg.EDI, edi
16 mov reg.ESI, esi
17 mov reg.ESP, esp
18 mov reg.EBP, ebp
19 }
20 _asm
21 {
22 mov eax, DWORD PTR ss : [esp + 0x28]
23 mov x, eax
24 mov eax, DWORD PTR ss : [esp + 0x2c]
25 mov y, eax
26 mov eax, DWORD PTR ss : [esp + 0x30]
27 mov z, eax
28
29 }
30 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);
31
32 printf("参数:%d %d %d\n", x, y, z);
33
34 _asm
35 {
36 popfd
37 popad
38 }
39
40 _asm
41 {
42 push ebp
43 mov ebp, esp
44 sub esp, 0C0h
45 }
46
47 _asm
48 {
49 jmp RetGlobleHookAddr;
50 }
51 }

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

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;
}

测试

 1 DWORD TestInlineHook()
2 {
3 PAddr = (BYTE*)Test + 1;
4 PAddr += *(DWORD*)PAddr+ 4;
5
6 SetInlineHook((LPBYTE)Test, HookProc,9);
7
8 Test(1, 2, 3);
9
10 UnInlineHook(9);
11
12 Test(1, 2, 3);
13 return 0;
14 }

这里有一个小的细节,我们用函数名Test传参的话,传进去的这个参数的值并不是真正的函数地址,而是一个间接地址,间接地址里面的值是JMP到真正的函数地址。也就是我们平时看反汇编时,如果我们F11一个CALL我们会发现他先到一个地址,然后再F11,才会到真正的函数地址。用函数名传参的话得到的并不是真正的函数地址,但其实也是个间接地址嘛,JMP + 真正函数地址经过运算后的地址,我们还是用“E9后面的值 = 要跳转的地址 - E9的地址 - 5”这个公式算一下。

这里我找到一篇文章说明为什么有这种机制:https://blog.csdn.net/x_iya/article/details/13161937

测试结果

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

完整代码

// 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;
}

InlineHook的更多相关文章

  1. C# inline-hook / api-hook

    我查阅了一下相关C#方面的资料,却没有发现有提供过关于api-hook方面的资 料包括应用库由此本人编写一套inline-hook的库用于支持x64.x86上的基于在 clr的公共语言,如: c#.c ...

  2. Inlinehook PspCreateProcess

    InineHook通过修改函数指令实现,此次以内核层的PspCreateProcess()为例. 本来是想写NtCreateProcess()的Inlinehook,但是想到PCHunter对于SSD ...

  3. 检测API函数的InlineHook

    BOOL GetProcHookStatus(LPCSTR lpModuleName, LPCSTR lpProcName) { HMODULE hModule = GetModuleHandleA( ...

  4. Win64 驱动内核编程-23.Ring0 InLineHook 和UnHook

    Ring0 InLineHook 和UnHook 如果是要在R0里hook,作者的建议是InLine HOOK,毕竟SSDT HOOK 和 SHADOW SSDT HOOK比较麻烦,不好修改.目前R3 ...

  5. x64 InlineHook 黑魔法

    目录 x64 InlineHook 黑魔法 为什么不能用X86 的HOOK方式? 原理:jmp + rip 进行寻址6字节方式跳转 手动InlineHook 临时地址x(找一块空内存) 计算偏移 源地 ...

  6. 驱动开发:内核层InlineHook挂钩函数

    在上一章<驱动开发:内核LDE64引擎计算汇编长度>中,LyShark教大家如何通过LDE64引擎实现计算反汇编指令长度,本章将在此基础之上实现内联函数挂钩,内核中的InlineHook函 ...

  7. Win32API起始处的mov edi, edi与用户空间InlineHook

    在x86平台上,无论是在调试器中跟到系统DLL中时,还是反汇编某个系统DLL时,经常会发现很多API的第一条汇编指令都是mov edi, edi.根据经验来讲,C函数的汇编形式,应该是首先push e ...

  8. 64位系统InlineHook

    APIHook64Class.h #ifndef APIHOOK64CLASS_H_ #define APIHOOK64CLASS_H_ #include <Windows.h> clas ...

  9. 64位下的InlineHook

    目录 x64下手工HOOK的方法 一丶HOOK的几种方法之远跳 1. 远跳 不影响寄存器 + 15字节方法 2.远跳 影响寄存器 + 12字节方法 3.影响寄存器,恢复寄存器 进行跳转. 4. 常用 ...

随机推荐

  1. SwiftUI render WKWebView

    SwiftUI render WKWebView // // ContentView.swift // webview-app // // Created by 夏凌晨 on 2020/10/27. ...

  2. js types & primitive & object

    js types & primitive & object js 数据类型 typeof null // "object" typeof undefined // ...

  3. D3 tree map

    D3 tree map D3 矩形树图 https://www.zhihu.com/question/55529379 https://zhuanlan.zhihu.com/p/57873460 ht ...

  4. NGK是如何运用IPFS分布式存储的?

    整个夏季,除了天气的火热,还有的火热莫过于IPFS挖矿这个领域了.IPFS的概念火热到,你可以看到到处都在卖IPFS矿机.那么,是什么原因导致IPFS这么火呢?在这之前,我们先了解一下什么是IPFS技 ...

  5. ffmpeg中的日志控制、保存日志到指定文件、处理进度查询

    ffmpeg默认就会输出一些日志,包含编译的工具(gcc版本).可用的组件(--enable-xxxx).组件版本号码,如下图 这些信息是默认输出的,每次执行合作都会打印在控制台中,如果不想看这些信息 ...

  6. Docker使用指南

    上文简单介绍了docker,这边记录一下docker的使用. 一.Docker启停 1.启动docker systemctl start docker 2.关闭docker systemctl sto ...

  7. .net使用CSRedis操作Redis缓存的简单笔记(新手教程)

    0.介绍 .NET Core or .NET Framework 4.0+ client for Redis and Redis Sentinel (2.8) and Cluster. Include ...

  8. [Python学习笔记]文件的读取写入

    文件与文件路径 路径合成 os.path.join() 在Windows上,路径中以倒斜杠作为文件夹之间的分隔符,Linux或OS X中则是正斜杠.如果想要程序正确运行于所有操作系统上,就必须要处理这 ...

  9. 敏捷史话(七):从程序员、作家到摇滚乐手——Andy Hunt的多面人生

    与其说 Andy Hunt 是敏捷宣言的创始人,不如说他是一名专业作家来得更为合适.他的<实用程序员><程序员修炼之道:从小工到专家><编程 Ruby:实用程序员指南&g ...

  10. Js和JQuery基础

    1.JavaScript的组成 CMAScript (核心):规定了JS的语法和基本对象 DOM 文档对象模型:处理网页内容的方法和接口 BOM 浏览器对象模型:与浏览器交互的方法和接口 2.Java ...