觀察被 uprobe 修改過的程序

在 SHLUG 月聚上分享了 ftrace, 在介紹 ftrace + uprobe 時, shell 問到有關 instruction 被替換時, debug 用的指令 (int 3) 通常為 2 bytes, 但 nop 則只有一 byte, ftrace 是怎麼處理這個問題的? 這裡實際來看個例子.

準備 C 代碼 (內容不是那麼重要)

/* test.c */
#include <stdio.h>
#include <stdlib.h>

int global_var = 0x12345678;

int main(int argc, char *args[])
{
    printf("waiting...\n");

    sleep(2);

    printf("hello %s, global_var = 0x%x, main()=%p\n", "world", global_var, &main);

    return 0;
}

Build 出 executable

$ gcc -o test test.c

匯編如下

$ objdump -d test
[...]
0000000000400596 <main>:
  400596:       55                      push   %rbp
  400597:       48 89 e5                mov    %rsp,%rbp
  40059a:       48 83 ec 10             sub    $0x10,%rsp
  40059e:       89 7d fc                mov    %edi,-0x4(%rbp)
  4005a1:       48 89 75 f0             mov    %rsi,-0x10(%rbp)
  4005a5:       bf 74 06 40 00          mov    $0x400674,%edi
  4005aa:       e8 a1 fe ff ff          callq  400450 <[email protected]>
  4005af:       bf 02 00 00 00          mov    $0x2,%edi
  4005b4:       b8 00 00 00 00          mov    $0x0,%eax
  4005b9:       e8 d2 fe ff ff          callq  400490 <[email protected]>
  4005be:       8b 05 4c 04 20 00       mov    0x20044c(%rip),%eax        # 600a10 <global_var>
  4005c4:       89 c2                   mov    %eax,%edx
  4005c6:       be 7f 06 40 00          mov    $0x40067f,%esi
  4005cb:       bf 85 06 40 00          mov    $0x400685,%edi
  4005d0:       b8 00 00 00 00          mov    $0x0,%eax
  4005d5:       e8 86 fe ff ff          callq  400460 <[email protected]>
  4005da:       b8 00 00 00 00          mov    $0x0,%eax
  4005df:       c9                      leaveq 
  4005e0:       c3                      retq   
  4005e1:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  4005e8:       00 00 00 
  4005eb:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
[...]

Load address 為 0x400000, 在調用 sleep() 的位置插入 uprobe, 所以 address 為 0x4005b9-0x400000=0x5b9

# cd /sys/kernel/debug/tracing
# echo 'p /home/derekdai/test:0x5b9 sec=%di:s32' >uprobe_events
# echo 1 >events/uprobes/enable
# echo 1 >tracing_on

啟動 test 後立即 stop 以利操作

$ ./test & kill -STOP $!
[1] 17276

用 gdb 將內存中的 main() dump 下來

$ gdb --pid 17276
(gdb) dump memory ./main.dump 0x400596 0x4005eb

比較內容

$ xxd -g1 <main.dump 
0000000: 55 48 89 e5 48 83 ec 10 89 7d fc 48 89 75 f0 bf  UH..H....}.H.u..
0000010: 78 06 40 00 e8 a1 fe ff ff bf 02 00 00 00 b8 00  [email protected]
0000020: 00 00 00 cc d2 fe ff ff 8b 05 64 04 20 00 b9 96  ..........d. ...
0000030: 05 40 00 89 c2 be 83 06 40 00 bf 90 06 40 00 b8  [email protected]@[email protected]
0000040: 00 00 00 00 e8 81 fe ff ff b8 00 00 00 00 c9 c3  ................
0000050: 66 2e 0f 1f 84

在第三行 (0000020) 處, 第 4 個 byte 起, 原本的 e8 d2 fe ff ff 現在成為了 cc d2 fe ff ff, 看起來只用到了一個 byte.

可參考 Eli 的 blog