当前位置: 首页 > news >正文

Linux ptrace系统调用

文章目录

  • 一、ptrace 简介
  • 二、ptrace 参数request
    • 2.1 PTRACE_TRACEME
    • 2.2 PTRACE_PEEKTEXT, PTRACE_PEEKDATA
    • 2.3 PTRACE_PEEKUSER
    • 2.4 PTRACE_POKETEXT, PTRACE_POKEDATA
    • 2.5 PTRACE_POKEUSER
    • 2.6 PTRACE_GETREGS, PTRACE_GETFPREGS
    • 2.7 PTRACE_GETREGSET
    • 2.8 PTRACE_SETREGS, PTRACE_SETFPREGS
    • 2.9 PTRACE_SETREGSET
    • 2.10 PTRACE_GETSIGINFO
    • 2.11 PTRACE_SETSIGINFO
    • 2.12 PTRACE_SETOPTIONS
    • 2.13 PTRACE_CONT
    • 2.14 PTRACE_SYSCALL, PTRACE_SINGLESTEP
    • 2.15 PTRACE_ATTACH
    • 2.16 PTRACE_SEIZE
    • 2.17 PTRACE_DETACH
  • 三、Stopped states
    • 3.1 简介
    • 3.2 ptrace-stop
    • 3.3 ptrace-stop内核源码
  • 四、Signal-delivery-stop
  • 五、Group-stop
  • 六、PTRACE_EVENT stops
  • 七、Syscall-stops
  • 八、 execve under ptrace

一、ptrace 简介

NAMEptrace - process traceSYNOPSIS#include <sys/ptrace.h>long ptrace(enum __ptrace_request request, pid_t pid,void *addr, void *data);

ptrace()系统调用提供了一种方法,通过这种方法,一个进程(the “tracer” – 跟踪器)可以观察和控制另一个进程的执行(the “tracee” – 被跟踪的进程),并检查和更改tracee的内存和寄存器。它主要用于实现断点、单步调试和系统调用跟踪。

ptrace()系统调用是 gdb 和 strace 实现的基础。

首先需要将tracee附加到 the tracer。附加和后续命令是按线程执行的:在多线程进程中,每个线程都可以单独附加到(可能不同的)tracer,或者不附加,因此不进行调试。因此,“tracee”总是指“(一个)线程”,而不是“(可能是多线程的)进程”。

这也是gdb可以调试多线程的原理。

GDB(GNU调试器)进行多线程调试的一些命名:

info threads:在GDB中运行此命令可以查看当前正在运行的线程列表。它将显示每个线程的唯一标识符(线程ID)和当前所在的位置。thread <thread-id>:使用该命令可以切换到指定的线程。通过提供线程ID作为参数,你可以选择要调试的特定线程。thread apply <thread-range> <command>:这个命令可以对一组线程执行指定的GDB命令。线程范围可以是线程ID的列表或范围。例如,thread apply 1-3 bt将对线程123执行bt命令(打印线程栈回溯)。set scheduler-locking off:启用此选项可以禁用调度器锁定,允许所有线程并发运行。这在调试多线程应用程序时可能很有用,因为默认情况下,GDB会通过锁定调度器以确保只有一个线程处于活动状态。set scheduler-locking on:通过设置此选项,可以重新启用调度器锁定,使只有一个线程处于活动状态。这在需要集中调试某个特定线程时可能很有用。

ptrace命令总是使用以下形式的调用发送到特定的tracee:

ptrace(PTRACE_foo, pid, ...)

其中pid是相应Linux线程的线程ID。

进程可以通过调用fork并让生成的子进程执行PTRACE_TRACEME,然后(通常)执行一个execve来启动跟踪。或者,一个进程可以使用PTRACE_ATTACH或PTRACE_SEIZE开始跟踪另一个进程。
(1)子进程执行PTRACE_TRACEME,子进程主动进入被跟踪状态 – gdb 调试原理
(2)一个进程 tracer 使用PTRACE_ATTACH开始跟踪另一个进程 tracee ,tracee 是被动进入被跟踪状态。

在跟踪过程中,每次传递信号时,跟踪都会停止,即使信号被忽略。(SIGKILL是一个例外,它具有通常的效果。)tracer 将在下一次调用waitpid(或一个相关的“wait”系统调用)时得到通知;该调用将返回一个状态值,该值包含指示tracee中停止原因的信息。当tracee停止时,tracer 可以使用各种ptrace请求来检查和修改tracee。跟踪器然后使跟踪继续,可选地忽略传递的信号(或者甚至传递不同的信号)。

如果PTRACE_O_TRACEEXEC选项无效,则被跟踪进程对execve的所有成功调用都将导致向其发送SIGTRAP信号,从而使父进程有机会在新程序开始执行之前获得控制权。

当tracer完成跟踪时,它可以通过PTRACE_DETACH结束跟踪,使 tracee 继续以正常的、未被跟踪的模式执行。

二、ptrace 参数request

ptrace系统调用第一个参数的 request 值决定要执行的操作:

2.1 PTRACE_TRACEME

该值仅tracee使用,指示此进程将由其父进程跟踪。如果进程的父进程不希望跟踪它,那么它可能不应该发出此请求。

PTRACE_TRACEME请求仅由tracee使用;剩下的request值仅由tracer使用。在以下请求中,pid指定要执行操作的 tracee 的线程ID。对于除PTRACE_ATTACH、PTRACE_SEIZE、PTRACE_INTERRUPT和PTRACE_KILL之外的请求,必须停止 tracee 。

以下request值仅由tracer使用

2.2 PTRACE_PEEKTEXT, PTRACE_PEEKDATA

PTRACE_PEEKTEXT和PTRACE_PEEKDATA是用于在被跟踪进程的内存中读取数据的ptrace系统调用的请求选项。

在Linux中,由于没有单独的文本和数据地址空间,因此这两个请求目前是等效的。

PTRACE_PEEKTEXT:通过使用ptrace系统调用的PTRACE_PEEKTEXT请求选项,可以读取被跟踪进程内存中给定地址处的一个字(word)并作为ptrace调用的结果返回。这个请求用于读取代码段(text segment)中的数据。

