Cmplog instrumentation for Frida (#99)

* libafl_targets: refactor sancov trace-pc

* cmp observer

* libaf_targets: new structure to isolate sancov

* fix C warning

* combined executor

* cmp observer and feedback

* I2SRandReplace mutator

* impl CmpMap for CmpLogMap in libafl_targets

* cmplog observer

* clippy

* TracingStage

* working random cmplog mutations

* enable cmplog for libfuzzer_stb_image

* re-enable new testcase stats print

* fix update stats display

* bump 0.3.1

* clippy

* clippy

* no clippy for fuzzers/

* fix

* add cmplog runtime instrumentation

* test cmplog against value profile feature

* fix compile error

* add target arch aarch64 for is_interesting_cmplog_instruction

* add cfg target aarch64 on cmplog related code within stalker loop

* revert changes in cargo.toml

* align code with 'main' branch

* revert accidently changed Cargo.toml file

* update cmplog runtime code to work with the cmplog backend implementation

* change magic to 8 bytes

* cmplog runs with observer- no crashes

* clippy fixes

* add cmplog_runtime as feature

* set cmplog command-line argument to false by default

* setup cmplog observer and mutator correctly

* decrease emitted code opcode count

* add cmplog testing to the harness

* get rid of irrelevant changes and unused code, add comments, change
feature name to "cmplog"

* get rid of some unessecery whitespaces and new lines

* fix clippy errors

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
Co-authored-by: Dominik Maier <domenukk@gmail.com>
Co-authored-by: Omree <you@example.com>
This commit is contained in:
OB 2021-06-09 15:11:43 +03:00 committed by GitHub
parent 9e9425c622
commit 7abd7c8162
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 711 additions and 122 deletions

View File

@ -21,10 +21,11 @@ num_cpus = "1.0"
which = "4.1"
[target.'cfg(unix)'.dependencies]
libafl = { path = "../../libafl/", features = [ "std", "llmp_bind_public" ] } #, "llmp_small_maps", "llmp_debug"]}
libafl_frida = { path = "../../libafl_frida" }
libafl = { path = "../../libafl/", features = [ "std", "llmp_compression", "llmp_bind_public" ] } #, "llmp_small_maps", "llmp_debug"]}
capstone = "0.8.0"
frida-gum = { version = "0.5.2", features = [ "auto-download", "backtrace", "event-sink", "invocation-listener"] }
libafl_frida = { path = "../../libafl_frida", version = "0.3.2", features = ["cmplog"] }
libafl_targets = { path = "../../libafl_targets", version = "0.3.2" , features = ["sancov_cmplog"] }
lazy_static = "1.4.0"
libc = "0.2"
libloading = "0.7.0"

View File

@ -108,6 +108,10 @@ void func1() {
// Roughly follows the libpng book example:
// http://www.libpng.org/pub/png/book/chapter13.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size >= 8 && *(uint64_t*)data == 0xABCDEFAA8F1324AA){
abort();
}
if (size < kPngHeaderSize) {
return 0;
}

View File

@ -29,10 +29,11 @@ use libafl::{
inputs::{BytesInput, HasTargetBytes, Input},
mutators::{
scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator},
token_mutations::I2SRandReplace,
token_mutations::Tokens,
},
observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver, TimeObserver},
stages::mutational::StdMutationalStage,
stages::{StdMutationalStage, TracingStage},
state::{HasCorpus, HasMetadata, StdState},
stats::MultiStats,
Error,
@ -57,6 +58,7 @@ use libafl_frida::{
helper::{FridaHelper, FridaInstrumentationHelper, MAP_SIZE},
FridaOptions,
};
use libafl_targets::cmplog::{CmpLogObserver, CMPLOG_MAP};
struct FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
where
@ -300,7 +302,7 @@ unsafe fn fuzz(
unsafe extern "C" fn(data: *const u8, size: usize) -> i32,
> = lib.get(symbol_name.as_bytes()).unwrap();
let mut frida_harness = move |input: &BytesInput| {
let mut frida_harness = |input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
(target_func)(buf.as_ptr(), buf.len());
@ -316,6 +318,9 @@ unsafe fn fuzz(
&modules_to_instrument,
);
// Create an observation channel using cmplog map
let cmplog_observer = CmpLogObserver::new("cmplog", &mut CMPLOG_MAP, true);
// Create an observation channel using the coverage map
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
"edges",
@ -378,7 +383,6 @@ unsafe fn fuzz(
// Setup a basic mutator with a mutational stage
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
// A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
@ -413,7 +417,54 @@ unsafe fn fuzz(
println!("We imported {} inputs from disk.", state.corpus().count());
}
if frida_options.cmplog_enabled() {
// Secondary harness due to mut ownership
let mut frida_harness = |input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
(target_func)(buf.as_ptr(), buf.len());
ExitKind::Ok
};
// Secondary helper due to mut ownership
let mut frida_helper = FridaInstrumentationHelper::new(
&gum,
&frida_options,
module_name,
&modules_to_instrument,
);
// Setup a tracing stage in which we log comparisons
let tracing = TracingStage::new(FridaInProcessExecutor::new(
&gum,
InProcessExecutor::new(
&mut frida_harness,
tuple_list!(cmplog_observer, AsanErrorsObserver::new(&ASAN_ERRORS)),
&mut fuzzer,
&mut state,
&mut mgr,
)?,
&mut frida_helper,
Duration::new(10, 0),
));
// Setup a randomic Input2State stage
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(
I2SRandReplace::new()
)));
// Setup a basic mutator
let mutational = StdMutationalStage::new(mutator);
// The order of the stages matter!
let mut stages = tuple_list!(tracing, i2s, mutational);
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
} else {
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
};
Ok(())
};

View File

@ -4,6 +4,7 @@ use alloc::{
string::{String, ToString},
vec::Vec,
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use crate::{

View File

@ -10,12 +10,17 @@ license = "MIT OR Apache-2.0"
keywords = ["fuzzing", "frida", "instrumentation"]
edition = "2018"
[features]
default = []
cmplog = []
[build-dependencies]
cc = { version = "1.0", features = ["parallel"] }
[dependencies]
libafl = { path = "../libafl", version = "0.3.1", features = ["std", "libafl_derive"] }
libafl_targets = { path = "../libafl_targets", version = "0.3.2" }
libafl_targets = { path = "../libafl_targets", version = "0.3.2", features = ["sancov_cmplog"] }
nix = "0.20.0"
libc = "0.2.92"
hashbrown = "0.11"

View File

@ -0,0 +1,101 @@
use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
use libafl_targets::cmplog::CMPLOG_MAP_W;
use std::ffi::c_void;
extern crate libafl_targets;
extern "C" {
pub fn libafl_targets_cmplog_wrapper(k: u64, shape: u8, arg1: u64, arg2: u64);
}
pub struct CmpLogRuntime {
ops_save_register_and_blr_to_populate: Option<Box<[u8]>>,
}
impl CmpLogRuntime {
#[must_use]
pub fn new() -> CmpLogRuntime {
Self {
ops_save_register_and_blr_to_populate: None,
}
}
/// Call the external function that populates the `cmplog_map` with the relevant values
extern "C" fn populate_lists(&mut self, op1: u64, op2: u64, retaddr: u64) {
// println!(
// "entered populate_lists with: {:#02x}, {:#02x}, {:#02x}",
// op1, op2, retaddr
// );
let mut k = (retaddr >> 4) ^ (retaddr << 8);
k &= (CMPLOG_MAP_W as u64) - 1;
unsafe {
libafl_targets_cmplog_wrapper(k, 8, op1, op2);
}
}
/// Generate the instrumentation blobs for the current arch.
fn generate_instrumentation_blobs(&mut self) {
macro_rules! blr_to_populate {
($ops:ident) => {dynasm!($ops
; .arch aarch64
; stp x2, x3, [sp, #-0x10]!
; stp x4, x5, [sp, #-0x10]!
; stp x6, x7, [sp, #-0x10]!
; stp x8, x9, [sp, #-0x10]!
; stp x10, x11, [sp, #-0x10]!
; stp x12, x13, [sp, #-0x10]!
; stp x14, x15, [sp, #-0x10]!
; stp x29, x30, [sp, #-0x10]!
// jump to rust based population of the lists
; mov x2, x0
; adr x3, >done
; ldr x4, >populate_lists
; ldr x0, >self_addr
; blr x4
// restore the reg state before returning to the caller
; ldp x29, x30, [sp], #0x10
; ldp x14, x15, [sp], #0x10
; ldp x12, x13, [sp], #0x10
; ldp x10, x11, [sp], #0x10
; ldp x8, x9, [sp], #0x10
; ldp x6, x7, [sp], #0x10
; ldp x4, x5, [sp], #0x10
; ldp x2, x3, [sp], #0x10
; b >done
; self_addr:
; .qword self as *mut _ as *mut c_void as i64
; populate_lists:
; .qword CmpLogRuntime::populate_lists as *mut c_void as i64
; done:
);};
}
let mut ops_save_register_and_blr_to_populate =
dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
blr_to_populate!(ops_save_register_and_blr_to_populate);
self.ops_save_register_and_blr_to_populate = Some(
ops_save_register_and_blr_to_populate
.finalize()
.unwrap()
.into_boxed_slice(),
);
}
pub fn init(&mut self) {
self.generate_instrumentation_blobs();
}
/// Get the blob which saves the context, jumps to the populate function and restores the context
#[inline]
#[must_use]
pub fn ops_save_register_and_blr_to_populate(&self) -> &[u8] {
self.ops_save_register_and_blr_to_populate.as_ref().unwrap()
}
}
impl Default for CmpLogRuntime {
fn default() -> Self {
Self::new()
}
}

View File

@ -33,9 +33,18 @@ use rangemap::RangeMap;
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
use crate::FridaOptions;
use crate::{asan_rt::AsanRuntime, FridaOptions};
use crate::asan_rt::AsanRuntime;
#[cfg(feature = "cmplog")]
use crate::cmplog_rt::CmpLogRuntime;
#[cfg(feature = "cmplog")]
enum CmplogOperandType {
Regid(capstone::RegId),
Imm(u64),
Cimm(u64),
Mem(capstone::RegId, capstone::RegId, i32, u32),
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
const ANONYMOUS_FLAG: MapFlags = MapFlags::MAP_ANON;
@ -80,6 +89,8 @@ pub struct FridaInstrumentationHelper<'a> {
#[cfg(target_arch = "aarch64")]
capstone: Capstone,
asan_runtime: AsanRuntime,
#[cfg(feature = "cmplog")]
cmplog_runtime: CmpLogRuntime,
ranges: RangeMap<usize, (u16, String)>,
module_map: ModuleMap,
options: &'a FridaOptions,
@ -265,6 +276,8 @@ impl<'a> FridaInstrumentationHelper<'a> {
.build()
.expect("Failed to create Capstone object"),
asan_runtime: AsanRuntime::new(options.clone()),
#[cfg(feature = "cmplog")]
cmplog_runtime: CmpLogRuntime::new(),
ranges: RangeMap::new(),
module_map: ModuleMap::new_from_names(modules_to_instrument),
options,
@ -327,7 +340,7 @@ impl<'a> FridaInstrumentationHelper<'a> {
todo!("Implement ASAN for non-aarch64 targets");
#[cfg(target_arch = "aarch64")]
if let Ok((basereg, indexreg, displacement, width, shift, extender)) =
helper.is_interesting_instruction(address, instr)
helper.asan_is_interesting_instruction(address, instr)
{
helper.emit_shadow_check(
address,
@ -341,6 +354,19 @@ impl<'a> FridaInstrumentationHelper<'a> {
);
}
}
if helper.options().cmplog_enabled() {
#[cfg(not(target_arch = "aarch64"))]
todo!("Implement cmplog for non-aarch64 targets");
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
// check if this instruction is a compare instruction and if so save the registers values
if let Ok((op1, op2)) =
helper.cmplog_is_interesting_instruction(address, instr)
{
//emit code that saves the relevant data in runtime(passes it to x0, x1)
helper.emit_comparison_handling(address, &output, op1, op2);
}
}
if helper.options().asan_enabled() || helper.options().drcov_enabled() {
helper.asan_runtime.add_stalked_address(
output.writer().pc() as usize - 4,
@ -355,6 +381,10 @@ impl<'a> FridaInstrumentationHelper<'a> {
if helper.options().asan_enabled() || helper.options().drcov_enabled() {
helper.asan_runtime.init(gum, modules_to_instrument);
}
#[cfg(feature = "cmplog")]
if helper.options.cmplog_enabled() {
helper.cmplog_runtime.init();
}
}
helper
}
@ -370,6 +400,312 @@ impl<'a> FridaInstrumentationHelper<'a> {
Aarch64Register::from_u32(regint as u32).unwrap()
}
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
#[inline]
/// Emit the instrumentation code which is responsible for opernads value extraction and cmplog map population
fn emit_comparison_handling(
&self,
_address: u64,
output: &StalkerOutput,
op1: CmplogOperandType,
op2: CmplogOperandType,
) {
let writer = output.writer();
// Preserve x0, x1:
writer.put_stp_reg_reg_reg_offset(
Aarch64Register::X0,
Aarch64Register::X1,
Aarch64Register::Sp,
-(16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i32) as i64,
IndexMode::PreAdjust,
);
// make sure operand1 value is saved into x0
match op1 {
CmplogOperandType::Imm(value) | CmplogOperandType::Cimm(value) => {
writer.put_ldr_reg_u64(Aarch64Register::X0, value);
}
CmplogOperandType::Regid(reg) => {
let reg = self.writer_register(reg);
match reg {
Aarch64Register::X0 | Aarch64Register::W0 => {}
Aarch64Register::X1 | Aarch64Register::W1 => {
writer.put_mov_reg_reg(Aarch64Register::X0, Aarch64Register::X1);
}
_ => {
if !writer.put_mov_reg_reg(Aarch64Register::X0, reg) {
writer.put_mov_reg_reg(Aarch64Register::W0, reg);
}
}
}
}
CmplogOperandType::Mem(basereg, indexreg, displacement, _width) => {
let basereg = self.writer_register(basereg);
let indexreg = if indexreg.0 != 0 {
Some(self.writer_register(indexreg))
} else {
None
};
// calculate base+index+displacment into x0
let displacement = displacement
+ if basereg == Aarch64Register::Sp {
16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i32
} else {
0
};
if indexreg.is_some() {
if let Some(indexreg) = indexreg {
writer.put_add_reg_reg_reg(Aarch64Register::X0, basereg, indexreg);
}
} else {
match basereg {
Aarch64Register::X0 | Aarch64Register::W0 => {}
Aarch64Register::X1 | Aarch64Register::W1 => {
writer.put_mov_reg_reg(Aarch64Register::X0, Aarch64Register::X1);
}
_ => {
if !writer.put_mov_reg_reg(Aarch64Register::X0, basereg) {
writer.put_mov_reg_reg(Aarch64Register::W0, basereg);
}
}
}
}
//add displacement
writer.put_add_reg_reg_imm(
Aarch64Register::X0,
Aarch64Register::X0,
displacement as u64,
);
//deref into x0 to get the real value
writer.put_ldr_reg_reg_offset(Aarch64Register::X0, Aarch64Register::X0, 0u64);
}
}
// make sure operand2 value is saved into x1
match op2 {
CmplogOperandType::Imm(value) | CmplogOperandType::Cimm(value) => {
writer.put_ldr_reg_u64(Aarch64Register::X1, value);
}
CmplogOperandType::Regid(reg) => {
let reg = self.writer_register(reg);
match reg {
Aarch64Register::X1 | Aarch64Register::W1 => {}
Aarch64Register::X0 | Aarch64Register::W0 => {
writer.put_ldr_reg_reg_offset(
Aarch64Register::X1,
Aarch64Register::Sp,
0u64,
);
}
_ => {
if !writer.put_mov_reg_reg(Aarch64Register::X1, reg) {
writer.put_mov_reg_reg(Aarch64Register::W1, reg);
}
}
}
}
CmplogOperandType::Mem(basereg, indexreg, displacement, _width) => {
let basereg = self.writer_register(basereg);
let indexreg = if indexreg.0 != 0 {
Some(self.writer_register(indexreg))
} else {
None
};
// calculate base+index+displacment into x1
let displacement = displacement
+ if basereg == Aarch64Register::Sp {
16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i32
} else {
0
};
if indexreg.is_some() {
if let Some(indexreg) = indexreg {
match indexreg {
Aarch64Register::X0 | Aarch64Register::W0 => {
match basereg {
Aarch64Register::X1 | Aarch64Register::W1 => {
// x0 is overwrittern indexreg by op1 value.
// x1 is basereg
// Preserve x2, x3:
writer.put_stp_reg_reg_reg_offset(
Aarch64Register::X2,
Aarch64Register::X3,
Aarch64Register::Sp,
-(16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i32) as i64,
IndexMode::PreAdjust,
);
//reload indexreg to x2
writer.put_ldr_reg_reg_offset(
Aarch64Register::X2,
Aarch64Register::Sp,
0u64,
);
//add them into basereg==x1
writer.put_add_reg_reg_reg(
basereg,
basereg,
Aarch64Register::X2,
);
// Restore x2, x3
assert!(writer.put_ldp_reg_reg_reg_offset(
Aarch64Register::X2,
Aarch64Register::X3,
Aarch64Register::Sp,
16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i64,
IndexMode::PostAdjust,
));
}
_ => {
// x0 is overwrittern indexreg by op1 value.
// basereg is not x1 nor x0
//reload indexreg to x1
writer.put_ldr_reg_reg_offset(
Aarch64Register::X1,
Aarch64Register::Sp,
0u64,
);
//add basereg into indexreg==x1
writer.put_add_reg_reg_reg(
Aarch64Register::X1,
basereg,
Aarch64Register::X1,
);
}
}
}
Aarch64Register::X1 | Aarch64Register::W1 => {
match basereg {
Aarch64Register::X0 | Aarch64Register::W0 => {
// x0 is overwrittern basereg by op1 value.
// x1 is indexreg
// Preserve x2, x3:
writer.put_stp_reg_reg_reg_offset(
Aarch64Register::X2,
Aarch64Register::X3,
Aarch64Register::Sp,
-(16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i32) as i64,
IndexMode::PreAdjust,
);
//reload basereg to x2
writer.put_ldr_reg_reg_offset(
Aarch64Register::X2,
Aarch64Register::Sp,
0u64,
);
//add basereg into indexreg==x1
writer.put_add_reg_reg_reg(
indexreg,
Aarch64Register::X2,
indexreg,
);
// Restore x2, x3
assert!(writer.put_ldp_reg_reg_reg_offset(
Aarch64Register::X2,
Aarch64Register::X3,
Aarch64Register::Sp,
16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i64,
IndexMode::PostAdjust,
));
}
_ => {
// indexreg is x1
// basereg is not x0 and not x1
//add them into x1
writer.put_add_reg_reg_reg(indexreg, basereg, indexreg);
}
}
}
_ => {
match basereg {
Aarch64Register::X0 | Aarch64Register::W0 => {
//basereg is overwrittern by op1 value
//index reg is not x0 nor x1
//reload basereg to x1
writer.put_ldr_reg_reg_offset(
Aarch64Register::X1,
Aarch64Register::Sp,
0u64,
);
//add indexreg to basereg==x1
writer.put_add_reg_reg_reg(
Aarch64Register::X1,
Aarch64Register::X1,
indexreg,
);
}
_ => {
//basereg is not x0, can be x1
//index reg is not x0 nor x1
//add them into x1
writer.put_add_reg_reg_reg(
Aarch64Register::X1,
basereg,
indexreg,
);
}
}
}
}
}
} else {
match basereg {
Aarch64Register::X1 | Aarch64Register::W1 => {}
Aarch64Register::X0 | Aarch64Register::W0 => {
// x0 is overwrittern basereg by op1 value.
//reload basereg to x1
writer.put_ldr_reg_reg_offset(
Aarch64Register::X1,
Aarch64Register::Sp,
0u64,
);
}
_ => {
writer.put_mov_reg_reg(Aarch64Register::W1, basereg);
}
}
}
// add displacement
writer.put_add_reg_reg_imm(
Aarch64Register::X1,
Aarch64Register::X1,
displacement as u64,
);
//deref into x1 to get the real value
writer.put_ldr_reg_reg_offset(Aarch64Register::X1, Aarch64Register::X1, 0u64);
}
}
//call cmplog runtime to populate the values map
writer.put_bytes(&self.cmplog_runtime.ops_save_register_and_blr_to_populate());
// Restore x0, x1
assert!(writer.put_ldp_reg_reg_reg_offset(
Aarch64Register::X0,
Aarch64Register::X1,
Aarch64Register::Sp,
16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i64,
IndexMode::PostAdjust,
));
}
#[cfg(target_arch = "aarch64")]
#[inline]
fn emit_shadow_check(
@ -647,7 +983,7 @@ impl<'a> FridaInstrumentationHelper<'a> {
#[cfg(target_arch = "aarch64")]
#[inline]
fn is_interesting_instruction(
fn asan_is_interesting_instruction(
&self,
_address: u64,
instr: &Insn,
@ -697,6 +1033,70 @@ impl<'a> FridaInstrumentationHelper<'a> {
Err(())
}
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
#[inline]
/// Check if the current instruction is cmplog relevant one(any opcode which sets the flags)
fn cmplog_is_interesting_instruction(
&self,
_address: u64,
instr: &Insn,
) -> Result<(CmplogOperandType, CmplogOperandType), ()> {
// We only care for compare instrunctions - aka instructions which set the flags
match instr.mnemonic().unwrap() {
"cmp" | "ands" | "subs" | "adds" | "negs" | "ngcs" | "sbcs" | "bics" | "cls" => (),
_ => return Err(()),
}
let operands = self
.capstone
.insn_detail(instr)
.unwrap()
.arch_detail()
.operands();
if operands.len() != 2 {
return Err(());
}
let operand1 = if let Arm64Operand(arm64operand) = operands.first().unwrap() {
match arm64operand.op_type {
Arm64OperandType::Reg(regid) => Some(CmplogOperandType::Regid(regid)),
Arm64OperandType::Imm(val) => Some(CmplogOperandType::Imm(val as u64)),
Arm64OperandType::Mem(opmem) => Some(CmplogOperandType::Mem(
opmem.base(),
opmem.index(),
opmem.disp(),
self.instruction_width(instr, &operands),
)),
Arm64OperandType::Cimm(val) => Some(CmplogOperandType::Cimm(val as u64)),
_ => return Err(()),
}
} else {
None
};
let operand2 = if let Arm64Operand(arm64operand2) = operands.last().unwrap() {
match arm64operand2.op_type {
Arm64OperandType::Reg(regid) => Some(CmplogOperandType::Regid(regid)),
Arm64OperandType::Imm(val) => Some(CmplogOperandType::Imm(val as u64)),
Arm64OperandType::Mem(opmem) => Some(CmplogOperandType::Mem(
opmem.base(),
opmem.index(),
opmem.disp(),
self.instruction_width(instr, &operands),
)),
Arm64OperandType::Cimm(val) => Some(CmplogOperandType::Cimm(val as u64)),
_ => return Err(()),
}
} else {
None
};
if operand1.is_some() && operand2.is_some() {
Ok((operand1.unwrap(), operand2.unwrap()))
} else {
Err(())
}
}
#[inline]
fn emit_coverage_mapping(&mut self, address: u64, output: &StalkerOutput) {
let writer = output.writer();

View File

@ -9,7 +9,12 @@ pub mod alloc;
pub mod asan_errors;
/// The frida address sanitizer runtime
pub mod asan_rt;
/// The `LibAFL` frida helper
#[cfg(feature = "cmplog")]
/// The frida cmplog runtime
pub mod cmplog_rt;
/// The `LibAFL` firda helper
pub mod helper;
// for parsing asan cores
@ -28,6 +33,7 @@ pub struct FridaOptions {
enable_coverage: bool,
enable_drcov: bool,
instrument_suppress_locations: Option<Vec<(String, usize)>>,
enable_cmplog: bool,
}
impl FridaOptions {
@ -100,6 +106,12 @@ impl FridaOptions {
);
}
}
"cmplog" => {
options.enable_cmplog = value.parse().unwrap();
if !cfg!(feature = "cmplog") && options.enable_cmplog {
panic!("cmplog feature is disabled!")
}
}
_ => {
panic!("unknown FRIDA option: '{}'", option);
}
@ -144,6 +156,13 @@ impl FridaOptions {
self.enable_drcov
}
/// Is `CmpLog` enabled?
#[must_use]
#[inline]
pub fn cmplog_enabled(&self) -> bool {
self.enable_cmplog
}
/// Should ASAN detect leaks
#[must_use]
#[inline]
@ -190,6 +209,7 @@ impl Default for FridaOptions {
enable_coverage: true,
enable_drcov: false,
instrument_suppress_locations: None,
enable_cmplog: false,
}
}
}

View File

@ -72,6 +72,12 @@ void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) {
}
#ifdef SANCOV_CMPLOG
void libafl_targets_cmplog_wrapper(uintptr_t k, uint8_t shape, uint64_t arg1, uint64_t arg2){
return __libafl_targets_cmplog(k, shape, arg1, arg2);
}
#endif
void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) {
uintptr_t rt = RETADDR;