fe2a6db786
rust / build / test / clippy / fmt (push) Has been cancelled
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>
183 lines
5.4 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|