use core::ptr::addr_of_mut;
use std::{cell::RefCell, marker::PhantomPinned, pin::Pin, rc::Rc};
#[cfg(target_arch = "aarch64")]
use dynasmrt::DynasmLabelApi;
use dynasmrt::{dynasm, DynasmApi};
use frida_gum::{instruction_writer::InstructionWriter, stalker::StalkerOutput};
use libafl::bolts::xxh3_rrmxmx_mixer;
use rangemap::RangeMap;
use crate::helper::FridaRuntime;
pub const MAP_SIZE: usize = 64 * 1024;
#[derive(Debug)]
struct CoverageRuntimeInner {
map: [u8; MAP_SIZE],
previous_pc: u64,
_pinned: PhantomPinned,
}
#[derive(Debug)]
pub struct CoverageRuntime(Pin<Rc<RefCell<CoverageRuntimeInner>>>);
impl Default for CoverageRuntime {
fn default() -> Self {
Self::new()
}
}
impl FridaRuntime for CoverageRuntime {
fn init(
&mut self,
_gum: &frida_gum::Gum,
_ranges: &RangeMap<usize, (u16, String)>,
_modules_to_instrument: &[&str],
) {
}
fn pre_exec<I: libafl::inputs::Input + libafl::inputs::HasTargetBytes>(
&mut self,
_input: &I,
) -> Result<(), libafl::Error> {
Ok(())
}
fn post_exec<I: libafl::inputs::Input + libafl::inputs::HasTargetBytes>(
&mut self,
_input: &I,
) -> Result<(), libafl::Error> {
Ok(())
}
}
impl CoverageRuntime {
#[must_use]
pub fn new() -> Self {
Self(Rc::pin(RefCell::new(CoverageRuntimeInner {
map: [0_u8; MAP_SIZE],
previous_pc: 0,
_pinned: PhantomPinned,
})))
}
pub fn map_mut_ptr(&mut self) -> *mut u8 {
self.0.borrow_mut().map.as_mut_ptr()
}
#[cfg(target_arch = "aarch64")]
pub fn generate_inline_code(&mut self, h64: u64) -> Box<[u8]> {
let mut borrow = self.0.borrow_mut();
let prev_loc_ptr = addr_of_mut!(borrow.previous_pc);
let map_addr_ptr = addr_of_mut!(borrow.map);
let mut ops = dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
dynasm!(ops
; .arch aarch64
; stp x0, x1, [sp, #-0xa0]
; ldr x1, >previous_loc
; ldr x1, [x1]
; ldr x0, >loc
; eor x0, x1, x0
; ldr x1, >map_addr
; add x0, x1, x0
; ldrb w1, [x0]
; add w1, w1, #1
; add x1, x1, x1, lsr #8
; strb w1, [x0]
; ldr x0, >loc_shr
; ldr x1, >previous_loc
; str x0, [x1]
; ldp x0, x1, [sp, #-0xa0]
; b >end
;map_addr:
;.qword map_addr_ptr as i64
;previous_loc:
;.qword prev_loc_ptr as i64
;loc:
;.qword h64 as i64
;loc_shr:
;.qword (h64 >> 1) as i64
;end:
);
let ops_vec = ops.finalize().unwrap();
ops_vec[..ops_vec.len()].to_vec().into_boxed_slice()
}
#[cfg(target_arch = "x86_64")]
pub fn generate_inline_code(&mut self, h64: u64) -> Box<[u8]> {
let mut borrow = self.0.borrow_mut();
let prev_loc_ptr = addr_of_mut!(borrow.previous_pc);
let map_addr_ptr = addr_of_mut!(borrow.map);
let mut ops = dynasmrt::VecAssembler::<dynasmrt::x64::X64Relocation>::new(0);
dynasm!(ops
; .arch x64
; mov QWORD [rsp-0x88], rax
; lahf
; mov QWORD [rsp-0x90], rax
; mov QWORD [rsp-0x98], rbx
; mov rax, QWORD prev_loc_ptr as *mut u64 as _
; mov rax, QWORD [rax]
; mov ebx, WORD h64 as i32
; xor rax, rbx
; mov rbx, QWORD map_addr_ptr as *mut [u8; MAP_SIZE] as _
; add rax, rbx
; mov bl, BYTE [rax]
; add bl,0x1
; adc bl,0x0
; mov BYTE [rax],bl
; mov rax, QWORD prev_loc_ptr as *mut u64 as _
; mov ebx, WORD (h64 >> 1) as i32
; mov QWORD [rax], rbx
; mov rbx, QWORD [rsp-0x98]
; mov rax, QWORD [rsp-0x90]
; sahf
; mov rax, QWORD [rsp-0x88]
);
let ops_vec = ops.finalize().unwrap();
ops_vec[..ops_vec.len()].to_vec().into_boxed_slice()
}
#[inline]
pub fn emit_coverage_mapping(&mut self, address: u64, output: &StalkerOutput) {
let h64 = xxh3_rrmxmx_mixer(address);
let writer = output.writer();
let code = self.generate_inline_code(h64 & (MAP_SIZE as u64 - 1));
writer.put_bytes(&code);
}
}