记录Clang插桩HOOK

官方文档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 
#include 
#include 

// 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 *)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 拿到函数调用栈

你可能感兴趣的:(记录Clang插桩HOOK)