调用源检查测验逻辑

时间:2019-10-29 05:45来源:永利皇宫463手机版
———————————————————————————————————————————————————————————————— 在上后生可畏篇小说中,大家已经观看

————————————————————————————————————————————————————————————————

在上后生可畏篇小说中,大家已经观看IopParseDevice() 怎么样对传播的 OPEN_PACKET 结构举行认证。假如ObReferenceObjectByName() 的调用者没有分配并早先化第八个参数 ParseContext,而仅是简轻易单地传颂 “NULL” ,那么当调用链长远到 IopParseDevice() 内部时,就能因验证战败再次回到 C0000024(STATUS_OBJECT_TYPE_MISMATCH)。

大家依据源码中的暗意来追踪OPEN_PACKET 结构毕竟在哪分配的,如前所述,调用链 NtCreateFile->IoCreateFile()->IopCreateFile() 的末段,相当于在 IopCreateFile() 内部,实际担任 OPEN_PACKET 的最初化。上边贴出的代码片段以 NT 5.2 版内核源码为样例:

 

永利皇宫463手机版 1

相当于说,大家一贯复制 IopCreateFile() 中的 OPEN_PACKET 结构伊始化部分逻辑就行了?

此间还应该有一个主题材料,担负分配该组织体内核内部存款和储蓄器的例程 IopAllocateOpenPacket() 是贰个宏,Visual C++ 二〇一四 中付出它是用 ExAllocatePoolWithTag() 定义的。那就好办了,在我们和好的驱动力源码中,加多相应定义就能够,如下图:

 

永利皇宫463手机版 2

 

————————————————————————————————————————————————————————————

因为 OPEN_PACKET 结构同样未有公开的文书档案来说述,所以依然在大家的驱动力源码中用  “#include” 满含定义它的头文件,要么直接复制订义的那有个别黏贴进来。很分明,前面一个相当的轻易——OPEN_PACKET 在基础源码的 “iomgr.h” 中定义,而该头文件又嵌套包涵了一群横三竖四的内核头文件,要清理这么些嵌套包涵关系很艰辛,并且最要紧的是,其间有的头文件定义的数据类型会与驱动开采中用的 “ntddk.h” 和“wdm.h”重复,引起编写翻译器的愤恨。据此直接在 “iomgr.h” 中寻觅字串 “typedef struct _OPEN_PACKET”,把找到的定义块拷贝进来就能够。

然而,OPEN_PACKET 结构中单唯贰个字段不是 “原生” 定义的——那正是 “PDUMMY_FILE_OBJECT” 类型,必要包蕴别的头文件才不产生编写翻译器报错。

本人的减轻方案是,直接把该字段的宣示所在行注释掉,下图呈现了该字段具体的岗位(在 “iomgr.h” 中的行号),方便各位神速搜索:

 

永利皇宫463手机版 3

——————————————————————————————————————————————————————————————————

在意,NT 6.1 版内核在编写翻译时刻的 OPEN_PACKET 结构显然是未经 “恶意” 更改的,所以编译器为其 “sizeof(OPEN_PACKET)” 表达式总计 0x70 的值,而大家在谐和的驱动中拿掉了 OPEN_PACKET 当中一个字段使得编译器为发挥式 “sizeof(OPEN_PACKET)” 预计算 0x58 的值(前面包车型客车调节和测量试验阶段会表明),那会招致 “Size” 字段不是 IopParseDevice() 内部逻辑预期的 0x70,进而致使重回C0000024(STATUS_OBJECT_TYPE_MISMATCH)。

消释办法也非常轻便,大家的驱动中,不要凭借理编辑译时刻的计量,直接把 “Size” 字段的值硬编码为 0x70 不就好了?

平日来讲图所示,你还有或许会潜心到,小编把 “Type” 字段的常量 “IO_TYPE_OPEN_PACKET” 改成了对应的数值,以管教学学风流倜傥旦。

 

永利皇宫463手机版 4

 

其余,由于 IopAllocateOpenPacket() 等价于 ExAllocatePoolWithTag(),而后人经常再次回到泛型指针(“ PVOID ,亦即 void * ”),
之所以作者强制把它转型为与 “openPacket” 意气风发致的花色。
齐全,“东风” 就在于调用 ObReferenceObjectByName() 时,为第三个参数字传送入“openPacket” 就可以,上图显示的很精通了。

——————————————————————————————————————————————————————————————————

十分不好的是,作者把编写翻译出来的驱动放到虚构机(Windows 7,基于 NT 6.1 版内核)里面动态加载测验,依旧无法获得到

