Frida suppress instrumentation locations option (#87)

* Implement  frida option

* Format
This commit is contained in:
s1341 2021-05-05 08:54:18 +03:00 committed by Andrea Fioraldi
parent 2ad5e71e9a
commit 099cb0b534
5 changed files with 86 additions and 26 deletions

View File

@ -32,3 +32,7 @@ libloading = "0.7.0"
num-traits = "0.2.14" num-traits = "0.2.14"
rangemap = "0.1.10" rangemap = "0.1.10"
seahash = "4.1.0" seahash = "4.1.0"
serde = "1.0"
backtrace = "0.3"
color-backtrace = "0.5"

View File

@ -184,6 +184,8 @@ pub fn main() {
// Needed only on no_std // Needed only on no_std
//RegistryBuilder::register::<Tokens>(); //RegistryBuilder::register::<Tokens>();
color_backtrace::install();
println!( println!(
"Workdir: {:?}", "Workdir: {:?}",
env::current_dir().unwrap().to_string_lossy().to_string() env::current_dir().unwrap().to_string_lossy().to_string()
@ -255,9 +257,10 @@ unsafe fn fuzz(
ExitKind::Ok ExitKind::Ok
}; };
let frida_options = FridaOptions::parse_env_options();
let mut frida_helper = FridaInstrumentationHelper::new( let mut frida_helper = FridaInstrumentationHelper::new(
&gum, &gum,
FridaOptions::parse_env_options(), &frida_options,
module_name, module_name,
&modules_to_instrument, &modules_to_instrument,
); );

View File

@ -7,7 +7,7 @@ use libafl::{
inputs::{HasTargetBytes, Input}, inputs::{HasTargetBytes, Input},
observers::{Observer, ObserversTuple}, observers::{Observer, ObserversTuple},
state::HasMetadata, state::HasMetadata,
utils::{find_mapping_for_address, walk_self_maps}, utils::{find_mapping_for_address, find_mapping_for_path, walk_self_maps},
Error, SerdeAny, Error, SerdeAny,
}; };
use nix::{ use nix::{
@ -25,6 +25,7 @@ use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
#[cfg(unix)] #[cfg(unix)]
use gothook::GotHookLibrary; use gothook::GotHookLibrary;
use libc::{getrlimit64, rlimit64, sysconf, _SC_PAGESIZE}; use libc::{getrlimit64, rlimit64, sysconf, _SC_PAGESIZE};
use rangemap::RangeMap;
use rangemap::RangeSet; use rangemap::RangeSet;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
@ -610,6 +611,7 @@ pub struct AsanRuntime {
blob_check_mem_64bytes: Option<Box<[u8]>>, blob_check_mem_64bytes: Option<Box<[u8]>>,
stalked_addresses: HashMap<usize, usize>, stalked_addresses: HashMap<usize, usize>,
options: FridaOptions, options: FridaOptions,
instrumented_ranges: RangeMap<usize, String>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -695,6 +697,7 @@ impl AsanRuntime {
blob_check_mem_64bytes: None, blob_check_mem_64bytes: None,
stalked_addresses: HashMap::new(), stalked_addresses: HashMap::new(),
options, options,
instrumented_ranges: RangeMap::new(),
})); }));
Allocator::init(res.clone()); Allocator::init(res.clone());
res res
@ -734,6 +737,9 @@ impl AsanRuntime {
self.generate_instrumentation_blobs(); self.generate_instrumentation_blobs();
self.unpoison_all_existing_memory(); self.unpoison_all_existing_memory();
for module_name in modules_to_instrument { 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)] #[cfg(unix)]
self.hook_library(module_name.to_str().unwrap()); self.hook_library(module_name.to_str().unwrap());
} }
@ -1101,13 +1107,13 @@ impl AsanRuntime {
| AsanError::WriteAfterFree(mut error) => { | AsanError::WriteAfterFree(mut error) => {
let (basereg, indexreg, _displacement, fault_address) = error.fault; 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!( writeln!(
output, output,
" at 0x{:x} ({}:0x{:04x}), faulting address 0x{:x}", " at 0x{:x} ({}@0x{:04x}), faulting address 0x{:x}",
error.pc, error.pc,
path, path,
error.pc - start, error.pc - range.start,
fault_address fault_address
) )
.unwrap(); .unwrap();

View File

@ -64,7 +64,7 @@ pub struct FridaInstrumentationHelper<'a> {
capstone: Capstone, capstone: Capstone,
asan_runtime: Rc<RefCell<AsanRuntime>>, asan_runtime: Rc<RefCell<AsanRuntime>>,
ranges: RangeMap<usize, (u16, &'a str)>, ranges: RangeMap<usize, (u16, &'a str)>,
options: FridaOptions, options: &'a FridaOptions,
drcov_basic_blocks: Vec<DrCovBasicBlock>, drcov_basic_blocks: Vec<DrCovBasicBlock>,
} }
@ -198,7 +198,7 @@ impl<'a> FridaInstrumentationHelper<'a> {
/// Constructor function to create a new FridaInstrumentationHelper, given a module_name. /// Constructor function to create a new FridaInstrumentationHelper, given a module_name.
pub fn new( pub fn new(
gum: &'a Gum, gum: &'a Gum,
options: FridaOptions, options: &'a FridaOptions,
_harness_module_name: &str, _harness_module_name: &str,
modules_to_instrument: &'a [PathBuf], modules_to_instrument: &'a [PathBuf],
) -> Self { ) -> Self {
@ -214,13 +214,13 @@ impl<'a> FridaInstrumentationHelper<'a> {
.detail(true) .detail(true)
.build() .build()
.expect("Failed to create Capstone object"), .expect("Failed to create Capstone object"),
asan_runtime: AsanRuntime::new(options), asan_runtime: AsanRuntime::new(options.clone()),
ranges: RangeMap::new(), ranges: RangeMap::new(),
options, options: options,
drcov_basic_blocks: vec![], drcov_basic_blocks: vec![],
}; };
if options.stalker_enabled() { if helper.options().stalker_enabled() {
for (id, module_name) in modules_to_instrument.iter().enumerate() { for (id, module_name) in modules_to_instrument.iter().enumerate() {
let (lib_start, lib_end) = find_mapping_for_path(module_name.to_str().unwrap()); let (lib_start, lib_end) = find_mapping_for_path(module_name.to_str().unwrap());
println!( 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") std::fs::create_dir_all("./coverage")
.expect("failed to create directory for coverage files"); .expect("failed to create directory for coverage files");
} }
@ -243,15 +258,15 @@ impl<'a> FridaInstrumentationHelper<'a> {
for instruction in basic_block { for instruction in basic_block {
let instr = instruction.instr(); let instr = instruction.instr();
let address = instr.address(); 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 helper.ranges.contains_key(&(address as usize)) {
if first { if first {
first = false; first = false;
//println!("block @ {:x} transformed to {:x}", address, output.writer().pc()); //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); helper.emit_coverage_mapping(address, &output);
} }
if helper.options.drcov_enabled() { if helper.options().drcov_enabled() {
instruction.put_callout(|context| { instruction.put_callout(|context| {
let real_address = match helper let real_address = match helper
.asan_runtime .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"))] #[cfg(not(target_arch = "aarch64"))]
todo!("Implement ASAN for non-aarch64 targets"); todo!("Implement ASAN for non-aarch64 targets");
#[cfg(target_arch = "aarch64")] #[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( helper.asan_runtime.borrow_mut().add_stalked_address(
output.writer().pc() as usize - 4, output.writer().pc() as usize - 4,
address as usize, address as usize,
@ -300,13 +315,17 @@ impl<'a> FridaInstrumentationHelper<'a> {
} }
}); });
helper.transformer = Some(transformer); 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.asan_runtime.borrow_mut().init(modules_to_instrument);
} }
} }
helper helper
} }
#[inline]
fn options(&self) -> &FridaOptions {
&self.options
}
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
#[inline] #[inline]
fn get_writer_register(&self, reg: capstone::RegId) -> Aarch64Register { fn get_writer_register(&self, reg: capstone::RegId) -> Aarch64Register {

View File

@ -2,7 +2,7 @@ pub mod asan_rt;
pub mod helper; pub mod helper;
/// A representation of the various Frida options /// A representation of the various Frida options
#[derive(Clone, Copy, Debug)] #[derive(Clone, Debug)]
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
pub struct FridaOptions { pub struct FridaOptions {
enable_asan: bool, enable_asan: bool,
@ -11,6 +11,7 @@ pub struct FridaOptions {
enable_asan_allocation_backtraces: bool, enable_asan_allocation_backtraces: bool,
enable_coverage: bool, enable_coverage: bool,
enable_drcov: bool, enable_drcov: bool,
instrument_suppress_locations: Option<Vec<(String, usize)>>,
} }
impl FridaOptions { impl FridaOptions {
@ -21,7 +22,7 @@ impl FridaOptions {
let mut options = Self::default(); let mut options = Self::default();
if let Ok(env_options) = std::env::var("LIBAFL_FRIDA_OPTIONS") { 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) = let (name, mut value) =
option.split_at(option.find('=').expect("Expected a '=' in option string")); option.split_at(option.find('=').expect("Expected a '=' in option string"));
value = value.get(1..).unwrap(); value = value.get(1..).unwrap();
@ -43,6 +44,27 @@ impl FridaOptions {
"asan-allocation-backtraces" => { "asan-allocation-backtraces" => {
options.enable_asan_allocation_backtraces = value.parse().unwrap(); 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" => { "coverage" => {
options.enable_coverage = value.parse().unwrap(); options.enable_coverage = value.parse().unwrap();
} }
@ -67,46 +89,51 @@ impl FridaOptions {
/// Is ASAN enabled? /// Is ASAN enabled?
#[inline] #[inline]
pub fn asan_enabled(self) -> bool { pub fn asan_enabled(&self) -> bool {
self.enable_asan self.enable_asan
} }
/// Is coverage enabled? /// Is coverage enabled?
#[inline] #[inline]
pub fn coverage_enabled(self) -> bool { pub fn coverage_enabled(&self) -> bool {
self.enable_coverage self.enable_coverage
} }
/// Is DrCov enabled? /// Is DrCov enabled?
#[inline] #[inline]
pub fn drcov_enabled(self) -> bool { pub fn drcov_enabled(&self) -> bool {
self.enable_drcov self.enable_drcov
} }
/// Should ASAN detect leaks /// Should ASAN detect leaks
#[inline] #[inline]
pub fn asan_detect_leaks(self) -> bool { pub fn asan_detect_leaks(&self) -> bool {
self.enable_asan_leak_detection self.enable_asan_leak_detection
} }
/// Should ASAN continue after a memory error is detected /// Should ASAN continue after a memory error is detected
#[inline] #[inline]
pub fn asan_continue_after_error(self) -> bool { pub fn asan_continue_after_error(&self) -> bool {
self.enable_asan_continue_after_error self.enable_asan_continue_after_error
} }
/// Should ASAN gather (and report) allocation-/free-site backtraces /// Should ASAN gather (and report) allocation-/free-site backtraces
#[inline] #[inline]
pub fn asan_allocation_backtraces(self) -> bool { pub fn asan_allocation_backtraces(&self) -> bool {
self.enable_asan_allocation_backtraces self.enable_asan_allocation_backtraces
} }
/// Whether stalker should be enabled. I.e. whether at least one stalker requiring option is /// Whether stalker should be enabled. I.e. whether at least one stalker requiring option is
/// enabled. /// enabled.
#[inline] #[inline]
pub fn stalker_enabled(self) -> bool { pub fn stalker_enabled(&self) -> bool {
self.enable_asan || self.enable_coverage || self.enable_drcov 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<Vec<(String, usize)>> {
self.instrument_suppress_locations.clone()
}
} }
impl Default for FridaOptions { impl Default for FridaOptions {
@ -118,6 +145,7 @@ impl Default for FridaOptions {
enable_asan_allocation_backtraces: true, enable_asan_allocation_backtraces: true,
enable_coverage: true, enable_coverage: true,
enable_drcov: false, enable_drcov: false,
instrument_suppress_locations: None,
} }
} }
} }