本页目录

使用QEMU在ARM64系统上调试x86_64程序

最近有时候会做一些CTF的逆向/二进制利用题目,目前的环境是Ubuntu 22.04.2 ARM64(MacOS上的Parallels Desktop虚拟机),然而大部分题目的可执行文件都是x86_64的,虽然可以利用Rosetta来正常运行x86_64的程序,但是没办法顺利调试。

由于实在不想在两台电脑上来回切换,所以花了两三天时间找了找解决方案,目前是在我的Ubuntu ARM64系统上用QEMU又套了一个x86_64的虚拟机,用GDB远程调试。在这里记录下配置的全过程~

之前的尝试

Windows笔记本 + VMware虚拟机

没什么问题,就是感觉搞两台电脑太麻烦了,所以才有了本文...

QEMU user mode + gdb-multiarch

首先,这种方式是可以成功调试的,具体步骤为:

1.

qemu-x86_64 -g 1234 /path/to/binary,这时会在1234端口上监听,等待GDB连接;

2.

然后新开一个终端,执行:gdb-multiarch /path/to/binary

3.

进入GDB后,执行target remote :1234,就可以调试了。

(这一步如果使用GDB插件GEF,需要进入GDB后执行gef-remote --qemu-user --qemu-binary /path/to/binary localhost 1234,参考gef-remote qemu-mode。)

然而QEMU user mode和GDB远程协议交换的信息不全,不足以在本地建立起等价的/proc/[pid]/maps(参考issue),这会导致一些原生命令如info proc mappings或者插件命令vmmapsearch-pattern等无法正常工作。

当前解决

所以,最后还是决定用QEMU模拟一个完整的x86_64虚拟机,一方面可以解决GDB调试的问题,另一方面有了完整的x86_64环境,也方便用来做一些其他的事情。

安装QEMU和x86_64系统

1.

sudo apt install qemu-user qemu-system-x86

2.

接下来选择一个磁盘镜像,我选择的是debian-12-nocloud-amd64.qcow2,可以直接在这里下载,注意选择nocloud版本,最轻量,默认用户root,无密码。

这一步原则上只要是x86_64的系统就行,也可以从.iso镜像安装,或者选择一些其他的最小化Linux发行版(有的只有几十MB大小)。个人感觉这个Debian镜像站还不错,可以下载.qcow2文件,QEMU可以免安装运行;并且包含glibc(Alpine就不包含,不过Alpine镜像更小),和大部分题目的环境一致。

3.

尝试启动系统:qemu-system-x86_64 -m 1024 -hda /path/to/debian-12-nocloud-amd64.qcow2 -nographic

-hda:下载的磁盘镜像路径;

-nographic:不启动图形界面,直接输出到终端。

4.

登录系统,用户名root,默认无密码。

看到下面的结果就说明成功了~

img

在x86_64系统上安装gdbserver

1.

sudo apt update

2.

sudo apt install gdbserver

配置共享文件夹和端口转发

下面语境中“主机”指的是运行QEMU的Ubuntu 22.04.2 ARM64虚拟机。

在这一步,我们让QEMU的x86_64系统和主机之间共享一个目录,可以把题目放在共享文件夹里。(对于我的环境,我可以让MacOS、Ubuntu ARM64虚拟机以及Ubuntu中的QEMU的x86_64系统共享同一个文件夹,共享文件很方便。)

同时,目前我使用user网络模式,QEMU的x86_64系统可以访问互联网,但是外部(包括主机)无法直接访问QEMU的x86_64系统,需要配置端口转发。这一步相当于把QEMU虚拟机的端口xxxx转发到主机的端口yyyy,然后主机可以通过localhost:yyyy来访问对应的服务,这一步很关键,QEMU虚拟机上的SSH和gdbserver都需要这种方式访问。

1.

上述功能通过QEMU启动参数来实现配置:

