From db13fa3b497c1a2f11f179054162198cec5df454 Mon Sep 17 00:00:00 2001 From: henri2h Date: Wed, 14 Dec 2022 17:51:41 +0100 Subject: [PATCH 01/31] feat: init --- libafl_unicorn/Cargo.toml | 9 ++ libafl_unicorn/libafl_unicorn_test/Makefile | 7 ++ libafl_unicorn/libafl_unicorn_test/foo.c | 21 ++++ libafl_unicorn/libafl_unicorn_test/foo.s | 58 +++++++++ libafl_unicorn/src/main.rs | 129 ++++++++++++++++++++ 5 files changed, 224 insertions(+) create mode 100644 libafl_unicorn/Cargo.toml create mode 100644 libafl_unicorn/libafl_unicorn_test/Makefile create mode 100644 libafl_unicorn/libafl_unicorn_test/foo.c create mode 100644 libafl_unicorn/libafl_unicorn_test/foo.s create mode 100644 libafl_unicorn/src/main.rs diff --git a/libafl_unicorn/Cargo.toml b/libafl_unicorn/Cargo.toml new file mode 100644 index 0000000000..fdd3f31890 --- /dev/null +++ b/libafl_unicorn/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "unicorn_test" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +unicorn-engine = "2.0.1" diff --git a/libafl_unicorn/libafl_unicorn_test/Makefile b/libafl_unicorn/libafl_unicorn_test/Makefile new file mode 100644 index 0000000000..77298536e4 --- /dev/null +++ b/libafl_unicorn/libafl_unicorn_test/Makefile @@ -0,0 +1,7 @@ + +assembly: + arm-linux-gnueabihf-gcc -O2 -S -c foo.c + + +binary: + arm-linux-gnueabihf-as foo.s \ No newline at end of file diff --git a/libafl_unicorn/libafl_unicorn_test/foo.c b/libafl_unicorn/libafl_unicorn_test/foo.c new file mode 100644 index 0000000000..2d480458d7 --- /dev/null +++ b/libafl_unicorn/libafl_unicorn_test/foo.c @@ -0,0 +1,21 @@ +#include + +int main() { + volatile unsigned char a; // = 0x1; + volatile unsigned char b; // = 0x0; + volatile unsigned char c; // = 0x0; + + if (a > b) { + c = 0x1; + if (a > 20) { + c = 0x2; + if (a == 50) { + c = 0x3; + if (b == 24) { + c = 0x4; + } + } + } + } + return c; +} \ No newline at end of file diff --git a/libafl_unicorn/libafl_unicorn_test/foo.s b/libafl_unicorn/libafl_unicorn_test/foo.s new file mode 100644 index 0000000000..c92fec0f6b --- /dev/null +++ b/libafl_unicorn/libafl_unicorn_test/foo.s @@ -0,0 +1,58 @@ + .arch armv7-a + .fpu vfpv3-d16 + .eabi_attribute 28, 1 + .eabi_attribute 20, 1 + .eabi_attribute 21, 1 + .eabi_attribute 23, 3 + .eabi_attribute 24, 1 + .eabi_attribute 25, 1 + .eabi_attribute 26, 2 + .eabi_attribute 30, 2 + .eabi_attribute 34, 1 + .eabi_attribute 18, 4 + .file "foo.c" + .text + .section .text.startup,"ax",%progbits + .align 1 + .p2align 2,,3 + .global main + .syntax unified + .thumb + .thumb_func + .type main, %function +main: + @ args = 0, pretend = 0, frame = 8 + @ frame_needed = 0, uses_anonymous_args = 0 + @ link register save eliminated. + sub sp, sp, #8 + ldrb r2, [sp, #5] @ zero_extendqisi2 + ldrb r3, [sp, #6] @ zero_extendqisi2 + cmp r2, r3 + bls .L3 + movs r3, #1 + strb r3, [sp, #7] + ldrb r3, [sp, #5] @ zero_extendqisi2 + cmp r3, #20 + bls .L3 + movs r3, #2 + strb r3, [sp, #7] + ldrb r3, [sp, #5] @ zero_extendqisi2 + cmp r3, #50 + beq .L7 +.L3: + ldrb r0, [sp, #7] @ zero_extendqisi2 + add sp, sp, #8 + @ sp needed + bx lr +.L7: + movs r3, #3 + strb r3, [sp, #7] + ldrb r3, [sp, #6] @ zero_extendqisi2 + cmp r3, #24 + itt eq + moveq r3, #4 + strbeq r3, [sp, #7] + b .L3 + .size main, .-main + .ident "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0" + .section .note.GNU-stack,"",%progbits diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs new file mode 100644 index 0000000000..1f6ebc3c0f --- /dev/null +++ b/libafl_unicorn/src/main.rs @@ -0,0 +1,129 @@ +use std::fs::File; +use std::io::Read; + +use unicorn_engine::unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}; +use unicorn_engine::RegisterARM; + +fn callback( + unicorn: &mut unicorn_engine::Unicorn<()>, + mem: MemType, + number: u64, + size: usize, + other_number: i64, +) -> bool { + println!( + "Bad registration done number: {}, size: {}, other_number: {}", + number, size, other_number + ); + println!("MemType: {:?}", mem); + return true; +} + +fn emulate() { + let address = 0x1000; + let r_sp = 0x8000; + let data_size = 0x1000; + + let mut f = File::open("test/a.out").expect("Could not open file"); + let mut buffer = Vec::new(); + + // read the whole file + f.read_to_end(&mut buffer).expect("Could not read file"); + + let arm_code32 = buffer; /*[ + 0x9a, 0x42, 0x15, 0xbf, 0x00, 0x9a, 0x01, 0x9a, 0x78, 0x23, 0x15, 0x23, + ];*/ + //buffer; + // cmp r2, r3; itete + // ne; ldrne r2, + // [sp]; ldreq r2, + // [sp,#4]; movne + // r3, #0x78; moveq + // r3, #0x15 + + // [0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 + println!("Program length: {}", arm_code32.len()); + + let mut emu = unicorn_engine::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN) + .expect("failed to initialize Unicorn instance"); + + // Define memory regions + emu.mem_map( + address, + ((arm_code32.len() / 1024) + 1) * 1024, + Permission::ALL, + ) + .expect("failed to map code page"); + emu.mem_map(r_sp, data_size * 8, Permission::ALL) + .expect("failed to map data page"); + + // Write memory + emu.mem_write(address, &arm_code32) + .expect("failed to write instructions"); + emu.mem_write(r_sp, &[0x2, 0x0]) + .expect("failed to write instructions"); + + // Set registry + emu.reg_write(RegisterARM::SP, r_sp) + .expect("Could not set registery"); + + // Add me mory hook + emu.add_mem_hook( + HookType::MEM_WRITE_UNMAPPED, + r_sp, + r_sp + (data_size) as u64, + callback, + ) + .expect("Failed to register watcher"); + + let result = emu.emu_start( + address, + address + (arm_code32.len()) as u64, + 10 * SECOND_SCALE, + 0x1000, + ); + + match result { + Ok(_) => { + println!("Ok"); + + assert_eq!(emu.reg_read(RegisterARM::R0), Ok(100)); + assert_eq!(emu.reg_read(RegisterARM::R5), Ok(1337)); + } + Err(err) => { + println!(); + println!("Snap... something went wrong"); + println!("Error: {:?}", err); + + let pc = emu.pc_read().unwrap(); + println!(); + println!("Status when crash happened"); + + println!("PC: {:X}", pc); + println!("SP: {:X}", emu.reg_read(RegisterARM::SP).unwrap()); + println!("R0: {:X}", emu.reg_read(RegisterARM::R0).unwrap()); + println!("R1: {:X}", emu.reg_read(RegisterARM::R1).unwrap()); + println!("R2: {:X}", emu.reg_read(RegisterARM::R2).unwrap()); + println!("R3: {:X}", emu.reg_read(RegisterARM::R3).unwrap()); + + println!(); + for i in 0..10 { + let pos = pc + i * 2 - 10; + + let read_result = emu.mem_read_as_vec(pos, 2); + match read_result { + Ok(data) => { + println!("{:X}: {}:\t 0x{:X}\t0x{:X}", pos, i as i64 - 5, data[0], data[1]); + } + Err(err) => { + println!("{:X} Err: {:?}", pos, err); + } + } + } + } + } +} + +fn main() { + emulate(); +} From dde3d3e39bea483e403d9ad562d9eb9d9482998c Mon Sep 17 00:00:00 2001 From: henri2h Date: Thu, 15 Dec 2022 16:10:03 +0100 Subject: [PATCH 02/31] feat: update dep --- Cargo.toml | 1 + libafl_unicorn/Cargo.toml | 15 ++++++++++++--- libafl_unicorn/libafl_unicorn_test/Makefile | 4 +++- libafl_unicorn/libafl_unicorn_test/foo.s | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d0352f178c..5d3e1a922b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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", diff --git a/libafl_unicorn/Cargo.toml b/libafl_unicorn/Cargo.toml index fdd3f31890..1bd4426252 100644 --- a/libafl_unicorn/Cargo.toml +++ b/libafl_unicorn/Cargo.toml @@ -1,9 +1,18 @@ [package] name = "unicorn_test" -version = "0.1.0" +version.workspace = true +authors = [""] +description = "Frida 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" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"] [dependencies] +#libafl = { path = "../libafl", default-features = false, version = "0.8.2", features = ["std", "libafl_derive", "frida_cli"] } +#libafl_targets = { path = "../libafl_targets", version = "0.8.2", features = ["std", "sancov_cmplog"] } + unicorn-engine = "2.0.1" diff --git a/libafl_unicorn/libafl_unicorn_test/Makefile b/libafl_unicorn/libafl_unicorn_test/Makefile index 77298536e4..f29f73fffe 100644 --- a/libafl_unicorn/libafl_unicorn_test/Makefile +++ b/libafl_unicorn/libafl_unicorn_test/Makefile @@ -4,4 +4,6 @@ assembly: binary: - arm-linux-gnueabihf-as foo.s \ No newline at end of file + arm-linux-gnueabihf-as foo.s + +# sudo apt install gcc-arm-linux-gnueabihf \ No newline at end of file diff --git a/libafl_unicorn/libafl_unicorn_test/foo.s b/libafl_unicorn/libafl_unicorn_test/foo.s index c92fec0f6b..8228d099f9 100644 --- a/libafl_unicorn/libafl_unicorn_test/foo.s +++ b/libafl_unicorn/libafl_unicorn_test/foo.s @@ -54,5 +54,5 @@ main: strbeq r3, [sp, #7] b .L3 .size main, .-main - .ident "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0" + .ident "GCC: (Ubuntu 12.2.0-3ubuntu1) 12.2.0" .section .note.GNU-stack,"",%progbits From 6381be7be760274ba3f60f9efd370a50243cd55b Mon Sep 17 00:00:00 2001 From: henri2h Date: Thu, 15 Dec 2022 17:32:24 +0100 Subject: [PATCH 03/31] feat: make it run --- libafl_unicorn/libafl_unicorn_test/Makefile | 9 +- libafl_unicorn/libafl_unicorn_test/foo.s | 87 +++++++++---------- libafl_unicorn/src/main.rs | 95 ++++++++++----------- 3 files changed, 93 insertions(+), 98 deletions(-) diff --git a/libafl_unicorn/libafl_unicorn_test/Makefile b/libafl_unicorn/libafl_unicorn_test/Makefile index f29f73fffe..358cdb05e9 100644 --- a/libafl_unicorn/libafl_unicorn_test/Makefile +++ b/libafl_unicorn/libafl_unicorn_test/Makefile @@ -1,9 +1,10 @@ - +class="aarch64-linux-gnu" +# arm-linux-gnueabihf assembly: - arm-linux-gnueabihf-gcc -O2 -S -c foo.c + $(class)-gcc -O2 -S -c foo.c binary: - arm-linux-gnueabihf-as foo.s + $(class)-as foo.s -# sudo apt install gcc-arm-linux-gnueabihf \ No newline at end of file +# sudo apt install gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu \ No newline at end of file diff --git a/libafl_unicorn/libafl_unicorn_test/foo.s b/libafl_unicorn/libafl_unicorn_test/foo.s index 8228d099f9..3e001e9ded 100644 --- a/libafl_unicorn/libafl_unicorn_test/foo.s +++ b/libafl_unicorn/libafl_unicorn_test/foo.s @@ -1,58 +1,53 @@ - .arch armv7-a - .fpu vfpv3-d16 - .eabi_attribute 28, 1 - .eabi_attribute 20, 1 - .eabi_attribute 21, 1 - .eabi_attribute 23, 3 - .eabi_attribute 24, 1 - .eabi_attribute 25, 1 - .eabi_attribute 26, 2 - .eabi_attribute 30, 2 - .eabi_attribute 34, 1 - .eabi_attribute 18, 4 + .arch armv8-a .file "foo.c" .text - .section .text.startup,"ax",%progbits - .align 1 - .p2align 2,,3 + .section .text.startup,"ax",@progbits + .align 2 + .p2align 4,,11 .global main - .syntax unified - .thumb - .thumb_func .type main, %function main: - @ args = 0, pretend = 0, frame = 8 - @ frame_needed = 0, uses_anonymous_args = 0 - @ link register save eliminated. - sub sp, sp, #8 - ldrb r2, [sp, #5] @ zero_extendqisi2 - ldrb r3, [sp, #6] @ zero_extendqisi2 - cmp r2, r3 +.LFB0: + .cfi_startproc + sub sp, sp, #16 + .cfi_def_cfa_offset 16 + ldrb w1, [sp, 13] + ldrb w0, [sp, 14] + and w0, w0, 255 + cmp w0, w1, uxtb + bcs .L3 + mov w0, 1 + strb w0, [sp, 15] + ldrb w0, [sp, 13] + and w0, w0, 255 + cmp w0, 20 bls .L3 - movs r3, #1 - strb r3, [sp, #7] - ldrb r3, [sp, #5] @ zero_extendqisi2 - cmp r3, #20 - bls .L3 - movs r3, #2 - strb r3, [sp, #7] - ldrb r3, [sp, #5] @ zero_extendqisi2 - cmp r3, #50 + mov w0, 2 + strb w0, [sp, 15] + ldrb w0, [sp, 13] + and w0, w0, 255 + cmp w0, 50 beq .L7 .L3: - ldrb r0, [sp, #7] @ zero_extendqisi2 - add sp, sp, #8 - @ sp needed - bx lr + ldrb w0, [sp, 15] + add sp, sp, 16 + .cfi_remember_state + .cfi_def_cfa_offset 0 + and w0, w0, 255 + ret .L7: - movs r3, #3 - strb r3, [sp, #7] - ldrb r3, [sp, #6] @ zero_extendqisi2 - cmp r3, #24 - itt eq - moveq r3, #4 - strbeq r3, [sp, #7] + .cfi_restore_state + mov w0, 3 + strb w0, [sp, 15] + ldrb w0, [sp, 14] + and w0, w0, 255 + cmp w0, 24 + bne .L3 + mov w0, 4 + strb w0, [sp, 15] b .L3 + .cfi_endproc +.LFE0: .size main, .-main .ident "GCC: (Ubuntu 12.2.0-3ubuntu1) 12.2.0" - .section .note.GNU-stack,"",%progbits + .section .note.GNU-stack,"",@progbits diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index 1f6ebc3c0f..f0ab90577c 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -2,10 +2,10 @@ use std::fs::File; use std::io::Read; use unicorn_engine::unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}; -use unicorn_engine::RegisterARM; +use unicorn_engine::{RegisterARM, RegisterARM64}; fn callback( - unicorn: &mut unicorn_engine::Unicorn<()>, + _unicorn: &mut unicorn_engine::Unicorn<()>, mem: MemType, number: u64, size: usize, @@ -24,33 +24,24 @@ fn emulate() { let r_sp = 0x8000; let data_size = 0x1000; - let mut f = File::open("test/a.out").expect("Could not open file"); + let mut f = File::open("libafl_unicorn_test/a.out").expect("Could not open file"); let mut buffer = Vec::new(); // read the whole file f.read_to_end(&mut buffer).expect("Could not read file"); - let arm_code32 = buffer; /*[ - 0x9a, 0x42, 0x15, 0xbf, 0x00, 0x9a, 0x01, 0x9a, 0x78, 0x23, 0x15, 0x23, - ];*/ - //buffer; - // cmp r2, r3; itete - // ne; ldrne r2, - // [sp]; ldreq r2, - // [sp,#4]; movne - // r3, #0x78; moveq - // r3, #0x15 + let arm_code = buffer; // [0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 - println!("Program length: {}", arm_code32.len()); + println!("Program length: {}", arm_code.len()); - let mut emu = unicorn_engine::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN) + let mut emu = unicorn_engine::Unicorn::new(Arch::ARM64, Mode::LITTLE_ENDIAN) .expect("failed to initialize Unicorn instance"); // Define memory regions emu.mem_map( address, - ((arm_code32.len() / 1024) + 1) * 1024, + ((arm_code.len() / 1024) + 1) * 1024, Permission::ALL, ) .expect("failed to map code page"); @@ -58,13 +49,14 @@ fn emulate() { .expect("failed to map data page"); // Write memory - emu.mem_write(address, &arm_code32) + emu.mem_write(address, &arm_code) .expect("failed to write instructions"); emu.mem_write(r_sp, &[0x2, 0x0]) .expect("failed to write instructions"); // Set registry - emu.reg_write(RegisterARM::SP, r_sp) + // TODO: For some reason, the compiled program start by substracting 0x10 to SP + emu.reg_write(RegisterARM64::SP, r_sp + 0x10) .expect("Could not set registery"); // Add me mory hook @@ -76,9 +68,11 @@ fn emulate() { ) .expect("Failed to register watcher"); + println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); + let result = emu.emu_start( - address, - address + (arm_code32.len()) as u64, + address + 0x40, // start at main. Position of main: 0x40 + address + (arm_code.len()) as u64, 10 * SECOND_SCALE, 0x1000, ); @@ -87,36 +81,41 @@ fn emulate() { Ok(_) => { println!("Ok"); - assert_eq!(emu.reg_read(RegisterARM::R0), Ok(100)); - assert_eq!(emu.reg_read(RegisterARM::R5), Ok(1337)); + assert_eq!(emu.reg_read(RegisterARM64::X0), Ok(100)); + assert_eq!(emu.reg_read(RegisterARM64::X1), Ok(1337)); } Err(err) => { - println!(); - println!("Snap... something went wrong"); - println!("Error: {:?}", err); - - let pc = emu.pc_read().unwrap(); - println!(); - println!("Status when crash happened"); - - println!("PC: {:X}", pc); - println!("SP: {:X}", emu.reg_read(RegisterARM::SP).unwrap()); - println!("R0: {:X}", emu.reg_read(RegisterARM::R0).unwrap()); - println!("R1: {:X}", emu.reg_read(RegisterARM::R1).unwrap()); - println!("R2: {:X}", emu.reg_read(RegisterARM::R2).unwrap()); - println!("R3: {:X}", emu.reg_read(RegisterARM::R3).unwrap()); - - println!(); - for i in 0..10 { - let pos = pc + i * 2 - 10; - - let read_result = emu.mem_read_as_vec(pos, 2); - match read_result { - Ok(data) => { - println!("{:X}: {}:\t 0x{:X}\t0x{:X}", pos, i as i64 - 5, data[0], data[1]); - } - Err(err) => { - println!("{:X} Err: {:?}", pos, err); + if emu.pc_read().unwrap() == 0 { + println!("Reached start"); + println!("Execution successfull ?"); + } else { + println!(); + println!("Snap... something went wrong"); + println!("Error: {:?}", err); + + let pc = emu.pc_read().unwrap(); + println!(); + println!("Status when crash happened"); + + println!("PC: {:X}", pc); + println!("PC: {:X}", emu.reg_read(RegisterARM64::PC).unwrap()); + println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); + println!("X0: {:X}", emu.reg_read(RegisterARM64::X0).unwrap()); + println!("X1: {:X}", emu.reg_read(RegisterARM64::X1).unwrap()); + println!("X2: {:X}", emu.reg_read(RegisterARM64::X2).unwrap()); + println!("X3: {:X}", emu.reg_read(RegisterARM64::X3).unwrap()); + + println!(); + for i in 0..10 { + let pos = i * 4 + pc - 4 * 5; // Instruction are on 4 bytes + let dec = pos as i64 - pc as i64; + + let read_result = emu.mem_read_as_vec(pos, 4); + match read_result { + Ok(data) => { + println!("{:X}: {:03}:\t {:02X} {:02X} {:02X} {:02X} {:08b} {:08b} {:08b} {:08b}", pos, dec, data[0], data[1], data[2], data[3], data[0], data[1], data[2], data[3]); + } + Err(_) => {} } } } From d49fa88c728d66d001c6cb230472998523cd1a47 Mon Sep 17 00:00:00 2001 From: henri2h Date: Thu, 15 Dec 2022 17:38:03 +0100 Subject: [PATCH 04/31] feat: print read access --- libafl_unicorn/src/main.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index f0ab90577c..b6b0c18e7d 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -2,7 +2,7 @@ use std::fs::File; use std::io::Read; use unicorn_engine::unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}; -use unicorn_engine::{RegisterARM, RegisterARM64}; +use unicorn_engine::{RegisterARM64}; fn callback( _unicorn: &mut unicorn_engine::Unicorn<()>, @@ -11,11 +11,7 @@ fn callback( size: usize, other_number: i64, ) -> bool { - println!( - "Bad registration done number: {}, size: {}, other_number: {}", - number, size, other_number - ); - println!("MemType: {:?}", mem); + println!("Memory access type: {:?} number: {} size: {} other_number: {}", mem, number, size, other_number); return true; } @@ -68,6 +64,23 @@ fn emulate() { ) .expect("Failed to register watcher"); + + emu.add_mem_hook( + HookType::MEM_READ, + r_sp, + r_sp + (data_size) as u64, + callback, + ) + .expect("Failed to register watcher"); + + emu.add_mem_hook( + HookType::MEM_FETCH, + r_sp, + r_sp + (data_size) as u64, + callback, + ) + .expect("Failed to register watcher"); + println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); let result = emu.emu_start( From 706a8ad46fda947d762800ab837bd843d6c00301 Mon Sep 17 00:00:00 2001 From: henri2h Date: Thu, 15 Dec 2022 19:13:42 +0100 Subject: [PATCH 05/31] feat: properly register memory --- libafl_unicorn/libafl_unicorn_test/Makefile | 1 + libafl_unicorn/libafl_unicorn_test/foo.c | 26 ++++++--- libafl_unicorn/libafl_unicorn_test/foo.s | 12 +++-- libafl_unicorn/src/main.rs | 60 ++++++++++++++++----- 4 files changed, 75 insertions(+), 24 deletions(-) diff --git a/libafl_unicorn/libafl_unicorn_test/Makefile b/libafl_unicorn/libafl_unicorn_test/Makefile index 358cdb05e9..b13b266906 100644 --- a/libafl_unicorn/libafl_unicorn_test/Makefile +++ b/libafl_unicorn/libafl_unicorn_test/Makefile @@ -7,4 +7,5 @@ assembly: binary: $(class)-as foo.s +all: assembly binary # sudo apt install gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu \ No newline at end of file diff --git a/libafl_unicorn/libafl_unicorn_test/foo.c b/libafl_unicorn/libafl_unicorn_test/foo.c index 2d480458d7..6f3de133c8 100644 --- a/libafl_unicorn/libafl_unicorn_test/foo.c +++ b/libafl_unicorn/libafl_unicorn_test/foo.c @@ -1,21 +1,31 @@ #include +#define len 2 int main() { - volatile unsigned char a; // = 0x1; - volatile unsigned char b; // = 0x0; - volatile unsigned char c; // = 0x0; + volatile unsigned char a; // = 0x1; + volatile unsigned char b; // = 0x0; + volatile unsigned char c; // = 0x0; + + /*volatile unsigned char f[len]; + + for(int i = 0; i< len; i++){ + f[i] = i; + }*/ if (a > b) { c = 0x1; - if (a > 20) { + if (a > 0x20) { c = 0x2; - if (a == 50) { + if (a == 0x50) { c = 0x3; - if (b == 24) { - c = 0x4; - } + if (b == 0x24) { c = 0x4; } } } } + /* + a = 0xDE; + b = 0xEA; + c = 0xBE; + */ return c; } \ No newline at end of file diff --git a/libafl_unicorn/libafl_unicorn_test/foo.s b/libafl_unicorn/libafl_unicorn_test/foo.s index 3e001e9ded..1e87df754e 100644 --- a/libafl_unicorn/libafl_unicorn_test/foo.s +++ b/libafl_unicorn/libafl_unicorn_test/foo.s @@ -20,15 +20,21 @@ main: strb w0, [sp, 15] ldrb w0, [sp, 13] and w0, w0, 255 - cmp w0, 20 + cmp w0, 35 bls .L3 mov w0, 2 strb w0, [sp, 15] ldrb w0, [sp, 13] and w0, w0, 255 - cmp w0, 50 + cmp w0, 80 beq .L7 .L3: + mov w0, -34 + strb w0, [sp, 13] + mov w0, -22 + strb w0, [sp, 14] + mov w0, -66 + strb w0, [sp, 15] ldrb w0, [sp, 15] add sp, sp, 16 .cfi_remember_state @@ -41,7 +47,7 @@ main: strb w0, [sp, 15] ldrb w0, [sp, 14] and w0, w0, 255 - cmp w0, 24 + cmp w0, 36 bne .L3 mov w0, 4 strb w0, [sp, 15] diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index b6b0c18e7d..998adcf918 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -2,7 +2,7 @@ use std::fs::File; use std::io::Read; use unicorn_engine::unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}; -use unicorn_engine::{RegisterARM64}; +use unicorn_engine::RegisterARM64; fn callback( _unicorn: &mut unicorn_engine::Unicorn<()>, @@ -11,14 +11,31 @@ fn callback( size: usize, other_number: i64, ) -> bool { - println!("Memory access type: {:?} number: {} size: {} other_number: {}", mem, number, size, other_number); + println!( + "Memory access type: {:?} number: {} size: {} other_number: {}", + mem, number, size, other_number + ); return true; } +fn memory_dump(emu: &mut unicorn_engine::Unicorn<()>, len: u64) { + let pc = emu.reg_read(RegisterARM64::SP).unwrap(); + for i in 0..len { + let pos = pc - len * 4 + i * 4; + + let data = emu.mem_read_as_vec(pos, 4).unwrap(); + + println!( + "{:X}:\t {:02X} {:02X} {:02X} {:02X} {:08b} {:08b} {:08b} {:08b}", + pos, data[0], data[1], data[2], data[3], data[0], data[1], data[2], data[3] + ); + } +} + fn emulate() { - let address = 0x1000; - let r_sp = 0x8000; - let data_size = 0x1000; + let address: u64 = 0x1000; + let r_sp: u64 = 0x8000; + let data_size: usize = 0x100; let mut f = File::open("libafl_unicorn_test/a.out").expect("Could not open file"); let mut buffer = Vec::new(); @@ -35,26 +52,43 @@ fn emulate() { .expect("failed to initialize Unicorn instance"); // Define memory regions + emu.mem_map( address, ((arm_code.len() / 1024) + 1) * 1024, - Permission::ALL, + Permission::EXEC, ) .expect("failed to map code page"); - emu.mem_map(r_sp, data_size * 8, Permission::ALL) - .expect("failed to map data page"); + + // TODO: For some reason, the compiled program start by substracting 0x10 to SP + println!( + "Registering memory from {:#X} to {:#X} size: {:} ", + r_sp - (data_size as u64) * 8, + r_sp, + data_size * 8 + ); + emu.mem_map( + r_sp - (data_size as u64) * 8, + data_size * 8, + Permission::ALL, + ) + .expect("failed to map data page"); // Write memory emu.mem_write(address, &arm_code) .expect("failed to write instructions"); - emu.mem_write(r_sp, &[0x2, 0x0]) - .expect("failed to write instructions"); // Set registry // TODO: For some reason, the compiled program start by substracting 0x10 to SP - emu.reg_write(RegisterARM64::SP, r_sp + 0x10) + emu.reg_write(RegisterARM64::SP, r_sp) .expect("Could not set registery"); + // TODO specific values + emu.mem_write(r_sp - 0x10, &[0x50, 0x24]) + .expect("failed to write instructions"); + + memory_dump(&mut emu, 5); + // Add me mory hook emu.add_mem_hook( HookType::MEM_WRITE_UNMAPPED, @@ -64,7 +98,6 @@ fn emulate() { ) .expect("Failed to register watcher"); - emu.add_mem_hook( HookType::MEM_READ, r_sp, @@ -101,6 +134,8 @@ fn emulate() { if emu.pc_read().unwrap() == 0 { println!("Reached start"); println!("Execution successfull ?"); + + memory_dump(&mut emu, 5); } else { println!(); println!("Snap... something went wrong"); @@ -111,7 +146,6 @@ fn emulate() { println!("Status when crash happened"); println!("PC: {:X}", pc); - println!("PC: {:X}", emu.reg_read(RegisterARM64::PC).unwrap()); println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); println!("X0: {:X}", emu.reg_read(RegisterARM64::X0).unwrap()); println!("X1: {:X}", emu.reg_read(RegisterARM64::X1).unwrap()); From 7307b5bec3cd06a0514c204f8c76ee80b76f4f46 Mon Sep 17 00:00:00 2001 From: henri2h Date: Wed, 21 Dec 2022 18:08:03 +0100 Subject: [PATCH 06/31] feat: display block hook and write correctly memory data --- libafl_unicorn/libafl_unicorn_test/foo.s | 8 +------- libafl_unicorn/src/main.rs | 9 ++++++++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/libafl_unicorn/libafl_unicorn_test/foo.s b/libafl_unicorn/libafl_unicorn_test/foo.s index 1e87df754e..c15bc7e723 100644 --- a/libafl_unicorn/libafl_unicorn_test/foo.s +++ b/libafl_unicorn/libafl_unicorn_test/foo.s @@ -20,7 +20,7 @@ main: strb w0, [sp, 15] ldrb w0, [sp, 13] and w0, w0, 255 - cmp w0, 35 + cmp w0, 32 bls .L3 mov w0, 2 strb w0, [sp, 15] @@ -29,12 +29,6 @@ main: cmp w0, 80 beq .L7 .L3: - mov w0, -34 - strb w0, [sp, 13] - mov w0, -22 - strb w0, [sp, 14] - mov w0, -66 - strb w0, [sp, 15] ldrb w0, [sp, 15] add sp, sp, 16 .cfi_remember_state diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index 998adcf918..5bbde5e0f2 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -32,6 +32,10 @@ fn memory_dump(emu: &mut unicorn_engine::Unicorn<()>, len: u64) { } } +fn block_hook(emu: &mut unicorn_engine::Unicorn<()>, data:u64, small:u32){ + println!("Block hook: {:X} {}", data, small); +} + fn emulate() { let address: u64 = 0x1000; let r_sp: u64 = 0x8000; @@ -84,7 +88,8 @@ fn emulate() { .expect("Could not set registery"); // TODO specific values - emu.mem_write(r_sp - 0x10, &[0x50, 0x24]) + let mem_data = [0x50, 0x20, 0x0]; + emu.mem_write(r_sp-(mem_data.len() as u64), &mem_data) .expect("failed to write instructions"); memory_dump(&mut emu, 5); @@ -114,6 +119,8 @@ fn emulate() { ) .expect("Failed to register watcher"); + emu.add_block_hook(block_hook).expect("Failed to register code hook"); + println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); let result = emu.emu_start( From 579295d627168ed494bac6c3756c07b268bd6e1d Mon Sep 17 00:00:00 2001 From: henri2h Date: Wed, 21 Dec 2022 18:22:08 +0100 Subject: [PATCH 07/31] fix: properly hook and display memory reads --- libafl_unicorn/src/main.rs | 57 ++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index 5bbde5e0f2..34af46279a 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -7,14 +7,26 @@ use unicorn_engine::RegisterARM64; fn callback( _unicorn: &mut unicorn_engine::Unicorn<()>, mem: MemType, - number: u64, + address: u64, size: usize, - other_number: i64, + value: i64, ) -> bool { - println!( - "Memory access type: {:?} number: {} size: {} other_number: {}", - mem, number, size, other_number - ); + match mem { + MemType::WRITE => println!( + "Memory is being WRITTEN at adress: {:X} size: {} value: {}", + address, size, value + ), + MemType::READ => println!( + "Memory is being READ at adress: {:X} size: {}", + address, size + ), + + _ => println!( + "Memory access type: {:?} adress: {:X} size: {} value: {}", + mem, address, size, value + ), + } + return true; } @@ -22,7 +34,7 @@ fn memory_dump(emu: &mut unicorn_engine::Unicorn<()>, len: u64) { let pc = emu.reg_read(RegisterARM64::SP).unwrap(); for i in 0..len { let pos = pc - len * 4 + i * 4; - + let data = emu.mem_read_as_vec(pos, 4).unwrap(); println!( @@ -32,7 +44,7 @@ fn memory_dump(emu: &mut unicorn_engine::Unicorn<()>, len: u64) { } } -fn block_hook(emu: &mut unicorn_engine::Unicorn<()>, data:u64, small:u32){ +fn block_hook(emu: &mut unicorn_engine::Unicorn<()>, data: u64, small: u32) { println!("Block hook: {:X} {}", data, small); } @@ -89,37 +101,22 @@ fn emulate() { // TODO specific values let mem_data = [0x50, 0x20, 0x0]; - emu.mem_write(r_sp-(mem_data.len() as u64), &mem_data) + emu.mem_write(r_sp - (mem_data.len() as u64), &mem_data) .expect("failed to write instructions"); - memory_dump(&mut emu, 5); + memory_dump(&mut emu, 2); // Add me mory hook emu.add_mem_hook( - HookType::MEM_WRITE_UNMAPPED, - r_sp, - r_sp + (data_size) as u64, - callback, - ) - .expect("Failed to register watcher"); - - emu.add_mem_hook( - HookType::MEM_READ, - r_sp, - r_sp + (data_size) as u64, - callback, - ) - .expect("Failed to register watcher"); - - emu.add_mem_hook( - HookType::MEM_FETCH, + HookType::MEM_ALL, + r_sp - (data_size) as u64, r_sp, - r_sp + (data_size) as u64, callback, ) .expect("Failed to register watcher"); - emu.add_block_hook(block_hook).expect("Failed to register code hook"); + emu.add_block_hook(block_hook) + .expect("Failed to register code hook"); println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); @@ -142,7 +139,7 @@ fn emulate() { println!("Reached start"); println!("Execution successfull ?"); - memory_dump(&mut emu, 5); + memory_dump(&mut emu, 2); } else { println!(); println!("Snap... something went wrong"); From 2a57bf17fcb789aeaf2c24f4fdfcec3db75aab16 Mon Sep 17 00:00:00 2001 From: henri2h Date: Wed, 4 Jan 2023 00:09:25 +0100 Subject: [PATCH 08/31] feat: added location tracker --- libafl_unicorn/src/main.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index 34af46279a..ec55b7a592 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -4,6 +4,8 @@ use std::io::Read; use unicorn_engine::unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}; use unicorn_engine::RegisterARM64; +static mut PREV_LOC: u64 = 0; + fn callback( _unicorn: &mut unicorn_engine::Unicorn<()>, mem: MemType, @@ -44,8 +46,12 @@ fn memory_dump(emu: &mut unicorn_engine::Unicorn<()>, len: u64) { } } -fn block_hook(emu: &mut unicorn_engine::Unicorn<()>, data: u64, small: u32) { - println!("Block hook: {:X} {}", data, small); +fn block_hook(emu: &mut unicorn_engine::Unicorn<()>, address: u64, small: u32) { + println!("Block hook: address: {:X} {}", address, small); + + unsafe { + PREV_LOC = address; + } } fn emulate() { @@ -107,13 +113,8 @@ fn emulate() { memory_dump(&mut emu, 2); // Add me mory hook - emu.add_mem_hook( - HookType::MEM_ALL, - r_sp - (data_size) as u64, - r_sp, - callback, - ) - .expect("Failed to register watcher"); + emu.add_mem_hook(HookType::MEM_ALL, r_sp - (data_size) as u64, r_sp, callback) + .expect("Failed to register watcher"); emu.add_block_hook(block_hook) .expect("Failed to register code hook"); From ad3e69ecbdda1b1f0b1046582accaea190251e02 Mon Sep 17 00:00:00 2001 From: henri2h Date: Sun, 8 Jan 2023 21:36:22 +0100 Subject: [PATCH 09/31] =?UTF-8?q?feat:=C2=A0added=20edge=20coverage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libafl_unicorn/Cargo.toml | 5 +++-- libafl_unicorn/src/helper.rs | 24 ++++++++++++++++++++++++ libafl_unicorn/src/hooks.rs | 24 ++++++++++++++++++++++++ libafl_unicorn/src/main.rs | 35 +++++++++++++---------------------- 4 files changed, 64 insertions(+), 24 deletions(-) create mode 100644 libafl_unicorn/src/helper.rs create mode 100644 libafl_unicorn/src/hooks.rs diff --git a/libafl_unicorn/Cargo.toml b/libafl_unicorn/Cargo.toml index 1bd4426252..dda922f00a 100644 --- a/libafl_unicorn/Cargo.toml +++ b/libafl_unicorn/Cargo.toml @@ -12,7 +12,8 @@ edition = "2021" categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"] [dependencies] -#libafl = { path = "../libafl", default-features = false, version = "0.8.2", features = ["std", "libafl_derive", "frida_cli"] } -#libafl_targets = { path = "../libafl_targets", version = "0.8.2", features = ["std", "sancov_cmplog"] } +libafl = { path = "../libafl", version = "0.8.2", default-features = false, features = ["std", "derive", "llmp_compression"] } +libafl_targets = { path = "../libafl_targets", version = "0.8.2" } +hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible unicorn-engine = "2.0.1" diff --git a/libafl_unicorn/src/helper.rs b/libafl_unicorn/src/helper.rs new file mode 100644 index 0000000000..066a043d5b --- /dev/null +++ b/libafl_unicorn/src/helper.rs @@ -0,0 +1,24 @@ +use unicorn_engine::unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}; +use unicorn_engine::RegisterARM64; + +#[must_use] +pub fn hash_me(mut x: u64) -> u64 { + x = (x.overflowing_shr(16).0 ^ x).overflowing_mul(0x45d9f3b).0; + x = (x.overflowing_shr(16).0 ^ x).overflowing_mul(0x45d9f3b).0; + x = (x.overflowing_shr(16).0 ^ x) ^ x; + x +} + +pub fn memory_dump(emu: &mut unicorn_engine::Unicorn<()>, len: u64) { + let pc = emu.reg_read(RegisterARM64::SP).unwrap(); + for i in 0..len { + let pos = pc - len * 4 + i * 4; + + let data = emu.mem_read_as_vec(pos, 4).unwrap(); + + println!( + "{:X}:\t {:02X} {:02X} {:02X} {:02X} {:08b} {:08b} {:08b} {:08b}", + pos, data[0], data[1], data[2], data[3], data[0], data[1], data[2], data[3] + ); + } +} diff --git a/libafl_unicorn/src/hooks.rs b/libafl_unicorn/src/hooks.rs new file mode 100644 index 0000000000..bf2626af2e --- /dev/null +++ b/libafl_unicorn/src/hooks.rs @@ -0,0 +1,24 @@ +use unicorn_engine::unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}; +use unicorn_engine::RegisterARM64; + +use std::{cell::UnsafeCell, cmp::max}; + +use hashbrown::{hash_map::Entry, HashMap}; +use libafl::{inputs::UsesInput, state::HasMetadata}; + +use crate::helper::hash_me; + +pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; + +static mut PREV_LOC: u64 = 0; + +pub fn block_hook(emu: &mut unicorn_engine::Unicorn<()>, address: u64, small: u32) { + println!("Block hook: address: {:X} {}", address, small); + + unsafe { + let hash = address ^ PREV_LOC; + // println!("Hash {}", hash); + EDGES_MAP[hash as usize] += 1; + PREV_LOC = address >> 1; + } +} diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index ec55b7a592..cb9e8d4953 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -1,10 +1,21 @@ +pub mod helper; +pub mod hooks; + use std::fs::File; use std::io::Read; use unicorn_engine::unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}; use unicorn_engine::RegisterARM64; -static mut PREV_LOC: u64 = 0; +use std::{cell::UnsafeCell, cmp::max}; + +use hashbrown::{hash_map::Entry, HashMap}; +use libafl::{inputs::UsesInput, state::HasMetadata}; + +pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; + +use crate::helper::{hash_me, memory_dump}; +use crate::hooks::block_hook; fn callback( _unicorn: &mut unicorn_engine::Unicorn<()>, @@ -32,27 +43,7 @@ fn callback( return true; } -fn memory_dump(emu: &mut unicorn_engine::Unicorn<()>, len: u64) { - let pc = emu.reg_read(RegisterARM64::SP).unwrap(); - for i in 0..len { - let pos = pc - len * 4 + i * 4; - - let data = emu.mem_read_as_vec(pos, 4).unwrap(); - - println!( - "{:X}:\t {:02X} {:02X} {:02X} {:02X} {:08b} {:08b} {:08b} {:08b}", - pos, data[0], data[1], data[2], data[3], data[0], data[1], data[2], data[3] - ); - } -} - -fn block_hook(emu: &mut unicorn_engine::Unicorn<()>, address: u64, small: u32) { - println!("Block hook: address: {:X} {}", address, small); - - unsafe { - PREV_LOC = address; - } -} +// emulating fn emulate() { let address: u64 = 0x1000; From 6a1a60c6a00c4ad2dcd5b31ab4698b8b257fe13b Mon Sep 17 00:00:00 2001 From: henri2h Date: Sun, 8 Jan 2023 23:47:37 +0100 Subject: [PATCH 10/31] feat: startingq hooking the fuzzer --- libafl_unicorn/src/emu.rs | 306 +++++++++++++++++++++++++++++++++++++ libafl_unicorn/src/main.rs | 276 ++++++++++++++++----------------- 2 files changed, 444 insertions(+), 138 deletions(-) create mode 100644 libafl_unicorn/src/emu.rs diff --git a/libafl_unicorn/src/emu.rs b/libafl_unicorn/src/emu.rs new file mode 100644 index 0000000000..70d68140d3 --- /dev/null +++ b/libafl_unicorn/src/emu.rs @@ -0,0 +1,306 @@ +use std::fs::File; +use std::io::Read; + +use unicorn_engine::unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}; +use unicorn_engine::RegisterARM64; + +use std::{cell::UnsafeCell, cmp::max}; + +use hashbrown::{hash_map::Entry, HashMap}; +use libafl::{inputs::UsesInput, state::HasMetadata}; + +pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; + +use crate::helper::{hash_me, memory_dump}; +use crate::hooks::block_hook; + +use libafl::{ + bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice}, + corpus::{InMemoryCorpus, OnDiskCorpus}, + events::SimpleEventManager, + executors::{inprocess::InProcessExecutor, ExitKind}, + feedbacks::{CrashFeedback, MaxMapFeedback}, + fuzzer::{Fuzzer, StdFuzzer}, + generators::RandPrintablesGenerator, + inputs::{BytesInput, HasTargetBytes}, + mutators::scheduled::{havoc_mutations, StdScheduledMutator}, + observers::StdMapObserver, + schedulers::QueueScheduler, + stages::mutational::StdMutationalStage, + state::StdState, +}; +use std::path::PathBuf; + +fn callback( + _unicorn: &mut unicorn_engine::Unicorn<()>, + mem: MemType, + address: u64, + size: usize, + value: i64, +) -> bool { + match mem { + MemType::WRITE => println!( + "Memory is being WRITTEN at adress: {:X} size: {} value: {}", + address, size, value + ), + MemType::READ => println!( + "Memory is being READ at adress: {:X} size: {}", + address, size + ), + + _ => println!( + "Memory access type: {:?} adress: {:X} size: {} value: {}", + mem, address, size, value + ), + } + + return true; +} + +fn emulate() { + let address: u64 = 0x1000; + let r_sp: u64 = 0x8000; + let data_size: usize = 0x100; + + let mut f = File::open("libafl_unicorn_test/a.out").expect("Could not open file"); + let mut buffer = Vec::new(); + + // read the whole file + f.read_to_end(&mut buffer).expect("Could not read file"); + + let arm_code = buffer; + + // [0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 + println!("Program length: {}", arm_code.len()); + + let mut emu = unicorn_engine::Unicorn::new(Arch::ARM64, Mode::LITTLE_ENDIAN) + .expect("failed to initialize Unicorn instance"); + + // Define memory regions + + emu.mem_map( + address, + ((arm_code.len() / 1024) + 1) * 1024, + Permission::EXEC, + ) + .expect("failed to map code page"); + + // TODO: For some reason, the compiled program start by substracting 0x10 to SP + println!( + "Registering memory from {:#X} to {:#X} size: {:} ", + r_sp - (data_size as u64) * 8, + r_sp, + data_size * 8 + ); + emu.mem_map( + r_sp - (data_size as u64) * 8, + data_size * 8, + Permission::ALL, + ) + .expect("failed to map data page"); + + // Write memory + emu.mem_write(address, &arm_code) + .expect("failed to write instructions"); + + // Set registry + // TODO: For some reason, the compiled program start by substracting 0x10 to SP + emu.reg_write(RegisterARM64::SP, r_sp) + .expect("Could not set registery"); + + // TODO specific values + let mem_data = [0x50, 0x20, 0x0]; + emu.mem_write(r_sp - (mem_data.len() as u64), &mem_data) + .expect("failed to write instructions"); + + memory_dump(&mut emu, 2); + + // Add me mory hook + emu.add_mem_hook(HookType::MEM_ALL, r_sp - (data_size) as u64, r_sp, callback) + .expect("Failed to register watcher"); + + emu.add_block_hook(block_hook) + .expect("Failed to register code hook"); + + println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); + + let result = emu.emu_start( + address + 0x40, // start at main. Position of main: 0x40 + address + (arm_code.len()) as u64, + 10 * SECOND_SCALE, + 0x1000, + ); + + match result { + Ok(_) => { + println!("Ok"); + + assert_eq!(emu.reg_read(RegisterARM64::X0), Ok(100)); + assert_eq!(emu.reg_read(RegisterARM64::X1), Ok(1337)); + } + Err(err) => { + if emu.pc_read().unwrap() == 0 { + println!("Reached start"); + println!("Execution successfull ?"); + + memory_dump(&mut emu, 2); + } else { + println!(); + println!("Snap... something went wrong"); + println!("Error: {:?}", err); + + let pc = emu.pc_read().unwrap(); + println!(); + println!("Status when crash happened"); + + println!("PC: {:X}", pc); + println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); + println!("X0: {:X}", emu.reg_read(RegisterARM64::X0).unwrap()); + println!("X1: {:X}", emu.reg_read(RegisterARM64::X1).unwrap()); + println!("X2: {:X}", emu.reg_read(RegisterARM64::X2).unwrap()); + println!("X3: {:X}", emu.reg_read(RegisterARM64::X3).unwrap()); + + println!(); + for i in 0..10 { + let pos = i * 4 + pc - 4 * 5; // Instruction are on 4 bytes + let dec = pos as i64 - pc as i64; + + let read_result = emu.mem_read_as_vec(pos, 4); + match read_result { + Ok(data) => { + println!("{:X}: {:03}:\t {:02X} {:02X} {:02X} {:02X} {:08b} {:08b} {:08b} {:08b}", pos, dec, data[0], data[1], data[2], data[3], data[0], data[1], data[2], data[3]); + } + Err(_) => {} + } + } + } + } + } +} + +// The closure that we want to fuzz +pub fn harness(input: &BytesInput) -> ExitKind { + let target = input.target_bytes(); + let buf = target.as_slice(); + + let address: u64 = 0x1000; + let r_sp: u64 = 0x8000; + let data_size: usize = 0x100; + + let mut f = File::open("libafl_unicorn_test/a.out").expect("Could not open file"); + let mut buffer = Vec::new(); + + // read the whole file + f.read_to_end(&mut buffer).expect("Could not read file"); + + let arm_code = buffer; + + // [0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 + println!("Program length: {}", arm_code.len()); + + let mut emu = unicorn_engine::Unicorn::new(Arch::ARM64, Mode::LITTLE_ENDIAN) + .expect("failed to initialize Unicorn instance"); + + // Define memory regions + + emu.mem_map( + address, + ((arm_code.len() / 1024) + 1) * 1024, + Permission::EXEC, + ) + .expect("failed to map code page"); + + // TODO: For some reason, the compiled program start by substracting 0x10 to SP + println!( + "Registering memory from {:#X} to {:#X} size: {:} ", + r_sp - (data_size as u64) * 8, + r_sp, + data_size * 8 + ); + emu.mem_map( + r_sp - (data_size as u64) * 8, + data_size * 8, + Permission::ALL, + ) + .expect("failed to map data page"); + + // Write memory + emu.mem_write(address, &arm_code) + .expect("failed to write instructions"); + + // Set registry + // TODO: For some reason, the compiled program start by substracting 0x10 to SP + emu.reg_write(RegisterARM64::SP, r_sp) + .expect("Could not set registery"); + + // TODO specific values + let mem_data = buf; + emu.mem_write(r_sp - (mem_data.len() as u64), &mem_data) + .expect("failed to write instructions"); + + memory_dump(&mut emu, 2); + + // Add me mory hook + emu.add_mem_hook(HookType::MEM_ALL, r_sp - (data_size) as u64, r_sp, callback) + .expect("Failed to register watcher"); + + emu.add_block_hook(block_hook) + .expect("Failed to register code hook"); + + println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); + + let result = emu.emu_start( + address + 0x40, // start at main. Position of main: 0x40 + address + (arm_code.len()) as u64, + 10 * SECOND_SCALE, + 0x1000, + ); + + match result { + Ok(_) => { + println!("Ok"); + + assert_eq!(emu.reg_read(RegisterARM64::X0), Ok(100)); + assert_eq!(emu.reg_read(RegisterARM64::X1), Ok(1337)); + } + Err(err) => { + if emu.pc_read().unwrap() == 0 { + println!("Reached start"); + println!("Execution successfull ?"); + + memory_dump(&mut emu, 2); + } else { + println!(); + println!("Snap... something went wrong"); + println!("Error: {:?}", err); + + let pc = emu.pc_read().unwrap(); + println!(); + println!("Status when crash happened"); + + println!("PC: {:X}", pc); + println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); + println!("X0: {:X}", emu.reg_read(RegisterARM64::X0).unwrap()); + println!("X1: {:X}", emu.reg_read(RegisterARM64::X1).unwrap()); + println!("X2: {:X}", emu.reg_read(RegisterARM64::X2).unwrap()); + println!("X3: {:X}", emu.reg_read(RegisterARM64::X3).unwrap()); + + println!(); + for i in 0..10 { + let pos = i * 4 + pc - 4 * 5; // Instruction are on 4 bytes + let dec = pos as i64 - pc as i64; + + let read_result = emu.mem_read_as_vec(pos, 4); + match read_result { + Ok(data) => { + println!("{:X}: {:03}:\t {:02X} {:02X} {:02X} {:02X} {:08b} {:08b} {:08b} {:08b}", pos, dec, data[0], data[1], data[2], data[3], data[0], data[1], data[2], data[3]); + } + Err(_) => {} + } + } + } + } + } + + ExitKind::Ok +} diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index cb9e8d4953..a8505845c9 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -1,3 +1,4 @@ +pub mod emu; pub mod helper; pub mod hooks; @@ -14,158 +15,157 @@ use libafl::{inputs::UsesInput, state::HasMetadata}; pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; +use crate::emu::harness; use crate::helper::{hash_me, memory_dump}; use crate::hooks::block_hook; -fn callback( - _unicorn: &mut unicorn_engine::Unicorn<()>, - mem: MemType, - address: u64, - size: usize, - value: i64, -) -> bool { - match mem { - MemType::WRITE => println!( - "Memory is being WRITTEN at adress: {:X} size: {} value: {}", - address, size, value - ), - MemType::READ => println!( - "Memory is being READ at adress: {:X} size: {}", - address, size - ), - - _ => println!( - "Memory access type: {:?} adress: {:X} size: {} value: {}", - mem, address, size, value - ), - } - - return true; -} +use std::path::PathBuf; +#[cfg(windows)] +use std::ptr::write_volatile; + +#[cfg(feature = "tui")] +use libafl::monitors::tui::TuiMonitor; +#[cfg(not(feature = "tui"))] +use libafl::monitors::SimpleMonitor; +use libafl::{ + bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice}, + corpus::{InMemoryCorpus, OnDiskCorpus}, + events::SimpleEventManager, + corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus}, + executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + feedback_or, feedback_or_fast, + feedbacks::{CrashFeedback, MaxMapFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, + fuzzer::{Fuzzer, StdFuzzer}, + generators::{RandPrintablesGenerator, RandBytesGenerator}, + inputs::{BytesInput, HasTargetBytes}, + mutators::scheduled::{havoc_mutations, StdScheduledMutator}, + monitors::MultiMonitor, + observers::{HitcountsMapObserver, TimeObserver, VariableMapObserver}, + schedulers::{QueueScheduler, IndexesLenTimeMinimizerScheduler}, + stages::mutational::StdMutationalStage, + state::StdState, +}; // emulating -fn emulate() { - let address: u64 = 0x1000; - let r_sp: u64 = 0x8000; - let data_size: usize = 0x100; - - let mut f = File::open("libafl_unicorn_test/a.out").expect("Could not open file"); - let mut buffer = Vec::new(); - - // read the whole file - f.read_to_end(&mut buffer).expect("Could not read file"); - - let arm_code = buffer; - - // [0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 - println!("Program length: {}", arm_code.len()); - - let mut emu = unicorn_engine::Unicorn::new(Arch::ARM64, Mode::LITTLE_ENDIAN) - .expect("failed to initialize Unicorn instance"); - - // Define memory regions +fn main() { - emu.mem_map( - address, - ((arm_code.len() / 1024) + 1) * 1024, - Permission::EXEC, - ) - .expect("failed to map code page"); - - // TODO: For some reason, the compiled program start by substracting 0x10 to SP - println!( - "Registering memory from {:#X} to {:#X} size: {:} ", - r_sp - (data_size as u64) * 8, - r_sp, - data_size * 8 + 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 = unsafe { &mut hooks::EDGES_MAP }; + let edges_counter = unsafe { &mut hooks::MAX_EDGES_NUM }; + let edges_observer = + HitcountsMapObserver::new(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, false), + // Time feedback, this one does not need a feedback state + TimeFeedback::new_with_observer(&time_observer) ); - emu.mem_map( - r_sp - (data_size as u64) * 8, - data_size * 8, - Permission::ALL, - ) - .expect("failed to map data page"); - - // Write memory - emu.mem_write(address, &arm_code) - .expect("failed to write instructions"); - - // Set registry - // TODO: For some reason, the compiled program start by substracting 0x10 to SP - emu.reg_write(RegisterARM64::SP, r_sp) - .expect("Could not set registery"); - // TODO specific values - let mem_data = [0x50, 0x20, 0x0]; - emu.mem_write(r_sp - (mem_data.len() as u64), &mem_data) - .expect("failed to write instructions"); + // 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 mut hooks = + QemuHooks::new(emulator, tuple_list!(QemuEdgeCoverageHelper::default())); + +let executor = QemuExecutor::new( + &mut hooks, + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut mgr, +)?; + // In case the corpus is empty (on first run), reset + if state.corpus().count() < 1 { + if self.input_dirs.is_empty() { + // Generator of printable bytearrays of max size 32 + let mut generator = RandBytesGenerator::new(32); + + // Generate 8 initial inputs + state + .generate_initial_inputs( + &mut fuzzer, + &mut executor, + &mut generator, + &mut mgr, + 8, + ) + .expect("Failed to generate the initial corpus"); + println!( + "We imported {} inputs from the generator.", + state.corpus().count() + ); + } else { + println!("Loading from {:?}", &self.input_dirs); + // Load from disk + state + .load_initial_inputs( + &mut fuzzer, + &mut executor, + &mut mgr, + self.input_dirs, + ) + .unwrap_or_else(|_| { + panic!("Failed to load initial corpus at {:?}", &self.input_dirs); + }); + println!("We imported {} inputs from disk.", state.corpus().count()); + } +} - memory_dump(&mut emu, 2); +let mut executor = TimeoutExecutor::new(executor, timeout); - // Add me mory hook - emu.add_mem_hook(HookType::MEM_ALL, r_sp - (data_size) as u64, r_sp, callback) - .expect("Failed to register watcher"); - emu.add_block_hook(block_hook) - .expect("Failed to register code hook"); + let mut executor = TimeoutExecutor::new(executor, timeout); - println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); + // Generator of printable bytearrays of max size 32 + let mut generator = RandPrintablesGenerator::new(32); - let result = emu.emu_start( - address + 0x40, // start at main. Position of main: 0x40 - address + (arm_code.len()) as u64, - 10 * SECOND_SCALE, - 0x1000, - ); + // Generate 8 initial inputs + state + .generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8) + .expect("Failed to generate the initial corpus"); - match result { - Ok(_) => { - println!("Ok"); - - assert_eq!(emu.reg_read(RegisterARM64::X0), Ok(100)); - assert_eq!(emu.reg_read(RegisterARM64::X1), Ok(1337)); - } - Err(err) => { - if emu.pc_read().unwrap() == 0 { - println!("Reached start"); - println!("Execution successfull ?"); - - memory_dump(&mut emu, 2); - } else { - println!(); - println!("Snap... something went wrong"); - println!("Error: {:?}", err); - - let pc = emu.pc_read().unwrap(); - println!(); - println!("Status when crash happened"); - - println!("PC: {:X}", pc); - println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); - println!("X0: {:X}", emu.reg_read(RegisterARM64::X0).unwrap()); - println!("X1: {:X}", emu.reg_read(RegisterARM64::X1).unwrap()); - println!("X2: {:X}", emu.reg_read(RegisterARM64::X2).unwrap()); - println!("X3: {:X}", emu.reg_read(RegisterARM64::X3).unwrap()); - - println!(); - for i in 0..10 { - let pos = i * 4 + pc - 4 * 5; // Instruction are on 4 bytes - let dec = pos as i64 - pc as i64; - - let read_result = emu.mem_read_as_vec(pos, 4); - match read_result { - Ok(data) => { - println!("{:X}: {:03}:\t {:02X} {:02X} {:02X} {:02X} {:08b} {:08b} {:08b} {:08b}", pos, dec, data[0], data[1], data[2], data[3], data[0], data[1], data[2], data[3]); - } - Err(_) => {} - } - } - } - } - } -} + // Setup a mutational stage with a basic bytes mutator + let mutator = StdScheduledMutator::new(havoc_mutations()); + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); -fn main() { - emulate(); + fuzzer + .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) + .expect("Error in the fuzzing loop"); } From 79e6ddf884ac2d100ae13f297e93dcbfc85c5ccb Mon Sep 17 00:00:00 2001 From: henri2h Date: Mon, 9 Jan 2023 20:59:55 +0100 Subject: [PATCH 11/31] feat: discard state --- libafl_unicorn/src/main.rs | 68 ++++++-------------------------------- 1 file changed, 11 insertions(+), 57 deletions(-) diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index a8505845c9..bb410521ea 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -29,19 +29,19 @@ use libafl::monitors::tui::TuiMonitor; use libafl::monitors::SimpleMonitor; use libafl::{ bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice}, + corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus}, corpus::{InMemoryCorpus, OnDiskCorpus}, events::SimpleEventManager, - corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus}, executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, - generators::{RandPrintablesGenerator, RandBytesGenerator}, + generators::{RandBytesGenerator, RandPrintablesGenerator}, inputs::{BytesInput, HasTargetBytes}, - mutators::scheduled::{havoc_mutations, StdScheduledMutator}, monitors::MultiMonitor, + mutators::scheduled::{havoc_mutations, StdScheduledMutator}, observers::{HitcountsMapObserver, TimeObserver, VariableMapObserver}, - schedulers::{QueueScheduler, IndexesLenTimeMinimizerScheduler}, + schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::mutational::StdMutationalStage, state::StdState, }; @@ -49,7 +49,6 @@ use libafl::{ // emulating fn main() { - 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 @@ -92,64 +91,19 @@ fn main() { ) .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(emulator, tuple_list!(QemuEdgeCoverageHelper::default())); - -let executor = QemuExecutor::new( - &mut hooks, - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut mgr, -)?; - // In case the corpus is empty (on first run), reset - if state.corpus().count() < 1 { - if self.input_dirs.is_empty() { - // Generator of printable bytearrays of max size 32 - let mut generator = RandBytesGenerator::new(32); - - // Generate 8 initial inputs - state - .generate_initial_inputs( - &mut fuzzer, - &mut executor, - &mut generator, - &mut mgr, - 8, - ) - .expect("Failed to generate the initial corpus"); - println!( - "We imported {} inputs from the generator.", - state.corpus().count() - ); - } else { - println!("Loading from {:?}", &self.input_dirs); - // Load from disk - state - .load_initial_inputs( - &mut fuzzer, - &mut executor, - &mut mgr, - self.input_dirs, - ) - .unwrap_or_else(|_| { - panic!("Failed to load initial corpus at {:?}", &self.input_dirs); - }); - println!("We imported {} inputs from disk.", state.corpus().count()); - } -} - -let mut executor = TimeoutExecutor::new(executor, timeout); - + let executor = InProcessExecutor::new( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut mgr, + ); let mut executor = TimeoutExecutor::new(executor, timeout); From 022584a53e220b9015f7338218a65ae57ab62954 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Tue, 10 Jan 2023 16:15:35 +0100 Subject: [PATCH 12/31] Fix compilation --- libafl_unicorn/src/emu.rs | 30 ++++++++---------- libafl_unicorn/src/helper.rs | 6 ++-- libafl_unicorn/src/hooks.rs | 10 +++--- libafl_unicorn/src/main.rs | 59 +++++++++++++++++++----------------- 4 files changed, 53 insertions(+), 52 deletions(-) diff --git a/libafl_unicorn/src/emu.rs b/libafl_unicorn/src/emu.rs index 70d68140d3..b73d37caa4 100644 --- a/libafl_unicorn/src/emu.rs +++ b/libafl_unicorn/src/emu.rs @@ -1,19 +1,6 @@ -use std::fs::File; -use std::io::Read; - -use unicorn_engine::unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}; -use unicorn_engine::RegisterARM64; - -use std::{cell::UnsafeCell, cmp::max}; +use std::{cell::UnsafeCell, cmp::max, fs::File, io::Read, path::PathBuf}; use hashbrown::{hash_map::Entry, HashMap}; -use libafl::{inputs::UsesInput, state::HasMetadata}; - -pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; - -use crate::helper::{hash_me, memory_dump}; -use crate::hooks::block_hook; - use libafl::{ bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice}, corpus::{InMemoryCorpus, OnDiskCorpus}, @@ -22,14 +9,23 @@ use libafl::{ feedbacks::{CrashFeedback, MaxMapFeedback}, fuzzer::{Fuzzer, StdFuzzer}, generators::RandPrintablesGenerator, - inputs::{BytesInput, HasTargetBytes}, + inputs::{BytesInput, HasTargetBytes, UsesInput}, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, observers::StdMapObserver, schedulers::QueueScheduler, stages::mutational::StdMutationalStage, - state::StdState, + state::{HasMetadata, StdState}, +}; +pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; +use unicorn_engine::{ + unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}, + RegisterARM64, +}; + +use crate::{ + helper::{hash_me, memory_dump}, + hooks::block_hook, }; -use std::path::PathBuf; fn callback( _unicorn: &mut unicorn_engine::Unicorn<()>, diff --git a/libafl_unicorn/src/helper.rs b/libafl_unicorn/src/helper.rs index 066a043d5b..54a926f139 100644 --- a/libafl_unicorn/src/helper.rs +++ b/libafl_unicorn/src/helper.rs @@ -1,5 +1,7 @@ -use unicorn_engine::unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}; -use unicorn_engine::RegisterARM64; +use unicorn_engine::{ + unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}, + RegisterARM64, +}; #[must_use] pub fn hash_me(mut x: u64) -> u64 { diff --git a/libafl_unicorn/src/hooks.rs b/libafl_unicorn/src/hooks.rs index bf2626af2e..205ed5fb7d 100644 --- a/libafl_unicorn/src/hooks.rs +++ b/libafl_unicorn/src/hooks.rs @@ -1,15 +1,15 @@ -use unicorn_engine::unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}; -use unicorn_engine::RegisterARM64; - use std::{cell::UnsafeCell, cmp::max}; use hashbrown::{hash_map::Entry, HashMap}; use libafl::{inputs::UsesInput, state::HasMetadata}; +pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; +use unicorn_engine::{ + unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}, + RegisterARM64, +}; use crate::helper::hash_me; -pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; - static mut PREV_LOC: u64 = 0; pub fn block_hook(emu: &mut unicorn_engine::Unicorn<()>, address: u64, small: u32) { diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index bb410521ea..641bc3ba6e 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -2,62 +2,63 @@ pub mod emu; pub mod helper; pub mod hooks; -use std::fs::File; -use std::io::Read; - -use unicorn_engine::unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}; -use unicorn_engine::RegisterARM64; - -use std::{cell::UnsafeCell, cmp::max}; - -use hashbrown::{hash_map::Entry, HashMap}; -use libafl::{inputs::UsesInput, state::HasMetadata}; - -pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; - -use crate::emu::harness; -use crate::helper::{hash_me, memory_dump}; -use crate::hooks::block_hook; - -use std::path::PathBuf; #[cfg(windows)] use std::ptr::write_volatile; +use std::{ + cell::UnsafeCell, cmp::max, fs::File, io::Read, path::PathBuf, ptr::addr_of_mut, time::Duration, +}; +use hashbrown::{hash_map::Entry, HashMap}; #[cfg(feature = "tui")] use libafl::monitors::tui::TuiMonitor; #[cfg(not(feature = "tui"))] use libafl::monitors::SimpleMonitor; use libafl::{ bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice}, - corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus}, corpus::{InMemoryCorpus, OnDiskCorpus}, events::SimpleEventManager, executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, feedback_or, feedback_or_fast, - feedbacks::{CrashFeedback, MaxMapFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, + feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, generators::{RandBytesGenerator, RandPrintablesGenerator}, - inputs::{BytesInput, HasTargetBytes}, + inputs::{BytesInput, HasTargetBytes, UsesInput}, monitors::MultiMonitor, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, observers::{HitcountsMapObserver, TimeObserver, VariableMapObserver}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::mutational::StdMutationalStage, - state::StdState, + state::{HasMetadata, StdState}, +}; +pub use libafl_targets::{edges_map_mut_slice, MAX_EDGES_NUM}; +use unicorn_engine::{ + unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}, + RegisterARM64, +}; + +use crate::{ + emu::harness, + helper::{hash_me, memory_dump}, + hooks::block_hook, }; // emulating fn main() { + 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 = unsafe { &mut hooks::EDGES_MAP }; - let edges_counter = unsafe { &mut hooks::MAX_EDGES_NUM }; - let edges_observer = - HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter)); + let edges_observer = unsafe { + HitcountsMapObserver::new(VariableMapObserver::from_mut_slice( + "edges", + edges_map_mut_slice(), + addr_of_mut!(MAX_EDGES_NUM), + )) + }; // Create an observation channel to keep track of the execution time let time_observer = TimeObserver::new("time"); @@ -97,13 +98,15 @@ fn main() { // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + let mut binding = harness; // being harness a function we need a local var to bind it let executor = InProcessExecutor::new( - &mut harness, + &mut binding, 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); From e98f753cf46e3584f4e7bcd2b1030111a0a0eb2e Mon Sep 17 00:00:00 2001 From: henri2h Date: Tue, 10 Jan 2023 18:53:40 +0100 Subject: [PATCH 13/31] feat: rewritten the emulator to be less verbose and remove code duplication --- libafl_unicorn/src/emu.rs | 262 ++++++++++++++++++-------------------- 1 file changed, 124 insertions(+), 138 deletions(-) diff --git a/libafl_unicorn/src/emu.rs b/libafl_unicorn/src/emu.rs index b73d37caa4..e08b60882f 100644 --- a/libafl_unicorn/src/emu.rs +++ b/libafl_unicorn/src/emu.rs @@ -1,4 +1,4 @@ -use std::{cell::UnsafeCell, cmp::max, fs::File, io::Read, path::PathBuf}; +use std::{cell::UnsafeCell, cmp::max, error::Error, fs::File, io::Read, path::PathBuf}; use hashbrown::{hash_map::Entry, HashMap}; use libafl::{ @@ -18,15 +18,83 @@ use libafl::{ }; pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; use unicorn_engine::{ - unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}, - RegisterARM64, + unicorn_const::{uc_error, Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}, + RegisterARM64, Unicorn, }; +static debug: bool = false; + use crate::{ helper::{hash_me, memory_dump}, hooks::block_hook, }; +fn load_code(emu: &mut Unicorn<()>, address: u64) -> u64 { + let mut f = File::open("libafl_unicorn_test/a.out").expect("Could not open file"); + let mut buffer = Vec::new(); + + // read the whole file + f.read_to_end(&mut buffer).expect("Could not read file"); + + let arm_code = buffer; + + // Define memory regions + emu.mem_map( + address, + ((arm_code.len() / 1024) + 1) * 1024, + Permission::EXEC, + ) + .expect("failed to map code page"); + + // Write memory + emu.mem_write(address, &arm_code) + .expect("failed to write instructions"); + return arm_code.len() as u64; +} + +fn debug_print(emu: &mut Unicorn<()>, err: uc_error) { + dbg!(); + dbg!("Snap... something went wrong"); + dbg!("Error: {:?}", err); + + let pc = emu.pc_read().unwrap(); + dbg!(); + dbg!("Status when crash happened"); + + dbg!("PC: {:X}", pc); + dbg!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); + dbg!("X0: {:X}", emu.reg_read(RegisterARM64::X0).unwrap()); + dbg!("X1: {:X}", emu.reg_read(RegisterARM64::X1).unwrap()); + dbg!("X2: {:X}", emu.reg_read(RegisterARM64::X2).unwrap()); + dbg!("X3: {:X}", emu.reg_read(RegisterARM64::X3).unwrap()); + + dbg!(); + for i in 0..10 { + let pos = i * 4 + pc - 4 * 5; // Instruction are on 4 bytes + let dec = pos as i64 - pc as i64; + + let read_result = emu.mem_read_as_vec(pos, 4); + match read_result { + Ok(data) => { + dbg!( + "{:X}: {:03}:\t {:02X} {:02X} {:02X} {:02X} {:08b} {:08b} {:08b} {:08b}", + pos, + dec, + data[0], + data[1], + data[2], + data[3], + data[0], + data[1], + data[2], + data[3] + ); + } + Err(_) => {} + } + } +} + fn callback( _unicorn: &mut unicorn_engine::Unicorn<()>, mem: MemType, @@ -34,20 +102,22 @@ fn callback( size: usize, value: i64, ) -> bool { - match mem { - MemType::WRITE => println!( - "Memory is being WRITTEN at adress: {:X} size: {} value: {}", - address, size, value - ), - MemType::READ => println!( - "Memory is being READ at adress: {:X} size: {}", - address, size - ), - - _ => println!( - "Memory access type: {:?} adress: {:X} size: {} value: {}", - mem, address, size, value - ), + if debug { + match mem { + MemType::WRITE => println!( + "Memory is being WRITTEN at adress: {:X} size: {} value: {}", + address, size, value + ), + MemType::READ => println!( + "Memory is being READ at adress: {:X} size: {}", + address, size + ), + + _ => println!( + "Memory access type: {:?} adress: {:X} size: {} value: {}", + mem, address, size, value + ), + } } return true; @@ -58,36 +128,20 @@ fn emulate() { let r_sp: u64 = 0x8000; let data_size: usize = 0x100; - let mut f = File::open("libafl_unicorn_test/a.out").expect("Could not open file"); - let mut buffer = Vec::new(); - - // read the whole file - f.read_to_end(&mut buffer).expect("Could not read file"); - - let arm_code = buffer; - - // [0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 - println!("Program length: {}", arm_code.len()); - let mut emu = unicorn_engine::Unicorn::new(Arch::ARM64, Mode::LITTLE_ENDIAN) .expect("failed to initialize Unicorn instance"); // Define memory regions - emu.mem_map( - address, - ((arm_code.len() / 1024) + 1) * 1024, - Permission::EXEC, - ) - .expect("failed to map code page"); - // TODO: For some reason, the compiled program start by substracting 0x10 to SP - println!( - "Registering memory from {:#X} to {:#X} size: {:} ", - r_sp - (data_size as u64) * 8, - r_sp, - data_size * 8 - ); + if debug { + dbg!( + "Registering memory from {:#X} to {:#X} size: {:} ", + r_sp - (data_size as u64) * 8, + r_sp, + data_size * 8 + ); + } emu.mem_map( r_sp - (data_size as u64) * 8, data_size * 8, @@ -96,8 +150,7 @@ fn emulate() { .expect("failed to map data page"); // Write memory - emu.mem_write(address, &arm_code) - .expect("failed to write instructions"); + let arm_code_len = load_code(&mut emu, address); // Set registry // TODO: For some reason, the compiled program start by substracting 0x10 to SP @@ -109,7 +162,9 @@ fn emulate() { emu.mem_write(r_sp - (mem_data.len() as u64), &mem_data) .expect("failed to write instructions"); - memory_dump(&mut emu, 2); + if debug { + memory_dump(&mut emu, 2); + } // Add me mory hook emu.add_mem_hook(HookType::MEM_ALL, r_sp - (data_size) as u64, r_sp, callback) @@ -118,57 +173,35 @@ fn emulate() { emu.add_block_hook(block_hook) .expect("Failed to register code hook"); - println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); + if debug { + dbg!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); + } let result = emu.emu_start( address + 0x40, // start at main. Position of main: 0x40 - address + (arm_code.len()) as u64, + address + (arm_code_len) as u64, 10 * SECOND_SCALE, 0x1000, ); match result { Ok(_) => { - println!("Ok"); + dbg!("Ok"); assert_eq!(emu.reg_read(RegisterARM64::X0), Ok(100)); assert_eq!(emu.reg_read(RegisterARM64::X1), Ok(1337)); } Err(err) => { if emu.pc_read().unwrap() == 0 { - println!("Reached start"); - println!("Execution successfull ?"); + if debug { + dbg!("Reached start"); + } + + dbg!("Execution successfull ?"); memory_dump(&mut emu, 2); } else { - println!(); - println!("Snap... something went wrong"); - println!("Error: {:?}", err); - - let pc = emu.pc_read().unwrap(); - println!(); - println!("Status when crash happened"); - - println!("PC: {:X}", pc); - println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); - println!("X0: {:X}", emu.reg_read(RegisterARM64::X0).unwrap()); - println!("X1: {:X}", emu.reg_read(RegisterARM64::X1).unwrap()); - println!("X2: {:X}", emu.reg_read(RegisterARM64::X2).unwrap()); - println!("X3: {:X}", emu.reg_read(RegisterARM64::X3).unwrap()); - - println!(); - for i in 0..10 { - let pos = i * 4 + pc - 4 * 5; // Instruction are on 4 bytes - let dec = pos as i64 - pc as i64; - - let read_result = emu.mem_read_as_vec(pos, 4); - match read_result { - Ok(data) => { - println!("{:X}: {:03}:\t {:02X} {:02X} {:02X} {:02X} {:08b} {:08b} {:08b} {:08b}", pos, dec, data[0], data[1], data[2], data[3], data[0], data[1], data[2], data[3]); - } - Err(_) => {} - } - } + debug_print(&mut emu, err); } } } @@ -183,36 +216,12 @@ pub fn harness(input: &BytesInput) -> ExitKind { let r_sp: u64 = 0x8000; let data_size: usize = 0x100; - let mut f = File::open("libafl_unicorn_test/a.out").expect("Could not open file"); - let mut buffer = Vec::new(); - - // read the whole file - f.read_to_end(&mut buffer).expect("Could not read file"); - - let arm_code = buffer; - // [0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 - println!("Program length: {}", arm_code.len()); let mut emu = unicorn_engine::Unicorn::new(Arch::ARM64, Mode::LITTLE_ENDIAN) .expect("failed to initialize Unicorn instance"); - // Define memory regions - - emu.mem_map( - address, - ((arm_code.len() / 1024) + 1) * 1024, - Permission::EXEC, - ) - .expect("failed to map code page"); - // TODO: For some reason, the compiled program start by substracting 0x10 to SP - println!( - "Registering memory from {:#X} to {:#X} size: {:} ", - r_sp - (data_size as u64) * 8, - r_sp, - data_size * 8 - ); emu.mem_map( r_sp - (data_size as u64) * 8, data_size * 8, @@ -220,9 +229,7 @@ pub fn harness(input: &BytesInput) -> ExitKind { ) .expect("failed to map data page"); - // Write memory - emu.mem_write(address, &arm_code) - .expect("failed to write instructions"); + let arm_code_len = load_code(&mut emu, address); // Set registry // TODO: For some reason, the compiled program start by substracting 0x10 to SP @@ -234,7 +241,9 @@ pub fn harness(input: &BytesInput) -> ExitKind { emu.mem_write(r_sp - (mem_data.len() as u64), &mem_data) .expect("failed to write instructions"); - memory_dump(&mut emu, 2); + if debug { + memory_dump(&mut emu, 2); + } // Add me mory hook emu.add_mem_hook(HookType::MEM_ALL, r_sp - (data_size) as u64, r_sp, callback) @@ -243,57 +252,34 @@ pub fn harness(input: &BytesInput) -> ExitKind { emu.add_block_hook(block_hook) .expect("Failed to register code hook"); - println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); + if debug { + dbg!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); + } let result = emu.emu_start( address + 0x40, // start at main. Position of main: 0x40 - address + (arm_code.len()) as u64, + address + arm_code_len, 10 * SECOND_SCALE, 0x1000, ); match result { Ok(_) => { - println!("Ok"); + dbg!("Ok"); assert_eq!(emu.reg_read(RegisterARM64::X0), Ok(100)); assert_eq!(emu.reg_read(RegisterARM64::X1), Ok(1337)); } Err(err) => { if emu.pc_read().unwrap() == 0 { - println!("Reached start"); - println!("Execution successfull ?"); + if debug { + dbg!("Reached start"); + } + dbg!("Execution successfull ?"); memory_dump(&mut emu, 2); } else { - println!(); - println!("Snap... something went wrong"); - println!("Error: {:?}", err); - - let pc = emu.pc_read().unwrap(); - println!(); - println!("Status when crash happened"); - - println!("PC: {:X}", pc); - println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); - println!("X0: {:X}", emu.reg_read(RegisterARM64::X0).unwrap()); - println!("X1: {:X}", emu.reg_read(RegisterARM64::X1).unwrap()); - println!("X2: {:X}", emu.reg_read(RegisterARM64::X2).unwrap()); - println!("X3: {:X}", emu.reg_read(RegisterARM64::X3).unwrap()); - - println!(); - for i in 0..10 { - let pos = i * 4 + pc - 4 * 5; // Instruction are on 4 bytes - let dec = pos as i64 - pc as i64; - - let read_result = emu.mem_read_as_vec(pos, 4); - match read_result { - Ok(data) => { - println!("{:X}: {:03}:\t {:02X} {:02X} {:02X} {:02X} {:08b} {:08b} {:08b} {:08b}", pos, dec, data[0], data[1], data[2], data[3], data[0], data[1], data[2], data[3]); - } - Err(_) => {} - } - } + debug_print(&mut emu, err); } } } From e09dda77fd0d5f8e01ad80d53848f52924a803cd Mon Sep 17 00:00:00 2001 From: henri2h Date: Thu, 19 Jan 2023 02:03:00 +0100 Subject: [PATCH 14/31] feat: reduce code duplication (again) --- libafl_unicorn/src/emu.rs | 86 +++------------------------------------ 1 file changed, 5 insertions(+), 81 deletions(-) diff --git a/libafl_unicorn/src/emu.rs b/libafl_unicorn/src/emu.rs index e08b60882f..3d36b0ba43 100644 --- a/libafl_unicorn/src/emu.rs +++ b/libafl_unicorn/src/emu.rs @@ -123,95 +123,19 @@ fn callback( return true; } -fn emulate() { - let address: u64 = 0x1000; - let r_sp: u64 = 0x8000; - let data_size: usize = 0x100; - - let mut emu = unicorn_engine::Unicorn::new(Arch::ARM64, Mode::LITTLE_ENDIAN) - .expect("failed to initialize Unicorn instance"); - - // Define memory regions - - // TODO: For some reason, the compiled program start by substracting 0x10 to SP - if debug { - dbg!( - "Registering memory from {:#X} to {:#X} size: {:} ", - r_sp - (data_size as u64) * 8, - r_sp, - data_size * 8 - ); - } - emu.mem_map( - r_sp - (data_size as u64) * 8, - data_size * 8, - Permission::ALL, - ) - .expect("failed to map data page"); - - // Write memory - let arm_code_len = load_code(&mut emu, address); - - // Set registry - // TODO: For some reason, the compiled program start by substracting 0x10 to SP - emu.reg_write(RegisterARM64::SP, r_sp) - .expect("Could not set registery"); - - // TODO specific values +pub fn emulate() { let mem_data = [0x50, 0x20, 0x0]; - emu.mem_write(r_sp - (mem_data.len() as u64), &mem_data) - .expect("failed to write instructions"); - - if debug { - memory_dump(&mut emu, 2); - } - - // Add me mory hook - emu.add_mem_hook(HookType::MEM_ALL, r_sp - (data_size) as u64, r_sp, callback) - .expect("Failed to register watcher"); - - emu.add_block_hook(block_hook) - .expect("Failed to register code hook"); - - if debug { - dbg!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); - } - - let result = emu.emu_start( - address + 0x40, // start at main. Position of main: 0x40 - address + (arm_code_len) as u64, - 10 * SECOND_SCALE, - 0x1000, - ); - - match result { - Ok(_) => { - dbg!("Ok"); - - assert_eq!(emu.reg_read(RegisterARM64::X0), Ok(100)); - assert_eq!(emu.reg_read(RegisterARM64::X1), Ok(1337)); - } - Err(err) => { - if emu.pc_read().unwrap() == 0 { - if debug { - dbg!("Reached start"); - } - - dbg!("Execution successfull ?"); - - memory_dump(&mut emu, 2); - } else { - debug_print(&mut emu, err); - } - } - } + prog(&mem_data); } // The closure that we want to fuzz pub fn harness(input: &BytesInput) -> ExitKind { let target = input.target_bytes(); let buf = target.as_slice(); + return prog(buf); +} +fn prog(buf: &[u8]) -> ExitKind { let address: u64 = 0x1000; let r_sp: u64 = 0x8000; let data_size: usize = 0x100; From 9f68628b6354d54fb8a1975ecf7f5a2fd1389590 Mon Sep 17 00:00:00 2001 From: henri2h Date: Thu, 19 Jan 2023 02:03:18 +0100 Subject: [PATCH 15/31] feat: allow to run emulator only --- libafl_unicorn/src/main.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index 641bc3ba6e..9c37b776bb 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -2,6 +2,8 @@ pub mod emu; pub mod helper; pub mod hooks; +use std::env; + #[cfg(windows)] use std::ptr::write_volatile; use std::{ @@ -43,8 +45,7 @@ use crate::{ }; // emulating - -fn main() { +fn runFuzzer() { let timeout = Duration::from_secs(1); let monitor = MultiMonitor::new(|s| println!("{s}")); @@ -126,3 +127,14 @@ fn main() { .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) .expect("Error in the fuzzing loop"); } + +fn main() { + let args: Vec<_> = env::args().collect(); + if args.len() > 1 { + if args[1] == "emu" { + emu::emulate(); + return; + } + } + runFuzzer(); +} From 5da5b93406f7d66178f53cb01f8b13ce59e0eade Mon Sep 17 00:00:00 2001 From: henri2h Date: Thu, 19 Jan 2023 02:14:47 +0100 Subject: [PATCH 16/31] feat: crash on wrong value --- libafl_unicorn/src/emu.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libafl_unicorn/src/emu.rs b/libafl_unicorn/src/emu.rs index 3d36b0ba43..50970a6fc2 100644 --- a/libafl_unicorn/src/emu.rs +++ b/libafl_unicorn/src/emu.rs @@ -124,7 +124,7 @@ fn callback( } pub fn emulate() { - let mem_data = [0x50, 0x20, 0x0]; + let mem_data = [0x50, 0x24, 0x0]; prog(&mem_data); } @@ -202,6 +202,14 @@ fn prog(buf: &[u8]) -> ExitKind { dbg!("Execution successfull ?"); memory_dump(&mut emu, 2); + let mut buf: [u8; 1] = [0]; + + let pc = emu.reg_read(RegisterARM64::SP).unwrap(); + + emu.mem_read(pc - 1, &mut buf) + .expect("Could not read memory"); + + assert_eq!(buf[0], 0x4); } else { debug_print(&mut emu, err); } From d3458ef37a3b5d4dff312902bdff9a97c3fa9803 Mon Sep 17 00:00:00 2001 From: henri2h Date: Thu, 19 Jan 2023 02:33:49 +0100 Subject: [PATCH 17/31] feat: replace debug and reset EDGES_MAP to 0 at start --- libafl_unicorn/src/emu.rs | 61 +++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/libafl_unicorn/src/emu.rs b/libafl_unicorn/src/emu.rs index 50970a6fc2..02901bfbcf 100644 --- a/libafl_unicorn/src/emu.rs +++ b/libafl_unicorn/src/emu.rs @@ -53,22 +53,22 @@ fn load_code(emu: &mut Unicorn<()>, address: u64) -> u64 { } fn debug_print(emu: &mut Unicorn<()>, err: uc_error) { - dbg!(); - dbg!("Snap... something went wrong"); - dbg!("Error: {:?}", err); + println!(); + println!("Snap... something went wrong"); + println!("Error: {:?}", err); let pc = emu.pc_read().unwrap(); - dbg!(); - dbg!("Status when crash happened"); + println!(); + println!("Status when crash happened"); - dbg!("PC: {:X}", pc); - dbg!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); - dbg!("X0: {:X}", emu.reg_read(RegisterARM64::X0).unwrap()); - dbg!("X1: {:X}", emu.reg_read(RegisterARM64::X1).unwrap()); - dbg!("X2: {:X}", emu.reg_read(RegisterARM64::X2).unwrap()); - dbg!("X3: {:X}", emu.reg_read(RegisterARM64::X3).unwrap()); + println!("PC: {:X}", pc); + println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); + println!("X0: {:X}", emu.reg_read(RegisterARM64::X0).unwrap()); + println!("X1: {:X}", emu.reg_read(RegisterARM64::X1).unwrap()); + println!("X2: {:X}", emu.reg_read(RegisterARM64::X2).unwrap()); + println!("X3: {:X}", emu.reg_read(RegisterARM64::X3).unwrap()); - dbg!(); + println!(); for i in 0..10 { let pos = i * 4 + pc - 4 * 5; // Instruction are on 4 bytes let dec = pos as i64 - pc as i64; @@ -130,9 +130,27 @@ pub fn emulate() { // The closure that we want to fuzz pub fn harness(input: &BytesInput) -> ExitKind { + unsafe { + // reset coverage + for i in 0..EDGES_MAP.len() { + EDGES_MAP[i] = 0; + } + } + let target = input.target_bytes(); let buf = target.as_slice(); - return prog(buf); + let result = prog(buf); + if debug { + unsafe { + for val in 0..EDGES_MAP.len() { + if EDGES_MAP[val] != 0 { + dbg!(val, EDGES_MAP[val]); + } + } + }; + } + + return result; } fn prog(buf: &[u8]) -> ExitKind { @@ -177,7 +195,7 @@ fn prog(buf: &[u8]) -> ExitKind { .expect("Failed to register code hook"); if debug { - dbg!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); + println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); } let result = emu.emu_start( @@ -189,7 +207,7 @@ fn prog(buf: &[u8]) -> ExitKind { match result { Ok(_) => { - dbg!("Ok"); + // never hapens assert_eq!(emu.reg_read(RegisterARM64::X0), Ok(100)); assert_eq!(emu.reg_read(RegisterARM64::X1), Ok(1337)); @@ -197,9 +215,9 @@ fn prog(buf: &[u8]) -> ExitKind { Err(err) => { if emu.pc_read().unwrap() == 0 { if debug { - dbg!("Reached start"); + println!("Reached start"); } - dbg!("Execution successfull ?"); + println!("Execution successfull ?"); memory_dump(&mut emu, 2); let mut buf: [u8; 1] = [0]; @@ -209,7 +227,14 @@ fn prog(buf: &[u8]) -> ExitKind { emu.mem_read(pc - 1, &mut buf) .expect("Could not read memory"); - assert_eq!(buf[0], 0x4); + + // check result + if buf[0] != 0x4 { + // error here + return ExitKind::Ok; + } + // success + return ExitKind::Ok; } else { debug_print(&mut emu, err); } From c826fbfd012bcb593860388ceee78ac2ce7883c8 Mon Sep 17 00:00:00 2001 From: henri2h Date: Thu, 19 Jan 2023 03:33:35 +0100 Subject: [PATCH 18/31] fix: change generator type --- libafl_unicorn/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index 9c37b776bb..fdc9fd89c8 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -112,7 +112,7 @@ fn runFuzzer() { let mut executor = TimeoutExecutor::new(executor, timeout); // Generator of printable bytearrays of max size 32 - let mut generator = RandPrintablesGenerator::new(32); + let mut generator = RandBytesGenerator::new(4); // Generate 8 initial inputs state From 71e579c5105fb3702cf41683d471e55f19545368 Mon Sep 17 00:00:00 2001 From: henri2h Date: Thu, 19 Jan 2023 03:34:35 +0100 Subject: [PATCH 19/31] fix: set MAX_EDGES_NUM and reduce verbosity --- libafl_unicorn/src/emu.rs | 24 +++++++++++++----------- libafl_unicorn/src/hooks.rs | 4 +++- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/libafl_unicorn/src/emu.rs b/libafl_unicorn/src/emu.rs index 02901bfbcf..bd2ee1ddff 100644 --- a/libafl_unicorn/src/emu.rs +++ b/libafl_unicorn/src/emu.rs @@ -130,16 +130,16 @@ pub fn emulate() { // The closure that we want to fuzz pub fn harness(input: &BytesInput) -> ExitKind { - unsafe { - // reset coverage - for i in 0..EDGES_MAP.len() { - EDGES_MAP[i] = 0; - } - } - + // convert input bytes let target = input.target_bytes(); let buf = target.as_slice(); + if debug { + dbg!(buf); + } + + println!("Run prog"); let result = prog(buf); + if debug { unsafe { for val in 0..EDGES_MAP.len() { @@ -217,23 +217,25 @@ fn prog(buf: &[u8]) -> ExitKind { if debug { println!("Reached start"); } - println!("Execution successfull ?"); - memory_dump(&mut emu, 2); + // check output let mut buf: [u8; 1] = [0]; - let pc = emu.reg_read(RegisterARM64::SP).unwrap(); emu.mem_read(pc - 1, &mut buf) .expect("Could not read memory"); - // check result if buf[0] != 0x4 { // error here return ExitKind::Ok; } + // success + println!("Correct input found"); + memory_dump(&mut emu, 2); + + panic!("Success :)"); return ExitKind::Ok; } else { debug_print(&mut emu, err); diff --git a/libafl_unicorn/src/hooks.rs b/libafl_unicorn/src/hooks.rs index 205ed5fb7d..460473af73 100644 --- a/libafl_unicorn/src/hooks.rs +++ b/libafl_unicorn/src/hooks.rs @@ -13,12 +13,14 @@ use crate::helper::hash_me; static mut PREV_LOC: u64 = 0; pub fn block_hook(emu: &mut unicorn_engine::Unicorn<()>, address: u64, small: u32) { - println!("Block hook: address: {:X} {}", address, small); + //println!("Block hook: address: {:X} {}", address, small); unsafe { let hash = address ^ PREV_LOC; // println!("Hash {}", hash); EDGES_MAP[hash as usize] += 1; PREV_LOC = address >> 1; + + MAX_EDGES_NUM = edges_max_num(); } } From 97539ee44d7b2bad0c969a071f761faab6181e92 Mon Sep 17 00:00:00 2001 From: henri2h Date: Thu, 19 Jan 2023 03:50:10 +0100 Subject: [PATCH 20/31] fix: properly initialize input --- libafl_unicorn/libafl_unicorn_test/foo.c | 4 ++-- libafl_unicorn/libafl_unicorn_test/foo.s | 1 + libafl_unicorn/src/emu.rs | 5 ++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/libafl_unicorn/libafl_unicorn_test/foo.c b/libafl_unicorn/libafl_unicorn_test/foo.c index 6f3de133c8..105c88133c 100644 --- a/libafl_unicorn/libafl_unicorn_test/foo.c +++ b/libafl_unicorn/libafl_unicorn_test/foo.c @@ -4,14 +4,14 @@ int main() { volatile unsigned char a; // = 0x1; volatile unsigned char b; // = 0x0; - volatile unsigned char c; // = 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; }*/ - + if (a > b) { c = 0x1; if (a > 0x20) { diff --git a/libafl_unicorn/libafl_unicorn_test/foo.s b/libafl_unicorn/libafl_unicorn_test/foo.s index c15bc7e723..0ad79176e9 100644 --- a/libafl_unicorn/libafl_unicorn_test/foo.s +++ b/libafl_unicorn/libafl_unicorn_test/foo.s @@ -11,6 +11,7 @@ main: .cfi_startproc sub sp, sp, #16 .cfi_def_cfa_offset 16 + strb wzr, [sp, 15] ldrb w1, [sp, 13] ldrb w0, [sp, 14] and w0, w0, 255 diff --git a/libafl_unicorn/src/emu.rs b/libafl_unicorn/src/emu.rs index bd2ee1ddff..9190b3932b 100644 --- a/libafl_unicorn/src/emu.rs +++ b/libafl_unicorn/src/emu.rs @@ -23,6 +23,7 @@ use unicorn_engine::{ }; static debug: bool = false; +static showInputs: bool = true; use crate::{ helper::{hash_me, memory_dump}, @@ -133,7 +134,7 @@ pub fn harness(input: &BytesInput) -> ExitKind { // convert input bytes let target = input.target_bytes(); let buf = target.as_slice(); - if debug { + if showInputs { dbg!(buf); } @@ -233,8 +234,10 @@ fn prog(buf: &[u8]) -> ExitKind { // success println!("Correct input found"); + dbg!(buf); memory_dump(&mut emu, 2); + panic!("Success :)"); return ExitKind::Ok; } else { From 00cbe7f870983f7978f04edb3e3730bc900644c8 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 19 Jan 2023 16:24:21 +0100 Subject: [PATCH 21/31] Fix OOM --- libafl_unicorn/src/emu.rs | 44 ++++++++++++++++++++++--------------- libafl_unicorn/src/hooks.rs | 4 +--- libafl_unicorn/src/main.rs | 14 +++++------- 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/libafl_unicorn/src/emu.rs b/libafl_unicorn/src/emu.rs index 9190b3932b..f670af4a13 100644 --- a/libafl_unicorn/src/emu.rs +++ b/libafl_unicorn/src/emu.rs @@ -154,6 +154,8 @@ pub fn harness(input: &BytesInput) -> ExitKind { return result; } +static mut EMU: Option> = None; + fn prog(buf: &[u8]) -> ExitKind { let address: u64 = 0x1000; let r_sp: u64 = 0x8000; @@ -161,18 +163,32 @@ fn prog(buf: &[u8]) -> ExitKind { // [0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 - let mut emu = unicorn_engine::Unicorn::new(Arch::ARM64, Mode::LITTLE_ENDIAN) - .expect("failed to initialize Unicorn instance"); + let mapped_addr = r_sp - (data_size as u64) * 8; + let mapped_len = data_size * 8; - // TODO: For some reason, the compiled program start by substracting 0x10 to SP - emu.mem_map( - r_sp - (data_size as u64) * 8, - data_size * 8, - Permission::ALL, - ) - .expect("failed to map data page"); + let mut arm_code_len = 0; + + let mut emu = unsafe { + EMU.get_or_insert_with(|| { + let mut emu = unicorn_engine::Unicorn::new(Arch::ARM64, Mode::LITTLE_ENDIAN) + .expect("failed to initialize Unicorn instance"); + + arm_code_len = load_code(&mut emu, address); - let arm_code_len = load_code(&mut emu, address); + // TODO: For some reason, the compiled program start by substracting 0x10 to SP + emu.mem_map(mapped_addr, mapped_len, Permission::ALL) + .expect("failed to map data page"); + + // Add me mory hook + emu.add_mem_hook(HookType::MEM_ALL, r_sp - (data_size) as u64, r_sp, callback) + .expect("Failed to register watcher"); + + emu.add_block_hook(block_hook) + .expect("Failed to register code hook"); + + emu + }) + }; // Set registry // TODO: For some reason, the compiled program start by substracting 0x10 to SP @@ -188,13 +204,6 @@ fn prog(buf: &[u8]) -> ExitKind { memory_dump(&mut emu, 2); } - // Add me mory hook - emu.add_mem_hook(HookType::MEM_ALL, r_sp - (data_size) as u64, r_sp, callback) - .expect("Failed to register watcher"); - - emu.add_block_hook(block_hook) - .expect("Failed to register code hook"); - if debug { println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); } @@ -237,7 +246,6 @@ fn prog(buf: &[u8]) -> ExitKind { dbg!(buf); memory_dump(&mut emu, 2); - panic!("Success :)"); return ExitKind::Ok; } else { diff --git a/libafl_unicorn/src/hooks.rs b/libafl_unicorn/src/hooks.rs index 460473af73..e34a61d111 100644 --- a/libafl_unicorn/src/hooks.rs +++ b/libafl_unicorn/src/hooks.rs @@ -16,11 +16,9 @@ pub fn block_hook(emu: &mut unicorn_engine::Unicorn<()>, address: u64, small: u3 //println!("Block hook: address: {:X} {}", address, small); unsafe { - let hash = address ^ PREV_LOC; + let hash = (address ^ PREV_LOC) & (EDGES_MAP_SIZE as u64 - 1); // println!("Hash {}", hash); EDGES_MAP[hash as usize] += 1; PREV_LOC = address >> 1; - - MAX_EDGES_NUM = edges_max_num(); } } diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index fdc9fd89c8..04eb64baa1 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -2,12 +2,11 @@ pub mod emu; pub mod helper; pub mod hooks; -use std::env; - #[cfg(windows)] use std::ptr::write_volatile; use std::{ - cell::UnsafeCell, cmp::max, fs::File, io::Read, path::PathBuf, ptr::addr_of_mut, time::Duration, + cell::UnsafeCell, cmp::max, env, fs::File, io::Read, path::PathBuf, ptr::addr_of_mut, + time::Duration, }; use hashbrown::{hash_map::Entry, HashMap}; @@ -27,12 +26,12 @@ use libafl::{ inputs::{BytesInput, HasTargetBytes, UsesInput}, monitors::MultiMonitor, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, - observers::{HitcountsMapObserver, TimeObserver, VariableMapObserver}, + observers::{ConstMapObserver, HitcountsMapObserver, TimeObserver}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::mutational::StdMutationalStage, state::{HasMetadata, StdState}, }; -pub use libafl_targets::{edges_map_mut_slice, MAX_EDGES_NUM}; +pub use libafl_targets::{EDGES_MAP_PTR, EDGES_MAP_SIZE}; use unicorn_engine::{ unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}, RegisterARM64, @@ -54,10 +53,9 @@ fn runFuzzer() { let mut mgr = SimpleEventManager::new(monitor); let edges_observer = unsafe { - HitcountsMapObserver::new(VariableMapObserver::from_mut_slice( + HitcountsMapObserver::new(ConstMapObserver::<_, EDGES_MAP_SIZE>::from_mut_ptr( "edges", - edges_map_mut_slice(), - addr_of_mut!(MAX_EDGES_NUM), + EDGES_MAP_PTR, )) }; From 20d70e311375711190e47b567f26f406a6c0562d Mon Sep 17 00:00:00 2001 From: henri2h Date: Fri, 3 Feb 2023 12:34:19 +0100 Subject: [PATCH 22/31] feat: code re write --- libafl_unicorn/libafl_unicorn_test/foo.c | 2 +- libafl_unicorn/libafl_unicorn_test/foo.s | 1 + libafl_unicorn/src/emu.rs | 186 +++++++++-------------- libafl_unicorn/src/helper.rs | 13 +- libafl_unicorn/src/hooks.rs | 11 -- libafl_unicorn/src/main.rs | 59 +++---- 6 files changed, 106 insertions(+), 166 deletions(-) diff --git a/libafl_unicorn/libafl_unicorn_test/foo.c b/libafl_unicorn/libafl_unicorn_test/foo.c index 105c88133c..11346887d0 100644 --- a/libafl_unicorn/libafl_unicorn_test/foo.c +++ b/libafl_unicorn/libafl_unicorn_test/foo.c @@ -11,7 +11,7 @@ int main() { for(int i = 0; i< len; i++){ f[i] = i; }*/ - + c = 0x0; if (a > b) { c = 0x1; if (a > 0x20) { diff --git a/libafl_unicorn/libafl_unicorn_test/foo.s b/libafl_unicorn/libafl_unicorn_test/foo.s index 0ad79176e9..5f960a09e3 100644 --- a/libafl_unicorn/libafl_unicorn_test/foo.s +++ b/libafl_unicorn/libafl_unicorn_test/foo.s @@ -12,6 +12,7 @@ main: sub sp, sp, #16 .cfi_def_cfa_offset 16 strb wzr, [sp, 15] + strb wzr, [sp, 15] ldrb w1, [sp, 13] ldrb w0, [sp, 14] and w0, w0, 255 diff --git a/libafl_unicorn/src/emu.rs b/libafl_unicorn/src/emu.rs index f670af4a13..8d5f80b8c4 100644 --- a/libafl_unicorn/src/emu.rs +++ b/libafl_unicorn/src/emu.rs @@ -1,34 +1,67 @@ -use std::{cell::UnsafeCell, cmp::max, error::Error, fs::File, io::Read, path::PathBuf}; - -use hashbrown::{hash_map::Entry, HashMap}; -use libafl::{ - bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice}, - corpus::{InMemoryCorpus, OnDiskCorpus}, - events::SimpleEventManager, - executors::{inprocess::InProcessExecutor, ExitKind}, - feedbacks::{CrashFeedback, MaxMapFeedback}, - fuzzer::{Fuzzer, StdFuzzer}, - generators::RandPrintablesGenerator, - inputs::{BytesInput, HasTargetBytes, UsesInput}, - mutators::scheduled::{havoc_mutations, StdScheduledMutator}, - observers::StdMapObserver, - schedulers::QueueScheduler, - stages::mutational::StdMutationalStage, - state::{HasMetadata, StdState}, -}; +use std::{fs::File, io::Read}; + pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; use unicorn_engine::{ unicorn_const::{uc_error, Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}, RegisterARM64, Unicorn, }; -static debug: bool = false; -static showInputs: bool = true; +static DEBUG: bool = false; -use crate::{ - helper::{hash_me, memory_dump}, - hooks::block_hook, -}; +static CODE_ADDRESS: u64 = 0x800; + +use crate::{helper::memory_dump, hooks::block_hook}; + +pub struct Emulator { + emu: unicorn_engine::Unicorn<'static, ()>, + code_len: u64, +} + +impl Emulator { + pub fn new() -> Emulator { + let emu = unicorn_engine::Unicorn::new(Arch::ARM64, Mode::LITTLE_ENDIAN) + .expect("failed to initialize Unicorn instance"); + Emulator { emu, code_len: 0 } + } + + pub fn setup(&mut self, input_addr: u64, input_size: usize) { + self.code_len = load_code(&mut self.emu, CODE_ADDRESS); + // TODO: For some reason, the compiled program start by substracting 0x10 to SP + self.emu + .mem_map(input_addr, input_size, Permission::ALL) + .expect("failed to map data page"); + } + + pub fn run(&mut self) { + prog(&mut self.emu, self.code_len); + } + + pub fn write_mem(&mut self, addr: u64, buf: &[u8]) { + //println!("{} -> {}", addr, addr + (buf.len() as u64)); + self.emu + .mem_write(addr, &buf) + .expect("failed to write instructions"); + } + + pub fn set_memory_hook(&mut self, addr: u64, length: u64) { + self.emu + .add_mem_hook(HookType::MEM_ALL, addr - length, addr, callback) + .expect("Failed to register watcher"); + } + + pub fn set_code_hook(&mut self) { + self.emu + .add_block_hook(block_hook) + .expect("Failed to register code hook"); + } + + pub fn write_reg(&mut self, regid: T, value: u64) + where + T: Into, + { + self.emu.reg_write(regid, value).expect("Could not set registry"); + } +} fn load_code(emu: &mut Unicorn<()>, address: u64) -> u64 { let mut f = File::open("libafl_unicorn_test/a.out").expect("Could not open file"); @@ -77,7 +110,7 @@ fn debug_print(emu: &mut Unicorn<()>, err: uc_error) { let read_result = emu.mem_read_as_vec(pos, 4); match read_result { Ok(data) => { - dbg!( + println!( "{:X}: {:03}:\t {:02X} {:02X} {:02X} {:02X} {:08b} {:08b} {:08b} {:08b}", pos, dec, @@ -103,7 +136,7 @@ fn callback( size: usize, value: i64, ) -> bool { - if debug { + if DEBUG { match mem { MemType::WRITE => println!( "Memory is being WRITTEN at adress: {:X} size: {} value: {}", @@ -126,91 +159,17 @@ fn callback( pub fn emulate() { let mem_data = [0x50, 0x24, 0x0]; - prog(&mem_data); + // TODO } -// The closure that we want to fuzz -pub fn harness(input: &BytesInput) -> ExitKind { - // convert input bytes - let target = input.target_bytes(); - let buf = target.as_slice(); - if showInputs { - dbg!(buf); - } - - println!("Run prog"); - let result = prog(buf); - - if debug { - unsafe { - for val in 0..EDGES_MAP.len() { - if EDGES_MAP[val] != 0 { - dbg!(val, EDGES_MAP[val]); - } - } - }; - } - - return result; -} - -static mut EMU: Option> = None; - -fn prog(buf: &[u8]) -> ExitKind { - let address: u64 = 0x1000; - let r_sp: u64 = 0x8000; - let data_size: usize = 0x100; - - // [0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 - - let mapped_addr = r_sp - (data_size as u64) * 8; - let mapped_len = data_size * 8; - - let mut arm_code_len = 0; - - let mut emu = unsafe { - EMU.get_or_insert_with(|| { - let mut emu = unicorn_engine::Unicorn::new(Arch::ARM64, Mode::LITTLE_ENDIAN) - .expect("failed to initialize Unicorn instance"); - - arm_code_len = load_code(&mut emu, address); - - // TODO: For some reason, the compiled program start by substracting 0x10 to SP - emu.mem_map(mapped_addr, mapped_len, Permission::ALL) - .expect("failed to map data page"); - - // Add me mory hook - emu.add_mem_hook(HookType::MEM_ALL, r_sp - (data_size) as u64, r_sp, callback) - .expect("Failed to register watcher"); - - emu.add_block_hook(block_hook) - .expect("Failed to register code hook"); - - emu - }) - }; - - // Set registry - // TODO: For some reason, the compiled program start by substracting 0x10 to SP - emu.reg_write(RegisterARM64::SP, r_sp) - .expect("Could not set registery"); - - // TODO specific values - let mem_data = buf; - emu.mem_write(r_sp - (mem_data.len() as u64), &mem_data) - .expect("failed to write instructions"); - - if debug { - memory_dump(&mut emu, 2); - } - - if debug { - println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); +pub fn prog(emu: &mut unicorn_engine::Unicorn<'static, ()>, arm_code_len: u64) { + if DEBUG { + memory_dump(emu, 2); } let result = emu.emu_start( - address + 0x40, // start at main. Position of main: 0x40 - address + arm_code_len, + CODE_ADDRESS + 0x40, // start at main. Position of main: 0x40 + CODE_ADDRESS + arm_code_len, 10 * SECOND_SCALE, 0x1000, ); @@ -224,7 +183,7 @@ fn prog(buf: &[u8]) -> ExitKind { } Err(err) => { if emu.pc_read().unwrap() == 0 { - if debug { + if DEBUG { println!("Reached start"); } @@ -237,22 +196,19 @@ fn prog(buf: &[u8]) -> ExitKind { // check result if buf[0] != 0x4 { - // error here - return ExitKind::Ok; + // didn't found the correct value + return; } // success println!("Correct input found"); - dbg!(buf); - memory_dump(&mut emu, 2); + println!("Output: {:#}", buf[0]); + memory_dump(emu, 2); panic!("Success :)"); - return ExitKind::Ok; } else { - debug_print(&mut emu, err); + debug_print(emu, err); } } } - - ExitKind::Ok } diff --git a/libafl_unicorn/src/helper.rs b/libafl_unicorn/src/helper.rs index 54a926f139..bf990f5124 100644 --- a/libafl_unicorn/src/helper.rs +++ b/libafl_unicorn/src/helper.rs @@ -1,15 +1,4 @@ -use unicorn_engine::{ - unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}, - RegisterARM64, -}; - -#[must_use] -pub fn hash_me(mut x: u64) -> u64 { - x = (x.overflowing_shr(16).0 ^ x).overflowing_mul(0x45d9f3b).0; - x = (x.overflowing_shr(16).0 ^ x).overflowing_mul(0x45d9f3b).0; - x = (x.overflowing_shr(16).0 ^ x) ^ x; - x -} +use unicorn_engine::RegisterARM64; pub fn memory_dump(emu: &mut unicorn_engine::Unicorn<()>, len: u64) { let pc = emu.reg_read(RegisterARM64::SP).unwrap(); diff --git a/libafl_unicorn/src/hooks.rs b/libafl_unicorn/src/hooks.rs index e34a61d111..33c7d3ae6f 100644 --- a/libafl_unicorn/src/hooks.rs +++ b/libafl_unicorn/src/hooks.rs @@ -1,20 +1,9 @@ -use std::{cell::UnsafeCell, cmp::max}; - -use hashbrown::{hash_map::Entry, HashMap}; -use libafl::{inputs::UsesInput, state::HasMetadata}; pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; -use unicorn_engine::{ - unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}, - RegisterARM64, -}; -use crate::helper::hash_me; static mut PREV_LOC: u64 = 0; pub fn block_hook(emu: &mut unicorn_engine::Unicorn<()>, address: u64, small: u32) { - //println!("Block hook: address: {:X} {}", address, small); - unsafe { let hash = (address ^ PREV_LOC) & (EDGES_MAP_SIZE as u64 - 1); // println!("Hash {}", hash); diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index 04eb64baa1..91b02a61c2 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -2,18 +2,9 @@ pub mod emu; pub mod helper; pub mod hooks; -#[cfg(windows)] -use std::ptr::write_volatile; -use std::{ - cell::UnsafeCell, cmp::max, env, fs::File, io::Read, path::PathBuf, ptr::addr_of_mut, - time::Duration, -}; +use std::{char::MAX, env, path::PathBuf, time::Duration}; -use hashbrown::{hash_map::Entry, HashMap}; -#[cfg(feature = "tui")] -use libafl::monitors::tui::TuiMonitor; -#[cfg(not(feature = "tui"))] -use libafl::monitors::SimpleMonitor; +use emu::Emulator; use libafl::{ bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice}, corpus::{InMemoryCorpus, OnDiskCorpus}, @@ -22,29 +13,28 @@ use libafl::{ feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, - generators::{RandBytesGenerator, RandPrintablesGenerator}, - inputs::{BytesInput, HasTargetBytes, UsesInput}, + generators::RandBytesGenerator, + inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, observers::{ConstMapObserver, HitcountsMapObserver, TimeObserver}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::mutational::StdMutationalStage, - state::{HasMetadata, StdState}, + state::StdState, }; pub use libafl_targets::{EDGES_MAP_PTR, EDGES_MAP_SIZE}; -use unicorn_engine::{ - unicorn_const::{Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}, - RegisterARM64, -}; +use unicorn_engine::RegisterARM64; -use crate::{ - emu::harness, - helper::{hash_me, memory_dump}, - hooks::block_hook, -}; +pub const MAX_INPUT_SIZE: usize = 0x1000; //1048576; // 1MB // emulating -fn runFuzzer() { +fn fuzzer() { + let input_addr_end: u64 = 0x8000; + let input_addr_start: u64 = input_addr_end - MAX_INPUT_SIZE as u64; + let emu = &mut Emulator::new(); + emu.setup(input_addr_start, MAX_INPUT_SIZE); + emu.set_code_hook(); + let timeout = Duration::from_secs(1); let monitor = MultiMonitor::new(|s| println!("{s}")); @@ -97,9 +87,24 @@ fn runFuzzer() { // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - let mut binding = harness; // being harness a function we need a local var to bind it + 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.write_reg(RegisterARM64::SP, input_addr_end); + + emu.run(); + + return ExitKind::Ok; + }; + let executor = InProcessExecutor::new( - &mut binding, + &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, &mut state, @@ -134,5 +139,5 @@ fn main() { return; } } - runFuzzer(); + fuzzer(); } From 43f21bf25104f074da2abae11c3f58cf0d65dc11 Mon Sep 17 00:00:00 2001 From: henri2h Date: Fri, 3 Feb 2023 15:20:46 +0100 Subject: [PATCH 23/31] feat: add multiple architectures --- libafl_unicorn/libafl_unicorn_test/Makefile | 34 ++++++++-- libafl_unicorn/libafl_unicorn_test/foo_arm | Bin 0 -> 960 bytes libafl_unicorn/libafl_unicorn_test/foo_arm.s | 61 ++++++++++++++++++ libafl_unicorn/libafl_unicorn_test/foo_arm64 | Bin 0 -> 1496 bytes .../{foo.s => foo_arm64.s} | 0 libafl_unicorn/libafl_unicorn_test/foo_x64 | Bin 0 -> 1360 bytes libafl_unicorn/libafl_unicorn_test/foo_x64.s | 55 ++++++++++++++++ 7 files changed, 143 insertions(+), 7 deletions(-) create mode 100644 libafl_unicorn/libafl_unicorn_test/foo_arm create mode 100644 libafl_unicorn/libafl_unicorn_test/foo_arm.s create mode 100644 libafl_unicorn/libafl_unicorn_test/foo_arm64 rename libafl_unicorn/libafl_unicorn_test/{foo.s => foo_arm64.s} (100%) create mode 100644 libafl_unicorn/libafl_unicorn_test/foo_x64 create mode 100644 libafl_unicorn/libafl_unicorn_test/foo_x64.s diff --git a/libafl_unicorn/libafl_unicorn_test/Makefile b/libafl_unicorn/libafl_unicorn_test/Makefile index b13b266906..994f16d39b 100644 --- a/libafl_unicorn/libafl_unicorn_test/Makefile +++ b/libafl_unicorn/libafl_unicorn_test/Makefile @@ -1,11 +1,31 @@ -class="aarch64-linux-gnu" -# arm-linux-gnueabihf -assembly: - $(class)-gcc -O2 -S -c foo.c +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 -binary: - $(class)-as foo.s +assembly_arm: + $(arm)-gcc -O2 -S -c foo.c -o foo_arm.s -all: assembly binary +binary_arm: + $(arm)-as foo_arm.s -o foo_arm + +assembly_x64: + $(x64)-gcc -O2 -S -c foo.c -o foo_x64.s + +binary_x64: + $(x64)-as foo_x64.s -o foo_x64 + +build_arm: assembly_arm binary_arm +build_arm64: assembly_arm64 binary_arm64 +build_x64: assembly_x64 binary_x64 + +clean: + rm foo_* + + +all: build_arm build_arm64 build_x64 # sudo apt install gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu \ No newline at end of file diff --git a/libafl_unicorn/libafl_unicorn_test/foo_arm b/libafl_unicorn/libafl_unicorn_test/foo_arm new file mode 100644 index 0000000000000000000000000000000000000000..6d3688a26d793b4bb2259bdf6e320d3a24fe4a5a GIT binary patch literal 960 zcmah{OH0E*5S~raq*lZSich3I&`RwRt043ii}s=-K7K$_BNju;gf;uqy=KJQ`batBS?cE(h2*@Cif*u2iYf&e*5kU(2Apti{ zSh(w0HqS;UmTYuP`(bOKO(GQ)JDE1y+7~-W>(7mj1zNLO(1mK+i)o`}MC|PqT2RR6 zbIH_+7nWI=T+7ldwKMBsMAue?08SRiaDCS+0ZCyQ+srs%CM+jT`m}^GVvLe8GOkaM zNiwC)kXdbBy-Rcje5jfM?;ldA!1_Jdov(e6x# zh$H;`S*1dYu*e{AOJx9BzY3V^A+lN*&t=8@O9u4RWiC*!UI)qr^tm+_R)H2PLEx9s zQLZqb7WPjv*eYHCbq@Ea%UG@Ch0L#io9J&cEoPOs>V6l!Gjg|c!Y8-&n{OF?am@dG ztKxm??;fKjLvB5X0-wPaja$SeQFGuuo_T=?Q9Q{)AouV%!o=CC5y1!dtxJL^eH_&< o;nf3QfD7?hj(XC>v+KE?J_A@DU4gMoZZep`oT+QDtSK76>onKy2D{5)+emixS z7Ytz9pETNgy@kQfEpK{!7E^@rVR_3x8xxb|(Fr^pV<%g9MuYgCWJUeV2?9QxAv#2# zB3a_o4N`_v;3Cejk1H}AqbV|s8y6y+->)V^K87TOYlP&Se$qK{)?U4?s+jL!eiu>j zqGp&gMi0@T{-~x}ucA1pK(*p#U+-Hh4m&Z_ZV(4hl~xOi0m=)Sar+ReT92ZzMj5qw z9IDNy9@3|NH$YkHzjq-vd_3;BQ*8fYHZB-xj$2eyfWVx3-)5)pZ|D=GbvWe*blf OW$_OUzv6J&l>A?rLTvW{ literal 0 HcmV?d00001 diff --git a/libafl_unicorn/libafl_unicorn_test/foo.s b/libafl_unicorn/libafl_unicorn_test/foo_arm64.s similarity index 100% rename from libafl_unicorn/libafl_unicorn_test/foo.s rename to libafl_unicorn/libafl_unicorn_test/foo_arm64.s diff --git a/libafl_unicorn/libafl_unicorn_test/foo_x64 b/libafl_unicorn/libafl_unicorn_test/foo_x64 new file mode 100644 index 0000000000000000000000000000000000000000..56162606b1a8849072354db1610cfad74ba45aca GIT binary patch literal 1360 zcmbtTOH0E*5T3Nv`lz;w2SJet#Ruq8^&%n?sE>t+t_EBSOY4AbUJ)0?Mb=eypdGD%c97C~ z67i-}{6OM+MQ&C2pB&9322Yi*YND7?EsjKIi2ry;!F5?4Y;(s6B)SmNctaxwVC6%b zn>qB{tNNmhxOKpRZJFEzmMfQm5rK84$NdtpyldN*gB6a;ErvrG#Z-P*;BCieCC@Ec z9zO);S$ih4iW>#bv@Oxfzg!QhX;zKOE{1|7RPS0pCWw>gC+iFuC|=bMV!jF2U-cuX zr9Hpv|Kl97hK%lB*rpJWJ`*M}J`;FA;bs5QXE871aOq_jgkrUp`lGv7cb5=f;Ud$6 zXYE0=3fnFM+Edlvi6Zs8RDA*K=x;DYy>us{(QZMspeL==Oa7~OlO!edn*`8WanTv8 d`ewnStFw~OC+gVGv+lsWdUr^2F7*B{`gbxiZEFAk literal 0 HcmV?d00001 diff --git a/libafl_unicorn/libafl_unicorn_test/foo_x64.s b/libafl_unicorn/libafl_unicorn_test/foo_x64.s new file mode 100644 index 0000000000..5722b727dd --- /dev/null +++ b/libafl_unicorn/libafl_unicorn_test/foo_x64.s @@ -0,0 +1,55 @@ + .file "foo.c" + .text + .section .text.startup,"ax",@progbits + .p2align 4 + .globl main + .type main, @function +main: +.LFB0: + .cfi_startproc + endbr64 + movb $0, -1(%rsp) + movb $0, -1(%rsp) + movzbl -3(%rsp), %eax + movzbl -2(%rsp), %edx + cmpb %al, %dl + jnb .L3 + movb $1, -1(%rsp) + movzbl -3(%rsp), %eax + cmpb $32, %al + jbe .L3 + movb $2, -1(%rsp) + movzbl -3(%rsp), %eax + cmpb $80, %al + je .L6 +.L3: + movzbl -1(%rsp), %eax + ret +.L6: + movb $3, -1(%rsp) + movzbl -2(%rsp), %eax + cmpb $36, %al + jne .L3 + movb $4, -1(%rsp) + jmp .L3 + .cfi_endproc +.LFE0: + .size main, .-main + .ident "GCC: (Ubuntu 12.2.0-3ubuntu1) 12.2.0" + .section .note.GNU-stack,"",@progbits + .section .note.gnu.property,"a" + .align 8 + .long 1f - 0f + .long 4f - 1f + .long 5 +0: + .string "GNU" +1: + .align 8 + .long 0xc0000002 + .long 3f - 2f +2: + .long 0x3 +3: + .align 8 +4: From e1a0e2c2b4ec24aadeb7793434905a182e616e46 Mon Sep 17 00:00:00 2001 From: henri2h Date: Sun, 5 Feb 2023 16:41:27 +0100 Subject: [PATCH 24/31] feat: provide disassembly and fuzz x86 code --- libafl_unicorn/Cargo.toml | 1 + libafl_unicorn/libafl_unicorn_test/Makefile | 12 +- .../libafl_unicorn_test/{foo_x64 => foo_x86} | Bin .../{foo_x64.s => foo_x86.s} | 0 libafl_unicorn/src/emu.rs | 175 +++++++++++++----- libafl_unicorn/src/helper.rs | 16 +- libafl_unicorn/src/main.rs | 73 +++++--- 7 files changed, 194 insertions(+), 83 deletions(-) rename libafl_unicorn/libafl_unicorn_test/{foo_x64 => foo_x86} (100%) rename libafl_unicorn/libafl_unicorn_test/{foo_x64.s => foo_x86.s} (100%) diff --git a/libafl_unicorn/Cargo.toml b/libafl_unicorn/Cargo.toml index dda922f00a..38a012b256 100644 --- a/libafl_unicorn/Cargo.toml +++ b/libafl_unicorn/Cargo.toml @@ -17,3 +17,4 @@ libafl_targets = { path = "../libafl_targets", version = "0.8.2" } 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" diff --git a/libafl_unicorn/libafl_unicorn_test/Makefile b/libafl_unicorn/libafl_unicorn_test/Makefile index 994f16d39b..3c32847919 100644 --- a/libafl_unicorn/libafl_unicorn_test/Makefile +++ b/libafl_unicorn/libafl_unicorn_test/Makefile @@ -13,19 +13,19 @@ assembly_arm: binary_arm: $(arm)-as foo_arm.s -o foo_arm -assembly_x64: - $(x64)-gcc -O2 -S -c foo.c -o foo_x64.s +assembly_x86: + $(x64)-gcc -O2 -S -c foo.c -o foo_x86.s -binary_x64: - $(x64)-as foo_x64.s -o foo_x64 +binary_x86: + $(x64)-as foo_x86.s -o foo_x86 build_arm: assembly_arm binary_arm build_arm64: assembly_arm64 binary_arm64 -build_x64: assembly_x64 binary_x64 +build_x86: assembly_x86 binary_x86 clean: rm foo_* -all: build_arm build_arm64 build_x64 +all: build_arm build_arm64 build_x86 # sudo apt install gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu \ No newline at end of file diff --git a/libafl_unicorn/libafl_unicorn_test/foo_x64 b/libafl_unicorn/libafl_unicorn_test/foo_x86 similarity index 100% rename from libafl_unicorn/libafl_unicorn_test/foo_x64 rename to libafl_unicorn/libafl_unicorn_test/foo_x86 diff --git a/libafl_unicorn/libafl_unicorn_test/foo_x64.s b/libafl_unicorn/libafl_unicorn_test/foo_x86.s similarity index 100% rename from libafl_unicorn/libafl_unicorn_test/foo_x64.s rename to libafl_unicorn/libafl_unicorn_test/foo_x86.s diff --git a/libafl_unicorn/src/emu.rs b/libafl_unicorn/src/emu.rs index 8d5f80b8c4..aa1773ff8b 100644 --- a/libafl_unicorn/src/emu.rs +++ b/libafl_unicorn/src/emu.rs @@ -1,16 +1,21 @@ use std::{fs::File, io::Read}; +use iced_x86::{Decoder, DecoderOptions, Formatter, Instruction, NasmFormatter}; pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; use unicorn_engine::{ unicorn_const::{uc_error, Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}, - RegisterARM64, Unicorn, + RegisterARM64, RegisterX86, Unicorn, }; static DEBUG: bool = false; -static CODE_ADDRESS: u64 = 0x800; +static CODE_ADDRESS: u64 = 0x9000; +static HEXBYTES_COLUMN_BYTE_LENGTH: usize = 10; -use crate::{helper::memory_dump, hooks::block_hook}; +use crate::{ + helper::{get_stack_pointer, memory_dump}, + hooks::block_hook, +}; pub struct Emulator { emu: unicorn_engine::Unicorn<'static, ()>, @@ -18,17 +23,17 @@ pub struct Emulator { } impl Emulator { - pub fn new() -> Emulator { - let emu = unicorn_engine::Unicorn::new(Arch::ARM64, Mode::LITTLE_ENDIAN) + pub fn new(arch: Arch) -> Emulator { + let emu = unicorn_engine::Unicorn::new(arch, Mode::MODE_64) .expect("failed to initialize Unicorn instance"); Emulator { emu, code_len: 0 } } - pub fn setup(&mut self, input_addr: u64, input_size: usize) { - self.code_len = load_code(&mut self.emu, CODE_ADDRESS); + pub fn setup(&mut self, input_addr: u64, input_size: usize, code_path: &str) { + self.code_len = load_code(&mut self.emu, CODE_ADDRESS, code_path); // TODO: For some reason, the compiled program start by substracting 0x10 to SP self.emu - .mem_map(input_addr, input_size, Permission::ALL) + .mem_map(input_addr, input_size, Permission::WRITE | Permission::READ) .expect("failed to map data page"); } @@ -59,12 +64,25 @@ impl Emulator { where T: Into, { - self.emu.reg_write(regid, value).expect("Could not set registry"); + self.emu + .reg_write(regid, value) + .expect("Could not set registry"); + } + + pub fn reg_read(&self, regid: T) -> Result + where + T: Into, + { + self.emu.reg_read(regid) + } + + pub fn get_arch(&self) -> Arch { + return self.emu.get_arch(); } } -fn load_code(emu: &mut Unicorn<()>, address: u64) -> u64 { - let mut f = File::open("libafl_unicorn_test/a.out").expect("Could not open file"); +fn load_code(emu: &mut Unicorn<()>, address: u64, path: &str) -> u64 { + let mut f = File::open(path).expect("Could not open file"); let mut buffer = Vec::new(); // read the whole file @@ -75,7 +93,7 @@ fn load_code(emu: &mut Unicorn<()>, address: u64) -> u64 { // Define memory regions emu.mem_map( address, - ((arm_code.len() / 1024) + 1) * 1024, + ((arm_code.len() / 4096) + 1) * 4096, Permission::EXEC, ) .expect("failed to map code page"); @@ -96,35 +114,82 @@ fn debug_print(emu: &mut Unicorn<()>, err: uc_error) { println!("Status when crash happened"); println!("PC: {:X}", pc); - println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); - println!("X0: {:X}", emu.reg_read(RegisterARM64::X0).unwrap()); - println!("X1: {:X}", emu.reg_read(RegisterARM64::X1).unwrap()); - println!("X2: {:X}", emu.reg_read(RegisterARM64::X2).unwrap()); - println!("X3: {:X}", emu.reg_read(RegisterARM64::X3).unwrap()); + let arch = emu.get_arch(); + + match arch { + Arch::ARM | Arch::ARM64 => { + println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); + println!("X0: {:X}", emu.reg_read(RegisterARM64::X0).unwrap()); + println!("X1: {:X}", emu.reg_read(RegisterARM64::X1).unwrap()); + println!("X2: {:X}", emu.reg_read(RegisterARM64::X2).unwrap()); + println!("X3: {:X}", emu.reg_read(RegisterARM64::X3).unwrap()); + } + Arch::X86 => { + println!("ESP: {:X}", emu.reg_read(RegisterX86::ESP).unwrap()); + println!("RAX: {:X}", emu.reg_read(RegisterX86::RAX).unwrap()); + println!("RCX: {:X}", emu.reg_read(RegisterX86::RCX).unwrap()); + println!("RPB: {:X}", emu.reg_read(RegisterX86::RBP).unwrap()); + println!("RSP: {:X}", emu.reg_read(RegisterX86::RSP).unwrap()); + println!("EAX: {:X}", emu.reg_read(RegisterX86::EAX).unwrap()); + println!("ECX: {:X}", emu.reg_read(RegisterX86::ECX).unwrap()); + println!("EDX: {:X}", emu.reg_read(RegisterX86::EDX).unwrap()); + } + _ => {} + } - println!(); - for i in 0..10 { - let pos = i * 4 + pc - 4 * 5; // Instruction are on 4 bytes - let dec = pos as i64 - pc as i64; - - let read_result = emu.mem_read_as_vec(pos, 4); - match read_result { - Ok(data) => { - println!( - "{:X}: {:03}:\t {:02X} {:02X} {:02X} {:02X} {:08b} {:08b} {:08b} {:08b}", - pos, - dec, - data[0], - data[1], - data[2], - data[3], - data[0], - data[1], - data[2], - data[3] - ); + if emu.get_arch() == Arch::X86 { + // Provide dissasembly at instant of crash for X86 assembly + let regions = emu.mem_regions().expect("Could not get memory regions"); + for i in 0..regions.len() { + if regions[i].perms == Permission::EXEC { + if pc >= regions[i].begin && pc <= regions[i].end { + let mut begin = pc - 32; + let mut end = pc + 32; + if begin < regions[i].begin { + begin = regions[i].begin; + } + if end > regions[i].end { + end = regions[i].end; + } + + let bytes = emu + .mem_read_as_vec(begin, (end - begin) as usize) + .expect("Could not get program code"); + + let mut decoder = Decoder::with_ip(64, &bytes, begin, DecoderOptions::NONE); + + let mut formatter = NasmFormatter::new(); + formatter.options_mut().set_digit_separator("`"); + formatter.options_mut().set_first_operand_char_index(10); + + let mut instruction = Instruction::default(); + let mut output = String::new(); + + while decoder.can_decode() { + decoder.decode_out(&mut instruction); + + // Format the instruction ("disassemble" it) + output.clear(); + formatter.format(&instruction, &mut output); + + // Eg. "00007FFAC46ACDB2 488DAC2400FFFFFF lea rbp,[rsp-100h]" + + let diff = instruction.ip() as i64 - pc as i64; + print!("{:02}\t{:016X} ", diff, instruction.ip()); + let start_index = (instruction.ip() - begin) as usize; + let instr_bytes = &bytes[start_index..start_index + instruction.len()]; + for b in instr_bytes.iter() { + print!("{:02X}", b); + } + if instr_bytes.len() < HEXBYTES_COLUMN_BYTE_LENGTH { + for _ in 0..HEXBYTES_COLUMN_BYTE_LENGTH - instr_bytes.len() { + print!(" "); + } + } + println!(" {}", output); + } + } } - Err(_) => {} } } } @@ -163,12 +228,8 @@ pub fn emulate() { } pub fn prog(emu: &mut unicorn_engine::Unicorn<'static, ()>, arm_code_len: u64) { - if DEBUG { - memory_dump(emu, 2); - } - let result = emu.emu_start( - CODE_ADDRESS + 0x40, // start at main. Position of main: 0x40 + CODE_ADDRESS, // + 0x40, // start at main. Position of main: 0x40 CODE_ADDRESS + arm_code_len, 10 * SECOND_SCALE, 0x1000, @@ -177,26 +238,40 @@ pub fn prog(emu: &mut unicorn_engine::Unicorn<'static, ()>, arm_code_len: u64) { match result { Ok(_) => { // never hapens - - assert_eq!(emu.reg_read(RegisterARM64::X0), Ok(100)); - assert_eq!(emu.reg_read(RegisterARM64::X1), Ok(1337)); + panic!("huh"); } Err(err) => { - if emu.pc_read().unwrap() == 0 { + let mut instruction = [0]; + + let pc = emu.pc_read().unwrap(); + let sp = get_stack_pointer(emu); + + 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]; - let pc = emu.reg_read(RegisterARM64::SP).unwrap(); - emu.mem_read(pc - 1, &mut buf) + emu.mem_read(sp - 1, &mut buf) .expect("Could not read memory"); // check result if buf[0] != 0x4 { // didn't found the correct value + if DEBUG { + println!("Incorrect output found!"); + println!("Output: {:#}", buf[0]); + + memory_dump(emu, 2); + } return; } diff --git a/libafl_unicorn/src/helper.rs b/libafl_unicorn/src/helper.rs index bf990f5124..9e5a6c142a 100644 --- a/libafl_unicorn/src/helper.rs +++ b/libafl_unicorn/src/helper.rs @@ -1,9 +1,9 @@ -use unicorn_engine::RegisterARM64; +use unicorn_engine::{unicorn_const::Arch, RegisterARM, RegisterARM64, RegisterX86}; pub fn memory_dump(emu: &mut unicorn_engine::Unicorn<()>, len: u64) { - let pc = emu.reg_read(RegisterARM64::SP).unwrap(); + let sp = get_stack_pointer(emu); for i in 0..len { - let pos = pc - len * 4 + i * 4; + let pos = sp + i * 4 - len * 4; let data = emu.mem_read_as_vec(pos, 4).unwrap(); @@ -13,3 +13,13 @@ pub fn memory_dump(emu: &mut unicorn_engine::Unicorn<()>, len: u64) { ); } } + +pub fn get_stack_pointer(emu: &mut unicorn_engine::Unicorn<()>) -> u64 { + let sp = match emu.get_arch() { + Arch::ARM => emu.reg_read(RegisterARM::SP).unwrap(), + Arch::ARM64 => emu.reg_read(RegisterARM64::SP).unwrap(), + Arch::X86 => emu.reg_read(RegisterX86::ESP).unwrap(), + _ => 0, + }; + sp +} diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index 91b02a61c2..d2e7b45045 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -23,18 +23,59 @@ use libafl::{ state::StdState, }; pub use libafl_targets::{EDGES_MAP_PTR, EDGES_MAP_SIZE}; -use unicorn_engine::RegisterARM64; +use unicorn_engine::{unicorn_const::Arch, RegisterARM64, RegisterX86}; -pub const MAX_INPUT_SIZE: usize = 0x1000; //1048576; // 1MB +pub const MAX_INPUT_SIZE: usize = 0x8000; //1048576; // 1MB // emulating -fn fuzzer() { +fn fuzzer(should_emulate: bool) { let input_addr_end: u64 = 0x8000; let input_addr_start: u64 = input_addr_end - MAX_INPUT_SIZE as u64; - let emu = &mut Emulator::new(); - emu.setup(input_addr_start, MAX_INPUT_SIZE); + let emu = &mut Emulator::new(unicorn_engine::unicorn_const::Arch::X86); + emu.setup( + input_addr_start, + MAX_INPUT_SIZE, + "libafl_unicorn_test/foo_x86", + ); emu.set_code_hook(); + 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); + + match emu.get_arch() { + Arch::ARM | Arch::ARM64 => { + emu.write_reg(RegisterX86::SP, input_addr_end); + } + + Arch::X86 => { + // clean emulator state + for i in 1..259 { + emu.write_reg(i, 0); + } + + emu.write_reg(RegisterX86::ESP, input_addr_end); + } + _ => {} + } + + emu.run(); + + return ExitKind::Ok; + }; + + if should_emulate { + let mem_data: Vec = vec![0x50, 0x24, 0x0]; + harness(&BytesInput::from(mem_data)); + return; + } + let timeout = Duration::from_secs(1); let monitor = MultiMonitor::new(|s| println!("{s}")); @@ -87,22 +128,6 @@ fn fuzzer() { // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - 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.write_reg(RegisterARM64::SP, input_addr_end); - - emu.run(); - - return ExitKind::Ok; - }; - let executor = InProcessExecutor::new( &mut harness, tuple_list!(edges_observer, time_observer), @@ -133,11 +158,11 @@ fn fuzzer() { fn main() { let args: Vec<_> = env::args().collect(); + let mut emu = false; if args.len() > 1 { if args[1] == "emu" { - emu::emulate(); - return; + emu = true; } } - fuzzer(); + fuzzer(emu); } From 4a6e7f24c73238ac052bd068f186108b03ce628e Mon Sep 17 00:00:00 2001 From: henri2h Date: Sun, 5 Feb 2023 17:21:28 +0100 Subject: [PATCH 25/31] fix: add possibility to fuzz different CPU platform --- libafl_unicorn/src/emu.rs | 45 +++++++++++++++++++++++++++----------- libafl_unicorn/src/main.rs | 22 ++++++++++++++----- 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/libafl_unicorn/src/emu.rs b/libafl_unicorn/src/emu.rs index aa1773ff8b..20dad9284f 100644 --- a/libafl_unicorn/src/emu.rs +++ b/libafl_unicorn/src/emu.rs @@ -4,7 +4,7 @@ use iced_x86::{Decoder, DecoderOptions, Formatter, Instruction, NasmFormatter}; pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; use unicorn_engine::{ unicorn_const::{uc_error, Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}, - RegisterARM64, RegisterX86, Unicorn, + RegisterARM, RegisterARM64, RegisterX86, Unicorn, }; static DEBUG: bool = false; @@ -24,8 +24,16 @@ pub struct Emulator { impl Emulator { pub fn new(arch: Arch) -> Emulator { - let emu = unicorn_engine::Unicorn::new(arch, Mode::MODE_64) - .expect("failed to initialize Unicorn instance"); + let emu = unicorn_engine::Unicorn::new( + arch, + match arch { + Arch::ARM => Mode::ARM, + Arch::ARM64 => Mode::ARM, + Arch::X86 => Mode::MODE_64, + _ => Mode::MODE_64, + }, + ) + .expect("failed to initialize Unicorn instance"); Emulator { emu, code_len: 0 } } @@ -48,6 +56,13 @@ impl Emulator { .expect("failed to write instructions"); } + pub fn positioned_write(&mut self, addr: u64, end_addr: u64, buf: &[u8]) { + //println!("{} -> {}", addr, addr + (buf.len() as u64)); + self.emu + .mem_write(addr, &buf) + .expect("failed to write instructions"); + } + pub fn set_memory_hook(&mut self, addr: u64, length: u64) { self.emu .add_mem_hook(HookType::MEM_ALL, addr - length, addr, callback) @@ -93,7 +108,12 @@ fn load_code(emu: &mut Unicorn<()>, address: u64, path: &str) -> u64 { // Define memory regions emu.mem_map( address, - ((arm_code.len() / 4096) + 1) * 4096, + match emu.get_arch() { + Arch::ARM => ((arm_code.len() / 1024) + 1) * 1024, + Arch::ARM64 => ((arm_code.len() / 1024) + 1) * 1024, + Arch::X86 => ((arm_code.len() / 4096) + 1) * 4096, + _ => 0, + }, Permission::EXEC, ) .expect("failed to map code page"); @@ -117,7 +137,10 @@ fn debug_print(emu: &mut Unicorn<()>, err: uc_error) { let arch = emu.get_arch(); match arch { - Arch::ARM | Arch::ARM64 => { + Arch::ARM => { + println!("SP: {:X}", emu.reg_read(RegisterARM::SP).unwrap()); + } + Arch::ARM64 => { println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); println!("X0: {:X}", emu.reg_read(RegisterARM64::X0).unwrap()); println!("X1: {:X}", emu.reg_read(RegisterARM64::X1).unwrap()); @@ -172,8 +195,6 @@ fn debug_print(emu: &mut Unicorn<()>, err: uc_error) { output.clear(); formatter.format(&instruction, &mut output); - // Eg. "00007FFAC46ACDB2 488DAC2400FFFFFF lea rbp,[rsp-100h]" - let diff = instruction.ip() as i64 - pc as i64; print!("{:02}\t{:016X} ", diff, instruction.ip()); let start_index = (instruction.ip() - begin) as usize; @@ -222,14 +243,12 @@ fn callback( return true; } -pub fn emulate() { - let mem_data = [0x50, 0x24, 0x0]; - // TODO -} - pub fn prog(emu: &mut unicorn_engine::Unicorn<'static, ()>, arm_code_len: u64) { let result = emu.emu_start( - CODE_ADDRESS, // + 0x40, // start at main. Position of main: 0x40 + 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 + arm_code_len, 10 * SECOND_SCALE, 0x1000, diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index d2e7b45045..0bca618cad 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -5,6 +5,7 @@ pub mod hooks; use std::{char::MAX, env, path::PathBuf, time::Duration}; use emu::Emulator; +use iced_x86::Register; use libafl::{ bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice}, corpus::{InMemoryCorpus, OnDiskCorpus}, @@ -23,19 +24,25 @@ use libafl::{ state::StdState, }; pub use libafl_targets::{EDGES_MAP_PTR, EDGES_MAP_SIZE}; -use unicorn_engine::{unicorn_const::Arch, RegisterARM64, RegisterX86}; +use unicorn_engine::{unicorn_const::Arch, RegisterARM, RegisterARM64, RegisterX86}; pub const MAX_INPUT_SIZE: usize = 0x8000; //1048576; // 1MB // emulating fn fuzzer(should_emulate: bool) { + let arch = Arch::ARM64; let input_addr_end: u64 = 0x8000; let input_addr_start: u64 = input_addr_end - MAX_INPUT_SIZE as u64; - let emu = &mut Emulator::new(unicorn_engine::unicorn_const::Arch::X86); + let emu = &mut Emulator::new(arch); emu.setup( input_addr_start, MAX_INPUT_SIZE, - "libafl_unicorn_test/foo_x86", + match arch { + Arch::ARM => "libafl_unicorn_test/foo_arm", + Arch::ARM64 => "libafl_unicorn_test/foo_arm64", + Arch::X86 => "libafl_unicorn_test/foo_x86", + _ => "", + }, ); emu.set_code_hook(); @@ -50,10 +57,12 @@ fn fuzzer(should_emulate: bool) { emu.write_mem(input_addr_end - buf.len() as u64, buf); match emu.get_arch() { - Arch::ARM | Arch::ARM64 => { - emu.write_reg(RegisterX86::SP, input_addr_end); + Arch::ARM => { + emu.write_reg(RegisterARM::SP, input_addr_end); + } + Arch::ARM64 => { + emu.write_reg(RegisterARM64::SP, input_addr_end); } - Arch::X86 => { // clean emulator state for i in 1..259 { @@ -71,6 +80,7 @@ fn fuzzer(should_emulate: bool) { }; if should_emulate { + println!("Starting emulation:"); let mem_data: Vec = vec![0x50, 0x24, 0x0]; harness(&BytesInput::from(mem_data)); return; From 6384d95984817be7a4f50bcc7949a483fb0074ad Mon Sep 17 00:00:00 2001 From: henri2h Date: Mon, 6 Feb 2023 16:32:45 +0100 Subject: [PATCH 26/31] feat: change signal --- libafl_unicorn/libafl_unicorn_test/foo.c | 10 +++--- libafl_unicorn/libafl_unicorn_test/foo_arm | Bin 960 -> 960 bytes libafl_unicorn/libafl_unicorn_test/foo_arm.s | 13 ++++---- libafl_unicorn/libafl_unicorn_test/foo_arm64 | Bin 1496 -> 1496 bytes .../libafl_unicorn_test/foo_arm64.s | 11 ++++--- libafl_unicorn/libafl_unicorn_test/foo_x86 | Bin 1360 -> 1360 bytes libafl_unicorn/libafl_unicorn_test/foo_x86.s | 10 +++--- libafl_unicorn/src/emu.rs | 30 +++++++++++------- libafl_unicorn/src/hooks.rs | 5 ++- libafl_unicorn/src/main.rs | 2 +- 10 files changed, 45 insertions(+), 36 deletions(-) diff --git a/libafl_unicorn/libafl_unicorn_test/foo.c b/libafl_unicorn/libafl_unicorn_test/foo.c index 11346887d0..efabee26fc 100644 --- a/libafl_unicorn/libafl_unicorn_test/foo.c +++ b/libafl_unicorn/libafl_unicorn_test/foo.c @@ -11,14 +11,14 @@ int main() { for(int i = 0; i< len; i++){ f[i] = i; }*/ - c = 0x0; + c = 0x1; if (a > b) { - c = 0x1; + c = 0x2; if (a > 0x20) { - c = 0x2; + c = 0x3; if (a == 0x50) { - c = 0x3; - if (b == 0x24) { c = 0x4; } + c = 0x4; + if (b == 0x24) { c = 0x5; } } } } diff --git a/libafl_unicorn/libafl_unicorn_test/foo_arm b/libafl_unicorn/libafl_unicorn_test/foo_arm index 6d3688a26d793b4bb2259bdf6e320d3a24fe4a5a..a1f707c4fc231abbba5491e5a895b221d3538aab 100644 GIT binary patch delta 99 zcmX@Wet>;~d7u)ba_|kMJ4B6bvXv_ovD0n0E delta 99 zcmX@Wet>;~d7yId4|W3(p8JDUVeSt$gIP|zHyOdAKoJ84ZMK_Ca9)5m^M$!T*cq5M i6u2`(McE8gv|0AEfO#LDGwk2k!NSNGw7Hkjm>~i=qNUL(qSF2FnLP{LtLN@`1I3C4&rukUWE8_98}x5QmA0;*1FsE7chr zCN30bTrhE`@x%`bd>srx;K(Ns#>L0r#69^Ulhk4hMn=Yt&6Z62fz;%m%;t>~3+sP-2Fw2weZ?ge7#e~e067oL9V{7S7=+{-7z$r9FoZZvtW}pxfJs_I zB_}WxKK%DTq+#M-V@8IFZIR S8IMdhWU*&_wYiWboe=;7jwclW diff --git a/libafl_unicorn/libafl_unicorn_test/foo_arm64.s b/libafl_unicorn/libafl_unicorn_test/foo_arm64.s index 5f960a09e3..43252756ce 100644 --- a/libafl_unicorn/libafl_unicorn_test/foo_arm64.s +++ b/libafl_unicorn/libafl_unicorn_test/foo_arm64.s @@ -11,20 +11,21 @@ main: .cfi_startproc sub sp, sp, #16 .cfi_def_cfa_offset 16 + mov w0, 1 strb wzr, [sp, 15] - strb wzr, [sp, 15] + strb w0, [sp, 15] ldrb w1, [sp, 13] ldrb w0, [sp, 14] and w0, w0, 255 cmp w0, w1, uxtb bcs .L3 - mov w0, 1 + mov w0, 2 strb w0, [sp, 15] ldrb w0, [sp, 13] and w0, w0, 255 cmp w0, 32 bls .L3 - mov w0, 2 + mov w0, 3 strb w0, [sp, 15] ldrb w0, [sp, 13] and w0, w0, 255 @@ -39,13 +40,13 @@ main: ret .L7: .cfi_restore_state - mov w0, 3 + mov w0, 4 strb w0, [sp, 15] ldrb w0, [sp, 14] and w0, w0, 255 cmp w0, 36 bne .L3 - mov w0, 4 + mov w0, 5 strb w0, [sp, 15] b .L3 .cfi_endproc diff --git a/libafl_unicorn/libafl_unicorn_test/foo_x86 b/libafl_unicorn/libafl_unicorn_test/foo_x86 index 56162606b1a8849072354db1610cfad74ba45aca..da37d026e05dd359d3387423ba31ea631ede3e8e 100644 GIT binary patch delta 79 zcmcb>b%ASwuL~ppHW!t@{M$lQ{#hIqbSECBAxAfW&N delta 79 zcmcb>b%ASwuL}eJHW!t@{M$lQ{#hI, err: uc_error) { } fn callback( - _unicorn: &mut unicorn_engine::Unicorn<()>, + emu: &mut unicorn_engine::Unicorn<()>, mem: MemType, address: u64, size: usize, @@ -225,17 +225,25 @@ fn callback( if DEBUG { match mem { MemType::WRITE => println!( - "Memory is being WRITTEN at adress: {:X} size: {} value: {}", - address, size, value + "0x{:X}\tMemory is being WRITTEN at adress: {:X} size: {} value: {}", + emu.pc_read().unwrap(), + address, + size, + value ), MemType::READ => println!( - "Memory is being READ at adress: {:X} size: {}", - address, size + "0x{}\tMemory is being READ at adress: {:X} size: {}", + emu.pc_read().unwrap(), + address, + size ), - _ => println!( - "Memory access type: {:?} adress: {:X} size: {} value: {}", - mem, address, size, value + "0x{}\tMemory access type: {:?} adress: {:X} size: {} value: {}", + emu.pc_read().unwrap(), + mem, + address, + size, + value ), } } @@ -283,7 +291,7 @@ pub fn prog(emu: &mut unicorn_engine::Unicorn<'static, ()>, arm_code_len: u64) { .expect("Could not read memory"); // check result - if buf[0] != 0x4 { + if buf[0] != 0x5 { // didn't found the correct value if DEBUG { println!("Incorrect output found!"); diff --git a/libafl_unicorn/src/hooks.rs b/libafl_unicorn/src/hooks.rs index 33c7d3ae6f..c4494da2b7 100644 --- a/libafl_unicorn/src/hooks.rs +++ b/libafl_unicorn/src/hooks.rs @@ -1,12 +1,11 @@ pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; - static mut PREV_LOC: u64 = 0; -pub fn block_hook(emu: &mut unicorn_engine::Unicorn<()>, address: u64, small: u32) { +pub fn block_hook(_emu: &mut unicorn_engine::Unicorn<()>, address: u64, small: u32) { unsafe { let hash = (address ^ PREV_LOC) & (EDGES_MAP_SIZE as u64 - 1); - // println!("Hash {}", hash); + //println!("Block hook: 0x{:X}\t size:{:#} hash: {:X}", address, small, hash); EDGES_MAP[hash as usize] += 1; PREV_LOC = address >> 1; } diff --git a/libafl_unicorn/src/main.rs b/libafl_unicorn/src/main.rs index 0bca618cad..32446680dc 100644 --- a/libafl_unicorn/src/main.rs +++ b/libafl_unicorn/src/main.rs @@ -30,7 +30,7 @@ pub const MAX_INPUT_SIZE: usize = 0x8000; //1048576; // 1MB // emulating fn fuzzer(should_emulate: bool) { - let arch = Arch::ARM64; + 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); From cf92098abe3dc5af92b54ad4ed2e5700f5107527 Mon Sep 17 00:00:00 2001 From: henri2h Date: Thu, 9 Feb 2023 17:19:07 +0100 Subject: [PATCH 27/31] feat: clean library --- fuzzers/unicorn/Cargo.toml | 12 + .../unicorn}/libafl_unicorn_test/Makefile | 0 .../unicorn}/libafl_unicorn_test/foo.c | 0 .../unicorn}/libafl_unicorn_test/foo_arm | Bin .../unicorn}/libafl_unicorn_test/foo_arm.s | 0 .../unicorn}/libafl_unicorn_test/foo_arm64 | Bin .../unicorn}/libafl_unicorn_test/foo_arm64.s | 0 .../unicorn}/libafl_unicorn_test/foo_x86 | Bin .../unicorn}/libafl_unicorn_test/foo_x86.s | 0 .../unicorn}/src/main.rs | 121 +++++- libafl_unicorn/Cargo.toml | 8 +- libafl_unicorn/src/emu.rs | 375 ++++++++---------- libafl_unicorn/src/helper.rs | 14 - libafl_unicorn/src/lib.rs | 3 + 14 files changed, 286 insertions(+), 247 deletions(-) create mode 100644 fuzzers/unicorn/Cargo.toml rename {libafl_unicorn => fuzzers/unicorn}/libafl_unicorn_test/Makefile (100%) rename {libafl_unicorn => fuzzers/unicorn}/libafl_unicorn_test/foo.c (100%) rename {libafl_unicorn => fuzzers/unicorn}/libafl_unicorn_test/foo_arm (100%) rename {libafl_unicorn => fuzzers/unicorn}/libafl_unicorn_test/foo_arm.s (100%) rename {libafl_unicorn => fuzzers/unicorn}/libafl_unicorn_test/foo_arm64 (100%) rename {libafl_unicorn => fuzzers/unicorn}/libafl_unicorn_test/foo_arm64.s (100%) rename {libafl_unicorn => fuzzers/unicorn}/libafl_unicorn_test/foo_x86 (100%) rename {libafl_unicorn => fuzzers/unicorn}/libafl_unicorn_test/foo_x86.s (100%) rename {libafl_unicorn => fuzzers/unicorn}/src/main.rs (61%) create mode 100644 libafl_unicorn/src/lib.rs diff --git a/fuzzers/unicorn/Cargo.toml b/fuzzers/unicorn/Cargo.toml new file mode 100644 index 0000000000..438a03ee78 --- /dev/null +++ b/fuzzers/unicorn/Cargo.toml @@ -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", version = "0.8.2" } + +unicorn-engine = "2.0.1" +iced-x86 = "1.18.0" \ No newline at end of file diff --git a/libafl_unicorn/libafl_unicorn_test/Makefile b/fuzzers/unicorn/libafl_unicorn_test/Makefile similarity index 100% rename from libafl_unicorn/libafl_unicorn_test/Makefile rename to fuzzers/unicorn/libafl_unicorn_test/Makefile diff --git a/libafl_unicorn/libafl_unicorn_test/foo.c b/fuzzers/unicorn/libafl_unicorn_test/foo.c similarity index 100% rename from libafl_unicorn/libafl_unicorn_test/foo.c rename to fuzzers/unicorn/libafl_unicorn_test/foo.c diff --git a/libafl_unicorn/libafl_unicorn_test/foo_arm b/fuzzers/unicorn/libafl_unicorn_test/foo_arm similarity index 100% rename from libafl_unicorn/libafl_unicorn_test/foo_arm rename to fuzzers/unicorn/libafl_unicorn_test/foo_arm diff --git a/libafl_unicorn/libafl_unicorn_test/foo_arm.s b/fuzzers/unicorn/libafl_unicorn_test/foo_arm.s similarity index 100% rename from libafl_unicorn/libafl_unicorn_test/foo_arm.s rename to fuzzers/unicorn/libafl_unicorn_test/foo_arm.s diff --git a/libafl_unicorn/libafl_unicorn_test/foo_arm64 b/fuzzers/unicorn/libafl_unicorn_test/foo_arm64 similarity index 100% rename from libafl_unicorn/libafl_unicorn_test/foo_arm64 rename to fuzzers/unicorn/libafl_unicorn_test/foo_arm64 diff --git a/libafl_unicorn/libafl_unicorn_test/foo_arm64.s b/fuzzers/unicorn/libafl_unicorn_test/foo_arm64.s similarity index 100% rename from libafl_unicorn/libafl_unicorn_test/foo_arm64.s rename to fuzzers/unicorn/libafl_unicorn_test/foo_arm64.s diff --git a/libafl_unicorn/libafl_unicorn_test/foo_x86 b/fuzzers/unicorn/libafl_unicorn_test/foo_x86 similarity index 100% rename from libafl_unicorn/libafl_unicorn_test/foo_x86 rename to fuzzers/unicorn/libafl_unicorn_test/foo_x86 diff --git a/libafl_unicorn/libafl_unicorn_test/foo_x86.s b/fuzzers/unicorn/libafl_unicorn_test/foo_x86.s similarity index 100% rename from libafl_unicorn/libafl_unicorn_test/foo_x86.s rename to fuzzers/unicorn/libafl_unicorn_test/foo_x86.s diff --git a/libafl_unicorn/src/main.rs b/fuzzers/unicorn/src/main.rs similarity index 61% rename from libafl_unicorn/src/main.rs rename to fuzzers/unicorn/src/main.rs index 32446680dc..69812fec68 100644 --- a/libafl_unicorn/src/main.rs +++ b/fuzzers/unicorn/src/main.rs @@ -1,11 +1,6 @@ -pub mod emu; -pub mod helper; -pub mod hooks; +use libafl_unicorn::emu::{Emulator, CODE_ADDRESS}; +use std::{env, path::PathBuf, time::Duration}; -use std::{char::MAX, env, path::PathBuf, time::Duration}; - -use emu::Emulator; -use iced_x86::Register; use libafl::{ bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice}, corpus::{InMemoryCorpus, OnDiskCorpus}, @@ -24,9 +19,10 @@ use libafl::{ state::StdState, }; pub use libafl_targets::{EDGES_MAP_PTR, EDGES_MAP_SIZE}; -use unicorn_engine::{unicorn_const::Arch, RegisterARM, RegisterARM64, RegisterX86}; +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) { @@ -45,6 +41,7 @@ fn fuzzer(should_emulate: bool) { }, ); 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(); @@ -56,26 +53,70 @@ fn fuzzer(should_emulate: bool) { emu.write_mem(input_addr_end - buf.len() as u64, buf); - match emu.get_arch() { - Arch::ARM => { - emu.write_reg(RegisterARM::SP, input_addr_end); - } - Arch::ARM64 => { - emu.write_reg(RegisterARM64::SP, input_addr_end); + 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"); } - Arch::X86 => { - // clean emulator state - for i in 1..259 { - emu.write_reg(i, 0); + 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"); } - emu.write_reg(RegisterX86::ESP, input_addr_end); + 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); + } } - _ => {} } - emu.run(); - return ExitKind::Ok; }; @@ -166,6 +207,42 @@ fn fuzzer(should_emulate: bool) { .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; diff --git a/libafl_unicorn/Cargo.toml b/libafl_unicorn/Cargo.toml index 38a012b256..092b1c1a4d 100644 --- a/libafl_unicorn/Cargo.toml +++ b/libafl_unicorn/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "unicorn_test" +name = "libafl_unicorn" version.workspace = true authors = [""] -description = "Frida backend library for LibAFL" +description = "Unicorn backend library for LibAFL" documentation = "https://docs.rs/" repository = "https://github.com/AFLplusplus/" readme = "../README.md" @@ -18,3 +18,7 @@ hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] unicorn-engine = "2.0.1" iced-x86 = "1.18.0" + +[lib] +name = "libafl_unicorn" +crate-type = ["cdylib", "rlib"] \ No newline at end of file diff --git a/libafl_unicorn/src/emu.rs b/libafl_unicorn/src/emu.rs index 0639f81292..558c4e588b 100644 --- a/libafl_unicorn/src/emu.rs +++ b/libafl_unicorn/src/emu.rs @@ -3,19 +3,14 @@ use std::{fs::File, io::Read}; use iced_x86::{Decoder, DecoderOptions, Formatter, Instruction, NasmFormatter}; pub use libafl_targets::{edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_SIZE, MAX_EDGES_NUM}; use unicorn_engine::{ - unicorn_const::{uc_error, Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}, + unicorn_const::{uc_error, Arch, HookType, MemType, Mode, Permission}, RegisterARM, RegisterARM64, RegisterX86, Unicorn, }; -static DEBUG: bool = true; - -static CODE_ADDRESS: u64 = 0x9000; +pub static CODE_ADDRESS: u64 = 0x9000; static HEXBYTES_COLUMN_BYTE_LENGTH: usize = 10; -use crate::{ - helper::{get_stack_pointer, memory_dump}, - hooks::block_hook, -}; +use crate::{helper::get_stack_pointer, hooks::block_hook}; pub struct Emulator { emu: unicorn_engine::Unicorn<'static, ()>, @@ -45,8 +40,8 @@ impl Emulator { .expect("failed to map data page"); } - pub fn run(&mut self) { - prog(&mut self.emu, self.code_len); + pub fn get_code_len(&self) -> u64 { + self.code_len } pub fn write_mem(&mut self, addr: u64, buf: &[u8]) { @@ -56,17 +51,13 @@ impl Emulator { .expect("failed to write instructions"); } - pub fn positioned_write(&mut self, addr: u64, end_addr: u64, buf: &[u8]) { - //println!("{} -> {}", addr, addr + (buf.len() as u64)); - self.emu - .mem_write(addr, &buf) - .expect("failed to write instructions"); - } - - pub fn set_memory_hook(&mut self, addr: u64, length: u64) { + pub fn set_memory_hook(&mut self, addr: u64, length: usize, callback: F) + where + F: FnMut(&mut Unicorn<()>, MemType, u64, usize, i64) -> bool, + { self.emu - .add_mem_hook(HookType::MEM_ALL, addr, addr + length, callback) - .expect("Failed to register watcher"); + .add_mem_hook(HookType::MEM_ALL, addr, addr + length as u64, callback) + .expect("Could not set memory hooks"); } pub fn set_code_hook(&mut self) { @@ -75,7 +66,7 @@ impl Emulator { .expect("Failed to register code hook"); } - pub fn write_reg(&mut self, regid: T, value: u64) + pub fn reg_write(&mut self, regid: T, value: u64) where T: Into, { @@ -91,123 +82,161 @@ impl Emulator { self.emu.reg_read(regid) } + pub fn init_registers(&mut self, sp: u64) { + match self.emu.get_arch() { + Arch::ARM => { + self.emu + .reg_write(RegisterARM::SP, sp) + .expect("Could not setup register"); + } + Arch::ARM64 => { + self.emu + .reg_write(RegisterARM64::SP, sp) + .expect("Could not setup register"); + } + Arch::X86 => { + // clean emulator state + for i in 1..259 { + self.emu.reg_write(i, 0).expect("Could not clean register"); + } + + self.emu + .reg_write(RegisterX86::ESP, sp) + .expect("Could not setup register"); + } + _ => {} + } + } + + pub fn mem_read(&self, address: u64, buf: &mut [u8]) -> Result<(), uc_error> { + self.emu.mem_read(address, buf) + } + + pub fn get_stack_pointer(&mut self) -> u64 { + get_stack_pointer(&mut self.emu) + } + + pub fn pc_read(&self) -> Result { + self.emu.pc_read() + } + pub fn get_arch(&self) -> Arch { return self.emu.get_arch(); } -} - -fn load_code(emu: &mut Unicorn<()>, address: u64, path: &str) -> u64 { - let mut f = File::open(path).expect("Could not open file"); - let mut buffer = Vec::new(); - // read the whole file - f.read_to_end(&mut buffer).expect("Could not read file"); + pub fn memory_dump(&mut self, len: u64) { + let sp = get_stack_pointer(&mut self.emu); + for i in 0..len { + let pos = sp + i * 4 - len * 4; - let arm_code = buffer; + let data = self.emu.mem_read_as_vec(pos, 4).unwrap(); - // Define memory regions - emu.mem_map( - address, - match emu.get_arch() { - Arch::ARM => ((arm_code.len() / 1024) + 1) * 1024, - Arch::ARM64 => ((arm_code.len() / 1024) + 1) * 1024, - Arch::X86 => ((arm_code.len() / 4096) + 1) * 4096, - _ => 0, - }, - Permission::EXEC, - ) - .expect("failed to map code page"); + println!( + "{:X}:\t {:02X} {:02X} {:02X} {:02X} {:08b} {:08b} {:08b} {:08b}", + pos, data[0], data[1], data[2], data[3], data[0], data[1], data[2], data[3] + ); + } + } - // Write memory - emu.mem_write(address, &arm_code) - .expect("failed to write instructions"); - return arm_code.len() as u64; -} + pub fn emu_start( + &mut self, + begin: u64, + until: u64, + timeout: u64, + count: usize, + ) -> Result<(), uc_error> { + self.emu.emu_start(begin, until, timeout, count) + } -fn debug_print(emu: &mut Unicorn<()>, err: uc_error) { - println!(); - println!("Snap... something went wrong"); - println!("Error: {:?}", err); + pub fn debug_print(&self, err: uc_error) { + println!(); + println!("Snap... something went wrong"); + println!("Error: {:?}", err); - let pc = emu.pc_read().unwrap(); - println!(); - println!("Status when crash happened"); + let pc = self.emu.pc_read().unwrap(); + println!(); + println!("Status when crash happened"); - println!("PC: {:X}", pc); - let arch = emu.get_arch(); + println!("PC: {:X}", pc); + let arch = self.emu.get_arch(); - match arch { - Arch::ARM => { - println!("SP: {:X}", emu.reg_read(RegisterARM::SP).unwrap()); - } - Arch::ARM64 => { - println!("SP: {:X}", emu.reg_read(RegisterARM64::SP).unwrap()); - println!("X0: {:X}", emu.reg_read(RegisterARM64::X0).unwrap()); - println!("X1: {:X}", emu.reg_read(RegisterARM64::X1).unwrap()); - println!("X2: {:X}", emu.reg_read(RegisterARM64::X2).unwrap()); - println!("X3: {:X}", emu.reg_read(RegisterARM64::X3).unwrap()); - } - Arch::X86 => { - println!("ESP: {:X}", emu.reg_read(RegisterX86::ESP).unwrap()); - println!("RAX: {:X}", emu.reg_read(RegisterX86::RAX).unwrap()); - println!("RCX: {:X}", emu.reg_read(RegisterX86::RCX).unwrap()); - println!("RPB: {:X}", emu.reg_read(RegisterX86::RBP).unwrap()); - println!("RSP: {:X}", emu.reg_read(RegisterX86::RSP).unwrap()); - println!("EAX: {:X}", emu.reg_read(RegisterX86::EAX).unwrap()); - println!("ECX: {:X}", emu.reg_read(RegisterX86::ECX).unwrap()); - println!("EDX: {:X}", emu.reg_read(RegisterX86::EDX).unwrap()); + match arch { + Arch::ARM => { + println!("SP: {:X}", self.emu.reg_read(RegisterARM::SP).unwrap()); + } + Arch::ARM64 => { + println!("SP: {:X}", self.emu.reg_read(RegisterARM64::SP).unwrap()); + println!("X0: {:X}", self.emu.reg_read(RegisterARM64::X0).unwrap()); + println!("X1: {:X}", self.emu.reg_read(RegisterARM64::X1).unwrap()); + println!("X2: {:X}", self.emu.reg_read(RegisterARM64::X2).unwrap()); + println!("X3: {:X}", self.emu.reg_read(RegisterARM64::X3).unwrap()); + } + Arch::X86 => { + println!("ESP: {:X}", self.emu.reg_read(RegisterX86::ESP).unwrap()); + println!("RAX: {:X}", self.emu.reg_read(RegisterX86::RAX).unwrap()); + println!("RCX: {:X}", self.emu.reg_read(RegisterX86::RCX).unwrap()); + println!("RPB: {:X}", self.emu.reg_read(RegisterX86::RBP).unwrap()); + println!("RSP: {:X}", self.emu.reg_read(RegisterX86::RSP).unwrap()); + println!("EAX: {:X}", self.emu.reg_read(RegisterX86::EAX).unwrap()); + println!("ECX: {:X}", self.emu.reg_read(RegisterX86::ECX).unwrap()); + println!("EDX: {:X}", self.emu.reg_read(RegisterX86::EDX).unwrap()); + } + _ => {} } - _ => {} - } - if emu.get_arch() == Arch::X86 { - // Provide dissasembly at instant of crash for X86 assembly - let regions = emu.mem_regions().expect("Could not get memory regions"); - for i in 0..regions.len() { - if regions[i].perms == Permission::EXEC { - if pc >= regions[i].begin && pc <= regions[i].end { - let mut begin = pc - 32; - let mut end = pc + 32; - if begin < regions[i].begin { - begin = regions[i].begin; - } - if end > regions[i].end { - end = regions[i].end; - } + if self.emu.get_arch() == Arch::X86 { + // Provide dissasembly at instant of crash for X86 assembly + let regions = self + .emu + .mem_regions() + .expect("Could not get memory regions"); + for i in 0..regions.len() { + if regions[i].perms == Permission::EXEC { + if pc >= regions[i].begin && pc <= regions[i].end { + let mut begin = pc - 32; + let mut end = pc + 32; + if begin < regions[i].begin { + begin = regions[i].begin; + } + if end > regions[i].end { + end = regions[i].end; + } - let bytes = emu - .mem_read_as_vec(begin, (end - begin) as usize) - .expect("Could not get program code"); + let bytes = self + .emu + .mem_read_as_vec(begin, (end - begin) as usize) + .expect("Could not get program code"); - let mut decoder = Decoder::with_ip(64, &bytes, begin, DecoderOptions::NONE); + let mut decoder = Decoder::with_ip(64, &bytes, begin, DecoderOptions::NONE); - let mut formatter = NasmFormatter::new(); - formatter.options_mut().set_digit_separator("`"); - formatter.options_mut().set_first_operand_char_index(10); + let mut formatter = NasmFormatter::new(); + formatter.options_mut().set_digit_separator("`"); + formatter.options_mut().set_first_operand_char_index(10); - let mut instruction = Instruction::default(); - let mut output = String::new(); + let mut instruction = Instruction::default(); + let mut output = String::new(); - while decoder.can_decode() { - decoder.decode_out(&mut instruction); + while decoder.can_decode() { + decoder.decode_out(&mut instruction); - // Format the instruction ("disassemble" it) - output.clear(); - formatter.format(&instruction, &mut output); + // Format the instruction ("disassemble" it) + output.clear(); + formatter.format(&instruction, &mut output); - let diff = instruction.ip() as i64 - pc as i64; - print!("{:02}\t{:016X} ", diff, instruction.ip()); - let start_index = (instruction.ip() - begin) as usize; - let instr_bytes = &bytes[start_index..start_index + instruction.len()]; - for b in instr_bytes.iter() { - print!("{:02X}", b); - } - if instr_bytes.len() < HEXBYTES_COLUMN_BYTE_LENGTH { - for _ in 0..HEXBYTES_COLUMN_BYTE_LENGTH - instr_bytes.len() { - print!(" "); + let diff = instruction.ip() as i64 - pc as i64; + print!("{:02}\t{:016X} ", diff, instruction.ip()); + let start_index = (instruction.ip() - begin) as usize; + let instr_bytes = &bytes[start_index..start_index + instruction.len()]; + for b in instr_bytes.iter() { + print!("{:02X}", b); + } + if instr_bytes.len() < HEXBYTES_COLUMN_BYTE_LENGTH { + for _ in 0..HEXBYTES_COLUMN_BYTE_LENGTH - instr_bytes.len() { + print!(" "); + } } + println!(" {}", output); } - println!(" {}", output); } } } @@ -215,102 +244,30 @@ fn debug_print(emu: &mut Unicorn<()>, err: uc_error) { } } -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 - ), - } - } +fn load_code(emu: &mut Unicorn<()>, address: u64, path: &str) -> u64 { + let mut f = File::open(path).expect("Could not open file"); + let mut buffer = Vec::new(); - return true; -} + // read the whole file + f.read_to_end(&mut buffer).expect("Could not read file"); -pub fn prog(emu: &mut unicorn_engine::Unicorn<'static, ()>, arm_code_len: u64) { - let result = emu.emu_start( + let arm_code = buffer; + + // Define memory regions + emu.mem_map( + address, 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, + Arch::ARM => ((arm_code.len() / 1024) + 1) * 1024, + Arch::ARM64 => ((arm_code.len() / 1024) + 1) * 1024, + Arch::X86 => ((arm_code.len() / 4096) + 1) * 4096, + _ => 0, }, - CODE_ADDRESS + arm_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 = get_stack_pointer(emu); - - 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]); - - memory_dump(emu, 2); - } - return; - } - - // success - println!("Correct input found"); - println!("Output: {:#}", buf[0]); - memory_dump(emu, 2); + Permission::EXEC, + ) + .expect("failed to map code page"); - panic!("Success :)"); - } else { - debug_print(emu, err); - } - } - } + // Write memory + emu.mem_write(address, &arm_code) + .expect("failed to write instructions"); + return arm_code.len() as u64; } diff --git a/libafl_unicorn/src/helper.rs b/libafl_unicorn/src/helper.rs index 9e5a6c142a..756cd84a52 100644 --- a/libafl_unicorn/src/helper.rs +++ b/libafl_unicorn/src/helper.rs @@ -1,19 +1,5 @@ use unicorn_engine::{unicorn_const::Arch, RegisterARM, RegisterARM64, RegisterX86}; -pub fn memory_dump(emu: &mut unicorn_engine::Unicorn<()>, len: u64) { - let sp = get_stack_pointer(emu); - for i in 0..len { - let pos = sp + i * 4 - len * 4; - - let data = emu.mem_read_as_vec(pos, 4).unwrap(); - - println!( - "{:X}:\t {:02X} {:02X} {:02X} {:02X} {:08b} {:08b} {:08b} {:08b}", - pos, data[0], data[1], data[2], data[3], data[0], data[1], data[2], data[3] - ); - } -} - pub fn get_stack_pointer(emu: &mut unicorn_engine::Unicorn<()>) -> u64 { let sp = match emu.get_arch() { Arch::ARM => emu.reg_read(RegisterARM::SP).unwrap(), diff --git a/libafl_unicorn/src/lib.rs b/libafl_unicorn/src/lib.rs new file mode 100644 index 0000000000..b6adb06fb1 --- /dev/null +++ b/libafl_unicorn/src/lib.rs @@ -0,0 +1,3 @@ +pub mod emu; +pub mod helper; +pub mod hooks; \ No newline at end of file From 96f4d12d4464b00604e1088c575792578d4ce5a5 Mon Sep 17 00:00:00 2001 From: henri2h Date: Thu, 9 Feb 2023 17:37:49 +0100 Subject: [PATCH 28/31] fix: removed dependency constraints --- fuzzers/unicorn/Cargo.toml | 2 +- fuzzers/unicorn/src/main.rs | 2 +- libafl_unicorn/Cargo.toml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fuzzers/unicorn/Cargo.toml b/fuzzers/unicorn/Cargo.toml index 438a03ee78..81a47a6e3e 100644 --- a/fuzzers/unicorn/Cargo.toml +++ b/fuzzers/unicorn/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] libafl = { path = "../../libafl/" } libafl_unicorn = { path = "../../libafl_unicorn/" } -libafl_targets = { path = "../../libafl_targets", version = "0.8.2" } +libafl_targets = { path = "../../libafl_targets" } unicorn-engine = "2.0.1" iced-x86 = "1.18.0" \ No newline at end of file diff --git a/fuzzers/unicorn/src/main.rs b/fuzzers/unicorn/src/main.rs index 69812fec68..58fabb624e 100644 --- a/fuzzers/unicorn/src/main.rs +++ b/fuzzers/unicorn/src/main.rs @@ -150,7 +150,7 @@ fn fuzzer(should_emulate: bool) { // 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::new_with_observer(&time_observer) + TimeFeedback::with_observer(&time_observer) ); // A feedback to choose if an input is a solution or not diff --git a/libafl_unicorn/Cargo.toml b/libafl_unicorn/Cargo.toml index 092b1c1a4d..febbd38fdd 100644 --- a/libafl_unicorn/Cargo.toml +++ b/libafl_unicorn/Cargo.toml @@ -12,8 +12,8 @@ edition = "2021" categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"] [dependencies] -libafl = { path = "../libafl", version = "0.8.2", default-features = false, features = ["std", "derive", "llmp_compression"] } -libafl_targets = { path = "../libafl_targets", version = "0.8.2" } +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" From 9305d5459c1a14bc1f1acef37b4887e48b20e7ec Mon Sep 17 00:00:00 2001 From: henri2h Date: Thu, 9 Feb 2023 17:40:35 +0100 Subject: [PATCH 29/31] fix: removed binaries to make CI happy --- .../{libafl_unicorn_test => bin}/Makefile | 0 .../{libafl_unicorn_test => bin}/foo.c | 0 fuzzers/unicorn/libafl_unicorn_test/foo_arm | Bin 960 -> 0 bytes fuzzers/unicorn/libafl_unicorn_test/foo_arm.s | 62 ------------------ fuzzers/unicorn/libafl_unicorn_test/foo_arm64 | Bin 1496 -> 0 bytes .../unicorn/libafl_unicorn_test/foo_arm64.s | 56 ---------------- fuzzers/unicorn/libafl_unicorn_test/foo_x86 | Bin 1360 -> 0 bytes fuzzers/unicorn/libafl_unicorn_test/foo_x86.s | 55 ---------------- fuzzers/unicorn/src/main.rs | 6 +- 9 files changed, 3 insertions(+), 176 deletions(-) rename fuzzers/unicorn/{libafl_unicorn_test => bin}/Makefile (100%) rename fuzzers/unicorn/{libafl_unicorn_test => bin}/foo.c (100%) delete mode 100644 fuzzers/unicorn/libafl_unicorn_test/foo_arm delete mode 100644 fuzzers/unicorn/libafl_unicorn_test/foo_arm.s delete mode 100644 fuzzers/unicorn/libafl_unicorn_test/foo_arm64 delete mode 100644 fuzzers/unicorn/libafl_unicorn_test/foo_arm64.s delete mode 100644 fuzzers/unicorn/libafl_unicorn_test/foo_x86 delete mode 100644 fuzzers/unicorn/libafl_unicorn_test/foo_x86.s diff --git a/fuzzers/unicorn/libafl_unicorn_test/Makefile b/fuzzers/unicorn/bin/Makefile similarity index 100% rename from fuzzers/unicorn/libafl_unicorn_test/Makefile rename to fuzzers/unicorn/bin/Makefile diff --git a/fuzzers/unicorn/libafl_unicorn_test/foo.c b/fuzzers/unicorn/bin/foo.c similarity index 100% rename from fuzzers/unicorn/libafl_unicorn_test/foo.c rename to fuzzers/unicorn/bin/foo.c diff --git a/fuzzers/unicorn/libafl_unicorn_test/foo_arm b/fuzzers/unicorn/libafl_unicorn_test/foo_arm deleted file mode 100644 index a1f707c4fc231abbba5491e5a895b221d3538aab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 960 zcmah{O-my|5UrliB(k`Q;_8abM$t%2GbV!IEgLi!U6x(*17_lgLGnS;19}oXdGY2+ z$jSSXf8by6>Rs@vx7>ECCf(XDtXoj?>b-hBsjke~_U;a23{)`4!7Bqu8A-?9NFWDe zkb(07WZB~7(Dg9={?PXNL&v}V`r+Onl@#C0JNM=b`<^%N`a>6tL8od-)$y0|)~%he zr$?w(DqCKz7RD_Rd+SABUT-5fDc`>;Q6=G3C%ZM;-dv9kBN#NCRpI=0&+b}aCC~N?Z3E- z8y(HGk2p!^58G{Cha~~cpwR;0z0;-$LPXIW;gPCDbRvKs1R?+)_IkicKwr=maR+$4 z-E2lJbhO$c;?-}p0=DYMz{@}OcpyZ#5ym3wy_x8J@>$NRZZ-NYUeBoAevv-4t$+De z(3isepKndRPyb!omkPD@Efi=5Up6ifmt{>rJ<5tigd~2cLLzx6eq!QmorK_nd?iH? tm5)&WQXY^GFw>ay|I&OPC0BPv3Rie=oC+E0eG|xa-wihE%7mzVe*jCTVH5xW diff --git a/fuzzers/unicorn/libafl_unicorn_test/foo_arm.s b/fuzzers/unicorn/libafl_unicorn_test/foo_arm.s deleted file mode 100644 index 7dc9c82111..0000000000 --- a/fuzzers/unicorn/libafl_unicorn_test/foo_arm.s +++ /dev/null @@ -1,62 +0,0 @@ - .arch armv7-a - .fpu vfpv3-d16 - .eabi_attribute 28, 1 - .eabi_attribute 20, 1 - .eabi_attribute 21, 1 - .eabi_attribute 23, 3 - .eabi_attribute 24, 1 - .eabi_attribute 25, 1 - .eabi_attribute 26, 2 - .eabi_attribute 30, 2 - .eabi_attribute 34, 1 - .eabi_attribute 18, 4 - .file "foo.c" - .text - .section .text.startup,"ax",%progbits - .align 1 - .p2align 2,,3 - .global main - .syntax unified - .thumb - .thumb_func - .type main, %function -main: - @ args = 0, pretend = 0, frame = 8 - @ frame_needed = 0, uses_anonymous_args = 0 - @ link register save eliminated. - sub sp, sp, #8 - movs r2, #0 - movs r3, #1 - strb r2, [sp, #7] - strb r3, [sp, #7] - ldrb r2, [sp, #5] @ zero_extendqisi2 - ldrb r3, [sp, #6] @ zero_extendqisi2 - cmp r2, r3 - bls .L3 - movs r3, #2 - strb r3, [sp, #7] - ldrb r3, [sp, #5] @ zero_extendqisi2 - cmp r3, #32 - bls .L3 - movs r3, #3 - strb r3, [sp, #7] - ldrb r3, [sp, #5] @ zero_extendqisi2 - cmp r3, #80 - beq .L7 -.L3: - ldrb r0, [sp, #7] @ zero_extendqisi2 - add sp, sp, #8 - @ sp needed - bx lr -.L7: - movs r3, #4 - strb r3, [sp, #7] - ldrb r3, [sp, #6] @ zero_extendqisi2 - cmp r3, #36 - itt eq - moveq r3, #5 - strbeq r3, [sp, #7] - b .L3 - .size main, .-main - .ident "GCC: (Ubuntu 12.2.0-3ubuntu1) 12.2.0" - .section .note.GNU-stack,"",%progbits diff --git a/fuzzers/unicorn/libafl_unicorn_test/foo_arm64 b/fuzzers/unicorn/libafl_unicorn_test/foo_arm64 deleted file mode 100644 index 357be8c6d49e209fc713398549e71a004c10d60e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1496 zcmbtUK~ED=5dM}0K|q3yi6#&!2~kPp1@J(M2c9;{g?PYXxZ$=e!Qi&E-6n;LJ#xf< zK>QP4J$m(GJeH#uqlssineBV>9G1lBB(F2`&3yA_=Vkil$=cIC&w~{Y?{V%~6!_A2 zZtK+47{v&ZB0lBOSx;6_=+Ze|(uM9rUBD!AQ<&a+=b@x2&ap1VQoMuuSD^G#;@Yd9 z5Z+zU1*-d&b3gc7pF1|s);&i3^jA_kG5F6Uxrx-t^C5GT2%9M4_c|a zFlzwndpzwO@DA6}-@fflj85SeVSHGQ_~+5s_>18&JRV^uTX;tO_>p8q{mcmhKAa&s zM4uvA;?oUM22^A1&z414^^o}QCOvn zYAp`c>a&eG3RU)?8sY0eg}YljjUWomz5lBwr~tpQoT$E}u-utx!z_oC+k%Vh93&0E0ek4E1Sxcz>sixjuEoBGXl99!mY8ou0^?}j7$ z;#s-!oE+W~?Sf=)nu2!UuM);vVEGp3e&pQ*A#WPBn)i$UCM2Kh%C{p3+7!*PlDl4?;C#E;j$_DXGrO8UjP6A diff --git a/fuzzers/unicorn/libafl_unicorn_test/foo_arm64.s b/fuzzers/unicorn/libafl_unicorn_test/foo_arm64.s deleted file mode 100644 index 43252756ce..0000000000 --- a/fuzzers/unicorn/libafl_unicorn_test/foo_arm64.s +++ /dev/null @@ -1,56 +0,0 @@ - .arch armv8-a - .file "foo.c" - .text - .section .text.startup,"ax",@progbits - .align 2 - .p2align 4,,11 - .global main - .type main, %function -main: -.LFB0: - .cfi_startproc - sub sp, sp, #16 - .cfi_def_cfa_offset 16 - mov w0, 1 - strb wzr, [sp, 15] - strb w0, [sp, 15] - ldrb w1, [sp, 13] - ldrb w0, [sp, 14] - and w0, w0, 255 - cmp w0, w1, uxtb - bcs .L3 - mov w0, 2 - strb w0, [sp, 15] - ldrb w0, [sp, 13] - and w0, w0, 255 - cmp w0, 32 - bls .L3 - mov w0, 3 - strb w0, [sp, 15] - ldrb w0, [sp, 13] - and w0, w0, 255 - cmp w0, 80 - beq .L7 -.L3: - ldrb w0, [sp, 15] - add sp, sp, 16 - .cfi_remember_state - .cfi_def_cfa_offset 0 - and w0, w0, 255 - ret -.L7: - .cfi_restore_state - mov w0, 4 - strb w0, [sp, 15] - ldrb w0, [sp, 14] - and w0, w0, 255 - cmp w0, 36 - bne .L3 - mov w0, 5 - strb w0, [sp, 15] - b .L3 - .cfi_endproc -.LFE0: - .size main, .-main - .ident "GCC: (Ubuntu 12.2.0-3ubuntu1) 12.2.0" - .section .note.GNU-stack,"",@progbits diff --git a/fuzzers/unicorn/libafl_unicorn_test/foo_x86 b/fuzzers/unicorn/libafl_unicorn_test/foo_x86 deleted file mode 100644 index da37d026e05dd359d3387423ba31ea631ede3e8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1360 zcmbtTOH0E*5T3Nv`lz;w2SJet#Ruq8^&%n?sE>vAtqk${@k_uC0Yt7w@wQc$@eE=J+oblQI;)bMZ* z3)$?HKD3_m9q#L6<7}K|Mko9LjtvV!b`!%Og{uMA!qPfmn^(j|aglXZ9cV`@pdF;N zoiNRCltC}cgREs0g8R9?QQE*+B2ix3n0*Nk!G~UpN0a*Fa z=4K8(_o}`qBW@jVU|S|Pf#u3&U_@Y@>2bdVEbrR3@-NqeYMND}vWuZ$3DvvSj|t-B`N=v%28vhpgP3o^^;i7} zYH82!`u{jbtRbVj7q%$`q|by&jL!rfP&N^I{F(7Q7_$zXtY}pE$B%r^^*VU-6Tl~{U!mlR$O$( es=it9=<2K_^oct5^Q=2CuihP!oD04Gi~b!-m2GeU diff --git a/fuzzers/unicorn/libafl_unicorn_test/foo_x86.s b/fuzzers/unicorn/libafl_unicorn_test/foo_x86.s deleted file mode 100644 index d0485a4313..0000000000 --- a/fuzzers/unicorn/libafl_unicorn_test/foo_x86.s +++ /dev/null @@ -1,55 +0,0 @@ - .file "foo.c" - .text - .section .text.startup,"ax",@progbits - .p2align 4 - .globl main - .type main, @function -main: -.LFB0: - .cfi_startproc - endbr64 - movb $0, -1(%rsp) - movb $1, -1(%rsp) - movzbl -3(%rsp), %eax - movzbl -2(%rsp), %edx - cmpb %al, %dl - jnb .L3 - movb $2, -1(%rsp) - movzbl -3(%rsp), %eax - cmpb $32, %al - jbe .L3 - movb $3, -1(%rsp) - movzbl -3(%rsp), %eax - cmpb $80, %al - je .L6 -.L3: - movzbl -1(%rsp), %eax - ret -.L6: - movb $4, -1(%rsp) - movzbl -2(%rsp), %eax - cmpb $36, %al - jne .L3 - movb $5, -1(%rsp) - jmp .L3 - .cfi_endproc -.LFE0: - .size main, .-main - .ident "GCC: (Ubuntu 12.2.0-3ubuntu1) 12.2.0" - .section .note.GNU-stack,"",@progbits - .section .note.gnu.property,"a" - .align 8 - .long 1f - 0f - .long 4f - 1f - .long 5 -0: - .string "GNU" -1: - .align 8 - .long 0xc0000002 - .long 3f - 2f -2: - .long 0x3 -3: - .align 8 -4: diff --git a/fuzzers/unicorn/src/main.rs b/fuzzers/unicorn/src/main.rs index 58fabb624e..3610098d3b 100644 --- a/fuzzers/unicorn/src/main.rs +++ b/fuzzers/unicorn/src/main.rs @@ -34,9 +34,9 @@ fn fuzzer(should_emulate: bool) { input_addr_start, MAX_INPUT_SIZE, match arch { - Arch::ARM => "libafl_unicorn_test/foo_arm", - Arch::ARM64 => "libafl_unicorn_test/foo_arm64", - Arch::X86 => "libafl_unicorn_test/foo_x86", + Arch::ARM => "bin/foo_arm", + Arch::ARM64 => "bin/foo_arm64", + Arch::X86 => "bin/foo_x86", _ => "", }, ); From 8a9005cfbffb2083c2e16f6d4a273da32c1b061a Mon Sep 17 00:00:00 2001 From: henri2h Date: Thu, 9 Feb 2023 19:51:53 +0100 Subject: [PATCH 30/31] fix: disable memory hook --- fuzzers/unicorn/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/unicorn/src/main.rs b/fuzzers/unicorn/src/main.rs index 3610098d3b..a33ee5e67f 100644 --- a/fuzzers/unicorn/src/main.rs +++ b/fuzzers/unicorn/src/main.rs @@ -41,7 +41,7 @@ fn fuzzer(should_emulate: bool) { }, ); emu.set_code_hook(); - emu.set_memory_hook(input_addr_start, MAX_INPUT_SIZE, callback); + //emu.set_memory_hook(input_addr_start, MAX_INPUT_SIZE, callback); let mut harness = |input: &BytesInput| { let target = input.target_bytes(); From a842044742f3bce297c5ec2285e029ab3f490cda Mon Sep 17 00:00:00 2001 From: henri2h Date: Fri, 10 Feb 2023 17:38:14 +0100 Subject: [PATCH 31/31] fix: add new line --- libafl_unicorn/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libafl_unicorn/src/lib.rs b/libafl_unicorn/src/lib.rs index b6adb06fb1..f6cef3da8f 100644 --- a/libafl_unicorn/src/lib.rs +++ b/libafl_unicorn/src/lib.rs @@ -1,3 +1,3 @@ pub mod emu; pub mod helper; -pub mod hooks; \ No newline at end of file +pub mod hooks;