Add redirect stdin module (#3077)

* forkserver api

* poc

* i'm dumb

* add things

* use snapshot

* delete println debug

* anglais

* d

* fixer

* take care of further read

* take care about u32

* aa

* fix cursor

* mm

* pushing things temporary so i can try this path later

* delete useless setter

* rme

* BytesConverter

* now revert

* clp

* typo

* change how input passing works

* fuck

* fmt

* fixer

* fix

* lol

* lol

* lol

* disable CI

* delete assert

* clp

* a
This commit is contained in:
Dongjia "toka" Zhang 2025-03-19 19:03:02 +01:00 committed by GitHub
parent 30946641cd
commit 4130e3860f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 151 additions and 0 deletions

View File

@ -17,3 +17,5 @@ pub use asan::AsanModule;
pub mod asan_guest;
#[cfg(not(cpu_target = "hexagon"))]
pub use asan_guest::AsanGuestModule;
pub mod redirect_stdin;
pub use redirect_stdin::*;

View File

@ -0,0 +1,149 @@
use core::fmt::Debug;
use libafl_bolts::HasLen;
use libafl_qemu_sys::GuestAddr;
#[cfg(not(cpu_target = "hexagon"))]
use crate::SYS_read;
use crate::{
Qemu,
emu::EmulatorModules,
modules::{EmulatorModule, EmulatorModuleTuple},
qemu::{Hook, SyscallHookResult},
};
#[cfg(cpu_target = "hexagon")]
/// Hexagon syscalls are not currently supported by the `syscalls` crate, so we just paste this here for now.
/// <https://github.com/qemu/qemu/blob/11be70677c70fdccd452a3233653949b79e97908/linux-user/hexagon/syscall_nr.h#L230>
#[expect(non_upper_case_globals)]
const SYS_read: u8 = 63;
/// This module hijacks any read to buffer from stdin, and instead fill the buffer from the specified input address
/// This is useful when your binary target reads the input from the stdin.
/// With this you can just fuzz more like afl++
/// You need to use this with snapshot module!
#[derive(Debug)]
pub struct RedirectStdinModule {
input_addr: *const u8,
read: usize,
total: usize,
}
impl Default for RedirectStdinModule {
fn default() -> Self {
Self::new()
}
}
impl RedirectStdinModule {
#[must_use]
/// constuctor
pub fn new() -> Self {
Self::with_input_addr(core::ptr::null())
}
#[must_use]
/// Create with specified input address
pub fn with_input_addr(addr: *const u8) -> Self {
Self {
input_addr: addr,
read: 0,
total: 0,
}
}
/// Tell this module where to look for the input addr
pub fn set_input_addr(&mut self, addr: *const u8) {
self.input_addr = addr;
}
pub fn reset_input_addr(&mut self) {
self.input_addr = core::ptr::null();
}
}
impl<I, S> EmulatorModule<I, S> for RedirectStdinModule
where
I: Unpin + HasLen + Debug,
S: Unpin,
{
fn first_exec<ET>(
&mut self,
_qemu: Qemu,
emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: &mut S,
) where
ET: EmulatorModuleTuple<I, S>,
{
emulator_modules.pre_syscalls(Hook::Function(syscall_read_hook::<ET, I, S>));
}
fn pre_exec<ET>(
&mut self,
_qemu: Qemu,
_emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: &mut S,
input: &I,
) where
ET: EmulatorModuleTuple<I, S>,
{
self.total = input.len();
self.read = 0;
}
}
#[expect(clippy::too_many_arguments)]
fn syscall_read_hook<ET, I, S>(
_qemu: Qemu,
emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: Option<&mut S>,
syscall: i32,
x0: GuestAddr,
x1: GuestAddr,
x2: GuestAddr,
_x3: GuestAddr,
_x4: GuestAddr,
_x5: GuestAddr,
_x6: GuestAddr,
_x7: GuestAddr,
) -> SyscallHookResult
where
ET: EmulatorModuleTuple<I, S>,
I: Unpin + HasLen + Debug,
S: Unpin,
{
let h = emulator_modules.get_mut::<RedirectStdinModule>().unwrap();
if h.input_addr.is_null() {
return SyscallHookResult::new(None);
}
if syscall == SYS_read as i32 && x0 == 0 {
/*
println!(
"Is sys read {:x} {} {:x} {:x} {} {} {} {} {}",
rip, x0, x1, x2, x3, x4, x5, x6, x7
);
*/
let size = unsafe {
let mut src = h.input_addr;
src = src.wrapping_add(h.read);
let dst = x1 as *mut u8;
if h.total >= h.read {
let size = std::cmp::min(x2, (h.total - h.read).try_into().unwrap());
/*
println!(
"trying to read {} bytes copying src: {:p} {:p} size: {} h.total: {} h.read {} ",
x2, src, dst, size, h.total, h.read
);
*/
dst.copy_from(src, size as usize);
size
} else {
0
}
};
// println!("copied {}", size);
h.read += size as usize;
return SyscallHookResult::new(Some(size));
}
SyscallHookResult::new(None)
}