1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Constructs for attacker control -------------------------------------------------------------------------------------

/// Check if instruction is a ROP/JOP/SYS gadget tail
#[inline(always)]
pub fn is_gadget_tail(instr: &iced_x86::Instruction) -> bool {
    is_ret(instr) || is_jop_gadget_tail(instr) || is_sys_gadget_tail(instr)
}

/// Check if instruction is a JOP gadget tail
#[inline(always)]
pub fn is_jop_gadget_tail(instr: &iced_x86::Instruction) -> bool {
    is_reg_indirect_call(instr) || is_reg_indirect_jmp(instr)
}

/// Check if instruction is a SYS gadget tail
#[inline(always)]
pub fn is_sys_gadget_tail(instr: &iced_x86::Instruction) -> bool {
    is_syscall(instr) || is_legacy_linux_syscall(instr)
}

// Categorization ------------------------------------------------------------------------------------------------------

/// Check if call instruction with register-controlled target
#[inline(always)]
pub fn is_reg_indirect_call(instr: &iced_x86::Instruction) -> bool {
    (instr.flow_control() == iced_x86::FlowControl::IndirectCall) && (has_ctrled_ops_only(instr))
}

/// Check if jump instruction with register-controlled target
#[inline(always)]
pub fn is_reg_indirect_jmp(instr: &iced_x86::Instruction) -> bool {
    (instr.flow_control() == iced_x86::FlowControl::IndirectBranch) && (has_ctrled_ops_only(instr))
}

/// Check if return instruction
#[inline(always)]
pub fn is_ret(instr: &iced_x86::Instruction) -> bool {
    (instr.mnemonic() == iced_x86::Mnemonic::Ret) || (instr.mnemonic() == iced_x86::Mnemonic::Retf)
}

/// Check if return instruction that adds to stack pointer
#[inline(always)]
pub fn is_ret_imm16(instr: &iced_x86::Instruction) -> bool {
    is_ret(instr) && (instr.op_count() != 0)
}

/// Check if direct call instruction
#[inline(always)]
pub fn is_direct_call(instr: &iced_x86::Instruction) -> bool {
    (instr.mnemonic() == iced_x86::Mnemonic::Call) && (!is_reg_indirect_call(instr))
}

/// Check if unconditional jmp instruction
pub fn is_uncond_fixed_jmp(instr: &iced_x86::Instruction) -> bool {
    (instr.mnemonic() == iced_x86::Mnemonic::Jmp) && (!is_reg_indirect_jmp(instr))
}

/// Check if interrupt instruction
#[inline(always)]
pub fn is_int(instr: &iced_x86::Instruction) -> bool {
    instr.flow_control() == iced_x86::FlowControl::Interrupt
}

/// Check if syscall/sysenter instruction
#[inline(always)]
pub fn is_syscall(instr: &iced_x86::Instruction) -> bool {
    (instr.mnemonic() == iced_x86::Mnemonic::Syscall)
        || (instr.mnemonic() == iced_x86::Mnemonic::Sysenter)
}

/// Check if legacy Linux syscall
#[inline(always)]
pub fn is_legacy_linux_syscall(instr: &iced_x86::Instruction) -> bool {
    match instr.try_immediate(0) {
        Ok(imm) => (imm == 0x80) && (instr.mnemonic() == iced_x86::Mnemonic::Int),
        _ => false,
    }
}

// Properties ----------------------------------------------------------------------------------------------------------

/// Check if instruction both reads and writes the same register
#[inline(always)]
pub fn is_reg_rw(instr: &iced_x86::Instruction, reg: &iced_x86::Register) -> bool {
    let mut info_factory = iced_x86::InstructionInfoFactory::new();
    let info = info_factory.info_options(&instr, iced_x86::InstructionInfoOptions::NO_MEMORY_USAGE);
    let reg_rw = iced_x86::UsedRegister::new(*reg, iced_x86::OpAccess::ReadWrite);

    info.used_registers().contains(&reg_rw)
}

/// Check if sets register from another register or stack (e.g. exclude constant write)
#[inline(always)]
pub fn is_reg_set(instr: &iced_x86::Instruction, reg: &iced_x86::Register) -> bool {
    let mut info_factory = iced_x86::InstructionInfoFactory::new();
    let info = info_factory.info_options(&instr, iced_x86::InstructionInfoOptions::NO_MEMORY_USAGE);
    let reg_w = iced_x86::UsedRegister::new(*reg, iced_x86::OpAccess::Write);

    let reg_read = |ur: iced_x86::UsedRegister| {
        ur.access() == iced_x86::OpAccess::Read || ur.access() == iced_x86::OpAccess::ReadWrite
    };

    if info.used_registers().iter().any(|ur| reg_read(*ur))
        && info.used_registers().contains(&reg_w)
    {
        return true;
    }

    false
}

/// Check if instruction has controllable operands only
#[inline(always)]
pub fn has_ctrled_ops_only(instr: &iced_x86::Instruction) -> bool {
    let op_cnt = instr.op_count();
    for op_idx in 0..op_cnt {
        match instr.try_op_kind(op_idx) {
            Ok(kind) => match kind {
                iced_x86::OpKind::Register => continue,
                iced_x86::OpKind::Memory => match instr.memory_base() {
                    iced_x86::Register::None => return false,
                    iced_x86::Register::RIP => return false,
                    iced_x86::Register::EIP => return false,
                    //iced_x86::Register::IP => false, // TODO: why missing?
                    _ => continue,
                },
                _ => return false,
            },
            _ => return false,
        }
    }

    op_cnt > 0
}