作者:0x7F@知道創宇404實驗室
日期:2023年2月27日

0x00 前言

windows內核調試常用于 windows 驅動開發調試、內核分析等,使用 WinDBG 可以很方便的進行本地內核調試,但本地內核調試存在較多的限制(如不能使用導致主機暫停運行的指令),通常我們都會通過虛擬機軟件搭建 windows 雙機調試環境,其中一臺作為調試機(debuger),另一臺作為被調試機(debugee),雙機調試幾乎可以滿足大部分的 windows 內核分析、調試等工作。

通過 Vmware 虛擬機軟件搭建 windows 雙機調試環境是最常見的方案,搭建步驟和坑點基本都由前輩梳理成章了,但我日常工作都由 ProxmoxVE 虛擬機支撐起來,遂想使用 ProxmoxVE 配置 windows 的內核調試環境,在此過程中遇到了不少難點。

本文對 ProxmoxVE 下的 windows 內核調試環境配置進行了詳細介紹和實驗演示,對其中的難點進行了簡易分析,希望本文能對有相同需求的小伙伴提供一些幫助。

0x01 基本環境

本文環境如下:

ProxmoxVE 7.2-3
Windows10 1909 專業版

ProxmoxVE 是一套基于 KVM 的虛擬化解決方案,由于其開源特性以及 Linux 的親和性,ProxmoxVE 通常在企業內部大量使用,同時也常常作為商業軟件的底層支撐組件。同類軟件還有大名鼎鼎的 Vmware 和 VirtualBox,這些軟件在使用方面都大同小異。

ProxmoxVE 底層是一臺 Debian 主機,然后基于 KVM+Qemu 實現了虛擬化軟件,配置完成后可通過 web 控制臺(https://[ip]:8006)進行管理和使用:

[1.PVE的web控制臺]

通常情況下,我們使用 Vmware 搭建 windows 雙機調試環境,都以宿主機作為調試機(debuger),以虛擬機作為被調試機(debugee),通過 Vmware 配置串口設備(serial) 通信進行調試;

而 ProxmoxVE 是一臺 Linux 主機,要搭建 windows 雙機調試環境必需要兩臺虛擬機才行。

0x02 本地內核調試

我們先從簡單的本地內核調試環境開始,以此來準備基本的調試環境;在 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本地內核調試]

0x03 網絡雙機調試

從 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 關閉調試功能。

[7.網絡雙機調試環境]

