Frida suppress instrumentation locations option (#87)
* Implement frida option * Format
This commit is contained in:
parent
2ad5e71e9a
commit
099cb0b534
@ -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"
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user