librasan: Support patching Thumb functions (#3176)

* librasan: Use bx instruction for ARM patch

* librasan: Support patching Thumb functions

* Get page size dynamically, protect two pages and undo changes after

* Rename ARM patch test functions

* librasan: Simplify patch for ARM
This commit is contained in:
Wim de With 2025-05-03 03:45:03 +02:00 committed by GitHub
parent 3b23012faf
commit 0ddc5f156d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 179 additions and 18 deletions

View File

@ -19,7 +19,12 @@ impl Patch for RawPatch {
if target == destination {
Err(RawPatchError::IdentityPatch(target))?;
}
let patch = Self::get_patch(destination)?;
let patch = Self::get_patch(target, destination)?;
// Mask the thumb mode indicator bit
#[cfg(target_arch = "arm")]
let target = target & !1;
trace!("patch: {:02x?}", patch);
let dest = unsafe { from_raw_parts_mut(target as *mut u8, patch.len()) };
dest.copy_from_slice(&patch);
@ -29,7 +34,7 @@ impl Patch for RawPatch {
impl RawPatch {
#[cfg(target_arch = "x86_64")]
fn get_patch(destination: GuestAddr) -> Result<Vec<u8>, RawPatchError> {
fn get_patch(_target: GuestAddr, destination: GuestAddr) -> Result<Vec<u8>, RawPatchError> {
// mov rax, 0xdeadfacef00dd00d
// jmp rax
let insns = [
@ -55,7 +60,7 @@ impl RawPatch {
}
#[cfg(target_arch = "x86")]
fn get_patch(destination: GuestAddr) -> Result<Vec<u8>, RawPatchError> {
fn get_patch(_target: GuestAddr, destination: GuestAddr) -> Result<Vec<u8>, RawPatchError> {
// mov eax, 0xdeadface
// jmp eax
let insns = [
@ -69,22 +74,37 @@ impl RawPatch {
}
#[cfg(target_arch = "arm")]
fn get_patch(destination: GuestAddr) -> Result<Vec<u8>, RawPatchError> {
// ldr ip, [pc]
// mov pc, ip
// .long 0xdeadface
let insns = [
[0x00, 0xc0, 0x9f, 0xe5].to_vec(),
[0x0c, 0xf0, 0xa0, 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())
fn get_patch(target: GuestAddr, destination: GuestAddr) -> Result<Vec<u8>, 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<Vec<u8>, RawPatchError> {
fn get_patch(_target: GuestAddr, destination: GuestAddr) -> Result<Vec<u8>, RawPatchError> {
// ldr x16, #8
// br x16
// .quad 0xdeadfacef00dd00d
@ -100,7 +120,7 @@ impl RawPatch {
}
#[cfg(target_arch = "powerpc")]
fn get_patch(destination: GuestAddr) -> Result<Vec<u8>, RawPatchError> {
fn get_patch(_target: GuestAddr, destination: GuestAddr) -> Result<Vec<u8>, RawPatchError> {
// lis 12, 0xdead
// ori 12, 12, 0xface
// mtctr 12

View File

@ -1,11 +1,15 @@
#![cfg_attr(target_arch = "arm", feature(arm_target_feature))]
#[cfg(test)]
#[cfg(feature = "libc")]
#[cfg(not(target_arch = "arm"))]
mod tests {
use asan::{
GuestAddr,
mmap::{Mmap, MmapProt, linux::LinuxMmap},
patch::{Patch, raw::RawPatch},
};
use libc::{_SC_PAGESIZE, sysconf};
use log::info;
#[unsafe(no_mangle)]
@ -42,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();
@ -53,5 +59,140 @@ 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();
}
}
#[cfg(test)]
#[cfg(feature = "libc")]
#[cfg(target_arch = "arm")]
mod tests {
use asan::{
GuestAddr,
mmap::{Mmap, MmapProt, linux::LinuxMmap},
patch::{Patch, raw::RawPatch},
};
use libc::{_SC_PAGESIZE, sysconf};
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;
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,
page_size * 2,
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);
LinuxMmap::protect(aligned_pfn, page_size * 2, MmapProt::READ | MmapProt::EXEC)
.unwrap();
}
}
};
}
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"),
thumb_patch_target,
0xcafebabe
);
define_test_function!(
target_feature(enable = "thumb-mode"),
patched_thumb_to_thumb,
0xbeeffade
);
define_test_function!(
target_feature(enable = "thumb-mode"),
patched_thumb_to_arm,
0xdeedcede
);
define_test!(
test_patch_arm_to_arm,
patched_arm_to_arm,
arm_patch_target,
0xd00df00d,
0xdeadface
);
define_test!(
test_patch_arm_to_thumb,
patched_arm_to_thumb,
thumb_patch_target,
0xfeeddeaf,
0xcafebabe
);
define_test!(
test_patch_thumb_to_arm,
patched_thumb_to_arm,
arm_patch_target,
0xdeedcede,
0xdeadface
);
define_test!(
test_patch_thumb_to_thumb,
patched_thumb_to_thumb,
thumb_patch_target,
0xbeeffade,
0xcafebabe
);
}