通过 dyld-interposing 实现C/C++代码注入
苹果系统的链接器/usr/lib/dyld
提供了一个叫dyld-interposing
的功能(从 Mac OS X 10.4 开始),可以在程序启动时替换掉某个函数的实现。这个功能可以用来实现代码注入(详见:《Mac OS X Internals: A Systems Approach》- Amit Singh - 第二章 2.6.3.4 dyld interposing)
举个栗子
比如,我们可以在程序运行时,替换掉malloc
函数的实现:
1 | // malloc_trace.c |
1 | // test.c |
1 | $ cc -dynamiclib -o libmalloctrace.dylib malloc_trace.c -install_name libmalloctrace.dylib |
ps. 不能在替换函数(replacement)中使用会调用被替换函数(replacee)的其它函数。比如printf()
内部可能会使用malloc()
则trace_malloc
中不能调用printf
,不然死给你看~
头文件<mach-o/dyld-interposing.h>
来自 dyld 源码:https://github.com/apple-oss-distributions/dyld/blob/dyld-1042.1/include/mach-o/dyld-interposing.h
其中宏 DYLD_INTERPOSE
的定义:
1 |
|
malloc_trace.c
展开后的代码如下:
1 | static struct { |
这段代码声明了一个_interpose_malloc
的结构体,replacement
成员指向trace_malloc
函数,replacee
成员指向malloc
函数。
并通过__attribute__
编译配置将这个结构体放到了 Mach-O 产物的 __DATA,__interpose
段中。
用 otool
查看:
1 | $ otool -l libmalloctrace.dylib | grep -A 5 __interpose |
__DATA,__interpose
段的大小为0x10
,也就是16字节,正好是代码中_interpose_malloc
结构体的大小。
也可以使用 MachOView 查看:
在程序加载dylib
时,dyld
会解析它的__DATA,__interpose
段,找到所有的struct { uintptr_t replacement; uintptr_t replacee; };
结构体,然后将replacee
成员指向的函数替换成replacement
成员指向的函数。
详见源码:RuntimeState::buildInterposingTables()
使用__DATA,__interpose
段实现函数替换是静态替换,在程序启动时(dylib
被加载时)就替换了。
曾经还有一种方式是动态替换,使用dyld
的私有函数dyld_dynamic_interpose
,在程序运行时替换函数,详见<mach-o/dyld_priv.h>
。不过经过测试,已经失效了~~
使用环境变量 DYLD_PRINT_INTERPOSING=1
,可以打印出被替换函数的替换信息:
1 | $ DYLD_PRINT_INTERPOSING=1 DYLD_INSERT_LIBRARIES=libmalloctrace.dylib ./test |
有什么用?
你可能疑惑通过dyld-interposing
实现代码注入有什么用?
- 方便调试(比如实现
malloc
的内存泄漏检测) - api trace(比如实现
opengl
api trace) - hook dylib api (比如实现 hot-reload)
- …
Prevent dyld-interposing?
阻止dyld
加载 DYLD_INSERT_LIBRARIES
:添加链接参数-Wl,-add_empty_section,__RESTRICT,__restrict
(详见:ProcessConfig::Security::getAMFI 、Header::isRestricted() )
Refs
- Mac OS X Internals: A Systems Approach
- dyld源码
- Mach-O linking and loading tricks
- Code Injection with Dyld Interposing
- https://github.com/DerekSelander/symbol-interposing
iOS 上有没有类似的功能?
fishhook 用于替换 Mach-O 二进制文件中符号的库,可以在 iOS 上实现类似的功能。
Author: Yrom
Link: https://yrom.net/blog/2023/10/19/dyld-interposing/
License: 知识共享署名-非商业性使用 4.0 国际许可协议