PTRACE_PEEKDATA:同样,PTRACE_PEEKDATA请求选项也用于读取被跟踪进程内存中给定地址处的一个字(word)。在Linux中,由于没有明确的数据段和代码段的区分,因此使用PTRACE_PEEKDATA请求选项也可以读取数据段(data segment)中的数据。

2.3 PTRACE_PEEKUSER

PTRACE_PEEKUSER是ptrace系统调用的一个请求选项,用于从被跟踪进程的USER区域中读取一个字(word)的数据。

在被跟踪进程的USER区域中存储着进程的寄存器和其他关于进程的信息。具体的结构和定义可以在<sys/user.h>头文件中找到。通过使用PTRACE_PEEKUSER选项,可以读取USER区域中指定偏移量(offset)处的一个字,并将其作为ptrace调用的结果返回。

#ifdef __x86_64__
/* Index into an array of 8 byte longs returned from ptrace forlocation of the users' stored general purpose registers.  */# define R15	0
# define R14	1
# define R13	2
# define R12	3
# define RBP	4
# define RBX	5
# define R11	6
# define R10	7
# define R9	8
# define R8	9
# define RAX	10
# define RCX	11
# define RDX	12
# define RSI	13
# define RDI	14
# define ORIG_RAX 15
# define RIP	16
# define CS	17
# define EFLAGS	18
# define RSP	19
# define SS	20
# define FS_BASE 21
# define GS_BASE 22
# define DS	23
# define ES	24
# define FS	25
# define GS	26

2.4 PTRACE_POKETEXT, PTRACE_POKEDATA

PTRACE_POKETEXT和PTRACE_POKEDATA是用于向被跟踪进程的内存写入数据的ptrace系统调用的请求选项。

在Linux中,由于没有单独的文本和数据地址空间,因此这两个请求目前是等效的。

PTRACE_POKETEXT:通过使用ptrace系统调用的PTRACE_POKETEXT请求选项,可以将指定的字(word)数据写入到被跟踪进程内存中的给定地址。这个请求用于写入代码段(text segment)中的数据。

PTRACE_POKEDATA:同样,PTRACE_POKEDATA请求选项也用于将指定的字(word)数据写入到被跟踪进程内存中的给定地址。在Linux中,由于没有明确的数据段和代码段的区分,因此使用PTRACE_POKEDATA请求选项也可以写入数据段(data segment)中的数据。

2.5 PTRACE_POKEUSER

PTRACE_POKEUSER是ptrace系统调用的一个请求选项,用于将一个字(word)的数据复制到被跟踪进程的USER区域的指定偏移量(offset)处。

在被跟踪进程的USER区域中存储着进程的寄存器和其他关于进程的信息。通过使用PTRACE_POKEUSER选项,可以将指定的字数据复制到USER区域的偏移量处。通常,偏移量需要按字对齐。

但需要注意的是,为了维护内核的完整性,一些对USER区域的修改是不允许的。

2.6 PTRACE_GETREGS, PTRACE_GETFPREGS

PTRACE_GETREGS和PTRACE_GETFPREGS是ptrace系统调用的两个请求选项,用于将被跟踪进程的通用寄存器(general-purpose registers)或浮点寄存器(floating-point registers)的值复制到跟踪进程中的指定地址。

PTRACE_GETREGS:通过使用ptrace系统调用的PTRACE_GETREGS请求选项,可以将被跟踪进程的通用寄存器的值复制到跟踪进程中的指定地址。可以使用<sys/user.h>头文件中定义的结构体来解析和访问复制的寄存器数据。

PTRACE_GETFPREGS:使用ptrace系统调用的PTRACE_GETFPREGS请求选项,可以将被跟踪进程的浮点寄存器的值复制到跟踪进程中的指定地址。浮点寄存器通常用于处理浮点数和浮点运算。同样,可以使用<sys/user.h>头文件中定义的结构体来解析和访问复制的寄存器数据。

2.7 PTRACE_GETREGSET

TRACE_GETREGSET是自Linux 2.6.34版本引入的ptrace系统调用的一个请求选项,用于读取被跟踪进程的寄存器信息。通过指定addr参数,在与体系结构相关的方式下,可以指定要读取的寄存器类型。

通常情况下,使用NT_PRSTATUS(数值为1)作为addr的值可以读取通用寄存器的值。如果CPU具有浮点寄存器和/或向量寄存器等其他类型的寄存器,可以通过将addr设置为相应的NT_foo常量来检索它们。

data参数指向一个struct iovec结构体,描述了目标缓冲区的位置和长度。在返回时,内核会修改iov.len以指示实际返回的字节数。

2.8 PTRACE_SETREGS, PTRACE_SETFPREGS

PTRACE_SETREGS和PTRACE_SETFPREGS是ptrace系统调用的两个请求选项,用于从跟踪进程中的指定地址修改被跟踪进程的通用寄存器(general-purpose registers)或浮点寄存器(floating-point registers)的值。

PTRACE_SETREGS:通过使用ptrace系统调用的PTRACE_SETREGS请求选项,可以从跟踪进程中的指定地址修改被跟踪进程的通用寄存器的值。被修改的寄存器数据必须符合特定的格式,具体取决于体系结构。对于某些操作系统,可能会限制对某些特定的通用寄存器进行修改。

PTRACE_SETFPREGS:使用ptrace系统调用的PTRACE_SETFPREGS请求选项,可以从跟踪进程中的指定地址修改被跟踪进程的浮点寄存器的值。被修改的浮点寄存器数据必须符合特定的格式,具体取决于体系结构。

2.9 PTRACE_SETREGSET

PTRACE_SETREGSET是自Linux 2.6.34版本引入的ptrace系统调用的请求选项,用于修改被跟踪进程的寄存器信息。addr和data的含义与PTRACE_GETREGSET类似。

与PTRACE_GETREGSET类似,PTRACE_SETREGSET请求选项允许以体系结构相关的方式指定要修改的寄存器类型。addr参数用于指定要修改的寄存器类型,而data参数指向一个struct iovec结构体,描述了包含要写入被跟踪进程寄存器信息的源缓冲区的位置和长度。

