From 225f77d6d14bb16dd838b27d0a92143f1d99b638 Mon Sep 17 00:00:00 2001 From: Wim de With Date: Thu, 24 Apr 2025 23:10:19 +0200 Subject: [PATCH 1/5] librasan: Use bx instruction for ARM patch --- libafl_qemu/librasan/asan/src/patch/raw.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libafl_qemu/librasan/asan/src/patch/raw.rs b/libafl_qemu/librasan/asan/src/patch/raw.rs index 8add18e2de..b4b752ca5a 100644 --- a/libafl_qemu/librasan/asan/src/patch/raw.rs +++ b/libafl_qemu/librasan/asan/src/patch/raw.rs @@ -71,11 +71,11 @@ impl RawPatch { #[cfg(target_arch = "arm")] fn get_patch(destination: GuestAddr) -> Result, RawPatchError> { // ldr ip, [pc] - // mov pc, ip + // bx ip // .long 0xdeadface let insns = [ [0x00, 0xc0, 0x9f, 0xe5].to_vec(), - [0x0c, 0xf0, 0xa0, 0xe1].to_vec(), + [0x1c, 0xff, 0x2f, 0xe1].to_vec(), [0xce, 0xfa, 0xad, 0xde].to_vec(), ]; let addr = destination.to_ne_bytes().to_vec(); From 50e12d3b2db8b803d0729535332c3f9720d0b65a Mon Sep 17 00:00:00 2001 From: Wim de With Date: Thu, 24 Apr 2025 23:10:41 +0200 Subject: [PATCH 2/5] librasan: Support patching Thumb functions --- libafl_qemu/librasan/asan/src/patch/raw.rs | 39 +++++- libafl_qemu/librasan/asan/tests/patch_raw.rs | 132 +++++++++++++++++++ 2 files changed, 170 insertions(+), 1 deletion(-) diff --git a/libafl_qemu/librasan/asan/src/patch/raw.rs b/libafl_qemu/librasan/asan/src/patch/raw.rs index b4b752ca5a..b308229bab 100644 --- a/libafl_qemu/librasan/asan/src/patch/raw.rs +++ b/libafl_qemu/librasan/asan/src/patch/raw.rs @@ -14,6 +14,8 @@ pub struct RawPatch; impl Patch for RawPatch { type Error = RawPatchError; + + #[cfg(not(target_arch = "arm"))] fn patch(target: GuestAddr, destination: GuestAddr) -> Result<(), Self::Error> { debug!("patch - addr: {:#x}, target: {:#x}", target, destination); if target == destination { @@ -25,6 +27,26 @@ impl Patch for RawPatch { dest.copy_from_slice(&patch); Ok(()) } + + #[cfg(target_arch = "arm")] + fn patch(target: GuestAddr, destination: GuestAddr) -> Result<(), Self::Error> { + debug!("patch - addr: {:#x}, target: {:#x}", target, destination); + if target == destination { + Err(RawPatchError::IdentityPatch(target))?; + } + + let patch = if target & 1 == 1 { + Self::get_patch_thumb(destination)? + } else { + Self::get_patch_arm(destination)? + }; + + trace!("patch: {:02x?}", patch); + let target = target & !1; + let dest = unsafe { from_raw_parts_mut(target as *mut u8, patch.len()) }; + dest.copy_from_slice(&patch); + Ok(()) + } } impl RawPatch { @@ -69,7 +91,7 @@ impl RawPatch { } #[cfg(target_arch = "arm")] - fn get_patch(destination: GuestAddr) -> Result, RawPatchError> { + fn get_patch_arm(destination: GuestAddr) -> Result, RawPatchError> { // ldr ip, [pc] // bx ip // .long 0xdeadface @@ -83,6 +105,21 @@ impl RawPatch { Ok(insns_mod.into_iter().flatten().cloned().collect()) } + #[cfg(target_arch = "arm")] + fn get_patch_thumb(destination: GuestAddr) -> Result, RawPatchError> { + // ldr ip, [pc, #2] + // bx ip + // .long 0xdeadface + let insns = [ + [0xdf, 0xf8, 0x02, 0xc0].to_vec(), + [0x60, 0x47].to_vec(), + [0xce, 0xfa, 0xad, 0xde].to_vec(), + ]; + let addr = destination.to_ne_bytes().to_vec(); + let insns_mod = [&insns[0], &insns[1], &addr]; + Ok(insns_mod.into_iter().flatten().cloned().collect()) + } + #[cfg(target_arch = "aarch64")] fn get_patch(destination: GuestAddr) -> Result, RawPatchError> { // ldr x16, #8 diff --git a/libafl_qemu/librasan/asan/tests/patch_raw.rs b/libafl_qemu/librasan/asan/tests/patch_raw.rs index 18a2a51f6a..42267af550 100644 --- a/libafl_qemu/librasan/asan/tests/patch_raw.rs +++ b/libafl_qemu/librasan/asan/tests/patch_raw.rs @@ -1,5 +1,8 @@ +#![cfg_attr(target_arch = "arm", feature(arm_target_feature))] + #[cfg(test)] #[cfg(feature = "libc")] +#[cfg(not(target_arch = "arm"))] mod tests { use asan::{ GuestAddr, @@ -55,3 +58,132 @@ mod tests { assert_eq!(ret, 0xd00df00d); } } + +#[cfg(test)] +#[cfg(feature = "libc")] +#[cfg(target_arch = "arm")] +mod tests { + use asan::{ + GuestAddr, + mmap::{Mmap, MmapProt, linux::LinuxMmap}, + patch::{Patch, raw::RawPatch}, + }; + use log::info; + + macro_rules! define_test_function { + ($fn_name:ident, $ret_val:expr) => { + define_test_function!([], $fn_name, $ret_val); + }; + + ($attr:meta, $fn_name:ident, $ret_val:expr) => { + define_test_function!([$attr], $fn_name, $ret_val); + }; + + ([$($attr:meta)*], $fn_name:ident, $ret_val:expr) => { + #[unsafe(no_mangle)] + $(#[$attr])* + extern "C" fn $fn_name( + a1: usize, + a2: usize, + a3: usize, + a4: usize, + a5: usize, + a6: usize, + ) -> usize { + assert_eq!(a1, 1); + assert_eq!(a2, 2); + assert_eq!(a3, 3); + assert_eq!(a4, 4); + assert_eq!(a5, 5); + assert_eq!(a6, 6); + return $ret_val; + } + }; + } + + macro_rules! define_test { + ( + $fn_name:ident, + $test_fn1:ident, + $test_fn2:ident, + $test_ret_val1:expr, + $test_ret_val2:expr + ) => { + #[test] + fn $fn_name() { + #[allow(unused_unsafe)] + unsafe { + let ret1 = $test_fn1(1, 2, 3, 4, 5, 6); + assert_eq!(ret1, $test_ret_val1); + + let ret2 = $test_fn2(1, 2, 3, 4, 5, 6); + assert_eq!(ret2, $test_ret_val2); + + let ptest1 = $test_fn1 as *const () as GuestAddr; + let ptest2 = $test_fn2 as *const () as GuestAddr; + info!("pfn: {:#x}", ptest1); + let aligned_pfn = ptest1 & !0xfff; + info!("aligned_pfn: {:#x}", aligned_pfn); + LinuxMmap::protect( + aligned_pfn, + 0x4096, + MmapProt::READ | MmapProt::WRITE | MmapProt::EXEC, + ) + .unwrap(); + + RawPatch::patch(ptest1, ptest2).unwrap(); + let ret = $test_fn1(1, 2, 3, 4, 5, 6); + assert_eq!(ret, $test_ret_val2); + } + } + }; + } + + define_test_function!(test_arm1, 0xdeadface); + define_test_function!(test_arm2, 0xd00df00d); + define_test_function!(test_arm3, 0xfeeddeaf); + define_test_function!( + target_feature(enable = "thumb-mode"), + test_thumb1, + 0xcafebabe + ); + define_test_function!( + target_feature(enable = "thumb-mode"), + test_thumb2, + 0xbeeffade + ); + define_test_function!( + target_feature(enable = "thumb-mode"), + test_thumb3, + 0xdeedcede + ); + + define_test!( + test_patch_arm_to_arm, + test_arm2, + test_arm1, + 0xd00df00d, + 0xdeadface + ); + define_test!( + test_patch_arm_to_thumb, + test_arm3, + test_thumb1, + 0xfeeddeaf, + 0xcafebabe + ); + define_test!( + test_patch_thumb_to_arm, + test_thumb3, + test_arm1, + 0xdeedcede, + 0xdeadface + ); + define_test!( + test_patch_thumb_to_thumb, + test_thumb2, + test_thumb1, + 0xbeeffade, + 0xcafebabe + ); +} From a38af3db7ea161245ebd029d78b63a574f07c8e6 Mon Sep 17 00:00:00 2001 From: Wim de With Date: Fri, 2 May 2025 18:52:22 +0200 Subject: [PATCH 3/5] Get page size dynamically, protect two pages and undo changes after --- libafl_qemu/librasan/asan/tests/patch_raw.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libafl_qemu/librasan/asan/tests/patch_raw.rs b/libafl_qemu/librasan/asan/tests/patch_raw.rs index 42267af550..7f31307c88 100644 --- a/libafl_qemu/librasan/asan/tests/patch_raw.rs +++ b/libafl_qemu/librasan/asan/tests/patch_raw.rs @@ -9,6 +9,7 @@ mod tests { mmap::{Mmap, MmapProt, linux::LinuxMmap}, patch::{Patch, raw::RawPatch}, }; + use libc::{_SC_PAGESIZE, sysconf}; use log::info; #[unsafe(no_mangle)] @@ -45,10 +46,12 @@ mod tests { let ptest2 = test2 as *const () as GuestAddr; info!("pfn: {:#x}", ptest1); let aligned_pfn = ptest1 & !0xfff; + let page_size = unsafe { sysconf(_SC_PAGESIZE) as usize }; info!("aligned_pfn: {:#x}", aligned_pfn); + info!("page_size: {:#x}", page_size); LinuxMmap::protect( aligned_pfn, - 0x4096, + page_size * 2, MmapProt::READ | MmapProt::WRITE | MmapProt::EXEC, ) .unwrap(); @@ -56,6 +59,7 @@ mod tests { RawPatch::patch(ptest1, ptest2).unwrap(); let ret = test1(1, 2, 3, 4, 5, 6); assert_eq!(ret, 0xd00df00d); + LinuxMmap::protect(aligned_pfn, page_size * 2, MmapProt::READ | MmapProt::EXEC).unwrap(); } } @@ -68,6 +72,7 @@ mod tests { mmap::{Mmap, MmapProt, linux::LinuxMmap}, patch::{Patch, raw::RawPatch}, }; + use libc::{_SC_PAGESIZE, sysconf}; use log::info; macro_rules! define_test_function { @@ -123,10 +128,12 @@ mod tests { let ptest2 = $test_fn2 as *const () as GuestAddr; info!("pfn: {:#x}", ptest1); let aligned_pfn = ptest1 & !0xfff; + let page_size = unsafe { sysconf(_SC_PAGESIZE) as usize }; info!("aligned_pfn: {:#x}", aligned_pfn); + info!("page_size: {:#x}", page_size); LinuxMmap::protect( aligned_pfn, - 0x4096, + page_size * 2, MmapProt::READ | MmapProt::WRITE | MmapProt::EXEC, ) .unwrap(); @@ -134,6 +141,8 @@ mod tests { RawPatch::patch(ptest1, ptest2).unwrap(); let ret = $test_fn1(1, 2, 3, 4, 5, 6); assert_eq!(ret, $test_ret_val2); + LinuxMmap::protect(aligned_pfn, page_size * 2, MmapProt::READ | MmapProt::EXEC) + .unwrap(); } } }; From 19f24cb39fd65737ae611cbcde63f61c57f16562 Mon Sep 17 00:00:00 2001 From: Wim de With Date: Fri, 2 May 2025 19:03:48 +0200 Subject: [PATCH 4/5] Rename ARM patch test functions --- libafl_qemu/librasan/asan/tests/patch_raw.rs | 28 ++++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/libafl_qemu/librasan/asan/tests/patch_raw.rs b/libafl_qemu/librasan/asan/tests/patch_raw.rs index 7f31307c88..df72c48737 100644 --- a/libafl_qemu/librasan/asan/tests/patch_raw.rs +++ b/libafl_qemu/librasan/asan/tests/patch_raw.rs @@ -148,50 +148,50 @@ mod tests { }; } - define_test_function!(test_arm1, 0xdeadface); - define_test_function!(test_arm2, 0xd00df00d); - define_test_function!(test_arm3, 0xfeeddeaf); + define_test_function!(arm_patch_target, 0xdeadface); + define_test_function!(patched_arm_to_arm, 0xd00df00d); + define_test_function!(patched_arm_to_thumb, 0xfeeddeaf); define_test_function!( target_feature(enable = "thumb-mode"), - test_thumb1, + thumb_patch_target, 0xcafebabe ); define_test_function!( target_feature(enable = "thumb-mode"), - test_thumb2, + patched_thumb_to_thumb, 0xbeeffade ); define_test_function!( target_feature(enable = "thumb-mode"), - test_thumb3, + patched_thumb_to_arm, 0xdeedcede ); define_test!( test_patch_arm_to_arm, - test_arm2, - test_arm1, + patched_arm_to_arm, + arm_patch_target, 0xd00df00d, 0xdeadface ); define_test!( test_patch_arm_to_thumb, - test_arm3, - test_thumb1, + patched_arm_to_thumb, + thumb_patch_target, 0xfeeddeaf, 0xcafebabe ); define_test!( test_patch_thumb_to_arm, - test_thumb3, - test_arm1, + patched_thumb_to_arm, + arm_patch_target, 0xdeedcede, 0xdeadface ); define_test!( test_patch_thumb_to_thumb, - test_thumb2, - test_thumb1, + patched_thumb_to_thumb, + thumb_patch_target, 0xbeeffade, 0xcafebabe ); From 19f4319b6dc1c9b58a2e948346802ff62a37b430 Mon Sep 17 00:00:00 2001 From: Wim de With Date: Fri, 2 May 2025 23:08:54 +0200 Subject: [PATCH 5/5] librasan: Simplify patch for ARM --- libafl_qemu/librasan/asan/src/patch/raw.rs | 87 +++++++++------------- 1 file changed, 35 insertions(+), 52 deletions(-) diff --git a/libafl_qemu/librasan/asan/src/patch/raw.rs b/libafl_qemu/librasan/asan/src/patch/raw.rs index b308229bab..d04bda6af5 100644 --- a/libafl_qemu/librasan/asan/src/patch/raw.rs +++ b/libafl_qemu/librasan/asan/src/patch/raw.rs @@ -14,35 +14,18 @@ pub struct RawPatch; impl Patch for RawPatch { type Error = RawPatchError; - - #[cfg(not(target_arch = "arm"))] fn patch(target: GuestAddr, destination: GuestAddr) -> Result<(), Self::Error> { debug!("patch - addr: {:#x}, target: {:#x}", target, destination); if target == destination { Err(RawPatchError::IdentityPatch(target))?; } - let patch = Self::get_patch(destination)?; - trace!("patch: {:02x?}", patch); - let dest = unsafe { from_raw_parts_mut(target as *mut u8, patch.len()) }; - dest.copy_from_slice(&patch); - Ok(()) - } + let patch = Self::get_patch(target, destination)?; - #[cfg(target_arch = "arm")] - fn patch(target: GuestAddr, destination: GuestAddr) -> Result<(), Self::Error> { - debug!("patch - addr: {:#x}, target: {:#x}", target, destination); - if target == destination { - Err(RawPatchError::IdentityPatch(target))?; - } - - let patch = if target & 1 == 1 { - Self::get_patch_thumb(destination)? - } else { - Self::get_patch_arm(destination)? - }; + // Mask the thumb mode indicator bit + #[cfg(target_arch = "arm")] + let target = target & !1; trace!("patch: {:02x?}", patch); - let target = target & !1; let dest = unsafe { from_raw_parts_mut(target as *mut u8, patch.len()) }; dest.copy_from_slice(&patch); Ok(()) @@ -51,7 +34,7 @@ impl Patch for RawPatch { impl RawPatch { #[cfg(target_arch = "x86_64")] - fn get_patch(destination: GuestAddr) -> Result, RawPatchError> { + fn get_patch(_target: GuestAddr, destination: GuestAddr) -> Result, RawPatchError> { // mov rax, 0xdeadfacef00dd00d // jmp rax let insns = [ @@ -77,7 +60,7 @@ impl RawPatch { } #[cfg(target_arch = "x86")] - fn get_patch(destination: GuestAddr) -> Result, RawPatchError> { + fn get_patch(_target: GuestAddr, destination: GuestAddr) -> Result, RawPatchError> { // mov eax, 0xdeadface // jmp eax let insns = [ @@ -91,37 +74,37 @@ impl RawPatch { } #[cfg(target_arch = "arm")] - fn get_patch_arm(destination: GuestAddr) -> Result, RawPatchError> { - // ldr ip, [pc] - // bx ip - // .long 0xdeadface - let insns = [ - [0x00, 0xc0, 0x9f, 0xe5].to_vec(), - [0x1c, 0xff, 0x2f, 0xe1].to_vec(), - [0xce, 0xfa, 0xad, 0xde].to_vec(), - ]; - let addr = destination.to_ne_bytes().to_vec(); - let insns_mod = [&insns[0], &insns[1], &addr]; - Ok(insns_mod.into_iter().flatten().cloned().collect()) - } - - #[cfg(target_arch = "arm")] - fn get_patch_thumb(destination: GuestAddr) -> Result, RawPatchError> { - // ldr ip, [pc, #2] - // bx ip - // .long 0xdeadface - let insns = [ - [0xdf, 0xf8, 0x02, 0xc0].to_vec(), - [0x60, 0x47].to_vec(), - [0xce, 0xfa, 0xad, 0xde].to_vec(), - ]; - let addr = destination.to_ne_bytes().to_vec(); - let insns_mod = [&insns[0], &insns[1], &addr]; - Ok(insns_mod.into_iter().flatten().cloned().collect()) + fn get_patch(target: GuestAddr, destination: GuestAddr) -> Result, RawPatchError> { + // If our target is in thumb mode + if target & 1 == 1 { + // ldr ip, [pc, #2] + // bx ip + // .long 0xdeadface + let insns = [ + [0xdf, 0xf8, 0x02, 0xc0].to_vec(), + [0x60, 0x47].to_vec(), + [0xce, 0xfa, 0xad, 0xde].to_vec(), + ]; + let addr = destination.to_ne_bytes().to_vec(); + let insns_mod = [&insns[0], &insns[1], &addr]; + Ok(insns_mod.into_iter().flatten().cloned().collect()) + } else { + // ldr ip, [pc] + // bx ip + // .long 0xdeadface + let insns = [ + [0x00, 0xc0, 0x9f, 0xe5].to_vec(), + [0x1c, 0xff, 0x2f, 0xe1].to_vec(), + [0xce, 0xfa, 0xad, 0xde].to_vec(), + ]; + let addr = destination.to_ne_bytes().to_vec(); + let insns_mod = [&insns[0], &insns[1], &addr]; + Ok(insns_mod.into_iter().flatten().cloned().collect()) + } } #[cfg(target_arch = "aarch64")] - fn get_patch(destination: GuestAddr) -> Result, RawPatchError> { + fn get_patch(_target: GuestAddr, destination: GuestAddr) -> Result, RawPatchError> { // ldr x16, #8 // br x16 // .quad 0xdeadfacef00dd00d @@ -137,7 +120,7 @@ impl RawPatch { } #[cfg(target_arch = "powerpc")] - fn get_patch(destination: GuestAddr) -> Result, RawPatchError> { + fn get_patch(_target: GuestAddr, destination: GuestAddr) -> Result, RawPatchError> { // lis 12, 0xdead // ori 12, 12, 0xface // mtctr 12