use windows::{
Win32::Foundation::CloseHandle,
Win32::System::Diagnostics::ToolHelp::{
CreateToolhelp32Snapshot, Module32First, Module32Next, Process32First, Process32Next,
MODULEENTRY32, PROCESSENTRY32, TH32CS_SNAPMODULE, TH32CS_SNAPMODULE32, TH32CS_SNAPPROCESS,
},
};
use crate::utils::char_array_to_string;
pub fn get_process_pid(process_name: &str) -> Option<u32> {
unsafe {
let snapshot = match CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) {
Ok(handle) => handle,
Err(_) => return None,
};
let mut entry = PROCESSENTRY32::default();
entry.dwSize = std::mem::size_of::<PROCESSENTRY32>() as u32;
if Process32First(snapshot, &mut entry).is_err() {
let _ = CloseHandle(snapshot);
return None;
}
loop {
let name = char_array_to_string(&entry.szExeFile);
if name.eq_ignore_ascii_case(process_name) {
let pid = entry.th32ProcessID;
let _ = CloseHandle(snapshot);
return Some(pid);
}
entry.szExeFile.fill(0);
if Process32Next(snapshot, &mut entry).is_err() {
break;
}
}
let _ = CloseHandle(snapshot);
None
}
}
pub fn list_processes() -> Vec<(u32, String)> {
let mut processes = Vec::new();
unsafe {
let snapshot = match CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) {
Ok(handle) => handle,
Err(_) => return processes,
};
let mut entry = PROCESSENTRY32::default();
entry.dwSize = std::mem::size_of::<PROCESSENTRY32>() as u32;
if Process32First(snapshot, &mut entry).is_ok() {
loop {
let name = char_array_to_string(&entry.szExeFile);
processes.push((entry.th32ProcessID, name));
entry.szExeFile.fill(0);
if Process32Next(snapshot, &mut entry).is_err() {
break;
}
}
}
let _ = CloseHandle(snapshot);
}
processes
}
pub fn get_module_base_address(pid: u32, module_name: &str) -> Option<usize> {
unsafe {
let snapshot = match CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid)
{
Ok(handle) => handle,
Err(_) => return None,
};
let mut entry = MODULEENTRY32::default();
entry.dwSize = std::mem::size_of::<MODULEENTRY32>() as u32;
if Module32First(snapshot, &mut entry).is_err() {
let _ = CloseHandle(snapshot);
return None;
}
loop {
let name = char_array_to_string(&entry.szModule);
if name.eq_ignore_ascii_case(module_name) {
let addr = entry.modBaseAddr as usize;
let _ = CloseHandle(snapshot);
return Some(addr);
}
entry.szModule = [0i8; 256];
entry.szExePath = [0i8; 260];
if Module32Next(snapshot, &mut entry).is_err() {
break;
}
}
let _ = CloseHandle(snapshot);
None
}
}
pub fn list_modules(pid: u32) -> Vec<(String, usize)> {
let mut modules = Vec::new();
unsafe {
let snapshot = match CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid)
{
Ok(handle) => handle,
Err(_) => return modules,
};
let mut entry = MODULEENTRY32::default();
entry.dwSize = std::mem::size_of::<MODULEENTRY32>() as u32;
if Module32First(snapshot, &mut entry).is_ok() {
loop {
let name = char_array_to_string(&entry.szModule);
let addr = entry.modBaseAddr as usize;
modules.push((name, addr));
entry.szModule = [0i8; 256];
entry.szExePath = [0i8; 260];
if Module32Next(snapshot, &mut entry).is_err() {
break;
}
}
}
let _ = CloseHandle(snapshot);
}
modules
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_find_nonexistent_process() {
let result = get_process_pid("definitely_not_running_process_12345_xyz.exe");
assert!(result.is_none());
}
#[test]
fn test_list_processes_not_empty() {
let processes = list_processes();
println!("{:?}", processes);
assert!(!processes.is_empty());
for (_pid, name) in &processes {
assert!(!name.is_empty());
}
assert!(processes.iter().any(|(pid, _)| *pid == 0));
}
#[test]
fn test_char_array_conversion() {
let chars = [
b'h' as i8, b'e' as i8, b'l' as i8, b'l' as i8, b'o' as i8, 0,
];
assert_eq!(char_array_to_string(&chars), "hello");
let chars = [0];
assert_eq!(char_array_to_string(&chars), "");
let chars = [b't' as i8, b'e' as i8, b's' as i8, b't' as i8];
assert_eq!(char_array_to_string(&chars), "test");
}
#[test]
fn test_list_modules_for_current_process() {
let current_pid = std::process::id();
let modules = list_modules(current_pid);
assert!(!modules.is_empty());
println!("Found {} modules in current process", modules.len());
for (name, addr) in &modules {
assert!(!name.is_empty());
assert!(*addr > 0);
println!(" Module: {}, Address: 0x{:X}", name, addr);
}
if let Some((first_name, _)) = modules.first() {
assert!(first_name.ends_with(".exe"));
}
}
#[test]
fn test_find_moudle_nonexistent() {
let current_pid = std::process::id();
let result = get_module_base_address(current_pid, "definitely_not_exist_12345.dll");
assert!(result.is_none());
}
#[test]
fn test_find_moudle_kernel32() {
let current_pid = std::process::id();
if let Some(addr) = get_module_base_address(current_pid, "kernel32.dll") {
assert!(addr > 0);
println!("kernel32.dll found at: 0x{:X}", addr);
} else {
if let Some(addr) = get_module_base_address(current_pid, "KERNEL32.DLL") {
assert!(addr > 0);
println!("KERNEL32.DLL found at: 0x{:X}", addr);
}
}
}
}