use memflow::os::process::*;
use memflow::prelude::v1::*;
use windows::core::PCSTR;
use windows::Win32::Foundation::{CloseHandle, HANDLE};
use windows::Win32::System::Diagnostics::ToolHelp::{
CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W, TH32CS_SNAPPROCESS,
};
use windows::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
use windows::Win32::Security::{
AdjustTokenPrivileges, LookupPrivilegeValueA, LUID_AND_ATTRIBUTES, SE_PRIVILEGE_ENABLED,
TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES,
};
use core::mem::{size_of, MaybeUninit};
use core::ptr;
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
pub mod mem;
use mem::ProcessVirtualMemory;
pub mod process;
use process::WindowsProcess;
struct KernelModule {}
pub(crate) struct Handle(HANDLE);
impl From<HANDLE> for Handle {
fn from(handle: HANDLE) -> Handle {
Handle(handle)
}
}
impl core::ops::Deref for Handle {
type Target = HANDLE;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Drop for Handle {
fn drop(&mut self) {
unsafe { CloseHandle(self.0) }.ok();
}
}
pub fn conv_err(_err: windows::core::Error) -> Error {
Error(ErrorOrigin::OsLayer, ErrorKind::Unknown)
}
unsafe fn enable_debug_privilege() -> Result<()> {
let process = GetCurrentProcess();
let mut token = HANDLE(0);
OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES, &mut token).map_err(conv_err)?;
let mut luid = Default::default();
let mut se_debug_name = *b"SeDebugPrivilege\0";
LookupPrivilegeValueA(
PCSTR(core::ptr::null_mut()),
PCSTR(se_debug_name.as_mut_ptr()),
&mut luid,
)
.map_err(conv_err)?;
let new_privileges = TOKEN_PRIVILEGES {
PrivilegeCount: 1,
Privileges: [LUID_AND_ATTRIBUTES {
Luid: luid,
Attributes: SE_PRIVILEGE_ENABLED,
}],
};
AdjustTokenPrivileges(
token,
false,
Some(&new_privileges),
std::mem::size_of_val(&new_privileges) as _,
None,
None,
)
.map_err(conv_err)
}
pub struct WindowsOs {
info: OsInfo,
cached_processes: Vec<ProcessInfo>,
cached_modules: Vec<KernelModule>,
}
impl WindowsOs {
pub fn new(args: &OsArgs) -> Result<Self> {
match args.extra_args.get("elevate_token") {
Some("off") | Some("OFF") | Some("Off") | Some("n") | Some("N") | Some("0") => {}
_ => {
unsafe { enable_debug_privilege() }?;
}
}
Ok(Default::default())
}
}
impl Clone for WindowsOs {
fn clone(&self) -> Self {
Self {
info: self.info.clone(),
cached_processes: vec![],
cached_modules: vec![],
}
}
}
impl Default for WindowsOs {
fn default() -> Self {
let info = OsInfo {
base: Address::NULL,
size: 0,
arch: ArchitectureIdent::X86(64, false),
};
Self {
info,
cached_modules: vec![],
cached_processes: vec![],
}
}
}
impl Os for WindowsOs {
type ProcessType<'a> = WindowsProcess;
type IntoProcessType = WindowsProcess;
fn process_address_list_callback(&mut self, callback: AddressCallback) -> Result<()> {
let handle =
Handle(unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }.map_err(conv_err)?);
let mut maybe_entry = MaybeUninit::<PROCESSENTRY32W>::uninit();
unsafe {
ptr::write(
&mut (*maybe_entry.as_mut_ptr()).dwSize,
size_of::<PROCESSENTRY32W>() as u32,
);
}
let ptr = maybe_entry.as_mut_ptr();
std::iter::once(unsafe { Process32FirstW(*handle, ptr) })
.chain(std::iter::repeat_with(|| unsafe {
Process32NextW(*handle, ptr)
}))
.take_while(|b| b.is_ok())
.map(|_| unsafe { maybe_entry.assume_init() })
.map(|p| {
let address = Address::from(p.th32ProcessID as umem);
let len = p.szExeFile.iter().take_while(|&&c| c != 0).count();
let path = OsString::from_wide(&p.szExeFile[..len]);
let path = path.to_string_lossy();
let path = &*path;
let name = path.rsplit_once('\\').map(|(_, end)| end).unwrap_or(path);
self.cached_processes.push(ProcessInfo {
address,
pid: address.to_umem() as _,
state: ProcessState::Alive,
name: name.into(),
path: path.into(),
command_line: "".into(),
sys_arch: self.info.arch,
proc_arch: self.info.arch,
dtb1: Address::invalid(),
dtb2: Address::invalid(),
});
address
})
.feed_into(callback);
Ok(())
}
fn process_info_by_address(&mut self, address: Address) -> Result<ProcessInfo> {
self.cached_processes
.iter()
.find(|p| p.address == address)
.cloned()
.ok_or(Error(ErrorOrigin::OsLayer, ErrorKind::NotFound))
}
fn process_by_info(&mut self, info: ProcessInfo) -> Result<Self::ProcessType<'_>> {
WindowsProcess::try_new(info)
}
fn into_process_by_info(mut self, info: ProcessInfo) -> Result<Self::IntoProcessType> {
self.process_by_info(info)
}
fn module_address_list_callback(&mut self, mut callback: AddressCallback) -> Result<()> {
(0..self.cached_modules.len())
.map(Address::from)
.take_while(|a| callback.call(*a))
.for_each(|_| {});
Ok(())
}
fn module_by_address(&mut self, _address: Address) -> Result<ModuleInfo> {
todo!()
}
fn primary_module_address(&mut self) -> Result<Address> {
Ok(self.module_by_name("ntoskrnl.exe")?.address)
}
fn primary_module(&mut self) -> Result<ModuleInfo> {
self.module_by_name("ntoskrnl.exe")
}
fn module_import_list_callback(
&mut self,
_info: &ModuleInfo,
_callback: ImportCallback,
) -> Result<()> {
Err(Error(ErrorOrigin::OsLayer, ErrorKind::NotImplemented))
}
fn module_export_list_callback(
&mut self,
_info: &ModuleInfo,
_callback: ExportCallback,
) -> Result<()> {
Err(Error(ErrorOrigin::OsLayer, ErrorKind::NotImplemented))
}
fn module_section_list_callback(
&mut self,
_info: &ModuleInfo,
_callback: SectionCallback,
) -> Result<()> {
Err(Error(ErrorOrigin::OsLayer, ErrorKind::NotImplemented))
}
fn info(&self) -> &OsInfo {
&self.info
}
}