什么是 qemu
qemu 是一款由 Fabrice Bellard 等人编写的可以执行硬件虚拟化的开源托管虚拟机,具有运行速度快(配合 kvm),跨平台等优点。
qemu 通过动态的二进制转化模拟 CPU,并且提供一组设备模型,使其能够运行多种未修改的客户机OS。
在 ctf 比赛中,qemu 多用于启动异架构(mips, arm 等)的程序、kernel 和 bootloader 等二进制程序,有时也会作为要 pwn 掉的目标。
运行模式
qemu 有多种运行模式,常用的有 User-mode emulation
和 System emulation
两种。
User-mode emulation
用户模式,在这个模式下,qemu 可以运行单个其他指令集的 linux 或者 macOS/darwin 程序,允许了为一种架构编译的程序在另外一种架构上面运行。
System emulation
系统模式,在这个模式下,qemu 将模拟一个完整的计算机系统,包括外围设备。
之后将分别为两种情况举例
安装 qemu
使用包管理
一般情况下,如无特殊需要(如为了运行某个 CTF 比赛中的异架构程序或者 kernel)直接使用对应的包管理直接安装即可
1
2
3
4
5
6
7
8
9
10
11
|
Arch: pacman -S qemu
Debian/Ubuntu: apt-get install qemu
Fedora: dnf install @virtualization
Gentoo: emerge --ask app-emulation/qemu
RHEL/CentOS: yum install qemu-kvm
SUSE: zypper install qemu
|
这里只说明在 linux 下的安装过程,其他系统的安装过程请参考 官方网站
从源码编译
通过包管理安装的 qemu 版本一般较老,如果需要新版的 qemu,可以从源码编译,这里以编译 3.1.0-rc3
的 qemu 为例。
1
2
3
|
wget https://download.qemu.org/qemu-3.1.0-rc3.tar.xz
tar xvJf qemu-3.1.0-rc3.tar.xz
cd qemu-3.1.0-rc3
|
通过 ./configure --help
的查看编译时的选项, --target-list
选项为可选的模拟器,默认全选
--target-list
中的 xxx-soft
和 xxx-linux-user
分别指系统模拟器和应用程序模拟器, 生成的二进制文件名字为 qemu-system-xxx
和 qemu-xxx
安装必要依赖
1
|
sudo apt-get install git libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev libsdl-dev
|
选择安装 可选依赖
这里直接使用默认选项进行编译
继续安装
成功安装
1
2
3
|
~ qemu-arm --version
qemu-arm version 3.0.93
Copyright (c) 2003-2018 Fabrice Bellard and the QEMU Project developers
|
使用 qemu
以 CISCN 2017 的 babydriver 举例,查看启动脚本
1
2
3
4
5
6
7
8
|
CISCN2017_babydriver [master●] bat boot.sh
───────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: boot.sh
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ #!/bin/bash
2 │
3 │ qemu-system-x86_64 -initrd rootfs.cpio -kernel bzImage -append 'console=ttyS0 root=/dev/ram oops=panic panic=1' -enable-kvm -monitor /dev/null -m 64M -
│ -nographic -smp cores=1,threads=1 -cpu kvm64,+smep
|
可以看出这道题目是用 qemu-system-x86_64
启动了以 rootfs.cpio
为文件系统的 kernel bzImage
,启动时的参数为 console=ttyS0 ... panic=1
,为这个进程分配 64M 内存。
更多参数的含义请通过 -h
或者 qemu-doc 查看。
如果使用包管理安装 qemu,直接安装 qemu-system-x86_64
即可
1
|
sudo apt install qemu-system_x86-64
|
因为使用了 kvm,所以启动时要用 root 权限启动
1
2
3
4
5
6
7
8
|
CISCN2017_babydriver [master●] sudo ./boot.sh
......
......
/ $ id
uid=1000(ctf) gid=1000(ctf) groups=1000(ctf)
/ $ ls
bin etc init linuxrc root sys usr
dev home lib proc sbin tmp
|
这道题目的更多分析可以看 link
同样,再看下 Codegate 2018 的Melong
1
2
3
4
5
6
7
8
9
10
|
Codegate2018_Melong [master] check melong
+ file melong
melong: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=2c55e75a072020303e7c802d32a5b82432f329e9, not stripped
+ checksec melong
[*] '/home/m4x/Projects/pwn_repo/Codegate2018_Melong/melong'
Arch: arm-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x10000)
|
可以看出是 32 位 的 arm 程序,需要安装 qemu-arm
如果使用包管理安装,则
1
2
|
$ sudo apt-get install qemu-user
$ sudo apt-get install qemu-use-binfmt qemu-user-binfmt:i386
|
这样就安装了 qemu-arm
但同时因为程序是动态链接的,还需要同时安装对应的 libc,可以使用 apt search "libc6-" | grep "ARCH"
搜索,如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
Codegate2018_Melong [master] apt search "libc6-"| grep arm
p libc6-arm64-cross - GNU C Library: Shared libraries (for cross-compiling)
v libc6-arm64-dcv1 -
v libc6-armel-armel-cross -
p libc6-armel-armhf-cross - Dummy package to get libc6:armel installed
p libc6-armel-cross - GNU C Library: Shared libraries (for cross-compiling)
v libc6-armel-dcv1 -
p libc6-armhf-armel-cross - Dummy package to get libc6:armhf installed
v libc6-armhf-armhf-cross -
p libc6-armhf-cross - GNU C Library: Shared libraries (for cross-compiling)
v libc6-armhf-dcv1 -
p libc6-dbg-arm64-cross - GNU C Library: detached debugging symbols (for cross-compiling)
v libc6-dbg-arm64-dcv1 -
p libc6-dbg-armel-cross - GNU C Library: detached debugging symbols (for cross-compiling)
v libc6-dbg-armel-dcv1 -
p libc6-dbg-armhf-cross - GNU C Library: detached debugging symbols (for cross-compiling)
v libc6-dbg-armhf-dcv1 -
p libc6-dev-arm64-cross - GNU C Library: Development Libraries and Header Files (for cross-compiling)
v libc6-dev-arm64-cross:i386 -
v libc6-dev-arm64-dcv1 -
v libc6-dev-armel-armel-cross -
p libc6-dev-armel-armhf-cross - Dummy package to get libc6-dev:armel installed
p libc6-dev-armel-cross - GNU C Library: Development Libraries and Header Files (for cross-compiling)
v libc6-dev-armel-cross:i386 -
v libc6-dev-armel-dcv1 -
p libc6-dev-armhf-armel-cross - Dummy package to get libc6-dev:armhf installed
v libc6-dev-armhf-armhf-cross -
p libc6-dev-armhf-cross - GNU C Library: Development Libraries and Header Files (for cross-compiling)
v libc6-dev-armhf-cross:i386 -
v libc6-dev-armhf-dcv1 -
|
只需要安装 libc6-ARCH-cross
的包即可。
装好后使用 -L
指定共享库路径即可运行文件。
1
|
$ qemu-arm -L /usr/arm-linux-gnueabi ./melong
|
这道题目的更多分析可以看 link
如果是静态的程序,不需要 libc,则可以不用 -L
选项,如 Jarvis-OJ 的 typo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
jarvisOJ_typo [master] check ./typo
+ file ./typo
./typo: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=211877f58b5a0e8774b8a3a72c83890f8cd38e63, stripped
+ checksec ./typo
[*] '/home/m4x/Projects/pwn_repo/jarvisOJ_typo/typo'
Arch: arm-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8000)
jarvisOJ_typo [master] qemu-arm ./typo
Let's Do Some Typing Exercise~
Press Enter to get start;
Input ~ if you want to quit
^C
|
如何 debug
分两种情况
- 调试 qemu 这个进程
- 调试 qemu 内运行的程序
调试 qemu
对于第一种情况,直接使用 gdb attach 到 qemu 的进程号即可,为了调试时方便可以在编译时加上 --enable-debug
选项以保留符号等信息。
1
|
--enable-debug enable common debug build options
|
在之后的 qemu 逃逸中会着重介绍这个过程。
调试 qemu 中的进程
qemu 提供了 gdb 的接口,通过 -g
指定端口来调用
1
|
-g port QEMU_GDB wait gdb connection to 'port'
|
同时为了调试异架构的程序,需要安装 gdb-multiarch
1
|
sudo apt install gdb-multiarch
|
例如 Melong 中,使用
1
|
$ qemu-arm -g 1234 -L /usr/arm-linux-gnueabi ./melong
|
启动程序,在另一个 shell 中使用 gdb-multiarch
启动程序并连接到指定的端口即可调试
1
2
3
4
5
|
Codegate2018_Melong [master] gdb-multiarch ./melong -q
pwndbg: loaded 175 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ./melong...(no debugging symbols found)...done.
pwndbg> target remote localhost:1234
|
使用 gdb-multriarch 可以调试大多数的程序。
但也有部分程序不能使用 gdb-multiarch,这时可以编译对应架构的 Toolchain,如 arm-none-eabi-gdb
或者使用系统模式的 qemu 创建一个对应架构的虚拟机,文末放了一片链接,以后也会介绍这种方法。
特别的是系统模式的 qemu 还提供了另外几个参数
1
2
3
|
-gdb dev wait for gdb connection on 'dev'
-s shorthand for -gdb tcp::1234
-S freeze CPU at startup (use 'c' to start execution)
|
-gdb
作用类似 -g
,使用 -gdb tcp::1234
即可在 gdb 中通过 1234 端口调试。
-s
是 -gdb tcp::1234
的缩写
-S
让虚拟机停在启动的地方方便调试,类似于 pwntools 的 gdb.debug()
Reference
https://wiki.qemu.org/Main_Page
https://qemu.weilnetz.de/doc/qemu-doc.html
https://wiki.qemu.org/Documentation
https://en.wikipedia.org/wiki/QEMU
https://www.ringzerolabs.com/2018/03/the-wonderful-world-of-mips.html
https://wiki.qemu.org/Hosts/Linux