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"
rangemap = "0.1.10"
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
//RegistryBuilder::register::<Tokens>();
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,
);

View File

@ -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<Box<[u8]>>,
stalked_addresses: HashMap<usize, usize>,
options: FridaOptions,
instrumented_ranges: RangeMap<usize, String>,
}
#[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();

View File

@ -64,7 +64,7 @@ pub struct FridaInstrumentationHelper<'a> {
capstone: Capstone,
asan_runtime: Rc<RefCell<AsanRuntime>>,
ranges: RangeMap<usize, (u16, &'a str)>,
options: FridaOptions,
options: &'a FridaOptions,
drcov_basic_blocks: Vec<DrCovBasicBlock>,
}
@ -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 {

View File

@ -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<Vec<(String, usize)>>,
}
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<Vec<(String, usize)>> {
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,
}
}
}