这个驱动本来准备端午节搞的,但后来端午节去了躺北京玩了几天,也就搁置了...最近连续花了4个晚上,大致把它搞的差不多了,有些收获...初学window驱动,水平很菜,有些东西我也未必清明,加上3环的代码还没有吃透,期间也没有用调试器跟踪,全是IDA(F5)静态分析的,谬误在所难免,高手请飘过,希望对菜鸟学习有点帮助.
1.SSDT检测
这个功能有三个IoControlCode与之对应,他们分别是22000fh(拷贝KeServiceDescriptorTable);220007h(拷贝KeServiceDescriptorTable->ServiceTable);22000bh(恢复一个ssdt hook),这个功能目前还不具备检测和恢复inline hook的功能
2.Shadow SSDT检测
这个功能实现和SSDT差不多,都是从KeSystemServiceDescriptorTableShadow入手,不过3环需要为每个平台配置一张函数名表(ssdt可以不配置函数名表,可以从ntdll.dll中收集),详细文章可以参见zhuwg兄弟写的<<shadow ssdt学习笔记>>一文
3.进程
3.1 进程检测
IoControlCode是22001Bh,在检测之前会设置检测方式,然后根据检测方式进行检测.这个功能主要由irp_mj_device_control_dispatch_function函数的is_22001Bh_list_process分支完成,这里集成了4种检测方法,大致如下:
3.1.1 基于PspCidTalbe的检测,这里rku对句柄表做了解析,对每个Object判定是不是EPROCESS,是则插入到输出列表(icesword也有这中检测,不过在1.22中没有解析句柄表,而是用ExEnumHandleTable搞定这些) 详细代码见list_process_in_2k_by_handle_table/list_process_in_not_2k_by_handle_table函数
3.2.1 基于KiWaitListHead/KiWaitOutListHead的检测,这个检测方法很简单,关键是KiWaitListHead/KiWaitOutListHead两个链表的获取, 详细代码见list_process_by_list_head函数
3.3.1 如果是xp系统则还会采用*淫**荡*的内存暴力搜索进程进行检测,这段代码看debugman的yykingking发过, 详细代码在list_process_by_search_memory_in_xp函数
3.4.1 rku还会inline hook SwapContext函数,在这个函数里会记录当前的活动进程情况,经过上面三种方法的洗礼后,对在SwapContext的记录里的,但不在输出列表里的继续加入,当然是有判定条件的(条件就是EPROCESS不能在ntoskrnl模块内,即不为idle进程). BTW:这里有个逻辑我觉得有问题:
.text:000134A7 loc_134A7: ; CODE XREF: irp_mj_device_control_dispatch_function+233j
.text:000134A7 3B 05 34 4E 01 00 cmp eax, g_nCurrentProcessCount
.text:000134AD 7D 5A jge short loc_13509
这个检测方法的代码就在loc_134A7开始位置
上面获取的只是EPROCESS,要获取进程路径信息,还需要IoControlCode(220047h)的代码, 详细功能代码在get_image_path_by_eprocess函数里,在这个函数里,我们可以看到rku先走peb获取进程全路径,然后对非2k系统再走一次我们更为熟悉的SectionObject获取路径
3.2 杀进程 rku提供了两种杀进程的方法
3.2.1 IoControlCode(22001Fh) ZwOpenProcess/ZwTerminateProcess/ZwClose杀进程 这种杀法比较的弱
3.2.2 ioControlCode(22004fh) 内存清0杀进程 具体代码是kill_process_by_fill_zero函数,这个代码看debugman的yykingking也发过
3.3 Dump All Process Memory
IoControlCode是22008bh, 没什么可说的, 详细代码在dump_all_process_memory函数
3.4 Dump Seleted
这个就是拷贝主程序的内存镜象, IoControlCode是220083h, 先获取文件大小(对xp及其以上系统,通过EPROCESS->SectionObject->segmentObject->SizeOfSegment获取主程序内存镜象大小,对2k系统,由于手头缺乏资料,那段代码什么意思不知道,详细代码在get_main_file_size_by_eprocess函数), 然后输出缓冲区足够大就进入dump_selected函数拷贝内存
4.内核模块和Stealth Code检测
IoControlCode是0x220023h,在检测之前也会设置检测方式,然后根据检测方式进行检测.这个功能主要由list_kernel_module函数完成,这个函数里面集成了很多检测方法,很多方法都比较*淫**荡*,大致的检测方法有:
4.1 用IoDriverObjectType指向的OBJECT_TYPE.TypeList链表检测,遍历这个链表中的每一项,并会取每个DriverObject上的DeviceObject,并向上扫描这个设备所在的设备栈(注意这里没有向下扫描),尝试发现其它的DriverObject 详细代码见list_module_by_IoDriverObjectType函数
4.2 用IoDeviceObjectType指向的OBJECT_TYPE.TypeList链表检测,遍历这个链表中的每一个设备,并也会向上扫描这个设备所在的设备栈(注意这里没有向下扫描),尝试发现其它的DriverObject 详细代码见list_module_by_IoDeviceObjectType
4.3 用目录对象树(ZwOpenDirectoryObject("\\"))检测,遍历这棵树,对叶子节点(具体对象)看其OBJECT_HEADER.Type字段是否指向IoDriverObjectType/IoDeviceObjectType,如果是的话,则会采用4.1/4.2相似的措施;对目录对象子树则递归处理之(内核堆栈空间太小,递归搞始终给人一种不安全感,看SnipeSword里面处理注册表部分也大量递归,比较汗) 详细代码见list_module_by_OpenDirectoryObject函数
4.4 用DriverSection(PsLoadedModuleList链表)检测,这个方法大家都比较熟悉, 详细代码见list_module_by_DriverSection函数
经过上面4个方法,DriverObject就获取完毕了,他们对应的都是有详细信息的内核模块(这项检测初步给人的感觉是比icesword可靠些,icesword则是先尝试恢复NtQuerySystemInformation,设置KernelMode,然后调用NtQuerySystemInformation获取内核模块信息),下面的检测主要是对一些零星的能执行代码的内存块进行检测(Stealth Code)
4.5 用上面几种方法已经找到了一些DriverObject,这里就对这些DriverObject下手,检测这些DriverObject的DriverStartIo/MajorFunction是否被hook,如果被hook且新地址不在我们已经找到的模块里,则说明是那种分内存,写代码似的hook,记录这块内存信息 详细代码见list_module_by_DriverStartIo_and_MajorFunction函数
4.6 暴力搜索内存, 找到PE头部,并判定OptionalHeader.Subsystem是否是Native 详细代码见list_module_by_scan_memory函数
4.7 利用句柄表检测,这个的具体原理我未能理解, 详细代码在list_module_use_suspicious_thread_by_handle_table_in_not_2k/list_module_use_suspicious_thread_by_handle_table_in_2k这两个函数,感觉跟内核模块检测无关......知道的请说一声
4.8 rku会inline hook三个调用频度很高的函数(ExAllocatePool/ExAllocatePoolWithTag/KeDelayExecutionThread),在这三个函数里会记录它们的返回地址,并利用这些地址来检测一些模块,如果返回地址不在某具体模块范界则会记录其内存信息 详细代码见list_module_by_return_address函数
4.9 检测KiSystemService/KiFastCallEntry是否在某具体模块范界,如果不在也记录其内存信息
到这里检测就完成了,上面的检测方法太多,有些检测获取的信息不足,rku会进行一些额外的修正处理,这些代码都在list_kernel_module的结尾部分,详细请参见源码,我都做了一些注释
5.File检测
由于目前一直在用IDA反,并没有使用调试器,主程序目前还只反了一点,这个部分具体做什么的,我不太清楚,不过在驱动里看到一个IoControlCode(2200A7)是在做扇区级别的操作......
6.Code Hooks检测
在驱动里并没有看到分析code hooks的代码, 只看到一些拷贝内存到3环的功能函数,把一些系统关键手工load展开后,进行内存对照比较就可以搞定这个了,当然这个里面还具备KiSystemService/KiFastCallEntry hook的检测和恢复代码,详细可以看IDB文件
7.rku的进程保护
7.1 ssdt上的保护,在ssdt上对三个函数(NtOpenProcess, NtOpenThread, NtDuplicateObject)进行了inline hook
NtOpenProcess 防止打开rku进程
NtOpenThread 防止打开rku的线程
NtDuplicateObject 防止复制rku进程的句柄
7.2 shadow ssdt上的保护,在shadow上对四个函数(NtUserQueryWindow, NtUserFindWindowEx, NtUserWindowFromPoint, NtUserNotifyIMEStatus)进行了inline hook,防止其它程序通过FindWindow,WindowFromPoint等函数来枚举rku的窗口,你可以看到spy++对rku已经失效
8.逆向这个的过程中,参考了网上很多大牛的文章,
IceSword&Rootkit Unhooker驱动简析
zhuwg <<shadow ssdt学习笔记>>
prince 翻译的<<侦测隐藏进程的强文>>
yykingking 发在debugman上的两个代码片段
还有些文章,无法一一列举,再次一并表示感谢
大致就是这样了.
linxer 2008-06-19 于哈尔滨
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)