How the SharpEmu GUI Launches the Emulator with Specific Mitigation Settings
The SharpEmu GUI disables Control-Flow Guard (CFG) and Control-Flow Enforcement Technology (CET) by configuring PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY in the STARTUPINFOEX structure before calling CreateProcessW in EmulatorProcess.StartWindows.
The SharpEmu.GUI project provides an Avalonia-based interface for the SharpEmu emulator, but it doesn't simply execute the CLI binary. Instead, it uses low-level Windows process creation APIs to launch the emulator with explicit security mitigations disabled, ensuring the child process inherits a controlled execution environment while maintaining full stdout/stderr capture capabilities.
Understanding the Mitigation Policies Being Disabled
When launching the emulator process, the GUI explicitly disables two Windows security features that would otherwise interfere with the emulator's execution model.
Control-Flow Guard (CFG) Always-Off
The GUI sets PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF to prevent the system from applying Control-Flow Guard to the child process. This ensures the emulator can freely manipulate execution flow without CFG validation overhead.
CET User Shadow Stacks and IP Validation
The GUI disables Control-Flow Enforcement Technology by combining two flags: PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_ALWAYS_OFF and PROCESS_CREATION_MITIGATION_POLICY2_USER_CET_SET_CONTEXT_IP_VALIDATION_ALWAYS_OFF. This prevents CET-related checks that would otherwise block the emulator's dynamic code generation and context switching mechanisms.
Step-by-Step Process Creation in EmulatorProcess.StartWindows
The launch sequence is implemented in src/SharpEmu.GUI/EmulatorProcess.cs within the StartWindows method. The process follows these eight critical steps:
1. Suppress the CLI's Own Mitigation Relaunch
The GUI first sets an environment variable to prevent the CLI from attempting its own mitigation configuration:
Environment.SetEnvironmentVariable("SHARPEMU_DISABLE_MITIGATION_RELAUNCH", "1");
This ensures the child process inherits only the GUI's explicitly defined policy, avoiding conflicting security settings.
2. Create Anonymous Pipes for Stream Capture
The GUI creates anonymous pipes to capture the emulator's output:
CreatePipe(out var stdoutRead, out var stdoutWrite, ref securityAttributes, 0);
CreatePipe(out var stderrRead, out var stderrWrite, ref securityAttributes, 0);
The write ends of these pipes are attached to the child's standard output and error streams.
3. Prepare the STARTUPINFOEX Structure
The method constructs a STARTUPINFOEX structure, assigning the pipe handles to hStdOutput and hStdError to redirect console output back to the GUI's readers.
4. Allocate the Proc-Thread Attribute List
The code initializes a process thread attribute list to hold the mitigation policy:
nuint attributeListSize = 0;
InitializeProcThreadAttributeList(0, 1, 0, ref attributeListSize);
var attributeList = Marshal.AllocHGlobal((nint)attributeListSize);
InitializeProcThreadAttributeList(attributeList, 1, 0, ref attributeListSize);
5. Compose the Mitigation Policy Payload
Two ulong values are written into native memory to define the disabled policies:
var policy1 = PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF;
var policy2 = PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_ALWAYS_OFF |
PROCESS_CREATION_MITIGATION_POLICY2_USER_CET_SET_CONTEXT_IP_VALIDATION_ALWAYS_OFF;
The first value disables CFG, while the second disables CET user-mode shadow stacks and IP validation.
6. Apply the Policy to the Attribute List
The mitigation policies are attached to the attribute list using UpdateProcThreadAttribute:
UpdateProcThreadAttribute(
attributeList,
0,
PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY,
mitigationPolicies,
(nuint)(sizeof(ulong) * 2),
0,
0);
7. Create the Child Process with Extended Startup Info
The GUI calls CreateProcessW with the EXTENDED_STARTUPINFO_PRESENT flag to apply the custom mitigation policy:
CreateProcessW(
exePath,
new StringBuilder(BuildCommandLine(exePath, arguments)),
0, 0, true,
EXTENDED_STARTUPINFO_PRESENT | CREATE_NO_WINDOW,
0,
currentDirectory,
ref startupInfoEx,
out var processInfo);
The CREATE_NO_WINDOW flag prevents the console window from appearing, while EXTENDED_STARTUPINFO_PRESENT instructs Windows to process the mitigation policy in the attribute list.
8. Tie the Child to a Job Object for Lifetime Management
The GUI creates a Job object using CreateJobObjectW and configures it with SetInformationJobObject using the JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag. This ensures that closing the GUI automatically terminates the emulator process.
Key Implementation Details
The core logic from src/SharpEmu.GUI/EmulatorProcess.cs (lines 31-84) demonstrates the native interop required to configure these policies:
// Disable the CLI's own mitigation relaunch
Environment.SetEnvironmentVariable("SHARPEMU_DISABLE_MITIGATION_RELAUNCH", "1");
// Allocate attribute list
nuint attributeListSize = 0;
InitializeProcThreadAttributeList(0, 1, 0, ref attributeListSize);
var attributeList = Marshal.AllocHGlobal((nint)attributeListSize);
InitializeProcThreadAttributeList(attributeList, 1, 0, ref attributeListSize);
// Build the mitigation policy payload
ulong policy1 = 0x00000002UL << 40; // CFG always-off
ulong policy2 = (0x00000002UL << 28) // CET user shadow stacks off
| (0x00000002UL << 32); // CET IP-validation off
var mitigationPolicies = Marshal.AllocHGlobal(sizeof(ulong) * 2);
Marshal.WriteInt64(mitigationPolicies, unchecked((long)policy1));
Marshal.WriteInt64(nint.Add(mitigationPolicies, sizeof(long)),
unchecked((long)policy2));
// Attach the mitigation policy attribute
UpdateProcThreadAttribute(
attributeList,
0,
PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY,
mitigationPolicies,
(nuint)(sizeof(ulong) * 2),
0,
0);
This approach uses raw ulong values shifted to the correct bit positions corresponding to the Windows mitigation policy constants.
Integration with the Avalonia UI Layer
The launch sequence originates in src/SharpEmu.GUI/MainWindow.axaml.cs, where the Launch method gathers user-selected options (such as log level and strict dynlib settings) and invokes EmulatorProcess.Start. The entry point GuiLauncher.cs bootstraps the Avalonia application and initializes the main window, which ultimately triggers the process creation logic described above.
Summary
- SharpEmu GUI launches the emulator via
EmulatorProcess.StartWindowsinsrc/SharpEmu.GUI/EmulatorProcess.cs - CFG and CET are explicitly disabled using
PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFFand related CET flags - Environment variable
SHARPEMU_DISABLE_MITIGATION_RELAUNCHprevents the CLI from applying its own security policies - Extended startup info (
STARTUPINFOEX) andCreateProcessWwithEXTENDED_STARTUPINFO_PRESENTapply the custom mitigation configuration - Job objects ensure the emulator terminates automatically when the GUI closes
- Anonymous pipes capture stdout/stderr while
CREATE_NO_WINDOWprevents console window creation
Frequently Asked Questions
Why does the SharpEmu GUI disable CFG and CET for the emulator?
The emulator performs dynamic code generation and low-level execution context manipulation that conflicts with Control-Flow Guard and Control-Flow Enforcement Technology. These security features validate indirect call targets and shadow stack integrity, which would block the emulator's just-in-time translation and CPU state management.
What happens if the SHARPEMU_DISABLE_MITIGATION_RELAUNCH environment variable is not set?
If this variable is not set to "1", the CLI executable (SharpEmu.CLI) will attempt to relaunch itself with its own preferred mitigation policies upon startup. This would override the GUI's specific configuration and potentially enable CFG or CET, causing the emulator to crash or behave unexpectedly when executing dynamic code.
How does the SharpEmu GUI capture output from the emulator process?
The GUI creates anonymous pipes using CreatePipe for both stdout and stderr, then attaches the write ends to the child's hStdOutput and hStdError handles in the STARTUPINFOEX structure. The GUI reads from the pipe handles asynchronously to display emulator output in the Avalonia interface without requiring a visible console window.
What ensures the emulator terminates when the user closes the GUI?
The GUI creates a Windows Job object using CreateJobObjectW and associates the emulator process with it. By setting the JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE limit via SetInformationJobObject, the system automatically terminates all processes in the job (including the emulator) when the GUI process (the job's last handle) closes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →