使用 QEMU/工具链对 ARM/MIPS ELF 进行交叉调试

逆向工程 调试 数据库 小精灵 米普 奇木
2021-06-29 01:23:31

因为我是交叉调试和交叉编译的新手,所以我需要一些帮助,因为我感到很困惑。我有一个 MIPS elf 文件, [myelf][1] 。你可以看到下面的输出file myelf

myelf: ELF 32-bit LSB executable, MIPS, MIPS-I version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, BuildID[sha1]=0xc89c3571514c7ec1afc74a189a9c2d24e276ec4c, with unknown capability 0xf41 = 0x756e6700, with unknown capability 0x70100 = 0x1040000  stripped

我只想运行和调试程序。所以我不需要交叉编译器对吗?由于我没有 MIPS 硬件(我有一个 INTEL 微处理器),我需要一个模拟器。我选择使用 QEMU。根据此站点,我下载了以下内核映像和 initrds:

debian_squeeze_mips_standard.qcow2

vmlinux-2.6.32-5-4kc-malta

然后我为 32 位(因为精灵信息)MIPS 系统运行了指定的命令。

qemu-system-mips -M malta -kernel vmlinux-2.6.32-5-4kc-malta -hda debian_squeeze_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0"

到目前为止,我在一个 shell 中运行了模拟器,命令 uname -a 给了我:

Linux debian-mips 2.6.32-5-4kc-malta #1 Tue Sep 24 00:02:22 UTC 2013 mips GNU/Linux

模拟器上只有非常基本的命令/工具。我已经读到 gdb 可以在我的 x86_64 主机上的远程目标(这里是 MIPS 模拟器)上进行调试。老实说,我不知道我现在应该做什么。我首先尝试在 qemu 模拟器上安装 gdb。当我运行 gdb my elf 时,我可以看到 gdb 自动配置为 mips-linux-gnu。

root@debian-mips:~# gdb myelf 
GNU gdb (GDB) 7.0.1-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "mips-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/myelf...(no debugging symbols found)...done.

信息文件为我提供了正确的信息(我用 IDA 拆卸了精灵,以便我可以确认)。