2.10 PTRACE_GETSIGINFO

PTRACE_GETSIGINFO是自Linux 2.3.99-pre6版本引入的ptrace系统调用的请求选项,用于从跟踪进程中获取导致进程停止的信号的相关信息。它将一个siginfo_t结构体(参见sigaction(2))从被跟踪进程复制到跟踪进程的指定地址中(data参数)。

2.11 PTRACE_SETSIGINFO

PTRACE_SETSIGINFO是自Linux 2.3.99-pre6版本引入的ptrace系统调用的请求选项,用于设置信号信息。它将一个siginfo_t结构体从跟踪进程的指定地址(data参数)复制到被跟踪进程中。这将仅影响通常会传递给被跟踪进程并被跟踪进程捕获的信号。

2.12 PTRACE_SETOPTIONS

PTRACE_SETOPTIONS是ptrace系统调用的一个请求选项,用于设置跟踪选项(trace options)。它允许在调用ptrace时指定一组选项,以控制跟踪进程的行为。
具体参考 man 手册。

2.13 PTRACE_CONT

PTRACE_CONT是ptrace系统调用的一个请求选项,用于重新启动被停止的被跟踪进程。当ptrace调用使用PTRACE_CONT选项时,被跟踪进程将继续执行。

以下是PTRACE_CONT请求选项的使用方式:

ptrace(PTRACE_CONT, pid, NULL, data);

pid是被跟踪进程的进程ID。
data参数如果非零,表示要发送给被跟踪进程的信号编号;如果为零,表示不发送任何信号。

使用PTRACE_CONT选项,跟踪进程可以控制是否向被跟踪进程发送信号。

2.14 PTRACE_SYSCALL, PTRACE_SINGLESTEP

PTRACE_SYSCALL和PTRACE_SINGLESTEP是ptrace系统调用的两个请求选项,用于重新启动被停止的被跟踪进程,并在特定条件下再次停止。

PTRACE_SYSCALL:重新启动被跟踪进程,并在下一次进入或退出系统调用时停止。当使用PTRACE_SYSCALL选项时,被跟踪进程将在下一次系统调用的入口或出口处停止执行,以供跟踪进程进行检查或操作。
PTRACE_SINGLESTEP:重新启动被跟踪进程,并在执行一条指令后停止。当使用PTRACE_SINGLESTEP选项时,被跟踪进程将在执行完一条指令后立即停止,以供跟踪进程进行单步调试或其他操作。

这两个选项都会使被跟踪进程看起来好像是接收到了一个SIGTRAP信号而停止执行。跟踪进程可以在被跟踪进程停止时进行进一步的检查或操作。

以下是这两个选项的使用方式:

ptrace(PTRACE_SYSCALL, pid, NULL, data);
ptrace(PTRACE_SINGLESTEP, pid, NULL, data);

pid是被跟踪进程的进程ID。
data参数如果非零,表示要发送给被跟踪进程的信号编号;如果为零,表示不发送任何信号。

在停止时,被跟踪进程会看起来好像是接收到了一个SIGTRAP信号。

2.15 PTRACE_ATTACH

PTRACE_ATTACH是ptrace系统调用的一个请求选项,用于将指定的进程附加到调用进程中,使其成为调用进程的被跟踪进程。被附加的进程将收到一个SIGSTOP信号,但不一定会在此调用完成后立即停止执行,可以使用waitpid等待被跟踪进程停止。

以下是PTRACE_ATTACH选项的使用方式:

ptrace(PTRACE_ATTACH, pid, NULL, NULL);

pid是要附加的进程的进程ID。

当使用PTRACE_ATTACH命令将跟踪器附加到目标进程时,会向目标进程的线程发送SIGSTOP信号。如果跟踪器希望这个SIGSTOP信号不产生任何效果,它需要将其抑制。需要注意的是,如果在附加过程中同时向该线程发送其他信号,则跟踪器可能会先看到跟踪对象进入 signal-delivery-stop 状态,并接收到其他信号!通常的做法是在看到SIGSTOP信号之前重新注入这些信号,然后抑制SIGSTOP信号的注入。这里的设计缺陷在于ptrace附加和同时传递的SIGSTOP信号可能存在竞争条件,导致同时传递的SIGSTOP信号可能会丢失。

当使用PTRACE_ATTACH命令附加到目标进程时,会发送SIGSTOP信号,而跟踪器通常会将其抑制。这可能会导致被跟踪进程中当前正在执行的系统调用出现意外的EINTR错误返回,就像在"Signal injection and suppression"部分中所描述的那样。

在一般情况下,当一个进程处于系统调用过程中,如果收到一个信号,系统调用可能会被中断,并返回一个EINTR错误。当跟踪器在附加过程中抑制了SIGSTOP信号时,如果被跟踪进程正好处于系统调用中,那么收到的SIGSTOP信号可能会导致系统调用被中断,从而返回一个EINTR错误。

这个情况可能会在跟踪过程中出现,并且需要被跟踪器适当处理。一种常见的方法是在收到SIGSTOP信号之前,跟踪器可以选择暂停被跟踪进程的执行,以确保不会在系统调用期间收到信号。然后,跟踪器可以继续附加和执行其他操作。

总之,由于SIGSTOP信号的抑制可能导致系统调用中断并返回EINTR错误,跟踪器在附加过程中应该注意处理这种情况,以确保跟踪的准确性。

2.16 PTRACE_SEIZE

PTRACE_SEIZE是自Linux 3.4版本引入的ptrace系统调用的一个请求选项。它用于将指定的进程附加到调用进程中,使其成为调用进程的被跟踪进程,但与PTRACE_ATTACH不同,PTRACE_SEIZE不会停止被跟踪进程的执行

以下是PTRACE_SEIZE选项的使用方式:

ptrace(PTRACE_SEIZE, pid, NULL, data);

pid是要附加的进程的进程ID。
data参数是一个位掩码,用于激活要立即启用的ptrace选项。

使用PTRACE_SEIZE选项时,被跟踪进程不会被停止,而是直接附加到调用进程中。只有经过PTRACE_SEIZE附加的进程才能接受PTRACE_INTERRUPT和PTRACE_LISTEN命令。

