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:
parent
3b23012faf
commit
0ddc5f156d
@ -19,7 +19,12 @@ impl Patch for RawPatch {
|
|||||||
if target == destination {
|
if target == destination {
|
||||||
Err(RawPatchError::IdentityPatch(target))?;
|
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);
|
trace!("patch: {:02x?}", patch);
|
||||||
let dest = unsafe { from_raw_parts_mut(target as *mut u8, patch.len()) };
|
let dest = unsafe { from_raw_parts_mut(target as *mut u8, patch.len()) };
|
||||||
dest.copy_from_slice(&patch);
|
dest.copy_from_slice(&patch);
|
||||||
@ -29,7 +34,7 @@ impl Patch for RawPatch {
|
|||||||
|
|
||||||
impl RawPatch {
|
impl RawPatch {
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[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
|
// mov rax, 0xdeadfacef00dd00d
|
||||||
// jmp rax
|
// jmp rax
|
||||||
let insns = [
|
let insns = [
|
||||||
@ -55,7 +60,7 @@ impl RawPatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "x86")]
|
#[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
|
// mov eax, 0xdeadface
|
||||||
// jmp eax
|
// jmp eax
|
||||||
let insns = [
|
let insns = [
|
||||||
@ -69,22 +74,37 @@ impl RawPatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "arm")]
|
#[cfg(target_arch = "arm")]
|
||||||
fn get_patch(destination: GuestAddr) -> Result<Vec<u8>, RawPatchError> {
|
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]
|
// ldr ip, [pc]
|
||||||
// mov pc, ip
|
// bx ip
|
||||||
// .long 0xdeadface
|
// .long 0xdeadface
|
||||||
let insns = [
|
let insns = [
|
||||||
[0x00, 0xc0, 0x9f, 0xe5].to_vec(),
|
[0x00, 0xc0, 0x9f, 0xe5].to_vec(),
|
||||||
[0x0c, 0xf0, 0xa0, 0xe1].to_vec(),
|
[0x1c, 0xff, 0x2f, 0xe1].to_vec(),
|
||||||
[0xce, 0xfa, 0xad, 0xde].to_vec(),
|
[0xce, 0xfa, 0xad, 0xde].to_vec(),
|
||||||
];
|
];
|
||||||
let addr = destination.to_ne_bytes().to_vec();
|
let addr = destination.to_ne_bytes().to_vec();
|
||||||
let insns_mod = [&insns[0], &insns[1], &addr];
|
let insns_mod = [&insns[0], &insns[1], &addr];
|
||||||
Ok(insns_mod.into_iter().flatten().cloned().collect())
|
Ok(insns_mod.into_iter().flatten().cloned().collect())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[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
|
// ldr x16, #8
|
||||||
// br x16
|
// br x16
|
||||||
// .quad 0xdeadfacef00dd00d
|
// .quad 0xdeadfacef00dd00d
|
||||||
@ -100,7 +120,7 @@ impl RawPatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "powerpc")]
|
#[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
|
// lis 12, 0xdead
|
||||||
// ori 12, 12, 0xface
|
// ori 12, 12, 0xface
|
||||||
// mtctr 12
|
// mtctr 12
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
|
#![cfg_attr(target_arch = "arm", feature(arm_target_feature))]
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[cfg(feature = "libc")]
|
#[cfg(feature = "libc")]
|
||||||
|
#[cfg(not(target_arch = "arm"))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use asan::{
|
use asan::{
|
||||||
GuestAddr,
|
GuestAddr,
|
||||||
mmap::{Mmap, MmapProt, linux::LinuxMmap},
|
mmap::{Mmap, MmapProt, linux::LinuxMmap},
|
||||||
patch::{Patch, raw::RawPatch},
|
patch::{Patch, raw::RawPatch},
|
||||||
};
|
};
|
||||||
|
use libc::{_SC_PAGESIZE, sysconf};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
@ -42,10 +46,12 @@ mod tests {
|
|||||||
let ptest2 = test2 as *const () as GuestAddr;
|
let ptest2 = test2 as *const () as GuestAddr;
|
||||||
info!("pfn: {:#x}", ptest1);
|
info!("pfn: {:#x}", ptest1);
|
||||||
let aligned_pfn = ptest1 & !0xfff;
|
let aligned_pfn = ptest1 & !0xfff;
|
||||||
|
let page_size = unsafe { sysconf(_SC_PAGESIZE) as usize };
|
||||||
info!("aligned_pfn: {:#x}", aligned_pfn);
|
info!("aligned_pfn: {:#x}", aligned_pfn);
|
||||||
|
info!("page_size: {:#x}", page_size);
|
||||||
LinuxMmap::protect(
|
LinuxMmap::protect(
|
||||||
aligned_pfn,
|
aligned_pfn,
|
||||||
0x4096,
|
page_size * 2,
|
||||||
MmapProt::READ | MmapProt::WRITE | MmapProt::EXEC,
|
MmapProt::READ | MmapProt::WRITE | MmapProt::EXEC,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -53,5 +59,140 @@ mod tests {
|
|||||||
RawPatch::patch(ptest1, ptest2).unwrap();
|
RawPatch::patch(ptest1, ptest2).unwrap();
|
||||||
let ret = test1(1, 2, 3, 4, 5, 6);
|
let ret = test1(1, 2, 3, 4, 5, 6);
|
||||||
assert_eq!(ret, 0xd00df00d);
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user