diff --git a/Cargo.toml b/Cargo.toml index 95f228e..f58059f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,4 @@ bytemuck = { version = "1.21.0", features = ["derive"] } memmap2 = "0.9.5" [target.'cfg(windows)'.dependencies] -windows = { version = "0.59.0", features = ["Win32_System_Memory", "Win32_Security"] } +windows = { version = "0.59.0", features = ["Win32_System_Memory", "Win32_Security", "Win32_System_SystemInformation"] } diff --git a/src/lib.rs b/src/lib.rs index dd17707..d65178c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -mod mmap; +mod sys; use std::{ffi::CStr, fmt::Debug, fs::File}; @@ -253,9 +253,19 @@ pub fn execute(file: File, pe: &[u8]) { // just some arbitrary offset that probably won't collide with anything let base = optional_header.image_base as usize + 0xFFFFFF0000; - assert_eq!(base & (4096 - 1), 0); + let allocation_granularity = crate::sys::allocation_granularity(); - let map = unsafe { crate::mmap::map(file).unwrap() }; + assert_eq!(base & (allocation_granularity - 1), 0); + + let total_size = section_table.last().unwrap().virtual_address as usize; + + unsafe { + crate::sys::anon_write_map( + total_size.next_multiple_of(allocation_granularity), + std::ptr::with_exposed_provenance(base), + ) + .unwrap(); + } // allocate the sections. for section in section_table { @@ -267,32 +277,30 @@ pub fn execute(file: File, pe: &[u8]) { .characteristics .contains(SectionFlags::IMAGE_SCN_MEM_EXECUTE) { - crate::mmap::Mode::Execute + crate::sys::Mode::Execute } else if section .characteristics .contains(SectionFlags::IMAGE_SCN_MEM_WRITE) { - crate::mmap::Mode::Write + crate::sys::Mode::Write } else { - crate::mmap::Mode::Read + crate::sys::Mode::Read }; let address = std::ptr::with_exposed_provenance::<()>(base + section.virtual_address as usize); dbg!(section); - // assert stuff is aligned (yes 4096 as a hardcoded page is bad) - //assert_eq!(section.pointer_to_raw_data & (4096 - 1), 0); - assert_eq!(address.addr() & (4096 - 1), 0); - unsafe { - map.view( - mode, - section.pointer_to_raw_data as u64, + std::slice::from_raw_parts_mut( + address.cast_mut().cast::(), section.size_of_raw_data as usize, - address, ) - .unwrap() - }; + .copy_from_slice( + &pe[section.pointer_to_raw_data as usize..][..section.size_of_raw_data as usize], + ); + + crate::sys::protect(address, section.virtual_size as usize, mode).unwrap(); + } } //let import_directory_table: &[ImportDirectoryTableEntry] = bytemuck::cast_slice(&file[optional_header.import_table.virtual_address as usize..][..optional_header.import_table.size as usize]); diff --git a/src/mmap.rs b/src/mmap.rs deleted file mode 100644 index c6e8987..0000000 --- a/src/mmap.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! memmap2 doesn't support MAP_FIXED so here's our own! - -#[expect(dead_code)] -pub(crate) struct MapView(*const ()); - -pub(crate) enum Mode { - Read, - Write, - Execute, -} - -#[cfg(windows)] -mod imp { - use std::{ffi::c_void, fs::File, io, os::windows::io::AsRawHandle, u32}; - - use windows::Win32::{ - Foundation::HANDLE, - System::Memory::{FILE_MAP_COPY, FILE_MAP_EXECUTE, FILE_MAP_READ, PAGE_EXECUTE_READ}, - }; - - use super::{MapView, Mode}; - - pub(crate) struct Map(HANDLE); - - pub(crate) unsafe fn map(file: File) -> io::Result { - windows::Win32::System::Memory::CreateFileMappingA( - HANDLE(file.as_raw_handle()), - None, - PAGE_EXECUTE_READ, - 0, - 0, - None, - ) - .map(Map) - .map_err(Into::into) - } - - impl Map { - pub(crate) unsafe fn view( - &self, - mode: Mode, - file_offset: u64, - size: usize, - address: *const (), - ) -> Result { - let addr = unsafe { - windows::Win32::System::Memory::MapViewOfFileEx( - self.0, - match mode { - Mode::Read => FILE_MAP_READ, - Mode::Write => FILE_MAP_COPY, - Mode::Execute => FILE_MAP_READ | FILE_MAP_EXECUTE, - }, - (file_offset << 32) as u32, - file_offset as u32, - size, - Some(address as *const c_void), - ) - }; - - if addr.Value.is_null() { - Err(io::Error::last_os_error()) - } else { - Ok(MapView(addr.Value as *const ())) - } - } - } - - impl Drop for Map { - fn drop(&mut self) { - let _ = unsafe { windows::Win32::Foundation::CloseHandle(self.0) }; - } - } -} -#[cfg(unix)] -mod imp { - compile_error!("no unix yet lol skill issue"); -} - -pub(crate) use imp::*; diff --git a/src/sys.rs b/src/sys.rs new file mode 100644 index 0000000..f2e3cde --- /dev/null +++ b/src/sys.rs @@ -0,0 +1,97 @@ +//! memmap2 doesn't support MAP_FIXED so here's our own! + +pub(crate) enum Mode { + Read, + Write, + Execute, +} + +#[cfg(windows)] +mod imp { + use std::{ffi::c_void, io, u32}; + + use windows::Win32::{ + Foundation::INVALID_HANDLE_VALUE, + System::{ + Memory::{FILE_MAP_EXECUTE, FILE_MAP_WRITE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_READONLY, PAGE_READWRITE}, + SystemInformation::SYSTEM_INFO, + }, + }; + + use super::Mode; + + pub(crate) fn allocation_granularity() -> usize { + let mut info = SYSTEM_INFO::default(); + unsafe { + windows::Win32::System::SystemInformation::GetSystemInfo(&mut info); + } + info.dwAllocationGranularity as usize + } + + pub(crate) fn page_size() -> usize { + let mut info = SYSTEM_INFO::default(); + unsafe { + windows::Win32::System::SystemInformation::GetSystemInfo(&mut info); + } + info.dwPageSize as usize + } + + pub(crate) unsafe fn anon_write_map(size: usize, address: *const ()) -> io::Result<()> { + let map = windows::Win32::System::Memory::CreateFileMappingA( + INVALID_HANDLE_VALUE, + None, + PAGE_EXECUTE_READWRITE, + (size >> 32) as u32, + size as u32, + None, + )?; + + eprintln!("created {address:p} {size:x}"); + + debug_assert_eq!(address.addr() & (allocation_granularity() - 1), 0); + debug_assert_eq!(size & (allocation_granularity() - 1), 0); + + let addr = unsafe { + windows::Win32::System::Memory::MapViewOfFileEx( + map, + FILE_MAP_WRITE | FILE_MAP_EXECUTE, + 0, + 0, + size, + Some(address as *const c_void), + ) + }; + + let _ = unsafe { windows::Win32::Foundation::CloseHandle(map) }; + + if addr.Value.is_null() { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + } + + pub(crate) fn protect(address: *const (), size: usize, mode: Mode) -> io::Result<()> { + debug_assert_eq!(address.addr() & (page_size() - 1), 0); + + unsafe { + windows::Win32::System::Memory::VirtualProtect( + address.cast::(), + size, + match mode { + Mode::Read => PAGE_READONLY, + Mode::Write => PAGE_READWRITE, + Mode::Execute => PAGE_EXECUTE_READ, + }, + std::ptr::null_mut(), + ) + .map_err(Into::into) + } + } +} +#[cfg(unix)] +mod imp { + compile_error!("no unix yet lol skill issue"); +} + +pub(crate) use imp::*;