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