The Essay
The following paper will uncover some interesting undocumented
functions relative to Windows Heap Enumeration.
RtlCreateQueryDebugBuffer
RtlQueryProcessDebugInformation
RtlDestroyQueryDebugBuffer
Here the prototypes for each function:
PDEBUG_BUFFER
NTAPI
RtlCreateQueryDebugBuffer(
IN ULONG Size,
IN BOOLEAN EventPair);
NTSTATUS
NTAPI
RtlQueryProcessDebugInformation(
IN ULONG ProcessId,
IN ULONG DebugInfoClassMask,
IN OUT PDEBUG_BUFFER DebugBuffer);
NTSTATUS
NTAPI
RtlDestroyQueryDebugBuffer(
IN PDEBUG_BUFFER DebugBuffer);
RtlCreateQueryDebugBuffer allocates buffer for storing heap data in case of success, it returns pointer to allocated debug buffer. Here the declaration of debug buffer:
typedef struct _DEBUG_BUFFER {
HANDLE SectionHandle;
PVOID SectionBase;
PVOID RemoteSectionBase;
ULONG SectionBaseDelta;
HANDLE EventPairHandle;
ULONG Unknown[2];
HANDLE RemoteThreadHandle;
ULONG InfoClassMask;
ULONG SizeOfInfo;
ULONG AllocatedSize;
ULONG SectionSize;
PVOID ModuleInformation;
PVOID BackTraceInformation;
PVOID HeapInformation;
PVOID LockInformation;
PVOID Reserved[8];
} DEBUG_BUFFER, *PDEBUG_BUFFER;
After the buffer allocation we can call the Heap Process Debug Information, we can call RtlQueryProcessDebugInformation to load all heap blocks of the process. This function loads entire heap nodes and corresponding heap blocks of the process. Debug Buffer contains the pointer to heap information structure at offset 0x38. First parameter of this heap structure is node count and after that it contains array of DEBUG_HEAP_INFORMATION structure which represent each heap node.
typedef struct _DEBUG_HEAP_INFORMATION
{
ULONG Base; // 0x00
ULONG Flags; // 0x04
USHORT Granularity; // 0x08
USHORT Unknown; // 0x0A
ULONG Allocated; // 0x0C
ULONG Committed; // 0x10
ULONG TagCount; // 0x14
ULONG BlockCount; // 0x18
ULONG Reserved[7]; // 0x1C
PVOID Tags; // 0x38
PVOID Blocks; // 0x3C Heap block pointer for this node.
} DEBUG_HEAP_INFORMATION, *PDEBUG_HEAP_INFORMATION;
As you should know a debugged process presents different flags respect a not debugged one, here the sample anti-dbg code:
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <Psapi.h>
#include “defs.h”
#pragma comment(lib,”ntdll.lib”)
#pragma comment(lib,”psapi.lib”)
void QueryDbgBufferMethod(void)
{
PDEBUG_BUFFER buffer;
NTSTATUS ntStatus;
buffer = RtlCreateQueryDebugBuffer(0,FALSE);
ntStatus = RtlQueryProcessDebugInformation(GetCurrentProcessId(),
PDI_HEAPS|PDI_HEAP_BLOCKS,
buffer);
PDEBUG_HEAP_INFORMATION heapInfo = PDEBUG_HEAP_INFORMATION(PULONG(buffer->HeapInformation) + 1);
if (heapInfo->Flags == 0x50000062)
MessageBoxA(NULL,”Debugged”,”Warning”,MB_OK);
else
MessageBoxA(NULL,”Not Debugged”,”Warning”,MB_OK);
RtlDestroyQueryDebugBuffer(buffer);
}
and here ‘defs.h‘
typedef LONG NTSTATUS;
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
typedef struct _DEBUG_BUFFER {
HANDLE SectionHandle;
PVOID SectionBase;
PVOID RemoteSectionBase;
ULONG SectionBaseDelta;
HANDLE EventPairHandle;
ULONG Unknown[2];
HANDLE RemoteThreadHandle;
ULONG InfoClassMask;
ULONG SizeOfInfo;
ULONG AllocatedSize;
ULONG SectionSize;
PVOID ModuleInformation;
PVOID BackTraceInformation;
PVOID HeapInformation;
PVOID LockInformation;
PVOID Reserved[8];
} DEBUG_BUFFER, *PDEBUG_BUFFER;
typedef struct _DEBUG_HEAP_INFORMATION
{
ULONG Base; // 0x00
ULONG Flags; // 0x04
USHORT Granularity; // 0x08
USHORT Unknown; // 0x0A
ULONG Allocated; // 0x0C
ULONG Committed; // 0x10
ULONG TagCount; // 0x14
ULONG BlockCount; // 0x18
ULONG Reserved[7]; // 0x1C
PVOID Tags; // 0x38
PVOID Blocks; // 0x3C
} DEBUG_HEAP_INFORMATION, *PDEBUG_HEAP_INFORMATION;
// RtlQueryProcessDebugInformation.DebugInfoClassMask constants
#define PDI_MODULES 0x01
#define PDI_BACKTRACE 0x02
#define PDI_HEAPS 0x04
#define PDI_HEAP_TAGS 0x08
#define PDI_HEAP_BLOCKS 0x10
#define PDI_LOCKS 0x20
extern “C”
__declspec(dllimport)
NTSTATUS
__stdcall
RtlQueryProcessDebugInformation(
IN ULONG ProcessId,
IN ULONG DebugInfoClassMask,
IN OUT PDEBUG_BUFFER DebugBuffer);
extern “C”
__declspec(dllimport)
PDEBUG_BUFFER
__stdcall
RtlCreateQueryDebugBuffer(
IN ULONG Size,
IN BOOLEAN EventPair);
extern “C”
__declspec(dllimport)
NTSTATUS
__stdcall
RtlDestroyQueryDebugBuffer(
IN PDEBUG_BUFFER DebugBuffer);
See you to the next post.. 🙂