使用 EXDI 设置 QEMU 内核模式调试

本主题介绍如何使用 EXDI 来设置 QEMU 内核模式调试。 Windows 调试器支持使用 EXDI 对 QEMU 环境进行内核调试。 本文档介绍了在 ExdiGdbSrv.dll(GDB 服务器客户端)和 QEMU GDB 服务器之间建立 GdbServer RSP 会话所需的步骤。

所述方案使用 Windows x64 虚拟机和同样在 Windows 上运行的 QEMU GDB 服务器。

可以连接到作为主机的其他操作系统,如 Linux。 QEMU 是一款虚拟化和计算机仿真软件,可在 x64 和 Arm64 等多种架构上运行。 ExdiGdb 调试服务器还支持其他处理器,例如,可以使用 WinDbg 调试在 Arm64 上运行的 QEMU。 这为调试 Windows VM 提供了多种选择,因此 Windows VM 可以通过连接到调试器主机 EXDI GDB 服务器客户端的可用 QEMU GDB 服务器来进行硬件调试。

有关设置配置 EXDI 连接和故障排除的一般信息,请参阅配置 EXDI 调试器传输

注意

EXDI 是一种针对特定环境的高级专业调试方式。 使用标准 KDNET 连接更容易配置,因此建议使用。 要自动设置网络调试,请参阅设置 KDNET 网络内核自动调试

EXDI COM 服务器

EXDI 是一个接口,可通过添加对硬件调试器(如基于 JTAG 或基于 GdbServer)的支持来扩展 WinDbg。 下图说明了 EXDI-GdbServer 的作用。

显示了 EXDI-GdbServer 作用的堆栈图,上面有 WinDbg-DbgEng、EXDI 接口和与 GDB 服务器通信的 EXDI COM 服务器。

重要

由于 EXDI 未使用 KDNET 协议,因此连接的调试器所掌握的有关电脑上运行情况的信息要少得多,许多命令的运行方式也会不同,甚至根本无法运行。 访问被调试代码的专用符号有助于调试器更好地理解目标系统的代码执行。 有关详细信息,请参阅公共符号和专用符号

在 QEMU 上设置与 Windows 映像的调试器连接

在本主题中,我们将介绍附加到在 Windows 上运行的 QEMU 虚拟 Windows 映像的过程。

  1. 在 Windows 上下载并安装 QEMU。
  2. 配置目标 QEMU 虚拟 Windows 映像,以启动调试所需的网络和 BIOS/UEFI 设置。
  3. 使用配置的启动脚本来启动 QEMU 环境。
  4. 启动 QEMU 上的 gdbserver。
  5. 检查网络连接,找到并记录目标图像 IP 地址。 (主机 IP 默认地址为 1.2.3.4)。
  6. 在主机系统中下载并安装 Windows 调试工具。
  7. 下载、生成、注册和配置 GitHub 上的 QEMU 的 EXDI 服务器。
  8. 通过编辑 EXDI 配置 XML 文件来配置调试器主机 (WinDbg)。
  9. 使用命令行启动 WinDbg 以连接到 EXDI 服务器。
  10. 使用 WinDbg 来调试目标 QEMU Windows 映像。

在 Windows 上下载并安装 QEMU

QEMU 是一种通用的开放源代码计算机模拟器和虚拟化程序,可进行动态转换。 当 QEMU 用作计算机模拟器时,它可以在不同的计算机上(x64 电脑)上运行为一种处理器(如 Arm64)开发的操作系统和程序。 它还可以运行/托管不同操作系统 (Windows/Linux/Mac) 的虚拟机镜像。

QEMU 可以使用其他管理程序(如 KVM)来使用 CPU 扩展 (HVM) 进行虚拟化。 将 QEMU 用作虚拟化程序时,QEMU 可直接在主机 CPU 上执行来宾代码,从而实现接近本地的性能。 QEMU 可利用 OS 虚拟机监控程序的功能将 CPU 和 MMU 仿真卸载到实际硬件上。

下载并安装 QEMU

