use firehazard::*;
use winapi::ctypes::c_void;
use winapi::shared::minwindef::FALSE;
use winapi::um::handleapi::DuplicateHandle;
use winapi::um::minwinbase::*;
use winapi::um::winnt::*;
use std::collections::*;
use std::mem::MaybeUninit;
use std::path::PathBuf;
pub fn debug_loop(
tokens: &crate::tokens::Tokens,
pi: &process::Information,
) {
let mut sandboxed = false;
let mut threads = HashMap::<thread::Id, thread::OwnedHandle>::new();
let mut dlls = HashMap::<*mut c_void, PathBuf>::new();
loop {
let event = wait_for_debug_event_ex(None).unwrap();
let DEBUG_EVENT { dwProcessId, dwThreadId, .. } = *event;
let dbg_continue = move || continue_debug_event(dwProcessId, dwThreadId, DBG_CONTINUE).unwrap();
let dbg_exception_not_handled = move || continue_debug_event(dwProcessId, dwThreadId, DBG_EXCEPTION_NOT_HANDLED).unwrap();
use debug::DebugEventU::*;
match event.u() {
Exception(event) => {
let code = event.ExceptionRecord.ExceptionCode;
let ty = match code {
EXCEPTION_ACCESS_VIOLATION => "EXCEPTION_ACCESS_VIOLATION",
EXCEPTION_BREAKPOINT => "EXCEPTION_BREAKPOINT",
EXCEPTION_DATATYPE_MISALIGNMENT => "EXCEPTION_DATATYPE_MISALIGNMENT",
EXCEPTION_SINGLE_STEP => "EXCEPTION_SINGLE_STEP",
DBG_CONTROL_C => "DBG_CONTROL_C",
0xE06D7363 => "Microsoft C++ Exception",
_ => "???",
};
println!("[{dwProcessId}:{dwThreadId}] exception: {ty} ({code})");
dbg_exception_not_handled();
},
CreateThread(event) => {
println!("[{dwProcessId}:{dwThreadId}] thread created");
let mut thread = event.hThread;
let process = get_current_process().as_handle();
assert!(FALSE != unsafe { DuplicateHandle(process, thread, process, &mut thread, access::GENERIC_ALL.into(), false as _, 0) });
let thread = unsafe { thread::OwnedHandle::from_raw(thread) }.unwrap();
set_thread_token(&thread, &tokens.permissive).unwrap();
let _prev_thread = threads.insert(dwThreadId, thread);
debug_assert!(_prev_thread.is_none());
dbg_continue();
},
CreateProcess(event) => {
println!("[{dwProcessId}:{dwThreadId}] process created");
let mut thread = event.hThread;
let process = get_current_process().as_handle();
assert!(FALSE != unsafe { DuplicateHandle(process, thread, process, &mut thread, access::GENERIC_ALL.into(), false as _, 0) });
let thread = unsafe { thread::OwnedHandle::from_raw(thread) }.unwrap();
set_thread_token(&thread, &tokens.permissive).unwrap(); let _prev_thread = threads.insert(dwThreadId, thread);
debug_assert!(_prev_thread.is_none());
dbg_continue();
},
ExitThread(event) => {
println!("[{dwProcessId}:{dwThreadId}] thread exited with code: {:?}", Error::from(event.dwExitCode));
let _thread = threads.remove(&dwThreadId);
debug_assert!(_thread.is_some());
dbg_continue();
},
ExitProcess(event) => {
println!("[{dwProcessId}:{dwThreadId}] process exited with code: {:?}", Error::from(event.dwExitCode));
let _thread = threads.remove(&dwThreadId);
debug_assert!(_thread.is_some());
dbg_continue();
break;
},
LoadDll(event) => {
let hfile = unsafe { io::FileHandle::from_raw(event.hFile) }.unwrap();
let image_name = get_final_path_name_by_handle(hfile, 0).unwrap();
println!("[{dwProcessId}:{dwThreadId}] dll loaded: {image_name:?}");
let _prev_name = dlls.insert(event.lpBaseOfDll, image_name);
dbg_continue();
},
UnloadDll(event) => {
let image_name = dlls.remove(&event.lpBaseOfDll).unwrap_or_default();
println!("[{dwProcessId}:{dwThreadId}] dll unloaded: {image_name:?}");
dbg_continue();
},
DebugString(event) => {
let bytes = usize::from(event.nDebugStringLength);
let narrow = if event.fUnicode != 0 {
let mut buffer = vec![MaybeUninit::<u16>::uninit(); (bytes+1)/2];
let buffer = unsafe { read_process_memory(&pi.process, event.lpDebugStringData.cast(), &mut buffer[..]) }.unwrap();
let nul = buffer.iter().position(|ch| *ch == 0).unwrap_or(buffer.len());
String::from_utf16_lossy(buffer.split_at(nul).0)
} else {
let mut buffer = vec![MaybeUninit::<u8>::uninit(); bytes];
let buffer = unsafe { read_process_memory(&pi.process, event.lpDebugStringData.cast(), &mut buffer[..]) }.unwrap();
let nul = buffer.iter().position(|ch| *ch == 0).unwrap_or(buffer.len());
String::from_utf8_lossy(buffer.split_at(nul).0).into_owned()
};
println!("[{dwProcessId}:{dwThreadId}] debug string: {:?}", narrow);
if narrow == "sandbox" {
for thread in threads.values() { suspend_thread(thread).unwrap(); }
debug_active_process_stop(pi.process_id).unwrap();
for thread in threads.values() { set_thread_token(thread, None).unwrap(); }
for thread in threads.values() { resume_thread(thread).unwrap(); }
threads.clear();
sandboxed = true;
println!("[{dwProcessId}:{dwThreadId}] sandboxed");
break;
} else {
dbg_continue();
}
},
Rip(_event) => {
println!("[{dwProcessId}:{dwThreadId}] rip event: {{ dwError: {}, dwType: {} }}", _event.dwError, _event.dwType);
dbg_continue();
},
_ => {},
}
}
assert!(sandboxed, "process was never sandboxed");
}