Trancing PCs
打开Clang文档介绍链接
- Tracing PCs: 跟踪的是CPU执行到的代码
-fsanitize-coverage=trace-pc-guard
首先在设置里面配置上面那一行代码 然后根据示例导入
#include <stdint.h>
#include <stdio.h>
#include <sanitizer/coverage_interface.h>
以及实现下面的这两个方法:
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop)
void __sanitizer_cov_trace_pc_guard(uint32_t *guard)
__sanitizer_cov_trace_pc_guard_init
看注释:编译器将此回调作为模块构造函数插入到每个DSO中。'start'和'stop'对应的是整个二进制文件(可执行文件或DSO)的开始和结束部分,也就是说这个函数反映了符号调用的次数,我们可以来验证一下
start
:开始部分
stop
:这里是个开区间所以需要减去4,所以这里整个符号的调用次数是25个
我们再加一个C函数和一个Block看下,这里的数量是不是等于27.
所以只要我们的项目中新增一个方法这里都会加1,都能监控到
__sanitizer_cov_trace_pc_guard
看注释:只要你添加了Clang插桩的标记,那么编译器就会在所有的方法、函数、block的代码实现的边缘添加这行代码。检测到当前调用的函数,我们看下函数调用栈就知道,这里可以hook住一起的回调函数(方法,函数和block)
Debug
->Debug Workflow
-> Always Show Disassembly
可以在汇编中看到在每一个符号开始调用的时候,都插入了这个函数,我们可以从这个函数中拿到调用者的地址
在这个函数里,我们根据地址可以拿到调用符号的一些信息
拿到符号的名字之后,就可以重新排列了。但是如果hook的方法是在子线程,那么这里也会是在子线程回调。所以在这个方法里存取符号的时候,就要考虑到线程安全的问题。
while循环问题
我们在这个方法里面把所有的符号先收集起来
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
void *PC = __builtin_return_address(0);
//创建结构体
SYNode * node = malloc(sizeof(SYNode));
*node = (SYNode){PC,NULL};
//结构体入栈
OSAtomicEnqueue(&symbolList, node, offsetof(SYNode, next));
}
然后在while循环的时候取出来
Build运行,发现死循环,调用的都是touchBegin
这个方法,next
为什么一直是它?
在这里断点,打开汇编,我们发现这个方法里面,自动加入了__sanitizer_cov_trace_pc_guard
方法,而且还不止一次,意思就是while循环也被该方法hook住了。那么怎么破解呢?
我们可以在设置里面新增一个范围限定func
-fsanitize-coverage=func,trace-pc-guard
运行,再点击屏幕发现就解决了死循环的问题。
符号取反和去重
上面拿到的符号有重复的,同时也需要按照正确的调用顺序来取
- 上面的打印我们知道main函数没有正确的下标,所以我们这里
给函数名称添加 _
BOOL isObjc = [name hasPrefix:@"+["] || [name hasPrefix:@"-["];
NSString * symbolName = isObjc ? name : [@"_" stringByAppendingString:name];
[symbleNames addObject:symbolName];
- 反向遍历数组
- 去掉当前的自己
[funcs removeObject:[NSString stringWithFormat:@"%s",__func__]]
4.生成order文件并写入
NSString * funcStr = [funcs componentsJoinedByString:@"\n"];
NSString * filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"hank.order"];
NSData * file = [funcStr dataUsingEncoding:NSUTF8StringEncoding];
[[NSFileManager defaultManager] createFileAtPath:filePath contents:file attributes:nil];
NSLog(@"%@",funcStr);
- 找到order文件的路径
配置生成的Order文件
- 把该文件放到项目的根目录之下
- 删除设置
-fsanitize-coverage=func,trace-pc-guard
,同时引入自制的order文件
Swift符号覆盖
在设置里面覆盖Swift代码
-sanitize-coverage=func
-sanitize=undefined
配置好之后重新打印下符号 这里的swift自带的混淆,相对于OC来说更安全,性能更高。每次编译的时候并不会变化。