Anti-Debugging
Debugger Detection Techniques
IsDebuggerPresent Debugger Detection
One of the easiest anti-debugging techniques is to use the IsDebuggerPresent WinAPI. This function returns TRUE if a debugger is attached to the calling process or FALSE if there isn't.
printf("\n[#] Running IsDebuggerPresent ... ");
if (IsDebuggerPresent())
printf("<<!>> IsDebuggerPresent detected a debugger <<!>> \n");
else
printf("[+] DONE \n");
IsDebuggerPresent Replacement 1 Debugger Detection
Calling the IsDebuggerPresent WinAPI is suspicious. Custom version of the IsDebuggerPresent:
BOOL IsDebuggerPresent2() {
#ifdef _WIN64
PPEB pPeb = (PEB*)(__readgsqword(0x60));
#elif _WIN32
PPEB pPeb = (PEB*)(__readfsdword(0x30));
#endif
if (pPeb->BeingDebugged == 1)
return TRUE;
return FALSE;
}
/*
printf("\n[#] Running IsDebuggerPresent2 ... ");
if (IsDebuggerPresent2())
printf("<<!>> IsDebuggerPresent2 Detected A Debugger <<!>> \n");
else
printf("[+] DONE \n");
*/
Then we should add the structs header:
#pragma once
#include <Windows.h>
#ifndef STRUCTS
#define STRUCTS
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
typedef struct _PEB_LDR_DATA {
ULONG Length;
ULONG Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, * PPEB_LDR_DATA;
typedef PVOID PACTIVATION_CONTEXT;
typedef struct _LDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
WORD LoadCount;
WORD TlsIndex;
union {
LIST_ENTRY HashLinks;
struct {
PVOID SectionPointer;
ULONG CheckSum;
};
};
union {
ULONG TimeDateStamp;
PVOID LoadedImports;
};
PACTIVATION_CONTEXT EntryPointActivationContext;
PVOID PatchInformation;
LIST_ENTRY ForwarderLinks;
LIST_ENTRY ServiceTagLinks;
LIST_ENTRY StaticLinks;
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
typedef struct _PEB
{
UCHAR InheritedAddressSpace;
UCHAR ReadImageFileExecOptions;
UCHAR BeingDebugged;
union
{
UCHAR BitField;
struct
{
UCHAR ImageUsesLargePages : 1;
UCHAR IsProtectedProcess : 1;
UCHAR IsImageDynamicallyRelocated : 1;
UCHAR SkipPatchingUser32Forwarders : 1;
UCHAR IsPackagedProcess : 1;
UCHAR IsAppContainer : 1;
UCHAR IsProtectedProcessLight : 1;
UCHAR IsLongPathAwareProcess : 1;
};
};
UCHAR Padding0[4];
VOID* Mutant;
VOID* ImageBaseAddress;
struct _PEB_LDR_DATA* Ldr;
struct _RTL_USER_PROCESS_PARAMETERS* ProcessParameters;
VOID* SubSystemData;
VOID* ProcessHeap;
struct _RTL_CRITICAL_SECTION* FastPebLock;
union _SLIST_HEADER* volatile AtlThunkSListPtr;
VOID* IFEOKey;
union
{
ULONG CrossProcessFlags;
struct
{
ULONG ProcessInJob : 1;
ULONG ProcessInitializing : 1;
ULONG ProcessUsingVEH : 1;
ULONG ProcessUsingVCH : 1;
ULONG ProcessUsingFTH : 1;
ULONG ProcessPreviouslyThrottled : 1;
ULONG ProcessCurrentlyThrottled : 1;
ULONG ProcessImagesHotPatched : 1;
ULONG ReservedBits0 : 24;
};
};
UCHAR Padding1[4];
union
{
VOID* KernelCallbackTable;
VOID* UserSharedInfoPtr;
};
ULONG SystemReserved;
ULONG AtlThunkSListPtr32;
VOID* ApiSetMap;
ULONG TlsExpansionCounter;
UCHAR Padding2[4];
VOID* TlsBitmap;
ULONG TlsBitmapBits[2];
VOID* ReadOnlySharedMemoryBase;
VOID* SharedData;
VOID** ReadOnlyStaticServerData;
VOID* AnsiCodePageData;
VOID* OemCodePageData;
VOID* UnicodeCaseTableData;
ULONG NumberOfProcessors;
ULONG NtGlobalFlag;
union _LARGE_INTEGER CriticalSectionTimeout;
ULONGLONG HeapSegmentReserve;
ULONGLONG HeapSegmentCommit;
ULONGLONG HeapDeCommitTotalFreeThreshold;
ULONGLONG HeapDeCommitFreeBlockThreshold;
ULONG NumberOfHeaps;
ULONG MaximumNumberOfHeaps;
VOID** ProcessHeaps;
VOID* GdiSharedHandleTable;
VOID* ProcessStarterHelper;
ULONG GdiDCAttributeList;
UCHAR Padding3[4];
struct _RTL_CRITICAL_SECTION* LoaderLock;
ULONG OSMajorVersion;
ULONG OSMinorVersion;
USHORT OSBuildNumber;
USHORT OSCSDVersion;
ULONG OSPlatformId;
ULONG ImageSubsystem;
ULONG ImageSubsystemMajorVersion;
ULONG ImageSubsystemMinorVersion;
UCHAR Padding4[4];
ULONGLONG ActiveProcessAffinityMask;
ULONG GdiHandleBuffer[60];
VOID(*PostProcessInitRoutine)();
VOID* TlsExpansionBitmap;
ULONG TlsExpansionBitmapBits[32];
ULONG SessionId;
UCHAR Padding5[4];
union _ULARGE_INTEGER AppCompatFlags;
union _ULARGE_INTEGER AppCompatFlagsUser;
VOID* pShimData;
VOID* AppCompatInfo;
struct _UNICODE_STRING CSDVersion;
struct _ACTIVATION_CONTEXT_DATA* ActivationContextData;
struct _ASSEMBLY_STORAGE_MAP* ProcessAssemblyStorageMap;
struct _ACTIVATION_CONTEXT_DATA* SystemDefaultActivationContextData;
struct _ASSEMBLY_STORAGE_MAP* SystemAssemblyStorageMap;
ULONGLONG MinimumStackCommit;
struct _FLS_CALLBACK_INFO* FlsCallback;
struct _LIST_ENTRY FlsListHead;
VOID* FlsBitmap;
ULONG FlsBitmapBits[4];
ULONG FlsHighIndex;
VOID* WerRegistrationData;
VOID* WerShipAssertPtr;
VOID* pUnused;
VOID* pImageHeaderHash;
union
{
ULONG TracingFlags;
struct
{
ULONG HeapTracingEnabled : 1;
ULONG CritSecTracingEnabled : 1;
ULONG LibLoaderTracingEnabled : 1;
ULONG SpareTracingBits : 29;
};
};
UCHAR Padding6[4];
ULONGLONG CsrServerReadOnlySharedMemoryBase;
ULONGLONG TppWorkerpListLock;
struct _LIST_ENTRY TppWorkerpList;
VOID* WaitOnAddressHashTable[128];
VOID* TelemetryCoverageHeader;
ULONG CloudFileFlags;
ULONG CloudFileDiagFlags;
CHAR PlaceholderCompatibilityMode;
CHAR PlaceholderCompatibilityModeReserved[7];
struct _LEAP_SECOND_DATA* LeapSecondData;
union
{
ULONG LeapSecondFlags;
struct
{
ULONG SixtySecondEnabled : 1;
ULONG Reserved : 31;
};
};
ULONG NtGlobalFlag2;
} PEB, * PPEB;
// https://github.com/winsiderss/systeminformer/blob/master/phnt/include/ntpsapi.h#L110
typedef enum _PROCESSINFOCLASS
{
ProcessBasicInformation, // q: PROCESS_BASIC_INFORMATION, PROCESS_EXTENDED_BASIC_INFORMATION
ProcessQuotaLimits, // qs: QUOTA_LIMITS, QUOTA_LIMITS_EX
ProcessIoCounters, // q: IO_COUNTERS
ProcessVmCounters, // q: VM_COUNTERS, VM_COUNTERS_EX, VM_COUNTERS_EX2
ProcessTimes, // q: KERNEL_USER_TIMES
ProcessBasePriority, // s: KPRIORITY
ProcessRaisePriority, // s: ULONG
ProcessDebugPort, // q: HANDLE
ProcessExceptionPort, // s: PROCESS_EXCEPTION_PORT (requires SeTcbPrivilege)
ProcessAccessToken, // s: PROCESS_ACCESS_TOKEN
ProcessLdtInformation, // qs: PROCESS_LDT_INFORMATION // 10
ProcessLdtSize, // s: PROCESS_LDT_SIZE
ProcessDefaultHardErrorMode, // qs: ULONG
ProcessIoPortHandlers, // (kernel-mode only) // PROCESS_IO_PORT_HANDLER_INFORMATION
ProcessPooledUsageAndLimits, // q: POOLED_USAGE_AND_LIMITS
ProcessWorkingSetWatch, // q: PROCESS_WS_WATCH_INFORMATION[]; s: void
ProcessUserModeIOPL, // qs: ULONG (requires SeTcbPrivilege)
ProcessEnableAlignmentFaultFixup, // s: BOOLEAN
ProcessPriorityClass, // qs: PROCESS_PRIORITY_CLASS
ProcessWx86Information, // qs: ULONG (requires SeTcbPrivilege) (VdmAllowed)
ProcessHandleCount, // q: ULONG, PROCESS_HANDLE_INFORMATION // 20
ProcessAffinityMask, // (q >WIN7)s: KAFFINITY, qs: GROUP_AFFINITY
ProcessPriorityBoost, // qs: ULONG
ProcessDeviceMap, // qs: PROCESS_DEVICEMAP_INFORMATION, PROCESS_DEVICEMAP_INFORMATION_EX
ProcessSessionInformation, // q: PROCESS_SESSION_INFORMATION
ProcessForegroundInformation, // s: PROCESS_FOREGROUND_BACKGROUND
ProcessWow64Information, // q: ULONG_PTR
ProcessImageFileName, // q: UNICODE_STRING
ProcessLUIDDeviceMapsEnabled, // q: ULONG
ProcessBreakOnTermination, // qs: ULONG
ProcessDebugObjectHandle, // q: HANDLE // 30
ProcessDebugFlags, // qs: ULONG
ProcessHandleTracing, // q: PROCESS_HANDLE_TRACING_QUERY; s: size 0 disables, otherwise enables
ProcessIoPriority, // qs: IO_PRIORITY_HINT
ProcessExecuteFlags, // qs: ULONG
ProcessTlsInformation, // PROCESS_TLS_INFORMATION // ProcessResourceManagement
ProcessCookie, // q: ULONG
ProcessImageInformation, // q: SECTION_IMAGE_INFORMATION
ProcessCycleTime, // q: PROCESS_CYCLE_TIME_INFORMATION // since VISTA
ProcessPagePriority, // qs: PAGE_PRIORITY_INFORMATION
ProcessInstrumentationCallback, // s: PVOID or PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION // 40
ProcessThreadStackAllocation, // s: PROCESS_STACK_ALLOCATION_INFORMATION, PROCESS_STACK_ALLOCATION_INFORMATION_EX
ProcessWorkingSetWatchEx, // q: PROCESS_WS_WATCH_INFORMATION_EX[]
ProcessImageFileNameWin32, // q: UNICODE_STRING
ProcessImageFileMapping, // q: HANDLE (input)
ProcessAffinityUpdateMode, // qs: PROCESS_AFFINITY_UPDATE_MODE
ProcessMemoryAllocationMode, // qs: PROCESS_MEMORY_ALLOCATION_MODE
ProcessGroupInformation, // q: USHORT[]
ProcessTokenVirtualizationEnabled, // s: ULONG
ProcessConsoleHostProcess, // qs: ULONG_PTR // ProcessOwnerInformation
ProcessWindowInformation, // q: PROCESS_WINDOW_INFORMATION // 50
ProcessHandleInformation, // q: PROCESS_HANDLE_SNAPSHOT_INFORMATION // since WIN8
ProcessMitigationPolicy, // s: PROCESS_MITIGATION_POLICY_INFORMATION
ProcessDynamicFunctionTableInformation,
ProcessHandleCheckingMode, // qs: ULONG; s: 0 disables, otherwise enables
ProcessKeepAliveCount, // q: PROCESS_KEEPALIVE_COUNT_INFORMATION
ProcessRevokeFileHandles, // s: PROCESS_REVOKE_FILE_HANDLES_INFORMATION
ProcessWorkingSetControl, // s: PROCESS_WORKING_SET_CONTROL
ProcessHandleTable, // q: ULONG[] // since WINBLUE
ProcessCheckStackExtentsMode, // qs: ULONG // KPROCESS->CheckStackExtents (CFG)
ProcessCommandLineInformation, // q: UNICODE_STRING // 60
ProcessProtectionInformation, // q: PS_PROTECTION
ProcessMemoryExhaustion, // PROCESS_MEMORY_EXHAUSTION_INFO // since THRESHOLD
ProcessFaultInformation, // PROCESS_FAULT_INFORMATION
ProcessTelemetryIdInformation, // q: PROCESS_TELEMETRY_ID_INFORMATION
ProcessCommitReleaseInformation, // PROCESS_COMMIT_RELEASE_INFORMATION
ProcessDefaultCpuSetsInformation, // SYSTEM_CPU_SET_INFORMATION[5]
ProcessAllowedCpuSetsInformation, // SYSTEM_CPU_SET_INFORMATION[5]
ProcessSubsystemProcess,
ProcessJobMemoryInformation, // q: PROCESS_JOB_MEMORY_INFO
ProcessInPrivate, // s: void // ETW // since THRESHOLD2 // 70
ProcessRaiseUMExceptionOnInvalidHandleClose, // qs: ULONG; s: 0 disables, otherwise enables
ProcessIumChallengeResponse,
ProcessChildProcessInformation, // q: PROCESS_CHILD_PROCESS_INFORMATION
ProcessHighGraphicsPriorityInformation, // qs: BOOLEAN (requires SeTcbPrivilege)
ProcessSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2
ProcessEnergyValues, // q: PROCESS_ENERGY_VALUES, PROCESS_EXTENDED_ENERGY_VALUES
ProcessPowerThrottlingState, // qs: POWER_THROTTLING_PROCESS_STATE
ProcessReserved3Information, // ProcessActivityThrottlePolicy // PROCESS_ACTIVITY_THROTTLE_POLICY
ProcessWin32kSyscallFilterInformation, // q: WIN32K_SYSCALL_FILTER
ProcessDisableSystemAllowedCpuSets, // 80
ProcessWakeInformation, // PROCESS_WAKE_INFORMATION
ProcessEnergyTrackingState, // PROCESS_ENERGY_TRACKING_STATE
ProcessManageWritesToExecutableMemory, // MANAGE_WRITES_TO_EXECUTABLE_MEMORY // since REDSTONE3
ProcessCaptureTrustletLiveDump,
ProcessTelemetryCoverage,
ProcessEnclaveInformation,
ProcessEnableReadWriteVmLogging, // PROCESS_READWRITEVM_LOGGING_INFORMATION
ProcessUptimeInformation, // q: PROCESS_UPTIME_INFORMATION
ProcessImageSection, // q: HANDLE
ProcessDebugAuthInformation, // since REDSTONE4 // 90
ProcessSystemResourceManagement, // PROCESS_SYSTEM_RESOURCE_MANAGEMENT
ProcessSequenceNumber, // q: ULONGLONG
ProcessLoaderDetour, // since REDSTONE5
ProcessSecurityDomainInformation, // PROCESS_SECURITY_DOMAIN_INFORMATION
ProcessCombineSecurityDomainsInformation, // PROCESS_COMBINE_SECURITY_DOMAINS_INFORMATION
ProcessEnableLogging, // PROCESS_LOGGING_INFORMATION
ProcessLeapSecondInformation, // PROCESS_LEAP_SECOND_INFORMATION
ProcessFiberShadowStackAllocation, // PROCESS_FIBER_SHADOW_STACK_ALLOCATION_INFORMATION // since 19H1
ProcessFreeFiberShadowStackAllocation, // PROCESS_FREE_FIBER_SHADOW_STACK_ALLOCATION_INFORMATION
ProcessAltSystemCallInformation, // qs: BOOLEAN (kernel-mode only) // INT2E // since 20H1 // 100
ProcessDynamicEHContinuationTargets, // PROCESS_DYNAMIC_EH_CONTINUATION_TARGETS_INFORMATION
ProcessDynamicEnforcedCetCompatibleRanges, // PROCESS_DYNAMIC_ENFORCED_ADDRESS_RANGE_INFORMATION // since 20H2
ProcessCreateStateChange, // since WIN11
ProcessApplyStateChange,
ProcessEnableOptionalXStateFeatures,
ProcessAltPrefetchParam, // since 22H1
ProcessAssignCpuPartitions,
ProcessPriorityClassEx, // s: PROCESS_PRIORITY_CLASS_EX
ProcessMembershipInformation,
ProcessEffectiveIoPriority, // q: IO_PRIORITY_HINT
ProcessEffectivePagePriority, // q: ULONG
MaxProcessInfoClass
} PROCESSINFOCLASS;
#endif // !STRUCTS
IsDebuggerPresent Replacement 2 Debugger Detection
Another custom version of the IsDebuggerPresent using NtGlobalFlag flag which is also found within the PEB structure:
/*
https://www.aldeid.com/wiki/PEB-Process-Environment-Block/NtGlobalFlag
https://www.geoffchappell.com/studies/windows/win32/ntdll/api/rtl/regutil/getntglobalflags.htm
*/
#define FLG_HEAP_ENABLE_TAIL_CHECK 0x10
#define FLG_HEAP_ENABLE_FREE_CHECK 0x20
#define FLG_HEAP_VALIDATE_PARAMETERS 0x40
BOOL IsDebuggerPresent3() {
#ifdef _WIN64
PPEB pPeb = (PEB*)(__readgsqword(0x60));
#elif _WIN32
PPEB pPeb = (PEB*)(__readfsdword(0x30));
#endif
if (pPeb->NtGlobalFlag & (FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS))
return TRUE;
return FALSE;
}
/*
printf("\n[#] Running IsDebuggerPresent3 ... ");
if (IsDebuggerPresent3())
printf("<<!>> IsDebuggerPresent3 Detected A Debugger <<!>> \n");
else
printf("[+] DONE \n");
*/
NtQueryInformationProcess Debugger Detection
// https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
typedef NTSTATUS (WINAPI* fnNtQueryInformationProcess)(
HANDLE ProcessHandle,
PROCESSINFOCLASS ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
BOOL NtQIPDebuggerCheck() {
NTSTATUS STATUS = NULL;
fnNtQueryInformationProcess pNtQueryInformationProcess = NULL;
DWORD64 dwIsDebuggerPresent = NULL;
DWORD64 hProcessDebugObject = NULL;
// getting NtQueryInformationProcess address
pNtQueryInformationProcess = (fnNtQueryInformationProcess)GetProcAddress(GetModuleHandle(TEXT("NTDLL.DLL")), "NtQueryInformationProcess");
if (pNtQueryInformationProcess == NULL) {
printf("\n\t[!] GetProcAddress Failed With Error : %d \n", GetLastError());
return FALSE;
}
// calling NtQueryInformationProcess with the 'ProcessDebugPort' flag
STATUS = pNtQueryInformationProcess(
GetCurrentProcess(),
ProcessDebugPort,
&dwIsDebuggerPresent,
sizeof(DWORD64),
NULL
);
// if STATUS is not
if (STATUS != 0x0) {
printf("\n\t[!] NtQueryInformationProcess [1] Failed With Status : 0x%0.8X \n", STATUS);
return FALSE;
}
// if NtQueryInformationProcess returned a non-zero value, the handle is valid, which means we are being debugged
if (dwIsDebuggerPresent != NULL) {
//printf("\n\t[i] NtQueryInformationProcess [1] - ProcessDebugPort Detected A Debugger \n");
return TRUE;
}
// calling NtQueryInformationProcess with the 'ProcessDebugObjectHandle' flag
STATUS = pNtQueryInformationProcess(
GetCurrentProcess(),
ProcessDebugObjectHandle,
&hProcessDebugObject,
sizeof(DWORD64),
NULL
);
// if STATUS is not 0 and not 0xC0000353 (that is 'STATUS_PORT_NOT_SET')
if (STATUS != 0x0 && STATUS != 0xC0000353) {
printf("\n\t[!] NtQueryInformationProcess [2] Failed With Status : 0x%0.8X \n", STATUS);
return FALSE;
}
// if NtQueryInformationProcess returned a non-zero value, the handle is valid, which means we are being debugged
if (hProcessDebugObject != NULL) {
//printf("\n\t[i] NtQueryInformationProcess [w] - hProcessDebugObject Detected A Debugger \n");
return TRUE;
}
return FALSE;
}
/*
printf("\n[#] Running NtQIPDebuggerCheck ... ");
if (NtQIPDebuggerCheck())
printf("<<!>> NtQIPDebuggerCheck Detected A Debugger <<!>> \n");
else
printf("[+] DONE \n");
*/
Hardware Breakpoints Debugger Detection
This method is only valid if hardware breakpoints are set during debugging.
BOOL HardwareBpCheck() {
CONTEXT Ctx = { .ContextFlags = CONTEXT_DEBUG_REGISTERS };
if (!GetThreadContext(GetCurrentThread(), &Ctx)) {
printf("\n\t[!] GetThreadContext Failed With Error : %d \n", GetLastError());
return FALSE;
}
// if one of these registers is not '0', then a hardware bp is installed
if (Ctx.Dr0 != NULL || Ctx.Dr1 != NULL || Ctx.Dr2 != NULL || Ctx.Dr3 != NULL)
return TRUE;
return FALSE;
}
/*
printf("\n[#] Running HardwareBpCheck ... ");
if (HardwareBpCheck())
printf("<<!>> HardwareBpCheck Detected A Debugger <<!>> \n");
else
printf("[+] DONE \n");
*/
BlackListed Arrays Debugger Detection
Detect debugging processes by checking the names of currently running processes against a list of known debugger names.
#define BLACKLISTARRAY_SIZE 5
WCHAR* g_BlackListedDebuggers[BLACKLISTARRAY_SIZE] = {
L"x64dbg.exe",
L"ida.exe",
L"ida64.exe",
L"VsDebugConsole.exe",
L"msvsmon.exe"
};
BOOL BlackListedProcessesCheck() {
HANDLE hSnapShot = NULL;
PROCESSENTRY32W ProcEntry = { .dwSize = sizeof(PROCESSENTRY32W) };
BOOL bSTATE = FALSE;
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (hSnapShot == INVALID_HANDLE_VALUE) {
printf("\n\t[!] CreateToolhelp32Snapshot Failed With Error : %d \n", GetLastError());
goto _EndOfFunction;
}
if (!Process32FirstW(hSnapShot, &ProcEntry)) {
printf("\n\t[!] Process32FirstW Failed With Error : %d \n", GetLastError());
goto _EndOfFunction;
}
do {
for (int i = 0; i < BLACKLISTARRAY_SIZE; i++){
if (wcscmp(ProcEntry.szExeFile, g_BlackListedDebuggers[i]) == 0) {
wprintf(L"\n\t[i] Found \"%s\" Of Pid : %d\n", ProcEntry.szExeFile, ProcEntry.th32ProcessID);
bSTATE = TRUE;
break; // breaking from the for loop
}
}
if (bSTATE)
break; // breaking from the do-while loop
} while (Process32Next(hSnapShot, &ProcEntry));
_EndOfFunction:
if (hSnapShot != NULL)
CloseHandle(hSnapShot);
return bSTATE;
}
/*
printf("\n[#] Running BlackListedProcessesCheck ... ");
if (BlackListedProcessesCheck())
printf("<<!>> BlackListedProcessesCheck Detected A Debugger <<!>> \n");
else
printf("[+] DONE \n");
*/
GetTickCount64 Breakpoint Detection
The pause of execution can be detected by using the GetTickCount64 WinAPI:
// https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-gettickcount64
BOOL TimeTickCheck1() {
DWORD dwTime1 = NULL,
dwTime2 = NULL;
dwTime1 = GetTickCount64();
dwTime2 = GetTickCount64();
printf("\n\t[i] (dwTime2 - dwTime1) : %d \n", (dwTime2 - dwTime1));
if ((dwTime2 - dwTime1) > 50) {
return TRUE;
}
return FALSE;
}
/*
printf("\n[#] Running TimeTickCheck1 ... ");
if (TimeTickCheck1())
printf("<<!>> TimeTickCheck1 Detected A Debugger <<!>> \n");
else
printf("[+] DONE \n");
*/
QueryPerformanceCounter Breakpoint Detection
The QueryPerformanceCounter WinAPI is the same as GetTickCount64 WinAPI but it uses a high-resolution performance counter provided by the hardware which can measure time in increments of nanoseconds whereas GetTickCount64 uses a time counter that increments every millisecond.
// https://learn.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter
BOOL TimeTickCheck2() {
LARGE_INTEGER Time1 = { 0 },
Time2 = { 0 };
if (!QueryPerformanceCounter(&Time1)) {
printf("\n\t[!] QueryPerformanceCounter [1] Failed With Error : %d \n", GetLastError());
return FALSE;
}
if (!QueryPerformanceCounter(&Time2)) {
printf("\n\t[!] QueryPerformanceCounter [2] Failed With Error : %d \n", GetLastError());
return FALSE;
}
printf("\n\t[i] (Time2.QuadPart - Time1.QuadPart) : %d \n", (Time2.QuadPart - Time1.QuadPart));
if ((Time2.QuadPart - Time1.QuadPart) > 100000){
return TRUE;
}
return FALSE;
}
/*
printf("\n[#] Running TimeTickCheck2 ... ");
if (TimeTickCheck2())
printf("<<!>> TimeTickCheck2 Detected A Debugger <<!>> \n");
else
printf("[+] DONE \n");
*/
DebugBreak Debugger Detection
DebugBreak causes the breakpoint exception, EXCEPTION_BREAKPOINT, to occur in the current process. This exception is supposed to be handled by a debugger if it is attached to the current process. The technique is to trigger the exception and see if a debugger attempts to handle this exception.
BOOL DebugBreakCheck() {
__try {
DebugBreak();
}
__except (GetExceptionCode() == EXCEPTION_BREAKPOINT ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
return FALSE;
}
return TRUE;
}
/*
printf("\n[#] Running DebugBreakCheck ... ");
if (DebugBreakCheck())
printf("<<!>> DebugBreakCheck Detected A Debugger <<!>> \n");
else
printf("[+] DONE \n");
*/
OutputDebugString Debugger Detection
Another WinAPI that can be utilized in detecting debuggers is OutputDebugString. This function is used to send a string to the debugger to display. If a debugger exists, then OutputDebugString will succeed in performing its task.
BOOL OutputDebugStringCheck() {
SetLastError(1);
OutputDebugStringW(L"MalDev Academy");
if (GetLastError() == 0) {
return TRUE;
}
return FALSE;
}
/*
printf("\n[#] Running OutputDebugStringCheck ... ");
if (OutputDebugStringCheck())
printf("<<!>> OutputDebugStringCheck Detected A Debugger <<!>> \n");
else
printf("[+] DONE \n");
*/
Techniques once Debugger is detected
Self-Deletion
#include <Windows.h>
#include <stdio.h>
// the new data stream name
#define NEW_STREAM L":Hola"
BOOL DeleteSelf() {
WCHAR szPath [MAX_PATH * 2] = { 0 };
FILE_DISPOSITION_INFO Delete = { 0 };
HANDLE hFile = INVALID_HANDLE_VALUE;
PFILE_RENAME_INFO pRename = NULL;
const wchar_t* NewStream = (const wchar_t*)NEW_STREAM;
SIZE_T sRename = sizeof(FILE_RENAME_INFO) + sizeof(NewStream);
// allocating enough buffer for the 'FILE_RENAME_INFO' structure
pRename = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sRename);
if (!pRename) {
printf("[!] HeapAlloc Failed With Error : %d \n", GetLastError());
return FALSE;
}
// cleaning up the structures
ZeroMemory(szPath, sizeof(szPath));
ZeroMemory(&Delete, sizeof(FILE_DISPOSITION_INFO));
//--------------------------------------------------------------------------------------------------------------------------
// marking the file for deletion (used in the 2nd SetFileInformationByHandle call)
Delete.DeleteFile = TRUE;
// setting the new data stream name buffer and size in the 'FILE_RENAME_INFO' structure
pRename->FileNameLength = sizeof(NewStream);
RtlCopyMemory(pRename->FileName, NewStream, sizeof(NewStream));
//--------------------------------------------------------------------------------------------------------------------------
// used to get the current file name
if (GetModuleFileNameW(NULL, szPath, MAX_PATH * 2) == 0) {
printf("[!] GetModuleFileNameW Failed With Error : %d \n", GetLastError());
return FALSE;
}
//--------------------------------------------------------------------------------------------------------------------------
// RENAMING
// openning a handle to the current file
hFile = CreateFileW(szPath, DELETE | SYNCHRONIZE, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("[!] CreateFileW [R] Failed With Error : %d \n", GetLastError());
return FALSE;
}
wprintf(L"[i] Renaming :$DATA to %s ...", NEW_STREAM);
// renaming the data stream
if (!SetFileInformationByHandle(hFile, FileRenameInfo, pRename, sRename)) {
printf("[!] SetFileInformationByHandle [R] Failed With Error : %d \n", GetLastError());
return FALSE;
}
wprintf(L"[+] DONE \n");
CloseHandle(hFile);
//--------------------------------------------------------------------------------------------------------------------------
// DELEING
// openning a new handle to the current file
hFile = CreateFileW(szPath, DELETE | SYNCHRONIZE, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
if (hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND) {
// in case the file is already deleted
return TRUE;
}
if (hFile == INVALID_HANDLE_VALUE) {
printf("[!] CreateFileW [D] Failed With Error : %d \n", GetLastError());
return FALSE;
}
wprintf(L"[i] DELETING ...");
// marking for deletion after the file's handle is closed
if (!SetFileInformationByHandle(hFile, FileDispositionInfo, &Delete, sizeof(Delete))) {
printf("[!] SetFileInformationByHandle [D] Failed With Error : %d \n", GetLastError());
return FALSE;
}
wprintf(L"[+] DONE \n");
CloseHandle(hFile);
//--------------------------------------------------------------------------------------------------------------------------
// freeing the allocated buffer
HeapFree(GetProcessHeap(), 0, pRename);
return TRUE;
}
int main(int argc, char* argv[]) {
if (!DeleteSelf()) {
return -1;
}
printf("[+] %s Should Be Deleted \n", argv[0]);
printf("[#] Press <Enter> To Quit ... ");
getchar();
return 0;
}
Last updated