GCC 的 -mmitigate-rop 是如何工作的?

信息安全 编译器 海合会 罗普
2021-09-04 06:18:16

GCC 6 有一个标志 ,-mmitigate-rop它以减少 ROP 可利用的小工具数量的方式编译二进制文件。解释此功能的 GCC 文档很少:

-mmitigate-rop
    尽量避免生成包含意外返回的代码序列
    操作码,以减轻某些形式的攻击。目前,这
    选项的功能有限,不应依赖其提供
    严重的保护。

我试图弄清楚它是如何工作的,以评估它对 ROP 的实际效果。我通读了源代码,但如果不了解 GCC 内部,很难理解。我的预感是绕过它是相当无用和微不足道的,但由于我什至不知道它背后的理论(它显然不是真正的 CFI,如 clang 的 ROP 缓解措施),我什至无法开始评估它。也许它可以确保如果使用不同的偏移量读取的操作码无法解码为ret? 我想知道这个功能是如何工作的,以及它对现实 ROP 链的影响。

下面复制了与此功能相关的功能(不分先后)。

/* 如果 T 是我们应该避免的字节之一,则返回 true
   -mmitigate-rop。*/

静态布尔
ix86_rop_should_change_byte_p (int t)
{
  返回 t == 0xc2 || t == 0xc3 || t == 0xca || t == 0xcb;
}

/* 给定一个带有 NOPERANDS 操作数的 insn INSN,返回使用的 modr/m 字节
   如果它可能与 ROP 缓解相关,则在其编码中,否则
   返回-1。如果 POPNO0 和 POPNO1 不为空,则存储操作数编号
   用于将其计算到它们中。*/

静态整数
ix86_get_modrm_for_rop (rtx_insn *insn, rtx *operands, int nooperands,
            int *popno0 = 0, int *popno1 = 0)
{
  if (asm_noperands (PATTERN (insn)) >= 0)
    返回-1;
  int has_modrm = get_attr_modrm (insn);
  如果(!has_modrm)
    返回-1;
  枚举 attr_modrm_class cls = get_attr_modrm_class (insn);
  rtx op0,op1;
  开关 (cls)
    {
    案例 MODRM_CLASS_OP02:
      gcc_assert (noperands >= 3);
      如果 (popno0)
    {
      *popno0 = 0;
      *popno1 = 2;
    }
      op0 = 操作数[0];
      op1 = 操作数[2];
      休息;
    案例 MODRM_CLASS_OP01:
      gcc_assert (noperands >= 2);
      如果 (popno0)
    {
      *popno0 = 0;
      *popno1 = 1;
    }
      op0 = 操作数[0];
      op1 = 操作数[1];
      休息;
    默认:
      返回-1;
    }
  if (REG_P (op0) && REG_P (op1))
    {
      int enc0 = reg_encoded_number (op0);
      int enc1 = reg_encoded_number (op1);
      返回 0xc0 + (enc1 << 3) + enc0;
    }
  返回-1;
}

/* 给定一个寄存器号 BASE,一组寄存器中最低的,更新
   regsets IN 和 OUT 与在输入中应避免的寄存器
   并在试图避免生成 modr/m 时分别输出操作数
   -mmitigate-rop 的字节。*/

静态空白
set_rop_modrm_reg_bits (int base, HARD_REG_SET &in, HARD_REG_SET &out)
{
  SET_HARD_REG_BIT(输出,基础);
  SET_HARD_REG_BIT (out, base + 1);
  SET_HARD_REG_BIT (in, base + 2);
  SET_HARD_REG_BIT (in, base + 3);
}

/* 如果 -mmitigate-rop 有效则调用。尝试重写指令
   modr/m 字节的某些编码不会发生。*/
