From 099cb0b5343c59cf7a05037d600dc985be0cf5e4 Mon Sep 17 00:00:00 2001 From: s1341 Date: Wed, 5 May 2021 08:54:18 +0300 Subject: [PATCH] Frida suppress instrumentation locations option (#87) * Implement frida option * Format --- fuzzers/frida_libpng/Cargo.toml | 4 +++ fuzzers/frida_libpng/src/fuzzer.rs | 5 +++- libafl_frida/src/asan_rt.rs | 14 ++++++--- libafl_frida/src/helper.rs | 43 ++++++++++++++++++++-------- libafl_frida/src/lib.rs | 46 ++++++++++++++++++++++++------ 5 files changed, 86 insertions(+), 26 deletions(-) diff --git a/fuzzers/frida_libpng/Cargo.toml b/fuzzers/frida_libpng/Cargo.toml index 572a42b0bb..56a6b3f603 100644 --- a/fuzzers/frida_libpng/Cargo.toml +++ b/fuzzers/frida_libpng/Cargo.toml @@ -32,3 +32,7 @@ libloading = "0.7.0" num-traits = "0.2.14" rangemap = "0.1.10" seahash = "4.1.0" +serde = "1.0" + +backtrace = "0.3" +color-backtrace = "0.5" diff --git a/fuzzers/frida_libpng/src/fuzzer.rs b/fuzzers/frida_libpng/src/fuzzer.rs index caeb3e279f..5639dd73d8 100644 --- a/fuzzers/frida_libpng/src/fuzzer.rs +++ b/fuzzers/frida_libpng/src/fuzzer.rs @@ -184,6 +184,8 @@ pub fn main() { // Needed only on no_std //RegistryBuilder::register::(); + color_backtrace::install(); + println!( "Workdir: {:?}", env::current_dir().unwrap().to_string_lossy().to_string() @@ -255,9 +257,10 @@ unsafe fn fuzz( ExitKind::Ok }; + let frida_options = FridaOptions::parse_env_options(); let mut frida_helper = FridaInstrumentationHelper::new( &gum, - FridaOptions::parse_env_options(), + &frida_options, module_name, &modules_to_instrument, ); diff --git a/libafl_frida/src/asan_rt.rs b/libafl_frida/src/asan_rt.rs index 5cc7b034ee..12d957933d 100644 --- a/libafl_frida/src/asan_rt.rs +++ b/libafl_frida/src/asan_rt.rs @@ -7,7 +7,7 @@ use libafl::{ inputs::{HasTargetBytes, Input}, observers::{Observer, ObserversTuple}, state::HasMetadata, - utils::{find_mapping_for_address, walk_self_maps}, + utils::{find_mapping_for_address, find_mapping_for_path, walk_self_maps}, Error, SerdeAny, }; use nix::{ @@ -25,6 +25,7 @@ use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi}; #[cfg(unix)] use gothook::GotHookLibrary; use libc::{getrlimit64, rlimit64, sysconf, _SC_PAGESIZE}; +use rangemap::RangeMap; use rangemap::RangeSet; use serde::{Deserialize, Serialize}; use std::{ @@ -610,6 +611,7 @@ pub struct AsanRuntime { blob_check_mem_64bytes: Option>, stalked_addresses: HashMap, options: FridaOptions, + instrumented_ranges: RangeMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -695,6 +697,7 @@ impl AsanRuntime { blob_check_mem_64bytes: None, stalked_addresses: HashMap::new(), options, + instrumented_ranges: RangeMap::new(), })); Allocator::init(res.clone()); res @@ -734,6 +737,9 @@ impl AsanRuntime { self.generate_instrumentation_blobs(); self.unpoison_all_existing_memory(); for module_name in modules_to_instrument { + let (start, end) = find_mapping_for_path(module_name.to_str().unwrap()); + self.instrumented_ranges + .insert(start..end, module_name.to_str().unwrap().to_string()); #[cfg(unix)] self.hook_library(module_name.to_str().unwrap()); } @@ -1101,13 +1107,13 @@ impl AsanRuntime { | AsanError::WriteAfterFree(mut error) => { let (basereg, indexreg, _displacement, fault_address) = error.fault; - if let Ok((start, _, _, path)) = find_mapping_for_address(error.pc) { + if let Some((range, path)) = self.instrumented_ranges.get_key_value(&error.pc) { writeln!( output, - " at 0x{:x} ({}:0x{:04x}), faulting address 0x{:x}", + " at 0x{:x} ({}@0x{:04x}), faulting address 0x{:x}", error.pc, path, - error.pc - start, + error.pc - range.start, fault_address ) .unwrap(); diff --git a/libafl_frida/src/helper.rs b/libafl_frida/src/helper.rs index fd9a76830e..078c82038f 100644 --- a/libafl_frida/src/helper.rs +++ b/libafl_frida/src/helper.rs @@ -64,7 +64,7 @@ pub struct FridaInstrumentationHelper<'a> { capstone: Capstone, asan_runtime: Rc>, ranges: RangeMap, - options: FridaOptions, + options: &'a FridaOptions, drcov_basic_blocks: Vec, } @@ -198,7 +198,7 @@ impl<'a> FridaInstrumentationHelper<'a> { /// Constructor function to create a new FridaInstrumentationHelper, given a module_name. pub fn new( gum: &'a Gum, - options: FridaOptions, + options: &'a FridaOptions, _harness_module_name: &str, modules_to_instrument: &'a [PathBuf], ) -> Self { @@ -214,13 +214,13 @@ impl<'a> FridaInstrumentationHelper<'a> { .detail(true) .build() .expect("Failed to create Capstone object"), - asan_runtime: AsanRuntime::new(options), + asan_runtime: AsanRuntime::new(options.clone()), ranges: RangeMap::new(), - options, + options: options, drcov_basic_blocks: vec![], }; - if options.stalker_enabled() { + if helper.options().stalker_enabled() { for (id, module_name) in modules_to_instrument.iter().enumerate() { let (lib_start, lib_end) = find_mapping_for_path(module_name.to_str().unwrap()); println!( @@ -233,7 +233,22 @@ impl<'a> FridaInstrumentationHelper<'a> { ); } - if helper.options.drcov_enabled() { + if let Some(suppressed_specifiers) = helper.options().dont_instrument_locations() { + for (module_name, offset) in suppressed_specifiers { + let (lib_start, _) = find_mapping_for_path( + std::fs::canonicalize(&module_name) + .unwrap() + .to_str() + .unwrap(), + ); + println!("removing address: {:#x}", lib_start + offset); + helper + .ranges + .remove((lib_start + offset)..(lib_start + offset + 4)); + } + } + + if helper.options().drcov_enabled() { std::fs::create_dir_all("./coverage") .expect("failed to create directory for coverage files"); } @@ -243,15 +258,15 @@ impl<'a> FridaInstrumentationHelper<'a> { for instruction in basic_block { let instr = instruction.instr(); let address = instr.address(); - //println!("address: {:x} contains: {:?}", address, helper.ranges.contains(&(address as usize))); + //println!("address: {:x} contains: {:?}", address, helper.ranges.contains_key(&(address as usize))); if helper.ranges.contains_key(&(address as usize)) { if first { first = false; //println!("block @ {:x} transformed to {:x}", address, output.writer().pc()); - if helper.options.coverage_enabled() { + if helper.options().coverage_enabled() { helper.emit_coverage_mapping(address, &output); } - if helper.options.drcov_enabled() { + if helper.options().drcov_enabled() { instruction.put_callout(|context| { let real_address = match helper .asan_runtime @@ -270,7 +285,7 @@ impl<'a> FridaInstrumentationHelper<'a> { } } - if helper.options.asan_enabled() { + if helper.options().asan_enabled() { #[cfg(not(target_arch = "aarch64"))] todo!("Implement ASAN for non-aarch64 targets"); #[cfg(target_arch = "aarch64")] @@ -289,7 +304,7 @@ impl<'a> FridaInstrumentationHelper<'a> { ); } } - if helper.options.asan_enabled() || helper.options.drcov_enabled() { + if helper.options().asan_enabled() || helper.options().drcov_enabled() { helper.asan_runtime.borrow_mut().add_stalked_address( output.writer().pc() as usize - 4, address as usize, @@ -300,13 +315,17 @@ impl<'a> FridaInstrumentationHelper<'a> { } }); helper.transformer = Some(transformer); - if helper.options.asan_enabled() || helper.options.drcov_enabled() { + if helper.options().asan_enabled() || helper.options().drcov_enabled() { helper.asan_runtime.borrow_mut().init(modules_to_instrument); } } helper } + #[inline] + fn options(&self) -> &FridaOptions { + &self.options + } #[cfg(target_arch = "aarch64")] #[inline] fn get_writer_register(&self, reg: capstone::RegId) -> Aarch64Register { diff --git a/libafl_frida/src/lib.rs b/libafl_frida/src/lib.rs index 277aaca000..d96fc4aa35 100644 --- a/libafl_frida/src/lib.rs +++ b/libafl_frida/src/lib.rs @@ -2,7 +2,7 @@ pub mod asan_rt; pub mod helper; /// A representation of the various Frida options -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] #[allow(clippy::struct_excessive_bools)] pub struct FridaOptions { enable_asan: bool, @@ -11,6 +11,7 @@ pub struct FridaOptions { enable_asan_allocation_backtraces: bool, enable_coverage: bool, enable_drcov: bool, + instrument_suppress_locations: Option>, } impl FridaOptions { @@ -21,7 +22,7 @@ impl FridaOptions { let mut options = Self::default(); if let Ok(env_options) = std::env::var("LIBAFL_FRIDA_OPTIONS") { - for option in env_options.trim().to_lowercase().split(':') { + for option in env_options.trim().split(':') { let (name, mut value) = option.split_at(option.find('=').expect("Expected a '=' in option string")); value = value.get(1..).unwrap(); @@ -43,6 +44,27 @@ impl FridaOptions { "asan-allocation-backtraces" => { options.enable_asan_allocation_backtraces = value.parse().unwrap(); } + "instrument-suppress-locations" => { + options.instrument_suppress_locations = Some( + value + .split(',') + .map(|val| { + let (module, offset) = val.split_at( + val.find('@') + .expect("Expected an '@' in location specifier"), + ); + ( + module.to_string(), + usize::from_str_radix( + offset.get(1..).unwrap().trim_start_matches("0x"), + 16, + ) + .unwrap(), + ) + }) + .collect(), + ); + } "coverage" => { options.enable_coverage = value.parse().unwrap(); } @@ -67,46 +89,51 @@ impl FridaOptions { /// Is ASAN enabled? #[inline] - pub fn asan_enabled(self) -> bool { + pub fn asan_enabled(&self) -> bool { self.enable_asan } /// Is coverage enabled? #[inline] - pub fn coverage_enabled(self) -> bool { + pub fn coverage_enabled(&self) -> bool { self.enable_coverage } /// Is DrCov enabled? #[inline] - pub fn drcov_enabled(self) -> bool { + pub fn drcov_enabled(&self) -> bool { self.enable_drcov } /// Should ASAN detect leaks #[inline] - pub fn asan_detect_leaks(self) -> bool { + pub fn asan_detect_leaks(&self) -> bool { self.enable_asan_leak_detection } /// Should ASAN continue after a memory error is detected #[inline] - pub fn asan_continue_after_error(self) -> bool { + pub fn asan_continue_after_error(&self) -> bool { self.enable_asan_continue_after_error } /// Should ASAN gather (and report) allocation-/free-site backtraces #[inline] - pub fn asan_allocation_backtraces(self) -> bool { + pub fn asan_allocation_backtraces(&self) -> bool { self.enable_asan_allocation_backtraces } /// Whether stalker should be enabled. I.e. whether at least one stalker requiring option is /// enabled. #[inline] - pub fn stalker_enabled(self) -> bool { + pub fn stalker_enabled(&self) -> bool { self.enable_asan || self.enable_coverage || self.enable_drcov } + + /// A list of locations which will not be instrumented for ASAN or coverage purposes + pub fn dont_instrument_locations(&self) -> Option> { + self.instrument_suppress_locations.clone() + } } impl Default for FridaOptions { @@ -118,6 +145,7 @@ impl Default for FridaOptions { enable_asan_allocation_backtraces: true, enable_coverage: true, enable_drcov: false, + instrument_suppress_locations: None, } } }