#![allow(non_snake_case)]
use super::definition::AppResult;
use std::ffi::c_void;
use windows::Win32::Foundation::UNICODE_STRING;
use windows::Win32::Security::{
LUID_AND_ATTRIBUTES, TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES, TOKEN_QUERY,
};
use windows::Win32::System::Kernel::STRING;
use windows::Win32::System::SystemServices::SE_DEBUG_NAME;
use windows::Win32::System::Threading::{
ProcessBasicInformation, PEB, PROCESS_BASIC_INFORMATION, PROCESS_INFORMATION,
PROCESS_QUERY_INFORMATION, PROCESS_VM_READ,
};
#[repr(C)]
#[derive(Debug)]
pub struct RTL_DRIVE_LETTER_CURDIR {
Flags: u16,
Length: u16,
TimeStamp: u32,
DosPath: STRING,
}
#[repr(C)]
#[derive(Debug)]
pub struct RTL_USER_PROCESS_PARAMETERS {
MaximumLength: u32,
Length: u32,
Flags: u32,
DebugFlags: u32,
ConsoleHandle: *const c_void,
ConsoleFlags: u32,
StdInputHandle: *const c_void,
StdOutputHandle: *const c_void,
StdErrorHandle: *const c_void,
CurrentDirectoryPath: UNICODE_STRING,
CurrentDirectoryHandle: *const c_void,
DllPath: UNICODE_STRING,
ImagePathName: UNICODE_STRING,
CommandLine: UNICODE_STRING,
Environment: *const c_void,
StartingPositionLeft: u32,
StartingPositionTop: u32,
Width: u32,
Height: u32,
CharWidth: u32,
CharHeight: u32,
ConsoleTextAttributes: u32,
WindowFlags: u32,
ShowWindowFlags: u32,
WindowTitle: UNICODE_STRING,
DesktopName: UNICODE_STRING,
ShellInfo: UNICODE_STRING,
RuntimeData: UNICODE_STRING,
DLCurrentDirectory: [RTL_DRIVE_LETTER_CURDIR; 32],
EnvironmentSize: u32,
}
impl Default for RTL_USER_PROCESS_PARAMETERS {
fn default() -> Self {
unsafe { ::core::mem::zeroed() }
}
}
mod oxidation {
use std::ffi::c_void;
use std::ptr::null_mut;
use std::rc::Rc;
use windows::Win32::Foundation::{CloseHandle, FARPROC, HANDLE, LUID, NTSTATUS};
use windows::Win32::Security::{
AdjustTokenPrivileges, LookupPrivilegeValueA, TOKEN_ACCESS_MASK, TOKEN_PRIVILEGES,
};
use windows::Win32::System::Diagnostics::Debug::ReadProcessMemory;
use windows::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress};
use windows::Win32::System::Threading::{
OpenProcess, PROCESSINFOCLASS, PROCESS_ACCESS_RIGHTS, PROCESS_INFORMATION,
};
#[derive(Debug)]
pub struct MyOwnedHandle {
inner: HANDLE,
}
impl From<HANDLE> for MyOwnedHandle {
fn from(h: HANDLE) -> Self {
Self { inner: h }
}
}
impl Drop for MyOwnedHandle {
fn drop(&mut self) {
let ok = unsafe { CloseHandle(self.inner) };
assert!(ok.as_bool(), "Failed to close handle");
}
}
#[derive(Debug, Clone)]
pub struct SharedHandle {
inner: Rc<MyOwnedHandle>,
}
impl From<HANDLE> for SharedHandle {
fn from(h: HANDLE) -> Self {
Self {
inner: Rc::new(MyOwnedHandle { inner: h }),
}
}
}
impl From<SharedHandle> for HANDLE {
fn from(shared_handle: SharedHandle) -> Self {
shared_handle.inner.inner
}
}
pub fn get_current_process() -> HANDLE {
unsafe { windows::Win32::System::Threading::GetCurrentProcess() }
}
pub fn open_process_token(
process_handle: HANDLE,
desired_access: TOKEN_ACCESS_MASK,
) -> ::windows::core::Result<SharedHandle> {
let mut out_handle: HANDLE = HANDLE::default();
let result = unsafe {
windows::Win32::System::Threading::OpenProcessToken(
process_handle,
desired_access,
&mut out_handle,
)
};
if result.as_bool() {
Ok(out_handle.into())
} else {
Err(windows::core::Error::from_win32())
}
}
pub fn open_process(
desired_access: PROCESS_ACCESS_RIGHTS,
inherit_handle: bool,
process_id: u32,
) -> ::windows::core::Result<SharedHandle> {
let result = unsafe { OpenProcess(desired_access, inherit_handle, process_id) };
result.map(std::convert::Into::into)
}
pub fn lookup_privilege_value(name: &str) -> ::windows::core::Result<LUID> {
let system_name = None;
let mut luid: LUID = LUID::default();
let result = unsafe { LookupPrivilegeValueA(system_name, name, &mut luid) };
if result.as_bool() {
Ok(luid)
} else {
Err(windows::core::Error::from_win32())
}
}
pub fn adjust_token_privileges<T: Into<HANDLE>>(
token_handle: T,
disable_all_privileges: bool,
new_state: TOKEN_PRIVILEGES,
) -> ::windows::core::Result<()> {
let buffer_length: u32 = 0;
let previous_state: *mut TOKEN_PRIVILEGES = null_mut();
let mut return_length: u32 = 0;
let result = unsafe {
AdjustTokenPrivileges(
token_handle.into(),
disable_all_privileges,
&new_state,
buffer_length,
previous_state,
&mut return_length,
)
};
if result.as_bool() {
Ok(())
} else {
Err(windows::core::Error::from_win32())
}
}
#[inline]
unsafe fn NtQueryInformationProcess_dyn(
processhandle: HANDLE,
processinformationclass: PROCESSINFOCLASS,
processinformation: *mut c_void,
processinformationlength: u32,
returnlength: *mut u32,
) -> ::windows::core::Result<()> {
type NtQueryInformationProcess = extern "system" fn(
processhandle: HANDLE,
processinformationclass: PROCESSINFOCLASS,
processinformation: *mut c_void,
processinformationlength: u32,
returnlength: *mut u32,
) -> NTSTATUS;
static mut FARPROC_STATIC: FARPROC = None;
if FARPROC_STATIC.is_none() {
let ntdll_handle = GetModuleHandleA("ntdll.dll")?;
FARPROC_STATIC.replace(
GetProcAddress(ntdll_handle, "NtQueryInformationProcess")
.expect("NtQueryInformationProcess unavailable"),
);
}
let proc: NtQueryInformationProcess = ::core::mem::transmute(FARPROC_STATIC.unwrap());
proc(
processhandle,
processinformationclass,
processinformation,
processinformationlength,
returnlength,
)
.ok()
}
pub fn nt_query_information_process<T: Into<HANDLE>>(
process_handle: T,
process_information_class: PROCESSINFOCLASS,
process_information: *mut PROCESS_INFORMATION,
size: usize,
) -> ::windows::core::Result<()> {
let mut return_length = 0;
#[allow(clippy::cast_possible_truncation)]
unsafe {
NtQueryInformationProcess_dyn(
process_handle.into(),
process_information_class,
process_information.cast::<c_void>(),
size as u32,
&mut return_length,
)
}
}
pub fn read_process_memory<T: Into<HANDLE>>(
process_handle: T,
base_addr: *const c_void,
buffer: *mut c_void,
nsize: usize,
) -> ::windows::core::Result<()> {
let mut number_of_bytes_read = 0;
let result = unsafe {
ReadProcessMemory(
process_handle.into(),
base_addr,
buffer,
nsize,
&mut number_of_bytes_read,
)
};
if result.as_bool() {
assert_eq!(number_of_bytes_read, nsize, "Not read");
Ok(())
} else {
Err(windows::core::Error::from_win32())
}
}
}
pub fn get_environment_string(pid: u32) -> AppResult<Vec<u8>> {
let current_process = oxidation::get_current_process();
let token_handle =
oxidation::open_process_token(current_process, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY)?;
let luid = oxidation::lookup_privilege_value(SE_DEBUG_NAME)?;
let token_privileges = TOKEN_PRIVILEGES {
PrivilegeCount: 1,
Privileges: [LUID_AND_ATTRIBUTES {
Luid: luid,
..LUID_AND_ATTRIBUTES::default()
}; 1],
};
oxidation::adjust_token_privileges(token_handle.clone(), false, token_privileges)?;
let process_handle =
oxidation::open_process(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid)?;
let basic_info = {
let mut out = PROCESS_BASIC_INFORMATION::default();
oxidation::nt_query_information_process(
process_handle.clone(),
ProcessBasicInformation,
std::ptr::addr_of_mut!(out).cast::<PROCESS_INFORMATION>(),
core::mem::size_of::<PROCESS_BASIC_INFORMATION>(),
)?;
out
};
let peb = {
let mut out = PEB::default();
oxidation::read_process_memory(
process_handle.clone(),
basic_info.PebBaseAddress.cast::<c_void>(),
std::ptr::addr_of_mut!(out).cast::<c_void>(),
core::mem::size_of::<PROCESS_BASIC_INFORMATION>(),
)?;
out
};
let user_process_parameters = {
let mut out = RTL_USER_PROCESS_PARAMETERS::default();
oxidation::read_process_memory(
process_handle.clone(),
peb.ProcessParameters.cast::<c_void>(),
std::ptr::addr_of_mut!(out).cast::<c_void>(),
core::mem::size_of::<RTL_USER_PROCESS_PARAMETERS>(),
)?;
out
};
let cap = (user_process_parameters.EnvironmentSize / 2) as usize;
let environment = {
let mut out = vec![0u16; cap];
oxidation::read_process_memory(
process_handle.clone(),
user_process_parameters.Environment,
out.as_mut_ptr().cast::<c_void>(),
user_process_parameters.EnvironmentSize as usize,
)?;
out
};
drop(token_handle);
drop(process_handle);
let result = {
let mut v: Vec<u8> = String::from_utf16(&environment)?.into();
v.truncate(v.len() - 1);
v
};
Ok(result)
}