首页
论坛
课程
招聘
[翻译]Ring3/Ring0层Rootkit Hook检测技术(一)
2018-1-4 18:46 6895

[翻译]Ring3/Ring0层Rootkit Hook检测技术(一)

2018-1-4 18:46
6895

引言

        近期的网络犯罪地下世界没有任何令人兴奋的恶意软件值得逆向分析,这导致我对新文章没有什么思路,因此我决定写两篇文章,来介绍一下rootkit拦截函数调用的相关技术,以及如何对其进行检测。第一部分会讲解一些挂钩方法,第二部分将介绍如何对其进行检测。由于本文并没有进行任何内核模式的相关工作,我将同时关注32位Windows系统上用户模式和内核模式两种情景下的挂钩技术。


执行流

        为了对攻击面有一个更好的理解,针对kernel32.dll中WriteFile函数的调用过程,我绘制了一张简化版的流程图;这只是值得关注的关键点的一个示例,之所以选择WriteFile函数,一方面是因为它确实是一个很好的示例,另一方面则是因为恶意软件通常选择磁盘I/O操作进行拦截,而且本图中的大部分内容对于很多函数是通用的。

(缺图:WriteFile函数从用户模式到内核模式的调用路径)

(PS:如果你连通过单击可以放大图像都不知道的话,那么本文可能不太适合你。)

        (1)

        ·WriteFile只是NtWriteFile函数的简单包装器。

        ·可以使用内部(inline),输入地址表(IAT)或输出地址表(EAT)方法进行挂钩。

        ·挂钩该函数将拦截对WriteFile函数的所有调用,不管挂钩过程位于哪个进程之中。

        ·kernel32库中所用到的所有路径基本都是Dos形式的路径(比如,C:file.txt)。

        (2)

        ·NtWriteFile函数是一个小型的桩程序,该函数用一个32位的值(随后我将详细解释这个值)为EAX寄存器赋值,然后调用KiFastSystemCall函数。

        ·可以使用内部(inline),输入地址表(IAT)或输出地址表(EAT)方法进行挂钩。

        ·挂钩该函数将拦截对CreateFile,NtWriteFile或ZwWriteFile函数的所有调用,不管挂钩过程位于哪个进程之中。

        ·ntdll库中文件函数所用到的所有路径基本都是NT形式的路径(比如C:file.txt,??)。

        (2.1)

        ·为了调用KiFastSystemCall函数,NtWriteFile函数将地址0x7FFE0300(KiFastSystemCall/KiFastSystemCall函数指针)放入EDX寄存器,然后它执行“call edx”或“call dword ptr [edx]”。

        ·为了实现挂钩,rootkit可以在NtWriteFile函数体中对地址0x7FFE0300进行替换。

        ·挂钩该函数将拦截对CreateFile,NtWriteFile或ZwWriteFile函数的所有调用,不管挂钩过程位于哪个进程之中。

        ·ntdll库中文件函数所用到的所有路径基本都是NT形式的路径(比如C:file.txt,??)。

        (3)

        ·KiFastSystemCall是一个小型的桩程序,它将栈指针放入EDX寄存器,然后执行sysenter指令。

        ·这个桩程序只有5字节大小,同时KiFastSystemCallRet指向它的最后一句指令(RETN),这就使得程序中只剩下4个可写字节(即,对于一个近距离的call/jmp指令而言空间不足)。

        而且,硬编码地址致使IAT或EAT挂钩技术不可用。

        ·有时KiFastSystemCall桩程序位于KUSER_SHARED_DATA结构体中,在这种情况下它对用户模式而言是不可写的。

        ·通过挂钩该函数,rootkit能够对内核函数的所有用户模式调用进行拦截。

        (4)

        ·为了执行一个内核函数,SYSENTER指令将执行流从用户模式转换到内核模式。当这条指令执行时,CPU将SYSENTER_CS寄存器的内容设置为代码段,将SYSENTER_ESP寄存器的内容设置为栈指针,并将SYSENTER_EIP寄存器的内容设置为EIP的值。SYSENTER_EIP寄存器指向ntoskrnl中的KiFastCallEntry函数,因此CPU将开始执行KiFastCallEntry。

        ·这些寄存器被称为模式相关寄存器(MSR,Model Specific Register),它们只能使用CPU指令RDMSR(读MSR)进行读取,或者使用WRMSR(写MSR)指令进行写入。这两条指令都需要相应的权限(只能在ring 0层执行),因此必须加载一个内核驱动才能实现挂钩。

        ·通过修改SYSENTER_EIP寄存器的值,rootkit能够对内核函数的所有用户模式调用进行拦截;但我们不能拦截任何内核模式调用,因为只有用户模式调用需要使用SYSENTER指令。

        (5)

        ·KiFastCallEntry函数负责从EAX寄存器中提取32位的值(即我们在第2小节所提到的值)。其中,前11位代表系统服务描述符表(SSDT)函数的序号,可以使用(SSDT_Address+(Ordinal*4))形式的表达式对其进行检索,第12和13位确定要用到的SSDT,最后剩余的位忽略。一旦函数确定要用到的SSDT,它将调用按照给定序号在表中索引到的地址。

        ·可以使用内部挂钩技术对其进行挂钩。

        ·通过挂钩该函数,rootkit可以拦截对内核函数的所有用户模式调用,以及对以“Zw”开头函数的所有内核模式调用,但是不能拦截对以“Nt”开头函数的内核模式调用。

        (5.1)

        ·因为SSDT是一个函数指针的列表,所以也可以通过替换SSDT中的指针来实现对调用进行挂钩操作。对于ntdll库中的每一个内核函数来说,在SSDT中都有一个等价指针,因此我们仅仅通过替换指针就可以实现随意挂钩任何函数。通过这种方法我们也可以挂钩对以“Zw”开头函数的所有内核模式调用,但我们无法挂钩对以“Nt”开头函数的内核模式调用。

        (6)

        ·再一次调用NtWriteFile。我们在第2小节中看到了对NtWriteFile函数的一次调用,但那只是ntdll.dll文件中一个用于进入内核模式的桩程序,现在这个才是实际调用根据给定SSDT序号找到的地址所指向的NtWriteFile函数。

        ·NtWriteFile函数构建了一个I/O请求包(IRP,I/O Request Packet),并将其递交给IopSynchronousServiceTail函数,NtWriteFile函数还会传递一个与待写入文件相关联的设备对象。

        ·可以使用内部挂钩技术对其进行挂钩。

        ·通过挂钩该函数,rootkit可以拦截对NtWriteFile和ZwWriteFile函数的用户模式和内核模式调用。

        (7)

        ·IopSynchronousServiceTail函数只在某些版本的Windows系统使用,它只是IofCallDriver函数的简单包装器,因此我将略过该函数。

        (8)

        ·IofCallDriver函数从参数中接收一个设备对象指针(PDEVICE_OBJECT)和IRP指针(两者都是由NtWriteFile函数所递交的)。设备对象中包含一个指向驱动对象的指针(PDRIVER_OBJECT),该驱动与指定设备相关联。驱动对象中包含一个名为“MajorFunction”的成员,这是一个由28个驱动定义函数指针组成的数组(有点像EAT或者SSDT),其中包括了IRP主函数名称的完整列表。IofCallDriver函数将根据所提交IRP的IO_STACK_LOCATION结构中的“MajorFunction”成员所指定的结果,来调用其中一个IRP主函数。在进行文件操作的情况下,NtWriteFile函数所提交的设备对象几乎全都是filesystemntfs(亦称为ntfs.sys),或者FileSystemNtfs上所附加的过滤设备;由于过滤驱动会将对设备的调用层层向下传递,直至抵达FileSystemNtfs,因此我们可以假定调用过程总是终止于filesystemntfs,除非其中一个过滤驱动取消了调用过程。

        ·通过对IofCallDriver函数进行挂钩,rootkit实际上可以拦截对任何驱动的任何调用。为了只拦截针对某个特定驱动的调用,rootkit可以检查驱动对象所指向的“DriverName”成员,而驱动对象由设备对象所指向;或者,rootkit可以对设备对象调用ObQueryNameString函数来拦截针对某个特定驱动的调用(需要格外注意的是,并不是所有设备都有名称)。rootkit还可以通过对IRP指针调用“IoGetCurrentIrpStackLocation”函数来筛选指定的IRP主函数调用,然后检查返回IO_STACK_LOCATION结构中的“MajorFunction”成员。

        (9)

        ·IRP_MJ_WRITE函数用于写入文件系统中的文件。

        ·通过将一个过滤设备附加到FileSystemNtfs的设备栈上,或者(使用它自带的其中一个函数指针来???)替换一个IRP主函数指针,rootkit可以拦截对FileSystemNtfs的任何调用。为了拦截对NtWriteFile函数的调用,rootkit需要检查过滤设备中的IRP_MJ_WRITE函数调用,或者替换驱动对象中的IRP_MJ_WRITE指针。

        (10)

        ·这里指的是FileSystemNtfs所用到的卷和分区驱动,rootkit通常不以它们为目标,因此我将它们排除在外。

        ·可以使用与第9小节相同的方法对这些驱动进行挂钩。

        (11)

        ·NTFS文件系统使用类驱动“驱动程序磁盘”(亦成为disk.sys)的IRP_MK_WRITE主函数来写入磁盘。因为驱动程序磁盘的层次比NTFS文件系统驱动要低得多,所以没有文件名称,只能通过逻辑区块地址(LBA,Logical Block Address)来开展工作。逻辑区块通过扇区以一种线性的方式在磁盘上定位,每个扇区的大小通常是512,1024,2048或4096字节。扇区编号从0(主引导记录)开始,根据磁盘大小可以无限增长。

        ·对低于ntfs.sys的驱动进行挂钩,通常只见于bootkit所用到的内核模式负载驱动。这是基于以下事实,即bootkit倾向于只对NTFS文件系统之外的文件进行操作,这样就不必为转换文件名称获取LBA而烦恼。

        (12)

        ·磁盘子系统是指低于disk.sys的任何驱动,通常是指端口/微端口驱动,它们是特定的硬件或协议驱动。在大部分情况下,它们是atapi.sys或scsiport.sys,即与ATA和SCSI磁盘设备相关的驱动。

        ·在这个层次上要用到一个新的IRP主函数IRP_MJ_SCSI,它是IRP_MJ_INTERNAL_DEVICE_CONTROL函数的别名。这里,rootkit必须使用SCSI_REQUEST_BLOCK参数,相比于对disk.sys的挂钩这要复杂得多。

        ·对端口/微端口的挂钩操作通常只在bootkit所使用的高级内核模式负载驱动中能够见到。


