Windows 双机调试全攻略:虚拟机配置与 Windbg 远程调试实战

开启双机调试

虚拟机开启/关闭 debug 调试

bash 复制
// 开启虚拟机调试
bcdedit /bootdebug ON
bcdedit /debug ON

// 关闭 debug 调试
bcdedit /bootdebug off 
bcdedit /debug off

在虚拟机中,使用管理员权限,在cmd中执行如下命令,关闭数字签名验证功能:

bash 复制
# 禁用强制签名
bcdedit /set nointegritychecks on
bcdedit -set loadoptions DDISABLE_INTEGRITY_CHECKS
# 开启系统测试模式
bcdedit /set testsigning on
复制

相应的,虚拟机中,管理员权限下,执行如下命令,打开数字签名验证功能:

bash 复制
bcdedit -set loadoptions ENABLE_INTEGRITY_CHECKS
bcdedit /set testsigning off
bcdedit /set nointegritychecks off

双机远程调试

COM远程调试

在虚拟机中,使用管理员权限,在cmd中执行如下命令,打开com端口远程调试功能:

复制
bcdedit /dbgsettings serial debugport:1 baudrate:115200

在主机中需要如下命令执行windbg:

bash 复制
"C:\Program Files\Debugging Tools for Windows (x64)\windbg.exe" -b -k com:pipe,port=\\.\pipe\com1,baud=115200,reconnect -y

关闭双机调试

bash 复制
# 要完全移除串口调试的设置,可以使用以下命令:
bcdedit /bootdebug off 
bcdedit /debug off

网络远程调试

由于串口调试太慢,只有11200b,实际操作时往往卡到无法忍受,在windows10及以后的系统中,在虚拟机中执行如下命令,可以打开网络调试功能:

在虚拟主机中开启网络调试(只支持windows10以及以后的版本):

bash 复制
bcdedit /dbgsettings net hostip:192.168.101.130 port:50000 key:1.2.3.4

其中192.168.101.130是 windbg 所在的主机 ip 地址,key 是用来标识多个连接的健。

主机中的windbg启动后,在windbg菜单:“File”->"kernel debug"的"key"一栏中输入命令中的key值:1.2.3.4后,即可连接到虚拟机。 此时,可以等待虚拟机中触发断点以便调试。

75efae1f7943600b962e9aa945576464.jpeg c8f8950b6e623fbc383cc7b5d8061fa5.jpeg

c 复制
vs中注释快捷键:
ctrl + /
ctrl + u

启动过程添加断点

在 vista 及后续的 windows 已经取消了 ntldr 模块管理引导,转而以 bootmgr 模块管理引导系统。在我的 windows 7中要设定在哪个部分进行调试。windws 7 可调试部分有 4 个:bootmgr 模块、winload 模块、WinResume 模块以及 windows 内核模块 Nt 模块

在 {bootmgr} 模块中下断点

(1) 以管理员身份运行“命令提示符”

(2) 在“命令提示符”窗口中,输入以下命令:

bash 复制
bcdedit /set {bootmgr} bootdebug on
bcdedit /set {bootmgr} debugtype serial
bcdedit /set {bootmgr} debugport 1
bcdedit /set {bootmgr} baudrate 115200

在 {winload} 模块下断点

同样在“命令提示符”中使用命令

bcdedit /enum

这条命令显示当前可用模块的 GUID

在非测试模式下使用驱动签名

生成根证书

bash 复制
# 生成私钥(生成根证书所需的私钥部分)
openssl genrsa -out ca.key 2048
# 或者:openssl genpkey -out mykey.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048

# 创建 CSR (Certificate Signing Request)
openssl req -new -key ca.key -out ca.csr

# OpenSSL创建的自签名证书在chrome端无法信任,需要添加如下
# 根据实际情况:echo "subjectAltName=DNS:rojao.test.com,IP:10.10.2.137" > cert_extensions

# 生成自签名证书(CA 根证书)
# 如果有cert_extensions 可以执行:openssl x509 -req -days 365 -in ca.csr -signkey ca.key -extfile cert_extensions -out ca.crt
openssl x509 -req -days 365 -in ca.csr -signkey ca.key -out ca.crt

# 验证证书,这将显示证书的所有详细信息,包括有效期、公钥信息、签名算法等
openssl x509 -noout -text -in ca.crt

签发时间戳证书

生成一个时间戳服务证书(TSA 证书),并用你的 CA 证书对其进行签名。

bash 复制
# 生成 TSA 的私钥
openssl genrsa -out tsa.key 2048

# 生成 TSA 的 CSR
# 注意如果在 git-bash 下使用 -subj 参数,可能有命令行错误,可以通过 -config 方式加载配置项.
openssl req -new -key tsa.key -out tsa.csr

# 用 CA 证书签发 TSA 证书
openssl x509 -req -days 365 -in tsa.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out tsa.crt

配置项(openssl.cnf)

openssl req -new -key rootCA.key -out rootCA.csr -config openssl.cnf

bash 复制
[req]
default_bits = 2048
prompt = no
distinguished_name = dn
[dn]
C = CN
ST = Beijing
L = Beijing
O = MyCompany
CN = MyRootCA

Windbg 常用命令

标准命令

复制
g - 继续执行程序直到遇到下一个断点或者异常。
k - 显示调用堆栈(Call Stack)。
p - 执行下一条指令。
t - 执行当前线程直到遇到下一个断点或异常。
dt - 显示类型的数据内容。
dv - 显示变量的值。
dps - 显示内存地址的内容。
u - 反汇编指定地址的代码。
bp - 设置断点。
bl - 列出所有断点。
bc - 清除指定断点。
ba - 设置硬件访问断点。
bm - 设置消息断点。
ln - 查找符号地址。
x - 显示内存区域或模块的符号信息。
r - 显示或修改寄存器内容。
q - 退出调试器。
.time - 显示或设置时间截断选项。

元命令

复制
.chain - 显示当前调试器链。
.load - 加载调试器扩展插件。
.unload - 卸载调试器扩展插件。
.restart - 重新启动目标计算机或进程。
.sympath - 设置符号路径。
.reload - 重新加载符号表。
.logopen - 打开调试器日志文件。
.logclose - 关闭调试器日志文件。

扩展命令

复制
!analyze - 分析当前异常。
!address - 显示内存使用情况。
!heap - 显示堆信息。
!pool - 显示非分页池信息。
!locks - 显示锁状态。
!threads - 显示线程列表。
!teb - 显示当前线程环境块信息。
!process - 显示进程信息。
!handle - 显示句柄信息。
!token - 显示安全令牌信息。

示例

bash 复制
# 从某个地址开始,以某个类型展示当前内存结构
dt 【数据类型】 【起始地址】

# 每1字节为一个值从起始地址展示内存值
db 【起始地址】
# 每2字节为一个值从起始地址展示内存值
dw 【起始地址】
# 每4字节为一个值从起始地址展示内存值
dd 【起始地址】
# 每8字节为一个值从起始地址展示内存值
dq 【起始地址】

# 继续执行程序
g

# 查看进程信息
!process 0 0 【进程名称】
# 查看对象信息
!object 【对象地址】

# 从当前地址进行反汇编显示,[NumberOfLines]表示多显示的行数
u [Address] L[NumberOfLines]
# 示例:
u nt!ZwCreateFile L10

# 给 OpenProcess 函数下一个断点
bp kernel32!OpenProcess

# 显示当前调试的进程
.process /r
# 分离当前正在调试的进程
.detach

# 查找显示模块中指定名称的符号信息。
x i8042ptr!*read* // 查找 i8042ptr模块中,包含read 符号的相关函数