搭建這臺被調試機(debugee)時需要注意,在配置操作系統類型時應選擇 Other 類型,如下:(如果選擇 windows 類型,ProxmoxVE 在虛擬化時會提供 Hyper-V 的各項支持,以此來提高虛擬機的性能,但這些項導致網絡調試無法正常運行,我們將在 ### 0x05 kdnet問題排查 進行簡要分析)

[8.系統類型配置為other]

由于配置為 Other 類型,ProxmoxVE 可能無法提供 windows 的推薦配置,最終導致無法正確安裝 windows 系統,若遇到該問題可排查磁盤是否設置為 IDE 類型。

除此之外,在網卡配置階段需要選擇 Intel E1000,如下:

[9.網卡配置為intel e1000]

根據測試 e1000 網卡在系統內部的硬件 id 為 VEN_8086&DEV_100E,滿足網絡調試對網卡的要求;另外 Realtek RTL8139 不滿足要求,而VirtIOVmware vmxnet3 需要安裝特定驅動才能使用。

接下來完成 windows10 系統安裝和基礎配置,隨后進行網絡調試的配置;官方推薦使用 kdnet 工具進行自動配置,但并不能順利配置;

我們從調試機(debuger) 的 WinDBG 目錄中(C:\Program Files (x86)\Windows Kits\10\Debuggers\x64) 拷貝 kdnet.exeVerifiedNICList.xml 到被調試機上(debugee),按官方教程操作如下:

[10.kdnet自動配置失敗]

雖然我們的網卡位于 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

[11.手動配置kdnet]

完成配置后重啟生效;隨即我們在調試機(debuger) 使用 WinDBG 進行網絡調試配置,端口號為 50000,密鑰為 p.a.s.s,如下:

[12.windbg-net標簽]

無論被調試機(debugee) 是在運行期間還是重啟階段,都可以被調試機(debuger)正確連接并進行調試,連接成功后可使用 break 斷下來:

[13.windbg網絡雙機調試]

如果 ProxmoxVE 和虛擬機未采用 DHCP 分配 ip 地址,被調試機(debugee) 會在啟動階段卡在 windows logo 階段 10min 左右,我們將在 ### 0x05 kdnet問題排查 進行簡要分析。

0x04 串口雙機調試

微軟從 windows8 才開始提供網絡調試功能,如果要調試 windows7 系統則需要使用傳統的串口雙機調試的方法了。這里我們復用上文環境,配置 windows10 虛擬機的串口雙機調試,windows7 同理可得,環境配置如下:

上文中網絡雙機調試中的各項配置、操作系統類型、網卡類型均不影響該步驟。

[14.串口雙機調試環境]

首先我們為兩臺 windows10 虛擬機添加串口(虛擬機關機后再開機硬件改動生效),如下:

[15.pve添加串口設備]

配置成功后,可在 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

執行如下:

[16.bcdedit配置串口調試]

隨后我們切換至調試機(debuger)下,使用 WinDBG 設置串口調試配置,波特率為 115200,端口為 1,不勾選 pipe,勾選 reconnect,如下:

[17.windbg-com標簽]

設置完畢后,在 WinDBG 顯示 Waiting to reconnect... 后,重啟被調試機(debugee),調試機(debuger)將在其系統啟動時連接上去,使用 break 可將其斷下來,如下:

[18.windbg串口雙機調試]

我這里首次連接時 WinDBG 將異常退出,不過重新啟動 WinDBG 并設置好參數即可成功連接。

ProxmoxVE串口調試的一些補充
熟悉 Vmware 搭建 windows 內核調試的朋友,通常都使用命名管道進行配置如 \\.\pipe\com1,但 ProxmoxVE 下的串口設備(serial) 僅支持 /dev/.+|socket 兩種類型(實際上底層的 kvm/qemu 支持很多,但 ProxmoxVE 會直接報錯無法啟動虛擬機),這為我們的串口調試帶了一些困難;

同時我們默認配置的串口設備類型為 socket,其實際運行的參數如下:

[19.kvm實際啟動參數-serial]

串口設備的參數為 -chardev socket,id=serial0,path=/var/run/qemu-server/133.serial0,server=on,wait=off,同樣其參數在 ProxmoxVE 下不能修改。

在此限制條件下,我們可以使用 socatUNIX-CLIENT 的方式將兩臺虛擬機的串口設備進行連接,從而實現串口雙機調試。

0x05 kdnet問題排查

1.hyper-v虛擬化導致kdnet無法工作
在上文「網絡雙機調試」的環境配置中,我們在 ProxmoxVE 配置被調試機(debugee)時將其操作系統類型設置為 Other 類型,這樣才能使 kdnet 正常工作,為什么呢?

我們按正常的安裝流程在 ProxmoxVE 中安裝一臺 windows10(即操作系統類型選擇為 win10/2016/2019) 并啟動,通過 ssh 登錄 ProxmoxVE 查看底層 kvm/qemu 的啟動參數,如下:

[20.kvm實際啟動參數-cpu]

我們可以看到其 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 命令的代碼如下:

[21.pve源碼拼接qemu命令]

隨后在 qemu-server/PVE/QemuServer/CPUConfig.pm#get_cpu_config() 找到 -cpu 參數的生成代碼:

[22.pve源碼拼接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 使用了 以太網(內核調試器) 替代了原始網卡:

[23.調試器網卡驅動]

以太網(內核調試器) 其默認采用 DHCP 的方式獲取 ip,而通常情況下 ProxmoxVE 都采用靜態 ip 分配,在系統啟動階段,該網卡將首先等待 DHCP 分配 ip,若獲取失敗,則自己分配 169.254.*.* 的地址;這個階段發生在 windows logo 界面,大致需要 10min。

采用靜態分配地址的 ProxmoxVE 服務器,可在被調試機(debugee)內修改網絡調試,關閉 DHCP 即可解決:

# 查看網絡調試配置
$ bcdedit /dbgsettings
# 關閉網絡調試配置中的 dhcp
$ bcdedit /set "{dbgsettings}" dhcp no
# 查看網絡調試配置
$ bcdedit /dbgsettings

執行如下:

[24.關閉網絡調試的dhcp]

3.kdnet下被調試機聯網問題
在某些場景下,我們需要在聯網條件下進行內核調試,串口調試不會影響網絡,但網絡調試會使用 以太網(內核調試器) 替代原始網卡,其默認采用 DHCP 方式,若上游配置好了 DHCP 服務器則可正常使用;

如果采用靜態地址分配,則進入虛擬機后,在 以太網(內核調試器) 上配置靜態地址即可,聯網和網絡調試不會沖突,都可以正常使用:

[25.調試器網卡配置靜態ip]

4.kdnet下多網卡的被調試機配置
某些場景下,我們的虛擬機具有多張網卡,若想指定具體的網卡作為調試網卡,可以使用如下命令:

# 在網絡調試配置成功的前提下
# 設置 busparams 參數
# 通過設備管理器查看對應網卡的 PCI 插槽 [bus.device.function]
$ bcdedit /set "{dbgsettings}" busparams 0.19.0
# 查看網絡調試配置
$ bcdedit /dbgsettings

執行如下:

[26.指定網絡調試器網卡]

0x06 vmware碎碎念

通過以上一陣折騰,不得不說 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,如下:

[27.vmware被調試機串口配置]

在虛擬機使用 bcdedit 配置串口調試,隨后在宿主機中打開 WinDBG 使用串口調試連接即可,如下:

[28.vmware宿主機串口調試]

但如果要使用虛擬機串口調試虛擬機,這就稍微有點不同了;首先配置被調試機(debugee)串口,配置命名管道 \\.\pipe\com1,設置該端是服務器,設置另一端是虛擬機,勾選 輪詢時主動放棄CPU,如下:

[29.vm-vm被調試機串口調試]

隨后配置調試機(debuger)串口,配置命名管道 \\.\pipe\com1,設置該端是客戶端,設置另一端是虛擬機,如下:

[30.vm-vm調試機串口調試]

同樣也在被調試機(debugee) 使用 bcdedit 配置串口調試,然后在調試機(debuger)中使用 WinDBG 進行串口調試,這里需要注意串口設備為 com1,且不能勾選 pipe(因為命名管道是對于宿主機的,而它在虛擬機內部僅僅是 com 口),如下:

[31.vm-vm windbg配置串口調試]

配置完成后,被調試機(debugee)重啟即可成功連接。

0x07 References

https://learn.microsoft.com/zh-cn/windows-hardware/drivers/debugger/debugger-download-tools
https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/bcdedit--dbgsettings
https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/supported-ethernet-nics-for-network-kernel-debugging-in-windows-10
https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-a-network-debugging-connection-automatically
https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-a-network-debugging-connection
https://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-290092
https://superuser.com/questions/1404669/crossover-computer-connection-vs-network-switch-broadcast-packet-differences
https://www.linux-kvm.org/page/WindowsGuestDrivers/GuestDebugging
https://www.osr.com/blog/2021/10/05/using-windbg-over-kdnet-on-qemu-kvm/
https://www.qemu.org/docs/master/system/i386/hyperv.html
https://git.proxmox.com/?p=qemu-server.git;a=summary


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/2052/