RtlQueryProcessHeapInformation As Anti-Dbg Trick

April 14, 2009


Directly derived from the previous trick, RtlQueryProcessHeapInformation could be used as Anti-Dbg Trick.

Starting by RtlQueryProcessDebugInformation.

IN ULONG  ProcessId,
IN ULONG  DebugInfoClassMask,

This function loads all heap blocks of the process into DebugBuffer according to the informations that we want and that could be specified throughout DebugInfoClassMask. If we implement RtlQueryProcessDebugInformation in this way

void QueryDbgBufferMethod(void)
NTSTATUS ntStatus;

buffer = RtlCreateQueryDebugBuffer(0,FALSE);

ntStatus = RtlQueryProcessDebugInformation(GetCurrentProcessId(),

The function according to the chosen mask will internally execute an call for heap functions, let’s see what happens inside this function.

__stdcall RtlQueryProcessDebugInformation(x, x, x)
7C9638EB                 mov     edi, edi
7C9638ED                 push    ebp
7C9638EE                 mov     ebp, esp
7C9638F0                 sub     esp, 44h
7C9638F3                 mov     eax, [ebp+14] ;EAX = DebugMassk
7C9638F6                 push    ebx
7C9638F7                 push    esi
7C9638F8                 mov     esi, [ebp+arg_8] ;ESI = DEBUG_BUFFER
7C9638FB                 xor     ebx, ebx
7C9638FD                 mov     [esi+20h], eax
7C963900                 mov     eax, [esi+24h]
7C963903                 cmp     eax, ebx
7C963905                 push    edi
7C963906                 mov     [ebp+var_4], ebx
7C963909                 mov     [ebp+var_8], ebx
7C96390C                 jz      short loc_7C963924
7C963924                 mov     dword ptr [esi+24h], 60h
7C96392B                 mov     eax, large fs:18h
7C963931                 mov     edi, [ebp+arg_0]
7C963934                 cmp     [eax+20h], edi
7C963937                 jz      short loc_7C963987
7C963939                 test    byte ptr [ebp+arg_4+3], 80h
7C96393D                 jz      short loc_7C963987
7C96393F                 test    byte ptr [ebp+0Ch], 41h
7C963943                 jz      short loc_7C963987
7C963987                 mov     eax, large fs:18h
7C96398D                 cmp     [eax+20h], edi
7C963990                 jz      loc_7C963A8E
7C963996                 cmp     [ebp+var_8], ebx


Here DebugMask Switch Cases:

7C963AA7                 test    byte ptr [ebp+arg_4], 2
7C963AAB                 jz      short loc_7C963ABA
7C963AAD                 push    esi
7C963AAE                 call    _RtlQueryProcessBackTraceInformation
7C963AB3                 cmp     eax, ebx
7C963AB5                 mov     [ebp+var_4], eax
7C963AB8                 jnz     short loc_7C963ADC
7C963ABA                 test    byte ptr [ebp+arg_4], 20h
7C963ABE                 jz      short loc_7C963ACD
7C963AC0                 push    esi

7C963AC1                 call    _RtlQueryProcessLockInformation
7C963AC6                 cmp     eax, ebx
7C963AC8                 mov     [ebp+var_4], eax
7C963ACB                 jnz     short loc_7C963ADC
7C963ACD                 test    byte ptr [ebp+arg_4], 1Ch ; DebugMask = PDI_HEAPS|PDI_HEAP_BLOCKS (Our Case)
7C963AD1                 jz      short loc_7C963ADC
7C963AD3                 push    esi   ;DEBUG_BUFFER
7C963AD4                 call    _RtlQueryProcessHeapInformation
7C963AD9                 mov     [ebp+var_4], eax

As you can see when we use DebugMask = PDI_HEAPS|PDI_HEAP_BLOCKS, is only called
RtlQueryProcessHeapInformation, function that takes only DEBUG_BUFFER as parameter, this means that this function is only applicable for the Current Process since does not take PID.

; __stdcall RtlQueryProcessHeapInformation(x)
7C963249                 push    58h
7C96324B                 push    offset stru_7C963708
7C963250                 call    __SEH_prolog
7C963255                 push    4
7C963257                 mov     ebx, [ebp+arg_0]
7C96325A                 push    ebx     ;DEBUG_BUFFER
7C96325B                 call    _RtlpCommitQueryDebugInfo
7C963260                 mov     edi, eax
7C963262                 mov     [ebp+var_48], edi
7C963265                 test    edi, edi
7C963267                 jnz     short loc_7C963270
7C963269                 mov     eax, C0000017h ;EAX = STATUS_NO_MEMORY
7C96326E                 jmp     short loc_7C9632CD
7C963270                 mov     [ebx+38h], edi
7C963273                 call    _RtlpAcquireHeapListLock
7C963278                 and     [ebp+ms_exc.disabled], 0
7C96327C                 push    ebx

7C96327D                 push    offset _RtlpQueryProcessEnumHeapsRoutine 7C963282                 call    _RtlEnumProcessHeaps
7C963287                 mov     [ebp+var_20], eax
7C96328A                 test    eax, eax
7C96328C                 jl      loc_7C963422
7C963292                 test    byte ptr [ebx+20h], 8
7C963296                 jz      loc_7C963426
7C96329C                 mov     esi, _RtlpGlobalTagHeap
7C9632A2                 mov     [ebp+var_28], esi
7C9632A5                 cmp     dword ptr [esi+3Ch], 0
7C9632A9                 jz      short loc_7C9632E5
7C9632AB                 push    40h
7C9632AD                 push    ebx
7C9632AE                 call    _RtlpCommitQueryDebugInfo
7C9632B3                 mov     [ebp+var_4C], eax
7C9632B6                 test    eax, eax
7C9632B8                 jnz     short loc_7C9632D5
7C9632BA                 mov     [ebp+var_20], 0C0000017h
7C9632C1                 or      [ebp+ms_exc.disabled], 0FFFFFFFFh
7C9632C5                 call    sub_7C9636FE ; RtlpReleaseHeapListLoc
7C9632CA                 mov     eax, [ebp+var_20]
7C9632CD                 call    __SEH_epilog
7C9632D2                 retn    4

RtlQueryProcessHeapInformation essentially fills DEBUG_BUFFER by taking informations from heap blocks. RtlpCommitQueryDebugInfo prepairs buffer by calling NtAllocateVirtualMemory, if RtlpCommitQueryDebugInfo fails the error
(0xC0000017 – STATUS_NO_MEMORY with consequent corrupted DEBUG_BUFFER) propagates throughout  RtlQueryProcessHeapInformation and finally (if used) RtlQueryProcessDebugInformation. So if you use these functions it’s a best practice to implement a check for this error, especially if you are working in applications that walks heap. Finally here the check anti-dbg.

void QueryProcessHeapMethod(void)

buffer = RtlCreateQueryDebugBuffer(0,FALSE);


if (buffer->RemoteSectionBase == (PVOID) 0x50000062)
MessageBoxA(NULL,”Not Debugged”,”Warning”,MB_OK);


extern “C”

The presence of RemoteXxx members is given to the fact that we need to avoid ther risk of a DeadLock that could happen when RtlQueryProcessDebugInformation request a remote information and consequently the debugger receives the thread start routine.

See you to the next post.. 🙂