“DeviceQQProtect” 相应的器材对象指针,ObReferenceObjectByName() 再次回到 C0000024。

为了找寻故障原因,笔者在分配 OPEN_PACKET 逻辑的先头利用内联系汇率编增加了二个软中断 “__asm{ int 3; }  ”,宿主机器上运转水源调节和测验器 kd.exe,我的开发银行参数疑似那样:

kd.exe -n -v -logo d:virtual_machine_debugging.txt -y SRV*C:Symbols* -k com:pipe,port=\.pipecom_1,baud=115200,reconnect

 

参数 “logo” 钦赐要把一切调节和测量试验进程的输出新闻写入日志;

“-y” 钦赐符号文件的岗位(机器指令中并未内核函数与变量的符号,所以调节和测量检验器须要查找额外的号子以向客商体现人类可读的名号);
“-k” 参数钦定调节和测量检验类型为 “取名管道模拟串口1”,Porter率数值越高,响应越快。

把重新编译好的驱动放到虚构机中,在进级权限后的吩咐提醒符中试行 bcdedit.exe,启用调试形式,那样重启设想机后,就能够跻身调节和测量检验格局(无需在起步进度中按下 F8 选取菜单)。

本人把温馨的驱动实现有按需加载,也便是利用劳务调控管理器sc.exe)发出命令来动态加载和卸载,达成此意义的应和批管理公事内容如下图,注意该文件要放在虚构机中施行,“start= demand” 申明通过 sc.exe 按需运营;“binpath” 就是驱动文件存放的磁盘路线,要是自身的驱动名为hideprocess.sys,奉行该批处理职务后,就在相关的注册表地点增加了生机勃勃项,今后只需在 cmd.exe 中实行 “sc.exe start/stop hideprocess” 就能够动态加卸载。

永利皇宫463手机版 5

 

安份守己上述措施加载时,就能够自行触发大家设定好的软件断点,就能够在宿主机中检查设想机的根本空间。
此外还需注意一点:编译驱动时的 “营造” 情形应当选用 Check Build,那样会大器晚成并生成同名称的标记文件,后缀为 “.pdb”,进而调节和测验器能够呈现大家同舟共济驱动中的函数与变量名称,进步调节和测量试验效用,如下图:

 

永利皇宫463手机版 6

——————————————————————————————————————————————————————————————————————

接触软件断点后,大家常常会用 “kv” 命令查看栈回溯音信,它披暴光我们的驱动入口点 DriverEntry() 是由 I/O 管理器的 IopLoadDriver() 调用的:

 

永利皇宫463手机版 7

栈的顶层函数 “ReferenceDeviceAndHookIRPdispatchRoutine+0x56” 是本人加多软中断的地点。试行 “r” 命令查看当前的 x86 通用贮存器状态,EIP 指向地址 0x8f4a3196 ,试行 “u hideprocess!ReferenceDeviceAndHookIRPdispatchRoutine+0x56 L2”,反汇编输出的第一行地址正是 0x8f4a3196,与 EIP 的值相符;第二行是把三个 16 进制值 “ 704F6F49h” 压栈,实际上它是 ASCII 字符 “pOoI” 的 16 进制编码,换言之,那是在经过内核栈传递 ExAllocatePoolWithTag() 的第二个参数(从右往左传递,请回想早前的 IopAllocateOpenPacket() 宏定义那张图)。

————————————————————————————————————————————————————————————————

连续按下 “t” 单步实施,如下图所示,你能够观看,ExAllocatePoolWithTag() 的第三个参数,分配的根本内部存款和储蓄器大小为 0x70 字节,因为本人在宏定义中硬编码了那些值,实际不是用 sizeof(OPEN_PACKET) 表明式让编写翻译器总计;另一面,图中的 “dt” 命令也验证了它的尺寸为 0x70 字节。

第2个传入的参数 “NonPagedPool” 为不可换页池,其内的数目十分小概被换出物理内部存款和储蓄器,该常量对应的数值为 “0”:

永利皇宫463手机版 8

 

作者不想浪费时间在查阅内核内部存款和储蓄器的分红细节上,所以本人按下 “p”,步过 ExAllocatePoolWithTag() 函数调用,接下去的 cmp/jne 汇编类别对应源码中反省是或不是中标分配了内部存款和储蓄器并用于 openPacket 指针,实际的进行结果是跳转到地址 0x8f4a31c6 ,对应源码中开头化 OPEN_PACKET 结构前七个字段的逻辑:

