Skip to main content

specter/memory/info/
image.rs

1//! Dynamic library image lookup utilities
2
3#[cfg(feature = "dev_release")]
4use crate::utils::logger;
5use std::ffi::CStr;
6
7#[cfg(feature = "dev_release")]
8use mach2::dyld::{
9    _dyld_get_image_header, _dyld_get_image_name, _dyld_get_image_vmaddr_slide, _dyld_image_count,
10};
11#[cfg(not(feature = "dev_release"))]
12use mach2::dyld::{_dyld_get_image_header, _dyld_get_image_name, _dyld_image_count};
13use thiserror::Error;
14
15#[derive(Error, Debug)]
16/// Errors that can occur during image lookup
17pub enum ImageError {
18    /// The specified image was not found
19    #[error("Image not found: {0}")]
20    NotFound(String),
21}
22
23use once_cell::sync::Lazy;
24use parking_lot::RwLock;
25use std::collections::HashMap;
26
27static IMAGE_CACHE: Lazy<RwLock<HashMap<String, usize>>> =
28    Lazy::new(|| RwLock::new(HashMap::new()));
29
30/// Retrieves the base address of a loaded image by name
31///
32/// # Arguments
33/// * `image_name` - The name of the image (or a substring of it)
34///
35/// # Returns
36/// * `Result<usize, ImageError>` - The base address of the image or an error
37pub fn get_image_base(image_name: &str) -> Result<usize, ImageError> {
38    {
39        let cache = IMAGE_CACHE.read();
40        if let Some(&base) = cache.get(image_name) {
41            return Ok(base);
42        }
43    }
44
45    unsafe {
46        let count = _dyld_image_count();
47
48        for i in 0..count {
49            let name_ptr = _dyld_get_image_name(i);
50            if name_ptr.is_null() {
51                continue;
52            }
53
54            let name = CStr::from_ptr(name_ptr).to_string_lossy();
55            if name.contains(image_name) {
56                let header = _dyld_get_image_header(i);
57                #[cfg(feature = "dev_release")]
58                let slide = _dyld_get_image_vmaddr_slide(i);
59
60                #[cfg(feature = "dev_release")]
61                logger::info(&format!(
62                    "Found image: {} (Index: {}, Base: {:p}, Slide: {:#x})",
63                    name, i, header, slide
64                ));
65
66                let base = header as usize;
67
68                IMAGE_CACHE.write().insert(image_name.to_string(), base);
69
70                return Ok(base);
71            }
72        }
73    }
74
75    #[cfg(feature = "dev_release")]
76    logger::warning(&format!("Image not found: {}", image_name));
77    Err(ImageError::NotFound(image_name.to_string()))
78}