Frida switch from walk-proc-maps to frida-gum based extraction of ranges (#149)

* Bump frida-gum version

* Move from walk of /proc/pid/maps to frida based range/module locatoin
This commit is contained in:
s1341 2021-06-06 11:40:07 +03:00 committed by GitHub
parent 156ed08905
commit 636194de0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 538 additions and 805 deletions

View File

@ -24,7 +24,7 @@ which = "4.1"
libafl = { path = "../../libafl/", features = [ "std", "llmp_bind_public" ] } #, "llmp_small_maps", "llmp_debug"]} libafl = { path = "../../libafl/", features = [ "std", "llmp_bind_public" ] } #, "llmp_small_maps", "llmp_debug"]}
libafl_frida = { path = "../../libafl_frida" } libafl_frida = { path = "../../libafl_frida" }
capstone = "0.8.0" capstone = "0.8.0"
frida-gum = { version = "0.5.1", features = [ "auto-download", "backtrace", "event-sink", "invocation-listener"] } frida-gum = { version = "0.5.2", features = [ "auto-download", "backtrace", "event-sink", "invocation-listener"] }
lazy_static = "1.4.0" lazy_static = "1.4.0"
libc = "0.2" libc = "0.2"
libloading = "0.7.0" libloading = "0.7.0"

View File

@ -242,7 +242,6 @@ pub fn main() {
.value_of("modules_to_instrument") .value_of("modules_to_instrument")
.unwrap() .unwrap()
.split(':') .split(':')
.map(|module_name| std::fs::canonicalize(module_name).unwrap())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
//modules_to_instrument, //modules_to_instrument,
&[PathBuf::from("./corpus")], &[PathBuf::from("./corpus")],
@ -278,7 +277,7 @@ fn fuzz(
unsafe fn fuzz( unsafe fn fuzz(
module_name: &str, module_name: &str,
symbol_name: &str, symbol_name: &str,
modules_to_instrument: &[PathBuf], modules_to_instrument: &[&str],
corpus_dirs: &[PathBuf], corpus_dirs: &[PathBuf],
objective_dir: &Path, objective_dir: &Path,
broker_port: u16, broker_port: u16,

View File

@ -90,75 +90,6 @@ pub fn startable_self() -> Result<Command, Error> {
Ok(startable) Ok(startable)
} }
/// Allows one to walk the mappings in /proc/self/maps, caling a callback function for each
/// mapping.
/// If the callback returns true, we stop the walk.
#[cfg(all(feature = "std", any(target_os = "linux", target_os = "android")))]
pub fn walk_self_maps(visitor: &mut dyn FnMut(usize, usize, String, String) -> bool) {
use regex::Regex;
use std::io::{BufRead, BufReader};
let re = Regex::new(r"^(?P<start>[0-9a-f]{8,16})-(?P<end>[0-9a-f]{8,16}) (?P<perm>[-rwxp]{4}) (?P<offset>[0-9a-f]{8}) [0-9a-f]+:[0-9a-f]+ [0-9]+\s+(?P<path>.*)$")
.unwrap();
let mapsfile = File::open("/proc/self/maps").expect("Unable to open /proc/self/maps");
for line in BufReader::new(mapsfile).lines() {
let line = line.unwrap();
if let Some(caps) = re.captures(&line) {
if visitor(
usize::from_str_radix(caps.name("start").unwrap().as_str(), 16).unwrap(),
usize::from_str_radix(caps.name("end").unwrap().as_str(), 16).unwrap(),
caps.name("perm").unwrap().as_str().to_string(),
caps.name("path").unwrap().as_str().to_string(),
) {
break;
};
}
}
}
/// Get the start and end address, permissions and path of the mapping containing a particular address
#[cfg(all(feature = "std", any(target_os = "linux", target_os = "android")))]
pub fn find_mapping_for_address(address: usize) -> Result<(usize, usize, String, String), Error> {
let mut result = (0, 0, "".to_string(), "".to_string());
walk_self_maps(&mut |start, end, permissions, path| {
if start <= address && address < end {
result = (start, end, permissions, path);
true
} else {
false
}
});
if result.0 == 0 {
Err(Error::Unknown(
"Couldn't find a mapping for this address".to_string(),
))
} else {
Ok(result)
}
}
/// Get the start and end address of the mapping containing with a particular path
#[cfg(all(feature = "std", any(target_os = "linux", target_os = "android")))]
#[must_use]
pub fn find_mapping_for_path(libpath: &str) -> (usize, usize) {
let mut libstart = 0;
let mut libend = 0;
walk_self_maps(&mut |start, end, _permissions, path| {
if libpath == path {
if libstart == 0 {
libstart = start;
}
libend = end;
}
false
});
(libstart, libend)
}
/// "Safe" wrapper around dup2 /// "Safe" wrapper around dup2
#[cfg(all(unix, feature = "std"))] #[cfg(all(unix, feature = "std"))]
pub fn dup2(fd: i32, device: i32) -> Result<(), Error> { pub fn dup2(fd: i32, device: i32) -> Result<(), Error> {

View File

@ -450,21 +450,11 @@ mod unix_signal_handler {
target_arch = "aarch64" target_arch = "aarch64"
))] ))]
{ {
use crate::bolts::os::find_mapping_for_address;
println!("{:━^100}", " CRASH "); println!("{:━^100}", " CRASH ");
println!( println!(
"Received signal {} at 0x{:016x}, fault address: 0x{:016x}", "Received signal {} at 0x{:016x}, fault address: 0x{:016x}",
_signal, _context.uc_mcontext.pc, _context.uc_mcontext.fault_address _signal, _context.uc_mcontext.pc, _context.uc_mcontext.fault_address
); );
if let Ok((start, _, _, path)) =
find_mapping_for_address(_context.uc_mcontext.pc as usize)
{
println!(
"pc is at offset 0x{:08x} in {}",
_context.uc_mcontext.pc as usize - start,
path
);
}
println!("{:━^100}", " REGISTERS "); println!("{:━^100}", " REGISTERS ");
for reg in 0..31 { for reg in 0..31 {

View File

@ -22,7 +22,7 @@ hashbrown = "0.11"
libloading = "0.7.0" libloading = "0.7.0"
rangemap = "0.1.10" rangemap = "0.1.10"
frida-gum-sys = { version = "0.3", features = [ "auto-download", "event-sink", "invocation-listener"] } frida-gum-sys = { version = "0.3", features = [ "auto-download", "event-sink", "invocation-listener"] }
frida-gum = { version = "0.5.1", features = [ "auto-download", "backtrace", "event-sink", "invocation-listener"] } frida-gum = { version = "0.5.2", features = [ "auto-download", "backtrace", "event-sink", "invocation-listener"] }
core_affinity = { version = "0.5", git = "https://github.com/s1341/core_affinity_rs" } core_affinity = { version = "0.5", git = "https://github.com/s1341/core_affinity_rs" }
regex = "1.4" regex = "1.4"
dynasmrt = "1.0.1" dynasmrt = "1.0.1"

View File

@ -1,6 +1,5 @@
use frida_gum::{PageProtection, RangeDetails};
use hashbrown::HashMap; use hashbrown::HashMap;
#[cfg(any(target_os = "linux", target_os = "android"))]
use libafl::bolts::os::walk_self_maps;
use nix::{ use nix::{
libc::memset, libc::memset,
sys::mman::{mmap, MapFlags, ProtFlags}, sys::mman::{mmap, MapFlags, ProtFlags},
@ -220,19 +219,18 @@ impl Allocator {
metadata metadata
} else { } else {
if !ptr.is_null() { if !ptr.is_null() {
AsanErrors::get_mut().report_error( AsanErrors::get_mut()
AsanError::UnallocatedFree((ptr as usize, Backtrace::new())), .report_error(AsanError::UnallocatedFree((ptr as usize, Backtrace::new())));
None,
);
} }
return; return;
}; };
if metadata.freed { if metadata.freed {
AsanErrors::get_mut().report_error( AsanErrors::get_mut().report_error(AsanError::DoubleFree((
AsanError::DoubleFree((ptr as usize, metadata.clone(), Backtrace::new())), ptr as usize,
None, metadata.clone(),
); Backtrace::new(),
)));
} }
let shadow_mapping_start = map_to_shadow!(self, ptr as usize); let shadow_mapping_start = map_to_shadow!(self, ptr as usize);
@ -389,21 +387,23 @@ impl Allocator {
for metadata in self.allocations.values() { for metadata in self.allocations.values() {
if !metadata.freed { if !metadata.freed {
AsanErrors::get_mut() AsanErrors::get_mut()
.report_error(AsanError::Leak((metadata.address, metadata.clone())), None); .report_error(AsanError::Leak((metadata.address, metadata.clone())));
} }
} }
} }
/// Unpoison all the memory that is currently mapped with read/write permissions. /// Unpoison all the memory that is currently mapped with read/write permissions.
pub fn unpoison_all_existing_memory(&mut self) { pub fn unpoison_all_existing_memory(&mut self) {
walk_self_maps(&mut |start, end, permissions, _path| { RangeDetails::enumerate_with_prot(PageProtection::NoAccess, &mut |range: &RangeDetails| {
if permissions.as_bytes()[0] == b'r' || permissions.as_bytes()[1] == b'w' { if range.protection() as u32 & PageProtection::ReadWrite as u32 != 0 {
let start = range.memory_range().base_address().0 as usize;
let end = start + range.memory_range().size();
if self.pre_allocated_shadow && start == 1 << self.shadow_bit { if self.pre_allocated_shadow && start == 1 << self.shadow_bit {
return false; return true;
} }
self.map_shadow_for_region(start, end, true); self.map_shadow_for_region(start, end, true);
} }
false true
}); });
} }
} }

View File

@ -3,9 +3,7 @@ use capstone::{arch::BuildsCapstone, Capstone};
use color_backtrace::{default_output_stream, BacktracePrinter, Verbosity}; use color_backtrace::{default_output_stream, BacktracePrinter, Verbosity};
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
use frida_gum::interceptor::Interceptor; use frida_gum::interceptor::Interceptor;
use frida_gum::ModuleDetails;
#[cfg(any(target_os = "linux", target_os = "android"))]
use libafl::bolts::os::find_mapping_for_address;
use libafl::{ use libafl::{
bolts::{ownedref::OwnedPtr, tuples::Named}, bolts::{ownedref::OwnedPtr, tuples::Named},
@ -18,7 +16,6 @@ use libafl::{
state::HasMetadata, state::HasMetadata,
Error, SerdeAny, Error, SerdeAny,
}; };
use rangemap::RangeMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::io::Write; use std::io::Write;
use termcolor::{Color, ColorSpec, WriteColor}; use termcolor::{Color, ColorSpec, WriteColor};
@ -111,11 +108,7 @@ impl AsanErrors {
/// Report an error /// Report an error
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub(crate) fn report_error( pub(crate) fn report_error(&mut self, error: AsanError) {
&mut self,
error: AsanError,
instrumented_ranges: Option<&RangeMap<usize, String>>,
) {
self.errors.push(error.clone()); self.errors.push(error.clone());
let mut out_stream = default_output_stream(); let mut out_stream = default_output_stream();
@ -144,13 +137,13 @@ impl AsanErrors {
| 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 Some((range, path)) = instrumented_ranges.unwrap().get_key_value(&error.pc) { if let Some(module_details) = ModuleDetails::with_address(error.pc as u64) {
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, module_details.path(),
error.pc - range.start, error.pc - module_details.range().base_address().0 as usize,
fault_address fault_address
) )
.unwrap(); .unwrap();
@ -267,17 +260,17 @@ impl AsanErrors {
{ {
let invocation = Interceptor::current_invocation(); let invocation = Interceptor::current_invocation();
let cpu_context = invocation.cpu_context(); let cpu_context = invocation.cpu_context();
if let Some((range, path)) = instrumented_ranges.unwrap().get_key_value(&pc) { if let Some(module_details) = ModuleDetails::with_address(pc as u64) {
writeln!( writeln!(
output, output,
" at 0x{:x} ({}@0x{:04x})", " at 0x{:x} ({}@0x{:04x})",
pc, pc,
path, module_details.path(),
pc - range.start, pc - module_details.range().base_address().0 as usize,
) )
.unwrap(); .unwrap();
} else { } else {
writeln!(output, " at 0x{:x}", pc,).unwrap(); writeln!(output, " at 0x{:x}", pc).unwrap();
} }
#[allow(clippy::non_ascii_literal)] #[allow(clippy::non_ascii_literal)]
@ -364,13 +357,13 @@ impl AsanErrors {
| AsanError::StackOobWrite((registers, pc, fault, backtrace)) => { | AsanError::StackOobWrite((registers, pc, fault, backtrace)) => {
let (basereg, indexreg, _displacement, fault_address) = fault; let (basereg, indexreg, _displacement, fault_address) = fault;
if let Ok((start, _, _, path)) = find_mapping_for_address(pc) { if let Some(module_details) = ModuleDetails::with_address(pc as u64) {
writeln!( writeln!(
output, output,
" at 0x{:x} ({}:0x{:04x}), faulting address 0x{:x}", " at 0x{:x} ({}:0x{:04x}), faulting address 0x{:x}",
pc, pc,
path, module_details.path(),
pc - start, pc - module_details.range().base_address().0 as usize,
fault_address fault_address
) )
.unwrap(); .unwrap();

File diff suppressed because it is too large Load Diff

View File

@ -3,9 +3,6 @@ use std::hash::Hasher;
use libafl::inputs::{HasTargetBytes, Input}; use libafl::inputs::{HasTargetBytes, Input};
#[cfg(any(target_os = "linux", target_os = "android"))]
use libafl::bolts::os::find_mapping_for_path;
use libafl_targets::drcov::{DrCovBasicBlock, DrCovWriter}; use libafl_targets::drcov::{DrCovBasicBlock, DrCovWriter};
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
@ -26,14 +23,13 @@ use frida_gum::instruction_writer::{Aarch64Register, IndexMode};
use frida_gum::{ use frida_gum::{
instruction_writer::InstructionWriter, instruction_writer::InstructionWriter,
stalker::{StalkerOutput, Transformer}, stalker::{StalkerOutput, Transformer},
CpuContext, CpuContext, ModuleDetails, ModuleMap,
}; };
use frida_gum::{Gum, Module, PageProtection}; use frida_gum::{Gum, Module, PageProtection};
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
use num_traits::cast::FromPrimitive; use num_traits::cast::FromPrimitive;
use rangemap::RangeMap; use rangemap::RangeMap;
use std::path::PathBuf;
use nix::sys::mman::{mmap, MapFlags, ProtFlags}; use nix::sys::mman::{mmap, MapFlags, ProtFlags};
@ -59,7 +55,7 @@ pub trait FridaHelper<'a> {
/// pointer to the frida coverage map /// pointer to the frida coverage map
fn map_ptr(&mut self) -> *mut u8; fn map_ptr(&mut self) -> *mut u8;
fn ranges(&self) -> &RangeMap<usize, (u16, &str)>; fn ranges(&self) -> &RangeMap<usize, (u16, String)>;
} }
/// (Default) map size for frida coverage reporting /// (Default) map size for frida coverage reporting
@ -77,7 +73,8 @@ pub struct FridaInstrumentationHelper<'a> {
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
capstone: Capstone, capstone: Capstone,
asan_runtime: AsanRuntime, asan_runtime: AsanRuntime,
ranges: RangeMap<usize, (u16, &'a str)>, ranges: RangeMap<usize, (u16, String)>,
module_map: ModuleMap,
options: &'a FridaOptions, options: &'a FridaOptions,
drcov_basic_blocks: Vec<DrCovBasicBlock>, drcov_basic_blocks: Vec<DrCovBasicBlock>,
} }
@ -125,7 +122,7 @@ impl<'a> FridaHelper<'a> for FridaInstrumentationHelper<'a> {
self.map.as_mut_ptr() self.map.as_mut_ptr()
} }
fn ranges(&self) -> &RangeMap<usize, (u16, &str)> { fn ranges(&self) -> &RangeMap<usize, (u16, String)> {
&self.ranges &self.ranges
} }
} }
@ -220,7 +217,7 @@ impl<'a> FridaInstrumentationHelper<'a> {
gum: &'a Gum, gum: &'a Gum,
options: &'a FridaOptions, options: &'a FridaOptions,
_harness_module_name: &str, _harness_module_name: &str,
modules_to_instrument: &'a [PathBuf], modules_to_instrument: &'a [&str],
) -> Self { ) -> Self {
// workaround frida's frida-gum-allocate-near bug: // workaround frida's frida-gum-allocate-near bug:
unsafe { unsafe {
@ -262,31 +259,23 @@ impl<'a> FridaInstrumentationHelper<'a> {
.expect("Failed to create Capstone object"), .expect("Failed to create Capstone object"),
asan_runtime: AsanRuntime::new(options.clone()), asan_runtime: AsanRuntime::new(options.clone()),
ranges: RangeMap::new(), ranges: RangeMap::new(),
module_map: ModuleMap::new_from_names(modules_to_instrument),
options, options,
drcov_basic_blocks: vec![], drcov_basic_blocks: vec![],
}; };
if helper.options().stalker_enabled() { if helper.options().stalker_enabled() {
for (id, module_name) in modules_to_instrument.iter().enumerate() { for (i, module) in helper.module_map.values().iter().enumerate() {
let (lib_start, lib_end) = find_mapping_for_path(module_name.to_str().unwrap()); let range = module.range();
println!( let start = range.base_address().0 as usize;
"including range {:x}-{:x} for {:?}", helper
lib_start, lib_end, module_name .ranges
); .insert(start..(start + range.size()), (i as u16, module.path()));
helper.ranges.insert(
lib_start..lib_end,
(id as u16, module_name.to_str().unwrap()),
);
} }
if let Some(suppressed_specifiers) = helper.options().dont_instrument_locations() { if let Some(suppressed_specifiers) = helper.options().dont_instrument_locations() {
for (module_name, offset) in suppressed_specifiers { for (module_name, offset) in suppressed_specifiers {
let (lib_start, _) = find_mapping_for_path( let module_details = ModuleDetails::with_name(module_name).unwrap();
std::fs::canonicalize(&module_name) let lib_start = module_details.range().base_address().0 as usize;
.unwrap()
.to_str()
.unwrap(),
);
println!("removing address: {:#x}", lib_start + offset); println!("removing address: {:#x}", lib_start + offset);
helper helper
.ranges .ranges

View File

@ -18,7 +18,7 @@ pub struct DrCovBasicBlock {
/// A writer for `DrCov` files /// A writer for `DrCov` files
pub struct DrCovWriter<'a> { pub struct DrCovWriter<'a> {
writer: BufWriter<File>, writer: BufWriter<File>,
module_mapping: &'a RangeMap<usize, (u16, &'a str)>, module_mapping: &'a RangeMap<usize, (u16, String)>,
basic_blocks: &'a mut Vec<DrCovBasicBlock>, basic_blocks: &'a mut Vec<DrCovBasicBlock>,
} }
@ -40,7 +40,7 @@ impl<'a> DrCovWriter<'a> {
/// Create a new [`DrCovWriter`] /// Create a new [`DrCovWriter`]
pub fn new( pub fn new(
path: &str, path: &str,
module_mapping: &'a RangeMap<usize, (u16, &str)>, module_mapping: &'a RangeMap<usize, (u16, String)>,
basic_blocks: &'a mut Vec<DrCovBasicBlock>, basic_blocks: &'a mut Vec<DrCovBasicBlock>,
) -> Self { ) -> Self {
Self { Self {
@ -58,7 +58,7 @@ impl<'a> DrCovWriter<'a> {
.write_all(b"DRCOV VERSION: 2\nDRCOV FLAVOR: libafl\n") .write_all(b"DRCOV VERSION: 2\nDRCOV FLAVOR: libafl\n")
.unwrap(); .unwrap();
let modules: Vec<(&std::ops::Range<usize>, &(u16, &str))> = let modules: Vec<(&std::ops::Range<usize>, &(u16, String))> =
self.module_mapping.iter().collect(); self.module_mapping.iter().collect();
self.writer self.writer
.write_all(format!("Module Table: version 2, count {}\n", modules.len()).as_bytes()) .write_all(format!("Module Table: version 2, count {}\n", modules.len()).as_bytes())