在本演练中,适用于 Windows x64 的 QEMU 将安装在一台 x64 电脑上,而 Windows 调试器也将在这台电脑上运行。

从 QEMU 下载页面下载 QEMU:https://www.qemu.org/download/

有关安装 QEMU 的信息,请参阅 QEMU 文档:https://www.qemu.org/documentation/

配置目标虚拟磁盘

找到或创建一个包含要调试的软件的虚拟磁盘镜像。

在本例中,将使用 Windows x64 VHDX 虚拟机磁盘映像。 要了解有关 Windows 虚拟机映像的详细信息,请参阅在 Windows 10 上使用 Hyper-V 创建虚拟机

在 Windows 映像中注入 VirtIO 驱动程序

要实现网络功能和合理的存储设备性能,可在 Windows 虚拟机磁盘镜像中注入或安装 VirtIO 驱动程序。 VirtIo 驱动程序可在此处获取:https://github.com/virtio-win/kvm-guest-drivers-windows

VirtIO 是一个标准化接口,允许虚拟机访问抽象硬件,如块设备、网络适配器和控制台。 Virtio 是 QEMU 等虚拟环境中硬件设备的抽象层。

将 VHDX 转换为 QEMU

这一步并非必需但建议使用,因为使用本地 QEMU QCOW 映像而不是 VHDX 可以获得更好的性能。

使用以下 qemu-img.exe 命令转换 vhdx。 此实用工具位于安装 QEMU 的位置,例如 C:\Program Files\qemu

C:\Program Files\qemu> qemu-img convert -c -p -O qcow2 MyVHDXFile.vhdx MyQEMUFile.qcow2 

下载 UEFI 固件

为获得最佳效果,请下载或编译 UEFI 固件文件 (OVMF.fd)。 之所以需要固件,是因为 QEMU 默认情况下会模拟旧版 BIOS 系统。

UEFI 固件的一个来源是 Open Clear Linux 项目:https://clearlinux.org/

UEFI OVMF.fd 文件示例可在此处获取:https://github.com/clearlinux/common/blob/master/OVMF.fd

提取 C:\Program Files\qemu\Firmware 中下载文件的内容。

对于 Intel AMD64 以外的平台,应从 EDK2 中编译固件。 有关详细信息,请参阅 https://github.com/tianocore/tianocore.github.io/wiki/How-to-build-OVMF

配置 QEMU 启动脚本

在 QEMU 中创建配置文件。 例如,在 QEMU 根目录下创建一个 StartQEMUx64Windows.bat 文件。 请参阅以下示例文件。

使用 QEMU 启动脚本启动 QEMU

执行 QEMU 启动脚本以启动 QEMU。

c:\Program Files\qemu\StartQEMUx64Windows.bat

如果出现防火墙防御程序提示,请授予该程序对所有类型网络的所有权限,以便通过调试器主机的 Windows 防火墙启用 Windbg。

选中 Windows Defender 防火墙的所有三个选项对话框。

一旦 Windows 虚拟机在 QEMU 环境中启动,QEMU UI 就会出现。

显示了视图菜单选项的 QEMU 屏幕截图。

使用 CTRL+ALT+ 数字组合键进入 QEMU 监控控制台。 使用 View->compatmonitor 也可以使用此监视器。

键入 gdbserver 以便在 QEMU 上启动前端 GDB 服务器。

QEMU 应显示 Waiting for gdb connection on device ‘tcp::1234’

使用 CTRL+ALT+1 组合键返回主窗口。

提示:GDB 控制台窗口支持“system_reset”命令,可快速重启仿真。 键入 help 可查看 GDB 控制台命令列表。

QEMU x64 Windows VM 启动脚本示例

