要在Bochs虚拟机里运行和调试PE文件,最稳妥的办法是让Bochs启动一个Windows虚拟机系统,然后在PE入口地址下断点,通过内置调试器或GDB单步跟踪。
Bochs虚拟机运行PE文件前的准备工作
在动手之前,先想清楚一个关键问题:Bochs不是那种“双击PE文件就能跑”的模拟器,它模拟的是整台PC,所以我们得给它准备一个可引导的磁盘,再在磁盘里搭建一个能执行PE文件的系统环境,准备工作的质量直接决定后面调试顺不顺手。
Bochs和QEMU哪个适合调试PE文件
不少人在选择模拟器时会纠结“Bochs和QEMU哪个适合调试PE文件”,行业共识认为,Bochs在指令级调试上更占优势,因为它的内置调试器可以直接拦截内存访问和指令执行,不需要额外装插件,QEMU启动速度快,日常运行虚拟机很方便,但它的调试能力主要依赖外接GDB,断点类型和操作灵敏度都比Bochs略逊一筹。
Bochs是开源免费软件,不存在授权费用问题,这对长期做逆向分析或恶意代码研究的场景非常友好。
安装带调试器的Bochs
Bochs默认发行版不一定开启调试功能,我们需要自己编译一个调试版本,从官网下载源码后,在目录下执行:
./configure --enable-debugger --enable-disasm
make
参数解释:
--enable-debugger:启用内置调试器,让Bochs启动后进入bochs>交互提示符。--enable-disasm:启用反汇编器,方便在断点处查看汇编指令。
编译完成后,输入bochs -q启动,如果能看到<bochs:1>这样的提示符,说明调试器已经正常工作。
准备Windows虚拟磁盘

PE文件是Windows可执行格式,所以虚拟磁盘里必须装一个Windows系统,先用bximage建立虚拟磁盘:
bximage -mode=create -hd=4096 -imgmode=flat winxp.img
参数含义是创建一个4GB的flat格式磁盘镜像,接着把Windows安装ISO挂载到Bochs光驱,写入下面配置:
ata0-master: type=disk, path="winxp.img", mode=flat
ata0-slave: type=cdrom, path="windows.iso", status=inserted
boot: cdrom
启动Bochs后按正常流程安装Windows,装完系统,调试环境的基础就搭好了。
在Bochs虚拟机中运行PE文件的步骤
系统准备好之后,剩下的问题是如何把你的PE文件送进虚拟机并运行起来,这一步操作不复杂,但有一个容易踩坑的细节:文件拷贝方式。
把PE文件复制进虚拟磁盘
在Linux主机上,可以使用guestmount挂载虚拟磁盘,然后直接复制,例如挂载到/mnt/vdisk:
guestmount -a winxp.img -i /mnt/vdisk
cp demo.exe /mnt/vdisk/
umount /mnt/vdisk
如果你的PE文件放在ISO镜像里,还可以用mkisofs做一个数据光盘,再在虚拟机的Windows环境中拷贝到本地磁盘,两种方法都验证过,后者的兼容性更好,对NTFS分区不敏感。
启动后执行PE程序
配置Bochs启动磁盘:
boot: disk
启动进入Windows后,打开命令行,运行你的PE文件,如果只是单纯运行,这一步就结束了,但为了调试方便,建议提前在Windows中关闭PE文件的ASLR,这样程序每次加载的基址是固定的,断点才能精准命中。
Bochs调试PE文件的几种方式
接下来是核心部分,Bochs的调试能力体现在两种接入方式:内置调试器和GDB远程调试,你可以按需求选一种,也可以混合使用。

如何在Bochs中给PE文件设置断点
先获取PE入口地址,用任意PE查看工具读取文件头中的ImageBase和AddressOfEntryPoint,两者相加就是入口地址,一个32位PE的ImageBase是0x00400000,AddressOfEntryPoint是0x00001000,那么入口地址就是0x00401000。
启动Bochs调试版,进入bochs>提示符,设置线性断点:
break 0x00401000
然后输入c继续运行,此时Bochs会觉得“系统中没有程序运行到自杀式编号”,但在Windows里手动运行目标PE,当CPU跳转到该地址时,Bochs会立即暂停,再用regs查看寄存器状态,用u 0x00401000反汇编当前指令,就能跟踪到PE的启动代码。
用GDB远程调试Bochs里的PE文件
在bochsrc.txt中追加一行:
gdbstub: enabled=1, port=1234
启动Bochs后,在另一个终端连接GDB:
gdb
target remote :1234
set architecture i386
break 0x00401000
这种方式的优势在于,GDB可以让断点挂在下一次执行时,也可以设置条件断点,比如当某个寄存器等于特定值时触发,注意,GDB连接的是整个虚拟机,不是某个进程,所以依然需要在Windows虚拟机内部手动运行PE来触发断点。
一次实际调试会话的完整命令序列
串成实际会话流程:
- 在终端启动Bochs调试版,看到
<bochs:1>提示符。 - 输入
b 0x00401000设置线性断点。 - 输入
c让虚拟机继续运行。 - 切换到Windows虚拟机窗口,运行
。
demo.exe
- 回看Bochs窗口,此时已经停在
0x00401000,输入regs查看寄存器。 - 输入
u 0x00401000查看PE入口处的反汇编代码。 - 用
s单步执行,或n步过调用,逐条分析指令。
这套流程不需要额外软件,干净直接。
Bochs调试PE文件常见报错排查与Q&A
问题:Bochs启动后没有出现bochs>提示符?
原因通常是编译时没有开启--enable-debugger,重新配置并编译,确认启动时的输出信息里能看到Debugger enabled字样。
问题:断点设置了,但一直不命中?
优先检查PE文件的ASLR设置,Windows 10及以上的系统默认对PE进行随机基址重定位,入口地址每次加载都不同,在Windows中关闭系统级随机化,或用工具修改PE的DllCharacteristics字段清除ASLR标记,再试一次,确认你算出的入口地址没写错,常见错误是把RVA当成绝对地址。
问题:用GDB连接Bochs时提示连接被拒绝?
检查bochsrc.txt中的gdbstub配置是否在启动时被加载,同时确认端口号没被防火墙拦截,Bochs和GDB要在同一台物理主机上,不能跨机器连接。
问题:Bochs和QEMU哪个更适合调试PE?
前面已经说过,Bochs的内置调试器在断点拦截和指令级单步方面更灵活,是调试PE文件的首选方案;QEMU更适合日常系统运行和性能测试。
用Bochs调试PE文件,本质上就是利用模拟器的断点能力来拦截CPU执行跳转,掌握好入口地址与断点命令,就能完成精确跟踪。