使用ptrace查找主功能的开始
我有一个文件范围内核扩展,它在应用程序启动时通知守护进程。守护进程需要在main()中的第一条指令开始时暂停启动的应用程序 使用PT_ATTACH调用ptrace时,守护进程似乎连接得太早,并且位于动态链接器(dyld)中 以下是线程0的调用堆栈在连接时的示例:-使用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
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;
}