addr参数必须为零。

PTRACE_INTERRUPT和PTRACE_LISTEN命令是自Linux 3.4版本引入的ptrace系统调用的请求选项。具体请参考man手册。

自从Linux 3.4版本以后,可以使用PTRACE_SEIZE命令来代替PTRACE_ATTACH。PTRACE_SEIZE在附加到进程时不会停止其执行。如果需要在附加后或任何其他时间停止进程而不发送任何信号,则可以使用PTRACE_INTERRUPT命令。

通过使用PTRACE_SEIZE,跟踪器可以在不立即停止进程执行的情况下附加到目标进程。这使得跟踪器可以在不中断进程正常流程的情况下对其进行操作。如果跟踪器希望在某个时刻停止进程,可以使用PTRACE_INTERRUPT命令,而无需向进程发送任何信号。

通过适当使用PTRACE_SEIZE和PTRACE_INTERRUPT,跟踪器可以更有控制地管理被附加进程的执行,并在需要时有选择地停止它,而无需依赖信号的发送。

2.17 PTRACE_DETACH

PTRACE_DETACH是ptrace系统调用的一个请求选项,用于从调用进程中分离(detach)被跟踪进程。它会在分离之前以类似于PTRACE_CONT的方式重新启动被跟踪进程。在Linux下,无论使用哪种方法进行跟踪,都可以使用PTRACE_DETACH选项将被跟踪进程分离。

以下是PTRACE_DETACH选项的使用方式:

ptrace(PTRACE_DETACH, pid, NULL, NULL);

pid是要分离的进程的进程ID。

使用PTRACE_DETACH选项时,被跟踪进程会被重新启动,然后与调用进程分离。该选项会将被跟踪进程从跟踪状态中解放出来,使其恢复正常执行。

PTRACE_DETACH是一个重启操作,因此需要 tracee 处于ptrace-stop状态才能执行。如果 tracee 处于 signal-delivery-stop 状态,可以注入信号。否则,sig参数可能会被忽略。

如果跟 tracee 在 tracer 想要分离它时正在运行,通常的解决方案是发送SIGSTOP信号(使用tgkill确保信号发送到正确的线程),等待 tracee 在信号传递停止状态下停止接收SIGSTOP信号,然后分离它(抑制SIGSTOP注入)。这里存在一个设计缺陷,即与并发的SIGSTOP信号可能存在竞争条件。另一个复杂性在 tracee 可能进入其他ptrace-stop状态,并需要重新启动和等待,直到看到SIGSTOP信号。还有一个问题是要确保 tracee 没有处于已经ptrace-stop的状态,因为在此期间不会发生任何信号传递,即使是SIGSTOP信号也不会传递。

如果 tracer 终止,所有 tracees 都会自动分离并重新启动,除非它们处于组停止状态。目前对于从组停止状态重新启动的处理存在问题,但“按计划”的行为是保持进程停止,并等待SIGCONT信号。如果 tracee 从 signal-delivery-stop 状态重新启动,挂起的信号将被注入。

三、Stopped states

3.1 简介

被跟踪的进程可以处于两种状态:运行状态或停止状态。就ptrace而言,即使 tracee 在系统调用(比如read、pause等)中被阻塞,仍然被认为是运行状态,即使进程被长时间阻塞。在使用PTRACE_LISTEN之后,跟踪对象的状态有些模糊:它既不处于任何ptrace-stop状态(ptrace命令无法对其起作用,并且会向其发送waitpid通知),但也可能被视为“停止”状态,因为它不执行指令(不在调度中)。如果在PTRACE_LISTEN之前处于组停止状态,它在接收到SIGCONT信号之前将不会对信号做出响应。

当tracee停止时,有很多种状态,在ptrace讨论中,它们经常被混为一谈。因此,使用精确的术语很重要。

当运行的tracee进入 ptrace-stop 时,它会通知其 tracer 使用waitpid系统调用(或其他“wait”系统调用之一)。这里假定tracer调用 “wait”系统调用为:

pid = waitpid(pid_or_minus_1, &status, __WALL);

跟踪器以返回值大于0且WIFSTOPPED(status)为真的形式报告处于ptrace-stop状态的跟踪对象。

__WALL标志不包括WSTOPPED和WEXITED标志,但隐含了它们的功能。

在调用waitpid时设置WCONTINUED标志并不推荐使用:"continued"状态是针对每个进程的,使用它可能会让跟踪对象的真实父进程产生困惑。

在调用waitpid时使用WNOHANG标志可能会导致返回0(“尚无可用的等待结果”),即使跟踪器知道应该有通知。例如:

 errno = 0;ptrace(PTRACE_CONT, pid, 0L, 0L);if (errno == ESRCH) {/* tracee is dead */r = waitpid(tracee, &status, __WALL | WNOHANG);/* r can still be 0 here! */}

存在以下几种类型的ptrace-stop:signal-delivery-stops、 group-stops、PTRACE_EVENT stops,、syscall-stops。它们都会通过waitpid报告为WIFSTOPPED(status)为真的返回值。可以通过检查status>>8的值来区分它们,如果该值有歧义,可以查询PTRACE_GETSIGINFO。(注意:不能使用WSTOPSIG(status)宏对其进行检查,因为它返回的是值(status>>8) & 0xff。)

3.2 ptrace-stop

大多数ptrace命令(除了PTRACE_ATTACH、PTRACE_SEIZE、PTRACE_TRACEME、PTRACE_INTERRUPT和PTRACE_KILL)要求被跟踪的进程处于 ptrace-stop 状态,否则它们将以ESRCH错误失败。

以下列举一些常用会进入ptrace-stop 状态的情况:

(1)被跟踪的进程触发int3 发送SIGTRAP信号-- 软件断点
(2)调试器发起PTRACE_SINGLESTEP请求
(3)调试器发起PTRACE_SYSCALL请求

当被跟踪的进程处于 ptrace-stop 状态时,跟踪器可以使用信息性命令向被跟踪的进程读取和写入数据。这些命令会使被跟踪的进程保持在ptrace停止状态,具体包括:

ptrace(PTRACE_PEEKTEXT/PEEKDATA/PEEKUSER, pid, addr, 0);
ptrace(PTRACE_POKETEXT/POKEDATA/POKEUSER, pid, addr, long_val);
ptrace(PTRACE_GETREGS/GETFPREGS, pid, 0, &struct);
ptrace(PTRACE_SETREGS/SETFPREGS, pid, 0, &struct);
ptrace(PTRACE_GETREGSET, pid, NT_foo, &iov);
ptrace(PTRACE_SETREGSET, pid, NT_foo, &iov);
ptrace(PTRACE_GETSIGINFO, pid, 0, &siginfo);
ptrace(PTRACE_SETSIGINFO, pid, 0, &siginfo);
ptrace(PTRACE_GETEVENTMSG, pid, 0, &long_var);
ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_flags);

3.3 ptrace-stop内核源码

内核源码中有一个ptrace_stop函数来处理 tracee 处于ptrace-stop的情况:

// linux-3.10/kernel/signal.c/** This must be called with current->sighand->siglock held.** This should be the path for all ptrace stops.* We always set current->last_siginfo while stopped here.* That makes it a way to test a stopped process for* being ptrace-stopped vs being job-control-stopped.** If we actually decide not to stop at all because the tracer* is gone, we keep current->exit_code unless clear_code.*/
static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)__releases(&current->sighand->siglock)__acquires(&current->sighand->siglock)
{....../** We're committing to trapping.  TRACED should be visible before* TRAPPING is cleared; otherwise, the tracer might fail do_wait().* Also, transition to TRACED and updates to ->jobctl should be* atomic with respect to siglock and should be done after the arch* hook as siglock is released and regrabbed across it.*/set_current_state(TASK_TRACED);current->last_siginfo = info;current->exit_code = exit_code;/** If @why is CLD_STOPPED, we're trapping to participate in a group* stop.  Do the bookkeeping.  Note that if SIGCONT was delievered* across siglock relocks since INTERRUPT was scheduled, PENDING* could be clear now.  We act as if SIGCONT is received after* TASK_TRACED is entered - ignore it.*/if (why == CLD_STOPPED && (current->jobctl & JOBCTL_STOP_PENDING))gstop_done = task_participate_group_stop(current);/* any trap clears pending STOP trap, STOP trap clears NOTIFY */task_clear_jobctl_pending(current, JOBCTL_TRAP_STOP);if (info && info->si_code >> 8 == PTRACE_EVENT_STOP)task_clear_jobctl_pending(current, JOBCTL_TRAP_NOTIFY);/* entering a trap, clear TRAPPING */task_clear_jobctl_trapping(current);spin_unlock_irq(&current->sighand->siglock);read_lock(&tasklist_lock);if (may_ptrace_stop()) {/** Notify parents of the stop.** While ptraced, there are two parents - the ptracer and* the real_parent of the group_leader.  The ptracer should* know about every stop while the real parent is only* interested in the completion of group stop.  The states* for the two don't interact with each other.  Notify* separately unless they're gonna be duplicates.*/do_notify_parent_cldstop(current, true, why);if (gstop_done && ptrace_reparented(current))do_notify_parent_cldstop(current, false, why);/** Don't want to allow preemption here, because* sys_ptrace() needs this task to be inactive.** XXX: implement read_unlock_no_resched().*/preempt_disable();read_unlock(&tasklist_lock);preempt_enable_no_resched();freezable_schedule();} ......}

ptrace_stop这个函数通常用于通知让调式器运行,子进程处于被跟踪状态和停止状态。

通知父进程进入调试状态:

static void do_notify_parent_cldstop(struct task_struct *tsk,bool for_ptracer, int why)
{......__group_send_sig_info(SIGCHLD, &info, parent);/** Even if SIGCHLD is not generated, we must wake up wait4 calls.*/__wake_up_parent(tsk, parent);......
}

四、Signal-delivery-stop

当一个(可能是多线程的)进程接收到除SIGKILL之外的任何信号时,内核会选择一个任意的线程来处理该信号。(如果使用tgkill生成信号,则调用者可以显式选择目标线程。)如果所选线程正在被跟踪,则进入 signal-delivery-stop 状态。此时,信号尚未传递给进程,并且跟踪器可以抑制该信号。如果跟踪器不抑制信号,则在下一个ptrace重启请求中将信号传递给跟踪对象。这里将信号传递的这第二步称为 signal injection 。注意,如果信号被阻塞,直到信号解除阻塞才会发生信号传递停止,通常的例外是SIGSTOP无法被阻塞。

跟踪器通过waitpid观察到 signal-delivery-stop ,以WIFSTOPPED(status)为真的形式返回,并返回由WSTOPSIG(status)返回的信号。如果信号是SIGTRAP,则可能是不同类型的ptrace-stop;有关详细信息,请参见下面的"Syscall-stops"和"execve"部分。如果WSTOPSIG(status)返回一个停止信号,则可能是 group-stop ;请参见下面的说明。

五、Group-stop

当一个(可能是多线程的)进程接收到停止信号时,所有线程都会停止运行。如果一些线程被跟踪,它们会进入一个组停止状态。需要注意的是,停止信号首先会导致信号传递停止(只在一个被跟踪的进程上),只有在由跟踪器注入该信号(或者发送给一个未被跟踪的线程后),组停止才会在多线程进程中的所有被跟踪进程中启动。通常情况下,每个被跟踪进程都会将其组停止状态单独报告给相应的跟踪器。

跟踪器通过waitpid返回WIFSTOPPED(status)为真来观察组停止,通过WSTOPSIG(status)获取停止信号。对于某些其他类别的ptrace停止,也会返回相同的结果,因此推荐的做法是执行以下调用:

ptrace(PTRACE_GETSIGINFO, pid, 0, &siginfo)

如果信号不是SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU,可以避免调用该函数;这四个信号是停止信号。如果跟踪器看到其他信号,它就不能是组停止。否则,跟踪器需要调用PTRACE_GETSIGINFO。如果PTRACE_GETSIGINFO返回EINVAL,则明确是组停止。(可能存在其他失败代码,例如如果SIGKILL杀死了被跟踪进程,则返回ESRCH(“没有这样的进程”))。

