Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/toolkit/crashreporter/mozwer-rust/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 29 kB image not shown  

Quelle  lib.rs   Sprache: unbekannt

 
/* 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/. */

use ini::Ini;
use libc::time;
use process_reader::ProcessReader;
use serde::Serialize;
use serde_json::ser::to_writer;
use std::convert::TryInto;
use std::ffi::{c_void, OsString};
use std::fs::{read_to_string, DirBuilder, File, OpenOptions};
use std::io::{BufRead, BufReader, Write};
use std::mem::{size_of, transmute, zeroed};
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle, RawHandle};
use std::path::{Path, PathBuf};
use std::ptr::{addr_of, null, null_mut};
use std::slice::from_raw_parts;
use uuid::Uuid;
use windows_sys::core::{HRESULT, PWSTR};
use windows_sys::Wdk::System::Threading::{NtQueryInformationProcess, ProcessBasicInformation};
use windows_sys::Win32::{
    Foundation::{
        CloseHandle, GetLastError, SetLastError, BOOL, ERROR_INSUFFICIENT_BUFFER, ERROR_SUCCESS,
        EXCEPTION_BREAKPOINT, E_UNEXPECTED, FALSE, FILETIME, HANDLE, HWND, LPARAM, MAX_PATH,
        STATUS_SUCCESS, S_OK, TRUE, WAIT_OBJECT_0,
    },
    Security::{
        GetSidSubAuthority, GetSidSubAuthorityCount, GetTokenInformation, IsTokenRestricted,
        TokenIntegrityLevel, TOKEN_MANDATORY_LABEL, TOKEN_QUERY,
    },
    System::Com::CoTaskMemFree,
    System::Diagnostics::Debug::{
        GetThreadContext, MiniDumpWithFullMemoryInfo, MiniDumpWithIndirectlyReferencedMemory,
        MiniDumpWithProcessThreadData, MiniDumpWithUnloadedModules, MiniDumpWriteDump,
        WriteProcessMemory, EXCEPTION_POINTERS, MINIDUMP_EXCEPTION_INFORMATION, MINIDUMP_TYPE,
    },
    System::ErrorReporting::WER_RUNTIME_EXCEPTION_INFORMATION,
    System::Memory::{
        VirtualAllocEx, VirtualFreeEx, MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_READWRITE,
    },
    System::ProcessStatus::K32GetModuleFileNameExW,
    System::SystemInformation::{
        VerSetConditionMask, VerifyVersionInfoW, OSVERSIONINFOEXW, VER_MAJORVERSION,
        VER_MINORVERSION, VER_SERVICEPACKMAJOR, VER_SERVICEPACKMINOR,
    },
    System::SystemServices::{SECURITY_MANDATORY_MEDIUM_RID, VER_GREATER_EQUAL},
    System::Threading::{
        CreateProcessW, CreateRemoteThread, GetProcessId, GetProcessTimes, GetThreadId,
        OpenProcess, OpenProcessToken, OpenThread, TerminateProcess, WaitForSingleObject,
        CREATE_NO_WINDOW, CREATE_UNICODE_ENVIRONMENT, LPTHREAD_START_ROUTINE,
        NORMAL_PRIORITY_CLASS, PROCESS_ALL_ACCESS, PROCESS_BASIC_INFORMATION, PROCESS_INFORMATION,
        STARTUPINFOW, THREAD_GET_CONTEXT,
    },
    UI::Shell::{FOLDERID_RoamingAppData, SHGetKnownFolderPath},
    UI::WindowsAndMessaging::{EnumWindows, GetWindowThreadProcessId, IsHungAppWindow},
};

type DWORD = u32;
type ULONG = u32;
type DWORDLONG = u64;
type LPVOID = *mut c_void;
type PVOID = LPVOID;
type PBOOL = *mut BOOL;
type PDWORD = *mut DWORD;
#[allow(non_camel_case_types)]
type PWER_RUNTIME_EXCEPTION_INFORMATION = *mut WER_RUNTIME_EXCEPTION_INFORMATION;

