我有一个文件范围内核扩展,可以在应用程序启动时通知守护进程。守护进程需要在 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
因此,有没有办法确保守护程序可以在加载库完成后附加到主函数的开头,或者重复单步执行到该点,在这种情况下,我如何才能找到考虑到可能没有可用于已启动应用程序的符号,主要地址?
谢谢。
最佳答案
只是一个猜测,但由于 libc 必须在 main
之后调用 exit
,您可能可以通过步行识别对 main
的调用_start
直到找到对 .text
部分的调用,然后是对 .plt
部分的调用。您还知道 main
的退出状态是 exit
的参数,因此您正在寻找 mov %eax, %edi
和可能 main
在 _start
之后,而 .plt
在 .text
之前,你知道两者都不会走得太远, 所以你可以让它变得非常通用。
鉴于运行时/libc/编译器是健全的(Mac OS X 基于 FreeBSD,对吗?),您可能只需匹配几个字节就可以逃脱,像这样(您可能至少应该增加一些安全性):
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;
}
你只需要添加.text
的虚拟地址,你基本上就完成了:)。
关于c - 使用 ptrace 查找 main 函数的开始,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16524988/