使用ptrace获取所有printfs

使用ptrace获取所有printfs,c,printf,ptrace,C,Printf,Ptrace,我想将自己连接到一个进程,并截获来自该进程的所有printf调用 main.c int main() { int i; for(i = 0; i < 10; i++) { printf("HelloWorld\n"); sleep(5); } return 0; } intmain() { int i; 对于(i=0;i

我想将自己连接到一个进程,并截获来自该进程的所有
printf
调用

main.c

int main()
{
    int i;
    for(i = 0; i < 10; i++)
    {
        printf("HelloWorld\n");
        sleep(5);
    }
    return 0;
}
intmain()
{
int i;
对于(i=0;i<10;i++)
{
printf(“HelloWorld\n”);
睡眠(5);
}
返回0;
}

然后附加我有这段代码,我想做一个无限循环,或者直到main.c完成——无限循环可以工作这只是Hello World和ptrace的测试,没什么特别的

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

int main(int argc, char *argv[])
{  
   struct user_regs_struct regs;

   pid_t traced_process = atoi(argv[1]);

   long t = ptrace(PTRACE_ATTACH, traced_process, NULL, NULL);

   wait(NULL);

   ptrace(PTRACE_GETREGS, traced_process, NULL, &regs);
   long ins = ptrace(PTRACE_PEEKTEXT, traced_process, regs.eip, NULL);

   printf("EIP: %lx Instruction executed: %lx\n", regs.eip, ins);

   char *c = &ins;
   printf("%c\n",c);

   ptrace(PTRACE_DETACH, traced_process, NULL, NULL);

   return 0;
}
#包括
#包括
#包括
#包括
#包含//用于用户\u注册表\u结构
int main(int argc,char*argv[])
{  
结构用户\u注册表\u结构注册表;
pid_t_进程=atoi(argv[1]);
长t=ptrace(ptrace\u ATTACH,tracked\u process,NULL,NULL);
等待(空);
ptrace(ptrace_GETREGS、tracked_process、NULL和regs);
long-ins=ptrace(ptrace_-peek-text,跟踪_-process,regs.eip,NULL);
printf(“执行的EIP:%lx指令:%lx\n”,regs.EIP,ins);
char*c=&ins;
printf(“%c\n”,c);
ptrace(ptrace_分离,跟踪_进程,NULL,NULL);
返回0;
}
我试图在附加后放置
while(1)
,但实际上这只是在main.c中执行的第一个printf上循环

我真的很难做到这一点,我遇到的每一个例子实际上都是另一个例子的复制粘贴,其中包含大量与我试图做的事情无关的代码。我确实知道printf在内核中是一个write(),所以这就是我应该寻找的


所以我想再次获得printf试图打印到另一个终端屏幕上的字符串的引用。我该怎么做

您需要复制调试器的一半才能执行此操作

简言之:

  • 在正在运行的进程中解析printf的地址

  • 在printf上设置断点

  • 找出您所在体系结构的ABI,并从堆栈或相关寄存器中读取printf的第一个参数

  • 让我们更详细地完成这些步骤

    步骤1-在正在运行的进程中查找printf的地址

    对于第一步,您的跟踪程序需要知道正在跟踪的二进制文件,以打开它并解析该二进制文件的符号。您可能需要阅读ELF规范,或者阅读一些执行类似操作的代码

    作为第一次尝试,我强烈建议您静态链接跟踪程序。因为接下来需要做的是找出动态链接器专门为调试器提供的钩子。这通常是操作系统未记录的。您可能需要在正在使用的操作系统上读取调试器的代码,并执行相同的操作来挂接动态链接器,以确定哪些库加载在何处,并使用该信息从这些库中提取符号并定位printf

    第一步可以通过让跟踪程序打印printf的地址来绕过。这可能更有意义,因为所有这些步骤一起进行会让所有人同时工作有点痛苦。我建议将第1步留到最后,因为这是最难纠正的一步,而执行第2步和第3步将教会您一些工具,您将需要这些工具来实现从跟踪过程中读取符号

    第2步-设置断点

    现在您有了printf的地址,让我们进入第二步。断点。如果幸运的话,您的操作系统提供了一个ptrace操作,可以在正在运行的进程中插入断点。只要用它,你就是黄金。阅读ptrace文档,了解如何向您发送断点信号(您通常只需等待)

    如果您的ptrace实现没有断点功能,请为您的体系结构找出断点指令,使用ptrace提供的任何机制覆盖跟踪过程中printf的开头。然后,当您收到断点时,写回用断点指令重写的代码的原始内容,用断点指令覆盖下一条指令(对于可变长度的指令体系结构,如x86,您可能需要有一个好的指令解析器),根据需要调整指令指针寄存器(某些体系结构不会重新启动断点指令,因此您必须自己重新启动),重新启动程序直到它到达下一条指令,恢复您这次重写的指令的先前内容,(在可变长度指令体系结构上,可能需要重复执行,直到可以将断点指令放入printf函数的第一条指令中)然后把断点重新放回第一条指令中。大多数现代系统在ptrace中都有断点功能,所以祈祷你不需要这样做,这真是一个麻烦

    步骤3-读取字符串


    这很简单。只需找到ABI文档,或阅读其他人编写的代码,或将一个简单函数编译到汇编程序中,然后查看汇编程序如何访问函数的第一个参数。使用该信息将第一个参数提取到printf。您需要使用GETREGS(或等效工具)ptrace中获取寄存器和数据的功能(或等效功能)读取字符串的数据。

    为什么不使用dtrace?尝试为需要它的实际项目学习ptrace。@Vijay必须有一种方法来完成这个HelloWorld ptrace。到目前为止,我发现的最好的例子不是附加到该进程,而是执行
    main
    ,然后使其成为子进程。似乎您想要实现一个函数调试器提供的特性…您可能需要找到tracee的printf地址,然后插入陷阱(作为断点)。请注意,源代码中的
    printf
    调用可能不会映射到可执行文件中的
    printf
    调用。例如,代码中的
    printf(“HelloWorld\n”)