编写最简单的汇编调试器

Dav*_*542 0 c linux x86 assembly ptrace

假设我有以下汇编代码,我想单步执行:

.globl _start
_start:
    nop
    mov $60, %eax
    syscall
Run Code Online (Sandbox Code Playgroud)

我可以将 a 附加ptrace到它以单步运行它的最简单方法是什么?我通常这样做,gdb但很好奇如何以最粗略的方式手动执行此操作(没有错误处理或除上述情况外的任何其他操作)以查看幕后发生的情况。任何语言都可以(尽管汇编可能是最好的)。

Jes*_*ter 5

为简单起见,我添加了int3触发断点陷阱的 。在实际使用中,您希望跟踪exec调用并在从 ELF 标头解析出的入口地址处放置一个软件或硬件断点。我已经将目标程序组装成a.out,它看起来像:

00000000004000d4 <_start>:
  4000d4:   cc                      int3   
  4000d5:   90                      nop
  4000d6:   b8 3c 00 00 00          mov    $0x3c,%eax
  4000db:   0f 05                   syscall 
Run Code Online (Sandbox Code Playgroud)

一个演示单步执行的简单程序:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/user.h>

int main() {
    int pid;
    int status;
    if ((pid = fork()) == 0) {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl("./a.out", "a.out", NULL);
    }
    printf("child: %d\n", pid);
    waitpid(pid, &status, __WALL);
    ptrace(PTRACE_CONT, pid, NULL, NULL);
    while(1) {
        unsigned long rip;
        waitpid(pid, &status, __WALL);
        if (WIFEXITED(status)) return 0;
        rip = ptrace(PTRACE_PEEKUSER, pid, 16*8, 0);    // RIP is the 16th register in the PEEKUSER layout
        printf("RIP: %016lx opcode: %02x\n", rip, (unsigned char)ptrace(PTRACE_PEEKTEXT, pid, rip, NULL));
        ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL);
    }
}
Run Code Online (Sandbox Code Playgroud)

示例输出:

$ ./singlestep 
child: 31254
RIP: 00000000004000d5 opcode: 90
RIP: 00000000004000d6 opcode: b8
RIP: 00000000004000db opcode: 0f
Run Code Online (Sandbox Code Playgroud)

  • 幸运的是,Marco 的答案有一个这样做的例子。我在你的版本中添加了对魔法常数的评论。 (2认同)