frida: Deduplicate with IfElseRuntime (#2792)

* frida: Deduplicate with IfElseRuntime

* clippy'

* get rid of cfg

* fmt

* documentation

* fix lint

* fix lint

* debug: add tmate

* debug: add tmate

* frida_windows_gdiplus: move to mimalloc on windows

* remove tmate
This commit is contained in:
s1341 2024-12-25 14:42:54 +02:00 committed by GitHub
parent 9b4cd51c63
commit 2a79ee5b4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 222 additions and 380 deletions

View File

@ -21,7 +21,7 @@ use libafl::{
}, },
observers::{CanTrack, HitcountsMapObserver, StdMapObserver, TimeObserver}, observers::{CanTrack, HitcountsMapObserver, StdMapObserver, TimeObserver},
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
stages::{ShadowTracingStage, StdMutationalStage}, stages::{IfElseStage, ShadowTracingStage, StdMutationalStage},
state::{HasCorpus, StdState}, state::{HasCorpus, StdState},
Error, HasMetadata, Error, HasMetadata,
}; };
@ -34,16 +34,15 @@ use libafl_bolts::{
tuples::{tuple_list, Merge}, tuples::{tuple_list, Merge},
AsSlice, AsSlice,
}; };
#[cfg(unix)]
use libafl_frida::asan::{
asan_rt::AsanRuntime,
errors::{AsanErrorsFeedback, AsanErrorsObserver},
};
use libafl_frida::{ use libafl_frida::{
asan::{
asan_rt::AsanRuntime,
errors::{AsanErrorsFeedback, AsanErrorsObserver},
},
cmplog_rt::CmpLogRuntime, cmplog_rt::CmpLogRuntime,
coverage_rt::{CoverageRuntime, MAP_SIZE}, coverage_rt::{CoverageRuntime, MAP_SIZE},
executor::FridaInProcessExecutor, executor::FridaInProcessExecutor,
helper::FridaInstrumentationHelper, helper::{FridaInstrumentationHelper, IfElseRuntime},
}; };
use libafl_targets::cmplog::CmpLogObserver; use libafl_targets::cmplog::CmpLogObserver;
use mimalloc::MiMalloc; use mimalloc::MiMalloc;
@ -74,6 +73,12 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
let monitor = MultiMonitor::new(|s| println!("{s}")); let monitor = MultiMonitor::new(|s| println!("{s}"));
let shmem_provider = StdShMemProvider::new()?; let shmem_provider = StdShMemProvider::new()?;
let is_asan = |options: &FuzzerOptions, client_description: &ClientDescription| {
options.asan && options.asan_cores.contains(client_description.core_id())
};
let is_cmplog = |options: &FuzzerOptions, client_description: &ClientDescription| {
options.cmplog && options.cmplog_cores.contains(client_description.core_id())
};
let mut run_client = |state: Option<_>, let mut run_client = |state: Option<_>,
mgr: LlmpRestartingEventManager<_, _, _>, mgr: LlmpRestartingEventManager<_, _, _>,
@ -94,390 +99,162 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
ExitKind::Ok ExitKind::Ok
}; };
if options.asan && options.asan_cores.contains(client_description.core_id()) { // if options.asan && options.asan_cores.contains(client_description.core_id()) {
(|state: Option<_>, (|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _, _>, _client_description| {
mut mgr: LlmpRestartingEventManager<_, _, _>, let gum = Gum::obtain();
_client_description| {
let gum = Gum::obtain();
let coverage = CoverageRuntime::new(); let coverage = CoverageRuntime::new();
#[cfg(unix)] let asan = AsanRuntime::new(options);
let asan = AsanRuntime::new(options); let cmplog = CmpLogRuntime::new();
#[cfg(unix)] let client_description_clone = client_description.clone();
let mut frida_helper = let options_clone = options.clone();
FridaInstrumentationHelper::new(&gum, options, tuple_list!(asan, coverage)); let client_description_clone2 = client_description.clone();
#[cfg(windows)] let options_clone2 = options.clone();
let mut frida_helper = let mut frida_helper = FridaInstrumentationHelper::new(
FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage)); &gum,
options,
tuple_list!(
IfElseRuntime::new(
move || Ok(is_asan(&options_clone, &client_description_clone)),
tuple_list!(asan),
tuple_list!()
),
IfElseRuntime::new(
move || Ok(is_cmplog(&options_clone2, &client_description_clone2)),
tuple_list!(cmplog),
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.map_mut_ptr().unwrap(),
MAP_SIZE, 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");
#[cfg(unix)] let asan_observer = AsanErrorsObserver::from_static_asan_errors();
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
// 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!(
// New maximization map feedback linked to the edges observer and the feedback state // New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new(&edges_observer), MaxMapFeedback::new(&edges_observer),
// Time feedback, this one does not need a feedback state // Time feedback, this one does not need a feedback state
TimeFeedback::new(&time_observer) TimeFeedback::new(&time_observer)
); );
// Feedbacks to recognize an input as solution // Feedbacks to recognize an input as solution
#[cfg(unix)] #[cfg(unix)]
let mut objective = feedback_or_fast!( let mut objective = feedback_or_fast!(
CrashFeedback::new(), CrashFeedback::new(),
TimeoutFeedback::new(), TimeoutFeedback::new(),
// true enables the AsanErrorFeedback // true enables the AsanErrorFeedback
feedback_and_fast!( feedback_and_fast!(
ConstFeedback::from(true), ConstFeedback::from(true),
AsanErrorsFeedback::new(&asan_observer) AsanErrorsFeedback::new(&asan_observer)
) )
); );
#[cfg(windows)] #[cfg(windows)]
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new()); let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
// If not restarting, create a State from scratch // If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| { let mut state = state.unwrap_or_else(|| {
StdState::new( StdState::new(
// RNG // RNG
StdRand::new(), StdRand::new(),
// Corpus that will be evolved, we keep it in memory for performance // Corpus that will be evolved, we keep it in memory for performance
CachedOnDiskCorpus::no_meta(PathBuf::from("./corpus_discovered"), 64) CachedOnDiskCorpus::no_meta(PathBuf::from("./corpus_discovered"), 64).unwrap(),
.unwrap(), // Corpus in which we store solutions (crashes in this example),
// Corpus in which we store solutions (crashes in this example), // on disk so the user can get them after stopping the fuzzer
// on disk so the user can get them after stopping the fuzzer OnDiskCorpus::new(options.output.clone()).unwrap(),
OnDiskCorpus::new(options.output.clone()).unwrap(), &mut feedback,
&mut feedback, &mut objective,
&mut objective, )
) .unwrap()
.unwrap() });
});
println!("We're a client, let's fuzz :)"); println!("We're a client, let's fuzz :)");
// Create a PNG dictionary if not existing // Create a PNG dictionary if not existing
if state.metadata_map().get::<Tokens>().is_none() { if state.metadata_map().get::<Tokens>().is_none() {
state.add_metadata(Tokens::from([ state.add_metadata(Tokens::from([
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
b"IHDR".to_vec(), b"IHDR".to_vec(),
b"IDAT".to_vec(), b"IDAT".to_vec(),
b"PLTE".to_vec(), b"PLTE".to_vec(),
b"IEND".to_vec(), b"IEND".to_vec(),
])); ]));
} }
// Setup a basic mutator with a mutational stage // Setup a basic mutator with a mutational stage
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
// A minimization+queue policy to get testcasess from the corpus // A minimization+queue policy to get testcasess from the corpus
let scheduler = let scheduler =
IndexesLenTimeMinimizerScheduler::new(&edges_observer, QueueScheduler::new()); IndexesLenTimeMinimizerScheduler::new(&edges_observer, QueueScheduler::new());
// A fuzzer with feedbacks and a corpus scheduler // A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
#[cfg(unix)] #[cfg(unix)]
let observers = tuple_list!(edges_observer, time_observer, asan_observer); let observers = tuple_list!(edges_observer, time_observer, asan_observer);
#[cfg(windows)] #[cfg(windows)]
let observers = tuple_list!(edges_observer, time_observer); let observers = tuple_list!(edges_observer, time_observer);
// Create the executor for an in-process function with just one observer for edge coverage // Create the executor for an in-process function with just one observer for edge coverage
let mut executor = FridaInProcessExecutor::new( let mut executor = FridaInProcessExecutor::new(
&gum, &gum,
InProcessExecutor::new( InProcessExecutor::new(
&mut frida_harness, &mut frida_harness,
observers, observers,
&mut fuzzer, &mut fuzzer,
&mut state, &mut state,
&mut mgr, &mut mgr,
)?, )?,
&mut frida_helper, &mut frida_helper,
); );
// Create an observation channel using cmplog map
let cmplog_observer = CmpLogObserver::new("cmplog", true);
// In case the corpus is empty (on first run), reset let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer));
if state.must_load_initial_inputs() {
state
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &options.input)
.unwrap_or_else(|_| {
panic!("Failed to load initial corpus at {:?}", &options.input)
});
println!("We imported {} inputs from disk.", state.corpus().count());
}
let mut stages = tuple_list!(StdMutationalStage::new(mutator)); let tracing = ShadowTracingStage::new(&mut executor);
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?; // Setup a randomic Input2State stage
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(
I2SRandReplace::new()
)));
Ok(()) // In case the corpus is empty (on first run), reset
})(state, mgr, client_description) if state.must_load_initial_inputs() {
} else if options.cmplog && options.cmplog_cores.contains(client_description.core_id()) { state
(|state: Option<_>, .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &options.input)
mut mgr: LlmpRestartingEventManager<_, _, _>, .unwrap_or_else(|_| {
_client_description| { panic!("Failed to load initial corpus at {:?}", &options.input)
let gum = Gum::obtain(); });
println!("We imported {} inputs from disk.", state.corpus().count());
}
let coverage = CoverageRuntime::new(); let mut stages = tuple_list!(
let cmplog = CmpLogRuntime::new(); IfElseStage::new(
println!("cmplog runtime created"); |_, _, _, _| Ok(is_cmplog(&options, &client_description)),
tuple_list!(tracing, i2s),
tuple_list!()
),
StdMutationalStage::new(mutator)
);
let mut frida_helper = fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage, cmplog));
// Create an observation channel using the coverage map Ok(())
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr( })(state, mgr, client_description.clone())
"edges",
frida_helper.map_mut_ptr().unwrap(),
MAP_SIZE,
))
.track_indices();
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
#[cfg(unix)]
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let mut feedback = feedback_or!(
// New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new(&edges_observer),
// Time feedback, this one does not need a feedback state
TimeFeedback::new(&time_observer)
);
#[cfg(unix)]
let mut objective = feedback_or_fast!(
CrashFeedback::new(),
TimeoutFeedback::new(),
feedback_and_fast!(
ConstFeedback::from(false),
AsanErrorsFeedback::new(&asan_observer)
)
);
#[cfg(windows)]
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
// If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| {
StdState::new(
// RNG
StdRand::new(),
// Corpus that will be evolved, we keep it in memory for performance
CachedOnDiskCorpus::no_meta(PathBuf::from("./corpus_discovered"), 64)
.unwrap(),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(options.output.clone()).unwrap(),
&mut feedback,
&mut objective,
)
.unwrap()
});
println!("We're a client, let's fuzz :)");
// Create a PNG dictionary if not existing
if state.metadata_map().get::<Tokens>().is_none() {
state.add_metadata(Tokens::from([
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
b"IHDR".to_vec(),
b"IDAT".to_vec(),
b"PLTE".to_vec(),
b"IEND".to_vec(),
]));
}
// Setup a basic mutator with a mutational stage
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
// A minimization+queue policy to get testcasess from the corpus
let scheduler =
IndexesLenTimeMinimizerScheduler::new(&edges_observer, QueueScheduler::new());
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
#[cfg(unix)]
let observers = tuple_list!(edges_observer, time_observer, asan_observer);
#[cfg(windows)]
let observers = tuple_list!(edges_observer, time_observer);
// Create the executor for an in-process function with just one observer for edge coverage
let mut executor = FridaInProcessExecutor::new(
&gum,
InProcessExecutor::new(
&mut frida_harness,
observers,
&mut fuzzer,
&mut state,
&mut mgr,
)?,
&mut frida_helper,
);
// In case the corpus is empty (on first run), reset
if state.must_load_initial_inputs() {
state
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &options.input)
.unwrap_or_else(|_| {
panic!("Failed to load initial corpus at {:?}", &options.input)
});
println!("We imported {} inputs from disk.", state.corpus().count());
}
// Create an observation channel using cmplog map
let cmplog_observer = CmpLogObserver::new("cmplog", true);
let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer));
let tracing = ShadowTracingStage::new(&mut executor);
// Setup a randomic Input2State stage
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(
I2SRandReplace::new()
)));
// Setup a basic mutator
let mutational = StdMutationalStage::new(mutator);
// The order of the stages matter!
let mut stages = tuple_list!(tracing, i2s, mutational);
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
Ok(())
})(state, mgr, client_description)
} else {
(|state: Option<_>,
mut mgr: LlmpRestartingEventManager<_, _, _>,
_client_description| {
let gum = Gum::obtain();
let coverage = CoverageRuntime::new();
let mut frida_helper =
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage));
// Create an observation channel using the coverage map
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr(
"edges",
frida_helper.map_mut_ptr().unwrap(),
MAP_SIZE,
))
.track_indices();
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
#[cfg(unix)]
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let mut feedback = feedback_or!(
// New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new(&edges_observer),
// Time feedback, this one does not need a feedback state
TimeFeedback::new(&time_observer)
);
#[cfg(unix)]
let mut objective = feedback_or_fast!(
CrashFeedback::new(),
TimeoutFeedback::new(),
feedback_and_fast!(
ConstFeedback::from(false),
AsanErrorsFeedback::new(&asan_observer)
)
);
#[cfg(windows)]
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
// If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| {
StdState::new(
// RNG
StdRand::new(),
// Corpus that will be evolved, we keep it in memory for performance
CachedOnDiskCorpus::no_meta(PathBuf::from("./corpus_discovered"), 64)
.unwrap(),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(options.output.clone()).unwrap(),
&mut feedback,
&mut objective,
)
.unwrap()
});
println!("We're a client, let's fuzz :)");
// Create a PNG dictionary if not existing
if state.metadata_map().get::<Tokens>().is_none() {
state.add_metadata(Tokens::from([
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
b"IHDR".to_vec(),
b"IDAT".to_vec(),
b"PLTE".to_vec(),
b"IEND".to_vec(),
]));
}
// Setup a basic mutator with a mutational stage
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
// A minimization+queue policy to get testcasess from the corpus
let scheduler =
IndexesLenTimeMinimizerScheduler::new(&edges_observer, QueueScheduler::new());
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
#[cfg(unix)]
let observers = tuple_list!(edges_observer, time_observer, asan_observer);
#[cfg(windows)]
let observers = tuple_list!(edges_observer, time_observer);
// Create the executor for an in-process function with just one observer for edge coverage
let mut executor = FridaInProcessExecutor::new(
&gum,
InProcessExecutor::new(
&mut frida_harness,
observers,
&mut fuzzer,
&mut state,
&mut mgr,
)?,
&mut frida_helper,
);
// In case the corpus is empty (on first run), reset
if state.must_load_initial_inputs() {
state
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &options.input)
.unwrap_or_else(|_| {
panic!("Failed to load initial corpus at {:?}", &options.input)
});
println!("We imported {} inputs from disk.", state.corpus().count());
}
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
Ok(())
})(state, mgr, client_description)
}
}; };
Launcher::builder() Launcher::builder()