/* The following struct must be kept in sync with the identically named one in
 * nsExceptionHandler.h. WER will use it to communicate with the main process
 * when a child process is encountered. */
#[repr(C)]
struct WindowsErrorReportingData {
    child_pid: DWORD,
    minidump_name: [u8; 40],
}

// 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
}

type Result<T> = std::result::Result<T, ()>;

fn out_of_process_exception_event_callback(
    context: PVOID,
    exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION,
) -> Result<()> {
    let exception_information = unsafe { &mut *exception_information };
    let is_fatal = exception_information.bIsFatal.to_bool();
    let mut is_ui_hang = false;
    if !is_fatal {
        'hang: {
            // Check whether this error is a hang. A hang always results in an EXCEPTION_BREAKPOINT.
            // Hangs may have an hThread/context that is unrelated to the hanging thread, so we get
            // it by searching for process windows that are hung.
            if exception_information.exceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT {
                if let Ok(thread_id) = find_hung_window_thread(exception_information.hProcess) {
                    // In the case of a hang, change the crashing thread to be the one that created
                    // the hung window.
                    //
                    // This is all best-effort, so don't return errors (just fall through to the
                    // Ok return).
                    let thread_handle = unsafe { OpenThread(THREAD_GET_CONTEXT, FALSE, thread_id) };
                    if thread_handle != 0
                        && unsafe {
                            GetThreadContext(thread_handle, &mut exception_information.context)
                        }
                        .to_bool()
                    {
                        exception_information.hThread = thread_handle;
                        break 'hang;
                    }
                }
            }

            // A non-fatal but non-hang exception should not do anything else.
            return Ok(());
        }
        is_ui_hang = true;
    }

    let process = exception_information.hProcess;
    let application_info = ApplicationInformation::from_process(process)?;
    let process_type: u32 = (context as usize).try_into().map_err(|_| ())?;
    let startup_time = get_startup_time(process)?;
    let crash_report = CrashReport::new(&application_info, startup_time, is_ui_hang);
    crash_report.write_minidump(exception_information)?;
    if process_type == MAIN_PROCESS_TYPE {
        match is_sandboxed_process(process) {
            Ok(false) => handle_main_process_crash(crash_report, &application_info),
            _ => {
                // The parent process should never be sandboxed, bail out so the
                // process which is impersonating it gets killed right away. Also
                // bail out if is_sandboxed_process() failed while checking.
                Ok(())
            }
        }
    } else {
        handle_child_process_crash(crash_report, process)
    }
}

/// Find whether the given process has a hung window, and return the thread id related to the
/// window.
fn find_hung_window_thread(process: HANDLE) -> Result<DWORD> {
    let process_id = get_process_id(process)?;

    struct WindowSearch {
        process_id: DWORD,
        ui_thread_id: Option<DWORD>,
    }

    let mut search = WindowSearch {
        process_id,
        ui_thread_id: None,
    };

    unsafe extern "system" fn enum_window_callback(wnd: HWND, data: LPARAM) -> BOOL {
        let data = &mut *(data as *mut WindowSearch);
        let mut window_proc_id = DWORD::default();
        let thread_id = GetWindowThreadProcessId(wnd, &mut window_proc_id);
        if thread_id != 0 && window_proc_id == data.process_id && IsHungAppWindow(wnd).to_bool() {
            data.ui_thread_id = Some(thread_id);
            FALSE
        } else {
            TRUE
        }
    }

    // Disregard the return value, we are trying for best-effort service (it's okay if ui_thread_id
    // is never set).
    unsafe { EnumWindows(Some(enum_window_callback), &mut search as *mut _ as LPARAM) };

    search.ui_thread_id.ok_or(())
}

