-
-
[原创]strace是啥?strace怎么用?
-
发表于: 2022-1-7 22:16 22171
-
这一篇文章会介绍strace如何工作,再稍微深入介绍一下什么是system call。再介绍一下ptrace、wait(strace依赖的system call)。最后再一起来造个轮子,动手用代码实现一个strace。聊天框回复“strace”,可以获取本文源码。
上一篇,我们介绍了strace工具,strace是非常实用的调试、分析工具,可以记录process调用system call的参数、返回值。
效果展示
下面展示一下我们实现简化版的strace的效果,每行打印一个system call。只不过没有根据system call的序号转换参数类型来打印,毕竟我们实现的目的是学习。
上一篇,我们简单介绍了系统调用(system call)。strace就是记录system call的工具,我们需要深入了解一下system call。
每个system call都有一个序号,记录在/usr/include/x86_64-linux-gnu/asm/unistd_64.h文件中。我们常见的read、write、open都在其中定义。
我们可以调用glibc封装的system call(例如close、connect、bind等),还可以使用syscall直接调用。glibc中的封装最终也是调用了syscall。
例如我们调用tgkill时
我们可以使用glibc封装的
也可以使用syscall直接传system call 序号和对于参数来调用。
我们需要了解一下调用syscall时,用户层与内核是交互交互返回值和参数的。
根据man syscall手册。不同的cpu通过不同寄存器来传递。
返回值:
x86-64位下,返回值在rax寄存器。
参数列表:
x86-64位下,参数依次是rdi rsi rdx r10 r8 r9。
首先介绍我们把tracer和tracee的概念:我们把跟踪者(strace)叫做tracer,被跟踪process叫做tracee。
strace整体工作流程如下:
磨刀不误砍柴工,我们也来介绍一下strace工作时两个重要函数。
通过上面流程图,可以看出strace在建立trace关系、跟踪system call时都依赖ptrace。
man ptrace
man手册是这么描述的:ptrace可以让tracer观察并控制tracee的执行,并可以获取并修改tracee的内存和寄存器。可以用来实现调试器或system call跟踪。实际上gdb和strace都是依赖ptrace来实现的。
参数
wait也是strace工作时也很重要,先看看man手册。
man wait
man手册是这么描述的:wait用来等待子进程状态改变,包括退出、stopped、resumed。
如果子进程状态已经改变了,wait会立刻返回。否则会卡住等待状态改变。
状态通过wstatus返回,wait也提供了一系列配套宏来判断状态。
strace使用wait有2个场景:
strace工作的第一步就是建立trace关系,按照不同启动模式采取不同的方式建立。无论是哪种模式,都需要与tracee建立trace关系。才能监控system call的调用。
strace的启动模式:
attach模式建立trace关系
strace调用ptrace(request=PTRACE_ATTACH)与tracee建立trace关系。
strace启动模式建立trace关系
此模式下,strace需要先执行fork。然后父进程作为tracer,子进程作为tracee。
子进程fork以后,还需要执行ptrace(request=PTRACE_TRACEME)来建立trace关系。
建立好trace关系以后,子进程还需要调用execv来执行tracee的逻辑。
代码
无论是attach模式还是strace启动模式,建立trace关系后,都执行相同的逻辑,代码可以复用。
建立trace关系后,strace需要调用wait,来等待tracer变为stop状态。
xx_waitstate2str是封装好,打印子进程状态的
屏幕输出
建立好trace关系后,tracee是处于stop状态的。下一步开始循环跟踪tracee的system call。
strace使用ptrace 跟踪tracee的system call时会有两次拦截,一次是调用前,一次是调用完成后。
调用前拦截时,有以下操作:
调用前拦截时,system call还没被调用,通过寄存器信息,可以获取:
调研后拦截时,system call已调用完毕。通过寄存器可以获取返回值,以及调用后的参数。前面1.3章节介绍了system call在不同cpu架构使用哪些寄存器。
下面我们来看看代码实现.
步骤1(唤醒)、2(等待)
我们封装了一个函数
循环跟踪主题
循环主题主要就是两次拦截、获取信息、打印信息。
保存system call信息
调用前拦截、获取system call序号
调用后拦截、获取参数、返回值
不足200行代码,实现了strace基础功能。造个轮子能更好的学习,大家学会了么?
最后,“东北码农” 求微信关注、点赞、转发,谢谢~
赞赏
- [原创]gcc xxx.cpp究竟经历了什么? 6681
- [原创]mutex实现与性能 16273
- [原创]SUID提权:CVE-2021-4034漏洞全解析 9029
- [原创]原理+代码实战:SUID提权渗透 7603
- [原创]CVE-2021-4034提权(一)进程凭证与文件权限 11180