永利皇宫463手机版 9

接下来直接单步实施到调用 ObReferenceObjectByName() 前夕,在那地大家要 “步入” 它的内部,举办故障逐个审查,所以按下 “t” 跟进,这里有贰个小技能,大家早就分析过 ObReferenceObjectByName() 的源码,知道它会调用超多函数,并且大概知道难题出未来ObpLookupObjectName() 里面,所以指令 “tc”能够追踪到各样函数调用处结束,再由顾客决定是还是不是跟进该函数内部。

那是我的美美好的梦想,但具中华全国体育总会是狠毒的,在本人追踪到原子操作连串函数

nt!ExInterlockedPopEntrySList() 调用时,kd.exe 就卡住了,不恐怕持续跟踪今后的调用链。从稍早的栈回溯音信来看,与源码仲春我们估计的调用连串大约切合,只是不掌握为什么在 nt!ObpAllocateObjectNameBuffer() 中,为了给传入的驱动对象名称 “DeviceQQProtect” 分配内核内部存款和储蓄器,调用 nt!ExInterlockedPopEntrySList(),而后人却不能跟踪。。。。是虚构机蒙受的因由,如故原子操作类函数的不可分割性质?

 

永利皇宫463手机版 10

 ——————————————————————————————————————————————————————————————

讲一些废话,日常大家在栈回溯中观察的顶层表达行,有三个 “Args to Child” 项目,表示调用者传递给它的参数,然则最多也只可以展现前几个。

以下图为例子吗,传递给 nt!ExAllocatePoolWithTag() 的多少个参数(从左到右)就是00000000(NonPagedPool),00000070(小编硬编码的值),704f6f49(ASCII 字符串“pOoI”),同理,传递给 hideprocess永利皇宫463手机版,!DriverEntry() 的第叁个参数 867c3550 是 _DRIVER_OBJECT 结构的地点,由I/O 管理器加载它时为它分配(注意与源码中 DriverEntry() 定义的生机勃勃枚 _DRIVER_OBJECT 指针区别,“Args to Child”

列出的多少一定于实行解引操作符 * 后的结果),第贰个参数是 UNICODE_ST福睿斯ING 结构的地址,对应源码定义中的大器晚成枚 _UNICODE_ST昂科拉ING 指针,该组织中存款和储蓄的是大家驱动在注册表中的完整路线:

 

永利皇宫463手机版 11
——————————————————————————————————————————————————————————————————

总来讲之,基于以上理由作者一点办法也想不出来持续跟进到 ObpLookupObjectName() 里面查看它是或不是施行了 IopParseDevice() 回调,进而不可能分明到底为何前者重回 C0000024。

我想也许是因为根本源码版本的更改,导致相关例程的决断逻辑也不等同了,不能够借助前意气风发版源码的逻辑来编排估摸运维在后后生可畏版内核上的驱动。

实在技术方案依然有的,相比花时间而已,正是使用 “u” 指令反汇编 ObpLookupObjectName() 开端处对应的机器指令,再反编写翻译成相像的 C 伪码,与 NT 5.2 版内核源码相比,搜索当中退换的地点,但那是一个费时费劲的办事,且收入甚微,还不及直接在互联英特网搜释出的 NT 6.1 版内核源码,可能临近的本子,再想想绕过的章程。

顺便说一下,依据 A 设备名得到 A 设备对象的指针,然后把 rootkit/本身驱动成立的黑心设备 attach 到 A 设备所在的配备栈,进而阻碍检查通过 A 设备的 IRP 内数据。。。。这种艺术已经相比过时了,因为今后反病毒软件的木本方式组件也会检查那几个器材栈,寻觅其余相称特征码的恶意设备,再者,内核调节和测验器的 “!devstack” 命令十分轻巧遍历揭破出给定设备所在的器材栈内容,被大范围用于计算机考查取证中,从 rootkit 的要紧指标——完结隐身——的角度来看, attach 到设备栈就不是三个好标准。

反倒,通过 ObReferenceObjectByName() 总是能够获得驱动对象的指针,进而能够 hook 该驱动的 IRP 分发例程,这种手法隐瞒性超级高,並且不便于被检查评定出来。

波路壮阔的博文将研商怎么样将这种工夫用在 rootkit 中,同期适应现阶段盛行的对称多管理器(SMP)情状。

————————————————————————————————————————————————————————————————

编辑:永利皇宫463手机版 本文来源:调用源检查测验逻辑

关键词: