PTRACE_TRACEME的范围是多少?

时间:2017-06-14 02:21:41

标签: c++ linux ptrace

如果我有这样的代码:

void child() {
    do_something();
    ptrace(PTRACE_TRACEME, 0, 0, 0);
    do_some_other_things();
}

然后由父母跟踪do_something()吗?

我在linux文档中发现,没有这样的东西。它只是说这应该在tracee中调用。

  

PTRACE_TRACEME

     

表明此进程将由其父进程跟踪。一个   如果父进程可能不应该发出此请求   不期待追查它。 (pid,addr和数据是   忽略)。

1 个答案:

答案 0 :(得分:1)

有关更好的解释,请参阅How Debuggers Work Part 1,但总的来说,不会,在tracee收到信号之前,它不会跟踪do_something()函数。

在您引用的 man ptrace ptrace 调用说明中:

  

进程可以通过调用fork(2)并拥有来启动跟踪   结果孩子做了   PTRACE_TRACEME,跟随(通常)由执行者(2)。或者,可以开始一个过程   使用PTRACE_ATTACH或PTRACE_SEIZE跟踪另一个进程。

     

在跟踪时,即使信号发出,每次传送信号时,tracee也会停止          被忽略了。 (SIGKILL是一个例外,它具有通常的效果。)跟踪器将是          在下次调用waitpid(2)(或其中一个相关"等待"系统调用)时通知;那个电话          将返回一个状态值,其中包含指示停止原因的信息          tracee。

当tracee调用exec时,它会收到一个信号,这就是它停止的原因。

对于illustrace,示踪程序mainer.c没有铃声或口哨声:

//mainer.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <unistd.h>

int main(int argc, char ** argv)
{
        pid_t child_pid;
        char * programname = argv[1];

        child_pid = fork();
        if (child_pid == 0)
        {
                ptrace(PTRACE_TRACEME, 0, 0, 0);
                execl(programname, programname, NULL);
        }

        else if (child_pid > 0) 
        {
            int status;

            wait(&status);

            while (WIFSTOPPED(status))
            {
                    struct user_regs_struct regs;
                    ptrace(PTRACE_GETREGS, child_pid, 0, &regs);
                    unsigned instr = (PTRACE_PEEKTEXT, child_pid, regs.eip, 0);
                    printf("EIP = 0x%08x, instr = 0x%08x\n", regs.eip, instr);

                    ptrace(PTRACE_SINGLESTEP, child_pid, 0, 0);

                    wait(&status);
            }

        }
}  

当tracee命中exec时,它将收到一个信号并将控制传递给正在等待的父母。 要跟踪的汇编程序,在跟踪C程序以提取任何有用的东西时,还有太多的进展:

; hello.asm
section .text
    global _start

_start:
    mov edx,len1
    mov ecx,hello1
    mov ebx,1
    mov eax,4

    int 0x80


    mov edx,len2
    mov ecx,hello2
    mov ebx,1
    mov eax,4

    int 0x80

    mov eax,1
    int 0x80

section .data
    hello1 db "Hello",0xA
    len1 equ $ - hello1

    hello2 db "World",0xA
    len2 equ $ - hello2

运行此./mainer hello

EIP = 0x08048080, instr = 0x00000000
EIP = 0x08048085, instr = 0x00000000
EIP = 0x0804808a, instr = 0x00000000
EIP = 0x0804808f, instr = 0x00000000
EIP = 0x08048094, instr = 0x00000000
Hello
EIP = 0x08048096, instr = 0x00000000
EIP = 0x0804809b, instr = 0x00000000
EIP = 0x080480a0, instr = 0x00000000
EIP = 0x080480a5, instr = 0x00000000
EIP = 0x080480aa, instr = 0x00000000
World
EIP = 0x080480ac, instr = 0x00000000
EIP = 0x080480b1, instr = 0x00000000

如果我们修改mainer.c,那么子进程在它执行之前调用do_something(),跟踪结果完全相同。这就是我修改它的方法,你可以确认自己是否喜欢结果是一样的。

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <unistd.h>

int do_something(void)        //added this function
{
        printf("Doing something");
        return 0;
}


int main(int argc, char ** argv)
{
        pid_t child_pid;
        char * programname = argv[1];

        child_pid = fork();
        if (child_pid == 0)
        {
                ptrace(PTRACE_TRACEME, 0, 0, 0);
                do_something();      //added this function call
                execl(programname, programname, NULL);
        }

        else if (child_pid > 0) 
        {
            int status;

            wait(&status);

            while (WIFSTOPPED(status))
            {
                    struct user_regs_struct regs;
                    ptrace(PTRACE_GETREGS, child_pid, 0, &regs);
                    unsigned instr = (PTRACE_PEEKTEXT, child_pid, regs.eip, 0);
                    printf("EIP = 0x%08x, instr = 0x%08x\n", regs.eip, instr);

                    ptrace(PTRACE_SINGLESTEP, child_pid, 0, 0);

                    wait(&status);
            }

        }
}   

所以tracee不会停止,直到它收到一个信号,这是调用exec时发生的事情,并且调用函数不会为tracee生成信号,但还有其他方法可以发送信号。向tracee发出信号并开始跟踪,尽管它们并不像exec那样整洁并等待。