Guest User

Untitled

a guest
Aug 12th, 2025
40
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 5.94 KB | None | 0 0
  1. //ll_utils.rs
  2. //Low Level cross-platform Memory Utils
  3. use std::arch::asm;
  4. use std::fs::File;
  5. use std::io::{BufRead, BufReader};
  6. use std::ops::Neg;
  7. use thiserror::Error;
  8. use crate::ll_utils::syscalls::{MprotFlags, MPROTECT_EXECUTE, MPROTECT_NONE, MPROTECT_READ, MPROTECT_WRITE};
  9.  
  10. #[derive(Error, Debug)]
  11. pub enum MemProtectError {
  12.     #[error("Address is either an invalid pointer or not aligned to page boundaries")]
  13.     InvalidPointer,
  14.     #[error("Invalid protection combination")]
  15.     InvalidProtection,
  16.     //ENOMEM https://man7.org/linux/man-pages/man2/mprotect.2.html
  17.     #[error("Protected memory areas are not accessible, likely unmapped memory")]
  18.     InternalError,
  19. }
  20.  
  21. #[cfg(all(target_os = "linux", target_arch = "x86_64"))]
  22. mod syscalls {
  23.     pub type MprotFlags = u32;
  24.  
  25.     pub const EINVAL: u64 = 22;
  26.     pub const ENOMEM: u64 = 12;
  27.     pub const SYSCALL_MPROTECT: u32 = 10;
  28.     pub const MPROTECT_READ: MprotFlags = 0x1;
  29.     pub const MPROTECT_WRITE: MprotFlags = 0x2;
  30.     pub const MPROTECT_EXECUTE: MprotFlags = 0x4;
  31.     pub const MPROTECT_NONE: MprotFlags = 0x0;
  32. }
  33.  
  34. #[cfg(target_os = "linux")]
  35. fn ll_get_memory_protection(addr: usize) -> Option<MprotFlags> {
  36.     let file = File::open("/proc/self/maps").ok()?;
  37.     let reader = BufReader::new(file);
  38.  
  39.     for line in reader.lines().flatten() {
  40.         // Line format: start-end perms offset dev inode pathname?
  41.         let parts: Vec<&str> = line.split_whitespace().collect();
  42.         if parts.len() < 2 {
  43.             continue;
  44.         }
  45.  
  46.         let range = parts[0];
  47.         let perms = parts[1];
  48.  
  49.         let mut range_parts = range.split('-');
  50.         let start = usize::from_str_radix(range_parts.next()?, 16).ok()?;
  51.         let end = usize::from_str_radix(range_parts.next()?, 16).ok()?;
  52.  
  53.         if addr >= start && addr < end {
  54.             let mut flags = MPROTECT_NONE;
  55.  
  56.             if perms.contains("x") {
  57.                 flags = flags | syscalls::MPROTECT_EXECUTE;
  58.             }
  59.  
  60.             if perms.contains("r") {
  61.                 flags = flags | syscalls::MPROTECT_READ;
  62.             }
  63.  
  64.             if perms.contains("w") {
  65.                 flags = flags | syscalls::MPROTECT_WRITE;
  66.             }
  67.             return Some(flags);
  68.         }
  69.     }
  70.  
  71.     None
  72. }
  73.  
  74.  
  75. #[cfg(all(target_os = "linux", target_arch = "x86_64"))]
  76. fn ll_linux_mprotect(addr: usize, length: usize, prot: syscalls::MprotFlags) -> Result<(), MemProtectError> {
  77.  
  78.     //addr needs to be page aligned, will need to check how that works easily with no deps
  79.  
  80.     let syscall_result: isize = 0;
  81.  
  82.     unsafe {
  83.         //Syscall goooo
  84.         asm!(
  85.         "syscall",
  86.         in("rax") syscalls::SYSCALL_MPROTECT,
  87.         in("rdi") addr,
  88.         in("rsi") length,
  89.         in("rdx") prot,
  90.         lateout("rax") syscall_result,
  91.         clobber: "rcx", "r11", "memory",
  92.         );
  93.     }
  94.  
  95.     // If there was no error, return
  96.     if syscall_result >= 0 {
  97.         return Ok(());
  98.     }
  99.  
  100.     //Flip to error code, then convert to u64, this is always safe
  101.     let syscall_result = syscall_result.neg() as u64;
  102.  
  103.     //We are on the error path!
  104.     let err = match syscall_result {
  105.         syscalls::EINVAL => {
  106.            //This could be either invalid flags or an invalid pointer, let's try to find out
  107.             let possible_flag_combinations_mask = !(syscalls::MPROTECT_READ | syscalls::MPROTECT_WRITE | syscalls::MPROTECT_EXECUTE);
  108.             //If our masked prot flags have any bits set now, we definitely have invalid flags!
  109.             if possible_flag_combinations_mask & prot > 0 {
  110.                 return Err(MemProtectError::InvalidProtection)
  111.             }
  112.             //The other possible error is a pointer issue
  113.             Err(MemProtectError::InvalidPointer)
  114.         },
  115.         syscalls::ENOMEM => {
  116.           Err(MemProtectError::InternalError)
  117.         },
  118.         _ => {
  119.             panic!("Unsupported syscall result: {}", syscall_result);
  120.         }
  121.     };
  122.     err
  123.  
  124. }
  125.  
  126. #[cfg(target_os = "linux")]
  127. pub fn ll_write_to_mem_internal(addr: usize, data: &[u8]) -> Result<(), MemProtectError> {
  128.     let old_protect = ll_get_memory_protection(addr)
  129.         .unwrap_or(MPROTECT_READ | MPROTECT_EXECUTE);
  130.  
  131.     //Memory is not writable
  132.     if old_protect & syscalls::MPROTECT_WRITE == 0 {
  133.         ll_linux_mprotect(addr, data.len(), MPROTECT_WRITE)?;
  134.     }
  135.  
  136.     let dst_ptr = addr as *mut u8;
  137.     let src_ptr = data.as_ptr();
  138.  
  139.     //I do not think we can make the copy more safe...
  140.     unsafe {
  141.         std::ptr::copy_nonoverlapping(src_ptr, dst_ptr, data.len());
  142.     }
  143.  
  144.  
  145.     //If old prot was not already readable, reapply the old options
  146.     if old_protect & syscalls::MPROTECT_WRITE == 0 {
  147.         ll_linux_mprotect(addr, data.len(), old_protect)?;
  148.     }
  149.  
  150.     Ok(())
  151. }
  152.  
  153. //vtable.rs
  154. use crate::ll_utils::ll_write_to_mem_internal;
  155. use std::slice;
  156.  
  157. struct VTableHook<'a, T> {
  158.    original: Option<&'a T>,
  159.     replacement: T,
  160.     base_addr: usize,
  161.     offset: usize,
  162.     installed: bool,
  163. }
  164.  
  165. impl<T> VTableHook<'_, T> {
  166.    pub fn new(base_addr: usize, replacement: T, offset: usize) -> Self {
  167.        Self {
  168.            original: None,
  169.            replacement,
  170.            offset,
  171.            base_addr,
  172.            installed: false,
  173.        }
  174.    }
  175.    
  176.  
  177.     // Is this safeish????
  178.    pub fn install(&mut self) {
  179.        let vtable_addr = self.base_addr + self.offset;
  180.        //This needs to be protected better
  181.        //We read the actual address of the original function from the vtable and safe it
  182.        unsafe {
  183.            let vtable_func_addr = *(vtable_addr as *mut usize);
  184.            self.original = Some(std::mem::transmute::<usize, &T>(vtable_func_addr));
  185.        }
  186.  
  187.        let bytes: &[u8] = unsafe {
  188.            slice::from_raw_parts(
  189.                &self.replacement as *const T as *const u8,
  190.                size_of::<T>(),
  191.            )
  192.        };
  193.  
  194.        ll_write_to_mem_internal(vtable_addr, &bytes).expect("Invalid");
  195.    }
  196. }
Advertisement
Add Comment
Please, Sign In to add comment