#![allow(clippy::field_reassign_with_default)]
use windows::core::{GUID, PCSTR, PCWSTR, PSTR};
use windows::Win32::Foundation::{
CloseHandle, DuplicateHandle, GetLastError, DUPLICATE_SAME_ACCESS, ERROR_SUCCESS,
ERROR_WMI_INSTANCE_NOT_FOUND, HANDLE, INVALID_HANDLE_VALUE, WIN32_ERROR,
};
use windows::Win32::Security::{
AdjustTokenPrivileges, LookupPrivilegeValueA, SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES,
TOKEN_PRIVILEGES,
};
use windows::Win32::System::Diagnostics::Debug::{
FormatMessageA, SymCleanup, SymFromAddr, SymGetModuleInfo64, SymGetOptions, SymInitialize,
SymLoadModuleExW, SymRefreshModuleList, SymSetOptions, FORMAT_MESSAGE_FROM_SYSTEM,
FORMAT_MESSAGE_IGNORE_INSERTS, IMAGEHLP_MODULE64, SYMBOL_INFO_FLAGS, SYMOPT_DEBUG,
SYM_LOAD_FLAGS,
};
use windows::Win32::System::Diagnostics::Etw::{
CloseTrace, ControlTraceA, OpenTraceA, ProcessTrace, StartTraceA, SystemTraceControlGuid,
TraceSampledProfileIntervalInfo, TraceSetInformation, TraceStackTracingInfo, CLASSIC_EVENT_ID,
EVENT_RECORD, EVENT_TRACE_CONTROL_STOP, EVENT_TRACE_FLAG_IMAGE_LOAD, EVENT_TRACE_FLAG_PROFILE,
EVENT_TRACE_LOGFILEA, EVENT_TRACE_PROPERTIES, EVENT_TRACE_REAL_TIME_MODE, KERNEL_LOGGER_NAMEA,
PROCESS_TRACE_MODE_EVENT_RECORD, PROCESS_TRACE_MODE_RAW_TIMESTAMP,
PROCESS_TRACE_MODE_REAL_TIME, TRACE_PROFILE_INTERVAL, WNODE_FLAG_TRACED_GUID,
};
use windows::Win32::System::SystemInformation::{GetVersionExA, OSVERSIONINFOA};
use windows::Win32::System::SystemServices::SE_SYSTEM_PROFILE_NAME;
use windows::Win32::System::Threading::{
GetCurrentProcess, GetCurrentThread, OpenProcessToken, SetThreadPriority, CREATE_SUSPENDED,
THREAD_PRIORITY_TIME_CRITICAL,
};
use std::ffi::OsString;
use std::io::Write;
use std::mem::size_of;
use std::os::windows::{
ffi::{OsStrExt, OsStringExt},
prelude::AsRawHandle,
};
use std::ptr::{addr_of, addr_of_mut, null_mut};
use std::sync::atomic::{AtomicBool, Ordering};
type StackMap = rustc_hash::FxHashMap<[u64; MAX_STACK_DEPTH], u64>;
struct TraceContext {
target_process_handle: HANDLE,
stack_counts_hashmap: StackMap,
target_proc_pid: u32,
trace_running: AtomicBool,
show_kernel_samples: bool,
}
impl TraceContext {
unsafe fn new(
target_process_handle: HANDLE,
target_proc_pid: u32,
kernel_stacks: bool,
) -> Result<Self> {
SymSetOptions(SymGetOptions() | SYMOPT_DEBUG);
let ret = SymInitialize(target_process_handle, PCSTR(null_mut()), false);
if ret.0 != 1 {
return Err(get_last_error("TraceContext::new SymInitialize"));
}
Ok(Self {
target_process_handle,
stack_counts_hashmap: Default::default(),
target_proc_pid,
trace_running: AtomicBool::new(false),
show_kernel_samples: std::env::var("BLONDIE_KERNEL")
.map(|value| {
let upper = value.to_uppercase();
["Y", "YES", "TRUE"].iter().any(|truthy| &upper == truthy)
})
.unwrap_or(kernel_stacks),
})
}
}
impl Drop for TraceContext {
fn drop(&mut self) {
unsafe {
let ret = SymCleanup(self.target_process_handle);
if ret.0 != 1 {
panic!("TraceContext::SymCleanup error:{:?}", get_last_error(""));
}
let ret = CloseHandle(self.target_process_handle);
if ret.0 == 0 {
panic!("TraceContext::CloseHandle error:{:?}", get_last_error(""));
}
}
}
}
const MAX_STACK_DEPTH: usize = 200;
#[derive(Debug)]
pub enum Error {
NotAnAdmin,
SpawnErr(std::io::Error),
WaitOnChildErr(std::io::Error),
Other(WIN32_ERROR, String, &'static str),
UnsupportedOsVersion,
UnknownError,
}
type Result<T> = std::result::Result<T, Error>;
fn get_last_error(extra: &'static str) -> Error {
const BUF_LEN: usize = 1024;
let mut buf = [0u8; BUF_LEN];
let code = unsafe { GetLastError() };
let chars_written = unsafe {
FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
null_mut(),
code.0,
0,
PSTR(buf.as_mut_ptr()),
BUF_LEN as u32,
null_mut(),
)
};
assert!(chars_written != 0);
let code_str = unsafe {
std::ffi::CStr::from_ptr(buf.as_ptr().cast())
.to_str()
.unwrap()
};
Error::Other(code, code_str.to_string(), extra)
}
unsafe fn clone_handle(h: HANDLE) -> Result<HANDLE> {
let mut target_h = HANDLE::default();
let ret = DuplicateHandle(
GetCurrentProcess(),
h,
GetCurrentProcess(),
&mut target_h,
0,
false,
DUPLICATE_SAME_ACCESS,
);
if ret.0 == 0 {
return Err(get_last_error("clone_handle"));
}
Ok(target_h)
}
fn acquire_priviledges() -> Result<()> {
let mut privs = TOKEN_PRIVILEGES::default();
privs.PrivilegeCount = 1;
privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if unsafe {
LookupPrivilegeValueA(
PCSTR(null_mut()),
PCSTR(
SE_SYSTEM_PROFILE_NAME
.as_bytes()
.iter()
.cloned()
.chain(Some(0))
.collect::<Vec<u8>>()
.as_ptr(),
),
&mut privs.Privileges[0].Luid,
)
.0 == 0
} {
return Err(get_last_error("acquire_privileges LookupPrivilegeValueA"));
}
let mut pt = HANDLE::default();
if unsafe { OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &mut pt).0 == 0 } {
return Err(get_last_error("OpenProcessToken"));
}
let adjust = unsafe {
AdjustTokenPrivileges(pt, false, addr_of!(privs).cast(), 0, null_mut(), null_mut())
};
if adjust.0 == 0 {
let err = Err(get_last_error("AdjustTokenPrivileges"));
unsafe {
CloseHandle(pt);
}
return err;
}
let ret = unsafe { CloseHandle(pt) };
if ret.0 == 0 {
return Err(get_last_error("acquire_privileges CloseHandle"));
}
let status = unsafe { GetLastError() };
if status != ERROR_SUCCESS {
return Err(Error::NotAnAdmin);
}
Ok(())
}
unsafe fn trace_from_process(
target_process: &mut std::process::Child,
is_suspended: bool,
kernel_stacks: bool,
) -> Result<TraceContext> {
let mut winver_info = OSVERSIONINFOA::default();
winver_info.dwOSVersionInfoSize = size_of::<OSVERSIONINFOA>() as u32;
let ret = GetVersionExA(&mut winver_info);
if ret.0 == 0 {
return Err(get_last_error("TraceSetInformation interval"));
}
if winver_info.dwMajorVersion < 6
|| (winver_info.dwMajorVersion == 6 && winver_info.dwMinorVersion == 0)
{
return Err(Error::UnsupportedOsVersion);
}
acquire_priviledges()?;
if winver_info.dwMajorVersion > 6
|| (winver_info.dwMajorVersion == 6 && winver_info.dwMinorVersion >= 2)
{
let mut interval = TRACE_PROFILE_INTERVAL::default();
interval.Interval = (1000000000 / 8000) / 100;
let ret = TraceSetInformation(
0,
TraceSampledProfileIntervalInfo,
addr_of!(interval).cast(),
size_of::<TRACE_PROFILE_INTERVAL>() as u32,
);
if ret != ERROR_SUCCESS.0 {
return Err(get_last_error("TraceSetInformation interval"));
}
}
let mut kernel_logger_name_with_nul = KERNEL_LOGGER_NAMEA
.as_bytes()
.iter()
.cloned()
.chain(Some(0))
.collect::<Vec<u8>>();
const PROPS_SIZE: usize = size_of::<EVENT_TRACE_PROPERTIES>() + KERNEL_LOGGER_NAMEA.len() + 1;
#[derive(Clone)]
#[repr(C)]
struct EVENT_TRACE_PROPERTIES_WITH_STRING {
data: EVENT_TRACE_PROPERTIES,
s: [u8; KERNEL_LOGGER_NAMEA.len() + 1],
}
let mut event_trace_props = EVENT_TRACE_PROPERTIES_WITH_STRING {
data: EVENT_TRACE_PROPERTIES::default(),
s: [0u8; KERNEL_LOGGER_NAMEA.len() + 1],
};
event_trace_props.data.EnableFlags = EVENT_TRACE_FLAG_PROFILE | EVENT_TRACE_FLAG_IMAGE_LOAD;
event_trace_props.data.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
event_trace_props.data.Wnode.BufferSize = PROPS_SIZE as u32;
event_trace_props.data.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
event_trace_props.data.Wnode.ClientContext = 3;
event_trace_props.data.Wnode.Guid = SystemTraceControlGuid;
event_trace_props.data.BufferSize = 1024;
let core_count = std::thread::available_parallelism().unwrap();
event_trace_props.data.MinimumBuffers = core_count.get() as u32 * 4;
event_trace_props.data.MaximumBuffers = core_count.get() as u32 * 6;
event_trace_props.data.LoggerNameOffset = size_of::<EVENT_TRACE_PROPERTIES>() as u32;
event_trace_props
.s
.copy_from_slice(&kernel_logger_name_with_nul[..]);
let kernel_logger_name_with_nul_pcstr = PCSTR(kernel_logger_name_with_nul.as_ptr());
{
let mut event_trace_props_copy = event_trace_props.clone();
let control_stop_retcode = ControlTraceA(
0,
kernel_logger_name_with_nul_pcstr,
addr_of_mut!(event_trace_props_copy) as *mut _,
EVENT_TRACE_CONTROL_STOP,
);
if control_stop_retcode != ERROR_SUCCESS.0
&& control_stop_retcode != ERROR_WMI_INSTANCE_NOT_FOUND.0
{
return Err(get_last_error("ControlTraceA STOP"));
}
}
let mut trace_session_handle = 0;
{
let start_retcode = StartTraceA(
&mut trace_session_handle,
kernel_logger_name_with_nul_pcstr,
addr_of_mut!(event_trace_props) as *mut _,
);
if start_retcode != ERROR_SUCCESS.0 {
return Err(get_last_error("StartTraceA"));
}
}
{
let mut stack_event_id = CLASSIC_EVENT_ID::default();
let perfinfo_guid = GUID {
data1: 0xce1dbfb4,
data2: 0x137e,
data3: 0x4da6,
data4: [0x87, 0xb0, 0x3f, 0x59, 0xaa, 0x10, 0x2c, 0xbc],
};
stack_event_id.EventGuid = perfinfo_guid;
stack_event_id.Type = 46; let enable_stacks_retcode = TraceSetInformation(
trace_session_handle,
TraceStackTracingInfo,
addr_of!(stack_event_id).cast(),
size_of::<CLASSIC_EVENT_ID>() as u32,
);
if enable_stacks_retcode != ERROR_SUCCESS.0 {
return Err(get_last_error("TraceSetInformation stackwalk"));
}
}
let target_pid = target_process.id();
let target_proc_handle = clone_handle(HANDLE(target_process.as_raw_handle() as isize))?;
let mut context = TraceContext::new(target_proc_handle, target_pid, kernel_stacks)?;
let mut log = EVENT_TRACE_LOGFILEA::default();
log.LoggerName = PSTR(kernel_logger_name_with_nul.as_mut_ptr());
log.Anonymous1.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME
| PROCESS_TRACE_MODE_EVENT_RECORD
| PROCESS_TRACE_MODE_RAW_TIMESTAMP;
log.Context = addr_of_mut!(context).cast();
unsafe extern "system" fn event_record_callback(record: *mut EVENT_RECORD) {
let provider_guid_data1 = (*record).EventHeader.ProviderId.data1;
let event_opcode = (*record).EventHeader.EventDescriptor.Opcode;
let context = &mut *(*record).UserContext.cast::<TraceContext>();
context.trace_running.store(true, Ordering::Relaxed);
const EVENT_TRACE_TYPE_LOAD: u8 = 10;
if event_opcode == EVENT_TRACE_TYPE_LOAD {
let event = (*record).UserData.cast::<ImageLoadEvent>().read_unaligned();
if event.ProcessId != context.target_proc_pid {
return;
}
let filename_p = (*record)
.UserData
.cast::<ImageLoadEvent>()
.offset(1)
.cast::<u16>();
let filename_str = OsString::from_wide(std::slice::from_raw_parts(
filename_p,
((*record).UserDataLength as usize - size_of::<ImageLoadEvent>()) / 2,
));
let filename_str = filename_str.to_str().unwrap();
let image_base = event.ImageBase;
let verbatim_path_os: OsString =
filename_str.replacen("\\Device\\", "\\\\?\\", 1).into();
let verbatim_path = verbatim_path_os
.encode_wide()
.chain(Some(0))
.collect::<Vec<_>>();
let ret = SymLoadModuleExW(
context.target_process_handle,
HANDLE(0),
PCWSTR(verbatim_path.as_ptr()),
PCWSTR(null_mut()),
image_base as u64,
0,
null_mut(),
SYM_LOAD_FLAGS(0),
);
if ret == 0 {
if GetLastError() != ERROR_SUCCESS {
println!(
"Error loading module in_path:{} verbatim_path:{} GetLastError:{:?} base_of_image:{}",
filename_str,
verbatim_path_os.to_string_lossy(),
get_last_error(""),
image_base
);
}
return;
}
SymRefreshModuleList(context.target_process_handle);
return;
}
let stackwalk_guid_data1 = 0xdef2fe46;
let stackwalk_event_type = 32;
if event_opcode != stackwalk_event_type || stackwalk_guid_data1 != provider_guid_data1 {
return;
}
let ud_p = (*record).UserData;
let _timestamp = ud_p.cast::<u64>().read_unaligned();
let proc = ud_p.cast::<u32>().offset(2).read_unaligned();
let _thread = ud_p.cast::<u32>().offset(3).read_unaligned();
if proc != context.target_proc_pid {
return;
}
let stack_depth_32 = ((*record).UserDataLength - 16) / 4;
let stack_depth_64 = stack_depth_32 / 2;
let stack_depth = if size_of::<usize>() == 8 {
stack_depth_64
} else {
stack_depth_32
};
let mut tmp = vec![];
let mut stack_addrs = if size_of::<usize>() == 8 {
std::slice::from_raw_parts(ud_p.cast::<u64>().offset(2), stack_depth as usize)
} else {
tmp.extend(
std::slice::from_raw_parts(
ud_p.cast::<u64>().offset(2).cast::<u32>(),
stack_depth as usize,
)
.iter()
.map(|x| *x as u64),
);
&tmp
};
if stack_addrs.len() > MAX_STACK_DEPTH {
stack_addrs = &stack_addrs[(stack_addrs.len() - MAX_STACK_DEPTH)..];
}
let mut stack = [0u64; MAX_STACK_DEPTH];
stack[..(stack_depth as usize).min(MAX_STACK_DEPTH)].copy_from_slice(stack_addrs);
let entry = context.stack_counts_hashmap.entry(stack);
*entry.or_insert(0) += 1;
}
log.Anonymous2.EventRecordCallback = Some(event_record_callback);
let trace_processing_handle = OpenTraceA(&mut log);
if trace_processing_handle == INVALID_HANDLE_VALUE.0 as u64 {
return Err(get_last_error("OpenTraceA processing"));
}
let (sender, recvr) = std::sync::mpsc::channel();
std::thread::spawn(move || {
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
ProcessTrace(&[trace_processing_handle], null_mut(), null_mut());
let ret = CloseTrace(trace_processing_handle);
if ret != ERROR_SUCCESS.0 {
println!("Error closing trace");
}
sender.send(()).unwrap();
});
while !context.trace_running.load(Ordering::Relaxed) {
std::hint::spin_loop();
}
if is_suspended {
let ntdll =
windows::Win32::System::LibraryLoader::GetModuleHandleA(PCSTR("ntdll.dll\0".as_ptr()))
.unwrap();
#[allow(non_snake_case)]
let NtResumeProcess = windows::Win32::System::LibraryLoader::GetProcAddress(
ntdll,
PCSTR("NtResumeProcess\0".as_ptr()),
)
.unwrap();
#[allow(non_snake_case)]
let NtResumeProcess: extern "system" fn(isize) -> i32 =
std::mem::transmute(NtResumeProcess);
NtResumeProcess(context.target_process_handle.0);
}
target_process.wait().map_err(Error::WaitOnChildErr)?;
let ret = ControlTraceA(
0,
PCSTR(kernel_logger_name_with_nul.as_ptr()),
addr_of_mut!(event_trace_props) as *mut _,
EVENT_TRACE_CONTROL_STOP,
);
if ret != ERROR_SUCCESS.0 {
return Err(get_last_error("ControlTraceA STOP ProcessTrace"));
}
if recvr.recv().is_err() {
return Err(Error::UnknownError);
}
SymRefreshModuleList(context.target_process_handle);
Ok(context)
}
pub struct CollectionResults(TraceContext);
pub fn trace_child(
mut process: std::process::Child,
kernel_stacks: bool,
) -> Result<CollectionResults> {
let res = unsafe { trace_from_process(&mut process, false, kernel_stacks) };
res.map(CollectionResults)
}
pub fn trace_command(
mut command: std::process::Command,
kernel_stacks: bool,
) -> Result<CollectionResults> {
use std::os::windows::process::CommandExt;
let mut proc = command
.creation_flags(CREATE_SUSPENDED.0)
.spawn()
.map_err(Error::SpawnErr)?;
let res = unsafe { trace_from_process(&mut proc, true, kernel_stacks) };
if res.is_err() {
let _ = proc.kill();
}
res.map(CollectionResults)
}
pub struct CallStack<'a> {
ctx: &'a TraceContext,
stack: &'a [u64; MAX_STACK_DEPTH],
sample_count: u64,
}
pub struct Address {
pub addr: u64,
pub displacement: u64,
pub symbol_name: Option<String>,
pub image_name: Option<String>,
}
impl<'a> CallStack<'a> {
pub fn iter_resolved_addresses(&'a self) -> impl std::iter::Iterator<Item = Address> + 'a {
self.stack
.iter()
.take_while(|&&addr| addr != 0)
.map(|&addr| {
let mut symbol_name = None;
let mut image_name = None;
let mut sym_info: SYMBOL_INFO_WITH_STRING = unsafe { std::mem::zeroed() };
sym_info.MaxNameLen = MAX_SYM_LEN as u32 - 4;
let offset_name2 = addr_of!(sym_info.Name) as usize - addr_of!(sym_info) as usize;
sym_info.SizeOfStruct = offset_name2 as u32 + 4;
let mut displacement = 0u64;
let ret = unsafe {
SymFromAddr(
self.ctx.target_process_handle,
addr,
&mut displacement,
addr_of_mut!(sym_info).cast(),
)
};
if ret.0 == 1 {
let name_addr = addr_of!(sym_info.Name);
let sym_str =
unsafe { std::ffi::CStr::from_ptr(name_addr.cast()).to_str().unwrap() };
symbol_name = Some(sym_str.to_string());
let mut image_info = IMAGEHLP_MODULE64::default();
image_info.SizeOfStruct = size_of::<IMAGEHLP_MODULE64>() as u32;
let ret = unsafe {
SymGetModuleInfo64(self.ctx.target_process_handle, addr, &mut image_info)
};
if ret.0 == 1 {
let image_name_str = unsafe {
std::ffi::CStr::from_ptr(addr_of!(image_info.ModuleName).cast())
.to_str()
.unwrap()
};
image_name = Some(image_name_str.to_string());
}
};
Address {
addr,
displacement,
symbol_name,
image_name,
}
})
}
}
impl CollectionResults {
pub fn iter_callstacks(&self) -> impl std::iter::Iterator<Item = CallStack<'_>> {
self.0.stack_counts_hashmap.iter().map(|x| CallStack {
ctx: &self.0,
stack: x.0,
sample_count: *x.1,
})
}
pub fn write_dtrace<W: Write>(&self, mut w: W) -> Result<()> {
if self.0.show_kernel_samples {
unsafe {
load_kernel_modules(self.0.target_process_handle);
}
}
'next_callstack: for callstack in self.iter_callstacks() {
for resolved_addr in callstack.iter_resolved_addresses() {
let displacement = resolved_addr.displacement;
let address = resolved_addr.addr;
if !self.0.show_kernel_samples {
if address & (1 << 63) != 0 {
continue 'next_callstack;
}
}
if let Some(symbol_name) = resolved_addr.symbol_name {
if let Some(image_name) = resolved_addr.image_name {
if displacement != 0 {
writeln!(w, "\t\t{image_name}`{symbol_name}+0x{displacement:X}")
.unwrap();
} else {
writeln!(w, "\t\t{image_name}`{symbol_name}").unwrap();
}
} else {
if displacement != 0 {
writeln!(w, "\t\t{symbol_name}+0x{displacement:X}").unwrap();
} else {
writeln!(w, "\t\t{symbol_name}").unwrap();
}
}
} else {
writeln!(w, "\t\t`0x{address:X}").unwrap();
}
}
let count = callstack.sample_count;
write!(w, "\t\t{count}\n\n").unwrap();
}
Ok(())
}
}
#[allow(non_snake_case)]
#[derive(Debug)]
#[repr(C)]
struct ImageLoadEvent {
ImageBase: usize,
ImageSize: usize,
ProcessId: u32,
ImageCheckSum: u32,
TimeDateStamp: u32,
Reserved0: u32,
DefaultBase: usize,
Reserved1: u32,
Reserved2: u32,
Reserved3: u32,
Reserved4: u32,
}
const MAX_SYM_LEN: usize = 8 * 1024;
#[allow(non_snake_case)]
#[derive(Clone)]
#[repr(C)]
struct SYMBOL_INFO_WITH_STRING {
SizeOfStruct: u32,
TypeIndex: u32,
Reserved: [u64; 2],
Index: u32,
Size: u32,
ModBase: u64,
Flags: SYMBOL_INFO_FLAGS,
Value: u64,
Address: u64,
Register: u32,
Scope: u32,
Tag: u32,
NameLen: u32,
MaxNameLen: u32,
Name: [u8; MAX_SYM_LEN],
}
unsafe fn load_kernel_modules(handle: HANDLE) {
#[link(name = "ntdll")]
extern "system" {
fn NtQuerySystemInformation(
SystemInformationClass: u32,
SystemInformation: *mut (),
SystemInformationLength: u32,
ReturnLength: *mut u32,
) -> i32;
}
const BUF_LEN: usize = 1024 * 1024;
let mut out_buf = vec![0u8; BUF_LEN];
let mut out_size = 0u32;
let retcode = NtQuerySystemInformation(
11,
out_buf.as_mut_ptr().cast(),
BUF_LEN as u32,
&mut out_size,
);
if retcode >= 0 {
let number_of_modules = out_buf.as_ptr().cast::<u32>().read_unaligned() as usize;
#[repr(C)]
#[derive(Debug)]
#[allow(non_snake_case)]
struct _RTL_PROCESS_MODULE_INFORMATION {
Section: *mut std::ffi::c_void,
MappedBase: *mut std::ffi::c_void,
ImageBase: *mut std::ffi::c_void,
ImageSize: u32,
Flags: u32,
LoadOrderIndex: u16,
InitOrderIndex: u16,
LoadCount: u16,
OffsetToFileName: u16,
FullPathName: [u8; 256],
}
let modules_ptr = out_buf
.as_ptr()
.cast::<u32>()
.offset(2)
.cast::<_RTL_PROCESS_MODULE_INFORMATION>();
let modules = std::slice::from_raw_parts(modules_ptr, number_of_modules);
for module in modules {
let mod_str_filepath = std::ffi::CStr::from_ptr(module.FullPathName.as_ptr().cast())
.to_str()
.unwrap();
let verbatim_path_osstring: OsString = mod_str_filepath
.replacen("\\SystemRoot\\", "\\\\?\\C:\\Windows\\", 1)
.into();
let verbatim_path = verbatim_path_osstring
.encode_wide()
.chain(Some(0))
.collect::<Vec<_>>();
let ret = SymLoadModuleExW(
handle,
HANDLE(0),
PCWSTR(verbatim_path.as_ptr()),
PCWSTR(null_mut()),
module.ImageBase as u64,
0,
null_mut(),
SYM_LOAD_FLAGS(0),
);
if ret == 0 {
if GetLastError() != ERROR_SUCCESS {
}
continue;
}
SymRefreshModuleList(handle);
}
} else {
println!("Failed to load kernel modules");
}
}