diff --git a/.gitignore b/.gitignore index c90546d363..4fcea77629 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ vendor *.dll *.exe *.dSYM +*.obj .cur_input .venv diff --git a/Cargo.toml b/Cargo.toml index dd0f3c8add..06b334e0ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "libafl_targets", "libafl_frida", "libafl_qemu", + "libafl_tinyinst", "libafl_sugar", "libafl_nyx", "libafl_concolic/symcc_runtime", diff --git a/Dockerfile b/Dockerfile index cbed899038..398d3a5d29 100644 --- a/Dockerfile +++ b/Dockerfile @@ -72,6 +72,9 @@ COPY scripts/dummy.rs libafl_concolic/symcc_libafl/src/lib.rs COPY libafl_nyx/Cargo.toml libafl_nyx/build.rs libafl_nyx/ COPY scripts/dummy.rs libafl_nyx/src/lib.rs +COPY libafl_tinyinst/Cargo.toml libafl_tinyinst/ +COPY scripts/dummy.rs libafl_tinyinst/src/lib.rs + COPY utils utils RUN cargo build && cargo build --release diff --git a/README.md b/README.md index 627120e9b4..4033feb307 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ LibAFL offers integrations with popular instrumentation frameworks. At the momen + SanitizerCoverage, in [libafl_targets](./libafl_targets) + Frida, in [libafl_frida](./libafl_frida) + QEMU user-mode, in [libafl_qemu](./libafl_qemu) ++ TinyInst, in [libafl_tinyinst](./libafl_tinyinst) by [elbiazo](https://github.com/elbiazo) ## Getting started diff --git a/fuzzers/tinyinst_simple/Cargo.toml b/fuzzers/tinyinst_simple/Cargo.toml new file mode 100644 index 0000000000..6183b66259 --- /dev/null +++ b/fuzzers/tinyinst_simple/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "tinyinst_simple" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libafl = { version = "0.8.1", path = "../../libafl", features = ["tui"] } +libafl_tinyinst = { version = "0.8.1", path = "../../libafl_tinyinst" } +[profile.release] +codegen-units = 1 +opt-level = 3 diff --git a/fuzzers/tinyinst_simple/Makefile.toml b/fuzzers/tinyinst_simple/Makefile.toml new file mode 100644 index 0000000000..896481cf05 --- /dev/null +++ b/fuzzers/tinyinst_simple/Makefile.toml @@ -0,0 +1,8 @@ +[tasks.build_test] +command = "cl" +args = ["./test/test.c", "-o", "./test/test.exe"] + +[tasks.run] +dependencies = ["build_test"] +command = "cargo" +args = ["run", "--release"] diff --git a/fuzzers/tinyinst_simple/README.md b/fuzzers/tinyinst_simple/README.md new file mode 100644 index 0000000000..3d99aa2178 --- /dev/null +++ b/fuzzers/tinyinst_simple/README.md @@ -0,0 +1,3 @@ +# Run Instruction +1. Open up developer powershell so that you have access to cl (Windows Default Compiler) +2. Run `cargo make run` to run the fuzzer \ No newline at end of file diff --git a/fuzzers/tinyinst_simple/src/main.rs b/fuzzers/tinyinst_simple/src/main.rs new file mode 100644 index 0000000000..97c5ddf5e9 --- /dev/null +++ b/fuzzers/tinyinst_simple/src/main.rs @@ -0,0 +1,62 @@ +use std::path::PathBuf; + +use libafl::{ + bolts::{ + rands::{RandomSeed, StdRand}, + tuples::tuple_list, + }, + corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus, Testcase}, + events::SimpleEventManager, + feedbacks::{CrashFeedback, ListFeedback}, + inputs::BytesInput, + monitors::SimpleMonitor, + mutators::{havoc_mutations, StdScheduledMutator}, + observers::ListObserver, + schedulers::RandScheduler, + stages::StdMutationalStage, + state::StdState, + Fuzzer, StdFuzzer, +}; +use libafl_tinyinst::executor::TinyInstExecutor; +static mut COVERAGE: Vec = vec![]; + +fn main() { + // Tinyinst things + let tinyinst_args = vec!["-instrument_module".to_string(), "test.exe".to_string()]; + + let args = vec![".\\test\\test.exe".to_string(), "@@".to_string()]; + + let observer = ListObserver::new("cov", unsafe { &mut COVERAGE }); + let mut feedback = ListFeedback::new_with_observer(&observer); + + let input = BytesInput::new(b"bad".to_vec()); + let rand = StdRand::new(); + let mut corpus = CachedOnDiskCorpus::new(PathBuf::from("./corpus_discovered"), 64).unwrap(); + corpus + .add(Testcase::new(input)) + .expect("error in adding corpus"); + let solutions = OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(); + + let mut objective = CrashFeedback::new(); + let mut state = StdState::new(rand, corpus, solutions, &mut feedback, &mut objective).unwrap(); + let scheduler = RandScheduler::new(); + let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + + let monitor = SimpleMonitor::new(|x| println!("{}", x)); + + let mut mgr = SimpleEventManager::new(monitor); + let mut executor = unsafe { + TinyInstExecutor::new( + &mut COVERAGE, + tinyinst_args, + args, + 5000, + tuple_list!(observer), + ) + }; + 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 fuzzing loop"); +} diff --git a/fuzzers/tinyinst_simple/test/crash_input.txt b/fuzzers/tinyinst_simple/test/crash_input.txt new file mode 100644 index 0000000000..a4cec4a9fb --- /dev/null +++ b/fuzzers/tinyinst_simple/test/crash_input.txt @@ -0,0 +1 @@ +bad12 \ No newline at end of file diff --git a/fuzzers/tinyinst_simple/test/ok_input.txt b/fuzzers/tinyinst_simple/test/ok_input.txt new file mode 100644 index 0000000000..b5754e2037 --- /dev/null +++ b/fuzzers/tinyinst_simple/test/ok_input.txt @@ -0,0 +1 @@ +ok \ No newline at end of file diff --git a/fuzzers/tinyinst_simple/test/test.c b/fuzzers/tinyinst_simple/test/test.c new file mode 100644 index 0000000000..2f2911e626 --- /dev/null +++ b/fuzzers/tinyinst_simple/test/test.c @@ -0,0 +1,36 @@ +#include + +void pass1(char *buf, int buf_size) { + char target[0x10]; + if (buf[0] == 'b') { + if (buf[1] == 'a') { + if (buf[2] == 'd') { + if (buf[3] == '1') { + if (buf[4] == '2') { + printf("You got me\n"); + memcpy(target, buf, 0x1000000); + printf("GG\n"); + } + } + } + } + } +} +int main(int argc, char *argv[]) { + FILE *fp; + char buf[0x1000]; + if (argc == 2) { + fp = fopen(argv[1], "r"); + if (fp == NULL) { + printf("File not found\n"); + printf("Received filename %s\n", argv[1]); + return 1; + } + fscanf(fp, "%s", buf); + + pass1(buf, sizeof(buf)); + + } else { + printf("there is nothing\n"); + } +} diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index 60e646747e..0b5ceee98d 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -963,6 +963,7 @@ where T: Debug + Serialize + serde::de::DeserializeOwned, { name: String, + last_addr: usize, phantom: PhantomData, } @@ -1012,6 +1013,7 @@ where pub fn new(name: &'static str) -> Self { Self { name: name.to_string(), + last_addr: 0, phantom: PhantomData, } } @@ -1021,6 +1023,7 @@ where pub fn new_with_observer(observer: &ListObserver) -> Self { Self { name: observer.name().to_string(), + last_addr: 0, phantom: PhantomData, } } diff --git a/libafl_nyx/build_nyx_support.sh b/libafl_nyx/build_nyx_support.sh index 9819812615..43f7570ae1 100755 --- a/libafl_nyx/build_nyx_support.sh +++ b/libafl_nyx/build_nyx_support.sh @@ -9,14 +9,16 @@ echo "[*] Making sure all Nyx is checked out" git status 1>/dev/null 2>/dev/null -if [ ! -d ./QEMU-Nyx ]; then +if [ ! -e ./QEMU-Nyx/.git ]; then + rm -rf ./QEMU-Nyx git clone https://github.com/nyx-fuzz/QEMU-Nyx.git || exit 1 pushd QEMU-Nyx git reset --hard 80f22f77d6aab14e62bf11c80db4e210bbca5fb5 popd fi -if [ ! -d ./packer ]; then +if [ ! -e ./packer/.git ]; then + rm -rf ./packer git clone https://github.com/syheliel/packer.git || exit 1 pushd QEMU-Nyx git reset --hard 86b159bafc0b2ba8feeaa8761a45b6201d34084f diff --git a/libafl_tinyinst/Cargo.toml b/libafl_tinyinst/Cargo.toml new file mode 100644 index 0000000000..ba36f9e7ca --- /dev/null +++ b/libafl_tinyinst/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "libafl_tinyinst" +version.workspace = true +edition = "2021" +authors = ["elbiazo ", "Dongjia Zhang "] +repository = "https://github.com/AFLplusplus/LibAFL/" +categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"] +license = "MIT OR Apache-2.0" +keywords = ["fuzzing", "testing", "security"] +description = "TinyInst backend for libafl" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cxx = "1.0" +libafl = { path = "../libafl", version = "0.8.0", features = [ + "std", + "libafl_derive", +] } +tinyinst-rs = { git = "https://github.com/elbiazo/tinyinst-rs" } +# tinyinst-rs = { path = "../../tinyinst-rs" } + +[build-dependencies] +cmake = "0.1" diff --git a/libafl_tinyinst/README.md b/libafl_tinyinst/README.md new file mode 100644 index 0000000000..4bcda1d13c --- /dev/null +++ b/libafl_tinyinst/README.md @@ -0,0 +1,3 @@ +# Requirements +- you need `Visual Studio 17 2022`, though it's not tested, it should work on older versions. +- you need `cxxbridge-cmd` to generate bridge files between Rust and c++, you can install this via `cargo install cxxbridge-cmd` \ No newline at end of file diff --git a/libafl_tinyinst/src/executor.rs b/libafl_tinyinst/src/executor.rs new file mode 100644 index 0000000000..3f22aea231 --- /dev/null +++ b/libafl_tinyinst/src/executor.rs @@ -0,0 +1,138 @@ +use core::marker::PhantomData; + +use libafl::{ + bolts::fs::{InputFile, INPUTFILE_STD}, + executors::{Executor, ExitKind, HasObservers}, + inputs::{HasTargetBytes, UsesInput}, + observers::{ObserversTuple, UsesObservers}, + prelude::AsSlice, + state::{State, UsesState}, + Error, +}; +use tinyinst_rs::tinyinst::{litecov::RunResult, TinyInst}; + +pub struct TinyInstExecutor<'a, S, OT> { + tinyinst: TinyInst, + coverage: &'a mut Vec, + timeout: u32, + observers: OT, + phantom: PhantomData, + cur_input: InputFile, + use_stdin: bool, +} + +impl<'a, S, OT> std::fmt::Debug for TinyInstExecutor<'a, S, OT> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TinyInstExecutor") + .field("timeout", &self.timeout) + .finish_non_exhaustive() + } +} + +impl<'a, EM, S, Z, OT> Executor for TinyInstExecutor<'a, S, OT> +where + EM: UsesState, + S: UsesInput, + S::Input: HasTargetBytes, + Z: UsesState, +{ + #[inline] + fn run_target( + &mut self, + _fuzzer: &mut Z, + _state: &mut Self::State, + _mgr: &mut EM, + input: &Self::Input, + ) -> Result { + if !self.use_stdin { + self.cur_input.write_buf(input.target_bytes().as_slice())?; + } + + #[allow(unused_assignments)] + let mut status = RunResult::OK; + unsafe { + status = self.tinyinst.run(); + self.tinyinst.vec_coverage(self.coverage, false); + } + + match status { + RunResult::CRASH | RunResult::HANG => Ok(ExitKind::Crash), + RunResult::OK => Ok(ExitKind::Ok), + RunResult::OTHER_ERROR => Err(Error::unknown( + "Tinyinst RunResult is other error".to_string(), + )), + _ => Err(Error::unknown("Tinyinst RunResult is unknown".to_string())), + } + } +} + +impl<'a, S, OT> TinyInstExecutor<'a, S, OT> +where + OT: ObserversTuple, + S: UsesInput, +{ + /// # Safety + pub unsafe fn new( + coverage: &'a mut Vec, + tinyinst_args: Vec, + program_args: Vec, + timeout: u32, + observers: OT, + ) -> Self { + let mut use_stdin = true; + + let program_args = program_args + .into_iter() + .map(|arg| { + if arg == "@@" { + println!("Not using stdin"); + use_stdin = false; + INPUTFILE_STD.to_string() + } else { + arg + } + }) + .collect(); + + let cur_input = InputFile::create(INPUTFILE_STD).expect("Unable to create cur_file"); + println!("post init"); + let tinyinst = TinyInst::new(tinyinst_args, program_args, timeout); + + Self { + tinyinst, + coverage, + timeout, + observers, + phantom: PhantomData, + cur_input, + use_stdin, + } + } +} + +impl<'a, S, OT> HasObservers for TinyInstExecutor<'a, S, OT> +where + S: State, + OT: ObserversTuple, +{ + fn observers(&self) -> &OT { + &self.observers + } + + fn observers_mut(&mut self) -> &mut OT { + &mut self.observers + } +} +impl<'a, S, OT> UsesState for TinyInstExecutor<'a, S, OT> +where + S: UsesInput, +{ + type State = S; +} +impl<'a, S, OT> UsesObservers for TinyInstExecutor<'a, S, OT> +where + OT: ObserversTuple, + S: UsesInput, +{ + type Observers = OT; +} diff --git a/libafl_tinyinst/src/lib.rs b/libafl_tinyinst/src/lib.rs new file mode 100644 index 0000000000..0c95fdab24 --- /dev/null +++ b/libafl_tinyinst/src/lib.rs @@ -0,0 +1 @@ +pub mod executor;