Windows Hypervisor&内核调试的几种常见/不常见方法

一、前言

本文主要介绍了使用调试器对Windows操作系统的内核层和Hypervisor层进行双机调试的几种常见和不常见的方法。本文中使用的windbg调试器和其附带的实用调试工具都可以在windows sdk安装包中选择安装,windows sdk安装包官方的下载地址是: (https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/)。有需要的读者可以自行下载并安装。

二、串口调试

首先我们先来介绍使用VMware虚拟机的情况下如何使用串口进行双机调试Windows内核及Hypervisor。

在VMware的虚拟机设置中,添加“串行端口”设备后,并设置“串行端口”设备“使用命名的管道”,这里命名管道的名称可以自己设定,并分别选择“该端是服务器”和“另一端是应用程序”。如下图。

然后我们在被调试机中设置bcdedit参数,这里的目的是在系统启动过程中添加debug参数。如下图。

在被调试机中我们分别使用bcdedit /dbgsettings serial debugport:1 baudrate:115200bcdedit /hypervisorsettings serial debugport:1 baudrate:115200命令将Windows内核和Hypervisor的调试参数设置为串口调试,串口为com1,波特率为115200。然后再使用bcdedit /debug onbcdedit /set hypervisordebug on命令分别开启windows内核和Hypervisor层的调试。最后设置dbgtransport为kdcom.dll,这里是为了保证被调试机在系统启动过程中使用串口进行调试。

现在被调试机已经整装待发做好了被调试的准备,但调试机还需要一些配置。因为我们需要同时调试windows的内核和Hypervisor,而且在被调试机的参数中使用了同一个串口(com1)作为调试串口,所以需要将不同层级的调试数据分发,根据不同层级将调试数据分发到不同的命名管道。我们可以使用& 'C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\vmdemux.exe' -src pipe:pipename=com2命令实现这一过程。

成功运行如上命令后,vmdemux进程会自动生成两个命名管道\\.\pipe\Vm0\\.\pipe\Vm1。分别使用& 'C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe' -k com:port=\\.\pipe\Vm0,pipe,resets=0,reconnect& 'C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe' -k com:port=\\.\pipe\Vm1,pipe,resets=0,reconnect命令打开windows Hypervisor和内核的调试窗口。如下图。

下面我们介绍在双实体机的情况下如何使用串口进行双机调试。

双实体机进行串口调试需要被调试机的主板上保留9针串口,在10年前的电脑主板上,串口几乎是标准配置,然而随着主板厂商的革新,主板串口也渐渐退出历史舞台。

除了需要主板中保留串口外,还需要拥有一条串口调试线:Null-modem线,或者准备一条2,3交叉线。关于Null-modem调试线的线序如下图,感兴趣的读者可以自己手动做一条。

当使用串口调试线连接好调试端和被调试端,就可以使用 & 'C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\vmdemux.exe' -src com:port=com2命令将不同层级的数据分发到指定的层级,实现windows内核和Hypervisor调试。

三、网络调试

Windows内核和Hypervisor调试中,可以使用网络进行调试,不需要特殊的调试线连接两台机器,网络调试大大方便了双机调试中的准备过程。

首先,配置被调试机bcdedit配置,这里假设我们的调试机IP地址为192.168.111.1,调试Hypervisor的端口为52201,调试windows内核的端口是52202。运行如下图的命令,设置网络调试,最后将dbgtransport设置为kdnet.dll。

在图中可以看到,如果成功设置了网络调试后,会返回一个key,这个key是用来给调试机中的windbg连接被调试机使用的。这里这两个key要先记下来。

重启被调试机后,在调试机端使用& "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe" -k net:port=52202,key=rq644uvs2p16.3ked98d1isrrq.hr7oioflkdt2.37b29ko4f79yj& "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe" -k net:port=52201,key=2mko67of9fjih.233nl9lfzytc2.13u0ikbj37np1.3im74f7f672zj命令打开windows内核和Hypervisor调试窗口。如下图。

四、USB 3.0调试

USB3.0接口也可以用作Windows内核调试的解决方案,在例如一些超薄笔记本电脑没有PCIE网卡的情况下,USB3.0便成了唯一的内核调试方案。

首先,需要将windows sdk套件中的usbview.exe这个程序复制到被调试机中,位置在:C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\usbview.exe

