系列索引
【原创】TP驱动掩护分析系列一 定位TenProtect掩护
【原创】TP驱动掩护分析系列二 代码定位内核函数
【原创】TP驱动掩护分析系列三 SSDT定位内核函数
前言
前面在【原创】TP驱动掩护分析系列一 定位TenProtect掩护中分析并找到了TenProtect在内核层所做的手脚,但只通过windbg等工具找到可不够,接下来继承介绍代码定位内核函数的方法
代码定位内核函数
驱动函数直接定位
在驱动开发引入的里提供了MmGetSystemRoutineAddress函数,根据函数名可以直接获取到已导出 的函数地点
该函数的官方文档直达:MmGetSystemRoutineAddress function (wdm.h) - Windows drivers | Microsoft Docs
这里再简朴介绍(翻译理解)一下:
函数原型
PVOID MmGetSystemRoutineAddress( PUNICODE_STRING SystemRoutineName);函数名MmGetSystemRoutineAddress函数功能 驱动程序可以使用这个Routine来确定一个Routine在特定版本的Windows上是否可用。它只能用于内核或HAL导出的例程 ,不能用于任何驱动程序定义的Routine参数名 SystemRoutineName参数名阐明 想要获取的系统Routine的名称参数类型 PUNICODE_STRING参数类型阐明 字符串,定义如下typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength;#ifdef MIDL_PASS [size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;#else // MIDL_PASS _Field_size_bytes_part_opt_(MaximumLength, Length) PWCH Buffer;#endif // MIDL_PASS} UNICODE_STRING;typedef UNICODE_STRING *PUNICODE_STRING;函数的使用
UNICODE_STRING s; //声明字符串 CHAR* string = L"RoutineName"; //要赋值的字符串 注意前面字符串前要加上L L告示编译器使用两个字节的 unicode 字符集。 RtlInitUnicodeString(&s, string); //初始化unicode字符串 MmGetSystemRoutineAddress(&s); //使用函数支持直接定位的函数
前面提到了MmGetSystemRoutineAddress只能用于内核或HAL导出的例程 ,也就是只能获取到内核中的导出函数
怎样知道内核导出了哪些函数?
找到C:\WINDOWS\system32\ntkrnlpa.exe的内核文件
PS:根据操纵系统版本的不同,对应的内核文件也大概不同,如在虚拟机XP系统下内核文件为ntkrnlpa.exe,而在实机WIN10下对应的内核文件为ntoskrnl.exe
除此之外,还有两种内核文件:ntkrnlmp.exe 和ntkrpamp.exe
这四种内核文件的区别如下:
ntoskrnl - 单处置惩罚器,不支持PAE
ntkrnlpa - 单处置惩罚器,支持PAE
ntkrnlmp - 多处置惩罚器,不支持PAE
ntkrpamp - 多处置惩罚器,支持PAE
PAE全称Physical Address Extension,即物理地点拓展,是x86处置惩罚器的一个功能,让中心处置惩罚器在32位操纵系统下访问超过4GB的物理内存
使用IDA Pro打开对应的内核文件,得到:
点击右上角的Exports,检察导出(不仅包含导出的函数,也包含了导出的变量等):
定位代码样例
下面以NtOpenProcess函数为例,通过MmGetSystemRoutineAddress定位函数地点
代码
#include "ntddk.h"VOID DriverUnload(IN PDRIVER_OBJECT DriverObject){ DbgPrint("卸载完成!\n");}NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath){ UNICODE_STRING s; CHAR* string = L"NtOpenProcess"; RtlInitUnicodeString(&s, string); PVOID address=MmGetSystemRoutineAddress(&s); DbgPrint("address:%p\n", address); DriverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS;}运行效果
验证效果
得到的地点为:805CC486
使用windbg检察对应地点的反汇编:
u 805CC486
可以验证得到的就是NtOpenProcess的地点
特征码定位
当想要定位的内核函数没有导出 ,但其在可获得地点 的附近 时,则可使用特征码定位法
所谓的特征码定位法,就是选一个地点作为搜刮的起始地点 ,然后开始逐一比对 匹配的特征码(字节码)
PsTerminateProcess
下面以PsTerminateProcess函数为例,介绍特征码定位法
确定起始地点
要使用特征码定位PsTerminateProcess函数,首先要确定一个起始地点 开始搜刮该函数
于是使用IDA Pro检察PsTerminateProcess附近的函数:
可以发现PsTerminateProcess函数的上面一个函数PsTerminateSystemThread正好是导出函数:
于是便可直接使用PsTerminateSystemThread作为起始地点
确定特征码
确定了起始位置后,便要确定特征码了
所谓特征码:关键就是要突出特征 ,即最好能够保持unique (独一无二),并且稳固 (固定不变)
使用Windbg检察PsTerminateProcess对应的反汇编
u PsTerminateProcess得到:
即:
nt!PsTerminateProcess:805d35c2 8bff mov edi,edi805d35c4 55 push ebp805d35c5 8bec mov ebp,esp805d35c7 5d pop ebp805d35c8 e9b5feffff jmp nt!PspTerminateProcess (805d3482)805d35cd cc int 3805d35ce cc int 3805d35cf cc int 3这里选取的特征码为:8b ff 55 8b ec 5d e9
轻微阐明一下为什么不选取jmp nt!PspTerminateProcess指令后面的b5feffff
jmp XXXX 对应的 硬编码为 e9 offset,e9代表jmp,后面的offset为偏移地点
实际要跳转的地点(XXXX) = 当前地点 + offset +当前指令长度
代入这里: 要跳转的地点(805d3482) = 805d35c8(当前地点) + fffffeb5(offset 小端存储) + 5(当前指令长度)
即 805d3482 = 805d35c8 + fffffeb5+ 5
也就是 805d3482-805d35c8-5 = fffffeb5
这里的fffffeb5是一个有符号负数,可以直接用系统自带的计算器来验证
(关于有符号数、无符号数可回顾:逆向基础笔记二 数据宽度和逻辑运算)
而偏移量是不固定的,即不满足特征码的稳固性要素,故不将后面字节码作为特征码
代码
确定了起始位置和特征码后,就可以写代码实现特征码定位了,代码如下:
#include "ntddk.h"/* 获取指定地点的数值 base:要获取的地点 offset:偏移量 size:获取的巨细*/PVOID GetAddrValue(PVOID* base, INT offset, INT size) { PVOID Addr = *base; PVOID templong = 0; //复制内存,将指定位置的内存的值读取出来 RtlCopyMemory(&templong, (PUCHAR)Addr + offset, size); return templong;}VOID DriverUnload(IN PDRIVER_OBJECT DriverObject){ DbgPrint("卸载完成!\n");}NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath){ //通过MmGetSystemRoutineAddress直接获取PsTerminateSystemThread地点 UNICODE_STRING s; CHAR* string = L"PsTerminateSystemThread"; RtlInitUnicodeString(&s, string); PVOID address = MmGetSystemRoutineAddress(&s); DbgPrint("PsTerminateSystemThread Address:%p\n", address); //将PsTerminateSystemThread作为起始地点 ULONG beginAdress =(ULONG) address; //搜刮范围限制为0x1000 ULONG endAdress = beginAdress + 0x1000; //刚开始匹配地点为0,表现尚未匹配到 PVOID matchAdress = 0; //开始循环比较 搜刮特征码 for (ULONG i=beginAdress;i< endAdress;i+=1) { PVOID value = GetAddrValue(&i, 0, 4); PVOID value2 = GetAddrValue(&i, 0, 3); //特征码为8b ff 55 8b ec 5d e9 小端存储 if (value == 0x8b55ff8b && value2 == 0xe95dec) { //匹配特征码后 赋值,终止循环 matchAdress = i; break; } } DbgPrint("PsTerminateProcess Address:%p\n", address); DriverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS;}运行效果
验证效果
得到的地点为:805D3594
使用windbg检察对应地点的反汇编:
u 805D3594
可以验证得到的就是PsTerminateProcess的地点
总结
本篇介绍了代码定位内核函数的两种方法:MmGetSystemRoutineAddress直接定位和特征码定位
MmGetSystemRoutineAddress只能定位到导出的内核函数,比较局限,但胜在方便稳固
特征码定位要注意特征码选取需要满足unique(独一无二)和稳固(固定不变)
限于篇幅,本章就先到这里,后续会再继承介绍SSDT定位法和符号表PDB解析法
PS:还有一种PE文件导出表扫描法在PE文件笔记十四 导出表中已阐明过,就不再赘述了
在介绍完这些定位方法后,就会针对TenProtect实例进行分析定位并绕过,敬请等待( •̀ ω •́ )✧
附件
最后附上本篇中用到的用具和最后编译出的驱动文件
包罗:
驱动加载工具:InstDrv 1.3 汉化版
调试信息检察工具:DbgView
特征码定位编译出的驱动文件:GetKernelAddress.sys
PS:特征码定位中用到了MmGetSystemRoutineAddress,故不但独提供驱动函数直接定位的驱动文件
DbgView可直接到微软官方下载:DebugView - Windows Sysinternals | Microsoft Docs
加载工具和驱动文件下载:点我下载
务必注意,驱动运行环境为Windows XP !!!WIN7和WIN7以上版本的操纵系统不支持
学习交换为主,请勿用于其他非法途径
驱动加载工具和成品.zip
11.42 KB, 下载次数: 20, 下载积分: 吾爱币 -1 CB
论坛备份,土豪专用
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
楼主热帖