通过 dyld-interposing 实现C/C++代码注入
source link: https://yrom.net/blog/2023/10/19/dyld-interposing/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
通过 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
函数的实现:
// malloc_trace.c
#include <stdio.h>
#include <stdlib.h>
#include <mach-o/dyld-interposing.h>
#include <memory.h> // memset
#include <malloc/malloc.h> // malloc_printf
void *trace_malloc(size_t size) {
char *p = malloc(size);
// fills with '#'
memset(p, '#', size);
malloc_printf("malloc(%u) = %p\n", size, p);
return (void *)p;
}
DYLD_INTERPOSE(trace_malloc, malloc);
// test.c
#include <stdio.h>
#include <stdlib.h>
int main() {
char *p = (char*)malloc(10);
printf("malloc return %p, %s\n", p, p);
free(p);
return 0;
}
$ cc -dynamiclib -o libmalloctrace.dylib malloc_trace.c -install_name libmalloctrace.dylib
$ cc -o test test.c
$ DYLD_INSERT_LIBRARIES=libmalloctrace.dylib ./test
test(46555,0x11bdd3600) malloc: malloc(1536) = 0x7febbc808200
test(46555,0x11bdd3600) malloc: malloc(32) = 0x7febbc704130
test(46555,0x11bdd3600) malloc: malloc(32) = 0x7febbc704170
test(46555,0x11bdd3600) malloc: malloc(20) = 0x7febbc705550
test(46555,0x11bdd3600) malloc: malloc(422) = 0x7febbc7055d0
test(46555,0x11bdd3600) malloc: malloc(50) = 0x7febbc7057e0
test(46555,0x11bdd3600) malloc: malloc(16) = 0x7febbc705880
test(46555,0x11bdd3600) malloc: malloc(52) = 0x7febbc705900
test(46555,0x11bdd3600) malloc: malloc(12) = 0x7febbc7059b0
test(46555,0x11bdd3600) malloc: malloc(10) = 0x7febbc705b00
test(46555,0x11bdd3600) malloc: malloc(4096) = 0x7febbc808800
malloc return 0x7febbc705b00, ##########
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
的定义:
#define DYLD_INTERPOSE(_replacement,_replacee) \
__attribute__((used)) static struct{ const void* replacement; const void* replacee; } _interpose_##_replacee \
__attribute__ ((section ("__DATA,__interpose,interposing"))) = { (const void*)(unsigned long)&_replacement, (const void*)(unsigned long)&_replacee };
malloc_trace.c
展开后的代码如下:
static struct {
const void *replacement;
const void *replacee;
} _interpose_malloc __attribute__ ((section ("__DATA,__interpose,interposing"))) = {
(const void *)&trace_malloc,
(const void *)&malloc
};
这段代码声明了一个_interpose_malloc
的结构体,replacement
成员指向trace_malloc
函数,replacee
成员指向malloc
函数。
并通过__attribute__
编译配置将这个结构体放到了 Mach-O 产物的 __DATA,__interpose
段中。
用 otool
查看:
$ otool -l libmalloctrace.dylib | grep -A 5 __interpose
sectname __interpose
segname __DATA
addr 0x0000000000008000
size 0x0000000000000010
offset 32768
align 2^3 (8)
__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
,可以打印出被替换函数的替换信息:
$ DYLD_PRINT_INTERPOSING=1 DYLD_INSERT_LIBRARIES=libmalloctrace.dylib ./test
dyld[92700]: libmalloctrace.dylib has interposed '_malloc' to replacing binds to 0x7FF805E19530 with 0x10151FF40
dyld[92700]: interpose replaced 0x7FF805E19530 with 0x7FF805E19530 in /path/to/libmalloctrace.dylib
dyld[92700]: interpose replaced 0x7FF805E19530 with 0x10151FF40 in /path/to/test
dyld[92700]: interpose: *0x7ff845cdd028 = 0x10151FF40 (dyld cache patch) to _malloc
dyld[92700]: interpose: *0x7ff845cddb38 = 0x10151FF40 (dyld cache patch) to _malloc
dyld[92700]: interpose: *0x7ff845ce35e0 = 0x10151FF40 (dyld cache patch) to _malloc
dyld[92700]: interpose: *0x7ff845ce47c0 = 0x10151FF40 (dyld cache patch) to _malloc
dyld[92700]: interpose: *0x7ff845ce6d78 = 0x10151FF40 (dyld cache patch) to _malloc
...
有什么用?
你可能疑惑通过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() )
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK