@@ -31,40 +31,16 @@ const SYSTEM_HANDLE_INFORMATION_CLASS: SYSTEM_INFORMATION_CLASS = SYSTEM_INFORMA
31
31
/// The number is based on what I observe on a pretty standard Windows 11
32
32
const SYSTEM_HANDLE_INFORMATION_INITIAL_SIZE : usize = 2_500_000 ;
33
33
34
+ /// Max retry attempts for querying system handle information before giving up
35
+ const MAX_RETRY_ATTEMPTS : u32 = 5 ;
36
+
34
37
/// Check if there are no open handles to the given files.
35
38
///
36
39
/// Uses undocumented NT API to obtain open handles on the system.
37
40
/// See: https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/get-all-open-handles-and-kernel-object-address-from-userland
38
41
pub async fn check_files_closed ( file_paths : & [ & Path ] ) -> io:: Result < bool > {
39
42
let current_pid = unsafe { GetCurrentProcessId ( ) } ;
40
-
41
- // Allocate info struct on heap with some initial value
42
- let mut reserved_memory = SYSTEM_HANDLE_INFORMATION_INITIAL_SIZE ;
43
- let mut handle_table_info = HeapGuard :: < SystemHandleInformation > :: new ( reserved_memory) ?;
44
-
45
- // Request system handle information
46
- let mut status: NTSTATUS = NTSTATUS :: default ( ) ;
47
- let mut return_len = reserved_memory as u32 ;
48
- for _ in 0 ..2 {
49
- status = unsafe {
50
- NtQuerySystemInformation (
51
- SYSTEM_HANDLE_INFORMATION_CLASS ,
52
- handle_table_info. as_mut_ptr ( ) as _ ,
53
- return_len,
54
- & mut return_len,
55
- )
56
- } ;
57
-
58
- // Buffer is too small, resize memory and retry again.
59
- if status == STATUS_INFO_LENGTH_MISMATCH {
60
- tracing:: trace!( "Buffer is too small ({reserved_memory}), resizing to {return_len}" ) ;
61
- reserved_memory = return_len as usize ;
62
- handle_table_info. reallocate ( reserved_memory) ?;
63
- } else {
64
- break ;
65
- }
66
- }
67
- status. ok ( ) ?;
43
+ let handle_table_info = query_system_handle_table ( ) ?;
68
44
69
45
// Convert returned data into slice
70
46
let num_handles = unsafe { ( * handle_table_info. inner ) . number_of_handles } ;
@@ -106,6 +82,43 @@ pub async fn check_files_closed(file_paths: &[&Path]) -> io::Result<bool> {
106
82
Ok ( true )
107
83
}
108
84
85
+ fn query_system_handle_table ( ) -> io:: Result < HeapGuard < SystemHandleInformation > > {
86
+ // Allocate info struct on heap with some initial value
87
+ let mut reserved_memory = SYSTEM_HANDLE_INFORMATION_INITIAL_SIZE ;
88
+ let mut handle_table_info = HeapGuard :: < SystemHandleInformation > :: new ( reserved_memory) ?;
89
+
90
+ // Request system handle information
91
+ let mut status: NTSTATUS = NTSTATUS :: default ( ) ;
92
+ for _ in 0 ..MAX_RETRY_ATTEMPTS {
93
+ let mut return_len = reserved_memory as u32 ;
94
+ status = unsafe {
95
+ NtQuerySystemInformation (
96
+ SYSTEM_HANDLE_INFORMATION_CLASS ,
97
+ handle_table_info. as_mut_ptr ( ) as _ ,
98
+ return_len,
99
+ & mut return_len,
100
+ )
101
+ } ;
102
+
103
+ // Buffer is too small, resize memory and retry again.
104
+ if status == STATUS_INFO_LENGTH_MISMATCH {
105
+ // Allocate a bit more memory since the size of table can change between calls
106
+ let resize_to = ( return_len as usize ) * 3 / 2 ;
107
+
108
+ tracing:: trace!(
109
+ "Buffer is too small ({reserved_memory}), returned length: {return_len}, resizing buffer to: {resize_to}"
110
+ ) ;
111
+
112
+ reserved_memory = resize_to;
113
+ handle_table_info. reallocate ( reserved_memory) ?;
114
+ } else {
115
+ break ;
116
+ }
117
+ }
118
+
119
+ Ok ( status. ok ( ) . map ( |_| handle_table_info) ?)
120
+ }
121
+
109
122
#[ repr( C ) ]
110
123
#[ derive( Copy , Clone ) ]
111
124
struct SystemHandleInformation {
0 commit comments