我正在尝试检查调用
dlopen
和 dlsym
的程序的行为:最终目标是插入一个小函数,记录对通过 dlsym
获得的函数的调用。
通常,对于您想要用
foo
包装的某些函数 DYLD_INSERT_LIBRARIES
,您可以编写如下内容:
void (*real_foo)(void);
void init() __attribute__((constructor)) {
real_foo = dlsym(RTLD_NEXT, "foo");
}
void foo(void) {
puts("foo was called");
real_foo();
}
问题是,当你试图包装
dlsym
时,你最终会得到这样的结果:
void* (*real_dlsym)(void* handle, const char* symbol);
void init() __attribute__((constructor)) {
real_dlsym = dlsym(RTLD_NEXT, "dlsym");
}
void* dlsym(void* handle, const char* symbol) {
return real_dlsym(handle, symbol);
}
init
最终会调用您自己的 dlsym
实现,这没有帮助。
当我导出另一个 dlsym 时,如何获得“真正的”dlsym?或者有没有更好的方法来拦截对 dlsym 的调用?
至少在 10.12 系统上,
dlsym
实际上是在 /usr/lib/system/libdyld.dylib
中实现的。它应该适用于 dlopen()
该文件,然后使用返回的句柄调用 dlsym()
以获得真正的 dlsym()
实现。您可能需要额外的 mode
标记到 dlopen()
,例如 RTLD_FIRST
和/或 RTLD_LOCAL
。有点像:
void *libdyld_handle = dlopen("/usr/lib/system/libdyld.dylib", RTLD_LAZY);
void *dlsym_funcptr = dlsym(libdyld_handle, "dlsym");
迟来的答案说明了 Dyld4 实现的一个有趣的怪癖(即 MacOS 12.x Monterey 和 iOS15)。本质上,全局 dyld 对象在
main
启动时通过硬件寄存器泄漏其详细信息(无论平台如何)。
显然这是一种黑客行为,永远不要考虑在严肃的生产代码中这样做。
int main(int argc, char * argv[]) {
#ifdef __x86_64__
register void* rbx asm("rbx"); //dyld4::gDyld
register void* (*typed_dlopen)( void*, char const*, int) asm("rax");
register void* (*typed_dlsym)(void*, char const*, void*) asm("rcx");
__asm volatile(".intel_syntax noprefix;"
#if TARGET_OS_SIMULATOR
"add rbx,0x100000;"
#endif
"mov rax,[rbx];"
"mov rcx,[rax + 0x88];"
"mov rax,[rax + 0x70];"
: "=r"(typed_dlopen), "=r"(rbx), "=r"(typed_dlsym)); //dyld4::gDyld
void* handle = typed_dlopen(rbx, "libc.dylib", RTLD_NOW);
int (*myPrintf)(const char * __restrict, ...) = typed_dlsym(rbx, handle, "printf");
myPrintf("Hello world");
#endif
#if TARGET_CPU_ARM64
register void* x19 asm("x19"); //dyld4::gDyld
register void* (*typed_dlopen)( void*, char const*, int) asm("x0");
register void* (*typed_dlsym)(void*, char const*, void*) asm("x1");
__asm volatile("ldr x0,[x19] \t\n"
"ldr x1,[x0, 0x88]\t\n"
"ldr x0,[x0, 0x70]\t\n"
: "=r"(typed_dlopen), "=r"(x19), "=r"(typed_dlsym)); //dyld4::gDyld
void* handle = typed_dlopen(x19, "libc.dylib", RTLD_NOW);
int (*myPrintf)(const char * __restrict, ...) = typed_dlsym(x19, handle, "printf");
myPrintf("Hello world");
#endif
//Rest of your startup code e.g. UIApplicationMain(...) / NSApplicationMain(...)
}
这些偏移量
0x88
、0x70
来自原始 libdyld.dylib
,并在与 dyld4 内部接口时用于真正的 dlopen
和 dlsym
。简而言之,我们直接利用内部实现(因此额外的第一个参数是 c++ this
指针)。时间会证明它们是否会在 Apple 操作系统的未来版本中保持稳定。
这也可以改用
void __attribute__ ((constructor)) premain(void) {
代替(分别使用 r15
和 x8
或 x20
)。
void __attribute__ ((constructor)) premain(void) {
#ifdef __x86_64__
register void* r15 asm("r15"); //dyld4::gDyld
register void* (*typed_dlopen)( void*, char const*, int) asm("rax");
register void* (*typed_dlsym)(void*, char const*, void*) asm("rcx");
__asm volatile(".intel_syntax noprefix;"
"mov rax,[r15];"
"mov rcx,[rax + 0x88];"
"mov rax,[rax + 0x70];"
: "=r"(typed_dlopen), "=r"(r15), "=r"(typed_dlsym)); //dyld4::gDyld
void* handle = typed_dlopen(r15, "libc.dylib", RTLD_NOW);
int (*myPrintf)(const char * __restrict, ...) = typed_dlsym(r15, handle, "printf");
myPrintf("Hello world");
#endif
#if TARGET_CPU_ARM64
register void* x8 asm("x8"); //dyld4::gDyld
register void* (*typed_dlopen)( void*, char const*, int) asm("x0");
register void* (*typed_dlsym)(void*, char const*, void*) asm("x1");
__asm volatile("ldr x0,[x8] \t\n"
"ldr x1,[x0, 0x88]\t\n"
"ldr x0,[x0, 0x70]\t\n"
: "=r"(typed_dlopen), "=r"(x8), "=r"(typed_dlsym)); //dyld4::gDyld
void* handle = typed_dlopen(x8, "libc.dylib", RTLD_NOW);
int (*myPrintf)(const char * __restrict, ...) = typed_dlsym(x8, handle, "printf");
myPrintf("Hello world");
#endif
}
模拟器更新 有趣的是,在主要变体中,我发现有 2 种不同的
dlopen
实现:
0x000000010e78272a (dyld`dyld4::APIs::dlopen(char const*, int))
0x000000010424b278 (dyld_sim`dyld4::APIs::dlopen(char const*, int))
前者在使用模拟器时会崩溃。但是,由于另一个粗略的巧合,premain 似乎使用了另一个(?)
//dyld4::gDyld
移动了偏移量 0x1000000
。如果我们将 rbx
移到 main
中,它就会起作用。
经黑客验证可用于:
iOS14.1使用Dyld3会崩溃。 未在 M1 及其同级产品上进行测试,但原则上它们应该像 iOS 手臂一样工作。
从 macOS 15+/iOS 18+ 开始,您可以使用
_dyld_register_dlsym_notifier
SPI 来实现此目的: https://github.com/apple-oss-distributions/dyld/blob/65bbeed63cec73f313b1d636e63f243964725a9d/include/mach-o/dyld_priv.h#L962