add cmplog runtime instrumentation
This commit is contained in:
parent
e6b95c1a4c
commit
2bbff1b7ab
187
libafl_frida/src/cmplog_rt.rs
Normal file
187
libafl_frida/src/cmplog_rt.rs
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
|
||||||
|
use nix::{
|
||||||
|
libc::{memmove, memset},
|
||||||
|
sys::mman::{mmap, MapFlags, ProtFlags},
|
||||||
|
};
|
||||||
|
use std::ffi::c_void;
|
||||||
|
|
||||||
|
// TODO compile time flag
|
||||||
|
//pub const CMPLOG_MAP_W: usize = 65536;
|
||||||
|
pub const CMPLOG_MAP_W: usize = 8000;
|
||||||
|
pub const CMPLOG_MAP_H: usize = 32;
|
||||||
|
pub const CMPLOG_MAP_SIZE: usize = CMPLOG_MAP_W * CMPLOG_MAP_H;
|
||||||
|
|
||||||
|
pub const CMPLOG_KIND_INS: u8 = 0;
|
||||||
|
pub const CMPLOG_KIND_RTN: u8 = 1;
|
||||||
|
|
||||||
|
extern crate libafl_targets;
|
||||||
|
extern "C" {
|
||||||
|
pub fn __sanitizer_cov_trace_cmp8(arg1: u64, arg2: u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct CmpLogHeader {
|
||||||
|
hits: u16,
|
||||||
|
shape: u8,
|
||||||
|
kind: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct CmpLogOperands(u64, u64);
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct CmpLogMap {
|
||||||
|
headers: [CmpLogHeader; CMPLOG_MAP_W],
|
||||||
|
operands: [CmpLogOperands; CMPLOG_MAP_W],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub static mut libafl_cmplog_map: CmpLogMap = CmpLogMap {
|
||||||
|
headers: [CmpLogHeader {
|
||||||
|
hits: 0,
|
||||||
|
shape: 0,
|
||||||
|
kind: 0,
|
||||||
|
}; CMPLOG_MAP_W],
|
||||||
|
operands: [CmpLogOperands(0, 0); CMPLOG_MAP_W],
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use libafl_cmplog_map as CMPLOG_MAP;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub static mut libafl_cmplog_enabled: u8 = 0;
|
||||||
|
|
||||||
|
pub use libafl_cmplog_enabled as CMPLOG_ENABLED;
|
||||||
|
|
||||||
|
pub struct CmpLogRuntime {
|
||||||
|
regs: [u64; 2],
|
||||||
|
cmp_idx: usize,
|
||||||
|
cmplog_map: CmpLogMap,
|
||||||
|
ops_save_register_and_blr_to_populate: Option<Box<[u8]>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmpLogRuntime {
|
||||||
|
pub fn new() -> CmpLogRuntime {
|
||||||
|
Self {
|
||||||
|
regs: [0; 2],
|
||||||
|
cmp_idx: 0,
|
||||||
|
cmplog_map: CmpLogMap {
|
||||||
|
headers: [CmpLogHeader {
|
||||||
|
hits: 0,
|
||||||
|
shape: 0,
|
||||||
|
kind: 0,
|
||||||
|
}; CMPLOG_MAP_W],
|
||||||
|
operands: [CmpLogOperands(0, 0); CMPLOG_MAP_W],
|
||||||
|
},
|
||||||
|
ops_save_register_and_blr_to_populate: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn populate_lists(&mut self) {
|
||||||
|
let op1 = self.regs[0];
|
||||||
|
let op2 = self.regs[1];
|
||||||
|
|
||||||
|
self.cmplog_map.headers[self.cmp_idx].hits += 1;
|
||||||
|
self.cmplog_map.headers[self.cmp_idx].shape = 8;
|
||||||
|
let cmplog_ops: CmpLogOperands = CmpLogOperands(op1, op2);
|
||||||
|
self.cmplog_map.operands[self.cmp_idx] = cmplog_ops;
|
||||||
|
self.cmp_idx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate the instrumentation blobs for the current arch.
|
||||||
|
fn generate_instrumentation_blobs(&mut self) {
|
||||||
|
macro_rules! blr_to_populate {
|
||||||
|
($ops:ident, $bit:expr) => {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 x16, x17, [sp, #-0x10]!
|
||||||
|
; stp x18, x19, [sp, #-0x10]!
|
||||||
|
; stp x20, x21, [sp, #-0x10]!
|
||||||
|
; stp x22, x23, [sp, #-0x10]!
|
||||||
|
; stp x24, x25, [sp, #-0x10]!
|
||||||
|
; stp x26, x27, [sp, #-0x10]!
|
||||||
|
; stp x28, x29, [sp, #-0x10]!
|
||||||
|
; stp x30, xzr, [sp, #-0x10]!
|
||||||
|
// jump to rust based population of the lists
|
||||||
|
// ; ldr x2, >self_regs_addr
|
||||||
|
// ; stp x0, x1, [x2]
|
||||||
|
// jump to c implementation of cmplog pupolation
|
||||||
|
; ldr x2, >populate_lists
|
||||||
|
; blr x2
|
||||||
|
; ldp x30, xzr, [sp], #0x10
|
||||||
|
; ldp x28, x29, [sp], #0x10
|
||||||
|
; ldp x26, x27, [sp], #0x10
|
||||||
|
; ldp x24, x25, [sp], #0x10
|
||||||
|
; ldp x22, x23, [sp], #0x10
|
||||||
|
; ldp x20, x21, [sp], #0x10
|
||||||
|
; ldp x18, x19, [sp], #0x10
|
||||||
|
; ldp x16, x17, [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_regs_addr: //for rust based population of the lists..
|
||||||
|
; .qword &mut self.regs as *mut _ as *mut c_void as i64
|
||||||
|
; populate_lists:
|
||||||
|
; .qword __sanitizer_cov_trace_cmp8 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, 0);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// workaround frida's frida-gum-allocate-near bug:
|
||||||
|
unsafe {
|
||||||
|
for _ in 0..64 {
|
||||||
|
mmap(
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
128 * 1024,
|
||||||
|
ProtFlags::PROT_NONE,
|
||||||
|
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("Failed to map dummy regions for frida workaround");
|
||||||
|
mmap(
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
4 * 1024 * 1024,
|
||||||
|
ProtFlags::PROT_NONE,
|
||||||
|
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("Failed to map dummy regions for frida workaround");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.generate_instrumentation_blobs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the blob which saves the context, jumps to the populate function and restores the context
|
||||||
|
#[inline]
|
||||||
|
pub fn ops_save_register_and_blr_to_populate(&self) -> &[u8] {
|
||||||
|
self.ops_save_register_and_blr_to_populate.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
}
|
@ -37,7 +37,14 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
|
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
|
||||||
|
|
||||||
use crate::{asan_rt::AsanRuntime, FridaOptions};
|
use crate::{asan_rt::AsanRuntime, cmplog_rt::CmpLogRuntime, FridaOptions};
|
||||||
|
|
||||||
|
enum CmplogOperandType {
|
||||||
|
Regid(capstone::RegId),
|
||||||
|
Imm(u64),
|
||||||
|
Cimm(u64),
|
||||||
|
Mem(capstone::RegId, capstone::RegId, i32, u32),
|
||||||
|
}
|
||||||
|
|
||||||
/// An helper that feeds [`FridaInProcessExecutor`] with user-supplied instrumentation
|
/// An helper that feeds [`FridaInProcessExecutor`] with user-supplied instrumentation
|
||||||
pub trait FridaHelper<'a> {
|
pub trait FridaHelper<'a> {
|
||||||
@ -76,7 +83,8 @@ pub struct FridaInstrumentationHelper<'a> {
|
|||||||
transformer: Option<Transformer<'a>>,
|
transformer: Option<Transformer<'a>>,
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
capstone: Capstone,
|
capstone: Capstone,
|
||||||
asan_runtime: AsanRuntime,
|
asan_runtime: Rc<RefCell<AsanRuntime>>,
|
||||||
|
cmplog_runtime: CmpLogRuntime,
|
||||||
ranges: RangeMap<usize, (u16, &'a str)>,
|
ranges: RangeMap<usize, (u16, &'a str)>,
|
||||||
options: &'a FridaOptions,
|
options: &'a FridaOptions,
|
||||||
drcov_basic_blocks: Vec<DrCovBasicBlock>,
|
drcov_basic_blocks: Vec<DrCovBasicBlock>,
|
||||||
@ -261,6 +269,7 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
.build()
|
.build()
|
||||||
.expect("Failed to create Capstone object"),
|
.expect("Failed to create Capstone object"),
|
||||||
asan_runtime: AsanRuntime::new(options.clone()),
|
asan_runtime: AsanRuntime::new(options.clone()),
|
||||||
|
cmplog_runtime: CmpLogRuntime::new(),
|
||||||
ranges: RangeMap::new(),
|
ranges: RangeMap::new(),
|
||||||
options,
|
options,
|
||||||
drcov_basic_blocks: vec![],
|
drcov_basic_blocks: vec![],
|
||||||
@ -331,7 +340,7 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
todo!("Implement ASAN for non-aarch64 targets");
|
todo!("Implement ASAN for non-aarch64 targets");
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
if let Ok((basereg, indexreg, displacement, width, shift, extender)) =
|
if let Ok((basereg, indexreg, displacement, width, shift, extender)) =
|
||||||
helper.is_interesting_instruction(address, instr)
|
helper.is_interesting_asan_instruction(address, instr)
|
||||||
{
|
{
|
||||||
helper.emit_shadow_check(
|
helper.emit_shadow_check(
|
||||||
address,
|
address,
|
||||||
@ -345,6 +354,18 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if helper.options().cmplog_enabled() {
|
||||||
|
// check if this instruction is a compare instruction and if so save the registers values
|
||||||
|
if let Ok((op1, op2)) =
|
||||||
|
helper.is_interesting_cmplog_instruction(address, instr)
|
||||||
|
{
|
||||||
|
println!("emmiting at {} => {}", 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() {
|
if helper.options().asan_enabled() || helper.options().drcov_enabled() {
|
||||||
helper.asan_runtime.add_stalked_address(
|
helper.asan_runtime.add_stalked_address(
|
||||||
output.writer().pc() as usize - 4,
|
output.writer().pc() as usize - 4,
|
||||||
@ -359,6 +380,9 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
if helper.options().asan_enabled() || helper.options().drcov_enabled() {
|
if helper.options().asan_enabled() || helper.options().drcov_enabled() {
|
||||||
helper.asan_runtime.init(gum, modules_to_instrument);
|
helper.asan_runtime.init(gum, modules_to_instrument);
|
||||||
}
|
}
|
||||||
|
if helper.options.cmplog_enabled() {
|
||||||
|
helper.cmplog_runtime.init();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
helper
|
helper
|
||||||
}
|
}
|
||||||
@ -374,6 +398,311 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
Aarch64Register::from_u32(regint as u32).unwrap()
|
Aarch64Register::from_u32(regint as u32).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
#[inline]
|
||||||
|
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.get_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.get_writer_register(basereg);
|
||||||
|
let indexreg = if indexreg.0 != 0 {
|
||||||
|
Some(self.get_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.get_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.get_writer_register(basereg);
|
||||||
|
let indexreg = if indexreg.0 != 0 {
|
||||||
|
Some(self.get_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")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
#[inline]
|
#[inline]
|
||||||
fn emit_shadow_check(
|
fn emit_shadow_check(
|
||||||
@ -651,7 +980,7 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_interesting_instruction(
|
fn is_interesting_asan_instruction(
|
||||||
&self,
|
&self,
|
||||||
_address: u64,
|
_address: u64,
|
||||||
instr: &Insn,
|
instr: &Insn,
|
||||||
@ -701,6 +1030,68 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_interesting_cmplog_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.get_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.get_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]
|
#[inline]
|
||||||
fn emit_coverage_mapping(&mut self, address: u64, output: &StalkerOutput) {
|
fn emit_coverage_mapping(&mut self, address: u64, output: &StalkerOutput) {
|
||||||
let writer = output.writer();
|
let writer = output.writer();
|
||||||
|
@ -9,7 +9,11 @@ pub mod alloc;
|
|||||||
pub mod asan_errors;
|
pub mod asan_errors;
|
||||||
/// The frida address sanitizer runtime
|
/// The frida address sanitizer runtime
|
||||||
pub mod asan_rt;
|
pub mod asan_rt;
|
||||||
/// The `LibAFL` frida helper
|
|
||||||
|
/// The frida cmplog runtime
|
||||||
|
pub mod cmplog_rt;
|
||||||
|
|
||||||
|
/// The `LibAFL` firda helper
|
||||||
pub mod helper;
|
pub mod helper;
|
||||||
|
|
||||||
// for parsing asan cores
|
// for parsing asan cores
|
||||||
@ -28,6 +32,7 @@ pub struct FridaOptions {
|
|||||||
enable_coverage: bool,
|
enable_coverage: bool,
|
||||||
enable_drcov: bool,
|
enable_drcov: bool,
|
||||||
instrument_suppress_locations: Option<Vec<(String, usize)>>,
|
instrument_suppress_locations: Option<Vec<(String, usize)>>,
|
||||||
|
enable_cmplog: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FridaOptions {
|
impl FridaOptions {
|
||||||
@ -100,6 +105,9 @@ impl FridaOptions {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"cmplog" => {
|
||||||
|
options.enable_cmplog = value.parse().unwrap();
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
panic!("unknown FRIDA option: '{}'", option);
|
panic!("unknown FRIDA option: '{}'", option);
|
||||||
}
|
}
|
||||||
@ -144,6 +152,12 @@ impl FridaOptions {
|
|||||||
self.enable_drcov
|
self.enable_drcov
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is CmpLog enabled?
|
||||||
|
#[inline]
|
||||||
|
pub fn cmplog_enabled(&self) -> bool {
|
||||||
|
self.enable_cmplog
|
||||||
|
}
|
||||||
|
|
||||||
/// Should ASAN detect leaks
|
/// Should ASAN detect leaks
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -190,6 +204,7 @@ impl Default for FridaOptions {
|
|||||||
enable_coverage: true,
|
enable_coverage: true,
|
||||||
enable_drcov: false,
|
enable_drcov: false,
|
||||||
instrument_suppress_locations: None,
|
instrument_suppress_locations: None,
|
||||||
|
enable_cmplog: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user