/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ extern crate ini; extern crate winapi; use ini::Ini; use libc::time; use serde::Serialize; use serde_json::ser::to_writer; use std::convert::TryInto; use std::ffi::OsString; use std::fs::{read_to_string, File}; use std::io::{BufRead, BufReader, Write}; use std::mem::{size_of, zeroed}; use std::os::windows::ffi::{OsStrExt, OsStringExt}; use std::os::windows::io::AsRawHandle; use std::path::{Path, PathBuf}; use std::ptr::{addr_of_mut, null, null_mut}; use std::slice::from_raw_parts; use uuid::Uuid; use winapi::shared::basetsd::{SIZE_T, ULONG_PTR}; use winapi::shared::minwindef::{ BOOL, BYTE, DWORD, FALSE, FILETIME, LPVOID, MAX_PATH, PBOOL, PDWORD, PULONG, TRUE, ULONG, WORD, }; use winapi::shared::ntdef::{NTSTATUS, STRING, UNICODE_STRING}; use winapi::shared::ntstatus::STATUS_SUCCESS; use winapi::shared::winerror::{E_UNEXPECTED, S_OK}; use winapi::um::combaseapi::CoTaskMemFree; use winapi::um::handleapi::CloseHandle; use winapi::um::knownfolders::FOLDERID_RoamingAppData; use winapi::um::memoryapi::{ReadProcessMemory, WriteProcessMemory}; use winapi::um::minwinbase::LPTHREAD_START_ROUTINE; use winapi::um::processthreadsapi::{ CreateProcessW, CreateRemoteThread, GetProcessId, GetProcessTimes, GetThreadId, OpenProcess, TerminateProcess, PROCESS_INFORMATION, STARTUPINFOW, }; use winapi::um::psapi::K32GetModuleFileNameExW; use winapi::um::shlobj::SHGetKnownFolderPath; use winapi::um::synchapi::WaitForSingleObject; use winapi::um::winbase::{ VerifyVersionInfoW, CREATE_NO_WINDOW, CREATE_UNICODE_ENVIRONMENT, NORMAL_PRIORITY_CLASS, WAIT_OBJECT_0, }; use winapi::um::winnt::{ VerSetConditionMask, CONTEXT, DWORDLONG, EXCEPTION_POINTERS, EXCEPTION_RECORD, HANDLE, HRESULT, LIST_ENTRY, LPOSVERSIONINFOEXW, OSVERSIONINFOEXW, PCWSTR, PEXCEPTION_POINTERS, PROCESS_ALL_ACCESS, PVOID, PWSTR, VER_GREATER_EQUAL, VER_MAJORVERSION, VER_MINORVERSION, VER_SERVICEPACKMAJOR, VER_SERVICEPACKMINOR, }; use winapi::STRUCT; /* The following struct must be kept in sync with the identically named one in * nsExceptionHandler.h. There is one copy of this structure for every child * process and they are all stored within the main process'. WER will use it to * communicate with the main process when a child process is encountered. */ #[repr(C)] struct WindowsErrorReportingData { wer_notify_proc: LPTHREAD_START_ROUTINE, child_pid: DWORD, minidump_name: [u8; 40], oom_allocation_size: usize, } /* The following struct must be kept in sync with the identically named one in * nsExceptionHandler.h. A copy of this is stored in every process and a pointer * to it is passed to the runtime exception module. We will read it to gather * information about the crashed process. */ #[repr(C)] struct InProcessWindowsErrorReportingData { process_type: u32, oom_allocation_size_ptr: *mut usize, } // This value comes from GeckoProcessTypes.h static MAIN_PROCESS_TYPE: u32 = 0; #[no_mangle] pub extern "C" fn OutOfProcessExceptionEventCallback( context: PVOID, exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION, b_ownership_claimed: PBOOL, _wsz_event_name: PWSTR, _pch_size: PDWORD, _dw_signature_count: PDWORD, ) -> HRESULT { let result = out_of_process_exception_event_callback(context, exception_information); match result { Ok(_) => { unsafe { // Inform WER that we claim ownership of this crash *b_ownership_claimed = TRUE; // Make sure that the process shuts down TerminateProcess((*exception_information).hProcess, 1); } S_OK } Err(_) => E_UNEXPECTED, } } #[no_mangle] pub extern "C" fn OutOfProcessExceptionEventSignatureCallback( _context: PVOID, _exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION, _w_index: DWORD, _wsz_name: PWSTR, _ch_name: PDWORD, _wsz_value: PWSTR, _ch_value: PDWORD, ) -> HRESULT { S_OK } #[no_mangle] pub extern "C" fn OutOfProcessExceptionEventDebuggerLaunchCallback( _context: PVOID, _exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION, b_is_custom_debugger: PBOOL, _wsz_debugger_launch: PWSTR, _ch_debugger_launch: PDWORD, _b_is_debugger_autolaunch: PBOOL, ) -> HRESULT { unsafe { *b_is_custom_debugger = FALSE; } S_OK } fn out_of_process_exception_event_callback( context: PVOID, exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION, ) -> Result<(), ()> { let is_fatal = unsafe { (*exception_information).bIsFatal } != FALSE; if !is_fatal { return Ok(()); } let process = unsafe { (*exception_information).hProcess }; let application_info = ApplicationInformation::from_process(process)?; let wer_data = read_from_process::( process, context as *mut InProcessWindowsErrorReportingData, )?; let process = unsafe { (*exception_information).hProcess }; let startup_time = get_startup_time(process)?; let oom_allocation_size = get_oom_allocation_size(process, &wer_data); let crash_report = CrashReport::new(&application_info, startup_time, oom_allocation_size); crash_report.write_minidump(exception_information)?; if wer_data.process_type == MAIN_PROCESS_TYPE { handle_main_process_crash(crash_report, process, &application_info) } else { handle_child_process_crash(crash_report, process) } } fn handle_main_process_crash( crash_report: CrashReport, process: HANDLE, application_information: &ApplicationInformation, ) -> Result<(), ()> { crash_report.write_extra_file()?; crash_report.write_event_file()?; let mut environment = read_environment_block(process)?; launch_crash_reporter_client( &application_information.install_path, &mut environment, &crash_report, ); Ok(()) } fn handle_child_process_crash(crash_report: CrashReport, child_process: HANDLE) -> Result<(), ()> { let command_line = read_command_line(child_process)?; let (parent_pid, data_ptr) = parse_child_data(&command_line)?; let parent_process = get_process_handle(parent_pid)?; let mut wer_data: WindowsErrorReportingData = read_from_process(parent_process, data_ptr)?; wer_data.child_pid = get_process_id(child_process)?; wer_data.minidump_name = crash_report.get_minidump_name(); wer_data.oom_allocation_size = crash_report.oom_allocation_size; let wer_notify_proc = wer_data.wer_notify_proc; write_to_process(parent_process, wer_data, data_ptr)?; notify_main_process(parent_process, wer_notify_proc, data_ptr) } fn read_from_process(process: HANDLE, data_ptr: *mut T) -> Result { let mut data: T = unsafe { zeroed() }; let res = unsafe { ReadProcessMemory( process, data_ptr as *mut _, addr_of_mut!(data) as *mut _, size_of::() as SIZE_T, null_mut(), ) }; bool_ok_or_err(res, data) } fn read_array_from_process( process: HANDLE, data_ptr: LPVOID, count: usize, ) -> Result, ()> { let mut array = vec![Default::default(); count]; let size = size_of::() as SIZE_T; let size = size.checked_mul(count).ok_or(())?; let res = unsafe { ReadProcessMemory( process, data_ptr, array.as_mut_ptr() as *mut _, size, null_mut(), ) }; bool_ok_or_err(res, array) } fn write_to_process(process: HANDLE, mut data: T, data_ptr: *mut T) -> Result<(), ()> { let res = unsafe { WriteProcessMemory( process, data_ptr as LPVOID, addr_of_mut!(data) as *mut _, size_of::() as SIZE_T, null_mut(), ) }; bool_ok_or_err(res, ()) } fn notify_main_process( process: HANDLE, wer_notify_proc: LPTHREAD_START_ROUTINE, data_ptr: *mut WindowsErrorReportingData, ) -> Result<(), ()> { let thread = unsafe { CreateRemoteThread( process, null_mut(), 0, wer_notify_proc, data_ptr as LPVOID, 0, null_mut(), ) }; if thread == null_mut() { return Err(()); } // Don't wait forever as we want the process to get killed eventually let res = unsafe { WaitForSingleObject(thread, 5000) }; if res != WAIT_OBJECT_0 { return Err(()); } let res = unsafe { CloseHandle(thread) }; bool_ok_or_err(res, ()) } fn get_startup_time(process: HANDLE) -> Result { let mut create_time: FILETIME = Default::default(); let mut exit_time: FILETIME = Default::default(); let mut kernel_time: FILETIME = Default::default(); let mut user_time: FILETIME = Default::default(); unsafe { if GetProcessTimes( process, &mut create_time as *mut _, &mut exit_time as *mut _, &mut kernel_time as *mut _, &mut user_time as *mut _, ) == 0 { return Err(()); } } let start_time_in_ticks = ((create_time.dwHighDateTime as u64) << 32) + create_time.dwLowDateTime as u64; let windows_tick: u64 = 10000000; let sec_to_unix_epoch = 11644473600; Ok((start_time_in_ticks / windows_tick) - sec_to_unix_epoch) } fn get_oom_allocation_size( process: HANDLE, wer_data: &InProcessWindowsErrorReportingData, ) -> usize { read_from_process(process, wer_data.oom_allocation_size_ptr).unwrap_or(0) } fn parse_child_data(command_line: &str) -> Result<(DWORD, *mut WindowsErrorReportingData), ()> { let mut itr = command_line.rsplit(' '); let address = itr.nth(1).ok_or(())?; let address = usize::from_str_radix(address, 16).map_err(|_err| (()))?; let address = address as *mut WindowsErrorReportingData; let parent_pid = itr.nth(2).ok_or(())?; let parent_pid = u32::from_str_radix(parent_pid, 10).map_err(|_err| (()))?; Ok((parent_pid, address)) } fn get_process_id(process: HANDLE) -> Result { match unsafe { GetProcessId(process) } { 0 => Err(()), pid => Ok(pid), } } fn get_process_handle(pid: DWORD) -> Result { let handle = unsafe { OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid) }; if handle != null_mut() { Ok(handle) } else { Err(()) } } fn launch_crash_reporter_client( install_path: &Path, environment: &mut Vec, crash_report: &CrashReport, ) { // Prepare the command line let client_path = install_path.join("crashreporter.exe"); let mut cmd_line = OsString::from("\""); cmd_line.push(client_path); cmd_line.push("\" \""); cmd_line.push(crash_report.get_minidump_path()); cmd_line.push("\"\0"); let mut cmd_line: Vec = cmd_line.encode_wide().collect(); let mut pi: PROCESS_INFORMATION = Default::default(); let mut si = STARTUPINFOW { cb: size_of::().try_into().unwrap(), ..Default::default() }; unsafe { if CreateProcessW( null_mut(), cmd_line.as_mut_ptr(), null_mut(), null_mut(), FALSE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, environment.as_mut_ptr() as *mut _, null_mut(), &mut si, &mut pi, ) != 0 { CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } } } #[derive(Debug)] struct ApplicationData { vendor: Option, name: String, version: String, build_id: String, product_id: String, server_url: String, } impl ApplicationData { fn load_from_disk(install_path: &Path) -> Result { let ini_path = ApplicationData::get_path(install_path); let conf = Ini::load_from_file(ini_path).map_err(|_e| ())?; // Parse the "App" section let app_section = conf.section(Some("App")).ok_or(())?; let vendor = app_section.get("Vendor").map(|s| s.to_owned()); let name = app_section.get("Name").ok_or(())?.to_owned(); let version = app_section.get("Version").ok_or(())?.to_owned(); let build_id = app_section.get("BuildID").ok_or(())?.to_owned(); let product_id = app_section.get("ID").ok_or(())?.to_owned(); // Parse the "Crash Reporter" section let crash_reporter_section = conf.section(Some("Crash Reporter")).ok_or(())?; let server_url = crash_reporter_section .get("ServerURL") .ok_or(())? .to_owned(); // InstallTime Ok(ApplicationData { vendor, name, version, build_id, product_id, server_url, }) } fn get_path(install_path: &Path) -> PathBuf { install_path.join("application.ini") } } #[derive(Serialize)] #[allow(non_snake_case)] struct Annotations { BuildID: String, CrashTime: String, InstallTime: String, #[serde(skip_serializing_if = "Option::is_none")] OOMAllocationSize: Option, ProductID: String, ProductName: String, ReleaseChannel: String, ServerURL: String, StartupTime: String, UptimeTS: String, #[serde(skip_serializing_if = "Option::is_none")] Vendor: Option, Version: String, WindowsErrorReporting: String, } impl Annotations { fn from_application_data( application_data: &ApplicationData, release_channel: String, install_time: String, crash_time: u64, startup_time: u64, oom_allocation_size: usize, ) -> Annotations { let oom_allocation_size = if oom_allocation_size != 0 { Some(oom_allocation_size.to_string()) } else { None }; Annotations { BuildID: application_data.build_id.clone(), CrashTime: crash_time.to_string(), InstallTime: install_time, OOMAllocationSize: oom_allocation_size, ProductID: application_data.product_id.clone(), ProductName: application_data.name.clone(), ReleaseChannel: release_channel, ServerURL: application_data.server_url.clone(), StartupTime: startup_time.to_string(), UptimeTS: (crash_time - startup_time).to_string() + ".0", Vendor: application_data.vendor.clone(), Version: application_data.version.clone(), WindowsErrorReporting: "1".to_string(), } } } /// Encapsulates the information about the application that crashed. This includes the install path as well as version information struct ApplicationInformation { install_path: PathBuf, application_data: ApplicationData, release_channel: String, crash_reports_dir: PathBuf, install_time: String, } impl ApplicationInformation { fn from_process(process: HANDLE) -> Result { let mut install_path = ApplicationInformation::get_application_path(process)?; install_path.pop(); let application_data = ApplicationData::load_from_disk(install_path.as_ref())?; let release_channel = ApplicationInformation::get_release_channel(install_path.as_ref())?; let crash_reports_dir = ApplicationInformation::get_crash_reports_dir(&application_data)?; let install_time = ApplicationInformation::get_install_time( &crash_reports_dir, &application_data.build_id, )?; Ok(ApplicationInformation { install_path, application_data, release_channel, crash_reports_dir, install_time, }) } fn get_application_path(process: HANDLE) -> Result { let mut path: [u16; MAX_PATH + 1] = [0; MAX_PATH + 1]; unsafe { let res = K32GetModuleFileNameExW( process, null_mut(), (&mut path).as_mut_ptr(), (MAX_PATH + 1) as DWORD, ); if res == 0 { return Err(()); } let application_path = PathBuf::from(OsString::from_wide(&path[0..res as usize])); Ok(application_path) } } fn get_release_channel(install_path: &Path) -> Result { let channel_prefs = File::open(install_path.join("defaults/pref/channel-prefs.js")).map_err(|_e| ())?; let lines = BufReader::new(channel_prefs).lines(); let line = lines .filter_map(Result::ok) .find(|line| line.contains("app.update.channel")) .ok_or(())?; line.split("\"").nth(3).map(|s| s.to_string()).ok_or(()) } fn get_crash_reports_dir(application_data: &ApplicationData) -> Result { let mut psz_path: PWSTR = null_mut(); unsafe { let res = SHGetKnownFolderPath( &FOLDERID_RoamingAppData as *const _, 0, null_mut(), &mut psz_path as *mut _, ); if res == S_OK { let mut len = 0; while psz_path.offset(len).read() != 0 { len += 1; } let str = OsString::from_wide(from_raw_parts(psz_path, len as usize)); CoTaskMemFree(psz_path as _); let mut path = PathBuf::from(str); if let Some(vendor) = &application_data.vendor { path.push(vendor); } path.push(&application_data.name); path.push("Crash Reports"); Ok(path) } else { Err(()) } } } fn get_install_time(crash_reports_path: &Path, build_id: &str) -> Result { let file_name = "InstallTime".to_owned() + build_id; let file_path = crash_reports_path.join(file_name); read_to_string(file_path).map_err(|_e| ()) } } struct CrashReport { uuid: String, crash_reports_path: PathBuf, release_channel: String, annotations: Annotations, crash_time: u64, oom_allocation_size: usize, } impl CrashReport { fn new( application_information: &ApplicationInformation, startup_time: u64, oom_allocation_size: usize, ) -> CrashReport { let uuid = Uuid::new_v4() .as_hyphenated() .encode_lower(&mut Uuid::encode_buffer()) .to_owned(); let crash_reports_path = application_information.crash_reports_dir.clone(); let crash_time: u64 = unsafe { time(null_mut()) as u64 }; let annotations = Annotations::from_application_data( &application_information.application_data, application_information.release_channel.clone(), application_information.install_time.clone(), crash_time, startup_time, oom_allocation_size, ); CrashReport { uuid, crash_reports_path, release_channel: application_information.release_channel.clone(), annotations, crash_time, oom_allocation_size, } } fn is_nightly(&self) -> bool { self.release_channel == "nightly" || self.release_channel == "default" } fn get_minidump_type(&self) -> MINIDUMP_TYPE { let mut minidump_type = MiniDumpWithFullMemoryInfo | MiniDumpWithUnloadedModules; if self.is_nightly() { // This is Nightly only because this doubles the size of minidumps based // on the experimental data. minidump_type = minidump_type | MiniDumpWithProcessThreadData; // dbghelp.dll on Win7 can't handle overlapping memory regions so we only // enable this feature on Win8 or later. if is_windows8_or_later() { // This allows us to examine heap objects referenced from stack objects // at the cost of further doubling the size of minidumps. minidump_type = minidump_type | MiniDumpWithIndirectlyReferencedMemory } } minidump_type } fn get_pending_path(&self) -> PathBuf { self.crash_reports_path.join("pending") } fn get_events_path(&self) -> PathBuf { self.crash_reports_path.join("events") } fn get_minidump_path(&self) -> PathBuf { self.get_pending_path().join(self.uuid.to_string() + ".dmp") } fn get_minidump_name(&self) -> [u8; 40] { let bytes = (self.uuid.to_string() + ".dmp").into_bytes(); bytes[0..40].try_into().unwrap() } fn get_extra_file_path(&self) -> PathBuf { self.get_pending_path() .join(self.uuid.to_string() + ".extra") } fn get_event_file_path(&self) -> PathBuf { self.get_events_path().join(self.uuid.to_string()) } fn write_minidump( &self, exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION, ) -> Result<(), ()> { let minidump_path = self.get_minidump_path(); let minidump_file = File::create(minidump_path).map_err(|_e| ())?; let minidump_type: MINIDUMP_TYPE = self.get_minidump_type(); unsafe { let mut exception_pointers = EXCEPTION_POINTERS { ExceptionRecord: &mut ((*exception_information).exceptionRecord), ContextRecord: &mut ((*exception_information).context), }; let mut exception = MINIDUMP_EXCEPTION_INFORMATION { ThreadId: GetThreadId((*exception_information).hThread), ExceptionPointers: &mut exception_pointers, ClientPointers: FALSE, }; let res = MiniDumpWriteDump( (*exception_information).hProcess, get_process_id((*exception_information).hProcess)?, minidump_file.as_raw_handle() as _, minidump_type, &mut exception, /* userStream */ null(), /* callback */ null(), ); bool_ok_or_err(res, ()) } } fn write_extra_file(&self) -> Result<(), ()> { let extra_file = File::create(self.get_extra_file_path()).map_err(|_e| ())?; to_writer(extra_file, &self.annotations).map_err(|_e| ()) } fn write_event_file(&self) -> Result<(), ()> { let mut event_file = File::create(self.get_event_file_path()).map_err(|_e| ())?; writeln!(event_file, "crash.main.3").map_err(|_e| ())?; writeln!(event_file, "{}", self.crash_time).map_err(|_e| ())?; writeln!(event_file, "{}", self.uuid).map_err(|_e| ())?; to_writer(event_file, &self.annotations).map_err(|_e| ()) } } fn is_windows8_or_later() -> bool { let mut info = OSVERSIONINFOEXW { dwOSVersionInfoSize: size_of::().try_into().unwrap(), dwMajorVersion: 6, dwMinorVersion: 2, ..Default::default() }; unsafe { let mut mask: DWORDLONG = 0; mask = VerSetConditionMask(mask, VER_MAJORVERSION, VER_GREATER_EQUAL); mask = VerSetConditionMask(mask, VER_MINORVERSION, VER_GREATER_EQUAL); mask = VerSetConditionMask(mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); mask = VerSetConditionMask(mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL); let res = VerifyVersionInfoW( &mut info as LPOSVERSIONINFOEXW, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, mask, ); res != FALSE } } fn bool_ok_or_err(res: BOOL, data: T) -> Result { match res { FALSE => Err(()), _ => Ok(data), } } fn read_environment_block(process: HANDLE) -> Result, ()> { let upp = read_user_process_parameters(process)?; // Read the environment let buffer = upp.Environment; let length = upp.EnvironmentSize; let count = length as usize / 2; read_array_from_process::(process, buffer, count) } fn read_command_line(process: HANDLE) -> Result { let upp = read_user_process_parameters(process)?; // Read the command-line let buffer = upp.CommandLine.Buffer; let length = upp.CommandLine.Length; let count = (length as usize) / 2; let command_line = read_array_from_process::(process, buffer as *mut _, count)?; String::from_utf16(&command_line).map_err(|_err| ()) } fn read_user_process_parameters(process: HANDLE) -> Result { let mut pbi: PROCESS_BASIC_INFORMATION = unsafe { zeroed() }; let mut length: ULONG = 0; let result = unsafe { NtQueryInformationProcess( process, ProcessBasicInformation, &mut pbi as *mut _ as _, size_of::().try_into().unwrap(), &mut length, ) }; if result != STATUS_SUCCESS { return Err(()); } // Read the process environment block let peb: PEB = read_from_process(process, pbi.PebBaseAddress)?; // Read the user process parameters read_from_process::(process, peb.ProcessParameters) } /****************************************************************************** * The stuff below should be migrated to the winapi crate, see bug 1696414 * ******************************************************************************/ // we can't use winapi's ENUM macro directly because it doesn't support // attributes, so let's define this one here until we migrate this code macro_rules! ENUM { {enum $name:ident { $($variant:ident = $value:expr,)+ }} => { #[allow(non_camel_case_types)] pub type $name = u32; $(#[allow(non_upper_case_globals)] pub const $variant: $name = $value;)+ }; } // winapi doesn't export the FN macro, so we duplicate it here macro_rules! FN { (stdcall $func:ident($($t:ty,)*) -> $ret:ty) => ( #[allow(non_camel_case_types)] pub type $func = Option $ret>; ); (stdcall $func:ident($($p:ident: $t:ty,)*) -> $ret:ty) => ( #[allow(non_camel_case_types)] pub type $func = Option $ret>; ); } // From um/WerApi.h STRUCT! {#[allow(non_snake_case)] struct WER_RUNTIME_EXCEPTION_INFORMATION { dwSize: DWORD, hProcess: HANDLE, hThread: HANDLE, exceptionRecord: EXCEPTION_RECORD, context: CONTEXT, pwszReportId: PCWSTR, bIsFatal: BOOL, dwReserved: DWORD, }} #[allow(non_camel_case_types)] pub type PWER_RUNTIME_EXCEPTION_INFORMATION = *mut WER_RUNTIME_EXCEPTION_INFORMATION; // From minidumpapiset.hProcess STRUCT! {#[allow(non_snake_case)] #[repr(packed(4))] struct MINIDUMP_EXCEPTION_INFORMATION { ThreadId: DWORD, ExceptionPointers: PEXCEPTION_POINTERS, ClientPointers: BOOL, }} #[allow(non_camel_case_types)] pub type PMINIDUMP_EXCEPTION_INFORMATION = *mut MINIDUMP_EXCEPTION_INFORMATION; ENUM! { enum MINIDUMP_TYPE { MiniDumpNormal = 0x00000000, MiniDumpWithDataSegs = 0x00000001, MiniDumpWithFullMemory = 0x00000002, MiniDumpWithHandleData = 0x00000004, MiniDumpFilterMemory = 0x00000008, MiniDumpScanMemory = 0x00000010, MiniDumpWithUnloadedModules = 0x00000020, MiniDumpWithIndirectlyReferencedMemory = 0x00000040, MiniDumpFilterModulePaths = 0x00000080, MiniDumpWithProcessThreadData = 0x00000100, MiniDumpWithPrivateReadWriteMemory = 0x00000200, MiniDumpWithoutOptionalData = 0x00000400, MiniDumpWithFullMemoryInfo = 0x00000800, MiniDumpWithThreadInfo = 0x00001000, MiniDumpWithCodeSegs = 0x00002000, MiniDumpWithoutAuxiliaryState = 0x00004000, MiniDumpWithFullAuxiliaryState = 0x00008000, MiniDumpWithPrivateWriteCopyMemory = 0x00010000, MiniDumpIgnoreInaccessibleMemory = 0x00020000, MiniDumpWithTokenInformation = 0x00040000, MiniDumpWithModuleHeaders = 0x00080000, MiniDumpFilterTriage = 0x00100000, MiniDumpWithAvxXStateContext = 0x00200000, MiniDumpWithIptTrace = 0x00400000, MiniDumpScanInaccessiblePartialPages = 0x00800000, MiniDumpValidTypeFlags = 0x00ffffff, }} // We don't actually need the following three structs so we use placeholders STRUCT! {#[allow(non_snake_case)] struct MINIDUMP_CALLBACK_INPUT { dummy: u32, }} #[allow(non_camel_case_types)] pub type PMINIDUMP_CALLBACK_INPUT = *const MINIDUMP_CALLBACK_INPUT; STRUCT! {#[allow(non_snake_case)] struct MINIDUMP_USER_STREAM_INFORMATION { dummy: u32, }} #[allow(non_camel_case_types)] pub type PMINIDUMP_USER_STREAM_INFORMATION = *const MINIDUMP_USER_STREAM_INFORMATION; STRUCT! {#[allow(non_snake_case)] struct MINIDUMP_CALLBACK_OUTPUT { dummy: u32, }} #[allow(non_camel_case_types)] pub type PMINIDUMP_CALLBACK_OUTPUT = *const MINIDUMP_CALLBACK_OUTPUT; // MiniDumpWriteDump() function and structs FN! {stdcall MINIDUMP_CALLBACK_ROUTINE( CallbackParam: PVOID, CallbackInput: PMINIDUMP_CALLBACK_INPUT, CallbackOutput: PMINIDUMP_CALLBACK_OUTPUT, ) -> BOOL} STRUCT! {#[allow(non_snake_case)] #[repr(packed(4))] struct MINIDUMP_CALLBACK_INFORMATION { CallbackRoutine: MINIDUMP_CALLBACK_ROUTINE, CallbackParam: PVOID, }} #[allow(non_camel_case_types)] pub type PMINIDUMP_CALLBACK_INFORMATION = *const MINIDUMP_CALLBACK_INFORMATION; extern "system" { pub fn MiniDumpWriteDump( hProcess: HANDLE, ProcessId: DWORD, hFile: HANDLE, DumpType: MINIDUMP_TYPE, Exceptionparam: PMINIDUMP_EXCEPTION_INFORMATION, UserStreamParam: PMINIDUMP_USER_STREAM_INFORMATION, CallbackParam: PMINIDUMP_CALLBACK_INFORMATION, ) -> BOOL; } // From um/winternl.h STRUCT! {#[allow(non_snake_case)] struct PEB_LDR_DATA { Reserved1: [BYTE; 8], Reserved2: [PVOID; 3], InMemoryOrderModuleList: LIST_ENTRY, }} #[allow(non_camel_case_types)] pub type PPEB_LDR_DATA = *mut PEB_LDR_DATA; STRUCT! {#[allow(non_snake_case)] struct RTL_DRIVE_LETTER_CURDIR { Flags: WORD, Length: WORD, TimeStamp: ULONG, DosPath: STRING, }} STRUCT! {#[allow(non_snake_case)] struct RTL_USER_PROCESS_PARAMETERS { Reserved1: [BYTE; 16], Reserved2: [PVOID; 10], ImagePathName: UNICODE_STRING, CommandLine: UNICODE_STRING, // Everything below this point is undocumented Environment: PVOID, StartingX: ULONG, StartingY: ULONG, CountX: ULONG, CountY: ULONG, CountCharsX: ULONG, CountCharsY: ULONG, FillAttribute: ULONG, WindowFlags: ULONG, ShowWindowFlags: ULONG, WindowTitle: UNICODE_STRING, DesktopInfo: UNICODE_STRING, ShellInfo: UNICODE_STRING, RuntimeData: UNICODE_STRING, CurrentDirectores: [RTL_DRIVE_LETTER_CURDIR; 32], EnvironmentSize: ULONG, }} #[allow(non_camel_case_types)] pub type PRTL_USER_PROCESS_PARAMETERS = *mut RTL_USER_PROCESS_PARAMETERS; FN! {stdcall PPS_POST_PROCESS_INIT_ROUTINE() -> ()} STRUCT! {#[allow(non_snake_case)] struct PEB { Reserved1: [BYTE; 2], BeingDebugged: BYTE, Reserved2: [BYTE; 1], Reserved3: [PVOID; 2], Ldr: PPEB_LDR_DATA, ProcessParameters: PRTL_USER_PROCESS_PARAMETERS, Reserved4: [PVOID; 3], AtlThunkSListPtr: PVOID, Reserved5: PVOID, Reserved6: ULONG, Reserved7: PVOID, Reserved8: ULONG, AtlThunkSListPtr32: ULONG, Reserved9: [PVOID; 45], Reserved10: [BYTE; 96], PostProcessInitRoutine: PPS_POST_PROCESS_INIT_ROUTINE, Reserved11: [BYTE; 128], Reserved12: [PVOID; 1], SessionId: ULONG, }} #[allow(non_camel_case_types)] pub type PPEB = *mut PEB; STRUCT! {#[allow(non_snake_case)] struct PROCESS_BASIC_INFORMATION { Reserved1: PVOID, PebBaseAddress: PPEB, Reserved2: [PVOID; 2], UniqueProcessId: ULONG_PTR, Reserved3: PVOID, }} ENUM! {enum PROCESSINFOCLASS { ProcessBasicInformation = 0, ProcessDebugPort = 7, ProcessWow64Information = 26, ProcessImageFileName = 27, ProcessBreakOnTermination = 29, }} extern "system" { pub fn NtQueryInformationProcess( ProcessHandle: HANDLE, ProcessInformationClass: PROCESSINFOCLASS, ProcessInformation: PVOID, ProcessInformationLength: ULONG, ReturnLength: PULONG, ) -> NTSTATUS; }