目录
前言
上文通过strace trace它自个看到了strace的原理:确实是调用了ptrace函数实现的。今天我们再细看一看。
strace与gdb的冲突
既然它两底层都是用ptrace,那么一定不能同时用在同一个进程上。实例验证:
[root]# strace sleep 1000000
...
nanosleep({tv_sec=1000000, tv_nsec=0},
#####阻塞在sleep, 方便我们有机会用gdb尝试attach它#########
启动另外一个session,
[root]# ps -ef|grep sleep
root 1463460 1462937 0 21:12 pts/1 00:00:00 strace sleep 1000000
root 1463463 1463460 0 21:12 pts/1 00:00:00 sleep 1000000
root 1463471 1038 0 21:12 ? 00:00:00 sleep 60
[root]# gdb -p 1463463
GNU gdb (GDB) Red Hat Enterprise Linux 8.2-19.el8
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
File "/home/mzhai/gdb_support/offsets.py", line 15
print argv[0], '{'
^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(argv[0], '{')?
Attaching to process 1463463
warning: process 1463463 is already traced by process 1463460
ptrace: Operation not permitted.
(gdb) quit
它说:已经被1463460跟踪了,而1463460就是strace sleep 1000000。
两者都要控制傀儡进程sleep,只有一个成功。
细看strace
还是上节的思路,strace strace ls, 只是不是统计各个系统函数的调用次数,而是要细看。
为了便于搜索和认清谁是谁,我会同时使用bcc工具中的execsnoop记录各个启动进程的PID和command。
开一个session,运行/usr/share/bcc/tools/execsnoop(我的Alma8.4和redhat9.2都自带):
[root]# execsnoop
再开一个session运行 strace strace ls:
[root]# strace strace ls
execve("/usr/bin/strace", ["strace", "ls"], 0x7ffe1f473048 /* 60 vars */) = 0
brk(NULL) = 0x56104a1bb000
...
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f64d4ec1f50) = 1462998
wait4(1462998, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGSTOP}], WSTOPPED, NULL) = 1462998
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_STOPPED, si_pid=1462998, si_uid=0, si_status=SIGSTOP, si_utime=0, si_stime=0} ---
ptrace(PTRACE_SEIZE, 1462998, NULL, PTRACE_O_TRACESYSGOOD|PTRACE_O_TRACEEXEC|PTRACE_O_TRACEEXIT) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_STOPPED, si_pid=1462998, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
ptrace(PTRACE_INTERRUPT, 1462998) = 0
kill(1462998, SIGCONT) = 0
close(0) = 0
...
ptrace(PTRACE_LISTEN, 1462998) = 0
wait4(-1, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGTRAP} | PTRACE_EVENT_STOP << 16], __WALL, NULL) = 1462998
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_CONTINUED, si_pid=1462998, si_uid=0, si_status=SIGCONT, si_utime=0, si_stime=0} ---
wait4(-1, 0x7ffc7c00d49c, WNOHANG|__WALL, NULL) = 0
ptrace(PTRACE_SYSCALL, 1462998, NULL, 0) = 0
wait4(-1, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGCONT}], __WALL, NULL) = 1462998
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_TRAPPED, si_pid=1462998, si_uid=0, si_status=SIGCONT, si_utime=0, si_stime=0} ---
ptrace(PTRACE_GETSIGINFO, 1462998, NULL, {si_signo=SIGCONT, si_code=SI_USER, si_pid=1462994, si_uid=0}) = 0
wait4(-1, 0x7ffc7c00d49c, WNOHANG|__WALL, NULL) = 0
ptrace(PTRACE_SYSCALL, 1462998, NULL, SIGCONT) = 0
wait4(-1, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGTRAP | 0x80}], __WALL, NULL) = 1462998
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_TRAPPED, si_pid=1462998, si_uid=0, si_status=SIGTRAP, si_utime=0, si_stime=0} ---
wait4(-1, 0x7ffc7c00d49c, WNOHANG|__WALL, NULL) = 0
ptrace(PTRACE_GETREGSET, 1462998, NT_PRSTATUS, {iov_base={r15=0xffffffff, r14=0x9, r13=0x7f64d4ec1b00, r12=0x7ffc7c01066e, rbp=0x7ffc7c00d750, rbx=0, r11=0x246, r10=0x7f64d4ec1c80, r9=0x7f64d420b2f0, r8=0, rax=0xffffffffffffffda, rcx=0x7f64d4248e7b, rdx=0x7ffc7c00d750, rsi=0x7ffc7c00d740, rdi=0x7ffc7c00c430, orig_rax=0x3b, rip=0x7f64d4248e7b, cs=0x33, eflags=0x246, rsp=0x7ffc7c00c1d8, ss=0x2b, fs_base=0x7f64d4ec1c80, gs_base=0, ds=0, es=0, fs=0, gs=0}, iov_len=216}) = 0
fstat(2, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x2), ...}) = 0
process_vm_readv(1462998, [{iov_base="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., iov_len=4096}], 1, [{iov_base=0x7ffc7c00c000, iov_len=4096}], 1, 0) = 4096
process_vm_readv(1462998, [{iov_base="\200\3339\324d\177\0\0\1\0\0\0\0\0\0\0\"\0\0\0\0\0\0\0\0\6b\25Y\7\251\210"..., iov_len=4096}], 1, [{iov_base=0x7ffc7c00d000, iov_len=4096}], 1, 0) = 4096
process_vm_readv(1462998, [{iov_base="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., iov_len=4096}], 1, [{iov_base=0x7ffc7c00f000, iov_len=4096}], 1, 0) = 4096
write(2, "execve(\"/usr/bin/ls\", [\"ls\"], 0x"..., 58execve("/usr/bin/ls", ["ls"], 0x7ffc7c00d750 /* 60 vars */) = 58
ptrace(PTRACE_SYSCALL, 1462998, NULL, 0) = 0
...
此时session1中的输出:
[root]# execsnoop
PCOMM PID PPID RET ARGS
strace 1462991 1462861 0 /usr/bin/strace strace ls
strace 1462994 1462991 0 /usr/bin/strace ls
ls 1462998 1462994 0 /usr/bin/ls
可以清晰的看到第一个strace PID=1462991,第二个=1462994,ls PID=1462998.
而session2中所有的ptrace(...,1462998...)这样的行都是第二个strace的详细活动的一部分(全被第一个strace扒了出来),它干的坏事就是秘密的控制了ls。ptrace第一个参数的含义通过man ptrace能查到。此处简单介绍下上面列出的(还有500多次调用,都省略不提)。
ptrace第一个参数 | 含义 |
| PTRACE_SEIZE | attach到ls |
| PTRACE_INTERRUPT | 停止ls |
| PTRACE_LISTEN | 继续ls, 但不让其执行指令 |
| PTRACE_SYSCALL | 继续ls, 但会停止在:1.系统调用退出时 或2.有信号时。目的是第一次得到系统函数的参数,之后得到返回值。 |
| PTRACE_GETSIGINFO | 得到signal相关的信息 |
| PTRACE_GETREGSET | 读ls的寄存器 |

本文探讨了strace与gdb在跟踪进程时的冲突,由于两者都基于ptrace函数,因此无法同时作用于同一进程。通过实例展示了当尝试同时使用strace和gdb时,只有一个能够成功控制目标进程。此外,文章还通过strace strace ls的组合,配合bcc工具的execsnoop,详细剖析了strace如何跟踪并控制ls进程的内部活动,揭示了ptrace函数的使用细节。

被折叠的 条评论
为什么被折叠?



