0x00 前言
最近在学习恶意代码分析实战,了解到了一些隐藏恶意代码启动的方式,这些隐藏方式的学习,对日常的安全应急也起到不少指导作用,回想起原来应急碰到过的一些病毒的隐藏方式,有种豁然开朗的感觉,所以把这段时间学习成果做个总结。主要是介绍各种隐藏方式的实现原理和如何应急处置,不会涉及到代码逆向分析这块,因为主要是为了让大家后面应急提供更多的思路去处置。
这次先介绍SSDT hook。
0x01 SSDT及shadow SSDT
其实这块知识涉及到内核调试,先讲解下概念。
SSDT全称为System Services Descriptor Table,中文为系统服务描述表,也称为系统服务分发表,微软使用它来查找进入内核的系统调用。
在解释这个内核调用之前,我还要给大家讲解一个概念,windows api的调用流程。
大家应该知道windows系统提供的dll,其实是一个函数库,常说的windows api就是这些dll的导出函数,比如你在windows上创建文件实际上调用的是CreateFile,这个函数存放在kernel32.dll里。而和CreateFile相似的一个函数为NtCreateFile,这个函数被称作原生API。
原生API是用来和windows系统进行交互的底层API。当调用windows API中的一个函数时,这个函数通常不会直接执行请求的动作,因为大多数重要数据结构之都被保存在内核中,在内核外面的代码(用户模式代码,windows分为ring3和ring0两个模式,分别为ring3用户模式,ring0内核模式,大多数应用在运行在用户模式)是无法访问它们的,微软为了使用户应用程序能够达到必须的功能,创建了一个多步骤的调用过程。如下图所示。
用户应用程序给与用户API(比如kernel32.dll和其他dll)的访问,这些DLL会调用ntdll.dll,这是一个特殊的DLL程序,它管理用户空间与内核的交互。然后处理器切换到内核模式,并执行一个内核中的函数,这个函数通常位于ntoskrnl.exe中。
举个例子,你在创建windows文件,调用kernel32.dll中CreateFile,CreateFile会再调用ntdll.dll中的NtCreateFile,NtCreateFile内部代码会调用一个call之类的指令切换到内核,然后在内核中调用Ntoskrnl.exe导出函数NtCreateFIle,通过该函数再操作内核数据结构去创建一个文件。可以看到ntdll和ntoskrnl.exe中都存在NtCreateFIle,实际上对内核数据结构操作的是ntoskrnl中的,而ntdll作用为用户控件和内核空间交互。
具体来讲原生API内部代码会通过SYSENTER/SYSCALL或INT 0x2E指令来切换到内核空间,切换前会把原生API的偏移量(如NtCreateFile偏移量为55h,xp中为25h)存放到eax。
切换到内核的时候,系统会根据eax存放的偏移量在一个表中搜索偏移量对应的函数地址,然后执行。这个表就是SSDT,SSDT包含一个庞大的地址索引表,通过偏移量可查找到原生API在内核中的地址(该地址范围应该在ktoskrnl.exe地址空间中,因为是它的导出函数)。
如上80502b8c是SSDT的基地址,每四字节存储函数地址。举个例子,要在内核中调用NtAccessCheck,那么它eax的值应该为1,根据SSDT表(80502b8c+4*1)=80502b90,该地址存储的805e7db6即NtAccessCheck的地址。
Shadow SSDT和SSDT的数据结构是相似,SSDT是索引的函数在ntosknrl.exe里,而shadow SSDT索引的函数位于win32k.sys。
1.主要处理user32.dll,GDI32.dll中调用的函数,如postMessage,SendMessage,FindWindow,主要在win32k.sys中实现.(微软未给出win32k代码)
2.需要注意的是shadowSSDT并未导出,可用ida在win32k.sys中的导出表搜索,且结构与ssdt相似,但是不能通过windbg dd命令查看值
3.ShadowSSDT表只能在GUI(即有界面的程序进程)环境下才有值,故我们需要调用KeAttachProcess来切换到GUI线程里。
4.用windbg 命令 .process 861ff020 (861ff020 是通过 命令 !process 0 0得到的)切换到GUI线程上下文
用PChunter也可以查看
上面花了大篇幅来介绍SSDT,主要是这个概念很少人接触,现在已经清楚SSDT作用和如何使用的。
回过头来说病毒,在rootkit使用的隐藏技术中,SSDT hook技术的使用程度是远远超过其他技术。也是因为它容易理解、实现灵活且容易。
Rookit如何使用SSDT呢。比如我想恶意文件不能被其他程序打开或者检查到。Rookit可以挂钩NtCreateFile函数,它修改SSDT中原NtCreateFile偏移位置指向的函数地址,让索引指向rootkit设置的恶意代码地址,比如SSDT中80502b90原来存储805e7db6,该地址为NtCreateFile地址,rootkit修改后80502b90存储77523c74,为恶意代码地址,就实现了SSDT hook。从而当内核态系统调用发生时,将会调用rootkit的代码,而不是SSDT中预期的系统函数。恶意代码可以调用原始的NtCreateFile函数来避免影响系统功能,并根据rootkit配置过滤返回值,当检测到文件名为恶意文件,则不返回文件句柄。这样就可以让用户无法删除恶意文件。
0x02 实验
接下来我们通过一个实验来具体看下SSDT hook效果。
可以看到病毒文件Mlwx486.sys通过资源管理器查看(已关闭系统文件隐藏等设置),无法找到该文件,但通过cmd却可以找到Mlwx486.sys文件。
通过PChunter查看内核钩子-SSDT,这里我们可以看到NtQueryDirectoryFile被Hook了,挂钩到一个名为Mlwx486.sys的驱动上,函数原地址为0x808E78C8,位于ntoskrnl里,挂钩后,函数地址为0xF7B40486,位于Mlwx486.sys驱动程序里,挂钩NtQueryDirectoryFile使得Mlwx486.sys不在目录下显示出来。
Mlwx486.sys的地址范围是0xF7B4000-0Xf7B41000,挂钩地址0xF7B40486确实位于该范围内。
我们尝试卸载该驱动从而恢复文件显示。当然卸载驱动操作很危险,因为驱动是运行在内核里,出问题内核崩溃蓝屏很正常,一般都会死机,所以卸载后,我也死机了,重启后我们可以看到SSDT hook没了。我们在看下文件。
发现文件也已经可以显示出来了。
做实验中大家可能会看到一个sysdiag.sys也进行了SSDT hook,这个其实是火绒剑的驱动,火绒剑还hook了shadow SSDT,这也是火绒剑能够监控程序的原因。
0x03 扩展
这边详细说明下SSDT的数据结构。
上面没细说,有几个概念需要先搞清楚,SDT、SST、KiServiceTable
SDT是Service Descriptor Table(服务描述表),数据结构如下
typedef struct _KSERVICE_TABLE_DESCRIPTOR { KSYSTEM_SERVICE_TABLE ntoskrnl; // ntoskrnl.exe 的服务函数(SSDT) KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 的服务函数(GDI32.dll/User32.dll 的内核支持,Shadow SSDT) KSYSTEM_SERVICE_TABLE notUsed1; KSYSTEM_SERVICE_TABLE notUsed2;
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
|
可以看到SDT结构体中包含了4个KSYSTEM_SERVICE_TABLE结构体的数据,KSYSTEM_SERVICE_TABLE就是SST(System Service Table,系统服务表)
我们先看下SST的数据结构
typedef struct _KSYSTEM_SERVICE_TABLE { PULONG ServiceTableBase; // SSDT (System Service Dispatch Table)的基地址 PULONG ServiceCounterTableBase; // 包含 SSDT 中每个服务被调用的次数 ULONG NumberOfService; // 服务函数的个数, NumberOfService * 4 就是整个地址表的大小 ULONG ParamTableBase; // SSPT(System Service Parameter Table)的基地址
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE; |
如上所示,实际上SSDT的入口地址,是保存在SST的第一个参数里,我们还需要关注的有第三个参数NumberOfService,记录原生API的个数,实际就是SSDT表的大小。
Windows系统中有两个SDT,一个是ServiceDescriptorTable,另一个是ServiceDescriptorTableShadow。
ServiceDescriptorTable中只有一个SST,名为KiServiceTable,KiServiceTable的SSDT保存着ntoskrnl的函数索引表。
第一列是地址,红框内容就是ServiceDescriptorTable,SST长度为16字节,所以每行表示一个SST,可以看到只有第一行有值,80502b8c即为SST(KiServiceTable)中SSDT的地址,长度为0x11c。
我们在查看下SSDT的内容,每四字节就表示ntoskrnl中函数的地址。
我们可以通过符号表来查下各个地址所对应的函数
ServiceDescriptorTableShadow中有两个SST,一个是KiServiceTable,另一个是Win32pServiceTable。Win32pServiceTable的SSDT保存着win32k.sys的函数索引表。
可以看到有两行是有值的,并且第一行和ServiceDescriptorTable是一致的,多出的第二行SST即为Win32pServiceTable,它的SSDT称为shadow SSDT,地址为bf999b80,长度为0x29b。
但shadow SSDT的函数符号没被导出,微软不提供这个函数说明。
0x04 参考
https://www.cnblogs.com/kuangke/p/5761586.html
http://www.blogfshare.com/get-original-ssdt.html
https://blog.csdn.net/baggiowangyu/article/details/41802229