frida-asan: Un-inline report funclet to reduce code bloat (#81)

* frida-asan: Outline report funclet to reduce code bloat

* fmt
This commit is contained in:
s1341 2021-05-03 10:34:15 +03:00 committed by GitHub
parent 730df64712
commit a78a4b73fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 194 additions and 288 deletions

View File

@ -6,9 +6,8 @@ edition = "2018"
build = "build.rs"
[features]
default = ["std", "frida"]
default = ["std"]
std = []
frida = ["frida-gum", "frida-gum-sys"]
[profile.release]
lto = true
@ -22,10 +21,10 @@ num_cpus = "1.0"
which = "4.1"
[target.'cfg(unix)'.dependencies]
libafl = { path = "../../libafl/", features = [ "std" ] } #, "llmp_small_maps", "llmp_debug"]}
libafl = { path = "../../libafl/", features = [ "std", "llmp_compression" ] } #, "llmp_small_maps", "llmp_debug"]}
capstone = "0.8.0"
frida-gum = { version = "0.4", optional = true, features = [ "auto-download", "event-sink", "invocation-listener"] }
frida-gum-sys = { version = "0.2.4", optional = true, features = [ "auto-download", "event-sink", "invocation-listener"] }
frida-gum = { version = "0.4", git = "https://github.com/s1341/frida-rust", features = [ "auto-download", "event-sink", "invocation-listener"] }
#frida-gum = { version = "0.4", path = "../../../frida-rust/frida-gum", features = [ "auto-download", "event-sink", "invocation-listener"] }
libafl_frida = { path = "../../libafl_frida", version = "0.1.0" }
lazy_static = "1.4.0"
libc = "0.2"

View File

@ -89,7 +89,8 @@ __attribute__((noinline))
void func3( char * alloc) {
printf("func3\n");
if (random() == 0) {
alloc[0xff] = 0xde;
alloc[0x1ff] = 0xde;
printf("alloc[0x200]: %d\n", alloc[0x200]);
}
}
__attribute__((noinline))

View File

@ -20,8 +20,10 @@ libc = "0.2.92"
hashbrown = "0.11"
libloading = "0.7.0"
rangemap = "0.1.10"
frida-gum = { version = "0.4.0", features = [ "auto-download", "backtrace", "event-sink", "invocation-listener"] }
frida-gum-sys = { version = "0.2.4", features = [ "auto-download", "event-sink", "invocation-listener"] }
frida-gum = { version = "0.4.0", git = "https://github.com/s1341/frida-rust", features = [ "auto-download", "backtrace", "event-sink", "invocation-listener"] }
frida-gum-sys = { version = "0.2.4", git = "https://github.com/s1341/frida-rust", features = [ "auto-download", "event-sink", "invocation-listener"] }
#frida-gum = { version = "0.4.0", path = "../../frida-rust/frida-gum", features = [ "auto-download", "backtrace", "event-sink", "invocation-listener"] }
#frida-gum-sys = { version = "0.2.4", path = "../../frida-rust/frida-gum-sys", features = [ "auto-download", "event-sink", "invocation-listener"] }
regex = "1.4"
dynasmrt = "1.0.1"
capstone = "0.8.0"

View File

@ -595,6 +595,7 @@ extern "C" {
pub struct AsanRuntime {
regs: [usize; 32],
blob_report: Option<Box<[u8]>>,
blob_check_mem_byte: Option<Box<[u8]>>,
blob_check_mem_halfword: Option<Box<[u8]>>,
blob_check_mem_dword: Option<Box<[u8]>>,
@ -679,6 +680,7 @@ impl AsanRuntime {
pub fn new(options: FridaOptions) -> Rc<RefCell<AsanRuntime>> {
let res = Rc::new(RefCell::new(Self {
regs: [0; 32],
blob_report: None,
blob_check_mem_byte: None,
blob_check_mem_halfword: None,
blob_check_mem_dword: None,
@ -1421,113 +1423,12 @@ impl AsanRuntime {
}
}
/// Generate the instrumentation blobs for the current arch.
#[allow(clippy::similar_names)] // We allow things like dword and qword
fn generate_instrumentation_blobs(&mut self) {
fn generate_shadow_check_blob(&mut self, bit: u32) -> Box<[u8]> {
let shadow_bit = Allocator::get().shadow_bit as u32;
macro_rules! shadow_check {
($ops:ident, $bit:expr) => {dynasm!($ops
; .arch aarch64
//; brk #5
; b >skip_report
; report:
; stp x29, x30, [sp, #-0x10]!
; mov x29, sp
; ldr x0, >self_regs_addr
; stp x2, x3, [x0, #0x10]
; stp x4, x5, [x0, #0x20]
; stp x6, x7, [x0, #0x30]
; stp x8, x9, [x0, #0x40]
; stp x10, x11, [x0, #0x50]
; stp x12, x13, [x0, #0x60]
; stp x14, x15, [x0, #0x70]
; stp x16, x17, [x0, #0x80]
; stp x18, x19, [x0, #0x90]
; stp x20, x21, [x0, #0xa0]
; stp x22, x23, [x0, #0xb0]
; stp x24, x25, [x0, #0xc0]
; stp x26, x27, [x0, #0xd0]
; stp x28, x29, [x0, #0xe0]
; stp x30, xzr, [x0, #0xf0]
; mov x28, x0
; .dword (0xd53b4218u32 as i32) // mrs x24, nzcv
//; ldp x0, x1, [sp], #144
; ldp x0, x1, [sp, 0x10]
; stp x0, x1, [x28]
; adr x25, >done
; str x25, [x28, 0xf8]
; adr x25, <report
; adr x0, >eh_frame_fde
; adr x27, >fde_address
; ldr w26, [x27]
; cmp w26, #0x0
; b.ne >skip_register
; sub x25, x25, x27
; str w25, [x27]
; ldr x1, >register_frame_func
//; brk #11
; blr x1
; skip_register:
; ldr x0, >self_addr
; ldr x1, >trap_func
; blr x1
; .dword (0xd51b4218u32 as i32) // msr nzcv, x24
; ldr x0, >self_regs_addr
; ldp x2, x3, [x0, #0x10]
; ldp x4, x5, [x0, #0x20]
; ldp x6, x7, [x0, #0x30]
; ldp x8, x9, [x0, #0x40]
; ldp x10, x11, [x0, #0x50]
; ldp x12, x13, [x0, #0x60]
; ldp x14, x15, [x0, #0x70]
; ldp x16, x17, [x0, #0x80]
; ldp x18, x19, [x0, #0x90]
; ldp x20, x21, [x0, #0xa0]
; ldp x22, x23, [x0, #0xb0]
; ldp x24, x25, [x0, #0xc0]
; ldp x26, x27, [x0, #0xd0]
; ldp x28, x29, [x0, #0xe0]
; ldp x30, xzr, [x0, #0xf0]
; ldp x29, x30, [sp], #0x10
; b >done
; self_addr:
; .qword self as *mut _ as *mut c_void as i64
; self_regs_addr:
; .qword &mut self.regs as *mut _ as *mut c_void as i64
; trap_func:
; .qword AsanRuntime::handle_trap as *mut c_void as i64
; register_frame_func:
; .qword __register_frame as *mut c_void as i64
; eh_frame_cie:
; .dword 0x14
; .dword 0x00
; .dword 0x00527a01
; .dword 0x011e7c01
; .dword 0x001f0c1b
; eh_frame_fde:
; .dword 0x14
; .dword 0x18
; fde_address:
; .dword 0x0 // <-- address offset goes here
; .dword 0x104
//advance_loc 12
//def_cfa r29 (x29) at offset 16
//offset r30 (x30) at cfa-8
//offset r29 (x29) at cfa-16
; .dword 0x1d0c4c00
; .dword (0x9d029e10 as u32 as i32)
; .dword 0x04
// empty next FDE:
; .dword 0x0
; .dword 0x0
; skip_report:
; mov x1, #1
; add x1, xzr, x1, lsl #shadow_bit
; add x1, x1, x0, lsr #3
@ -1539,20 +1440,65 @@ impl AsanRuntime {
; lsr x1, x1, #16
; lsr x1, x1, x0
; tbnz x1, #$bit, >done
; b <report
; adr x1, >done
; nop // will be replaced by b to report
; done:
);};
}
let mut ops = dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
shadow_check!(ops, bit);
let ops_vec = ops.finalize().unwrap();
ops_vec[..ops_vec.len() - 4].to_vec().into_boxed_slice()
}
fn generate_shadow_check_exact_blob(&mut self, val: u32) -> Box<[u8]> {
let shadow_bit = Allocator::get().shadow_bit as u32;
macro_rules! shadow_check_exact {
($ops:ident, $val:expr) => {dynasm!($ops
; .arch aarch64
; b >skip_report
; mov x1, #1
; add x1, xzr, x1, lsl #shadow_bit
; add x1, x1, x0, lsr #3
; ubfx x1, x1, #0, #(shadow_bit + 2)
; ldrh w1, [x1, #0]
; and x0, x0, #7
; rev16 w1, w1
; rbit w1, w1
; lsr x1, x1, #16
; lsr x1, x1, x0
; .dword -717536768 // 0xd53b4200 //mrs x0, NZCV
; and x1, x1, #$val as u64
; cmp x1, #$val
; b.eq >done
; adr x1, >done
; nop // will be replaced by b to report
; done:
);};
}
let mut ops = dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
shadow_check_exact!(ops, val);
let ops_vec = ops.finalize().unwrap();
ops_vec[..ops_vec.len() - 4].to_vec().into_boxed_slice()
}
///
/// Generate the instrumentation blobs for the current arch.
#[allow(clippy::similar_names)] // We allow things like dword and qword
fn generate_instrumentation_blobs(&mut self) {
let mut ops_report = dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
dynasm!(ops_report
; .arch aarch64
; report:
; stp x29, x30, [sp, #-0x10]!
; mov x29, sp
// save the nvcz and the 'return-address'/address of instrumented instruction
; stp x0, x1, [sp, #-0x10]!
; ldr x0, >self_regs_addr
; stp x2, x3, [x0, #0x10]
@ -1571,14 +1517,14 @@ impl AsanRuntime {
; stp x28, x29, [x0, #0xe0]
; stp x30, xzr, [x0, #0xf0]
; mov x28, x0
; .dword (0xd53b4218u32 as i32) // mrs x24, nzcv
; ldp x0, x1, [sp, 0x10]
; stp x0, x1, [x28]
; adr x25, >done
; add x25, x25, 4
; mov x25, x1 // address of instrumented instruction.
; str x25, [x28, 0xf8]
; .dword 0xd53b4218u32 as i32 // mrs x24, nzcv
; ldp x0, x1, [sp, 0x20]
; stp x0, x1, [x28]
; adr x25, <report
; adr x0, >eh_frame_fde
; adr x27, >fde_address
@ -1595,7 +1541,7 @@ impl AsanRuntime {
; ldr x1, >trap_func
; blr x1
; .dword (0xd51b4218u32 as i32) // msr nzcv, x24
; .dword 0xd51b4218u32 as i32 // msr nzcv, x24
; ldr x0, >self_regs_addr
; ldp x2, x3, [x0, #0x10]
; ldp x4, x5, [x0, #0x20]
@ -1613,8 +1559,11 @@ impl AsanRuntime {
; ldp x28, x29, [x0, #0xe0]
; ldp x30, xzr, [x0, #0xf0]
// restore nzcv. and 'return address'
; ldp x0, x1, [sp], #0x10
; ldp x29, x30, [sp], #0x10
; b >done
; br x1 // go back to the 'return address'
; self_addr:
; .qword self as *mut _ as *mut c_void as i64
; self_regs_addr:
@ -1640,111 +1589,34 @@ impl AsanRuntime {
//offset r30 (x30) at cfa-8
//offset r29 (x29) at cfa-16
; .dword 0x1d0c4c00
; .dword (0x9d029e10 as u32 as i32)
; .dword 0x9d029e10 as u32 as i32
; .dword 0x04
// empty next FDE:
; .dword 0x0
; .dword 0x0
; skip_report:
; mov x1, #1
; add x1, xzr, x1, lsl #shadow_bit
; add x1, x1, x0, lsr #3
; ubfx x1, x1, #0, #(shadow_bit + 2)
; ldrh w1, [x1, #0]
; and x0, x0, #7
; rev16 w1, w1
; rbit w1, w1
; lsr x1, x1, #16
; lsr x1, x1, x0
; .dword -717536768 // 0xd53b4200 //mrs x0, NZCV
; and x1, x1, #$val
; cmp x1, #$val
; b.eq >done
; b <report
; done:
; .dword -719633920 //0xd51b4200 // msr nvcz, x0
);};
}
let mut ops_check_mem_byte =
dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
shadow_check!(ops_check_mem_byte, 0);
self.blob_check_mem_byte = Some(ops_check_mem_byte.finalize().unwrap().into_boxed_slice());
let mut ops_check_mem_halfword =
dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
shadow_check!(ops_check_mem_halfword, 1);
self.blob_check_mem_halfword = Some(
ops_check_mem_halfword
.finalize()
.unwrap()
.into_boxed_slice(),
);
self.blob_report = Some(ops_report.finalize().unwrap().into_boxed_slice());
let mut ops_check_mem_dword =
dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
shadow_check!(ops_check_mem_dword, 2);
self.blob_check_mem_dword =
Some(ops_check_mem_dword.finalize().unwrap().into_boxed_slice());
self.blob_check_mem_byte = Some(self.generate_shadow_check_blob(0));
self.blob_check_mem_halfword = Some(self.generate_shadow_check_blob(1));
self.blob_check_mem_dword = Some(self.generate_shadow_check_blob(2));
self.blob_check_mem_qword = Some(self.generate_shadow_check_blob(3));
self.blob_check_mem_16bytes = Some(self.generate_shadow_check_blob(4));
let mut ops_check_mem_qword =
dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
shadow_check!(ops_check_mem_qword, 3);
self.blob_check_mem_qword =
Some(ops_check_mem_qword.finalize().unwrap().into_boxed_slice());
let mut ops_check_mem_16bytes =
dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
shadow_check!(ops_check_mem_16bytes, 4);
self.blob_check_mem_16bytes =
Some(ops_check_mem_16bytes.finalize().unwrap().into_boxed_slice());
let mut ops_check_mem_3bytes =
dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
shadow_check_exact!(ops_check_mem_3bytes, 3);
self.blob_check_mem_3bytes =
Some(ops_check_mem_3bytes.finalize().unwrap().into_boxed_slice());
let mut ops_check_mem_6bytes =
dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
shadow_check_exact!(ops_check_mem_6bytes, 6);
self.blob_check_mem_6bytes =
Some(ops_check_mem_6bytes.finalize().unwrap().into_boxed_slice());
let mut ops_check_mem_12bytes =
dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
shadow_check_exact!(ops_check_mem_12bytes, 12);
self.blob_check_mem_12bytes =
Some(ops_check_mem_12bytes.finalize().unwrap().into_boxed_slice());
let mut ops_check_mem_24bytes =
dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
shadow_check_exact!(ops_check_mem_24bytes, 24);
self.blob_check_mem_24bytes =
Some(ops_check_mem_24bytes.finalize().unwrap().into_boxed_slice());
let mut ops_check_mem_32bytes =
dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
shadow_check_exact!(ops_check_mem_32bytes, 32);
self.blob_check_mem_32bytes =
Some(ops_check_mem_32bytes.finalize().unwrap().into_boxed_slice());
let mut ops_check_mem_48bytes =
dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
shadow_check_exact!(ops_check_mem_48bytes, 48);
self.blob_check_mem_48bytes =
Some(ops_check_mem_48bytes.finalize().unwrap().into_boxed_slice());
let mut ops_check_mem_64bytes =
dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
shadow_check_exact!(ops_check_mem_64bytes, 64);
self.blob_check_mem_64bytes =
Some(ops_check_mem_64bytes.finalize().unwrap().into_boxed_slice());
self.blob_check_mem_3bytes = Some(self.generate_shadow_check_exact_blob(3));
self.blob_check_mem_6bytes = Some(self.generate_shadow_check_exact_blob(6));
self.blob_check_mem_12bytes = Some(self.generate_shadow_check_exact_blob(12));
self.blob_check_mem_24bytes = Some(self.generate_shadow_check_exact_blob(24));
self.blob_check_mem_32bytes = Some(self.generate_shadow_check_exact_blob(32));
self.blob_check_mem_48bytes = Some(self.generate_shadow_check_exact_blob(48));
self.blob_check_mem_64bytes = Some(self.generate_shadow_check_exact_blob(64));
}
/// Get the blob which implements the report funclet
#[inline]
pub fn blob_report(&self) -> &[u8] {
self.blob_report.as_ref().unwrap()
}
/// Get the blob which checks a byte access
#[inline]
pub fn blob_check_mem_byte(&self) -> &[u8] {

View File

@ -58,6 +58,7 @@ pub struct FridaInstrumentationHelper<'a> {
map: [u8; MAP_SIZE],
previous_pc: [u64; 1],
current_log_impl: u64,
current_report_impl: u64,
/// Transformer that has to be passed to FridaInProcessExecutor
transformer: Option<Transformer<'a>>,
capstone: Capstone,
@ -205,6 +206,7 @@ impl<'a> FridaInstrumentationHelper<'a> {
map: [0u8; MAP_SIZE],
previous_pc: [0u64; 1],
current_log_impl: 0,
current_report_impl: 0,
transformer: None,
capstone: Capstone::new()
.arm64()
@ -315,7 +317,7 @@ impl<'a> FridaInstrumentationHelper<'a> {
#[cfg(target_arch = "aarch64")]
#[inline]
fn emit_shadow_check(
&self,
&mut self,
_address: u64,
output: &StalkerOutput,
basereg: capstone::RegId,
@ -334,6 +336,22 @@ impl<'a> FridaInstrumentationHelper<'a> {
None
};
if self.current_report_impl == 0
|| !writer.can_branch_directly_to(self.current_report_impl)
|| !writer.can_branch_directly_between(writer.pc() + 128, self.current_report_impl)
{
let after_report_impl = writer.code_offset() + 2;
#[cfg(target_arch = "x86_64")]
writer.put_jmp_near_label(after_report_impl);
#[cfg(target_arch = "aarch64")]
writer.put_b_label(after_report_impl);
self.current_report_impl = writer.pc();
writer.put_bytes(self.asan_runtime.borrow().blob_report());
writer.put_label(after_report_impl);
}
//writer.put_brk_imm(1);
// Preserve x0, x1:
@ -477,9 +495,23 @@ impl<'a> FridaInstrumentationHelper<'a> {
16 => writer.put_bytes(&self.asan_runtime.borrow().blob_check_mem_16bytes()),
24 => writer.put_bytes(&self.asan_runtime.borrow().blob_check_mem_24bytes()),
32 => writer.put_bytes(&self.asan_runtime.borrow().blob_check_mem_32bytes()),
48 => writer.put_bytes(&self.asan_runtime.borrow().blob_check_mem_48bytes()),
64 => writer.put_bytes(&self.asan_runtime.borrow().blob_check_mem_64bytes()),
_ => false,
};
// Add the branch to report
//writer.put_brk_imm(0x12);
writer.put_branch_address(self.current_report_impl);
match width {
3 | 6 | 12 | 24 | 32 | 48 | 64 => {
let msr_nvcz_x0: u32 = 0xd51b4200;
writer.put_bytes(&msr_nvcz_x0.to_le_bytes());
}
_ => (),
}
// Restore x0, x1
assert!(writer.put_ldp_reg_reg_reg_offset(
Aarch64Register::X0,