静态空白
ix86_mitigate_rop(无效)
{
  HARD_REG_SET 输入风险;
  HARD_REG_SET output_risky;
  HARD_REG_SET inout_risky;

  CLEAR_HARD_REG_SET(输出风险);
  CLEAR_HARD_REG_SET(输入风险);
  SET_HARD_REG_BIT(输出风险,AX_REG);
  SET_HARD_REG_BIT(输出风险,CX_REG);
  SET_HARD_REG_BIT (input_risky, BX_REG);
  SET_HARD_REG_BIT(输入风险,DX_REG);
  set_rop_modrm_reg_bits (FIRST_SSE_REG, input_risky, output_risky);
  set_rop_modrm_reg_bits (FIRST_REX_INT_REG, input_risky, output_risky);
  set_rop_modrm_reg_bits (FIRST_REX_SSE_REG, input_risky, output_risky);
  set_rop_modrm_reg_bits (FIRST_EXT_REX_SSE_REG, input_risky, output_risky);
  set_rop_modrm_reg_bits (FIRST_MASK_REG, input_risky, output_risky);
  set_rop_modrm_reg_bits (FIRST_BND_REG, input_risky, output_risky);
  COPY_HARD_REG_SET (inout_risky, input_risky);
  IOR_HARD_REG_SET (inout_risky, output_risky);

  df_note_add_problem ();
  /* 修复 stack-regs 所做的事情。*/
  df_insn_rescan_all ();
  df_analyze();

  regrename_init (true);
  regrename_analyze (NULL);

  auto_vec 糖果;

  for (rtx_insn *insn = get_insns (); insn; insn = NEXT_INSN (insn))
    {
      if (!NONDEBUG_INSN_P (insn))
    继续;

      if (GET_CODE (PATTERN (insn)) == 使用
      || GET_CODE (PATTERN (insn)) == CLOBBER)
    继续;

      extract_insn (insn);

      int opno0,opno1;
      int modrm = ix86_get_modrm_for_rop (insn, recog_data.operand,
                      recog_data.n_operands, &opno0,
                      &opno1);

      如果(!ix86_rop_should_change_byte_p(modrm))
    继续;

      insn_rr_info *info = &insn_rr[INSN_UID (insn)];

      /* 当 regrename 必须使块失败时会发生这种情况。*/
      如果 (!info->op_info)
    继续;

      if (info->op_info[opno0].n_chains != 0)
    {
      gcc_assert (info->op_info[opno0].n_chains == 1);
      du_head_p op0c;
      op0c = regrename_chain_from_id (info->op_info[opno0].heads[0]->id);
      如果 (op0c->target_data_1 + op0c->target_data_2 == 0
          && !op0c->cannot_rename)
        cands.safe_push (op0c);

      op0c->target_data_1++;
    }
      if (info->op_info[opno1].n_chains != 0)
    {
      gcc_assert (info->op_info[opno1].n_chains == 1);
      du_head_p op1c;
      op1c = regrename_chain_from_id (info->op_info[opno1].heads[0]->id);
      如果 (op1c->target_data_1 + op1c->target_data_2 == 0
          && !op1c->cannot_rename)
        cands.safe_push (op1c);

      op1c->target_data_2++;
    }
    }

  诠释我;
  du_head_p 头;
  FOR_EACH_VEC_ELT(糖果,我,头)
    {
      int old_reg, best_reg;
      HARD_REG_SET 不可用;

      CLEAR_HARD_REG_SET(不可用);
      如果 (head->target_data_1)
    IOR_HARD_REG_SET(不可用,output_risky);
      如果 (head->target_data_2)
    IOR_HARD_REG_SET(不可用,input_risky);

      诠释 n_uses;
      reg_class superclass = regrename_find_superclass (head, &n_uses,
                            &不可用);
      old_reg = head->regno;
      best_reg = find_rename_reg (head, superclass, &unavailable,
                  旧注册,假);
      bool ok = regrename_do_replace (head, best_reg);
      gcc_assert (ok);
      如果(转储文件)
    fprintf (dump_file, "链 %d 在 %s\n 中重命名为 %s", head->id,
         reg_names[best_reg], reg_class_names[superclass]);

    }

  regrename_finish ();

  df_analyze();

  basic_block bb;
  regset_head 直播;

  INIT_REG_SET (&live);

  FOR_EACH_BB_FN (bb, cfun)
    {
      rtx_insn *insn;

      COPY_REG_SET (&live, DF_LR_OUT (bb));
      df_simulate_initialize_backwards (bb, &live);

      FOR_BB_INSNS_REVERSE (bb, insn)
    {
      if (!NONDEBUG_INSN_P (insn))
        继续;

      df_simulate_one_insn_backwards (bb, insn, &live);

      if (GET_CODE (PATTERN (insn)) == 使用
          || GET_CODE (PATTERN (insn)) == CLOBBER)
        继续;

      extract_insn (insn);
      constrain_operands_cached (insn, reload_completed);
      int opno0,opno1;
      int modrm = ix86_get_modrm_for_rop (insn, recog_data.operand,
                          recog_data.n_operands, &opno0,
                          &opno1);
      if (modrm %d\n", i);
      rtx newreg = gen_rtx_REG (recog_data.operand_mode[opno1], i);
      validate_change (insn, recog_data.operand_loc[opno1], newreg, false);
      insn = emit_insn_before (gen_move_insn (newreg, oldreg), insn);
    }
    }
}
1个回答

看起来像是添加了: https ://gcc.gnu.org/ml/gcc-patches/2015-11/msg01773.html

我不知道以后是否添加了更多内容:但我认为这段代码只是寻找可以重新解释为返回的指令。