在被调试机中打开usbview.exe,并找到USB3.0主控器,查看主控制器的信息,如下图。

如图中所示,图中选中的USB3.0主机控制器中的信息显示它的Debug Port Number不为None,这个主控器设备所在的位置在0.20.0这个位置上。Debug Port Number的信息不为None说明当前的USB3.0主控器拥有调试能力。

当确定好了主控制器位置后,还需要准备一条USB3.0调试线,根据USB3.0引脚定义可以得知,USB3.0的引脚1是供电,引脚2、3是USB2.0数据传输所用引脚。

所以根据微软给出的文档:(https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-a-usb-3-0-debug-cable-connection),只需要先购买一条USB3.0 A/A公对公线,然后使用透明胶带将买来的线材的1、2、3号引脚绝缘屏蔽后就得到了一条USB3.0调试线。

随后将调试线的一端插入调试机USB3.0接口中,另一端插入到被调试机位置在0.20.0主控制器所在的端口中。如果这里无法确定哪个接口是所在主控制器所在端口的话,可以找一个U盘挨个尝试,在usbview.exe中观察,直到找到正确的端口。

下面我们在被调试机中配置bcdedit参数。使用如下三条命令开启USB3.0内核调试:

  • bcdedit /debug on
  • bcdedit /dbgsettings usb targetname:ikun
  • bcdedit /set "{dbgsettings}" busparams 0.20.0

这里的targetname可以取一个任意的名字;busparams要填入刚才找到的拥有USB3.0调试能力的USB3.0主控制器设备的位置。

重启被调试机后,使用如下命令打开Windows内核调试窗口:

& "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe" -k usb:targetname=ikun

读到这里有人肯定发现了这里的USB3.0调试好像并没有配置Hypervisor层的调试,这个原因是因为微软只实现了Windows内核的USB3.0调试,Windows Hypervisor层并不支持USB3.0调试。

#五、VMware + IDA调试

VMware虚拟化软件也拥有强大的调试能力,我们可以借用VMware和IDA调试任意操作系统。

首先,在配置及安装完虚拟机后,打开虚拟机的配置文件,例如:Windows10_x64_hyperv.vmx。打开vmx文件后,在文件的末尾添加如下配置:

  • debugStub.listen.guest64 = "TRUE"
  • debugStub.hideBreakpoints = "TRUE"
  • debugStub.listen.guest64.remote = "TRUE"
  • monitor.debugOnStartGuest64 = "TRUE“

这四句配置是启用VMware的调试能力。

如果想在虚拟机中安装Hyper-V的话,还需要使用如下配置“欺骗”Windows系统让其觉得在一个可以安装Hyper-V的环境:

  • hypervisor.cpuid.v0 = "FALSE"
  • mce.enable = "TRUE"
  • vhv.enable = "TRUE"

修改完虚拟机配置后,启动虚拟机,此时的虚拟机会直接黑屏。这时打开IDA,在菜单中选择Debugger->Attach->Remote GDB Debugger,连接本地调试器,端口号为8864,如下图。

随后选择attach到目标进程选项后,会弹出IDA的调试窗口,此时IDA调试窗口显示当前RIP所在的位置,如下图。

从图中可以看出,当前RIP的显示还是有问题,所以还需要添加内存的地址范围。打开菜单-> Debugger→Manual memory regions,在Manual memory regions窗口中按下insert键,添加一个0~0xffffffffffffff00的地址范围。如下图。

此时按F9可以继续虚拟机的运行,如果需要调试就按下suspend进行调试。

IDA调试的优点是无需操作系统支持调试,而且虚拟机中无法检测调试器的存在,但是缺点是需要手动加载符号,调试时也不如windbg那样方便。

六、USB3.0 DCI调试

USB3.0 DCI调试听起来相对比较陌生,那么DCI是什么呢?DCI的全称是Direct Connect Interface,是Intel平台中提供的调试方法,这个技术主要实现了可以直接通过使用USB3.0端口来调试目标系统。说白了其实就是这个技术实现了直接通过一根USB3.0调试线调试被调试机,而且这种办法是JTAG调试,调试器会暂停CPU的运行,是一种硬件级别的调试。

