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
// Constructs for attacker control -------------------------------------------------------------------------------------

/// Check if instruction's sole operand is a register
#[inline(always)]
pub fn is_single_reg(instr: &zydis::DecodedInstruction) -> bool {

    let regs_read_cnt = instr.operands.iter().filter(|&o| {
        (o.action == zydis::enums::OperandAction::READ)
        && (o.ty == zydis::enums::OperandType::REGISTER)
        }).count();

    regs_read_cnt == 1
}

/// Check if instruction's sole operand is a register-controlled memory deref
#[inline(always)]
pub fn is_single_reg_deref(instr: &zydis::DecodedInstruction) -> bool {

    let regs_deref_cnt = instr.operands.iter().filter(|&o| {
        (o.action == zydis::enums::OperandAction::READ)
        && (o.ty == zydis::enums::OperandType::MEMORY)
        && (o.mem.base != zydis::Register::NONE)
        }).count();

    regs_deref_cnt == 1
}

/// Check if jump instruction with register-controlled target
#[inline(always)]
pub fn is_reg_set_call(instr: &zydis::DecodedInstruction) -> bool {
    is_call(&instr)
    && is_single_reg(&instr)
}

/// Check if jump instruction with register-controlled target
#[inline(always)]
pub fn is_reg_set_jmp(instr: &zydis::DecodedInstruction) -> bool {
    is_jmp(&instr)
    && is_single_reg(&instr)
}

/// Check if jump instruction with register-controlled memory deref target
#[inline(always)]
pub fn is_mem_ptr_set_jmp(instr: &zydis::DecodedInstruction) -> bool {
    is_jmp(&instr)
    && is_single_reg_deref(&instr)
}

/// Check if call instruction with register-controlled memory deref target
#[inline(always)]
pub fn is_mem_ptr_set_call(instr: &zydis::DecodedInstruction) -> bool {
    is_call(&instr)
    && is_single_reg_deref(&instr)
}

/// Check if instruction is a ROP/JOP/SYS gadget tail
#[inline(always)]
pub fn is_gadget_tail(instr: &zydis::DecodedInstruction) -> 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: &zydis::DecodedInstruction) -> bool {
    is_reg_set_jmp(instr)
    || is_reg_set_call(instr)
    || is_mem_ptr_set_jmp(instr)
    || is_mem_ptr_set_call(instr)
}

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

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

/// Check if return instruction
#[inline(always)]
pub fn is_ret(instr: &zydis::DecodedInstruction) -> bool {
    instr.meta.category == zydis::enums::InstructionCategory::RET
}

/// Check if call instruction
#[inline(always)]
pub fn is_call(instr: &zydis::DecodedInstruction) -> bool {
    instr.meta.category == zydis::enums::InstructionCategory::CALL
}

/// Check if jmp instruction
pub fn is_jmp(instr: &zydis::DecodedInstruction) -> bool {
    instr.mnemonic == zydis::enums::Mnemonic::JMP
}

/// Check if interrupt instruction
#[inline(always)]
pub fn is_int(instr: &zydis::DecodedInstruction) -> bool {
    instr.meta.category == zydis::enums::InstructionCategory::INTERRUPT
}

/// Check if syscall instruction
#[inline(always)]
pub fn is_syscall(instr: &zydis::DecodedInstruction) -> bool {
    instr.meta.category == zydis::enums::InstructionCategory::SYSCALL
}

/// Check if Linux syscall instruction
#[inline(always)]
pub fn is_linux_syscall(instr: &zydis::DecodedInstruction) -> bool {

     let imm_0x80_cnt = instr.operands.iter().filter(|&o| {
        (o.action == zydis::enums::OperandAction::READ)
        && (o.ty == zydis::enums::OperandType::IMMEDIATE)
        && (o.imm.value == 0x80)
        }).count();

    (instr.meta.category == zydis::enums::InstructionCategory::INTERRUPT)
    && (imm_0x80_cnt == 1)
}