Bash
qemu-system-x86_64 -m 1024 -hda /path/to/debian-12-nocloud-amd64.qcow2 \
    -virtfs local,path=/media/psf/shared,mount_tag=shared,security_model=none,id=shared \
    -device e1000,netdev=net0 \
    -netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::1234-:1234,hostfwd=tcp::1235-:1235,hostfwd=tcp::1236-:1236 \
    -nographic

-virtfs行:启用虚拟文件系统共享,/media/psf/shared是主机上的路径,后面的mount_tag=shared是虚拟机上的挂载点标签,id=shared是QEMU使用的标识符,可以自定义;

-device行:模拟网卡,net0是标识符,下面有用到;

-netdev行:使用user网络模式,转发了虚拟机上的22123412351236四个端口,注意虚拟机的22端口被转发到主机的2222端口,因为主机的22端口通常已经被占用;另外三个端口留给gdbserver或其他目的使用,与转发给主机的端口号一致。

2.

关闭QEMU虚拟机,用上述命令再次启动;

3.

还需要在虚拟机中挂载共享目录:

cd /mnt

mkdir shared

mount -t 9p shared /mnt/shared

-t:指定文件系统类型,QEMU的-virtfs默认使用9P文件系统。

到这里,在QEMU虚拟机执行ls /mnt/shared应当可以看到主机上的共享文件夹的内容。

gdbserver + pwndbg远程调试

对比了一下GEF和pwndbg,感觉pwndbg远程调试的命令更简洁一些,并且自带其他架构,不需要安装gdb-multiarch,所以决定使用pwndbg。

1.

在Ubuntu ARM64上安装pwndbg,在pwndbg/releases中选择pwndbg_yyyy.mm.dd_arm64.deb下载。

2.

sudo dpkg -i pwndbg_yyyy.mm.dd_arm64.deb

接下来在QEMU虚拟机中启动gdbserver,监听1234端口,并在主机中连接。

3.

QEMU虚拟机终端中执行:gdbserver :1234 /mnt/shared/binary,这里假设二进制文件binary已经拷贝到共享目录中。

4.

主机终端中执行:pwndbg,进入调试界面说明前面安装成功了。

5.

主机pwndbg调试界面执行:target remote :1234

img

至此就配置成功了!验证一下vmmapsearch均可以正常使用~

img

更多配置

自动挂载共享目录

每次重启QEMU虚拟机都需要手动挂载共享目录比较麻烦,可以在/etc/fstab中添加一行来实现自动挂载,在QEMU虚拟机中执行:

Bash
echo "shared /mnt/shared 9p trans=virtio,access=any 0 0" | sudo tee -a /etc/fstab

SSH

安装与配置

调试程序不需要SSH,但是建议装一下,便于从主机连接,可以实现远程开发之类的。

1.

sudo apt install openssh-server

2.

我使用的qcow2镜像默认无密码,SSH连接时会提示Permission denied,需要修改SSH配置文件sshd_config

在QEMU虚拟机中:sudo vi /etc/ssh/sshd_config

找到以下两行:

Plain Text
#PermitRootLogin prohibit-password
...
#PermitEmptyPasswords no

修改为:

Plain Text
PermitRootLogin yes
...
PermitEmptyPasswords yes
3.

保存退出后,重启SSH服务:sudo systemctl restart sshd

4.

在Ubuntu主机中连接:ssh -p 2222 root@localhost

-p:指定端口,前面QEMU启动时已经将虚拟机的22端口转发到主机的2222端口,所以这里连接localhost:2222

img

下面语境中“主机”指的是MacOS。

5.

如果需要,也可以在MacOS中SSH连接到QEMU虚拟机,我的Ubuntu ARM64 Parallels Desktop虚拟机使用“共享网络”模式,MacOS主机可以访问到Ubuntu虚拟机。在Ubuntu虚拟机中执行ip addr,查到Ubuntu虚拟机的IP地址:

img
6.

然后在MacOS主机中SSH连接:ssh -p 2222 root@10.211.55.3

img