我正在编写一个程序,它可以跟踪自己的系统调用。我很难做到这一点。我试着调用一个fork()来创建它自己的一个示例(代码),然后监视产生的子进程。
目标是让父进程返回子进程所做的每个系统调用的索引,并将其输出到屏幕上。不知何故,它并没有按计划工作。
下面是代码:
# include <unistd.h> /* for read(), write(), close(), fork() */
# include <fcntl.h> /* for open() */
# include <stdio.h>
# include <sys/ptrace.h>
# include <sys/reg.h>
# include <sys/wait.h>
# include <sys/types.h>
int main(int argc, char *argv[]) {
pid_t child;
long orig_eax;
child = fork();
if (0 == child)
{
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
if (argc != 3) {
fprintf(stderr, "Usage: copy <filefrom> <fileto>\n");
return 1;
}
int c;
size_t file1_fd, file2_fd;
if ((file1_fd = open(argv[1], O_RDONLY)) < 0) {
fprintf(stderr, "copy: can't open %s\n", argv[1]);
return 1;
}
if ((file2_fd = open(argv[2], O_WRONLY | O_CREAT)) < 0) {
fprintf(stderr, "copy: can't open %s\n", argv[2]);
return 1;
}
while (read(file1_fd, &c, 1) > 0)
write(file2_fd, &c, 1);
}
else
{
wait(NULL);
orig_eax = ptrace (PTRACE_PEEKUSER, child, 4 * ORIG_EAX, NULL);
printf("copy made a system call %ld\n", orig_eax);
ptrace(PTRACE_CONT, child, NULL, NULL);
}
return 0;
}
此代码基于以下代码:
# include <sys/ptrace.h>
# include <sys/types.h>
# include <sys/wait.h>
# include <unistd.h>
# include <linux/user.h> /* For constants
ORIG_EAX etc */
int main()
{
pid_t child;
long orig_eax;
child = fork();
if(child == 0) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execl("/bin/ls", "ls", NULL);
}
else {
wait(NULL);
orig_eax = ptrace(PTRACE_PEEKUSER,
child, 4 * ORIG_EAX,
NULL);
printf("The child made a "
"system call %ld\n", orig_eax);
ptrace(PTRACE_CONT, child, NULL, NULL);
}
return 0;
}
此示例的输出为:
The child made a system call 11
它是exec系统调用的索引。
根据wait()的手册页:
All of these system calls are used to wait for state changes in a child
of the calling process, and obtain information about the child whose
state has changed. A state change is considered to be: the child terminated;
the child was stopped by a signal; or the child was resumed by
a signal.
我的理解是,每次用户程序调用系统调用时,内核都会在执行系统调用例程之前首先检查进程是否被跟踪,然后用一个信号暂停该进程,并将控制权返回给父进程。这难道不是一个状态变化吗?
4条答案
按热度按时间6ss1mwsb1#
问题是,当子进程调用
ptrace(TRACEME)
时,它会设置自己进行跟踪,但实际上并没有停止--它会一直运行,直到调用exec
为止(在这种情况下,它会以SIGTRAP停止),或者它会得到其他一些信号。所以为了让父进程看到它在没有执行调用的情况下做了什么,您需要安排孩子接收信号。最简单的方法可能是让孩子在调用ptrace(TRACEME)
后立即调用raise(SIGCONT);
(或任何其他信号现在在父进程中,您只需等待(一次),并假设子进程在系统调用时停止。如果它在信号处停止,则不会出现这种情况,因此您需要调用
wait(&status)
来获取子进程的状态,并调用WIFSTOPPED(status)
和WSTOPSIG(status)
来查看它为什么停止。如果它是由于系统调用而停止的,则信号将是SIGTRAP。如果您希望在客户机中看到多个系统调用,则需要在一个循环中完成所有这些操作;类似于:
请注意,对于每个系统调用,它将停止两次--一次在系统调用之前,第二次在系统调用完成之后。
xxe27gdn2#
您基本上是在尝试在linux中编写strace二进制文件,它跟踪进程的系统调用。Linux为此提供了ptrace(2)系统调用。ptrace系统调用需要4个参数,第一个参数告诉您需要做什么。操作系统通过信号与父进程通信,子进程通过发送SIGSTOP停止。大体上,您需要遵循以下步骤。
请注意,寄存器的阅读和解释将取决于您的架构。上面的代码只是一个例子,以获得正确的,你需要深入挖掘。有一个看strace代码进一步了解。
41ik7eoe3#
在你的父母中,你想监听多少个电话?如果你想监听不止一个电话,你就需要某种循环。
请注意示例中的行,这一点很重要:
查看man page,子进程需要执行
PTRACE_TRACEME
和exec
,或者父进程需要使用PTRACE_ATTACH
进行跟踪。我在您的代码中没有看到这两种情况:父进程可以通过调用fork(2)并让生成的子进程执行PTRACE_TRACEME,然后(通常)执行exec(3)来启动跟踪。或者,父进程可以使用PTRACE_ATTACH开始跟踪现有进程。
o0lyfsai4#
把克里斯·多德说的话放在一起