下面是可用于 AMD64 虚拟机的 QEMU 配置脚本示例。 将指向 DISK 和 CDROM 文件的链接替换为电脑上的位置。

    REM
    REM  This script is used to run a Windows x64 VM on QEMU that is hosted by a Windows x64 host system
    REM  The Host system is a PC with Intel(R) Xeon(R) CPU.
    REM
    set EXECUTABLE=qemu-system-x86_64
    set MACHINE=-m 6G -smp 4

    REM No acceleration
    REM generic cpu emulation.
    REM to find out which CPU types are supported by the QEMU version on your system, then run:
    REM	 qemu-system-x86_64.exe -cpu help
    REM the see if your host system CPU is listed
    REM

    set CPU=-machine q35 

    REM Enables x64 UEFI-BIOS that will be used by QEMU :
    set BIOS=-bios D:\temp\firmware\OVMF.fd

    REM  Use regular GFX simulation
    set GFX=-device ramfb -device VGA 
    set USB_CTRL=-device usb-ehci,id=usbctrl
    set KEYB_MOUSE=-device usb-kbd -device usb-tablet

    REM # The following line enable the full-speed HD controller (requires separate driver)
    REM # Following line uses the AHCI controller for the Virtual Hard Disk:
    set DRIVE0=-device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0

    REM
    REM This will set the Windows VM x64 disk image that will be launched by QEMU
    REM The disk image is in the qcow2 format accepted by QEMU.
    REM You get the .qcow2 image, once you get the VHDX Windows VM x64 image 
    REM and apply the script to inject the virtio x64 drivers and then run the 
    REM the QEMU tool to convert the .VHDX image to .qcow2 format
    REM 	i.e. 
    REM	qemu-img convert -c -p -O qcow2 Windows_VM_VHDX_with_injected_drivers_file.vhdx file.qcow2
    REM file : points to the specified qcow2 image path.
    REM
    set DISK0=-drive id=disk,file=D:\temp\x64_image_qcow2_for_windows\basex64Client.qcow2,if=none

    REM
    REM for kdnet on, then best option:
    REM   NETWORK0="-netdev user,id=net0,hostfwd=tcp::53389-:3389,hostfwd=tcp::50001-:50001 -device virtio-net,netdev=net0,disable-legacy=on"
    REM
    set NETHOST=-netdev user,id=net0,hostfwd=tcp::3589-:3389
    set NETGUEST=-device e1000,netdev=net0

    REM # The following line should enable the Daemon (instead of interactive)
    set DAEMON=-daemonize"
    %EXECUTABLE% %MACHINE% %CPU% %BIOS% %GFX% %USB_CTRL% %DRIVE0% %DISK0% %NETHOST% %NETGUEST%

检查网络连接

确保获取 Windows IP 地址(如果调试器主机会话与 QEMU VM 不在同一台 Windows 计算机上)。

如果 GDB 服务器正常启动,则会看到 GDB 服务器侦听的端口号,需要使用这个端口来设置主机调试器(exdiConfigData.xml 中的 IP:Port 对)。

如果主机调试器位于托管 QEMU 来宾的同一台计算机上,则 exdiconfigdata.xml 中的 Localhost 标识将以 IP:Port 对的形式使用(例如 LocalHost:Port:1234)。 在本例中,服务器和主机调试器在同一台电脑上,将使用默认值。

在 ExdiConfigData.xml 文件中将当前目标名称属性 (CurrentTarget) 值设为“QEMU”。

如果在远程电脑上工作,请设置 GDB 服务器监听的目标 QEMU IP <address> : Port <number>

  • 在 exdiCondifgData.xml 中找到 QEMU 组件标记元素。
  • 设置 QEMU GDB 服务器的 IP:端口号(如果调试器与 QEMU VM 在同一主机上运行,则为 LocalHost):
  • 将更改保存到位于 EXDI_GDBSRV_XML_CONFIG_FILE 环境变量指定路径下的 exdiConfigdata.xml 文件中。

有关 QEMU 网络连接的详细信息,请参阅https://wiki.qemu.org/Documentation/Networking

可在 QEMU 控制台 (compatmonitor0) 上发出以下命令,以显示有关网络和连接状态的信息。

info network
info usernet

在主机系统中下载并安装 Windows 调试工具

