Pr/fasan multithreading fixes upstream (#2955)
* Fixing the test_harness library name * Fasan works, but testing of all features is pending * Tests pass, before fixing clippy and fmt * CLippy+fmt * CLippy+fmt+tests running on linux * Clippy * Not stalkering the fuzzer. In the correct way * Removing the instrumentation upon crash. Proper hooking of UnmapViewOfFile * Fixes after the merge from the upstream (before 0.15.0). Still need to add the observer, clippy, fmt, and at least linux compilation * Adding the helper observer and using it in the test * Removing the observer from the wrong location * Adapting to the new helper ownership model * Adding an observer to shut down instrumentation upon crash * Clippy + fmt * Using mimalloc everywhere * Deactivating before activating with the harness. Otherwise, gets stuck on Linux. * Fixing imports for windows * Using the new way of passing the handler * Using frida_helper_shutdown_observer * Clippy+fmt * no-std, clippy * Fmt * Stable thread_id * Clippy 18 * More clippy * Formatting toml * Fixing apples * Fixing apples 2 * Fixing apples 3 * Upping to 0.16.7 (necessary for Windows) * Clippy+fmt * Enabling the allocator test after the fix and clarifying the importantce of the static runtime linking. * Moving has_tls to bolts * Proper handling of no-std, hopefully * Another attempt to fix win no-std * Not mine clippy complaint... * Not mine clippy complaint #2... * Dlmalloc not used, removing from dependencies * Restoring target in config.toml (otherwise fails CI on Linux) * lots of digging around, pray for us * fixup? * Revert "lots of digging around, pray for us" This reverts commit 706c27201918e906e3401cd0d9e76546f889d1f5. * Revert "fixup?" This reverts commit 1d7c5d4fb5b1bd31f5e0c07492aa8ed64c6822f3. * Revert artifact * Revert fixups * Removing unused * Reverting to upstream/main --------- Co-authored-by: Addison Crump <addison.crump@cispa.de> Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
This commit is contained in:
parent
f9715392af
commit
b3fe744e57
2
.gitignore
vendored
2
.gitignore
vendored
@ -75,3 +75,5 @@ harness
|
|||||||
program
|
program
|
||||||
fuzzer_libpng*
|
fuzzer_libpng*
|
||||||
forkserver_simple
|
forkserver_simple
|
||||||
|
|
||||||
|
*.patch
|
||||||
|
@ -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.16.5", features = [
|
frida-gum = { version = "0.16.7", features = [
|
||||||
"auto-download",
|
"auto-download",
|
||||||
"event-sink",
|
"event-sink",
|
||||||
"invocation-listener",
|
"invocation-listener",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
|
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
|
||||||
//! The example harness is built for libpng.
|
//! The example harness is built for libpng.
|
||||||
use std::{path::PathBuf, ptr::null};
|
use std::{cell::RefCell, path::PathBuf, ptr::null, rc::Rc};
|
||||||
|
|
||||||
use frida_gum::Gum;
|
use frida_gum::Gum;
|
||||||
use libafl::{
|
use libafl::{
|
||||||
@ -43,6 +43,7 @@ use libafl_frida::{
|
|||||||
cmplog_rt::CmpLogRuntime,
|
cmplog_rt::CmpLogRuntime,
|
||||||
coverage_rt::{CoverageRuntime, MAP_SIZE},
|
coverage_rt::{CoverageRuntime, MAP_SIZE},
|
||||||
executor::FridaInProcessExecutor,
|
executor::FridaInProcessExecutor,
|
||||||
|
frida_helper_shutdown_observer::FridaHelperObserver,
|
||||||
helper::FridaInstrumentationHelper,
|
helper::FridaInstrumentationHelper,
|
||||||
};
|
};
|
||||||
use libafl_targets::cmplog::CmpLogObserver;
|
use libafl_targets::cmplog::CmpLogObserver;
|
||||||
@ -113,16 +114,22 @@ unsafe fn fuzz(
|
|||||||
let asan = AsanRuntime::new(options);
|
let asan = AsanRuntime::new(options);
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let mut frida_helper =
|
let frida_helper = Rc::new(RefCell::new(FridaInstrumentationHelper::new(
|
||||||
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage, asan));
|
&gum,
|
||||||
|
options,
|
||||||
|
tuple_list!(coverage, asan),
|
||||||
|
)));
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let mut frida_helper =
|
let frida_helper = Rc::new(RefCell::new(FridaInstrumentationHelper::new(
|
||||||
FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage));
|
&gum,
|
||||||
|
&options,
|
||||||
|
tuple_list!(coverage),
|
||||||
|
)));
|
||||||
|
|
||||||
// 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(StdMapObserver::from_mut_ptr(
|
||||||
"edges",
|
"edges",
|
||||||
frida_helper.map_mut_ptr().unwrap(),
|
frida_helper.borrow_mut().map_mut_ptr().unwrap(),
|
||||||
MAP_SIZE,
|
MAP_SIZE,
|
||||||
))
|
))
|
||||||
.track_indices();
|
.track_indices();
|
||||||
@ -131,6 +138,7 @@ unsafe fn fuzz(
|
|||||||
let time_observer = TimeObserver::new("time");
|
let time_observer = TimeObserver::new("time");
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
|
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
|
||||||
|
let frida_helper_observer = FridaHelperObserver::new(Rc::clone(&frida_helper));
|
||||||
|
|
||||||
// Feedback to rate the interestingness of an input
|
// Feedback to rate the interestingness of an input
|
||||||
// This one is composed by two Feedbacks in OR
|
// This one is composed by two Feedbacks in OR
|
||||||
@ -196,9 +204,14 @@ unsafe fn fuzz(
|
|||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let observers = tuple_list!(edges_observer, time_observer, asan_observer);
|
let observers = tuple_list!(
|
||||||
|
frida_helper_observer,
|
||||||
|
edges_observer,
|
||||||
|
time_observer,
|
||||||
|
asan_observer
|
||||||
|
);
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let observers = tuple_list!(edges_observer, time_observer);
|
let observers = tuple_list!(frida_helper_observer, 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 mut executor = FridaInProcessExecutor::new(
|
||||||
@ -210,7 +223,7 @@ unsafe fn fuzz(
|
|||||||
&mut state,
|
&mut state,
|
||||||
&mut mgr,
|
&mut mgr,
|
||||||
)?,
|
)?,
|
||||||
&mut frida_helper,
|
Rc::clone(&frida_helper),
|
||||||
);
|
);
|
||||||
|
|
||||||
// In case the corpus is empty (on first run), reset
|
// In case the corpus is empty (on first run), reset
|
||||||
@ -238,13 +251,16 @@ unsafe fn fuzz(
|
|||||||
let coverage = CoverageRuntime::new();
|
let coverage = CoverageRuntime::new();
|
||||||
let cmplog = CmpLogRuntime::new();
|
let cmplog = CmpLogRuntime::new();
|
||||||
|
|
||||||
let mut frida_helper =
|
let mut frida_helper = Rc::new(RefCell::new(FridaInstrumentationHelper::new(
|
||||||
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage, cmplog));
|
&gum,
|
||||||
|
options,
|
||||||
|
tuple_list!(coverage, cmplog),
|
||||||
|
)));
|
||||||
|
|
||||||
// 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(StdMapObserver::from_mut_ptr(
|
||||||
"edges",
|
"edges",
|
||||||
frida_helper.map_mut_ptr().unwrap(),
|
frida_helper.borrow_mut().map_mut_ptr().unwrap(),
|
||||||
MAP_SIZE,
|
MAP_SIZE,
|
||||||
))
|
))
|
||||||
.track_indices();
|
.track_indices();
|
||||||
@ -253,6 +269,7 @@ unsafe fn fuzz(
|
|||||||
let time_observer = TimeObserver::new("time");
|
let time_observer = TimeObserver::new("time");
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
|
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
|
||||||
|
let frida_helper_observer = FridaHelperObserver::new(Rc::clone(&frida_helper));
|
||||||
|
|
||||||
// Feedback to rate the interestingness of an input
|
// Feedback to rate the interestingness of an input
|
||||||
// This one is composed by two Feedbacks in OR
|
// This one is composed by two Feedbacks in OR
|
||||||
@ -316,9 +333,14 @@ unsafe fn fuzz(
|
|||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let observers = tuple_list!(edges_observer, time_observer, asan_observer);
|
let observers = tuple_list!(
|
||||||
|
frida_helper_observer,
|
||||||
|
edges_observer,
|
||||||
|
time_observer,
|
||||||
|
asan_observer
|
||||||
|
);
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let observers = tuple_list!(edges_observer, time_observer);
|
let observers = tuple_list!(frida_helper_observer, 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 mut executor = FridaInProcessExecutor::new(
|
||||||
@ -330,7 +352,7 @@ unsafe fn fuzz(
|
|||||||
&mut state,
|
&mut state,
|
||||||
&mut mgr,
|
&mut mgr,
|
||||||
)?,
|
)?,
|
||||||
&mut frida_helper,
|
Rc::clone(&frida_helper),
|
||||||
);
|
);
|
||||||
|
|
||||||
// In case the corpus is empty (on first run), reset
|
// In case the corpus is empty (on first run), reset
|
||||||
@ -373,13 +395,16 @@ unsafe fn fuzz(
|
|||||||
|
|
||||||
let coverage = CoverageRuntime::new();
|
let coverage = CoverageRuntime::new();
|
||||||
|
|
||||||
let mut frida_helper =
|
let mut frida_helper = Rc::new(RefCell::new(FridaInstrumentationHelper::new(
|
||||||
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage));
|
&gum,
|
||||||
|
options,
|
||||||
|
tuple_list!(coverage),
|
||||||
|
)));
|
||||||
|
|
||||||
// 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(StdMapObserver::from_mut_ptr(
|
||||||
"edges",
|
"edges",
|
||||||
frida_helper.map_mut_ptr().unwrap(),
|
frida_helper.borrow_mut().map_mut_ptr().unwrap(),
|
||||||
MAP_SIZE,
|
MAP_SIZE,
|
||||||
))
|
))
|
||||||
.track_indices();
|
.track_indices();
|
||||||
@ -388,6 +413,7 @@ unsafe fn fuzz(
|
|||||||
let time_observer = TimeObserver::new("time");
|
let time_observer = TimeObserver::new("time");
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
|
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
|
||||||
|
let frida_helper_observer = FridaHelperObserver::new(Rc::clone(&frida_helper));
|
||||||
|
|
||||||
// Feedback to rate the interestingness of an input
|
// Feedback to rate the interestingness of an input
|
||||||
// This one is composed by two Feedbacks in OR
|
// This one is composed by two Feedbacks in OR
|
||||||
@ -451,9 +477,14 @@ unsafe fn fuzz(
|
|||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let observers = tuple_list!(edges_observer, time_observer, asan_observer);
|
let observers = tuple_list!(
|
||||||
|
frida_helper_observer,
|
||||||
|
edges_observer,
|
||||||
|
time_observer,
|
||||||
|
asan_observer
|
||||||
|
);
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let observers = tuple_list!(edges_observer, time_observer);
|
let observers = tuple_list!(frida_helper_observer, 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 mut executor = FridaInProcessExecutor::new(
|
||||||
@ -465,7 +496,7 @@ unsafe fn fuzz(
|
|||||||
&mut state,
|
&mut state,
|
||||||
&mut mgr,
|
&mut mgr,
|
||||||
)?,
|
)?,
|
||||||
&mut frida_helper,
|
Rc::clone(&frida_helper),
|
||||||
);
|
);
|
||||||
|
|
||||||
// In case the corpus is empty (on first run), reset
|
// In case the corpus is empty (on first run), reset
|
||||||
|
@ -27,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.16.5", features = [
|
frida-gum = { version = "0.16.7", features = [
|
||||||
"auto-download",
|
"auto-download",
|
||||||
"event-sink",
|
"event-sink",
|
||||||
"invocation-listener",
|
"invocation-listener",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
|
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
|
||||||
//! The example harness is built for libpng.
|
//! The example harness is built for libpng.
|
||||||
use std::path::PathBuf;
|
use std::{cell::RefCell, path::PathBuf, rc::Rc};
|
||||||
|
|
||||||
use frida_gum::Gum;
|
use frida_gum::Gum;
|
||||||
use libafl::{
|
use libafl::{
|
||||||
@ -40,6 +40,7 @@ use libafl_frida::{
|
|||||||
cmplog_rt::CmpLogRuntime,
|
cmplog_rt::CmpLogRuntime,
|
||||||
coverage_rt::{CoverageRuntime, MAP_SIZE},
|
coverage_rt::{CoverageRuntime, MAP_SIZE},
|
||||||
executor::FridaInProcessExecutor,
|
executor::FridaInProcessExecutor,
|
||||||
|
frida_helper_shutdown_observer::FridaHelperObserver,
|
||||||
helper::{FridaInstrumentationHelper, IfElseRuntime},
|
helper::{FridaInstrumentationHelper, IfElseRuntime},
|
||||||
};
|
};
|
||||||
use libafl_targets::cmplog::CmpLogObserver;
|
use libafl_targets::cmplog::CmpLogObserver;
|
||||||
@ -104,7 +105,7 @@ fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
let options_clone = options.clone();
|
let options_clone = options.clone();
|
||||||
let client_description_clone2 = client_description.clone();
|
let client_description_clone2 = client_description.clone();
|
||||||
let options_clone2 = options.clone();
|
let options_clone2 = options.clone();
|
||||||
let mut frida_helper = FridaInstrumentationHelper::new(
|
let frida_helper = Rc::new(RefCell::new(FridaInstrumentationHelper::new(
|
||||||
&gum,
|
&gum,
|
||||||
options,
|
options,
|
||||||
tuple_list!(
|
tuple_list!(
|
||||||
@ -120,17 +121,22 @@ fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
),
|
),
|
||||||
coverage
|
coverage
|
||||||
),
|
),
|
||||||
);
|
)));
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
// Create an observation channel using the coverage map
|
||||||
let edges_observer = HitcountsMapObserver::new(unsafe {
|
let edges_observer = HitcountsMapObserver::new(unsafe {
|
||||||
StdMapObserver::from_mut_ptr("edges", frida_helper.map_mut_ptr().unwrap(), MAP_SIZE)
|
StdMapObserver::from_mut_ptr(
|
||||||
|
"edges",
|
||||||
|
frida_helper.borrow_mut().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
|
||||||
let time_observer = TimeObserver::new("time");
|
let time_observer = TimeObserver::new("time");
|
||||||
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
|
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
|
||||||
|
let frida_helper_observer = FridaHelperObserver::new(Rc::clone(&frida_helper));
|
||||||
|
|
||||||
// Feedback to rate the interestingness of an input
|
// Feedback to rate the interestingness of an input
|
||||||
// This one is composed by two Feedbacks in OR
|
// This one is composed by two Feedbacks in OR
|
||||||
@ -187,7 +193,12 @@ 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);
|
||||||
|
|
||||||
let observers = tuple_list!(edges_observer, time_observer, asan_observer);
|
let observers = tuple_list!(
|
||||||
|
frida_helper_observer,
|
||||||
|
edges_observer,
|
||||||
|
time_observer,
|
||||||
|
asan_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 executor = FridaInProcessExecutor::new(
|
let executor = FridaInProcessExecutor::new(
|
||||||
@ -200,7 +211,7 @@ fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
&mut mgr,
|
&mut mgr,
|
||||||
options.timeout,
|
options.timeout,
|
||||||
)?,
|
)?,
|
||||||
&mut frida_helper,
|
Rc::clone(&frida_helper),
|
||||||
);
|
);
|
||||||
// Create an observation channel using cmplog map
|
// Create an observation channel using cmplog map
|
||||||
let cmplog_observer = CmpLogObserver::new("cmplog", true);
|
let cmplog_observer = CmpLogObserver::new("cmplog", true);
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
[build]
|
||||||
|
rustflags = ["-C", "target-feature=+crt-static"]
|
@ -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.16.5", features = [
|
frida-gum = { version = "0.16.7", features = [
|
||||||
"auto-download",
|
"auto-download",
|
||||||
"event-sink",
|
"event-sink",
|
||||||
"invocation-listener",
|
"invocation-listener",
|
||||||
|
@ -9,6 +9,8 @@ To build this example, run `cargo build --release` in this folder.
|
|||||||
|
|
||||||
Then compile the harness `cl.exe /LD harness.cc /link /dll gdiplus.lib ole32.lib`
|
Then compile the harness `cl.exe /LD harness.cc /link /dll gdiplus.lib ole32.lib`
|
||||||
|
|
||||||
|
Note: this fuzzer is **statically linked** with C runtime. This is achieved by specifying `rustflags = ["-C", "target-feature=+crt-static"]` in `.cargo/config.toml`. The static linking is necessary to avoid Asan function hooks to hook the calls from the fuzzer itself, as such self-hooking can eventually lead to deadlocks in internal Frida mechanisms.
|
||||||
|
|
||||||
## Run
|
## Run
|
||||||
|
|
||||||
To run the example `target\release\frida_windows_gdiplus.exe -H harness.dll -i corpus -o output --libs-to-instrument gdi32.dll --libs-to-instrument gdi32full.dll --libs-to-instrument gdiplus.dll --libs-to-instrument WindowsCodecs.dll --disable-excludes`
|
To run the example `target\release\frida_windows_gdiplus.exe -H harness.dll -i corpus -o output --libs-to-instrument gdi32.dll --libs-to-instrument gdi32full.dll --libs-to-instrument gdiplus.dll --libs-to-instrument WindowsCodecs.dll --disable-excludes`
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
[build]
|
|
||||||
target = "x86_64-pc-windows-msvc"
|
|
@ -10,7 +10,7 @@ use mimalloc::MiMalloc;
|
|||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static GLOBAL: MiMalloc = MiMalloc;
|
static GLOBAL: MiMalloc = MiMalloc;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::{cell::RefCell, path::PathBuf, rc::Rc};
|
||||||
|
|
||||||
use frida_gum::Gum;
|
use frida_gum::Gum;
|
||||||
use libafl::{
|
use libafl::{
|
||||||
@ -50,6 +50,7 @@ use libafl_frida::{
|
|||||||
cmplog_rt::CmpLogRuntime,
|
cmplog_rt::CmpLogRuntime,
|
||||||
coverage_rt::{CoverageRuntime, MAP_SIZE},
|
coverage_rt::{CoverageRuntime, MAP_SIZE},
|
||||||
executor::FridaInProcessExecutor,
|
executor::FridaInProcessExecutor,
|
||||||
|
frida_helper_shutdown_observer::FridaHelperObserver,
|
||||||
helper::FridaInstrumentationHelper,
|
helper::FridaInstrumentationHelper,
|
||||||
};
|
};
|
||||||
use libafl_targets::cmplog::CmpLogObserver;
|
use libafl_targets::cmplog::CmpLogObserver;
|
||||||
@ -105,13 +106,16 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
let coverage = CoverageRuntime::new();
|
let coverage = CoverageRuntime::new();
|
||||||
let asan = AsanRuntime::new(options);
|
let asan = AsanRuntime::new(options);
|
||||||
|
|
||||||
let mut frida_helper =
|
let frida_helper = Rc::new(RefCell::new(FridaInstrumentationHelper::new(
|
||||||
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage, asan));
|
&gum,
|
||||||
|
options,
|
||||||
|
tuple_list!(coverage, asan),
|
||||||
|
)));
|
||||||
//
|
//
|
||||||
// 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(StdMapObserver::from_mut_ptr(
|
||||||
"edges",
|
"edges",
|
||||||
frida_helper.map_mut_ptr().unwrap(),
|
frida_helper.borrow_mut().map_mut_ptr().unwrap(),
|
||||||
MAP_SIZE,
|
MAP_SIZE,
|
||||||
))
|
))
|
||||||
.track_indices();
|
.track_indices();
|
||||||
@ -121,6 +125,8 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
|
|
||||||
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
|
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
|
||||||
|
|
||||||
|
let frida_helper_observer = FridaHelperObserver::new(Rc::clone(&frida_helper));
|
||||||
|
|
||||||
// Feedback to rate the interestingness of an input
|
// Feedback to rate the interestingness of an input
|
||||||
// This one is composed by two Feedbacks in OR
|
// This one is composed by two Feedbacks in OR
|
||||||
let mut feedback = feedback_or!(
|
let mut feedback = feedback_or!(
|
||||||
@ -180,7 +186,12 @@ 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);
|
||||||
|
|
||||||
let observers = tuple_list!(edges_observer, time_observer, asan_observer,);
|
let observers = tuple_list!(
|
||||||
|
frida_helper_observer,
|
||||||
|
edges_observer,
|
||||||
|
time_observer,
|
||||||
|
asan_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 mut executor = FridaInProcessExecutor::new(
|
||||||
@ -193,7 +204,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
&mut mgr,
|
&mut mgr,
|
||||||
options.timeout,
|
options.timeout,
|
||||||
)?,
|
)?,
|
||||||
&mut frida_helper,
|
Rc::clone(&frida_helper),
|
||||||
);
|
);
|
||||||
|
|
||||||
// In case the corpus is empty (on first run), reset
|
// In case the corpus is empty (on first run), reset
|
||||||
@ -220,14 +231,16 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
|
|
||||||
let coverage = CoverageRuntime::new();
|
let coverage = CoverageRuntime::new();
|
||||||
let cmplog = CmpLogRuntime::new();
|
let cmplog = CmpLogRuntime::new();
|
||||||
|
let frida_helper = Rc::new(RefCell::new(FridaInstrumentationHelper::new(
|
||||||
let mut frida_helper =
|
&gum,
|
||||||
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage, cmplog));
|
options,
|
||||||
|
tuple_list!(coverage, cmplog),
|
||||||
|
)));
|
||||||
|
|
||||||
// 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(StdMapObserver::from_mut_ptr(
|
||||||
"edges",
|
"edges",
|
||||||
frida_helper.map_mut_ptr().unwrap(),
|
frida_helper.borrow_mut().map_mut_ptr().unwrap(),
|
||||||
MAP_SIZE,
|
MAP_SIZE,
|
||||||
))
|
))
|
||||||
.track_indices();
|
.track_indices();
|
||||||
@ -235,6 +248,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
// Create an observation channel to keep track of the execution time
|
// Create an observation channel to keep track of the execution time
|
||||||
let time_observer = TimeObserver::new("time");
|
let time_observer = TimeObserver::new("time");
|
||||||
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
|
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
|
||||||
|
let frida_helper_observer = FridaHelperObserver::new(Rc::clone(&frida_helper));
|
||||||
|
|
||||||
// Feedback to rate the interestingness of an input
|
// Feedback to rate the interestingness of an input
|
||||||
// This one is composed by two Feedbacks in OR
|
// This one is composed by two Feedbacks in OR
|
||||||
@ -294,7 +308,12 @@ 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);
|
||||||
|
|
||||||
let observers = tuple_list!(edges_observer, time_observer, asan_observer);
|
let observers = tuple_list!(
|
||||||
|
frida_helper_observer,
|
||||||
|
edges_observer,
|
||||||
|
time_observer,
|
||||||
|
asan_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 mut executor = FridaInProcessExecutor::new(
|
||||||
@ -306,7 +325,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
&mut state,
|
&mut state,
|
||||||
&mut mgr,
|
&mut mgr,
|
||||||
)?,
|
)?,
|
||||||
&mut frida_helper,
|
Rc::clone(&frida_helper),
|
||||||
);
|
);
|
||||||
|
|
||||||
// In case the corpus is empty (on first run), reset
|
// In case the corpus is empty (on first run), reset
|
||||||
@ -350,13 +369,16 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
|
|
||||||
let coverage = CoverageRuntime::new();
|
let coverage = CoverageRuntime::new();
|
||||||
|
|
||||||
let mut frida_helper =
|
let frida_helper = Rc::new(RefCell::new(FridaInstrumentationHelper::new(
|
||||||
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage));
|
&gum,
|
||||||
|
options,
|
||||||
|
tuple_list!(coverage),
|
||||||
|
)));
|
||||||
|
|
||||||
// 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(StdMapObserver::from_mut_ptr(
|
||||||
"edges",
|
"edges",
|
||||||
frida_helper.map_mut_ptr().unwrap(),
|
frida_helper.borrow_mut().map_mut_ptr().unwrap(),
|
||||||
MAP_SIZE,
|
MAP_SIZE,
|
||||||
))
|
))
|
||||||
.track_indices();
|
.track_indices();
|
||||||
@ -366,6 +388,8 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
|
|
||||||
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
|
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
|
||||||
|
|
||||||
|
let frida_helper_observer = FridaHelperObserver::new(Rc::clone(&frida_helper));
|
||||||
|
|
||||||
// Feedback to rate the interestingness of an input
|
// Feedback to rate the interestingness of an input
|
||||||
// This one is composed by two Feedbacks in OR
|
// This one is composed by two Feedbacks in OR
|
||||||
let mut feedback = feedback_or!(
|
let mut feedback = feedback_or!(
|
||||||
@ -424,7 +448,12 @@ 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);
|
||||||
|
|
||||||
let observers = tuple_list!(edges_observer, time_observer, asan_observer);
|
let observers = tuple_list!(
|
||||||
|
frida_helper_observer,
|
||||||
|
edges_observer,
|
||||||
|
time_observer,
|
||||||
|
asan_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 mut executor = FridaInProcessExecutor::new(
|
||||||
@ -437,7 +466,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
&mut mgr,
|
&mut mgr,
|
||||||
options.timeout,
|
options.timeout,
|
||||||
)?,
|
)?,
|
||||||
&mut frida_helper,
|
Rc::clone(&frida_helper),
|
||||||
);
|
);
|
||||||
|
|
||||||
// In case the corpus is empty (on first run), reset
|
// In case the corpus is empty (on first run), reset
|
||||||
|
@ -18,7 +18,8 @@ pub fn main() {
|
|||||||
|
|
||||||
let mut cc = ClangWrapper::new();
|
let mut cc = ClangWrapper::new();
|
||||||
if let Some(code) = cc
|
if let Some(code) = cc
|
||||||
.cpp(is_cpp)
|
.cpp(true) // Link with C++ standard library (Frida links to it in order to hook C++ functions)
|
||||||
|
// .cpp(is_cpp)
|
||||||
// silence the compiler wrapper output, needed for some configure scripts.
|
// silence the compiler wrapper output, needed for some configure scripts.
|
||||||
.silence(true)
|
.silence(true)
|
||||||
.parse_args(&args)
|
.parse_args(&args)
|
||||||
|
@ -327,6 +327,12 @@ windows = { workspace = true, features = [
|
|||||||
"Win32_Security",
|
"Win32_Security",
|
||||||
"Win32_System_SystemInformation",
|
"Win32_System_SystemInformation",
|
||||||
] }
|
] }
|
||||||
|
winapi = { version = "0.3", features = [
|
||||||
|
"dbghelp",
|
||||||
|
"minwindef",
|
||||||
|
"winnt",
|
||||||
|
"errhandlingapi",
|
||||||
|
"processthreadsapi",
|
||||||
|
] }
|
||||||
[target.'cfg(windows)'.build-dependencies]
|
[target.'cfg(windows)'.build-dependencies]
|
||||||
windows = { workspace = true }
|
windows = { workspace = true }
|
||||||
|
@ -369,7 +369,15 @@ pub mod windows_exception_handler {
|
|||||||
|
|
||||||
let exception_list = data.exceptions();
|
let exception_list = data.exceptions();
|
||||||
if exception_list.contains(&code) {
|
if exception_list.contains(&code) {
|
||||||
log::error!("Crashed with {code}");
|
log::error!(
|
||||||
|
"Crashed with {code} at {:?} in thread {:?}",
|
||||||
|
exception_pointers
|
||||||
|
.ExceptionRecord
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.ExceptionAddress,
|
||||||
|
winapi::um::processthreadsapi::GetCurrentThreadId()
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// log::trace!("Exception code received, but {code} is not in CRASH_EXCEPTIONS");
|
// log::trace!("Exception code received, but {code} is not in CRASH_EXCEPTIONS");
|
||||||
is_crash = false;
|
is_crash = false;
|
||||||
@ -422,7 +430,17 @@ pub mod windows_exception_handler {
|
|||||||
|
|
||||||
// Make sure we don't crash in the crash handler forever.
|
// Make sure we don't crash in the crash handler forever.
|
||||||
if is_crash {
|
if is_crash {
|
||||||
let input = data.take_current_input::<I>();
|
log::warn!("Running observers and exiting!");
|
||||||
|
// // I want to disable the hooks before doing anything, especially before taking a stack dump
|
||||||
|
let input = data.take_current_input::<I>(); // log::set_max_level(log::LevelFilter::Trace);
|
||||||
|
run_observers_and_save_state::<E, EM, I, OF, S, Z>(
|
||||||
|
executor,
|
||||||
|
state,
|
||||||
|
input,
|
||||||
|
fuzzer,
|
||||||
|
event_mgr,
|
||||||
|
ExitKind::Crash,
|
||||||
|
);
|
||||||
{
|
{
|
||||||
let mut bsod = Vec::new();
|
let mut bsod = Vec::new();
|
||||||
{
|
{
|
||||||
@ -434,14 +452,6 @@ pub mod windows_exception_handler {
|
|||||||
}
|
}
|
||||||
log::error!("{}", std::str::from_utf8(&bsod).unwrap());
|
log::error!("{}", std::str::from_utf8(&bsod).unwrap());
|
||||||
}
|
}
|
||||||
run_observers_and_save_state::<E, EM, I, OF, S, Z>(
|
|
||||||
executor,
|
|
||||||
state,
|
|
||||||
input,
|
|
||||||
fuzzer,
|
|
||||||
event_mgr,
|
|
||||||
ExitKind::Crash,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
// This is not worth saving
|
// This is not worth saving
|
||||||
}
|
}
|
||||||
|
@ -185,6 +185,15 @@ windows = { workspace = true, features = [
|
|||||||
"Win32_System_SystemInformation",
|
"Win32_System_SystemInformation",
|
||||||
"Win32_System_Console",
|
"Win32_System_Console",
|
||||||
] }
|
] }
|
||||||
|
once_cell = "1.10.0"
|
||||||
|
winapi = { version = "0.3", features = [
|
||||||
|
"fileapi",
|
||||||
|
"handleapi",
|
||||||
|
"processenv",
|
||||||
|
"processthreadsapi",
|
||||||
|
"winbase",
|
||||||
|
"winnt",
|
||||||
|
] }
|
||||||
windows-result = "0.3.0"
|
windows-result = "0.3.0"
|
||||||
|
|
||||||
[target.'cfg(windows)'.build-dependencies]
|
[target.'cfg(windows)'.build-dependencies]
|
||||||
|
@ -970,6 +970,109 @@ impl SimpleStdoutLogger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
#[must_use]
|
||||||
|
/// Return thread ID without using TLS
|
||||||
|
pub fn get_thread_id() -> u64 {
|
||||||
|
use std::arch::asm;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
unsafe {
|
||||||
|
let teb: *const u8;
|
||||||
|
asm!("mov {}, gs:[0x30]", out(reg) teb);
|
||||||
|
let thread_id_ptr = teb.add(0x48) as *const u32;
|
||||||
|
u64::from(*thread_id_ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86")]
|
||||||
|
unsafe {
|
||||||
|
let teb: *const u8;
|
||||||
|
asm!("mov {}, fs:[0x18]", out(reg) teb);
|
||||||
|
let thread_id_ptr = teb.add(0x24) as *const u32;
|
||||||
|
*thread_id_ptr as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
#[must_use]
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
|
/// Return thread ID without using TLS
|
||||||
|
pub fn get_thread_id() -> u64 {
|
||||||
|
use libc::{syscall, SYS_gettid};
|
||||||
|
|
||||||
|
unsafe { syscall(SYS_gettid) as u64 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
|
||||||
|
#[must_use]
|
||||||
|
/// Return thread ID using Rust's `std::thread`
|
||||||
|
pub fn get_thread_id() -> u64 {
|
||||||
|
// Fallback for other platforms
|
||||||
|
let thread_id = std::thread::current().id();
|
||||||
|
unsafe { mem::transmute::<_, u64>(thread_id) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
mod windows_logging {
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
use winapi::um::{
|
||||||
|
fileapi::WriteFile, handleapi::INVALID_HANDLE_VALUE, processenv::GetStdHandle,
|
||||||
|
winbase::STD_OUTPUT_HANDLE, winnt::HANDLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Safe wrapper around HANDLE
|
||||||
|
struct StdOutHandle(HANDLE);
|
||||||
|
|
||||||
|
// Implement Send and Sync for StdOutHandle, assuming it's safe to share
|
||||||
|
unsafe impl Send for StdOutHandle {}
|
||||||
|
unsafe impl Sync for StdOutHandle {}
|
||||||
|
|
||||||
|
static H_STDOUT: OnceCell<StdOutHandle> = OnceCell::new();
|
||||||
|
|
||||||
|
fn get_stdout_handle() -> HANDLE {
|
||||||
|
H_STDOUT
|
||||||
|
.get_or_init(|| {
|
||||||
|
let handle = unsafe { GetStdHandle(STD_OUTPUT_HANDLE) };
|
||||||
|
StdOutHandle(handle)
|
||||||
|
})
|
||||||
|
.0
|
||||||
|
}
|
||||||
|
/// A function that writes directly to stdout using `WinAPI`.
|
||||||
|
/// Works much faster than println and does not need TLS
|
||||||
|
pub fn direct_log(message: &str) {
|
||||||
|
// Get the handle to standard output
|
||||||
|
let h_stdout: HANDLE = get_stdout_handle();
|
||||||
|
|
||||||
|
if h_stdout == INVALID_HANDLE_VALUE {
|
||||||
|
eprintln!("Failed to get standard output handle");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let bytes = message.as_bytes();
|
||||||
|
let mut bytes_written = 0;
|
||||||
|
|
||||||
|
// Write the message to standard output
|
||||||
|
let result = unsafe {
|
||||||
|
WriteFile(
|
||||||
|
h_stdout,
|
||||||
|
bytes.as_ptr() as *const _,
|
||||||
|
bytes.len() as u32,
|
||||||
|
&mut bytes_written,
|
||||||
|
ptr::null_mut(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if result == 0 {
|
||||||
|
eprintln!("Failed to write to standard output");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl log::Log for SimpleStdoutLogger {
|
impl log::Log for SimpleStdoutLogger {
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -977,16 +1080,32 @@ impl log::Log for SimpleStdoutLogger {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
fn log(&self, record: &Record) {
|
fn log(&self, record: &Record) {
|
||||||
println!(
|
println!(
|
||||||
"[{:?}, {:?}] {}: {}",
|
"[{:?}, {:?}:{:?}] {}: {}",
|
||||||
current_time(),
|
current_time(),
|
||||||
std::process::id(),
|
std::process::id(),
|
||||||
|
get_thread_id(),
|
||||||
record.level(),
|
record.level(),
|
||||||
record.args()
|
record.args()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn log(&self, record: &Record) {
|
||||||
|
// println is not safe in TLS-less environment
|
||||||
|
let msg = format!(
|
||||||
|
"[{:?}, {:?}:{:?}] {}: {}\n",
|
||||||
|
current_time(),
|
||||||
|
std::process::id(),
|
||||||
|
get_thread_id(),
|
||||||
|
record.level(),
|
||||||
|
record.args()
|
||||||
|
);
|
||||||
|
windows_logging::direct_log(msg.as_str());
|
||||||
|
}
|
||||||
|
|
||||||
fn flush(&self) {}
|
fn flush(&self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1143,6 +1262,65 @@ pub unsafe fn set_error_print_panic_hook(new_stderr: RawFd) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
|
struct TEB {
|
||||||
|
reserved1: [u8; 0x58],
|
||||||
|
tls_pointer: *mut *mut u8,
|
||||||
|
reserved2: [u8; 0xC0],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
#[inline(always)]
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn nt_current_teb() -> *mut TEB {
|
||||||
|
use std::arch::asm;
|
||||||
|
let teb: *mut TEB;
|
||||||
|
unsafe {
|
||||||
|
asm!("mov {}, gs:0x30", out(reg) teb);
|
||||||
|
}
|
||||||
|
teb
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Some of our hooks can be invoked from threads that do not have TLS yet.
|
||||||
|
/// Many Rust and Frida functions require TLS to be set up, so we need to check if we have TLS.
|
||||||
|
/// This was observed on Windows, so for now for other platforms we assume that we have TLS.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[inline]
|
||||||
|
#[allow(unreachable_code)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn has_tls() -> bool {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
unsafe {
|
||||||
|
let teb = nt_current_teb();
|
||||||
|
if teb.is_null() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tls_array = (*teb).tls_pointer;
|
||||||
|
if tls_array.is_null() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
unsafe {
|
||||||
|
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;
|
||||||
|
return tlsptr.add(0x102).read() != 0u64;
|
||||||
|
}
|
||||||
|
// Default
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
/// Zero-cost way to construct [`core::num::NonZeroUsize`] at compile-time.
|
/// Zero-cost way to construct [`core::num::NonZeroUsize`] at compile-time.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! nonzero {
|
macro_rules! nonzero {
|
||||||
|
@ -62,17 +62,18 @@ 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.16.5", features = [
|
frida-gum-sys = { version = "0.16.7", features = [
|
||||||
"event-sink",
|
"event-sink",
|
||||||
"invocation-listener",
|
"invocation-listener",
|
||||||
] }
|
] }
|
||||||
frida-gum = { version = "0.16.5", features = [
|
frida-gum = { version = "0.16.7", features = [
|
||||||
"event-sink",
|
"event-sink",
|
||||||
"invocation-listener",
|
"invocation-listener",
|
||||||
"module-names",
|
"module-names",
|
||||||
"script",
|
"script",
|
||||||
"backtrace",
|
"backtrace",
|
||||||
] }
|
] }
|
||||||
|
os-thread-local = "0.1.3"
|
||||||
dynasmrt = "3.0.1"
|
dynasmrt = "3.0.1"
|
||||||
|
|
||||||
color-backtrace = { version = "0.7.0", features = ["resolve-modules"] }
|
color-backtrace = { version = "0.7.0", features = ["resolve-modules"] }
|
||||||
@ -112,6 +113,15 @@ iced-x86 = { version = "1.21.0", features = ["code_asm"], optional = true }
|
|||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winsafe = { version = "0.0.22", features = ["kernel"] }
|
winsafe = { version = "0.0.22", features = ["kernel"] }
|
||||||
|
winapi = { version = "0.3", features = [
|
||||||
|
"processthreadsapi",
|
||||||
|
"winnt",
|
||||||
|
"memoryapi",
|
||||||
|
"errhandlingapi",
|
||||||
|
"debugapi",
|
||||||
|
] }
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
errno = "0.2"
|
||||||
[target.'cfg(target_vendor="apple")'.dependencies]
|
[target.'cfg(target_vendor="apple")'.dependencies]
|
||||||
mach-sys = { version = "0.5.4" }
|
mach-sys = { version = "0.5.4" }
|
||||||
|
@ -57,7 +57,7 @@ fn main() {
|
|||||||
cmd.arg("/dll").arg(format!(
|
cmd.arg("/dll").arg(format!(
|
||||||
"/OUT:{}",
|
"/OUT:{}",
|
||||||
Path::new(&out_dir)
|
Path::new(&out_dir)
|
||||||
.join("test_harness.so")
|
.join("test_harness.dll")
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
));
|
));
|
||||||
|
@ -200,7 +200,10 @@ impl Allocator {
|
|||||||
}
|
}
|
||||||
metadata
|
metadata
|
||||||
} else {
|
} else {
|
||||||
// log::trace!("{:x}, {:x}", self.current_mapping_addr, rounded_up_size);
|
// log::info!(
|
||||||
|
// "Mapping {:x}, size {rounded_up_size:x}",
|
||||||
|
// self.current_mapping_addr
|
||||||
|
// );
|
||||||
let mapping = match MmapOptions::new(rounded_up_size)
|
let mapping = match MmapOptions::new(rounded_up_size)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.with_address(self.current_mapping_addr)
|
.with_address(self.current_mapping_addr)
|
||||||
@ -247,18 +250,14 @@ 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::info!(
|
// log::info!("serving address: {address:?}, size: {size:x}");
|
||||||
"serving address: {:#x}, size: {:#x}",
|
|
||||||
address as usize,
|
|
||||||
size
|
|
||||||
);
|
|
||||||
address
|
address
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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::info!("release {:?}", ptr);
|
// log::info!("releasing {:?}", 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()
|
||||||
@ -405,14 +404,14 @@ impl Allocator {
|
|||||||
unpoison: bool,
|
unpoison: bool,
|
||||||
) -> (usize, usize) {
|
) -> (usize, usize) {
|
||||||
let shadow_mapping_start = map_to_shadow!(self, start);
|
let shadow_mapping_start = map_to_shadow!(self, start);
|
||||||
log::trace!("map_shadow_for_region: {:x}, {:x}", start, end);
|
// log::trace!("map_shadow_for_region: {:x}, {:x}", start, end);
|
||||||
let shadow_start = self.round_down_to_page(shadow_mapping_start);
|
let shadow_start = self.round_down_to_page(shadow_mapping_start);
|
||||||
let shadow_end = self.round_up_to_page((end - start) / 8 + self.page_size + shadow_start);
|
let shadow_end = self.round_up_to_page((end - start) / 8 + self.page_size + shadow_start);
|
||||||
log::trace!(
|
// log::trace!(
|
||||||
"map_shadow_for_region: shadow_start {:x}, shadow_end {:x}",
|
// "map_shadow_for_region: shadow_start {:x}, shadow_end {:x}",
|
||||||
shadow_start,
|
// shadow_start,
|
||||||
shadow_end
|
// shadow_end
|
||||||
);
|
// );
|
||||||
if self.using_pre_allocated_shadow_mapping {
|
if self.using_pre_allocated_shadow_mapping {
|
||||||
let mut newly_committed_regions = Vec::new();
|
let mut newly_committed_regions = Vec::new();
|
||||||
for gap in self.shadow_pages.gaps(&(shadow_start..shadow_end)) {
|
for gap in self.shadow_pages.gaps(&(shadow_start..shadow_end)) {
|
||||||
@ -441,11 +440,11 @@ impl Allocator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for newly_committed_region in newly_committed_regions {
|
for newly_committed_region in newly_committed_regions {
|
||||||
log::trace!(
|
// log::trace!(
|
||||||
"committed shadow pages: start {:x}, end {:x}",
|
// "committed shadow pages: start {:x}, end {:x}",
|
||||||
newly_committed_region.start(),
|
// newly_committed_region.start(),
|
||||||
newly_committed_region.end()
|
// newly_committed_region.end()
|
||||||
);
|
// );
|
||||||
self.shadow_pages
|
self.shadow_pages
|
||||||
.insert(newly_committed_region.start()..newly_committed_region.end());
|
.insert(newly_committed_region.start()..newly_committed_region.end());
|
||||||
self.mappings
|
self.mappings
|
||||||
@ -571,7 +570,21 @@ impl Allocator {
|
|||||||
map_to_shadow!(self, start)
|
map_to_shadow!(self, start)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the current address is one of ours - is this address in the allocator region
|
/// Is this a valid and mapped shadow address?
|
||||||
|
#[must_use]
|
||||||
|
pub fn valid_shadow(&self, start: usize, size: usize) -> bool {
|
||||||
|
let range_to_check = start..(start + size);
|
||||||
|
let valid = self
|
||||||
|
.shadow_pages
|
||||||
|
.overlapping(&range_to_check)
|
||||||
|
.any(|r| r.start <= start && r.end >= start + size);
|
||||||
|
|
||||||
|
if !valid {
|
||||||
|
log::error!("Not a valid shadow: {:#x}!", start);
|
||||||
|
}
|
||||||
|
valid
|
||||||
|
}
|
||||||
|
/// Checks if the currennt address is one of ours
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_managed(&self, ptr: *mut c_void) -> bool {
|
pub fn is_managed(&self, ptr: *mut c_void) -> bool {
|
||||||
//self.allocations.contains_key(&(ptr as usize))
|
//self.allocations.contains_key(&(ptr as usize))
|
||||||
@ -664,7 +677,7 @@ impl Allocator {
|
|||||||
if self.shadow_offset <= start && end <= self.current_mapping_addr {
|
if self.shadow_offset <= start && end <= self.current_mapping_addr {
|
||||||
log::trace!("Reached the shadow/allocator region - skipping");
|
log::trace!("Reached the shadow/allocator region - skipping");
|
||||||
} else {
|
} else {
|
||||||
log::trace!("Unpoisoning: {:#x}-{:#x}", start, end);
|
// log::trace!("Unpoisoning: {:#x}-{:#x}", start, end);
|
||||||
self.map_shadow_for_region(start, end, true);
|
self.map_shadow_for_region(start, end, true);
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
@ -843,8 +856,9 @@ impl Default for Allocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(windows))] // not working yet
|
|
||||||
fn check_shadow() {
|
fn check_shadow() {
|
||||||
|
use frida_gum::Gum;
|
||||||
|
let _gum = Gum::obtain();
|
||||||
let mut allocator = Allocator::default();
|
let mut allocator = Allocator::default();
|
||||||
allocator.init();
|
allocator.init();
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ use std::{
|
|||||||
ffi::{c_char, c_void},
|
ffi::{c_char, c_void},
|
||||||
ptr::write_volatile,
|
ptr::write_volatile,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::MutexGuard,
|
sync::{Mutex, MutexGuard},
|
||||||
};
|
};
|
||||||
|
|
||||||
use backtrace::Backtrace;
|
use backtrace::Backtrace;
|
||||||
@ -27,7 +27,7 @@ use frida_gum::{
|
|||||||
};
|
};
|
||||||
use frida_gum_sys::Insn;
|
use frida_gum_sys::Insn;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use libafl_bolts::cli::FuzzerOptions;
|
use libafl_bolts::{cli::FuzzerOptions, get_thread_id, has_tls};
|
||||||
use libc::wchar_t;
|
use libc::wchar_t;
|
||||||
use rangemap::RangeMap;
|
use rangemap::RangeMap;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
@ -62,6 +62,144 @@ extern "C" {
|
|||||||
fn tls_ptr() -> *const c_void;
|
fn tls_ptr() -> *const c_void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reentrancy guard for the hooks
|
||||||
|
// We don't want to hook any operation initiated by the code of our hook
|
||||||
|
// Otherwise, we get into infinite recursion or deadlock
|
||||||
|
thread_local! {
|
||||||
|
static ASAN_IN_HOOK: Cell<bool> = const { Cell::new(false) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RAII guard to set and reset the `ASAN_IN_HOOK` properly
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AsanInHookGuard;
|
||||||
|
|
||||||
|
impl AsanInHookGuard {
|
||||||
|
/// Constructor to save the current last error
|
||||||
|
#[must_use]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
ASAN_IN_HOOK.set(true);
|
||||||
|
AsanInHookGuard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Drop for AsanInHookGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
ASAN_IN_HOOK.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Default for AsanInHookGuard {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// The Lock below is a simple spinlock that uses the thread id as the lock value.
|
||||||
|
/// This is a simple way to prevent reentrancy in the hooks when we don't have TLS.
|
||||||
|
/// This is not a perfect solution, as it is global so it orders all threads without TLS.
|
||||||
|
/// However, this is a rare situation and should not affect performance significantly.
|
||||||
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
use std::thread;
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Lock {
|
||||||
|
state: AtomicU64,
|
||||||
|
}
|
||||||
|
impl Lock {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Lock {
|
||||||
|
state: AtomicU64::new(u64::MAX),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lock(&self) -> LockResult {
|
||||||
|
let current_thread_id = get_thread_id();
|
||||||
|
loop {
|
||||||
|
let current_lock = self.state.load(Ordering::Relaxed);
|
||||||
|
if current_lock == u64::MAX {
|
||||||
|
if self
|
||||||
|
.state
|
||||||
|
.compare_exchange(
|
||||||
|
u64::MAX,
|
||||||
|
current_thread_id,
|
||||||
|
Ordering::Acquire,
|
||||||
|
Ordering::Relaxed,
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
return LockResult::Acquired; // Lock acquired
|
||||||
|
}
|
||||||
|
} else if current_lock == current_thread_id {
|
||||||
|
return LockResult::AlreadyLocked; // Already locked by the same thread
|
||||||
|
}
|
||||||
|
thread::yield_now(); // Busy wait
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unlock(&self) -> UnlockResult {
|
||||||
|
let current_thread_id = get_thread_id();
|
||||||
|
let current_lock = self.state.load(Ordering::Relaxed);
|
||||||
|
if current_lock == current_thread_id {
|
||||||
|
self.state.store(u64::MAX, Ordering::Release);
|
||||||
|
return UnlockResult::Success; // Lock released
|
||||||
|
}
|
||||||
|
UnlockResult::NotOwner // Lock not owned by the current thread
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
||||||
|
use errno::{errno, set_errno, Errno};
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
use winapi::shared::minwindef::DWORD;
|
||||||
|
/// We need to save and restore the last error in the hooks
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
use winapi::um::errhandlingapi::{GetLastError, SetLastError};
|
||||||
|
|
||||||
|
struct LastErrorGuard {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
last_error: DWORD,
|
||||||
|
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
||||||
|
last_error: Errno,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LastErrorGuard {
|
||||||
|
// Constructor to save the current last error
|
||||||
|
fn new() -> Self {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
let last_error = unsafe { GetLastError() };
|
||||||
|
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
||||||
|
let last_error = errno();
|
||||||
|
|
||||||
|
LastErrorGuard { last_error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement the Drop trait to restore the last error
|
||||||
|
impl Drop for LastErrorGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
unsafe {
|
||||||
|
SetLastError(self.last_error);
|
||||||
|
}
|
||||||
|
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
||||||
|
set_errno(self.last_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum LockResult {
|
||||||
|
Acquired,
|
||||||
|
AlreadyLocked,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum UnlockResult {
|
||||||
|
Success,
|
||||||
|
NotOwner,
|
||||||
|
}
|
||||||
|
|
||||||
|
// For threads without TLS, we use a static lock to prevent hook reentrancy
|
||||||
|
// This is not as efficient as using TLS, because it prevent TLS-free threads
|
||||||
|
// from running in parallel, but such situations are very rare (Windows loaded thread pool)
|
||||||
|
// and should not affect performance significantly
|
||||||
|
static TLS_LESS_LOCK: Lock = Lock::new();
|
||||||
|
|
||||||
/// 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.
|
||||||
///
|
///
|
||||||
/// Sixteen general purpose registers are put in this order, `rax`, `rbx`, `rcx`, `rdx`, `rbp`, `rsp`, `rsi`, `rdi`, `r8-r15`, plus instrumented `rip`, accessed memory addr and true `rip`
|
/// Sixteen general purpose registers are put in this order, `rax`, `rbx`, `rcx`, `rdx`, `rbp`, `rsp`, `rsi`, `rdi`, `r8-r15`, plus instrumented `rip`, accessed memory addr and true `rip`
|
||||||
@ -92,29 +230,6 @@ pub const ASAN_SAVE_REGISTER_NAMES: [&str; ASAN_SAVE_REGISTER_COUNT] = [
|
|||||||
"actual rip",
|
"actual rip",
|
||||||
];
|
];
|
||||||
|
|
||||||
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
|
/// 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;
|
||||||
@ -135,7 +250,7 @@ const ASAN_EH_FRAME_FDE_ADDRESS_OFFSET: u32 = 28;
|
|||||||
pub struct AsanRuntime {
|
pub struct AsanRuntime {
|
||||||
check_for_leaks_enabled: bool,
|
check_for_leaks_enabled: bool,
|
||||||
current_report_impl: u64,
|
current_report_impl: u64,
|
||||||
allocator: Allocator,
|
allocator: Mutex<Allocator>,
|
||||||
regs: [usize; ASAN_SAVE_REGISTER_COUNT],
|
regs: [usize; ASAN_SAVE_REGISTER_COUNT],
|
||||||
blob_report: Option<Box<[u8]>>,
|
blob_report: Option<Box<[u8]>>,
|
||||||
blob_check_mem_byte: Option<Box<[u8]>>,
|
blob_check_mem_byte: Option<Box<[u8]>>,
|
||||||
@ -158,6 +273,7 @@ pub struct AsanRuntime {
|
|||||||
pc: Option<usize>,
|
pc: Option<usize>,
|
||||||
hooks: Vec<NativePointer>,
|
hooks: Vec<NativePointer>,
|
||||||
pub(crate) hooks_enabled: bool,
|
pub(crate) hooks_enabled: bool,
|
||||||
|
// thread_in_hook: ThreadLocal<Cell<bool>>,
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
eh_frame: [u32; ASAN_EH_FRAME_DWORD_COUNT],
|
eh_frame: [u32; ASAN_EH_FRAME_DWORD_COUNT],
|
||||||
}
|
}
|
||||||
@ -184,7 +300,7 @@ impl FridaRuntime for AsanRuntime {
|
|||||||
_ranges: &RangeMap<u64, (u16, String)>,
|
_ranges: &RangeMap<u64, (u16, String)>,
|
||||||
module_map: &Rc<ModuleMap>,
|
module_map: &Rc<ModuleMap>,
|
||||||
) {
|
) {
|
||||||
self.allocator.init();
|
self.allocator_mut().init();
|
||||||
|
|
||||||
AsanErrors::get_mut_blocking().set_continue_on_error(self.continue_on_error);
|
AsanErrors::get_mut_blocking().set_continue_on_error(self.continue_on_error);
|
||||||
|
|
||||||
@ -247,7 +363,7 @@ impl AsanRuntime {
|
|||||||
let continue_on_error = options.continue_on_error;
|
let continue_on_error = options.continue_on_error;
|
||||||
Self {
|
Self {
|
||||||
check_for_leaks_enabled: options.detect_leaks,
|
check_for_leaks_enabled: options.detect_leaks,
|
||||||
allocator: Allocator::new(options),
|
allocator: Mutex::new(Allocator::new(options)),
|
||||||
skip_ranges,
|
skip_ranges,
|
||||||
continue_on_error,
|
continue_on_error,
|
||||||
..Self::default()
|
..Self::default()
|
||||||
@ -256,23 +372,22 @@ impl AsanRuntime {
|
|||||||
|
|
||||||
/// Reset all allocations so that they can be reused for new allocation requests.
|
/// Reset all allocations so that they can be reused for new allocation requests.
|
||||||
pub fn reset_allocations(&mut self) {
|
pub fn reset_allocations(&mut self) {
|
||||||
self.allocator.reset();
|
self.allocator_mut().reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the allocator
|
/// Gets the allocator
|
||||||
#[must_use]
|
pub fn allocator(&self) -> MutexGuard<Allocator> {
|
||||||
pub fn allocator(&self) -> &Allocator {
|
self.allocator.lock().unwrap()
|
||||||
&self.allocator
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the allocator (mutable)
|
/// Gets the allocator (mutable)
|
||||||
pub fn allocator_mut(&mut self) -> &mut Allocator {
|
pub fn allocator_mut(&mut self) -> MutexGuard<Allocator> {
|
||||||
&mut self.allocator
|
self.allocator.lock().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the test leaked any memory and report it if so.
|
/// Check if the test leaked any memory and report it if so.
|
||||||
pub fn check_for_leaks(&mut self) {
|
pub fn check_for_leaks(&mut self) {
|
||||||
self.allocator.check_for_leaks();
|
self.allocator_mut().check_for_leaks();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `AsanErrors` from the recent run.
|
/// Returns the `AsanErrors` from the recent run.
|
||||||
@ -283,7 +398,7 @@ impl AsanRuntime {
|
|||||||
|
|
||||||
/// Make sure the specified memory is unpoisoned
|
/// Make sure the specified memory is unpoisoned
|
||||||
pub fn unpoison(&mut self, address: usize, size: usize) {
|
pub fn unpoison(&mut self, address: usize, size: usize) {
|
||||||
self.allocator
|
self.allocator_mut()
|
||||||
.map_shadow_for_region(address, address + size, true);
|
.map_shadow_for_region(address, address + size, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +408,10 @@ impl AsanRuntime {
|
|||||||
/// The address needs to be a valid address, the size needs to be correct.
|
/// The address needs to be a valid address, the size needs to be correct.
|
||||||
/// This will dereference at the address.
|
/// This will dereference at the address.
|
||||||
pub unsafe fn poison(&mut self, address: usize, size: usize) {
|
pub unsafe fn poison(&mut self, address: usize, size: usize) {
|
||||||
Allocator::poison(self.allocator.map_to_shadow(address), size);
|
let start = self.allocator_mut().map_to_shadow(address);
|
||||||
|
if self.allocator_mut().valid_shadow(start, size) {
|
||||||
|
Allocator::poison(start, size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a stalked address to real address mapping.
|
/// Add a stalked address to real address mapping.
|
||||||
@ -313,16 +431,18 @@ impl AsanRuntime {
|
|||||||
|
|
||||||
/// Unpoison all the memory that is currently mapped with read/write permissions.
|
/// Unpoison all the memory that is currently mapped with read/write permissions.
|
||||||
pub fn unpoison_all_existing_memory(&mut self) {
|
pub fn unpoison_all_existing_memory(&mut self) {
|
||||||
self.allocator.unpoison_all_existing_memory();
|
self.allocator_mut().unpoison_all_existing_memory();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enable all function hooks
|
/// Enable all function hooks
|
||||||
pub fn enable_hooks(&mut self) {
|
pub fn enable_hooks(&mut self) {
|
||||||
|
log::info!("Enabling hooks");
|
||||||
self.hooks_enabled = true;
|
self.hooks_enabled = true;
|
||||||
}
|
}
|
||||||
/// Disable all function hooks
|
/// Disable all function hooks
|
||||||
pub fn disable_hooks(&mut self) {
|
pub fn disable_hooks(&mut self) {
|
||||||
self.hooks_enabled = false;
|
self.hooks_enabled = false;
|
||||||
|
log::info!("Disabling hooks");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register the current thread with the runtime, implementing shadow memory for its stack and
|
/// Register the current thread with the runtime, implementing shadow memory for its stack and
|
||||||
@ -331,14 +451,15 @@ impl AsanRuntime {
|
|||||||
pub fn register_thread(&mut self) {
|
pub fn register_thread(&mut self) {
|
||||||
let (stack_start, stack_end) = Self::current_stack();
|
let (stack_start, stack_end) = Self::current_stack();
|
||||||
let (tls_start, tls_end) = Self::current_tls();
|
let (tls_start, tls_end) = Self::current_tls();
|
||||||
log::info!(
|
println!(
|
||||||
"registering thread with stack {stack_start:x}:{stack_end:x} and tls {tls_start:x}:{tls_end:x}"
|
"registering thread {:?} with stack {stack_start:x}:{stack_end:x} and tls {tls_start:x}:{tls_end:x}",
|
||||||
|
get_thread_id()
|
||||||
);
|
);
|
||||||
self.allocator
|
self.allocator_mut()
|
||||||
.map_shadow_for_region(stack_start, stack_end, true);
|
.map_shadow_for_region(stack_start, stack_end, true);
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
self.allocator
|
self.allocator_mut()
|
||||||
.map_shadow_for_region(tls_start, tls_end, true);
|
.map_shadow_for_region(tls_start, tls_end, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,7 +467,7 @@ impl AsanRuntime {
|
|||||||
#[cfg(target_vendor = "apple")]
|
#[cfg(target_vendor = "apple")]
|
||||||
pub fn register_thread(&mut self) {
|
pub fn register_thread(&mut self) {
|
||||||
let (stack_start, stack_end) = Self::current_stack();
|
let (stack_start, stack_end) = Self::current_stack();
|
||||||
self.allocator
|
self.allocator_mut()
|
||||||
.map_shadow_for_region(stack_start, stack_end, true);
|
.map_shadow_for_region(stack_start, stack_end, true);
|
||||||
|
|
||||||
log::info!("registering thread with stack {stack_start:x}:{stack_end:x}");
|
log::info!("registering thread with stack {stack_start:x}:{stack_end:x}");
|
||||||
@ -489,27 +610,28 @@ impl AsanRuntime {
|
|||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[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 _last_error_guard = LastErrorGuard::new();
|
||||||
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 this.hooks_enabled {
|
||||||
if thread_local_initted() {
|
if has_tls() {
|
||||||
if !ASAN_IN_HOOK.get() {
|
if !ASAN_IN_HOOK.get(){
|
||||||
ASAN_IN_HOOK.set(true);
|
let _guard = AsanInHookGuard::new(); // Ensure ASAN_IN_HOOK is set and reset
|
||||||
let ret = this.[<hook_ $name>](*original, $($param),*);
|
return this.[<hook_ $name>](*original, $($param),*);
|
||||||
ASAN_IN_HOOK.set(false);
|
}
|
||||||
ret
|
|
||||||
} else {
|
|
||||||
(original)($($param),*)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(original)($($param),*)
|
|
||||||
}
|
}
|
||||||
} else {
|
// else{
|
||||||
(original)($($param),*)
|
// log::warn!("{} called without TLS", stringify!($name));
|
||||||
|
// $(
|
||||||
|
// log::warn!("{}: {:?}", stringify!($param), $param);
|
||||||
|
// )*
|
||||||
|
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
(original)($($param),*)
|
||||||
}
|
}
|
||||||
|
|
||||||
let self_ptr = core::ptr::from_ref(self) as usize;
|
let self_ptr = core::ptr::from_ref(self) as usize;
|
||||||
@ -536,27 +658,21 @@ impl AsanRuntime {
|
|||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[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 _last_error_guard = LastErrorGuard::new();
|
||||||
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 = [<$lib_ident:snake:upper _ $name:snake:upper _PTR>].get().unwrap();
|
let original = [<$lib_ident:snake:upper _ $name:snake:upper _PTR>].get().unwrap();
|
||||||
if this.hooks_enabled {
|
if this.hooks_enabled {
|
||||||
if thread_local_initted() {
|
if has_tls() {
|
||||||
if !ASAN_IN_HOOK.get() {
|
if !ASAN_IN_HOOK.get(){
|
||||||
ASAN_IN_HOOK.set(true);
|
let _guard = AsanInHookGuard::new(); // Ensure ASAN_IN_HOOK is set and reset
|
||||||
let ret = this.[<hook_ $name>](*original, $($param),*);
|
return this.[<hook_ $name>](*original, $($param),*);
|
||||||
ASAN_IN_HOOK.set(false);
|
}
|
||||||
ret
|
|
||||||
} else {
|
|
||||||
(original)($($param),*)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(original)($($param),*)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
(original)($($param),*)
|
|
||||||
}
|
}
|
||||||
|
(original)($($param),*)
|
||||||
}
|
}
|
||||||
|
|
||||||
let self_ptr = core::ptr::from_ref(self) as usize;
|
let self_ptr = core::ptr::from_ref(self) as usize;
|
||||||
@ -573,7 +689,8 @@ impl AsanRuntime {
|
|||||||
|
|
||||||
#[allow(unused_macro_rules)]
|
#[allow(unused_macro_rules)]
|
||||||
macro_rules! hook_func_with_check {
|
macro_rules! hook_func_with_check {
|
||||||
($name:ident, ($($param:ident : $param_type:ty),*), $return_type:ty, $always_enabled:expr) => {
|
//No library case
|
||||||
|
($name:ident, ($($param:ident : $param_type:ty),*), $return_type:ty, $always_enabled:expr ) => {
|
||||||
paste::paste! {
|
paste::paste! {
|
||||||
let target_function = Module::find_global_export_by_name(stringify!($name)).expect("Failed to find function");
|
let target_function = Module::find_global_export_by_name(stringify!($name)).expect("Failed to find function");
|
||||||
|
|
||||||
@ -582,39 +699,43 @@ impl AsanRuntime {
|
|||||||
|
|
||||||
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)]
|
#[allow(non_snake_case)] // depends on the values the macro is invoked with
|
||||||
|
#[allow(clippy::redundant_else)]
|
||||||
unsafe extern "C" fn [<replacement_ $name>]($($param: $param_type),*) -> $return_type {
|
unsafe extern "C" fn [<replacement_ $name>]($($param: $param_type),*) -> $return_type {
|
||||||
|
let _last_error_guard = LastErrorGuard::new();
|
||||||
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
|
|
||||||
//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 $always_enabled || this.hooks_enabled {
|
if $always_enabled || this.hooks_enabled {
|
||||||
if thread_local_initted() {
|
if has_tls() {
|
||||||
if !ASAN_IN_HOOK.get() {
|
if !ASAN_IN_HOOK.get(){
|
||||||
ASAN_IN_HOOK.set(true);
|
let _guard = AsanInHookGuard::new(); // Ensure ASAN_IN_HOOK is set and reset
|
||||||
let ret = if this.[<hook_check_ $name>]($($param),*) {
|
if this.[<hook_check_ $name>]($($param),*){
|
||||||
this.[<hook_ $name>](*original, $($param),*)
|
return this.[<hook_ $name>](*original, $($param),*);
|
||||||
} else {
|
}
|
||||||
(original)($($param),*)
|
}
|
||||||
};
|
|
||||||
ASAN_IN_HOOK.set(false);
|
|
||||||
ret
|
|
||||||
} else {
|
|
||||||
(original)($($param),*)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let ret = if $always_enabled && this.[<hook_check_ $name>]($($param),*) {
|
|
||||||
this.[<hook_ $name>](*original, $($param),*)
|
|
||||||
} else {
|
|
||||||
(original)($($param),*)
|
|
||||||
};
|
|
||||||
ret
|
|
||||||
}
|
}
|
||||||
} else {
|
else{
|
||||||
(original)($($param),*)
|
// log::warn!("{} called without TLS", stringify!($name));
|
||||||
}
|
// $(
|
||||||
|
// log::warn!("Params: {}: {:?}", stringify!($param), $param);
|
||||||
|
// )*
|
||||||
|
if $always_enabled {
|
||||||
|
if TLS_LESS_LOCK.lock() == LockResult::Acquired && this.[<hook_check_ $name>]($($param),*){
|
||||||
|
// There is no TLS and we have grabbed the lock - call the hook
|
||||||
|
let ret = this.[<hook_ $name>](*original, $($param),*);
|
||||||
|
TLS_LESS_LOCK.unlock();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TLS_LESS_LOCK.unlock(); // Return the original function
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(original)($($param),*)
|
||||||
}
|
}
|
||||||
|
|
||||||
let self_ptr = core::ptr::from_ref(self) as usize;
|
let self_ptr = core::ptr::from_ref(self) as usize;
|
||||||
@ -627,50 +748,47 @@ 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, $always_enabled:expr) => {
|
($lib:literal, $lib_ident:ident, $name:ident, ($($param:ident : $param_type:ty),*), $return_type:ty, $always_enabled:expr ) => {
|
||||||
paste::paste! {
|
paste::paste! {
|
||||||
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 = 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);
|
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();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let _ = [<$lib_ident:snake:upper _ $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 _ = [<$lib_ident:snake:upper _ $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)]
|
#[allow(non_snake_case)]
|
||||||
|
#[allow(clippy::redundant_else)]
|
||||||
unsafe extern "C" fn [<replacement_ $name>]($($param: $param_type),*) -> $return_type {
|
unsafe extern "C" fn [<replacement_ $name>]($($param: $param_type),*) -> $return_type {
|
||||||
|
let _last_error_guard = LastErrorGuard::new();
|
||||||
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 = [<$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
|
|
||||||
//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 $always_enabled || this.hooks_enabled {
|
if $always_enabled || this.hooks_enabled {
|
||||||
if thread_local_initted() {
|
if has_tls() {
|
||||||
if !ASAN_IN_HOOK.get() {
|
if !ASAN_IN_HOOK.get(){
|
||||||
ASAN_IN_HOOK.set(true);
|
let _guard = AsanInHookGuard::new(); // Ensure ASAN_IN_HOOK is set and reset
|
||||||
let ret = if this.[<hook_check_ $name>]($($param),*) {
|
if this.[<hook_check_ $name>]($($param),*){
|
||||||
this.[<hook_ $name>](*original, $($param),*)
|
return this.[<hook_ $name>](*original, $($param),*);
|
||||||
} else {
|
}
|
||||||
(original)($($param),*)
|
}
|
||||||
};
|
|
||||||
ASAN_IN_HOOK.set(false);
|
|
||||||
ret
|
|
||||||
} else {
|
|
||||||
(original)($($param),*)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let ret = if $always_enabled && this.[<hook_check_ $name>]($($param),*) {
|
|
||||||
this.[<hook_ $name>](*original, $($param),*)
|
|
||||||
} else {
|
|
||||||
(original)($($param),*)
|
|
||||||
};
|
|
||||||
ret
|
|
||||||
}
|
}
|
||||||
} else {
|
else{
|
||||||
(original)($($param),*)
|
if $always_enabled {
|
||||||
}
|
if TLS_LESS_LOCK.lock() == LockResult::Acquired && this.[<hook_check_ $name>]($($param),*){
|
||||||
|
// There is no TLS and we have grabbed the lock - call the hook
|
||||||
|
let ret = this.[<hook_ $name>](*original, $($param),*);
|
||||||
|
TLS_LESS_LOCK.unlock();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TLS_LESS_LOCK.unlock(); // Return the original function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(original)($($param),*)
|
||||||
}
|
}
|
||||||
|
|
||||||
let self_ptr = core::ptr::from_ref(self) as usize;
|
let self_ptr = core::ptr::from_ref(self) as usize;
|
||||||
@ -681,7 +799,14 @@ impl AsanRuntime {
|
|||||||
);
|
);
|
||||||
self.hooks.push(target_function);
|
self.hooks.push(target_function);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
// Default case without check_enabled parameter
|
||||||
|
($name:ident, ($($param:ident : $param_type:ty),*), $return_type:ty) => {
|
||||||
|
hook_func_with_check!($name, ($($param: $param_type),*), $return_type, false);
|
||||||
|
};
|
||||||
|
($lib:literal, $lib_ident:ident, $name:ident, ($($param:ident : $param_type:ty),*), $return_type:ty) => {
|
||||||
|
hook_func_with_check!($lib, $lib_ident, $name, ($($param: $param_type),*), $return_type, false);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
// Hook the memory allocator functions
|
// Hook the memory allocator functions
|
||||||
|
|
||||||
@ -779,6 +904,8 @@ impl AsanRuntime {
|
|||||||
"HeapFree" => {
|
"HeapFree" => {
|
||||||
hook_func_with_check!($libname, $lib_ident, HeapFree, (handle: *mut c_void, flags: u32, mem: *mut c_void), bool, true);
|
hook_func_with_check!($libname, $lib_ident, HeapFree, (handle: *mut c_void, flags: u32, mem: *mut c_void), bool, true);
|
||||||
}
|
}
|
||||||
|
// NOTE: we call it with always_enabled, because on Windows, some COM memory deallocation occurs later in the process
|
||||||
|
// after we have completed the run
|
||||||
"RtlFreeHeap" => {
|
"RtlFreeHeap" => {
|
||||||
hook_func_with_check!($libname, $lib_ident, RtlFreeHeap, (handle: *mut c_void, flags: u32, mem: *mut c_void), usize, true);
|
hook_func_with_check!($libname, $lib_ident, RtlFreeHeap, (handle: *mut c_void, flags: u32, mem: *mut c_void), usize, true);
|
||||||
}
|
}
|
||||||
@ -926,6 +1053,14 @@ impl AsanRuntime {
|
|||||||
*const c_void
|
*const c_void
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
"UnmapViewOfFile" => {
|
||||||
|
hook_func!(
|
||||||
|
$libname, $lib_ident,
|
||||||
|
UnmapViewOfFile,
|
||||||
|
(ptr: *const c_void),
|
||||||
|
bool
|
||||||
|
);
|
||||||
|
}
|
||||||
"LoadLibraryExW" => {
|
"LoadLibraryExW" => {
|
||||||
hook_func!(
|
hook_func!(
|
||||||
$libname, $lib_ident,
|
$libname, $lib_ident,
|
||||||
@ -963,6 +1098,8 @@ impl AsanRuntime {
|
|||||||
"api-ms-win-core-heap-obsolete-l1-1-0",
|
"api-ms-win-core-heap-obsolete-l1-1-0",
|
||||||
api_ms_heap2_obsolete
|
api_ms_heap2_obsolete
|
||||||
);
|
);
|
||||||
|
hook_heap_windows!("api-ms-win-core-memory-l1-1-0", api_ms_memory1);
|
||||||
|
hook_heap_windows!("VCRUNTIME140", VCRUNTIME140);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1360,6 +1497,8 @@ impl AsanRuntime {
|
|||||||
#[expect(clippy::cast_sign_loss)]
|
#[expect(clippy::cast_sign_loss)]
|
||||||
#[expect(clippy::too_many_lines)]
|
#[expect(clippy::too_many_lines)]
|
||||||
extern "system" fn handle_trap(&mut self) {
|
extern "system" fn handle_trap(&mut self) {
|
||||||
|
// log::error!("Attach the debugger to process {:#?}", std::process::id());
|
||||||
|
// std::thread::sleep(std::time::Duration::from_secs(30));
|
||||||
self.disable_hooks();
|
self.disable_hooks();
|
||||||
|
|
||||||
self.dump_registers();
|
self.dump_registers();
|
||||||
@ -1454,6 +1593,8 @@ impl AsanRuntime {
|
|||||||
} else if base_value.is_some() {
|
} else if base_value.is_some() {
|
||||||
if let Some(metadata) = self
|
if let Some(metadata) = self
|
||||||
.allocator
|
.allocator
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
.find_metadata(fault_address, base_value.unwrap())
|
.find_metadata(fault_address, base_value.unwrap())
|
||||||
{
|
{
|
||||||
match access_type {
|
match access_type {
|
||||||
@ -1609,6 +1750,8 @@ impl AsanRuntime {
|
|||||||
}
|
}
|
||||||
} else if let Some(metadata) = self
|
} else if let Some(metadata) = self
|
||||||
.allocator
|
.allocator
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
.find_metadata(fault_address, self.regs[base_reg as usize])
|
.find_metadata(fault_address, self.regs[base_reg as usize])
|
||||||
{
|
{
|
||||||
let asan_readwrite_error = AsanReadWriteError {
|
let asan_readwrite_error = AsanReadWriteError {
|
||||||
@ -1778,7 +1921,7 @@ impl AsanRuntime {
|
|||||||
*/
|
*/
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn generate_shadow_check_blob(&mut self, size: u32) -> Box<[u8]> {
|
fn generate_shadow_check_blob(&mut self, size: u32) -> Box<[u8]> {
|
||||||
let shadow_bit = self.allocator.shadow_bit();
|
let shadow_bit = self.allocator_mut().shadow_bit();
|
||||||
// Rcx, Rax, Rdi, Rdx, Rsi, R8 are used, so we save them in emit_shadow_check
|
// Rcx, Rax, Rdi, Rdx, Rsi, R8 are used, so we save them in emit_shadow_check
|
||||||
//at this point RDI contains the
|
//at this point RDI contains the
|
||||||
let mask_shift = 32 - size;
|
let mask_shift = 32 - size;
|
||||||
@ -1826,7 +1969,7 @@ impl AsanRuntime {
|
|||||||
x0 and x1 are saved by the asan_check
|
x0 and x1 are saved by the asan_check
|
||||||
The maximum size this supports is up to 25 bytes. This is because we load 4 bytes of the shadow value. And, in the case that we have a misaligned address with an offset of 7 into the word. For example, if we load 25 bytes from 0x1007 - [0x1007,0x101f], then we require the shadow values from 0x1000, 0x1008, 0x1010, and 0x1018 */
|
The maximum size this supports is up to 25 bytes. This is because we load 4 bytes of the shadow value. And, in the case that we have a misaligned address with an offset of 7 into the word. For example, if we load 25 bytes from 0x1007 - [0x1007,0x101f], then we require the shadow values from 0x1000, 0x1008, 0x1010, and 0x1018 */
|
||||||
|
|
||||||
let shadow_bit = self.allocator.shadow_bit();
|
let shadow_bit = self.allocator_mut().shadow_bit();
|
||||||
macro_rules! shadow_check {
|
macro_rules! shadow_check {
|
||||||
($ops:ident, $width:expr) => {dynasm!($ops
|
($ops:ident, $width:expr) => {dynasm!($ops
|
||||||
; .arch aarch64
|
; .arch aarch64
|
||||||
@ -1872,7 +2015,7 @@ impl AsanRuntime {
|
|||||||
|
|
||||||
assert!(width <= 64, "width must be <= 64");
|
assert!(width <= 64, "width must be <= 64");
|
||||||
let shift = 64 - width;
|
let shift = 64 - width;
|
||||||
let shadow_bit = self.allocator.shadow_bit();
|
let shadow_bit = self.allocator_mut().shadow_bit();
|
||||||
macro_rules! shadow_check_exact {
|
macro_rules! shadow_check_exact {
|
||||||
($ops:ident, $shift:expr) => {dynasm!($ops
|
($ops:ident, $shift:expr) => {dynasm!($ops
|
||||||
; .arch aarch64
|
; .arch aarch64
|
||||||
@ -2543,6 +2686,8 @@ impl AsanRuntime {
|
|||||||
// on amd64 jump can takes 10 bytes at most, so that's why I put 10 bytes.
|
// on amd64 jump can takes 10 bytes at most, so that's why I put 10 bytes.
|
||||||
writer.put_nop();
|
writer.put_nop();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log::trace!("Cannot check instructions for {:?} bytes.", width);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.put_pop_reg(X86Register::Rdi);
|
writer.put_pop_reg(X86Register::Rdi);
|
||||||
@ -2788,7 +2933,7 @@ impl Default for AsanRuntime {
|
|||||||
Self {
|
Self {
|
||||||
check_for_leaks_enabled: false,
|
check_for_leaks_enabled: false,
|
||||||
current_report_impl: 0,
|
current_report_impl: 0,
|
||||||
allocator: Allocator::default(),
|
allocator: Mutex::new(Allocator::default()),
|
||||||
regs: [0; ASAN_SAVE_REGISTER_COUNT],
|
regs: [0; ASAN_SAVE_REGISTER_COUNT],
|
||||||
blob_report: None,
|
blob_report: None,
|
||||||
blob_check_mem_byte: None,
|
blob_check_mem_byte: None,
|
||||||
@ -2813,6 +2958,7 @@ impl Default for AsanRuntime {
|
|||||||
pc: None,
|
pc: None,
|
||||||
hooks: Vec::new(),
|
hooks: Vec::new(),
|
||||||
hooks_enabled: false,
|
hooks_enabled: false,
|
||||||
|
// thread_in_hook: ThreadLocal::new(|| Cell::new(false)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,14 @@ extern "system" {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn memset(s: *mut c_void, c: i32, n: usize) -> *mut c_void;
|
fn memset(s: *mut c_void, c: i32, n: usize) -> *mut c_void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
use winapi::um::memoryapi::VirtualQuery;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use winapi::um::winnt::MEMORY_BASIC_INFORMATION;
|
||||||
|
|
||||||
#[expect(clippy::not_unsafe_ptr_arg_deref)]
|
#[expect(clippy::not_unsafe_ptr_arg_deref)]
|
||||||
impl AsanRuntime {
|
impl AsanRuntime {
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -131,6 +139,7 @@ impl AsanRuntime {
|
|||||||
_context: usize,
|
_context: usize,
|
||||||
_entry_point: usize,
|
_entry_point: usize,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("LdrpCallInitRoutine");
|
||||||
// winsafe::OutputDebugString("LdrpCallInitRoutine");
|
// winsafe::OutputDebugString("LdrpCallInitRoutine");
|
||||||
// let result = unsafe { LdrLoadDll(path, file, flags,x )};
|
// let result = unsafe { LdrLoadDll(path, file, flags,x )};
|
||||||
// self.allocator_mut().unpoison_all_existing_memory();
|
// self.allocator_mut().unpoison_all_existing_memory();
|
||||||
@ -174,6 +183,7 @@ impl AsanRuntime {
|
|||||||
_lock: *const c_void,
|
_lock: *const c_void,
|
||||||
_parameters: *const c_void,
|
_parameters: *const c_void,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("RtlCreateHeap");
|
||||||
0xc0debeef as *mut c_void
|
0xc0debeef as *mut c_void
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -184,7 +194,8 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(_handle: *const c_void) -> *mut c_void,
|
_original: extern "C" fn(_handle: *const c_void) -> *mut c_void,
|
||||||
_handle: *const c_void,
|
_handle: *const c_void,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
std::ptr::null_mut()
|
log::trace!("RtlDestroyHeap");
|
||||||
|
ptr::null_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -197,7 +208,8 @@ impl AsanRuntime {
|
|||||||
flags: u32,
|
flags: u32,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
let allocator = self.allocator_mut();
|
log::trace!("HeapAlloc");
|
||||||
|
let mut allocator = self.allocator_mut();
|
||||||
let ret = unsafe { allocator.alloc(size, 8) };
|
let ret = unsafe { allocator.alloc(size, 8) };
|
||||||
|
|
||||||
if flags & 8 == 8 {
|
if flags & 8 == 8 {
|
||||||
@ -210,6 +222,7 @@ impl AsanRuntime {
|
|||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@ -220,7 +233,9 @@ impl AsanRuntime {
|
|||||||
flags: u32,
|
flags: u32,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
let allocator = self.allocator_mut();
|
log::trace!("hook_RtlAllocateHeap handle {_handle:#?} flags {flags:x} size {size}");
|
||||||
|
|
||||||
|
let mut allocator = self.allocator_mut();
|
||||||
let ret = unsafe { allocator.alloc(size, 8) };
|
let ret = unsafe { allocator.alloc(size, 8) };
|
||||||
|
|
||||||
if flags & 8 == 8 {
|
if flags & 8 == 8 {
|
||||||
@ -249,7 +264,8 @@ impl AsanRuntime {
|
|||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
let allocator = self.allocator_mut();
|
log::trace!("hook_HeapReAlloc handle {handle:#?} flags {flags:x} ptr {ptr:#?} size {size}");
|
||||||
|
let mut allocator = self.allocator_mut();
|
||||||
if !allocator.is_managed(ptr) {
|
if !allocator.is_managed(ptr) {
|
||||||
return original(handle, flags, ptr, size);
|
return original(handle, flags, ptr, size);
|
||||||
}
|
}
|
||||||
@ -293,7 +309,7 @@ impl AsanRuntime {
|
|||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
let allocator = self.allocator_mut();
|
let mut allocator = self.allocator_mut();
|
||||||
log::trace!("RtlReAllocateHeap({ptr:?}, {size:x})");
|
log::trace!("RtlReAllocateHeap({ptr:?}, {size:x})");
|
||||||
if !allocator.is_managed(ptr) {
|
if !allocator.is_managed(ptr) {
|
||||||
return original(handle, flags, ptr, size);
|
return original(handle, flags, ptr, size);
|
||||||
@ -331,6 +347,7 @@ impl AsanRuntime {
|
|||||||
_flags: u32,
|
_flags: u32,
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
log::trace!("hook_check_RtlFreeHeap ptr {ptr:#?}");
|
||||||
self.allocator_mut().is_managed(ptr)
|
self.allocator_mut().is_managed(ptr)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -343,6 +360,7 @@ impl AsanRuntime {
|
|||||||
_flags: u32,
|
_flags: u32,
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("hook_RtlFreeHeap address handle {_handle:#?} flags 0x{_flags:x} ptr {ptr:#?}");
|
||||||
unsafe { self.allocator_mut().release(ptr) };
|
unsafe { self.allocator_mut().release(ptr) };
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
@ -355,6 +373,7 @@ impl AsanRuntime {
|
|||||||
_flags: u32,
|
_flags: u32,
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
log::trace!("hook_check_HeapFree");
|
||||||
self.allocator_mut().is_managed(ptr)
|
self.allocator_mut().is_managed(ptr)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -367,6 +386,7 @@ impl AsanRuntime {
|
|||||||
_flags: u32,
|
_flags: u32,
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
log::trace!("hook_HeapFree");
|
||||||
unsafe { self.allocator_mut().release(ptr) };
|
unsafe { self.allocator_mut().release(ptr) };
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -379,6 +399,7 @@ impl AsanRuntime {
|
|||||||
_flags: u32,
|
_flags: u32,
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
log::trace!("hook_check_HeapSize");
|
||||||
self.allocator_mut().is_managed(ptr)
|
self.allocator_mut().is_managed(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,6 +412,7 @@ impl AsanRuntime {
|
|||||||
_flags: u32,
|
_flags: u32,
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("hook_HeapSize");
|
||||||
self.allocator().get_usable_size(ptr)
|
self.allocator().get_usable_size(ptr)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -402,6 +424,7 @@ impl AsanRuntime {
|
|||||||
_flags: u32,
|
_flags: u32,
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
log::trace!("hook_check_RtlSizeHeap");
|
||||||
self.allocator_mut().is_managed(ptr)
|
self.allocator_mut().is_managed(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,6 +437,7 @@ impl AsanRuntime {
|
|||||||
_flags: u32,
|
_flags: u32,
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("hook_RtlSizeHeap");
|
||||||
self.allocator().get_usable_size(ptr)
|
self.allocator().get_usable_size(ptr)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -425,6 +449,7 @@ impl AsanRuntime {
|
|||||||
_flags: u32,
|
_flags: u32,
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
log::trace!("hook_check_RtlValidateHeap");
|
||||||
self.allocator_mut().is_managed(ptr)
|
self.allocator_mut().is_managed(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,6 +462,7 @@ impl AsanRuntime {
|
|||||||
_flags: u32,
|
_flags: u32,
|
||||||
_ptr: *mut c_void,
|
_ptr: *mut c_void,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
log::trace!("hook_RtlValidateHeap");
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,6 +474,7 @@ impl AsanRuntime {
|
|||||||
flags: u32,
|
flags: u32,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_LocalAlloc");
|
||||||
let ret = unsafe { self.allocator_mut().alloc(size, 8) };
|
let ret = unsafe { self.allocator_mut().alloc(size, 8) };
|
||||||
|
|
||||||
if flags & 0x40 == 0x40 {
|
if flags & 0x40 == 0x40 {
|
||||||
@ -466,6 +493,7 @@ impl AsanRuntime {
|
|||||||
size: usize,
|
size: usize,
|
||||||
_flags: u32,
|
_flags: u32,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_LocalReAlloc");
|
||||||
unsafe {
|
unsafe {
|
||||||
let ret = self.allocator_mut().alloc(size, 0x8);
|
let ret = self.allocator_mut().alloc(size, 0x8);
|
||||||
if !mem.is_null() && !ret.is_null() {
|
if !mem.is_null() && !ret.is_null() {
|
||||||
@ -480,6 +508,7 @@ impl AsanRuntime {
|
|||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn hook_check_LocalFree(&mut self, mem: *mut c_void) -> bool {
|
pub fn hook_check_LocalFree(&mut self, mem: *mut c_void) -> bool {
|
||||||
|
log::trace!("hook_check_LocalFree");
|
||||||
let res = self.allocator_mut().is_managed(mem);
|
let res = self.allocator_mut().is_managed(mem);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@ -491,6 +520,7 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(mem: *mut c_void) -> *mut c_void,
|
_original: extern "C" fn(mem: *mut c_void) -> *mut c_void,
|
||||||
mem: *mut c_void,
|
mem: *mut c_void,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_LocalFree");
|
||||||
unsafe { self.allocator_mut().release(mem) };
|
unsafe { self.allocator_mut().release(mem) };
|
||||||
mem
|
mem
|
||||||
}
|
}
|
||||||
@ -498,6 +528,7 @@ impl AsanRuntime {
|
|||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn hook_check_LocalHandle(&mut self, mem: *mut c_void) -> bool {
|
pub fn hook_check_LocalHandle(&mut self, mem: *mut c_void) -> bool {
|
||||||
|
log::trace!("hook_check_LocalHandle");
|
||||||
self.allocator_mut().is_managed(mem)
|
self.allocator_mut().is_managed(mem)
|
||||||
}
|
}
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
@ -507,11 +538,13 @@ impl AsanRuntime {
|
|||||||
_soriginal: extern "C" fn(mem: *mut c_void) -> *mut c_void,
|
_soriginal: extern "C" fn(mem: *mut c_void) -> *mut c_void,
|
||||||
mem: *mut c_void,
|
mem: *mut c_void,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_LocalHandle");
|
||||||
mem
|
mem
|
||||||
}
|
}
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn hook_check_LocalLock(&mut self, mem: *mut c_void) -> bool {
|
pub fn hook_check_LocalLock(&mut self, mem: *mut c_void) -> bool {
|
||||||
|
log::trace!("hook_check_LocalLock");
|
||||||
self.allocator_mut().is_managed(mem)
|
self.allocator_mut().is_managed(mem)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,11 +555,13 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(mem: *mut c_void) -> *mut c_void,
|
_original: extern "C" fn(mem: *mut c_void) -> *mut c_void,
|
||||||
mem: *mut c_void,
|
mem: *mut c_void,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_LocalLock");
|
||||||
mem
|
mem
|
||||||
}
|
}
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn hook_check_LocalUnlock(&mut self, mem: *mut c_void) -> bool {
|
pub fn hook_check_LocalUnlock(&mut self, mem: *mut c_void) -> bool {
|
||||||
|
log::trace!("hook_check_LocalUnlock");
|
||||||
self.allocator_mut().is_managed(mem)
|
self.allocator_mut().is_managed(mem)
|
||||||
}
|
}
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
@ -536,11 +571,13 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(_mem: *mut c_void) -> bool,
|
_original: extern "C" fn(_mem: *mut c_void) -> bool,
|
||||||
_mem: *mut c_void,
|
_mem: *mut c_void,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
log::trace!("hook_LocalUnlock");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn hook_check_LocalSize(&mut self, mem: *mut c_void) -> bool {
|
pub fn hook_check_LocalSize(&mut self, mem: *mut c_void) -> bool {
|
||||||
|
log::trace!("hook_check_LocalSize");
|
||||||
self.allocator_mut().is_managed(mem)
|
self.allocator_mut().is_managed(mem)
|
||||||
}
|
}
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
@ -550,11 +587,13 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(mem: *mut c_void) -> usize,
|
_original: extern "C" fn(mem: *mut c_void) -> usize,
|
||||||
mem: *mut c_void,
|
mem: *mut c_void,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("hook_LocalSize");
|
||||||
self.allocator_mut().get_usable_size(mem)
|
self.allocator_mut().get_usable_size(mem)
|
||||||
}
|
}
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn hook_check_LocalFlags(&mut self, mem: *mut c_void) -> bool {
|
pub fn hook_check_LocalFlags(&mut self, mem: *mut c_void) -> bool {
|
||||||
|
log::trace!("hook_check_LocalFlags");
|
||||||
self.allocator_mut().is_managed(mem)
|
self.allocator_mut().is_managed(mem)
|
||||||
}
|
}
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
@ -564,6 +603,7 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(_mem: *mut c_void) -> u32,
|
_original: extern "C" fn(_mem: *mut c_void) -> u32,
|
||||||
_mem: *mut c_void,
|
_mem: *mut c_void,
|
||||||
) -> u32 {
|
) -> u32 {
|
||||||
|
log::trace!("hook_LocalFlags");
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -575,6 +615,7 @@ impl AsanRuntime {
|
|||||||
flags: u32,
|
flags: u32,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_GlobalAlloc");
|
||||||
let ret = unsafe { self.allocator_mut().alloc(size, 8) };
|
let ret = unsafe { self.allocator_mut().alloc(size, 8) };
|
||||||
|
|
||||||
if flags & 0x40 == 0x40 {
|
if flags & 0x40 == 0x40 {
|
||||||
@ -596,6 +637,7 @@ impl AsanRuntime {
|
|||||||
_flags: u32,
|
_flags: u32,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_GlobalReAlloc");
|
||||||
unsafe {
|
unsafe {
|
||||||
let ret = self.allocator_mut().alloc(size, 0x8);
|
let ret = self.allocator_mut().alloc(size, 0x8);
|
||||||
if !mem.is_null() && !ret.is_null() {
|
if !mem.is_null() && !ret.is_null() {
|
||||||
@ -610,6 +652,7 @@ impl AsanRuntime {
|
|||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn hook_check_GlobalFree(&mut self, mem: *mut c_void) -> bool {
|
pub fn hook_check_GlobalFree(&mut self, mem: *mut c_void) -> bool {
|
||||||
|
log::trace!("hook_check_GlobalFree");
|
||||||
self.allocator_mut().is_managed(mem)
|
self.allocator_mut().is_managed(mem)
|
||||||
}
|
}
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
@ -619,6 +662,7 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(mem: *mut c_void) -> *mut c_void,
|
_original: extern "C" fn(mem: *mut c_void) -> *mut c_void,
|
||||||
mem: *mut c_void,
|
mem: *mut c_void,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_GlobalFree");
|
||||||
unsafe { self.allocator_mut().release(mem) };
|
unsafe { self.allocator_mut().release(mem) };
|
||||||
mem
|
mem
|
||||||
}
|
}
|
||||||
@ -635,6 +679,7 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(mem: *mut c_void) -> *mut c_void,
|
_original: extern "C" fn(mem: *mut c_void) -> *mut c_void,
|
||||||
mem: *mut c_void,
|
mem: *mut c_void,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_GlobalHandle");
|
||||||
mem
|
mem
|
||||||
}
|
}
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
@ -650,6 +695,7 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(mem: *mut c_void) -> *mut c_void,
|
_original: extern "C" fn(mem: *mut c_void) -> *mut c_void,
|
||||||
mem: *mut c_void,
|
mem: *mut c_void,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_GlobalLock");
|
||||||
mem
|
mem
|
||||||
}
|
}
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
@ -664,6 +710,7 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(_mem: *mut c_void) -> bool,
|
_original: extern "C" fn(_mem: *mut c_void) -> bool,
|
||||||
_mem: *mut c_void,
|
_mem: *mut c_void,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
log::trace!("hook_GlobalUnlock");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
@ -678,6 +725,7 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(mem: *mut c_void) -> usize,
|
_original: extern "C" fn(mem: *mut c_void) -> usize,
|
||||||
mem: *mut c_void,
|
mem: *mut c_void,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("hook_GlobalSize");
|
||||||
self.allocator_mut().get_usable_size(mem)
|
self.allocator_mut().get_usable_size(mem)
|
||||||
}
|
}
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
@ -692,6 +740,7 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(mem: *mut c_void) -> u32,
|
_original: extern "C" fn(mem: *mut c_void) -> u32,
|
||||||
_mem: *mut c_void,
|
_mem: *mut c_void,
|
||||||
) -> u32 {
|
) -> u32 {
|
||||||
|
log::trace!("hook_GlobalFlags");
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -702,7 +751,10 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(size: usize) -> *mut c_void,
|
_original: extern "C" fn(size: usize) -> *mut c_void,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
unsafe { self.allocator_mut().alloc(size, 8) }
|
unsafe {
|
||||||
|
log::trace!("hook_malloc");
|
||||||
|
self.allocator_mut().alloc(size, 8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -711,7 +763,10 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(size: usize) -> *mut c_void,
|
_original: extern "C" fn(size: usize) -> *mut c_void,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
unsafe { self.allocator_mut().alloc(size, 8) }
|
unsafe {
|
||||||
|
log::trace!("hook_malloc");
|
||||||
|
self.allocator_mut().alloc(size, 8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -720,7 +775,10 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(size: usize) -> *mut c_void,
|
_original: extern "C" fn(size: usize) -> *mut c_void,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
unsafe { self.allocator_mut().alloc(size, 8) }
|
unsafe {
|
||||||
|
log::trace!("hook_o_malloc");
|
||||||
|
self.allocator_mut().alloc(size, 8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
@ -730,7 +788,10 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(size: usize) -> *mut c_void,
|
_original: extern "C" fn(size: usize) -> *mut c_void,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
unsafe { self.allocator_mut().alloc(size, 8) }
|
unsafe {
|
||||||
|
log::trace!("hook__Znam");
|
||||||
|
self.allocator_mut().alloc(size, 8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
@ -741,6 +802,7 @@ impl AsanRuntime {
|
|||||||
size: usize,
|
size: usize,
|
||||||
_nothrow: *const c_void,
|
_nothrow: *const c_void,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook__ZnamRKSt9nothrow_t");
|
||||||
unsafe { self.allocator_mut().alloc(size, 8) }
|
unsafe { self.allocator_mut().alloc(size, 8) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -752,6 +814,7 @@ impl AsanRuntime {
|
|||||||
size: usize,
|
size: usize,
|
||||||
alignment: usize,
|
alignment: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook__ZnamSt11align_val_t");
|
||||||
unsafe { self.allocator_mut().alloc(size, alignment) }
|
unsafe { self.allocator_mut().alloc(size, alignment) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -768,6 +831,7 @@ impl AsanRuntime {
|
|||||||
alignment: usize,
|
alignment: usize,
|
||||||
_nothrow: *const c_void,
|
_nothrow: *const c_void,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook__ZnamSt11align_val_tRKSt9nothrow_t");
|
||||||
unsafe { self.allocator_mut().alloc(size, alignment) }
|
unsafe { self.allocator_mut().alloc(size, alignment) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -780,6 +844,7 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(size: usize) -> *mut c_void,
|
_original: extern "C" fn(size: usize) -> *mut c_void,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook__Znwm");
|
||||||
let result = unsafe { self.allocator_mut().alloc(size, 8) };
|
let result = unsafe { self.allocator_mut().alloc(size, 8) };
|
||||||
if result.is_null() {
|
if result.is_null() {
|
||||||
extern "system" {
|
extern "system" {
|
||||||
@ -803,6 +868,7 @@ impl AsanRuntime {
|
|||||||
size: usize,
|
size: usize,
|
||||||
_nothrow: *const c_void,
|
_nothrow: *const c_void,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook__ZnwmRKSt9nothrow_t");
|
||||||
unsafe { self.allocator_mut().alloc(size, 8) }
|
unsafe { self.allocator_mut().alloc(size, 8) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -816,6 +882,7 @@ impl AsanRuntime {
|
|||||||
size: usize,
|
size: usize,
|
||||||
alignment: usize,
|
alignment: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook__ZnwmSt11align_val_t");
|
||||||
let result = unsafe { self.allocator_mut().alloc(size, alignment) };
|
let result = unsafe { self.allocator_mut().alloc(size, alignment) };
|
||||||
if result.is_null() {
|
if result.is_null() {
|
||||||
extern "system" {
|
extern "system" {
|
||||||
@ -842,6 +909,7 @@ impl AsanRuntime {
|
|||||||
alignment: usize,
|
alignment: usize,
|
||||||
_nothrow: *const c_void,
|
_nothrow: *const c_void,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook__ZnwmSt11align_val_tRKSt9nothrow_t");
|
||||||
unsafe { self.allocator_mut().alloc(size, alignment) }
|
unsafe { self.allocator_mut().alloc(size, alignment) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -852,6 +920,7 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(size: usize) -> *mut c_void,
|
_original: extern "C" fn(size: usize) -> *mut c_void,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook__o_malloc");
|
||||||
unsafe { self.allocator_mut().alloc(size, 8) }
|
unsafe { self.allocator_mut().alloc(size, 8) }
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -864,6 +933,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn memset(s: *mut c_void, c: i32, n: usize) -> *mut c_void;
|
fn memset(s: *mut c_void, c: i32, n: usize) -> *mut c_void;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_calloc");
|
||||||
let ret = unsafe { self.allocator_mut().alloc(size * nmemb, 8) };
|
let ret = unsafe { self.allocator_mut().alloc(size * nmemb, 8) };
|
||||||
// if size * nmemb == 0x10 {
|
// if size * nmemb == 0x10 {
|
||||||
// log::error!("backtrace: {:0x?}", frida_gum::Backtracer::accurate());
|
// log::error!("backtrace: {:0x?}", frida_gum::Backtracer::accurate());
|
||||||
@ -887,6 +957,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn memset(s: *mut c_void, c: i32, n: usize) -> *mut c_void;
|
fn memset(s: *mut c_void, c: i32, n: usize) -> *mut c_void;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook__o_calloc");
|
||||||
let ret = unsafe { self.allocator_mut().alloc(size * nmemb, 8) };
|
let ret = unsafe { self.allocator_mut().alloc(size * nmemb, 8) };
|
||||||
unsafe {
|
unsafe {
|
||||||
memset(ret, 0, size * nmemb);
|
memset(ret, 0, size * nmemb);
|
||||||
@ -907,16 +978,17 @@ impl AsanRuntime {
|
|||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_realloc");
|
||||||
unsafe {
|
unsafe {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
self.allocator_mut().release(ptr);
|
self.allocator_mut().release(ptr);
|
||||||
#[cfg(not(target_vendor = "apple"))]
|
#[cfg(not(target_vendor = "apple"))]
|
||||||
return std::ptr::null_mut();
|
return ptr::null_mut();
|
||||||
#[cfg(target_vendor = "apple")]
|
#[cfg(target_vendor = "apple")]
|
||||||
return self.allocator_mut().alloc(0, 0x8);
|
return self.allocator_mut().alloc(0, 0x8);
|
||||||
}
|
}
|
||||||
let ret = self.allocator_mut().alloc(size, 0x8);
|
let ret = self.allocator_mut().alloc(size, 0x8);
|
||||||
if ptr != std::ptr::null_mut() && ret != std::ptr::null_mut() {
|
if ptr != ptr::null_mut() && ret != ptr::null_mut() {
|
||||||
let old_size = self.allocator_mut().get_usable_size(ptr);
|
let old_size = self.allocator_mut().get_usable_size(ptr);
|
||||||
let copy_size = if size < old_size { size } else { old_size };
|
let copy_size = if size < old_size { size } else { old_size };
|
||||||
(ptr as *mut u8).copy_to(ret as *mut u8, copy_size);
|
(ptr as *mut u8).copy_to(ret as *mut u8, copy_size);
|
||||||
@ -947,7 +1019,7 @@ impl AsanRuntime {
|
|||||||
return self.allocator_mut().alloc(0, 0x8);
|
return self.allocator_mut().alloc(0, 0x8);
|
||||||
}
|
}
|
||||||
let ret = self.allocator_mut().alloc(size, 0x8);
|
let ret = self.allocator_mut().alloc(size, 0x8);
|
||||||
if ptr != std::ptr::null_mut() && ret != std::ptr::null_mut() {
|
if ptr != ptr::null_mut() && ret != ptr::null_mut() {
|
||||||
let old_size = self.allocator_mut().get_usable_size(ptr);
|
let old_size = self.allocator_mut().get_usable_size(ptr);
|
||||||
let copy_size = if size < old_size { size } else { old_size };
|
let copy_size = if size < old_size { size } else { old_size };
|
||||||
(ptr as *mut u8).copy_to(ret as *mut u8, copy_size);
|
(ptr as *mut u8).copy_to(ret as *mut u8, copy_size);
|
||||||
@ -966,9 +1038,10 @@ impl AsanRuntime {
|
|||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook__o_realloc");
|
||||||
unsafe {
|
unsafe {
|
||||||
let ret = self.allocator_mut().alloc(size, 0x8);
|
let ret = self.allocator_mut().alloc(size, 0x8);
|
||||||
if ptr != std::ptr::null_mut() && ret != std::ptr::null_mut() {
|
if ptr != ptr::null_mut() && ret != ptr::null_mut() {
|
||||||
let old_size = self.allocator_mut().get_usable_size(ptr);
|
let old_size = self.allocator_mut().get_usable_size(ptr);
|
||||||
let copy_size = if size < old_size { size } else { old_size };
|
let copy_size = if size < old_size { size } else { old_size };
|
||||||
(ptr as *mut u8).copy_to(ret as *mut u8, copy_size);
|
(ptr as *mut u8).copy_to(ret as *mut u8, copy_size);
|
||||||
@ -981,6 +1054,7 @@ impl AsanRuntime {
|
|||||||
#[expect(non_snake_case)]
|
#[expect(non_snake_case)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hook_check__o_free(&mut self, ptr: *mut c_void) -> bool {
|
pub fn hook_check__o_free(&mut self, ptr: *mut c_void) -> bool {
|
||||||
|
log::trace!("hook_check__o_free");
|
||||||
self.allocator_mut().is_managed(ptr)
|
self.allocator_mut().is_managed(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -992,7 +1066,8 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(ptr: *mut c_void) -> usize,
|
_original: extern "C" fn(ptr: *mut c_void) -> usize,
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
if ptr != std::ptr::null_mut() {
|
log::trace!("hook__o_free");
|
||||||
|
if ptr != ptr::null_mut() {
|
||||||
unsafe { self.allocator_mut().release(ptr) }
|
unsafe { self.allocator_mut().release(ptr) }
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
@ -1009,7 +1084,7 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(ptr: *mut c_void) -> usize,
|
_original: extern "C" fn(ptr: *mut c_void) -> usize,
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
if ptr != std::ptr::null_mut() {
|
if ptr != ptr::null_mut() {
|
||||||
unsafe { self.allocator_mut().release(ptr) }
|
unsafe { self.allocator_mut().release(ptr) }
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
@ -1023,6 +1098,7 @@ impl AsanRuntime {
|
|||||||
alignment: usize,
|
alignment: usize,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_memalign");
|
||||||
unsafe { self.allocator_mut().alloc(size, alignment) }
|
unsafe { self.allocator_mut().alloc(size, alignment) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1034,6 +1110,7 @@ impl AsanRuntime {
|
|||||||
alignment: usize,
|
alignment: usize,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
|
log::trace!("hook_posix_memalign");
|
||||||
unsafe {
|
unsafe {
|
||||||
*pptr = self.allocator_mut().alloc(size, alignment);
|
*pptr = self.allocator_mut().alloc(size, alignment);
|
||||||
}
|
}
|
||||||
@ -1047,6 +1124,7 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(ptr: *mut c_void) -> usize,
|
_original: extern "C" fn(ptr: *mut c_void) -> usize,
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("hook_malloc_usable_size");
|
||||||
self.allocator_mut().get_usable_size(ptr)
|
self.allocator_mut().get_usable_size(ptr)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -1219,6 +1297,7 @@ impl AsanRuntime {
|
|||||||
_file_offset_low: u32,
|
_file_offset_low: u32,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> *const c_void {
|
) -> *const c_void {
|
||||||
|
log::trace!("hook_MapViewOfFile size {:?}", size);
|
||||||
let ret = original(
|
let ret = original(
|
||||||
_handle,
|
_handle,
|
||||||
_desired_access,
|
_desired_access,
|
||||||
@ -1226,7 +1305,86 @@ impl AsanRuntime {
|
|||||||
_file_offset_low,
|
_file_offset_low,
|
||||||
size,
|
size,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut size = size;
|
||||||
|
if size == 0 {
|
||||||
|
// The entire file is mapped starting from the offset
|
||||||
|
// We need to get the real size before unpoisoning it
|
||||||
|
// Use VirtualQuery to get the size of the mapped memory
|
||||||
|
let mut mem_info = MEMORY_BASIC_INFORMATION {
|
||||||
|
BaseAddress: ptr::null_mut(),
|
||||||
|
AllocationBase: ptr::null_mut(),
|
||||||
|
AllocationProtect: 0,
|
||||||
|
RegionSize: 0,
|
||||||
|
State: 0,
|
||||||
|
Protect: 0,
|
||||||
|
Type: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = unsafe {
|
||||||
|
VirtualQuery(
|
||||||
|
ret as *const winapi::ctypes::c_void,
|
||||||
|
&mut mem_info,
|
||||||
|
size_of::<MEMORY_BASIC_INFORMATION>(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if result == 0 {
|
||||||
|
log::error!("Failed to query virtual memory");
|
||||||
|
} else {
|
||||||
|
size = mem_info.RegionSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.unpoison(ret as usize, size);
|
self.unpoison(ret as usize, size);
|
||||||
|
log::trace!("hook_MapViewOfFile returns {:p}", ret);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[expect(non_snake_case)]
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub fn hook_UnmapViewOfFile(
|
||||||
|
&mut self,
|
||||||
|
original: extern "C" fn(ptr: *const c_void) -> bool,
|
||||||
|
ptr: *const c_void,
|
||||||
|
) -> bool {
|
||||||
|
log::info!("hook_UnmapViewOfFile {:p}", ptr);
|
||||||
|
|
||||||
|
let mut size = 0;
|
||||||
|
// We need to get the mapping size before poisoning it
|
||||||
|
// Use VirtualQuery to get the size of the mapped memory
|
||||||
|
let mut mem_info = MEMORY_BASIC_INFORMATION {
|
||||||
|
BaseAddress: ptr::null_mut(),
|
||||||
|
AllocationBase: ptr::null_mut(),
|
||||||
|
AllocationProtect: 0,
|
||||||
|
RegionSize: 0,
|
||||||
|
State: 0,
|
||||||
|
Protect: 0,
|
||||||
|
Type: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = unsafe {
|
||||||
|
VirtualQuery(
|
||||||
|
ptr as *const winapi::ctypes::c_void,
|
||||||
|
&mut mem_info,
|
||||||
|
size_of::<MEMORY_BASIC_INFORMATION>(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if result == 0 {
|
||||||
|
log::error!("Failed to query virtual memory for poisoning");
|
||||||
|
} else {
|
||||||
|
size = mem_info.RegionSize;
|
||||||
|
log::info!("Size of mapped memory: {} bytes", size);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = original(ptr);
|
||||||
|
|
||||||
|
if size > 0 {
|
||||||
|
unsafe { self.poison(ptr as usize, size) };
|
||||||
|
}
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1238,7 +1396,8 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(ptr: *mut c_void) -> usize,
|
_original: extern "C" fn(ptr: *mut c_void) -> usize,
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
if ptr != std::ptr::null_mut() {
|
log::trace!("delete[]");
|
||||||
|
if ptr != ptr::null_mut() {
|
||||||
unsafe { self.allocator_mut().release(ptr) }
|
unsafe { self.allocator_mut().release(ptr) }
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
@ -1253,7 +1412,8 @@ impl AsanRuntime {
|
|||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
_ulong: u64,
|
_ulong: u64,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
if ptr != std::ptr::null_mut() {
|
log::trace!("delete[]");
|
||||||
|
if ptr != ptr::null_mut() {
|
||||||
unsafe { self.allocator_mut().release(ptr) }
|
unsafe { self.allocator_mut().release(ptr) }
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
@ -1269,7 +1429,8 @@ impl AsanRuntime {
|
|||||||
_ulong: u64,
|
_ulong: u64,
|
||||||
_alignment: usize,
|
_alignment: usize,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
if ptr != std::ptr::null_mut() {
|
log::trace!("delete[](void*, std::size_t)");
|
||||||
|
if ptr != ptr::null_mut() {
|
||||||
unsafe { self.allocator_mut().release(ptr) }
|
unsafe { self.allocator_mut().release(ptr) }
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
@ -1284,7 +1445,8 @@ impl AsanRuntime {
|
|||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
_nothrow: *const c_void,
|
_nothrow: *const c_void,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
if ptr != std::ptr::null_mut() {
|
log::trace!("delete[](void*, std::size_t, std::align_val_t)");
|
||||||
|
if ptr != ptr::null_mut() {
|
||||||
unsafe { self.allocator_mut().release(ptr) }
|
unsafe { self.allocator_mut().release(ptr) }
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
@ -1304,7 +1466,8 @@ impl AsanRuntime {
|
|||||||
_alignment: usize,
|
_alignment: usize,
|
||||||
_nothrow: *const c_void,
|
_nothrow: *const c_void,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
if ptr != std::ptr::null_mut() {
|
log::trace!("delete[](void*, std::nothrow_t const&)");
|
||||||
|
if ptr != ptr::null_mut() {
|
||||||
unsafe { self.allocator_mut().release(ptr) }
|
unsafe { self.allocator_mut().release(ptr) }
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
@ -1319,7 +1482,8 @@ impl AsanRuntime {
|
|||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
_alignment: usize,
|
_alignment: usize,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
if ptr != std::ptr::null_mut() {
|
log::trace!("delete[](void*, std::align_val_t)");
|
||||||
|
if ptr != ptr::null_mut() {
|
||||||
unsafe { self.allocator_mut().release(ptr) }
|
unsafe { self.allocator_mut().release(ptr) }
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
@ -1333,7 +1497,7 @@ impl AsanRuntime {
|
|||||||
_original: extern "C" fn(ptr: *mut c_void) -> usize,
|
_original: extern "C" fn(ptr: *mut c_void) -> usize,
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
if ptr != std::ptr::null_mut() {
|
if ptr != ptr::null_mut() {
|
||||||
unsafe { self.allocator_mut().release(ptr) }
|
unsafe { self.allocator_mut().release(ptr) }
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
@ -1348,7 +1512,8 @@ impl AsanRuntime {
|
|||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
_ulong: u64,
|
_ulong: u64,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
if ptr != std::ptr::null_mut() {
|
log::trace!("delete(void*)");
|
||||||
|
if ptr != ptr::null_mut() {
|
||||||
unsafe { self.allocator_mut().release(ptr) }
|
unsafe { self.allocator_mut().release(ptr) }
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
@ -1364,7 +1529,8 @@ impl AsanRuntime {
|
|||||||
_ulong: u64,
|
_ulong: u64,
|
||||||
_alignment: usize,
|
_alignment: usize,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
if ptr != std::ptr::null_mut() {
|
log::trace!("delete(void*)");
|
||||||
|
if ptr != ptr::null_mut() {
|
||||||
unsafe { self.allocator_mut().release(ptr) }
|
unsafe { self.allocator_mut().release(ptr) }
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
@ -1379,7 +1545,8 @@ impl AsanRuntime {
|
|||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
_nothrow: *const c_void,
|
_nothrow: *const c_void,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
if ptr != std::ptr::null_mut() {
|
log::trace!("delete(void*)");
|
||||||
|
if ptr != ptr::null_mut() {
|
||||||
unsafe { self.allocator_mut().release(ptr) }
|
unsafe { self.allocator_mut().release(ptr) }
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
@ -1399,7 +1566,8 @@ impl AsanRuntime {
|
|||||||
_alignment: usize,
|
_alignment: usize,
|
||||||
_nothrow: *const c_void,
|
_nothrow: *const c_void,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
if ptr != std::ptr::null_mut() {
|
log::trace!("delete(void*)");
|
||||||
|
if ptr != ptr::null_mut() {
|
||||||
unsafe { self.allocator_mut().release(ptr) }
|
unsafe { self.allocator_mut().release(ptr) }
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
@ -1414,7 +1582,8 @@ impl AsanRuntime {
|
|||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
_alignment: usize,
|
_alignment: usize,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
if ptr != std::ptr::null_mut() {
|
log::trace!("delete(void*)");
|
||||||
|
if ptr != ptr::null_mut() {
|
||||||
unsafe { self.allocator_mut().release(ptr) }
|
unsafe { self.allocator_mut().release(ptr) }
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
@ -1439,6 +1608,7 @@ impl AsanRuntime {
|
|||||||
fd: i32,
|
fd: i32,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_mmap");
|
||||||
let res = original(addr, length, prot, flags, fd, offset);
|
let res = original(addr, length, prot, flags, fd, offset);
|
||||||
if res != (-1_isize as *mut c_void) {
|
if res != (-1_isize as *mut c_void) {
|
||||||
self.allocator_mut()
|
self.allocator_mut()
|
||||||
@ -1456,6 +1626,7 @@ impl AsanRuntime {
|
|||||||
addr: *const c_void,
|
addr: *const c_void,
|
||||||
length: usize,
|
length: usize,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
|
log::trace!("hook_munmap");
|
||||||
let res = original(addr, length);
|
let res = original(addr, length);
|
||||||
if res != -1 {
|
if res != -1 {
|
||||||
Allocator::poison(self.allocator_mut().map_to_shadow(addr as usize), length);
|
Allocator::poison(self.allocator_mut().map_to_shadow(addr as usize), length);
|
||||||
@ -1472,6 +1643,7 @@ impl AsanRuntime {
|
|||||||
buf: *const c_void,
|
buf: *const c_void,
|
||||||
count: usize,
|
count: usize,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("hook__write");
|
||||||
self.hook_write(original, fd, buf, count)
|
self.hook_write(original, fd, buf, count)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -1482,6 +1654,7 @@ impl AsanRuntime {
|
|||||||
buf: *const c_void,
|
buf: *const c_void,
|
||||||
count: usize,
|
count: usize,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("hook_write");
|
||||||
if !self.allocator_mut().check_shadow(buf, count)
|
if !self.allocator_mut().check_shadow(buf, count)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
||||||
"write".to_string(),
|
"write".to_string(),
|
||||||
@ -1505,6 +1678,7 @@ impl AsanRuntime {
|
|||||||
buf: *mut c_void,
|
buf: *mut c_void,
|
||||||
count: usize,
|
count: usize,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("hook__read");
|
||||||
self.hook_read(original, fd, buf, count)
|
self.hook_read(original, fd, buf, count)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -1515,6 +1689,7 @@ impl AsanRuntime {
|
|||||||
buf: *mut c_void,
|
buf: *mut c_void,
|
||||||
count: usize,
|
count: usize,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("hook_read");
|
||||||
if !self.allocator_mut().check_shadow(buf, count)
|
if !self.allocator_mut().check_shadow(buf, count)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
||||||
"read".to_string(),
|
"read".to_string(),
|
||||||
@ -1537,6 +1712,7 @@ impl AsanRuntime {
|
|||||||
size: u32,
|
size: u32,
|
||||||
stream: *mut c_void,
|
stream: *mut c_void,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_fgets");
|
||||||
if !self.allocator_mut().check_shadow(s, size as usize)
|
if !self.allocator_mut().check_shadow(s, size as usize)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
||||||
"fgets".to_string(),
|
"fgets".to_string(),
|
||||||
@ -1559,6 +1735,7 @@ impl AsanRuntime {
|
|||||||
s2: *const c_void,
|
s2: *const c_void,
|
||||||
n: usize,
|
n: usize,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
|
log::trace!("hook_memcmp");
|
||||||
if !self.allocator_mut().check_shadow(s1, n)
|
if !self.allocator_mut().check_shadow(s1, n)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
||||||
"memcmp".to_string(),
|
"memcmp".to_string(),
|
||||||
@ -1592,6 +1769,7 @@ impl AsanRuntime {
|
|||||||
src: *const c_void,
|
src: *const c_void,
|
||||||
n: usize,
|
n: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_memcpy dest {dest:#?} src {src:#?} size {n}");
|
||||||
if !self.allocator_mut().check_shadow(dest, n)
|
if !self.allocator_mut().check_shadow(dest, n)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
||||||
"memcpy".to_string(),
|
"memcpy".to_string(),
|
||||||
@ -1626,6 +1804,7 @@ impl AsanRuntime {
|
|||||||
src: *const c_void,
|
src: *const c_void,
|
||||||
n: usize,
|
n: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_mempcpy");
|
||||||
if !self.allocator_mut().check_shadow(dest, n)
|
if !self.allocator_mut().check_shadow(dest, n)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
||||||
"mempcpy".to_string(),
|
"mempcpy".to_string(),
|
||||||
@ -1659,6 +1838,7 @@ impl AsanRuntime {
|
|||||||
src: *const c_void,
|
src: *const c_void,
|
||||||
n: usize,
|
n: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_memmove");
|
||||||
if !self.allocator_mut().check_shadow(dest, n)
|
if !self.allocator_mut().check_shadow(dest, n)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
||||||
"memmove".to_string(),
|
"memmove".to_string(),
|
||||||
@ -1693,6 +1873,7 @@ impl AsanRuntime {
|
|||||||
c: i32,
|
c: i32,
|
||||||
n: usize,
|
n: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_memset");
|
||||||
if !self.allocator_mut().check_shadow(dest, n)
|
if !self.allocator_mut().check_shadow(dest, n)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
||||||
"memset".to_string(),
|
"memset".to_string(),
|
||||||
@ -1715,6 +1896,7 @@ impl AsanRuntime {
|
|||||||
c: i32,
|
c: i32,
|
||||||
n: usize,
|
n: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_memchr");
|
||||||
if !self.allocator_mut().check_shadow(s, n)
|
if !self.allocator_mut().check_shadow(s, n)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
||||||
"memchr".to_string(),
|
"memchr".to_string(),
|
||||||
@ -1738,6 +1920,7 @@ impl AsanRuntime {
|
|||||||
c: i32,
|
c: i32,
|
||||||
n: usize,
|
n: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_memrchr");
|
||||||
if !self.allocator_mut().check_shadow(s, n)
|
if !self.allocator_mut().check_shadow(s, n)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
||||||
"memrchr".to_string(),
|
"memrchr".to_string(),
|
||||||
@ -1766,6 +1949,7 @@ impl AsanRuntime {
|
|||||||
needle: *const c_void,
|
needle: *const c_void,
|
||||||
needlelen: usize,
|
needlelen: usize,
|
||||||
) -> *mut c_void {
|
) -> *mut c_void {
|
||||||
|
log::trace!("hook_memmem");
|
||||||
if !self.allocator_mut().check_shadow(haystack, haystacklen)
|
if !self.allocator_mut().check_shadow(haystack, haystacklen)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
||||||
"memmem".to_string(),
|
"memmem".to_string(),
|
||||||
@ -1799,6 +1983,7 @@ impl AsanRuntime {
|
|||||||
s: *mut c_void,
|
s: *mut c_void,
|
||||||
n: usize,
|
n: usize,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("hook_bzero");
|
||||||
if !self.allocator_mut().check_shadow(s, n)
|
if !self.allocator_mut().check_shadow(s, n)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
||||||
"bzero".to_string(),
|
"bzero".to_string(),
|
||||||
@ -1821,6 +2006,7 @@ impl AsanRuntime {
|
|||||||
s: *mut c_void,
|
s: *mut c_void,
|
||||||
n: usize,
|
n: usize,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("hook_explicit_bzero");
|
||||||
if !self.allocator_mut().check_shadow(s, n)
|
if !self.allocator_mut().check_shadow(s, n)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
||||||
"explicit_bzero".to_string(),
|
"explicit_bzero".to_string(),
|
||||||
@ -1844,6 +2030,7 @@ impl AsanRuntime {
|
|||||||
s2: *const c_void,
|
s2: *const c_void,
|
||||||
n: usize,
|
n: usize,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
|
log::trace!("hook_bcmp");
|
||||||
if !self.allocator_mut().check_shadow(s1, n)
|
if !self.allocator_mut().check_shadow(s1, n)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
||||||
"bcmp".to_string(),
|
"bcmp".to_string(),
|
||||||
@ -1880,6 +2067,7 @@ impl AsanRuntime {
|
|||||||
|
|
||||||
fn strlen(s: *const c_char) -> usize;
|
fn strlen(s: *const c_char) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_strchr");
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
.check_shadow(s as *const c_void, unsafe { strlen(s) })
|
.check_shadow(s as *const c_void, unsafe { strlen(s) })
|
||||||
@ -1906,6 +2094,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn strlen(s: *const c_char) -> usize;
|
fn strlen(s: *const c_char) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_strrchr");
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
.check_shadow(s as *const c_void, unsafe { strlen(s) })
|
.check_shadow(s as *const c_void, unsafe { strlen(s) })
|
||||||
@ -1932,6 +2121,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn strlen(s: *const c_char) -> usize;
|
fn strlen(s: *const c_char) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_strcasecmp");
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
.check_shadow(s1 as *const c_void, unsafe { strlen(s1) })
|
.check_shadow(s1 as *const c_void, unsafe { strlen(s1) })
|
||||||
@ -1969,6 +2159,7 @@ impl AsanRuntime {
|
|||||||
s2: *const c_char,
|
s2: *const c_char,
|
||||||
n: usize,
|
n: usize,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
|
log::trace!("hook_strncasecmp");
|
||||||
if !self.allocator_mut().check_shadow(s1 as *const c_void, n)
|
if !self.allocator_mut().check_shadow(s1 as *const c_void, n)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
||||||
"strncasecmp".to_string(),
|
"strncasecmp".to_string(),
|
||||||
@ -2004,6 +2195,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn strlen(s: *const c_char) -> usize;
|
fn strlen(s: *const c_char) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_strcat");
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
.check_shadow(s1 as *const c_void, unsafe { strlen(s1) })
|
.check_shadow(s1 as *const c_void, unsafe { strlen(s1) })
|
||||||
@ -2043,6 +2235,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn strlen(s: *const c_char) -> usize;
|
fn strlen(s: *const c_char) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_strcmp");
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
.check_shadow(s1 as *const c_void, unsafe { strlen(s1) })
|
.check_shadow(s1 as *const c_void, unsafe { strlen(s1) })
|
||||||
@ -2083,6 +2276,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn strnlen(s: *const c_char, n: usize) -> usize;
|
fn strnlen(s: *const c_char, n: usize) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_strncmp");
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
.check_shadow(s1 as *const c_void, unsafe { strnlen(s1, n) })
|
.check_shadow(s1 as *const c_void, unsafe { strnlen(s1, n) })
|
||||||
@ -2122,6 +2316,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn strlen(s: *const c_char) -> usize;
|
fn strlen(s: *const c_char) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_strcpy");
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
.check_shadow(dest as *const c_void, unsafe { strlen(src) })
|
.check_shadow(dest as *const c_void, unsafe { strlen(src) })
|
||||||
@ -2162,6 +2357,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn strlen(s: *const c_char) -> usize;
|
fn strlen(s: *const c_char) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_strncpy");
|
||||||
if !self.allocator_mut().check_shadow(dest as *const c_void, n)
|
if !self.allocator_mut().check_shadow(dest as *const c_void, n)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgWrite((
|
||||||
"strncpy".to_string(),
|
"strncpy".to_string(),
|
||||||
@ -2198,6 +2394,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn strlen(s: *const c_char) -> usize;
|
fn strlen(s: *const c_char) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_stpcpy");
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
.check_shadow(dest as *const c_void, unsafe { strlen(src) })
|
.check_shadow(dest as *const c_void, unsafe { strlen(src) })
|
||||||
@ -2234,6 +2431,7 @@ impl AsanRuntime {
|
|||||||
original: extern "C" fn(s: *const c_char) -> *mut c_char,
|
original: extern "C" fn(s: *const c_char) -> *mut c_char,
|
||||||
s: *const c_char,
|
s: *const c_char,
|
||||||
) -> *mut c_char {
|
) -> *mut c_char {
|
||||||
|
log::trace!("hook__strdup");
|
||||||
self.hook_strdup(original, s)
|
self.hook_strdup(original, s)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -2246,6 +2444,7 @@ impl AsanRuntime {
|
|||||||
fn strlen(s: *const c_char) -> usize;
|
fn strlen(s: *const c_char) -> usize;
|
||||||
fn strcpy(dest: *mut c_char, src: *const c_char) -> *mut c_char;
|
fn strcpy(dest: *mut c_char, src: *const c_char) -> *mut c_char;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_strdup");
|
||||||
let size = unsafe { strlen(s) + 1 };
|
let size = unsafe { strlen(s) + 1 };
|
||||||
if !self.allocator_mut().check_shadow(s as *const c_void, size)
|
if !self.allocator_mut().check_shadow(s as *const c_void, size)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
||||||
@ -2272,6 +2471,7 @@ impl AsanRuntime {
|
|||||||
original: extern "C" fn(s: *const c_char) -> usize,
|
original: extern "C" fn(s: *const c_char) -> usize,
|
||||||
s: *const c_char,
|
s: *const c_char,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("hook_strlen");
|
||||||
let size = original(s);
|
let size = original(s);
|
||||||
if !self.allocator_mut().check_shadow(s as *const c_void, size)
|
if !self.allocator_mut().check_shadow(s as *const c_void, size)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
||||||
@ -2294,6 +2494,7 @@ impl AsanRuntime {
|
|||||||
s: *const c_char,
|
s: *const c_char,
|
||||||
n: usize,
|
n: usize,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("hook_strnlen");
|
||||||
let size = original(s, n);
|
let size = original(s, n);
|
||||||
if !self.allocator_mut().check_shadow(s as *const c_void, size)
|
if !self.allocator_mut().check_shadow(s as *const c_void, size)
|
||||||
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
&& AsanErrors::get_mut_blocking().report_error(AsanError::BadFuncArgRead((
|
||||||
@ -2319,6 +2520,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn strlen(s: *const c_char) -> usize;
|
fn strlen(s: *const c_char) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_strstr");
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
.check_shadow(haystack as *const c_void, unsafe { strlen(haystack) })
|
.check_shadow(haystack as *const c_void, unsafe { strlen(haystack) })
|
||||||
@ -2358,6 +2560,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn strlen(s: *const c_char) -> usize;
|
fn strlen(s: *const c_char) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_strcasestr");
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
.check_shadow(haystack as *const c_void, unsafe { strlen(haystack) })
|
.check_shadow(haystack as *const c_void, unsafe { strlen(haystack) })
|
||||||
@ -2396,6 +2599,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn strlen(s: *const c_char) -> usize;
|
fn strlen(s: *const c_char) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_atoi");
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
.check_shadow(s as *const c_void, unsafe { strlen(s) })
|
.check_shadow(s as *const c_void, unsafe { strlen(s) })
|
||||||
@ -2422,6 +2626,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn strlen(s: *const c_char) -> usize;
|
fn strlen(s: *const c_char) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_atol");
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
.check_shadow(s as *const c_void, unsafe { strlen(s) })
|
.check_shadow(s as *const c_void, unsafe { strlen(s) })
|
||||||
@ -2448,6 +2653,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn strlen(s: *const c_char) -> usize;
|
fn strlen(s: *const c_char) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_atoll");
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
.check_shadow(s as *const c_void, unsafe { strlen(s) })
|
.check_shadow(s as *const c_void, unsafe { strlen(s) })
|
||||||
@ -2471,6 +2677,7 @@ impl AsanRuntime {
|
|||||||
original: extern "C" fn(s: *const wchar_t) -> usize,
|
original: extern "C" fn(s: *const wchar_t) -> usize,
|
||||||
s: *const wchar_t,
|
s: *const wchar_t,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
log::trace!("hook_wcslen");
|
||||||
let size = original(s);
|
let size = original(s);
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
@ -2499,6 +2706,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn wcslen(s: *const wchar_t) -> usize;
|
fn wcslen(s: *const wchar_t) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_wcscpy");
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
.check_shadow(dest as *const c_void, unsafe { (wcslen(src) + 1) * 2 })
|
.check_shadow(dest as *const c_void, unsafe { (wcslen(src) + 1) * 2 })
|
||||||
@ -2539,6 +2747,7 @@ impl AsanRuntime {
|
|||||||
extern "system" {
|
extern "system" {
|
||||||
fn wcslen(s: *const wchar_t) -> usize;
|
fn wcslen(s: *const wchar_t) -> usize;
|
||||||
}
|
}
|
||||||
|
log::trace!("hook_wcscmp");
|
||||||
if !self
|
if !self
|
||||||
.allocator_mut()
|
.allocator_mut()
|
||||||
.check_shadow(s1 as *const c_void, unsafe { (wcslen(s1) + 1) * 2 })
|
.check_shadow(s1 as *const c_void, unsafe { (wcslen(s1) + 1) * 2 })
|
||||||
|
@ -1,23 +1,19 @@
|
|||||||
use core::fmt::{self, Debug, Formatter};
|
use core::fmt::{self, Debug, Formatter};
|
||||||
#[cfg(all(windows, not(test)))]
|
#[cfg(all(windows, not(test)))]
|
||||||
use std::process::abort;
|
use std::process::abort;
|
||||||
use std::{ffi::c_void, marker::PhantomData};
|
use std::{cell::RefCell, ffi::c_void, marker::PhantomData, rc::Rc};
|
||||||
|
|
||||||
use frida_gum::{
|
use frida_gum::{
|
||||||
stalker::{NoneEventSink, Stalker},
|
stalker::{NoneEventSink, Stalker},
|
||||||
Gum, MemoryRange, NativePointer,
|
Gum, MemoryRange, NativePointer,
|
||||||
};
|
};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use libafl::{
|
use libafl::executors::{hooks::inprocess::InProcessHooks, inprocess::HasInProcessHooks};
|
||||||
executors::{hooks::inprocess::InProcessHooks, inprocess::HasInProcessHooks},
|
|
||||||
inputs::Input,
|
|
||||||
state::{HasCurrentTestcase, HasSolutions},
|
|
||||||
};
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
executors::{Executor, ExitKind, HasObservers, InProcessExecutor},
|
executors::{Executor, ExitKind, HasObservers, InProcessExecutor},
|
||||||
inputs::{NopTargetBytesConverter, TargetBytesConverter},
|
inputs::{Input, NopTargetBytesConverter, TargetBytesConverter},
|
||||||
observers::ObserversTuple,
|
observers::ObserversTuple,
|
||||||
state::HasExecutions,
|
state::{HasCurrentTestcase, HasExecutions, HasSolutions},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
use libafl_bolts::{tuples::RefIndexable, AsSlice};
|
use libafl_bolts::{tuples::RefIndexable, AsSlice};
|
||||||
@ -29,38 +25,41 @@ use crate::helper::{FridaInstrumentationHelper, FridaRuntimeTuple};
|
|||||||
use crate::windows_hooks::initialize;
|
use crate::windows_hooks::initialize;
|
||||||
|
|
||||||
/// The [`FridaInProcessExecutor`] is an [`Executor`] that executes the target in the same process, usinig [`frida`](https://frida.re/) for binary-only instrumentation.
|
/// The [`FridaInProcessExecutor`] is an [`Executor`] that executes the target in the same process, usinig [`frida`](https://frida.re/) for binary-only instrumentation.
|
||||||
pub struct FridaInProcessExecutor<'a, 'b, 'c, EM, H, I, OT, RT, S, TC, Z> {
|
pub struct FridaInProcessExecutor<'a, 'b, EM, H, I, OT, RT, S, TC, Z> {
|
||||||
base: InProcessExecutor<'a, EM, H, I, OT, S, Z>,
|
base: InProcessExecutor<'a, EM, H, I, OT, S, Z>,
|
||||||
/// `thread_id` for the Stalker
|
/// `thread_id` for the Stalker
|
||||||
thread_id: Option<u32>,
|
thread_id: Option<u32>,
|
||||||
/// Frida's dynamic rewriting engine
|
/// Frida's dynamic rewriting engine
|
||||||
stalker: Stalker,
|
stalker: Stalker,
|
||||||
/// User provided callback for instrumentation
|
/// User provided callback for instrumentation
|
||||||
helper: &'c mut FridaInstrumentationHelper<'b, RT>,
|
helper: Rc<RefCell<FridaInstrumentationHelper<'b, RT>>>,
|
||||||
target_bytes_converter: TC,
|
target_bytes_converter: TC,
|
||||||
followed: bool,
|
followed: bool,
|
||||||
_phantom: PhantomData<&'b u8>,
|
_phantom: PhantomData<&'b u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<EM, H, I, OT, RT, S, TC, Z> Debug
|
impl<EM, H, I, OT, RT, S, TC, Z> Debug
|
||||||
for FridaInProcessExecutor<'_, '_, '_, EM, H, I, OT, RT, S, TC, Z>
|
for FridaInProcessExecutor<'_, '_, EM, H, I, OT, RT, S, TC, Z>
|
||||||
where
|
where
|
||||||
OT: Debug,
|
OT: Debug,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("FridaInProcessExecutor")
|
f.debug_struct("FridaInProcessExecutor")
|
||||||
.field("base", &self.base)
|
.field("base", &self.base)
|
||||||
.field("helper", &self.helper)
|
.field("helper", &self.helper.borrow_mut())
|
||||||
.field("followed", &self.followed)
|
.field("followed", &self.followed)
|
||||||
.finish_non_exhaustive()
|
.finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<EM, H, I, OT, RT, S, TC, Z> Executor<EM, I, S, Z>
|
impl<EM, H, I, OT, RT, S, TC, Z> Executor<EM, I, S, Z>
|
||||||
for FridaInProcessExecutor<'_, '_, '_, EM, H, I, OT, RT, S, TC, Z>
|
for FridaInProcessExecutor<'_, '_, EM, H, I, OT, RT, S, TC, Z>
|
||||||
where
|
where
|
||||||
H: FnMut(&I) -> ExitKind,
|
H: FnMut(&I) -> ExitKind,
|
||||||
|
I: Input,
|
||||||
S: HasExecutions,
|
S: HasExecutions,
|
||||||
|
S: HasCurrentTestcase<I>,
|
||||||
|
S: HasSolutions<I>,
|
||||||
TC: TargetBytesConverter<I>,
|
TC: TargetBytesConverter<I>,
|
||||||
OT: ObserversTuple<I, S>,
|
OT: ObserversTuple<I, S>,
|
||||||
RT: FridaRuntimeTuple,
|
RT: FridaRuntimeTuple,
|
||||||
@ -75,13 +74,12 @@ where
|
|||||||
input: &I,
|
input: &I,
|
||||||
) -> Result<ExitKind, Error> {
|
) -> Result<ExitKind, Error> {
|
||||||
let target_bytes = self.target_bytes_converter.to_target_bytes(input);
|
let target_bytes = self.target_bytes_converter.to_target_bytes(input);
|
||||||
self.helper.pre_exec(target_bytes.as_slice())?;
|
self.helper.borrow_mut().pre_exec(target_bytes.as_slice())?;
|
||||||
if self.helper.stalker_enabled() {
|
if self.helper.borrow_mut().stalker_enabled() {
|
||||||
if self.followed {
|
if !(self.followed) {
|
||||||
self.stalker.activate(NativePointer(core::ptr::null_mut()));
|
|
||||||
} else {
|
|
||||||
self.followed = true;
|
self.followed = true;
|
||||||
let transformer = self.helper.transformer();
|
let helper_binding = self.helper.borrow_mut();
|
||||||
|
let transformer = helper_binding.transformer();
|
||||||
if let Some(thread_id) = self.thread_id {
|
if let Some(thread_id) = self.thread_id {
|
||||||
self.stalker.follow::<NoneEventSink>(
|
self.stalker.follow::<NoneEventSink>(
|
||||||
thread_id.try_into().unwrap(),
|
thread_id.try_into().unwrap(),
|
||||||
@ -90,11 +88,19 @@ where
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
self.stalker.follow_me::<NoneEventSink>(transformer, None);
|
self.stalker.follow_me::<NoneEventSink>(transformer, None);
|
||||||
|
self.stalker.deactivate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// We removed the fuzzer from the stalked ranges,
|
||||||
|
// but we need to pass the harness entry point
|
||||||
|
// so that Stalker knows to pick it despite the module being excluded
|
||||||
|
let harness_fn_ref: &H = self.base.harness();
|
||||||
|
let ptr: *const H = harness_fn_ref as *const H;
|
||||||
|
log::info!("Activating Stalker for {:p}", ptr);
|
||||||
|
self.stalker.activate(NativePointer(ptr as *mut c_void));
|
||||||
}
|
}
|
||||||
let res = self.base.run_target(fuzzer, state, mgr, input);
|
let res = self.base.run_target(fuzzer, state, mgr, input);
|
||||||
if self.helper.stalker_enabled() {
|
if self.helper.borrow_mut().stalker_enabled() {
|
||||||
self.stalker.deactivate();
|
self.stalker.deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,13 +113,15 @@ where
|
|||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.helper.post_exec(target_bytes.as_slice())?;
|
self.helper
|
||||||
|
.borrow_mut()
|
||||||
|
.post_exec(target_bytes.as_slice())?;
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<EM, H, I, OT, RT, S, TC, Z> HasObservers
|
impl<EM, H, I, OT, RT, S, TC, Z> HasObservers
|
||||||
for FridaInProcessExecutor<'_, '_, '_, EM, H, I, OT, RT, S, TC, Z>
|
for FridaInProcessExecutor<'_, '_, EM, H, I, OT, RT, S, TC, Z>
|
||||||
{
|
{
|
||||||
type Observers = OT;
|
type Observers = OT;
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -127,8 +135,8 @@ impl<EM, H, I, OT, RT, S, TC, Z> HasObservers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, 'c, EM, H, I, OT, RT, S, Z>
|
impl<'a, 'b, EM, H, I, OT, RT, S, Z>
|
||||||
FridaInProcessExecutor<'a, 'b, 'c, EM, H, I, OT, RT, S, NopTargetBytesConverter<I>, Z>
|
FridaInProcessExecutor<'a, 'b, EM, H, I, OT, RT, S, NopTargetBytesConverter<I>, Z>
|
||||||
where
|
where
|
||||||
RT: FridaRuntimeTuple,
|
RT: FridaRuntimeTuple,
|
||||||
{
|
{
|
||||||
@ -136,7 +144,7 @@ where
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
gum: &'a Gum,
|
gum: &'a Gum,
|
||||||
base: InProcessExecutor<'a, EM, H, I, OT, S, Z>,
|
base: InProcessExecutor<'a, EM, H, I, OT, S, Z>,
|
||||||
helper: &'c mut FridaInstrumentationHelper<'b, RT>,
|
helper: Rc<RefCell<FridaInstrumentationHelper<'b, RT>>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
FridaInProcessExecutor::with_target_bytes_converter(
|
FridaInProcessExecutor::with_target_bytes_converter(
|
||||||
gum,
|
gum,
|
||||||
@ -151,7 +159,7 @@ where
|
|||||||
pub fn on_thread(
|
pub fn on_thread(
|
||||||
gum: &'a Gum,
|
gum: &'a Gum,
|
||||||
base: InProcessExecutor<'a, EM, H, I, OT, S, Z>,
|
base: InProcessExecutor<'a, EM, H, I, OT, S, Z>,
|
||||||
helper: &'c mut FridaInstrumentationHelper<'b, RT>,
|
helper: Rc<RefCell<FridaInstrumentationHelper<'b, RT>>>,
|
||||||
thread_id: u32,
|
thread_id: u32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
FridaInProcessExecutor::with_target_bytes_converter(
|
FridaInProcessExecutor::with_target_bytes_converter(
|
||||||
@ -164,8 +172,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, 'c, EM, H, I, OT, RT, S, TC, Z>
|
impl<'a, 'b, EM, H, I, OT, RT, S, TC, Z> FridaInProcessExecutor<'a, 'b, EM, H, I, OT, RT, S, TC, Z>
|
||||||
FridaInProcessExecutor<'a, 'b, 'c, EM, H, I, OT, RT, S, TC, Z>
|
|
||||||
where
|
where
|
||||||
RT: FridaRuntimeTuple,
|
RT: FridaRuntimeTuple,
|
||||||
{
|
{
|
||||||
@ -173,31 +180,39 @@ where
|
|||||||
pub fn with_target_bytes_converter(
|
pub fn with_target_bytes_converter(
|
||||||
gum: &'a Gum,
|
gum: &'a Gum,
|
||||||
base: InProcessExecutor<'a, EM, H, I, OT, S, Z>,
|
base: InProcessExecutor<'a, EM, H, I, OT, S, Z>,
|
||||||
helper: &'c mut FridaInstrumentationHelper<'b, RT>,
|
helper: Rc<RefCell<FridaInstrumentationHelper<'b, RT>>>,
|
||||||
thread_id: Option<u32>,
|
thread_id: Option<u32>,
|
||||||
target_bytes_converter: TC,
|
target_bytes_converter: TC,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut stalker = Stalker::new(gum);
|
let mut stalker = Stalker::new(gum);
|
||||||
// Include the current module (the fuzzer) in stalked ranges. We clone the ranges so that
|
let ranges = helper.borrow_mut().ranges().clone();
|
||||||
// we don't add it to the INSTRUMENTED ranges.
|
|
||||||
let mut ranges = helper.ranges().clone();
|
|
||||||
for module in frida_gum::Process::obtain(gum).enumerate_modules() {
|
for module in frida_gum::Process::obtain(gum).enumerate_modules() {
|
||||||
let range = module.range();
|
let range = module.range();
|
||||||
if (range.base_address().0 as usize) < Self::with_target_bytes_converter as usize
|
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)
|
||||||
< range.base_address().0 as u64 + range.size() as u64
|
< range.base_address().0 as u64 + range.size() as u64
|
||||||
{
|
{
|
||||||
ranges.insert(
|
log::info!(
|
||||||
range.base_address().0 as u64
|
"Fuzzer range: {:x}-{:x}",
|
||||||
..(range.base_address().0 as u64 + range.size() as u64),
|
range.base_address().0 as u64,
|
||||||
(0xffff, "fuzzer".to_string()),
|
range.base_address().0 as u64 + range.size() as u64
|
||||||
);
|
);
|
||||||
|
// Exclude the fuzzer from the stalked ranges, it is really unnecessary and harmfull.
|
||||||
|
// Otherwise, Stalker starts messing with our hooks and their callbacks
|
||||||
|
// wrecking havoc and causing deadlocks
|
||||||
|
stalker.exclude(&MemoryRange::new(
|
||||||
|
NativePointer(range.base_address().0),
|
||||||
|
range.size(),
|
||||||
|
));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!("disable_excludes: {:}", helper.disable_excludes);
|
log::info!(
|
||||||
if !helper.disable_excludes {
|
"disable_excludes: {:}",
|
||||||
|
helper.borrow_mut().disable_excludes
|
||||||
|
);
|
||||||
|
if !helper.borrow_mut().disable_excludes {
|
||||||
for range in ranges.gaps(&(0..u64::MAX)) {
|
for range in ranges.gaps(&(0..u64::MAX)) {
|
||||||
log::info!("excluding range: {:x}-{:x}", range.start, range.end);
|
log::info!("excluding range: {:x}-{:x}", range.start, range.end);
|
||||||
stalker.exclude(&MemoryRange::new(
|
stalker.exclude(&MemoryRange::new(
|
||||||
@ -225,8 +240,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
impl<'a, 'b, 'c, EM, H, I, OT, RT, S, TC, Z> HasInProcessHooks<I, S>
|
impl<'a, 'b, EM, H, I, OT, RT, S, TC, Z> HasInProcessHooks<I, S>
|
||||||
for FridaInProcessExecutor<'a, 'b, 'c, EM, H, I, OT, RT, S, TC, Z>
|
for FridaInProcessExecutor<'a, 'b, EM, H, I, OT, RT, S, TC, Z>
|
||||||
where
|
where
|
||||||
H: FnMut(&I) -> ExitKind,
|
H: FnMut(&I) -> ExitKind,
|
||||||
S: HasSolutions<I> + HasCurrentTestcase<I> + HasExecutions,
|
S: HasSolutions<I> + HasCurrentTestcase<I> + HasExecutions,
|
||||||
|
97
libafl_frida/src/frida_helper_shutdown_observer.rs
Normal file
97
libafl_frida/src/frida_helper_shutdown_observer.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
use std::{borrow::Cow, cell::RefCell, fmt, rc::Rc};
|
||||||
|
|
||||||
|
use libafl::{executors::ExitKind, inputs::HasTargetBytes, observers::Observer};
|
||||||
|
use libafl_bolts::{Error, Named};
|
||||||
|
use serde::{
|
||||||
|
de::{self, Deserialize, Deserializer, MapAccess, Visitor},
|
||||||
|
Serialize,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::helper::{FridaInstrumentationHelper, FridaRuntimeTuple};
|
||||||
|
|
||||||
|
#[allow(clippy::unsafe_derive_deserialize)]
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
/// An observer that shuts down the Frida helper upon crash
|
||||||
|
/// This is necessary as we don't want to keep the instrumentation around when processing the crash
|
||||||
|
pub struct FridaHelperObserver<'a, RT> {
|
||||||
|
#[serde(skip)]
|
||||||
|
// helper: &'a RefCell<FridaInstrumentationHelper<'a, RT>>,
|
||||||
|
helper: Rc<RefCell<FridaInstrumentationHelper<'a, RT>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, RT> FridaHelperObserver<'a, RT>
|
||||||
|
where
|
||||||
|
RT: FridaRuntimeTuple + 'a,
|
||||||
|
{
|
||||||
|
/// Creates a new [`FridaHelperObserver`] with the given name.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(
|
||||||
|
// helper: &'a RefCell<FridaInstrumentationHelper<'a, RT>>,
|
||||||
|
helper: Rc<RefCell<FridaInstrumentationHelper<'a, RT>>>,
|
||||||
|
) -> Self {
|
||||||
|
Self { helper }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, I, S, RT> Observer<I, S> for FridaHelperObserver<'a, RT>
|
||||||
|
where
|
||||||
|
// S: UsesInput,
|
||||||
|
// S::Input: HasTargetBytes,
|
||||||
|
RT: FridaRuntimeTuple + 'a,
|
||||||
|
I: HasTargetBytes,
|
||||||
|
{
|
||||||
|
fn post_exec(&mut self, _state: &mut S, input: &I, exit_kind: &ExitKind) -> Result<(), Error> {
|
||||||
|
if *exit_kind == ExitKind::Crash {
|
||||||
|
// Custom implementation logic for `FridaInProcessExecutor`
|
||||||
|
log::error!("Custom post_exec called for FridaInProcessExecutorHelper");
|
||||||
|
// Add any custom logic specific to FridaInProcessExecutor
|
||||||
|
return self.helper.borrow_mut().post_exec(&input.target_bytes());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<RT> Named for FridaHelperObserver<'_, RT> {
|
||||||
|
fn name(&self) -> &Cow<'static, str> {
|
||||||
|
static NAME: Cow<'static, str> = Cow::Borrowed("FridaHelperObserver");
|
||||||
|
&NAME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de, RT> Deserialize<'de> for FridaHelperObserver<'_, RT> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct FridaHelperObserverVisitor<'a, RT> {
|
||||||
|
// marker: std::marker::PhantomData<&'b mut FridaInstrumentationHelper<'a, RT>>,
|
||||||
|
marker: std::marker::PhantomData<&'a RT>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de, 'a, RT> Visitor<'de> for FridaHelperObserverVisitor<'a, RT> {
|
||||||
|
type Value = FridaHelperObserver<'a, RT>;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("a FridaHelperObserver struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<M>(self, _map: M) -> Result<Self::Value, M::Error>
|
||||||
|
where
|
||||||
|
M: MapAccess<'de>,
|
||||||
|
{
|
||||||
|
// Construct the struct without deserializing `helper`
|
||||||
|
Err(de::Error::custom(
|
||||||
|
"Cannot deserialize `FridaHelperObserver` with a mutable reference",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_struct(
|
||||||
|
"FridaHelperObserver",
|
||||||
|
&[], // No fields to deserialize
|
||||||
|
FridaHelperObserverVisitor {
|
||||||
|
marker: std::marker::PhantomData,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -451,18 +451,22 @@ impl FridaInstrumentationHelperBuilder {
|
|||||||
start..(start + range.size() as u64),
|
start..(start + range.size() as u64),
|
||||||
(i as u16, module.path()),
|
(i as u16, module.path()),
|
||||||
);
|
);
|
||||||
}
|
for skip in &skip_ranges {
|
||||||
for skip in skip_ranges {
|
match skip {
|
||||||
match skip {
|
SkipRange::Absolute(range) => ranges
|
||||||
SkipRange::Absolute(range) => ranges
|
.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 } => {
|
if name.eq(&module.name()) {
|
||||||
let module_details = Module::load(gum, &name);
|
log::trace!("Skipping {:?} {:?}", name, range);
|
||||||
let lib_start = module_details.range().base_address().0 as u64;
|
let module_details = Module::load(gum, &name.to_string());
|
||||||
ranges.borrow_mut().remove(
|
let lib_start = module_details.range().base_address().0 as u64;
|
||||||
(lib_start + range.start as u64)..(lib_start + range.end as u64),
|
ranges.borrow_mut().remove(
|
||||||
);
|
(lib_start + range.start as u64)
|
||||||
|
..(lib_start + range.end as u64),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -650,6 +654,7 @@ where
|
|||||||
let mut first = true;
|
let mut first = true;
|
||||||
let mut basic_block_start = 0;
|
let mut basic_block_start = 0;
|
||||||
let mut basic_block_size = 0;
|
let mut basic_block_size = 0;
|
||||||
|
// let _guard = AsanInHookGuard::new(); // Ensure ASAN_IN_HOOK is set and reset
|
||||||
for instruction in basic_block {
|
for instruction in basic_block {
|
||||||
let instr = instruction.instr();
|
let instr = instruction.instr();
|
||||||
let instr_size = instr.bytes().len();
|
let instr_size = instr.bytes().len();
|
||||||
|
@ -61,7 +61,7 @@ pub mod pthread_hook;
|
|||||||
#[cfg(feature = "cmplog")]
|
#[cfg(feature = "cmplog")]
|
||||||
pub mod cmplog_rt;
|
pub mod cmplog_rt;
|
||||||
|
|
||||||
/// The `LibAFL` firda helper
|
/// The `LibAFL` frida helper
|
||||||
pub mod helper;
|
pub mod helper;
|
||||||
|
|
||||||
pub mod drcov_rt;
|
pub mod drcov_rt;
|
||||||
@ -72,6 +72,9 @@ pub mod executor;
|
|||||||
/// Utilities
|
/// Utilities
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
/// The frida helper shutdown observer, needed to remove the instrumentation upon crashing
|
||||||
|
pub mod frida_helper_shutdown_observer;
|
||||||
|
|
||||||
// for parsing asan and cmplog cores
|
// for parsing asan and cmplog cores
|
||||||
|
|
||||||
use libafl_bolts::core_affinity::{get_core_ids, CoreId, Cores};
|
use libafl_bolts::core_affinity::{get_core_ids, CoreId, Cores};
|
||||||
@ -323,7 +326,7 @@ impl Default for FridaOptions {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use core::num::NonZero;
|
use core::num::NonZero;
|
||||||
use std::sync::OnceLock;
|
use std::{cell::RefCell, rc::Rc, sync::OnceLock};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use frida_gum::Gum;
|
use frida_gum::Gum;
|
||||||
@ -343,7 +346,6 @@ mod tests {
|
|||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
cli::FuzzerOptions, rands::StdRand, tuples::tuple_list, AsSlice, SimpleStdoutLogger,
|
cli::FuzzerOptions, rands::StdRand, tuples::tuple_list, AsSlice, SimpleStdoutLogger,
|
||||||
};
|
};
|
||||||
#[cfg(unix)]
|
|
||||||
use mimalloc::MiMalloc;
|
use mimalloc::MiMalloc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -353,16 +355,11 @@ mod tests {
|
|||||||
},
|
},
|
||||||
coverage_rt::CoverageRuntime,
|
coverage_rt::CoverageRuntime,
|
||||||
executor::FridaInProcessExecutor,
|
executor::FridaInProcessExecutor,
|
||||||
|
frida_helper_shutdown_observer::FridaHelperObserver,
|
||||||
helper::FridaInstrumentationHelper,
|
helper::FridaInstrumentationHelper,
|
||||||
};
|
};
|
||||||
#[cfg(unix)]
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static GLOBAL: MiMalloc = MiMalloc;
|
static GLOBAL: MiMalloc = MiMalloc;
|
||||||
#[cfg(windows)]
|
|
||||||
use dlmalloc::GlobalDlmalloc;
|
|
||||||
#[cfg(windows)]
|
|
||||||
#[global_allocator]
|
|
||||||
static GLOBAL: GlobalDlmalloc = GlobalDlmalloc;
|
|
||||||
|
|
||||||
static GUM: OnceLock<Gum> = OnceLock::new();
|
static GUM: OnceLock<Gum> = OnceLock::new();
|
||||||
|
|
||||||
@ -404,6 +401,14 @@ mod tests {
|
|||||||
),
|
),
|
||||||
("malloc_heap_uaf_write", Some("heap use-after-free write")),
|
("malloc_heap_uaf_write", Some("heap use-after-free write")),
|
||||||
("malloc_heap_uaf_read", Some("heap use-after-free read")),
|
("malloc_heap_uaf_read", Some("heap use-after-free read")),
|
||||||
|
(
|
||||||
|
"heap_oob_memcpy_read",
|
||||||
|
Some("function arg resulting in bad read"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"heap_oob_memcpy_write",
|
||||||
|
Some("function arg resulting in bad write"),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
//NOTE: RTLD_NOW is required on linux as otherwise the hooks will NOT work
|
//NOTE: RTLD_NOW is required on linux as otherwise the hooks will NOT work
|
||||||
@ -420,11 +425,16 @@ mod tests {
|
|||||||
|
|
||||||
let coverage = CoverageRuntime::new();
|
let coverage = CoverageRuntime::new();
|
||||||
let asan = AsanRuntime::new(options);
|
let asan = AsanRuntime::new(options);
|
||||||
let mut frida_helper = FridaInstrumentationHelper::new(
|
// let mut frida_helper = FridaInstrumentationHelper::new(
|
||||||
|
// GUM.get().expect("Gum uninitialized"),
|
||||||
|
// options,
|
||||||
|
// tuple_list!(coverage, asan),
|
||||||
|
// );
|
||||||
|
let frida_helper = Rc::new(RefCell::new(FridaInstrumentationHelper::new(
|
||||||
GUM.get().expect("Gum uninitialized"),
|
GUM.get().expect("Gum uninitialized"),
|
||||||
options,
|
options,
|
||||||
tuple_list!(coverage, asan),
|
tuple_list!(coverage, asan),
|
||||||
);
|
)));
|
||||||
|
|
||||||
// Run the tests for each function
|
// Run the tests for each function
|
||||||
for test in tests {
|
for test in tests {
|
||||||
@ -442,6 +452,7 @@ mod tests {
|
|||||||
let mut feedback = ConstFeedback::new(true);
|
let mut feedback = ConstFeedback::new(true);
|
||||||
|
|
||||||
let asan_obs = AsanErrorsObserver::from_static_asan_errors();
|
let asan_obs = AsanErrorsObserver::from_static_asan_errors();
|
||||||
|
let frida_helper_observer = FridaHelperObserver::new(Rc::clone(&frida_helper));
|
||||||
|
|
||||||
// Feedbacks to recognize an input as solution
|
// Feedbacks to recognize an input as solution
|
||||||
let mut objective = feedback_or_fast!(
|
let mut objective = feedback_or_fast!(
|
||||||
@ -466,6 +477,7 @@ mod tests {
|
|||||||
let mut fuzzer = StdFuzzer::new(StdScheduler::new(), feedback, objective);
|
let mut fuzzer = StdFuzzer::new(StdScheduler::new(), feedback, objective);
|
||||||
|
|
||||||
let observers = tuple_list!(
|
let observers = tuple_list!(
|
||||||
|
frida_helper_observer,
|
||||||
asan_obs //,
|
asan_obs //,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -497,7 +509,8 @@ mod tests {
|
|||||||
&mut event_manager,
|
&mut event_manager,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
&mut frida_helper,
|
// &mut frida_helper,
|
||||||
|
Rc::clone(&frida_helper),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mutator = StdScheduledMutator::new(tuple_list!(BitFlipMutator::new()));
|
let mutator = StdScheduledMutator::new(tuple_list!(BitFlipMutator::new()));
|
||||||
@ -523,7 +536,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frida_helper.deinit(GUM.get().expect("Gum uninitialized"));
|
frida_helper
|
||||||
|
.borrow_mut()
|
||||||
|
.deinit(GUM.get().expect("Gum uninitialized"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -545,8 +560,14 @@ mod tests {
|
|||||||
|
|
||||||
SimpleStdoutLogger::set_logger().unwrap();
|
SimpleStdoutLogger::set_logger().unwrap();
|
||||||
|
|
||||||
let out_dir = std::env::var_os("OUT_DIR").unwrap();
|
if let Ok(out_dir) = std::env::var("OUT_DIR") {
|
||||||
let out_dir = out_dir.to_string_lossy().to_string();
|
println!("OUT_DIR is set to: {out_dir}");
|
||||||
|
} else {
|
||||||
|
println!("OUT_DIR is not set!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||||
// Check if the harness dynamic library is present, if not - skip the test
|
// Check if the harness dynamic library is present, if not - skip the test
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let test_harness_name = "test_harness.so";
|
let test_harness_name = "test_harness.so";
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <winnt.h>
|
#include <winnt.h>
|
||||||
#include <winternl.h>
|
#include <winternl.h>
|
||||||
|
|
||||||
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call,
|
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call,
|
||||||
LPVOID lpReserved) {
|
LPVOID lpReserved) {
|
||||||
(void)hModule;
|
(void)hModule;
|
||||||
@ -48,7 +52,7 @@ EXTERN int heap_oob_read(const uint8_t *_data, size_t _size) {
|
|||||||
|
|
||||||
// OutputDebugStringA("heap_oob_read\n");
|
// OutputDebugStringA("heap_oob_read\n");
|
||||||
int *array = new int[100];
|
int *array = new int[100];
|
||||||
fprintf(stdout, "%d\n", array[100]);
|
fprintf(stdout, "heap_oob_read %d\n", array[100]);
|
||||||
delete[] array;
|
delete[] array;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -66,7 +70,7 @@ EXTERN int malloc_heap_uaf_read(const uint8_t *_data, size_t _size) {
|
|||||||
(void)_size;
|
(void)_size;
|
||||||
int *array = static_cast<int *>(malloc(100 * sizeof(int)));
|
int *array = static_cast<int *>(malloc(100 * sizeof(int)));
|
||||||
free(array);
|
free(array);
|
||||||
fprintf(stdout, "%d\n", array[5]);
|
fprintf(stdout, "malloc_heap_uaf_read %d\n", array[5]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +87,7 @@ EXTERN int malloc_heap_oob_read(const uint8_t *_data, size_t _size) {
|
|||||||
(void)_data;
|
(void)_data;
|
||||||
(void)_size;
|
(void)_size;
|
||||||
int *array = static_cast<int *>(malloc(100 * sizeof(int)));
|
int *array = static_cast<int *>(malloc(100 * sizeof(int)));
|
||||||
fprintf(stdout, "%d\n", array[100]);
|
fprintf(stdout, "malloc_heap_oob_read %d\n", array[100]);
|
||||||
free(array);
|
free(array);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -163,6 +167,56 @@ EXTERN int malloc_heap_oob_write_0x17_int_at_0x13(const uint8_t *_data,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EXTERN int heap_oob_memcpy_write(const uint8_t *_data, size_t _size) {
|
||||||
|
(void)_data;
|
||||||
|
(void)_size;
|
||||||
|
|
||||||
|
const size_t REAL_SIZE = 10;
|
||||||
|
const size_t LARGER_SIZE = REAL_SIZE + 1;
|
||||||
|
|
||||||
|
char *dest = new char[REAL_SIZE];
|
||||||
|
char *src = new char[LARGER_SIZE];
|
||||||
|
memcpy(dest, src, LARGER_SIZE);
|
||||||
|
|
||||||
|
delete[] dest;
|
||||||
|
delete[] src;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXTERN int heap_oob_memcpy_read(const uint8_t *_data, size_t _size) {
|
||||||
|
(void)_data;
|
||||||
|
(void)_size;
|
||||||
|
|
||||||
|
const size_t REAL_SIZE = 10;
|
||||||
|
const size_t LARGER_SIZE = REAL_SIZE + 1;
|
||||||
|
|
||||||
|
char *dest = new char[LARGER_SIZE];
|
||||||
|
char *src = new char[REAL_SIZE];
|
||||||
|
memcpy(dest, src, LARGER_SIZE);
|
||||||
|
|
||||||
|
delete[] dest;
|
||||||
|
delete[] src;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXTERN int heap_oob_memcpy_write_avx(const uint8_t *_data, size_t _size) {
|
||||||
|
(void)_data;
|
||||||
|
(void)_size;
|
||||||
|
|
||||||
|
// Using 127 bytes to make sure to fall on the AVX instruction in the
|
||||||
|
// optimized implementation
|
||||||
|
const size_t REAL_SIZE = 127;
|
||||||
|
const size_t LARGER_SIZE = REAL_SIZE + 1;
|
||||||
|
|
||||||
|
char *dest = new char[LARGER_SIZE];
|
||||||
|
char *src = new char[REAL_SIZE];
|
||||||
|
memcpy(dest, src, LARGER_SIZE);
|
||||||
|
|
||||||
|
delete[] dest;
|
||||||
|
delete[] src;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
EXTERN int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
EXTERN int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||||
// abort();
|
// abort();
|
||||||
(void)data;
|
(void)data;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user