Hi all,
I was poking around with this a bit and noticed another debugger detection method somewhat similar and associated with the Peb->ProcessHeap.ForceFlags check.
btw, Those ForceFlags flag values of 40000060h may correspond to:
HEAP_TAIL_CHECKING_ENABLED equ 00000020h
HEAP_FREE_CHECKING_ENABLED equ 00000040h
though I can't find a corresponding one for the 40000000h.
The other way of detecting a ring3 debugger is using Peb->NtGlobalFlag. This has probably been used before since it's kind of apparent but anyway...Here is the detection method first of all as inline asm:
Code:
/*******************************************************
Test debugger detection via PEB NtGlobalFlag
*******************************************************/
// Simple inline asm implementation
_asm{
mov eax, fs:[0x18] // TEB
mov eax, [eax+0x30] // PEB
mov eax, [eax+0x68] // Peb->NtGlobalFlag
test eax, 0x70 // FLG_HEAP_ENABLE_FREE_CHECK |
// FLG_HEAP_ENABLE_TAIL_CHECK |
// FLG_HEAP_VALIDATE_PARAMETERS
jz nodebugger
}
MessageBox(NULL, "Debugger Found via PEB NtGlobalFlag", "Busted", NULL);
nodebugger:
The *reason* this works can be exposed by examining ntdll!LdrInitializeThunk. LdrInitializeThunk is the loader initialization routine that is executed as a usermode APC by PspUserThreadStartup and is the first usermode code to execute on process startup.
In Softice at least you can set a global breakpoint on this function and trace its execution when you change external parameters. One of the first things LdrInitializeThunk does is to check any registry settings under
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\<program name>]
It does this using several calls to LdrQueryImageFileExecutionOptions with different string values:
Code:
(ReactOS define)
LdrQueryImageFileExecutionOptions (IN PUNICODE_STRING SubKey,
IN PCWSTR ValueName,
IN ULONG Type,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG ReturnedLength OPTIONAL)
One of the calls is to check for GlobalFlag settings and it sets the Peb->NtGlobalFlag field accordingly. Now, here's the critical part in terms of the debugger detection and is outlined in the following code:
If there are NO previously defined values for "GlobalFlag" under the registry Image File Execution Options,
AND there is a debugger attached as set under Peb->BeingDebugged,
then Peb->NtGlobalFlag will automatically be filled with the following flag values:
#define FLG_HEAP_ENABLE_TAIL_CHECK 0x00000010
#define FLG_HEAP_ENABLE_FREE_CHECK 0x00000020
#define FLG_HEAP_VALIDATE_PARAMETERS 0x00000040
Here is the ntdll!LdrInitializeThunk code which defines that (Win2Ksp4):
Code:
:77F83A03 lea eax, [PEB+68h] ; Peb->NtGlobalFlag
:77F83A06 push ebx ; NULL (ReturnedLength)
:77F83A07 push esi ; 0x04 (BufferSize)
:77F83A08 push eax ; return buffer Peb->NtGlobalFlag
:77F83A09 push esi ; 0x04 (REG_SZ)
:77F83A0A push offset aGlobalflag ; "GlobalFlag"
:77F83A0F lea eax, [ebp+Peb.UserProcessParameters.ImagePathName]
:77F83A12 push eax ; Unicode ImageName
:77F83A13 call LdrQueryImageFileExecutionOptions ;
; If successful, i.e. an entry for "GlobalFlag" is found, returns 0
; If entry not found, returns 0x0C0000034 STATUS_OBJECT_NAME_NOT_FOUND
;
:77F83A18 cmp eax, ebx
:77F83A1A jge short loc_77F83A25
:77F83A1C cmp [PEB+2], bl ; if (Peb->BeingDebugged)
:77F83A1F jnz @set_Peb.NtGlobalFlag
:77F83A25 loc_77F83A25: ; CODE XREF: LdrInitializeThunk-10D8
:77F83A25 mov eax, [PEB+68h]
:77F83A28 test eax, 2000000h ; FLG_HEAP_PAGE_ALLOCS
:77F83A2D jnz @checkPageHeapflags
...
; OR NtGlobalFlag value with these 3 flags
:77F99C6E @set_Peb.NtGlobalFlag: ; CODE XREF: LdrInitializeThunk-10D3
:77F99C6E or dword ptr [PEB+68h], 70h
; FLG_HEAP_ENABLE_FREE_CHECK | // 0x10
; FLG_HEAP_ENABLE_TAIL_CHECK | // 0x20
; FLG_HEAP_VALIDATE_PARAMETERS // 0x40
:77F99C72 jmp loc_77F83A25
:77F99C77 @checkPageHeapflags: ; CODE XREF: LdrInitializeThunk-10C5
:77F99C77 and ax, 670Fh
...
By examining the above code you can see one simple way to bypass a debugger check based on Peb->NtGlobalFlag, make sure there IS an existing entry for
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\program.exe]
"GlobalFlag"=""
Note that there doesn't have to be any value for "GlobalFlag", just the presence of the string is enough.
By itself this isn't too significant, BUT for some reason the presence of a "GlobalFlag" entry ALSO bypasses the Peb->ProcessHeap.ForceFlags debugger check trick as well. This is why I said above that these two detection methods are somewhat associated.
In terms of an Olly plugin one could either add a "Global Flag" registry entry for the target program *before* it is loaded, this will take care of both debugger checks, or of course simply wipe out the Peb fields after loading if you don't need them active.
I was going to attach a small test project but there's enough info here for anyone who wants to do it. For reference here is a complete listing of all the possible GlobalFlag settings (Peb->NtGlobalFlag) which I put together from two different sources:
Code:
// NtGlobalFlag flags
#define FLG_STOP_ON_EXCEPTION 0x00000001
#define FLG_SHOW_LDR_SNAPS 0x00000002
#define FLG_DEBUG_INITIAL_COMMAND 0x00000004
#define FLG_STOP_ON_HUNG_GUI 0x00000008
#define FLG_HEAP_ENABLE_TAIL_CHECK 0x00000010
#define FLG_HEAP_ENABLE_FREE_CHECK 0x00000020
#define FLG_HEAP_VALIDATE_PARAMETERS 0x00000040
#define FLG_HEAP_VALIDATE_ALL 0x00000080
#define FLG_POOL_ENABLE_TAIL_CHECK 0x00000100
#define FLG_POOL_ENABLE_FREE_CHECK 0x00000200
#define FLG_POOL_ENABLE_TAGGING 0x00000400
#define FLG_HEAP_ENABLE_TAGGING 0x00000800
#define FLG_USER_STACK_TRACE_DB 0x00001000
#define FLG_KERNEL_STACK_TRACE_DB 0x00002000
#define FLG_MAINTAIN_OBJECT_TYPELIST 0x00004000
#define FLG_HEAP_ENABLE_TAG_BY_DLL 0x00008000
#define FLG_IGNORE_DEBUG_PRIV 0x00010000
#define FLG_ENABLE_CSRDEBUG 0x00020000
#define FLG_ENABLE_KDEBUG_SYMBOL_LOAD 0x00040000
#define FLG_DISABLE_PAGE_KERNEL_STACKS 0x00080000
#define FLG_HEAP_ENABLE_CALL_TRACING 0x00100000
#define FLG_HEAP_DISABLE_COALESCING 0x00200000
#define FLG_VALID_BITS 0x003FFFFF
#define FLG_ENABLE_CLOSE_EXCEPTION 0x00400000
#define FLG_ENABLE_EXCEPTION_LOGGING 0x00800000
#define FLG_ENABLE_HANDLE_TYPE_TAGGING 0x01000000
#define FLG_HEAP_PAGE_ALLOCS 0x02000000
#define FLG_DEBUG_WINLOGON 0x04000000
#define FLG_ENABLE_DBGPRINT_BUFFERING 0x08000000
#define FLG_EARLY_CRITICAL_SECTION_EVT 0x10000000
#define FLG_DISABLE_DLL_VERIFICATION 0x80000000
For fun here is one possible C++ implementation of the Peb->NtGlobalFlag debugger check:
Code:
// A more formal C++ implementation of the same trick
NTSTATUS ntstatus;
HMODULE hNtDll;
PPEB Peb;
NTQUERYINFORMATIONPROCESS pNtQueryInformationProcess;
PROCESS_BASIC_INFORMATION ProcessInfo;
ULONG ReturnLength;
///////////////////////////////////////////////////////
hNtDll = GetModuleHandle("ntdll.dll"

;
if(!hNtDll)
return FALSE;
pNtQueryInformationProcess = (NTQUERYINFORMATIONPROCESS)GetProcAddress(hNtDll, "NtQueryInformationProcess"

;
if(IsBadCodePtr((FARPROC)pNtQueryInformationProcess))
return FALSE;
ntstatus = pNtQueryInformationProcess(GetCurrentProcess(),
ProcessBasicInformation,
(PVOID)&ProcessInfo,
sizeof(PROCESS_BASIC_INFORMATION),
&ReturnLength);
if(ntstatus != STATUS_SUCCESS)
return FALSE;
Peb = (PPEB) ProcessInfo.PebBaseAddress;
if ( Peb->NtGlobalFlag & FLG_HEAP_ENABLE_TAIL_CHECK &&
Peb->NtGlobalFlag & FLG_HEAP_ENABLE_FREE_CHECK &&
Peb->NtGlobalFlag & FLG_HEAP_VALIDATE_PARAMETERS)
{
MessageBox(NULL, "Debugger detected via Peb->NtGlobalFlag", NULL, NULL);
}
Cheers,
Kayaker