记录Clang插桩HOOK

作者: 第x个等于4乘x的阶乘 | 来源:发表于2020-10-27 17:50 被阅读0次

    官方文档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 拿到函数调用栈

    相关文章

      网友评论

        本文标题:记录Clang插桩HOOK

        本文链接:https://www.haomeiwen.com/subject/pgsoyktx.html