extern crate capstone;
use std::collections::HashSet;
use capstone::arch::arm::ArmInsn;
use capstone::arch::arm::ArmOperand;
use capstone::arch::arm64::Arm64Insn;
use capstone::arch::arm64::Arm64Operand;
use capstone::arch::riscv::RiscVInsn;
use capstone::arch::riscv::RiscVOperand;
use capstone::arch::x86::X86Insn;
use capstone::arch::x86::X86Operand;
use capstone::arch::*;
use capstone::prelude::*;
use capstone::{Insn, InsnId, Instructions};
use gdb_command::mappings::*;
use gdb_command::memory::*;
use gdb_command::registers::*;
use gdb_command::siginfo::*;
use goblin::container::Endian;
use goblin::elf::header;
use crate::constants::{
SI_KERNEL, SIGINFO_SIGABRT, SIGINFO_SIGBUS, SIGINFO_SIGFPE, SIGINFO_SIGILL, SIGINFO_SIGSEGV,
SIGINFO_SIGSYS, SIGINFO_SIGTRAP,
};
use crate::error::*;
use crate::execution_class::{ExecutionClass, is_near_null};
use crate::severity::Severity;
#[derive(Clone, Default)]
pub struct MachineInfo {
pub arch: u16,
pub endianness: Endian,
pub byte_width: u8,
}
#[derive(Clone, Default)]
pub struct GdbContext {
pub siginfo: Siginfo,
pub registers: Registers,
pub mappings: MappedFiles,
pub machine: MachineInfo,
pub pc_memory: MemoryObject,
pub stacktrace: Vec<String>,
}
impl Severity for GdbContext {
fn severity(&self) -> Result<ExecutionClass> {
match self.siginfo.si_signo {
SIGINFO_SIGABRT => {
if self.stacktrace.iter().any(|entry| entry.contains("cfree")) {
return ExecutionClass::find("HeapError");
}
if self
.stacktrace
.iter()
.any(|entry| entry.contains("__chk_fail"))
{
return ExecutionClass::find("SafeFunctionCheck");
}
if self
.stacktrace
.iter()
.any(|entry| entry.contains("_stack_chk_fail"))
{
return ExecutionClass::find("StackGuard");
}
ExecutionClass::find("AbortSignal")
}
SIGINFO_SIGTRAP => ExecutionClass::find("TrapSignal"),
SIGINFO_SIGILL | SIGINFO_SIGSYS => ExecutionClass::find("BadInstruction"),
SIGINFO_SIGSEGV | SIGINFO_SIGFPE | SIGINFO_SIGBUS => {
let pc = self.pc();
if pc.is_none() {
return Err(Error::Casr("Unable to get Program counter.".to_string()));
}
let pc = pc.unwrap();
if (self.siginfo.si_signo == SIGINFO_SIGSEGV
|| self.siginfo.si_signo == SIGINFO_SIGBUS)
&& *pc == self.siginfo.si_addr
{
if is_near_null(self.siginfo.si_addr) {
return ExecutionClass::find("SegFaultOnPcNearNull");
} else {
return ExecutionClass::find("SegFaultOnPc");
};
}
if (self.siginfo.si_signo == SIGINFO_SIGSEGV
|| self.siginfo.si_signo == SIGINFO_SIGBUS)
&& self.pc_memory.data.is_empty()
&& self.siginfo.si_code == SI_KERNEL
{
return ExecutionClass::find("SegFaultOnPc");
}
let cs = match self.machine.arch {
header::EM_386 => Capstone::new()
.x86()
.mode(arch::x86::ArchMode::Mode32)
.syntax(arch::x86::ArchSyntax::Intel)
.detail(true)
.build(),
header::EM_X86_64 => Capstone::new()
.x86()
.mode(arch::x86::ArchMode::Mode64)
.syntax(arch::x86::ArchSyntax::Intel)
.detail(true)
.build(),
header::EM_AARCH64 => Capstone::new()
.arm64()
.mode(arch::arm64::ArchMode::Arm)
.detail(true)
.endian(if self.machine.endianness == Endian::Little {
capstone::Endian::Little
} else {
capstone::Endian::Big
})
.build(),
header::EM_ARM => {
if let Some(cpsr) = self.registers.get("cpsr") {
Capstone::new()
.arm()
.mode(if *cpsr & 0x20 != 0 {
arch::arm::ArchMode::Thumb
} else {
arch::arm::ArchMode::Arm
})
.detail(true)
.endian(if self.machine.endianness == Endian::Little {
capstone::Endian::Little
} else {
capstone::Endian::Big
})
.build()
} else {
return Err(Error::Casr(
"Unable to initialize disassembler for EM_ARM".to_string(),
));
}
}
header::EM_RISCV => {
let mode = match self.machine.byte_width {
8 => arch::riscv::ArchMode::RiscV64,
4 => arch::riscv::ArchMode::RiscV32,
_ => {
return Err(Error::Casr(
"Only rv64 or rv32 are supported.".to_string(),
));
}
};
Capstone::new().riscv().mode(mode).detail(true).build()
}
_ => {
return Err(Error::Casr(format!(
"Unsupported machine architecture: {}",
self.machine.arch
)));
}
};
if let Ok(cs) = cs {
let insns = cs.disasm_all(&self.pc_memory.data, *pc);
if let Ok(insns) = insns {
if self.siginfo.si_signo == SIGINFO_SIGSEGV
|| self.siginfo.si_signo == SIGINFO_SIGBUS
{
Self::analyze_instructions(&cs, &insns, self)
} else {
ExecutionClass::find("FPE")
}
} else {
Err(Error::Casr(
"Unable to get Capstone Instructions.".to_string(),
))
}
} else {
Err(Error::Casr(format!(
"Unable to initialize architecture disassembler: {}",
self.machine.arch
)))
}
}
_ => Err(Error::Casr(format!(
"Unsupported signal :{}",
self.siginfo.si_signo
))),
}
}
}
impl GdbContext {
pub fn sp(&self) -> Option<&u64> {
match self.machine.arch {
header::EM_X86_64 => self.registers.get("rsp"),
header::EM_386 => self.registers.get("esp"),
header::EM_ARM | header::EM_AARCH64 | header::EM_RISCV => self.registers.get("sp"),
_ => None,
}
}
pub fn pc(&self) -> Option<&u64> {
match self.machine.arch {
header::EM_386 => self.registers.get("eip"),
header::EM_X86_64 => self.registers.get("rip"),
header::EM_ARM | header::EM_AARCH64 | header::EM_RISCV => self.registers.get("pc"),
_ => None,
}
}
fn analyze_instructions(
cs: &Capstone,
insns: &Instructions,
context: &GdbContext,
) -> Result<ExecutionClass> {
match context.machine.arch {
header::EM_386 | header::EM_X86_64 => {
Self::analyze_instructions_x86(cs, insns, context)
}
header::EM_ARM => Self::analyze_instructions_arm(cs, insns, &context.siginfo),
header::EM_AARCH64 => Self::analyze_instructions_arm64(cs, insns, &context.siginfo),
header::EM_RISCV => Self::analyze_instructions_riscv(cs, insns, &context.siginfo),
_ => Err(Error::Casr(format!(
"Unsupported machine arch: {}",
context.machine.arch
))),
}
}
fn analyze_instructions_x86(
cs: &Capstone,
insns: &Instructions,
context: &GdbContext,
) -> Result<ExecutionClass> {
let Some(insn) = insns.iter().next() else {
return Err(Error::Casr(
"Couldn't get first x86 instruction".to_string(),
));
};
let Ok(detail) = cs.insn_detail(insn) else {
return Err(Error::Casr("Couldn't get instruction details".to_string()));
};
if detail
.groups()
.iter()
.any(|x| cs.group_name(*x).unwrap() == "ret")
{
return ExecutionClass::find("ReturnAv");
}
if detail
.groups()
.iter()
.any(|x| cs.group_name(*x).unwrap() == "call")
{
if let Some(sp) = context.sp() {
if (*sp - context.machine.byte_width as u64) == context.siginfo.si_addr {
return ExecutionClass::find("StackOverflow");
}
}
if !detail.regs_read().is_empty() {
if !is_near_null(context.siginfo.si_addr) || context.siginfo.si_code == SI_KERNEL {
return ExecutionClass::find("CallAv");
} else {
return ExecutionClass::find("CallAvNearNull");
}
}
}
if detail
.groups()
.iter()
.any(|x| cs.group_name(*x).unwrap() == "jump")
{
if !detail.regs_read().is_empty() {
if !is_near_null(context.siginfo.si_addr) || context.siginfo.si_code == SI_KERNEL {
return ExecutionClass::find("BranchAv");
} else {
return ExecutionClass::find("BranchAvNearNull");
}
}
}
let mnemonic = insn.mnemonic();
if mnemonic.is_none() {
return Err(Error::Casr("Couldn't get instruction mnemonic".to_string()));
}
let mnemonic = mnemonic.unwrap();
if mnemonic.to_string().contains("mov") {
let ops = detail.arch_detail().operands();
for (num, op) in ops.iter().enumerate() {
let operand = if let capstone::arch::ArchOperand::X86Operand(operand) = op {
operand
} else {
return Err(Error::Casr("Couldn't get instruction operands".to_string()));
};
if let capstone::arch::x86::X86OperandType::Mem(_) = operand.op_type {
match (
context.siginfo.si_signo,
context.siginfo.si_code,
num,
is_near_null(context.siginfo.si_addr),
) {
(SIGINFO_SIGBUS, _, 1, _) => {
return ExecutionClass::find("SourceAv");
}
(_, SI_KERNEL, 0, _) | (_, _, 0, false) | (SIGINFO_SIGBUS, _, 0, _) => {
return ExecutionClass::find("DestAv");
}
(_, _, 0, true) => {
return ExecutionClass::find("DestAvNearNull");
}
(_, SI_KERNEL, 1, _) | (_, _, 1, false) => {
if let Ok(new_class) = check_taint(cs, insns) {
return Ok(new_class);
} else {
return ExecutionClass::find("SourceAv");
}
}
(_, _, 1, true) => return ExecutionClass::find("SourceAvNearNull"),
_ => return ExecutionClass::find("AccessViolation"),
}
}
}
}
ExecutionClass::find("AccessViolation")
}
fn analyze_instructions_arm(
cs: &Capstone,
insns: &Instructions,
info: &Siginfo,
) -> Result<ExecutionClass> {
let Some(insn) = insns.iter().next() else {
return Err(Error::Casr(
"Couldn't get first arm instruction".to_string(),
));
};
let Ok(detail) = cs.insn_detail(insn) else {
return Err(Error::Casr("Couldn't get instruction details".to_string()));
};
let Some(mnemonic) = insn.mnemonic() else {
return Err(Error::Casr("Couldn't get instruction mnemonic".to_string()));
};
let m = mnemonic.to_string();
let ops = detail.arch_detail().operands();
for op in ops.iter() {
let capstone::arch::ArchOperand::ArmOperand(operand) = op else {
return Err(Error::Casr("Couldn't get instruction operands".to_string()));
};
if let capstone::arch::arm::ArmOperandType::Mem(_) = operand.op_type {
return classify_memory_operation(
cs,
insns,
info.si_code,
m.contains("str"),
m.contains("ldr"),
is_near_null(info.si_addr),
);
}
}
ExecutionClass::find("AccessViolation")
}
fn analyze_instructions_arm64(
cs: &Capstone,
insns: &Instructions,
info: &Siginfo,
) -> Result<ExecutionClass> {
let Some(insn) = insns.iter().next() else {
return Err(Error::Casr(
"Couldn't get first aarch64 instruction".to_string(),
));
};
let Ok(detail) = cs.insn_detail(insn) else {
return Err(Error::Casr("Couldn't get instruction details".to_string()));
};
let id = insn.id();
let ops = detail.arch_detail().operands();
for op in ops.iter() {
let capstone::arch::ArchOperand::Arm64Operand(operand) = op else {
return Err(Error::Casr("Couldn't get instruction operands".to_string()));
};
if let capstone::arch::arm64::Arm64OperandType::Mem(_) = operand.op_type {
return classify_memory_operation(
cs,
insns,
info.si_code,
is_store_arm64(id),
is_load_arm64(id),
is_near_null(info.si_addr),
);
}
}
ExecutionClass::find("AccessViolation")
}
fn analyze_instructions_riscv(
cs: &Capstone,
insns: &Instructions,
info: &Siginfo,
) -> Result<ExecutionClass> {
let Some(insn) = insns.iter().next() else {
return Err(Error::Casr(
"Couldn't get first riscv instruction".to_string(),
));
};
let Ok(detail) = cs.insn_detail(insn) else {
return Err(Error::Casr("Couldn't get instruction details".to_string()));
};
let id = insn.id();
let ops = detail.arch_detail().operands();
for op in ops.iter() {
let capstone::arch::ArchOperand::RiscVOperand(operand) = op else {
return Err(Error::Casr("Couldn't get instruction operands".to_string()));
};
if let capstone::arch::riscv::RiscVOperand::Mem(_) = operand {
return classify_memory_operation(
cs,
insns,
info.si_code,
is_store_riscv(id),
is_load_riscv(id),
is_near_null(info.si_addr),
);
}
}
ExecutionClass::find("AccessViolation")
}
}
fn classify_memory_operation(
cs: &Capstone,
insns: &Instructions,
code: u32,
is_store: bool,
is_load: bool,
is_near_null: bool,
) -> Result<ExecutionClass> {
match (code, is_store, is_load, is_near_null) {
(SI_KERNEL, true, false, _) | (_, true, false, false) => ExecutionClass::find("DestAv"),
(_, true, false, true) => ExecutionClass::find("DestAvNearNull"),
(SI_KERNEL, false, true, _) | (_, false, true, false) => {
if let Ok(new_class) = check_taint(cs, insns) {
Ok(new_class)
} else {
ExecutionClass::find("SourceAv")
}
}
(_, false, true, true) => ExecutionClass::find("SourceAvNearNull"),
_ => ExecutionClass::find("AccessViolation"),
}
}
fn check_taint(cs: &Capstone, insns: &Instructions) -> Result<ExecutionClass> {
let mut taint_set: HashSet<RegId> = HashSet::new();
for (index, insn) in insns.iter().enumerate() {
match process_instruction(cs, insn, index, &mut taint_set) {
InstructionType::ControlFlowTransfer | InstructionType::Unknown => break,
InstructionType::TaintedCall => return ExecutionClass::find("CallAvTainted"),
InstructionType::TaintedJMP => return ExecutionClass::find("BranchAvTainted"),
InstructionType::TaintedRet => return ExecutionClass::find("ReturnAv"),
InstructionType::TaintedMemStore => return ExecutionClass::find("DestAvTainted"),
InstructionType::TaintedPc => return ExecutionClass::find("SegFaultOnPc"),
_ => {}
}
}
Err(Error::Casr(
"Couldn't find new ExecutionClass using taint tracking".to_string(),
))
}
fn process_instruction(
cs: &Capstone,
insn: &Insn,
index: usize,
taint_set: &mut HashSet<RegId>,
) -> InstructionType {
let detail = cs.insn_detail(insn);
if detail.is_err() {
return InstructionType::Unknown;
}
let detail = detail.unwrap();
match detail.arch_detail() {
ArchDetail::ArmDetail(arm_detail) => {
let operands: Vec<ArmOperand> = arm_detail.operands().collect();
match insn_type_arm(insn.id()) {
InstructionType::ControlFlowTransfer => {
match operands[0].op_type {
arm::ArmOperandType::Reg(t1) => {
if taint_set.contains(&t1) {
InstructionType::TaintedJMP
} else {
InstructionType::ControlFlowTransfer
}
}
_ => InstructionType::ControlFlowTransfer,
}
}
InstructionType::Arithmetic => {
if let (arm::ArmOperandType::Reg(t1), arm::ArmOperandType::Reg(t2)) =
(&operands[0].op_type, &operands[1].op_type)
{
match &operands[1].shift {
arm::ArmShift::AsrReg(r)
| arm::ArmShift::LsrReg(r)
| arm::ArmShift::LslReg(r)
| arm::ArmShift::RorReg(r)
| arm::ArmShift::RrxReg(r) => {
if taint_set.contains(r) {
taint_set.insert(*t2);
}
}
_ => {}
}
if taint_set.contains(t2) {
taint_set.insert(*t1);
}
}
InstructionType::Arithmetic
}
InstructionType::DataTransfer => {
let mut t = InstructionType::DataTransfer;
match insn.id() {
ARM_INS_MOV => {
match (&operands[0].op_type, &operands[1].op_type) {
(arm::ArmOperandType::Reg(t1), arm::ArmOperandType::Reg(t2)) => {
match &operands[1].shift {
arm::ArmShift::AsrReg(r)
| arm::ArmShift::LsrReg(r)
| arm::ArmShift::LslReg(r)
| arm::ArmShift::RorReg(r)
| arm::ArmShift::RrxReg(r) => {
if taint_set.contains(r) {
taint_set.insert(*t2);
}
}
_ => {}
}
if taint_set.contains(t2) {
taint_set.insert(*t1);
}
if *t1 == RegId(11) {
t = if taint_set.contains(t1) {
InstructionType::TaintedPc
} else {
InstructionType::ControlFlowTransfer
};
}
}
(arm::ArmOperandType::Reg(t1), arm::ArmOperandType::Imm(_)) => {
taint_set.remove(t1);
}
_ => {}
}
}
ARM_INS_LDR => {
if let (arm::ArmOperandType::Reg(t1), arm::ArmOperandType::Mem(t2)) =
(&operands[0].op_type, &operands[1].op_type)
{
if taint_set.contains(&t2.base())
|| taint_set.contains(&t2.index())
|| index == 0
{
taint_set.insert(*t1);
if *t1 == RegId(11) {
t = InstructionType::TaintedPc;
}
} else {
taint_set.remove(t1);
}
}
}
ARM_INS_STR => {
if let (arm::ArmOperandType::Reg(_), arm::ArmOperandType::Mem(t1)) =
(&operands[0].op_type, &operands[1].op_type)
{
if taint_set.contains(&t1.base()) || taint_set.contains(&t1.index())
{
t = InstructionType::TaintedMemStore;
}
}
}
_ => {}
}
t
}
InstructionType::BinaryCMP => {
InstructionType::BinaryCMP
}
InstructionType::Unary => InstructionType::Unary,
_ => InstructionType::Unknown,
}
}
ArchDetail::Arm64Detail(arm64_detail) => {
let operands: Vec<Arm64Operand> = arm64_detail.operands().collect();
match insn_type_arm64(insn.id()) {
InstructionType::ControlFlowTransfer => {
if !operands.is_empty() {
match operands[0].op_type {
arm64::Arm64OperandType::Reg(t1) => {
if taint_set.contains(&t1) {
InstructionType::TaintedCall
} else {
InstructionType::ControlFlowTransfer
}
}
_ => InstructionType::ControlFlowTransfer,
}
} else {
InstructionType::ControlFlowTransfer
}
}
InstructionType::Arithmetic => {
if let (arm64::Arm64OperandType::Reg(t1), arm64::Arm64OperandType::Reg(t2)) =
(&operands[0].op_type, &operands[1].op_type)
{
if taint_set.contains(t2) {
taint_set.insert(*t1);
}
}
InstructionType::Arithmetic
}
InstructionType::DataTransfer => {
let mut t = InstructionType::DataTransfer;
match insn.id() {
ARM64_INS_MOV | ARM64_INS_ADR => {
match (&operands[0].op_type, &operands[1].op_type) {
(
arm64::Arm64OperandType::Reg(t1),
arm64::Arm64OperandType::Reg(t2),
) => {
if taint_set.contains(t2) {
taint_set.insert(*t1);
}
}
(
arm64::Arm64OperandType::Reg(t1),
arm64::Arm64OperandType::Imm(_),
) => {
taint_set.remove(t1);
}
_ => {}
}
}
ARM64_INS_LDR | ARM64_INS_LDTR | ARM64_INS_LDUR | ARM64_INS_LDRB
| ARM64_INS_LDRSB | ARM64_INS_LDRH | ARM64_INS_LDRSH | ARM64_INS_LDRSW
| ARM64_INS_LDTRB | ARM64_INS_LDTRH | ARM64_INS_LDTRSB
| ARM64_INS_LDTRSH | ARM64_INS_LDTRSW | ARM64_INS_LDURB
| ARM64_INS_LDURSB | ARM64_INS_LDURH | ARM64_INS_LDURSH
| ARM64_INS_LDURSW => {
if let (
arm64::Arm64OperandType::Reg(t1),
arm64::Arm64OperandType::Mem(t2),
) = (&operands[0].op_type, &operands[1].op_type)
{
if taint_set.contains(&t2.base())
|| taint_set.contains(&t2.index())
|| index == 0
{
taint_set.insert(*t1);
} else {
taint_set.remove(t1);
}
}
}
ARM64_INS_LDP => {
if let (
arm64::Arm64OperandType::Reg(t1),
arm64::Arm64OperandType::Reg(t2),
arm64::Arm64OperandType::Mem(t3),
) = (
&operands[0].op_type,
&operands[1].op_type,
&operands[2].op_type,
) {
if taint_set.contains(&t3.base())
|| taint_set.contains(&t3.index())
|| index == 0
{
taint_set.insert(*t1);
taint_set.insert(*t2);
} else {
taint_set.remove(t1);
taint_set.remove(t2);
}
}
}
ARM64_INS_STR | ARM64_INS_STTR | ARM64_INS_STUR | ARM64_INS_STRB
| ARM64_INS_STRH | ARM64_INS_STTRB | ARM64_INS_STTRH | ARM64_INS_STURB
| ARM64_INS_STURH => {
if let (
arm64::Arm64OperandType::Reg(_),
arm64::Arm64OperandType::Mem(t1),
) = (&operands[0].op_type, &operands[1].op_type)
{
if taint_set.contains(&t1.base()) || taint_set.contains(&t1.index())
{
t = InstructionType::TaintedMemStore;
}
}
}
ARM64_INS_STP => {
if let (
arm64::Arm64OperandType::Reg(_),
arm64::Arm64OperandType::Reg(_),
arm64::Arm64OperandType::Mem(t1),
) = (
&operands[0].op_type,
&operands[1].op_type,
&operands[2].op_type,
) {
if taint_set.contains(&t1.base()) || taint_set.contains(&t1.index())
{
t = InstructionType::TaintedMemStore;
}
}
}
_ => {}
}
t
}
InstructionType::BinaryCMP => {
InstructionType::BinaryCMP
}
InstructionType::Unary => InstructionType::Unary,
_ => InstructionType::Unknown,
}
}
ArchDetail::RiscVDetail(riscv_detail) => {
let operands: Vec<RiscVOperand> = riscv_detail.operands().collect();
match insn_type_riscv(insn.id()) {
InstructionType::ControlFlowTransfer => {
if insn.id() == RISCV_INS_JALR {
if let RiscVOperand::Reg(rs) = operands[0] {
if taint_set.contains(&rs) {
return InstructionType::TaintedCall;
}
}
}
InstructionType::ControlFlowTransfer
}
InstructionType::DataTransfer => {
let mut t = InstructionType::DataTransfer;
match insn.id() {
RISCV_INS_AUIPC | RISCV_INS_LUI => {
if let RiscVOperand::Reg(rd) = operands[0] {
taint_set.remove(&rd);
}
}
RISCV_INS_SB | RISCV_INS_SH | RISCV_INS_SW | RISCV_INS_SD => {
if let RiscVOperand::Mem(mem) = operands[1] {
if taint_set.contains(&mem.base()) {
t = InstructionType::TaintedMemStore;
}
}
}
_ => {
if let RiscVOperand::Mem(mem) = operands[1] {
if taint_set.contains(&mem.base()) || index == 0 {
if let RiscVOperand::Reg(rd) = operands[0] {
taint_set.insert(rd);
}
}
}
}
}
t
}
InstructionType::Arithmetic => {
if insn.id() == RISCV_INS_SLT
|| insn.id() == RISCV_INS_SLTU
|| insn.id() == RISCV_INS_SLTI
|| insn.id() == RISCV_INS_SLTIU
{
if let RiscVOperand::Reg(rd) = operands[0] {
taint_set.remove(&rd);
}
return InstructionType::Arithmetic;
}
let (rd, rs1, rs2) = if operands.len() == 3 {
(&operands[0], &operands[1], &operands[2])
} else {
(&operands[0], &operands[0], &operands[1])
};
match (rs1, rs2) {
(RiscVOperand::Reg(rs1), RiscVOperand::Reg(rs2)) => {
if taint_set.contains(rs1) || taint_set.contains(rs2) {
if let RiscVOperand::Reg(rd) = rd {
taint_set.insert(*rd);
}
} else if let RiscVOperand::Reg(rd) = rd {
taint_set.remove(rd);
}
}
(RiscVOperand::Reg(rs1), RiscVOperand::Imm(_)) => {
if taint_set.contains(rs1) {
if let RiscVOperand::Reg(rd) = rd {
taint_set.insert(*rd);
}
} else if let RiscVOperand::Reg(rd) = rd {
taint_set.remove(rd);
}
}
_ => {}
}
InstructionType::Arithmetic
}
_ => InstructionType::Unknown,
}
}
ArchDetail::X86Detail(x86_detail) => {
let operands: Vec<X86Operand> = x86_detail.operands().collect();
match insn_type_x86(insn.id()) {
InstructionType::ControlFlowTransfer => {
if insn.id() == X86_INS_RET {
if taint_set.contains(&RegId(30)) || taint_set.contains(&RegId(44)) {
InstructionType::TaintedRet
} else {
InstructionType::ControlFlowTransfer
}
} else if insn.id() == X86_INS_JMP || insn.id() == X86_INS_CALL {
match &operands[0].op_type {
x86::X86OperandType::Reg(t1) => {
if taint_set.contains(t1) {
if insn.id() == X86_INS_JMP {
InstructionType::TaintedJMP
} else {
InstructionType::TaintedCall
}
} else {
InstructionType::ControlFlowTransfer
}
}
x86::X86OperandType::Mem(t1) => {
if taint_set.contains(&t1.base()) || taint_set.contains(&t1.index())
{
if insn.id() == X86_INS_JMP {
InstructionType::TaintedJMP
} else {
InstructionType::TaintedCall
}
} else {
InstructionType::ControlFlowTransfer
}
}
_ => InstructionType::ControlFlowTransfer,
}
} else {
InstructionType::ControlFlowTransfer
}
}
InstructionType::DataTransfer => {
let mut t = InstructionType::DataTransfer;
match insn.id() {
X86_INS_MOV => {
match (&operands[0].op_type, &operands[1].op_type) {
(x86::X86OperandType::Reg(t1), x86::X86OperandType::Reg(t2)) => {
if taint_set.contains(t2) {
taint_set.insert(*t1);
}
}
(x86::X86OperandType::Reg(t1), x86::X86OperandType::Imm(_)) => {
taint_set.remove(t1);
}
(x86::X86OperandType::Reg(t1), x86::X86OperandType::Mem(t2)) => {
if taint_set.contains(&t2.base())
|| taint_set.contains(&t2.index())
|| index == 0
{
taint_set.insert(*t1);
} else {
taint_set.remove(t1);
}
}
(x86::X86OperandType::Mem(t1), x86::X86OperandType::Reg(_)) => {
if taint_set.contains(&t1.base())
|| taint_set.contains(&t1.index())
{
t = InstructionType::TaintedMemStore;
}
}
_ => {
}
}
}
X86_INS_XCHG => match (&operands[0].op_type, &operands[1].op_type) {
(x86::X86OperandType::Reg(t1), x86::X86OperandType::Reg(t2)) => {
if taint_set.contains(t1) && !taint_set.contains(t2) {
taint_set.insert(*t2);
taint_set.remove(t1);
}
if !taint_set.contains(t1) && taint_set.contains(t2) {
taint_set.insert(*t1);
taint_set.remove(t2);
}
}
(x86::X86OperandType::Reg(t1), x86::X86OperandType::Mem(_)) => {
taint_set.remove(t1);
}
(x86::X86OperandType::Mem(_), x86::X86OperandType::Reg(t2)) => {
taint_set.remove(t2);
}
_ => {}
},
X86_INS_LEA => {
if let (x86::X86OperandType::Reg(t1), x86::X86OperandType::Mem(t2)) =
(&operands[0].op_type, &operands[1].op_type)
{
if taint_set.contains(&t2.base()) || taint_set.contains(&t2.index())
{
taint_set.insert(*t1);
} else {
taint_set.remove(t1);
}
}
}
X86_INS_MOVZX | X86_INS_MOVSX => {
if let x86::X86OperandType::Reg(t1) = &operands[0].op_type {
taint_set.remove(t1);
}
}
_ => {}
}
t
}
InstructionType::BinaryCMP => {
InstructionType::BinaryCMP
}
InstructionType::Unary => {
if insn.id() == X86_INS_POP {
if let x86::X86OperandType::Reg(t1) = &operands[0].op_type {
taint_set.remove(t1);
}
}
InstructionType::Unary
}
InstructionType::Arithmetic => {
if let (x86::X86OperandType::Reg(t1), x86::X86OperandType::Reg(t2)) =
(&operands[0].op_type, &operands[1].op_type)
{
if taint_set.contains(t2) {
taint_set.insert(*t1);
}
}
InstructionType::Arithmetic
}
_ => InstructionType::Unknown,
}
}
_ => InstructionType::Unknown,
}
}
fn insn_type_x86(id: InsnId) -> InstructionType {
match id {
X86_INS_CALL | X86_INS_RET | X86_INS_JMP | X86_INS_JA | X86_INS_JAE | X86_INS_JBE
| X86_INS_JB | X86_INS_JCXZ | X86_INS_JECXZ | X86_INS_JE | X86_INS_JGE | X86_INS_JG
| X86_INS_JLE | X86_INS_JL | X86_INS_JNE | X86_INS_JNO | X86_INS_JNP | X86_INS_JNS
| X86_INS_JO | X86_INS_JP | X86_INS_JRCXZ | X86_INS_JS => {
InstructionType::ControlFlowTransfer
}
X86_INS_OR | X86_INS_SUB | X86_INS_XOR | X86_INS_ROL | X86_INS_ROR | X86_INS_SAL
| X86_INS_SAR | X86_INS_SHL | X86_INS_SHR | X86_INS_ADD | X86_INS_AND => {
InstructionType::Arithmetic
}
X86_INS_MOV | X86_INS_XCHG | X86_INS_LEA | X86_INS_MOVZX | X86_INS_MOVSX => {
InstructionType::DataTransfer
}
X86_INS_NEG | X86_INS_NOT | X86_INS_POP | X86_INS_DEC | X86_INS_INC => {
InstructionType::Unary
}
X86_INS_TEST | X86_INS_CMP => InstructionType::BinaryCMP,
_ => InstructionType::Unknown,
}
}
fn insn_type_arm(id: InsnId) -> InstructionType {
match id {
ARM_INS_B | ARM_INS_BL | ARM_INS_CBZ | ARM_INS_CBNZ | ARM_INS_BX | ARM_INS_BLX => {
InstructionType::ControlFlowTransfer
}
ARM_INS_ADC | ARM_INS_ADD | ARM_INS_ADDW | ARM_INS_AND | ARM_INS_EOR | ARM_INS_LSL
| ARM_INS_LSR | ARM_INS_ASR | ARM_INS_ORN | ARM_INS_ROR | ARM_INS_RRX | ARM_INS_SUBW
| ARM_INS_SUB | ARM_INS_RSB | ARM_INS_ORR | ARM_INS_MUL | ARM_INS_SDIV => {
InstructionType::Arithmetic
}
ARM_INS_STR | ARM_INS_LDR | ARM_INS_MOV => InstructionType::DataTransfer,
ARM_INS_REV | ARM_INS_RBIT => InstructionType::Unary,
ARM_INS_CMP | ARM_INS_TST | ARM_INS_TEQ => InstructionType::BinaryCMP,
_ => InstructionType::Unknown,
}
}
fn insn_type_arm64(id: InsnId) -> InstructionType {
match id {
ARM64_INS_B | ARM64_INS_BL | ARM64_INS_CBZ | ARM64_INS_CBNZ | ARM64_INS_BLR
| ARM64_INS_RET => InstructionType::ControlFlowTransfer,
ARM64_INS_ADC | ARM64_INS_ADD | ARM64_INS_BIC | ARM64_INS_AND | ARM64_INS_EOR
| ARM64_INS_LSL | ARM64_INS_LSR | ARM64_INS_ASR | ARM64_INS_ORN | ARM64_INS_ROR
| ARM64_INS_MVN | ARM64_INS_UDIV | ARM64_INS_SUB | ARM64_INS_ORR | ARM64_INS_MUL
| ARM64_INS_SDIV => InstructionType::Arithmetic,
ARM64_INS_STR | ARM64_INS_LDR | ARM64_INS_MOV | ARM64_INS_STP | ARM64_INS_LDP
| ARM64_INS_ADR | ARM64_INS_LDTR | ARM64_INS_LDUR | ARM64_INS_STTR | ARM64_INS_STUR
| ARM64_INS_LDRB | ARM64_INS_LDRSB | ARM64_INS_LDRH | ARM64_INS_LDRSH | ARM64_INS_LDRSW
| ARM64_INS_LDTRB | ARM64_INS_LDTRH | ARM64_INS_LDTRSB | ARM64_INS_LDTRSH
| ARM64_INS_LDTRSW | ARM64_INS_STRB | ARM64_INS_STRH | ARM64_INS_STTRB
| ARM64_INS_STTRH | ARM64_INS_LDURB | ARM64_INS_LDURSB | ARM64_INS_LDURH
| ARM64_INS_LDURSH | ARM64_INS_LDURSW | ARM64_INS_STURB | ARM64_INS_STURH => {
InstructionType::DataTransfer
}
ARM64_INS_REV | ARM64_INS_RBIT => InstructionType::Unary,
ARM64_INS_CMP | ARM64_INS_TST => InstructionType::BinaryCMP,
_ => InstructionType::Unknown,
}
}
fn is_load_arm64(id: InsnId) -> bool {
matches!(
id,
ARM64_INS_LDR
| ARM64_INS_LDRB
| ARM64_INS_LDRSB
| ARM64_INS_LDRH
| ARM64_INS_LDRSH
| ARM64_INS_LDRSW
| ARM64_INS_LDTR
| ARM64_INS_LDTRB
| ARM64_INS_LDTRSB
| ARM64_INS_LDTRH
| ARM64_INS_LDTRSH
| ARM64_INS_LDTRSW
| ARM64_INS_LDUR
| ARM64_INS_LDURB
| ARM64_INS_LDURH
| ARM64_INS_LDURSB
| ARM64_INS_LDURSH
| ARM64_INS_LDURSW
| ARM64_INS_LDP
)
}
fn is_store_arm64(id: InsnId) -> bool {
matches!(
id,
ARM64_INS_STR
| ARM64_INS_STRB
| ARM64_INS_STRH
| ARM64_INS_STTR
| ARM64_INS_STTRB
| ARM64_INS_STTRH
| ARM64_INS_STP
| ARM64_INS_STUR
| ARM64_INS_STURB
| ARM64_INS_STURH
)
}
fn is_load_riscv(id: InsnId) -> bool {
matches!(
id,
RISCV_INS_LB
| RISCV_INS_LBU
| RISCV_INS_LH
| RISCV_INS_LHU
| RISCV_INS_LW
| RISCV_INS_LWU
| RISCV_INS_LD
)
}
fn is_store_riscv(id: InsnId) -> bool {
matches!(
id,
RISCV_INS_SB | RISCV_INS_SH | RISCV_INS_SW | RISCV_INS_SD
)
}
fn insn_type_riscv(id: InsnId) -> InstructionType {
match id {
RISCV_INS_BEQ | RISCV_INS_BNE | RISCV_INS_BLT | RISCV_INS_BGE | RISCV_INS_BLTU
| RISCV_INS_BGEU | RISCV_INS_JAL | RISCV_INS_JALR => InstructionType::ControlFlowTransfer,
RISCV_INS_ADDI | RISCV_INS_ADD | RISCV_INS_ANDI | RISCV_INS_AND | RISCV_INS_XORI
| RISCV_INS_XOR | RISCV_INS_ORI | RISCV_INS_OR | RISCV_INS_SLLI | RISCV_INS_SLL
| RISCV_INS_SRLI | RISCV_INS_SRL | RISCV_INS_SRAI | RISCV_INS_SRA | RISCV_INS_SLTI
| RISCV_INS_SLT | RISCV_INS_SLTIU | RISCV_INS_SLTU | RISCV_INS_SUB | RISCV_INS_ADDIW
| RISCV_INS_ADDW | RISCV_INS_SLLIW | RISCV_INS_SLLW | RISCV_INS_SRLIW | RISCV_INS_SRLW
| RISCV_INS_SRAIW | RISCV_INS_SRAW | RISCV_INS_SUBW | RISCV_INS_MUL | RISCV_INS_MULH
| RISCV_INS_MULHSU | RISCV_INS_MULHU | RISCV_INS_DIV | RISCV_INS_DIVU | RISCV_INS_REM
| RISCV_INS_REMU => InstructionType::Arithmetic,
RISCV_INS_LB | RISCV_INS_LBU | RISCV_INS_LH | RISCV_INS_LHU | RISCV_INS_LW
| RISCV_INS_LWU | RISCV_INS_LD | RISCV_INS_LUI | RISCV_INS_AUIPC | RISCV_INS_SB
| RISCV_INS_SH | RISCV_INS_SW | RISCV_INS_SD => InstructionType::DataTransfer,
_ => InstructionType::Unknown,
}
}
enum InstructionType {
Unknown,
ControlFlowTransfer,
DataTransfer,
Arithmetic,
Unary,
BinaryCMP,
TaintedCall,
TaintedJMP,
TaintedRet,
TaintedMemStore,
TaintedPc,
}
const ARM_INS_B: InsnId = InsnId(ArmInsn::ARM_INS_B as u32);
const ARM_INS_BL: InsnId = InsnId(ArmInsn::ARM_INS_BL as u32);
const ARM_INS_CBZ: InsnId = InsnId(ArmInsn::ARM_INS_CBZ as u32);
const ARM_INS_CBNZ: InsnId = InsnId(ArmInsn::ARM_INS_CBNZ as u32);
const ARM_INS_BX: InsnId = InsnId(ArmInsn::ARM_INS_BX as u32);
const ARM_INS_BLX: InsnId = InsnId(ArmInsn::ARM_INS_BLX as u32);
const ARM_INS_ADC: InsnId = InsnId(ArmInsn::ARM_INS_ADC as u32);
const ARM_INS_ADD: InsnId = InsnId(ArmInsn::ARM_INS_ADD as u32);
const ARM_INS_ADDW: InsnId = InsnId(ArmInsn::ARM_INS_ADDW as u32);
const ARM_INS_AND: InsnId = InsnId(ArmInsn::ARM_INS_AND as u32);
const ARM_INS_EOR: InsnId = InsnId(ArmInsn::ARM_INS_EOR as u32);
const ARM_INS_LSL: InsnId = InsnId(ArmInsn::ARM_INS_LSL as u32);
const ARM_INS_LSR: InsnId = InsnId(ArmInsn::ARM_INS_LSR as u32);
const ARM_INS_ASR: InsnId = InsnId(ArmInsn::ARM_INS_ASR as u32);
const ARM_INS_ORN: InsnId = InsnId(ArmInsn::ARM_INS_ORN as u32);
const ARM_INS_ROR: InsnId = InsnId(ArmInsn::ARM_INS_ROR as u32);
const ARM_INS_RRX: InsnId = InsnId(ArmInsn::ARM_INS_RRX as u32);
const ARM_INS_SUBW: InsnId = InsnId(ArmInsn::ARM_INS_SUBW as u32);
const ARM_INS_SUB: InsnId = InsnId(ArmInsn::ARM_INS_SUB as u32);
const ARM_INS_RSB: InsnId = InsnId(ArmInsn::ARM_INS_RSB as u32);
const ARM_INS_ORR: InsnId = InsnId(ArmInsn::ARM_INS_ORR as u32);
const ARM_INS_MUL: InsnId = InsnId(ArmInsn::ARM_INS_MUL as u32);
const ARM_INS_SDIV: InsnId = InsnId(ArmInsn::ARM_INS_SDIV as u32);
const ARM_INS_STR: InsnId = InsnId(ArmInsn::ARM_INS_STR as u32);
const ARM_INS_LDR: InsnId = InsnId(ArmInsn::ARM_INS_LDR as u32);
const ARM_INS_MOV: InsnId = InsnId(ArmInsn::ARM_INS_MOV as u32);
const ARM_INS_REV: InsnId = InsnId(ArmInsn::ARM_INS_REV as u32);
const ARM_INS_RBIT: InsnId = InsnId(ArmInsn::ARM_INS_RBIT as u32);
const ARM_INS_CMP: InsnId = InsnId(ArmInsn::ARM_INS_CMP as u32);
const ARM_INS_TST: InsnId = InsnId(ArmInsn::ARM_INS_TST as u32);
const ARM_INS_TEQ: InsnId = InsnId(ArmInsn::ARM_INS_TEQ as u32);
const ARM64_INS_B: InsnId = InsnId(Arm64Insn::ARM64_INS_B as u32);
const ARM64_INS_BL: InsnId = InsnId(Arm64Insn::ARM64_INS_BL as u32);
const ARM64_INS_CBZ: InsnId = InsnId(Arm64Insn::ARM64_INS_CBZ as u32);
const ARM64_INS_CBNZ: InsnId = InsnId(Arm64Insn::ARM64_INS_CBNZ as u32);
const ARM64_INS_BLR: InsnId = InsnId(Arm64Insn::ARM64_INS_BLR as u32);
const ARM64_INS_RET: InsnId = InsnId(Arm64Insn::ARM64_INS_RET as u32);
const ARM64_INS_ADC: InsnId = InsnId(Arm64Insn::ARM64_INS_ADC as u32);
const ARM64_INS_ADD: InsnId = InsnId(Arm64Insn::ARM64_INS_ADD as u32);
const ARM64_INS_AND: InsnId = InsnId(Arm64Insn::ARM64_INS_AND as u32);
const ARM64_INS_BIC: InsnId = InsnId(Arm64Insn::ARM64_INS_BIC as u32);
const ARM64_INS_EOR: InsnId = InsnId(Arm64Insn::ARM64_INS_EOR as u32);
const ARM64_INS_LSL: InsnId = InsnId(Arm64Insn::ARM64_INS_LSL as u32);
const ARM64_INS_LSR: InsnId = InsnId(Arm64Insn::ARM64_INS_LSR as u32);
const ARM64_INS_ASR: InsnId = InsnId(Arm64Insn::ARM64_INS_ASR as u32);
const ARM64_INS_ORN: InsnId = InsnId(Arm64Insn::ARM64_INS_ORN as u32);
const ARM64_INS_ROR: InsnId = InsnId(Arm64Insn::ARM64_INS_ROR as u32);
const ARM64_INS_SUB: InsnId = InsnId(Arm64Insn::ARM64_INS_SUB as u32);
const ARM64_INS_ORR: InsnId = InsnId(Arm64Insn::ARM64_INS_ORR as u32);
const ARM64_INS_MUL: InsnId = InsnId(Arm64Insn::ARM64_INS_MUL as u32);
const ARM64_INS_MVN: InsnId = InsnId(Arm64Insn::ARM64_INS_MVN as u32);
const ARM64_INS_SDIV: InsnId = InsnId(Arm64Insn::ARM64_INS_SDIV as u32);
const ARM64_INS_UDIV: InsnId = InsnId(Arm64Insn::ARM64_INS_UDIV as u32);
const ARM64_INS_STR: InsnId = InsnId(Arm64Insn::ARM64_INS_STR as u32);
const ARM64_INS_STRB: InsnId = InsnId(Arm64Insn::ARM64_INS_STRB as u32);
const ARM64_INS_STRH: InsnId = InsnId(Arm64Insn::ARM64_INS_STRH as u32);
const ARM64_INS_STTR: InsnId = InsnId(Arm64Insn::ARM64_INS_STTR as u32);
const ARM64_INS_STTRB: InsnId = InsnId(Arm64Insn::ARM64_INS_STTRB as u32);
const ARM64_INS_STTRH: InsnId = InsnId(Arm64Insn::ARM64_INS_STTRH as u32);
const ARM64_INS_STP: InsnId = InsnId(Arm64Insn::ARM64_INS_STP as u32);
const ARM64_INS_STUR: InsnId = InsnId(Arm64Insn::ARM64_INS_STUR as u32);
const ARM64_INS_STURB: InsnId = InsnId(Arm64Insn::ARM64_INS_STURB as u32);
const ARM64_INS_STURH: InsnId = InsnId(Arm64Insn::ARM64_INS_STURH as u32);
const ARM64_INS_LDR: InsnId = InsnId(Arm64Insn::ARM64_INS_LDR as u32);
const ARM64_INS_LDRB: InsnId = InsnId(Arm64Insn::ARM64_INS_LDRB as u32);
const ARM64_INS_LDRSB: InsnId = InsnId(Arm64Insn::ARM64_INS_LDRSB as u32);
const ARM64_INS_LDRH: InsnId = InsnId(Arm64Insn::ARM64_INS_LDRH as u32);
const ARM64_INS_LDRSH: InsnId = InsnId(Arm64Insn::ARM64_INS_LDRSH as u32);
const ARM64_INS_LDRSW: InsnId = InsnId(Arm64Insn::ARM64_INS_LDRSW as u32);
const ARM64_INS_LDTR: InsnId = InsnId(Arm64Insn::ARM64_INS_LDTR as u32);
const ARM64_INS_LDTRB: InsnId = InsnId(Arm64Insn::ARM64_INS_LDTRB as u32);
const ARM64_INS_LDTRSB: InsnId = InsnId(Arm64Insn::ARM64_INS_LDTRSB as u32);
const ARM64_INS_LDTRH: InsnId = InsnId(Arm64Insn::ARM64_INS_LDTRH as u32);
const ARM64_INS_LDTRSH: InsnId = InsnId(Arm64Insn::ARM64_INS_LDTRSH as u32);
const ARM64_INS_LDTRSW: InsnId = InsnId(Arm64Insn::ARM64_INS_LDTRSW as u32);
const ARM64_INS_LDUR: InsnId = InsnId(Arm64Insn::ARM64_INS_LDUR as u32);
const ARM64_INS_LDURB: InsnId = InsnId(Arm64Insn::ARM64_INS_LDURB as u32);
const ARM64_INS_LDURH: InsnId = InsnId(Arm64Insn::ARM64_INS_LDURH as u32);
const ARM64_INS_LDURSB: InsnId = InsnId(Arm64Insn::ARM64_INS_LDURSB as u32);
const ARM64_INS_LDURSH: InsnId = InsnId(Arm64Insn::ARM64_INS_LDURSH as u32);
const ARM64_INS_LDURSW: InsnId = InsnId(Arm64Insn::ARM64_INS_LDURSW as u32);
const ARM64_INS_LDP: InsnId = InsnId(Arm64Insn::ARM64_INS_LDP as u32);
const ARM64_INS_MOV: InsnId = InsnId(Arm64Insn::ARM64_INS_MOV as u32);
const ARM64_INS_ADR: InsnId = InsnId(Arm64Insn::ARM64_INS_ADR as u32);
const ARM64_INS_REV: InsnId = InsnId(Arm64Insn::ARM64_INS_REV as u32);
const ARM64_INS_RBIT: InsnId = InsnId(Arm64Insn::ARM64_INS_RBIT as u32);
const ARM64_INS_CMP: InsnId = InsnId(Arm64Insn::ARM64_INS_CMP as u32);
const ARM64_INS_TST: InsnId = InsnId(Arm64Insn::ARM64_INS_TST as u32);
const RISCV_INS_ADDI: InsnId = InsnId(RiscVInsn::RISCV_INS_ADDI as u32);
const RISCV_INS_ADD: InsnId = InsnId(RiscVInsn::RISCV_INS_ADD as u32);
const RISCV_INS_ANDI: InsnId = InsnId(RiscVInsn::RISCV_INS_ANDI as u32);
const RISCV_INS_AND: InsnId = InsnId(RiscVInsn::RISCV_INS_AND as u32);
const RISCV_INS_XORI: InsnId = InsnId(RiscVInsn::RISCV_INS_XORI as u32);
const RISCV_INS_XOR: InsnId = InsnId(RiscVInsn::RISCV_INS_XOR as u32);
const RISCV_INS_ORI: InsnId = InsnId(RiscVInsn::RISCV_INS_ORI as u32);
const RISCV_INS_OR: InsnId = InsnId(RiscVInsn::RISCV_INS_OR as u32);
const RISCV_INS_SLLI: InsnId = InsnId(RiscVInsn::RISCV_INS_SLLI as u32);
const RISCV_INS_SLL: InsnId = InsnId(RiscVInsn::RISCV_INS_SLL as u32);
const RISCV_INS_SRLI: InsnId = InsnId(RiscVInsn::RISCV_INS_SRLI as u32);
const RISCV_INS_SRL: InsnId = InsnId(RiscVInsn::RISCV_INS_SRL as u32);
const RISCV_INS_SRAI: InsnId = InsnId(RiscVInsn::RISCV_INS_SRAI as u32);
const RISCV_INS_SRA: InsnId = InsnId(RiscVInsn::RISCV_INS_SRA as u32);
const RISCV_INS_SLTI: InsnId = InsnId(RiscVInsn::RISCV_INS_SLTI as u32);
const RISCV_INS_SLT: InsnId = InsnId(RiscVInsn::RISCV_INS_SLT as u32);
const RISCV_INS_SLTIU: InsnId = InsnId(RiscVInsn::RISCV_INS_SLTIU as u32);
const RISCV_INS_SLTU: InsnId = InsnId(RiscVInsn::RISCV_INS_SLTU as u32);
const RISCV_INS_SUB: InsnId = InsnId(RiscVInsn::RISCV_INS_SUB as u32);
const RISCV_INS_ADDIW: InsnId = InsnId(RiscVInsn::RISCV_INS_ADDIW as u32);
const RISCV_INS_ADDW: InsnId = InsnId(RiscVInsn::RISCV_INS_ADDW as u32);
const RISCV_INS_SLLIW: InsnId = InsnId(RiscVInsn::RISCV_INS_SLLIW as u32);
const RISCV_INS_SLLW: InsnId = InsnId(RiscVInsn::RISCV_INS_SLLW as u32);
const RISCV_INS_SRLIW: InsnId = InsnId(RiscVInsn::RISCV_INS_SRLIW as u32);
const RISCV_INS_SRLW: InsnId = InsnId(RiscVInsn::RISCV_INS_SRLW as u32);
const RISCV_INS_SRAIW: InsnId = InsnId(RiscVInsn::RISCV_INS_SRAIW as u32);
const RISCV_INS_SRAW: InsnId = InsnId(RiscVInsn::RISCV_INS_SRAW as u32);
const RISCV_INS_SUBW: InsnId = InsnId(RiscVInsn::RISCV_INS_SUBW as u32);
const RISCV_INS_MUL: InsnId = InsnId(RiscVInsn::RISCV_INS_MUL as u32);
const RISCV_INS_MULH: InsnId = InsnId(RiscVInsn::RISCV_INS_MULH as u32);
const RISCV_INS_MULHSU: InsnId = InsnId(RiscVInsn::RISCV_INS_MULHSU as u32);
const RISCV_INS_MULHU: InsnId = InsnId(RiscVInsn::RISCV_INS_MULHU as u32);
const RISCV_INS_DIV: InsnId = InsnId(RiscVInsn::RISCV_INS_DIV as u32);
const RISCV_INS_DIVU: InsnId = InsnId(RiscVInsn::RISCV_INS_DIVU as u32);
const RISCV_INS_REM: InsnId = InsnId(RiscVInsn::RISCV_INS_REM as u32);
const RISCV_INS_REMU: InsnId = InsnId(RiscVInsn::RISCV_INS_REMU as u32);
const RISCV_INS_LB: InsnId = InsnId(RiscVInsn::RISCV_INS_LB as u32);
const RISCV_INS_LBU: InsnId = InsnId(RiscVInsn::RISCV_INS_LBU as u32);
const RISCV_INS_LH: InsnId = InsnId(RiscVInsn::RISCV_INS_LH as u32);
const RISCV_INS_LHU: InsnId = InsnId(RiscVInsn::RISCV_INS_LHU as u32);
const RISCV_INS_LW: InsnId = InsnId(RiscVInsn::RISCV_INS_LW as u32);
const RISCV_INS_LWU: InsnId = InsnId(RiscVInsn::RISCV_INS_LWU as u32);
const RISCV_INS_LD: InsnId = InsnId(RiscVInsn::RISCV_INS_LD as u32);
const RISCV_INS_LUI: InsnId = InsnId(RiscVInsn::RISCV_INS_LUI as u32);
const RISCV_INS_AUIPC: InsnId = InsnId(RiscVInsn::RISCV_INS_AUIPC as u32);
const RISCV_INS_SB: InsnId = InsnId(RiscVInsn::RISCV_INS_SB as u32);
const RISCV_INS_SH: InsnId = InsnId(RiscVInsn::RISCV_INS_SH as u32);
const RISCV_INS_SW: InsnId = InsnId(RiscVInsn::RISCV_INS_SW as u32);
const RISCV_INS_SD: InsnId = InsnId(RiscVInsn::RISCV_INS_SD as u32);
const RISCV_INS_BEQ: InsnId = InsnId(RiscVInsn::RISCV_INS_BEQ as u32);
const RISCV_INS_BNE: InsnId = InsnId(RiscVInsn::RISCV_INS_BNE as u32);
const RISCV_INS_BLT: InsnId = InsnId(RiscVInsn::RISCV_INS_BLT as u32);
const RISCV_INS_BGE: InsnId = InsnId(RiscVInsn::RISCV_INS_BGE as u32);
const RISCV_INS_BLTU: InsnId = InsnId(RiscVInsn::RISCV_INS_BLTU as u32);
const RISCV_INS_BGEU: InsnId = InsnId(RiscVInsn::RISCV_INS_BGEU as u32);
const RISCV_INS_JAL: InsnId = InsnId(RiscVInsn::RISCV_INS_JAL as u32);
const RISCV_INS_JALR: InsnId = InsnId(RiscVInsn::RISCV_INS_JALR as u32);
const X86_INS_CALL: InsnId = InsnId(X86Insn::X86_INS_CALL as u32);
const X86_INS_RET: InsnId = InsnId(X86Insn::X86_INS_RET as u32);
const X86_INS_JMP: InsnId = InsnId(X86Insn::X86_INS_JMP as u32);
const X86_INS_JAE: InsnId = InsnId(X86Insn::X86_INS_JAE as u32);
const X86_INS_JA: InsnId = InsnId(X86Insn::X86_INS_JA as u32);
const X86_INS_JBE: InsnId = InsnId(X86Insn::X86_INS_JBE as u32);
const X86_INS_JB: InsnId = InsnId(X86Insn::X86_INS_JB as u32);
const X86_INS_JCXZ: InsnId = InsnId(X86Insn::X86_INS_JCXZ as u32);
const X86_INS_JECXZ: InsnId = InsnId(X86Insn::X86_INS_JECXZ as u32);
const X86_INS_JE: InsnId = InsnId(X86Insn::X86_INS_JE as u32);
const X86_INS_JGE: InsnId = InsnId(X86Insn::X86_INS_JGE as u32);
const X86_INS_JG: InsnId = InsnId(X86Insn::X86_INS_JG as u32);
const X86_INS_JLE: InsnId = InsnId(X86Insn::X86_INS_JLE as u32);
const X86_INS_JL: InsnId = InsnId(X86Insn::X86_INS_JL as u32);
const X86_INS_JNE: InsnId = InsnId(X86Insn::X86_INS_JNE as u32);
const X86_INS_JNO: InsnId = InsnId(X86Insn::X86_INS_JNO as u32);
const X86_INS_JNP: InsnId = InsnId(X86Insn::X86_INS_JNP as u32);
const X86_INS_JNS: InsnId = InsnId(X86Insn::X86_INS_JNS as u32);
const X86_INS_JO: InsnId = InsnId(X86Insn::X86_INS_JO as u32);
const X86_INS_JP: InsnId = InsnId(X86Insn::X86_INS_JP as u32);
const X86_INS_JRCXZ: InsnId = InsnId(X86Insn::X86_INS_JRCXZ as u32);
const X86_INS_JS: InsnId = InsnId(X86Insn::X86_INS_JS as u32);
const X86_INS_OR: InsnId = InsnId(X86Insn::X86_INS_OR as u32);
const X86_INS_SUB: InsnId = InsnId(X86Insn::X86_INS_SUB as u32);
const X86_INS_XOR: InsnId = InsnId(X86Insn::X86_INS_XOR as u32);
const X86_INS_ROL: InsnId = InsnId(X86Insn::X86_INS_ROL as u32);
const X86_INS_ROR: InsnId = InsnId(X86Insn::X86_INS_ROR as u32);
const X86_INS_SAL: InsnId = InsnId(X86Insn::X86_INS_SAL as u32);
const X86_INS_SAR: InsnId = InsnId(X86Insn::X86_INS_SAR as u32);
const X86_INS_SHL: InsnId = InsnId(X86Insn::X86_INS_SHL as u32);
const X86_INS_SHR: InsnId = InsnId(X86Insn::X86_INS_SHR as u32);
const X86_INS_ADD: InsnId = InsnId(X86Insn::X86_INS_ADD as u32);
const X86_INS_AND: InsnId = InsnId(X86Insn::X86_INS_AND as u32);
const X86_INS_MOV: InsnId = InsnId(X86Insn::X86_INS_MOV as u32);
const X86_INS_XCHG: InsnId = InsnId(X86Insn::X86_INS_XCHG as u32);
const X86_INS_LEA: InsnId = InsnId(X86Insn::X86_INS_LEA as u32);
const X86_INS_MOVZX: InsnId = InsnId(X86Insn::X86_INS_MOVZX as u32);
const X86_INS_MOVSX: InsnId = InsnId(X86Insn::X86_INS_MOVSX as u32);
const X86_INS_NEG: InsnId = InsnId(X86Insn::X86_INS_NEG as u32);
const X86_INS_NOT: InsnId = InsnId(X86Insn::X86_INS_NOT as u32);
const X86_INS_POP: InsnId = InsnId(X86Insn::X86_INS_POP as u32);
const X86_INS_DEC: InsnId = InsnId(X86Insn::X86_INS_DEC as u32);
const X86_INS_INC: InsnId = InsnId(X86Insn::X86_INS_INC as u32);
const X86_INS_TEST: InsnId = InsnId(X86Insn::X86_INS_TEST as u32);
const X86_INS_CMP: InsnId = InsnId(X86Insn::X86_INS_CMP as u32);
#[cfg(test)]
#[cfg(feature = "exploitable")]
mod tests {
use super::*;
use gdb_command::registers::Registers;
use gdb_command::siginfo::Siginfo;
#[test]
fn test_call_av_x86_taint() {
let data: &[u8] = &[0x8b, 0x00, 0x8b, 0x00, 0xff, 0xd0];
let expected_class = ExecutionClass::find("CallAvTainted").unwrap();
let cs = Capstone::new()
.x86()
.mode(arch::x86::ArchMode::Mode32)
.syntax(arch::x86::ArchSyntax::Intel)
.detail(true)
.build();
if let Ok(cs) = cs {
if let Ok(insns) = cs.disasm_all(data, 0) {
if let Ok(result) = check_taint(&cs, &insns) {
assert_eq!(expected_class, result);
} else {
unreachable!();
}
}
}
}
#[test]
fn test_call_av_x64() {
let data: &[u8] = &[0x48, 0x8b, 0x00, 0x48, 0x8b, 0x00, 0xff, 0xd0];
let expected_class = ExecutionClass::find("CallAvTainted").unwrap();
let cs = Capstone::new()
.x86()
.mode(arch::x86::ArchMode::Mode64)
.syntax(arch::x86::ArchSyntax::Intel)
.detail(true)
.build();
if let Ok(cs) = cs {
if let Ok(insns) = cs.disasm_all(data, 0) {
if let Ok(result) = check_taint(&cs, &insns) {
assert_eq!(expected_class, result);
} else {
unreachable!();
}
}
}
}
#[test]
fn test_dest_av_x64() {
let data: &[u8] = &[0x48, 0x8b, 0x00, 0x48, 0x01, 0xc2, 0x48, 0x89, 0x02];
let expected_class = ExecutionClass::find("DestAvTainted").unwrap();
let cs = Capstone::new()
.x86()
.mode(arch::x86::ArchMode::Mode64)
.syntax(arch::x86::ArchSyntax::Intel)
.detail(true)
.build();
if let Ok(cs) = cs {
if let Ok(insns) = cs.disasm_all(data, 0) {
if let Ok(result) = check_taint(&cs, &insns) {
assert_eq!(expected_class, result);
} else {
unreachable!();
}
}
}
}
#[test]
fn test_dest_av_x86_taint() {
let data: &[u8] = &[0x8b, 0x00, 0x01, 0xc2, 0x89, 0x02];
let expected_class = ExecutionClass::find("DestAvTainted").unwrap();
let cs = Capstone::new()
.x86()
.mode(arch::x86::ArchMode::Mode32)
.syntax(arch::x86::ArchSyntax::Intel)
.detail(true)
.build();
if let Ok(cs) = cs {
if let Ok(insns) = cs.disasm_all(data, 0) {
if let Ok(result) = check_taint(&cs, &insns) {
assert_eq!(expected_class, result);
} else {
unreachable!();
}
}
}
}
#[test]
fn test_jmp_untainted_x86() {
let data: &[u8] = &[0x8b, 0x00, 0x01, 0xc2, 0xff, 0xe3];
let cs = Capstone::new()
.x86()
.mode(arch::x86::ArchMode::Mode32)
.syntax(arch::x86::ArchSyntax::Intel)
.detail(true)
.build();
if let Ok(cs) = cs {
if let Ok(insns) = cs.disasm_all(data, 0) {
if check_taint(&cs, &insns).is_ok() {
unreachable!()
}
}
}
}
#[test]
fn test_jump_av_arm() {
let data: &[u8] = &[
0x00, 0x00, 0x90, 0xe5, 0x00, 0x10, 0xa0, 0xe1, 0x01, 0x10, 0x01, 0xe2, 0x00, 0x00,
0x91, 0xe5, 0x30, 0xff, 0x2f, 0xe1,
];
let expected_class = ExecutionClass::find("BranchAvTainted").unwrap();
let cs = Capstone::new()
.arm()
.mode(arch::arm::ArchMode::Arm)
.endian(capstone::Endian::Little)
.detail(true)
.build();
if let Ok(cs) = cs {
if let Ok(insns) = cs.disasm_all(data, 0) {
if let Ok(result) = check_taint(&cs, &insns) {
assert_eq!(expected_class, result);
} else {
unreachable!();
}
}
}
}
#[test]
fn test_dest_av_arm() {
let sig = Siginfo {
si_signo: SIGINFO_SIGSEGV,
si_code: 2,
si_errno: 0,
si_addr: 0xdeadbeaf,
};
let machine = MachineInfo {
byte_width: 4,
endianness: Endian::Little,
arch: header::EM_ARM,
};
let mut registers = Registers::new();
registers.insert("r0".to_string(), 0xdeadbeaf);
registers.insert("pc".to_string(), 0x400000);
registers.insert("cpsr".to_string(), 0x80200000);
let context = GdbContext {
siginfo: sig,
registers,
mappings: MappedFiles::new(),
pc_memory: MemoryObject {
address: 0x400000,
data: vec![
0x00, 0x00, 0x90, 0xe5, 0x00, 0x10, 0xa0, 0xe1, 0x01, 0x10, 0x81, 0xe3, 0x00,
0x00, 0x91, 0xe5, 0x00, 0x10, 0x80, 0xe5,
],
},
machine,
stacktrace: Vec::new(),
};
let expected_class = ExecutionClass::find("DestAvTainted").unwrap();
if let Ok(res) = context.severity() {
assert_eq!(res, expected_class);
} else {
unreachable!();
}
}
#[test]
fn test_bl_arm() {
let data: &[u8] = &[
0x00, 0x00, 0x90, 0xe5, 0x00, 0x10, 0xa0, 0xe1, 0x01, 0x10, 0x61, 0xe2, 0xfb, 0xff,
0xff, 0xeb,
];
let cs = Capstone::new()
.arm()
.mode(arch::arm::ArchMode::Arm)
.endian(capstone::Endian::Little)
.detail(true)
.build();
if let Ok(cs) = cs {
if let Ok(insns) = cs.disasm_all(data, 0) {
if check_taint(&cs, &insns).is_ok() {
unreachable!();
}
}
}
}
#[test]
fn test_blr_arm64() {
let data: &[u8] = &[
0xa0, 0x83, 0x5e, 0xf8, 0x08, 0x00, 0x40, 0xf9, 0x08, 0x01, 0x40, 0xf9, 0x00, 0x01,
0x3f, 0xd6,
];
let expected_class = ExecutionClass::find("CallAvTainted").unwrap();
let cs = Capstone::new()
.arm64()
.mode(arch::arm64::ArchMode::Arm)
.endian(capstone::Endian::Little)
.detail(true)
.build();
if let Ok(cs) = cs {
if let Ok(insns) = cs.disasm_all(data, 0) {
if let Ok(result) = check_taint(&cs, &insns) {
assert_eq!(expected_class, result);
} else {
unreachable!();
}
}
}
}
#[test]
fn test_str_arm64() {
let sig = Siginfo {
si_signo: SIGINFO_SIGSEGV,
si_code: 2,
si_errno: 0,
si_addr: 0xcafecafedeadbeaf,
};
let machine = MachineInfo {
byte_width: 8,
endianness: Endian::Little,
arch: header::EM_AARCH64,
};
let mut registers = Registers::new();
registers.insert("x0".to_string(), 0xcafecafedeadbeaf);
registers.insert("pc".to_string(), 0x400000);
let context = GdbContext {
siginfo: sig,
registers,
mappings: MappedFiles::new(),
pc_memory: MemoryObject {
address: 0x400000,
data: vec![
0x08, 0x00, 0x40, 0xf9, 0x08, 0x01, 0x40, 0xf9, 0x01, 0x01, 0x00, 0xf9,
],
},
machine,
stacktrace: Vec::new(),
};
let expected_class = ExecutionClass::find("DestAvTainted").unwrap();
if let Ok(res) = context.severity() {
assert_eq!(res, expected_class);
} else {
unreachable!();
}
}
#[test]
fn test_ldr_arm64() {
let data: &[u8] = &[
0x08, 0x00, 0x40, 0xf9, 0x08, 0x01, 0x40, 0xf9, 0xc0, 0x03, 0x5f, 0xd6,
];
let cs = Capstone::new()
.arm64()
.mode(arch::arm64::ArchMode::Arm)
.endian(capstone::Endian::Little)
.detail(true)
.build();
if let Ok(cs) = cs {
if let Ok(insns) = cs.disasm_all(data, 0) {
if check_taint(&cs, &insns).is_ok() {
unreachable!();
}
}
}
}
#[test]
fn test_jalr_riscv64() {
let data: &[u8] = &[
0x83, 0xb7, 0x07, 0x00, 0x83, 0xb7, 0x07, 0x00, 0x03, 0x35, 0x04, 0xfd, 0xe7, 0x80,
0x07, 0x00,
];
let expected_class = ExecutionClass::find("CallAvTainted").unwrap();
let cs = Capstone::new()
.riscv()
.mode(arch::riscv::ArchMode::RiscV64)
.detail(true)
.build();
if let Ok(cs) = cs {
if let Ok(insns) = cs.disasm_all(data, 0) {
let result = check_taint(&cs, &insns);
if let Ok(result) = result {
assert_eq!(expected_class, result);
} else {
panic!("{}", result.err().unwrap());
}
}
}
}
#[test]
fn test_sd_riscv64() {
let data: &[u8] = &[
0x83, 0xb7, 0x07, 0x00, 0x83, 0xb7, 0x07, 0x00, 0x03, 0x35, 0x04, 0xfd, 0x23, 0xb0,
0xa7, 0x00,
];
let expected_class = ExecutionClass::find("DestAvTainted").unwrap();
let cs = Capstone::new()
.riscv()
.mode(arch::riscv::ArchMode::RiscV64)
.detail(true)
.build();
if let Ok(cs) = cs {
if let Ok(insns) = cs.disasm_all(data, 0) {
let result = check_taint(&cs, &insns);
if let Ok(result) = result {
assert_eq!(expected_class, result);
} else {
panic!("{}", result.err().unwrap());
}
}
}
}
#[test]
fn test_ld_riscv64() {
let data: &[u8] = &[
0x83, 0xb7, 0x07, 0x00, 0x83, 0xb7, 0x07, 0x00, 0x03, 0x35, 0x04, 0xfd, 0xef, 0x00,
0x00, 0x00,
];
let cs = Capstone::new()
.riscv()
.mode(arch::riscv::ArchMode::RiscV64)
.detail(true)
.build();
if let Ok(cs) = cs {
if let Ok(insns) = cs.disasm_all(data, 0) {
if check_taint(&cs, &insns).is_ok() {
unreachable!();
}
}
}
}
#[test]
fn test_dest_av_x86() {
let cs = Capstone::new()
.x86()
.mode(arch::x86::ArchMode::Mode32)
.syntax(arch::x86::ArchSyntax::Intel)
.detail(true)
.build();
if let Ok(cs) = cs {
let sig = Siginfo {
si_signo: SIGINFO_SIGSEGV,
si_code: 2,
si_errno: 0,
si_addr: 0xdeadbeaf,
};
let machine = MachineInfo {
byte_width: 4,
endianness: Endian::Little,
arch: header::EM_386,
};
let mut registers = Registers::new();
registers.insert("eax".to_string(), 0xdeadbeaf);
let context = GdbContext {
siginfo: sig,
registers,
mappings: MappedFiles::new(),
pc_memory: MemoryObject {
address: 0x0,
data: vec![0x8b, 0x00, 0x01, 0xc2, 0x89, 0x02],
},
machine,
stacktrace: Vec::new(),
};
let data: &[u8] = &[0x8b, 0x00, 0x01, 0xc2, 0x89, 0x02];
let insns = cs.disasm_all(data, 0).unwrap();
let expected_class = ExecutionClass::find("DestAvTainted").unwrap();
if let Ok(res) = GdbContext::analyze_instructions(&cs, &insns, &context) {
assert_eq!(res, expected_class);
} else {
unreachable!();
}
}
}
#[test]
fn test_call_av_x86() {
let sig = Siginfo {
si_signo: SIGINFO_SIGSEGV,
si_code: 2,
si_errno: 0,
si_addr: 0xdeadbeaf,
};
let machine = MachineInfo {
byte_width: 4,
endianness: Endian::Little,
arch: header::EM_386,
};
let mut registers = Registers::new();
registers.insert("eax".to_string(), 0xdeadbeaf);
registers.insert("eip".to_string(), 0x400000);
let context = GdbContext {
siginfo: sig,
registers,
mappings: MappedFiles::new(),
pc_memory: MemoryObject {
address: 0x400000,
data: vec![0x8b, 0x00, 0x8b, 0x00, 0xff, 0xd0],
},
machine,
stacktrace: Vec::new(),
};
let expected_class = ExecutionClass::find("CallAvTainted").unwrap();
if let Ok(res) = context.severity() {
assert_eq!(res, expected_class);
} else {
unreachable!();
}
}
#[test]
fn test_call_av_riscv() {
let machine = MachineInfo {
byte_width: 8,
endianness: Endian::Little,
arch: header::EM_RISCV,
};
let sig = Siginfo {
si_signo: SIGINFO_SIGSEGV,
si_code: 2,
si_errno: 0,
si_addr: 0xcafecafedeadbeaf,
};
let mut registers = Registers::new();
registers.insert("a5".to_string(), 0xcafecafedeadbeaf);
registers.insert("pc".to_string(), 0x400000);
let context = GdbContext {
siginfo: sig,
registers,
mappings: MappedFiles::new(),
pc_memory: MemoryObject {
address: 0x400000,
data: vec![
0x83, 0xb7, 0x07, 0x00, 0x83, 0xb7, 0x07, 0x00, 0x03, 0x35, 0x04, 0xfd, 0xe7,
0x80, 0x07, 0x00,
],
},
machine,
stacktrace: Vec::new(),
};
let expected_class = ExecutionClass::find("CallAvTainted").unwrap();
if let Ok(res) = context.severity() {
assert_eq!(res, expected_class);
} else {
unreachable!();
}
}
}