自定义跳转函数的通用unhook方法

简介: 自定义跳转函数的通用unhook方法

0x00 前言

   本文介绍一种比较有意思的unhook手法,来源于小伙伴发的一个GitHub的POC:https://github.com/trickster0/LdrLoadDll-Unhooking,本文参照此POC来一步步解读这个方法。

   目前大家常用的经典手法大都是直接系统调用(Syscall)或是找到ntdll的地址并重新映射磁盘中的.text段,去获得一个干净的dll去寻找函数地址的方式。

   下面介绍这种方式相当于我们自己去组装一个“跳转函数”,巧妙地规避了一些Hook,具有一定的参考以及学习价值。

0x01 流程分析

  1. 首先,构造Nt函数参数的结构体
UNICODE_STRING ldrldll;
 OBJECT_ATTRIBUTES objectAttributes = { 0 };
 wchar_t ldrstring[] = L"Wininet.dll";
 RtlInitUnicodeString(&ldrldll, ldrstring);
 InitializeObjectAttributes(&objectAttributes, &ldrldll, OBJ_CASE_INSENSITIVE, NULL, NULL);
  1. 接着定义和初始化要修补的指令的头部、地址、尾部
unsigned char jumpPrelude[] = { 0x49, 0xBB }; 
 unsigned char jumpAddress[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF };
 unsigned char jumpEpilogue[] = { 0x41, 0xFF, 0xE3, 0xC3 };
  1. 新建一块内存页,属性为可读可写(opsec),这块内存页是为了保存最终要使用的LdrLoadDll的地址。
LPVOID trampoline = VirtualAlloc(NULL,19, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  1. 获取ntdll内导出函数LdrLoadDll,原始的地址
LPVOID origLdrLoadDll = GetProcAddress(GetModuleHandleA("ntdll.dll"),"LdrLoadDll");

许多EDR去Hook API的方式就是去修改Windows DLL中的函数,通过在函数开头插入JMP指令来跳转到自己的检测函数;如果API被Hook了的话,一般前5个字节会变为JMP xxxxh,跳转到检测函数的地址,下方图片说明了这个流程。

上述获取到的原始函数地址,放在内存窗口查看

为了更方便查看,使用Windbg反汇编LdrLoadDll查看其结构,记录前五个字节,也就是\x48\x89\x5c\x24\x10

5.将原始的前5个字节,放入我们开始申请的地址中。

CCopyMemory(trampoline,(PVOID)"\x48\x89\x5c\x24\x10", 5);

   这一步比较巧妙,即使是EDR修改了前5个字节为跳转指令,我们也不用在意,因为我们不会去使用原始的前5个字节,而是自己放进去。

  1. 获取原始地址前5个字节后的地址,放入第2步申请的jumpAddress
LPVOID jmpAddr = (void*)((char*)origLdrLoadDll + 0x5);
 *(void**)(jumpAddress) = jmpAddr; //jmpaddr的地址

查看jumpAddress的内存,发现也就是地址7ff8d8ae6a15

  1. 接着是3个拷贝操作
CCopyMemory((PBYTE)trampoline+5, jumpPrelude, 2);
 CCopyMemory((PBYTE)trampoline + 5 + 2, jumpAddress, sizeof(jumpAddress)); 
 CCopyMemory((PBYTE)trampoline + 5 + 2 + 8, jumpEpilogue, 4);

   首先将jumpPrelude拷贝到前5个字节后,然后拷贝原始函数5个字节后的地址,最后拷贝jumpEpilogue指令尾部

在第5步的时候trampoline事先写入了前5个字节,内存看起来是这样的

经过3次内容的拷贝,最终指令为:

  1. 修改这个最终要使用的“自定义跳转函数”的内存空间为可执行
VirtualProtect(trampoline,30,PAGE_EXECUTE_READ,&oldProtect);
  1. 最后,将地址赋给事先定义的函数结构,并且调用
LdrLoadrDll = (pNewLdrLoadDll)trampoline;
 HANDLE wininetmodule = NULL;
 LdrLoadrDll(NULL, 0 , &ldrldll, &wininetmodule);

开启VS的汇编窗口,到了函数调用时,步入进去即可更清晰的看到最终trampoline中所做的操作,右边蓝框

0x02 总结

   这样就构建了一个完整的自定义的跳转函数,这个函数实际上的功能还是跳转回原始的LdrLoadDll的前5个字节后的地址去执行,相当于避免了修改前5个字节JMP到检测函数的这种Hook方法。

   我们自定义的跳转函数不受其影响,并且调用也是从NTDLL发出的,以一张图来说明流程:

相关文章
无参函数和有参函数的定义使用方法及其调用
无参函数和有参函数的定义使用方法及其调用
457 0
|
2月前
|
存储 数据可视化 JavaScript
可视化集成API接口请求+变量绑定+源码输出
可视化集成API接口请求+变量绑定+源码输出
62 4
|
3月前
|
前端开发
ThinkPHP6表单上传的数据获取的四种方式【请求对象调用,静态调用,助手函数调用,原生的get|post】
本文介绍了在ThinkPHP6中获取表单上传数据的四种方式:请求对象调用、静态调用(Facade)、助手函数调用以及原生的$_GET和$_POST数组。文章通过示例代码展示了每种方式的具体使用方法,并强调了在使用请求对象调用时引入正确的Request类的重要性。
|
5月前
uniapp实战 —— 轮播图【自定义指示点】(含组件封装,自动注册全局组件,添加全局组件类型声明)
uniapp实战 —— 轮播图【自定义指示点】(含组件封装,自动注册全局组件,添加全局组件类型声明)
319 1
|
7月前
|
小程序
小程序封装组件简单案例,所有小程序适用(传入参数、外抛事件、传入样式)
小程序封装组件简单案例,所有小程序适用(传入参数、外抛事件、传入样式)
98 0
|
存储 PHP
PHPlstat函数的使用方法与实例解析
PHP是一种广泛应用于Web开发的编程语言,它的开放性、通用性和易用性使其成为了Web领域中的主流语言。在PHP编程中,我们经常需要使用到一些函数来完成任务,其中非常重要的一个函数就是“PHPlstat”。这个函数可以用来获取文件的相关信息,本文将介绍PHPlstat函数的使用方法以及一些实例解析。
88 0
|
小程序
【小程序】跳转时传递多个参数
【小程序】跳转时传递多个参数
117 0
【小程序】跳转时传递多个参数
08配置基础路径 同时导出一个函数和一个变量 封装微信请求Api
08配置基础路径 同时导出一个函数和一个变量 封装微信请求Api
|
Windows 开发工具
UWP项目生成错误: 未能使用“CompileXaml”任务的输入参数初始化该任务。“CompileXaml”任务不支持“PlatformXmlDir”参数。请确认该参数存在于此任务中,并且是可设置的公共实例属性。
项目属性: 目标版本 16299  最低版本 14393   解决方法:目标版本 15063 最低版本 14393   The issue is a bug in the Windows SDK that is causing an incompatible MSBuild tasks as...
1490 0