1use core::ffi::c_void;
4
5#[repr(C)]
7#[derive(Clone, Copy)]
8pub struct ListEntry {
9 pub flink: *mut ListEntry,
10 pub blink: *mut ListEntry,
11}
12
13#[repr(C)]
15#[derive(Clone, Copy)]
16pub struct UnicodeString {
17 pub length: u16,
18 pub maximum_length: u16,
19 pub buffer: *mut u16,
20}
21
22#[repr(C)]
30pub struct LdrDataTableEntry {
31 pub in_load_order_links: ListEntry,
32 pub in_memory_order_links: ListEntry,
33 pub in_initialization_order_links: ListEntry,
34 pub dll_base: *mut c_void,
35 pub entry_point: *mut c_void,
36 pub size_of_image: u32,
37 pub full_dll_name: UnicodeString,
38 pub base_dll_name: UnicodeString,
39}
40
41#[repr(C)]
46pub struct PebLdrData {
47 pub _length_and_init: [u8; 8],
48 pub _ss_handle: *mut c_void,
49 pub in_load_order_module_list: ListEntry,
50}
51
52#[repr(C)]
60pub struct Peb {
61 pub _reserved1: [u8; 2],
62 pub being_debugged: u8,
63 pub _reserved2: [u8; 1],
64 pub _reserved3: [*mut c_void; 2],
65 pub ldr: *mut PebLdrData,
66}
67
68#[inline(always)]
70pub fn current_peb() -> *mut Peb {
71 let peb: *mut Peb;
72 #[cfg(target_arch = "x86_64")]
73 unsafe {
74 core::arch::asm!(
75 "mov {peb}, gs:[0x60]",
76 peb = out(reg) peb,
77 options(nostack, preserves_flags, readonly)
78 );
79 }
80 #[cfg(target_arch = "x86")]
81 unsafe {
82 core::arch::asm!(
83 "mov {peb}, fs:[0x30]",
84 peb = out(reg) peb,
85 options(nostack, preserves_flags, readonly)
86 );
87 }
88 #[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
89 compile_error!("dyncvoke PEB walker only supports x86 / x86_64");
90 peb
91}
92
93pub const fn hash_name(bytes: &[u8]) -> u32 {
95 let mut hash: u32 = 5381;
96 let mut i = 0;
97 while i < bytes.len() {
98 let mut c = bytes[i];
99 if c >= b'A' && c <= b'Z' {
100 c += 32;
101 }
102 hash = hash.wrapping_mul(33).wrapping_add(c as u32);
103 i += 1;
104 }
105 hash
106}
107
108unsafe fn unicode_eq_ascii_ci(us: &UnicodeString, target: &[u8]) -> bool {
109 if us.buffer.is_null() {
110 return target.is_empty() && us.length == 0;
111 }
112 let len = (us.length / 2) as usize;
113 if len != target.len() {
114 return false;
115 }
116 for i in 0..len {
117 let wide = *us.buffer.add(i);
118 if wide >= 0x80 {
119 return false;
120 }
121 let mut byte = wide as u8;
122 if byte >= b'A' && byte <= b'Z' {
123 byte += 32;
124 }
125 let mut tgt = target[i];
126 if tgt >= b'A' && tgt <= b'Z' {
127 tgt += 32;
128 }
129 if byte != tgt {
130 return false;
131 }
132 }
133 true
134}
135
136unsafe fn hash_unicode_string(us: &UnicodeString) -> u32 {
137 if us.buffer.is_null() {
138 return 0;
139 }
140 let len = (us.length / 2) as usize;
141 let mut hash: u32 = 5381;
142 for i in 0..len {
143 let wide = *us.buffer.add(i);
144 if wide >= 0x80 {
145 return 0;
146 }
147 let mut byte = wide as u8;
148 if byte >= b'A' && byte <= b'Z' {
149 byte += 32;
150 }
151 hash = hash.wrapping_mul(33).wrapping_add(byte as u32);
152 }
153 hash
154}
155
156unsafe fn walk_modules<F>(mut visit: F) -> usize
157where
158 F: FnMut(&LdrDataTableEntry) -> bool,
159{
160 let peb = current_peb();
161 if peb.is_null() {
162 return 0;
163 }
164 let ldr = (*peb).ldr;
165 if ldr.is_null() {
166 return 0;
167 }
168 let head = &(*ldr).in_load_order_module_list as *const ListEntry as *mut ListEntry;
169 let mut cursor = (*head).flink;
170 while !cursor.is_null() && cursor != head {
171 let entry = cursor as *mut LdrDataTableEntry;
172 if visit(&*entry) {
173 return (*entry).dll_base as usize;
174 }
175 cursor = (*cursor).flink;
176 }
177 0
178}
179
180pub fn get_module_by_name(name: &str) -> usize {
182 unsafe { walk_modules(|entry| unicode_eq_ascii_ci(&entry.base_dll_name, name.as_bytes())) }
183}
184
185pub fn get_module_by_hash(hash: u32) -> usize {
187 unsafe { walk_modules(|entry| hash_unicode_string(&entry.base_dll_name) == hash) }
188}