在主机系统上安装 Windows 调试工具。 有关下载和安装调试器工具的信息,请参阅适用于 Windows 的调试工具

下载、生成和注册 EXDI 服务器 DLL

从 GitHub https://github.com/microsoft/WinDbg-Samples 上的 microsoft/WinDbg-Samples 下载相应的 ExdiGdbSrv.dll 二进制文件(EXDI COM 服务器客户端)源代码

git clone https://github.com/microsoft/WinDbg-Samples

根据安装在 Exdi/exdigdbsrv 中的主机调试器的体系结构来生成 VS 解决方案 (ExdiGdbSrv.sln)。

找到生成的 ExdiGdbSrv.dll 文件。

将 EXDI com 服务器 (ExdiGdbSrv.dll) 复制到主机上包含调试器的目录中,.g. C:\Program Files (x86)\Windows Kits\10\Debuggers\x64C:\Debuggers

使用 regsvr32 在管理员命令提示符下注册 DLL。

C:\Program Files (x86)\Windows Kits\10\Debuggers\x64>regsvr32 ExdiGdbSrv.dll

RegSvr32 应返回一条消息,指明 DLLRegisterServer in ExdiGdbSrv.dll succeeded

这一步只需完成一次,但如果更改了 ExdiGdbSrv.dll 的位置,则需要重新注册 COM 服务器。

另一种方法是使用 PowerShell 脚本示例来安装 EXDI 动态链接库,然后首次启动调试器。 有关详细信息,请参阅配置 EXDI 调试器传输中的 EXDI PowerShell 脚本示例

通过编辑 EXDI 配置 XML 文件来配置调试器主机 (WinDbg)

WinDbg-Samples/Exdi/exdigdbsrv/ 中找到所需的两个配置文件,并将其复制到安装调试器的主机本地。

  • exdiConfigData.xml
  • systemregisters.xml

EXDI_GDBSRV_XML_CONFIG_FILE – 描述 EXDI xml 配置文件的完整路径。

EXDI_SYSTEM_REGISTERS_MAP_XML_FILE – 描述 EXDI xml 系统寄存器映射文件的完整路径。

有关设置 EXDI 连接的配置和故障排除以及 exdiConfigData.xml 标记和属性的一般信息,请参阅配置 EXDI 调试器传输

设置环境变量 EXDI_GDBSRV_XML_CONFIG_FILE 和 EXDI_SYSTEM_REGISTERS_MAP_XML_FILE 以描述 exdi xml 配置文件的完整路径。

命令提示符

打开命令提示符并设置以下环境变量。

set EXDI_GDBSRV_XML_CONFIG_FILE="C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\exdiConfigData.xml"

set EXDI_SYSTEM_REGISTERS_MAP_XML_FILE="C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\systemregisters.xml"

键入 SET 以确认指定的路径可从ExdiGdbSrvSample.dll的位置获取

Powershell

打开 PowerShell 提示符并设置以下环境变量:

$env:EXDI_GDBSRV_XML_CONFIG_FILE = 'C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\exdiConfigData.xml'

$env:EXDI_SYSTEM_REGISTERS_MAP_XML_FILE = 'C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\systemregisters.xml'

键入 dir env: 以确认指定的路径可从ExdiGdbSrvSample.dll的位置获取

在主机系统上启动 WinDbg

在设置环境变量(EXDI_GDBSRV_XML_CONFIG_FILE 和 EXDI_SYSTEM_REGISTERS_MAP_XML_FILE)的同一命令提示符下,通过 exdi 接口启动 windbg 会话。

c:\Debuggers> windbg.exe -v -kx exdi:CLSID={29f9906e-9dbe-4d4b-b0fb-6acf7fb6d014},Kd=Guess,DataBreaks=Exdi

要显示更多输出,可使用 -v: 详细会话。 有关 WinDbg 选项的一般信息,请参阅 WinDbg 命令行选项

另一种方法是使用 PowerShell 脚本示例来安装 EXDI 动态链接库,然后首次启动调试器。 有关详细信息,请参阅配置 EXDI 调试器传输中的 EXDI PowerShell 脚本示例

