获取系统调用ID并将其存储在.txt文件中(LINUX)

时间:2017-05-22 14:47:06

标签: c linux operating-system system monitors

所以我一直在努力练习。我必须得到我选择的任何给定Linux命令(I.E. ls或cd)所做的系统调用,将它们列在.txt文件中,并在它们旁边列出它们的唯一ID。

到目前为止,我得到的是:

strace -o filename.txt ls

这在Linux shell中执行时会给我一个" filename.txt"包含ls命令的所有系统调用的文件。现在在我的C脚本中:

#include <stdio.h>
#include <stdlib.h>

int main(){
     system("strace -o filename.txt ls");
     return 0;
}

这应该和前面的代码一样,但它没有给我任何回报,尽管代码成功编译。我将如何解决此问题,然后获取ID?我正在使用&#34; stdlib&#34;库因为在我的研究中我发现它与系统调用ID有一些关系,但是没有找到关于如何获取它们的任何指示。基本上我必须读取我创建的文件并让它为每个系统调用它的ID。

1 个答案:

答案 0 :(得分:0)

这个练习显然是通过使用ptrace()工具来解决的,因为strace实用程序没有打印系统调用号的选项(据我所知)。

从技术上讲,你可以使用像

这样的东西
printf '#include <sys/syscall.h>\n' | gcc -dD -E - | awk '$1 == "#define" { m[$2] = $3 } END { for (name in m) if (name ~ /^SYS_/) { v = name; while (v in m) v = m[v]; sub(/^SYS_/, "", name); printf "%s %s\n", v, name } }'

生成许多syscall-number syscall-name行,用于将系统调用名称映射回系统调用号,但这很愚蠢且容易出错。愚蠢,因为能够使用ptrace()比使用strace实用程序和使用&#34;聪明的黑客&#34;提供了更多的控制权。如上所述只是意味着你避免学习如何做到这一点,在我看来,根据定义是弄巧成拙,因此完全愚蠢;并且容易出错,因为绝对无法保证已安装的标头与正在运行的体系结构相匹配。这在多架构架构上尤其成问题,您可以使用-m32-m64编译器选项在32位和64位架构之间切换。它们通常具有完全不同的系统调用号码。

基本上,你的程序应该:

  1. fork()子进程。

    在子进程中:

    1. 通过调用prctl(PR_SET_DUMPABLE, 1L)

    2. 启用ptracing
    3. 通过调用ptrace(PTRACE_TRACEME, (pid_t)0, (void *)0, (void *)0)

    4. 让父进程处理跟踪器
    5. (可选)设置跟踪选项。例如,调用ptrace(PTRACE_SETOPTIONS, getpid(), PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT | PTRACE_O_TRACEFORK)以便至少捕获clone()fork()和exec()系列调用。

      如果您未设置PTRACE_O_TRACEEXEC选项,则应使用以下选项停止子进程: raise(SIGSTOP);,以便父进程可以开始跟踪此子进程。

    6. 执行使用例如跟踪的命令。 execv()。特别是,如果第一个命令行参数是要运行的命令,可选地后跟其选项,则可以使用execvp(argv[1], argv + 1);

      如果您在上面设置PTRACE_O_TRACEEXEC选项,那么内核将在执行新二进制文件之前自动暂停子进程。

      如果exec失败,子进程应该退出。我喜欢使用exit(127);来返回退出状态127。

  2. 在父进程中,在循环中使用waitpid(childpid, &status, WUNTRACED | WCONTINUED来捕获子进程中的事件。

    第一个事件应该是初始暂停,即WIFSTOPPED(status)为真。 (如果没有,则出现其他问题。)

  3. waitpid(childpid, &status, WUNTRACED | WCONTINUED)可能返回的原因有三个:

    • 当孩子退出时(WIFEXITED(status)将为真)。 这显然应该结束跟踪,并让父跟踪器进程退出。

    • 当孩子恢复执行时(WIFCONTINUED(status)将为真)。

      您无法假设PTRACE_SYSCALLPTRACE_SYSEMUPTRACE_CONT等命令实际上已导致子进程继续,直到父进程获得此信号。换句话说,你不能只是向子进程发出ptrace()命令,并期望它们以有序的方式发生! ptrace()工具是异步的,调用将立即返回;您需要waitpid() WIFCONTINUED(status)类型的事件才能知道子进程注意到该命令。

    • 当内核停止子进程(SIGTRAP)时,因为子进程即将执行系统调用。 (在父级中,WIFSTOPPED(status)将为真。)

  4. 每当子进程因为要执行系统调用而停止时,您需要使用ptrace(PTRACE_GETREGS, childpid, (void *)0, &regs)在系统调用执行时获取子进程中的CPU寄存器状态。

    regs的类型为struct user,在<sys/user.h>中定义。对于Intel / AMD体系结构,regs.regs.eax(对于32位)或regs.regs.rax(对于64位)包含SYS_foo中定义的系统调用号(<sys/syscall.h>

    然后,您需要调用ptrace(PTRACE_SYSCALL, childpid, (void *)0, (void *)0)来告诉内核执行该系统调用,并再次waitpid()等待WIFCONTINUED(status)事件通知它。

    系统调用完成后,将发生WIFSTOPPED(status)的下一个waitpid()类型事件。如果需要,可以再次使用PTRACE_GETREGS来检查regs.regs.eaxregs.regs.rax,其中包含系统调用返回值;在Intel / AMD上,如果发生错误,它将是一个负的errno值(即-EACCES-EINVAL或类似值。)

    你需要调用ptrace(PTRACE_SYSCALL, childpid, (void *)0, (void *)0)告诉内核继续运行子进程,直到下一次系统调用。

  5. 有很多示例在线显示上面的一些细节,尽管我个人看到的大部分内容在错误检查方面都非常宽松,偶尔也会忽略检查WIFCONTINUED(status) waitpid()事件。我甚至写了一篇详细说明如何在StackOverflow上停止和继续单个线程的答案。由于该技术可以用作非常强大的自定义调试工具,我建议您尝试学习该工具,以便在工作中利用它,而不是仅仅复制粘贴一些现有代码以获得练习的及格分数。