官方文档http://clang.llvm.org/docs/SanitizerCoverage.html
当我们hook oc函数的时候其原理是objc_msgSend 只能hook一些oc的函数 对于系统的库函数 C函数无法hook 或者利用fishhook来hook一些c函数
现在clang 12 提供了一种代码覆盖检测工具 它在函数,基本块和边缘级别上插入对用户定义函数的调用。提供了这些回调的默认实现 . 简单说就是在每个方法的里面插入bl跳转到
image.png可以hook oc swift block c函数基本上都能拿到
[使用防护装置追踪]
接下来我们使用这个来做个demo hook每个方法
在主项目Target--Build Settings中添加编译选项
Other C Flags增加-fsanitize-coverage=func,trace-pc-guard
增加配置运行demo 运行报错
我们再来看看官方文档
// trace-pc-guard-cb.cc
#include <stdint.h>
#include <stdio.h>
#include <sanitizer/coverage_interface.h>
// This callback is inserted by the compiler as a module constructor
// into every DSO. 'start' and 'stop' correspond to the
// beginning and end of the section with the guards for the entire
// binary (executable or DSO). The callback will be called at least
// once per DSO and may be called multiple times with the same parameters.
extern "C" 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; // Guards should start from 1.
}
// This callback is inserted by the compiler on every edge in the
// control flow (some optimizations apply).
// Typically, the compiler will emit the code like this:
// if(*guard)
// __sanitizer_cov_trace_pc_guard(guard);
// But for large functions it will emit a simple call:
// __sanitizer_cov_trace_pc_guard(guard);
extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
if (!*guard) return; // Duplicate the guard check.
// If you set *guard to 0 this code will not be called again for this edge.
// Now you can get the PC and do whatever you want:
// store it somewhere or symbolize it and print right away.
// The values of `*guard` are as you set them in
// __sanitizer_cov_trace_pc_guard_init and so you can make them consecutive
// and use them to dereference an array or a bit vector.
void *PC = __builtin_return_address(0);
char PcDescr[1024];
// This function is a part of the sanitizer run-time.
// To use it, link with AddressSanitizer or other sanitizer.
__sanitizer_symbolize_pc(PC, "%p %F %L", PcDescr, sizeof(PcDescr));
printf("guard: %p %x PC %s\n", guard, *guard, PcDescr);
}
对应我们这个两个报错的方法 现在我们来集成这个方法
1.运行项目 打印__sanitizer_cov_trace_pc_guard_init这个方法
printf("INIT: %u\n", *x);
看到最后一次打印输出15
2.我们手动添加一个方法
暂时不需要调用 3.运行一下 再看下打印输出 可以看到最后打印输入多添加一次
由此可见这是项目中函数调用的方法个数
接下来分析下__sanitizer_cov_trace_pc_guard 这个函数的调用情况 官方文档会将这个函数注入到我们运行的每个函数里面 相当于一个函数的回掉
示例:
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self test];
}
- (void)test {
NSLog(@"123");
}
1.我们构造一个函数 点击的时候 再方法里面 利用__builtin_return_address(0)来获得当前函数返回地址,也就是调用方的地址。
2.通过dladdr来将指针解析成Dl_info结构体信息,其中dli_sname就是符号的名称
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;
通过这样我们能拿到点击操作的方法函数名称
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
if (!*guard) return; // Duplicate the guard check.
// If you set *guard to 0 this code will not be called again for this edge.
// Now you can get the PC and do whatever you want:
// store it somewhere or symbolize it and print right away.
// The values of `*guard` are as you set them in
// __sanitizer_cov_trace_pc_guard_init and so you can make them consecutive
// and use them to dereference an array or a bit vector.
// void *PC = __builtin_return_address(0);
char PcDescr[1024];
void *PC = __builtin_return_address(0);
PCNode *node = malloc(sizeof(PCNode));
*node = (PCNode){PC, NULL};
OSAtomicEnqueue(&queue, node, offsetof(PCNode, next));
Dl_info info;
dladdr(node->pc, &info);
NSString *name = @(info.dli_sname);
NSLog(@"name==%@",name);
}
这样我们就能动态拿到当前操作的方法
我们还可以在相应的方法断点拿到image list 拿到函数调用栈
网友评论