fn get_parent_process(process: HANDLE) -> Result<HANDLE> {
    let pbi = get_process_basic_information(process)?;
    get_process_handle(pbi.InheritedFromUniqueProcessId as u32)
}

fn handle_main_process_crash(
    crash_report: CrashReport,
    application_information: &ApplicationInformation,
) -> Result<()> {
    crash_report.write_extra_file()?;
    crash_report.write_event_file()?;

    launch_crash_reporter_client(&application_information.install_path, &crash_report);

    Ok(())
}

fn handle_child_process_crash(crash_report: CrashReport, child_process: HANDLE) -> Result<()> {
    let parent_process = get_parent_process(child_process)?;
    let process_reader = ProcessReader::new(parent_process).map_err(|_e| ())?;
    let libxul_address = process_reader.find_module("xul.dll").map_err(|_e| ())?;
    let wer_notify_proc = process_reader
        .find_section(libxul_address, b"mozwerpt")
        .map_err(|_e| ())?;
    let wer_notify_proc = unsafe { transmute::<_, LPTHREAD_START_ROUTINE>(wer_notify_proc) };

    let wer_data = WindowsErrorReportingData {
        child_pid: get_process_id(child_process)?,
        minidump_name: crash_report.get_minidump_name(),
    };
    let address = copy_object_into_process(parent_process, wer_data)?;
    notify_main_process(parent_process, wer_notify_proc, address)
}

fn copy_object_into_process<T>(process: HANDLE, data: T) -> Result<*mut T> {
    let address = unsafe {
        VirtualAllocEx(
            process,
            null(),
            size_of::<T>(),
            MEM_RESERVE | MEM_COMMIT,
            PAGE_READWRITE,
        )
    };

    if address.is_null() {
        return Err(());
    }

    let res = unsafe {
        WriteProcessMemory(
            process,
            address,
            addr_of!(data) as *const _,
            size_of::<T>(),
            null_mut(),
        )
    };

    if res == 0 {
        unsafe { VirtualFreeEx(process, address as *mut _, 0, MEM_RELEASE) };
        Err(())
    } else {
        Ok(address as *mut T)
    }
}

fn notify_main_process(
    process: HANDLE,
    wer_notify_proc: LPTHREAD_START_ROUTINE,
    address: *mut WindowsErrorReportingData,
) -> Result<()> {
    let thread = unsafe {
        CreateRemoteThread(
            process,
            null_mut(),
            0,
            wer_notify_proc,
            address as LPVOID,
            0,
            null_mut(),
        )
    };

    if thread == 0 {
        unsafe { VirtualFreeEx(process, address as *mut _, 0, MEM_RELEASE) };
        return Err(());
    }

    // From this point on the memory pointed to by address is owned by the
    // thread we've created in the main process, so we don't free it.

    let thread = unsafe { OwnedHandle::from_raw_handle(thread as RawHandle) };

    // Don't wait forever as we want the process to get killed eventually
    let res = unsafe { WaitForSingleObject(thread.as_raw_handle() as HANDLE, 5000) };
    if res != WAIT_OBJECT_0 {
        return Err(());
    }

    Ok(())
}

fn get_startup_time(process: HANDLE) -> Result<u64> {
    const ZERO_FILETIME: FILETIME = FILETIME {
        dwLowDateTime: 0,
        dwHighDateTime: 0,
    };
    let mut create_time: FILETIME = ZERO_FILETIME;
    let mut exit_time: FILETIME = ZERO_FILETIME;
    let mut kernel_time: FILETIME = ZERO_FILETIME;
    let mut user_time: FILETIME = ZERO_FILETIME;
    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_process_id(process: HANDLE) -> Result<DWORD> {
    match unsafe { GetProcessId(process) } {
        0 => Err(()),
        pid => Ok(pid),
    }
}

fn get_process_handle(pid: DWORD) -> Result<HANDLE> {
    let handle = unsafe { OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid) };
    if handle != 0 {
        Ok(handle)
    } else {
        Err(())
    }
}

