Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Unicorn engine #1054

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
db13fa3
feat: init
henri2h Dec 14, 2022
dde3d3e
feat: update dep
henri2h Dec 15, 2022
6381be7
feat: make it run
henri2h Dec 15, 2022
d49fa88
feat: print read access
henri2h Dec 15, 2022
706a8ad
feat: properly register memory
henri2h Dec 15, 2022
7307b5b
feat: display block hook and write correctly memory data
henri2h Dec 21, 2022
579295d
fix: properly hook and display memory reads
henri2h Dec 21, 2022
2a57bf1
feat: added location tracker
henri2h Jan 3, 2023
ad3e69e
feat: added edge coverage
henri2h Jan 8, 2023
6a1a60c
feat: startingq hooking the fuzzer
henri2h Jan 8, 2023
79e6ddf
feat: discard state
henri2h Jan 9, 2023
022584a
Fix compilation
andreafioraldi Jan 10, 2023
e98f753
feat: rewritten the emulator to be less verbose and remove code dupli…
henri2h Jan 10, 2023
e09dda7
feat: reduce code duplication (again)
henri2h Jan 19, 2023
9f68628
feat: allow to run emulator only
henri2h Jan 19, 2023
5da5b93
feat: crash on wrong value
henri2h Jan 19, 2023
d3458ef
feat: replace debug and reset EDGES_MAP to 0 at start
henri2h Jan 19, 2023
c826fbf
fix: change generator type
henri2h Jan 19, 2023
71e579c
fix: set MAX_EDGES_NUM and reduce verbosity
henri2h Jan 19, 2023
97539ee
fix: properly initialize input
henri2h Jan 19, 2023
00cbe7f
Fix OOM
andreafioraldi Jan 19, 2023
20d70e3
feat: code re write
henri2h Feb 3, 2023
43f21bf
feat: add multiple architectures
henri2h Feb 3, 2023
e1a0e2c
feat: provide disassembly and fuzz x86 code
henri2h Feb 5, 2023
4a6e7f2
fix: add possibility to fuzz different CPU platform
henri2h Feb 5, 2023
6384d95
feat: change signal
henri2h Feb 6, 2023
cf92098
feat: clean library
henri2h Feb 9, 2023
96f4d12
fix: removed dependency constraints
henri2h Feb 9, 2023
9305d54
fix: removed binaries to make CI happy
henri2h Feb 9, 2023
8a9005c
fix: disable memory hook
henri2h Feb 9, 2023
a842044
fix: add new line
henri2h Feb 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ members = [
"libafl_tinyinst",
"libafl_sugar",
"libafl_nyx",
"libafl_unicorn",
"libafl_concolic/symcc_runtime",
"libafl_concolic/symcc_libafl",
"libafl_concolic/test/dump_constraints",
Expand Down
12 changes: 12 additions & 0 deletions fuzzers/unicorn/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "unicorn"
version = "0.1.0"
edition = "2021"

[dependencies]
libafl = { path = "../../libafl/" }
libafl_unicorn = { path = "../../libafl_unicorn/" }
libafl_targets = { path = "../../libafl_targets" }

unicorn-engine = "2.0.1"
iced-x86 = "1.18.0"
31 changes: 31 additions & 0 deletions fuzzers/unicorn/bin/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
arm64="aarch64-linux-gnu"
arm="arm-linux-gnueabihf"
x64="x86_64-linux-gnu"
assembly_arm64:
$(arm64)-gcc -O2 -S -c foo.c -o foo_arm64.s

binary_arm64:
$(arm64)-as foo_arm64.s -o foo_arm64

assembly_arm:
$(arm)-gcc -O2 -S -c foo.c -o foo_arm.s

binary_arm:
$(arm)-as foo_arm.s -o foo_arm

assembly_x86:
$(x64)-gcc -O2 -S -c foo.c -o foo_x86.s

binary_x86:
$(x64)-as foo_x86.s -o foo_x86

build_arm: assembly_arm binary_arm
build_arm64: assembly_arm64 binary_arm64
build_x86: assembly_x86 binary_x86

clean:
rm foo_*


all: build_arm build_arm64 build_x86
# sudo apt install gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu
31 changes: 31 additions & 0 deletions fuzzers/unicorn/bin/foo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include <stdint.h>
#define len 2

int main() {
volatile unsigned char a; // = 0x1;
volatile unsigned char b; // = 0x0;
volatile unsigned char c = 0; // The result, so should be initialized at 0;

/*volatile unsigned char f[len];

for(int i = 0; i< len; i++){
f[i] = i;
}*/
c = 0x1;
if (a > b) {
c = 0x2;
if (a > 0x20) {
c = 0x3;
if (a == 0x50) {
c = 0x4;
if (b == 0x24) { c = 0x5; }
}
}
}
/*
a = 0xDE;
b = 0xEA;
c = 0xBE;
*/
return c;
}
255 changes: 255 additions & 0 deletions fuzzers/unicorn/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
use libafl_unicorn::emu::{Emulator, CODE_ADDRESS};
use std::{env, path::PathBuf, time::Duration};

use libafl::{
bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice},
corpus::{InMemoryCorpus, OnDiskCorpus},
events::SimpleEventManager,
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
feedback_or, feedback_or_fast,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, StdFuzzer},
generators::RandBytesGenerator,
inputs::{BytesInput, HasTargetBytes},
monitors::MultiMonitor,
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
observers::{ConstMapObserver, HitcountsMapObserver, TimeObserver},
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
stages::mutational::StdMutationalStage,
state::StdState,
};
pub use libafl_targets::{EDGES_MAP_PTR, EDGES_MAP_SIZE};
use unicorn_engine::unicorn_const::{Arch, MemType, SECOND_SCALE};

