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

标签 c linux operating-system system monitors

因此,我一直在努力进行这项练习。我必须获得我选择的任何给定Linux命令(即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?我之所以使用“ stdlib”库,是因为在研究中我发现它与系统调用ID有一定关系,但没有找到有关如何获取它们的迹象。基本上,我必须阅读我创建的文件,并让每个系统调用它的ID。

最佳答案

该练习显然设计为使用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行,用于将syscall名称映射回syscall编号,但这将很愚蠢且容易出错。愚蠢的是,因为与使用ptrace()实用程序相比,能够使用strace给您更多的控制权,并且使用上述“聪明的hack”只是意味着您避免学习如何做,在我看来,这是自定义的-击败并因此完全愚蠢;并且容易出错,因为绝对不能保证已安装的标头与运行的体系结构匹配。在多体系结构上,这尤其成问题,您可以使用-m32-m64编译器选项在32位和64位体系结构之间切换。它们通常具有完全不同的系统调用号。

本质上,您的程序应:


fork()子进程。

在子进程中:


通过调用prctl(PR_SET_DUMPABLE, 1L)启用追踪
通过调用ptrace(PTRACE_TRACEME, (pid_t)0, (void *)0, (void *)0)使父进程处理跟踪程序
(可选)设置跟踪选项。例如,调用ptrace(PTRACE_SETOPTIONS, getpid(), PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT | PTRACE_O_TRACEFORK),以便至少捕获clone()fork()和exec()系列的系统调用。

如果您未设置PTRACE_O_TRACEEXEC选项,则应在此时使用以下命令停止子进程: raise(SIGSTOP);,以便父进程可以开始跟踪该子进程。
使用以下命令执行要跟踪的命令execv()。特别是,如果第一个命令行参数是要运行的命令(可选地,其后跟其选项),则可以使用execvp(argv[1], argv + 1);

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

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

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

第一个事件应该是初始暂停,即WIFSTOPPED(status)为true。 (如果没有,则其他地方出了问题。)
waitpid(childpid, &status, WUNTRACED | WCONTINUED)可能返回的三种原因有以下三种:


子项退出时(WIFEXITED(status)为true)。
显然,这应该结束跟踪,并退出父跟踪程序。
当子级恢复执行时(WIFCONTINUED(status)为true)。

您不能假定PTRACE_SYSCALLPTRACE_SYSEMUPTRACE_CONT等命令实际上已导致子进程继续运行,直到父进程获得此信号为止。换句话说,您不能仅将ptrace()命令发送到子进程,并期望它们以有序的方式发生! ptrace()工具是异步的,调用将立即返回;您需要为waitpid()事件类型指定WIFCONTINUED(status)才能知道子进程已注意该命令。
当内核停止子进程(使用SIGTRAP)时,因为子进程将要执行syscall。 (在父级中,WIFSTOPPED(status)为true。)

每当子进程由于要执行系统调用而停止时,您都需要使用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)来告诉内核继续运行子级,直到下一个syscall。


有很多在线示例显示了上面的一些详细信息,尽管我个人看到的大多数示例在错误检查方面相当松懈,并且有时会忽略检查WIFCONTINUED(status) waitpid()事件。我什至写了一个答案,详细说明了如何在StackOverflow上停止和继续各个线程。由于该技术可以用作功能非常强大的自定义调试工具,因此我建议您尝试学习该功能,以便可以在工作中利用它,而不仅仅是将现有代码复制粘贴以在练习中获得合格的分数。

关于c - 获取系统调用ID并将其存储在.txt文件中(LINUX),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44115933/

相关文章:

c - 调用malloc()和free()时寄存器/缓存和主存之间是否有数据传输

linux - 尝试在 Mandriva 上构建 OS161 时出现 "bmake: no system rules (sys.mk)"

c - C变量中是否有可能 "Turn off"或 "Turn on"波动?

linux - 在单实例模式下使用 xinetd 运行 shell 脚本

linux - 衡量开发/随机效率

python - 运行时错误与 Django on Amazon AWS EC2 Linux AMI 问题

java - 在 Java 中查询系统(非 JVM)正常运行时间

c - 写入/访问I/O端口硬件看门狗寄存器linux

C: OpenSSL RSA_private_decrypt() 失败,返回 "error:0407A079:rsa routines:RSA_padding_check_PKCS1_OAEP:oaep decoding error”

C++:将 LPTSTR 转换为字符数组