首页 > 其他分享 >MinHook 项目学习笔记

MinHook 项目学习笔记

时间:2024-03-02 16:55:55浏览次数:23  
标签:字节 jmp 笔记 学习 地址 指令 MinHook 跳转 原函数

​​ MinHook是一个常用的 InlineHook 库,学习完整个项目后对 Hook 技术会有更深的认识。

项目路径TsudaKageyu/minhook: The Minimalistic x86/x64 API Hooking Library for Windows (github.com)

1. 基本原理

1)获取原函数地址

2)将原函数的前五个字节改为跳转指令跳转到 FakeFunc

3)FakeFunc 执行完后跳转到内存槽,执行原函数指令后跳回原函数的后续指令

x86特点:

​ x86,2GB 的用户空间,jmp call 指令,经过一次跳转就可以到达目标地址。

x64特点:

​ x64,128TB 的用户空间,jmp call 指令,经过一次跳转往往无法到达目标地址。

​ 由于内存空间较大,利用 E9 指令可能会跳不到 FakeFunc,即在原函数附近申请一片内存作为跳板,将原函数前五个字节指令改为跳转指令使其跳转到跳板的 Relay 中,Relay 处存放 FF 25 指令,使其跳转到 FakeFunc,FakeFunc 执行完后即跳到内存槽中执行原函数指令后跳回原函数后续指令

2. 比较重要的两个结构体

typedef struct _HOOK_ENTRY
{
	LPVOID TargetFunctionAddress;        
	LPVOID FakeFunctionAddress;           
	LPVOID MemorySlot;        
	UINT8  Backup[8];           //恢复Hook使用的存放原先数据

	UINT8  PatchAbove : 1;     // Uses the hot patch area.   位域:1位
	UINT8  IsEnabled  : 1;     // Enabled.

//	UINT8  queueEnable : 1;     // Queued for enabling/disabling when != isEnabled.

	UINT   Index : 4;            // Count of the instruction boundaries.???
	UINT8  OldIPs[8];           // Instruction boundaries of the target function.???
	UINT8  NewIPs[8];           // Instruction boundaries of the trampoline function ???

} HOOK_ENTRY, *PHOOK_ENTRY;     //44字节

typedef struct _TRAMPOLINE
{
	LPVOID TargetFunctionAddress;      
	LPVOID FakeFunctionAddress;        
	LPVOID MemorySlot;                   // MemorySlot 32字节

#if defined(_M_X64) || defined(__x86_64__)
	LPVOID Relay;           // [Out] Address of the relay function.   原函数 到 Fake函数的中转站 
#endif
	BOOL   PatchAbove;      // [Out] Should use the hot patch area?  //Patch  --->补丁   //0xA 0xB
	UINT   Index;           // [Out] Number of the instruction boundaries.
	UINT8  OldIPs[8];       // [Out] Instruction boundaries of the target function.      //恢复
	UINT8  NewIPs[8];       // [Out] Instruction boundaries of the trampoline function.  //Hook
} TRAMPOLINE, *PTRAMPOLINE;

3. MiniHook可Hook的函数类型

1)普通函数

​ 计算从 Memory 跳转到原函数后续指令的偏移值 offset,保存在 jmp 指令中作为跳转地址,将 jmp 指令写入 memoryslot

2)函数入口为 jmp

①E9 指令:保存原函数 jmp 指令跳转处的地址,保存在自己的 jmp 指令中作为跳转地址将jmp指令写入 memoryslot

②EB 指令:

​ a. 第一条短跳转地址在原函数前五个字节,将其写入 memoryslot,如果第二条指令还是 jmp 跳转地址超过前五个字节,此时把该地址保存在 jmp 指令中作为跳转地址,写入 memoryslot

​ b. 第一条指令是稍远的短跳转,构造 jmp 指令,计算此地址和 memoryslot 之间的偏移值,保存在 jmp 指令中作为跳转地址,写入memoryslot
可能会遇到比较越界问题:即第一步短跳转到不远处执行代码,后面又短跳转到前五个字节处

3)函数入口为call(E8)

①得到原函数call指令跳转处的地址,计算此地址和memoryslot之间偏移值,保存在call指令中作为跳转地址,写入memoryslot

②call指令会将下一条指令压入,不能直接跳出循环,还需要计算从Memory跳转到原函数后续指令的偏移值offset,保存在jmp指令中作为跳转地址,写入memoryslot

4)函数入口为jcc

①得到原函数指令跳转处的地址

②第一个跳转的地址在原函数的前五个字节里,保存跳转处的地址,存入memorySlot,继续循环

③若第二条指令还是jcc,跳转地址超过五个字节,进入else块,把MemorySlot后续改为jcc跳到原函数跳转地址处

可能会遇到比较越界问题:即第一步短跳转到不远处执行代码,后面又短跳转到前五个字节处

5)函数入口为ret

​ 直接返回

6)热补丁Hook