如果使用PTRACE_SEIZE连接跟踪进程,则通过PTRACE_EVENT_STOP指示组停止:status>>16 == PTRACE_EVENT_STOP。这样可以在无需额外的PTRACE_GETSIGINFO调用的情况下检测组停止。

从Linux 2.6.38开始,当跟踪器看到被跟踪进程的ptrace停止,并且在重新启动或终止它之前,被跟踪进程不会运行,并且不会向跟踪器发送通知(除非通过SIGKILL终止)。即使跟踪器进入另一个waitpid(2)调用,也是如此。

上述段落中描述的内核行为导致了对停止信号的透明处理问题。如果跟踪器在组停止后重新启动被跟踪进程,停止信号实际上会被忽略,被跟踪进程不会继续停止,而是继续运行。如果跟踪器在进入下一个waitpid(2)之前不重新启动被跟踪进程,将不会向跟踪器报告未来的SIGCONT信号;这将导致SIGCONT信号对被跟踪进程没有影响。

自Linux 3.4以来,有一种方法可以解决这个问题:可以使用PTRACE_LISTEN命令来重新启动被跟踪进程,它不会执行,而是等待一个新的事件,可以通过waitpid(2)报告该事件(例如,当它被SIGCONT重新启动时)。

六、PTRACE_EVENT stops

当跟踪器设置了PTRACE_O_TRACE_*选项时,被跟踪的进程将进入被称为PTRACE_EVENT停止的状态。

通过waitpid系统调用,跟踪器可以观察到PTRACE_EVENT停止。waitpid的返回值将指示进程被停止,WIFSTOPPED(status)返回true,WSTOPSIG(status)返回SIGTRAP。在状态字的高字节中,还会设置一个额外的位,其中status>>8的值为:

(SIGTRAP | PTRACE_EVENT_foo << 8)

以下是可用的PTRACE_EVENT停止类型:
PTRACE_EVENT_VFORK:在从vfork或clone系统调用中返回之前,跟踪器会停止该进程。停止发生时,使用了CLONE_VFORK标志。当该进程在此停止后继续执行时,它将等待子进程退出或执行新程序,然后再恢复执行。这个行为模拟了通常的vfork(2)语义。

PTRACE_EVENT_FORK:在从fork或clone系统调用中返回之前,跟踪器会停止该进程。退出信号被设置为SIGCHLD。

PTRACE_EVENT_CLONE:在从clone系统调用中返回之前,跟踪器会停止该进程。

PTRACE_EVENT_VFORK_DONE:在从vfork或clone系统调用中返回之前,跟踪器会停止该进程,使用了CLONE_VFORK标志,并且在子进程通过退出或执行新程序之后。

七、Syscall-stops

如果被跟踪的进程通过PTRACE_SYSCALL重新启动,那么在进入任何系统调用之前,该进程将进入系统调用进入停止状态(syscall-enter-stop)。如果跟踪器使用PTRACE_SYSCALL重新启动被跟踪进程,当系统调用完成或被信号中断时,被跟踪的进程将进入系统调用退出停止状态(syscall-exit-stop)。也就是说,在系统调用进入停止状态(syscall-enter-stop)和系统调用退出停止状态(syscall-exit-stop)之间不会发生信号传递停止(signal-delivery-stop);信号传递停止会在系统调用退出停止状态之后发生。

其他可能的情况是,被跟踪的进程可能会在PTRACE_EVENT停止中停止、退出(如果进入了_exit(2)或exit_group(2))、被SIGKILL终止,或者默默死亡(如果它是线程组的领导者,execve(2)发生在另一个线程中,并且该线程不受相同跟踪器的跟踪;此情况将在后面讨论)。

跟踪器通过waitpid观察到系统调用进入停止状态(syscall-enter-stop)和系统调用退出停止状态(syscall-exit-stop),此时WIFSTOPPED(status)为true,WSTOPSIG(status)返回SIGTRAP。如果跟踪器设置了PTRACE_O_TRACESYSGOOD选项,那么WSTOPSIG(status)将返回(SIGTRAP | 0x80)。

可以通过查询PTRACE_GETSIGINFO来区分系统调用停止(syscall-stops)和带有SIGTRAP的信号传递停止(signal-delivery-stop)。具体情况如下:

si_code <= 0:SIGTRAP是由用户空间操作(例如tgkill(2)kill(2)sigqueue(3)等)触发的,或者由POSIX定时器到期、POSIX消息队列状态变化或异步I/O请求完成引起的。
si_code == SI_KERNEL (0x80):SIGTRAP是由内核发送的。
si_code == SIGTRAP或si_code == (SIGTRAP | 0x80):这是一个系统调用停止(syscall-stop)。

然而,系统调用停止(syscall-stops)非常频繁(每个系统调用两次),对于每个系统调用停止都执行PTRACE_GETSIGINFO可能会有一些性能开销。

一些体系结构允许通过检查寄存器来区分这些情况。例如,在x86上,syscall-enter-stop时rax == -ENOSYS。由于SIGTRAP(像任何其他信号一样)总是在syscall-exit-stop之后发生,并且此时rax几乎不会包含-ENOSYS,SIGTRAP看起来像是“不是syscall-enter-stop的syscall-stop”;换句话说,它看起来像是“错误的syscall-exit-stop”,可以通过这种方式检测到。但这种检测是脆弱的,最好避免使用。

使用PTRACE_O_TRACESYSGOOD选项是区分系统调用停止和其他类型ptrace停止的推荐方法,因为它可靠且不会带来性能损失。

对于跟踪器来说,系统调用进入停止状态(syscall-enter-stop)和系统调用退出停止状态(syscall-exit-stop)在外观上是无法区分的。跟踪器需要跟踪ptrace停止的顺序,以避免将系统调用进入停止状态错误地解释为系统调用退出停止状态或反之。规则是syscall-enter-stop后始终紧随syscall-exit-stop、PTRACE_EVENT停止或被跟踪进程的终止;在其间不会发生其他类型的ptrace停止。