pub const MAX_INPUT_SIZE: usize = 0x8000; //1048576; // 1MB
pub const DEBUG: bool = false;

// emulating
fn fuzzer(should_emulate: bool) {
let arch = Arch::X86;
let input_addr_end: u64 = 0x8000;
let input_addr_start: u64 = input_addr_end - MAX_INPUT_SIZE as u64;
let emu = &mut Emulator::new(arch);
emu.setup(
input_addr_start,
MAX_INPUT_SIZE,
match arch {
Arch::ARM => "bin/foo_arm",
Arch::ARM64 => "bin/foo_arm64",
Arch::X86 => "bin/foo_x86",
_ => "",
},
);
emu.set_code_hook();
//emu.set_memory_hook(input_addr_start, MAX_INPUT_SIZE, callback);

let mut harness = |input: &BytesInput| {
let target = input.target_bytes();
let mut buf = target.as_slice();
let len = buf.len();
if len > MAX_INPUT_SIZE {
buf = &buf[0..MAX_INPUT_SIZE];
}

emu.write_mem(input_addr_end - buf.len() as u64, buf);

emu.init_registers(input_addr_end);

let result = emu.emu_start(
match emu.get_arch() {
Arch::ARM64 => CODE_ADDRESS + 0x40, // Position of main: 0x40 TODO: see if possible to get the main position from header file. Seems weird doing so
_ => CODE_ADDRESS,
},
CODE_ADDRESS + emu.get_code_len(),
10 * SECOND_SCALE,
0x1000,
);

match result {
Ok(_) => {
// never hapens
panic!("huh");
}
Err(err) => {
let mut instruction = [0];

let pc = emu.pc_read().unwrap();
let sp = emu.get_stack_pointer();

if emu.get_arch() == Arch::X86 {
emu.mem_read(pc, &mut instruction)
.expect("could not read at pointer address");
}

if pc == 0 || instruction[0] == 0xC3 {
// Did we reached the beginning of the stack or is it a return ?
if DEBUG {
println!("Reached start");
}

// check output
let mut buf: [u8; 1] = [0];

emu.mem_read(sp - 1, &mut buf)
.expect("Could not read memory");

// check result
if buf[0] != 0x5 {
// didn't found the correct value
if DEBUG {
println!("Incorrect output found!");
println!("Output: {:#}", buf[0]);

emu.memory_dump(2);
}
return ExitKind::Ok;
}

// success
println!("Correct input found");
println!("Output: {:#}", buf[0]);
emu.memory_dump(2);

panic!("Success :)");
} else {
emu.debug_print(err);
}
}
}

return ExitKind::Ok;
};

if should_emulate {
println!("Starting emulation:");
let mem_data: Vec<u8> = vec![0x50, 0x24, 0x0];
harness(&BytesInput::from(mem_data));
return;
}

let timeout = Duration::from_secs(1);

let monitor = MultiMonitor::new(|s| println!("{s}"));
// The event manager handle the various events generated during the fuzzing loop
// such as the notification of the addition of a new item to the corpus
let mut mgr = SimpleEventManager::new(monitor);

let edges_observer = unsafe {
HitcountsMapObserver::new(ConstMapObserver::<_, EDGES_MAP_SIZE>::from_mut_ptr(
"edges",
EDGES_MAP_PTR,
))
};

// 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, false),
// Time feedback, this one does not need a feedback state
TimeFeedback::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());

// create a State from scratch
let mut state = 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(PathBuf::from("./crashes")).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 executor = InProcessExecutor::new(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut mgr,
)
.expect("Failed to create the executor");

let mut executor = TimeoutExecutor::new(executor, timeout);

// Generator of printable bytearrays of max size 32
let mut generator = RandBytesGenerator::new(4);

// Generate 8 initial inputs
state
.generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8)
.expect("Failed to generate the initial corpus");

// Setup a mutational stage with a basic bytes mutator
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)
.expect("Error in the fuzzing loop");
}

fn callback(
emu: &mut unicorn_engine::Unicorn<()>,
mem: MemType,
address: u64,
size: usize,
value: i64,
) -> bool {
if DEBUG {
match mem {
MemType::WRITE => println!(
"0x{:X}\tMemory is being WRITTEN at adress: {:X} size: {} value: {}",
emu.pc_read().unwrap(),
address,
size,
value
),
MemType::READ => println!(
"0x{}\tMemory is being READ at adress: {:X} size: {}",
emu.pc_read().unwrap(),
address,
size
),
_ => println!(
"0x{}\tMemory access type: {:?} adress: {:X} size: {} value: {}",
emu.pc_read().unwrap(),
mem,
address,
size,
value
),
}
}

return true;
}

fn main() {
let args: Vec<_> = env::args().collect();
let mut emu = false;
if args.len() > 1 {
if args[1] == "emu" {
emu = true;
}
}
fuzzer(emu);
}
24 changes: 24 additions & 0 deletions libafl_unicorn/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "libafl_unicorn"
version.workspace = true
authors = [""]
description = "Unicorn backend library for LibAFL"
documentation = "https://docs.rs/"
repository = "https://github.com/AFLplusplus/"
readme = "../README.md"
license = "MIT OR Apache-2.0"
keywords = ["fuzzing", "unicorn"]
edition = "2021"
categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"]

[dependencies]
libafl = { path = "../libafl", default-features = false, features = ["std", "derive", "llmp_compression"] }
libafl_targets = { path = "../libafl_targets" }
hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible

unicorn-engine = "2.0.1"
iced-x86 = "1.18.0"

[lib]
name = "libafl_unicorn"
crate-type = ["cdylib", "rlib"]
Loading