Lab 10-1
一些准备工作
首先就是配置内核调试的环境
我这里用的是VirtualBox而不是书上的VMware
所以这里可能会有一些的不同
书上说的是这样的
与用户态调试不同,内核调试需要一些初始化配置。首先需要配置虚拟操作系统并开启内核调试,然后配置VMware使虚拟机和宿主操作系统之间有一条虚拟化的串口,同时还应该配置宿主操作系统中的WinDbg
我们按照书上的做法开始,先配置C:\boot.ini
这里要把隐藏文件和隐藏系统配置文件取消勾选
然后我们打开这个boot.ini
注意改之前记得备份系统,防止起不来
然后我们开始修改boot.ini
文件
[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operation systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /noexecute=optin /fastdetect
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional with Kernel Debugging" /noexecute=optin /fastdetect /debug /debugport=COM1 /baudrate=115200
注意这里的COM1
这个端口,待会选的时候别选错了
然后这就是改变后的样子
然后下次重启虚拟机的时候,就会出现一个开启内核调试的选项给你选择
然后下面我们设置虚拟机和宿主机之间的那个虚拟连接,我这里是VritualBox,所以用VMware的同学照书做就行
选择这里
然后这里开启串口
开机的时候我们还开启不了,我们先关闭虚拟机
关了之后就可以修改了
然后我们这样设置,注意那个连接至
不要打勾,不然启动虚拟机就会报错(如果你没报错可以点了试试哈哈哈)
然后进去就可以发现这个了
然后发现我这没激活的系统已经进不去了(无语),我还是先激活一下这个系统算了
然后这里放一个XP
的OEM
激活工具,省的找了,直接运行就行了
如果你打算使用简单一点只包括WinDbg
的东西,用CSDN
那个(这个好像只支持xp和win7)
如果没积分的话,可以试试龟速的百度云这个我下载好上传的连接
资源中有两个文件:64位系统6.12版本和32位系统6.11版本
解压后是这样的
我们这是32
位的系统,我们安装x86
的就行
打开之后就是这样的
然后安装就行了
这里有个问题,笔者以前一直以为这个WinDbg
是要安装在虚拟机上的,然后今天有时间好好查了一下,这个是要安装在宿主机上,也就是你运行虚拟机那个机器上(这就尴尬了)
现在我bing了一下,要安装Win10
的WinDbg
的话,你要先安装WDK
,要安装WDK
的话,你要安装这个
你必须先安装这个studio
这个东西,慢慢装吧,继续链接在这里 安装WDK
然后我们安装这个studio
我就点了一个Python
的开发还有一个Linux C
的开发,就两个包,已经要下8G
的内容,我也是很无语,这里需要选择这个使用 C++ 的桌面开发
,这个会包含这个WDK
东西
我们慢慢装一下这个东西吧,然后我们装这个Windbg
然后这里我们选择这个
就行了(默认是全部都选的)
然后在开始里面查找一下,出现这个就说明安装成功了
到这里,你已经做完了安装WinDbg
的任务,现在我们开始设置VirtualBox
我这里使用的Version 5.2.6
的最新版(2018-1-22),英文的
这里我们找到这个Serial Ports
这个东西,然后Enable Serial Port
这里点上勾勾,然后设置这个Port Mode
为Host Pipe
,然后确保这个Connect to existing pipe/socket
这个没有打勾,其他的都不用配置
然后我们开启虚拟机
然后我们下载Windows Symbol
去,我这里下载了一份,是SO的大神告诉我哪里下载的。。。
然后双击运行就行了
我是安装在了C
盘
然后我们开始调试,我们设置好Windbg
之后,重启虚拟机然后等着就行了
出现这个就说明脸上了,然后我们开始设置Symbol
我的做法是这样的,其他做法也可以,只要你可以调试就行了,我找到Windbg
的文件夹,然后把这个程序创建一个桌面快捷方式,然后我们打开这个快捷方式
把这里这个目标改为这个(Windbg
的路径一般不需要改,只需要增加后面那些参数就行了,记得结合你的虚拟机设置来稍微改动一下)
"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe" -b -k com:port=\\.\pipe\com1,baud=115200,pipe -y C:\Windows\Symbols
也就是在原来的
"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe"
后面加上
-b -k com:port=\\.\pipe\com1,baud=115200,pipe -y C:\Windows\Symbols
记得这两个字符串之间要有个空格
然后我们双击之后就会自动出来那个窗口了
这样就说明已经处于调试状态了
我这个Windbg
并不会显示多余的信息,我们可以安装下面的这个教程拷出来的图片操作,输入g
就继续
然后这样我们就配置好了WindowsXP Kernel
调试环境
问题
1.这个程序是否直接修改了注册表(使用procmon来检查)?
解答: 现在我们调试器准备好之后,开始准备运行这个病毒,然后之前先做一下系统的备份
在运行调试器的时候,如果你不需要调试中断了,输入一个g
就可以了
然后如果你突然需要终端介入了,然后点一下这里
然后就会产生一个中断,系统这时候就会暂停运行了
然后我们把这两个文件拖到C:\Windows\System32
下面
也就是Lab10-01
的两个实验样本(记得在这之前做备份)
复制完之后,我们按照书上的一些办法,执行一些基本的静态分析
然后我们可以发现第一个DLL
里面导入了这么几个函数
ControlService
CreateServiceA
OpenSCManagerA
OpenServiceA
StartServiceA
都是和服务控制有关的,创建一个服务,控制一个服务,启动一个服务
然后第二个DLL
导入的函数是这些
有点多,其中毕竟要注意的是
WriteFile
说明会有一个写入文件的操作
然后我们打开IDA
来看看字符串有哪些
这个程序就操作了这么几个函数就完了,没有特别复杂的逻辑
然后我们看看字符串都有些什么东西
我们注意到最后那里有个C:\\Windows\\System32\\Lab10-01.sys
这个路径,说明这个程序会去调用这个sys
文件
然后我们研究一下这个sys
文件是啥样的
这个文件只导入了这么几个函数
按照书上的说法,第一个函数KeTickCount
是所有驱动都会包含的一个函数,这个可以忽略
然后第二个RtlCreateRegistryKey
和第三个RtlWriteRegistryValue
,看到这个Registry
我们就大概知道这个操作是和注册表有关的操作,然后一个是Create
一个是Write
,所以这个是创建和写入注册表的操作
然后打开字符串看看都有哪些字符串
然后发现有个小Error
这个是缺少了一个Symbol
,然后看来我们还要装一个,不行,装了还是不行Symbol
这个东西
这里看不见书上那些字符串,过~
所以这里我们缺个symbol
,sys
文件没法看到具体的字符串是啥样的
然后下面我们开始动态分析,还原虚拟机,去除装的那个Symbol
执行之后,用procmon
检查操作,会发现这么几个操作
这里有个调用恶意程序的操作,放大之后就是这样的,一个QueryOpen
还有这个
有个操作就是CreateFile
然后如果我们设置筛选条件为Process Name
= Lab10-01.exe
的话,我们就可以发现这些东西
这里显示我们运行这个Lab10-01.exe
之后,起了一个线程(Thread Create
),然后就是加载了一下DLL
,然后创建了一个文件在C:\WINDOWS\system32\Lab10-01.EXE-xxxxx.pf
这里有个RegOpenKey
,然后打开了这个键
这个的执行结果是
这里唯一改变了键的就只有这个操作
这里用RegSetValue
改变了一个加密用的种子值,但是这个对分析恶意代码没用
然后函数在这里调用了sys
驱动文件
这是我们动态分析能分析到的信息了,下面我们开始静态分析代码
然后这是整个函数的大概样子,其中有两个小+
号里面藏着一些隐藏的函数
我们进入main
函数看看
这里唯一的一个函数调用是OpenSCManagerA
,这个是
在指定的计算机上建立与服务控制管理器的连接,并打开指定的服务控制管理器数据库。
这是连接到服务管理的一个函数,然后我们继续分析
如果失败,返回的NULL
,然后这里用test
测试了返回值,test
指令是和and
类似的,如果返回的是NULL
,test
之后,结果为0
,则ZF
=1
,JNZ
不会跳转,继续执行,走红线,然后就返回了
如果没有失败,我们继续往下走
往下走就是这里,之类调用了CreateServiceA
这个函数,我们对应一下入参就是如下
SC_HANDLE WINAPI CreateService(
_In_ SC_HANDLE hSCManager = edi,
_In_ LPCTSTR lpServiceName = "Lab10-01",
_In_opt_ LPCTSTR lpDisplayName = "Lab10-01",
_In_ DWORD dwDesiredAccess = 0F01FFh,
_In_ DWORD dwServiceType = 1,
_In_ DWORD dwStartType = 3,
_In_ DWORD dwErrorControl = 1,
_In_opt_ LPCTSTR lpBinaryPathName = "C:\\Windows\\System32\\Lab10-01.sys",
_In_opt_ LPCTSTR lpLoadOrderGroup = 0,
_Out_opt_ LPDWORD lpdwTagId = 0,
_In_opt_ LPCTSTR lpDependencies = 0,
_In_opt_ LPCTSTR lpServiceStartName = 0,
_In_opt_ LPCTSTR lpPassword = 0
);
这里我们就关注这么几个点,首先是可以看出这个服务的名字就是Lab10-01
,然后访问权限dwDesiredAccess
是SERVICE_ALL_ACCESS
然后下一个是dwServiceType
是SERVICE_KERNEL_DRIVER
,这意味这这个文件会加载到内核里面
然后dwStartType
是SERVICE_DEMAND_START
,也就是会自动启动的一个服务
最后需要注意的就是lpBinaryPathName
,这个的值是"C:\\Windows\\System32\\Lab10-01.sys"
,意味着服务起来的时候会去加载这个二进制文件
一样的,这个函数调用失败会返回NULL
在这里,如果函数失败,返回NULL
,test
之后,ZF
=1
,然后JNZ
不会跳转,所以函数失败之后,会继续走红线,也就这写代码
这里程序会尝试打开这个服务通过OpenServiceA
来实现
SC_HANDLE WINAPI OpenService(
_In_ SC_HANDLE hSCManager = edi,
_In_ LPCTSTR lpServiceName = "Lab10-01",
_In_ DWORD dwDesiredAccess = 0F01FFh
);
如果上面这个OpenServiceA
失败,然后会继续通过StartServiceA
来开启这个服务
如果上面的函数调用失败,还是会继续通过ControlService
来将控制码发送给服务
然后对应一下就是
BOOL WINAPI ControlService(
_In_ SC_HANDLE hService = esi,
_In_ DWORD dwControl = 1,
_Out_ LPSERVICE_STATUS lpServiceStatus = eax
);
这里值得注意的是dwControl
,代表的意思SERVICE_CONTROL_STOP
,这个应该是停止这个服务,然后卸载这个驱动
(书上的原话)
所以这些操作,只要一个成功了,就会直接一个JZ
跳转然后返回了,如果失败,则继续往下尝试另外的函数
然后我们继续分析,这次我们分析Lab10-01.sys
我们找到驱动的入口点,然后查找
前面的
mov edi, edi
push ebp
mov ebp, esp
这些操作是在调用函数之前的初始化栈空间操作,如果你好好分析就会知道,这些操作会在栈上重新分配一些空间给要调用的函数,对我们分析来说,无关紧要
然后之后就调用了sub_10920
,我们进去看看
这里执行的是驱动函数的一些必要操作
然后我们退出来,看看下一个函数sub_10906
这个代码我们来解释一下
mov edi, edi
push ebp
mov ebp, esp
/* 上面那些都是函数调用之前的初始化栈操作 */
mov eax, [ebp + arg_0] // 将入参的第一个值也就是文件名赋值给eax
mov dword ptr [eax+34h], offset sub_10486 // 然后将sub_10486的地址赋值给eax+34h
xor eax, eax
pop ebp
retn 8
这里虽然没有调用任何函数,然后出现了一个函数的赋值sub_10486
,然后我们进去看看就会发现这个
这个函数会调用RtlCreateRegistryKey
这个内核函数来创建一个键在\Registry\Machine\SOFTWARE\Policies\Microsoft
,然后设置为0
然后下一个调用是
然后这里创建了一个键在\Registry\Machine\SOFTWARE\Policies\Microsoft\WindowsFire
,然后写入的值是wall
为0
再下面也是一样的\Registry\Machine\SOFTWARE\Policies\Microsoft\WindowsFire
,然后写入的信息是wall\StandarProfile
为0
然后下一个函数是RtlWriteRegistryValue
这里写入的值是\Registry\Machine\SOFTWARE\Policies\Microsoft\WindowsFire
入参是这么几个
这个函数在MSDN
中的定义是这样的
NTSTATUS RtlWriteRegistryValue(
_In_ ULONG RelativeTo = 0,
_In_ PCWSTR Path = ebx,
_In_ PCWSTR ValueName = edi,
_In_ ULONG ValueType = 4,
_In_opt_ PVOID ValueData = eax,
_In_ ULONG ValueLength = 4
);
这里我们查看MSDN
里面RelativeTo
的数据会发现
这里并没有任何是值显示,我们用IDA
替换一下
这里我们搜索之后会发现,这里只有这个,然后我们替换成这个RTL_REGISTRY_ABSOLUTE
这个的意思在MSDN
里面解释是这样
意思就是
路径是一个绝对的注册表路径
然后我们看看这个Path
的路径是多少,在这里的ebx
显示是这样的,他被赋值为aRegistryMach_2
之后就没被改变过了
所以这里的Path
的值就是\Registry\Machine\SOFTWARE\Policies\Microsoft\WindowsFire
,然后值为wall\DomainProfile, 0
然后ValueName
的值是edi
,在IDA
里查找最后改变edi
的值
最上面那里一个mov
赋值之后就没有改变过了,然后这个ValueName
的值是一个dw
类型的
也就是E
然后下面的ValueType
的值是4
,查看MSDN
这里有个REG
我们根据这个查查看
这里有很多的待选,我们再继续查找
这时候只有一个REG_DWORD_LITTLE_ENDIAN
符合,这个选项的意思是
最低地址处的最低有效字节的4字节数值。与REG_DWORD相同。
所以最后,总结一下这个函数会做什么
RtlWriteRegistryValue例程将调用方提供的数据以指定的值名称写入指定的相对路径。
这里会将\Registry\Machine\SOFTWARE\Policies\Microsoft\WindowsFire\wall\DomainProfile
的值写入成45h
2.用户态的程序调用了ControlService函数,你是否能够使用WinDbg设置一个断点,以此来观察由于ControlService的调用导致内核执行了怎样的操作?
解答: 我们开始刚刚一开始我们配置的Windbg
来就行内核调试,书上的原话我就不重复了
这里说一下,我的Windbg
连WinXP
虚拟机的内核的时候,必须重启才可以连进去,然后现在书上的做法是虚拟机里面用Windbg
来进行调试,加个断点之后,在来宿主物理机上,连Windbg
,这个方法我感觉是不靠谱的,我的想法是最好还是虚拟机启动的时候就连Windbg
,然后用g
命令执行,再用OD
来加载exe
文件加断点,省的windbg
界面不好调试,windbg
还是用于内核调试算了
我们先在IDA
里面找到这个ControlService
的地址,这个是系统函数
然后找到地址为401080
,我们在OD
里面加载断点
我们找到这个位置,然后加断点,然后执行到这里
命中断点之后,我们的OD
显示和Windbg
一样,比Windbg
更具有可读性
然后我们现在可以跳出虚拟机了,Windbg
发送break
然后暂停虚拟机的运行
然后我们继续跟着书走
我们已经暂停了虚拟机的执行,Windbg
在等待我们的输入
我们输入!drvobj Lab10-01
, 我在Win10
物理机上做是有点慢,慢慢的才会出结果来,等一下
这里显示的和书上的不一样,因为我们输入的是大写的Lab10-01
,然后我们试试小写的lab10-01
还是一样的无法得出和书中结果相似的结果,书中的结果如下
然后我们看这个WinDbg
的ERROR
,说的是symbols could not be loaded for Lab10-01
这个东西,我以前装的symbols
是WinXP SP3
的…
现在我换个Symbols
看看,换的是Windows 10 1701
的,试试看
现在我们更换这个Symbols
为安装在D
盘的Windows 10
的,但是这里显示是找不到这个Symbols
文件,这里保证路径没有打错哈哈哈
看来我们只能用WindowsXP SP3
那个版本的Symbol
了
所以这里不知道什么Symbol
的原因,无法显示和书中相似的结果,我们继续下面的操作
然后我们查找这个驱动对象的地址,在书本上是这里
然后我们查看我们出来的结果
这里我们试了大写和小写的,出来的驱动对象地址都是一样的896fa5f0
,不过我们先试试82636b418
这个地址
这里显示什么也没有,我们再试试我们得出的结果的那个地址896fa5f0
这里就和书上结论类似了,我们查看在0x034
偏移DriverUnload(驱动程序卸载)
的这个地方的值,这里有个DriverUnload
的函数,这个偏移的地址是0xf7ab9486
下面我们在这个地址这里设置一个断点,来查看这里发生了什么(这个断点用物理机上的WinDbg
设置)
bp 0xf7ab9486
注:每次运行地址都会不同
然后我们恢复虚拟机的运行,回到OD
中用OD
的F9
运行代码
注:这里是我第二次运行虚拟机,上面那个是昨天写的,今天重连pipe
所以重启了一下虚拟机,所以这个地址和前面的有不同,说明一下
这里我们的断点已经被命中,然后我们继续,这时候虚拟机也已经暂停了运行,下面我们执行单步调试,WinDbg
的单步调试对话框输入p
就可以了
这里显示了三行汇编代码,从这个结构和处理来说,我们刚刚WinDbg
的断点应该是断在了一个函数的入口第一行汇编代码的地方
mov edi, edi
push ebp
mov ebp, esp
这三段代码都是调用函数之后的栈预处理,然后接下来的代码整理之后就是如下的
push ecx
push ebx
push esi
move esi, dword ptr [Lab10_01+0x780 f(7a54780)]
push edi
xor edi, edi
push offset Lab10_01+0x6bc(f7a546bc)
push edi
mov dword ptr [ebp-4], edi
call esi
执行到这里的时候我们想知道这个调用esi
是什么函数,可以执行
u esi
然后就会显示了
这是一个nt!RtlCreateRegistryKey
调用,而这个函数的定义是如下
NTSTATUS RtlCreateRegistryKey(
_In_ ULONG RelativeTo,
_In_ PWSTR Path
);
有两个入参,第一个入参是edi
,即RelativeTo
=edi
,第二个是offset Lab10_01+0x6bc(f7a546bc)
,即Path
=offset Lab10_01+0x6bc(f7a546bc)
这里的RelativeTo
的值为0
,接下来我们看看Path
的值,这里我们先用da
命令
在地址f7a546bc
处的值的ASCII
是\
,当然,这样对查看单个地址上的数据比较有用,如果数据很多,要查看的话,我们用dc
命令
dc f7a546bc
这里的Path
的值就是\Registry\Machine\SOFTWARE\Policies\Microsoft
,然后后面就是全0
的截断符了
然后下面又是一个相同的调用nt!RtlCreateRegistryKey
,edi
的值没有改变还是0
,我们看看f7a54640
的值
push offset Lab10_01+0x640(f7a54640)
push edi
call esi
注意这里下面被圈黄的截断符(web渗透里面叫截断符’\00’,计算机里面叫文件结束符),这里我们得出的Path
是\Registry\Machine\SOFTWARE\Policies\Microsoft\WindowsFirewall
从字面意思应该可以大概知道这个注册表是关于防火墙的
继续下面的汇编代码
push offset Lab10_01+0x6a8(f7a545a8)
push edi
call esi
还是一个nt!RtlCreateRegistryKey
调用,和上面两个片段类似
edi
至今未变,我们看看Path
的值
这里还未出现文件结束符,说明这里还有数据没有显示出来,我们跳到最后显示这个地址继续显示
图上圈黄的是文件结束符的位置,然后我们可以得出这次调用的Path
的值是\Registry\Machine\SOFTWARE\Policies\Microsoft\WindowsFirewall\DomainProfile
然后继续往下看代码
mov ebx, offset Lab10_01+0x50c(f7a5450c)
push ebx
push edi
call esi
这里的esi
还是未变,我们看看Path
的值
这里依旧是一次未能显示全,我们往下跳
可得出Path
的值就是\Registry\Machine\SOFTWARE\Policies\Microsoft\WindowsFirewall\StandardProfile
所以应该是调用了四次nt!RtlCreateRegistryKey
(书中有误),然后依次创建的键路径在如下所示
\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
然后我们继续往下分析,下面的代码如下
mov esi, dword ptr [Lab10_01+0x788(f7a54788)]
push 4
lea eax,[ebp-4]
push eax
push 4
mov edi,offset Lab10_01+0x4ee(f7a544ee)
push edi
push offset Lab10_01+0x5a8(f7a545a8)
push 0
call esi
遇到一个call
函数了,这里的第一句已经改变了esi
,再也不是那个nt!RtlCreateRegistryKey
我们看看这个函数是什么
是nt!RtlWriteRegistryValue
,向注册表里面写入值的内核函数,这个函数定义如下
NTSTATUS RtlWriteRegistryValue(
_In_ ULONG RelativeTo,
_In_ PCWSTR Path,
_In_ PCWSTR ValueName,
_In_ ULONG ValueType,
_In_opt_ PVOID ValueData,
_In_ ULONG ValueLength
);
对应上面的汇编代码,我们可以得出下面对照
NTSTATUS RtlWriteRegistryValue(
_In_ ULONG RelativeTo = 0,
_In_ PCWSTR Path = offset Lab10_01+0x5a8(f7a545a8),
_In_ PCWSTR ValueName = offset Lab10_01+0x4ee(f7a544ee),
_In_ ULONG ValueType = 4,
_In_opt_ PVOID ValueData = eax,
_In_ ULONG ValueLength = 4
);
这里对我们来说有意义的参数,一个是Path
,这个Path
的值我们查查应该是\Registry\Machine\SOFTWARE\Policies\Microsoft\WindowsFirewall\DomainProfile
,然后ValueName
的值是EnableFirewall
然后下一个是ValueData
的值是,这里的eax
的值是[ebp-4]
的值,所以我们找ebp-4
的值
所以这个eax
的值是0
,这里的意思就是将EnableFirewall
这个的值设置为了0
,意义就是从内核禁止了Windows
的防火墙功能,然后我们继续往下分析看看
push 4
lea eax,[ebp-4]
push eax
push 4
push edi
push ebx
push 0
call esi
对照结构体,我们可以得出一下结论
NTSTATUS RtlWriteRegistryValue(
_In_ ULONG RelativeTo = 0,
_In_ PCWSTR Path = ebx,
_In_ PCWSTR ValueName = edi,
_In_ ULONG ValueType = 4,
_In_opt_ PVOID ValueData = eax,
_In_ ULONG ValueLength = 4
);
我们看看Path
的值应该是\Registry\Machine\SOFTWARE\Policies\Microsoft\WindowsFirewall\StandardProfile
然后ValueName
的值是EnableFirewall
最后是ValueData
的值是0
所以这里还是通过内核改变注册表\Registry\Machine\SOFTWARE\Policies\Microsoft\WindowsFirewall\StandardProfile
的键EnableFireawll
为0
来关闭防火墙
然后下面的代码就是退出和一些清理函数了
pop edi
pop esi
pop ebx
leave
然后书中还讲了如何计算偏移地址的方法,如何在静态分析中找到驱动卸载程序的地址等等,就不一一概述了
3.这个程序做了什么?
解答: 这个程序通过我们上面的分析可以知道,程序运行之后会启动一个服务,然后通过运行一个驱动创建修改注册表的值,来关闭防火墙
本文完