From fde24f2616b894bfb2c831491932098254d3938f Mon Sep 17 00:00:00 2001 From: OfficeCom Codex Date: Fri, 17 Jul 2026 01:07:37 +0200 Subject: [PATCH] Add standalone OCSentinel setup executable builder --- .gitignore | 1 + build/build-client-installer.ps1 | 50 ++++++++++++ .../OCSentinelBootstrapper.csproj | 22 ++++++ installer/OCSentinelBootstrapper/Program.cs | 76 +++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 build/build-client-installer.ps1 create mode 100644 installer/OCSentinelBootstrapper/OCSentinelBootstrapper.csproj create mode 100644 installer/OCSentinelBootstrapper/Program.cs diff --git a/.gitignore b/.gitignore index 659eb11..3d1f906 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ payload/ *.log *.zip *.sha256 +payload.zip SetupAttackTracer.exe decompiled/ msi-admin/ diff --git a/build/build-client-installer.ps1 b/build/build-client-installer.ps1 new file mode 100644 index 0000000..b2c676c --- /dev/null +++ b/build/build-client-installer.ps1 @@ -0,0 +1,50 @@ +param( + [string]$Configuration = "Release" +) + +$ErrorActionPreference = "Stop" + +$repoRoot = Split-Path -Parent $PSScriptRoot +$artifactsRoot = Join-Path $repoRoot "artifacts" +$zipPath = Join-Path $artifactsRoot "OCSentinelClient-win-x64.zip" +$bootstrapperRoot = Join-Path $repoRoot "installer\OCSentinelBootstrapper" +$payloadPath = Join-Path $bootstrapperRoot "payload.zip" +$projectPath = Join-Path $bootstrapperRoot "OCSentinelBootstrapper.csproj" +$publishRoot = Join-Path $artifactsRoot "bootstrapper-publish\win-x64" +$outputExe = Join-Path $artifactsRoot "OCSentinelSetup.exe" + +if (-not (Test-Path $zipPath)) { + & powershell.exe -ExecutionPolicy Bypass -File (Join-Path $repoRoot "build\build-client-package.ps1") -Configuration $Configuration + if ($LASTEXITCODE -ne 0) { + throw "build-client-package.ps1 failed" + } +} + +Copy-Item -Path $zipPath -Destination $payloadPath -Force + +if (Test-Path $publishRoot) { + Remove-Item -LiteralPath $publishRoot -Recurse -Force +} + +if (Test-Path $outputExe) { + Remove-Item -LiteralPath $outputExe -Force +} + +New-Item -ItemType Directory -Force -Path $publishRoot | Out-Null + +& dotnet publish $projectPath ` + -c $Configuration ` + -r win-x64 ` + --self-contained true ` + -p:PublishSingleFile=true ` + -p:EnableCompressionInSingleFile=true ` + -p:IncludeNativeLibrariesForSelfExtract=true ` + -o $publishRoot + +if ($LASTEXITCODE -ne 0) { + throw "dotnet publish failed for bootstrapper" +} + +Copy-Item -Path (Join-Path $publishRoot "OCSentinelBootstrapper.exe") -Destination $outputExe -Force + +Write-Host "OfficeCom Sentinel setup executable created at $outputExe" diff --git a/installer/OCSentinelBootstrapper/OCSentinelBootstrapper.csproj b/installer/OCSentinelBootstrapper/OCSentinelBootstrapper.csproj new file mode 100644 index 0000000..c3b18bc --- /dev/null +++ b/installer/OCSentinelBootstrapper/OCSentinelBootstrapper.csproj @@ -0,0 +1,22 @@ + + + + Exe + net10.0-windows + enable + enable + false + OCSentinelBootstrapper + OCSentinelBootstrapper + true + true + win-x64 + true + true + + + + + + + diff --git a/installer/OCSentinelBootstrapper/Program.cs b/installer/OCSentinelBootstrapper/Program.cs new file mode 100644 index 0000000..34095fc --- /dev/null +++ b/installer/OCSentinelBootstrapper/Program.cs @@ -0,0 +1,76 @@ +using System.Diagnostics; +using System.IO.Compression; +using System.Reflection; + +namespace OCSentinelBootstrapper; + +internal static class Program +{ + private static int Main() + { + string tempRoot = Path.Combine(Path.GetTempPath(), "OCSentinelSetup", Guid.NewGuid().ToString("N")); + string zipPath = Path.Combine(tempRoot, "payload.zip"); + string extractRoot = Path.Combine(tempRoot, "payload"); + + try + { + Directory.CreateDirectory(tempRoot); + Directory.CreateDirectory(extractRoot); + + ExtractEmbeddedPayload(zipPath); + ZipFile.ExtractToDirectory(zipPath, extractRoot, overwriteFiles: true); + + string installScript = Path.Combine(extractRoot, "scripts", "install-ocsentinel.ps1"); + if (!File.Exists(installScript)) + { + throw new FileNotFoundException("Embedded payload did not contain install-ocsentinel.ps1", installScript); + } + + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "powershell.exe", + Arguments = $"-ExecutionPolicy Bypass -File \"{installScript}\"", + UseShellExecute = false + } + }; + + process.Start(); + process.WaitForExit(); + return process.ExitCode; + } + catch (Exception ex) + { + Console.Error.WriteLine($"OCSentinel installer failed: {ex}"); + return 1; + } + finally + { + try + { + if (Directory.Exists(tempRoot)) + { + Directory.Delete(tempRoot, recursive: true); + } + } + catch + { + // Best-effort cleanup only. + } + } + } + + private static void ExtractEmbeddedPayload(string destinationPath) + { + Assembly assembly = Assembly.GetExecutingAssembly(); + using Stream? resourceStream = assembly.GetManifestResourceStream("payload.zip"); + if (resourceStream is null) + { + throw new InvalidOperationException("Embedded payload.zip resource was not found."); + } + + using FileStream output = File.Create(destinationPath); + resourceStream.CopyTo(output); + } +}