I'm staring some system programming in Rust and am playing with the windows create.
As a simple starter solution I want to get some system information via the NtQuerySystemInformation function call.
To get the buffer I use the following function:
fn query_system_information_buffer(information_class: SYSTEM_INFORMATION_CLASS) -> Option<Vec<u8>> {
let mut size: u32 = 1 << 20; // 1 MB
let max_size: u32 = 1 << 24; // 16 MB
let mut buffer = vec![0u8; size as usize];
let mut status: windows::core::Result<()>;
loop {
let mut return_length: u32 = 0;
status = unsafe {
NtQuerySystemInformation(
information_class,
buffer.as_mut_ptr() as *mut c_void,
size,
&mut return_length,
)
};
// In case the system information returned Ok, then we can safely beark from the loop and transform the byte
// buffer into the required structure. If we have an error, we null the buffer (safety!) and resize it to the
// desired new size.
match &status {
Ok(_) => break,
Err(e) => {
let h_status = e.code();
if h_status == STATUS_INFO_LENGTH_MISMATCH.to_hresult() {
size = if return_length > size {
return_length
} else {
size * 2
};
buffer.fill(0);
buffer.resize(size as usize, 0);
}
}
};
// If the resized buffer exceeds the maximum size, we just break from the loop, since we can't get anything
// useful anymore.
if size >= max_size {
break;
}
}
// If the status returned an error, we simply return None (Rust's memory system will deal with the buffer), else we
// transform the buffer into the info struct and return that to the caller.
if status.is_err() {
None
} else {
Some(buffer)
}
}
This all works fine and well, but as soon as I try to convert the byte buffer into a struct (called SYSTEM_HANDLE_INFORMATION_EX
) I run into problems. As far as I can tell, the C struct definition uses the C89 struct hack, at least in the definitions I could find online.
My first approach was to recreate the struct verbatim like
use std::ffi::{c_ulong, c_ushort, c_void};
#[derive(Debug)]
#[repr(C)]
struct SYSTEM_HANDLE_INFORMATION_EX {
pub num_handles: usize,
reserved: *const c_ulong,
pub handles: [SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX ; 1],
}
#[derive(Debug)]
#[repr(C)]
struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
pub object: *const c_void,
pub unique_process_id: *const c_ulong,
pub handle_value: *const c_void,
pub granted_access: c_ulong,
pub creator_back_trace_index: c_ushort,
pub object_type_index: c_ushort,
pub handle_attributes: c_ulong,
reserved: c_ulong,
}
fn query_system_information(information_class: SYSTEM_INFORMATION_CLASS) -> Option<SYSTEM_HANDLE_INFORMATION_EX> {
let buffer = query_system_information_buffer(information_class);
let info: SYSTEM_HANDLE_INFORMATION_EX = unsafe {
std::ptr::read(buffer.as_ptr() as *const _)
};
Some(info)
}
However, since I want to loop over the handles later on, this compiles, but obviously won't work (and on top of that is UB).
Then I read around a bit and possible solutions pointed me to replace the handles
field with a slice, i.e.
#[derive(Debug)]
#[repr(C)]
struct SYSTEM_HANDLE_INFORMATION_EX {
pub num_handles: usize,
reserved: *const c_ulong,
pub handles: [SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX],
}
But now the size is unknown and it won't compile. More searching led me to THIS thread here.
I understand the concept behind it, but somehow I cannot get it to run on my side. And pointers (heh!) to what I'm doing wrong get me going?