因此,我一直在努力进行这项练习。我必须获得我选择的任何给定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_SYSCALL
,PTRACE_SYSEMU
,PTRACE_CONT
等命令实际上已导致子进程继续运行,直到父进程获得此信号为止。换句话说,您不能仅将ptrace()
命令发送到子进程,并期望它们以有序的方式发生! ptrace()
工具是异步的,调用将立即返回;您需要为waitpid()
事件类型指定WIFCONTINUED(status)
才能知道子进程已注意该命令。当内核停止子进程(使用
SIGTRAP
)时,因为子进程将要执行syscall。 (在父级中,WIFSTOPPED(status)
为true。)每当子进程由于要执行系统调用而停止时,您都需要使用
ptrace(PTRACE_GETREGS, childpid, (void *)0, ®s)
来在系统调用执行时获取子进程中的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.eax
或regs.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/