Frida updates and FASAN fixes (#2838)
* Frida updates and FASAN fixes * fmt * Fixes * clippy * fmt * Clippy * Update to frida 0.16.2 * fix windows hooks * Fix * Fmt * windows fix * Bump frida version to 0.16.3 * Get rid of call to LLVMFuzzerInitialize * bump version 0.16.5; use find_global_export_by_name * allow unused_macro_rules * Don't do stdout_file on windows * fmt * Add tmate to debug * fix windows frida_libpng --------- Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
This commit is contained in:
parent
ba0da5121b
commit
7c84a7903a
@ -25,7 +25,7 @@ libafl = { path = "../../../libafl", features = [
|
||||
"frida_cli",
|
||||
] } #, "llmp_small_maps", "llmp_debug"]}
|
||||
libafl_bolts = { path = "../../../libafl_bolts" }
|
||||
frida-gum = { version = "0.15.1", features = [
|
||||
frida-gum = { version = "0.16.5", features = [
|
||||
"auto-download",
|
||||
"event-sink",
|
||||
"invocation-listener",
|
||||
|
@ -16,6 +16,7 @@ lto = true
|
||||
codegen-units = 1
|
||||
opt-level = 3
|
||||
debug = true
|
||||
panic = 'abort'
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../../libafl", features = [
|
||||
@ -26,7 +27,7 @@ libafl = { path = "../../../libafl", features = [
|
||||
"errors_backtrace",
|
||||
] } #, "llmp_small_maps", "llmp_debug"]}
|
||||
libafl_bolts = { path = "../../../libafl_bolts" }
|
||||
frida-gum = { version = "0.15.1", features = [
|
||||
frida-gum = { version = "0.16.5", features = [
|
||||
"auto-download",
|
||||
"event-sink",
|
||||
"invocation-listener",
|
||||
@ -37,7 +38,9 @@ libafl_targets = { path = "../../../libafl_targets", features = [
|
||||
"sancov_cmplog",
|
||||
] }
|
||||
libloading = "0.8.5"
|
||||
log = { version = "0.4.22", features = ["release_max_level_info"] }
|
||||
mimalloc = { version = "0.1.43", default-features = false }
|
||||
color-backtrace = "0.6.1"
|
||||
log = { version = "0.4.22", features = ["release_max_level_trace"] }
|
||||
mimalloc = { version = "0.1.43", default-features = true, features = [
|
||||
"local_dynamic_tls",
|
||||
] }
|
||||
color-backtrace = { version = "0.6.1", features = ["resolve-modules"] }
|
||||
env_logger = "0.11.5"
|
||||
|
@ -25,8 +25,6 @@ use libafl::{
|
||||
state::{HasCorpus, StdState},
|
||||
Error, HasMetadata,
|
||||
};
|
||||
#[cfg(unix)]
|
||||
use libafl::{feedback_and_fast, feedbacks::ConstFeedback};
|
||||
use libafl_bolts::{
|
||||
cli::{parse_args, FuzzerOptions},
|
||||
rands::StdRand,
|
||||
@ -56,19 +54,16 @@ pub fn main() {
|
||||
color_backtrace::install();
|
||||
let options = parse_args();
|
||||
|
||||
unsafe {
|
||||
log::info!("Frida fuzzer starting up.");
|
||||
match fuzz(&options) {
|
||||
Ok(()) | Err(Error::ShuttingDown) => println!("\nFinished fuzzing. Good bye."),
|
||||
Err(e) => panic!("Error during fuzzing: {e:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The actual fuzzer
|
||||
#[expect(clippy::too_many_lines)]
|
||||
unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
||||
log::info!("Frida fuzzer starting up.");
|
||||
|
||||
fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
||||
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
|
||||
let monitor = MultiMonitor::new(|s| println!("{s}"));
|
||||
|
||||
@ -81,28 +76,24 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
||||
};
|
||||
|
||||
let mut run_client = |state: Option<_>,
|
||||
mgr: LlmpRestartingEventManager<_, _, _, _, _>,
|
||||
mut mgr: LlmpRestartingEventManager<_, _, _, _, _>,
|
||||
client_description: ClientDescription| {
|
||||
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
||||
|
||||
// println!("{:?}", mgr.mgr_id());
|
||||
|
||||
let lib = libloading::Library::new(options.clone().harness.unwrap()).unwrap();
|
||||
let lib = unsafe { libloading::Library::new(options.clone().harness.unwrap()).unwrap() };
|
||||
let target_func: libloading::Symbol<
|
||||
unsafe extern "C" fn(data: *const u8, size: usize) -> i32,
|
||||
> = lib.get(options.harness_function.as_bytes()).unwrap();
|
||||
> = unsafe { lib.get(options.harness_function.as_bytes()).unwrap() };
|
||||
|
||||
let mut frida_harness = |input: &BytesInput| {
|
||||
let target = input.target_bytes();
|
||||
let buf = target.as_slice();
|
||||
(target_func)(buf.as_ptr(), buf.len());
|
||||
unsafe { (target_func)(buf.as_ptr(), buf.len()) };
|
||||
ExitKind::Ok
|
||||
};
|
||||
|
||||
// if options.asan && options.asan_cores.contains(client_description.core_id()) {
|
||||
(|state: Option<_>,
|
||||
mut mgr: LlmpRestartingEventManager<_, _, _, _, _>,
|
||||
_client_description| {
|
||||
let gum = Gum::obtain();
|
||||
|
||||
let coverage = CoverageRuntime::new();
|
||||
@ -132,11 +123,9 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
||||
);
|
||||
|
||||
// Create an observation channel using the coverage map
|
||||
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr(
|
||||
"edges",
|
||||
frida_helper.map_mut_ptr().unwrap(),
|
||||
MAP_SIZE,
|
||||
))
|
||||
let edges_observer = HitcountsMapObserver::new(unsafe {
|
||||
StdMapObserver::from_mut_ptr("edges", frida_helper.map_mut_ptr().unwrap(), MAP_SIZE)
|
||||
})
|
||||
.track_indices();
|
||||
|
||||
// Create an observation channel to keep track of the execution time
|
||||
@ -153,18 +142,11 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
||||
);
|
||||
|
||||
// Feedbacks to recognize an input as solution
|
||||
#[cfg(unix)]
|
||||
let mut objective = feedback_or_fast!(
|
||||
CrashFeedback::new(),
|
||||
AsanErrorsFeedback::new(&asan_observer),
|
||||
TimeoutFeedback::new(),
|
||||
// true enables the AsanErrorFeedback
|
||||
feedback_and_fast!(
|
||||
ConstFeedback::from(true),
|
||||
AsanErrorsFeedback::new(&asan_observer)
|
||||
)
|
||||
);
|
||||
#[cfg(windows)]
|
||||
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
||||
|
||||
// If not restarting, create a State from scratch
|
||||
let mut state = state.unwrap_or_else(|| {
|
||||
@ -205,20 +187,18 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
||||
#[cfg(unix)]
|
||||
let observers = tuple_list!(edges_observer, time_observer, asan_observer);
|
||||
#[cfg(windows)]
|
||||
let observers = tuple_list!(edges_observer, time_observer);
|
||||
|
||||
// Create the executor for an in-process function with just one observer for edge coverage
|
||||
let mut executor = FridaInProcessExecutor::new(
|
||||
let executor = FridaInProcessExecutor::new(
|
||||
&gum,
|
||||
InProcessExecutor::new(
|
||||
InProcessExecutor::with_timeout(
|
||||
&mut frida_harness,
|
||||
observers,
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
&mut mgr,
|
||||
options.timeout,
|
||||
)?,
|
||||
&mut frida_helper,
|
||||
);
|
||||
@ -230,14 +210,20 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
||||
let tracing = ShadowTracingStage::new(&mut executor);
|
||||
|
||||
// Setup a randomic Input2State stage
|
||||
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(
|
||||
I2SRandReplace::new()
|
||||
)));
|
||||
let i2s =
|
||||
StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new())));
|
||||
|
||||
// In case the corpus is empty (on first run), reset
|
||||
if state.must_load_initial_inputs() {
|
||||
state
|
||||
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &options.input)
|
||||
.load_initial_inputs_multicore(
|
||||
&mut fuzzer,
|
||||
&mut executor,
|
||||
&mut mgr,
|
||||
&options.input,
|
||||
&client_description.core_id(),
|
||||
&options.cores,
|
||||
)
|
||||
.unwrap_or_else(|_| {
|
||||
panic!("Failed to load initial corpus at {:?}", &options.input)
|
||||
});
|
||||
@ -256,18 +242,19 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
||||
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
|
||||
|
||||
Ok(())
|
||||
})(state, mgr, client_description.clone())
|
||||
};
|
||||
|
||||
Launcher::builder()
|
||||
let builder = Launcher::builder()
|
||||
.configuration(EventConfig::AlwaysUnique)
|
||||
.shmem_provider(shmem_provider)
|
||||
.monitor(monitor)
|
||||
.run_client(&mut run_client)
|
||||
.cores(&options.cores)
|
||||
.broker_port(options.broker_port)
|
||||
// .stdout_file(Some(&options.stdout))
|
||||
.remote_broker_addr(options.remote_broker_addr)
|
||||
.build()
|
||||
.launch()
|
||||
.remote_broker_addr(options.remote_broker_addr);
|
||||
|
||||
#[cfg(not(windows))]
|
||||
let builder = builder.stdout_file(Some(&options.stdout));
|
||||
|
||||
builder.build().launch()
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ libafl = { path = "../../../libafl", features = [
|
||||
"errors_backtrace",
|
||||
] } #, "llmp_small_maps", "llmp_debug"]}
|
||||
libafl_bolts = { path = "../../../libafl_bolts" }
|
||||
frida-gum = { version = "0.15.1", features = [
|
||||
frida-gum = { version = "0.16.5", features = [
|
||||
"auto-download",
|
||||
"event-sink",
|
||||
"invocation-listener",
|
||||
|
@ -62,15 +62,16 @@ nix = { workspace = true, default-features = true, features = ["mman"] }
|
||||
libc = { workspace = true }
|
||||
hashbrown = { workspace = true, default-features = true }
|
||||
rangemap = { workspace = true }
|
||||
frida-gum-sys = { version = "0.15.1", features = [
|
||||
frida-gum-sys = { version = "0.16.5", features = [
|
||||
"event-sink",
|
||||
"invocation-listener",
|
||||
] }
|
||||
frida-gum = { version = "0.15.1", features = [
|
||||
frida-gum = { version = "0.16.5", features = [
|
||||
"event-sink",
|
||||
"invocation-listener",
|
||||
"module-names",
|
||||
"script",
|
||||
"backtrace",
|
||||
] }
|
||||
dynasmrt = "3.0.1"
|
||||
|
||||
@ -98,7 +99,6 @@ serial_test = { workspace = true, default-features = false, features = [
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
libloading = "0.8.5"
|
||||
mimalloc = { workspace = true, default-features = false }
|
||||
dlmalloc = { version = "0.2.6", features = ["global"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@ -14,6 +14,7 @@ fn main() {
|
||||
let target_family = std::env::var("CARGO_CFG_TARGET_FAMILY").unwrap();
|
||||
|
||||
// Force linking against libc++
|
||||
#[cfg(not(target_vendor = "apple"))]
|
||||
if target_family == "unix" {
|
||||
println!("cargo:rustc-link-lib=dylib=c++");
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ use std::{collections::BTreeMap, ffi::c_void};
|
||||
|
||||
use backtrace::Backtrace;
|
||||
use frida_gum::{PageProtection, RangeDetails};
|
||||
use hashbrown::HashMap;
|
||||
use libafl_bolts::cli::FuzzerOptions;
|
||||
#[cfg(target_vendor = "apple")]
|
||||
use mach_sys::{
|
||||
@ -59,9 +58,9 @@ pub struct Allocator {
|
||||
/// Whether we've pre allocated a shadow mapping:
|
||||
using_pre_allocated_shadow_mapping: bool,
|
||||
/// All tracked allocations
|
||||
allocations: HashMap<usize, AllocationMetadata>,
|
||||
allocations: BTreeMap<usize, AllocationMetadata>,
|
||||
/// All mappings
|
||||
mappings: HashMap<usize, MmapMut>,
|
||||
mappings: BTreeMap<usize, MmapMut>,
|
||||
/// The shadow memory pages
|
||||
shadow_pages: RangeSet<usize>,
|
||||
/// A list of allocations
|
||||
@ -171,7 +170,6 @@ impl Allocator {
|
||||
#[must_use]
|
||||
#[expect(clippy::missing_safety_doc)]
|
||||
pub unsafe fn alloc(&mut self, size: usize, _alignment: usize) -> *mut c_void {
|
||||
log::trace!("alloc");
|
||||
let mut is_malloc_zero = false;
|
||||
let size = if size == 0 {
|
||||
is_malloc_zero = true;
|
||||
@ -249,7 +247,7 @@ impl Allocator {
|
||||
let address = (metadata.address + self.page_size) as *mut c_void;
|
||||
|
||||
self.allocations.insert(address as usize, metadata);
|
||||
log::trace!(
|
||||
log::info!(
|
||||
"serving address: {:#x}, size: {:#x}",
|
||||
address as usize,
|
||||
size
|
||||
@ -260,21 +258,25 @@ impl Allocator {
|
||||
/// Releases the allocation at the given address.
|
||||
#[expect(clippy::missing_safety_doc)]
|
||||
pub unsafe fn release(&mut self, ptr: *mut c_void) {
|
||||
log::trace!("release {:?}", ptr);
|
||||
log::info!("release {:?}", ptr);
|
||||
let Some(metadata) = self.allocations.get_mut(&(ptr as usize)) else {
|
||||
if !ptr.is_null() {
|
||||
AsanErrors::get_mut_blocking()
|
||||
.report_error(AsanError::UnallocatedFree((ptr as usize, Backtrace::new())));
|
||||
if !ptr.is_null()
|
||||
&& AsanErrors::get_mut_blocking()
|
||||
.report_error(AsanError::UnallocatedFree((ptr as usize, Backtrace::new())))
|
||||
{
|
||||
panic!("ASAN: Crashing target!");
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
if metadata.freed {
|
||||
AsanErrors::get_mut_blocking().report_error(AsanError::DoubleFree((
|
||||
if metadata.freed
|
||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::DoubleFree((
|
||||
ptr as usize,
|
||||
metadata.clone(),
|
||||
Backtrace::new(),
|
||||
)));
|
||||
)))
|
||||
{
|
||||
panic!("ASAN: Crashing target!");
|
||||
}
|
||||
let shadow_mapping_start = map_to_shadow!(self, ptr as usize);
|
||||
|
||||
@ -316,7 +318,7 @@ impl Allocator {
|
||||
/// Resets the allocator contents
|
||||
pub fn reset(&mut self) {
|
||||
let mut tmp_allocations = Vec::new();
|
||||
for (address, mut allocation) in self.allocations.drain() {
|
||||
while let Some((address, mut allocation)) = self.allocations.pop_first() {
|
||||
if !allocation.freed {
|
||||
tmp_allocations.push(allocation);
|
||||
continue;
|
||||
@ -579,9 +581,17 @@ impl Allocator {
|
||||
/// Checks if any of the allocations has not been freed
|
||||
pub fn check_for_leaks(&self) {
|
||||
for metadata in self.allocations.values() {
|
||||
if !metadata.freed {
|
||||
AsanErrors::get_mut_blocking()
|
||||
.report_error(AsanError::Leak((metadata.address, metadata.clone())));
|
||||
if !metadata.freed
|
||||
&& AsanErrors::get_mut_blocking()
|
||||
.report_error(AsanError::Leak((metadata.address, metadata.clone())))
|
||||
{
|
||||
unsafe {
|
||||
println!(
|
||||
"{:x?}",
|
||||
std::slice::from_raw_parts(metadata.address as *const u8, metadata.size)
|
||||
);
|
||||
};
|
||||
panic!("ASAN: Crashing target!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -818,10 +828,10 @@ impl Default for Allocator {
|
||||
page_size,
|
||||
pre_allocated_shadow_mappings: Vec::new(),
|
||||
using_pre_allocated_shadow_mapping: false,
|
||||
mappings: HashMap::new(),
|
||||
mappings: BTreeMap::new(),
|
||||
shadow_offset: 0,
|
||||
shadow_bit: 0,
|
||||
allocations: HashMap::new(),
|
||||
allocations: BTreeMap::new(),
|
||||
shadow_pages: RangeSet::new(),
|
||||
allocation_queue: BTreeMap::new(),
|
||||
largest_allocation: 0,
|
||||
|
@ -23,7 +23,7 @@ use frida_gum::instruction_writer::X86Register;
|
||||
use frida_gum::instruction_writer::{Aarch64Register, IndexMode};
|
||||
use frida_gum::{
|
||||
instruction_writer::InstructionWriter, interceptor::Interceptor, stalker::StalkerOutput, Gum,
|
||||
Module, ModuleDetails, ModuleMap, NativePointer, PageProtection, RangeDetails,
|
||||
Module, ModuleMap, NativePointer, PageProtection, Process, RangeDetails,
|
||||
};
|
||||
use frida_gum_sys::Insn;
|
||||
use hashbrown::HashMap;
|
||||
@ -96,6 +96,25 @@ thread_local! {
|
||||
static ASAN_IN_HOOK: Cell<bool> = const { Cell::new(false) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
unsafe fn thread_local_initted() -> bool {
|
||||
let mut tid: u64;
|
||||
std::arch::asm!(
|
||||
"mrs {tid}, TPIDRRO_EL0",
|
||||
tid = out(reg) tid,
|
||||
);
|
||||
tid &= 0xffff_ffff_ffff_fff8;
|
||||
let tlsptr = tid as *const u64;
|
||||
tlsptr.add(0x102).read() != 0u64
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
unsafe fn thread_local_initted() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// The count of registers that need to be saved by the asan runtime
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub const ASAN_SAVE_REGISTER_COUNT: usize = 32;
|
||||
@ -174,8 +193,8 @@ impl FridaRuntime for AsanRuntime {
|
||||
.extend(self.skip_ranges.iter().map(|skip| match skip {
|
||||
SkipRange::Absolute(range) => range.start,
|
||||
SkipRange::ModuleRelative { name, range } => {
|
||||
let module_details = ModuleDetails::with_name(name.clone()).unwrap();
|
||||
let lib_start = module_details.range().base_address().0 as usize;
|
||||
let module = Module::load(gum, name);
|
||||
let lib_start = module.range().base_address().0 as usize;
|
||||
lib_start + range.start
|
||||
}
|
||||
}));
|
||||
@ -456,35 +475,40 @@ impl AsanRuntime {
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn register_hooks(&mut self, gum: &Gum) {
|
||||
let mut interceptor = Interceptor::obtain(gum);
|
||||
let module = Module::obtain(gum);
|
||||
let process = Process::obtain(gum);
|
||||
macro_rules! hook_func {
|
||||
//No library case
|
||||
($name:ident, ($($param:ident : $param_type:ty),*), $return_type:ty) => {
|
||||
paste::paste! {
|
||||
log::trace!("Hooking {}", stringify!($name));
|
||||
|
||||
let target_function = module.find_export_by_name(None, stringify!($name)).expect("Failed to find function");
|
||||
let target_function = Module::find_global_export_by_name(stringify!($name)).expect("Failed to find function");
|
||||
log::warn!("Hooking {} = {:?}", stringify!($name), target_function.0);
|
||||
|
||||
static [<$name:snake:upper _PTR>]: std::sync::OnceLock<extern "C" fn($($param: $param_type),*) -> $return_type> = std::sync::OnceLock::new();
|
||||
|
||||
let _ = [<$name:snake:upper _PTR>].set(unsafe {std::mem::transmute::<*const c_void, extern "C" fn($($param: $param_type),*) -> $return_type>(target_function.0)}).unwrap();
|
||||
|
||||
#[allow(non_snake_case)] // depends on the values the macro is invoked with
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn [<replacement_ $name>]($($param: $param_type),*) -> $return_type {
|
||||
let mut invocation = Interceptor::current_invocation();
|
||||
let this = &mut *(invocation.replacement_data().unwrap().0 as *mut AsanRuntime);
|
||||
//is this necessary? The stalked return address will always be the real return address
|
||||
// let real_address = this.real_address_for_stalked(invocation.return_addr());
|
||||
let original = [<$name:snake:upper _PTR>].get().unwrap();
|
||||
|
||||
if !ASAN_IN_HOOK.get() && this.hooks_enabled {
|
||||
if this.hooks_enabled {
|
||||
if thread_local_initted() {
|
||||
if !ASAN_IN_HOOK.get() {
|
||||
ASAN_IN_HOOK.set(true);
|
||||
let ret = this.[<hook_ $name>](*original, $($param),*);
|
||||
ASAN_IN_HOOK.set(false);
|
||||
ret
|
||||
} else {
|
||||
let ret = (original)($($param),*);
|
||||
ret
|
||||
(original)($($param),*)
|
||||
}
|
||||
} else {
|
||||
(original)($($param),*)
|
||||
}
|
||||
} else {
|
||||
(original)($($param),*)
|
||||
}
|
||||
}
|
||||
|
||||
@ -501,9 +525,10 @@ impl AsanRuntime {
|
||||
//Library specific macro rule. lib and lib_ident are both needed because we need to generate a unique static variable and only name is insufficient. In addition, the lib name could contain invalid characters (i.e., lib.so is an invalid name)
|
||||
($lib:literal, $lib_ident:ident, $name:ident, ($($param:ident : $param_type:ty),*), $return_type:ty) => {
|
||||
paste::paste! {
|
||||
log::trace!("Hooking {}:{}", $lib, stringify!($name));
|
||||
|
||||
let target_function = module.find_export_by_name(Some($lib), stringify!($name)).expect("Failed to find function");
|
||||
log::warn!("Hooking {}:{}", $lib, stringify!($name));
|
||||
let target_function = process.find_module_by_name($lib).expect("Failed to find module").find_export_by_name(stringify!($name)).expect("Failed to find function");
|
||||
log::warn!("Hooking {}:{} = {:?}", $lib, stringify!($name), target_function.0);
|
||||
|
||||
static [<$lib_ident:snake:upper _ $name:snake:upper _PTR>]: std::sync::OnceLock<extern "C" fn($($param: $param_type),*) -> $return_type> = std::sync::OnceLock::new();
|
||||
|
||||
@ -516,14 +541,21 @@ impl AsanRuntime {
|
||||
//is this necessary? The stalked return address will always be the real return address
|
||||
// let real_address = this.real_address_for_stalked(invocation.return_addr());
|
||||
let original = [<$lib_ident:snake:upper _ $name:snake:upper _PTR>].get().unwrap();
|
||||
if !ASAN_IN_HOOK.get() && this.hooks_enabled {
|
||||
if this.hooks_enabled {
|
||||
if thread_local_initted() {
|
||||
if !ASAN_IN_HOOK.get() {
|
||||
ASAN_IN_HOOK.set(true);
|
||||
let ret = this.[<hook_ $name>](*original, $($param),*);
|
||||
ASAN_IN_HOOK.set(false);
|
||||
ret
|
||||
} else {
|
||||
let ret = (original)($($param),*);
|
||||
ret
|
||||
(original)($($param),*)
|
||||
}
|
||||
} else {
|
||||
(original)($($param),*)
|
||||
}
|
||||
} else {
|
||||
(original)($($param),*)
|
||||
}
|
||||
}
|
||||
|
||||
@ -539,36 +571,49 @@ impl AsanRuntime {
|
||||
};
|
||||
}
|
||||
|
||||
#[expect(unused_macro_rules)]
|
||||
#[allow(unused_macro_rules)]
|
||||
macro_rules! hook_func_with_check {
|
||||
//No library case
|
||||
($name:ident, ($($param:ident : $param_type:ty),*), $return_type:ty) => {
|
||||
($name:ident, ($($param:ident : $param_type:ty),*), $return_type:ty, $always_enabled:expr) => {
|
||||
paste::paste! {
|
||||
log::trace!("Hooking {}", stringify!($name));
|
||||
let target_function = module.find_export_by_name(None, stringify!($name)).expect("Failed to find function");
|
||||
let target_function = Module::find_global_export_by_name(stringify!($name)).expect("Failed to find function");
|
||||
|
||||
log::warn!("Hooking {} = {:?}", stringify!($name), target_function.0);
|
||||
static [<$name:snake:upper _PTR>]: std::sync::OnceLock<extern "C" fn($($param: $param_type),*) -> $return_type> = std::sync::OnceLock::new();
|
||||
|
||||
|
||||
|
||||
let _ = [<$name:snake:upper _PTR>].set(unsafe {std::mem::transmute::<*const c_void, extern "C" fn($($param: $param_type),*) -> $return_type>(target_function.0)}).unwrap_or_else(|e| println!("{:?}", e));
|
||||
|
||||
#[allow(non_snake_case)] // depends on the values the macro is invoked with
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn [<replacement_ $name>]($($param: $param_type),*) -> $return_type {
|
||||
let mut invocation = Interceptor::current_invocation();
|
||||
let this = &mut *(invocation.replacement_data().unwrap().0 as *mut AsanRuntime);
|
||||
let original = [<$name:snake:upper _PTR>].get().unwrap();
|
||||
//don't check if hooks are enabled as there are certain cases where we want to run the hook even if we are out of the program
|
||||
//For example, sometimes libafl will allocate certain things during the run and free them after the run. This results in a bug where a buffer will come from libafl-frida alloc and be freed in the normal allocator.
|
||||
if !ASAN_IN_HOOK.get() && this.[<hook_check_ $name>]($($param),*){
|
||||
if $always_enabled || this.hooks_enabled {
|
||||
if thread_local_initted() {
|
||||
if !ASAN_IN_HOOK.get() {
|
||||
ASAN_IN_HOOK.set(true);
|
||||
let ret = this.[<hook_ $name>](*original, $($param),*);
|
||||
let ret = if this.[<hook_check_ $name>]($($param),*) {
|
||||
this.[<hook_ $name>](*original, $($param),*)
|
||||
} else {
|
||||
(original)($($param),*)
|
||||
};
|
||||
ASAN_IN_HOOK.set(false);
|
||||
ret
|
||||
} else {
|
||||
let ret = (original)($($param),*);
|
||||
(original)($($param),*)
|
||||
}
|
||||
} else {
|
||||
let ret = if $always_enabled && this.[<hook_check_ $name>]($($param),*) {
|
||||
this.[<hook_ $name>](*original, $($param),*)
|
||||
} else {
|
||||
(original)($($param),*)
|
||||
};
|
||||
ret
|
||||
}
|
||||
} else {
|
||||
(original)($($param),*)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -582,11 +627,11 @@ impl AsanRuntime {
|
||||
}
|
||||
};
|
||||
//Library specific macro rule. lib and lib_ident are both needed because we need to generate a unique static variable and only name is insufficient. In addition, the lib name could contain invalid characters (i.e., lib.so is an invalid name)
|
||||
($lib:literal, $lib_ident:ident, $name:ident, ($($param:ident : $param_type:ty),*), $return_type:ty) => {
|
||||
($lib:literal, $lib_ident:ident, $name:ident, ($($param:ident : $param_type:ty),*), $return_type:ty, $always_enabled:expr) => {
|
||||
paste::paste! {
|
||||
log::trace!("Hooking {}:{}", $lib, stringify!($name));
|
||||
let target_function = module.find_export_by_name(Some($lib), stringify!($name)).expect("Failed to find function");
|
||||
let target_function = process.find_module_by_name($lib).expect("Failed to find module").find_export_by_name(stringify!($name)).expect("Failed to find function");
|
||||
|
||||
log::warn!("Hooking {}:{} = {:?}", $lib, stringify!($name), target_function.0);
|
||||
static [<$lib_ident:snake:upper _ $name:snake:upper _PTR>]: std::sync::OnceLock<extern "C" fn($($param: $param_type),*) -> $return_type> = std::sync::OnceLock::new();
|
||||
|
||||
|
||||
@ -600,15 +645,31 @@ impl AsanRuntime {
|
||||
let original = [<$lib_ident:snake:upper _ $name:snake:upper _PTR>].get().unwrap();
|
||||
//don't check if hooks are enabled as there are certain cases where we want to run the hook even if we are out of the program
|
||||
//For example, sometimes libafl will allocate certain things during the run and free them after the run. This results in a bug where a buffer will come from libafl-frida alloc and be freed in the normal allocator.
|
||||
if !ASAN_IN_HOOK.get() && this.[<hook_check_ $name>]($($param),*){
|
||||
if $always_enabled || this.hooks_enabled {
|
||||
if thread_local_initted() {
|
||||
if !ASAN_IN_HOOK.get() {
|
||||
ASAN_IN_HOOK.set(true);
|
||||
let ret = this.[<hook_ $name>](*original, $($param),*);
|
||||
let ret = if this.[<hook_check_ $name>]($($param),*) {
|
||||
this.[<hook_ $name>](*original, $($param),*)
|
||||
} else {
|
||||
(original)($($param),*)
|
||||
};
|
||||
ASAN_IN_HOOK.set(false);
|
||||
ret
|
||||
} else {
|
||||
let ret = (original)($($param),*);
|
||||
(original)($($param),*)
|
||||
}
|
||||
} else {
|
||||
let ret = if $always_enabled && this.[<hook_check_ $name>]($($param),*) {
|
||||
this.[<hook_ $name>](*original, $($param),*)
|
||||
} else {
|
||||
(original)($($param),*)
|
||||
};
|
||||
ret
|
||||
}
|
||||
} else {
|
||||
(original)($($param),*)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -629,9 +690,9 @@ impl AsanRuntime {
|
||||
#[cfg(not(windows))]
|
||||
hook_func!(calloc, (nmemb: usize, size: usize), *mut c_void);
|
||||
#[cfg(not(windows))]
|
||||
hook_func!(realloc, (ptr: *mut c_void, size: usize), *mut c_void);
|
||||
hook_func_with_check!(realloc, (ptr: *mut c_void, size: usize), *mut c_void, false);
|
||||
#[cfg(not(windows))]
|
||||
hook_func_with_check!(free, (ptr: *mut c_void), usize);
|
||||
hook_func_with_check!(free, (ptr: *mut c_void), usize, true);
|
||||
#[cfg(not(any(target_vendor = "apple", windows)))]
|
||||
hook_func!(memalign, (size: usize, alignment: usize), *mut c_void);
|
||||
#[cfg(not(windows))]
|
||||
@ -642,6 +703,28 @@ impl AsanRuntime {
|
||||
);
|
||||
#[cfg(not(any(target_vendor = "apple", windows)))]
|
||||
hook_func!(malloc_usable_size, (ptr: *mut c_void), usize);
|
||||
#[cfg(target_vendor = "apple")]
|
||||
hook_func!(valloc, (size: usize), *mut c_void);
|
||||
#[cfg(target_vendor = "apple")]
|
||||
hook_func_with_check!(reallocf, (ptr: *mut c_void, size: usize), *mut c_void, false);
|
||||
#[cfg(target_vendor = "apple")]
|
||||
hook_func_with_check!(malloc_size, (ptr: *mut c_void), usize, false);
|
||||
#[cfg(target_vendor = "apple")]
|
||||
hook_func_with_check!(malloc_good_size, (ptr: *mut c_void), usize, false);
|
||||
#[cfg(target_vendor = "apple")]
|
||||
hook_func!("libSystem.B.dylib", libSystemB, os_log_type_enabled, (oslog: *mut c_void, r#type: u8), bool);
|
||||
#[cfg(target_vendor = "apple")]
|
||||
hook_func!("libSystem.B.dylib", libSystemB, _os_log_impl, (dso: *const c_void, log: *mut c_void, r#type: u8, format: *const c_char, buf: *const u8, size: u32), ());
|
||||
#[cfg(target_vendor = "apple")]
|
||||
hook_func!("libSystem.B.dylib", libSystemB, _os_log_fault_impl, (dso: *const c_void, log: *mut c_void, r#type: u8, format: *const c_char, buf: *const u8, size: u32), ());
|
||||
#[cfg(target_vendor = "apple")]
|
||||
hook_func!("libSystem.B.dylib", libSystemB, _os_log_error_impl, (dso: *const c_void, log: *mut c_void, r#type: u8, format: *const c_char, buf: *const u8, size: u32), ());
|
||||
#[cfg(target_vendor = "apple")]
|
||||
hook_func!("libSystem.B.dylib", libSystemB, _os_log_debug_impl, (dso: *const c_void, log: *mut c_void, r#type: u8, format: *const c_char, buf: *const u8, size: u32), ());
|
||||
#[cfg(target_vendor = "apple")]
|
||||
hook_func!("libc++.1.dylib", libcpp, __cxa_allocate_exception, (size: usize), *const c_void);
|
||||
#[cfg(target_vendor = "apple")]
|
||||
hook_func!("libc++.1.dylib", libcpp, __cxa_free_exception, (ptr: *mut c_void), usize);
|
||||
// // #[cfg(windows)]
|
||||
// hook_priv_func!(
|
||||
// "c:\\windows\\system32\\ntdll.dll",
|
||||
@ -674,7 +757,8 @@ impl AsanRuntime {
|
||||
macro_rules! hook_heap_windows {
|
||||
($libname:literal, $lib_ident:ident) => {
|
||||
log::info!("Hooking allocator functions in {}", $libname);
|
||||
for export in module.enumerate_exports($libname) {
|
||||
if let Some(module) = process.find_module_by_name($libname) {
|
||||
for export in module.enumerate_exports() {
|
||||
// log::trace!("- {}", export.name);
|
||||
match &export.name[..] {
|
||||
"NtGdiCreateCompatibleDC" => {
|
||||
@ -693,16 +777,16 @@ impl AsanRuntime {
|
||||
hook_func!($libname, $lib_ident, RtlAllocateHeap, (handle: *mut c_void, flags: u32, bytes: usize), *mut c_void);
|
||||
}
|
||||
"HeapFree" => {
|
||||
hook_func_with_check!($libname, $lib_ident, HeapFree, (handle: *mut c_void, flags: u32, mem: *mut c_void), bool);
|
||||
hook_func_with_check!($libname, $lib_ident, HeapFree, (handle: *mut c_void, flags: u32, mem: *mut c_void), bool, true);
|
||||
}
|
||||
"RtlFreeHeap" => {
|
||||
hook_func_with_check!($libname, $lib_ident, RtlFreeHeap, (handle: *mut c_void, flags: u32, mem: *mut c_void), usize);
|
||||
hook_func_with_check!($libname, $lib_ident, RtlFreeHeap, (handle: *mut c_void, flags: u32, mem: *mut c_void), usize, true);
|
||||
}
|
||||
"HeapSize" => {
|
||||
hook_func_with_check!($libname, $lib_ident, HeapSize, (handle: *mut c_void, flags: u32, mem: *mut c_void), usize);
|
||||
hook_func_with_check!($libname, $lib_ident, HeapSize, (handle: *mut c_void, flags: u32, mem: *mut c_void), usize, false);
|
||||
}
|
||||
"RtlSizeHeap" => {
|
||||
hook_func_with_check!($libname, $lib_ident, RtlSizeHeap , (handle: *mut c_void, flags: u32, mem: *mut c_void), usize);
|
||||
hook_func_with_check!($libname, $lib_ident, RtlSizeHeap , (handle: *mut c_void, flags: u32, mem: *mut c_void), usize, false);
|
||||
}
|
||||
"RtlReAllocateHeap" => {
|
||||
hook_func!(
|
||||
@ -737,22 +821,22 @@ impl AsanRuntime {
|
||||
hook_func!($libname, $lib_ident, LocalReAlloc, (mem: *mut c_void, size: usize, flags: u32), *mut c_void);
|
||||
}
|
||||
"LocalHandle" => {
|
||||
hook_func_with_check!($libname, $lib_ident, LocalHandle, (mem: *mut c_void), *mut c_void);
|
||||
hook_func_with_check!($libname, $lib_ident, LocalHandle, (mem: *mut c_void), *mut c_void, false);
|
||||
}
|
||||
"LocalLock" => {
|
||||
hook_func_with_check!($libname, $lib_ident, LocalLock, (mem: *mut c_void), *mut c_void);
|
||||
hook_func_with_check!($libname, $lib_ident, LocalLock, (mem: *mut c_void), *mut c_void, false);
|
||||
}
|
||||
"LocalUnlock" => {
|
||||
hook_func_with_check!($libname, $lib_ident, LocalUnlock, (mem: *mut c_void), bool);
|
||||
hook_func_with_check!($libname, $lib_ident, LocalUnlock, (mem: *mut c_void), bool, false);
|
||||
}
|
||||
"LocalSize" => {
|
||||
hook_func_with_check!($libname, $lib_ident, LocalSize, (mem: *mut c_void),usize);
|
||||
hook_func_with_check!($libname, $lib_ident, LocalSize, (mem: *mut c_void),usize, false);
|
||||
}
|
||||
"LocalFree" => {
|
||||
hook_func_with_check!($libname, $lib_ident, LocalFree, (mem: *mut c_void), *mut c_void);
|
||||
hook_func_with_check!($libname, $lib_ident, LocalFree, (mem: *mut c_void), *mut c_void, true);
|
||||
}
|
||||
"LocalFlags" => {
|
||||
hook_func_with_check!($libname, $lib_ident, LocalFlags, (mem: *mut c_void),u32);
|
||||
hook_func_with_check!($libname, $lib_ident, LocalFlags, (mem: *mut c_void),u32, false);
|
||||
}
|
||||
"GlobalAlloc" => {
|
||||
hook_func!($libname, $lib_ident, GlobalAlloc, (flags: u32, size: usize), *mut c_void);
|
||||
@ -761,22 +845,22 @@ impl AsanRuntime {
|
||||
hook_func!($libname, $lib_ident, GlobalReAlloc, (mem: *mut c_void, flags: u32, size: usize), *mut c_void);
|
||||
}
|
||||
"GlobalHandle" => {
|
||||
hook_func_with_check!($libname, $lib_ident, GlobalHandle, (mem: *mut c_void), *mut c_void);
|
||||
hook_func_with_check!($libname, $lib_ident, GlobalHandle, (mem: *mut c_void), *mut c_void, false);
|
||||
}
|
||||
"GlobalLock" => {
|
||||
hook_func_with_check!($libname, $lib_ident, GlobalLock, (mem: *mut c_void), *mut c_void);
|
||||
hook_func_with_check!($libname, $lib_ident, GlobalLock, (mem: *mut c_void), *mut c_void, false);
|
||||
}
|
||||
"GlobalUnlock" => {
|
||||
hook_func_with_check!($libname, $lib_ident, GlobalUnlock, (mem: *mut c_void), bool);
|
||||
hook_func_with_check!($libname, $lib_ident, GlobalUnlock, (mem: *mut c_void), bool, false);
|
||||
}
|
||||
"GlobalSize" => {
|
||||
hook_func_with_check!($libname, $lib_ident, GlobalSize, (mem: *mut c_void),usize);
|
||||
hook_func_with_check!($libname, $lib_ident, GlobalSize, (mem: *mut c_void),usize, false);
|
||||
}
|
||||
"GlobalFree" => {
|
||||
hook_func_with_check!($libname, $lib_ident, GlobalFree, (mem: *mut c_void), *mut c_void);
|
||||
hook_func_with_check!($libname, $lib_ident, GlobalFree, (mem: *mut c_void), *mut c_void, true);
|
||||
}
|
||||
"GlobalFlags" => {
|
||||
hook_func_with_check!($libname, $lib_ident, GlobalFlags, (mem: *mut c_void),u32);
|
||||
hook_func_with_check!($libname, $lib_ident, GlobalFlags, (mem: *mut c_void),u32, false);
|
||||
}
|
||||
"memmove" => {
|
||||
hook_func!(
|
||||
@ -813,10 +897,10 @@ impl AsanRuntime {
|
||||
hook_func!($libname, $lib_ident, _o_realloc, (ptr: *mut c_void, size: usize), *mut c_void);
|
||||
}
|
||||
"free" => {
|
||||
hook_func_with_check!($libname, $lib_ident, free, (ptr: *mut c_void), usize);
|
||||
hook_func_with_check!($libname, $lib_ident, free, (ptr: *mut c_void), usize, true);
|
||||
}
|
||||
"_o_free" | "o_free" => {
|
||||
hook_func_with_check!($libname, $lib_ident, _o_free, (ptr: *mut c_void), usize);
|
||||
hook_func_with_check!($libname, $lib_ident, _o_free, (ptr: *mut c_void), usize, true);
|
||||
}
|
||||
"_write" => {
|
||||
hook_func!(
|
||||
@ -860,7 +944,7 @@ impl AsanRuntime {
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
@ -900,7 +984,8 @@ impl AsanRuntime {
|
||||
macro_rules! hook_cpp {
|
||||
($libname:literal, $lib_ident:ident) => {
|
||||
log::info!("Hooking c++ functions in {}", $libname);
|
||||
for export in module.enumerate_exports($libname) {
|
||||
if let Some(module) = process.find_module_by_name($libname) {
|
||||
for export in module.enumerate_exports() {
|
||||
match &export.name[..] {
|
||||
"_Znam" => {
|
||||
hook_func!($libname, $lib_ident, _Znam, (size: usize), *mut c_void);
|
||||
@ -1034,7 +1119,7 @@ impl AsanRuntime {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
@ -1057,6 +1142,7 @@ impl AsanRuntime {
|
||||
|
||||
#[cfg(not(windows))]
|
||||
hook_func!(
|
||||
|
||||
mmap,
|
||||
(
|
||||
addr: *const c_void,
|
||||
@ -1074,6 +1160,7 @@ impl AsanRuntime {
|
||||
// Hook libc functions which may access allocated memory
|
||||
#[cfg(not(windows))]
|
||||
hook_func!(
|
||||
|
||||
write,
|
||||
(fd: i32, buf: *const c_void, count: usize),
|
||||
usize
|
||||
@ -1081,22 +1168,26 @@ impl AsanRuntime {
|
||||
#[cfg(not(windows))]
|
||||
hook_func!(read, (fd: i32, buf: *mut c_void, count: usize), usize);
|
||||
hook_func!(
|
||||
|
||||
fgets,
|
||||
(s: *mut c_void, size: u32, stream: *mut c_void),
|
||||
*mut c_void
|
||||
);
|
||||
hook_func!(
|
||||
|
||||
memcmp,
|
||||
(s1: *const c_void, s2: *const c_void, n: usize),
|
||||
i32
|
||||
);
|
||||
hook_func!(
|
||||
|
||||
memcpy,
|
||||
(dest: *mut c_void, src: *const c_void, n: usize),
|
||||
*mut c_void
|
||||
);
|
||||
#[cfg(not(any(target_vendor = "apple", windows)))]
|
||||
hook_func!(
|
||||
|
||||
mempcpy,
|
||||
(dest: *mut c_void, src: *const c_void, n: usize),
|
||||
*mut c_void
|
||||
@ -1109,23 +1200,27 @@ impl AsanRuntime {
|
||||
// *mut c_void
|
||||
// );
|
||||
hook_func!(
|
||||
|
||||
memset,
|
||||
(s: *mut c_void, c: i32, n: usize),
|
||||
*mut c_void
|
||||
);
|
||||
hook_func!(
|
||||
|
||||
memchr,
|
||||
(s: *mut c_void, c: i32, n: usize),
|
||||
*mut c_void
|
||||
);
|
||||
#[cfg(not(any(target_vendor = "apple", windows)))]
|
||||
hook_func!(
|
||||
|
||||
memrchr,
|
||||
(s: *mut c_void, c: i32, n: usize),
|
||||
*mut c_void
|
||||
);
|
||||
#[cfg(not(windows))]
|
||||
hook_func!(
|
||||
|
||||
memmem,
|
||||
(
|
||||
haystack: *const c_void,
|
||||
@ -1150,39 +1245,46 @@ impl AsanRuntime {
|
||||
hook_func!(strrchr, (s: *mut c_char, c: i32), *mut c_char);
|
||||
#[cfg(not(windows))]
|
||||
hook_func!(
|
||||
|
||||
strcasecmp,
|
||||
(s1: *const c_char, s2: *const c_char),
|
||||
i32
|
||||
);
|
||||
#[cfg(not(windows))]
|
||||
hook_func!(
|
||||
|
||||
strncasecmp,
|
||||
(s1: *const c_char, s2: *const c_char, n: usize),
|
||||
i32
|
||||
);
|
||||
hook_func!(
|
||||
|
||||
strcat,
|
||||
(dest: *mut c_char, src: *const c_char),
|
||||
*mut c_char
|
||||
);
|
||||
hook_func!(strcmp, (s1: *const c_char, s2: *const c_char), i32);
|
||||
hook_func!(
|
||||
|
||||
strncmp,
|
||||
(s1: *const c_char, s2: *const c_char, n: usize),
|
||||
i32
|
||||
);
|
||||
hook_func!(
|
||||
|
||||
strcpy,
|
||||
(dest: *mut c_char, src: *const c_char),
|
||||
*mut c_char
|
||||
);
|
||||
hook_func!(
|
||||
|
||||
strncpy,
|
||||
(dest: *mut c_char, src: *const c_char, n: usize),
|
||||
*mut c_char
|
||||
);
|
||||
#[cfg(not(windows))]
|
||||
hook_func!(
|
||||
|
||||
stpcpy,
|
||||
(dest: *mut c_char, src: *const c_char),
|
||||
*mut c_char
|
||||
@ -1194,12 +1296,14 @@ impl AsanRuntime {
|
||||
hook_func!(strlen, (s: *const c_char), usize);
|
||||
hook_func!(strnlen, (s: *const c_char, n: usize), usize);
|
||||
hook_func!(
|
||||
|
||||
strstr,
|
||||
(haystack: *const c_char, needle: *const c_char),
|
||||
*mut c_char
|
||||
);
|
||||
#[cfg(not(windows))]
|
||||
hook_func!(
|
||||
|
||||
strcasestr,
|
||||
(haystack: *const c_char, needle: *const c_char),
|
||||
*mut c_char
|
||||
@ -1209,6 +1313,7 @@ impl AsanRuntime {
|
||||
hook_func!(atoll, (nptr: *const c_char), i64);
|
||||
hook_func!(wcslen, (s: *const wchar_t), usize);
|
||||
hook_func!(
|
||||
|
||||
wcscpy,
|
||||
(dest: *mut wchar_t, src: *const wchar_t),
|
||||
*mut wchar_t
|
||||
@ -1216,18 +1321,21 @@ impl AsanRuntime {
|
||||
hook_func!(wcscmp, (s1: *const wchar_t, s2: *const wchar_t), i32);
|
||||
#[cfg(target_vendor = "apple")]
|
||||
hook_func!(
|
||||
|
||||
memset_pattern4,
|
||||
(s: *mut c_void, c: *const c_void, n: usize),
|
||||
()
|
||||
);
|
||||
#[cfg(target_vendor = "apple")]
|
||||
hook_func!(
|
||||
|
||||
memset_pattern8,
|
||||
(s: *mut c_void, c: *const c_void, n: usize),
|
||||
()
|
||||
);
|
||||
#[cfg(target_vendor = "apple")]
|
||||
hook_func!(
|
||||
|
||||
memset_pattern16,
|
||||
(s: *mut c_void, c: *const c_void, n: usize),
|
||||
()
|
||||
@ -1397,16 +1505,19 @@ impl AsanRuntime {
|
||||
backtrace,
|
||||
))
|
||||
};
|
||||
AsanErrors::get_mut_blocking().report_error(error);
|
||||
#[allow(clippy::manual_assert)]
|
||||
if AsanErrors::get_mut_blocking().report_error(error) {
|
||||
panic!("ASAN: Crashing target!");
|
||||
}
|
||||
|
||||
// This is not even a mem instruction??
|
||||
} else {
|
||||
AsanErrors::get_mut_blocking().report_error(AsanError::Unknown((
|
||||
} else if AsanErrors::get_mut_blocking().report_error(AsanError::Unknown((
|
||||
self.regs,
|
||||
actual_pc,
|
||||
(None, None, 0, fault_address),
|
||||
backtrace,
|
||||
)));
|
||||
))) {
|
||||
panic!("ASAN: Crashing target!");
|
||||
}
|
||||
|
||||
// log::info!("ASAN Error, attach the debugger!");
|
||||
@ -1536,7 +1647,10 @@ impl AsanRuntime {
|
||||
backtrace,
|
||||
))
|
||||
};
|
||||
AsanErrors::get_mut_blocking().report_error(error);
|
||||
#[allow(clippy::manual_assert)]
|
||||
if AsanErrors::get_mut_blocking().report_error(error) {
|
||||
panic!("ASAN: Crashing target!");
|
||||
}
|
||||
self.enable_hooks();
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ use backtrace::Backtrace;
|
||||
use color_backtrace::{default_output_stream, BacktracePrinter, Verbosity};
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use frida_gum::interceptor::Interceptor;
|
||||
use frida_gum::ModuleDetails;
|
||||
use frida_gum::{Gum, Process};
|
||||
use libafl::{
|
||||
corpus::Testcase,
|
||||
executors::ExitKind,
|
||||
@ -24,6 +24,7 @@ use libafl_bolts::{
|
||||
tuples::{Handle, Handled, MatchNameRef},
|
||||
Named, SerdeAny,
|
||||
};
|
||||
use mmap_rs::MmapOptions;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use termcolor::{Color, ColorSpec, WriteColor};
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
@ -151,9 +152,9 @@ impl AsanErrors {
|
||||
self.continue_on_error = continue_on_error;
|
||||
}
|
||||
|
||||
/// Report an error
|
||||
/// Report an error, returns true if the caller should panic
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub(crate) fn report_error(&mut self, error: AsanError) {
|
||||
pub(crate) fn report_error(&mut self, error: AsanError) -> bool {
|
||||
let mut out_stream = default_output_stream();
|
||||
let output = out_stream.as_mut();
|
||||
|
||||
@ -180,7 +181,9 @@ impl AsanErrors {
|
||||
| AsanError::WriteAfterFree(error) => {
|
||||
let (basereg, indexreg, _displacement, fault_address) = error.fault;
|
||||
|
||||
if let Some(module_details) = ModuleDetails::with_address(error.pc as u64) {
|
||||
if let Some(module_details) =
|
||||
Process::obtain(&Gum::obtain()).find_module_by_address(error.pc)
|
||||
{
|
||||
writeln!(
|
||||
output,
|
||||
" at 0x{:x} ({}@0x{:04x}), faulting address 0x{:x}",
|
||||
@ -294,7 +297,9 @@ impl AsanErrors {
|
||||
writeln!(output, "{:━^100}", " ALLOCATION INFO ").unwrap();
|
||||
let fault_address: i64 = fault_address.try_into().unwrap();
|
||||
let metadata_address: i64 = error.metadata.address.try_into().unwrap();
|
||||
let offset: i64 = fault_address - (metadata_address + 0x1000);
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
let offset: i64 =
|
||||
fault_address - (metadata_address + MmapOptions::page_size() as i64);
|
||||
let direction = if offset > 0 { "right" } else { "left" };
|
||||
writeln!(
|
||||
output,
|
||||
@ -302,7 +307,7 @@ impl AsanErrors {
|
||||
offset,
|
||||
direction,
|
||||
error.metadata.size,
|
||||
error.metadata.address + 0x1000
|
||||
error.metadata.address + MmapOptions::page_size()
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@ -343,7 +348,9 @@ impl AsanErrors {
|
||||
{
|
||||
let invocation = Interceptor::current_invocation();
|
||||
let cpu_context = invocation.cpu_context();
|
||||
if let Some(module_details) = ModuleDetails::with_address(*_pc as u64) {
|
||||
if let Some(module_details) =
|
||||
Process::obtain(&Gum::obtain()).find_module_by_address(*_pc)
|
||||
{
|
||||
writeln!(
|
||||
output,
|
||||
" at 0x{:x} ({}@0x{:04x})",
|
||||
@ -388,7 +395,7 @@ impl AsanErrors {
|
||||
writeln!(
|
||||
output,
|
||||
"allocation at 0x{:x}, with size 0x{:x}",
|
||||
metadata.address + 0x1000,
|
||||
metadata.address + MmapOptions::page_size(),
|
||||
metadata.size
|
||||
)
|
||||
.unwrap();
|
||||
@ -426,7 +433,7 @@ impl AsanErrors {
|
||||
writeln!(
|
||||
output,
|
||||
"allocation at 0x{:x}, with size 0x{:x}",
|
||||
metadata.address + 0x1000,
|
||||
metadata.address + MmapOptions::page_size(),
|
||||
metadata.size
|
||||
)
|
||||
.unwrap();
|
||||
@ -447,7 +454,9 @@ impl AsanErrors {
|
||||
| AsanError::StackOobWrite((registers, pc, fault, backtrace)) => {
|
||||
let (basereg, indexreg, _displacement, fault_address) = fault;
|
||||
|
||||
if let Some(module_details) = ModuleDetails::with_address(*pc as u64) {
|
||||
if let Some(module_details) =
|
||||
Process::obtain(&Gum::obtain()).find_module_by_address(*pc)
|
||||
{
|
||||
writeln!(
|
||||
output,
|
||||
" at 0x{:x} ({}:0x{:04x}), faulting address 0x{:x}",
|
||||
@ -556,10 +565,7 @@ impl AsanErrors {
|
||||
|
||||
self.errors.push(error);
|
||||
|
||||
#[expect(clippy::manual_assert)]
|
||||
if !self.continue_on_error {
|
||||
panic!("ASAN: Crashing target!");
|
||||
}
|
||||
!self.continue_on_error
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -177,13 +177,15 @@ where
|
||||
// Include the current module (the fuzzer) in stalked ranges. We clone the ranges so that
|
||||
// we don't add it to the INSTRUMENTED ranges.
|
||||
let mut ranges = helper.ranges().clone();
|
||||
for module in frida_gum::Module::obtain(gum).enumerate_modules() {
|
||||
if module.base_address < Self::with_target_bytes_converter as usize
|
||||
for module in frida_gum::Process::obtain(gum).enumerate_modules() {
|
||||
let range = module.range();
|
||||
if (range.base_address().0 as usize) < Self::with_target_bytes_converter as usize
|
||||
&& (Self::with_target_bytes_converter as usize as u64)
|
||||
< module.base_address as u64 + module.size as u64
|
||||
< range.base_address().0 as u64 + range.size() as u64
|
||||
{
|
||||
ranges.insert(
|
||||
module.base_address as u64..(module.base_address as u64 + module.size as u64),
|
||||
range.base_address().0 as u64
|
||||
..(range.base_address().0 as u64 + range.size() as u64),
|
||||
(0xffff, "fuzzer".to_string()),
|
||||
);
|
||||
break;
|
||||
|
@ -11,7 +11,7 @@ use std::{
|
||||
use frida_gum::{
|
||||
instruction_writer::InstructionWriter,
|
||||
stalker::{StalkerIterator, StalkerOutput, Transformer},
|
||||
Backend, Gum, ModuleDetails, ModuleMap, Script,
|
||||
Backend, Gum, Module, ModuleMap, Script,
|
||||
};
|
||||
use frida_gum_sys::gchar;
|
||||
use libafl::Error;
|
||||
@ -276,8 +276,8 @@ pub struct FridaInstrumentationHelperBuilder {
|
||||
stalker_enabled: bool,
|
||||
disable_excludes: bool,
|
||||
#[expect(clippy::type_complexity)]
|
||||
instrument_module_predicate: Option<Box<dyn FnMut(&ModuleDetails) -> bool>>,
|
||||
skip_module_predicate: Box<dyn FnMut(&ModuleDetails) -> bool>,
|
||||
instrument_module_predicate: Option<Box<dyn FnMut(&Module) -> bool>>,
|
||||
skip_module_predicate: Box<dyn FnMut(&Module) -> bool>,
|
||||
skip_ranges: Vec<SkipRange>,
|
||||
}
|
||||
|
||||
@ -353,7 +353,7 @@ impl FridaInstrumentationHelperBuilder {
|
||||
/// .instrument_module_if(|module| module.path().starts_with("/usr/lib"));
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn instrument_module_if<F: FnMut(&ModuleDetails) -> bool + 'static>(
|
||||
pub fn instrument_module_if<F: FnMut(&Module) -> bool + 'static>(
|
||||
mut self,
|
||||
mut predicate: F,
|
||||
) -> Self {
|
||||
@ -382,10 +382,7 @@ impl FridaInstrumentationHelperBuilder {
|
||||
/// .skip_module_if(|module| module.name() == "libfoo.so");
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn skip_module_if<F: FnMut(&ModuleDetails) -> bool + 'static>(
|
||||
mut self,
|
||||
mut predicate: F,
|
||||
) -> Self {
|
||||
pub fn skip_module_if<F: FnMut(&Module) -> bool + 'static>(mut self, mut predicate: F) -> Self {
|
||||
let new = move |module: &_| (self.skip_module_predicate)(module) || predicate(module);
|
||||
Self {
|
||||
skip_module_predicate: Box::new(new),
|
||||
@ -430,7 +427,7 @@ impl FridaInstrumentationHelperBuilder {
|
||||
!skip_module_predicate(&module)
|
||||
}
|
||||
});
|
||||
let module_map = Rc::new(ModuleMap::new_with_filter(gum, &mut module_filter));
|
||||
let module_map = Rc::new(ModuleMap::new_with_filter(&mut module_filter));
|
||||
|
||||
let ranges = RangeMap::new();
|
||||
// Wrap ranges and runtimes in reference-counted refcells in order to move
|
||||
@ -461,7 +458,7 @@ impl FridaInstrumentationHelperBuilder {
|
||||
.borrow_mut()
|
||||
.remove(range.start as u64..range.end as u64),
|
||||
SkipRange::ModuleRelative { name, range } => {
|
||||
let module_details = ModuleDetails::with_name(name).unwrap();
|
||||
let module_details = Module::load(gum, &name);
|
||||
let lib_start = module_details.range().base_address().0 as u64;
|
||||
ranges.borrow_mut().remove(
|
||||
(lib_start + range.start as u64)..(lib_start + range.end as u64),
|
||||
@ -550,7 +547,7 @@ pub unsafe extern "C" fn test_function(message: *const gchar) {
|
||||
}
|
||||
}
|
||||
|
||||
fn pathlist_contains_module<I, P>(list: I, module: &ModuleDetails) -> bool
|
||||
fn pathlist_contains_module<I, P>(list: I, module: &Module) -> bool
|
||||
where
|
||||
I: IntoIterator<Item = P>,
|
||||
P: AsRef<Path>,
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Based on the example of setting hooks: Https://github.com/frida/frida-rust/blob/main/examples/gum/hook_open/src/lib.rs
|
||||
use std::ffi::c_void;
|
||||
|
||||
use frida_gum::{interceptor::Interceptor, Gum, Module, NativePointer};
|
||||
use frida_gum::{interceptor::Interceptor, Gum, NativePointer, Process};
|
||||
use libafl_bolts::os::windows_exceptions::{
|
||||
handle_exception, IsProcessorFeaturePresent, UnhandledExceptionFilter, EXCEPTION_POINTERS,
|
||||
PROCESSOR_FEATURE_ID,
|
||||
@ -21,16 +21,16 @@ unsafe extern "C" fn unhandled_exception_filter_detour(
|
||||
}
|
||||
/// Initialize the hooks
|
||||
pub fn initialize(gum: &Gum) {
|
||||
let module = Module::obtain(gum);
|
||||
let is_processor_feature_present =
|
||||
module.find_export_by_name(Some("kernel32.dll"), "IsProcessorFeaturePresent");
|
||||
let module = Process::obtain(gum)
|
||||
.find_module_by_name("kernel32.dll")
|
||||
.unwrap();
|
||||
let is_processor_feature_present = module.find_export_by_name("IsProcessorFeaturePresent");
|
||||
let is_processor_feature_present = is_processor_feature_present.unwrap();
|
||||
assert!(
|
||||
!is_processor_feature_present.is_null(),
|
||||
"IsProcessorFeaturePresent not found"
|
||||
);
|
||||
let unhandled_exception_filter =
|
||||
module.find_export_by_name(Some("kernel32.dll"), "UnhandledExceptionFilter");
|
||||
let unhandled_exception_filter = module.find_export_by_name("UnhandledExceptionFilter");
|
||||
let unhandled_exception_filter = unhandled_exception_filter.unwrap();
|
||||
assert!(
|
||||
!unhandled_exception_filter.is_null(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user