PS>.\Start-ExdiDebugger.ps1 -ExdiTarget "QEMU" -GdbPort 1234 -Architecture x64 -ExdiDropPath "C:\path\to\built\exdi\files"

调试器应启动并连接至 QEMU GdbServer。

在窗口标题中显示 EXDI CLSID 的 WinDbg 主会话。

调试器将显示 EXDI 传输初始化成功。

EXDI: DbgCoInitialize returned 0x00000001
EXDI: CoCreateInstance() returned 0x00000000
EXDI: QueryInterface(IExdiServer3) returned 0x00000000
Target command response: QEMU
exdiCmd: The function: 'ExdiDbgType' was completed.
EXDI: Server::GetTargetInfo() returned 0x00000000
EXDI: Server::SetKeepaliveInterface() returned 0x00000000
EXDI: Server::GetNbCodeBpAvail() returned 0x00000000
EXDI: ExdiNotifyRunChange::Initialize() returned 0x00000000
EXDI: LiveKernelTargetInfo::Initialize() returned 0x00000000
EXDI: Target initialization succeeded

如果在 exdiConfigData.xml 文件中设置了 displayCommPackets="yes",EXDIGdbServer 控制台数据包窗口还可以显示有关 EXDI 连接状态的信息。 有关详细信息,请参阅配置 EXDI 调试器传输中的故障排除信息。

使用 WinDbg 来调试目标 QEMU Windows 映像

dbgeng.dll 使用启发式算法来查找中断命令发生时 NT 基本加载地址的位置。 如果没有专用符号,此过程将失败。

这意味着在许多连接序列下,中断将无法发挥预期的功能。 如果手动侵入代码,它将是 Windows 当时正在执行的一个随机位置。 由于可能无法获得目标代码的符号,因此很难使用符号设置断点。

以下直接访问内存的命令可以正常使用。

k、kb、kc、kd、kp、kP、kv(显示堆栈回溯)

r(寄存器)

d、da、db、dc、dd、dD、df、dp、dq、du、dw(显示内存)

u(取消汇编)

还可以通过代码进行操作。

p(步进)

还可以使用一些命令来尝试查找要调试的代码。

s(搜索内存)

.imgscan(查找映像标头)

Imgscan 对 EDXI 调试很有帮助,因为与传统的基于 KDNET 的内核调试不同,它可能无法根据符号设置断点。 定位所需的目标图像,可以方便地使用其位置来设置内存访问断点。

.exdicmd(EXDI 命令)

.exdicmd 使用活动 EXDI 调试连接向目标系统发送 EXDI 命令。 有关详细信息,请参阅 .exdicmd(EXDI 命令)

EXDI XML 配置文件

EXDI GDB COM 服务器 (ExdiGdbSrv.dll) 需要使用两个 xml 文件。

  1. exdiConfigData.xml - 该文件包含 GDB 服务器客户端与 HW 调试器 GDB 服务器目标成功建立 GDB 会话所需的主要配置数据,因此如果 EXDI_GDBSRV_XML_CONFIG_FILE 环境变量设置该文件位置,GDB 服务器客户端将无法运行。 每个 xml 标记都可以配置特定的 GDB 服务器功能集。 有关可以在 XML 中修改的属性列表和示例 XML,请参阅下文。

  2. Systemregister.xml - 此文件包含系统寄存器与其访问代码之间的映射。 之所以需要这样做,是因为 GDB 服务器没有在 xml 文件中提供访问代码,而调试器是通过访问代码来访问每个系统寄存器的。

有关 XML 配置文件中定义的 GDBServer 标记和属性的详细信息和说明,请参阅配置 EXDI 调试器传输

故障排除

请参阅配置 EXDI 调试器传输中的故障排除信息。

另请参阅

配置 EXDI 调试器传输

.exdicmd(EXDI 命令)

自动设置 KDNET 网络内核调试

手动设置 KDNET 网络内核调试