微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用ptrace抓取所有printfs

我想将自己附加到一个进程并拦截该进程的所有printf调用.

main.c中

int main()
{
    int i;
    for(i = 0; i < 10; i++)
    {
        printf("HelloWorld\n");
        sleep(5);
    }
    return 0;
}

然后要附加我有这个代码,我想做一个无限循环或直到main.c完成 – 无限循环将工作这只是带有ptrace的Hello World进行测试,没什么特别的.

#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,&regs);
   long ins = ptrace(PTRACE_PEEKTEXT,regs.eip,NULL);

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

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

   ptrace(PTRACE_DETACH,NULL);

   return 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的第一个参数拉出来.您需要使用ptrace中的GETREGS(或等效)功能获取寄存器,然后使用PEEKDATA(或等效的)来读取字符串的数据.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