View File

@ -36,7 +36,7 @@ libafl_targets = { path = "../../../libafl_targets", features = [
libloading = "0.8.5" libloading = "0.8.5"
log = { version = "0.4.22", features = ["release_max_level_info"] } log = { version = "0.4.22", features = ["release_max_level_info"] }
mimalloc = { version = "0.1.43", default-features = false } mimalloc = { version = "0.1.43", default-features = false }
dlmalloc = { version = "0.2.6", features = ["global"] } #dlmalloc = { version = "0.2.6", features = ["global"] }
color-backtrace = "0.6.1" color-backtrace = "0.6.1"
env_logger = "0.11.5" env_logger = "0.11.5"
iced-x86 = { version = "1.21.0", features = ["code_asm"] } iced-x86 = { version = "1.21.0", features = ["code_asm"] }

View File

@ -6,16 +6,9 @@
//! going to make it compilable only for Windows, don't forget to modify the //! going to make it compilable only for Windows, don't forget to modify the
//! `scripts/test_fuzzer.sh` to opt-out this fuzzer from that test. //! `scripts/test_fuzzer.sh` to opt-out this fuzzer from that test.
#[cfg(unix)]
use mimalloc::MiMalloc; use mimalloc::MiMalloc;
#[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;
use std::path::PathBuf; use std::path::PathBuf;

View File

@ -509,7 +509,7 @@ impl AsanRuntime {
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(); 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();
#[expect(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 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);
@ -593,7 +593,7 @@ impl AsanRuntime {
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));
#[expect(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 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);

View File

@ -52,6 +52,78 @@ pub trait FridaRuntime: 'static + Debug {
fn post_exec(&mut self, input_bytes: &[u8]) -> Result<(), Error>; fn post_exec(&mut self, input_bytes: &[u8]) -> Result<(), Error>;
} }
/// Use the runtime if closure evaluates to true
pub struct IfElseRuntime<CB, FR1, FR2> {
closure: CB,
if_runtimes: FR1,
else_runtimes: FR2,
}
impl<CB, FR1, FR2> Debug for IfElseRuntime<CB, FR1, FR2>
where
FR1: Debug,
FR2: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self.if_runtimes, f)?;
Debug::fmt(&self.else_runtimes, f)?;
Ok(())
}
}
impl<CB, FR1, FR2> IfElseRuntime<CB, FR1, FR2> {
/// Constructor for this conditionally enabled runtime
pub fn new(closure: CB, if_runtimes: FR1, else_runtimes: FR2) -> Self {
Self {
closure,
if_runtimes,
else_runtimes,
}
}
}
impl<CB, FR1, FR2> FridaRuntime for IfElseRuntime<CB, FR1, FR2>
where
CB: FnMut() -> Result<bool, Error> + 'static,
FR1: FridaRuntimeTuple + 'static,
FR2: FridaRuntimeTuple + 'static,
{
fn init(
&mut self,
gum: &Gum,
ranges: &RangeMap<u64, (u16, String)>,
module_map: &Rc<ModuleMap>,
) {
if (self.closure)().unwrap() {
self.if_runtimes.init_all(gum, ranges, module_map);
} else {
self.else_runtimes.init_all(gum, ranges, module_map);
}
}
fn deinit(&mut self, gum: &Gum) {
if (self.closure)().unwrap() {
self.if_runtimes.deinit_all(gum);
} else {
self.else_runtimes.deinit_all(gum);
}
}
fn pre_exec(&mut self, input_bytes: &[u8]) -> Result<(), Error> {
if (self.closure)()? {
self.if_runtimes.pre_exec_all(input_bytes)
} else {
self.else_runtimes.pre_exec_all(input_bytes)
}
}
fn post_exec(&mut self, input_bytes: &[u8]) -> Result<(), Error> {
if (self.closure)()? {
self.if_runtimes.post_exec_all(input_bytes)
} else {
self.else_runtimes.post_exec_all(input_bytes)
}
}
}
/// The tuple for Frida Runtime /// The tuple for Frida Runtime
pub trait FridaRuntimeTuple: MatchFirstType + Debug { pub trait FridaRuntimeTuple: MatchFirstType + Debug {
/// Initialization /// Initialization