配置WinDbg双机调试。
- 下载Windbg(WDK)。
- 事实上你自己的win10上应该会自带。
- 配置WinXP虚拟机的boot.ini
- 改成如图所示的样子
- 修改主机上Windb的属性。
修改成如图所示:
- 启动WinXP选择调试状态,启动Windbg即可开始调试。
7.1 Lab 10-1
分析
首先,使用一些静态分析技术来检查这个可执行文件。
像是有什么窗口之类的。
,这个程序可能创建了一个服务。
,Lab 10-01.sys可能包含了这个服务的代码。
分析该Lab 10-01.sys文件,发现导入表只有三个函数,第一个函数是KeTickCount,剩下两个函数是RtlCreateRegistry和RtlWriteRegistryValue,这告诉我们驱动可能访问了注册表。
这些字符串看起来很想是注册表键值,然后开头的\Registry\Machine却比较怪异,并不像是诸如HKLM此类的常见注册表根键。当从内核态访问列表时,前缀\Registry\Machine等同于用户态程序访问的HKEY_LOCAL_MACHINE。EnableFirewall置为0,表示禁用Windows XP自带的防火墙。
由于这些字符串预示着恶意代码写了注册表,所以我们打开procmon来验证我们的假设。procmon中显示有一些读注册表的调用,但是仅有一个写注册表的调用:RegSetValue写了HKLM\SOFTWARE\Policies\Microsoft\Cryptography\RNG\Seed键值。
这个注册表键值一直改变,对于恶意代码分析毫无意义,但是因为涉及到内核代码,我们需要确保驱动没有秘密地修改注册表。接下来我们打开执行文件,移动到主函数,发现只有四个函数调用。
首先,它在①调用了OpenSCMangagerA获取服务管理器的句柄,然后在②调用CreateServiceA,创建一个名为Lab 10-01的服务。CreateServiceA调用告诉我们服务使用了"C:\Windows\System32\Lab10-01.sys"中的代码,服务类型为3,意味着这个文件将被加载到内核。(好奇怪,没看到3)
如果CreateServiceA调用失败,代码会使用相同的服务名调用OpenServiceA。
如果因为服务已经存在而导致调用CreateServiceA失败,这将打开Lab 10-01服务的句柄。
接下来,程序将调用StartServiceA来启动服务,最后u,调用ControlService。ControlService的第二个参数是发送控制消息的类型。本例中。它的值是1,查询文档,我们发现他是SERVICE_CONTROL_STOP,这将会卸载驱动,并调用驱动的卸载函数。
使用IDA Pro查看 Lab 10-01.sys
在试图使用WinDbg分析这个驱动之前,我们可以用IDA Pro打开这个驱动,来检查它的DriverEntry函数。当打开驱动文件,移动到它的入口时,看到如下代码。
这个函数是驱动的入口点,但是它不是DriverEntry函数。编译器在DriverEntry的周围插入封装代码。真正的DriverEntry函数位于sub_10906。
函数的主体部分似乎将一个偏移量转移到另一个内存位置。除此之外,它没有进行任何函数调用,也没有与系统进行交互。
使用WinDbg分析Lab10-01.sys
现在我们可以使用WinDbg检查一下Lab10-01.sys,来查看调用ControlService卸载Lab10-01.sys时会发生什么事。用户空间可执行的代码加载Lab10-01.sys,然后立即卸载它。如果我们在运行恶意可执行程序之前使用内核调试器,因为此时驱动还未在内存中,所以我们还不能检查它。但是,如果我们等待应用程序运行完成,那时驱动又已经从内存中卸载了。
为了在Lab10-01.sys载入内存后,使用WinDbg分析它,在虚拟机中,我们将可执行程序载入到WinDbg中。使用下列命令,我们在驱动加载和卸载之间设置一个断点(在ControlService调用上):
使用OllyDbg下一个断点。
启动WinDbg进行调试,输入命令来获取驱动对象。
!drvobj Lab10-01
命令!drvobj输出给我们提供了驱动对象的地址,因为在设备对象列表中没有设备列出,所有以我们明白这个驱动没有供用户空间中应用程序访问的设备。
可以使用 !object \Driver 命令获取当前内核中驱动对象列表。
获取了对驱动对象的地址,就可以使用dt命令查看它。
dt _DRIVER_OBJECT 8a1158b0
我们尝试确定驱动卸载时调用的函数——偏移量为0x034的信息DriverUnload。然后,我们使用如下命令设置一个断点。
bp 0xba799486
然后恢复运行。
文件被加载到ba799000。从上文中,可以得知,卸载函数位于0xba799486。我们从0xba799486减去0xba799000得到偏移量。然后在IDA中跳转到卸载函数。例如,如果IDA中架子基地址0性00000,那么我们在IDA Pro中地址0星00486处找到卸载函数。
继续运行会触发断点,单步运行可以看到调用了nt!RtlCreateRegistryKey
NTSTATUS RtlWriteRegistryValue(
_In_ ULONG RelativeTo,
_In_ PCWSTR Path,
_In_ PCWSTR ValueName,
_In_ ULONG ValueType,
_In_opt_ PVOID ValueData,
_In_ ULONG ValueLength
);
- 这个程序是否直接修改了注册表(使用procmon来检查)?
唯一的写入是写入一个随机值,其他的探测不到。
- 用户态的程序调用了ControlService函数,你是否能够使用WinDbg设置一个断点,以此来观察由ControlService的调用导致内核执行了怎么样的操作?
使用OllyDbg将可执行文件下断点到函数前,然后使用另外一个操作系统的WinDbg,使用!drvobj命令获得驱动设备的句柄,它包含一个卸载函数的指针。接下来,在驱动的卸载函数上设置一个断点。重启可执行文件之后,断点将会被出发。
- 这个程序做了些什么?
修改了注册表,并关闭了防火墙。
7.2 Lab10-02
分析
查看文件的导入表。
这几个函数告诉我们这个程序创建并启动衣蛾服务。因为程序也调用了CreateFile和WriteFile,说明这个程序可能创建一个文件,而LoadResource和FindResource则说明了该程序对Lab10-02.exe的资源节做了某些处理。
认识到程序可能对资源节进行了操作,我们使用Ressource Hacker来检查资源节。我们可以发现在资源节中藏了另外的一个PE头文件。
使用ProcessMon进行监视发现,该程序创建了一个文件,但我们在当前目录下却并不能查找到该文件。
查找Rootkit
为了进一步的调查,我们要检查我们的内核驱动是否被加载。我们使用sc命令,检查运行我们内核驱动程序的服务状态。
我们查询名为486 WS Driver的服务,它在CreateService中被指定。我们看到这个服务仍然运行②,这告诉我们在内核代码中。奇怪的是仍然运行,但是它没有在硬盘上。现在为了确定怎么回事,我们使用WinDbg机型调试。使用lm命令,查看驱动是否被真正加载。我们看到一个与Lab10-02.exe创建文件名匹配的条目:
现在,我们确定了文件名为Mlwx486.sys的驱动被载入到内存,但是文件没有在硬盘上显示,这暗示了它可能是一个Rookit。
接下来,我们检查SSDT的所有修改项。
我们可以看到有一处条目所在的内存位置很明显位与ntoskrn1模块的范围之外,位与Mlwx486.sys驱动内。为了确定替换了哪个函数,我们回复虚拟机安装Rootkit之前的状态,以便于我们查看存储在SSDT中的哪个函数被覆盖了。
分析函数
- 找到SSDT地址和NtQueryDirectoryFile地址
- 遍历SSDT找到NtQueryDirectoryFile项
- 修改NtQueryDirectoryFile项修改为FakeNtQueryDirectoryFile
sub_10486函数:循环将文件名与”Mlxw“比较,如果匹配就修改上一个的NextEntryOffset跳过匹配文件
返回字符串代表函数的地址
PVOID MmGetSystemRoutineAddress(
PUNICODE_STRING SystemRoutineName
);
根据传入的目录句柄返回下面的各种文件信息
__kernel_entry NTSYSCALLAPI NTSTATUS NtQueryDirectoryFile(
HANDLE FileHandle,
HANDLE Event,
PIO_APC_ROUTINE ApcRoutine,
PVOID ApcContext,
PIO_STATUS_BLOCK IoStatusBlock,
PVOID FileInformation,
ULONG Length,
FILE_INFORMATION_CLASS FileInformationClass,
BOOLEAN ReturnSingleEntry,
PUNICODE_STRING FileName,
BOOLEAN RestartScan
);
结构体
typedef struct _FILE_BOTH_DIR_INFORMATION {
ULONG NextEntryOffset;//指向下一个结构体
ULONG FileIndex;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG FileNameLength;
ULONG EaSize;
CCHAR ShortNameLength;
WCHAR ShortName[12];
WCHAR FileName[1];
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
- 这个程序创建文件了吗?它创建了什么文件?
创建了文件,C:\Windows\System32\Mlwx486.sys。
- 这个程序有内核组件吗?
有内核组件,就在Lab10-02.exe的资源节中。
- 这个程序做了些什么?
通过修改SSDT的NtQueryDirectoryFile为FakeNtQueryDirectoryFIle,来隐藏文件。
7.3 Lab10-03
- 这个程序做了些什么?
· 这个程序首先依然是安装驱动,驱动位置在C:\Windows\System32\Lab10-03.sys
然后接着打开设备对象:
通过IO通信给驱动发送了控制码0ABCDEF01h,接下来的几个函数初始化了COM对象,并在每30s执行一次某个函数,这个函数有一个参数是这个网址字符串,经过在虚拟机运行程序可知,这是每30s弹出一个广告网页。
加载驱动,每30秒弹出一次广告。驱动从系统_EPROCESS中摘除进程
- 一旦程序运行,怎么停止?
重启系统。
- 它的内核组件做了什么操作?
内核组件负责响应,从进程链表中摘除进程的DeviceIoControl请求。