如果在syscall-enter-stop之后,跟踪器使用除PTRACE_SYSCALL之外的重新启动命令,则不会生成syscall-exit-stop。

在syscall-stops上使用PTRACE_GETSIGINFO将返回si_signo为SIGTRAP,si_code设置为SIGTRAP或(SIGTRAP | 0x80)。

八、 execve under ptrace

请参考man手册

相关文章:

Linux ptrace系统调用

文章目录 一、ptrace 简介二、ptrace 参数request2.1 PTRACE_TRACEME2.2 PTRACE_PEEKTEXT, PTRACE_PEEKDATA2.3 PTRACE_PEEKUSER2.4 PTRACE_POKETEXT, PTRACE_POKEDATA2.5 PTRACE_POKEUSER2.6 PTRACE_GETREGS, PTRACE_GETFPREGS2.7 PTRACE_GETREGSET2.8 PTRACE_SETREGS, PTRACE…...

CSDN每日一练 |『贝博士发奖金』『Longest Continuous Increasing Subsequence』『最小差值』2023-09-01

CSDN每日一练 |『贝博士发奖金』『Longest Continuous Increasing Subsequence』『最小差值』2023-09-01 一、题目名称:贝博士发奖金二、题目名称:Longest Continuous Increasing Subsequence三、题目名称:最小差值一、题目名称:贝博士发奖金 时间限制:1000ms内存限制:25…...

二维数组创建方式比较

暑假跟着地质队去跑山了&#xff0c;到现在还没结束&#xff0c;今天休息的时候突然刷到了一篇关于C二维数组创建方面的文章&#xff0c;我觉得还是非常不错滴&#xff0c;就将其中提到的新方法和我已经使用过的三种方法进行了比较&#xff0c;发现该方法提高了二维数组的分配、…...

安达发|富士康科技集团利用自动排程APS软件打造智慧工厂

富士康科技集团作为全球领先的3C产品研发制造企业&#xff0c;近年来积极布局智能制造领域&#xff0c;通过引入先进的自动化排程系统(APS),成功打造了智慧工厂&#xff0c;提高了生产质量与效率&#xff0c;降低了生产成本。 富士康集团自2019年下半年提出在观澜厂区建立数字可…...

云计算在大数据分析中的应用与优势

文章目录 云计算在大数据分析中的应用云计算在大数据分析中的优势云计算在大数据分析中的示例未来发展和拓展结论 &#x1f389;欢迎来到AIGC人工智能专栏~云计算在大数据分析中的应用与优势 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT陈寒的博客&…...

linux————ELK(日志收集系统集群)

目录 一、为什么要使用ELK 二、ELK作用 二、组件 一、elasticsearch 特点 二、logstash 工作过程 INPUT&#xff08;输入&#xff09; FILETER(过滤) OUTPUTS&#xff08;输出&#xff09; 三、kibana 三、架构类型 ELK ELKK ELFK ELFKK EFK 四、构建ELk集群…...

Leetcode213 打劫家舍2

思路&#xff1a;既然头尾不能同时取&#xff0c;那就分别算只取头或者只取尾&#xff0c;不考虑特殊情况的话是一个简单的动态规划 class Solution:def rob(self, nums: list[int]) -> int:if len(nums) < 3:return max(nums)max_sum [nums[0], max(nums[1], nums[0])…...

Redis全局命令

"那篝火在银河尽头~" Redis-cli命令启动 现如今&#xff0c;我们已经启动了Redis服务&#xff0c;下⾯将介绍如何使⽤redis-cli连接、操作Redis服务。客户端与服务端交互的方式有两种: ● 第⼀种是交互式⽅式: 后续所有的操作都是通过交互式的⽅式实现&#xff0c;…...

Xml转json

利用fastjson转换,pom文件依赖: <dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version> </dependency> <dependency><groupId>com.alibaba</groupId><artifa…...

Spring框架知识点汇总

01.Spring框架的基本理解 关键字&#xff1a;核心思想IOC/AOP&#xff0c;作用&#xff08;解耦&#xff0c;简化&#xff09;&#xff0c;简单描述框架组成&#xff1b; Spring框架是一款轻量级的开发框架&#xff0c;核心思想是IOC&#xff08;反转控制&#xff09;和AOP&a…...

JavaScript Web APIs - 06 正则表达式

Web APIs - 06 文章目录 Web APIs - 06正则表达式正则基本使用元字符边界符量词范围字符类 替换和修饰符正则插件change 事件判断是否有类 目标&#xff1a;能够利用正则表达式完成小兔鲜注册页面的表单验证&#xff0c;具备常见的表单验证能力 正则表达式综合案例阶段案例 正…...

Python入门教程 | Python3 字符串

