Make FridaInProcessExecutor compatible with TargetBytesConverter, decouple input type from FridaRuntime trait (#2741)

* decouple input type from FridaRuntime trait

* fmt

* fmt2

* remove HasTargetBytes requirement from FridaInProcessExecutor

* fmt

* restore comment

* fix clippy comment error
This commit is contained in:
jejuisland87654 2024-12-03 23:44:19 +01:00 committed by GitHub
parent ffbb7a0f42
commit 1809c31a46
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 84 additions and 83 deletions

View File

@ -27,7 +27,7 @@ use frida_gum::{
};
use frida_gum_sys::Insn;
use hashbrown::HashMap;
use libafl_bolts::{cli::FuzzerOptions, AsSlice};
use libafl_bolts::cli::FuzzerOptions;
use libc::wchar_t;
use rangemap::RangeMap;
#[cfg(target_arch = "aarch64")]
@ -190,33 +190,22 @@ impl FridaRuntime for AsanRuntime {
self.deregister_hooks(gum);
}
fn pre_exec<I: libafl::inputs::Input + libafl::inputs::HasTargetBytes>(
&mut self,
input: &I,
) -> Result<(), libafl::Error> {
let target_bytes = input.target_bytes();
let slice = target_bytes.as_slice();
self.unpoison(slice.as_ptr() as usize, slice.len());
fn pre_exec(&mut self, input_bytes: &[u8]) -> Result<(), libafl::Error> {
self.unpoison(input_bytes.as_ptr() as usize, input_bytes.len());
self.enable_hooks();
Ok(())
}
fn post_exec<I: libafl::inputs::Input + libafl::inputs::HasTargetBytes>(
&mut self,
input: &I,
) -> Result<(), libafl::Error> {
fn post_exec(&mut self, input_bytes: &[u8]) -> Result<(), libafl::Error> {
self.disable_hooks();
if self.check_for_leaks_enabled {
self.check_for_leaks();
}
let target_bytes = input.target_bytes();
let slice = target_bytes.as_slice();
// # Safety
// The ptr and length are correct.
unsafe {
self.poison(slice.as_ptr() as usize, slice.len());
self.poison(input_bytes.as_ptr() as usize, input_bytes.len());
}
self.reset_allocations();

View File

@ -29,10 +29,7 @@ use iced_x86::{
BlockEncoder, Code, DecoderOptions, Instruction, InstructionBlock, MemoryOperand, MemorySize,
OpKind, Register,
};
use libafl::{
inputs::{HasTargetBytes, Input},
Error,
};
use libafl::Error;
use libafl_targets::{cmps::__libafl_targets_cmplog_instructions, CMPLOG_MAP_W};
use rangemap::RangeMap;
@ -132,11 +129,11 @@ impl FridaRuntime for CmpLogRuntime {
fn deinit(&mut self, _gum: &frida_gum::Gum) {}
fn pre_exec<I: Input + HasTargetBytes>(&mut self, _input: &I) -> Result<(), Error> {
fn pre_exec(&mut self, _input_bytes: &[u8]) -> Result<(), Error> {
Ok(())
}
fn post_exec<I: Input + HasTargetBytes>(&mut self, _input: &I) -> Result<(), Error> {
fn post_exec(&mut self, _input_bytes: &[u8]) -> Result<(), Error> {
Ok(())
}
}

View File

@ -44,17 +44,11 @@ impl FridaRuntime for CoverageRuntime {
fn deinit(&mut self, _gum: &frida_gum::Gum) {}
fn pre_exec<I: libafl::inputs::Input + libafl::inputs::HasTargetBytes>(
&mut self,
_input: &I,
) -> Result<(), libafl::Error> {
fn pre_exec(&mut self, _input_bytes: &[u8]) -> Result<(), libafl::Error> {
Ok(())
}
fn post_exec<I: libafl::inputs::Input + libafl::inputs::HasTargetBytes>(
&mut self,
_input: &I,
) -> Result<(), libafl::Error> {
fn post_exec(&mut self, _input_bytes: &[u8]) -> Result<(), libafl::Error> {
Ok(())
}
}

View File

@ -7,11 +7,7 @@ use std::{
use ahash::RandomState;
use frida_gum::ModuleMap;
use libafl::{
inputs::{HasTargetBytes, Input},
Error,
};
use libafl_bolts::AsSlice;
use libafl::Error;
use libafl_targets::drcov::{DrCovBasicBlock, DrCovWriter};
use rangemap::RangeMap;
@ -43,20 +39,20 @@ impl FridaRuntime for DrCovRuntime {
fn deinit(&mut self, _gum: &frida_gum::Gum) {}
/// Called before execution, does nothing
fn pre_exec<I: Input + HasTargetBytes>(&mut self, _input: &I) -> Result<(), Error> {
fn pre_exec(&mut self, _input_bytes: &[u8]) -> Result<(), Error> {
Ok(())
}
/// Called after execution, writes the trace to a unique `DrCov` file for this trace
/// into `./coverage/<input_hash>_<coverage_hash>.drcov`. Empty coverages will be skipped.
fn post_exec<I: Input + HasTargetBytes>(&mut self, input: &I) -> Result<(), Error> {
fn post_exec(&mut self, input_bytes: &[u8]) -> Result<(), Error> {
// We don't need empty coverage files
if self.drcov_basic_blocks.is_empty() {
return Ok(());
}
let mut input_hasher = RandomState::with_seeds(0, 0, 0, 0).build_hasher();
input_hasher.write(input.target_bytes().as_slice());
input_hasher.write(input_bytes);
let input_hash = input_hasher.finish();
let mut coverage_hasher = RandomState::with_seeds(0, 0, 0, 0).build_hasher();

View File

@ -15,12 +15,12 @@ use libafl::{
};
use libafl::{
executors::{Executor, ExitKind, HasObservers, InProcessExecutor},
inputs::HasTargetBytes,
inputs::{HasTargetBytes, NopTargetBytesConverter, TargetBytesConverter},
observers::ObserversTuple,
state::{HasExecutions, State, UsesState},
Error,
};
use libafl_bolts::tuples::RefIndexable;
use libafl_bolts::{tuples::RefIndexable, AsSlice};
#[cfg(not(test))]
use crate::asan::errors::AsanErrors;
@ -29,30 +29,31 @@ use crate::helper::{FridaInstrumentationHelper, FridaRuntimeTuple};
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.
pub struct FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S>
pub struct FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S, TC>
where
H: FnMut(&S::Input) -> ExitKind,
S::Input: HasTargetBytes,
TC: TargetBytesConverter<Input = S::Input>,
S: State,
OT: ObserversTuple<S::Input, S>,
'b: 'a,
{
base: InProcessExecutor<'a, H, OT, S>,
// thread_id for the Stalker
/// `thread_id` for the Stalker
thread_id: Option<u32>,
/// Frida's dynamic rewriting engine
stalker: Stalker,
/// User provided callback for instrumentation
helper: &'c mut FridaInstrumentationHelper<'b, RT>,
target_bytes_converter: TC,
followed: bool,
_phantom: PhantomData<&'b u8>,
}
impl<H, OT, RT, S> Debug for FridaInProcessExecutor<'_, '_, '_, H, OT, RT, S>
impl<H, OT, RT, S, TC> Debug for FridaInProcessExecutor<'_, '_, '_, H, OT, RT, S, TC>
where
H: FnMut(&S::Input) -> ExitKind,
S: State,
S::Input: HasTargetBytes,
TC: TargetBytesConverter<Input = S::Input>,
OT: ObserversTuple<S::Input, S> + Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
@ -64,12 +65,13 @@ where
}
}
impl<EM, H, OT, RT, S, Z> Executor<EM, Z> for FridaInProcessExecutor<'_, '_, '_, H, OT, RT, S>
impl<EM, H, OT, RT, S, TC, Z> Executor<EM, Z>
for FridaInProcessExecutor<'_, '_, '_, H, OT, RT, S, TC>
where
EM: UsesState<State = S>,
H: FnMut(&S::Input) -> ExitKind,
S: State + HasExecutions,
S::Input: HasTargetBytes,
TC: TargetBytesConverter<Input = S::Input>,
OT: ObserversTuple<S::Input, S>,
RT: FridaRuntimeTuple,
Z: UsesState<State = S>,
@ -83,7 +85,8 @@ where
mgr: &mut EM,
input: &Self::Input,
) -> Result<ExitKind, Error> {
self.helper.pre_exec(input)?;
let target_bytes = self.target_bytes_converter.to_target_bytes(input);
self.helper.pre_exec(target_bytes.as_slice())?;
if self.helper.stalker_enabled() {
if self.followed {
self.stalker.activate(NativePointer(core::ptr::null_mut()));
@ -115,25 +118,25 @@ where
abort();
}
}
self.helper.post_exec(input)?;
self.helper.post_exec(target_bytes.as_slice())?;
res
}
}
impl<H, OT, RT, S> UsesState for FridaInProcessExecutor<'_, '_, '_, H, OT, RT, S>
impl<H, OT, RT, S, TC> UsesState for FridaInProcessExecutor<'_, '_, '_, H, OT, RT, S, TC>
where
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S::Input, S>,
S: State,
S::Input: HasTargetBytes,
TC: TargetBytesConverter<Input = S::Input>,
{
type State = S;
}
impl<H, OT, RT, S> HasObservers for FridaInProcessExecutor<'_, '_, '_, H, OT, RT, S>
impl<H, OT, RT, S, TC> HasObservers for FridaInProcessExecutor<'_, '_, '_, H, OT, RT, S, TC>
where
H: FnMut(&S::Input) -> ExitKind,
S::Input: HasTargetBytes,
TC: TargetBytesConverter<Input = S::Input>,
S: State,
OT: ObserversTuple<S::Input, S>,
{
@ -149,7 +152,8 @@ where
}
}
impl<'a, 'b, 'c, H, OT, S, RT> FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S>
impl<'a, 'b, 'c, H, OT, RT, S>
FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S, NopTargetBytesConverter<S::Input>>
where
H: FnMut(&S::Input) -> ExitKind,
S: State,
@ -163,7 +167,13 @@ where
base: InProcessExecutor<'a, H, OT, S>,
helper: &'c mut FridaInstrumentationHelper<'b, RT>,
) -> Self {
Self::_on_thread(gum, base, helper, None)
FridaInProcessExecutor::with_target_bytes_converter(
gum,
base,
helper,
None,
NopTargetBytesConverter::new(),
)
}
/// Creates a new [`FridaInProcessExecutor`] tracking the given `thread_id`.
@ -173,23 +183,40 @@ where
helper: &'c mut FridaInstrumentationHelper<'b, RT>,
thread_id: u32,
) -> Self {
Self::_on_thread(gum, base, helper, Some(thread_id))
FridaInProcessExecutor::with_target_bytes_converter(
gum,
base,
helper,
Some(thread_id),
NopTargetBytesConverter::new(),
)
}
}
/// Creates a new [`FridaInProcessExecutor`] tracking the given `thread_id`, of `thread_id` is provided.
fn _on_thread(
impl<'a, 'b, 'c, H, OT, RT, S, TC> FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S, TC>
where
H: FnMut(&S::Input) -> ExitKind,
S: State,
TC: TargetBytesConverter<Input = S::Input>,
OT: ObserversTuple<S::Input, S>,
RT: FridaRuntimeTuple,
{
/// Creates a new [`FridaInProcessExecutor`].
pub fn with_target_bytes_converter(
gum: &'a Gum,
base: InProcessExecutor<'a, H, OT, S>,
helper: &'c mut FridaInstrumentationHelper<'b, RT>,
thread_id: Option<u32>,
target_bytes_converter: TC,
) -> Self {
let mut stalker = Stalker::new(gum);
// Include the current module (the fuzzer) in stalked ranges. We clone the ranges so that
// we don't add it to the INSTRUMENTED ranges.
let mut ranges = helper.ranges().clone();
for module in frida_gum::Module::obtain(gum).enumerate_modules() {
if module.base_address < Self::new as usize
&& (Self::new as usize as u64) < module.base_address as u64 + module.size as u64
if module.base_address < Self::with_target_bytes_converter as usize
&& (Self::with_target_bytes_converter as usize as u64)
< module.base_address as u64 + module.size as u64
{
ranges.insert(
module.base_address as u64..(module.base_address as u64 + module.size as u64),
@ -220,6 +247,7 @@ where
thread_id,
stalker,
helper,
target_bytes_converter,
followed: false,
_phantom: PhantomData,
}
@ -227,12 +255,12 @@ where
}
#[cfg(windows)]
impl<'a, 'b, 'c, H, OT, RT, S> HasInProcessHooks<S>
for FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S>
impl<'a, 'b, 'c, H, OT, RT, S, TC> HasInProcessHooks<S>
for FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S, TC>
where
H: FnMut(&S::Input) -> ExitKind,
S: State + HasSolutions + HasCorpus + HasExecutions,
S::Input: HasTargetBytes,
TC: TargetBytesConverter<Input = S::Input>,
OT: ObserversTuple<S::Input, S>,
RT: FridaRuntimeTuple,
<S as HasSolutions>::Solutions: Corpus<Input = S::Input>, //delete me

View File

@ -13,10 +13,7 @@ use frida_gum::{
Backend, Gum, ModuleDetails, ModuleMap, Script,
};
use frida_gum_sys::gchar;
use libafl::{
inputs::{HasTargetBytes, Input},
Error,
};
use libafl::Error;
use libafl_bolts::{
cli::{FridaScriptBackend, FuzzerOptions},
tuples::MatchFirstType,
@ -49,10 +46,10 @@ pub trait FridaRuntime: 'static + Debug {
fn deinit(&mut self, gum: &Gum);
/// Method called before execution
fn pre_exec<I: Input + HasTargetBytes>(&mut self, input: &I) -> Result<(), Error>;
fn pre_exec(&mut self, input_bytes: &[u8]) -> Result<(), Error>;
/// Method called after execution
fn post_exec<I: Input + HasTargetBytes>(&mut self, input: &I) -> Result<(), Error>;
fn post_exec(&mut self, input_bytes: &[u8]) -> Result<(), Error>;
}
/// The tuple for Frida Runtime
@ -69,10 +66,10 @@ pub trait FridaRuntimeTuple: MatchFirstType + Debug {
fn deinit_all(&mut self, gum: &Gum);
/// Method called before execution
fn pre_exec_all<I: Input + HasTargetBytes>(&mut self, input: &I) -> Result<(), Error>;
fn pre_exec_all(&mut self, input_bytes: &[u8]) -> Result<(), Error>;
/// Method called after execution
fn post_exec_all<I: Input + HasTargetBytes>(&mut self, input: &I) -> Result<(), Error>;
fn post_exec_all(&mut self, input_bytes: &[u8]) -> Result<(), Error>;
}
impl FridaRuntimeTuple for () {
@ -85,10 +82,10 @@ impl FridaRuntimeTuple for () {
}
fn deinit_all(&mut self, _gum: &Gum) {}
fn pre_exec_all<I: Input + HasTargetBytes>(&mut self, _input: &I) -> Result<(), Error> {
fn pre_exec_all(&mut self, _input_bytes: &[u8]) -> Result<(), Error> {
Ok(())
}
fn post_exec_all<I: Input + HasTargetBytes>(&mut self, _input: &I) -> Result<(), Error> {
fn post_exec_all(&mut self, _input_bytes: &[u8]) -> Result<(), Error> {
Ok(())
}
}
@ -113,14 +110,14 @@ where
self.1.deinit_all(gum);
}
fn pre_exec_all<I: Input + HasTargetBytes>(&mut self, input: &I) -> Result<(), Error> {
self.0.pre_exec(input)?;
self.1.pre_exec_all(input)
fn pre_exec_all(&mut self, input_bytes: &[u8]) -> Result<(), Error> {
self.0.pre_exec(input_bytes)?;
self.1.pre_exec_all(input_bytes)
}
fn post_exec_all<I: Input + HasTargetBytes>(&mut self, input: &I) -> Result<(), Error> {
self.0.post_exec(input)?;
self.1.post_exec_all(input)
fn post_exec_all(&mut self, input_bytes: &[u8]) -> Result<(), Error> {
self.0.post_exec(input_bytes)?;
self.1.post_exec_all(input_bytes)
}
}
@ -709,13 +706,13 @@ where
}
/// Method called before execution
pub fn pre_exec<I: Input + HasTargetBytes>(&mut self, input: &I) -> Result<(), Error> {
(*self.runtimes).borrow_mut().pre_exec_all(input)
pub fn pre_exec(&mut self, input_bytes: &[u8]) -> Result<(), Error> {
(*self.runtimes).borrow_mut().pre_exec_all(input_bytes)
}
/// Method called after execution
pub fn post_exec<I: Input + HasTargetBytes>(&mut self, input: &I) -> Result<(), Error> {
(*self.runtimes).borrow_mut().post_exec_all(input)
pub fn post_exec(&mut self, input_bytes: &[u8]) -> Result<(), Error> {
(*self.runtimes).borrow_mut().post_exec_all(input_bytes)
}
/// If stalker is enabled