我是从 main 函数开始分析的

tinyinst-converage.cpp 中的 面函数

int main(int argc, char **argv)
{
  instrumentation = new LiteCov();
  instrumentation->Init(argc, argv);

  int target_opt_ind = 0;
  for (int i = 1; i < argc; i++) {
    if (strcmp(argv[i], "--") == 0) {
      target_opt_ind = i + 1;
      break;
    }
  }

  int target_argc = (target_opt_ind) ? argc - target_opt_ind : 0;
  char **target_argv = (target_opt_ind) ? argv + target_opt_ind : NULL;

  unsigned int pid = GetIntOption("-pid", argc, argv, 0);
  persist = GetBinaryOption("-persist", argc, argv, false);
  num_iterations = GetIntOption("-iterations", argc, argv, 1);
  char *outfile = GetOption("-coverage_file", argc, argv);

  if (!target_argc && !pid) {
    printf("Usage:\\n");
    printf("%s <options> -- <target command line>\\n", argv[0]);
    printf("Or:\\n");
    printf("%s <options> -pid <pid to attach to>\\n", argv[0]);
    return 0;
  }

  Coverage coverage, newcoverage;

  for (int i = 0; i < num_iterations; i++) {
    RunTarget(target_argc, target_argv, pid, 0xFFFFFFFF);

    Coverage newcoverage;

    instrumentation->GetCoverage(newcoverage, true);

    for (auto iter = newcoverage.begin(); iter != newcoverage.end(); iter++) {
      printf("Found %zd new offsets in %s\\n", iter->offsets.size(), iter->module_name.c_str());
    }

    instrumentation->IgnoreCoverage(newcoverage);

    MergeCoverage(coverage, newcoverage);
  }
 
  if (outfile) WriteCoverage(coverage, outfile);

  instrumentation->Kill();

  return 0;
}
  1. 首先创建一个 ListCov 对象

  2. 然后进行初始化 instrumentation->Init(argc, argv)

    2.1 这里先初始化 父类 TinyInst::Init

    void LiteCov::Init(int argc, char **argv) {
      TinyInst::Init(argc, argv);
    
      coverage_type = COVTYPE_BB;
      char *option = GetOption("-covtype", argc, argv);
      if (option) {
        if (strcmp(option, "bb") == 0)
          coverage_type = COVTYPE_BB;
        else if (strcmp(option, "edge") == 0)
          coverage_type = COVTYPE_EDGE;
        else
          FATAL("Unknown coverage type");
      }
    
      compare_coverage = GetBinaryOption("-cmp_coverage", argc, argv, false);
    
      for (auto iter = instrumented_modules.begin();
           iter != instrumented_modules.end(); iter++) {
        ModuleInfo *module = *iter;
        module->client_data = new ModuleCovData();
      }
    }
    

    2.1.1 其中父类 TinyInst 的初始化 主要就是我们的根据我们的命令行参数进行一个对应的 参数初始化

    // initializes instrumentation from command line options
    void TinyInst::Init(int argc, char **argv) {
      // init the debugger first
      Debugger::Init(argc, argv);
    
    #ifdef ARM64
    #else
      assembler_ = new X86Assembler(*this);
    #endif
      assembler_->Init();
    
      instrumentation_disabled = false;
    
      instrument_modules_on_load = GetBinaryOption("-instrument_modules_on_load", argc, argv, false);
      patch_return_addresses = GetBinaryOption("-patch_return_addresses", argc, argv, false);
      instrument_cross_module_calls = GetBinaryOption("-instrument_cross_module_calls", argc, argv, true);
      persist_instrumentation_data = GetBinaryOption("-persist_instrumentation_data", argc, argv, true);
    
      trace_basic_blocks = GetBinaryOption("-trace_basic_blocks", argc, argv, false);
      trace_module_entries = GetBinaryOption("-trace_module_entries", argc, argv, false);
    
      sp_offset = GetIntOption("-stack_offset", argc, argv, 0);
    
      list <char *> module_names;
      GetOptionAll("-instrument_module", argc, argv, &module_names);
      for (auto iter = module_names.begin(); iter != module_names.end(); iter++) {
        ModuleInfo *new_module = new ModuleInfo();
        new_module->module_name = *iter;
        instrumented_modules.push_back(new_module);
      }
    
      char *option;
    
      indirect_instrumentation_mode = II_AUTO;
      option = GetOption("-indirect_instrumentation", argc, argv);
      if (option) {
        if (strcmp(option, "none") == 0)
          indirect_instrumentation_mode = II_NONE;
        else if (strcmp(option, "local") == 0)
          indirect_instrumentation_mode = II_LOCAL;
        else if (strcmp(option, "global") == 0)
          indirect_instrumentation_mode = II_GLOBAL;
        else if (strcmp(option, "auto") == 0)
          indirect_instrumentation_mode = II_AUTO;
        else
          FATAL("Unknown indirect instrumentation mode");
      }
    
      generate_unwind = GetBinaryOption("-generate_unwind", argc, argv, false);
      if (!generate_unwind) {
        unwind_generator = new UnwindGenerator(*this);
      } else {
      #ifdef __APPLE__
        unwind_generator = new UnwindGeneratorMacOS(*this);
      #else
        WARN("Unwind generator not implemented for the current platform");
        unwind_generator = new UnwindGenerator(*this);
      #endif
      }
    }
    

    其中初始化 了 Debugger类 根据逻辑 这个类是最先被初始化的

    然后其中还初始化了 每个模块,且把每个 模块加入到了 instrumented_modules 中。

    2.1接着回到 LiteCov 的初始化。

    这里初始化 覆盖的 类型 coverage_type 默认是基本快覆盖率。

    coverage_type = COVTYPE_BB;
      char *option = GetOption("-covtype", argc, argv);
      if (option) {
        if (strcmp(option, "bb") == 0)
          coverage_type = COVTYPE_BB;
        else if (strcmp(option, "edge") == 0)
          coverage_type = COVTYPE_EDGE;
        else
          FATAL("Unknown coverage type");
      }
    

    然后每个 模块的 client_data 初始化为 ModuleCovData类 记录覆盖率相关的内容

  3. 然后获取对应的 参数 格式的定义和 其他参数的初始化

    
    int target_opt_ind = 0;
      for (int i = 1; i < argc; i++) {54
        if (strcmp(argv[i], "--") == 0) {
          target_opt_ind = i + 1;
          break;
        }
      }
    
      int target_argc = (target_opt_ind) ? argc - target_opt_ind : 0;
      char **target_argv = (target_opt_ind) ? argv + target_opt_ind : NULL;
    
      unsigned int pid = GetIntOption("-pid", argc, argv, 0);
      persist = GetBinaryOption("-persist", argc, argv, false);
      num_iterations = GetIntOption("-iterations", argc, argv, 1);
      char *outfile = GetOption("-coverage_file", argc, argv);
    
      if (!target_argc && !pid) {
        printf("Usage:\\n");
        printf("%s <options> -- <target command line>\\n", argv[0]);
        printf("Or:\\n");
        printf("%s <options> -pid <pid to attach to>\\n", argv[0]);
        return 0;
      }
    
  4. 循环运算

    for (int i = 0; i < num_iterations; i++) {
        RunTarget(target_argc, target_argv, pid, 0xFFFFFFFF);
    
        Coverage newcoverage;
    
        instrumentation->GetCoverage(newcoverage, true);
    
        for (auto iter = newcoverage.begin(); iter != newcoverage.end(); iter++) {
          printf("Found %zd new offsets in %s\\n", iter->offsets.size(), iter->module_name.c_str());
        }
    
        instrumentation->IgnoreCoverage(newcoverage);
    
        MergeCoverage(coverage, newcoverage);
      }
    

    4.1 RunTarget(target_argc, target_argv, pid, 0xFFFFFFFF);

    函数定义在 tinyinst-coverage.cpp

    在目标进程上运行single迭代,不管它是whole进程还是目标方法,也不管目标是否持久(应该知道在几乎所有情况下该怎么做)

    void RunTarget(int argc, char **argv, unsigned int pid, uint32_t timeout) {
      DebuggerStatus status;
    
      if (instrumentation->IsTargetFunctionDefined()) {
        if (cur_iteration == num_iterations) {
          instrumentation->Kill();
          cur_iteration = 0;
        }
      }
    
      // else clear only when the target function is reached
      if (!instrumentation->IsTargetFunctionDefined()) {
        instrumentation->ClearCoverage();
      }
    
      if (instrumentation->IsTargetAlive() && persist) {
        status = instrumentation->Continue(timeout);
      } else {
        instrumentation->Kill();
        cur_iteration = 0;
        if (argc) {
          status = instrumentation->Run(argc, argv, timeout);
        } else {
          status = instrumentation->Attach(pid, timeout);
        }
      }
    
      // if target function is defined,
      // we should wait until it is hit
      if (instrumentation->IsTargetFunctionDefined()) {
        if ((status != DEBUGGER_TARGET_START) && argc) {
          // try again with a clean process
          WARN("Target function not reached, retrying with a clean process\\n");
          instrumentation->Kill();
          cur_iteration = 0;
          status = instrumentation->Run(argc, argv, timeout);
        }
    
        if (status != DEBUGGER_TARGET_START) {
          switch (status) {
          case DEBUGGER_CRASHED:
            FATAL("Process crashed before reaching the target method\\n");
            break;
          case DEBUGGER_HANGED:
            FATAL("Process hanged before reaching the target method\\n");
            break;
          case DEBUGGER_PROCESS_EXIT:
            FATAL("Process exited before reaching the target method\\n");
            break;
          default:
            FATAL("An unknown problem occured before reaching the target method\\n");
            break;
          }
        }
    
        instrumentation->ClearCoverage();
    
        status = instrumentation->Continue(timeout);
      }
    
      switch (status) {
      case DEBUGGER_CRASHED:
        printf("Process crashed\\n");
        instrumentation->Kill();
        break;
      case DEBUGGER_HANGED:
        printf("Process hanged\\n");
        instrumentation->Kill();
        break;
      case DEBUGGER_PROCESS_EXIT:
        if (instrumentation->IsTargetFunctionDefined()) {
          printf("Process exit during target function\\n");
        } else {
          printf("Process finished normally\\n");
        }
        break;
      case DEBUGGER_TARGET_END:
        if (instrumentation->IsTargetFunctionDefined()) {
          printf("Target function returned normally\\n");
          cur_iteration++;
        } else {
          FATAL("Unexpected status received from the debugger\\n");
        }
        break;
      default:
        FATAL("Unexpected status received from the debugger\\n");
        break;
      }
    }
    

    4.1.1其中 IsTargetFunctionDefined 返回 target_function_defined 这个变量,这个变量的初始化在 debugger.cpp Debugger::Init 中 默认为 false,然后根据我们的

    target_module[0] || target_offset || target_method[0] 是否定义了 目标函数

    从而赋值接 true 检查我们是否在持久化模式下运行

    // check if we are running in persistence mode
      if (target_module[0] || target_offset || target_method[0]) {
        target_function_defined = true;
        if ((target_module[0] == 0) || ((target_offset == 0) && (target_method[0] == 0))) {
          FATAL("target_module and either target_offset or target_method must be specified together\\n");
        }
      }
    

    4.1.2 然后是一个 根据我们的 一个 num_iterations 迭代数 判断是否会被 kill 掉。

    4.1.3 如果没有到达目标 会 ClearCoverage

    4.1.4 然后是一个 if 判断 首先先 Run 启动进程 如果下次循环,程序在存活就 Continue

    if (instrumentation->IsTargetAlive() && persist) {
        status = instrumentation->Continue(timeout);
      } else {
        instrumentation->Kill();
        cur_iteration = 0;
        if (argc) {
          status = instrumentation->Run(argc, argv, timeout);
        } else {
          status = instrumentation->Attach(pid, timeout);
        }
      }
    

    Run 函数continues 函数

    // starts the process
     and waits for the next event
    DebuggerStatus Debugger::Run(char *cmd, uint32_t timeout) {
      attach_mode = false;
    
      StartProcess(cmd);
    
      return Continue(timeout);
    }
    
    DebuggerStatus Debugger::Run(int argc, char **argv, uint32_t timeout) {
        char* cmd = NULL;
        cmd = ArgvToCmd(argc, argv);
    
        DebuggerStatus ret_dbg_status = Run(cmd, timeout);
        free(cmd);
    
        return ret_dbg_status;
    }
    
    // continues after Run() or previous Continue()
    // return with a non-terminal status
    DebuggerStatus Debugger::Continue(uint32_t timeout) {
      if (!child_handle && (dbg_last_status != DEBUGGER_ATTACHED))
        return DEBUGGER_PROCESS_EXIT;
    
      if (loop_mode && (dbg_last_status == DEBUGGER_TARGET_END)) {
        // saves us a breakpoint
        dbg_last_status = DEBUGGER_TARGET_START;
        return dbg_last_status;
      }
    
      dbg_last_status = DebugLoop(timeout);
    
      if (dbg_last_status == DEBUGGER_PROCESS_EXIT) {
        CloseHandle(child_handle);
        CloseHandle(child_thread_handle);
        child_handle = NULL;
        child_thread_handle = NULL;
      }
    
      return dbg_last_status;
    }
    

    Run 函数里面 的 StartProcess(cmd); 这个函数初始化 dbg_continue_needed 参数为 false 这个参数在 (后面 Debugger::Continue() 中是一个 重要的标志位)然后删除断点 ,分配内存,创建进程 。

    其中 Debugger::Continue()DebugLoop() 很重要。

    传入的参数为 timeout 的时间。

    这里会根据不同的时间 进行不同的 处理。

    // standard debugger loop that listens to events in the target process
    DebuggerStatus Debugger::DebugLoop(uint32_t timeout, bool killing)
    {
      DebuggerStatus ret;
      bool alive = true;
    
      if (dbg_continue_needed) {
        ContinueDebugEvent(dbg_debug_event.dwProcessId,
          dbg_debug_event.dwThreadId,
          dbg_continue_status);
      }
    
      LPDEBUG_EVENT DebugEv = &dbg_debug_event;
    
      while (alive)
      {
        have_thread_context = false;
    
        uint64_t begin_time = GetCurTime();
        BOOL wait_ret = WaitForDebugEvent(DebugEv, 100);
        uint64_t end_time = GetCurTime();
    
        uint64_t time_elapsed = end_time - begin_time;
        timeout = ((uint64_t)timeout >= time_elapsed) ? timeout - (uint32_t)time_elapsed : 0;
    
        // printf("timeout: %u\\n", timeout);
        // printf("time: %lld\\n", get_cur_time_us());
    
        if (wait_ret) {
          dbg_continue_needed = true;
        } else {
          dbg_continue_needed = false;
        }
    
        if (timeout == 0) return DEBUGGER_HANGED;
    
        if (!wait_ret) {
          //printf("WaitForDebugEvent returned 0\\n");
          continue;
        }
    
        dbg_continue_status = DBG_CONTINUE;
    
        thread_id = DebugEv->dwThreadId;
    
        // printf("eventCode: %x\\n", DebugEv->dwDebugEventCode);
    
        switch (DebugEv->dwDebugEventCode)
        {
        case EXCEPTION_DEBUG_EVENT:
          if (!killing) {
            ret = HandleExceptionInternal(&DebugEv->u.Exception.ExceptionRecord);
            if (ret == DEBUGGER_CRASHED) OnCrashed(&last_exception);
            if (ret != DEBUGGER_CONTINUE) return ret;
          } else {
            dbg_continue_status = DBG_EXCEPTION_NOT_HANDLED;
          }
          break;
    
        case CREATE_THREAD_DEBUG_EVENT:
          break;
    
        case CREATE_PROCESS_DEBUG_EVENT: {
          if (trace_debug_events) printf("Debugger: Process created or attached\\n");
          OnProcessCreated();
          CloseHandle(DebugEv->u.CreateProcessInfo.hFile);
          break;
        }
    
        case EXIT_THREAD_DEBUG_EVENT:
          break;
    
        case EXIT_PROCESS_DEBUG_EVENT:
          if (trace_debug_events) printf("Debugger: Process exit\\n");
          OnProcessExit();
          alive = false;
          break;
    
        case LOAD_DLL_DEBUG_EVENT: {
          if(!killing) HandleDllLoadInternal(&DebugEv->u.LoadDll);
          CloseHandle(DebugEv->u.LoadDll.hFile);
          break;
        }
    
        case UNLOAD_DLL_DEBUG_EVENT:
          if (trace_debug_events)
            printf("Debugger: Unloaded module from %p\\n", DebugEv->u.UnloadDll.lpBaseOfDll);
          OnModuleUnloaded(DebugEv->u.UnloadDll.lpBaseOfDll);
          break;
    
       default:
          break;
        }
    
        ContinueDebugEvent(DebugEv->dwProcessId,
          DebugEv->dwThreadId,
          dbg_continue_status);
      }
    
      return DEBUGGER_PROCESS_EXIT;
    }
    

    其中进行不同的 操作 是通过 DebugEvdwDebugEventCode 属性来实现的。

  5. GetCoverage 接着调用这个函数

    检查该模块是否已经在覆盖范围列表中(如果客户端使用非空的初始覆盖范围调用)

    如果没有就加入到 coverage 列表中。

  6. IgnoreCoverage 函数调用

    设置要忽略的(新)覆盖率

    通过名字获得 对应的 模块。然后通过偏移量来确定 需要忽略的地方。 data->ignore_coverage.insert(iter->offsets.begin(), iter->offsets.end());

  7. 然后是 MergeCoverage 函数

    coverage 进行一个管理

  8. 最后写入 文件 WriteCoverage

上面是我的对 tinyinst-converage.cpp 中 main 函数运行过程的分析。

在安全客上有对所有函数的分析。

https://www.anquanke.com/post/id/234925#h2-4

其中 一共 4个 关键的 cpp 文件。