#[derive(Debug)]
pub enum Error {
ProcessAccessError(String),
ExecutablePathError(String),
MemoryMapError(String),
NoMemoryMapping,
PlatformError(String),
}
pub fn read_aslr_offset() -> Result<u64, Error> {
imp::_read_aslr_offset()
}
#[cfg(target_os = "linux")]
mod imp {
use super::Error;
use std::{
fs::File,
io::{BufRead, BufReader},
path::PathBuf,
};
pub(super) fn _read_aslr_offset() -> Result<u64, Error> {
let exe: PathBuf = std::fs::read_link("/proc/self/exe")
.map_err(|e| Error::ExecutablePathError(format!("readlink failed: {e}")))?;
let file = File::open("/proc/self/maps")
.map_err(|e| Error::MemoryMapError(format!("open maps: {e}")))?;
let reader = BufReader::new(file);
let mut addrs: Vec<u64> = Vec::new();
for line in reader.lines() {
let line = line.map_err(|e| Error::MemoryMapError(format!("read maps: {e}")))?;
let mut parts = line.split_whitespace();
let range = parts
.next()
.ok_or_else(|| Error::MemoryMapError("malformed maps line".into()))?;
let pathname = parts.nth(4);
if pathname.map(|p| PathBuf::from(p) == exe).unwrap_or(false) {
if let Some(start_hex) = range.split('-').next() {
let addr = u64::from_str_radix(start_hex, 16)
.map_err(|_| Error::MemoryMapError("invalid addr".into()))?;
addrs.push(addr);
}
}
}
addrs.sort_unstable();
addrs.first().copied().ok_or(Error::NoMemoryMapping)
}
}
#[cfg(target_os = "macos")]
mod imp {
use super::Error;
extern "C" {
fn _dyld_get_image_vmaddr_slide(image_index: u32) -> isize;
}
pub(super) fn _read_aslr_offset() -> Result<u64, Error> {
let slide = unsafe { _dyld_get_image_vmaddr_slide(0) };
Ok(slide as u64)
}
}
#[cfg(target_os = "windows")]
mod imp {
use super::Error;
use std::ptr::null_mut;
use windows_sys::Win32::System::LibraryLoader::GetModuleHandleW;
pub(super) fn _read_aslr_offset() -> Result<u64, Error> {
use windows_sys::Win32::System::SystemServices::{IMAGE_DOS_HEADER, IMAGE_NT_HEADERS64};
let base = unsafe { GetModuleHandleW(null_mut()) as usize };
if base == 0 {
return Err(Error::PlatformError(
"Failed to get module handle".to_string(),
));
}
unsafe {
let dos = &*(base as *const IMAGE_DOS_HEADER);
let nth = &*((base + dos.e_lfanew as usize) as *const IMAGE_NT_HEADERS64);
let preferred = nth.OptionalHeader.ImageBase as usize;
Ok((base - preferred) as u64)
}
}
}