Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
5ef91d2524 | |||
7fc994ee1e | |||
1399da5d7e | |||
685ae4bd91 | |||
2adf43858a | |||
73ccda8b4b | |||
5c22f11d17 | |||
2bf590e03f | |||
7fac8f9540 |
1
fuzzers/fret/.gitignore
vendored
Normal file
1
fuzzers/fret/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.qcow2
|
18
fuzzers/fret/Cargo.toml
Normal file
18
fuzzers/fret/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "qemu_systemmode"
|
||||
version = "0.8.2"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
debug = true
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../libafl/" }
|
||||
libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] }
|
26
fuzzers/fret/README.md
Normal file
26
fuzzers/fret/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# Qemu systemmode with launcher
|
||||
|
||||
This folder contains an example fuzzer for the qemu systemmode, using LLMP for fast multi-process fuzzing and crash detection.
|
||||
|
||||
## Build
|
||||
|
||||
To build this example, run
|
||||
|
||||
```bash
|
||||
cargo build --release
|
||||
cd example; sh build.sh; cd ..
|
||||
```
|
||||
|
||||
This will build the the fuzzer (src/fuzzer.rs) and a small example binary based on FreeRTOS, which can run under a qemu emulation target.
|
||||
|
||||
## Run
|
||||
|
||||
Since the instrumentation is based on snapshtos QEMU needs a virtual drive (even if it is unused...).
|
||||
Create on and then run the fuzzer:
|
||||
```bash
|
||||
# create an image
|
||||
qemu-img create -f qcow2 dummy.qcow2 32M
|
||||
# run the fuzzer
|
||||
KERNEL=./example/example.elf target/release/qemu_launcher -icount shift=auto,align=off,sleep=off -machine mps2-an385 -monitor null -kernel ./example/example.elf -serial null -nographic -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 -S
|
||||
```
|
||||
Currently the ``KERNEL`` variable is needed because the fuzzer does not parse QEMUs arguments to find the binary.
|
1
fuzzers/fret/corpus/random
Normal file
1
fuzzers/fret/corpus/random
Normal file
@ -0,0 +1 @@
|
||||
<1F>y¯»Jv•<>ֵָ
l<>ֵp†אZGײ…rֶs‚Yֿס‘ֻ—CMRױ÷}קS7;Iֹo¿7ָ6זlע1<D7A2>˜1R¯¬ף׳ָ^־<>p¬ד¯>G÷&_¬<5F>˜|1½¾;ג¥ינװסr<D7A1>ח<EFBFBD>o4ג¼ֶ¯ֽU<D6BD>ׁE<`ש÷²÷ּLע"<22>ף6Vֶ·k<1D>4/³"–<>צtכקֺf7ײF¯°ְ<C2B0><D6B0>d]<5D>®%ץ|8•#wµוּNU›ƒnש‹8€<>%¼4ַo־<6F>ֻ—–
|
BIN
fuzzers/fret/corpus/zero
Normal file
BIN
fuzzers/fret/corpus/zero
Normal file
Binary file not shown.
2
fuzzers/fret/example/build.sh
Executable file
2
fuzzers/fret/example/build.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
arm-none-eabi-gcc -ggdb -ffreestanding -nostartfiles -lgcc -T mps2_m3.ld -mcpu=cortex-m3 main.c startup.c -o example.elf
|
38
fuzzers/fret/example/main.c
Normal file
38
fuzzers/fret/example/main.c
Normal file
@ -0,0 +1,38 @@
|
||||
int BREAKPOINT() {
|
||||
for (;;)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
int LLVMFuzzerTestOneInput(unsigned int* Data, unsigned int Size) {
|
||||
if (Data[3] == 0) {while(1){}} // cause a timeout
|
||||
for (int i=0; i<Size; i++) {
|
||||
if (Data[i] > 0xFFd0 && Data[i] < 0xFFFF) {return 1;} // cause qemu to crash
|
||||
for (int j=i+1; j<Size; j++) {
|
||||
if (Data[j] == 0) {continue;}
|
||||
if (Data[j]>Data[i]) {
|
||||
int tmp = Data[i];
|
||||
Data[i]=Data[j];
|
||||
Data[j]=tmp;
|
||||
if (Data[i] <= 100) {j--;}
|
||||
}
|
||||
}
|
||||
}
|
||||
return BREAKPOINT();
|
||||
}
|
||||
unsigned int FUZZ_INPUT[] = {
|
||||
101,201,700,230,860,
|
||||
234,980,200,340,678,
|
||||
230,134,900,236,900,
|
||||
123,800,123,658,607,
|
||||
246,804,567,568,207,
|
||||
407,246,678,457,892,
|
||||
834,456,878,246,699,
|
||||
854,234,844,290,125,
|
||||
324,560,852,928,910,
|
||||
790,853,345,234,586,
|
||||
};
|
||||
|
||||
int main() {
|
||||
LLVMFuzzerTestOneInput(FUZZ_INPUT, 50);
|
||||
}
|
141
fuzzers/fret/example/mps2_m3.ld
Normal file
141
fuzzers/fret/example/mps2_m3.ld
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* FreeRTOS V202112.00
|
||||
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
FLASH (xr) : ORIGIN = 0x00000000, LENGTH = 4M /* to 0x00003FFF = 0x007FFFFF*/
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4M /* to 0x21FFFFFF = 0xFFFFFF */
|
||||
}
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
_Min_Heap_Size = 0x300000 ; /* Required amount of heap. */
|
||||
_Min_Stack_Size = 0x4000 ; /* Required amount of stack. */
|
||||
M_VECTOR_RAM_SIZE = (16 + 48) * 4;
|
||||
_estack = ORIGIN(RAM) + LENGTH(RAM);
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
|
||||
.isr_vector :
|
||||
{
|
||||
__vector_table = .;
|
||||
KEEP(*(.isr_vector))
|
||||
. = ALIGN(4);
|
||||
} > FLASH
|
||||
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.text*)
|
||||
KEEP (*(.init))
|
||||
KEEP (*(.fini))
|
||||
KEEP(*(.eh_frame))
|
||||
*(.rodata*)
|
||||
. = ALIGN(4);
|
||||
_etext = .;
|
||||
} > FLASH
|
||||
|
||||
.ARM.extab :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
.ARM :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
|
||||
__exidx_end = .;
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
.interrupts_ram :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__VECTOR_RAM__ = .;
|
||||
__interrupts_ram_start__ = .;
|
||||
. += M_VECTOR_RAM_SIZE;
|
||||
. = ALIGN(4);
|
||||
__interrupts_ram_end = .;
|
||||
} > RAM
|
||||
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
.data : /* AT ( _sidata ) */
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sdata = .;
|
||||
*(.data*)
|
||||
. = ALIGN(4);
|
||||
_edata = .;
|
||||
} > RAM AT > FLASH
|
||||
|
||||
.uninitialized (NOLOAD):
|
||||
{
|
||||
. = ALIGN(32);
|
||||
__uninitialized_start = .;
|
||||
*(.uninitialized)
|
||||
KEEP(*(.keep.uninitialized))
|
||||
. = ALIGN(32);
|
||||
__uninitialized_end = .;
|
||||
} > RAM
|
||||
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sbss = .;
|
||||
__bss_start__ = _sbss;
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
__bss_end__ = _ebss;
|
||||
} >RAM
|
||||
|
||||
.heap :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE ( end = . );
|
||||
PROVIDE ( _end = . );
|
||||
_heap_bottom = .;
|
||||
. = . + _Min_Heap_Size;
|
||||
_heap_top = .;
|
||||
. = . + _Min_Stack_Size;
|
||||
. = ALIGN(8);
|
||||
} >RAM
|
||||
|
||||
/* Set stack top to end of RAM, and stack limit move down by
|
||||
* size of stack_dummy section */
|
||||
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
|
||||
__StackLimit = __StackTop - _Min_Stack_Size;
|
||||
PROVIDE(__stack = __StackTop);
|
||||
|
||||
/* Check if data + heap + stack exceeds RAM limit */
|
||||
ASSERT(__StackLimit >= _heap_top, "region RAM overflowed with stack")
|
||||
}
|
||||
|
113
fuzzers/fret/example/startup.c
Normal file
113
fuzzers/fret/example/startup.c
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* FreeRTOS V202112.00
|
||||
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
typedef unsigned int uint32_t;
|
||||
|
||||
extern int main();
|
||||
|
||||
extern uint32_t _estack, _sidata, _sdata, _edata, _sbss, _ebss;
|
||||
|
||||
/* Prevent optimization so gcc does not replace code with memcpy */
|
||||
__attribute__( ( optimize( "O0" ) ) )
|
||||
__attribute__( ( naked ) )
|
||||
void Reset_Handler( void )
|
||||
{
|
||||
/* set stack pointer */
|
||||
__asm volatile ( "ldr r0, =_estack" );
|
||||
__asm volatile ( "mov sp, r0" );
|
||||
|
||||
/* copy .data section from flash to RAM */
|
||||
for( uint32_t * src = &_sidata, * dest = &_sdata; dest < &_edata; )
|
||||
{
|
||||
*dest++ = *src++;
|
||||
}
|
||||
|
||||
/* zero out .bss section */
|
||||
for( uint32_t * dest = &_sbss; dest < &_ebss; )
|
||||
{
|
||||
*dest++ = 0;
|
||||
}
|
||||
|
||||
/* jump to board initialisation */
|
||||
void _start( void );
|
||||
_start();
|
||||
}
|
||||
|
||||
const uint32_t * isr_vector[] __attribute__( ( section( ".isr_vector" ) ) ) =
|
||||
{
|
||||
( uint32_t * ) &_estack,
|
||||
( uint32_t * ) &Reset_Handler, /* Reset -15 */
|
||||
0, /* NMI_Handler -14 */
|
||||
0, /* HardFault_Handler -13 */
|
||||
0, /* MemManage_Handler -12 */
|
||||
0, /* BusFault_Handler -11 */
|
||||
0, /* UsageFault_Handler -10 */
|
||||
0, /* reserved */
|
||||
0, /* reserved */
|
||||
0, /* reserved */
|
||||
0, /* reserved -6 */
|
||||
0, /* SVC_Handler -5 */
|
||||
0, /* DebugMon_Handler -4 */
|
||||
0, /* reserved */
|
||||
0, /* PendSV handler -2 */
|
||||
0, /* SysTick_Handler -1 */
|
||||
0, /* uart0 receive 0 */
|
||||
0, /* uart0 transmit */
|
||||
0, /* uart1 receive */
|
||||
0, /* uart1 transmit */
|
||||
0, /* uart 2 receive */
|
||||
0, /* uart 2 transmit */
|
||||
0, /* GPIO 0 combined interrupt */
|
||||
0, /* GPIO 2 combined interrupt */
|
||||
0, /* Timer 0 */
|
||||
0, /* Timer 1 */
|
||||
0, /* Dial Timer */
|
||||
0, /* SPI0 SPI1 */
|
||||
0, /* uart overflow 1, 2,3 */
|
||||
0, /* Ethernet 13 */
|
||||
};
|
||||
|
||||
__attribute__( ( naked ) ) void exit(__attribute__((unused)) int status )
|
||||
{
|
||||
/* Force qemu to exit using ARM Semihosting */
|
||||
__asm volatile (
|
||||
"mov r1, r0\n"
|
||||
"cmp r1, #0\n"
|
||||
"bne .notclean\n"
|
||||
"ldr r1, =0x20026\n" /* ADP_Stopped_ApplicationExit, a clean exit */
|
||||
".notclean:\n"
|
||||
"movs r0, #0x18\n" /* SYS_EXIT */
|
||||
"bkpt 0xab\n"
|
||||
"end: b end\n"
|
||||
);
|
||||
}
|
||||
|
||||
void _start( void )
|
||||
{
|
||||
main( );
|
||||
exit( 0 );
|
||||
}
|
||||
|
244
fuzzers/fret/src/fuzzer.rs
Normal file
244
fuzzers/fret/src/fuzzer.rs
Normal file
@ -0,0 +1,244 @@
|
||||
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
||||
//!
|
||||
use core::time::Duration;
|
||||
use std::{env, path::PathBuf, process};
|
||||
|
||||
use libafl::{
|
||||
bolts::{
|
||||
core_affinity::Cores,
|
||||
current_nanos,
|
||||
launcher::Launcher,
|
||||
rands::StdRand,
|
||||
shmem::{ShMemProvider, StdShMemProvider},
|
||||
tuples::tuple_list,
|
||||
AsSlice,
|
||||
},
|
||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||
events::EventConfig,
|
||||
executors::{ExitKind, TimeoutExecutor},
|
||||
feedback_or,
|
||||
feedback_or_fast,
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||
fuzzer::{Fuzzer, StdFuzzer},
|
||||
inputs::{BytesInput, HasTargetBytes},
|
||||
monitors::MultiMonitor,
|
||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||
observers::{TimeObserver, VariableMapObserver},
|
||||
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
||||
stages::StdMutationalStage,
|
||||
state::{HasCorpus, StdState},
|
||||
Error,
|
||||
//prelude::{SimpleMonitor, SimpleEventManager},
|
||||
};
|
||||
use libafl_qemu::{
|
||||
//asan::QemuAsanHelper,
|
||||
edges,
|
||||
edges::QemuEdgeCoverageHelper,
|
||||
elf::EasyElf,
|
||||
emu::Emulator,
|
||||
//snapshot::QemuSnapshotHelper,
|
||||
QemuExecutor,
|
||||
QemuHooks,
|
||||
Regs,
|
||||
};
|
||||
|
||||
fn virt2phys(vaddr: u32, tab: &EasyElf) -> u32 {
|
||||
let ret;
|
||||
for i in &tab.goblin().program_headers {
|
||||
if i.vm_range()
|
||||
.contains(&vaddr.try_into().expect("Can not cast u64 to usize"))
|
||||
{
|
||||
ret = vaddr - TryInto::<u32>::try_into(i.p_vaddr).unwrap()
|
||||
+ TryInto::<u32>::try_into(i.p_paddr).unwrap();
|
||||
return ret - (ret % 2);
|
||||
}
|
||||
}
|
||||
// unlike the arm-toolcahin goblin produces some off-by one errors when parsing arm
|
||||
vaddr - (vaddr % 2)
|
||||
}
|
||||
|
||||
pub static mut MAX_INPUT_SIZE: usize = 50;
|
||||
|
||||
pub fn fuzz() {
|
||||
if let Ok(s) = env::var("FUZZ_SIZE") {
|
||||
str::parse::<usize>(&s).expect("FUZZ_SIZE was not a number");
|
||||
};
|
||||
// Hardcoded parameters
|
||||
let timeout = Duration::from_secs(3);
|
||||
let broker_port = 1337;
|
||||
let cores = Cores::from_cmdline("1").unwrap();
|
||||
let corpus_dirs = [PathBuf::from("./corpus")];
|
||||
let objective_dir = PathBuf::from("./crashes");
|
||||
|
||||
let mut elf_buffer = Vec::new();
|
||||
let elf = EasyElf::from_file(
|
||||
env::var("KERNEL").expect("KERNEL env not set"),
|
||||
&mut elf_buffer,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let input_addr = elf
|
||||
.resolve_symbol(
|
||||
&env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()),
|
||||
0,
|
||||
)
|
||||
.expect("Symbol or env FUZZ_INPUT not found");
|
||||
let input_addr = virt2phys(input_addr, &elf);
|
||||
println!("FUZZ_INPUT @ {:#x}", input_addr);
|
||||
let breakpoint = elf
|
||||
.resolve_symbol(
|
||||
&env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()),
|
||||
0,
|
||||
)
|
||||
.expect("Symbol or env BREAKPOINT not found");
|
||||
println!("Breakpoint address = {:#x}", breakpoint);
|
||||
|
||||
let mut run_client = |state: Option<_>, mut mgr, _core_id| {
|
||||
// Initialize QEMU
|
||||
env::remove_var("LD_LIBRARY_PATH");
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let env: Vec<(String, String)> = env::vars().collect();
|
||||
let emu = Emulator::new(&args, &env);
|
||||
emu.save_snapshot("start", true);
|
||||
emu.set_breakpoint(breakpoint); // BREAKPOINT
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |input: &BytesInput| {
|
||||
let target = input.target_bytes();
|
||||
let mut buf = target.as_slice();
|
||||
let len = buf.len();
|
||||
unsafe {
|
||||
if len > MAX_INPUT_SIZE {
|
||||
buf = &buf[0..MAX_INPUT_SIZE];
|
||||
// len = MAX_INPUT_SIZE;
|
||||
}
|
||||
|
||||
emu.write_mem(input_addr, buf);
|
||||
|
||||
emu.run();
|
||||
|
||||
// If the execution stops at any point other then the designated breakpoint (e.g. a breakpoint on a panic method) we consider it a crash
|
||||
let mut pcs = (0..emu.num_cpus())
|
||||
.map(|i| emu.cpu_from_index(i))
|
||||
.map(|cpu| -> Result<u32, String> { cpu.read_reg(Regs::Pc) });
|
||||
let ret = match pcs
|
||||
.find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0)))
|
||||
{
|
||||
Some(_) => ExitKind::Ok,
|
||||
None => ExitKind::Crash,
|
||||
};
|
||||
|
||||
emu.load_snapshot("start", true);
|
||||
|
||||
ret
|
||||
}
|
||||
};
|
||||
|
||||
// Create an observation channel using the coverage map
|
||||
let edges = unsafe { &mut edges::EDGES_MAP };
|
||||
let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM };
|
||||
let edges_observer = VariableMapObserver::new("edges", edges, edges_counter);
|
||||
|
||||
// Create an observation channel to keep track of the execution time
|
||||
let time_observer = TimeObserver::new("time");
|
||||
|
||||
// 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_tracking(&edges_observer, true, true),
|
||||
// Time feedback, this one does not need a feedback state
|
||||
TimeFeedback::new_with_observer(&time_observer)
|
||||
);
|
||||
|
||||
// A feedback to choose if an input is a solution or not
|
||||
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::with_seed(current_nanos()),
|
||||
// Corpus that will be evolved, we keep it in memory for performance
|
||||
InMemoryCorpus::new(),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
// on disk so the user can get them after stopping the fuzzer
|
||||
OnDiskCorpus::new(objective_dir.clone()).unwrap(),
|
||||
// States of the feedbacks.
|
||||
// The feedbacks can report the data that should persist in the State.
|
||||
&mut feedback,
|
||||
// Same for objective feedbacks
|
||||
&mut objective,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
// A minimization+queue policy to get testcasess from the corpus
|
||||
let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new());
|
||||
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
||||
let mut hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default()));
|
||||
|
||||
// Create a QEMU in-process executor
|
||||
let executor = QemuExecutor::new(
|
||||
&mut hooks,
|
||||
&mut harness,
|
||||
tuple_list!(edges_observer, time_observer),
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
&mut mgr,
|
||||
)
|
||||
.expect("Failed to create QemuExecutor");
|
||||
|
||||
// Wrap the executor to keep track of the timeout
|
||||
let mut executor = TimeoutExecutor::new(executor, timeout);
|
||||
|
||||
if state.corpus().count() < 1 {
|
||||
state
|
||||
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs)
|
||||
.unwrap_or_else(|_| {
|
||||
println!("Failed to load initial corpus at {:?}", &corpus_dirs);
|
||||
process::exit(0);
|
||||
});
|
||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
}
|
||||
|
||||
// Setup an havoc mutator with a mutational stage
|
||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||
|
||||
fuzzer
|
||||
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
|
||||
.unwrap();
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// The shared memory allocator
|
||||
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||
|
||||
// The stats reporter for the broker
|
||||
let monitor = MultiMonitor::new(|s| println!("{}", s));
|
||||
|
||||
// let monitor = SimpleMonitor::new(|s| println!("{}", s));
|
||||
// let mut mgr = SimpleEventManager::new(monitor);
|
||||
// run_client(None, mgr, 0);
|
||||
|
||||
// Build and run a Launcher
|
||||
match Launcher::builder()
|
||||
.shmem_provider(shmem_provider)
|
||||
.broker_port(broker_port)
|
||||
.configuration(EventConfig::from_build_id())
|
||||
.monitor(monitor)
|
||||
.run_client(&mut run_client)
|
||||
.cores(&cores)
|
||||
// .stdout_file(Some("/dev/null"))
|
||||
.build()
|
||||
.launch()
|
||||
{
|
||||
Ok(()) => (),
|
||||
Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."),
|
||||
Err(err) => panic!("Failed to run launcher: {:?}", err),
|
||||
}
|
||||
}
|
13
fuzzers/fret/src/main.rs
Normal file
13
fuzzers/fret/src/main.rs
Normal file
@ -0,0 +1,13 @@
|
||||
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
||||
#[cfg(target_os = "linux")]
|
||||
mod fuzzer;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn main() {
|
||||
fuzzer::fuzz();
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub fn main() {
|
||||
panic!("qemu-user and libafl_qemu is only supported on linux!");
|
||||
}
|
1
fuzzers/qemu_systemmode/.gitignore
vendored
Normal file
1
fuzzers/qemu_systemmode/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.qcow2
|
18
fuzzers/qemu_systemmode/Cargo.toml
Normal file
18
fuzzers/qemu_systemmode/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "qemu_systemmode"
|
||||
version = "0.8.2"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
debug = true
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../libafl/" }
|
||||
libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] }
|
26
fuzzers/qemu_systemmode/README.md
Normal file
26
fuzzers/qemu_systemmode/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# Qemu systemmode with launcher
|
||||
|
||||
This folder contains an example fuzzer for the qemu systemmode, using LLMP for fast multi-process fuzzing and crash detection.
|
||||
|
||||
## Build
|
||||
|
||||
To build this example, run
|
||||
|
||||
```bash
|
||||
cargo build --release
|
||||
cd example; sh build.sh; cd ..
|
||||
```
|
||||
|
||||
This will build the the fuzzer (src/fuzzer.rs) and a small example binary based on FreeRTOS, which can run under a qemu emulation target.
|
||||
|
||||
## Run
|
||||
|
||||
Since the instrumentation is based on snapshtos QEMU needs a virtual drive (even if it is unused...).
|
||||
Create on and then run the fuzzer:
|
||||
```bash
|
||||
# create an image
|
||||
qemu-img create -f qcow2 dummy.qcow2 32M
|
||||
# run the fuzzer
|
||||
KERNEL=./example/example.elf target/release/qemu_systemmode -icount shift=auto,align=off,sleep=off -machine mps2-an385 -monitor null -kernel ./example/example.elf -serial null -nographic -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 -S
|
||||
```
|
||||
Currently the ``KERNEL`` variable is needed because the fuzzer does not parse QEMUs arguments to find the binary.
|
1
fuzzers/qemu_systemmode/corpus/random
Normal file
1
fuzzers/qemu_systemmode/corpus/random
Normal file
@ -0,0 +1 @@
|
||||
<1F>y¯»Jv•<>ֵָ
l<>ֵp†אZGײ…rֶs‚Yֿס‘ֻ—CMRױ÷}קS7;Iֹo¿7ָ6זlע1<D7A2>˜1R¯¬ף׳ָ^־<>p¬ד¯>G÷&_¬<5F>˜|1½¾;ג¥ינװסr<D7A1>ח<EFBFBD>o4ג¼ֶ¯ֽU<D6BD>ׁE<`ש÷²÷ּLע"<22>ף6Vֶ·k<1D>4/³"–<>צtכקֺf7ײF¯°ְ<C2B0><D6B0>d]<5D>®%ץ|8•#wµוּNU›ƒnש‹8€<>%¼4ַo־<6F>ֻ—–
|
BIN
fuzzers/qemu_systemmode/corpus/zero
Normal file
BIN
fuzzers/qemu_systemmode/corpus/zero
Normal file
Binary file not shown.
2
fuzzers/qemu_systemmode/example/build.sh
Executable file
2
fuzzers/qemu_systemmode/example/build.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
arm-none-eabi-gcc -ggdb -ffreestanding -nostartfiles -lgcc -T mps2_m3.ld -mcpu=cortex-m3 main.c startup.c -o example.elf
|
38
fuzzers/qemu_systemmode/example/main.c
Normal file
38
fuzzers/qemu_systemmode/example/main.c
Normal file
@ -0,0 +1,38 @@
|
||||
int BREAKPOINT() {
|
||||
for (;;)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
int LLVMFuzzerTestOneInput(unsigned int* Data, unsigned int Size) {
|
||||
if (Data[3] == 0) {while(1){}} // cause a timeout
|
||||
for (int i=0; i<Size; i++) {
|
||||
if (Data[i] > 0xFFd0 && Data[i] < 0xFFFF) {return 1;} // cause qemu to crash
|
||||
for (int j=i+1; j<Size; j++) {
|
||||
if (Data[j] == 0) {continue;}
|
||||
if (Data[j]>Data[i]) {
|
||||
int tmp = Data[i];
|
||||
Data[i]=Data[j];
|
||||
Data[j]=tmp;
|
||||
if (Data[i] <= 100) {j--;}
|
||||
}
|
||||
}
|
||||
}
|
||||
return BREAKPOINT();
|
||||
}
|
||||
unsigned int FUZZ_INPUT[] = {
|
||||
101,201,700,230,860,
|
||||
234,980,200,340,678,
|
||||
230,134,900,236,900,
|
||||
123,800,123,658,607,
|
||||
246,804,567,568,207,
|
||||
407,246,678,457,892,
|
||||
834,456,878,246,699,
|
||||
854,234,844,290,125,
|
||||
324,560,852,928,910,
|
||||
790,853,345,234,586,
|
||||
};
|
||||
|
||||
int main() {
|
||||
LLVMFuzzerTestOneInput(FUZZ_INPUT, 50);
|
||||
}
|
141
fuzzers/qemu_systemmode/example/mps2_m3.ld
Normal file
141
fuzzers/qemu_systemmode/example/mps2_m3.ld
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* FreeRTOS V202112.00
|
||||
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
FLASH (xr) : ORIGIN = 0x00000000, LENGTH = 4M /* to 0x00003FFF = 0x007FFFFF*/
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4M /* to 0x21FFFFFF = 0xFFFFFF */
|
||||
}
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
_Min_Heap_Size = 0x300000 ; /* Required amount of heap. */
|
||||
_Min_Stack_Size = 0x4000 ; /* Required amount of stack. */
|
||||
M_VECTOR_RAM_SIZE = (16 + 48) * 4;
|
||||
_estack = ORIGIN(RAM) + LENGTH(RAM);
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
|
||||
.isr_vector :
|
||||
{
|
||||
__vector_table = .;
|
||||
KEEP(*(.isr_vector))
|
||||
. = ALIGN(4);
|
||||
} > FLASH
|
||||
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.text*)
|
||||
KEEP (*(.init))
|
||||
KEEP (*(.fini))
|
||||
KEEP(*(.eh_frame))
|
||||
*(.rodata*)
|
||||
. = ALIGN(4);
|
||||
_etext = .;
|
||||
} > FLASH
|
||||
|
||||
.ARM.extab :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
.ARM :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
|
||||
__exidx_end = .;
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
.interrupts_ram :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__VECTOR_RAM__ = .;
|
||||
__interrupts_ram_start__ = .;
|
||||
. += M_VECTOR_RAM_SIZE;
|
||||
. = ALIGN(4);
|
||||
__interrupts_ram_end = .;
|
||||
} > RAM
|
||||
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
.data : /* AT ( _sidata ) */
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sdata = .;
|
||||
*(.data*)
|
||||
. = ALIGN(4);
|
||||
_edata = .;
|
||||
} > RAM AT > FLASH
|
||||
|
||||
.uninitialized (NOLOAD):
|
||||
{
|
||||
. = ALIGN(32);
|
||||
__uninitialized_start = .;
|
||||
*(.uninitialized)
|
||||
KEEP(*(.keep.uninitialized))
|
||||
. = ALIGN(32);
|
||||
__uninitialized_end = .;
|
||||
} > RAM
|
||||
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sbss = .;
|
||||
__bss_start__ = _sbss;
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
__bss_end__ = _ebss;
|
||||
} >RAM
|
||||
|
||||
.heap :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE ( end = . );
|
||||
PROVIDE ( _end = . );
|
||||
_heap_bottom = .;
|
||||
. = . + _Min_Heap_Size;
|
||||
_heap_top = .;
|
||||
. = . + _Min_Stack_Size;
|
||||
. = ALIGN(8);
|
||||
} >RAM
|
||||
|
||||
/* Set stack top to end of RAM, and stack limit move down by
|
||||
* size of stack_dummy section */
|
||||
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
|
||||
__StackLimit = __StackTop - _Min_Stack_Size;
|
||||
PROVIDE(__stack = __StackTop);
|
||||
|
||||
/* Check if data + heap + stack exceeds RAM limit */
|
||||
ASSERT(__StackLimit >= _heap_top, "region RAM overflowed with stack")
|
||||
}
|
||||
|
113
fuzzers/qemu_systemmode/example/startup.c
Normal file
113
fuzzers/qemu_systemmode/example/startup.c
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* FreeRTOS V202112.00
|
||||
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
typedef unsigned int uint32_t;
|
||||
|
||||
extern int main();
|
||||
|
||||
extern uint32_t _estack, _sidata, _sdata, _edata, _sbss, _ebss;
|
||||
|
||||
/* Prevent optimization so gcc does not replace code with memcpy */
|
||||
__attribute__( ( optimize( "O0" ) ) )
|
||||
__attribute__( ( naked ) )
|
||||
void Reset_Handler( void )
|
||||
{
|
||||
/* set stack pointer */
|
||||
__asm volatile ( "ldr r0, =_estack" );
|
||||
__asm volatile ( "mov sp, r0" );
|
||||
|
||||
/* copy .data section from flash to RAM */
|
||||
for( uint32_t * src = &_sidata, * dest = &_sdata; dest < &_edata; )
|
||||
{
|
||||
*dest++ = *src++;
|
||||
}
|
||||
|
||||
/* zero out .bss section */
|
||||
for( uint32_t * dest = &_sbss; dest < &_ebss; )
|
||||
{
|
||||
*dest++ = 0;
|
||||
}
|
||||
|
||||
/* jump to board initialisation */
|
||||
void _start( void );
|
||||
_start();
|
||||
}
|
||||
|
||||
const uint32_t * isr_vector[] __attribute__( ( section( ".isr_vector" ) ) ) =
|
||||
{
|
||||
( uint32_t * ) &_estack,
|
||||
( uint32_t * ) &Reset_Handler, /* Reset -15 */
|
||||
0, /* NMI_Handler -14 */
|
||||
0, /* HardFault_Handler -13 */
|
||||
0, /* MemManage_Handler -12 */
|
||||
0, /* BusFault_Handler -11 */
|
||||
0, /* UsageFault_Handler -10 */
|
||||
0, /* reserved */
|
||||
0, /* reserved */
|
||||
0, /* reserved */
|
||||
0, /* reserved -6 */
|
||||
0, /* SVC_Handler -5 */
|
||||
0, /* DebugMon_Handler -4 */
|
||||
0, /* reserved */
|
||||
0, /* PendSV handler -2 */
|
||||
0, /* SysTick_Handler -1 */
|
||||
0, /* uart0 receive 0 */
|
||||
0, /* uart0 transmit */
|
||||
0, /* uart1 receive */
|
||||
0, /* uart1 transmit */
|
||||
0, /* uart 2 receive */
|
||||
0, /* uart 2 transmit */
|
||||
0, /* GPIO 0 combined interrupt */
|
||||
0, /* GPIO 2 combined interrupt */
|
||||
0, /* Timer 0 */
|
||||
0, /* Timer 1 */
|
||||
0, /* Dial Timer */
|
||||
0, /* SPI0 SPI1 */
|
||||
0, /* uart overflow 1, 2,3 */
|
||||
0, /* Ethernet 13 */
|
||||
};
|
||||
|
||||
__attribute__( ( naked ) ) void exit(__attribute__((unused)) int status )
|
||||
{
|
||||
/* Force qemu to exit using ARM Semihosting */
|
||||
__asm volatile (
|
||||
"mov r1, r0\n"
|
||||
"cmp r1, #0\n"
|
||||
"bne .notclean\n"
|
||||
"ldr r1, =0x20026\n" /* ADP_Stopped_ApplicationExit, a clean exit */
|
||||
".notclean:\n"
|
||||
"movs r0, #0x18\n" /* SYS_EXIT */
|
||||
"bkpt 0xab\n"
|
||||
"end: b end\n"
|
||||
);
|
||||
}
|
||||
|
||||
void _start( void )
|
||||
{
|
||||
main( );
|
||||
exit( 0 );
|
||||
}
|
||||
|
244
fuzzers/qemu_systemmode/src/fuzzer.rs
Normal file
244
fuzzers/qemu_systemmode/src/fuzzer.rs
Normal file
@ -0,0 +1,244 @@
|
||||
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
||||
//!
|
||||
use core::time::Duration;
|
||||
use std::{env, path::PathBuf, process};
|
||||
|
||||
use libafl::{
|
||||
bolts::{
|
||||
core_affinity::Cores,
|
||||
current_nanos,
|
||||
launcher::Launcher,
|
||||
rands::StdRand,
|
||||
shmem::{ShMemProvider, StdShMemProvider},
|
||||
tuples::tuple_list,
|
||||
AsSlice,
|
||||
},
|
||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||
events::EventConfig,
|
||||
executors::{ExitKind, TimeoutExecutor},
|
||||
feedback_or,
|
||||
feedback_or_fast,
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||
fuzzer::{Fuzzer, StdFuzzer},
|
||||
inputs::{BytesInput, HasTargetBytes},
|
||||
monitors::MultiMonitor,
|
||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||
observers::{TimeObserver, VariableMapObserver},
|
||||
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
||||
stages::StdMutationalStage,
|
||||
state::{HasCorpus, StdState},
|
||||
Error,
|
||||
//prelude::{SimpleMonitor, SimpleEventManager},
|
||||
};
|
||||
use libafl_qemu::{
|
||||
//asan::QemuAsanHelper,
|
||||
edges,
|
||||
edges::QemuEdgeCoverageHelper,
|
||||
elf::EasyElf,
|
||||
emu::Emulator,
|
||||
GuestAddr,
|
||||
//snapshot::QemuSnapshotHelper,
|
||||
QemuExecutor,
|
||||
QemuHooks,
|
||||
Regs,
|
||||
};
|
||||
|
||||
/// Read ELF program headers to resolve physical load addresses.
|
||||
fn virt2phys(vaddr: GuestAddr, tab: &EasyElf) -> GuestAddr {
|
||||
let ret;
|
||||
for i in &tab.goblin().program_headers {
|
||||
if i.vm_range().contains(&vaddr.try_into().unwrap()) {
|
||||
ret = vaddr - TryInto::<GuestAddr>::try_into(i.p_vaddr).unwrap()
|
||||
+ TryInto::<GuestAddr>::try_into(i.p_paddr).unwrap();
|
||||
return ret - (ret % 2);
|
||||
}
|
||||
}
|
||||
// unlike the arm-toolcahin goblin produces some off-by one errors when parsing arm
|
||||
vaddr - (vaddr % 2)
|
||||
}
|
||||
|
||||
pub static mut MAX_INPUT_SIZE: usize = 50;
|
||||
|
||||
pub fn fuzz() {
|
||||
if let Ok(s) = env::var("FUZZ_SIZE") {
|
||||
str::parse::<usize>(&s).expect("FUZZ_SIZE was not a number");
|
||||
};
|
||||
// Hardcoded parameters
|
||||
let timeout = Duration::from_secs(3);
|
||||
let broker_port = 1337;
|
||||
let cores = Cores::from_cmdline("1").unwrap();
|
||||
let corpus_dirs = [PathBuf::from("./corpus")];
|
||||
let objective_dir = PathBuf::from("./crashes");
|
||||
|
||||
let mut elf_buffer = Vec::new();
|
||||
let elf = EasyElf::from_file(
|
||||
env::var("KERNEL").expect("KERNEL env not set"),
|
||||
&mut elf_buffer,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let input_addr = elf
|
||||
.resolve_symbol(
|
||||
&env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()),
|
||||
0,
|
||||
)
|
||||
.expect("Symbol or env FUZZ_INPUT not found");
|
||||
let input_addr = virt2phys(input_addr, &elf);
|
||||
println!("FUZZ_INPUT @ {:#x}", input_addr);
|
||||
let breakpoint = elf
|
||||
.resolve_symbol(
|
||||
&env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()),
|
||||
0,
|
||||
)
|
||||
.expect("Symbol or env BREAKPOINT not found");
|
||||
println!("Breakpoint address = {:#x}", breakpoint);
|
||||
|
||||
let mut run_client = |state: Option<_>, mut mgr, _core_id| {
|
||||
// Initialize QEMU
|
||||
env::remove_var("LD_LIBRARY_PATH");
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let env: Vec<(String, String)> = env::vars().collect();
|
||||
let emu = Emulator::new(&args, &env);
|
||||
emu.save_snapshot("start", true);
|
||||
emu.set_breakpoint(breakpoint); // BREAKPOINT
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |input: &BytesInput| {
|
||||
let target = input.target_bytes();
|
||||
let mut buf = target.as_slice();
|
||||
let len = buf.len();
|
||||
unsafe {
|
||||
if len > MAX_INPUT_SIZE {
|
||||
buf = &buf[0..MAX_INPUT_SIZE];
|
||||
// len = MAX_INPUT_SIZE;
|
||||
}
|
||||
|
||||
emu.write_phys_mem(input_addr, buf);
|
||||
|
||||
emu.run();
|
||||
|
||||
// If the execution stops at any point other then the designated breakpoint (e.g. a breakpoint on a panic method) we consider it a crash
|
||||
let mut pcs = (0..emu.num_cpus())
|
||||
.map(|i| emu.cpu_from_index(i))
|
||||
.map(|cpu| -> Result<u32, String> { cpu.read_reg(Regs::Pc) });
|
||||
let ret = match pcs
|
||||
.find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0)))
|
||||
{
|
||||
Some(_) => ExitKind::Ok,
|
||||
None => ExitKind::Crash,
|
||||
};
|
||||
|
||||
emu.load_snapshot("start", true);
|
||||
|
||||
ret
|
||||
}
|
||||
};
|
||||
|
||||
// Create an observation channel using the coverage map
|
||||
let edges = unsafe { &mut edges::EDGES_MAP };
|
||||
let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM };
|
||||
let edges_observer = VariableMapObserver::new("edges", edges, edges_counter);
|
||||
|
||||
// Create an observation channel to keep track of the execution time
|
||||
let time_observer = TimeObserver::new("time");
|
||||
|
||||
// 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_tracking(&edges_observer, true, true),
|
||||
// Time feedback, this one does not need a feedback state
|
||||
TimeFeedback::new_with_observer(&time_observer)
|
||||
);
|
||||
|
||||
// A feedback to choose if an input is a solution or not
|
||||
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::with_seed(current_nanos()),
|
||||
// Corpus that will be evolved, we keep it in memory for performance
|
||||
InMemoryCorpus::new(),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
// on disk so the user can get them after stopping the fuzzer
|
||||
OnDiskCorpus::new(objective_dir.clone()).unwrap(),
|
||||
// States of the feedbacks.
|
||||
// The feedbacks can report the data that should persist in the State.
|
||||
&mut feedback,
|
||||
// Same for objective feedbacks
|
||||
&mut objective,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
// A minimization+queue policy to get testcasess from the corpus
|
||||
let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new());
|
||||
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
||||
let mut hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default()));
|
||||
|
||||
// Create a QEMU in-process executor
|
||||
let executor = QemuExecutor::new(
|
||||
&mut hooks,
|
||||
&mut harness,
|
||||
tuple_list!(edges_observer, time_observer),
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
&mut mgr,
|
||||
)
|
||||
.expect("Failed to create QemuExecutor");
|
||||
|
||||
// Wrap the executor to keep track of the timeout
|
||||
let mut executor = TimeoutExecutor::new(executor, timeout);
|
||||
|
||||
if state.corpus().count() < 1 {
|
||||
state
|
||||
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs)
|
||||
.unwrap_or_else(|_| {
|
||||
println!("Failed to load initial corpus at {:?}", &corpus_dirs);
|
||||
process::exit(0);
|
||||
});
|
||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
}
|
||||
|
||||
// Setup an havoc mutator with a mutational stage
|
||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||
|
||||
fuzzer
|
||||
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
|
||||
.unwrap();
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// The shared memory allocator
|
||||
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||
|
||||
// The stats reporter for the broker
|
||||
let monitor = MultiMonitor::new(|s| println!("{}", s));
|
||||
|
||||
// let monitor = SimpleMonitor::new(|s| println!("{}", s));
|
||||
// let mut mgr = SimpleEventManager::new(monitor);
|
||||
// run_client(None, mgr, 0);
|
||||
|
||||
// Build and run a Launcher
|
||||
match Launcher::builder()
|
||||
.shmem_provider(shmem_provider)
|
||||
.broker_port(broker_port)
|
||||
.configuration(EventConfig::from_build_id())
|
||||
.monitor(monitor)
|
||||
.run_client(&mut run_client)
|
||||
.cores(&cores)
|
||||
// .stdout_file(Some("/dev/null"))
|
||||
.build()
|
||||
.launch()
|
||||
{
|
||||
Ok(()) => (),
|
||||
Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."),
|
||||
Err(err) => panic!("Failed to run launcher: {:?}", err),
|
||||
}
|
||||
}
|
13
fuzzers/qemu_systemmode/src/main.rs
Normal file
13
fuzzers/qemu_systemmode/src/main.rs
Normal file
@ -0,0 +1,13 @@
|
||||
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
||||
#[cfg(target_os = "linux")]
|
||||
mod fuzzer;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn main() {
|
||||
fuzzer::fuzz();
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub fn main() {
|
||||
panic!("qemu-user and libafl_qemu is only supported on linux!");
|
||||
}
|
@ -26,6 +26,8 @@ be = []
|
||||
usermode = []
|
||||
systemmode = []
|
||||
|
||||
slirp = [ "systemmode" ] # build qemu with host libslirp (for user networking)
|
||||
|
||||
clippy = [] # special feature for clippy, don't use in normal projects§
|
||||
|
||||
[dependencies]
|
||||
|
@ -4,7 +4,7 @@ use which::which;
|
||||
|
||||
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
||||
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
||||
const QEMU_REVISION: &str = "ddb71cf43844f8848ae655ca696bdfc3fb7839f1";
|
||||
const QEMU_REVISION: &str = "5bd9736af04ea8126532df5fb5e3f82c3fda72fe";
|
||||
|
||||
fn build_dep_check(tools: &[&str]) {
|
||||
for tool in tools {
|
||||
@ -44,8 +44,6 @@ pub fn build() {
|
||||
println!("cargo:rustc-cfg=emulation_mode=\"{emulation_mode}\"");
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=src/asan-giovese.c");
|
||||
println!("cargo:rerun-if-changed=src/asan-giovese.h");
|
||||
println!("cargo:rerun-if-env-changed=CROSS_CC");
|
||||
|
||||
// Make sure we have at most one architecutre feature set
|
||||
@ -215,7 +213,7 @@ pub fn build() {
|
||||
//.arg("--as-static-lib")
|
||||
.arg("--as-shared-lib")
|
||||
.arg(&format!("--target-list={cpu_target}-{target_suffix}"))
|
||||
.arg("--enable-slirp=internal")
|
||||
.arg(if cfg!(feature = "slirp") {"--enable-slirp"} else {"--disable-slirp"})
|
||||
.arg("--enable-fdt=internal")
|
||||
.arg("--audio-drv-list=")
|
||||
.arg("--disable-alsa")
|
||||
@ -422,7 +420,6 @@ pub fn build() {
|
||||
build_dir.display()
|
||||
))
|
||||
.arg(format!("{}/libfdt.a", build_dir.display()))
|
||||
.arg(format!("{}/libslirp.a", build_dir.display()))
|
||||
.arg(format!("{}/libmigration.fa", build_dir.display()))
|
||||
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
||||
.arg(format!("{}/libqom.fa", build_dir.display()))
|
||||
@ -457,6 +454,8 @@ pub fn build() {
|
||||
println!("cargo:rustc-link-lib=glib-2.0");
|
||||
println!("cargo:rustc-link-lib=stdc++");
|
||||
println!("cargo:rustc-link-lib=z");
|
||||
#[cfg(all(feature = "slirp", feature = "systemmode"))]
|
||||
println!("cargo:rustc-link-lib=slirp");
|
||||
|
||||
if emulation_mode == "systemmode" {
|
||||
println!("cargo:rustc-link-lib=pixman-1");
|
||||
|
@ -7,7 +7,7 @@ use core::{
|
||||
};
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
use core::{mem::MaybeUninit, ptr::copy_nonoverlapping};
|
||||
use std::{slice::from_raw_parts, str::from_utf8_unchecked};
|
||||
use std::{ffi::CString, slice::from_raw_parts, str::from_utf8_unchecked};
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
use libc::c_int;
|
||||
@ -226,6 +226,7 @@ extern "C" {
|
||||
extern "C" {
|
||||
fn qemu_init(argc: i32, argv: *const *const u8, envp: *const *const u8);
|
||||
|
||||
fn vm_start();
|
||||
fn qemu_main_loop();
|
||||
fn qemu_cleanup();
|
||||
|
||||
@ -241,14 +242,13 @@ extern "C" {
|
||||
len: i32,
|
||||
is_write: i32,
|
||||
);
|
||||
fn cpu_physical_memory_rw(addr: GuestAddr, buf: *mut u8, len: i32, iswrite: bool);
|
||||
|
||||
static mut libafl_start_vcpu: extern "C" fn(cpu: CPUStatePtr);
|
||||
|
||||
/*
|
||||
fn libafl_save_qemu_snapshot(name: *const u8);
|
||||
fn libafl_save_qemu_snapshot(name: *const u8, sync: bool);
|
||||
#[allow(unused)]
|
||||
fn libafl_load_qemu_snapshot(name: *const u8);
|
||||
*/
|
||||
fn libafl_load_qemu_snapshot(name: *const u8, sync: bool);
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
@ -659,11 +659,28 @@ impl Emulator {
|
||||
}
|
||||
|
||||
pub unsafe fn write_mem(&self, addr: GuestAddr, buf: &[u8]) {
|
||||
self.current_cpu().unwrap().write_mem(addr, buf);
|
||||
self.current_cpu()
|
||||
.unwrap_or_else(|| self.cpu_from_index(0))
|
||||
.write_mem(addr, buf);
|
||||
}
|
||||
|
||||
pub unsafe fn read_mem(&self, addr: GuestAddr, buf: &mut [u8]) {
|
||||
self.current_cpu().unwrap().read_mem(addr, buf);
|
||||
self.current_cpu()
|
||||
.unwrap_or_else(|| self.cpu_from_index(0))
|
||||
.read_mem(addr, buf);
|
||||
}
|
||||
|
||||
/// Write a value to a phsical guest address, including ROM areas.
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
pub unsafe fn write_phys_mem(&self, addr: GuestAddr, buf: &[u8]) {
|
||||
cpu_physical_memory_rw(addr, buf.as_ptr() as *mut u8, buf.len() as i32, true);
|
||||
}
|
||||
|
||||
/// Read a value from a physical guest address.
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
pub unsafe fn read_phys_mem(&self, addr: GuestAddr, buf: &mut [u8]) {
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
cpu_physical_memory_rw(addr, buf.as_mut_ptr(), buf.len() as i32, false);
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@ -723,7 +740,10 @@ impl Emulator {
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
libafl_qemu_run();
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
qemu_main_loop();
|
||||
{
|
||||
vm_start();
|
||||
qemu_main_loop();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
@ -910,17 +930,17 @@ impl Emulator {
|
||||
}
|
||||
}
|
||||
|
||||
/*#[cfg(emulation_mode = "systemmode")]
|
||||
pub fn save_snapshot(&self, name: &str) {
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
pub fn save_snapshot(&self, name: &str, sync: bool) {
|
||||
let s = CString::new(name).expect("Invalid snapshot name");
|
||||
unsafe { libafl_save_qemu_snapshot(s.as_ptr() as *const _) };
|
||||
unsafe { libafl_save_qemu_snapshot(s.as_ptr() as *const _, sync) };
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
pub fn load_snapshot(&self, name: &str) {
|
||||
pub fn load_snapshot(&self, name: &str, sync: bool) {
|
||||
let s = CString::new(name).expect("Invalid snapshot name");
|
||||
unsafe { libafl_load_qemu_snapshot(s.as_ptr() as *const _) };
|
||||
}*/
|
||||
unsafe { libafl_load_qemu_snapshot(s.as_ptr() as *const _, sync) };
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
pub fn set_pre_syscall_hook(
|
||||
|
Loading…
x
Reference in New Issue
Block a user