可以使用windbg+vmWare,通过虚拟COM口和NamePipe进行内核或用户级的双机调试,但是由于COM的速率最大限制只要115200,调试速度是相当慢的,如果你想使用如.dump这样转储命令,你可能需要等到几个小时或者更多.
VMKD Virtual Machine KD Extensions 这个工具就是解决关于这个问题的.
下载和使用方法: http://www.nynaeve.net/?page_id=168
原理介绍: http://www.nynaeve.net/?p=174
作者声明VMKD仅仅在 VMware Server 1.0.3 and 1.0.4上测试使用成功.
很不幸我使用的说VMWARE station 6.0, 我试了一下果然不支持.
这个东东挺不错的,于是调试和分析了一下kdvmware.sys和vmxpatch.dll,发现驱动是没有问题的,问题出现在vmxpatch.dll上,在这个DLL被vmxinject.exe注入到VMWARE-VMX.EXE中的DLLMain初始化过程中,它需要根据特征码找出VMWare后门处理函数的偏移和IsVmPriviligeMode这个函数的地址,然后内存补丁,Hook住这个后门处理函数. 结果是IsVmPriviligeMode这个函数的特征失效.
我的解决方法是:
使用UltraEdit打开vmxpatch.dll,
查找:
A1 00 FF FF FF FF FF FF FF FF 64 00 8B 00 0D 00
2C 00 00 00 00 00 00 00 8B 00 14 00 81 00 8B 00
82 00 FC 00 03 00 00 00 00 00 8B 00 15 00 FF FF
FF FF FF FF FF FF C1 00 E0 00 0C 00 56 00 8B 00
B4 00 10 00 54 00 C2 00 FF 00 FF 00 33 00 C9 00
85 00 F6 00 0F 00 94 00 C1 00 8A 00 C1 00 5E 00
C3 00
替换成:
56 00 E8 00 FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF C1 00 E0 00 0C 00 8B 00
B4 00 10 00 3C 00 C3 00 FF 00 FF 00 33 00 C9 00
85 00 F6 00 0F 00 94 00 C1 00 8A 00 FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF
保存.
重新使用vmxinject.exe把vmxpatch.dll这个文件注入VMWARE-VMX.EXE,然后在虚拟的操作系统中,net start kdvmware,然后打开windbg, Ctrl+Break,工作正常, J.
找Bug的过程中顺便分析了一下这个程序的一些原理,呵呵,原文长篇大论,看到我很头晕,有些意思,这里顺便简要说一下.
这个工具实现的基本原理,
1. 利用驱动kdvmware.sys在ring0中Hook住KDCOM.DLL中的调试通信协议函数, "KdSendPacket","KdReceivePacket","KdSave","KdRestore",绕开真正的COM通信,把发/送的数据直接复制到一个缓冲内存中, 利用VMWare的Backdoor的0x0B功能(osNotFound)在目标虚拟机操作系统和宿主机器之间进行通知,.
kdvmware.sys中实现这个通知的代码如下:
push ebx
.text:00011009 mov eax, 564D5868h ;“VMHx”
.text:0001100E xor ebx, ebx
.text:00011010 mov cx, 12h ;osNotFound
.text:00011014 mov dx, 5658h ; “Vx”
.text:00011018 in eax, dx ; 返回eax = 564D5868h
.text:00011019 pop ebx
.text:0001101A retn
2.一旦kdvmware.sys调用这个函数, vmxpatch.dll装好的钩子函数就会收到通知,在VMWARE-VMX.EXE的虚拟内存中查找内容为"KdVm Communications Back Channel Interf"...的地址(查找一次就可以,下次直接使用找好的地址),直接进行读写操作,这样就实现在host和vm之间的数据交换,然后vmxpatch.dll再处理一下和windbg的namepipe的数据转发, 这个整个windbg调试协议的通信就通了。windbg的读写namepipe的时候是不会限制速度的,没有疑问,这样肯定是比较COM口通信要快.
另外,关于kdvmware.sys在内核级Hook的修改内存的保护属性,它没有使用设置CR0.wp这个标志位的方法,而是另一种方法,这里顺便贴一下.
- /**
- 正规的方式来获取读的权限.
- */
- NTSTATUS EnableWritable(PVOID *dest,int len,PMDL *ppMdl, PVOID *lppv)
- {
- PMDL pMdl=NULL;
- pMdl = IoAllocateMdl(dest,len,FALSE,FALSE,NULL);
- *ppMdl =pMdl;
- if( !pMdl )
- {
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- MmProbeAndLockPages(pMdl,KernelMode,IoModifyAccess);
- PVOID pv = MmMapLockedPagesSpecifyCache(pMdl,KernelMode, MmCached,NULL,0,NormalPagePriority);
- *lppv = pv;
- if(!pv)
- {
- IoFreeMdl(pMdl);
- return STATUS_ACCESS_VIOLATION;
- }
- NTSTATUS status = MmProtectMdlSystemAddress(pMdl,PAGE_EXECUTE_READWRITE);
- if(status!=STATUS_SUCCESS)
- {
- MmUnmapLockedPages(pv,pMdl);
- MmUnlockPages(pMdl);
- IoFreeMdl(pMdl);
- }
- return status;
- }
- /**
- 正规的方式来关闭写的权限
- */
- NTSTATUS __stdcall DisableWritable(PMDL pMdl,PVOID lpv)
- {
- if(lpv)
- {
- MmUnmapLockedPages(lpv,pMdl);
- MmUnlockPages(pMdl);
- IoFreeMdl(pMdl);
- }
- return STATUS_SUCCESS;
- }