但Intel平台的调试支持并不是一个新技术,在过去Intel平台的硬件调试器是一个被大家叫做蓝盒子的调试器。这个调试器官方叫它Intel ITP(In Target Probe),ITP要求在被调试机的主板制造时预留并安装ITP/XDP接口,并且如果你使用ITP进行调试必须要购买Intel的硬件调试器,也就是“蓝盒子”,官方购买的话大概要几千美元。所以在过去,Intel平台的硬件调试几乎是给Intel的合作伙伴使用的,用作硬/软件的开发和调试。

不过现在好日子来了,DCI在Intel Skylake系列之后的产品中,硬件上都支持DCI功能。也就是说,从Intel 6代的CPU和100系列芯片组之后的产品都会支持DCI功能。

但是坏消息是市面上大多数的主板都会默认关闭DCI这个功能,当然这是考虑到安全性问题。所以我们如果要启用这个功能,则需要修改主板的BIOS。那目前可以进行BIOS修改的主板有支持AMI BIOS的主板,例如华硕,微星等。

修改BIOS第一个办法可以使用AMIBCP工具修改BIOS镜像。例如这里通过修改BIOS镜像中的Setup →PCH-IO Configuration→DCI Enable(HDCIEN)选项,将其设置为Enabled。如下图。

随后保存镜像并刷BIOS。

第二种办法是使用UEFITOOL和IFRExtractor工具将BIOS固件导出IRF(UEFI Internal Form Representation)表。并在IRF表中寻找如下字段:

  • Debug Interface
  • Debug Interface Lock
  • DCI enable(HDCIEN)

记录下这些字段的VarStoreInfo (VarOffset/VarName)VarStore的值,例如作者这里的信息如下图。

可以看到这些字段的VarStore的值都是1,此时我们翻到IRF表的最前头,查找VarStoreId为1的条目。如下图。

可以看到这个是BIOS选项里的Setup条目。

下面重启被调试机,使用RU.exe工具修改BIOS,注意这里的RU工具版本可以选择旧一点的,新版的可能会有bug,笔者这里选择的RU版本是5.28.0397。

进入到RU的界面后,按下ALT+=进入BIOS界面,选择Setup条目,然后根据我们找的上文中那三个字段的VarOffset值来修改对应字段的配置。

这里我们需要将字段的配置修改至如下状态:

  • Debug Interface → 1
  • Debug Interface Lock → 0
  • DCI enable(HDCIEN) → 1

修改Debug Interface字段是为了启用硬件调试功能;Debug Interface Lock如果为enable会影响CPU的频率,如果不修改CPU可能会以0.x Ghz的频率运行;DCI enable是开启DCI功能的设置。

修改完成后,被调试机的准备工作就做完了。除此之外,我们还需要一条USB3.0调试线,具体的制作方法前文已经介绍了。

此时我们需要配置下调试端的环境,首先安装Intel DCI驱动,这个驱动是将被调试机的USB端口识别成Intel USB Native Debug Class设备。运行Setup_x64_Intel_DCI_Driver_1.10.0.0.msi并安装驱动,安装驱动完成并且连接好调试机和被调试机后,启动被调试端,调试端的设备管理器就会显示Intel USB Native Debug Class Devices设备,这说明DCI运行良好。如下图。

配置好调试机环境后,使用我们提供的DCI调试相关的工具包,安装python库ipccli。使用ipccli.baseaccess()连接被调试机,再使用ipc.status()查看被调试机CPU状态。如下图。

也可以使用另外一种办法进行调试,首先到DCI调试相关的工具包的位置,然后打开%DCI工具包%\windbg-ext\iajtagserver\intel64\路径,以管理员权限运行regsvr32.exe ExdiIpc.dll。这一步骤是为了注册COM组件,可以让后面使用windbg exdi调试时唤起IntelExdiServer。

下面运行工具包中的windbg_iajtag_console.bat批处理连接上被调试机,并在批处理窗口中输入windbg()命令开始进行调试。如下图。

目前的windbg已经连接上了被调试机,但是还没有加载上NT内核以及其他内核模块的符号。我们可以使用.scriptload reload_manual.js加载查找内核模块地址的脚本,并使用!reloadmod查找并加载符号。如下图。

七、总结

优缺点分析如图所示。

与其他的调试方式不同的是,DCI调试可以在ring-2层级上工作,也就是说可以进行SMM的调试,也可以调试比如Intel ME组件。