Upgrade to Pro — share decks privately, control downloads, hide ads and more …

デバッガを自作してみよう (M3 tech talk)

デバッガを自作してみよう (M3 tech talk)

Yutaro Oguri

March 03, 2023
Tweet

More Decks by Yutaro Oguri

Other Decks in Programming

Transcript

  1. 自己紹介 • 名前: 小栗 悠太郎 (Yutaro Oguri) @irungo_ic • 所属:

    東京大学 工学部 電子情報工学科B3 ◦ 現在、M3 AIチームにてインターンシップに参加 • 趣味: 音楽(ヴァイオリン🎻)、お酒(🍺、🍶)
  2. デバッガの例: gdb 1. -gをつけてコンパイル 2. 起動 3. Breakpointを設置 4. 実行

    5. 状態を観察 e.g.) レジスタの中身を出力 -> (gdb) info registers rax 0x7fffffffd310 140737488343824 rbx 0x401260 4199008 rcx 0x0 0 rdx 0x7fffffffd2f0 140737488343792 rsi 0x402004 4202500 rdi 0x402004 4202500 rbp 0x7fffffffd410 0x7fffffffd410 rsp 0x7fffffffd2f0 0x7fffffffd2f0 r8 0x0 0 r9 0x7ffff7fe0d60 140737354009952 r10 0x402004 4202500 r11 0x7ffff7de7c90 140737351941264 r12 0x401050 4198480 r13 0x7fffffffd500 140737488344320 r14 0x0 0 r15 0x0 0 rip 0x7ffff7de7d21 0x7ffff7de7d21 <__printf+145> eflags 0x246 [ PF ZF IF ] cs 0x33 51 ss 0x2b 43 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0
  3. ptrace request: 今回使うもの • PTRACE_GETREGS / PTRACE_SETREGS traceeのレジスタ値をRead / Write

    する • PTRACE_CONT 停止していたtraceeの実行を再開する • PTRACE_SINGLESTEP 停止していたtraceeの実行を次の命令まで進め、再び停止させる
  4. ELF形式 LinuxなどのOSで広く採用されている実行形式 ELF Header: ファイルのメタデータ・Offset Program Header: 実行時に使う情報 Section Header:

    リンク時に使う情報 +------------------------+ | ELF header | +------------------------+ |+----------------------+| || Program header table || |+----------------------+| ||+--------------------+|| ||| ||| ||| data ||| ||| ||| ||+--------------------+|| |+----------------------+| || Setion header table || |+----------------------+| +------------------------ + source
  5. ELF形式 LinuxなどのOSで広く採用されている実行形式 ELF Header: ファイルのメタデータ・Offset Program Header: 実行時に使う情報 Section Header:

    リンク時に使う情報 +------------------------+ | ELF header | +------------------------+ |+----------------------+| || Program header table || |+----------------------+| ||+--------------------+|| ||| ||| ||| data ||| ||| ||| ||+--------------------+|| |+----------------------+| || Setion header table || |+----------------------+| +------------------------ + source
  6. 実装: ELFのHandler構造体 ELFファイルの中身、 ヘッダ、Traceeの情報を 管理する構造体 #include <elf.h> #include <sys/user.h> ...

    typedef struct ElfHandler { Elf64_Ehdr *ehdr; // ELF header Elf64_Phdr *phdr; // program header Elf64_Shdr *shdr; // section header uint8_t *mem; // memory map of the executable char *exec_cmd; // exec command char *symbol_name; // symbol name to be traced Elf64_Addr symbol_addr; // symbol address struct user_regs_struct regs; // registers } ElfHandler_t;
  7. 実装: ELFファイルの読み込み ELFファイルの読み込み 巨大かもしれないので mmapで #include <sys/mman.h> ... // read

    mode int fd = open(argv[1], O_RDONLY); // ファイルサイズの取得のためにstatを使用 struct stat st; fstat(fd, &st); // fdの内容をmapする (copy-on-write) eh.mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); // ヘッダを抽出 eh.ehdr = (Elf64_Ehdr *)eh.mem; eh.phdr = (Elf64_Phdr *)(eh.mem + eh.ehdr->e_phoff); eh.shdr = (Elf64_Shdr *)(eh.mem + eh.ehdr->e_shoff);
  8. 実装: BPを置きたいSymbolのAddrを特定 1. section header tableを総当たり. symbol tableに辿り着くまで. 2. symbol

    tableの実体を取ってくる. 3. Linkされているsymbol name tableを取ってくる. 4. symbol tableを総当たり. 目的のSymbol名に一致するEntryを返し, Addressを取 得 eh.symbol_addr = lookup_symbol_addr_by_name(&eh, eh.symbol_name);
  9. 実装: traceeの実行開始 fork/exec/waitで子プロセスを実行 PTRACE_TRACEMEによりtrace // process id int pid =

    fork(); ... // child executes the given program if (pid == 0) { ptrace(PTRACE_TRACEME, 0, NULL, NULL); execve(eh.exec_cmd, args, envp); exit(EXIT_SUCCESS); } int status; wait(&status);
  10. 実装: Breakpointを設置 BPの設置 = Trap命令の差し込み Trap命令: ソフトウェア割り込みを生成 // trap命令のopcode (x86)

    #define OPCODE_INT3 0xcc ... // get original instruction const long original_inst = ptrace(PTRACE_PEEKTEXT, pid, eh.symbol_addr, NULL); // modify to trap instruction const long trap_inst = (original_inst & ~0xff) | OPCODE_INT3; ptrace(PTRACE_POKETEXT, pid, eh.symbol_addr, trap_inst);
  11. 実装: main loop Trap! ↓ レジスタ読み取り ↓ 元の命令/レジスタを復元し、 1つ前から再実行 ↓

    trap命令を復元 while (1) { // resume process execution ptrace(PTRACE_CONT, pid, NULL, NULL); … if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { // get registers info and display them ptrace(PTRACE_GETREGS, pid, NULL, &eh.regs); display_registers(&eh); … // restore original instruction ptrace(PTRACE_POKETEXT, pid, eh.symbol_addr, original_inst); // single step to execute the original instruction eh.regs.rip -= 1; ptrace(PTRACE_SETREGS, pid, NULL, &eh.regs); ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL); … // restore trap instruction ptrace(PTRACE_POKETEXT, pid, eh.symbol_addr, trap_inst); } }
  12. サンプルを実行してみる 足し算を3回する。 関数add(a, b, c)の引数と、 レジスタrdi, rsi, rdx*の値が 一致していればOK! *x86の呼び出し規約

    int add (int a, int b, int c) { return a + b + c; } int main(int argc, char **argv, char **envp) { int a = 1; int b = 2; int c = 9; printf("a = %d, b = %d, c = %d\n", a, b, c); int d = add(a, b, 23); // 1(1) + 2(2) + 23(17) = 26(1a) printf("%d + %d + %d = %d\n", a, b, 23, d); int e = add(d, c, 54); // 26(1a) + 9(9) + 54(36) = 89(59) printf("%d + %d + %d = %d\n", d, c, 54, e); int f = add(e, 1, 7); // 89(59) + 1(1) + 7(7) = 97(61) printf("%d + %d + %d = %d\n", e, 1, 7, f); return 0; }
  13. 実行結果 add関数(symbol)にBPを設置 期待通りのレジスタ状態を観測できた $ ./debugger ./test_add add Tracing pid:43130 at

    symbol addr 401136 a = 1, b = 2, c = 9 %rax: 1 %rbx: 401260 %rcx: 2 %rdx: 17 // add関数の第3引数 %rsi: 2 // add関数の第2引数 %rdi: 1 // add関数の第1引数 %rbp: 7ffd2c1049d0 %rsp: 7ffd2c104988 ... %gs: 0 Please hit [ENTER] key to continue:
  14. Reference • はじめてのgdb, https://qiita.com/arene-calix/items/a08363db88f21c81d351 • ELF Formatについて, https://www.hazymoon.jp/OpenBSD/annex/elf.html • 最小限のELF,

    https://keens.github.io/blog/2020/04/12/saishougennoelf/ • ptraceシステムコール入門 ― プロセスの出力を覗き見してみよう! , https://itchyny.hatenablog.com/entry/2017/07/31/090000 • Ryan "elfmaster" O'Neill, Learning Linux Binary Analysis, 2016, Packt • man page of MMAP, https://linuxjm.osdn.jp/html/LDP_man-pages/man2/mmap.2.html • x86_64で関数の引数とレジスタの対応を確認する (アセンブラ), https://qiita.com/hara0219/items/6556ef17d00922536fa8 • デバッガとは何ぞや, https://zenn.dev/satoru_takeuchi/articles/8de139a52af5c4