由于个人习惯原因,我现在热衷于在Linux平台上工作.所以没有像网上大多数文章描述的那样: 物理机作为工作机,然后开一个虚拟机作为调试机.像我的这种调试Windows内核的方式, 在网上实在是少见,因此,我也为此而付出了许多代价.诸多摸索之后,终于搞定.写下此文, 以供后来有像我这种工作方式的人以参考.
由于我使用的是Windows XP SP3,调试内核需要用到WDK工具套装, 点击这里从微软的官方网站下载. 这里应该下载适用于Windows XP的驱动工具,找到下面的位置:#1:完整的开发环境,包括编译环境,样例,工具以及帮助文档 Full Development Environment Build Environments Samples Tools Help (Documentation Collection) #2:调试工具,如WinDbg Debugging Tools for Windows #3.设备模拟框架(这里不推荐安装) Device Simulation Framework #4.Windows设备测试框架 Windows Device Testing Framework按照上图所选,然后就是一路Next下去...和漫长的等待安装完成...安装完成后, 测试是否安装成功什么的乱七八糟我就不管了,如果你以前没安装过,一般都不会出错. 另外需要注意的是,VHosht和VGuest都需要进行该WDK的安装.
cd c:\ attrib -s -h -r boot.ini notepad boot.ini这里,我们使用notepad打开了引导配置文件boot.ini,编辑该文件,使其看起来如下所示:
[boot loader] timeout=30 default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS [operating systems] multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional [DEBUG]" /noexecute=optin /fastdetect /Debug /debugport=com1 /baudrate=115200 multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /noexecute=optin /fastdetect粗体的那一行就是你需要添加的,不过你应根据你自己的情况修改/debugport选项,假如你是端口2,则应修改为/debugport=com2. 然后保存,并执行命令[attrib +s +h +r boot.ini]恢复boot.ini的原有属性.
#1.虚拟机调试虚拟机: "C:\Program Files\Debugging Tools for Windows (x86)\windbg.exe" -b -k com:port=com1,resets=0,reconnect P.S. #2.物理机调试虚拟机(这种情况下适用于Windows物理机调试虚拟Windows机): "C:\Program Files\Debugging Tools for Windows (x86)\windbg.exe" -b -k com:pipe,port=\\.\pipe\dport,resets=0,reconnect启动后如下图所示:
srv*C:\WindbgSymbols*http://msdl.microsoft.com/download/symbols如下图所示:
************************************************************************* *** *** *** *** *** Your debugger is not using the correct symbols *** *** *** *** In order for this command to work properly, your symbol path *** *** must point to .pdb files that have full type information. *** *** *** *** Certain .pdb files (such as the public OS symbols) do not *** *** contain the required information. Contact the group that *** *** provided you with these symbols if you need this command to *** *** work. *** *** *** *** Type referenced: nt!_OBJECT_DIRECTORY *** *** *** ************************************************************************* Cannot find _OBJECT_DIRECTORY type. *** ERROR: Module load completed but symbols could not be loaded for vmmemctl.sys ... Device object ProcHelper not found WARNING: Whitespace at end of path element请再手动执行一次.reload命令,然后等待一段时间,使WinDbg可以下载符号.
DriverObject= dword ptr 8 RegistryPath= dword ptr 0Ch mov edi, edi push ebp mov ebp, esp call sub_10794 pop ebp jmp sub_10706 DriverEntry endp一般的我们不需要关注其中的CALL只需要关注其跳转地址即可,双击jmp处的地址,跳转到这里:
SymbolicLinkName= UNICODE_STRING ptr -14h DestinationString= UNICODE_STRING ptr -0Ch DeviceObject= dword ptr -4 DriverObject= dword ptr 8 mov edi, edi push ebp mov ebp, esp sub esp, 14h and [ebp+DeviceObject], 0 push esi push edi mov edi, ds:RtlInitUnicodeString push offset aDeviceProchelp ; "\\Device\\ProcHelper" lea eax, [ebp+DestinationString] push eax ; DestinationString call edi ; RtlInitUnicodeString mov esi, [ebp+DriverObject] lea eax, [ebp+DeviceObject] push eax ; DeviceObject push 0 ; Exclusive push 100h ; DeviceCharacteristics push 22h ; DeviceType lea eax, [ebp+DestinationString] push eax ; DeviceName push 0 ; DeviceExtensionSize push esi ; DriverObject call ds:IoCreateDevice test eax, eax jl short loc_10789这基本上就是所有我们需要关注的信息了,该驱动会创建一个设备,设备名称为ProcHelper,我们需要调试的就是该设备, 看看该设备做了什么.调试该设备需要通过VHost调试VGuest来实现.这就是我们接下来要做的.
kd> !devobj ProcHelper Device object (81fcf310) is for: ProcHelper*** ERROR: Module load completed but symbols could not be loaded for Lab10-03.sys \Driver\Process Helper DriverObject 82141be0 Current Irp 00000000 RefCount 1 Type 00000022 Flags 00000040 Dacl e144f4d4 DevExt 00000000 DevObjExt 81fcf3c8 ExtensionFlags (0000000000) Device queue is not busy.从第四行,我们可得知驱动对象的基地址为0x82141be0.然后我们借助该驱动的基地址获取该驱动的详细信息:
kd> dt nt!_DRIVER_OBJECT 82141be0 +0x000 Type : 0n4 +0x002 Size : 0n168 +0x004 DeviceObject : 0x81fcf310 _DEVICE_OBJECT +0x008 Flags : 0x12 +0x00c DriverStart : 0xf8cc6000 Void +0x010 DriverSize : 0xe00 +0x014 DriverSection : 0x8208fe60 Void +0x018 DriverExtension : 0x82141c88 _DRIVER_EXTENSION +0x01c DriverName : _UNICODE_STRING "\Driver\Process Helper" +0x024 HardwareDatabase : 0x80670ae0 _UNICODE_STRING "\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM" +0x028 FastIoDispatch : (null) +0x02c DriverInit : 0xf8cc67cd long +0 +0x030 DriverStartIo : (null) +0x034 DriverUnload : 0xf8cc662a void +0 +0x038 MajorFunction : [28] 0xf8cc6606 long +0可以看到我们可以得到驱动的起始地址,大小,名称,初始化,卸载以及主函数. 首先为了结合IDA进行分析,可以通过其实地址来在IDA中设置基准地址,具体为依次[Edit/Segments/Rebase Program/Value], 将Values设置为0xf8cc6000即可.这样IDA中的地址就和WinDbg中的地址显示一致了,方便大家调试. 我们主要关注主函数部分,驱动的主函数由如下几种类型(位于Path2DDK\WinDDk\version\inc\ddk\wdm.h):
// Define the major function codes for IRPs. #define IRP_MJ_CREATE 0x00 #define IRP_MJ_CREATE_NAMED_PIPE 0x01 #define IRP_MJ_CLOSE 0x02 #define IRP_MJ_READ 0x03 #define IRP_MJ_WRITE 0x04 #define IRP_MJ_QUERY_INFORMATION 0x05 #define IRP_MJ_SET_INFORMATION 0x06 #define IRP_MJ_QUERY_EA 0x07 #define IRP_MJ_SET_EA 0x08 #define IRP_MJ_FLUSH_BUFFERS 0x09 #define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a #define IRP_MJ_SET_VOLUME_INFORMATION 0x0b #define IRP_MJ_DIRECTORY_CONTROL 0x0c #define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d #define IRP_MJ_DEVICE_CONTROL 0x0e #define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f #define IRP_MJ_SHUTDOWN 0x10 #define IRP_MJ_LOCK_CONTROL 0x11 #define IRP_MJ_CLEANUP 0x12 #define IRP_MJ_CREATE_MAILSLOT 0x13 #define IRP_MJ_QUERY_SECURITY 0x14 #define IRP_MJ_SET_SECURITY 0x15 #define IRP_MJ_POWER 0x16 #define IRP_MJ_SYSTEM_CONTROL 0x17 #define IRP_MJ_DEVICE_CHANGE 0x18 #define IRP_MJ_QUERY_QUOTA 0x19 #define IRP_MJ_SET_QUOTA 0x1a #define IRP_MJ_PNP 0x1b #define IRP_MJ_PNP_POWER IRP_MJ_PNP // Obsolete.... #define IRP_MJ_MAXIMUM_FUNCTION 0x1b一共由0x1C个,所以我们在WinDbg中用如下命令显示:
kd> dd 82141be0+0x038 L1C 82141c18 f8cc6606 804f354a f8cc6606 804f354a 82141c28 804f354a 804f354a 804f354a 804f354a 82141c38 804f354a 804f354a 804f354a 804f354a 82141c48 804f354a 804f354a f8cc6666 804f354a 82141c58 804f354a 804f354a 804f354a 804f354a 82141c68 804f354a 804f354a 804f354a 804f354a 82141c78 804f354a 804f354a 804f354a 804f354a每一个对应于一个主函数,我们主要关注偏移0xE处的管理设备的函数(IRP_MJ_DEVICE_CONTROL),由上面的结果可以得知, 该函数的地址为0xf8cc6666,在IDA中按下g,输入f8cc6666转到该函数处:
Irp= dword ptr 0Ch mov edi, edi push ebp mov ebp, esp call ds:IoGetCurrentProcess mov ecx, [eax+8Ch] add eax, 88h mov edx, [eax] mov [ecx], edx mov ecx, [eax] mov eax, [eax+4] mov [ecx+4], eax mov ecx, [ebp+Irp] ; Irp and dword ptr [ecx+18h], 0 and dword ptr [ecx+1Ch], 0 xor dl, dl ; PriorityBoost call ds:IofCompleteRequest xor eax, eax pop ebp retn 8 sub_F8CC6666 endp该段代码执行了进程的脱链操作,类似与链表中删去一个节点的操作. 进程管理器会通过一个进程链表来检测进程,该恶意程序通过驱动级代码将其自身进程从进程链表中剔除掉, 进而可以比较彻底的隐藏自身.