[原创]默默无闻·恶意代码分析Lab1
[原创] 默默无闻·恶意代码分析Lab5
[原创]默默无闻·恶意代码分析Lab6
[原创]默默无闻·恶意代码分析第七章
[原创] 默默无闻·恶意代码分析Lab7
[原创] 默默无闻·恶意代码分析Lab9
参照书籍:《恶意代码分析实战》、《加密与解密》;
文件来源:文件来源:官网随书文件、或者附件中(文件密码:apebro
);;
使用工具:WinHex、CFF、StudyPE+、Exeinfo PE、Resource Hacker、Depends Walker、IDA、MSDN;
本实验包括一个驱动程序和一个可执行文件。逆可以从任意位置运行可执行文件,但为了使程序能够正常运行,必须将驱动程序放到C:\Windows\System32
目录下,这个目录在受害者计算机中已经存在。可执行文件是Lab10-1.exe,驱动程序是Lab10-01.sys。
1、两个文件都查壳一下;
干净的;
2、查看Lab10-1.exe的导入函数和Lab10.01.sys的导入导出函数;
首先,导出表没有导出函数;
导入的这仨都是内核函数,是对注册表进行操作;
导入表有加载库和服务以及查看代码页的行为;
3、看一看字符串;
有注册表、内核信息等行为,还有对防火墙的操作;
而且\\Registry\\Machine
开头,这是内核访问注册表的标准形态,等同于用户态的HKEY_LOCAL_MACHINE
;
目前有用的就是一个文件路径和弹窗相关操作;
3、使用OD
和IDA
跟踪Lab10-1.exe
;
因为在dll
文件没有导出表的情况下,直接从exe查看对dll
文件的使用,会更快一些;
使用IDA
打开Lab10-1.exe;
首先打开服务控制管理器,如果打开成功,进入下一步,否则,退出程序;
面对下面的好几个系统函数,我们首先要理清这个程序的框架是什么?
只是大概理了一下思路,语法规范并不完全符合;
首先,假设我们是正确打开了服务控制管理器,下一步我们就要看看创建了什么服务,以及打开了什么服务;
我们看到一个路径,很明确,这个路径并没有这个文件,所以并不会正确创建;
翻译一下就是在进程调用StartService函数时由服务控制管理器启动的服务;
翻译一下就是指定为驱动服务;
这里要说一下的是,书中说的类型3
指的是dwStartType
,但是真正说明是驱动服务的是dwServiceType
字段,所以我觉得是书中错误,如有正确看法,还请大佬指出;
因为是无法正确创建的一个服务,但是也能够暗示出,这个服务是在这个Lab10-01.sys
中;
如果创建失败,那就打开服务OpenService
;
这里或许会很烦恼,创建都失败了,怎么可能打的开呢?
其实道理很简单,这个文件路径的问题,在之前的例子中,都会找到当前文件的路径,再找文件,所以找到对应文件并不难,在这里只是为了让我们体验R0层的病毒分析;
所以创建失败的情况应该理解为系统已经有一个相应服务了,所以才会使用打开,找到名为Lab10-01
的服务;
再下面就是开始服务了,这个没什么好看的;
最后就是ControlService
;
因为调用它的前提是打开服务,而打开服务的前提是创建服务失败(因为已经存在);
所以,我们要搞清楚,服务已经存在且已经运行后,再次打开服务又做了什么;
我们先看看这个字段的意思;
给大哥们翻译翻译这个惊喜:请求服务停止;
这个逻辑就很清晰了,现在问题是这个服务到底干了嘛;
IDA
打开Lab10-1.sys;
分析dll
程序的时候,常说DllMain
并不是真正的入口,只是在系统加载动态库的时候会加载这个函数进行必要的初始化后,再跳转到真正的程序入口;
sys
文件也是同样的道理,在DriverEntry
的处是一些必要的初始化和检查,然后才跳转到真正的驱动入口;
在这里,我们可以确认的是sub_10906
才是真正的驱动程序入口,如果不放心,可以去上面的call
看一下都干了啥;
我们看一下sub_10906
;
这就结束了,就是把一个函数的偏移给了一个内存位置,既无调用也无跳转,看的有点迷糊,不慌,换个视角就相对容易理解了;
所以就是相当于把一个函数指针放在了一个服务里,让服务去执行;
那就进sub_10486
去瞅一瞅;
然后就是一堆函数跟字符串,换个视角;
在此,因为Rtl
开头的是微软未公开的函数,我使用的MSDN并没有查到,而且使用某个大佬分享的未公开函数查询网站也没有查到;
最后还是查询了微软官方文档,了解了一些功能细节;
到这里,我们就需要关注每一次的注册表操作细节;
分别是给一下的路径进行添加键对象:
然后就是两次写入键值;
在此直接给出代码观:
我就很好奇,这个ValueType
字段是啥意思;
找到官网,找到字段描述:
点进去,找到类型;
到VC++
中,输入字段名,右击找到定义;
是4
的有两个,所以只能猜测是描述32位系统了;
这是我使用的方式,如果有不对或者更好的,还请大佬赐教;
到这里就很清晰了,这个函数做了很多关于注册表的操作,因为跟防火墙相关,所以我们可以合理猜测是禁用防火墙;
1、这个程序是否直接修改了注册表(使用procmon
来检查)?
没有用Procmon
工具,但是经过分析,是直接修改了注册表;
2、用户态的程序调用了ControlService
函数,你是否能够使用WinDbg
设置一个断点,以此来观察由于ControlService
的调用导致内核执行了怎样的操作?
没有用WinDbg
工具,但是经过分析,我们知道,是卸载操作;
3、这个程序做了些什么?
创建一个服务加载驱动,然后创建和写入了一些注册表项,用于禁用防火墙;
该实验的文件为Lab10-02.exe;
1、查壳;
无壳;
2、查看导入表;
因为过长,就不全面截图了;
首先依旧是服务相关的函数,其次是发现了资源相关的函数,所以我们有理由怀疑是否资源节中隐藏了什么秘密;
貌似加载了什么库,还申请了内存空间,并且获取程序地址;
3、查看字符串;
一个路径,和一个驱动服务名;
4、过程分析;
程序开始就加载了资源,所以我们还是使用Resource Hacker
查看一下资源节;
基本可以确定里面隐藏了一个程序;
保存到桌面,用IDA
打开;
IDA
已经自动识别这是一个驱动文件了;
暂时不要着急分析这个驱动文件,我们先去看看原程序是如何加载这个驱动文件的;
这一段主程序,不算复杂,依旧是使用代码表示结构;
由此,我们可以了解到,先是从资源中去除,然后将其写入到路径为C:\\Windows\\System32\\Mlwx486.sys
文件,然后就是依次的创建服务打开服务;
既然如此,我们把重心放在从资源节导出的驱动文件上;
都是内核操作;
暂时没看到什么亮点;
过程分析;
这一段貌似跟我们之前做的有些区别,在最后将一个函数指针给某个内存空间前,貌似做了很多的工作,我们先理一下结构;
在此我们要先搞清楚这里面用到的内核函数是什么,使用了微软官方文档;
再看反汇编,先是把N
和KeServiceDescriptorTable
转换成unicode
,然后再解析成函数指针;
转换成unicode
好理解,因为在Windows系统内部的底层,都是先将A
结尾的函数解析成W
结尾的函数,也就是说,内核层应该都是使用宽字符的;
至于返回函数指针我也是有点懵,看了书后答案才知道,是返回N
和KeServiceDescriptorTable
的地址偏移量偏移;
到此,对于do{}while
语句的理解貌似卡克了,但是好在到目前为止,还不是我们要找的重点;
在此直接借鉴书后答案的原话:我们看到DriverEntry
例程以参数KeServiceDescriptorTable
和·NtQueryDirectoryFile
调用了RtlInitUnicodeString
函数,然后调用MmGetSystemRoutineAddressPatchFunction
的地址覆盖这个项。
进而来分析sub_10486
字段;
还是先整理一下函数逻辑:
再去整理一下和内核函数的功能:
关于NtQueryDirectoryFile
书中是重点描述了关于FileInformationClass
字段,如果它是3以外的值,便会返回NtQueryDirectoryFile
的原始返回值;
关于这个,以我目前浅短的经验,该字段我是没遇到3
以外的值,在查找相关函数功能时,以外发现了一篇写的很不错的Lab10-2习题笔记里面也提供了官方工作人员的回复,毕竟是买了正版MSDN的才有的服务,我就不厚颜无耻的复制粘贴了,有兴趣的直接点进超链接去看吧;
我们再逐步的看一下;
首先,我们看一下这里的NtQueryDirectoryFile
函数;
首先是这个call
,如果不是认字不认颜色的话,我都以为这是一个参数了,这很显然是IDA
想告诉我们什么;
点进去看一看;
是一个跳转语句,再双击进去就是正牌的NtQueryDirectoryFile
了;
很显然,上面程序中构造的函数,是想在伪装什么;
继续往下看,我们假设它正确跳转且返回了;
继续往下后,它先是比较了FileInformationClass
是否等于3
,我们之前说过的,它正常来讲应该是等于3
的;
第二部是检查NtQueryDirectoryFile()
是否正确执行,在上面参数都正确构造,也正确跳转的情况下,我们可以合理假设它正确的执行且返回了;
再然后就是关于ReturnSingleEntry
字段了,关于该字段的描述如下:
到此,或许有点一头雾水;
这里我们要先了解一下NtQueryDirectoryFile
返回的东西是啥:
其实返回的是FileInforMation
结构,而该结构在FileInformationClass
字段中有定义,经过继续查询,我们知道是由一系列的FILE_BOTH_DIR_INFORMATION
结构组成;
所以,上述的三个条件都是在检查我们伪装的这个NtQueryDirectoryFile()
是否正确运行;
继续往下看:
很显然这是一个循环,而且在没有找到循环步长的情况下,这是一个死循环;
重点是我们要关注一下RtlCompareMemory()
函数比较了什么;
经过向上夙愿,我们找到FileInformation
字段,我们知道这是一个叫做FILE_BOTH_DIR_INFORMATION
的结构体,找到定义(上面官网插查询链接);
经过计算,我们得到的是FileName
字段;
至于word_1051A
字段,是内存没定义值,且交叉引用也没有找到赋值语句,所以我们可以合理猜测是一个运行时的值,具体是什么,我们暂时可以画个问号,先看如果比较成功应该会怎么样;
继续往下走,假设返回值是8
,也就是正确返回了,进行下一步;
这一段跳转,我觉得可以使用上面的代码结构看的清楚;
这里,貌似不太好理解,其实带着上面的FILE_BOTH_DIR_INFORMATION
的结构体看,再看我这个灵魂画家画个图,就会简单点;
所以嘛,这就是一个隐藏功能;
到此分析就算是结束;
经过分析我们知道,这个程序构造了一个伪装的NtQueryDirectoryFile
程序,并且将其隐藏;
并没有创建设备,也没有向驱动对象添加什么操作函数;
1、这个程序创建文件了吗?它创建了什么文件?
创建了C:\\Windows\\System32\\Mlwx486.sys
;
2、这个程序有内核组件吗?
一个内核驱动,被隐藏在资源节中,最后作为一个服务加载到内核;
3、这个程序做了些什么?
隐藏文件的Rootkit,使用了SSDT挂钩来覆盖NtQueryDirectoryFile
的入口,会隐藏任何以Mlwx
开头的文件,使用WinDbg
进行内存查询得到字符串;
本实验包括一个驱动程序和一个可执行文件。你可以从任意位置运行可执行文件,但为了程序能够正常运行,必须将驱动程序放到C:\Windows\System32
目录下,这个目录在受害者计算机中已经存在。可执行文件是Lab10-3.exe,驱动程序是Lab10-03.sys。
1、查壳;
Lab10-03.sys:
Lab10-03.exe:
皆无壳;
2、导入表;
Lab10-03.sys:
IoGetCurrentProcess
说明这个驱动涉嫌修改正在运行的进程;
Lab10-03.exe:
有服务相关程序和加载动态库和写文件嫌疑;
3、字符串;
Lab10-03.sys:
可以看到疑似句柄或者注册表键的字符串;
Lab10-03.exe:
一个网址,两个和sys
文件相近的字符串,和上一个文件的路径名;
4、过程分析;
用IDA
打开Lab10-03.exe文件;
从main()
函数开始看起,思路比较清晰,构建程序框架:
这就很显然了,创建一个叫做Process Helper
的服务,加载C:\\Windows\\System32\\Lab10-03.sys
的内核驱动;
如果创建成功了,就启动服务,加载Lab10-03
到内核,并且打开一个由ProcHer
驱动创建的内核设备驱动句柄\\\\.\\ProcHelper
;
再后就是DeviceIoControl
函数的调用,我们可以看见它的关键字段lpOutBuffer
和lpInBuffer
都是0,这是很不正常的,而且在dwIoControlCode
字段的值为0x0ABCDEF01
也是不正常的;
DeviceIoControl
将控制代码直接发送到指定的设备驱动程序,使相应的设备执行相应的操作。
既然知道是加载了内核驱动,我们重点是它做了什么坏事,看看DeviceIoControl
请求了啥,所以在此可以直接去看看Lab10-03.sys,一探究竟;
进入DriverEntry()
找到正确的跳转sub_10706
;
先看一下整体结构:
首先,我们可以看到IoCreateDevice()
创建了名为\\Device\\ProcHelper
的设备;
往下是如果创建成功,则进入;
成功跳转后是给DriverObject
偏移添加函数;
所以,我们先要去了解一下这个IoCreateDevice()
函数的DeviceObject
字段的意思;
翻译翻译:指向调用者的驱动程序对象的指针。每个驱动接收到一个指向它的驱动对象的指针,这个指针在驱动入口例程的一个参数中。WDM函数和筛选器驱动程序也在它们的添加设备例程中接收驱动程序对象指针。
所以,下一步搞清楚都是这些驱动程序都有哪些行为;
sub_10606;
IoCompleteRequest()
宏表示调用者已经完成了给定I/O请求的所有处理,并将给定的IRP返回给I/O管理器;
sub_10666;
IoGetCurrentProcess()
返回一个指向当前进程的指针;
IofCompleteRequest()
宏指示调用者已经完成了给定I/O请求的所有处理,并将给定的IRP返回给I/O管理器。
sub_10666
在驱动正确安装,服务正常启动后,处理DeviceIoControl
的请求;
我们注意到,IoGetCurrentProcess()
返回的是当前进程是指针,它返回的其实是EPROCESS
(进程对象),每一个进程都有一个EPROCESS
;
想了解EPROCESS
之前,先简短的总结一下TEB
、PEB
和TIB
;
TEB
:(Thread Environment Block,线程环境块),进程中的每个线程都有自己的一个TEB
。一个进程的所有TEB
都以堆栈的方式,存放在从虚拟地址的某一个地方,每4KB为一个完整的TEB,向下扩展;在用户模式下,当前线程的TEB位于独立的4KB段,可通过CPU的FS寄存器来访问该段,一般存储在fs:[0x00]
,还有一个自引用fs:[0x18]
;
TIB
:(Thread Information Block,线程信息块),是TEB
的第一个成员;
PEB
:(Process Environment Block,进程环境块)存放进程信息,每个进程都有自己的PEB信息,位于用户地址空间。我们经常看见的fs:[0x30]
就是用来访问PEB
结构体的地址;
因为这两个结构太长且内容庞大这里就不赘述了,至于关于TEB
和PEB
结构的更多细节,推荐火哥整理的内核结构网站;
首先这里要说到的是IoGetCurrentProcess()
函数,它返回调用DeviceIoControl
进程的EPROCESS
结构,然后访问[+88]
处偏移量,然后再访问[+8C]
处的;
关于此题在火哥整理的内核结构网站中找到的Win7中的结构跟书后答案有所区别,最后是找到了86版本的XP系统的还原了书中对于_EPROCESS
结果的样子;
至于具体如何,还是建议使用WinDbg
在当前系统中查找;
以下关于EPROCESS
截图默认为XP系统
在EPROCESS
结构的第一个叫做进程控制块的_KPROCESS
;偏移0x88
就在其中;
点进去后我们发现这是一个双向链表,[+8c]
其实指的是下一项的指针;
回到sub_10666
,我们可以看到:
看着有点眼晕,直接上图吧;
就是这么个意思,双向链表删除节点操作,但是这里只是解除链接并不删除;
当操作系统正常运行时,中间那个进程就被Rootkit
隐藏了,当OS
遍历进程链表时,隐藏进程总是被跳过;
在这里,或许有萌新会疑惑,隐藏后为什么不影响运行;
这里要解释的是,进程只是进程中运行线程的容器,线程被调度到CPU上运行;只要线程合理的占用操作系统,它就会被调度,所以进程也会继续正常运行;
sub_1062A;
删除了一个名为\DosDevices\ProcHelper
的符号链接,这个符号链接可以给用户态提供访问;
看完上面三个功能,程序走向了最后;
创建了一个名为\DosDevices\ProcHelper
的符号链接,如果创建成功则删除设备驱动;
大体的了解过Lab10-03.sys
做了什么后,我们返回Lab10-03.exe
看一看后续的代码;
接着从OleInitiallize
往下看;
我们知道这是一个COM
函数,一直往下,是一个CoCreateInstance
函数;
在MSDN中,它的描述是:用于创建COM对象;
将网址压入栈中;
然后打开网址,然后睡眠30秒;
这里就跟Lab7-2
处是一样的,所以就不再赘述了;
1、这个程序做了些什么?
加载驱动,每隔30秒弹出一个广告。这个驱动通过系统链表中摘除进程环境块(PEB),来隐藏进程;
2、一旦程序运行,你怎样停止它?
重启,无它;
3、它的内核组件做了什么操作?
内核组件负责响应,从进程链表中摘除进程的DeviceIoControl
请求;
这章节是讲WinDbg
的,可惜的是我的WinDbg
并没有配好,而且用IDA
还能解决;
至于没有用OD
那是因为OD
是R3层的调试器,内核只能用R0层的;
挖个坑吧,写完这本书就开始写内核的帖子;
欢迎大佬指正,感谢!
if
(OpenSCManagerA(...)){
if
(CreateServiceA(...) || h1
=
OpenServiceA(...) !
=
0
){
StartService(...);
if
(h1){
ControlService(...);
}
}
}
if
(OpenSCManagerA(...)){
if
(CreateServiceA(...) || h1
=
OpenServiceA(...) !
=
0
){
StartService(...);
if
(h1){
ControlService(...);
}
}
}
sub_10906(...){
DriverObject
-
>DriverUnload
=
(
*
)sub_10486;
return
0
;
}
sub_10906(...){
DriverObject
-
>DriverUnload
=
(
*
)sub_10486;
return
0
;
}
sub(...){
RtlCreteRegistryKey(...);
RtlCreteRegistryKey(...);
RtlCreteRegistryKey(...);
RtlCreteRegistryKey(...);
RtlWriteRegistryValue(...);
return
RtlWriteRegistryValue(...);
}
sub(...){
RtlCreteRegistryKey(...);
RtlCreteRegistryKey(...);
RtlCreteRegistryKey(...);
RtlCreteRegistryKey(...);
RtlWriteRegistryValue(...);
return
RtlWriteRegistryValue(...);
}
RtlCreteRegistryKey(...);
/
/
沿着给定的相对路径在注册表中添加一个键值对象。
RtlWriteRegistryValue(...);
/
/
调用方提供的数据沿着指定的相对路径在给定的值名处写入注册表。
RtlCreteRegistryKey(...);
/
/
沿着给定的相对路径在注册表中添加一个键值对象。
RtlWriteRegistryValue(...);
/
/
调用方提供的数据沿着指定的相对路径在给定的值名处写入注册表。
"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft"
;
"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft\\WindowsFirewall"
;
"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft\\WindowsFirewall\\DomainProfile"
;
"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft\\WindowsFirewall\\StandardProfile"
;
"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft"
;
"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft\\WindowsFirewall"
;
"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft\\WindowsFirewall\\DomainProfile"
;
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2021-4-20 19:23
被平头猿小哥编辑
,原因: