通过qemu-system程序启动的虚机,命令行里一般带有monitor。连接上这个monitor,可以启动gdbserver。通过此gdbserver,可以查看虚机的cpu寄存器,虚拟地址内存等。同时,qemu-system提供了一个tcpserver。另一台机的windbg可以连接上这个tcpserver,进而调试虚机。
虚机windows未开启调试,但是虚机偶发的卡死发生时。
优势:
A. 不需被调试机bcdedit事先配置
B. 若开启嵌套虚拟化,还能观察L1虚机的hvix64的代码运行
缺陷和限制:
仅限于qemu-system启动的虚机,tci产品不支持。
windbg里定位pe模块比较复杂。
在qemu-system的命令行中可以看到-qmp unix:/tmp/vm.monitor,server,nowait,由此,在linux shell里执行qmp-shell -H /tmp/vm.monitor即可进入qmp的交互终端。
输入gdbserver tcp::35100得到Waiting for gdb connection on device 'tcp::35100'即表明gdbserver开启成功。gdbserver默认端口1234,会被ranios的防火墙拦截。这里推荐端口选35100,它在rainos防火墙的白名单中。
退出qmp交互终端用Ctrl+d,不可用Ctrl+c,它会让qemu-system进程终止。
先找出vdi虚机的id和所在运行服务器。ssh连上服务器,先找出虚机对应的domain:
virsh list|grep 120fb070-6b04-4766-b534-38e3131a6c11
得到315 120fb070-6b04-4766-b534-38e3131a6c11 running。
遂用libvirt打开它的gdbserver
virsh qemu-monitor-command 315 --hmp gdbserver tcp::35900
参考使用 EXDI 设置 QEMU 内核模式调试
ExdiGdbSrv.dll(64位),exdiConfigData.xml,systemregisters.xml我已集成到\\rcc.ruijie.net\Upload\lida\Microsoft.WinDbg_1.2202.7001.0.zip。
设置环境变量:
EXDI_GDBSRV_XML_CONFIG_FILE为"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\exdiConfigData.xml"
EXDI_SYSTEM_REGISTERS_MAP_XML_FILE为"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\systemregisters.xml"
解压此zip,在里面有这个dll和两个xml。以管理员权限执行如下:
注册dll:
regsvr32 ExdiGdbSrv.dll
编辑exdiconfigData.xml,找到< = "QEMU">里的
在windbg里输入Ctrl+break或已经发现windbg暂停了target机,输入k,lm,.reload等命令发现没有任何模块被识别。具体原因不明。但是我们可以自行帮windbg定位符号。暂停时,通过r rip查看rip寄存器,若为fffff802`3c7f5e8f前5位是fffff如此字样则表示暂停在内核态,若为00007ffa`f2ebc742前四位是0如此字样则表明暂停在用户态,此时可g再暂停或~1s切换一个cpu线程,直到rip走到内核态。
以rip=fffff802`3c7f5e8f为例,猜测这段地址处于nt模块中,而nt模块的载入地址一般是0x100000的整数倍。用windbg的!dh来解析这个pe头,挨个输入700000,600000,500000……直到解析成功
0: kd> !dh fffff8023c700000
No file header
0: kd> !dh fffff8023c600000
No file header
0: kd> !dh fffff8023c500000
No file header
0: kd> !dh fffff8023c400000
File Type: EXECUTABLE IMAGE
FILE HEADER VALUES
8664 machine (X64)
21 number of sections
C3F2329E time date stamp Mon Mar 5 06:56:30 2074
如上,表明fffff8023c400000是个pe头位置。翻阅pe信息,查看
OPTIONAL HEADER VALUES
20B magic #
14.20 linker version
8BB400 size of code
1B7400 size of initialized data
48E000 size of uninitialized data
98F010 address of entry point
1000 base of code
----- new -----
fffff8023c400000 image base
1000 section alignment
200 file alignment
1 subsystem (Native)
10.00 operating system version
10.00 image version
10.00 subsystem version
1046000 size of image
可知这个pe文件占据空间0x1046000字节。
查看
Debug Directories(3)
Type Size Address Pointer
cv 25 406e0 3fee0 Format: RSDS, guid, 1, ntkrnlmp.pdb
可知这个pe文件是nt。
如此,可用.reload指定这段空间为nt:.reload /f nt=fffff802`3c400000,1046000
之后,可以使用!process 0 0查看所有进程。
例如给新进程的诞生处下断点:bp nt!PspInsertProcess;g。待断点触发时,看到栈回溯是:
Breakpoint 0 hit
nt!PspInsertProcess:
0010:fffff8023cae52a4 488bc4 mov rax,rsp
0: kd> k
# Child-SP RetAddr Call Site
00 ffff85052613aeb8 fffff8023cac9028 nt!PspInsertProcess
01 ffff85052613aec0 fffff8023c80d8f5 nt!NtCreateUserProcess+0xd88
02 ffff85052613bb90 00007ffaf2ece9b4 nt!KiSystemServiceCopyEnd+0x25
03 00000062d50fcd08 00007ffaf0529053 0x00007ffaf2ece9b4
04 00000062d50fcd10 0000000000000000 0x00007ffa`f0529053
看RetAddr,猜测地址7ffaf2ece9b4位于用户态,而且一般是ntdll.dll或kernelbase.dll。输入!dh 7ffa`f0520000,果然它是kernelbase.dll:
0: kd> !dh 7ffa`f0520000
File Type: DLL
FILE HEADER VALUES
8664 machine (X64)
7 number of sections
E8E9AC9B time date stamp Thu Oct 29 12:16:27 2093
……
2D2000 size of image
……
Debug Directories(4)
Type Size Address Pointer
cv 27 26a170 269370 Format: RSDS, guid, 1, kernelbase.pdb
如此可指定kernelbase的地址:.reload /f /user kernelbase=7ffa`f0520000,2D2000。
输入k查看栈回溯:
0: kd> k
# Child-SP RetAddr Call Site
00 ffff85052613aeb8 fffff8023cac9028 nt!PspInsertProcess
01 ffff85052613aec0 fffff8023c80d8f5 nt!NtCreateUserProcess+0xd88
02 ffff85052613bb90 00007ffaf2ece9b4 nt!KiSystemServiceCopyEnd+0x25
03 00000062d50fcd08 00007ffaf0529053 0x00007ffaf2ece9b4
04 00000062d50fcd10 00007ffaf05265e3 kernelbase!CreateProcessInternalW+0xfe3
05 00000062d50fe2e0 00007ffaf26ee2e0 kernelbase!CreateProcessAsUserW+0x63
06 00000062d50fe350 0000000000000001 0x00007ffaf26ee2e0
07 00000062d50fe358 00007ffaee361e09 0x1
08 00000062d50fe360 00007ffaee488dd0 0x00007ffaee361e09
09 00000062d50fe368 00007ffaee3a3a48 0x00007ffaee488dd0
0a 00000062d50fe370 0000000000000000 0x00007ffa`ee3a3a48
猜测7ffaf2ece9b4位于ntdll,从7ffaf2ec0000逐个递减0x1000用!dh翻译,直到找出了pe头:
0: kd> !dh 7ffa`f2e30000
File Type: DLL
FILE HEADER VALUES
8664 machine (X64)
……
1F8000 size of image
……
Debug Directories(4)
Type Size Address Pointer
cv 22 140160 13d360 Can't read debug data cb=0
这里Debug Directories无法显示pdb文件名,原因未知。强行把它指定为ntdll:
.reload /f /user ntdll=7ffa`f2e30000,1f8000
查看栈回溯:
# Child-SP RetAddr Call Site
00 ffff85052613aeb8 fffff8023cac9028 nt!PspInsertProcess
01 ffff85052613aec0 fffff8023c80d8f5 nt!NtCreateUserProcess+0xd88
02 ffff85052613bb90 00007ffaf2ece9b4 nt!KiSystemServiceCopyEnd+0x25
03 00000062d50fcd08 00007ffaf0529053 ntdll!NtCreateUserProcess+0x14
04 00000062d50fcd10 00007ffaf05265e3 kernelbase!CreateProcessInternalW+0xfe3
05 00000062d50fe2e0 00007ffaf26ee2e0 kernelbase!CreateProcessAsUserW+0x63
06 00000062d50fe350 0000000000000001 0x00007ffaf26ee2e0
07 00000062d50fe358 00007ffaee361e09 0x1
08 00000062d50fe360 00007ffaee488dd0 0x00007ffaee361e09
09 00000062d50fe368 00007ffaee3a3a48 0x00007ffaee488dd0
0a 00000062d50fe370 0000000000000000 0x00007ffa`ee3a3a48
猜测7ffa`f26ee2e0地址位于user32.dll里。果然7ffa`f26d0000是个pe头。但是也是不显示pdb名。那强行指定吧:.reload /user /f user32=7ffa`f26d0000,BF000
!running,.crash,.dump都不行。原因有待继续研究。