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",
|
"frida_cli",
|
||||||
] } #, "llmp_small_maps", "llmp_debug"]}
|
] } #, "llmp_small_maps", "llmp_debug"]}
|
||||||
libafl_bolts = { path = "../../../libafl_bolts" }
|
libafl_bolts = { path = "../../../libafl_bolts" }
|
||||||
frida-gum = { version = "0.15.1", features = [
|
frida-gum = { version = "0.16.5", features = [
|
||||||
"auto-download",
|
"auto-download",
|
||||||
"event-sink",
|
"event-sink",
|
||||||
"invocation-listener",
|
"invocation-listener",
|
||||||
|
@ -16,6 +16,7 @@ lto = true
|
|||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
debug = true
|
debug = true
|
||||||
|
panic = 'abort'
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../../../libafl", features = [
|
libafl = { path = "../../../libafl", features = [
|
||||||
@ -26,7 +27,7 @@ libafl = { path = "../../../libafl", features = [
|
|||||||
"errors_backtrace",
|
"errors_backtrace",
|
||||||
] } #, "llmp_small_maps", "llmp_debug"]}
|
] } #, "llmp_small_maps", "llmp_debug"]}
|
||||||
libafl_bolts = { path = "../../../libafl_bolts" }
|
libafl_bolts = { path = "../../../libafl_bolts" }
|
||||||
frida-gum = { version = "0.15.1", features = [
|
frida-gum = { version = "0.16.5", features = [
|
||||||
"auto-download",
|
"auto-download",
|
||||||
"event-sink",
|
"event-sink",
|
||||||
"invocation-listener",
|
"invocation-listener",
|
||||||
@ -37,7 +38,9 @@ libafl_targets = { path = "../../../libafl_targets", features = [
|
|||||||
"sancov_cmplog",
|
"sancov_cmplog",
|
||||||
] }
|
] }
|
||||||
libloading = "0.8.5"
|
libloading = "0.8.5"
|
||||||
log = { version = "0.4.22", features = ["release_max_level_info"] }
|
log = { version = "0.4.22", features = ["release_max_level_trace"] }
|
||||||
mimalloc = { version = "0.1.43", default-features = false }
|
mimalloc = { version = "0.1.43", default-features = true, features = [
|
||||||
color-backtrace = "0.6.1"
|
"local_dynamic_tls",
|
||||||
|
] }
|
||||||
|
color-backtrace = { version = "0.6.1", features = ["resolve-modules"] }
|
||||||
env_logger = "0.11.5"
|
env_logger = "0.11.5"
|
||||||
|
@ -25,8 +25,6 @@ use libafl::{
|
|||||||
state::{HasCorpus, StdState},
|
state::{HasCorpus, StdState},
|
||||||
Error, HasMetadata,
|
Error, HasMetadata,
|
||||||
};
|
};
|
||||||
#[cfg(unix)]
|
|
||||||
use libafl::{feedback_and_fast, feedbacks::ConstFeedback};
|
|
||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
cli::{parse_args, FuzzerOptions},
|
cli::{parse_args, FuzzerOptions},
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
@ -56,19 +54,16 @@ pub fn main() {
|
|||||||
color_backtrace::install();
|
color_backtrace::install();
|
||||||
let options = parse_args();
|
let options = parse_args();
|
||||||
|
|
||||||
unsafe {
|
log::info!("Frida fuzzer starting up.");
|
||||||
match fuzz(&options) {
|
match fuzz(&options) {
|
||||||
Ok(()) | Err(Error::ShuttingDown) => println!("\nFinished fuzzing. Good bye."),
|
Ok(()) | Err(Error::ShuttingDown) => println!("\nFinished fuzzing. Good bye."),
|
||||||
Err(e) => panic!("Error during fuzzing: {e:?}"),
|
Err(e) => panic!("Error during fuzzing: {e:?}"),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The actual fuzzer
|
/// The actual fuzzer
|
||||||
#[expect(clippy::too_many_lines)]
|
#[expect(clippy::too_many_lines)]
|
||||||
unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
||||||
log::info!("Frida fuzzer starting up.");
|
|
||||||
|
|
||||||
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
|
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
|
||||||
let monitor = MultiMonitor::new(|s| println!("{s}"));
|
let monitor = MultiMonitor::new(|s| println!("{s}"));
|
||||||
|
|
||||||
@ -81,28 +76,24 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut run_client = |state: Option<_>,
|
let mut run_client = |state: Option<_>,
|
||||||
mgr: LlmpRestartingEventManager<_, _, _, _, _>,
|
mut mgr: LlmpRestartingEventManager<_, _, _, _, _>,
|
||||||
client_description: ClientDescription| {
|
client_description: ClientDescription| {
|
||||||
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
||||||
|
|
||||||
// println!("{:?}", mgr.mgr_id());
|
// 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<
|
let target_func: libloading::Symbol<
|
||||||
unsafe extern "C" fn(data: *const u8, size: usize) -> i32,
|
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 mut frida_harness = |input: &BytesInput| {
|
||||||
let target = input.target_bytes();
|
let target = input.target_bytes();
|
||||||
let buf = target.as_slice();
|
let buf = target.as_slice();
|
||||||
(target_func)(buf.as_ptr(), buf.len());
|
unsafe { (target_func)(buf.as_ptr(), buf.len()) };
|
||||||
ExitKind::Ok
|
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 gum = Gum::obtain();
|
||||||
|
|
||||||
let coverage = CoverageRuntime::new();
|
let coverage = CoverageRuntime::new();
|
||||||
@ -132,11 +123,9 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
// Create an observation channel using the coverage map
|
||||||
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr(
|
let edges_observer = HitcountsMapObserver::new(unsafe {
|
||||||
"edges",
|
StdMapObserver::from_mut_ptr("edges", frida_helper.map_mut_ptr().unwrap(), MAP_SIZE)
|
||||||
frida_helper.map_mut_ptr().unwrap(),
|
})
|
||||||
MAP_SIZE,
|
|
||||||
))
|
|
||||||
.track_indices();
|
.track_indices();
|
||||||
|
|
||||||
// Create an observation channel to keep track of the execution time
|
// 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
|
// Feedbacks to recognize an input as solution
|
||||||
#[cfg(unix)]
|
|
||||||
let mut objective = feedback_or_fast!(
|
let mut objective = feedback_or_fast!(
|
||||||
CrashFeedback::new(),
|
CrashFeedback::new(),
|
||||||
|
AsanErrorsFeedback::new(&asan_observer),
|
||||||
TimeoutFeedback::new(),
|
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
|
// If not restarting, create a State from scratch
|
||||||
let mut state = state.unwrap_or_else(|| {
|
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
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
let observers = tuple_list!(edges_observer, time_observer, asan_observer);
|
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
|
// 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,
|
&gum,
|
||||||
InProcessExecutor::new(
|
InProcessExecutor::with_timeout(
|
||||||
&mut frida_harness,
|
&mut frida_harness,
|
||||||
observers,
|
observers,
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut mgr,
|
&mut mgr,
|
||||||
|
options.timeout,
|
||||||
)?,
|
)?,
|
||||||
&mut frida_helper,
|
&mut frida_helper,
|
||||||
);
|
);
|
||||||
@ -230,14 +210,20 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
let tracing = ShadowTracingStage::new(&mut executor);
|
let tracing = ShadowTracingStage::new(&mut executor);
|
||||||
|
|
||||||
// Setup a randomic Input2State stage
|
// Setup a randomic Input2State stage
|
||||||
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(
|
let i2s =
|
||||||
I2SRandReplace::new()
|
StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new())));
|
||||||
)));
|
|
||||||
|
|
||||||
// In case the corpus is empty (on first run), reset
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.must_load_initial_inputs() {
|
if state.must_load_initial_inputs() {
|
||||||
state
|
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(|_| {
|
.unwrap_or_else(|_| {
|
||||||
panic!("Failed to load initial corpus at {:?}", &options.input)
|
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)?;
|
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})(state, mgr, client_description.clone())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Launcher::builder()
|
let builder = Launcher::builder()
|
||||||
.configuration(EventConfig::AlwaysUnique)
|
.configuration(EventConfig::AlwaysUnique)
|
||||||
.shmem_provider(shmem_provider)
|
.shmem_provider(shmem_provider)
|
||||||
.monitor(monitor)
|
.monitor(monitor)
|
||||||
.run_client(&mut run_client)
|
.run_client(&mut run_client)
|
||||||
.cores(&options.cores)
|
.cores(&options.cores)
|
||||||
.broker_port(options.broker_port)
|
.broker_port(options.broker_port)
|
||||||
// .stdout_file(Some(&options.stdout))
|
.remote_broker_addr(options.remote_broker_addr);
|
||||||
.remote_broker_addr(options.remote_broker_addr)
|
|
||||||
.build()
|
#[cfg(not(windows))]
|
||||||
.launch()
|
let builder = builder.stdout_file(Some(&options.stdout));
|
||||||
|
|
||||||
|
builder.build().launch()
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ libafl = { path = "../../../libafl", features = [
|
|||||||
"errors_backtrace",
|
"errors_backtrace",
|
||||||
] } #, "llmp_small_maps", "llmp_debug"]}
|
] } #, "llmp_small_maps", "llmp_debug"]}
|
||||||
libafl_bolts = { path = "../../../libafl_bolts" }
|
libafl_bolts = { path = "../../../libafl_bolts" }
|
||||||
frida-gum = { version = "0.15.1", features = [
|
frida-gum = { version = "0.16.5", features = [
|
||||||
"auto-download",
|
"auto-download",
|
||||||
"event-sink",
|
"event-sink",
|
||||||
"invocation-listener",
|
"invocation-listener",
|
||||||
|
@ -62,15 +62,16 @@ nix = { workspace = true, default-features = true, features = ["mman"] }
|
|||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
hashbrown = { workspace = true, default-features = true }
|
hashbrown = { workspace = true, default-features = true }
|
||||||
rangemap = { workspace = true }
|
rangemap = { workspace = true }
|
||||||
frida-gum-sys = { version = "0.15.1", features = [
|
frida-gum-sys = { version = "0.16.5", features = [
|
||||||
"event-sink",
|
"event-sink",
|
||||||
"invocation-listener",
|
"invocation-listener",
|
||||||
] }
|
] }
|
||||||
frida-gum = { version = "0.15.1", features = [
|
frida-gum = { version = "0.16.5", features = [
|
||||||
"event-sink",
|
"event-sink",
|
||||||
"invocation-listener",
|
"invocation-listener",
|
||||||
"module-names",
|
"module-names",
|
||||||
"script",
|
"script",
|
||||||
|
"backtrace",
|
||||||
] }
|
] }
|
||||||
dynasmrt = "3.0.1"
|
dynasmrt = "3.0.1"
|
||||||
|
|
||||||
@ -98,7 +99,6 @@ serial_test = { workspace = true, default-features = false, features = [
|
|||||||
clap = { workspace = true, features = ["derive"] }
|
clap = { workspace = true, features = ["derive"] }
|
||||||
libloading = "0.8.5"
|
libloading = "0.8.5"
|
||||||
mimalloc = { workspace = true, default-features = false }
|
mimalloc = { workspace = true, default-features = false }
|
||||||
dlmalloc = { version = "0.2.6", features = ["global"] }
|
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
@ -14,6 +14,7 @@ fn main() {
|
|||||||
let target_family = std::env::var("CARGO_CFG_TARGET_FAMILY").unwrap();
|
let target_family = std::env::var("CARGO_CFG_TARGET_FAMILY").unwrap();
|
||||||
|
|
||||||
// Force linking against libc++
|
// Force linking against libc++
|
||||||
|
#[cfg(not(target_vendor = "apple"))]
|
||||||
if target_family == "unix" {
|
if target_family == "unix" {
|
||||||
println!("cargo:rustc-link-lib=dylib=c++");
|
println!("cargo:rustc-link-lib=dylib=c++");
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ use std::{collections::BTreeMap, ffi::c_void};
|
|||||||
|
|
||||||
use backtrace::Backtrace;
|
use backtrace::Backtrace;
|
||||||
use frida_gum::{PageProtection, RangeDetails};
|
use frida_gum::{PageProtection, RangeDetails};
|
||||||
use hashbrown::HashMap;
|
|
||||||
use libafl_bolts::cli::FuzzerOptions;
|
use libafl_bolts::cli::FuzzerOptions;
|
||||||
#[cfg(target_vendor = "apple")]
|
#[cfg(target_vendor = "apple")]
|
||||||
use mach_sys::{
|
use mach_sys::{
|
||||||
@ -59,9 +58,9 @@ pub struct Allocator {
|
|||||||
/// Whether we've pre allocated a shadow mapping:
|
/// Whether we've pre allocated a shadow mapping:
|
||||||
using_pre_allocated_shadow_mapping: bool,
|
using_pre_allocated_shadow_mapping: bool,
|
||||||
/// All tracked allocations
|
/// All tracked allocations
|
||||||
allocations: HashMap<usize, AllocationMetadata>,
|
allocations: BTreeMap<usize, AllocationMetadata>,
|
||||||
/// All mappings
|
/// All mappings
|
||||||
mappings: HashMap<usize, MmapMut>,
|
mappings: BTreeMap<usize, MmapMut>,
|
||||||
/// The shadow memory pages
|
/// The shadow memory pages
|
||||||
shadow_pages: RangeSet<usize>,
|
shadow_pages: RangeSet<usize>,
|
||||||
/// A list of allocations
|
/// A list of allocations
|
||||||
@ -171,7 +170,6 @@ impl Allocator {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
#[expect(clippy::missing_safety_doc)]
|
#[expect(clippy::missing_safety_doc)]
|
||||||
pub unsafe fn alloc(&mut self, size: usize, _alignment: usize) -> *mut c_void {
|
pub unsafe fn alloc(&mut self, size: usize, _alignment: usize) -> *mut c_void {
|
||||||
log::trace!("alloc");
|
|
||||||
let mut is_malloc_zero = false;
|
let mut is_malloc_zero = false;
|
||||||
let size = if size == 0 {
|
let size = if size == 0 {
|
||||||
is_malloc_zero = true;
|
is_malloc_zero = true;
|
||||||
@ -249,7 +247,7 @@ impl Allocator {
|
|||||||
let address = (metadata.address + self.page_size) as *mut c_void;
|
let address = (metadata.address + self.page_size) as *mut c_void;
|
||||||
|
|
||||||
self.allocations.insert(address as usize, metadata);
|
self.allocations.insert(address as usize, metadata);
|
||||||
log::trace!(
|
log::info!(
|
||||||
"serving address: {:#x}, size: {:#x}",
|
"serving address: {:#x}, size: {:#x}",
|
||||||
address as usize,
|
address as usize,
|
||||||
size
|
size
|
||||||
@ -260,21 +258,25 @@ impl Allocator {
|
|||||||
/// Releases the allocation at the given address.
|
/// Releases the allocation at the given address.
|
||||||
#[expect(clippy::missing_safety_doc)]
|
#[expect(clippy::missing_safety_doc)]
|
||||||
pub unsafe fn release(&mut self, ptr: *mut c_void) {
|
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 {
|
let Some(metadata) = self.allocations.get_mut(&(ptr as usize)) else {
|
||||||
if !ptr.is_null() {
|
if !ptr.is_null()
|
||||||
AsanErrors::get_mut_blocking()
|
&& AsanErrors::get_mut_blocking()
|
||||||
.report_error(AsanError::UnallocatedFree((ptr as usize, Backtrace::new())));
|
.report_error(AsanError::UnallocatedFree((ptr as usize, Backtrace::new())))
|
||||||
|
{
|
||||||
|
panic!("ASAN: Crashing target!");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if metadata.freed {
|
if metadata.freed
|
||||||
AsanErrors::get_mut_blocking().report_error(AsanError::DoubleFree((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::DoubleFree((
|
||||||
ptr as usize,
|
ptr as usize,
|
||||||
metadata.clone(),
|
metadata.clone(),
|
||||||
Backtrace::new(),
|
Backtrace::new(),
|
||||||
)));
|
)))
|
||||||
|
{
|
||||||
|
panic!("ASAN: Crashing target!");
|
||||||
}
|
}
|
||||||
let shadow_mapping_start = map_to_shadow!(self, ptr as usize);
|
let shadow_mapping_start = map_to_shadow!(self, ptr as usize);
|
||||||
|
|
||||||
@ -316,7 +318,7 @@ impl Allocator {
|
|||||||
/// Resets the allocator contents
|
/// Resets the allocator contents
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
let mut tmp_allocations = Vec::new();
|
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 {
|
if !allocation.freed {
|
||||||
tmp_allocations.push(allocation);
|
tmp_allocations.push(allocation);
|
||||||
continue;
|
continue;
|
||||||
@ -579,9 +581,17 @@ impl Allocator {
|
|||||||
/// Checks if any of the allocations has not been freed
|
/// Checks if any of the allocations has not been freed
|
||||||
pub fn check_for_leaks(&self) {
|
pub fn check_for_leaks(&self) {
|
||||||
for metadata in self.allocations.values() {
|
for metadata in self.allocations.values() {
|
||||||
if !metadata.freed {
|
if !metadata.freed
|
||||||
AsanErrors::get_mut_blocking()
|
&& AsanErrors::get_mut_blocking()
|
||||||
.report_error(AsanError::Leak((metadata.address, metadata.clone())));
|
.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,
|
page_size,
|
||||||
pre_allocated_shadow_mappings: Vec::new(),
|
pre_allocated_shadow_mappings: Vec::new(),
|
||||||
using_pre_allocated_shadow_mapping: false,
|
using_pre_allocated_shadow_mapping: false,
|
||||||
mappings: HashMap::new(),
|
mappings: BTreeMap::new(),
|
||||||
shadow_offset: 0,
|
shadow_offset: 0,
|
||||||
shadow_bit: 0,
|
shadow_bit: 0,
|
||||||
allocations: HashMap::new(),
|
allocations: BTreeMap::new(),
|
||||||
shadow_pages: RangeSet::new(),
|
shadow_pages: RangeSet::new(),
|
||||||
allocation_queue: BTreeMap::new(),
|
allocation_queue: BTreeMap::new(),
|
||||||
largest_allocation: 0,
|
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::{Aarch64Register, IndexMode};
|
||||||
use frida_gum::{
|
use frida_gum::{
|
||||||
instruction_writer::InstructionWriter, interceptor::Interceptor, stalker::StalkerOutput, 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 frida_gum_sys::Insn;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
@ -96,6 +96,25 @@ thread_local! {
|
|||||||
static ASAN_IN_HOOK: Cell<bool> = const { Cell::new(false) };
|
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
|
/// The count of registers that need to be saved by the asan runtime
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
pub const ASAN_SAVE_REGISTER_COUNT: usize = 32;
|
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 {
|
.extend(self.skip_ranges.iter().map(|skip| match skip {
|
||||||
SkipRange::Absolute(range) => range.start,
|
SkipRange::Absolute(range) => range.start,
|
||||||
SkipRange::ModuleRelative { name, range } => {
|
SkipRange::ModuleRelative { name, range } => {
|
||||||
let module_details = ModuleDetails::with_name(name.clone()).unwrap();
|
let module = Module::load(gum, name);
|
||||||
let lib_start = module_details.range().base_address().0 as usize;
|
let lib_start = module.range().base_address().0 as usize;
|
||||||
lib_start + range.start
|
lib_start + range.start
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
@ -456,35 +475,40 @@ impl AsanRuntime {
|
|||||||
#[expect(clippy::too_many_lines)]
|
#[expect(clippy::too_many_lines)]
|
||||||
pub fn register_hooks(&mut self, gum: &Gum) {
|
pub fn register_hooks(&mut self, gum: &Gum) {
|
||||||
let mut interceptor = Interceptor::obtain(gum);
|
let mut interceptor = Interceptor::obtain(gum);
|
||||||
let module = Module::obtain(gum);
|
let process = Process::obtain(gum);
|
||||||
macro_rules! hook_func {
|
macro_rules! hook_func {
|
||||||
//No library case
|
|
||||||
($name:ident, ($($param:ident : $param_type:ty),*), $return_type:ty) => {
|
($name:ident, ($($param:ident : $param_type:ty),*), $return_type:ty) => {
|
||||||
paste::paste! {
|
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();
|
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();
|
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 {
|
unsafe extern "C" fn [<replacement_ $name>]($($param: $param_type),*) -> $return_type {
|
||||||
let mut invocation = Interceptor::current_invocation();
|
let mut invocation = Interceptor::current_invocation();
|
||||||
let this = &mut *(invocation.replacement_data().unwrap().0 as *mut AsanRuntime);
|
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
|
//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 real_address = this.real_address_for_stalked(invocation.return_addr());
|
||||||
let original = [<$name:snake:upper _PTR>].get().unwrap();
|
let original = [<$name:snake:upper _PTR>].get().unwrap();
|
||||||
|
if this.hooks_enabled {
|
||||||
if !ASAN_IN_HOOK.get() && this.hooks_enabled {
|
if thread_local_initted() {
|
||||||
|
if !ASAN_IN_HOOK.get() {
|
||||||
ASAN_IN_HOOK.set(true);
|
ASAN_IN_HOOK.set(true);
|
||||||
let ret = this.[<hook_ $name>](*original, $($param),*);
|
let ret = this.[<hook_ $name>](*original, $($param),*);
|
||||||
ASAN_IN_HOOK.set(false);
|
ASAN_IN_HOOK.set(false);
|
||||||
ret
|
ret
|
||||||
} else {
|
} else {
|
||||||
let ret = (original)($($param),*);
|
(original)($($param),*)
|
||||||
ret
|
}
|
||||||
|
} 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)
|
//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) => {
|
||||||
paste::paste! {
|
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();
|
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
|
//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 real_address = this.real_address_for_stalked(invocation.return_addr());
|
||||||
let original = [<$lib_ident:snake:upper _ $name:snake:upper _PTR>].get().unwrap();
|
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);
|
ASAN_IN_HOOK.set(true);
|
||||||
let ret = this.[<hook_ $name>](*original, $($param),*);
|
let ret = this.[<hook_ $name>](*original, $($param),*);
|
||||||
ASAN_IN_HOOK.set(false);
|
ASAN_IN_HOOK.set(false);
|
||||||
ret
|
ret
|
||||||
} else {
|
} else {
|
||||||
let ret = (original)($($param),*);
|
(original)($($param),*)
|
||||||
ret
|
}
|
||||||
|
} 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 {
|
macro_rules! hook_func_with_check {
|
||||||
//No library case
|
($name:ident, ($($param:ident : $param_type:ty),*), $return_type:ty, $always_enabled:expr) => {
|
||||||
($name:ident, ($($param:ident : $param_type:ty),*), $return_type:ty) => {
|
|
||||||
paste::paste! {
|
paste::paste! {
|
||||||
log::trace!("Hooking {}", stringify!($name));
|
let target_function = Module::find_global_export_by_name(stringify!($name)).expect("Failed to find function");
|
||||||
let target_function = module.find_export_by_name(None, 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();
|
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));
|
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 {
|
unsafe extern "C" fn [<replacement_ $name>]($($param: $param_type),*) -> $return_type {
|
||||||
let mut invocation = Interceptor::current_invocation();
|
let mut invocation = Interceptor::current_invocation();
|
||||||
let this = &mut *(invocation.replacement_data().unwrap().0 as *mut AsanRuntime);
|
let this = &mut *(invocation.replacement_data().unwrap().0 as *mut AsanRuntime);
|
||||||
let original = [<$name:snake:upper _PTR>].get().unwrap();
|
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
|
//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.
|
//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);
|
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);
|
ASAN_IN_HOOK.set(false);
|
||||||
ret
|
ret
|
||||||
} else {
|
} 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
|
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)
|
//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! {
|
paste::paste! {
|
||||||
log::trace!("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");
|
||||||
let target_function = module.find_export_by_name(Some($lib), 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();
|
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();
|
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
|
//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.
|
//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);
|
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);
|
ASAN_IN_HOOK.set(false);
|
||||||
ret
|
ret
|
||||||
} else {
|
} 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
|
ret
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
(original)($($param),*)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,9 +690,9 @@ impl AsanRuntime {
|
|||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
hook_func!(calloc, (nmemb: usize, size: usize), *mut c_void);
|
hook_func!(calloc, (nmemb: usize, size: usize), *mut c_void);
|
||||||
#[cfg(not(windows))]
|
#[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))]
|
#[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)))]
|
#[cfg(not(any(target_vendor = "apple", windows)))]
|
||||||
hook_func!(memalign, (size: usize, alignment: usize), *mut c_void);
|
hook_func!(memalign, (size: usize, alignment: usize), *mut c_void);
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
@ -642,6 +703,28 @@ impl AsanRuntime {
|
|||||||
);
|
);
|
||||||
#[cfg(not(any(target_vendor = "apple", windows)))]
|
#[cfg(not(any(target_vendor = "apple", windows)))]
|
||||||
hook_func!(malloc_usable_size, (ptr: *mut c_void), usize);
|
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)]
|
// // #[cfg(windows)]
|
||||||
// hook_priv_func!(
|
// hook_priv_func!(
|
||||||
// "c:\\windows\\system32\\ntdll.dll",
|
// "c:\\windows\\system32\\ntdll.dll",
|
||||||
@ -674,7 +757,8 @@ impl AsanRuntime {
|
|||||||
macro_rules! hook_heap_windows {
|
macro_rules! hook_heap_windows {
|
||||||
($libname:literal, $lib_ident:ident) => {
|
($libname:literal, $lib_ident:ident) => {
|
||||||
log::info!("Hooking allocator functions in {}", $libname);
|
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);
|
// log::trace!("- {}", export.name);
|
||||||
match &export.name[..] {
|
match &export.name[..] {
|
||||||
"NtGdiCreateCompatibleDC" => {
|
"NtGdiCreateCompatibleDC" => {
|
||||||
@ -693,16 +777,16 @@ impl AsanRuntime {
|
|||||||
hook_func!($libname, $lib_ident, RtlAllocateHeap, (handle: *mut c_void, flags: u32, bytes: usize), *mut c_void);
|
hook_func!($libname, $lib_ident, RtlAllocateHeap, (handle: *mut c_void, flags: u32, bytes: usize), *mut c_void);
|
||||||
}
|
}
|
||||||
"HeapFree" => {
|
"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" => {
|
"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" => {
|
"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" => {
|
"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" => {
|
"RtlReAllocateHeap" => {
|
||||||
hook_func!(
|
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);
|
hook_func!($libname, $lib_ident, LocalReAlloc, (mem: *mut c_void, size: usize, flags: u32), *mut c_void);
|
||||||
}
|
}
|
||||||
"LocalHandle" => {
|
"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" => {
|
"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" => {
|
"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" => {
|
"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" => {
|
"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" => {
|
"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" => {
|
"GlobalAlloc" => {
|
||||||
hook_func!($libname, $lib_ident, GlobalAlloc, (flags: u32, size: usize), *mut c_void);
|
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);
|
hook_func!($libname, $lib_ident, GlobalReAlloc, (mem: *mut c_void, flags: u32, size: usize), *mut c_void);
|
||||||
}
|
}
|
||||||
"GlobalHandle" => {
|
"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" => {
|
"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" => {
|
"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" => {
|
"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" => {
|
"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" => {
|
"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" => {
|
"memmove" => {
|
||||||
hook_func!(
|
hook_func!(
|
||||||
@ -813,10 +897,10 @@ impl AsanRuntime {
|
|||||||
hook_func!($libname, $lib_ident, _o_realloc, (ptr: *mut c_void, size: usize), *mut c_void);
|
hook_func!($libname, $lib_ident, _o_realloc, (ptr: *mut c_void, size: usize), *mut c_void);
|
||||||
}
|
}
|
||||||
"free" => {
|
"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" => {
|
"_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" => {
|
"_write" => {
|
||||||
hook_func!(
|
hook_func!(
|
||||||
@ -860,7 +944,7 @@ impl AsanRuntime {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@ -900,7 +984,8 @@ impl AsanRuntime {
|
|||||||
macro_rules! hook_cpp {
|
macro_rules! hook_cpp {
|
||||||
($libname:literal, $lib_ident:ident) => {
|
($libname:literal, $lib_ident:ident) => {
|
||||||
log::info!("Hooking c++ functions in {}", $libname);
|
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[..] {
|
match &export.name[..] {
|
||||||
"_Znam" => {
|
"_Znam" => {
|
||||||
hook_func!($libname, $lib_ident, _Znam, (size: usize), *mut c_void);
|
hook_func!($libname, $lib_ident, _Znam, (size: usize), *mut c_void);
|
||||||
@ -1034,7 +1119,7 @@ impl AsanRuntime {
|
|||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
@ -1057,6 +1142,7 @@ impl AsanRuntime {
|
|||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
mmap,
|
mmap,
|
||||||
(
|
(
|
||||||
addr: *const c_void,
|
addr: *const c_void,
|
||||||
@ -1074,6 +1160,7 @@ impl AsanRuntime {
|
|||||||
// Hook libc functions which may access allocated memory
|
// Hook libc functions which may access allocated memory
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
write,
|
write,
|
||||||
(fd: i32, buf: *const c_void, count: usize),
|
(fd: i32, buf: *const c_void, count: usize),
|
||||||
usize
|
usize
|
||||||
@ -1081,22 +1168,26 @@ impl AsanRuntime {
|
|||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
hook_func!(read, (fd: i32, buf: *mut c_void, count: usize), usize);
|
hook_func!(read, (fd: i32, buf: *mut c_void, count: usize), usize);
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
fgets,
|
fgets,
|
||||||
(s: *mut c_void, size: u32, stream: *mut c_void),
|
(s: *mut c_void, size: u32, stream: *mut c_void),
|
||||||
*mut c_void
|
*mut c_void
|
||||||
);
|
);
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
memcmp,
|
memcmp,
|
||||||
(s1: *const c_void, s2: *const c_void, n: usize),
|
(s1: *const c_void, s2: *const c_void, n: usize),
|
||||||
i32
|
i32
|
||||||
);
|
);
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
memcpy,
|
memcpy,
|
||||||
(dest: *mut c_void, src: *const c_void, n: usize),
|
(dest: *mut c_void, src: *const c_void, n: usize),
|
||||||
*mut c_void
|
*mut c_void
|
||||||
);
|
);
|
||||||
#[cfg(not(any(target_vendor = "apple", windows)))]
|
#[cfg(not(any(target_vendor = "apple", windows)))]
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
mempcpy,
|
mempcpy,
|
||||||
(dest: *mut c_void, src: *const c_void, n: usize),
|
(dest: *mut c_void, src: *const c_void, n: usize),
|
||||||
*mut c_void
|
*mut c_void
|
||||||
@ -1109,23 +1200,27 @@ impl AsanRuntime {
|
|||||||
// *mut c_void
|
// *mut c_void
|
||||||
// );
|
// );
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
memset,
|
memset,
|
||||||
(s: *mut c_void, c: i32, n: usize),
|
(s: *mut c_void, c: i32, n: usize),
|
||||||
*mut c_void
|
*mut c_void
|
||||||
);
|
);
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
memchr,
|
memchr,
|
||||||
(s: *mut c_void, c: i32, n: usize),
|
(s: *mut c_void, c: i32, n: usize),
|
||||||
*mut c_void
|
*mut c_void
|
||||||
);
|
);
|
||||||
#[cfg(not(any(target_vendor = "apple", windows)))]
|
#[cfg(not(any(target_vendor = "apple", windows)))]
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
memrchr,
|
memrchr,
|
||||||
(s: *mut c_void, c: i32, n: usize),
|
(s: *mut c_void, c: i32, n: usize),
|
||||||
*mut c_void
|
*mut c_void
|
||||||
);
|
);
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
memmem,
|
memmem,
|
||||||
(
|
(
|
||||||
haystack: *const c_void,
|
haystack: *const c_void,
|
||||||
@ -1150,39 +1245,46 @@ impl AsanRuntime {
|
|||||||
hook_func!(strrchr, (s: *mut c_char, c: i32), *mut c_char);
|
hook_func!(strrchr, (s: *mut c_char, c: i32), *mut c_char);
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
strcasecmp,
|
strcasecmp,
|
||||||
(s1: *const c_char, s2: *const c_char),
|
(s1: *const c_char, s2: *const c_char),
|
||||||
i32
|
i32
|
||||||
);
|
);
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
strncasecmp,
|
strncasecmp,
|
||||||
(s1: *const c_char, s2: *const c_char, n: usize),
|
(s1: *const c_char, s2: *const c_char, n: usize),
|
||||||
i32
|
i32
|
||||||
);
|
);
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
strcat,
|
strcat,
|
||||||
(dest: *mut c_char, src: *const c_char),
|
(dest: *mut c_char, src: *const c_char),
|
||||||
*mut c_char
|
*mut c_char
|
||||||
);
|
);
|
||||||
hook_func!(strcmp, (s1: *const c_char, s2: *const c_char), i32);
|
hook_func!(strcmp, (s1: *const c_char, s2: *const c_char), i32);
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
strncmp,
|
strncmp,
|
||||||
(s1: *const c_char, s2: *const c_char, n: usize),
|
(s1: *const c_char, s2: *const c_char, n: usize),
|
||||||
i32
|
i32
|
||||||
);
|
);
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
strcpy,
|
strcpy,
|
||||||
(dest: *mut c_char, src: *const c_char),
|
(dest: *mut c_char, src: *const c_char),
|
||||||
*mut c_char
|
*mut c_char
|
||||||
);
|
);
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
strncpy,
|
strncpy,
|
||||||
(dest: *mut c_char, src: *const c_char, n: usize),
|
(dest: *mut c_char, src: *const c_char, n: usize),
|
||||||
*mut c_char
|
*mut c_char
|
||||||
);
|
);
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
stpcpy,
|
stpcpy,
|
||||||
(dest: *mut c_char, src: *const c_char),
|
(dest: *mut c_char, src: *const c_char),
|
||||||
*mut c_char
|
*mut c_char
|
||||||
@ -1194,12 +1296,14 @@ impl AsanRuntime {
|
|||||||
hook_func!(strlen, (s: *const c_char), usize);
|
hook_func!(strlen, (s: *const c_char), usize);
|
||||||
hook_func!(strnlen, (s: *const c_char, n: usize), usize);
|
hook_func!(strnlen, (s: *const c_char, n: usize), usize);
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
strstr,
|
strstr,
|
||||||
(haystack: *const c_char, needle: *const c_char),
|
(haystack: *const c_char, needle: *const c_char),
|
||||||
*mut c_char
|
*mut c_char
|
||||||
);
|
);
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
strcasestr,
|
strcasestr,
|
||||||
(haystack: *const c_char, needle: *const c_char),
|
(haystack: *const c_char, needle: *const c_char),
|
||||||
*mut c_char
|
*mut c_char
|
||||||
@ -1209,6 +1313,7 @@ impl AsanRuntime {
|
|||||||
hook_func!(atoll, (nptr: *const c_char), i64);
|
hook_func!(atoll, (nptr: *const c_char), i64);
|
||||||
hook_func!(wcslen, (s: *const wchar_t), usize);
|
hook_func!(wcslen, (s: *const wchar_t), usize);
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
wcscpy,
|
wcscpy,
|
||||||
(dest: *mut wchar_t, src: *const wchar_t),
|
(dest: *mut wchar_t, src: *const wchar_t),
|
||||||
*mut wchar_t
|
*mut wchar_t
|
||||||
@ -1216,18 +1321,21 @@ impl AsanRuntime {
|
|||||||
hook_func!(wcscmp, (s1: *const wchar_t, s2: *const wchar_t), i32);
|
hook_func!(wcscmp, (s1: *const wchar_t, s2: *const wchar_t), i32);
|
||||||
#[cfg(target_vendor = "apple")]
|
#[cfg(target_vendor = "apple")]
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
memset_pattern4,
|
memset_pattern4,
|
||||||
(s: *mut c_void, c: *const c_void, n: usize),
|
(s: *mut c_void, c: *const c_void, n: usize),
|
||||||
()
|
()
|
||||||
);
|
);
|
||||||
#[cfg(target_vendor = "apple")]
|
#[cfg(target_vendor = "apple")]
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
memset_pattern8,
|
memset_pattern8,
|
||||||
(s: *mut c_void, c: *const c_void, n: usize),
|
(s: *mut c_void, c: *const c_void, n: usize),
|
||||||
()
|
()
|
||||||
);
|
);
|
||||||
#[cfg(target_vendor = "apple")]
|
#[cfg(target_vendor = "apple")]
|
||||||
hook_func!(
|
hook_func!(
|
||||||
|
|
||||||
memset_pattern16,
|
memset_pattern16,
|
||||||
(s: *mut c_void, c: *const c_void, n: usize),
|
(s: *mut c_void, c: *const c_void, n: usize),
|
||||||
()
|
()
|
||||||
@ -1397,16 +1505,19 @@ impl AsanRuntime {
|
|||||||
backtrace,
|
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??
|
// This is not even a mem instruction??
|
||||||
} else {
|
} else if AsanErrors::get_mut_blocking().report_error(AsanError::Unknown((
|
||||||
AsanErrors::get_mut_blocking().report_error(AsanError::Unknown((
|
|
||||||
self.regs,
|
self.regs,
|
||||||
actual_pc,
|
actual_pc,
|
||||||
(None, None, 0, fault_address),
|
(None, None, 0, fault_address),
|
||||||
backtrace,
|
backtrace,
|
||||||
)));
|
))) {
|
||||||
|
panic!("ASAN: Crashing target!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// log::info!("ASAN Error, attach the debugger!");
|
// log::info!("ASAN Error, attach the debugger!");
|
||||||
@ -1536,7 +1647,10 @@ impl AsanRuntime {
|
|||||||
backtrace,
|
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();
|
self.enable_hooks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ use backtrace::Backtrace;
|
|||||||
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;
|
use frida_gum::{Gum, Process};
|
||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::Testcase,
|
corpus::Testcase,
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
@ -24,6 +24,7 @@ use libafl_bolts::{
|
|||||||
tuples::{Handle, Handled, MatchNameRef},
|
tuples::{Handle, Handled, MatchNameRef},
|
||||||
Named, SerdeAny,
|
Named, SerdeAny,
|
||||||
};
|
};
|
||||||
|
use mmap_rs::MmapOptions;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use termcolor::{Color, ColorSpec, WriteColor};
|
use termcolor::{Color, ColorSpec, WriteColor};
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
@ -151,9 +152,9 @@ impl AsanErrors {
|
|||||||
self.continue_on_error = continue_on_error;
|
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)]
|
#[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 mut out_stream = default_output_stream();
|
||||||
let output = out_stream.as_mut();
|
let output = out_stream.as_mut();
|
||||||
|
|
||||||
@ -180,7 +181,9 @@ impl AsanErrors {
|
|||||||
| AsanError::WriteAfterFree(error) => {
|
| AsanError::WriteAfterFree(error) => {
|
||||||
let (basereg, indexreg, _displacement, fault_address) = error.fault;
|
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!(
|
writeln!(
|
||||||
output,
|
output,
|
||||||
" at 0x{:x} ({}@0x{:04x}), faulting address 0x{:x}",
|
" at 0x{:x} ({}@0x{:04x}), faulting address 0x{:x}",
|
||||||
@ -294,7 +297,9 @@ impl AsanErrors {
|
|||||||
writeln!(output, "{:━^100}", " ALLOCATION INFO ").unwrap();
|
writeln!(output, "{:━^100}", " ALLOCATION INFO ").unwrap();
|
||||||
let fault_address: i64 = fault_address.try_into().unwrap();
|
let fault_address: i64 = fault_address.try_into().unwrap();
|
||||||
let metadata_address: i64 = error.metadata.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" };
|
let direction = if offset > 0 { "right" } else { "left" };
|
||||||
writeln!(
|
writeln!(
|
||||||
output,
|
output,
|
||||||
@ -302,7 +307,7 @@ impl AsanErrors {
|
|||||||
offset,
|
offset,
|
||||||
direction,
|
direction,
|
||||||
error.metadata.size,
|
error.metadata.size,
|
||||||
error.metadata.address + 0x1000
|
error.metadata.address + MmapOptions::page_size()
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -343,7 +348,9 @@ 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(module_details) = ModuleDetails::with_address(*_pc as u64) {
|
if let Some(module_details) =
|
||||||
|
Process::obtain(&Gum::obtain()).find_module_by_address(*_pc)
|
||||||
|
{
|
||||||
writeln!(
|
writeln!(
|
||||||
output,
|
output,
|
||||||
" at 0x{:x} ({}@0x{:04x})",
|
" at 0x{:x} ({}@0x{:04x})",
|
||||||
@ -388,7 +395,7 @@ impl AsanErrors {
|
|||||||
writeln!(
|
writeln!(
|
||||||
output,
|
output,
|
||||||
"allocation at 0x{:x}, with size 0x{:x}",
|
"allocation at 0x{:x}, with size 0x{:x}",
|
||||||
metadata.address + 0x1000,
|
metadata.address + MmapOptions::page_size(),
|
||||||
metadata.size
|
metadata.size
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -426,7 +433,7 @@ impl AsanErrors {
|
|||||||
writeln!(
|
writeln!(
|
||||||
output,
|
output,
|
||||||
"allocation at 0x{:x}, with size 0x{:x}",
|
"allocation at 0x{:x}, with size 0x{:x}",
|
||||||
metadata.address + 0x1000,
|
metadata.address + MmapOptions::page_size(),
|
||||||
metadata.size
|
metadata.size
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -447,7 +454,9 @@ 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 Some(module_details) = ModuleDetails::with_address(*pc as u64) {
|
if let Some(module_details) =
|
||||||
|
Process::obtain(&Gum::obtain()).find_module_by_address(*pc)
|
||||||
|
{
|
||||||
writeln!(
|
writeln!(
|
||||||
output,
|
output,
|
||||||
" at 0x{:x} ({}:0x{:04x}), faulting address 0x{:x}",
|
" at 0x{:x} ({}:0x{:04x}), faulting address 0x{:x}",
|
||||||
@ -556,10 +565,7 @@ impl AsanErrors {
|
|||||||
|
|
||||||
self.errors.push(error);
|
self.errors.push(error);
|
||||||
|
|
||||||
#[expect(clippy::manual_assert)]
|
!self.continue_on_error
|
||||||
if !self.continue_on_error {
|
|
||||||
panic!("ASAN: Crashing target!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// Include the current module (the fuzzer) in stalked ranges. We clone the ranges so that
|
||||||
// we don't add it to the INSTRUMENTED ranges.
|
// we don't add it to the INSTRUMENTED ranges.
|
||||||
let mut ranges = helper.ranges().clone();
|
let mut ranges = helper.ranges().clone();
|
||||||
for module in frida_gum::Module::obtain(gum).enumerate_modules() {
|
for module in frida_gum::Process::obtain(gum).enumerate_modules() {
|
||||||
if module.base_address < Self::with_target_bytes_converter as usize
|
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)
|
&& (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(
|
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()),
|
(0xffff, "fuzzer".to_string()),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -11,7 +11,7 @@ use std::{
|
|||||||
use frida_gum::{
|
use frida_gum::{
|
||||||
instruction_writer::InstructionWriter,
|
instruction_writer::InstructionWriter,
|
||||||
stalker::{StalkerIterator, StalkerOutput, Transformer},
|
stalker::{StalkerIterator, StalkerOutput, Transformer},
|
||||||
Backend, Gum, ModuleDetails, ModuleMap, Script,
|
Backend, Gum, Module, ModuleMap, Script,
|
||||||
};
|
};
|
||||||
use frida_gum_sys::gchar;
|
use frida_gum_sys::gchar;
|
||||||
use libafl::Error;
|
use libafl::Error;
|
||||||
@ -276,8 +276,8 @@ pub struct FridaInstrumentationHelperBuilder {
|
|||||||
stalker_enabled: bool,
|
stalker_enabled: bool,
|
||||||
disable_excludes: bool,
|
disable_excludes: bool,
|
||||||
#[expect(clippy::type_complexity)]
|
#[expect(clippy::type_complexity)]
|
||||||
instrument_module_predicate: Option<Box<dyn FnMut(&ModuleDetails) -> bool>>,
|
instrument_module_predicate: Option<Box<dyn FnMut(&Module) -> bool>>,
|
||||||
skip_module_predicate: Box<dyn FnMut(&ModuleDetails) -> bool>,
|
skip_module_predicate: Box<dyn FnMut(&Module) -> bool>,
|
||||||
skip_ranges: Vec<SkipRange>,
|
skip_ranges: Vec<SkipRange>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,7 +353,7 @@ impl FridaInstrumentationHelperBuilder {
|
|||||||
/// .instrument_module_if(|module| module.path().starts_with("/usr/lib"));
|
/// .instrument_module_if(|module| module.path().starts_with("/usr/lib"));
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use]
|
#[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 self,
|
||||||
mut predicate: F,
|
mut predicate: F,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -382,10 +382,7 @@ impl FridaInstrumentationHelperBuilder {
|
|||||||
/// .skip_module_if(|module| module.name() == "libfoo.so");
|
/// .skip_module_if(|module| module.name() == "libfoo.so");
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn skip_module_if<F: FnMut(&ModuleDetails) -> bool + 'static>(
|
pub fn skip_module_if<F: FnMut(&Module) -> bool + 'static>(mut self, mut predicate: F) -> Self {
|
||||||
mut self,
|
|
||||||
mut predicate: F,
|
|
||||||
) -> Self {
|
|
||||||
let new = move |module: &_| (self.skip_module_predicate)(module) || predicate(module);
|
let new = move |module: &_| (self.skip_module_predicate)(module) || predicate(module);
|
||||||
Self {
|
Self {
|
||||||
skip_module_predicate: Box::new(new),
|
skip_module_predicate: Box::new(new),
|
||||||
@ -430,7 +427,7 @@ impl FridaInstrumentationHelperBuilder {
|
|||||||
!skip_module_predicate(&module)
|
!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();
|
let ranges = RangeMap::new();
|
||||||
// Wrap ranges and runtimes in reference-counted refcells in order to move
|
// Wrap ranges and runtimes in reference-counted refcells in order to move
|
||||||
@ -461,7 +458,7 @@ impl FridaInstrumentationHelperBuilder {
|
|||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.remove(range.start as u64..range.end as u64),
|
.remove(range.start as u64..range.end as u64),
|
||||||
SkipRange::ModuleRelative { name, range } => {
|
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;
|
let lib_start = module_details.range().base_address().0 as u64;
|
||||||
ranges.borrow_mut().remove(
|
ranges.borrow_mut().remove(
|
||||||
(lib_start + range.start as u64)..(lib_start + range.end as u64),
|
(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
|
where
|
||||||
I: IntoIterator<Item = P>,
|
I: IntoIterator<Item = P>,
|
||||||
P: AsRef<Path>,
|
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
|
// 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 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::{
|
use libafl_bolts::os::windows_exceptions::{
|
||||||
handle_exception, IsProcessorFeaturePresent, UnhandledExceptionFilter, EXCEPTION_POINTERS,
|
handle_exception, IsProcessorFeaturePresent, UnhandledExceptionFilter, EXCEPTION_POINTERS,
|
||||||
PROCESSOR_FEATURE_ID,
|
PROCESSOR_FEATURE_ID,
|
||||||
@ -21,16 +21,16 @@ unsafe extern "C" fn unhandled_exception_filter_detour(
|
|||||||
}
|
}
|
||||||
/// Initialize the hooks
|
/// Initialize the hooks
|
||||||
pub fn initialize(gum: &Gum) {
|
pub fn initialize(gum: &Gum) {
|
||||||
let module = Module::obtain(gum);
|
let module = Process::obtain(gum)
|
||||||
let is_processor_feature_present =
|
.find_module_by_name("kernel32.dll")
|
||||||
module.find_export_by_name(Some("kernel32.dll"), "IsProcessorFeaturePresent");
|
.unwrap();
|
||||||
|
let is_processor_feature_present = module.find_export_by_name("IsProcessorFeaturePresent");
|
||||||
let is_processor_feature_present = is_processor_feature_present.unwrap();
|
let is_processor_feature_present = is_processor_feature_present.unwrap();
|
||||||
assert!(
|
assert!(
|
||||||
!is_processor_feature_present.is_null(),
|
!is_processor_feature_present.is_null(),
|
||||||
"IsProcessorFeaturePresent not found"
|
"IsProcessorFeaturePresent not found"
|
||||||
);
|
);
|
||||||
let unhandled_exception_filter =
|
let unhandled_exception_filter = module.find_export_by_name("UnhandledExceptionFilter");
|
||||||
module.find_export_by_name(Some("kernel32.dll"), "UnhandledExceptionFilter");
|
|
||||||
let unhandled_exception_filter = unhandled_exception_filter.unwrap();
|
let unhandled_exception_filter = unhandled_exception_filter.unwrap();
|
||||||
assert!(
|
assert!(
|
||||||
!unhandled_exception_filter.is_null(),
|
!unhandled_exception_filter.is_null(),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user