①如果原函数所有指令不足五个字节,将其写入memoryslot退出循环,判断能否进行短跳转

②如果只能写短跳转,使用热补丁技术,确定该函数前面的地址为可执行地址,前面的内容可改,设置热补丁标志

③改变原函数:构造jmp短跳转,使其可以从原函数跳到原函数之前五个字节处,前五个字节被改为jmp指令跳转到FakeFunc

4. MiniHook注意点

1)跳转地址偏移值计算公式

​ 计算公式:目标 = 源 + Offset + 5 Offset = 目标 - (源 + 5)

2)函数的真正地址

​ 当调用自定义的函数时,代码执行到被调用函数处,第一条指令将是一条jmp指令跳转到真正的被调用函数入口地址,即测试自定义函数第一步就是获得原函数的真正地址

​ (利用汇编,传入假函数地址,此地址加1即得真实地址)

3)64位构建内存槽

​ 确定Ring3层进程空间访问的范围,将MemoryBlock地址范围控制在原函数地址±1024MB,最后还要对MaxAddress进行微调:MaxAddress -= MEMORY_BLOCK_SIZE - 1;

4)ShellCode

​ x86:jmp-5个字节 call-5个字节 jcc-6个字节

​ x64:jmp-14个字节 call-16个字节 jcc-16个字节

CALL_ABS call = {
	0xFF, 0x15, 0x00000002, // FF15 00000002: CALL [RIP+8]
	0xEB, 0x08,             // EB 08:         JMP +10
	0x0000000000000000ULL   // Absolute destination address
};
JMP_ABS jmp = {
	0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6]
	0x0000000000000000ULL   // Absolute destination address
};
JCC_ABS jcc = {
	0x70, 0x0E,             // 7* 0E:         J** +16
	0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6]
	0x0000000000000000ULL   // Absolute destination address
};
CALL_REL call = {
	0xE8,                   // E8 xxxxxxxx: CALL +5 + xxxxxxxx  Push Eip    Jmp    Ret
	0x00000000              // Relative destination address
};
JMP_REL jmp = {
	0xE9,                   // E9 xxxxxxxx: JMP +5+xxxxxxxx                   
	0x00000000              // Relative destination address
};
JCC_REL jcc = {
	0x0F, 0x80,             // 0F8* xxxxxxxx: J** +6+xxxxxxxx
	0x00000000              // Relative destination address
};

5)FF 25绝对地址

​ x64跳转指令都是使用绝对地址,不需要计算偏移值

6)线程同步问题

① 加锁和Sleep

​ 第一次进入不会进入循环,第一次后 g_isLocked 赋值为 TRUE,后面其他线程进入后会进入循环

static VOID EnterSpinLock(VOID)
{
   SIZE_T spinCount = 0;
   while (InterlockedCompareExchange(&g_isLocked, TRUE, FALSE) != FALSE)
   {
       if (spinCount < 32)
           Sleep(0);
       else
           Sleep(1);
       spinCount++;
   }
}

​ 函数调用了原子操作函数,这整个操作过程是锁定内存的,其它处理器不会同时访问内存,从而实现多处理器环境下的线程互斥。

Sleep(0)与Sleep(1):

​ Sleep 的意思是告诉操作系统自己要休息 n 毫秒,这段时间片可以让给另一个就绪的线程。

​ n=0 时,当前线程放弃自己剩下的时间片,仍然是就绪。Sleep(0)只允许优先级相等或更高的线程使用当前CPU,其它线程等待。如果没有合适的线程,当前线程会重新使用CPU时间片

​ n=1 时,要当前线程放弃剩下的时间片,休息一下。且所有其它就绪状态的线程都有机会竞争时间片,而不用在乎优先级。

② 热补丁

​ Hook 原函数的前两个字节,将其改为 EB F9 短跳转指令,即跳转到原函数之前五个字节处,若此处内存被 0xcc 填充,将此处设置为 E9 指令跳转到 FakeFunc 中

解决了线程同步问题:避免 Hook 时其他线程执行前几个字节指令,热补丁 Hook 的为无效指令,所以即使有其他线程执行,也不会造成错误

7)解决了 Hook 重入问题

①设置了内存槽存放原函数被Hook的指令及跳回原函数后续指令的指令

②使用热补丁

8)钩子链

出现原因:一个函数被多次Hook,形成了Hook链

解决方法

①新Hook加入Hook链

②穿透Hook链,跳过Hook点,直接钩在原函数上(风险较大)

③重载内核模块(重新加载一遍模块,即得到新的原函数)

9)指令缓存

​ hook 原函数后跳转到 FakeFunc,虽然修改了内存中的指令,但有可能被修改的指令已经被缓存起来了,再执行,CPU 可能会优先执行缓存中的指令,使得修改的指令得不到执行

解决方法:需要调用 FlushInstructionCache(GetCurrentProcess(), pPatchTarget, patchSize);来刷新缓存

