由于个人习惯原因,我现在热衷于在Linux平台上工作.所以没有像网上大多数文章描述的那样: 物理机作为工作机,然后开一个虚拟机作为调试机.像我的这种调试Windows内核的方式, 在网上实在是少见,因此,我也为此而付出了许多代价.诸多摸索之后,终于搞定.写下此文, 以供后来有像我这种工作方式的人以参考.
由于我使用的是Windows XP SP3,调试内核需要用到WDK工具套装, 点击这里从微软的官方网站下载. 这里应该下载适用于Windows XP的驱动工具,找到下面的位置:
下载得到一个GRMWDK_EN_7600_1.ISO的文件,你可以解压也可以使用UltraISO来加载为虚拟光驱,我推荐使用后面的方式.
加载为光驱后,双击光驱盘标就会出现下图:
然后稍后就会弹出如下界面:
包含的组件有四大部分:
#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的安装.
选择序列端口(Serial Port),然后Next,弹出Serial Port Type界面:
这里我们需要选择Output to socket选项,然后Next,弹出Specify Socket界面:
在这里我们需要声明命名管道的名称,这是任意的,这里我取名为dport(言外之意,debug port),
但是,你需要注意的是管道的根路径,对于linux,管道路径为/tmp/,而在Windows下管道路径为\\.\pipe\.
因此如果这里应该使用/tmp/dport,如果是windows则需要使用\\.\pipe\dport.
另外对于VHost需要选择为From Client to A Virtual Machine.然后可以点击Finish按钮完成配置.
接着你就会回到最初的设置界面:
注意勾选Yield CPU on poll,然后查看整体设置是否正确.
另外需要注意的是,这里用到的管道序列端口为1,如上图标记2所示,序列端口号不一定必须为1,
但至少要保证VHost和VGuest之间的序列端口号是一致的,端口号一般会在Serial Port后面有一个数字,
这就是端口号的ID,如果没有,则为1.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启动后如下图所示:
然后启动VGuest,首先会进入下面的界面:
选择第一项(debugger enabled),然后启动,进入VHost,稍等片刻,你就会发现Windbg已经输出了如下连接信息:
先来解决这个错误: *** ERROR: Symbol file could not be found,这个错误是由于没有设置符号路径而造成的.
有两种方式设置符号搜索路径,一种是设置环境变量_NT_SYMBOL_PATH,
另一种就是依次在Windbg中[File/Symbol File Path]打开符号路径设置窗口.
符号路径的格式为[srv*符号存储路径*http://msdl.microsoft.com/download/symbols],多个符号路径之间用分号隔开.
这里我的符号路径设置为C:\WindbgSymbols,然后打开路径设置窗口输入如下路径:
srv*C:\WindbgSymbols*http://msdl.microsoft.com/download/symbols如下图所示:
注意勾选Reload选项.如果你遇到下面的问题Your debugger is not using the correct 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可以下载符号.
其中驱动文件(sys)需要放到C:/WINDOWS/SYSTEM32目录下面.
我们首先使用IDA打开exe程序来简单看一下:
在EXE执行之前会创建一个服务,服务显示名称为Process Helper,
服务实体为驱动Lab10-03.sys(需要你自己拷贝到VGuest的system32目录去),然后启动服务,其他的打开浏览器等动作,
这里就不分析了,因为这里我们主要调试的就是Lab10-03.sys这个驱动文件.在该服务启动后,exe才会继续执行,
exe的行为是打开浏览器转到广告页面,如果你手动关闭浏览器,等待几秒后就会自动再次打开.
而你是无法在进程管理器中找到进程实体的,这个正是该驱动所做的事情.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该段代码执行了进程的脱链操作,类似与链表中删去一个节点的操作. 进程管理器会通过一个进程链表来检测进程,该恶意程序通过驱动级代码将其自身进程从进程链表中剔除掉, 进而可以比较彻底的隐藏自身.