在本篇文章中,我将分享一个可以从文件中加载一段shellcode到内核空间并且建立内核线程运行它的windbg脚本。由于我玩过的脚本并不是很多,所以如果有一些bug请告知我。
你可以从我的github上找到这段脚本:
load_code_to_kernel_memory.wdbg
脚本的运行参数:
$$>a<load_code_to_kernel_memory.wdbg <src code> <mem size> <offset start routine>
第一个参数是含有shellcode的文件的路径。第二个参数是申请的内存大小(足够分配shellcode即可)。第三个参数是被执行的shellcode的起始偏移。
注意:包含shellcode的文件的大小应该被填充至内存分页的大小。我们是使用.readmem
命令来加载shellcode,该命令将会读取一个0x1000字节大小的区块。举个例子,若你的shellcode有0x2800字节,那么.readmem
将只会加载0x2000字节。所以你需要使用0x800附加的垃圾数据来补全文件以便加载完整的代码。
我们需要劫持一个正在运行的线程一小会。将该线程的执行重定向到ExAllocatePool
来为shellcode申请内存(同时需要操作被劫持线程的栈来完成这个过程,之后再来恢复)
为此,我们需要在NtCreateFile
中设置一个断点(非常频繁使用的一个API)。当线程停在该断点上的时候,就可以进行操作了:
一旦建立线程之后,修改EIP以及栈信息来执行ExAllocatePool
此时,我们已经为shellcode分配了足够的内存空间,加载它吧:
现在,我们希望在shellcode + arg3处创建一个线程:
最后,我们恢复堆栈和eip以在NtCreateFile正确地继续执行被劫持的线程,否则系统将崩溃:
在此之后,windbg应该在线程启动的shellcode的偏移处的断点处停止。
我们将使用从一个worm/ransom 恶意软件 i don’t want to remember的中提取的DoublePulsar Shellcode来测试脚本。
你可以在此处下载shellcode here (rar password: infected).
文件的大小是0x3000。我并没有深入的逆向shellcode,但开始调试的好起点看起来应该是0x221偏移处(稍后我们将会知道why)。
执行脚本,下变是打印的调试跟踪:
可以看见新线程停在了我们指定的位置:
在0x20B偏移处可以看见shellcode hook了SYSENTER_EIP。
shellcode使用了地址0xFFDFFFFC
来存储 MSR[0x176]
。可以执行下边的命令:
Nt!_kuser_shared_data
结构位于0xFFDF0000处,所以我猜测shellcode是使用了和nt!_kuser_shared_data
的结构之后的位于同页的内存空间来存储其需要的临时变量。
关于通过MSR来HOOK系统调用可以参考下边这篇文章(非常有趣哦):
http://resources.infosecinstitute.com/hooking-system-calls-msrs
所以,0x221偏移是HOOK SYSENTER_EIP,因此,我认为这里是一个好的调试点。
然我们继续逆向 SYSENTER_EIP HOOK的代码:
在内核领空中,FS:[0]
指向_KPCR
结构。在这里,可以看见shellcode是如何从_KPCR
中获取一些它所需要的值以及其他的结构体指针的过程。
fs:0x40 -> _KTSS
_KTSS + 4 -> Esp0 (correct Esp for continue executing in kernel)
nt!kuser_shared_data+0x304 -> SystemCallReturn
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课