get_proc_address_rs/
lib.rs

1#![allow(unused_unsafe)]
2#![cfg(target_os = "windows")]
3
4//! # Get-Proc-Address-rs
5//!
6//! `get_proc_address` provides a rust native alternative to GetProcAddress
7//!
8//! ## Example
9//! ```rust
10//! use get_proc_address_rs::get_proc_address;
11//! let pointer = get_proc_address("kernel32.dll", "IsDebuggerPresent").unwrap();
12//! unsafe {
13//!     let is_debugger_present: IsDebuggerPresent = std::mem::transmute(pointer);
14//!     let being_debugged = is_debugger_present();
15//!     assert!(!being_debugged);
16//! }
17//! ```
18
19#[cfg(not(target_os = "windows"))]
20compile_error!("This crate only supports Windows.");
21
22use std::{arch::asm, ffi::CStr, mem::offset_of};
23mod types;
24use types::*;
25pub mod win_types;
26pub use win_types::*;
27
28macro_rules! ptr_at {
29    ($base:expr, $rva:expr, $T:ty) => {
30        unsafe { ($base as *const u8).add($rva as usize).cast::<$T>() }
31    };
32}
33
34/// Fetches the PEB offset by offsetting from gs
35/// # Examples
36/// ```rust
37/// let peb_offset = get_proc_address_rs::fetch_peb_offset();
38/// assert!(!peb_offset == 0);
39/// ```
40#[cfg(target_arch = "x86_64")]
41pub fn fetch_peb_offset() -> u64 {
42    #[allow(unused_assignments)]
43    let mut offset: u64 = 0;
44    unsafe {
45        asm!(
46            "mov {}, gs:[0x60]",
47            out(reg) offset,
48        );
49    }
50    offset
51}
52
53/// Fetches the PEB offset by offsetting from fs
54#[cfg(target_arch = "x86")]
55pub fn fetch_peb_offset() -> u32 {
56    #[allow(unused_assignments)]
57    let mut offset: u32 = 0;
58    unsafe {
59        asm!(
60            "mov {}, fs:[0x30]",
61            out(reg) offset,
62        );
63    }
64    offset
65}
66
67fn fetch_loaded_modules(ldr: &LdrData) -> Vec<DllEntry> {
68    let mut dll_entries: Vec<DllEntry> = Vec::new();
69    unsafe {
70        // Iterate through the ldr flink
71        let mut flink = ldr.in_memory_order_module_list.flink;
72        let head = &ldr.in_memory_order_module_list as *const _ as *mut _;
73        while flink != head {
74            // We have CONTAINING_RECORD at home
75            let entry = (flink as usize - offset_of!(LdrDataTableEntry, in_memory_order_links)) as *const LdrDataTableEntry;
76
77            dll_entries.push(entry.into());
78            flink = (*flink).flink;
79        }
80    }
81    dll_entries
82}
83
84fn fetch_module_functions(base_address: *mut u8, image_export_dir: &ImageExportDirectory) -> Vec<ModuleFunction> {
85    let mut functions: Vec<ModuleFunction> = Vec::new();
86    // Set up required offsets
87    let function_name_offset = ptr_at!(base_address, image_export_dir.address_of_names, u32);
88    let function_ordinal_offset = ptr_at!(base_address, image_export_dir.address_of_name_ordinals, u16);
89    let function_address_offset = ptr_at!(base_address, image_export_dir.address_of_functions, u32);
90
91    for idx in 0..image_export_dir.number_of_names {
92        // Fetch the name pointer by offsetting start of array
93        let name_offset = unsafe { function_name_offset.add(idx as usize) };
94
95        // Deref to the actual name
96        let raw_name_ptr = ptr_at!(base_address, *(name_offset as *mut u32), i8);
97        let name = unsafe { CStr::from_ptr(raw_name_ptr as *mut i8) };
98        let name = match name.to_str() {
99            Ok(valid_name) => valid_name,
100            Err(_) => continue,
101        };
102
103        // Match the name to a function
104        let function_address = unsafe {
105            let function_index = *(function_ordinal_offset.add(idx as usize));
106            let function_rva = *(function_address_offset.byte_offset((function_index * 4) as isize));
107            ptr_at!(base_address, function_rva, u8)
108        };
109
110        functions.push(ModuleFunction {
111            name: name.to_string(),
112            offset: function_address,
113        })
114    }
115    functions
116}
117
118/// Takes in the dll and function names and returns a pointer to the function.
119/// # Example
120/// ```rust
121/// let pointer = get_proc_address_rs::get_proc_address("kernel32.dll", "GetProcAddress");
122/// assert!(pointer.is_some());
123/// ```
124pub fn get_proc_address(dll_name: &str, function_name: &str) -> Option<*const u8> {
125    // First, let's fetch the PEB
126    let peb_offset = fetch_peb_offset();
127    let peb_ptr = peb_offset as *const Peb;
128
129    // Deref to peb
130    let peb_ref = unsafe { &*peb_ptr };
131
132    // Deref to ldr
133    let ldr = unsafe { &*peb_ref.ldr };
134
135    let modules = fetch_loaded_modules(ldr);
136
137    if let Some(module) = modules.iter().find(|m| m.module_name() == dll_name) {
138        // Find dos header
139        let dos_header = module.base_address as *const ImageDosHeader;
140        let e_lfanew = unsafe { (*dos_header).e_lfanew };
141
142        // We offset with the RVA as the e_lfanew is image offset
143        let nt_headers = ptr_at!(module.base_address, e_lfanew, ImageNtHeaders);
144        let optional_header = unsafe { &(*nt_headers).optional_header };
145
146        // Calculate the Export directory RVA
147        let export_dir_entry: &ImageDataDirectory = &optional_header.data_directory[0];
148        let export_rva = export_dir_entry.virtual_address;
149
150        // No exports available
151        if export_rva == 0 {
152            return None;
153        }
154
155        let export_ptr = ptr_at!(module.base_address, export_rva, ImageExportDirectory);
156        let image_export_dir: &ImageExportDirectory = unsafe { &*export_ptr };
157
158        return fetch_module_functions(module.base_address, image_export_dir)
159            .into_iter()
160            .find(|f| f.name == function_name)
161            .map(|f| f.offset);
162    }
163    return None;
164}