diff --git a/fuzzers/frida_executable_libpng/Cargo.toml b/fuzzers/frida_executable_libpng/Cargo.toml index e4614b01b3..38844df516 100644 --- a/fuzzers/frida_executable_libpng/Cargo.toml +++ b/fuzzers/frida_executable_libpng/Cargo.toml @@ -29,7 +29,7 @@ reqwest = { version = "0.11.4", features = ["blocking"] } libafl = { path = "../../libafl/", features = [ "std", "llmp_compression", "llmp_bind_public", "frida_cli" ] } #, "llmp_small_maps", "llmp_debug"]} libafl_bolts = { path = "../../libafl_bolts/" } capstone = "0.11.0" -frida-gum = { version = "0.8.1", features = [ "auto-download", "event-sink", "invocation-listener"] } +frida-gum = { version = "0.13.2", features = [ "auto-download", "event-sink", "invocation-listener"] } libafl_frida = { path = "../../libafl_frida", features = ["cmplog"] } libafl_targets = { path = "../../libafl_targets", features = ["sancov_cmplog"] } libc = "0.2" diff --git a/fuzzers/frida_gdiplus/Cargo.toml b/fuzzers/frida_gdiplus/Cargo.toml index b02aeecde4..2153023d19 100644 --- a/fuzzers/frida_gdiplus/Cargo.toml +++ b/fuzzers/frida_gdiplus/Cargo.toml @@ -26,7 +26,7 @@ reqwest = { version = "0.11.4", features = ["blocking"] } [dependencies] libafl = { path = "../../libafl/", features = [ "std", "llmp_compression", "llmp_bind_public", "frida_cli" ] } #, "llmp_small_maps", "llmp_debug"]} libafl_bolts = { path = "../../libafl_bolts/" } -frida-gum = { version = "0.8.1", features = [ "auto-download", "event-sink", "invocation-listener"] } +frida-gum = { version = "0.13.2", features = [ "auto-download", "event-sink", "invocation-listener"] } libafl_frida = { path = "../../libafl_frida", features = ["cmplog"] } libafl_targets = { path = "../../libafl_targets", features = ["sancov_cmplog"] } libloading = "0.7" diff --git a/fuzzers/frida_libpng/Cargo.toml b/fuzzers/frida_libpng/Cargo.toml index 1946d97ee1..3240a5f7ee 100644 --- a/fuzzers/frida_libpng/Cargo.toml +++ b/fuzzers/frida_libpng/Cargo.toml @@ -28,7 +28,7 @@ reqwest = { version = "0.11.4", features = ["blocking"] } [dependencies] libafl = { path = "../../libafl/", features = [ "std", "llmp_compression", "llmp_bind_public", "frida_cli" ] } #, "llmp_small_maps", "llmp_debug"]} libafl_bolts = { path = "../../libafl_bolts/" } -frida-gum = { version = "0.8.1", features = [ "auto-download", "event-sink", "invocation-listener"] } +frida-gum = { version = "0.13.2", features = [ "auto-download", "event-sink", "invocation-listener"] } libafl_frida = { path = "../../libafl_frida", features = ["cmplog"] } libafl_targets = { path = "../../libafl_targets", features = ["sancov_cmplog"] } libloading = "0.7" diff --git a/libafl_frida/Cargo.toml b/libafl_frida/Cargo.toml index 39126508d4..fdd42c4ca5 100644 --- a/libafl_frida/Cargo.toml +++ b/libafl_frida/Cargo.toml @@ -28,8 +28,8 @@ nix = "0.26" libc = "0.2" hashbrown = "0.14" rangemap = "1.3" -frida-gum-sys = { version = "0.4.1", features = [ "auto-download", "event-sink", "invocation-listener"] } -frida-gum = { version = "0.8.1", features = [ "auto-download", "event-sink", "invocation-listener"] } +frida-gum-sys = { version = "0.8.1", features = [ "auto-download", "event-sink", "invocation-listener"] } +frida-gum = { version = "0.13.2", features = [ "auto-download", "event-sink", "invocation-listener", "module-names"] } dynasmrt = "2" capstone = "0.11.0" color-backtrace ={ version = "0.5", features = [ "resolve-modules" ] } diff --git a/libafl_frida/src/asan/asan_rt.rs b/libafl_frida/src/asan/asan_rt.rs index 7f356d71dd..4ebe96cb37 100644 --- a/libafl_frida/src/asan/asan_rt.rs +++ b/libafl_frida/src/asan/asan_rt.rs @@ -16,7 +16,7 @@ use backtrace::Backtrace; #[cfg(target_arch = "x86_64")] use capstone::{ arch::{self, x86::X86OperandType, ArchOperand::X86Operand, BuildsCapstone}, - Capstone, Insn, RegAccessType, RegId, + Capstone, RegAccessType, RegId, }; #[cfg(target_arch = "aarch64")] use capstone::{ @@ -25,7 +25,7 @@ use capstone::{ ArchOperand::Arm64Operand, BuildsCapstone, }, - Capstone, Insn, + Capstone, }; use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi}; #[cfg(target_arch = "x86_64")] @@ -36,6 +36,8 @@ use frida_gum::{ instruction_writer::InstructionWriter, interceptor::Interceptor, stalker::StalkerOutput, Gum, Module, ModuleDetails, ModuleMap, NativePointer, RangeDetails, }; +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] +use frida_gum_sys::Insn; use hashbrown::HashMap; use libafl_bolts::{cli::FuzzerOptions, AsSlice}; #[cfg(unix)] @@ -48,6 +50,8 @@ use libc::{getrlimit64, rlimit64}; use nix::sys::mman::{mmap, MapFlags, ProtFlags}; use rangemap::RangeMap; +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] +use crate::utils::frida_to_cs; #[cfg(target_arch = "aarch64")] use crate::utils::instruction_width; use crate::{ @@ -1094,7 +1098,7 @@ impl AsanRuntime { 3, ) .unwrap(); - let instructions = instructions.iter().collect::>(); + let instructions = instructions.iter().collect::>(); let mut insn = instructions.first().unwrap(); if insn.mnemonic().unwrap() == "msr" && insn.op_str().unwrap() == "nzcv, x0" { insn = instructions.get(2).unwrap(); @@ -2206,9 +2210,13 @@ impl AsanRuntime { Arm64Shift, Arm64Extender, )> { + // We need to re-decode frida-internal capstone values to upstream capstone + let cs_instr = frida_to_cs(capstone, instr); + let cs_instr = cs_instr.first().unwrap(); + // We have to ignore these instructions. Simulating them with their side effects is // complex, to say the least. - match instr.mnemonic().unwrap() { + match cs_instr.mnemonic().unwrap() { "ldaxr" | "stlxr" | "ldxr" | "stxr" | "ldar" | "stlr" | "ldarb" | "ldarh" | "ldaxp" | "ldaxrb" | "ldaxrh" | "stlrb" | "stlrh" | "stlxp" | "stlxrb" | "stlxrh" | "ldxrb" | "ldxrh" | "stxrb" | "stxrh" => return None, @@ -2216,7 +2224,7 @@ impl AsanRuntime { } let operands = capstone - .insn_detail(instr) + .insn_detail(cs_instr) .unwrap() .arch_detail() .operands(); @@ -2230,7 +2238,7 @@ impl AsanRuntime { opmem.base(), opmem.index(), opmem.disp(), - instruction_width(instr, &operands), + instruction_width(cs_instr, &operands), arm64operand.shift, arm64operand.ext, )); @@ -2250,15 +2258,18 @@ impl AsanRuntime { _address: u64, instr: &Insn, ) -> Option<(RegId, u8, RegId, RegId, i32, i64)> { + // We need to re-decode frida-internal capstone values to upstream capstone + let cs_instr = frida_to_cs(capstone, instr); + let cs_instr = cs_instr.first().unwrap(); let operands = capstone - .insn_detail(instr) + .insn_detail(cs_instr) .unwrap() .arch_detail() .operands(); // Ignore lea instruction // put nop into the white-list so that instructions like // like `nop dword [rax + rax]` does not get caught. - match instr.mnemonic().unwrap() { + match cs_instr.mnemonic().unwrap() { "lea" | "nop" => return None, _ => (), @@ -2266,7 +2277,7 @@ impl AsanRuntime { // This is a TODO! In this case, both the src and the dst are mem operand // so we would need to return two operadns? - if instr.mnemonic().unwrap().starts_with("rep") { + if cs_instr.mnemonic().unwrap().starts_with("rep") { return None; } @@ -2317,9 +2328,9 @@ impl AsanRuntime { basereg: RegId, indexreg: RegId, scale: i32, - disp: i64, + disp: isize, ) { - let redzone_size = i64::from(frida_gum_sys::GUM_RED_ZONE_SIZE); + let redzone_size = isize::try_from(frida_gum_sys::GUM_RED_ZONE_SIZE).unwrap(); let writer = output.writer(); let true_rip = address; diff --git a/libafl_frida/src/cmplog_rt.rs b/libafl_frida/src/cmplog_rt.rs index 6db5a82864..49aa9459d7 100644 --- a/libafl_frida/src/cmplog_rt.rs +++ b/libafl_frida/src/cmplog_rt.rs @@ -24,9 +24,11 @@ use frida_gum::{ instruction_writer::{Aarch64Register, IndexMode, InstructionWriter}, stalker::StalkerOutput, }; +#[cfg(target_arch = "aarch64")] +use frida_gum_sys::Insn; #[cfg(all(feature = "cmplog", target_arch = "aarch64"))] -use crate::utils::{instruction_width, writer_register}; +use crate::utils::{frida_to_cs, instruction_width, writer_register}; #[cfg(all(feature = "cmplog", target_arch = "aarch64"))] /// Speciial `CmpLog` Cases for `aarch64` @@ -41,7 +43,7 @@ pub enum SpecialCmpLogCase { #[cfg(target_arch = "aarch64")] use capstone::{ arch::{arm64::Arm64OperandType, ArchOperand::Arm64Operand}, - Capstone, Insn, + Capstone, }; /// The [`frida_gum_sys::GUM_RED_ZONE_SIZE`] casted to [`i32`] @@ -599,14 +601,18 @@ impl CmpLogRuntime { CmplogOperandType, Option, )> { + // We need to re-decode frida-internal capstone values to upstream capstone + let cs_instr = frida_to_cs(capstone, instr); + let cs_instr = cs_instr.first().unwrap(); + // We only care for compare instructions - aka instructions which set the flags - match instr.mnemonic().unwrap() { + match cs_instr.mnemonic().unwrap() { "cmp" | "ands" | "subs" | "adds" | "negs" | "ngcs" | "sbcs" | "bics" | "cbz" | "cbnz" | "tbz" | "tbnz" | "adcs" => (), _ => return None, } let mut operands = capstone - .insn_detail(instr) + .insn_detail(cs_instr) .unwrap() .arch_detail() .operands(); @@ -615,20 +621,21 @@ impl CmpLogRuntime { let special_case = [ "cbz", "cbnz", "tbz", "tbnz", "subs", "adds", "ands", "sbcs", "bics", "adcs", ] - .contains(&instr.mnemonic().unwrap()); + .contains(&cs_instr.mnemonic().unwrap()); if operands.len() != 2 && !special_case { return None; } // handle special opcodes case which have 3 operands, but the 1st(dest) is not important to us - if ["subs", "adds", "ands", "sbcs", "bics", "adcs"].contains(&instr.mnemonic().unwrap()) { + if ["subs", "adds", "ands", "sbcs", "bics", "adcs"].contains(&cs_instr.mnemonic().unwrap()) + { //remove the dest operand from the list operands.remove(0); } // cbz marked as special since there is only 1 operand #[allow(clippy::cast_sign_loss)] - let special_case = matches!(instr.mnemonic().unwrap(), "cbz" | "cbnz"); + let special_case = matches!(cs_instr.mnemonic().unwrap(), "cbz" | "cbnz"); #[allow(clippy::cast_sign_loss, clippy::similar_names)] let operand1 = if let Arm64Operand(arm64operand) = operands.first().unwrap() { @@ -639,7 +646,7 @@ impl CmpLogRuntime { opmem.base(), opmem.index(), opmem.disp(), - instruction_width(instr, &operands), + instruction_width(cs_instr, &operands), )), Arm64OperandType::Cimm(val) => Some(CmplogOperandType::Cimm(val as u64)), _ => return None, @@ -659,7 +666,7 @@ impl CmpLogRuntime { opmem.base(), opmem.index(), opmem.disp(), - instruction_width(instr, &operands), + instruction_width(cs_instr, &operands), )), Arm64OperandType::Cimm(val) => Some(CmplogOperandType::Cimm(val as u64)), _ => return None, @@ -669,7 +676,7 @@ impl CmpLogRuntime { }; // tbz will need to have special handling at emit time(masking operand1 value with operand2) - let special_case = match instr.mnemonic().unwrap() { + let special_case = match cs_instr.mnemonic().unwrap() { "tbz" => Some(SpecialCmpLogCase::Tbz), "tbnz" => Some(SpecialCmpLogCase::Tbnz), _ => None, diff --git a/libafl_frida/src/helper.rs b/libafl_frida/src/helper.rs index 2c2a30c0a8..e16e9bd1b2 100644 --- a/libafl_frida/src/helper.rs +++ b/libafl_frida/src/helper.rs @@ -298,8 +298,14 @@ where if let Some((segment, width, basereg, indexreg, scale, disp)) = res { if let Some(rt) = runtimes.match_first_type_mut::() { rt.emit_shadow_check( - address, &output, segment, width, basereg, indexreg, scale, - disp, + address, + &output, + segment, + width, + basereg, + indexreg, + scale, + disp.try_into().unwrap(), ); } } @@ -325,9 +331,7 @@ where if let Some(rt) = runtimes.match_first_type_mut::() { if let Some((op1, op2, special_case)) = CmpLogRuntime::cmplog_is_interesting_instruction( - &helper.capstone, - address, - instr, + &capstone, address, instr, ) { //emit code that saves the relevant data in runtime(passes it to x0, x1) diff --git a/libafl_frida/src/utils.rs b/libafl_frida/src/utils.rs index 0ecee44c03..592cded95c 100644 --- a/libafl_frida/src/utils.rs +++ b/libafl_frida/src/utils.rs @@ -1,3 +1,5 @@ +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] +use capstone::Capstone; #[cfg(target_arch = "aarch64")] use capstone::{ arch::{self, arm64::Arm64OperandType, ArchOperand::Arm64Operand}, @@ -7,6 +9,8 @@ use capstone::{ use frida_gum::instruction_writer::Aarch64Register; #[cfg(target_arch = "x86_64")] use frida_gum::instruction_writer::X86Register; +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] +use frida_gum_sys; #[cfg(target_arch = "aarch64")] use num_traits::cast::FromPrimitive; @@ -132,3 +136,15 @@ pub fn writer_register(reg: capstone::RegId) -> X86Register { _ => X86Register::None, // Ignore Xax..Xip } } + +/// Translates a frida instruction to a capstone instruction. +/// Returns a [`capstone::Instructions`] with a single [`capstone::Insn`] inside. +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] +pub(crate) fn frida_to_cs<'a>( + capstone: &'a Capstone, + frida_insn: &frida_gum_sys::Insn, +) -> capstone::Instructions<'a> { + capstone + .disasm_count(frida_insn.bytes(), frida_insn.address(), 1) + .unwrap() +}