get_proc_address_rs/
lib.rs1#![allow(unused_unsafe)]
2#![cfg(target_os = "windows")]
3
4#[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#[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#[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 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 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 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 let name_offset = unsafe { function_name_offset.add(idx as usize) };
94
95 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 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
118pub fn get_proc_address(dll_name: &str, function_name: &str) -> Option<*const u8> {
125 let peb_offset = fetch_peb_offset();
127 let peb_ptr = peb_offset as *const Peb;
128
129 let peb_ref = unsafe { &*peb_ptr };
131
132 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 let dos_header = module.base_address as *const ImageDosHeader;
140 let e_lfanew = unsafe { (*dos_header).e_lfanew };
141
142 let nt_headers = ptr_at!(module.base_address, e_lfanew, ImageNtHeaders);
144 let optional_header = unsafe { &(*nt_headers).optional_header };
145
146 let export_dir_entry: &ImageDataDirectory = &optional_header.data_directory[0];
148 let export_rva = export_dir_entry.virtual_address;
149
150 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}