fn launch_crash_reporter_client(install_path: &Path, 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<u16> = cmd_line.encode_wide().collect();

    let mut pi = unsafe { zeroed::<PROCESS_INFORMATION>() };
    let mut si = STARTUPINFOW {
        cb: size_of::<STARTUPINFOW>().try_into().unwrap(),
        ..unsafe { zeroed() }
    };

    unsafe {
        if CreateProcessW(
            null_mut(),
            cmd_line.as_mut_ptr(),
            null_mut(),
            null_mut(),
            FALSE,
            NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
            null_mut(),
            null_mut(),
            &mut si,
            &mut pi,
        ) != 0
        {
            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);
        }
    }
}

#[derive(Debug)]
struct ApplicationData {
    vendor: Option<String>,
    name: String,
    version: String,
    build_id: String,
    product_id: String,
    server_url: String,
}

impl ApplicationData {
    fn load_from_disk(install_path: &Path) -> Result<ApplicationData> {
        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<build_id>

        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")]
    Hang: Option<String>,
    ProductID: String,
    ProductName: String,
    ReleaseChannel: String,
    ServerURL: String,
    StartupTime: String,
    UptimeTS: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    Vendor: Option<String>,
    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,
        ui_hang: bool,
    ) -> Annotations {
        Annotations {
            BuildID: application_data.build_id.clone(),
            CrashTime: crash_time.to_string(),
            InstallTime: install_time,
            Hang: ui_hang.then(|| "ui".to_string()),
            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<ApplicationInformation> {
        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<PathBuf> {
        let mut path: [u16; MAX_PATH as usize + 1] = [0; MAX_PATH as usize + 1];
        unsafe {
            let res = K32GetModuleFileNameExW(
                process,
                0,
                (&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<String> {
        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(std::result::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<PathBuf> {
        let mut psz_path: PWSTR = null_mut();
        unsafe {
            let res = SHGetKnownFolderPath(
                &FOLDERID_RoamingAppData as *const _,
                0,
                0,
                &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) -> String {
        let file_name = "InstallTime".to_owned() + build_id;
        let file_path = crash_reports_path.join(file_name);

        // If the file isn't present we'll attempt to atomically create it and
        // populate it. This code essentially matches the corresponding code in
        // nsExceptionHandler.cpp SetupExtraData().
        if let Ok(mut file) = OpenOptions::new()
            .create_new(true)
            .write(true)
            .open(&file_path)
        {
            // SAFETY: No risks in calling `time()` with a null pointer.
            let _ = write!(&mut file, "{}", unsafe { time(null_mut()) }.to_string());
        }

        // As a last resort, if we can't read the file we fall back to the
        // current time. This might cause us to overstate the number of users
        // affected by a crash, but given it's very unlikely to hit this particular
        // path it won't be a problem.
        //
        // SAFETY: No risks in calling `time()` with a null pointer.
        read_to_string(&file_path).unwrap_or(unsafe { time(null_mut()) }.to_string())
    }
}

struct CrashReport {
    uuid: String,
    crash_reports_path: PathBuf,
    release_channel: String,
    annotations: Annotations,
    crash_time: u64,
}

impl CrashReport {
    fn new(
        application_information: &ApplicationInformation,
        startup_time: u64,
        ui_hang: bool,
    ) -> 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,
            ui_hang,
        );
        CrashReport {
            uuid,
            crash_reports_path,
            release_channel: application_information.release_channel.clone(),
            annotations,
            crash_time,
        }
    }

    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<()> {
        // Make sure that the target directory is present
        DirBuilder::new()
            .recursive(true)
            .create(self.get_pending_path())
            .map_err(|_e| ())?;

        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,
            };

            MiniDumpWriteDump(
                (*exception_information).hProcess,
                get_process_id((*exception_information).hProcess)?,
                minidump_file.as_raw_handle() as _,
                minidump_type,
                &mut exception,
                /* userStream */ null(),
                /* callback */ null(),
            )
            .success()
        }
    }

    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<()> {
        // Make that the target directory is present
        DirBuilder::new()
            .recursive(true)
            .create(self.get_events_path())
            .map_err(|_e| ())?;

        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::<OSVERSIONINFOEXW>().try_into().unwrap(),
        dwMajorVersion: 6,
        dwMinorVersion: 2,
        ..unsafe { zeroed() }
    };

    unsafe {
        let mut mask: DWORDLONG = 0;
        let ge: u8 = VER_GREATER_EQUAL.try_into().unwrap();
        mask = VerSetConditionMask(mask, VER_MAJORVERSION, ge);
        mask = VerSetConditionMask(mask, VER_MINORVERSION, ge);
        mask = VerSetConditionMask(mask, VER_SERVICEPACKMAJOR, ge);
        mask = VerSetConditionMask(mask, VER_SERVICEPACKMINOR, ge);

        VerifyVersionInfoW(
            &mut info,
            VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
            mask,
        )
        .to_bool()
    }
}

trait WinBool: Sized {
    fn to_bool(self) -> bool;
    fn if_true<T>(self, value: T) -> Result<T> {
        if self.to_bool() {
            Ok(value)
        } else {
            Err(())
        }
    }
    fn success(self) -> Result<()> {
        self.if_true(())
    }
}

impl WinBool for BOOL {
    fn to_bool(self) -> bool {
        match self {
            FALSE => false,
            _ => true,
        }
    }
}

fn get_process_basic_information(process: HANDLE) -> Result<PROCESS_BASIC_INFORMATION> {
    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::<PROCESS_BASIC_INFORMATION>().try_into().unwrap(),
            &mut length,
        )
    };

    if result != STATUS_SUCCESS {
        return Err(());
    }

    Ok(pbi)
}

fn is_sandboxed_process(process: HANDLE) -> Result<bool> {
    let mut token: HANDLE = 0;
    let res = unsafe { OpenProcessToken(process, TOKEN_QUERY, &mut token as *mut _) };

    if res != TRUE {
        return Err(());
    }

    let is_restricted = unsafe { IsTokenRestricted(token) } != FALSE;

    unsafe { SetLastError(ERROR_SUCCESS) };
    let mut buffer_size: DWORD = 0;
    let res = unsafe {
        GetTokenInformation(
            token,
            TokenIntegrityLevel,
            null_mut(),
            0,
            &mut buffer_size as *mut _,
        )
    };

    if (res != FALSE) || (unsafe { GetLastError() } != ERROR_INSUFFICIENT_BUFFER) {
        return Err(());
    }

    let mut buffer: Vec<u8> = vec![Default::default(); buffer_size as usize];
    let res = unsafe {
        GetTokenInformation(
            token,
            TokenIntegrityLevel,
            buffer.as_mut_ptr() as *mut _,
            buffer_size,
            &mut buffer_size as *mut _,
        )
    };

    if res != TRUE {
        return Err(());
    }

    let token_mandatory_label = &unsafe { *(buffer.as_ptr() as *const TOKEN_MANDATORY_LABEL) };
    let sid = token_mandatory_label.Label.Sid;
    // We're not checking for errors in the following two calls because these
    // functions can only fail if provided with an invalid SID and we know the
    // one we obtained from `GetTokenInformation()` is valid.
    let sid_subauthority_count = unsafe { *GetSidSubAuthorityCount(sid) - 1u8 };
    let integrity_level = unsafe { *GetSidSubAuthority(sid, sid_subauthority_count.into()) };

    Ok((integrity_level < SECURITY_MANDATORY_MEDIUM_RID as u32) || is_restricted)
}

[ Dauer der Verarbeitung: 0.3 Sekunden  (vorverarbeitet)  ]