Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/macos/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用ptrace查找主功能的开始_C_Macos_Debugging_Ptrace - Fatal编程技术网

使用ptrace查找主功能的开始

使用ptrace查找主功能的开始,c,macos,debugging,ptrace,C,Macos,Debugging,Ptrace,我有一个文件范围内核扩展,它在应用程序启动时通知守护进程。守护进程需要在main()中的第一条指令开始时暂停启动的应用程序 使用PT_ATTACH调用ptrace时,守护进程似乎连接得太早,并且位于动态链接器(dyld)中 以下是线程0的调用堆栈在连接时的示例:- Thread 0: 0 dyld 0x00007fff6e4cd35e mach_reply_port + 10 1 dyld

我有一个文件范围内核扩展,它在应用程序启动时通知守护进程。守护进程需要在main()中的第一条指令开始时暂停启动的应用程序

使用PT_ATTACH调用ptrace时,守护进程似乎连接得太早,并且位于动态链接器(dyld)中

以下是线程0的调用堆栈在连接时的示例:-

Thread 0:
0   dyld                            0x00007fff6e4cd35e mach_reply_port + 10
1   dyld                            0x00007fff6e4cd4d4 _mig_init + 13
2   dyld                            0x00007fff6e4cd17f mach_init + 46
3   dyld                            0x00007fff6e4aa239 dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*) + 411
4   dyld                            0x00007fff6e4aa05e _dyld_start + 54
因此,有没有办法确保后台程序可以在加载库完成后连接到main函数的开头,或者重复执行单个步骤到该点,在这种情况下,考虑到启动的应用程序可能没有可用的符号,我如何才能找到main的地址


谢谢。

Main不是实际的入口点,而是映射到可执行程序集的_开始符号的名称。根据您的可执行文件类型(elf-32或其他),它将从不同的地址开始(这可能是随机的)。您可以使用GDB查找入口点的地址(或执行的第一条指令),方法是单步执行一次程序(使用GDB,然后是si),然后打印PC寄存器的值(使用显示寄存器或其他方法)


您还可以切换到gdb中的程序集布局,查看整个堆栈跟踪,并查看其开始位置。可执行代码通常从0x0800xxx开始,这只是一个猜测,但由于libc必须在
main
之后调用
exit
,因此您可能可以通过遍历
\u start
来识别对
main
的调用,直到找到对
.text
部分的调用,然后是对
.plt
部分的调用。您还知道
main
的退出状态是
exit
的参数,因此您正在寻找
mov%eax,%edi
,并且可能
main
\u start
之后。plt
之前。text
之前,并且您知道两者都不会走得太远,因此您可以使其非常通用

鉴于runtime/libc/compiler是健全的(Mac OS X是基于FreeBSD的,对吧?),您可能只需要匹配几个字节就可以了,比如这样(您可能至少应该增加一些安全性):


您只需将
.text
的虚拟地址添加到该地址,基本上就完成了:)。

谢谢,但我认为您没有领会我的意思。我正在编写的守护进程需要停止任何启动的应用程序。我不能使用GDB,main是一个可能的入口点,但我只需要确保所有库的加载已经完成,并且没有其他指令被执行。我完全知道入口点的地址会有所不同,除了ASLR之外,每个应用程序都会有不同的内存占用。如果您是ptrace,只需在每次系统调用时让ptrace停止即可。或者让您的跟踪程序调用一个ptrace_信号来停止执行,并让您的守护进程知道它在那里。但这将在主入口点之后,优化可能会导致main()和信号之间发生其他事情。如何让跟踪程序调用ptrace_信号来停止执行?跟踪程序是计算机上用户可能运行的任何应用程序,而不是我编写的应用程序?对不起,我误解你了吗?如果不是你写的程序,你可以在程序映像中插入代码。除此之外,您的最佳选择是单步完成应用程序,然后确定main在哪里。这可以通过使用GDB来实现,GDB将向您显示在执行程序映像时PC应位于的地址(如我前面所说)。然后,使用ptrace,您可以在PC中插入数据,直到达到您使用gdb找到的PC所持有的指令值。你为什么要知道这个?好吧,现在我有一个鸡和蛋的情况;实际上,我希望将代码注入所有已启动的应用程序中,但需要确保注入在调用main(或等效)中的第一条指令之前完成。如果我在动态加载期间注入,目标应用程序将崩溃。如果我注入得太晚,我用注入代码覆盖的函数可能已经被调用了。我注意到一些libc实现使用了包装器(例如glibc),因此在这种情况下它会稍微复杂一些,但它应该以类似的方式被识别。
size_t *find_main_vmoffset(char *text_start, size_t text_size)
{
    char *p = text_start;

    const char sig[] = {
        /* 0xe8, ??, ??, */ 0, 0,       /* callq  main */
        0x89, 0xc7,             /* mov    %eax, %edi */ 
        0xe8, /* ??, ??, 0xff, 0xff, */     /* callq  exit */
    };

    while (p = memmem(p, text_size - (p - text_start), sig, sizeof(sig))) {
        /* Check it's one call followed by another */
        if (p[-3] != 0xe8)
            continue;
        /* Check the second call is backwards (into .plt) */
        if (p[7] != 0xff || p[8] != 0xff)
            continue;

        /* Eureka! */
        return (p - text_start) + 2 + *(int32_t *)(p - 2);
    }
    return 0;
}