(gdb) info files 
Symbols from "/root/myelf".
Local exec file:
    `/root/myelf', file type elf32-tradlittlemips.
    Entry point: 0x400670
    0x00400154 - 0x00400161 is .interp
    0x00400164 - 0x00400184 is .note.ABI-tag
    0x00400184 - 0x0040019c is .reginfo
    0x0040019c - 0x004001c0 is .note.gnu.build-id
    0x004001c0 - 0x00400298 is .dynamic
    0x00400298 - 0x0040033c is .hash
    0x0040033c - 0x0040049c is .dynsym
    0x0040049c - 0x0040057b is .dynstr
    0x0040057c - 0x004005a8 is .gnu.version
    0x004005a8 - 0x004005d8 is .gnu.version_r
    0x004005d8 - 0x00400668 is .init
    0x00400670 - 0x00400b00 is .text
    0x00400b00 - 0x00400ba0 is .MIPS.stubs
    0x00400ba0 - 0x00400bec is .fini
    0x00400bec - 0x00400c2c is .rodata
    0x00400c2c - 0x00400c30 is .eh_frame
    0x00410c30 - 0x00410c3c is .ctors
    0x00410c3c - 0x00410c44 is .dtors
    0x00410c44 - 0x00410c48 is .jcr
    0x00410c50 - 0x00410e00 is .data
    0x00410e00 - 0x00410e04 is .rld_map
    0x00410e10 - 0x00410e6c is .got
    0x00410e6c - 0x00410e70 is .sdata
    0x00410e70 - 0x00410e80 is .bss

但是当我想运行程序时什么都没有发生:

(gdb) r
Starting program: /root/myelf 

我等了大约 10 分钟,什么也没发生。(通常程序应该打印一个字符串“Usage : ./myelf password”,因为我没有给出任何参数)。然后我尝试在模拟器上使用 gdbserver 和在主机上为 mipsel 处理器配置的 gdb 但它没有工作..

我可能会做错或愚蠢的事情,因为我很困惑。如果有人能告诉我我的过程出了什么问题,或者有人试图运行myelf 文件,我会知道他是如何做到的,以便能够在不同的机器上运行任何程序。

谢谢你,祝你有美好的一天!

4个回答

准备好冒险!

你需要一些东西来完成你的任务!让我们从头开始。

QEMU 和 GDB

QEMU 是适用于各种架构的模拟器。通常,它用于模拟整个 PC(即运行虚拟机)。但是,对于调试单个程序,这不是必需的。在 Linux 上,您可以使用 QEMU 用户空间模拟。

$ sudo apt-get install qemu qemu-user qemu-user-static

此外,Ubuntu 和类似操作系统默认安装的 GDB 对其他架构一无所知。幸运的是,有一个gdb-multiarch包可以做到!

$ sudo apt-get install gdb-multiarch

最后,Linux 通常依靠#!shell 脚本顶部的 shebang ( ) 来通知它使用什么解释器。对于二进制文件,没有这样的标准。为了填补这个空白,binfmt可以使用包来查看文件的类型,并自动调用正确的解释器。在我们的例子中,它会看到您正在尝试运行一个 little-endian MIPS( mipsel) 二进制文件并调用qemu-mipsel.

$ sudo apt-get install 'binfmt*'

运行静态链接的二进制文件

对于静态链接的 MIPSEL 二进制文件,这通常是必需的。但是,您链接的那个依赖于外部库。如果它是静态链接的,您现在可以运行它。您可以创建一个示例二进制文件来演示这一点:

$ echo 'int main() {puts("Hello world!");}' > hello.c
$ mipsel-linux-gnu-gcc -xc -static -o mipsel-test hello.c
$ file mipsel-test
a.out: ELF 32-bit LSB executable, MIPS, MIPS-I version 1 (SYSV), statically linked, for GNU/Linux 2.6.18, BuildID[sha1]=2556cc80429de1ab3116278ac10832d72bd7ebab, not stripped
$ ./mipsel-test
Hello world!

图书馆

由于您选择的二进制动态链接,你将需要安装库,例如libc.sold.so用于合适的体系结构。我们还需要告诉binfmt在哪里可以找到它们。

Ubuntu 为 ARM 和 AArch64 提供了跨架构的包。例如:

$ sudo apt-get install libc6-armhf-armel-cross

Ubuntu(如果早于 Ubuntu 16.04 / Xenial)

Ubuntu 14.04 并且不为 MIPS 提供软件包。幸运的是,Debian(Ubuntu 的基础)确实提供了软件包,并且这些软件包与 Ubuntu 兼容。取消幸运的是,Debian不会支持小尾数MIPS(mipsel体系)。再次幸运的是,作为一个不同的 Debian 衍生产品,嵌入式 Debian (emdebian) 确实提供了这些软件包。

您可以使用以下命令将这两个存储库添加到您的 Ubuntu 或其他基于 Debian 的发行版中。 如果您只使用 ARM 或 AArch64,则无需执行此操作

$ sudo apt-get install debian-keyring
$ sudo apt-get install debian-archive-keyring
$ sudo apt-get install emdebian-archive-keyring
$ sudo tee /etc/apt/sources.list.d/emdebian.list << EOF
deb http://mirrors.mit.edu/debian squeeze main
deb http://www.emdebian.org/debian squeeze main
EOF
$ sudo apt-get update
清理

安装完软件包后(见下文),我强烈建议删除emdebian.list我们之前创建的文件虽然 Emdebian 包是兼容的,apt但会做一些奇怪的事情,并且可能会选择使用一个Debian包而不是你的发行版应该使用的包。如果您以后需要安装更多的软件包,您可以再次添加它。

$ sudo rm /etc/apt/sources.list.d/emdebian.list
$ sudo apt-get update

安装库

现在我们可以安装软件包了!包含mipsel运行所选二进制文件所需的所有的包

$ sudo apt-get install libc6-mipsel-cross      # For MIPS-EL
$ sudo apt-get install libc6-armhf-armel-cross # For ARM

如果你想像上面的例子那样构建程序,你需要一个交叉编译器。

$ sudo apt-get install gcc-4.4-mipsel-linux-gnu # For MIPS-EL on Ubuntu 14.04
$ sudo apt-get install gcc-mipsel-linux-gnu     # For MIPS-EL on Ubuntu 16.04
$ sudo apt-get install gcc-arm-linux-gnueabihf  # For ARM

最后,我们需要告诉二进制文件binfmt的库在哪里mipsel

$ sudo mkdir /etc/qemu-binfmt
$ sudo ln -s /usr/mipsel-linux-gnu /etc/qemu-binfmt/mipsel # MIPSEL
$ sudo ln -s /usr/arm-linux-gnueabihf /etc/qemu-binfmt/arm # ARM

现在您可以在您的系统上运行二进制文件*!

$ ./myelf
Usage: ./crackme password

调试

这就是重点,对吧?

斯特拉斯

最快的办法是能够strace在二进制文件上运行你可以这样做:

$ qemu-mipsel -strace ./myelf
12825 brk(NULL) = 0x00411000
12825 mmap(NULL,4096,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) = 0x767ca000
...
12825 ptrace(0,0,0,0,0,0) = -1 errno=89 (Function not implemented)
qemu: Unsupported syscall: 4026

您应该从运行二进制文件以及从strace中看到该二进制文件试图调用ptrace自身。这是 的限制之一qemu-user,即ptrace不受支持。如果二进制文件需要ptrace 自身,您将需要构建一个完整的 QEMU 系统映像。我通常使用这里可用的那些运行 QEMU 不在本答案的范围内,但我链接到的页面具有有用的命令行。

您可以轻松地ptrace从二进制文件中删除调用(它是反调试的东西),并且运行良好。但是,我认为这是crackme的一部分:P。

$./myelf
KO
$ sed -i 's|ptrace|isnanl|' myelf
$ ./myelf
Usage: ./crackme password

QEMU GDB 存根

为了使用 GDB 调试二进制文件,您需要启动qemu-mips以公开 GDB 存根,并从 GDB 连接。

$ qemu-mipsel -g 12345 ./a.out &
$ gdb-multiarch ./a.out
(gdb) set arch mips
The target architecture is assumed to be mips
(gdb) set endian little
The target is assumed to be little endian
(gdb) target remote localhost:12345
Remote debugging using localhost:12345
0x00400280 in _ftext ()
(gdb) x/i $pc
  => 0x767cb880    move   $t9, $ra

您现在可以像往常一样进行调试。请注意,由于您在 内部运行qemu-user,因此某些命令无法按预期工作。特别是,info proc maps不起作用。您可能想看看我的pwndbg项目,它解决了其中的一些限制。

你可以尝试使用radare2工具连接远程gdbserver,下面一行:

r2 -a mips gdb://[address]

qemu-system-mips 使用 Buildroot 进行完整的系统仿真

此方法模拟整个 MIPS CPU,可以克服用户态模拟中提到的限制:https : //reverseengineering.stackexchange.com/a/8917/12321

这个脚本完全自动化了你可以自动化的一切:https : //github.com/cirosantilli/linux-kernel-module-cheat/tree/1d197f35ee177c6ab3e6a2f518292b8dc4e53431#gdbserver

Buildroot里面下载和编译整个目标文件系统对于我们来说,uncluding gdbserver,也是主机QEMU和gdb主要步骤如下。

现在剩下的“唯一”的事情是:

  • 以可执行文件的正确版本交叉编译/安装所有库。stdlib 已经被交叉编译了,但是你仍然可能会遇到版本不匹配的问题......
  • 确保动态加载器配置正确。TODO:Buildroot 是否支持自定义,还是可以在可执行文件本身上打补丁?

以确保您能够运行可执行文件。

但请注意,userland 方法也需要这些步骤。

我的脚本完成的主要步骤是:

  1. gdbserver在 Buildroot 配置上启用

    BR2_DEBUG_3=y
    BR2_ENABLE_DEBUG=y
    BR2_OPTIMIZE_0=y
    BR2_PACKAGE_GDB=y
    BR2_PTHREAD_DEBUG=y
    
  2. BR2_ROOTFS_OVERLAY在 Buildroot 配置上使用以将可执行文件放置在文件系统中。

  3. 使用命令行选项将主机端口转发到 QEMU 目标(此处4545545455):

    -net user,hostfwd=tcp::45455-:45455
    
  4. 启动gdbserver目标:

    gdbserver :45455 myexec
    
  5. 从主机启动交叉编译的 GDB 并将其指向交叉编译的可执行文件和转发的端口:

    buildroot/output/host/usr/bin/mips64-linux-gdb \
      -ex 'target remote localhost:45455' \
      -ex 'tb main' \
      -ex 'c' \
      buildroot/output/build/mypackage-1.0.0/myexec
    

最小工作用户模式示例

作为完整系统的更快更脏的替代方案,您可能会使用用户模式,详情参见:https : //stackoverflow.com/questions/20590155/how-to-single-step-arm-assembly-in-gdb -on-qemu/51310791#51310791

我只想添加一个关于qemu-user的快速收据ptrace

  1. qemu-binfmt在主机上启动服务以注册 qemu-user 二进制文件。
  2. 使用buildah创建AMD64容器。
  3. 在这个 amd64 容器中安装最新的 qemu,内核将使用它们而不是主机。
  4. 在 amd64 容器中构建一些工具链。
  5. 使用常规strace来跟踪您的二进制文件。

例如:

buildah copy 2f64fe2cdbfd /tmp/readdir32.c /tmp/
buildah run --cap-add=CAP_SYS_PTRACE 2f64fe2cdbfd -- sh -c "mips-unknown-linux-gnu-gcc /tmp/readdir32.c -o /tmp/readdir32 && LD_PRELOAD='/usr/mips-unknown-linux-gnu/lib/libc.so.6' strace -v /tmp/readdir32"

write(1, "errno 89 函数未实现"...

工作完美。

完整示例在这里请注意 - 测试图像尚未完成,我仍在与 mips 作斗争。