Skip to content

Commit 4130e38

Browse files
authored
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
1 parent 3094664 commit 4130e38

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed

libafl_qemu/src/modules/usermode/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ pub use asan::AsanModule;
1717
pub mod asan_guest;
1818
#[cfg(not(cpu_target = "hexagon"))]
1919
pub use asan_guest::AsanGuestModule;
20+
pub mod redirect_stdin;
21+
pub use redirect_stdin::*;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
use core::fmt::Debug;
2+
3+
use libafl_bolts::HasLen;
4+
use libafl_qemu_sys::GuestAddr;
5+
6+
#[cfg(not(cpu_target = "hexagon"))]
7+
use crate::SYS_read;
8+
use crate::{
9+
Qemu,
10+
emu::EmulatorModules,
11+
modules::{EmulatorModule, EmulatorModuleTuple},
12+
qemu::{Hook, SyscallHookResult},
13+
};
14+
15+
#[cfg(cpu_target = "hexagon")]
16+
/// Hexagon syscalls are not currently supported by the `syscalls` crate, so we just paste this here for now.
17+
/// <https://github.com/qemu/qemu/blob/11be70677c70fdccd452a3233653949b79e97908/linux-user/hexagon/syscall_nr.h#L230>
18+
#[expect(non_upper_case_globals)]
19+
const SYS_read: u8 = 63;
20+
21+
/// This module hijacks any read to buffer from stdin, and instead fill the buffer from the specified input address
22+
/// This is useful when your binary target reads the input from the stdin.
23+
/// With this you can just fuzz more like afl++
24+
/// You need to use this with snapshot module!
25+
#[derive(Debug)]
26+
pub struct RedirectStdinModule {
27+
input_addr: *const u8,
28+
read: usize,
29+
total: usize,
30+
}
31+
32+
impl Default for RedirectStdinModule {
33+
fn default() -> Self {
34+
Self::new()
35+
}
36+
}
37+
38+
impl RedirectStdinModule {
39+
#[must_use]
40+
/// constuctor
41+
pub fn new() -> Self {
42+
Self::with_input_addr(core::ptr::null())
43+
}
44+
45+
#[must_use]
46+
/// Create with specified input address
47+
pub fn with_input_addr(addr: *const u8) -> Self {
48+
Self {
49+
input_addr: addr,
50+
read: 0,
51+
total: 0,
52+
}
53+
}
54+
55+
/// Tell this module where to look for the input addr
56+
pub fn set_input_addr(&mut self, addr: *const u8) {
57+
self.input_addr = addr;
58+
}
59+
60+
pub fn reset_input_addr(&mut self) {
61+
self.input_addr = core::ptr::null();
62+
}
63+
}
64+
65+
impl<I, S> EmulatorModule<I, S> for RedirectStdinModule
66+
where
67+
I: Unpin + HasLen + Debug,
68+
S: Unpin,
69+
{
70+
fn first_exec<ET>(
71+
&mut self,
72+
_qemu: Qemu,
73+
emulator_modules: &mut EmulatorModules<ET, I, S>,
74+
_state: &mut S,
75+
) where
76+
ET: EmulatorModuleTuple<I, S>,
77+
{
78+
emulator_modules.pre_syscalls(Hook::Function(syscall_read_hook::<ET, I, S>));
79+
}
80+
81+
fn pre_exec<ET>(
82+
&mut self,
83+
_qemu: Qemu,
84+
_emulator_modules: &mut EmulatorModules<ET, I, S>,
85+
_state: &mut S,
86+
input: &I,
87+
) where
88+
ET: EmulatorModuleTuple<I, S>,
89+
{
90+
self.total = input.len();
91+
self.read = 0;
92+
}
93+
}
94+
95+
#[expect(clippy::too_many_arguments)]
96+
fn syscall_read_hook<ET, I, S>(
97+
_qemu: Qemu,
98+
emulator_modules: &mut EmulatorModules<ET, I, S>,
99+
_state: Option<&mut S>,
100+
syscall: i32,
101+
x0: GuestAddr,
102+
x1: GuestAddr,
103+
x2: GuestAddr,
104+
_x3: GuestAddr,
105+
_x4: GuestAddr,
106+
_x5: GuestAddr,
107+
_x6: GuestAddr,
108+
_x7: GuestAddr,
109+
) -> SyscallHookResult
110+
where
111+
ET: EmulatorModuleTuple<I, S>,
112+
I: Unpin + HasLen + Debug,
113+
S: Unpin,
114+
{
115+
let h = emulator_modules.get_mut::<RedirectStdinModule>().unwrap();
116+
if h.input_addr.is_null() {
117+
return SyscallHookResult::new(None);
118+
}
119+
if syscall == SYS_read as i32 && x0 == 0 {
120+
/*
121+
println!(
122+
"Is sys read {:x} {} {:x} {:x} {} {} {} {} {}",
123+
rip, x0, x1, x2, x3, x4, x5, x6, x7
124+
);
125+
*/
126+
let size = unsafe {
127+
let mut src = h.input_addr;
128+
src = src.wrapping_add(h.read);
129+
let dst = x1 as *mut u8;
130+
if h.total >= h.read {
131+
let size = std::cmp::min(x2, (h.total - h.read).try_into().unwrap());
132+
/*
133+
println!(
134+
"trying to read {} bytes copying src: {:p} {:p} size: {} h.total: {} h.read {} ",
135+
x2, src, dst, size, h.total, h.read
136+
);
137+
*/
138+
dst.copy_from(src, size as usize);
139+
size
140+
} else {
141+
0
142+
}
143+
};
144+
// println!("copied {}", size);
145+
h.read += size as usize;
146+
return SyscallHookResult::new(Some(size));
147+
}
148+
SyscallHookResult::new(None)
149+
}

0 commit comments

Comments
 (0)