iOS 底层探索(二十六) 启动优化☞二进制重排
在iOS 底层探索(二十五)启动优化方案中我们得知,二进制重排是有效优化启动时间的操作,但是这个方案需要对所有启动所需的方法进行二进制重排,即自己编辑order_file
文件。那么就有一下几个点
- 完全自己手写,并且对自己的项目特别熟,还要随时与其他开发人员交流。
- 找到一种自动生成文件的方案。
SanitizerCoverage
查看SanitizerCoverage网站,查看Tracing PCs with guards
内容,根据其内容修改TestDemo
工程。
复制官网中的-fsanitize-coverage=trace-pc-guard
内容到工程中。
然后Command + B
编译一下,编译之后发现会报错如下错误
这是因为有两个方法没有实现,我们在ViewController.m
文件中实现这两个方法,代码如下:
#include <stdint.h>
#include <stdio.h>
#include <sanitizer/coverage_interface.h>
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,
uint32_t *stop) {
static uint64_t N; // Counter for the guards.
if (start == stop || *start) return; // Initialize only once.
printf("INIT: %p %p\n", start, stop);
for (uint32_t *x = start; x < stop; x++)
*x = ++N;
}
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
// 当前方法为子线程回调。
if (!*guard) return;
// 当前函数返回上一个调用的地址
void *PC = __builtin_return_address(0);
char PcDescr[1024];
printf("guard: %p %x PC %s\n", guard, *guard, PcDescr);
}
再次编译就没有错误了。
运行,打印__sanitizer_cov_trace_pc_guard_init
方法中的start
与stop
的值,如下
这个值为方法的符号个数。
__sanitizer_cov_trace_pc_guard
方法为,当调用某方法时执行。
查看汇编
这个方法的意义为Hook
汇编的跳转,在模拟器运行为callq
,在真机上为b
或bl
,即方法的调用。
那么现在已经知道这个方法可以HOOK
住所有方法的调用,那么剩下的就是拿到方法名,并生成文件即可。
引入#import <dlfcn.h>
框架,为动态库的显示调用。使用Dl_info
获取方法名
typedef struct dl_info {
const char *dli_fname; /* Pathname of shared object 镜像文件的位置 */
void *dli_fbase; /* Base address of shared object 镜像文件的地址 */
const char *dli_sname; /* Name of nearest symbol 方法的名称 */
void *dli_saddr; /* Address of nearest symbol 方法的起始地址 */
} Dl_info;
引入#import <libkern/OSAtomic.h>
创建原子队列。
取出内容,代码如下:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
while (YES) {
SYNode *node = OSAtomicDequeue(&symbolList, offsetof(SYNode, next));
if (node == NULL) {
break;
}
Dl_info info = {0};
dladdr(node->PC, &info);
}
}
当取出时,会进入死循环,查看汇编可知。
搜索__sanitizer_cov_trace_pc_guard
方法的调用,发现这里有三个__sanitizer_cov_trace_pc_guard
方法的调用,这是因为while
循环导致的,因为while
循环也使用了callq
、b
、bl
等汇编跳转方法。这样就导致了循环一次就会被HOOK
一次。这也是因为我们使用的是pc
的HOOK
的原因。
修改方法为将-fsanitize-coverage=trace-pc-guard
修改为-fsanitize-coverage=func,trace-pc-guard
,该问题就解决了。
整体代码如下
#include <stdint.h>
#include <stdio.h>
#include <sanitizer/coverage_interface.h>
#import <dlfcn.h>
#import <libkern/OSAtomic.h>
// 定义原子队列
static OSQueueHead symbolList = OS_ATOMIC_QUEUE_INIT;
// 定义符号结构体
typedef struct {
void *PC;
void *next;
}SYNode;
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,
uint32_t *stop) {
static uint64_t N; // Counter for the guards.
if (start == stop || *start) return; // Initialize only once.
printf("INIT: %p %p\n", start, stop);
for (uint32_t *x = start; x < stop; x++)
*x = ++N;
}
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
void *PC = __builtin_return_address(0);
// 创建结构体
SYNode *node = malloc(sizeof(SYNode));
*node = (SYNode){PC, NULL};
// 加入结构体
// offsetof 第一个参数为结构体,第二个参数为偏移到下一个节点。
OSAtomicEnqueue(&symbolList, node, offsetof(SYNode, next));
}
// 这个函数为执行的最后一个函数。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 定义数组
NSMutableArray<NSString *> *symbolNames = [NSMutableArray array];
while (YES) {
SYNode *node = OSAtomicDequeue(&symbolList, offsetof(SYNode, next));
if (node == NULL) {
break;
}
Dl_info info = {0};
dladdr(node->PC, &info);
printf("%s\n", info.dli_sname);
NSString *name = @(info.dli_sname);
if (![name hasPrefix:@"+["] && ![name hasPrefix:@"-["]) {
// 非oc方法添加 _
name = [@"_" stringByAppendingString:name];
}
if (name && ![symbolNames containsObject:name]) {
// 去重
[symbolNames addObject:name];
}
}
// 数组倒叙
symbolNames = (NSMutableArray<NSString *> *)[[symbolNames reverseObjectEnumerator] allObjects];
// 数组转字符串
NSString *funcString = [symbolNames componentsJoinedByString:@"\n"];
// 字符串写入文件
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"testDemo.order"];
NSData *fileContents = [funcString dataUsingEncoding:NSUTF8StringEncoding];
[[NSFileManager defaultManager] createFileAtPath:filePath contents:fileContents attributes:nil];
NSLog(@"funcString = %@", funcString);
}
内容如下
混编Swift
代码时,在Other Swift
中添加-sanitize=undefined
与-sanitize-coverage=func
,如下
创建swift
文件,并在load
中调用swift方法
以上就是全部代码,去沙盒中找到该文件,利用上一篇文章的内容进行植入。