在了解目標模組的動態行為時, 最近覺得從模組的 public interface 設中斷點是滿不錯的作法。廣意來說, system call 則是最一般化的 public interface, 從 system call 回頭夾擊目標也滿有效的。比方像這篇用 strace 找到切入點。
若想獲得更多資訊, 可以用 gdb 在 system call 上設中斷點, 一步步從退回來找到呼叫者、傳遞的資料等更多訊息。但在尋找 write、send 這類廣為使用的 system call 時, 被呼叫的次數過於頻繁, 就有點難找了。
查了一下, 看到這篇提到從 register 設定 conditional break 的方法:
(gdb) b write if 1==$rdi # stop only when write(1, ...)
要在 syscall argument 上設條件,且要 portable 以目前的工具不容易。用 gdb 的話需能背出『x86-64 上參數依序擺在 rdi, rsi, rdx, rcx, r8d, r9d』。 x86-64 上特別好記,因 syscall 與 一般 function call 被設計成將參數擺在同位置。ARM 上還有 64bit 參數開頭一定擺在偶數暫存器如 r0-r1, r2-r3 而不擺在 r1-r2 等規則。
目前 gdb 沒有將 syscall parameter 存在如 $syscall_arg0 的 convenience variable 中,否則寫: catch syscall write if $syscall_arg0 == 1 即可。
用 "system call calling convention" 當關鍵字查到《What are the calling conventions for UNIX & Linux system calls on x86-64》, 文中有許多相關資訊, 就先備忘吧。
更正:
回覆刪除x86-64 一般函式參數擺在 rdi, rsi, rdx, rcx, r8, r9
Linux syscall 參數擺在 rdi, rsi, rdx, r10, r8, r9
第四個參數放的位置不同: rcx VS. r10
syscall 並可能複寫 rcx 與 r11
見 http://www.x86-64.org/documentation/abi.pdf Appendix A, Linux Conventions
傳統上 mmap() 是參數個數最多的 syscall (6 個)。x86-64 glibc mmap() syscall wrapper 如下:
0x0000003c774eed40 <+0>: mov %rcx,%r10
0x0000003c774eed43 <+3>: mov $0x9,%eax
0x0000003c774eed48 <+8>: syscall
0x0000003c774eed4a <+10>: cmp $0xfffffffffffff001,%rax
0x0000003c774eed50 <+16>: jae 0x3c774eed53
0x0000003c774eed52 <+18>: retq
0x0000003c774eed53 <+19>: mov 0x2c20de(%rip),%rcx # 0x3c777b0e38
0x0000003c774eed5a <+26>: neg %eax
0x0000003c774eed5c <+28>: mov %eax,%fs:(%rcx)
0x0000003c774eed5f <+31>: or $0xffffffffffffffff,%rax
0x0000003c774eed63 <+35>: retq
可見其中將 rcx 複製到 r10。放在 eax 中的常數 9 為 mmap 的 syscall number。retq 指令後是錯誤時執行的 code path,對應:
if (-4095 <= eax && eax <= -1)
errno = -eax; /* errno is thread local */
return 0xffffffffffffffff;