无法正确找到如何使用ptrace()

时间:2018-05-08 16:19:03

标签: c ptrace

目前,对于一个项目,我需要使用ptrace()编写某种调试器代码。最后,它应该显示在程序中输入/退出的每个函数/系统调用以进行跟踪。

现在,我很困惑。我做了一个小程序,应该尝试跟踪一个给定的程序,如果它找到一个调用或基于操作码的系统调用打印(用寄存器检索)。这是:

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

int main()
{
        pid_t child;
        const int long_size = sizeof(long);

        child = fork();

        if(child == 0) {
                ptrace(PTRACE_TRACEME, 0, NULL, NULL);
                execl("./bin", "bin", NULL);
        } else {
                int status;
                unsigned ins;
                struct user_regs_struct regs;
                unsigned char prim, sec;

                while (1) {
                        wait(&status);
                        if (WIFEXITED(status))
                                break;
                        ptrace(PTRACE_GETREGS, child, NULL, &regs);
                        ins = ptrace(PTRACE_PEEKTEXT, child, regs.rip, NULL);
                        prim = (unsigned)0xFF & ins;
                        sec = ((unsigned)0xFF00 & ins) >> 8;
                        if (prim == 0xE8 && sec == 0xCD)
                                printf("call found!\n");
                        if (prim == 0x80 && sec == 0xCD)
                                printf("syscall found!\n");
                        ptrace(PTRACE_SINGLESTEP, child, NULL, NULL);
                }
        }
        return 0;
}

这里是&#34; bin&#34;的代码。二进制:

#include <unistd.h>

void toto()
{
        write(1, "hello\n", 6);
}

int main()
{
        toto();
        toto();
        return (1);
}

当我查看迷你调试器的输出时,似乎只找到一个系统调用和一个调用...我试着弄乱寄存器和偏移量,但我在互联网上找到的每个教程似乎都是对于32位机器,在我的情况下不能工作:/

有人可以给我一些小提示来帮助我继续吗?

谢谢,祝你有愉快的一天!

1 个答案:

答案 0 :(得分:2)

你几乎就在那里,但你的掩护(首先被怀疑)并没有抓住callq操作码。使用callq时,还会发现整批额外的PTRACE_SINGLESTEP代码,我不确定您是否意识到这一点。

我静态编制了您的bin计划,因此您可以获得maintoto的一致地址

在64位计算机上

gcc bin.c -o bin -g -Wall -static

然后在主脚本中,我更改了ins变量上的屏蔽操作:

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

int main()
{
        pid_t child;

        child = fork();

        if(child == 0) {
                ptrace(PTRACE_TRACEME, 0, NULL, NULL);
                execl("./bin", "bin", NULL);
        } else {
                int status;
                unsigned ins;
                struct user_regs_struct regs;
                unsigned char prim;

                while (1) {
                        ptrace(PTRACE_SINGLESTEP, child, NULL, NULL);
                        wait(&status);
                        if (WIFEXITED(status))
                                break;
                        ptrace(PTRACE_GETREGS, child, NULL, &regs);
                        ins = ptrace(PTRACE_PEEKTEXT, child, regs.rip, NULL);
                        prim = (unsigned)0xFF & ins;
                        // Here in prim just mask for the first byte
                        if (prim == 0xe8) {
                        // Print the addresses to check out too
                                printf("RIP: %#x --> %#x\n", regs.rip, ins);
                                printf("call found!\n");
                        }
                }
        }
        return 0;
}

您只需要屏蔽以检查第一个字节,看它是否与call操作码匹配。我添加了一些带有指令指针地址的额外打印语句,因此您可以检查静态源代码,以确保您正在捕获正确的调用。

您可以重定向主程序的输出(我称之为stepper):

./stepper > calls.txt

如果您执行objdump -S bin > dump.txt,则可以看到来自totomain的{​​{1}}指令的地址也将位于callq文件< / p>

您最终会收到来自crt函数,链接器和库调用的所有额外调用。