1 个不稳定版本

0.1.0 2023年10月9日

#506Unix APIs

GPL-3.0-only

63KB
1.5K SLoC

是什么?

追踪程序,以便您可以在没有断点的情况下调试它们。断点很烦人。

记录所有运行的CPU指令、寄存器写入、内存写入和系统调用。

目前它仅适用于Linux程序,包括 x86_64aarch64。程序在docker内部进行追踪,因此您可以从任何操作系统进行追踪。这也意味着您可以追踪不同的架构,因为docker支持通过qemu进行仿真。这也使得许多与libc相关的操作变得可能。这意味着您有高达两层仿真。

设置

大部分没有文档说明,如果您发现任何不明显的内容,请发送电子邮件或创建一个issue!

构建docker容器

我们使用增强的qemu来追踪二进制文件,所以需要编译它

cd tools
./build_all.sh

您也可以手动执行命令

docker build --platform linux/arm64 . -t rebg:arm64
docker build --platform linux/amd64 . -t rebg:amd64

安装

cargo install --path .

如果您不想安装rebg,可以使用以下命令中的 cargo r -r -- 代替 rebg

启动Web UI

您可能想启动Web UI并让它保持运行。

cd web
npm i
npm run dev

使用方法

在docker内部调试二进制文件

最后一个参数指定您是否要在docker内部运行qemu,或者本地运行,如果您已经全局安装了我们的修改过的qemu。

$ rebg ./memory-arm64 docker
stat: 4919
dyn: 52428
arr[]: 0x411040
main: 0x40069c
sp: 0x5500800c50
printf: 0x4004e0
<hangs>

您现在可以访问 Web UI,以调试过去的执行。

不启动Web服务器

如果您不希望rebg启动其WebSocket服务器以供UI使用,可以在执行完成后使其退出。

$ rebg ./memory-arm64 -q docker
stat: 4919
dyn: 52428
arr[]: 0x411040
main: 0x40069c
sp: 0x5500800c50
printf: 0x4004e0
$ 

调试信息

如果某些内容工作不正常,您可以通过将详细程度设置为 tracedebuginfowarnerror 来启用调试输出

$ RUST_LOG="rebg=info" rebg ./memory-arm64 docker
2023-11-02T11:24:16.916549Z  INFO rebg::host::docker: Starting qemu
2023-11-02T11:24:16.917392Z  INFO rebg::tracer::qemu: Waiting for connection...
2023-11-02T11:24:17.071363Z  INFO rebg::tracer::qemu: Connected! TcpStream { addr: [::ffff:127.0.0.1]:1337, peer: [::ffff:127.0.0.1]:59211, fd: 5 }
stat: 4919
dyn: 52428
arr[]: 0x411040
main: 0x40069c
sp: 0x5500800c50
printf: 0x4004e0
2023-11-02T11:24:18.498586Z  WARN rebg::analyzer::dump: Error decoding syscall: BadFormat
2023-11-02T11:24:18.500490Z  INFO rebg::serve: Execution done, starting WS server.

在MacOS上的性能

如果您正在使用macos并追踪 linux/amd64,您可以使用docker中的rosetta将运行时间缩短约60%。在Docker Desktop中,您可以在“开发中的功能”下找到它。

未来工作

  • 引脚:Linux 和 Windows 跟踪。
  • 高效存储内存历史。

用户界面

我们现在有用户界面了!:D

您可以使用基本的 vim 快捷键:j/k用于上下移动。我不确定我们是否想覆盖箭头键,但最终我们可能还是会这样做。

记录所有状态可以引入一些类似于您可能从 gdb 中熟悉的绑定

下一条/最后一条指令

  • Shift-J:等同于 gdb ni
  • Shift-K:相同,但方向相反(时间上)

跳到下一个调用/父调用

  • h:跳转到调用此函数的任何内容(时间倒流)
  • l:跳到下一个调用
    • 如果没有下一个调用,则跳转到 ret

备注

这样逐步调试的感觉真是太棒了,我认为仅仅展示这一点就足以说明使用这种方式进行调试是多么出色

Docker 中开发 QEMU

要启动容器并在其中构建和运行 qemu,您可以使用以下命令

启动容器

cd tools
docker compose up --build -d

进入容器

docker compose exec develop /bin/bash

配置和编译

root@54541497458c:~/qemu# ./configure --with-git-submodules=ignore --enable-tcg-interpreter --target-list=aarch64-linux-user,x86_64-linux-user
root@54541497458c:~/qemu# make -j $(nproc)

