use std::{
cell::OnceCell,
mem,
os::windows::io::{AsRawHandle, OwnedHandle},
thread,
};
use windows::Win32::{
Foundation::HANDLE,
System::{
LibraryLoader::FreeLibraryAndExitThread,
Threading::{
INFINITE, OpenProcess, PROCESS_SYNCHRONIZE, TerminateThread, WaitForSingleObject,
},
},
};
use crate::process::{Pid, module::Module};
pub use dtor::declarative::dtor;
pub fn free_current_module_and_exit_thread(code: u32) -> ! {
unsafe { FreeLibraryAndExitThread(Module::current().0, code) }
}
pub fn wait_and_free_current_module(
pid: Pid,
teardown: impl FnOnce() -> u32,
) -> windows::core::Result<()> {
let process = unsafe { OpenProcess(PROCESS_SYNCHRONIZE, false, *pid) }?;
unsafe { WaitForSingleObject(process, INFINITE) };
free_current_module_and_exit_thread(teardown())
}
pub struct ThreadGuard(OwnedHandle);
impl<T> From<thread::JoinHandle<T>> for ThreadGuard {
fn from(thread: thread::JoinHandle<T>) -> Self {
Self(thread.into())
}
}
impl ThreadGuard {
pub fn dismiss(self) -> OwnedHandle {
let t = mem::ManuallyDrop::new(self);
unsafe { std::ptr::read(&t.0) }
}
}
impl Drop for ThreadGuard {
fn drop(&mut self) {
_ = unsafe { TerminateThread(HANDLE(self.0.as_raw_handle()), 0) };
}
}
#[doc(hidden)]
pub mod manual {
use super::*;
pub unsafe fn spawn_wait_and_free_current_module(
pid: Pid,
teardown: impl FnOnce() -> u32 + Send + 'static,
) -> ThreadGuard {
thread::spawn(move || wait_and_free_current_module(pid, teardown)).into()
}
static mut WAIT_AND_FREE: OnceCell<ThreadGuard> = OnceCell::new();
pub unsafe fn spawn_wait_and_free_current_module_once(
pid: Pid,
teardown: impl FnOnce() -> u32 + Send + 'static,
) {
let teardown = || {
unsafe { &mut *&raw mut WAIT_AND_FREE }
.take()
.map(|w| w.dismiss());
teardown()
};
unsafe { &*&raw const WAIT_AND_FREE }
.get_or_init(move || unsafe { spawn_wait_and_free_current_module(pid, teardown) });
}
pub fn free() {
unsafe { &mut *&raw mut WAIT_AND_FREE }.take();
}
}
#[macro_export]
macro_rules! spawn_wait_and_free_current_module_once {
($pid:expr, $teardown:expr) => {
$crate::inject::dll::dll::dtor! {
#[dtor(anonymous)]
fn free() {
$crate::inject::dll::dll::manual::free();
}
}
unsafe {
$crate::inject::dll::dll::manual::spawn_wait_and_free_current_module_once(
$pid, $teardown,
)
}
};
}
pub use spawn_wait_and_free_current_module_once;