作者:[email protected]知道创宇404实验室
日期:2023年2月27日
Windows内核调试常用于 windows 驱动开发调试、内核分析等,使用 WinDBG 可以很方便的进行本地内核调试,但本地内核调试存在较多的限制(如不能使用导致主机暂停运行的指令),通常我们都会通过虚拟机软件搭建 windows 双机调试环境,其中一台作为调试机(debuger),另一台作为被调试机(debugee),双机调试几乎可以满足大部分的 windows 内核分析、调试等工作。
通过 Vmware 虚拟机软件搭建 windows 双机调试环境是最常见的方案,搭建步骤和坑点基本都由前辈梳理成章了,但我日常工作都由 ProxmoxVE 虚拟机支撑起来,遂想使用 ProxmoxVE 配置 windows 的内核调试环境,在此过程中遇到了不少难点。本文对 ProxmoxVE 下的 windows 内核调试环境配置进行了详细介绍和实验演示,对其中的难点进行了简易分析,希望本文能对有相同需求的小伙伴提供一些帮助。ProxmoxVE 7.2-3
Windows10 1909 专业版
ProxmoxVE 是一套基于 KVM 的虚拟化解决方案,由于其开源特性以及 Linux 的亲和性,ProxmoxVE 通常在企业内部大量使用,同时也常常作为商业软件的底层支撑组件。同类软件还有大名鼎鼎的 Vmware 和 VirtualBox,这些软件在使用方面都大同小异。ProxmoxVE 底层是一台 Debian 主机,然后基于 KVM+Qemu 实现了虚拟化软件,配置完成后可通过 web 控制台(https://[ip]:8006)进行管理和使用:通常情况下,我们使用 Vmware 搭建 windows 双机调试环境,都以宿主机作为调试机(debuger),以虚拟机作为被调试机(debugee),通过 Vmware 配置串口设备(serial) 通信进行调试;而 ProxmoxVE 是一台 Linux 主机,要搭建 windows 双机调试环境必需要两台虚拟机才行。我们先从简单的本地内核调试环境开始,以此来准备基本的调试环境;在 ProxmoxVE 中安装 windows10 系统,并完成基本的配置如下:
[2.本地内核调试环境]
我们从官网下载 WinDBG 并在 windows10 系统上进行安装:
[3.windbg安装配置]
并在环境变量中(系统变量)配置符号表设置:
_NT_SYMBOL_PATH
SRV*c:\symbols*http://msdl.microsoft.com/download/symbols
配置完成后,WinDBG在调试过程中将自动从微软符号表服务器下载对应数据,并保存至 C:\symbols 下;
也可以在 WinDBG 中使用 Ctrl+S 配置符号表,不过采用环境变量的方式还可以方便其他应用使用该配置。
随后我们使用 bcdedit 修改 windows 的启动配置数据文件,使用管理员权限打开 powershell:
# 开启 debug
$ bcdedit /debug on
# 查看 bcdedit 配置
$ bcdedit
# 查看 dbgsettings 配置(默认为 local)
$ bcdedit /dbgsettings
执行如下:
[4.bcdedit配置本地调试]
通过 windows 开机启动项选择「启用调试模式」也是一样的,不过通过 bcdedit 修改是永久有效的。
如果不想影响目前的配置,可以通过 bcdedit /copy "{current}" /d "debug test" 复制当前配置,随后使用 bcdedit /set "{id}" debug on 进行配置,在开机时可选择不同的启动项进入系统。
随后重启 windows10 虚拟机生效配置,使用管理员权限启动 WinDBG,选择 File - Kernel Debug,选择 Local 本地调试标签:
[5.windbg-local标签]
随后便可以正常进行本地内核调试,我们能够查看内核中的各项数据;但本地内核调试不能影响系统的运行,所以不能打断点、单步调试等,当然 go 指令也是不能使用的:
[6.windbg本地内核调试]
从 windows8 开始微软提供了网络调试内核的方法,其简称为 kdnet,因为通信效率要比串口高,所以使用起来体验更好,是目前微软推荐的内核调试方法。
网络双机调试除了对系统版本有要求,对网卡也有一定的要求,支持的厂商和型号可以查阅 https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/supported-ethernet-nics-for-network-kernel-debugging-in-windows-10 ;除此之外,还需要两台主机位于同一子网内。那么我们需要在 ProxmoxVE 再添加一台 windows10 虚拟机作为被调试机(debugee),以我们上文本地内核调试中的主机作为调试机(debuger),以此用两台虚拟机组成 windows 网络双机调试的环境,如下:本地内核调试中的配置 bcdedit /debug on 不会影响该步骤,也可以手动设置 bcdedit /debug off 关闭调试功能。
搭建这台被调试机(debugee)时需要注意,在配置操作系统类型时应选择 Other 类型,如下:(如果选择 windows 类型,ProxmoxVE 在虚拟化时会提供 Hyper-V 的各项支持,以此来提高虚拟机的性能,但这些项导致网络调试无法正常运行,我们将在 ### 0x05 kdnet问题排查 进行简要分析)由于配置为 Other 类型,ProxmoxVE 可能无法提供 windows 的推荐配置,最终导致无法正确安装 windows 系统,若遇到该问题可排查磁盘是否设置为 IDE 类型。除此之外,在网卡配置阶段需要选择 Intel E1000,如下:根据测试 e1000 网卡在系统内部的硬件 id 为 VEN_8086&DEV_100E,满足网络调试对网卡的要求;另外 Realtek RTL8139 不满足要求,而VirtIO 和 Vmware vmxnet3 需要安装特定驱动才能使用。接下来完成 windows10 系统安装和基础配置,随后进行网络调试的配置;官方推荐使用 kdnet 工具进行自动配置,但并不能顺利配置;我们从调试机(debuger) 的 WinDBG 目录中(C:\Program Files (x86)\Windows Kits\10\Debuggers\x64) 拷贝 kdnet.exe 和 VerifiedNICList.xml 到被调试机上(debugee),按官方教程操作如下:虽然我们的网卡位于 VerifiedNICList 中,但 kdnet.exe 无法正确解析。我们按官方手动配置教程进行设置:# 开启 debug
$ bcdedit /debug on
# 设置网络调试参数
# 设置调试机(debuger)的 ip 地址为 10.0.25.192
# 设置被调试机的端口为 50000 (必须>=49152)
# 设置被调试机的连接密码为 p.a.s.s (必须为 x.x.x.x 格式)
$ bcdedit /dbgsettings NET HOSTIP:10.0.25.192 PORT:50000 KEY:p.a.s.s
# 查看调试配置
$ bcdedit /dbgsettings
完成配置后重启生效;随即我们在调试机(debuger) 使用 WinDBG 进行网络调试配置,端口号为 50000,密钥为 p.a.s.s,如下:无论被调试机(debugee) 是在运行期间还是重启阶段,都可以被调试机(debuger)正确连接并进行调试,连接成功后可使用 break 断下来:如果 ProxmoxVE 和虚拟机未采用 DHCP 分配 ip 地址,被调试机(debugee) 会在启动阶段卡在 windows logo 阶段 10min 左右,我们将在 ### 0x05 kdnet问题排查 进行简要分析。
微软从 windows8 才开始提供网络调试功能,如果要调试 windows7 系统则需要使用传统的串口双机调试的方法了。这里我们复用上文环境,配置 windows10 虚拟机的串口双机调试,windows7 同理可得,环境配置如下:
上文中网络双机调试中的各项配置、操作系统类型、网卡类型均不影响该步骤。
首先我们为两台 windows10 虚拟机添加串口(虚拟机关机后再开机硬件改动生效),如下:配置成功后,可在 windows 设备管理器中看到 com 设备。
目前这两个串口独立运行,我们通过 ssh 登录 ProxmoxVE 的控制台,使用 socat 将两个接口连接起来:# 正常启动两台虚拟机后
# pve(windows10-1)=132 / pve(windows10-2)=133
# 使用 tmux 开启后台终端,socat 需要一直运行
$ tmux
# socat 连接两个串口设备
# 使用 -v 查看运行日志
# 使用 UNIX-CLIENT 的类型打开文件
$ socat -v UNIX-CLIENT:/var/run/qemu-server/132.serial0 UNIX-CLIENT:/var/run/qemu-server/133.serial0
配置完成后,我们在被调试机(debugee)中设置串口调试:
# 开启 debug
$ bcdedit /debug on
# 设置串口调试参数
# 设置调试串口为 1 (com1)
# 设置串口波特率为 115200
$ bcdedit /dbgsettings SERIAL DEBUGPORT:1 BAUDRATE:115200
# 查看调试配置
$ bcdedit /dbgsettings
随后我们切换至调试机(debuger)下,使用 WinDBG 设置串口调试配置,波特率为 115200,端口为 1,不勾选 pipe,勾选 reconnect,如下:设置完毕后,在 WinDBG 显示 Waiting to reconnect... 后,重启被调试机(debugee),调试机(debuger)将在其系统启动时连接上去,使用 break 可将其断下来,如下:我这里首次连接时 WinDBG 将异常退出,不过重新启动 WinDBG 并设置好参数即可成功连接。
熟悉 Vmware 搭建 windows 内核调试的朋友,通常都使用命名管道进行配置如 \\.\pipe\com1,但 ProxmoxVE 下的串口设备(serial) 仅支持 /dev/.+|socket 两种类型(实际上底层的 kvm/qemu 支持很多,但 ProxmoxVE 会直接报错无法启动虚拟机),这为我们的串口调试带了一些困难;同时我们默认配置的串口设备类型为 socket,其实际运行的参数如下:串口设备的参数为 -chardev socket,id=serial0,path=/var/run/qemu-server/133.serial0,server=on,wait=off,同样其参数在 ProxmoxVE 下不能修改。在此限制条件下,我们可以使用 socat 以 UNIX-CLIENT 的方式将两台虚拟机的串口设备进行连接,从而实现串口双机调试。在上文「网络双机调试」的环境配置中,我们在 ProxmoxVE 配置被调试机(debugee)时将其操作系统类型设置为 Other 类型,这样才能使 kdnet 正常工作,为什么呢?我们按正常的安装流程在 ProxmoxVE 中安装一台 windows10(即操作系统类型选择为 win10/2016/2019) 并启动,通过 ssh 登录 ProxmoxVE 查看底层 kvm/qemu 的启动参数,如下:我们可以看到其 cpu 参数为 -cpu kvm64,enforce,hv_ipi,hv_relaxed,hv_reset,hv_runtime,hv_spinlocks=0x1fff,hv_stimer,hv_synic,hv_time,hv_vapic,hv_vpindex,+kvm_pv_eoi,+kvm_pv_unhalt,+lahf_lm,+sep,其中 hv_* 的配置表示 kvm 将以 hyper-v 的方式提供虚拟化功能,windws 虚拟机将认为自己运行在 hyper-v 的技术之上,以便使用 hyper-v 的功能并在一定程度上提高运行性能。而根据前辈在 kvm/qemu 下使用的 kdnet 的经验(https://www.osr.com/blog/2021/10/05/using-windbg-over-kdnet-on-qemu-kvm/) 来看,hv_* 配置项会导致 kdnet 工作时认为自身位于 hyper-v 环境下,从而使用 hyper-v 中未公开的通信机制,最终导致 kdnet 无法正常工作;经过测试验证,在我们的环境下的表现和前辈文章不一致,hv-vendor-id(CPUID) 并不会被修改,这可能和 qemu 的版本有关系,但 hv_* 的配置项确实会影响 kdnet 的工作。我们沿着这个思路查找 ProxmoxVE 调用 kvm/qemu 的源码,在 qemu-server 源码包中 qemu-server/PVE/QemuServer.pm#vm_start() 找到调用 kvm/qemu 的代码入口;随后跟入该函数,在 qemu-server/PVE/QemuServer.pm#config_to_command() 找到拼接 qemu 命令的代码如下:随后在 qemu-server/PVE/QemuServer/CPUConfig.pm#get_cpu_config() 找到 -cpu 参数的生成代码:结合上下文可以了解到,当操作系统为 win10 等类型时,此处将自动在 -cpu 参数中添加 hv_* 参数,以更好的支持 windows 虚拟机。那么在设置虚拟机硬件时,我们只需要选择操作系统类型为 other,即可避免 ProxmoxVE 使用 hv_* 参数启动虚拟机,从而保证 kdnet 可以正常工作。PS:
1.对于已配置好的虚拟机,可使用 ssh 登录 ProxmoxVE,修改虚拟机配置文件 /etc/pve/qemu-server/[id].conf,设置启动的 ostype: other,也可以关闭 hyber-v 的虚拟化。
2.对于已成功配置网络调试的主机,即便再重新打开 hyber-v 的虚拟化,kdnet 也能正常工作(这可能和已成功配置的网络调试器驱动有关?)
2.非DHCP的调试机(debugee)启动时卡logo界面
当我们使用 bcdedit 配置好网络调试后,重启虚拟机可以发现 windows 使用了 以太网(内核调试器) 替代了原始网卡:以太网(内核调试器) 其默认采用 DHCP 的方式获取 ip,而通常情况下 ProxmoxVE 都采用静态 ip 分配,在系统启动阶段,该网卡将首先等待 DHCP 分配 ip,若获取失败,则自己分配 169.254.*.* 的地址;这个阶段发生在 windows logo 界面,大致需要 10min。采用静态分配地址的 ProxmoxVE 服务器,可在被调试机(debugee)内修改网络调试,关闭 DHCP 即可解决:# 查看网络调试配置
$ bcdedit /dbgsettings
# 关闭网络调试配置中的 dhcp
$ bcdedit /set "{dbgsettings}" dhcp no
# 查看网络调试配置
$ bcdedit /dbgsettings
3.kdnet下被调试机联网问题
在某些场景下,我们需要在联网条件下进行内核调试,串口调试不会影响网络,但网络调试会使用 以太网(内核调试器) 替代原始网卡,其默认采用 DHCP 方式,若上游配置好了 DHCP 服务器则可正常使用;如果采用静态地址分配,则进入虚拟机后,在 以太网(内核调试器) 上配置静态地址即可,联网和网络调试不会冲突,都可以正常使用:4.kdnet下多网卡的被调试机配置
某些场景下,我们的虚拟机具有多张网卡,若想指定具体的网卡作为调试网卡,可以使用如下命令:# 在网络调试配置成功的前提下
# 设置 busparams 参数
# 通过设备管理器查看对应网卡的 PCI 插槽 [bus.device.function]
$ bcdedit /set "{dbgsettings}" busparams 0.19.0
# 查看网络调试配置
$ bcdedit /dbgsettings
通过以上一阵折腾,不得不说 vmware 在搭建 windows 调试环境这条路上帮我们铺平了道路;在实验过程中,我同时也配置了 vmware 下的环境,在这里我补充两个偏门的点,希望可以帮助到使用 vmware 搭建环境的小伙伴。
Windows10 1909 专业版(宿主机)
Vmware Workstation 17
Windows10 1909 专业版(虚拟机)
1.vmware下的网络调试搭建
在网络调试的需求下,无论是使用宿主机调试虚拟机,还是使用虚拟机调试虚拟机,vmware 均可以完美支持;其 vmware 提供的虚拟机网卡默认支持 windows 网络调试,同时 vmware 默认采用 NAT 网络并默认开启 DHCP。2.vmware串口调试搭建
使用 vmware 通过宿主机串口调试虚拟机,这我们再熟悉不过了,在虚拟机串口中配置命名管道 \\.\pipe\com1,设置该端是服务器,设置另一端是应用程序,勾选 轮询时主动放弃CPU,如下:在虚拟机使用 bcdedit 配置串口调试,随后在宿主机中打开 WinDBG 使用串口调试连接即可,如下:但如果要使用虚拟机串口调试虚拟机,这就稍微有点不同了;首先配置被调试机(debugee)串口,配置命名管道 \\.\pipe\com1,设置该端是服务器,设置另一端是虚拟机,勾选 轮询时主动放弃CPU,如下:随后配置调试机(debuger)串口,配置命名管道 \\.\pipe\com1,设置该端是客户端,设置另一端是虚拟机,如下:同样也在被调试机(debugee) 使用 bcdedit 配置串口调试,然后在调试机(debuger)中使用 WinDBG 进行串口调试,这里需要注意串口设备为 com1,且不能勾选 pipe(因为命名管道是对于宿主机的,而它在虚拟机内部仅仅是 com 口),如下:配置完成后,被调试机(debugee)重启即可成功连接。https://learn.microsoft.com/zh-cn/windows-hardware/drivers/debugger/debugger-download-toolshttps://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/bcdedit--dbgsettingshttps://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/supported-ethernet-nics-for-network-kernel-debugging-in-windows-10https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-a-network-debugging-connection-automaticallyhttps://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-a-network-debugging-connectionhttps://forum.proxmox.com/threads/two-windows-guests-communicating-via-serial-console-comn.67588/https://forum.proxmox.com/threads/serial-port-between-two-vms.63833/#post-290092https://superuser.com/questions/1404669/crossover-computer-connection-vs-network-switch-broadcast-packet-differenceshttps://www.linux-kvm.org/page/WindowsGuestDrivers/GuestDebugginghttps://www.osr.com/blog/2021/10/05/using-windbg-over-kdnet-on-qemu-kvm/https://www.qemu.org/docs/master/system/i386/hyperv.htmlhttps://git.proxmox.com/?p=qemu-server.git;a=summary