运行和测试

root@54541497458c:~/qemu# ./build/aarch64-linux-user/qemu-aarch64 /bin/ls

使用 nc 测试

首先,启动一个监听器。由于某种原因,ipv4 请求到 v6 监听器会自动转换为 ipv6。

root@94772de42933:~/qemu# while true; do echo -e '\n\n===='; nc -6 -lnvp 1337; done
====
Listening on :: 1337
Connection received on ::ffff:127.0.0.1 48382
elflibload|/usr/bin/ls|5500000000|550202f53f
elflibload|/lib/ld-linux-aarch64.so.1|5502831000|550286e36f
regs|pc=5502848c40|r0=0|r1=0|r2=0|r3=0|r4=0|r5=0|r6=0|r7=0|r8=0|r9=0|r10=0|r11=0|r12=0|r13=0|r14=0|r15=0|r16=0|r17=0|r18=0|r19=0|r20=0|r21=0|r22=0|r23=0|r24=0|r25=0|r26=0|r27=0|r28=0|r29=0|r30=0|r31=5502830740|flags=40000010

然后,在另一个地方,构建并运行!

root@94772de42933:~/qemu# make -j $(nproc) && ./build/aarch64-linux-user/qemu-aarch64 -rebgtcp localhost:1337 -rebglog /dev/null /bin/ls

这将在 qemu 中挂载文件夹,您可以在您的正常编辑器中编辑它,并在 docker 中构建和测试它。

开发者调试技巧

使用具有许多核心的计算机或 VPS 来调试 qemu,它将编译得更快。如果您不是在原生 Linux 上,至少启动一个持久的 docker,这样您就不必为每一个小小的变化重新编译一切

QEMU 有很多奇怪的间接引用,我做的补丁也很复杂。您可以在 GDB 中调试它(再次,如果您没有 x86_64,请使用 VPS,因为 ptrace 在 qemu-system 中不起作用,docker 就使用这个)。例如,调试打印了某些内容到 stderr

(gdb) b write if $rdi==2
Breakpoint 2 at 0x7ffff7c4ba20: file ../sysdeps/unix/sysv/linux/write.c, line 25.
(gdb) r
Thread 1 "qemu-x86_64" hit Breakpoint 2, __GI___libc_write (fd=2, buf=0x7fffffffb5f0, nbytes=1) at ../sysdeps/unix/sysv/linux/write.c:25
(gdb) bt
#0  __GI___libc_write (fd=2, buf=0x7fffffffb5f0, nbytes=1) at ../sysdeps/unix/sysv/linux/write.c:25
#8  0x000055555569e10d in vfprintf (__ap=0x7fffffffd630, __fmt=0x5555556d9e6a "{", __stream=0x7ffff7d516a0 <_IO_2_1_stderr_>) at /usr/include/x86_64-linux-gnu/bits/stdio2.h:135
#9  qemu_log (fmt=fmt@entry=0x5555556d9e6a "{") at ../util/log.c:153
#10 0x0000555555665b85 in thunk_print (arg=0x4002823fe0, type_ptr=0x55555578416c <ioctl_entries+268>, type_ptr@entry=0x555555784164 <ioctl_entries+260>)
    at ../linux-user/thunk.c:417
#11 0x0000555555650368 in print_syscall_ret_ioctl (cpu_env=<optimized out>, name=<optimized out>, ret=0, arg0=<optimized out>, arg1=<optimized out>, arg2=274919997408, 
    arg3=274922302144, arg4=180, arg5=0) at ../linux-user/strace.c:959
#12 0x000055555565135a in print_syscall_ret (cpu_env=cpu_env@entry=0x555555836660, num=num@entry=16, ret=ret@entry=0, arg1=arg1@entry=1, arg2=arg2@entry=21523, 
    arg3=arg3@entry=274919997408, arg4=274922302144, arg5=180, arg6=0) at ../linux-user/strace.c:4153
#13 0x00005555556650aa in do_syscall (cpu_env=cpu_env@entry=0x555555836660, num=16, arg1=1, arg2=21523, arg3=274919997408, arg4=274922302144, arg5=180, arg6=0, arg7=0, arg8=0)
    at ../linux-user/syscall.c:13379
#14 0x000055555558f844 in cpu_loop (env=env@entry=0x555555836660) at ../linux-user/x86_64/../i386/cpu_loop.c:233
#15 0x000055555558b78d in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at ../linux-user/main.c:968

编辑以简洁,但这让您可以快速追踪问题。

依赖关系

~62MB
~1.5M SLoC