dobby-hook-core 0.1.4

Dobby-like low-level inline hook core in Rust
Documentation
use super::{Backend, HookBuild};
use crate::arch::aarch64;
use crate::error::Result;
use crate::platform;
use core::ffi::{c_char, c_void};
use core::ptr;

pub(crate) static BACKEND: UnixAarch64 = UnixAarch64;
pub(crate) struct UnixAarch64;

impl UnixAarch64 {
    const JMP_STUB_SIZE: usize = 16;
    const PATCH_LEN: usize = 16;
    fn abs_jmp(dest: u64) -> [u8; 16] {
        let ldr_x17_lit_8: u32 = 0x58000000 | (2 << 5) | 17;
        let br_x17: u32 = 0xD61F0000 | (17 << 5);
        let mut buf = [0u8; 16];
        buf[0..4].copy_from_slice(&ldr_x17_lit_8.to_le_bytes());
        buf[4..8].copy_from_slice(&br_x17.to_le_bytes());
        buf[8..16].copy_from_slice(&dest.to_le_bytes());
        buf
    }
}

impl Backend for UnixAarch64 {
    unsafe fn code_patch(
        &self,
        address: *mut c_void,
        buffer: *const u8,
        size: usize,
    ) -> Result<()> {
        platform::unix::code_patch(address, buffer, size)
    }
    unsafe fn hook_build(&self, address: *mut c_void, fake_func: *mut c_void) -> Result<HookBuild> {
        let stolen = core::slice::from_raw_parts(address as *const u8, Self::PATCH_LEN);
        let mut words = [0u32; 4];
        for i in 0..4 {
            words[i] = u32::from_le_bytes([
                stolen[i * 4],
                stolen[i * 4 + 1],
                stolen[i * 4 + 2],
                stolen[i * 4 + 3],
            ]);
        }
        let relocated = aarch64::relocate(&words, address as u64, 0x1000)?;
        let tramp_size = 256usize;
        let tramp = platform::unix::alloc_executable(tramp_size)?;
        let mut offset = 0usize;
        for w in relocated {
            ptr::copy_nonoverlapping(w.to_le_bytes().as_ptr(), (tramp as *mut u8).add(offset), 4);
            offset += 4;
        }
        let jb = Self::abs_jmp(address as u64 + Self::PATCH_LEN as u64);
        ptr::copy_nonoverlapping(
            jb.as_ptr(),
            (tramp as *mut u8).add(offset),
            Self::JMP_STUB_SIZE,
        );
        platform::unix::flush_icache(tramp, offset + Self::JMP_STUB_SIZE);
        let detour = Self::abs_jmp(fake_func as u64);
        platform::unix::code_patch(address, detour.as_ptr(), detour.len())?;
        Ok(HookBuild {
            trampoline: tramp,
            trampoline_size: tramp_size,
            original: stolen.to_vec(),
            patch_len: Self::PATCH_LEN,
        })
    }
    unsafe fn hook_destroy(
        &self,
        address: *mut c_void,
        original: &[u8],
        patch_len: usize,
        trampoline: *mut c_void,
        trampoline_size: usize,
    ) -> Result<()> {
        platform::unix::restore_patch(address, original, patch_len)?;
        platform::unix::free_executable(trampoline, trampoline_size)
    }
    unsafe fn symbol_resolver(
        &self,
        image_name: *const c_char,
        symbol_name: *const c_char,
    ) -> *mut c_void {
        platform::unix::symbol_resolver(image_name, symbol_name)
    }
}