字符串是 Python 中最常用的数据类型。我们可以使用引号( ’ 或 " )来创建字符串。 创建字符串很简单&#xff0c;只要为变量分配一个值即可。例如&#xff1a; var1 Hello World! var2 "Tarzan"Python 访问字符串中的值 Python 不支持单字符类型&#xff…...

Playwright for Python:安装及初步使用

文章目录 一、Playwright介绍1.1 简单介绍1.2 支持的平台1.3 支持语言1.4 官方文档&#xff08;python&#xff09; 二、开始2.1 安装要求2.2 安装2.3 脚本录制2.4 代码示例 一、Playwright介绍 1.1 简单介绍 Playwright是微软推出来的一款自动化测试工具&#xff0c;是专门为…...

Ubuntu 20.04.5 怎么安装微信

这是我的ubutun版本号 在这个系统装桌面版微信很多功能不健全。搜索了很多方法&#xff0c;这个算是不错的一个法子。 1.添加仓库 首次使用时&#xff0c;你需要运行如下一条命令将移植仓库添加到系统中。 wget -O- https://deepin-wine.i-m.dev/setup.sh | sh 2.应用安装 …...

HummerRisk V1.4.0发布

大家好&#xff0c;HummerRisk 1.4.0和大家见面了&#xff0c;在这个版本中我们变更了多云检测的底层逻辑&#xff0c;增加了每次检测的project概念&#xff0c;更好的去支持检测历史和检索需要&#xff0c;增加阿里云最佳实践中资源监控检测规则&#xff0c;增加资源态势中的细…...

C语言每日一练----Day(12)

本专栏为c语言练习专栏&#xff0c;适合刚刚学完c语言的初学者。本专栏每天会不定时更新&#xff0c;通过每天练习&#xff0c;进一步对c语言的重难点知识进行更深入的学习。 今日练习题关键字&#xff1a;最大连续1的个数 完全数计算 &#x1f493;博主csdn个人主页&#xff1…...

【Tkinter系列11/15】小部件 (Text)

24. 小部件Text 文本小部件是一种更通用的方法 处理比小部件多行文本。文本小部件几乎是一个完整的文本 窗口中的编辑器&#xff1a;Label 您可以将文本与不同的字体、颜色和 背景。 您可以用文本穿插嵌入的图像。一 图像被视为单个字符。请参见第 24.3 节 “文本小部件图像”…...

通过「内网穿透」技术,实现出差期间远程访问企业局域网中的象过河ERP系统

文章目录 概述1.查看象过河服务端端口2.内网穿透3. 异地公网连接4. 固定公网地址4.1 保留一个固定TCP地址4.2 配置固定TCP地址 5. 使用固定地址连接 概述 ERP系统对于企业来说重要性不言而喻&#xff0c;不管是财务、生产、销售还是采购&#xff0c;都需要用到ERP系统来协助。…...

ChatGPT和大型语言模型(LLM)是什么关系?

参考&#xff1a;https://zhuanlan.zhihu.com/p/615203178 # ChatGPT和大型语言模型(LLM)是什么关系&#xff1f; 参考&#xff1a;https://zhuanlan.zhihu.com/p/622518771 # 什么是LLM大语言模型&#xff1f;Large Language Model&#xff0c;从量变到质变 https://zhuanla…...

list(介绍与实现)

目录 1. list的介绍及使用 1.1 list的介绍 1.2 list的使用 1.2.1 list的构造 1.2.2 list iterator的使用 1.2.3 list capacity 1.2.4 list element access 1.2.5 list modififiers 1.2.6 list的迭代器失效 2. list的模拟实现 2.1 模拟实现list 2.2 list的反向迭代器 1.…...

Centos7 使用docker安装oracle数据库(超详细)

在linux中采用解压安装包的方式安装oracle非常麻烦&#xff0c;并且稍微不注意就会出现问题&#xff0c;因此采用docker来安装&#xff0c;下面为详细的步骤&#xff1a; 若不知道是否安装docker可查看这篇文章&#xff1a;docker安装 1、拉取oracle镜像 docker pull registr…...

昨天面试的时候被提问到的问题集合(答案)

1、vue的双向绑定原理是什么&#xff1f;里面的关键点在哪里&#xff1f; Vue的双向绑定原理是基于Object.defineProperty或者Proxy来实现的&#xff0c;其关键点在于数据劫持&#xff0c;即对数据的读取和修改进行拦截&#xff0c;在数据发生变化时自动更新视图 2、实现水平垂…...

PYTHON用户流失数据挖掘:建立逻辑回归、XGBOOST、随机森林、决策树、支持向量机、朴素贝叶斯和KMEANS聚类用户画像...

原文链接&#xff1a;http://tecdat.cn/?p24346 在今天产品高度同质化的品牌营销阶段&#xff0c;企业与企业之间的竞争集中地体现在对客户的争夺上&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 “用户就是上帝”促使众多的企业不惜代价去争夺尽可能多的客…...

详解IP协议

在介绍IP协议之前&#xff0c;先抛出一个概念&#xff1a;IP地址的作用——定位主机&#xff0c;具有将数据从主机A跨网络传输到主机B的能力&#xff0c;有了TCP提供的策略&#xff0c;例如滑动窗口、拥塞控制等&#xff0c;IP去执行它&#xff0c;所以我们通常叫TCP/IP协议&am…...

Stream流式编程用例

Stream流式编程用例&#xff1a; filter, map, flatmap, limit, skip, sort, distinct, collect, reduce, summary statistics public class StreamTest {public static void main(String[] args) {//filterStream<Integer> stream Stream.of(1, 2, 3, 4, 5);Stream&l…...

【Pytorch笔记】1. tensor的创建

参考视频&#xff1a; 深度之眼官方账号&#xff1a;01-02-张量简介与创建 torch.tensor() b torch.tensor(data, dtypeNone, deviceNone, requires_gradFalse, pin_memoryFalse)data&#xff1a;创建的tensor的数据来源&#xff0c;可以是list或numpy dtype&#xff1a;数据…...

Maven 基础之安装和命令行使用

Maven 的安装和命令行使用 1. 下载安装 下载解压 maven 压缩包&#xff08;http://maven.apache.org/&#xff09; 配置环境变量 前提&#xff1a;需要安装 java 。 在命令行执行如下命令&#xff1a; mvn --version如出现类似如下结果&#xff0c;则证明 maven 安装正确…...

运动耳机需要具备哪些功能、挂耳式运动蓝牙耳机推荐

作为运动爱好者&#xff0c;长时间的运动很容易枯燥&#xff0c;所以我会选择佩戴耳机来缓解运动的枯燥感&#xff0c;一款好的运动耳机可以让运动变得更加激情&#xff0c;还可以更好的享受运动的乐趣。 但现在的运动耳机产品实在是五花八门&#xff0c;到底什么样的运动蓝牙耳…...

【MCU】SD NAND芯片之国产新选择

文章目录 前言传统SD卡和可贴片SD卡传统SD卡可贴片SD卡 实际使用总结 前言 随着目前时代的快速发展&#xff0c;即使是使用MCU的项目上也经常有大数据存储的需求。可以看到经常有小伙伴这样提问&#xff1a; 大家好&#xff0c;请问有没有SD卡芯片&#xff0c;可以直接焊接到P…...

java 多线程

01.多线程类java.lang.Thread 这里继承Thread类的方法是比较常用的一种&#xff0c;如果说你只是想起一条线程。没有什么其它特殊的要求&#xff0c;那么可以使用Thread.&#xff08;笔者推荐使用Runable&#xff0c;后头会说明为什么&#xff09;。下面来看一个简单的实例&…...