gpt4 book ai didi

c - 为什么这个 ptrace 程序说系统调用返回了 -38?

转载 作者:IT王子 更新时间:2023-10-29 01:09:11 25 4
gpt4 key购买 nike

this one相同除了我正在运行 execl("/bin/ls", "ls", NULL);

结果显然是错误的,因为每个系统调用都返回 -38:

[user@ test]# ./test_trace 
syscall 59 called with rdi(0), rsi(0), rdx(0)
syscall 12 returned with -38
syscall 12 called with rdi(0), rsi(0), rdx(140737288485480)
syscall 9 returned with -38
syscall 9 called with rdi(0), rsi(4096), rdx(3)
syscall 9 returned with -38
syscall 9 called with rdi(0), rsi(4096), rdx(3)
syscall 21 returned with -38
syscall 21 called with rdi(233257948048), rsi(4), rdx(233257828696)
...

有人知道原因吗?

更新

现在的问题是:

execve called with rdi(4203214), rsi(140733315680464), rdx(140733315681192)
execve returned with 0
execve returned with 0
...

execve 返回 0 两次,为什么?

最佳答案

该代码不考虑来自子进程的 exec 通知,因此最终将系统调用入口作为系统调用导出处理,将系统调用导出作为系统调用入口处理。这就是为什么您会看到“系统调用 12 已返回之前系统调用 12 已调用”等(-38ENOSYS 由内核的系统调用入口代码作为默认返回值放入 RAX。)

作为ptrace(2) man page状态:

PTRACE_TRACEME

Indicates that this process is to be traced by its parent. Any signal (except SIGKILL) delivered to this process will cause it to stop and its parent to be notified via wait(). Also, all subsequent calls to exec() by this process will cause a SIGTRAP to be sent to it, giving the parent a chance to gain control before the new program begins execution. [...]

您说您运行的原始代码“与 this one 相同,只是我运行的是 execl("/bin/ls", "ls", NULL);” .嗯,显然不是,因为您使用的是 x86_64 而不是 32 位,并且至少更改了消息。

但是,假设您没有做太多其他更改,wait() 第一次 唤醒父级时,它不是用于系统调用进入或退出 - parent 还没有执行 ptrace(PTRACE_SYSCALL,...)。相反,您看到的是 child 执行了 exec 的通知(在 x86_64 上,系统调用 59 是 execve)。

代码错误地将其解释为系统调用条目。 然后它调用ptrace(PTRACE_SYSCALL,...),下次父级被唤醒时它是一个系统调用入口(系统调用12 ), 但代码将其报告为系统调用退出。

请注意,在这种原始情况下,您永远不会看到 execve 系统调用进入/退出 - 只有附加通知 - 因为父级不执行 ptrace(PTRACE_SYSCALL,...) 直到它发生。

如果您确实安排代码以便捕获execve 系统调用入口/导出,您将看到您观察到的新行为。父级将被唤醒 三次 次:一次用于 execve 系统调用条目(由于使用了 ptrace(PTRACE_SYSCALL,...),一次用于execve 系统调用退出(也是由于使用了 ptrace(PTRACE_SYSCALL,...),第三次是 exec 通知(发生无论如何)。


这是一个完整的示例(对于 x86 或 x86_64),它通过首先停止子进程来注意显示 exec 本身的行为:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/reg.h>

#ifdef __x86_64__
#define SC_NUMBER (8 * ORIG_RAX)
#define SC_RETCODE (8 * RAX)
#else
#define SC_NUMBER (4 * ORIG_EAX)
#define SC_RETCODE (4 * EAX)
#endif

static void child(void)
{
/* Request tracing by parent: */
ptrace(PTRACE_TRACEME, 0, NULL, NULL);

/* Stop before doing anything, giving parent a chance to catch the exec: */
kill(getpid(), SIGSTOP);

/* Now exec: */
execl("/bin/ls", "ls", NULL);
}

static void parent(pid_t child_pid)
{
int status;
long sc_number, sc_retcode;

while (1)
{
/* Wait for child status to change: */
wait(&status);

if (WIFEXITED(status)) {
printf("Child exit with status %d\n", WEXITSTATUS(status));
exit(0);
}
if (WIFSIGNALED(status)) {
printf("Child exit due to signal %d\n", WTERMSIG(status));
exit(0);
}
if (!WIFSTOPPED(status)) {
printf("wait() returned unhandled status 0x%x\n", status);
exit(0);
}
if (WSTOPSIG(status) == SIGTRAP) {
/* Note that there are *three* reasons why the child might stop
* with SIGTRAP:
* 1) syscall entry
* 2) syscall exit
* 3) child calls exec
*/
sc_number = ptrace(PTRACE_PEEKUSER, child_pid, SC_NUMBER, NULL);
sc_retcode = ptrace(PTRACE_PEEKUSER, child_pid, SC_RETCODE, NULL);
printf("SIGTRAP: syscall %ld, rc = %ld\n", sc_number, sc_retcode);
} else {
printf("Child stopped due to signal %d\n", WSTOPSIG(status));
}
fflush(stdout);

/* Resume child, requesting that it stops again on syscall enter/exit
* (in addition to any other reason why it might stop):
*/
ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL);
}
}

int main(void)
{
pid_t pid = fork();

if (pid == 0)
child();
else
parent(pid);

return 0;
}

它给出了这样的东西(这是针对 64 位的 - 系统调用编号对于 32 位是不同的;特别是 execve 是 11,而不是 59):

Child stopped due to signal 19SIGTRAP: syscall 59, rc = -38SIGTRAP: syscall 59, rc = 0SIGTRAP: syscall 59, rc = 0SIGTRAP: syscall 63, rc = -38SIGTRAP: syscall 63, rc = 0SIGTRAP: syscall 12, rc = -38SIGTRAP: syscall 12, rc = 5324800...

信号 19 是明确的 SIGSTOP;如上所述, child 为 execve 停止了 三次 次;然后两次(进入和退出)用于其他系统调用。

如果您真的对 ptrace() 的所有细节感兴趣,我所知道的最好的文档是 README-linux-ptrace文件在 strace资源。正如它所说,“API 很复杂并且有微妙的怪癖”......

关于c - 为什么这个 ptrace 程序说系统调用返回了 -38?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7514837/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com