wraith/navigation/
module_iter.rs1#[cfg(all(not(feature = "std"), feature = "alloc"))]
4use alloc::{string::String, vec::Vec};
5
6#[cfg(feature = "std")]
7use std::{string::String, vec::Vec};
8
9use super::module::Module;
10use crate::error::{Result, WraithError};
11use crate::structures::ldr::{
12 IN_INITIALIZATION_ORDER_LINKS_OFFSET, IN_LOAD_ORDER_LINKS_OFFSET, IN_MEMORY_ORDER_LINKS_OFFSET,
13};
14use crate::structures::{LdrDataTableEntry, ListEntry, Peb};
15
16pub use crate::error::ModuleListType;
18
19const MAX_MODULES: usize = 4096;
21
22pub struct ModuleIterator<'a> {
24 head: *const ListEntry,
25 current: *const ListEntry,
26 offset: usize,
27 list_type: ModuleListType,
28 iterations: usize,
29 _peb: &'a Peb, }
31
32impl<'a> ModuleIterator<'a> {
33 pub fn new(peb: &'a Peb, list_type: ModuleListType) -> Result<Self> {
35 let ldr = peb.ldr().ok_or(WraithError::NullPointer {
36 context: "PEB.Ldr",
37 })?;
38
39 let (head, offset) = match list_type {
40 ModuleListType::InLoadOrder => (
41 &ldr.in_load_order_module_list as *const ListEntry,
42 IN_LOAD_ORDER_LINKS_OFFSET,
43 ),
44 ModuleListType::InMemoryOrder => (
45 &ldr.in_memory_order_module_list as *const ListEntry,
46 IN_MEMORY_ORDER_LINKS_OFFSET,
47 ),
48 ModuleListType::InInitializationOrder => (
49 &ldr.in_initialization_order_module_list as *const ListEntry,
50 IN_INITIALIZATION_ORDER_LINKS_OFFSET,
51 ),
52 };
53
54 let current = unsafe { (*head).flink };
56
57 Ok(Self {
58 head,
59 current,
60 offset,
61 list_type,
62 iterations: 0,
63 _peb: peb,
64 })
65 }
66
67 pub fn list_type(&self) -> ModuleListType {
69 self.list_type
70 }
71}
72
73impl<'a> Iterator for ModuleIterator<'a> {
74 type Item = Module<'a>;
75
76 fn next(&mut self) -> Option<Self::Item> {
77 if core::ptr::eq(self.current, self.head) {
79 return None;
80 }
81
82 if self.iterations >= MAX_MODULES {
84 return None;
85 }
86 self.iterations += 1;
87
88 if self.current.is_null() {
90 return None;
91 }
92
93 let entry_addr = (self.current as usize) - self.offset;
95 let entry = unsafe { &*(entry_addr as *const LdrDataTableEntry) };
97
98 self.current = unsafe { (*self.current).flink };
100
101 Some(Module::from_entry(entry))
102 }
103}
104
105pub struct InLoadOrderIter<'a>(ModuleIterator<'a>);
107
108impl<'a> InLoadOrderIter<'a> {
109 pub fn new(peb: &'a Peb) -> Result<Self> {
110 Ok(Self(ModuleIterator::new(peb, ModuleListType::InLoadOrder)?))
111 }
112}
113
114impl<'a> Iterator for InLoadOrderIter<'a> {
115 type Item = Module<'a>;
116
117 fn next(&mut self) -> Option<Self::Item> {
118 self.0.next()
119 }
120}
121
122pub struct InMemoryOrderIter<'a>(ModuleIterator<'a>);
124
125impl<'a> InMemoryOrderIter<'a> {
126 pub fn new(peb: &'a Peb) -> Result<Self> {
127 Ok(Self(ModuleIterator::new(
128 peb,
129 ModuleListType::InMemoryOrder,
130 )?))
131 }
132}
133
134impl<'a> Iterator for InMemoryOrderIter<'a> {
135 type Item = Module<'a>;
136
137 fn next(&mut self) -> Option<Self::Item> {
138 self.0.next()
139 }
140}
141
142pub struct InInitializationOrderIter<'a>(ModuleIterator<'a>);
144
145impl<'a> InInitializationOrderIter<'a> {
146 pub fn new(peb: &'a Peb) -> Result<Self> {
147 Ok(Self(ModuleIterator::new(
148 peb,
149 ModuleListType::InInitializationOrder,
150 )?))
151 }
152}
153
154impl<'a> Iterator for InInitializationOrderIter<'a> {
155 type Item = Module<'a>;
156
157 fn next(&mut self) -> Option<Self::Item> {
158 self.0.next()
159 }
160}
161
162pub fn module_count(peb: &Peb) -> Result<usize> {
164 Ok(ModuleIterator::new(peb, ModuleListType::InLoadOrder)?.count())
165}
166
167pub fn collect_modules(peb: &Peb) -> Result<Vec<ModuleInfo>> {
169 let iter = ModuleIterator::new(peb, ModuleListType::InLoadOrder)?;
170 Ok(iter
171 .map(|m| ModuleInfo {
172 name: m.name(),
173 path: m.full_path(),
174 base: m.base(),
175 size: m.size(),
176 entry_point: m.entry_point(),
177 })
178 .collect())
179}
180
181#[derive(Debug, Clone)]
183pub struct ModuleInfo {
184 pub name: String,
185 pub path: String,
186 pub base: usize,
187 pub size: usize,
188 pub entry_point: usize,
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn test_module_count() {
197 let peb = Peb::current().expect("should get PEB");
198 let count = module_count(&peb).expect("should count modules");
199 assert!(count > 0, "should have at least one module");
200 }
201
202 #[test]
203 fn test_collect_modules() {
204 let peb = Peb::current().expect("should get PEB");
205 let modules = collect_modules(&peb).expect("should collect modules");
206 assert!(!modules.is_empty(), "should have modules");
207
208 let first = &modules[0];
210 assert!(first.base > 0, "first module should have valid base");
211 }
212
213 #[test]
214 fn test_all_three_lists() {
215 let peb = Peb::current().expect("should get PEB");
216
217 let load_order: Vec<_> = ModuleIterator::new(&peb, ModuleListType::InLoadOrder)
218 .expect("load order iter")
219 .collect();
220 let memory_order: Vec<_> = ModuleIterator::new(&peb, ModuleListType::InMemoryOrder)
221 .expect("memory order iter")
222 .collect();
223 let init_order: Vec<_> = ModuleIterator::new(&peb, ModuleListType::InInitializationOrder)
224 .expect("init order iter")
225 .collect();
226
227 assert_eq!(
229 load_order.len(),
230 memory_order.len(),
231 "load and memory order should have same count"
232 );
233 assert!(
235 init_order.len() <= load_order.len(),
236 "init order should have <= load order count"
237 );
238 }
239}