说明

(缺图:Windows系统调用路径)

        ·术语“内核函数”是指以“Nt”或“Zw”开头的任何函数。我将它们称之为内核函数,是因为其代码存在于内核之中,对于一个用户模式的应用程序来说,要调用这些函数必须通过SYSENTER指令进入内核。

        ·在上述所介绍的各个小节中,只有第1,2,3小节的函数可以在用户模式中对其进行挂钩操作,其余的都需要内核模式的驱动程序。

        ·为什么在第5和5.1小节中进行的挂钩操作无法拦截对以“Nt”开头函数的内核模式调用?原因在于这些函数的工作原理。从内核模式调用任何以“Nt”开头的函数,都涉及到ntoskrnl库中的实际函数;然而当从内核模式调用以“Zw”开头的函数时,它将使用与第2小节所设置的相同的值为EAX寄存器赋值,然后调用KiSystemService函数。

        KiSystemService函数“落入”KiFastCallEntry函数;我使用“落入”这个词,是因为这里并不是调用或者跳转,KiFastCallEntry函数位于KiSystemService函数的某一偏移处,因此KiFastCallEntry函数实际上是KiSystemService函数的一部分。如果你仍然感到迷惑,上述图表将帮助你加以理解。

        ·在用户模式中,“Nt”和“Zw”开头的函数调用都严格遵循同一路径。同样,如果你感到迷惑的话请参考上述图表。

        ·通过对流程图中的某一点进行挂钩,rootkit能够拦截对该点及该点以上的所有调用。换言之,通过对第3小节的函数进行挂钩,rootkit可以拦截对第3,2,1小节函数的所有成功调用。

        ·如果在阅读本文的内核模式部分时,对任何内容你觉得“我可能没有完全理解”乃至“我的天哪这特么都是什么玩意儿”,你可能需要补充一些Windows内核方面的基本知识(特别是驱动/设备栈,I/O请求包以及IRP主函数)。


总结

        这系列文章分为第1部分和第2部分,下一部分很快就会发布,其中将会讲解如何检测(以及可能的话,移除)本文中所介绍的挂钩方法。如果你有任何问题,或者我对某些方面讲解的不够透彻,请在评论中指出,我将对本文进行修改完善。



原文地址:http://www.pentestingexperts.com/ring3-ring0-rootkit-hook-detection-1-2/
本文由  看雪翻译小组 木无聊偶  编译
转载请注明来源

[注意] 欢迎加入看雪团队!base上海,招聘安全工程师、逆向工程师多个坑位等你投递!

收藏
点赞0
打赏
分享
最新回复 (1)
雪    币: 583
活跃值: 活跃值 (116)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
supersoar 活跃值 2020-1-6 02:02
2
0
另外补图:
https://www.malwaretech.com/2013/09/ring3-ring0-rootkit-hook-detection-12.html

没有FQ工具,拿不到高清图。
游客
登录 | 注册 方可回帖
返回