initial commit
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Principal;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using PureCrack.Panel;
|
||||
using PureCrack.Setup;
|
||||
using PureCrack.Util;
|
||||
|
||||
namespace PureCrack;
|
||||
|
||||
public static class Preflight
|
||||
{
|
||||
public sealed class Result
|
||||
{
|
||||
public List<string> Problems { get; } = new List<string>();
|
||||
|
||||
public string? PanelExePath { get; set; }
|
||||
|
||||
public bool Ok => Problems.Count == 0;
|
||||
}
|
||||
|
||||
public static Result Run()
|
||||
{
|
||||
Result result = new Result();
|
||||
if (!IsAdmin())
|
||||
{
|
||||
result.Problems.Add("not running as administrator (need admin to bind :443 + write hosts + install root cert)");
|
||||
}
|
||||
if (!IsTcpPortFree(443))
|
||||
{
|
||||
string text = LookupTcpListenerHolder(443);
|
||||
string arg = text ?? "another process";
|
||||
string arg2 = ((text != null && text.StartsWith("PID ")) ? (" (taskkill /F /PID " + text.Substring(4).Split(new char[1] { ' ' })[0] + " to stop it)") : "");
|
||||
result.Problems.Add($":{443} is already bound by {arg}{arg2}");
|
||||
}
|
||||
try
|
||||
{
|
||||
if (!HostsManager.IsWritable())
|
||||
{
|
||||
result.Problems.Add(HostsManager.Path + " is not writable (file marked read-only? AV blocking?)");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Problems.Add("hosts file check threw: " + ex.Message);
|
||||
}
|
||||
try
|
||||
{
|
||||
result.PanelExePath = PanelLauncher.FindExe();
|
||||
}
|
||||
catch (FileNotFoundException ex2)
|
||||
{
|
||||
result.Problems.Add(ex2.Message);
|
||||
}
|
||||
try
|
||||
{
|
||||
_ = typeof(CSharpCompilation).Assembly.FullName;
|
||||
}
|
||||
catch (Exception ex3)
|
||||
{
|
||||
result.Problems.Add("Roslyn (Microsoft.CodeAnalysis.CSharp) not loadable: " + ex3.Message);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void Report(Result r)
|
||||
{
|
||||
if (r.Ok)
|
||||
{
|
||||
Log.Ok("preflight passed (panel at " + r.PanelExePath + ")");
|
||||
return;
|
||||
}
|
||||
Log.Err($"preflight: {r.Problems.Count} problem(s) — fix all then re-launch:");
|
||||
foreach (string problem in r.Problems)
|
||||
{
|
||||
Log.Bullet(" - " + problem);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsAdmin()
|
||||
{
|
||||
try
|
||||
{
|
||||
using WindowsIdentity ntIdentity = WindowsIdentity.GetCurrent();
|
||||
return new WindowsPrincipal(ntIdentity).IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsTcpPortFree(int port)
|
||||
{
|
||||
TcpListener tcpListener = null;
|
||||
try
|
||||
{
|
||||
tcpListener = new TcpListener(IPAddress.Any, port);
|
||||
tcpListener.Start();
|
||||
return true;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
tcpListener?.Stop();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string? LookupTcpListenerHolder(int port)
|
||||
{
|
||||
try
|
||||
{
|
||||
using Process process = Process.Start(new ProcessStartInfo("netstat", "-ano")
|
||||
{
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
CreateNoWindow = true
|
||||
});
|
||||
if (process == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string text = process.StandardOutput.ReadToEnd();
|
||||
process.WaitForExit(5000);
|
||||
string[] array = text.Split(new char[1] { '\n' });
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
string text2 = array[i].Trim();
|
||||
if (!text2.StartsWith("TCP", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string[] array2 = text2.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (array2.Length >= 5 && !(array2[3] != "LISTENING") && array2[1].EndsWith(":" + port, StringComparison.Ordinal) && int.TryParse(array2[4], out var result))
|
||||
{
|
||||
try
|
||||
{
|
||||
Process processById = Process.GetProcessById(result);
|
||||
return $"PID {result} ({processById.ProcessName})";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return $"PID {result}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
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)");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user