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); } } }