use std::ops::Not;
use crate::{
analysis::{
dis,
dis::{zydis::Decoder, DecodedInstruction},
},
aspace::AddressSpace,
module::{Module, Permissions},
VA,
};
#[derive(Clone, Copy, Debug)]
enum MnemonicDescriptor {
_NONE = 1,
PUSH = 2,
POP = 3,
MOV = 4,
LEA = 5,
CALL = 6,
RET = 7,
COMPARE = 8,
JMP = 9,
CJMP = 10,
ARITH = 11,
OTHER = 12,
}
#[derive(Debug)]
struct CodeFeatures {
has_invalid_instruction: bool,
has_zero_instruction: bool,
has_uncommon_instruction: bool,
}
fn extract_mnemonic_feature(insn: &DecodedInstruction) -> MnemonicDescriptor {
match insn.mnemonic {
zydis::Mnemonic::PUSH => MnemonicDescriptor::PUSH,
zydis::Mnemonic::POP => MnemonicDescriptor::POP,
zydis::Mnemonic::MOV => MnemonicDescriptor::MOV,
zydis::Mnemonic::LEA => MnemonicDescriptor::LEA,
zydis::Mnemonic::CALL => MnemonicDescriptor::CALL,
zydis::Mnemonic::RET | zydis::Mnemonic::IRET | zydis::Mnemonic::IRETD | zydis::Mnemonic::IRETQ => {
MnemonicDescriptor::RET
}
zydis::Mnemonic::CMP => MnemonicDescriptor::COMPARE,
zydis::Mnemonic::TEST => MnemonicDescriptor::COMPARE,
zydis::Mnemonic::JMP => MnemonicDescriptor::JMP,
zydis::Mnemonic::JB
| zydis::Mnemonic::JBE
| zydis::Mnemonic::JCXZ
| zydis::Mnemonic::JECXZ
| zydis::Mnemonic::JKNZD
| zydis::Mnemonic::JKZD
| zydis::Mnemonic::JL
| zydis::Mnemonic::JLE
| zydis::Mnemonic::JNB
| zydis::Mnemonic::JNBE
| zydis::Mnemonic::JNL
| zydis::Mnemonic::JNLE
| zydis::Mnemonic::JNO
| zydis::Mnemonic::JNP
| zydis::Mnemonic::JNS
| zydis::Mnemonic::JNZ
| zydis::Mnemonic::JO
| zydis::Mnemonic::JP
| zydis::Mnemonic::JRCXZ
| zydis::Mnemonic::JS
| zydis::Mnemonic::JZ => MnemonicDescriptor::CJMP,
zydis::Mnemonic::CMOVB
| zydis::Mnemonic::CMOVBE
| zydis::Mnemonic::CMOVL
| zydis::Mnemonic::CMOVLE
| zydis::Mnemonic::CMOVNB
| zydis::Mnemonic::CMOVNBE
| zydis::Mnemonic::CMOVNL
| zydis::Mnemonic::CMOVNLE
| zydis::Mnemonic::CMOVNO
| zydis::Mnemonic::CMOVNP
| zydis::Mnemonic::CMOVNS
| zydis::Mnemonic::CMOVNZ
| zydis::Mnemonic::CMOVO
| zydis::Mnemonic::CMOVP
| zydis::Mnemonic::CMOVS
| zydis::Mnemonic::CMOVZ => MnemonicDescriptor::CJMP,
zydis::Mnemonic::ADD
| zydis::Mnemonic::SUB
| zydis::Mnemonic::XOR
| zydis::Mnemonic::MUL
| zydis::Mnemonic::DIV
| zydis::Mnemonic::IMUL
| zydis::Mnemonic::IDIV
| zydis::Mnemonic::INC
| zydis::Mnemonic::DEC
| zydis::Mnemonic::NEG
| zydis::Mnemonic::NOT
| zydis::Mnemonic::AND
| zydis::Mnemonic::OR
| zydis::Mnemonic::SHL
| zydis::Mnemonic::SHR
| zydis::Mnemonic::SAR
| zydis::Mnemonic::ROL
| zydis::Mnemonic::ROR
| zydis::Mnemonic::RCL
| zydis::Mnemonic::RCR => MnemonicDescriptor::ARITH,
_ => MnemonicDescriptor::OTHER,
}
}
fn extract_code_features(decoder: &Decoder, buf: &[u8]) -> CodeFeatures {
assert!(buf.len() >= 256);
let mut has_invalid_instruction = false;
let mut has_zero_instruction = false;
let mut has_uncommon_instruction = false;
for (offset, insn) in dis::linear_disassemble(decoder, buf) {
if let (Some(0x0), Some(0x0)) = (buf.get(offset), buf.get(offset + 1)) {
has_zero_instruction = true;
}
if let Ok(Some(insn)) = insn {
let mnem = extract_mnemonic_feature(&insn);
if matches!(mnem, MnemonicDescriptor::OTHER) {
has_uncommon_instruction = true;
}
if matches!(
mnem,
MnemonicDescriptor::JMP | MnemonicDescriptor::CJMP | MnemonicDescriptor::RET
) {
break;
};
} else {
if offset < 256 - 0x10 {
has_invalid_instruction = true;
} else {
}
break;
}
}
CodeFeatures {
has_invalid_instruction,
has_zero_instruction,
has_uncommon_instruction,
}
}
pub fn is_pointer(module: &Module, va: VA) -> bool {
match module.read_va_at_va(va) {
Ok(dst) => module.probe_va(dst, Permissions::R),
Err(_) => false,
}
}
pub fn is_pointer_table(module: &Module, va: VA) -> bool {
let psize = module.arch.pointer_size();
is_pointer(module, va) && (is_pointer(module, va + psize as u64) || is_pointer(module, va - psize as u64))
}
pub fn is_probably_code(module: &Module, decoder: &Decoder, va: VA) -> bool {
if module.probe_va(va, Permissions::X).not() {
return false;
}
if module.is_in_image(va).not() {
return false;
}
if is_pointer_table(module, va) {
return false;
}
if let Ok(buf) = module.address_space.read_bytes(va, 256) {
let features = extract_code_features(decoder, &buf);
if features.has_invalid_instruction {
return false;
}
if features.has_zero_instruction {
return false;
}
if features.has_uncommon_instruction {
return false;
}
true
} else {
false
}
}