Files
mxaccess/analysis/decompiled/aaServicesCommon/ArchestrAServices.Common/DetachedProcess.cs
T
Joseph Doherty fe2a6db786
rust / build / test / clippy / fmt (push) Has been cancelled
Initial project state: .NET reference, design, Rust port (M0+M1), evidence
Layout:
- src/                    .NET 10 x64 reference: MxNativeCodec, MxNativeClient,
                          MxAsbClient, probes, tests, harnesses. Executable spec.
- design/                 Architectural plan for the Rust port (M0–M6), error
                          model, protocol invariants, risks (R1–R16), adversarial
                          review log (review.md).
- rust/                   Rust workspace. M0 skeleton + M1 codec parity.
                          mxaccess-codec: 215 unit tests + 2 cross-implementation
                          parity tests (byte-identical against .NET reference).
                          Other crates are M0 stubs awaiting M2+.
- captures/               Frida + netsh + pcap evidence per CLAUDE.md
                          ("captures are evidence, not throwaway logs").
- analysis/               Decompiled C# (frida/proxy/decompiled-*),
                          Ghidra exports for native DLLs (`exports/` only —
                          working state at `projects/` and AVEVA's input
                          binaries at `input/` are gitignored).
- docs/                   Reverse-engineering reference docs.
- tools/                  Setup-LiveProbeEnv.ps1 (Infisical credential fetcher),
                          Compute-Crc.ps1 (.NET parity helper).
- .github/workflows/      Rust CI: fmt + build + test + clippy on Windows.
- LICENSE                 MIT (Joseph Doherty, 2026).

Verified:
- cargo test --workspace → 217 passed (215 unit + 2 .NET parity), 0 failed
- cargo clippy --workspace -- -D warnings → clean
- cargo fmt --all -- --check → clean
- cargo publish --dry-run -p mxaccess-codec → packages cleanly

Excluded from history (see .gitignore):
- **/bin, **/obj, **/target — build artifacts
- analysis/ghidra/projects/ — Ghidra working state (regenerable)
- analysis/ghidra/input/ — AVEVA proprietary DLLs (vendor IP)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 06:21:00 -04:00

183 lines
5.4 KiB
C#

#define TRACE
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
namespace ArchestrAServices.Common;
public static class DetachedProcess
{
private static class NativeMethods
{
public struct STARTUPINFO
{
public int cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public int dwX;
public int dwY;
public int dwXSize;
public int dwXCountChars;
public int dwYCountChars;
public int dwFillAttribute;
public int dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
public struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
}
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessID;
public int dwThreadID;
}
public struct SECURITY_ATTRIBUTES
{
public int Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
public const uint ZERO_FLAG = 0u;
public const uint CREATE_BREAKAWAY_FROM_JOB = 16777216u;
public const uint CREATE_DEFAULT_ERROR_MODE = 67108864u;
public const uint CREATE_NEW_CONSOLE = 16u;
public const uint CREATE_NEW_PROCESS_GROUP = 512u;
public const uint CREATE_NO_WINDOW = 134217728u;
public const uint CREATE_PROTECTED_PROCESS = 262144u;
public const uint CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 33554432u;
public const uint CREATE_SEPARATE_WOW_VDM = 4096u;
public const uint CREATE_SHARED_WOW_VDM = 4096u;
public const uint CREATE_SUSPENDED = 4u;
public const uint CREATE_UNICODE_ENVIRONMENT = 1024u;
public const uint DEBUG_ONLY_THIS_PROCESS = 2u;
public const uint DEBUG_PROCESS = 1u;
public const uint DETACHED_PROCESS = 8u;
public const uint EXTENDED_STARTUPINFO_PRESENT = 524288u;
public const uint INHERIT_PARENT_AFFINITY = 65536u;
public const uint PROC_THREAD_ATTRIBUTE_HANDLE_LIST = 131074u;
public const uint PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 131072u;
public const uint FILE_ACCESS_WRITE = 1073741824u;
public const int STARTF_USESTDHANDLES = 256;
[DllImport("kernel32.dll", BestFitMapping = false, CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CreateProcess([MarshalAs(UnmanagedType.LPTStr)] string lpApplicationName, StringBuilder lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, [MarshalAs(UnmanagedType.LPTStr)] string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InitializeProcThreadAttributeList(IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hHandle);
}
public static Process Start(string executablePath, string commandLine)
{
SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "Starting detached process {0} {1} with command '{2}'", Environment.CurrentDirectory, executablePath, commandLine);
NativeMethods.STARTUPINFOEX lpStartupInfo = default(NativeMethods.STARTUPINFOEX);
lpStartupInfo.StartupInfo.cb = Marshal.SizeOf((object)lpStartupInfo);
lpStartupInfo.lpAttributeList = IntPtr.Zero;
try
{
IntPtr lpSize = IntPtr.Zero;
if (NativeMethods.InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize) || lpSize == IntPtr.Zero)
{
return null;
}
IntPtr intPtr = Marshal.AllocHGlobal(lpSize);
if (!NativeMethods.InitializeProcThreadAttributeList(intPtr, 1, 0, ref lpSize))
{
Marshal.FreeHGlobal(intPtr);
return null;
}
lpStartupInfo.lpAttributeList = intPtr;
NativeMethods.SECURITY_ATTRIBUTES lpProcessAttributes = default(NativeMethods.SECURITY_ATTRIBUTES);
lpProcessAttributes.Length = Marshal.SizeOf((object)lpProcessAttributes);
lpProcessAttributes.bInheritHandle = true;
StringBuilder lpCommandLine = new StringBuilder(commandLine);
if (NativeMethods.CreateProcess(executablePath, lpCommandLine, ref lpProcessAttributes, ref lpProcessAttributes, bInheritHandles: false, 8u, IntPtr.Zero, null, ref lpStartupInfo, out var lpProcessInformation))
{
NativeMethods.CloseHandle(lpProcessInformation.hProcess);
NativeMethods.CloseHandle(lpProcessInformation.hThread);
return Process.GetProcessById(lpProcessInformation.dwProcessID);
}
int lastWin32Error = Marshal.GetLastWin32Error();
SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Warning, 0, "Starting detached process {0} failed with error {1}", executablePath, lastWin32Error);
return null;
}
catch (Exception ex)
{
SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, "Detached process start failed: {0}", ex.Message);
return null;
}
finally
{
if (lpStartupInfo.lpAttributeList != IntPtr.Zero)
{
NativeMethods.DeleteProcThreadAttributeList(lpStartupInfo.lpAttributeList);
Marshal.FreeHGlobal(lpStartupInfo.lpAttributeList);
}
}
}
}