Skip to content

Instantly share code, notes, and snippets.

@akkuman
Forked from reinsteam/enum_heaps.c
Created September 16, 2021 15:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akkuman/cc552d6575ce73dafd2cfa4d417d606b to your computer and use it in GitHub Desktop.
Save akkuman/cc552d6575ce73dafd2cfa4d417d606b to your computer and use it in GitHub Desktop.
Sample code of heap enumeration without using Tool Help functions <tlhelp32.h>
#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
#pragma warning (push)
/* 4820: '<struct-name>' : 'n' bytes padding added after data member '<member-name>'*/
# pragma warning (disable : 4820)
# include <windows.h>
# include <stdio.h>
# include <stdlib.h>
#pragma warning (pop)
typedef unsigned int u32;
/*--------------------------------------------------------------------------------------------------------------------*/
#define DYNAMIC_IMPORTED_FUNC(rval, func, ...)\
typedef rval (WINAPI *tpfn_##func)(__VA_ARGS__); \
static tpfn_##func pfn_##func = 0;
#define DYNAMIC_SYSTEM_LOAD(lib, func)\
( ( pfn_##func = (tpfn_##func)dynamic_system_func_load(lib, #func) ) != 0 )
#define CALL(func, ...)\
pfn_##func(__VA_ARGS__)
/*--------------------------------------------------------------------------------------------------------------------*/
#ifdef __cplusplus
# define INLINE __forceinline
#else
# define INLINE __inline
#endif
/* To prevent MSVC warning 4820 when compiling for x64 */
#ifdef _WIN64
# define EXPLICIT_PADDING(n, size) BYTE padding_##n[size]
#else
# define EXPLICIT_PADDING(n, size)
#endif
/*--------------------------------------------------------------------------------------------------------------------*/
static INLINE FARPROC dynamic_system_func_load(const char * module, const char * func)
{
void * handle = GetModuleHandleA(module);
if (handle == 0)
{
handle = LoadLibraryA(module);
}
if (handle != 0)
{
return GetProcAddress(handle, func);
}
return 0;
}
/*----------------------------------------------------------------------------------------------------------------------
/* Helper structures to query debug heap info
/*--------------------------------------------------------------------------------------------------------------------*/
typedef struct _RTL_HEAP_TAG
{
ULONG NumberOfAllocations;
ULONG NumberOfFrees;
SIZE_T BytesAllocated;
USHORT TagIndex;
USHORT CreatorBackTraceIndex;
WCHAR TagName[24];
EXPLICIT_PADDING(0, 4);
} RTL_HEAP_TAG, *PRTL_HEAP_TAG;
typedef struct _RTL_HEAP_ENTRY
{
SIZE_T Size;
USHORT Flags;
USHORT AllocatorBackTraceIndex;
EXPLICIT_PADDING(0, 4);
union
{
struct
{
SIZE_T Settable;
ULONG Tag;
EXPLICIT_PADDING(1, 4);
} s1;
struct
{
SIZE_T CommitedSize;
PVOID FirstBlock;
} s2;
} u;
} RTL_HEAP_ENTRY, *PRTL_HEAP_ENTRY;
typedef struct _RTL_DEBUG_HEAP_INFORMATION
{
PVOID BaseAddress;
ULONG Flags;
USHORT EntryOverhead;
USHORT CreatorBackTraceIndex;
SIZE_T BytesAllocated;
SIZE_T BytesCommited;
ULONG NumberOfTags;
ULONG NumberOfEntries;
ULONG NumberOfPseudoTags;
ULONG PseudoTagGranularity;
ULONG Reserved[5];
EXPLICIT_PADDING(0, 4);
PRTL_HEAP_TAG Tags;
PRTL_HEAP_ENTRY Entries;
} RTL_DEBUG_HEAP_INFORMATION, *PRTL_DEBUG_HEAP_INFORMATION;
typedef struct _RTL_PROCESS_HEAPS
{
ULONG NumberOfHeaps;
EXPLICIT_PADDING(0, 4);
RTL_DEBUG_HEAP_INFORMATION Heaps[1];
} RTL_PROCESS_HEAPS, *PRTL_PROCESS_HEAPS;
typedef struct _RTL_DEBUG_INFORMATION
{
HANDLE SectionHandleClient;
PVOID ViewBaseClient;
PVOID ViewBaseTarget;
ULONG_PTR ViewBaseDelta;
HANDLE EventPairClient;
HANDLE EventPairTarget;
HANDLE TargetProcessId;
HANDLE TargetThreadHandle;
ULONG Flags;
EXPLICIT_PADDING(0, 4);
SIZE_T OffsetFree;
SIZE_T CommitSize;
SIZE_T ViewSize;
union
{
PVOID Modules;
PVOID ModulesEx;
};
PVOID BackTraces;
struct _RTL_PROCESS_HEAPS *Heaps;
PVOID Locks;
PVOID SpecificHeap;
HANDLE TargetProcessHandle;
PVOID VerifierOptions;
PVOID ProcessHeap;
HANDLE CriticalSectionHandle;
HANDLE CriticalSectionOwnerThread;
PVOID Reserved[4];
} RTL_DEBUG_INFORMATION, *PRTL_DEBUG_INFORMATION;
typedef struct _RTL_HEAP_USAGE_ENTRY
{
struct _RTL_HEAP_USAGE_ENTRY * Next;
PVOID Address;
SIZE_T Size;
USHORT AllocatorBackTraceIndex;
USHORT TagIndex;
EXPLICIT_PADDING(0, 4);
} RTL_HEAP_USAGE_ENTRY, *PRTL_HEAP_USAGE_ENTRY;
typedef enum DebugInfoClassMask
{
PDI_MODULES = 0x01,
PDI_BACKTRACE = 0x02,
PDI_HEAPS = 0x04,
PDI_HEAP_TAGS = 0x08,
PDI_HEAP_BLOCKS = 0x10,
PDI_LOCKS = 0x20
} DebugInfoClassMask;
DYNAMIC_IMPORTED_FUNC(PRTL_DEBUG_INFORMATION, RtlCreateQueryDebugBuffer, u32, u32);
DYNAMIC_IMPORTED_FUNC(u32, RtlDestroyQueryDebugBuffer, PRTL_DEBUG_INFORMATION);
DYNAMIC_IMPORTED_FUNC(u32, RtlQueryProcessDebugInformation, u32, u32, PRTL_DEBUG_INFORMATION);
int main(int argc, char ** argv)
{
u32 proc_id = 0;
if (argc > 1)
{
proc_id = atoi(argv[1]);
}
u32 result = 1;
result = result && DYNAMIC_SYSTEM_LOAD("ntdll.dll", RtlCreateQueryDebugBuffer);
result = result && DYNAMIC_SYSTEM_LOAD("ntdll.dll", RtlDestroyQueryDebugBuffer);
result = result && DYNAMIC_SYSTEM_LOAD("ntdll.dll", RtlQueryProcessDebugInformation);
printf("---- Heap stats for PID : %u ---- \n", proc_id);
if (result)
{
PRTL_DEBUG_INFORMATION buffer = CALL(RtlCreateQueryDebugBuffer, 0, 0);
if (buffer)
{
u32 i;
CALL(RtlQueryProcessDebugInformation, proc_id, PDI_HEAPS, buffer);
PRTL_PROCESS_HEAPS prtl_heaps = buffer->Heaps;
if (prtl_heaps != 0)
{
printf("---------------------------------------------------------------------------\n");
printf("Heap # | Address | Commited | Allocated | Flags | \n");
printf("---------------------------------------------------------------------------\n");
for (i = 0; i < prtl_heaps->NumberOfHeaps; ++i)
{
PRTL_DEBUG_HEAP_INFORMATION heap = prtl_heaps->Heaps + i;
printf("%6u | 0x%p | %9u bytes | %9u bytes | %#06x |\n", i, heap->BaseAddress, (u32)heap->BytesCommited, (u32)heap->BytesAllocated, heap->Flags);
}
}
CALL(RtlDestroyQueryDebugBuffer, buffer);
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment