使用 strace 命令了解 Linux 系统调用

当在 Linux 上运行的程序想要使用操作系统管理的资源(读取文件、创建进程等)时,它们会对操作系统进行系统调用。 系统调用在内核级别工作并执行必要的操作,将控制权交还给调用程序。 strace 工具提供了在 Linux 上跟踪这些系统调用的能力。

strace 命令的典型用法

要监视应用程序的系统调用,只需调用命令 跟踪 采用以下格式:

strace ls /tmp

但是,通常有一些进程启动得更早,并继续在后台工作。 由于任何问题,您可能希望收集与此类流程相关的其他信息。 你可以附上 跟踪 通过将进程的进程 ID 提供给任何正在运行的应用程序 -p 范围:

strace -p 2759

输出:

跟踪应用程序的线程和分叉

使用 strace,您可以使用 -F 旗帜。

strace -f -p 2759

输出:

使用 strace 检查某些系统调用

默认的 strace 输出有时会非常拥挤。 如果您只想跟踪某些系统调用,您可以使用 -e 范围:

strace -f -e trace=open,write,close,connect,select -p 19770

要仅跟踪与文件操作相关的系统调用,请使用 -e 跟踪=文件

strace -e trace=file -p 19770

要仅过滤与网络相关的系统调用,请指定 -e 跟踪=网络 在命令中:

strace -e trace=network -p 19770

以秒为单位获取时间信息

输出系统调用时,可以使用 -t 参数以秒为单位获取时间信息。 大多数情况下,精度不足以满足您的需求。 在这种情况下,您可以使用 -tt 以微秒精度获取时间信息的参数:

strace -tt ls /tmp

收集有关系统调用的统计信息

随着 -C 参数,您可以根据需要收集有关系统调用的统计信息:

strace -f -c -p 19770

Save 记录到文件

如果您长时间运行 strace 并希望稍后更详细地检查生成的日志,则需要保存日志。 随着 -o 参数您可以指定 strace 应保存日志的文件:

strace -f -o /tmp/strace.log -e trace=file ls /tmp

ptrace 阻塞进程

使用 prctl 系统调用,Linux 下的任何应用程序都可以防止自己被使用 ptrace 的非 root 用户控制。 如果应用程序清除 PR_SET_DUMPABLE 通过 prctl 为自己标记,除 root 以外的用户将无法使用 ptrace 控制此应用程序,即使他们有权向应用程序发出信号。

在 OpenSSH 身份验证代理软件中可以看到此功能的最典型用途之一。 因此,另一个应用程序对应用程序的控制 跟踪 在用户认证时被阻止。

ptrace 和安全性

由于传统 Linux 进程模型中设置的 ptrace 工具,您在系统上与您的用户一起运行的任何软件都有权将恶意代码插入其中。 从最简单的 xterm 工具到高级 Web 浏览器应用程序,此类恶意软件可以控制您所有其他正在运行的应用程序(多亏了 ptrace 系统调用),并在您不注意的情况下复制重要信息。

针对这种许多用户不知道的情况,已经开发了一种保护机制,其中的安全模块称为 Linux 内核中的 Yama.

您可以通过以下方式控制对 ptrace 系统调用的响应 /proc/sys/kernel/yama/ptrace_scope 文件。 默认情况下,此文件写入值 0。

以下值是可接受的:

价值 意义
0 常规行为:所有有权使用的应用程序 跟踪 可以检查。
1 受限 ptrace:只有应用程序的直接父级或调试应用程序允许的应用程序具有 PR_SET_PTRACER 选项有控制权。 因此,使用 gdb 程序名称strace 程序名称 将继续工作,但之后您将无法附加正在运行的应用程序。
2 Ptrace 给系统管理员:只有定义的应用程序 CAP_SYS_PTRACE 属性或子进程定义 PTRACE_TRACEME 选项 prctl 可以控制。
3 完全禁用: 不 跟踪 在任何情况下都是允许的。 如果此属性定义一次,则无法在运行时再次更改它。

许多开发人员不知道应用程序可以通过 prctl 自行禁用 ptrace,root 用户除外。 尽管 OpenSSH 代理等安全相关软件执行这些操作,但期望系统上运行的所有软件都具有相同的行为是不正确的。

最近,一些Linux发行版开始设置默认值 ptrace_scope 文件,如上所述,到 1. 因此,在 ptrace 操作受限的情况下,整个系统都提供了更安全的工作环境。

使用示例 strace

使用名称注册下面的示例应用程序 迷你跟踪.c. 然后你可以用下面的命令编译它:

gcc -o ministrace ministrace.c

代码:

#include <sys/ptrace.h>
#include <sys/reg.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int wait_for_syscall (pid_t child)
{
int status;
while (1) {
ptrace(PTRACE_SYSCALL, child, 0, 0);
waitpid(child, &status, 0);
if (WIFSTOPPED(status) && WSTOPSIG(status) & 0x80)
return 0;
if (WIFEXITED(status))
return 1;
}
}

int do_child (int argc, char **argv)
{
char *args [argc+1];
memcpy(args, argv, argc * sizeof(char*));
args[argc] = NULL;
ptrace(PTRACE_TRACEME);
kill(getpid(), SIGSTOP);
return execvp(args[0], args);
}

int do_trace (pid_t child)
{
int status, syscall, retval;
waitpid(child, &status, 0);
ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESYSGOOD);
while(1) {
if (wait_for_syscall(child) != 0) break;

syscall = ptrace(PTRACE_PEEKUSER, child, sizeof(long)*ORIG_RAX);
fprintf(stderr, "syscall(%d) = ", syscall);

if (wait_for_syscall(child) != 0) break;

retval = ptrace(PTRACE_PEEKUSER, child, sizeof(long)*RAX);
fprintf(stderr, "%d
", retval);
}
return 0;
}
int main (int argc, char **argv)
{
if (argc < 2) {
fprintf(stderr, "Usage: %s prog args
", argv[0]);
exit(1);
}
pid_t child = fork();
if (child == 0) {
return do_child(argc-1, argv+1);
} else {
return do_trace(child);
}
}

编译应用程序后,您可以运行任何命令 微信 并检查输出:

您可以将 strace 用于多种用途

strace 可以帮助发现程序中不必要地使用系统资源的错误。 同样,程序在使用操作系统资源时表现出的特性也可以通过 strace 显示出来。

由于 strace 直接侦听系统调用,因此无论正在运行的程序的代码是否打开/关闭,它都可以揭示运行时动态。 可以了解为什么程序在开始使用 strace 时会抛出错误。

同样,strace 可以帮助您理解程序意外终止的原因。 因此,熟悉 strace 在 Linux 内核开发和系统管理中非常重要。