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"
|
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"
|
||||||
|
@ -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,
|
||||||
);
|
);
|
||||||
|
@ -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();
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user