@@ -2,10 +2,11 @@ use memflow::os::process::*;
22use memflow:: prelude:: v1:: * ;
33
44use windows:: core:: PCSTR ;
5+ use windows:: Wdk :: System :: SystemInformation :: { NtQuerySystemInformation , SYSTEM_INFORMATION_CLASS } ;
56use windows:: Win32 :: Foundation :: {
6- CloseHandle , HANDLE , NTSTATUS , STATUS_ACCESS_DENIED , STATUS_INSUFFICIENT_RESOURCES ,
7- STATUS_INVALID_HANDLE , STATUS_INVALID_INFO_CLASS , STATUS_INVALID_PARAMETER ,
8- STATUS_NOT_IMPLEMENTED , STATUS_NO_MEMORY , STATUS_PRIVILEGE_NOT_HELD ,
7+ CloseHandle , HANDLE , NTSTATUS , STATUS_ACCESS_DENIED , STATUS_INFO_LENGTH_MISMATCH ,
8+ STATUS_INSUFFICIENT_RESOURCES , STATUS_INVALID_HANDLE , STATUS_INVALID_INFO_CLASS ,
9+ STATUS_INVALID_PARAMETER , STATUS_NOT_IMPLEMENTED , STATUS_NO_MEMORY , STATUS_PRIVILEGE_NOT_HELD ,
910} ;
1011
1112use windows:: Win32 :: System :: Diagnostics :: ToolHelp :: {
@@ -19,21 +20,43 @@ use windows::Win32::Security::{
1920 TOKEN_ADJUST_PRIVILEGES , TOKEN_PRIVILEGES ,
2021} ;
2122
22- use core:: mem:: { size_of, MaybeUninit } ;
23+ use core:: mem:: { align_of , size_of, MaybeUninit } ;
2324use core:: ptr;
2425use std:: ffi:: OsString ;
2526use std:: os:: windows:: ffi:: OsStringExt ;
2627
27- pub mod mem;
28- use mem:: ProcessVirtualMemory ;
28+ const SYSTEM_MODULE_INFORMATION_CLASS : SYSTEM_INFORMATION_CLASS = SYSTEM_INFORMATION_CLASS ( 11 ) ;
29+ const MODULE_QUERY_INITIAL_CAPACITY : usize = 256 * 1024 ;
30+ const MODULE_QUERY_MAX_RETRIES : usize = 6 ;
2931
32+ pub mod mem;
3033pub mod process;
3134pub use process:: WindowsProcess ;
3235
3336pub mod keyboard;
3437pub use keyboard:: { WindowsKeyboard , WindowsKeyboardState } ;
3538
36- struct KernelModule { }
39+ struct KernelModule {
40+ base : Address ,
41+ size : umem ,
42+ path : ReprCString ,
43+ name : ReprCString ,
44+ }
45+
46+ #[ repr( C ) ]
47+ #[ allow( non_snake_case) ]
48+ struct RtlProcessModuleInformation {
49+ Section : * mut core:: ffi:: c_void ,
50+ MappedBase : * mut core:: ffi:: c_void ,
51+ ImageBase : * mut core:: ffi:: c_void ,
52+ ImageSize : u32 ,
53+ Flags : u32 ,
54+ LoadOrderIndex : u16 ,
55+ InitOrderIndex : u16 ,
56+ LoadCount : u16 ,
57+ OffsetToFileName : u16 ,
58+ FullPathName : [ u8 ; 256 ] ,
59+ }
3760
3861pub ( crate ) struct Handle ( HANDLE ) ;
3962
@@ -65,17 +88,50 @@ pub fn conv_err(_err: windows::core::Error) -> Error {
6588
6689pub ( crate ) fn conv_ntstatus ( status : NTSTATUS ) -> Error {
6790 let kind = match status {
68- STATUS_ACCESS_DENIED | STATUS_PRIVILEGE_NOT_HELD => ErrorKind :: Permissions ,
91+ STATUS_ACCESS_DENIED | STATUS_PRIVILEGE_NOT_HELD => ErrorKind :: NotSupported ,
6992 STATUS_INVALID_PARAMETER => ErrorKind :: ArgValidation ,
7093 STATUS_NOT_IMPLEMENTED | STATUS_INVALID_INFO_CLASS => ErrorKind :: NotSupported ,
71- STATUS_INSUFFICIENT_RESOURCES | STATUS_NO_MEMORY => ErrorKind :: OutOfMemory ,
94+ STATUS_INSUFFICIENT_RESOURCES | STATUS_NO_MEMORY => ErrorKind :: Unknown ,
7295 STATUS_INVALID_HANDLE => ErrorKind :: ProcessNotFound ,
7396 _ => ErrorKind :: Unknown ,
7497 } ;
7598
7699 Error ( ErrorOrigin :: OsLayer , kind)
77100}
78101
102+ fn split_basename ( path : & str ) -> & str {
103+ path. rsplit_once ( '\\' )
104+ . or_else ( || path. rsplit_once ( '/' ) )
105+ . map ( |( _, name) | name)
106+ . unwrap_or ( path)
107+ }
108+
109+ fn parse_module_path ( info : & RtlProcessModuleInformation ) -> ( ReprCString , ReprCString ) {
110+ let path_end = info
111+ . FullPathName
112+ . iter ( )
113+ . position ( |& c| c == 0 )
114+ . unwrap_or ( info. FullPathName . len ( ) ) ;
115+
116+ let path_raw = & info. FullPathName [ ..path_end] ;
117+ let path = String :: from_utf8_lossy ( path_raw) ;
118+ let path = path. as_ref ( ) ;
119+
120+ let name = if ( info. OffsetToFileName as usize ) < path_end {
121+ let name_raw = & info. FullPathName [ info. OffsetToFileName as usize ..path_end] ;
122+ let parsed = String :: from_utf8_lossy ( name_raw) ;
123+ if parsed. is_empty ( ) {
124+ split_basename ( path) . to_owned ( )
125+ } else {
126+ parsed. into_owned ( )
127+ }
128+ } else {
129+ split_basename ( path) . to_owned ( )
130+ } ;
131+
132+ ( path. into ( ) , name. into ( ) )
133+ }
134+
79135unsafe fn enable_debug_privilege ( ) -> Result < ( ) > {
80136 let process = GetCurrentProcess ( ) ;
81137 let mut token = HANDLE :: default ( ) ;
@@ -129,6 +185,85 @@ impl WindowsOs {
129185
130186 Ok ( Default :: default ( ) )
131187 }
188+
189+ fn query_kernel_modules ( ) -> Result < Vec < KernelModule > > {
190+ let mut buffer = vec ! [ 0u8 ; MODULE_QUERY_INITIAL_CAPACITY ] ;
191+
192+ for _ in 0 ..MODULE_QUERY_MAX_RETRIES {
193+ let mut return_len = 0u32 ;
194+ let status = unsafe {
195+ NtQuerySystemInformation (
196+ SYSTEM_MODULE_INFORMATION_CLASS ,
197+ buffer. as_mut_ptr ( ) . cast ( ) ,
198+ buffer. len ( ) as u32 ,
199+ & mut return_len,
200+ )
201+ } ;
202+
203+ if status == STATUS_INFO_LENGTH_MISMATCH {
204+ let table_off = align_of :: < RtlProcessModuleInformation > ( ) ;
205+ let next_len = usize:: max (
206+ buffer. len ( ) . saturating_mul ( 2 ) ,
207+ ( return_len as usize ) . saturating_add ( table_off) ,
208+ ) ;
209+ buffer. resize ( next_len, 0 ) ;
210+ continue ;
211+ }
212+
213+ if status. is_err ( ) {
214+ return Err ( conv_ntstatus ( status) ) ;
215+ }
216+
217+ let valid_len = if return_len as usize > buffer. len ( ) {
218+ buffer. len ( )
219+ } else {
220+ return_len as usize
221+ } ;
222+
223+ let data = & buffer[ ..valid_len] ;
224+ let table_off = align_of :: < RtlProcessModuleInformation > ( ) ;
225+
226+ if data. len ( ) < table_off {
227+ return Err ( Error ( ErrorOrigin :: OsLayer , ErrorKind :: Unknown ) ) ;
228+ }
229+
230+ let mut header_buf = [ 0u8 ; core:: mem:: size_of :: < u32 > ( ) ] ;
231+ header_buf. copy_from_slice ( & data[ ..core:: mem:: size_of :: < u32 > ( ) ] ) ;
232+ let header_number = u32:: from_ne_bytes ( header_buf) ;
233+ let entry_size = core:: mem:: size_of :: < RtlProcessModuleInformation > ( ) ;
234+ let entries_len = header_number as usize ;
235+ let table_len = entries_len
236+ . checked_mul ( entry_size)
237+ . ok_or ( Error ( ErrorOrigin :: OsLayer , ErrorKind :: Unknown ) ) ?;
238+ let table_end = table_off
239+ . checked_add ( table_len)
240+ . ok_or ( Error ( ErrorOrigin :: OsLayer , ErrorKind :: Unknown ) ) ?;
241+
242+ if table_end > data. len ( ) {
243+ return Err ( Error ( ErrorOrigin :: OsLayer , ErrorKind :: ArgValidation ) ) ;
244+ }
245+
246+ let mut parsed = Vec :: with_capacity ( entries_len) ;
247+ for idx in 0 ..entries_len {
248+ let module_off = table_off + idx * entry_size;
249+ let module = unsafe {
250+ ( data. as_ptr ( ) . add ( module_off) as * const RtlProcessModuleInformation )
251+ . read_unaligned ( )
252+ } ;
253+ let ( path, name) = parse_module_path ( & module) ;
254+ parsed. push ( KernelModule {
255+ base : Address :: from ( module. ImageBase as umem ) ,
256+ size : module. ImageSize as umem ,
257+ path,
258+ name,
259+ } ) ;
260+ }
261+
262+ return Ok ( parsed) ;
263+ }
264+
265+ Err ( Error ( ErrorOrigin :: OsLayer , ErrorKind :: Unknown ) )
266+ }
132267}
133268
134269impl Clone for WindowsOs {
@@ -244,6 +379,8 @@ impl Os for WindowsOs {
244379 /// # Arguments
245380 /// * `callback` - where to pass each matching module to. This is an opaque callback.
246381 fn module_address_list_callback ( & mut self , mut callback : AddressCallback ) -> Result < ( ) > {
382+ self . cached_modules = Self :: query_kernel_modules ( ) ?;
383+
247384 ( 0 ..self . cached_modules . len ( ) )
248385 . map ( Address :: from)
249386 . take_while ( |a| callback. call ( * a) )
@@ -256,29 +393,19 @@ impl Os for WindowsOs {
256393 ///
257394 /// # Arguments
258395 /// * `address` - address where module's information resides in
259- fn module_by_address ( & mut self , _address : Address ) -> Result < ModuleInfo > {
260- /*self.cached_modules
261- .iter()
262- .skip(address.to_umem() as usize)
263- .next()
264- .map(|km| ModuleInfo {
265- address,
266- size: km.size as umem,
267- base: Address::NULL,
268- name: km
269- .name
270- .split("/")
271- .last()
272- .or(Some(""))
273- .map(ReprCString::from)
274- .unwrap(),
275- arch: self.info.arch,
276- path: km.name.clone().into(),
277- parent_process: Address::INVALID,
278- })
279- .ok_or(Error(ErrorOrigin::OsLayer, ErrorKind::NotFound))*/
280-
281- todo ! ( )
396+ fn module_by_address ( & mut self , address : Address ) -> Result < ModuleInfo > {
397+ self . cached_modules
398+ . get ( address. to_umem ( ) as usize )
399+ . map ( |km| ModuleInfo {
400+ address,
401+ size : km. size ,
402+ base : km. base ,
403+ name : km. name . clone ( ) ,
404+ arch : self . info . arch ,
405+ path : km. path . clone ( ) ,
406+ parent_process : Address :: INVALID ,
407+ } )
408+ . ok_or ( Error ( ErrorOrigin :: OsLayer , ErrorKind :: NotFound ) )
282409 }
283410
284411 /// Retrieves address of the primary module structure of the process
0 commit comments