注意:如果使用WriteProcessMemory写其他进程内存,不需要再额外调用FlushInstructionCache函数刷新缓存,因为WriteProcessMemory本身就会调用NtFlushInstructionCache函数来刷新缓存

10)暂停线程,捕获上下文,提取EIP / RIP

​ 改写原函数的前五个字节指令时,需要挂起所有线程,循环遍历比对查找,如果有线程的 EIP/RIP 指向原函数的前五个字节处,将其 EIP/RIP 改为指向 MemorySlot 里保存的相应指令处(利用 HookEntry->NewIPS, 这里面存了保存在 MemorySlot 里指令的偏移长度)
在恢复 Hook 时需要对线程的EIP/RIP进行恢复:利用 HookEntry->OldIPS,里面存的该指令在原函数指令的偏移长度

标签:字节,jmp,笔记,学习,地址,指令,MinHook,跳转,原函数
From: https://www.cnblogs.com/XiuzhuKirakira/p/18048840

相关文章

  • 嵌入式学习
    总体认识:Ubuntu桌面上右键/ctrl+alt+T,打开终端一般文件路径看不出磁盘(没CDE分盘)需要运行disk查看上图就是表示只有一个磁盘,四个分区,橙色部分是第二个分区,挂载在/boot目录下,在/boot建文件1.txt,它就会被放在第二分区sd-磁盘a-第一个磁盘1-第一个分区......
  • 开课啦!走进大数据讲堂,一文从0到1学习数据湖Paimon(实践篇一)之集成hive实战演练?助力数据
     第3章集成Hive引擎前面与Flink集成时,通过使用paimonHiveCatalog,可以从Flink创建、删除、查询和插入到paimon表中。这些操作直接影响相应的Hive元存储。以这种方式创建的表也可以直接从Hive访问。更进一步的与Hive集成,可以使用HiveSQL创建、查询Paimon表。......
  • unlink学习笔记
    unlink学习笔记一、什么是unlink首先不妨假设有三个free掉的chunk分别称为first_chunk、second_chunk、third_chunkunlink其实是想把second_chunk摘掉,那怎么摘呢?second_fd=first_prev_addrsecond_bk=third_prev_addrfirst_bk=third_prev_addrthird_fd=first_prev_......
  • 解决Puppeteersharp 被检测到的方法, 顺带学习了js如何实现 模拟点击拖动事件
    varlaunchOptions=newLaunchOptions{Headless=false,DefaultViewport=null,IgnoreHTTPSErrors=true,ExecutablePath=path+"\\.local-chromium\\chrome-win\\chr......
  • C++ 多线程笔记2 线程同步
    C++多线程笔记2线程同步并发(Concurrency)和并行(Parallelism)并发是指在单核CPU上,通过时间片轮转的方式,让多个任务看起来像是同时进行的。实际上,CPU在一个时间段内只会处理一个任务,但是由于切换时间非常快,用户感觉像是多个任务同时在进行。这种方式的优点是可以充分利用CPU资源,......
  • Tomcat学习路线roadmap和个人入门知识摘录
    Tomcat学习路线roadmap和个人入门知识摘录roadmap参考《TOMCAT与JAVAWEB开发技术详解第3版》,内容非常非常详细,初期入门并不需要学习到那么详细,后面精进学习可按图索骥,或者有需要再看看就行第1章Web运作原理探析读者不妨带着以下问题去阅读本章开头的内容:●在整个......
  • 0302笔记
    //机器语言:0000111//汇编语言:add--->+//高级语言:C,C++,python,java..//编译器--编辑器(IDE)#include<iostream>//导入输入输出流的头文件//#预处理指令//include导入,包含//<iostream>=inputoutputstream输入输出流//单行注释:用来解释的内容,机器不识......
  • Python学习笔记03
    函数语法:def函数名(传入参数):函数体return返回值#遍历字符串,并统计字符串的长度str1="helloword"str2="shen_mo_xie_xing"count=0foriinstr1:count+=1print(i,end='')print()print(f"字符串{str1}的长度是{count}")count=0fo......
  • Tomcat学习路线roadmap和个人入门知识摘录
    Tomcat学习路线roadmap和个人入门知识摘录roadmap参考《TOMCAT与JAVAWEB开发技术详解第3版》,内容非常非常详细,初期入门并不需要学习到那么详细,后面精进学习可按图索骥,或者有需要再看看就行第1章Web运作原理探析读者不妨带着以下问题去阅读本章开头的内容:●在整个......
  • Tomcat学习路线roadmap和个人入门知识摘录
    Tomcat学习路线roadmap和个人入门知识摘录roadmap参考《TOMCAT与JAVAWEB开发技术详解第3版》,内容非常非常详细,初期入门并不需要学习到那么详细,后面精进学习可按图索骥,或者有需要再看看就行第1章Web运作原理探析读者不妨带着以下问题去阅读本章开头的内容:●在整个......