You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2348 lines
70 KiB
2348 lines
70 KiB
/*++
|
|
|
|
Copyright (c) 2000-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
heaputil.cpp
|
|
|
|
Revision History:
|
|
|
|
ivanbrug oct 2000 created
|
|
|
|
--*/
|
|
|
|
#include <wmiexts.h>
|
|
#include <utilfun.h>
|
|
#include <malloc.h>
|
|
|
|
/*
|
|
#if defined(_WIN64)
|
|
#define HEAP_GRANULARITY_SHIFT 4 // Log2( HEAP_GRANULARITY )
|
|
#else
|
|
#define HEAP_GRANULARITY_SHIFT 3 // Log2( HEAP_GRANULARITY )
|
|
#endif
|
|
|
|
#define HEAP_ENTRY_BUSY 0x01
|
|
#define HEAP_ENTRY_EXTRA_PRESENT 0x02
|
|
#define HEAP_ENTRY_FILL_PATTERN 0x04
|
|
#define HEAP_ENTRY_VIRTUAL_ALLOC 0x08
|
|
#define HEAP_ENTRY_LAST_ENTRY 0x10
|
|
#define HEAP_ENTRY_SETTABLE_FLAG1 0x20
|
|
#define HEAP_ENTRY_SETTABLE_FLAG2 0x40
|
|
#define HEAP_ENTRY_SETTABLE_FLAG3 0x80
|
|
#define HEAP_ENTRY_SETTABLE_FLAGS 0xE0
|
|
|
|
|
|
typedef struct _HEAP_ENTRY {
|
|
|
|
//
|
|
// This field gives the size of the current block in allocation
|
|
// granularity units. (i.e. Size << HEAP_GRANULARITY_SHIFT
|
|
// equals the size in bytes).
|
|
//
|
|
// Except if this is part of a virtual alloc block then this
|
|
// value is the difference between the commit size in the virtual
|
|
// alloc entry and the what the user asked for.
|
|
//
|
|
|
|
USHORT Size;
|
|
|
|
//
|
|
// This field gives the size of the previous block in allocation
|
|
// granularity units. (i.e. PreviousSize << HEAP_GRANULARITY_SHIFT
|
|
// equals the size of the previous block in bytes).
|
|
//
|
|
|
|
USHORT PreviousSize;
|
|
|
|
//
|
|
// This field contains the index into the segment that controls
|
|
// the memory for this block.
|
|
//
|
|
|
|
UCHAR SegmentIndex;
|
|
|
|
//
|
|
// This field contains various flag bits associated with this block.
|
|
// Currently these are:
|
|
//
|
|
// 0x01 - HEAP_ENTRY_BUSY
|
|
// 0x02 - HEAP_ENTRY_EXTRA_PRESENT
|
|
// 0x04 - HEAP_ENTRY_FILL_PATTERN
|
|
// 0x08 - HEAP_ENTRY_VIRTUAL_ALLOC
|
|
// 0x10 - HEAP_ENTRY_LAST_ENTRY
|
|
// 0x20 - HEAP_ENTRY_SETTABLE_FLAG1
|
|
// 0x40 - HEAP_ENTRY_SETTABLE_FLAG2
|
|
// 0x80 - HEAP_ENTRY_SETTABLE_FLAG3
|
|
//
|
|
|
|
UCHAR Flags;
|
|
|
|
//
|
|
// This field contains the number of unused bytes at the end of this
|
|
// block that were not actually allocated. Used to compute exact
|
|
// size requested prior to rounding requested size to allocation
|
|
// granularity. Also used for tail checking purposes.
|
|
//
|
|
|
|
UCHAR UnusedBytes;
|
|
|
|
//
|
|
// Small (8 bit) tag indexes can go here.
|
|
//
|
|
|
|
UCHAR SmallTagIndex;
|
|
|
|
#if defined(_WIN64)
|
|
ULONGLONG Reserved1;
|
|
#endif
|
|
|
|
} HEAP_ENTRY, *PHEAP_ENTRY;
|
|
|
|
typedef struct _HEAP_UNCOMMMTTED_RANGE {
|
|
struct _HEAP_UNCOMMMTTED_RANGE *Next;
|
|
ULONG_PTR Address;
|
|
SIZE_T Size;
|
|
ULONG filler;
|
|
} HEAP_UNCOMMMTTED_RANGE, *PHEAP_UNCOMMMTTED_RANGE;
|
|
|
|
|
|
typedef struct _HEAP_SEGMENT {
|
|
HEAP_ENTRY Entry;
|
|
|
|
ULONG Signature;
|
|
ULONG Flags;
|
|
struct _HEAP *Heap;
|
|
SIZE_T LargestUnCommittedRange;
|
|
|
|
PVOID BaseAddress;
|
|
ULONG NumberOfPages;
|
|
PHEAP_ENTRY FirstEntry;
|
|
PHEAP_ENTRY LastValidEntry;
|
|
|
|
ULONG NumberOfUnCommittedPages;
|
|
ULONG NumberOfUnCommittedRanges;
|
|
PHEAP_UNCOMMMTTED_RANGE UnCommittedRanges;
|
|
USHORT AllocatorBackTraceIndex;
|
|
USHORT Reserved;
|
|
PHEAP_ENTRY LastEntryInSegment;
|
|
} HEAP_SEGMENT, *PHEAP_SEGMENT;
|
|
*/
|
|
|
|
|
|
typedef ULONG_PTR ERESOURCE_THREAD;
|
|
typedef ERESOURCE_THREAD *PERESOURCE_THREAD;
|
|
|
|
typedef struct _OWNER_ENTRY {
|
|
ERESOURCE_THREAD OwnerThread;
|
|
union {
|
|
LONG OwnerCount;
|
|
ULONG TableSize;
|
|
};
|
|
|
|
} OWNER_ENTRY, *POWNER_ENTRY;
|
|
|
|
|
|
typedef void * PKSEMAPHORE;
|
|
typedef void * PKEVENT;
|
|
|
|
typedef struct _ERESOURCE {
|
|
LIST_ENTRY SystemResourcesList;
|
|
POWNER_ENTRY OwnerTable;
|
|
SHORT ActiveCount;
|
|
USHORT Flag;
|
|
PKSEMAPHORE SharedWaiters;
|
|
PKEVENT ExclusiveWaiters;
|
|
OWNER_ENTRY OwnerThreads[2];
|
|
ULONG ContentionCount;
|
|
USHORT NumberOfSharedWaiters;
|
|
USHORT NumberOfExclusiveWaiters;
|
|
union {
|
|
PVOID Address;
|
|
ULONG_PTR CreatorBackTraceIndex;
|
|
};
|
|
|
|
KSPIN_LOCK SpinLock;
|
|
} ERESOURCE, *PERESOURCE;
|
|
|
|
//typedef void * PRTL_TRACE_BLOCK;
|
|
|
|
#include "heap.h"
|
|
#include "heappagi.h"
|
|
|
|
#define HE_VERBOSITY_FLAGS 1
|
|
#define HE_VERBOSITY_NUMERIC 2
|
|
#define HE_VERBOSITY_VTABLE 4
|
|
|
|
#if defined(_X86_)
|
|
#ifndef PAGE_SIZE
|
|
#define PAGE_SIZE 0x1000
|
|
#endif
|
|
#define USER_ALIGNMENT 8
|
|
|
|
#elif defined(_IA64_)
|
|
#ifndef PAGE_SIZE
|
|
#define PAGE_SIZE 0x2000
|
|
#endif
|
|
#define USER_ALIGNMENT 16
|
|
|
|
#elif defined(_AMD64_)
|
|
#ifndef PAGE_SIZE
|
|
#define PAGE_SIZE 0x1000
|
|
#endif
|
|
#define USER_ALIGNMENT 16
|
|
|
|
#else
|
|
#error // platform not defined
|
|
#endif
|
|
|
|
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
|
|
typedef DWORD (__stdcall * fnCallBack)(ULONG_PTR pParam1,ULONG_PTR pParam2);
|
|
|
|
DWORD g_UsedInHeap = 0;
|
|
|
|
void
|
|
PrintHeapEntry(HEAP_ENTRY * pEntry,void * pAddr)
|
|
{
|
|
|
|
BYTE varEntry[sizeof(HEAP_ENTRY)+2*sizeof(void *)];
|
|
LIST_ENTRY * pListEntry = (LIST_ENTRY *)((HEAP_ENTRY *)varEntry+1);
|
|
|
|
DWORD PrintSize = 0;
|
|
BOOL bIsPossiblePageHeap = FALSE;
|
|
if (pEntry->Flags & HEAP_ENTRY_BUSY)
|
|
{
|
|
// re-read the entry, to get if it's on the LookAside
|
|
if (ReadMemory((MEMORY_ADDRESS)pAddr,varEntry,sizeof(varEntry),NULL))
|
|
{
|
|
#ifdef WIN64
|
|
if (0xf0f0f0f0f0f0f0f0 == (ULONG_PTR)pListEntry->Blink )
|
|
#else
|
|
if (0xf0f0f0f0 == (ULONG_PTR)pListEntry->Blink )
|
|
#endif
|
|
{
|
|
PrintSize = 0xf7eef7ee;
|
|
}
|
|
else
|
|
{
|
|
PrintSize = (pEntry->Size<<HEAP_GRANULARITY_SHIFT)-pEntry->UnusedBytes;
|
|
g_UsedInHeap += PrintSize;
|
|
}
|
|
|
|
DWORD Sign = *((DWORD *)pListEntry);
|
|
//dprintf("Sign %08x\n",Sign);
|
|
if (0xabcdaaaa == Sign)
|
|
{
|
|
bIsPossiblePageHeap = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PrintSize = (pEntry->Size<<HEAP_GRANULARITY_SHIFT)-pEntry->UnusedBytes;
|
|
g_UsedInHeap += PrintSize;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PrintSize = 0xf7eef7ee;
|
|
}
|
|
|
|
dprintf(" %p: %04x . %04x [%02x] - (%x)\n",
|
|
pAddr,pEntry->Size,pEntry->PreviousSize,pEntry->Flags,PrintSize);
|
|
|
|
if (bIsPossiblePageHeap)
|
|
{
|
|
//dprintf("Possible %p\n",(MEMORY_ADDRESS)((BYTE*)pAddr+sizeof(HEAP_ENTRY)+sizeof(DPH_BLOCK_INFORMATION)));
|
|
GetVTable((MEMORY_ADDRESS)((BYTE*)pAddr+sizeof(HEAP_ENTRY)+sizeof(DPH_BLOCK_INFORMATION)));
|
|
}
|
|
else
|
|
GetVTable((MEMORY_ADDRESS)((BYTE*)pAddr+sizeof(HEAP_ENTRY)));
|
|
};
|
|
|
|
//
|
|
//
|
|
// print the HEAP_ENTRY structure
|
|
//
|
|
|
|
DECLARE_API(he) {
|
|
|
|
INIT_API();
|
|
|
|
DEFINE_CPP_VAR( HEAP_ENTRY, varHEAP_ENTRY);
|
|
HEAP_ENTRY * pEntry = GET_CPP_VAR_PTR( HEAP_ENTRY , varHEAP_ENTRY );
|
|
memset(pEntry,0xfe,sizeof(HEAP_ENTRY));
|
|
|
|
MEMORY_ADDRESS pByte = GetExpression(args);
|
|
|
|
if (pByte)
|
|
{
|
|
if (ReadMemory((MEMORY_ADDRESS)pByte,pEntry ,sizeof(HEAP_ENTRY),NULL))
|
|
{
|
|
PrintHeapEntry(pEntry,(void *)pByte);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pByte);
|
|
}
|
|
} else {
|
|
dprintf("invalid HEAP_ENTRY address %s\n",args);
|
|
}
|
|
}
|
|
|
|
//
|
|
// HEAP_ENTRY list
|
|
// finds the beginning of the "list" of HEAP_ENTRYs
|
|
//
|
|
|
|
DECLARE_API(heb) {
|
|
|
|
INIT_API();
|
|
|
|
MEMORY_ADDRESS pEntry = GetExpression(args);
|
|
|
|
if (pEntry){
|
|
HEAP_ENTRY HeapEntry;
|
|
ReadMemory((MEMORY_ADDRESS)pEntry,&HeapEntry,sizeof(HeapEntry),0);
|
|
|
|
PrintHeapEntry(&HeapEntry,(void *)pEntry);
|
|
|
|
while (HeapEntry.PreviousSize)
|
|
{
|
|
pEntry = (MEMORY_ADDRESS)((HEAP_ENTRY*)pEntry - HeapEntry.PreviousSize);
|
|
if (ReadMemory((MEMORY_ADDRESS)pEntry,&HeapEntry,sizeof(HeapEntry),0))
|
|
{
|
|
PrintHeapEntry(&HeapEntry,(void *)pEntry);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pEntry);
|
|
break;
|
|
}
|
|
|
|
if (CheckControlC())
|
|
break;
|
|
}
|
|
|
|
dprintf(" begin %08x\n",pEntry);
|
|
} else {
|
|
dprintf("invalid address %s\n",args);
|
|
};
|
|
|
|
}
|
|
|
|
//
|
|
//
|
|
// HEAP_ENTRY forward
|
|
//
|
|
//////////////////////////////////////////////////////
|
|
|
|
DECLARE_API(hef) {
|
|
|
|
INIT_API();
|
|
|
|
DWORD BeginNum=0;
|
|
|
|
MEMORY_ADDRESS pEntry = GetExpression(args);
|
|
|
|
if (pEntry)
|
|
{
|
|
HEAP_ENTRY HeapEntry;
|
|
ReadMemory((MEMORY_ADDRESS)pEntry,&HeapEntry,sizeof(HeapEntry),0);
|
|
|
|
PrintHeapEntry(&HeapEntry,(void *)pEntry);
|
|
|
|
while (!(HeapEntry.Flags & HEAP_ENTRY_LAST_ENTRY))
|
|
{
|
|
pEntry = (MEMORY_ADDRESS)((HEAP_ENTRY*)pEntry + HeapEntry.Size);
|
|
if (ReadMemory((MEMORY_ADDRESS)pEntry,&HeapEntry,sizeof(HeapEntry),0))
|
|
{
|
|
PrintHeapEntry(&HeapEntry,(void *)pEntry);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pEntry);
|
|
break;
|
|
}
|
|
|
|
if (CheckControlC())
|
|
break;
|
|
|
|
}
|
|
|
|
dprintf(" end %08x\n",pEntry);
|
|
} else {
|
|
dprintf("invalid address %s\n",args);
|
|
};
|
|
|
|
}
|
|
|
|
DWORD EnumEntries(HEAP_ENTRY * pEntry,DWORD * pSize,fnCallBack CallBack,ULONG_PTR Addr){
|
|
|
|
DWORD i=0;
|
|
HEAP_ENTRY HeapEntry;
|
|
DWORD Size=0;
|
|
ReadMemory((MEMORY_ADDRESS)pEntry,&HeapEntry,sizeof(HeapEntry),0);
|
|
|
|
if (CallBack)
|
|
{
|
|
CallBack((ULONG_PTR)pEntry,Addr);
|
|
}
|
|
else
|
|
{
|
|
PrintHeapEntry(&HeapEntry,pEntry);
|
|
};
|
|
|
|
while (!(HeapEntry.Flags & HEAP_ENTRY_LAST_ENTRY))
|
|
{
|
|
if (0 == HeapEntry.Size)
|
|
{
|
|
dprintf("HEAP_ENTRY %p with zero Size\n",pEntry);
|
|
break;
|
|
}
|
|
pEntry = (HEAP_ENTRY*)pEntry + HeapEntry.Size;
|
|
if (ReadMemory((MEMORY_ADDRESS)pEntry,&HeapEntry,sizeof(HeapEntry),0))
|
|
{
|
|
if (CallBack)
|
|
{
|
|
CallBack((ULONG_PTR)pEntry,Addr);
|
|
}
|
|
else
|
|
{
|
|
PrintHeapEntry(&HeapEntry,pEntry);
|
|
};
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pEntry);
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
Size += ((HeapEntry.Size<<HEAP_GRANULARITY_SHIFT)-HeapEntry.UnusedBytes);
|
|
|
|
if (CheckControlC())
|
|
break;
|
|
|
|
}
|
|
|
|
if (pSize){
|
|
*pSize = Size;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
void
|
|
PrintHEAP_SEGMENT(HEAP_SEGMENT * pSeg_OOP,
|
|
fnCallBack CallBack,
|
|
ULONG_PTR Addr)
|
|
{
|
|
DEFINE_CPP_VAR( HEAP_SEGMENT, varHEAP_SEGMENT);
|
|
HEAP_SEGMENT * pSeg = GET_CPP_VAR_PTR( HEAP_SEGMENT , varHEAP_SEGMENT );
|
|
|
|
BOOL bRet = ReadMemory((MEMORY_ADDRESS)pSeg_OOP,pSeg ,sizeof(HEAP_SEGMENT),0);
|
|
|
|
if (bRet)
|
|
{
|
|
if (!CallBack)
|
|
dprintf(" Flags %08x HEAP %p\n",pSeg->Flags,pSeg->Heap);
|
|
|
|
//SIZE_T LargestUnCommittedRange;
|
|
|
|
//PVOID BaseAddress;
|
|
//ULONG NumberOfPages;
|
|
//PHEAP_ENTRY FirstEntry;
|
|
//PHEAP_ENTRY LastValidEntry;
|
|
|
|
//ULONG NumberOfUnCommittedPages;
|
|
DWORD unComm = pSeg->NumberOfUnCommittedRanges;
|
|
//PHEAP_UNCOMMMTTED_RANGE UnCommittedRanges;
|
|
//USHORT AllocatorBackTraceIndex;
|
|
//USHORT Reserved;
|
|
//PHEAP_ENTRY LastEntryInSegment;
|
|
|
|
HEAP_UNCOMMMTTED_RANGE UncRange;
|
|
HEAP_UNCOMMMTTED_RANGE * pUncRange = pSeg->UnCommittedRanges;
|
|
HEAP_ENTRY ** pCommRange = (HEAP_ENTRY **)_alloca(sizeof(HEAP_ENTRY *)*(unComm+1));
|
|
|
|
DWORD Size=0;
|
|
DWORD Num=0;
|
|
|
|
pCommRange[0] = (HEAP_ENTRY *)pSeg->FirstEntry;
|
|
|
|
Num = EnumEntries(pCommRange[0],&Size,CallBack,Addr);
|
|
|
|
if (!CallBack)
|
|
dprintf(" - %p Size %p entries %d \n",pCommRange[0],Size,Num);
|
|
|
|
for (DWORD i=0; i<unComm; i++)
|
|
{
|
|
|
|
|
|
bRet = ReadMemory((MEMORY_ADDRESS)pUncRange,&UncRange,sizeof(UncRange),NULL);
|
|
if (bRet)
|
|
{
|
|
pUncRange = UncRange.Next;
|
|
pCommRange[1+i] = (HEAP_ENTRY *)(UncRange.Address + UncRange.Size);
|
|
|
|
if (NULL == pUncRange)
|
|
{
|
|
if ((ULONG_PTR)pCommRange[1+i] == (ULONG_PTR)pSeg->LastValidEntry)
|
|
break;
|
|
}
|
|
|
|
Num = EnumEntries(pCommRange[1+i],&Size,CallBack,Addr);
|
|
|
|
if (!CallBack)
|
|
dprintf(" - %p Size %p entries %d\n",pCommRange[1+i],Size,Num);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pUncRange);
|
|
}
|
|
}
|
|
} else {
|
|
dprintf("RM %p\n",pSeg_OOP);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
//
|
|
// Dump the HEAP_SEGMENT
|
|
//
|
|
|
|
DECLARE_API(hs) {
|
|
|
|
INIT_API();
|
|
|
|
MEMORY_ADDRESS pByte = 0;
|
|
pByte = GetExpression(args);
|
|
|
|
if (pByte)
|
|
{
|
|
PrintHEAP_SEGMENT((HEAP_SEGMENT *)pByte,NULL,NULL);
|
|
}
|
|
else
|
|
{
|
|
dprintf("invalid address %s\n",args);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Define heap lookaside list allocation functions.
|
|
//
|
|
|
|
struct SLIST_HEADER_
|
|
{
|
|
SLIST_HEADER_ * Next;
|
|
ULONG_PTR Align;
|
|
};
|
|
|
|
typedef struct _HEAP_LOOKASIDE {
|
|
SLIST_HEADER_ ListHead;
|
|
|
|
USHORT Depth;
|
|
USHORT MaximumDepth;
|
|
|
|
ULONG TotalAllocates;
|
|
ULONG AllocateMisses;
|
|
ULONG TotalFrees;
|
|
ULONG FreeMisses;
|
|
|
|
ULONG LastTotalAllocates;
|
|
ULONG LastAllocateMisses;
|
|
|
|
ULONG Counters[2];
|
|
#ifdef _IA64_
|
|
DWORD Pad[3];
|
|
#else
|
|
DWORD Pad;
|
|
#endif
|
|
|
|
} HEAP_LOOKASIDE, *PHEAP_LOOKASIDE;
|
|
|
|
|
|
|
|
void Dump_LookAside(ULONG_PTR pLookAside_OOP)
|
|
{
|
|
DWORD dwSize = sizeof(HEAP_LOOKASIDE) * HEAP_MAXIMUM_FREELISTS ;
|
|
|
|
BYTE * pData = new BYTE[dwSize];
|
|
|
|
if (pData)
|
|
{
|
|
if (ReadMemory(pLookAside_OOP,pData,dwSize,NULL))
|
|
{
|
|
DWORD i;
|
|
HEAP_LOOKASIDE * pLookasideArray = (HEAP_LOOKASIDE *)pData;
|
|
|
|
|
|
BYTE varHEAP_ENTRY[sizeof(HEAP_ENTRY)+sizeof(SLIST_HEADER_)];
|
|
HEAP_ENTRY * pEntry = GET_CPP_VAR_PTR( HEAP_ENTRY , varHEAP_ENTRY );
|
|
SLIST_HEADER_ * pSListEntry = (SLIST_HEADER_ *)(pEntry+1);
|
|
|
|
char Fill[8];
|
|
memset(Fill,0xf0,8);
|
|
|
|
for(i=0;i<HEAP_MAXIMUM_FREELISTS;i++)
|
|
{
|
|
ULONG_PTR pTmp;
|
|
SLIST_HEADER_ * pHead_OOP = pLookasideArray[i].ListHead.Next;
|
|
#ifdef _IA64_
|
|
pTmp = (ULONG_PTR)pHead_OOP;
|
|
pTmp>>=25;
|
|
pTmp<<=4;
|
|
pHead_OOP = (SLIST_HEADER_ *)pTmp;
|
|
#endif
|
|
|
|
dprintf(" LookAside[%x] - %p Depth %x Maximum %x\n",
|
|
i,
|
|
pHead_OOP, //pLookasideArray[i].ListHead.Next,
|
|
pLookasideArray[i].Depth,
|
|
pLookasideArray[i].MaximumDepth);
|
|
|
|
//dprintf("size %x %p\n",sizeof(HEAP_LOOKASIDE),pHead_OOP);
|
|
USHORT Depth = 0;
|
|
while(pHead_OOP)
|
|
{
|
|
HEAP_ENTRY * pEntry_OOP = (HEAP_ENTRY *)pHead_OOP-1;
|
|
|
|
if (ReadMemory((MEMORY_ADDRESS)pEntry_OOP,pEntry,sizeof(varHEAP_ENTRY),NULL))
|
|
{
|
|
ULONG_PTR pToWrite = sizeof(ULONG_PTR) + (ULONG_PTR)pHead_OOP;
|
|
WriteMemory(pToWrite,Fill,sizeof(ULONG_PTR),0);
|
|
|
|
pHead_OOP = pSListEntry->Next;
|
|
PrintHeapEntry(pEntry,pEntry_OOP);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %d\n",GetLastError());
|
|
break;
|
|
}
|
|
Depth++;
|
|
if (Depth > pLookasideArray[i].MaximumDepth)
|
|
{
|
|
dprintf("MaximumDepth exceeded\n");
|
|
break;
|
|
|
|
}
|
|
};
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %d\n",GetLastError());
|
|
}
|
|
delete [] pData;
|
|
}
|
|
|
|
}
|
|
|
|
#define HEAP_FRONT_LOOKASIDE 1
|
|
#define HEAP_FRONT_LOWFRAGHEAP 2
|
|
|
|
//
|
|
// prepares the Lookaside list for dump
|
|
//
|
|
DECLARE_API(lhp)
|
|
{
|
|
INIT_API();
|
|
|
|
DEFINE_CPP_VAR( HEAP, varHEAP);
|
|
HEAP * pHeap = GET_CPP_VAR_PTR( HEAP , varHEAP );
|
|
MEMORY_ADDRESS pByte = GetExpression(args);
|
|
DWORD i;
|
|
|
|
if (pByte)
|
|
{
|
|
if (ReadMemory(pByte,pHeap ,sizeof(HEAP),NULL))
|
|
{
|
|
|
|
//dprintf("----- LookAside %p\n",pHeap->FrontEndHeap);
|
|
if (1 == pHeap->FrontEndHeapType)
|
|
Dump_LookAside((ULONG_PTR)pHeap->FrontEndHeap);
|
|
else
|
|
dprintf("_HEAP FrontEndHeapType %d not recognized \n",pHeap->FrontEndHeapType);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p %d\n",pByte,GetLastError());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("invalid heap address %s\n",args);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Low fragmentation heap data structures
|
|
//
|
|
|
|
typedef struct _BLOCK_ENTRY : HEAP_ENTRY
|
|
{
|
|
USHORT LinkOffset;
|
|
USHORT Reserved2;
|
|
|
|
} BLOCK_ENTRY, *PBLOCK_ENTRY;
|
|
|
|
|
|
|
|
typedef struct _INTERLOCK_SEQ {
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
USHORT Depth;
|
|
USHORT FreeEntryOffset;
|
|
};
|
|
volatile ULONG OffsetAndDepth;
|
|
};
|
|
volatile ULONG Sequence;
|
|
};
|
|
|
|
volatile LONGLONG Exchg;
|
|
};
|
|
|
|
} INTERLOCK_SEQ, *PINTERLOCK_SEQ;
|
|
|
|
struct _HEAP_USERDATA_HEADER;
|
|
|
|
typedef struct _HEAP_SUBSEGMENT {
|
|
|
|
PVOID Bucket;
|
|
|
|
volatile struct _HEAP_USERDATA_HEADER * UserBlocks;
|
|
|
|
INTERLOCK_SEQ AggregateExchg;
|
|
|
|
union {
|
|
|
|
struct {
|
|
USHORT BlockSize;
|
|
USHORT FreeThreshold;
|
|
USHORT BlockCount;
|
|
UCHAR SizeIndex;
|
|
UCHAR AffinityIndex;
|
|
};
|
|
|
|
ULONG Alignment[2];
|
|
};
|
|
|
|
SINGLE_LIST_ENTRY SFreeListEntry;
|
|
volatile ULONG Lock;
|
|
|
|
} HEAP_SUBSEGMENT, *PHEAP_SUBSEGMENT;
|
|
|
|
typedef struct _HEAP_USERDATA_HEADER {
|
|
|
|
union {
|
|
|
|
SINGLE_LIST_ENTRY SFreeListEntry;
|
|
PHEAP_SUBSEGMENT SubSegment;
|
|
};
|
|
|
|
PVOID HeapHandle;
|
|
|
|
ULONG_PTR SizeIndex;
|
|
ULONG_PTR Signature;
|
|
|
|
} HEAP_USERDATA_HEADER, *PHEAP_USERDATA_HEADER;
|
|
|
|
|
|
typedef union _HEAP_BUCKET_COUNTERS{
|
|
|
|
struct {
|
|
|
|
volatile ULONG TotalBlocks;
|
|
volatile ULONG SubSegmentCounts;
|
|
};
|
|
|
|
volatile LONGLONG Aggregate64;
|
|
|
|
} HEAP_BUCKET_COUNTERS, *PHEAP_BUCKET_COUNTERS;
|
|
|
|
//
|
|
// The HEAP_BUCKET structure handles same size allocations
|
|
//
|
|
|
|
typedef struct _HEAP_BUCKET {
|
|
|
|
HEAP_BUCKET_COUNTERS Counters;
|
|
|
|
USHORT BlockUnits;
|
|
UCHAR SizeIndex;
|
|
UCHAR UseAffinity;
|
|
|
|
LONG Conversions;
|
|
|
|
} HEAP_BUCKET, *PHEAP_BUCKET;
|
|
|
|
//
|
|
// LFH heap uses zones to allocate sub-segment descriptors. This will preallocate
|
|
// a large block and then for each individual sub-segment request will move the
|
|
// water mark pointer with a non-blocking operation
|
|
//
|
|
|
|
typedef struct _LFH_BLOCK_ZONE {
|
|
|
|
LIST_ENTRY ListEntry;
|
|
PVOID FreePointer;
|
|
PVOID Limit;
|
|
|
|
} LFH_BLOCK_ZONE, *PLFH_BLOCK_ZONE;
|
|
|
|
#define FREE_CACHE_SIZE 16
|
|
|
|
typedef struct _HEAP_LOCAL_SEGMENT_INFO {
|
|
|
|
PHEAP_SUBSEGMENT Hint;
|
|
PHEAP_SUBSEGMENT ActiveSubsegment;
|
|
|
|
PHEAP_SUBSEGMENT CachedItems[ FREE_CACHE_SIZE ];
|
|
SLIST_HEADER SListHeader;
|
|
|
|
SIZE_T BusyEntries;
|
|
SIZE_T LastUsed;
|
|
|
|
} HEAP_LOCAL_SEGMENT_INFO, *PHEAP_LOCAL_SEGMENT_INFO;
|
|
|
|
#define HEAP_BUCKETS_COUNT 128
|
|
|
|
typedef struct _HEAP_LOCAL_DATA {
|
|
|
|
//
|
|
// We reserve the 128 bytes below to avoid sharing memory
|
|
// into the same cacheline on MP machines
|
|
//
|
|
|
|
UCHAR Reserved[128];
|
|
|
|
volatile PLFH_BLOCK_ZONE CrtZone;
|
|
struct _LFH_HEAP * LowFragHeap;
|
|
|
|
HEAP_LOCAL_SEGMENT_INFO SegmentInfo[HEAP_BUCKETS_COUNT];
|
|
SLIST_HEADER DeletedSubSegments;
|
|
|
|
ULONG Affinity;
|
|
ULONG Reserved1;
|
|
|
|
} HEAP_LOCAL_DATA, *PHEAP_LOCAL_DATA;
|
|
|
|
//
|
|
// Fixed size large block cache data structures & definitions
|
|
// This holds in S-Lists the blocks that can be free, but it
|
|
// delay the free until no other thread is doing a heap operation
|
|
// This helps reducing the contention on the heap lock,
|
|
// improve the scalability with a relatively low memory footprint
|
|
//
|
|
|
|
#define HEAP_LOWEST_USER_SIZE_INDEX 7
|
|
#define HEAP_HIGHEST_USER_SIZE_INDEX 18
|
|
|
|
#define HEAP_USER_ENTRIES (HEAP_HIGHEST_USER_SIZE_INDEX - HEAP_LOWEST_USER_SIZE_INDEX + 1)
|
|
|
|
typedef struct _USER_MEMORY_CACHE {
|
|
|
|
SLIST_HEADER UserBlocks[ HEAP_USER_ENTRIES ];
|
|
|
|
ULONG FreeBlocks;
|
|
ULONG Sequence;
|
|
|
|
ULONG MinDepth[ HEAP_USER_ENTRIES ];
|
|
ULONG AvailableBlocks[ HEAP_USER_ENTRIES ];
|
|
|
|
} USER_MEMORY_CACHE, *PUSER_MEMORY_CACHE;
|
|
|
|
typedef struct _LFH_HEAP {
|
|
|
|
RTL_CRITICAL_SECTION Lock;
|
|
|
|
LIST_ENTRY SubSegmentZones;
|
|
SIZE_T ZoneBlockSize;
|
|
HANDLE Heap;
|
|
LONG Conversions;
|
|
LONG ConvertedSpace;
|
|
|
|
ULONG SegmentChange; //
|
|
ULONG SegmentCreate; // Various counters (optional)
|
|
ULONG SegmentInsertInFree; //
|
|
ULONG SegmentDelete; //
|
|
|
|
USER_MEMORY_CACHE UserBlockCache;
|
|
|
|
//
|
|
// Bucket data
|
|
//
|
|
|
|
HEAP_BUCKET Buckets[HEAP_BUCKETS_COUNT];
|
|
|
|
//
|
|
// The LocalData array must be the last field in LFH structures
|
|
// The sizes of the array is choosen depending upon the
|
|
// number of processors.
|
|
//
|
|
|
|
HEAP_LOCAL_DATA LocalData[1];
|
|
|
|
} LFH_HEAP, *PLFH_HEAP;
|
|
|
|
|
|
/*
|
|
MEMORY_ADDRESS Addr = GetExpression(args);
|
|
if (NULL == Addr)
|
|
{
|
|
dprintf("unable to resolve %s\n",args);
|
|
return;
|
|
}
|
|
HEAP_USERDATA_HEADER UserData;
|
|
if (ReadMemory(Addr,&UserData,sizeof(UserData),NULL))
|
|
{
|
|
dprintf(" Next %p HeapHandle %p SizeIndex %p Signature %p\n",
|
|
UserData.SFreeListEntry.Next,
|
|
UserData.HeapHandle,
|
|
UserData.SizeIndex,
|
|
UserData.Signature);
|
|
BLOCK_ENTRY * pBlkEntry = (BLOCK_ENTRY *)((HEAP_USERDATA_HEADER*)Addr+1);
|
|
while(pBlkEntry)
|
|
{
|
|
BLOCK_ENTRY BlkEntry;
|
|
|
|
if (ReadMemory((ULONG_PTR)pBlkEntry,&BlkEntry,sizeof(BlkEntry),NULL))
|
|
{
|
|
dprintf(" %p : %08x %02x %02x %02x %02x - %04x %04x\n",
|
|
pBlkEntry,
|
|
*(DWORD *)&BlkEntry.Size,
|
|
BlkEntry.SmallTagIndex,
|
|
BlkEntry.Flags,
|
|
BlkEntry.UnusedBytes,
|
|
BlkEntry.SegmentIndex,
|
|
BlkEntry.LinkOffset,
|
|
BlkEntry.Reserved2);
|
|
|
|
GetVTable((MEMORY_ADDRESS)((BYTE*)pBlkEntry+sizeof(BLOCK_ENTRY)));
|
|
|
|
if (1 == BlkEntry.SmallTagIndex)
|
|
{
|
|
pBlkEntry = (BLOCK_ENTRY *)((HEAP_ENTRY *)pBlkEntry+BlkEntry.LinkOffset);
|
|
}
|
|
else
|
|
{
|
|
pBlkEntry = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pBlkEntry);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n");
|
|
}
|
|
*/
|
|
|
|
void Print_HEAP_SUBSEGMENT( HEAP_SUBSEGMENT * pSubSeg_OOP,BOOL bHere)
|
|
{
|
|
HEAP_SUBSEGMENT HeapSubSeg;
|
|
HEAP_SUBSEGMENT * pSubSegHERE = &HeapSubSeg;
|
|
if (NULL == pSubSeg_OOP) return;
|
|
|
|
if (bHere)
|
|
{
|
|
pSubSegHERE = pSubSeg_OOP;
|
|
}
|
|
else
|
|
{
|
|
if (!ReadMemory((ULONG_PTR)pSubSeg_OOP,&HeapSubSeg,sizeof(HeapSubSeg),0))
|
|
{
|
|
dprintf("RM %p\n",pSubSeg_OOP);
|
|
return;
|
|
}
|
|
}
|
|
dprintf(" - Bkt %p Blk %p D %04x F %04x S %08x\n",
|
|
pSubSegHERE->Bucket,
|
|
pSubSegHERE->UserBlocks,
|
|
pSubSegHERE->AggregateExchg.Depth,
|
|
pSubSegHERE->AggregateExchg.FreeEntryOffset,
|
|
pSubSegHERE->AggregateExchg.Sequence);
|
|
dprintf(" Bs %04x FT %04x BC %04x S %02x A %02x Lock %08x\n",
|
|
pSubSegHERE->BlockSize,
|
|
pSubSegHERE->FreeThreshold,
|
|
pSubSegHERE->BlockCount,
|
|
pSubSegHERE->SizeIndex,
|
|
pSubSegHERE->AffinityIndex,
|
|
pSubSegHERE->Lock);
|
|
|
|
if (0 == pSubSegHERE->AggregateExchg.Depth) return;
|
|
|
|
struct EntryTable : HEAP_ENTRY
|
|
{
|
|
ULONG_PTR vTable;
|
|
} Entry;
|
|
HEAP_ENTRY * pEntry_OOP = (HEAP_ENTRY *)((HEAP_USERDATA_HEADER *)pSubSegHERE->UserBlocks+1);
|
|
DWORD Count = 0;
|
|
do
|
|
{
|
|
if (ReadMemory((ULONG_PTR)pEntry_OOP,&Entry,sizeof(Entry),NULL))
|
|
{
|
|
dprintf(" %p: %04x . %04x [%02x] - (%x)\n",
|
|
pEntry_OOP,1+pSubSegHERE->SizeIndex,0,
|
|
Entry.Flags,
|
|
(sizeof(HEAP_ENTRY))*( pSubSegHERE->BlockSize)-Entry.UnusedBytes);
|
|
GetVTable(Entry.vTable);
|
|
pEntry_OOP += ( pSubSegHERE->BlockSize );
|
|
Count++;
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pEntry_OOP);
|
|
break;
|
|
}
|
|
} while(1 == Entry.SmallTagIndex && Count < pSubSegHERE->AggregateExchg.Depth);
|
|
|
|
}
|
|
|
|
DWORD EnumListCrtZone(VOID * pZone_OOP,
|
|
VOID * pBlockZone)
|
|
{
|
|
LFH_BLOCK_ZONE CrtZone;
|
|
LFH_BLOCK_ZONE * pBlockZone_OOP = (LFH_BLOCK_ZONE *)pZone_OOP;
|
|
if (ReadMemory((ULONG_PTR)pBlockZone_OOP,&CrtZone,sizeof(CrtZone),NULL))
|
|
{
|
|
ULONG_PTR Size = (ULONG_PTR)CrtZone.FreePointer - (ULONG_PTR)pBlockZone_OOP;
|
|
BYTE * pMem = (BYTE *)HeapAlloc(GetProcessHeap(),0,Size);
|
|
if (pMem)
|
|
{
|
|
ULONG_PTR AddrCrtZone = (ULONG_PTR)pBlockZone_OOP;
|
|
if (ReadMemory(AddrCrtZone,pMem,(ULONG)Size,NULL))
|
|
{
|
|
HEAP_SUBSEGMENT * pSubSeg = (HEAP_SUBSEGMENT *)((LFH_BLOCK_ZONE *)pMem + 1);
|
|
HEAP_SUBSEGMENT * pSubSegEnd = (HEAP_SUBSEGMENT *)((BYTE *)pSubSeg+Size);
|
|
|
|
HEAP_SUBSEGMENT * pSubSeg_OOP = (HEAP_SUBSEGMENT *)((LFH_BLOCK_ZONE *)AddrCrtZone+1);
|
|
while (pSubSeg < pSubSegEnd )
|
|
{
|
|
//dprintf("SS - %p\n",pSubSeg_OOP);
|
|
Print_HEAP_SUBSEGMENT(pSubSeg,TRUE);
|
|
pSubSeg++;
|
|
pSubSeg_OOP++;
|
|
}
|
|
}
|
|
HeapFree(GetProcessHeap(),0,pMem);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pBlockZone_OOP);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void Print_LFH(LFH_HEAP * pLFH_OOP)
|
|
{
|
|
ULONG_PTR AddrAff = GetExpression("ntdll!RtlpHeapMaxAffinity");
|
|
if (AddrAff)
|
|
{
|
|
ULONG Affinity = 0;
|
|
if (ReadMemory(AddrAff,&Affinity,sizeof(Affinity),0))
|
|
{
|
|
ULONG_PTR SizeRead = sizeof(LFH_HEAP) + Affinity*sizeof(HEAP_LOCAL_DATA);
|
|
LFH_HEAP * pLFH = (LFH_HEAP *)HeapAlloc(GetProcessHeap(),0,SizeRead);
|
|
if (NULL == pLFH) return;
|
|
|
|
if (ReadMemory((ULONG_PTR)pLFH_OOP,pLFH,(ULONG)SizeRead,0))
|
|
{
|
|
dprintf("LFH_HEAP %p\n",pLFH_OOP);
|
|
|
|
LIST_ENTRY * pListHead_oop = &pLFH_OOP->SubSegmentZones;
|
|
EnumLinkedListCB(pListHead_oop,
|
|
sizeof(LFH_BLOCK_ZONE),
|
|
FIELD_OFFSET(LFH_BLOCK_ZONE,ListEntry),
|
|
EnumListCrtZone);
|
|
|
|
/*
|
|
for (DWORD i=0;i<HEAP_USER_ENTRIES;i++)
|
|
{
|
|
dprintf(" HEAP_USERDATA_HEADER - %d\n",i);
|
|
HEAP_USERDATA_HEADER * pUsrDataHdr = (HEAP_USERDATA_HEADER *)pLFH->UserBlockCache.UserBlocks[i].Next.Next;
|
|
|
|
dprintf(" Next %p Available %x\n",pUsrDataHdr,pLFH->UserBlockCache.AvailableBlocks[i]);
|
|
|
|
HEAP_USERDATA_HEADER UsrDataHdr;
|
|
UsrDataHdr.SFreeListEntry.Next = NULL;
|
|
for (;pUsrDataHdr;pUsrDataHdr = (HEAP_USERDATA_HEADER *)UsrDataHdr.SFreeListEntry.Next)
|
|
{
|
|
if (ReadMemory((ULONG_PTR)pUsrDataHdr,&UsrDataHdr,sizeof(UsrDataHdr),NULL))
|
|
{
|
|
dprintf(" ----\n");
|
|
dprintf(" Next %p\n",UsrDataHdr.SFreeListEntry.Next);
|
|
dprintf(" HeapHandle %p\n",UsrDataHdr.HeapHandle);
|
|
dprintf(" SizeIndex %p\n",UsrDataHdr.SizeIndex);
|
|
dprintf(" Signature %p\n",UsrDataHdr.Signature);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pUsrDataHdr);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
//
|
|
// Buckets
|
|
//
|
|
/*
|
|
dprintf(" Buckets\n");
|
|
dprintf(" # BUnt S A Conv TotBlk SubSegCnt\n");
|
|
for (DWORD i = 0; i < HEAP_BUCKETS_COUNT; i++)
|
|
{
|
|
//+0x000 Counters : _HEAP_BUCKET
|
|
dprintf(" %02x - %04x %02x %02x %p %08x %08x\n",
|
|
i,
|
|
pLFH->Buckets[i].BlockUnits,
|
|
pLFH->Buckets[i].SizeIndex,
|
|
pLFH->Buckets[i].UseAffinity,
|
|
pLFH->Buckets[i].Conversions,
|
|
pLFH->Buckets[i].Counters.TotalBlocks,
|
|
pLFH->Buckets[i].Counters.SubSegmentCounts);
|
|
}
|
|
*/
|
|
|
|
for (DWORD i = 0; i <= Affinity; i++)
|
|
{
|
|
if (0 == i)
|
|
dprintf(" HEAP_LOCAL_DATA - NO AFFINITY\n");
|
|
else
|
|
dprintf(" HEAP_LOCAL_DATA - AFFINITY %d\n",i-1);
|
|
|
|
dprintf(" @ %p CrtZone %p LFHeap %p Affinity %x\n",
|
|
(ULONG_PTR)pLFH_OOP + (ULONG_PTR)&pLFH->LocalData[i] - (ULONG_PTR)pLFH,
|
|
pLFH->LocalData[i].CrtZone,
|
|
pLFH->LocalData[i].LowFragHeap,
|
|
pLFH->LocalData[i].Affinity);
|
|
|
|
//Print_BLOCK_ZONE((LFH_BLOCK_ZONE *)pLFH->LocalData[i].CrtZone);
|
|
|
|
HEAP_LOCAL_DATA * pLocData = &pLFH->LocalData[i];
|
|
|
|
dprintf(" # Hint Active Next BusyEntries LastUsed\n");
|
|
for (DWORD j=0;j<HEAP_BUCKETS_COUNT;j++)
|
|
{
|
|
dprintf(" %02x %p %p %p %08x %08x\n",
|
|
j,
|
|
pLocData->SegmentInfo[j].Hint,
|
|
pLocData->SegmentInfo[j].ActiveSubsegment,
|
|
pLocData->SegmentInfo[j].SListHeader, //.Next, //.next
|
|
pLocData->SegmentInfo[j].BusyEntries,
|
|
pLocData->SegmentInfo[j].LastUsed);
|
|
//Print_HEAP_SUBSEGMENT(pLocData->SegmentInfo[j].Hint,FALSE);
|
|
//Print_HEAP_SUBSEGMENT(pLocData->SegmentInfo[j].ActiveSubsegment,FALSE);
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pLFH_OOP);
|
|
}
|
|
HeapFree(GetProcessHeap(),0,pLFH);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",AddrAff);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("unable to resolve ntdll!RtlpHeapMaxAffinity\n");
|
|
}
|
|
}
|
|
|
|
DECLARE_API(lfhp)
|
|
{
|
|
INIT_API();
|
|
|
|
MEMORY_ADDRESS Addr = GetExpression(args);
|
|
if (NULL == Addr)
|
|
{
|
|
dprintf("unable to resolve %s\n",args);
|
|
return;
|
|
}
|
|
|
|
DEFINE_CPP_VAR( HEAP, varHEAP);
|
|
HEAP * pHeap = GET_CPP_VAR_PTR( HEAP , varHEAP );
|
|
|
|
if (ReadMemory(Addr,pHeap ,sizeof(HEAP),NULL))
|
|
{
|
|
|
|
//dprintf("----- LookAside %p\n",pHeap->FrontEndHeap);
|
|
//Dump_LookAside((ULONG_PTR)pHeap->FrontEndHeap);
|
|
if (HEAP_FRONT_LOWFRAGHEAP == pHeap->FrontEndHeapType )
|
|
{
|
|
Print_LFH((LFH_HEAP*)pHeap->FrontEndHeap);
|
|
}
|
|
else
|
|
{
|
|
dprintf("Unrecognized FrontEndHeapType %d\n",pHeap->FrontEndHeapType);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",Addr);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
//
|
|
// dump the HEAP, incomplete
|
|
//
|
|
//
|
|
|
|
DECLARE_API(hp)
|
|
{
|
|
INIT_API();
|
|
|
|
g_UsedInHeap = 0;
|
|
|
|
DEFINE_CPP_VAR( HEAP, varHEAP);
|
|
HEAP * pHeap = GET_CPP_VAR_PTR( HEAP , varHEAP );
|
|
MEMORY_ADDRESS pByte = GetExpression(args);
|
|
DWORD i;
|
|
|
|
if (pByte)
|
|
{
|
|
if (ReadMemory(pByte,pHeap ,sizeof(HEAP),NULL))
|
|
{
|
|
for (i=0;i<HEAP_MAXIMUM_SEGMENTS;i++)
|
|
{
|
|
if (pHeap->Segments[i])
|
|
PrintHEAP_SEGMENT(pHeap->Segments[i],NULL,NULL);
|
|
//dprintf(" seg %i - %p\n",i,pHeap->Segments[i]);
|
|
|
|
}
|
|
dprintf("Used Bytes %p\n",g_UsedInHeap);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p %d\n",pByte,GetLastError());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("invalid address %s\n",args);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
/////////////////////////////////////////////////////////////
|
|
|
|
DWORD g_BlockSize;
|
|
ULONG_PTR * g_pBlockBlob;
|
|
ULONG g_NumMatch;
|
|
|
|
DWORD CallBackSearch(ULONG_PTR pHeapEntry_OOP,ULONG_PTR Addr)
|
|
{
|
|
if (!g_pBlockBlob)
|
|
{
|
|
dprintf("no GLOBAL search block\n");
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
HEAP_ENTRY Entry;
|
|
if (ReadMemory(pHeapEntry_OOP,&Entry,sizeof(HEAP_ENTRY),NULL))
|
|
{
|
|
HEAP_ENTRY * pEntry = (HEAP_ENTRY *)&Entry;
|
|
DWORD Size = (pEntry->Flags & HEAP_ENTRY_BUSY)?(pEntry->Size<<HEAP_GRANULARITY_SHIFT)-pEntry->UnusedBytes:0;
|
|
if (Size)
|
|
{
|
|
if (Size < g_BlockSize)
|
|
{
|
|
ULONG_PTR * pData = (ULONG_PTR *)g_pBlockBlob;
|
|
ReadMemory(pHeapEntry_OOP+sizeof(HEAP_ENTRY),pData,Size,NULL);
|
|
|
|
// here is the assumption that pointers are aligned
|
|
DWORD nTimes = Size/sizeof(ULONG_PTR);
|
|
DWORD i;
|
|
for (i=0;i<nTimes;i++)
|
|
{
|
|
if (Addr == pData[i])
|
|
{
|
|
dprintf("- %p off %p\n",pHeapEntry_OOP,sizeof(ULONG_PTR)*i);
|
|
PrintHeapEntry((HEAP_ENTRY *)pEntry,(void *)pHeapEntry_OOP);
|
|
g_NumMatch++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf(" entry %p too big\n",pHeapEntry_OOP);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pHeapEntry_OOP);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
//
|
|
// search the HEAP, incomplete
|
|
//
|
|
///////////////////////////////////////////////
|
|
|
|
DECLARE_API(shp)
|
|
{
|
|
INIT_API();
|
|
|
|
DEFINE_CPP_VAR( HEAP, varHEAP);
|
|
HEAP * pHeap = GET_CPP_VAR_PTR( HEAP , varHEAP );
|
|
|
|
|
|
char * pArgs = (char *)args;
|
|
while(isspace(*pArgs)) pArgs++;
|
|
|
|
MEMORY_ADDRESS pByte = GetExpression(pArgs);
|
|
|
|
while(!isspace(*pArgs)) pArgs++;
|
|
|
|
MEMORY_ADDRESS Addr = GetExpression(pArgs);
|
|
|
|
DWORD i;
|
|
|
|
if (pByte && Addr)
|
|
{
|
|
g_BlockSize = 0x10000*sizeof(HEAP_ENTRY);
|
|
g_pBlockBlob = (ULONG_PTR *)VirtualAlloc(NULL,g_BlockSize,MEM_COMMIT,PAGE_READWRITE);
|
|
|
|
if (!g_pBlockBlob)
|
|
{
|
|
dprintf("VirtualAlloc err %d\n",GetLastError());
|
|
return;
|
|
}
|
|
|
|
if (ReadMemory(pByte,pHeap ,sizeof(HEAP),NULL))
|
|
{
|
|
|
|
g_NumMatch = 0;
|
|
for (i=0;i<HEAP_MAXIMUM_SEGMENTS;i++)
|
|
{
|
|
if (pHeap->Segments[i])
|
|
PrintHEAP_SEGMENT(pHeap->Segments[i],CallBackSearch,(ULONG_PTR)Addr);
|
|
// dprintf(" seg %i - %p\n",i,pHeap->Segments[i]);
|
|
|
|
}
|
|
dprintf("%d matches found\n",g_NumMatch);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p %d\n",pByte,GetLastError());
|
|
}
|
|
|
|
if (g_pBlockBlob)
|
|
{
|
|
VirtualFree(g_pBlockBlob,0,MEM_RELEASE);
|
|
g_pBlockBlob = NULL;
|
|
g_BlockSize = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("invalid heap address pair in%s\n",args);
|
|
}
|
|
}
|
|
|
|
//
|
|
// decode heap flags
|
|
//
|
|
/*
|
|
#define HEAP_NO_SERIALIZE 0x00000001 // winnt
|
|
#define HEAP_GROWABLE 0x00000002 // winnt
|
|
#define HEAP_GENERATE_EXCEPTIONS 0x00000004 // winnt
|
|
#define HEAP_ZERO_MEMORY 0x00000008 // winnt
|
|
#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010 // winnt
|
|
#define HEAP_TAIL_CHECKING_ENABLED 0x00000020 // winnt
|
|
#define HEAP_FREE_CHECKING_ENABLED 0x00000040 // winnt
|
|
#define HEAP_DISABLE_COALESCE_ON_FREE 0x00000080 // winnt
|
|
|
|
#define HEAP_SETTABLE_USER_VALUE 0x00000100
|
|
#define HEAP_SETTABLE_USER_FLAG1 0x00000200
|
|
#define HEAP_SETTABLE_USER_FLAG2 0x00000400
|
|
#define HEAP_SETTABLE_USER_FLAG3 0x00000800
|
|
|
|
#define HEAP_CLASS_0 0x00000000 // process heap
|
|
#define HEAP_CLASS_1 0x00001000 // private heap
|
|
#define HEAP_CLASS_2 0x00002000 // Kernel Heap
|
|
#define HEAP_CLASS_3 0x00003000 // GDI heap
|
|
#define HEAP_CLASS_4 0x00004000 // User heap
|
|
#define HEAP_CLASS_5 0x00005000 // Console heap
|
|
#define HEAP_CLASS_6 0x00006000 // User Desktop heap
|
|
#define HEAP_CLASS_7 0x00007000 // Csrss Shared heap
|
|
#define HEAP_CLASS_8 0x00008000 // Csr Port heap
|
|
*/
|
|
#define HEAP_LOCK_USER_ALLOCATED (ULONG)0x80000000
|
|
#define HEAP_VALIDATE_PARAMETERS_ENABLED (ULONG)0x40000000
|
|
#define HEAP_VALIDATE_ALL_ENABLED (ULONG)0x20000000
|
|
#define HEAP_SKIP_VALIDATION_CHECKS (ULONG)0x10000000
|
|
#define HEAP_CAPTURE_STACK_BACKTRACES (ULONG)0x08000000
|
|
|
|
#define HEAP_FLAG_PAGE_ALLOCS 0x01000000
|
|
|
|
#define HEAP_PROTECTION_ENABLED 0x02000000
|
|
#define HEAP_BREAK_WHEN_OUT_OF_VM 0x04000000
|
|
#define HEAP_NO_ALIGNMENT 0x08000000
|
|
|
|
|
|
|
|
void DecodeFlags(ULONG Flags)
|
|
{
|
|
dprintf(" Flags: %08x ",Flags);
|
|
if (Flags & HEAP_NO_SERIALIZE)
|
|
dprintf("HEAP_NO_SERIALIZE ");
|
|
if (Flags & HEAP_GROWABLE)
|
|
dprintf("HEAP_GROWABLE ");
|
|
if (Flags & HEAP_GENERATE_EXCEPTIONS)
|
|
dprintf("HEAP_GENERATE_EXCEPTIONS ");
|
|
if (Flags & HEAP_ZERO_MEMORY)
|
|
dprintf("HEAP_ZERO_MEMORY ");
|
|
|
|
if (Flags & HEAP_REALLOC_IN_PLACE_ONLY)
|
|
dprintf("HEAP_REALLOC_IN_PLACE_ONLY ");
|
|
if (Flags & HEAP_TAIL_CHECKING_ENABLED)
|
|
dprintf("HEAP_TAIL_CHECKING_ENABLED ");
|
|
if (Flags & HEAP_FREE_CHECKING_ENABLED)
|
|
dprintf("HEAP_FREE_CHECKING_ENABLED ");
|
|
if (Flags & HEAP_DISABLE_COALESCE_ON_FREE)
|
|
dprintf("HEAP_DISABLE_COALESCE_ON_FREE ");
|
|
|
|
if (Flags & HEAP_SETTABLE_USER_VALUE)
|
|
dprintf("HEAP_SETTABLE_USER_VALUE ");
|
|
if (Flags & HEAP_SETTABLE_USER_FLAG1)
|
|
dprintf("HEAP_SETTABLE_USER_FLAG1 ");
|
|
if (Flags & HEAP_SETTABLE_USER_FLAG2)
|
|
dprintf("HEAP_SETTABLE_USER_FLAG2 ");
|
|
if (Flags & HEAP_SETTABLE_USER_FLAG3)
|
|
dprintf("HEAP_SETTABLE_USER_FLAG3 ");
|
|
|
|
if (Flags & HEAP_CLASS_MASK)
|
|
dprintf("HEAP_CLASS %d",(Flags&HEAP_CLASS_MASK)>>12);
|
|
/*
|
|
if (Flags & HEAP_CLASS_1)
|
|
dprintf("HEAP_CLASS_1 ");
|
|
if (Flags & HEAP_CLASS_2)
|
|
dprintf("HEAP_CLASS_2 ");
|
|
if (Flags & HEAP_CLASS_3)
|
|
dprintf("HEAP_CLASS_3 ");
|
|
if (Flags & HEAP_CLASS_4)
|
|
dprintf("HEAP_CLASS_4 ");
|
|
if (Flags & HEAP_CLASS_5)
|
|
dprintf("HEAP_CLASS_5 ");
|
|
if (Flags & HEAP_CLASS_6)
|
|
dprintf("HEAP_CLASS_6 ");
|
|
if (Flags & HEAP_CLASS_7)
|
|
dprintf("HEAP_CLASS_7 ");
|
|
*/
|
|
|
|
//if (Flags & HEAP_CAPTURE_STACK_BACKTRACES)
|
|
// dprintf("HEAP_CAPTURE_STACK_BACKTRACES ");
|
|
if (Flags &HEAP_SKIP_VALIDATION_CHECKS)
|
|
dprintf("HEAP_SKIP_VALIDATION_CHECKS ");
|
|
if (Flags &HEAP_VALIDATE_ALL_ENABLED)
|
|
dprintf("HEAP_VALIDATE_ALL_ENABLED ");
|
|
if (Flags &HEAP_VALIDATE_PARAMETERS_ENABLED)
|
|
dprintf("HEAP_VALIDATE_PARAMETERS_ENABLED ");
|
|
if (Flags &HEAP_LOCK_USER_ALLOCATED)
|
|
dprintf("HEAP_LOCK_USER_ALLOCATED ");
|
|
|
|
if (Flags &HEAP_FLAG_PAGE_ALLOCS)
|
|
dprintf("HEAP_FLAG_PAGE_ALLOCS ");
|
|
if (Flags &HEAP_PROTECTION_ENABLED)
|
|
dprintf("HEAP_PROTECTION_ENABLED ");
|
|
if (Flags &HEAP_BREAK_WHEN_OUT_OF_VM)
|
|
dprintf("HEAP_BREAK_WHEN_OUT_OF_VM ");
|
|
if (Flags &HEAP_NO_ALIGNMENT)
|
|
dprintf("HEAP_NO_ALIGNMENT ");
|
|
|
|
//if (Flags &)
|
|
// dprintf(" ");
|
|
dprintf("\n");
|
|
}
|
|
|
|
//
|
|
// Get all the heaps
|
|
//
|
|
|
|
DECLARE_API(hps)
|
|
{
|
|
INIT_API();
|
|
|
|
PEB * pPeb = NULL;
|
|
PEB ThisPeb;
|
|
GetPeb(hCurrentProcess,&pPeb);
|
|
|
|
if(!pPeb)
|
|
{
|
|
#ifdef _WIN64
|
|
pPeb = (PEB *)0x6fbfffde000;
|
|
#else
|
|
pPeb = (PEB *)0x7ffdf000;
|
|
#endif
|
|
}
|
|
|
|
if (pPeb)
|
|
{
|
|
ReadMemory((MEMORY_ADDRESS)pPeb,&ThisPeb,sizeof(PEB),0);
|
|
void ** pHeaps = (void**)_alloca(ThisPeb.NumberOfHeaps*sizeof(void*));
|
|
DWORD i,j;
|
|
ULONG_PTR TotCommitSize = 0;
|
|
ULONG_PTR TotVirtSize = 0;
|
|
|
|
if (ReadMemory((MEMORY_ADDRESS)ThisPeb.ProcessHeaps,pHeaps,ThisPeb.NumberOfHeaps*sizeof(void*),0))
|
|
{
|
|
for(i=0;i<ThisPeb.NumberOfHeaps;i++)
|
|
{
|
|
DEFINE_CPP_VAR( HEAP, varHEAP);
|
|
HEAP * pHeap = GET_CPP_VAR_PTR( HEAP , varHEAP );
|
|
ULONG_PTR TotHeapCommitSize = 0;
|
|
ULONG_PTR TotHeapVirtSize = 0;
|
|
|
|
if (ReadMemory((MEMORY_ADDRESS)pHeaps[i],pHeap ,sizeof(HEAP),0))
|
|
{
|
|
for (j=0;j<HEAP_MAXIMUM_SEGMENTS;j++)
|
|
{
|
|
if (pHeap->Segments[j])
|
|
{
|
|
DEFINE_CPP_VAR( HEAP_SEGMENT, varHEAP_SEGMENT);
|
|
HEAP_SEGMENT * pHeapSeg = GET_CPP_VAR_PTR( HEAP_SEGMENT , varHEAP_SEGMENT );
|
|
|
|
if (ReadMemory((MEMORY_ADDRESS)pHeap->Segments[j],pHeapSeg,sizeof(HEAP_SEGMENT),0))
|
|
{
|
|
dprintf(" - %p (C %p - R %p)\n",
|
|
pHeap->Segments[j],
|
|
(pHeapSeg->NumberOfPages - pHeapSeg->NumberOfUnCommittedPages) * PAGE_SIZE,
|
|
(pHeapSeg->NumberOfPages) * PAGE_SIZE);
|
|
|
|
TotHeapCommitSize += ((pHeapSeg->NumberOfPages - pHeapSeg->NumberOfUnCommittedPages) * PAGE_SIZE);
|
|
TotHeapVirtSize += ((pHeapSeg->NumberOfPages) * PAGE_SIZE);
|
|
// now print the beggining of a committed range
|
|
dprintf(" CR %p\n",pHeapSeg->BaseAddress);
|
|
HEAP_UNCOMMMTTED_RANGE * pUncomm_OOP = pHeapSeg->UnCommittedRanges;
|
|
for (DWORD i=0;i<pHeapSeg->NumberOfUnCommittedRanges && pUncomm_OOP;i++)
|
|
{
|
|
HEAP_UNCOMMMTTED_RANGE UncommRange;
|
|
if (ReadMemory((MEMORY_ADDRESS)pUncomm_OOP,&UncommRange,sizeof(HEAP_UNCOMMMTTED_RANGE),NULL))
|
|
{
|
|
if (UncommRange.Next)
|
|
{
|
|
pUncomm_OOP = UncommRange.Next;
|
|
}
|
|
ULONG_PTR RangeAddr = (ULONG_PTR)UncommRange.Address+UncommRange.Size;
|
|
if (RangeAddr != (ULONG_PTR)pHeapSeg->LastEntryInSegment)
|
|
dprintf(" CR %p\n",RangeAddr);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pHeapSeg->UnCommittedRanges);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pHeap->Segments[j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pHeaps[i]);
|
|
pHeap = NULL;
|
|
}
|
|
dprintf(" HEAP %p - %p\n",pHeaps[i],TotHeapCommitSize);
|
|
if (pHeap)
|
|
{
|
|
DecodeFlags(pHeap->Flags|pHeap->ForceFlags);
|
|
dprintf(" FrontEndHeapType %d\n",pHeap->FrontEndHeapType);
|
|
}
|
|
TotCommitSize += TotHeapCommitSize;
|
|
TotVirtSize += TotHeapVirtSize;
|
|
}
|
|
dprintf(" -- Tot C %p Tot R %p\n",TotCommitSize, TotVirtSize);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",ThisPeb.ProcessHeaps);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("unable to get PEB\n");
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// reverse heap free list
|
|
//
|
|
//////////////////
|
|
|
|
DWORD
|
|
CallBackFreeList(VOID * pStructure_OOP,
|
|
VOID * pLocalCopy)
|
|
{
|
|
HEAP_FREE_ENTRY * pFreeEntry = (HEAP_FREE_ENTRY *)pLocalCopy;
|
|
|
|
dprintf(" %p (%p,%p): %04x - %04x [%02x] %02x %02x (%x)\n",
|
|
pStructure_OOP,
|
|
pFreeEntry->FreeList.Flink,
|
|
pFreeEntry->FreeList.Blink,
|
|
pFreeEntry->Size,
|
|
pFreeEntry->PreviousSize,
|
|
pFreeEntry->Flags,
|
|
pFreeEntry->Index,
|
|
pFreeEntry->Mask,
|
|
pFreeEntry->Size*sizeof(HEAP_ENTRY));
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
DECLARE_API( rllc )
|
|
{
|
|
INIT_API();
|
|
|
|
MEMORY_ADDRESS Addr = GetExpression(args);
|
|
|
|
if (Addr)
|
|
{
|
|
EnumReverseLinkedListCB((LIST_ENTRY *)Addr,
|
|
sizeof(HEAP_FREE_ENTRY),
|
|
FIELD_OFFSET(HEAP_FREE_ENTRY,FreeList),
|
|
CallBackFreeList);
|
|
}
|
|
else
|
|
{
|
|
dprintf("cannot resolve %s\n",args);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Print the Free Lists of the Heap
|
|
//
|
|
/////////////////////////////////////////////////
|
|
|
|
DWORD
|
|
CallBackFreeList2(VOID * pStructure_OOP,
|
|
VOID * pLocalCopy)
|
|
{
|
|
HEAP_FREE_ENTRY * pFreeEntry = (HEAP_FREE_ENTRY *)pLocalCopy;
|
|
|
|
dprintf(" %p (%p,%p): %04x - %04x [%02x] %02x %02x (%x)",
|
|
pStructure_OOP,
|
|
pFreeEntry->FreeList.Flink,
|
|
pFreeEntry->FreeList.Blink,
|
|
pFreeEntry->Size,
|
|
pFreeEntry->PreviousSize,
|
|
pFreeEntry->Flags,
|
|
pFreeEntry->Index,
|
|
pFreeEntry->Mask,
|
|
pFreeEntry->Size*sizeof(HEAP_ENTRY));
|
|
|
|
MEMORY_ADDRESS pEntry = (MEMORY_ADDRESS)pStructure_OOP;
|
|
|
|
HEAP_ENTRY HeapEntry;
|
|
ReadMemory((MEMORY_ADDRESS)pEntry,&HeapEntry,sizeof(HeapEntry),0);
|
|
|
|
while (HeapEntry.PreviousSize)
|
|
{
|
|
pEntry = (MEMORY_ADDRESS)((HEAP_ENTRY*)pEntry - HeapEntry.PreviousSize);
|
|
if (ReadMemory((MEMORY_ADDRESS)pEntry,&HeapEntry,sizeof(HeapEntry),0))
|
|
{
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pEntry);
|
|
break;
|
|
}
|
|
|
|
if (CheckControlC())
|
|
break;
|
|
}
|
|
dprintf(" -B %p\n",pEntry);
|
|
return 0;
|
|
}
|
|
|
|
#define EmptyFull( expr ) (( expr )?'F':'-')
|
|
|
|
DECLARE_API( hpf )
|
|
{
|
|
INIT_API();
|
|
|
|
DEFINE_CPP_VAR( HEAP, varHEAP);
|
|
HEAP * pHeap = GET_CPP_VAR_PTR( HEAP , varHEAP );
|
|
MEMORY_ADDRESS pByte = GetExpression(args);
|
|
|
|
if (pByte)
|
|
{
|
|
if (ReadMemory(pByte,pHeap ,sizeof(HEAP),NULL))
|
|
{
|
|
dprintf(" -- 00 01 02 03 04 05 06 07\n");
|
|
DWORD nBytes = HEAP_MAXIMUM_FREELISTS / 8 ;
|
|
for (DWORD i=0;i<nBytes;i++)
|
|
{
|
|
BYTE Set = pHeap->u.FreeListsInUseBytes[i];
|
|
dprintf(" %02x : %c %c %c %c %c %c %c %c\n",8*i,
|
|
EmptyFull(Set & 0x01),EmptyFull(Set & 0x02),EmptyFull(Set & 0x04),EmptyFull(Set & 0x08),
|
|
EmptyFull(Set & 0x10),EmptyFull(Set & 0x20),EmptyFull(Set & 0x40),EmptyFull(Set & 0x80));
|
|
}
|
|
dprintf(" ------------\n");
|
|
|
|
HEAP * pHeap_OOP = (HEAP *)pByte;
|
|
for (DWORD i=0;i<HEAP_MAXIMUM_FREELISTS;i++)
|
|
{
|
|
dprintf(" FreeList[%x] @ %p\n",i,&pHeap_OOP->FreeLists[i]);
|
|
EnumReverseLinkedListCB((LIST_ENTRY *)&pHeap_OOP->FreeLists[i],
|
|
sizeof(HEAP_FREE_ENTRY),
|
|
FIELD_OFFSET(HEAP_FREE_ENTRY,FreeList),
|
|
CallBackFreeList2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",pByte);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("invalid address %s\n",args);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// dumps the DPH_HEAP_ROOT
|
|
//
|
|
///////////////////////////////////////
|
|
|
|
DECLARE_API( php )
|
|
{
|
|
INIT_API();
|
|
|
|
char * pHeapAddr = (char *)args;
|
|
while (isspace(*pHeapAddr)) pHeapAddr++;
|
|
|
|
char * pNext = pHeapAddr;
|
|
while (!isspace(*pNext)) pNext++; // skipt the Heap Addr
|
|
if (*pNext)
|
|
{
|
|
*pNext = 0;
|
|
pNext++;
|
|
}
|
|
MEMORY_ADDRESS Addr = GetExpression(pHeapAddr);
|
|
while (isspace(*pNext)) pNext++; // skip the other spaces
|
|
MEMORY_ADDRESS SearchAddr = 0;
|
|
if (*pNext == 's' ||*pNext == 'S')
|
|
{
|
|
pNext++; // skip the 's'
|
|
if (*pNext)
|
|
{
|
|
while(isspace(*pNext)) pNext++; // skip the spaces
|
|
SearchAddr = GetExpression(pNext);
|
|
}
|
|
}
|
|
|
|
//dprintf("heap %p addr %p\n",Addr,SearchAddr);
|
|
|
|
if (Addr)
|
|
{
|
|
|
|
g_BlockSize = 0x10000*sizeof(HEAP_ENTRY);
|
|
g_pBlockBlob = NULL;
|
|
if (SearchAddr)
|
|
g_pBlockBlob = (ULONG_PTR *)VirtualAlloc(NULL,g_BlockSize,MEM_COMMIT,PAGE_READWRITE);
|
|
|
|
if (SearchAddr && !g_pBlockBlob)
|
|
{
|
|
dprintf("VirtualAlloc err %d\n",GetLastError());
|
|
return;
|
|
}
|
|
|
|
HEAP Heap;
|
|
DPH_HEAP_ROOT HeapPage;
|
|
if (0 == SearchAddr)
|
|
dprintf(" HEAP @ %p\n",Addr);
|
|
if (ReadMemory((MEMORY_ADDRESS)Addr,&Heap,sizeof(HEAP),NULL))
|
|
{
|
|
if (Heap.ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
|
|
{
|
|
Addr += PAGE_SIZE;
|
|
dprintf(" DPH_HEAP_ROOT @ %p\n",Addr);
|
|
if (ReadMemory((MEMORY_ADDRESS)Addr,&HeapPage,sizeof(DPH_HEAP_ROOT),NULL))
|
|
{
|
|
DPH_HEAP_BLOCK HeapBlock;
|
|
DPH_HEAP_BLOCK * pNextBlock;
|
|
|
|
if (0 == SearchAddr)
|
|
{
|
|
pNextBlock = HeapPage.pVirtualStorageListHead;
|
|
dprintf(" - pVirtualStorageListHead\n");
|
|
while(pNextBlock)
|
|
{
|
|
|
|
if (ReadMemory((MEMORY_ADDRESS)pNextBlock,&HeapBlock,sizeof(DPH_HEAP_BLOCK),NULL))
|
|
{
|
|
dprintf(" %p - (%p) B %p S %p \n",
|
|
pNextBlock,
|
|
HeapBlock.pNextAlloc,
|
|
HeapBlock.pVirtualBlock,
|
|
HeapBlock.nVirtualBlockSize);
|
|
pNextBlock = HeapBlock.pNextAlloc;
|
|
}
|
|
else
|
|
{
|
|
pNextBlock = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
pNextBlock = HeapPage.pBusyAllocationListHead;
|
|
if (0 == SearchAddr)
|
|
dprintf(" - pBusyAllocationListHead\n");
|
|
while(pNextBlock)
|
|
{
|
|
if (ReadMemory((MEMORY_ADDRESS)pNextBlock,&HeapBlock,sizeof(DPH_HEAP_BLOCK),NULL))
|
|
{
|
|
if (0 == SearchAddr)
|
|
{
|
|
dprintf(" %p - (%p) %x %x %x U %p S %p\n",
|
|
pNextBlock,
|
|
HeapBlock.pNextAlloc,
|
|
ULONG_PTR(HeapBlock.pVirtualBlock)/PAGE_SIZE,
|
|
HeapBlock.nVirtualBlockSize/PAGE_SIZE,
|
|
HeapBlock.nVirtualAccessSize/PAGE_SIZE,
|
|
HeapBlock.pUserAllocation,
|
|
HeapBlock.StackTrace_);
|
|
GetVTable((MEMORY_ADDRESS)HeapBlock.pUserAllocation+sizeof(DPH_BLOCK_INFORMATION));
|
|
}
|
|
else // do the real search
|
|
{
|
|
MEMORY_ADDRESS Size = (MEMORY_ADDRESS)HeapBlock.pVirtualBlock+HeapBlock.nVirtualAccessSize-(MEMORY_ADDRESS)HeapBlock.pUserAllocation;
|
|
if (ReadMemory((MEMORY_ADDRESS)HeapBlock.pUserAllocation,g_pBlockBlob,(ULONG)Size,NULL))
|
|
{
|
|
Size /= sizeof(ULONG_PTR);
|
|
BOOL bFound = FALSE;
|
|
for (ULONG_PTR j =0;j<Size;j++)
|
|
{
|
|
if (SearchAddr == g_pBlockBlob[j])
|
|
{
|
|
bFound = TRUE;
|
|
dprintf(" OFF %p\n",j*sizeof(ULONG_PTR));
|
|
}
|
|
}
|
|
if (bFound)
|
|
{
|
|
dprintf(" B %p\n",HeapBlock.pUserAllocation);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",HeapBlock.pUserAllocation);
|
|
}
|
|
}
|
|
pNextBlock = HeapBlock.pNextAlloc;
|
|
}
|
|
else
|
|
{
|
|
pNextBlock = NULL;
|
|
}
|
|
}
|
|
|
|
if (0 == SearchAddr)
|
|
{
|
|
pNextBlock = HeapPage.pFreeAllocationListHead;
|
|
dprintf(" - pFreeAllocationListHead\n");
|
|
while(pNextBlock)
|
|
{
|
|
if (ReadMemory((MEMORY_ADDRESS)pNextBlock,&HeapBlock,sizeof(DPH_HEAP_BLOCK),NULL))
|
|
{
|
|
dprintf(" %p - (%p) %x %x %x U %p S %p\n",
|
|
pNextBlock,
|
|
HeapBlock.pNextAlloc,
|
|
ULONG_PTR(HeapBlock.pVirtualBlock)/PAGE_SIZE,
|
|
HeapBlock.nVirtualBlockSize/PAGE_SIZE,
|
|
HeapBlock.nVirtualAccessSize/PAGE_SIZE,
|
|
HeapBlock.pUserAllocation,
|
|
HeapBlock.StackTrace_);
|
|
pNextBlock = HeapBlock.pNextAlloc;
|
|
}
|
|
else
|
|
{
|
|
pNextBlock = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (0 == SearchAddr)
|
|
{
|
|
pNextBlock = HeapPage.pAvailableAllocationListHead;
|
|
dprintf(" - pAvailableAllocationListHead\n");
|
|
while(pNextBlock)
|
|
{
|
|
|
|
if (ReadMemory((MEMORY_ADDRESS)pNextBlock,&HeapBlock,sizeof(DPH_HEAP_BLOCK),NULL))
|
|
{
|
|
dprintf(" %p - (%p) B %p S %p \n",
|
|
pNextBlock,
|
|
HeapBlock.pNextAlloc,
|
|
HeapBlock.pVirtualBlock,
|
|
HeapBlock.nVirtualBlockSize);
|
|
pNextBlock = HeapBlock.pNextAlloc;
|
|
}
|
|
else
|
|
{
|
|
pNextBlock = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (0 == SearchAddr)
|
|
{
|
|
pNextBlock = HeapPage.pNodePoolListHead;
|
|
dprintf(" - pNodePoolListHead\n");
|
|
while(pNextBlock)
|
|
{
|
|
|
|
if (ReadMemory((MEMORY_ADDRESS)pNextBlock,&HeapBlock,sizeof(DPH_HEAP_BLOCK),NULL))
|
|
{
|
|
dprintf(" %p - (%p) B %p S %p \n",
|
|
pNextBlock,
|
|
HeapBlock.pNextAlloc,
|
|
HeapBlock.pVirtualBlock,
|
|
HeapBlock.nVirtualBlockSize);
|
|
pNextBlock = HeapBlock.pNextAlloc;
|
|
}
|
|
else
|
|
{
|
|
pNextBlock = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
dprintf(" NormalHeap @ %p\n",HeapPage.NormalHeap);
|
|
if (ReadMemory((ULONG_PTR)HeapPage.NormalHeap,&Heap ,sizeof(HEAP),NULL))
|
|
{
|
|
for (DWORD h=0;h<HEAP_MAXIMUM_SEGMENTS;h++)
|
|
{
|
|
if (Heap.Segments[h])
|
|
{
|
|
if (SearchAddr)
|
|
PrintHEAP_SEGMENT(Heap.Segments[h],CallBackSearch,(ULONG_PTR)SearchAddr);
|
|
else
|
|
PrintHEAP_SEGMENT(Heap.Segments[h],NULL,NULL);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",HeapPage.NormalHeap);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",Addr);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
DecodeFlags(Heap.ForceFlags|Heap.Flags);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",Addr);
|
|
}
|
|
|
|
if (g_pBlockBlob)
|
|
{
|
|
VirtualFree(g_pBlockBlob,g_BlockSize,MEM_RELEASE);
|
|
g_pBlockBlob = NULL;
|
|
g_BlockSize = 0;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
dprintf("unable to resolve %s\n",args);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// virtual_query helper
|
|
//
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
char * GetState(DWORD State)
|
|
{
|
|
switch(State)
|
|
{
|
|
case MEM_COMMIT:
|
|
return "MEM_COMMIT";
|
|
case MEM_RESERVE:
|
|
return "MEM_RESERVE";
|
|
case MEM_FREE:
|
|
return "MEM_FREE";
|
|
};
|
|
return "";
|
|
}
|
|
|
|
char * GetType(DWORD Type)
|
|
{
|
|
switch(Type)
|
|
{
|
|
case MEM_IMAGE:
|
|
return "MEM_IMAGE";
|
|
case MEM_MAPPED:
|
|
return "MEM_MAPPED";
|
|
case MEM_PRIVATE:
|
|
return "MEM_PRIVATE";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
char * GetProtect(DWORD Protect)
|
|
{
|
|
switch(Protect)
|
|
{
|
|
case PAGE_NOACCESS:
|
|
return "PAGE_NOACCESS";
|
|
case PAGE_READONLY:
|
|
return "PAGE_READONLY";
|
|
case PAGE_READWRITE:
|
|
return "PAGE_READWRITE";
|
|
case PAGE_WRITECOPY:
|
|
return "PAGE_WRITECOPY";
|
|
case PAGE_EXECUTE:
|
|
return "PAGE_EXECUTE";
|
|
case PAGE_EXECUTE_READ:
|
|
return "PAGE_EXECUTE_READ";
|
|
case PAGE_EXECUTE_READWRITE:
|
|
return "PAGE_EXECUTE_READWRITE";
|
|
case PAGE_EXECUTE_WRITECOPY:
|
|
return "PAGE_EXECUTE_WRITECOPY";
|
|
case PAGE_GUARD:
|
|
return "PAGE_GUARD";
|
|
case PAGE_NOCACHE:
|
|
return "PAGE_NOCACHE";
|
|
case PAGE_WRITECOMBINE:
|
|
return "PAGE_WRITECOMBINE";
|
|
}
|
|
return "<unk>";
|
|
}
|
|
|
|
//
|
|
//
|
|
// VirtualQueryEx
|
|
//
|
|
//
|
|
// vq -a address
|
|
// vq -f filter <all address space>
|
|
//
|
|
///////////////////////////////////////////
|
|
|
|
DECLARE_API(vq)
|
|
{
|
|
INIT_API();
|
|
|
|
MEMORY_ADDRESS pVA = 0;
|
|
MEMORY_ADDRESS Filter = (MEMORY_ADDRESS)-1;
|
|
DWORD FilterSize = 0;
|
|
BOOL bAll = TRUE;
|
|
|
|
char * pCurrent = (char *)args;
|
|
|
|
if(0 < strlen(pCurrent))
|
|
{
|
|
while (isspace(*pCurrent)) pCurrent++;
|
|
if ('-' == *pCurrent ||
|
|
'/' == *pCurrent)
|
|
{
|
|
pCurrent++;
|
|
while (isspace(*pCurrent)) pCurrent++;
|
|
if ('a' == *pCurrent)
|
|
{
|
|
pCurrent++;
|
|
while (*pCurrent && isspace(*pCurrent)) pCurrent++;
|
|
pVA = GetExpression(pCurrent);
|
|
bAll = FALSE;
|
|
}
|
|
else if ('f' == *pCurrent)
|
|
{
|
|
pCurrent++;
|
|
while (*pCurrent && isspace(*pCurrent)) pCurrent++;
|
|
Filter = GetExpression(pCurrent);
|
|
dprintf("FILTER %08x\n",Filter);
|
|
}
|
|
else if ('s' == *pCurrent)
|
|
{
|
|
pCurrent++;
|
|
while (*pCurrent && isspace(*pCurrent)) pCurrent++;
|
|
FilterSize = (DWORD)GetExpression(pCurrent);
|
|
dprintf("Size %08x\n",FilterSize);
|
|
}
|
|
else
|
|
{
|
|
dprintf("usage: -a ADDR\n"
|
|
"usage: -F Filter <all address space>\n");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("no param\n");
|
|
}
|
|
|
|
|
|
ULONG_PTR Tot = 0;
|
|
MEMORY_BASIC_INFORMATION MemInfo;
|
|
SIZE_T dwRet = 0;
|
|
|
|
do
|
|
{
|
|
dwRet = VirtualQueryEx(hCurrentProcess,(LPCVOID)pVA,&MemInfo,sizeof(MemInfo));
|
|
|
|
if (dwRet &&
|
|
(MemInfo.AllocationProtect & Filter) &&
|
|
(MemInfo.RegionSize > FilterSize))
|
|
{
|
|
dprintf(" Base %p Size %p Alloc %p Prot %s %s %s %s\n",
|
|
MemInfo.BaseAddress,
|
|
MemInfo.RegionSize,
|
|
MemInfo.AllocationBase,
|
|
GetProtect(MemInfo.AllocationProtect),
|
|
GetState(MemInfo.State),
|
|
GetProtect(MemInfo.Protect),
|
|
GetType(MemInfo.Type));
|
|
Tot += MemInfo.RegionSize;
|
|
}
|
|
|
|
pVA = (ULONG_PTR)MemInfo.BaseAddress + (ULONG_PTR)MemInfo.RegionSize;
|
|
|
|
if (CheckControlC())
|
|
break;
|
|
} while (dwRet && bAll);
|
|
|
|
dprintf(" Total %p\n",Tot);
|
|
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
//
|
|
|
|
#ifdef KDEXT_64BIT
|
|
|
|
struct _HEAP_ENTRY_64
|
|
{
|
|
WORD Size ;
|
|
WORD PreviousSize ;
|
|
BYTE SegmentIndex ;
|
|
BYTE Flags ;
|
|
BYTE UnusedBytes ;
|
|
BYTE SmallTagIndex;
|
|
ULONG64 Pointer;
|
|
};
|
|
|
|
#endif /*KDEXT_64BIT*/
|
|
|
|
DECLARE_API(hef64)
|
|
{
|
|
INIT_API();
|
|
#ifdef KDEXT_64BIT
|
|
|
|
_HEAP_ENTRY_64 HeapEntry;
|
|
ULONG64 MemAddr = GetExpression(args);
|
|
ULONG64 pVTable = 0;
|
|
|
|
if (MemAddr)
|
|
{
|
|
if (ReadMemory(MemAddr,&HeapEntry,sizeof(HeapEntry),NULL))
|
|
{
|
|
dprintf(" %p: %04x - %04x [%02x] (%x)\n",MemAddr,HeapEntry.Size,HeapEntry.PreviousSize,HeapEntry.Flags,HeapEntry.Size*sizeof(_HEAP_ENTRY_64)-HeapEntry.UnusedBytes);
|
|
GetVTable(MemAddr + sizeof(_HEAP_ENTRY_64));
|
|
MemAddr = MemAddr+HeapEntry.Size*sizeof(_HEAP_ENTRY_64);
|
|
|
|
// 0x10 is LAST_ENTRY
|
|
while(!(HeapEntry.Flags & 0x10))
|
|
{
|
|
if (ReadMemory(MemAddr,&HeapEntry,sizeof(HeapEntry),NULL))
|
|
{
|
|
dprintf(" %p: %04x - %04x [%02x] (%x)\n",MemAddr,HeapEntry.Size,HeapEntry.PreviousSize,HeapEntry.Flags,HeapEntry.Size*sizeof(_HEAP_ENTRY_64)-HeapEntry.UnusedBytes);
|
|
GetVTable(MemAddr + sizeof(_HEAP_ENTRY_64));
|
|
MemAddr = MemAddr+HeapEntry.Size*sizeof(_HEAP_ENTRY_64);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",MemAddr);
|
|
break;
|
|
}
|
|
if (CheckControlC())
|
|
break;
|
|
}
|
|
dprintf("last %p\n",MemAddr);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",MemAddr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("unable to resolve %s\n",args);
|
|
}
|
|
|
|
#endif /*KDEXT_64BIT*/
|
|
}
|
|
|
|
DECLARE_API(heb64)
|
|
{
|
|
INIT_API();
|
|
#ifdef KDEXT_64BIT
|
|
|
|
_HEAP_ENTRY_64 HeapEntry;
|
|
ULONG64 MemAddr = GetExpression(args);
|
|
ULONG64 pVTable = 0;
|
|
|
|
if (MemAddr)
|
|
{
|
|
if (ReadMemory(MemAddr,&HeapEntry,sizeof(HeapEntry),NULL))
|
|
{
|
|
dprintf(" %p: %04x - %04x [%02x] (%x)\n",MemAddr,HeapEntry.Size,HeapEntry.PreviousSize,HeapEntry.Flags,HeapEntry.Size*sizeof(_HEAP_ENTRY_64)-HeapEntry.UnusedBytes);
|
|
GetVTable(MemAddr + sizeof(_HEAP_ENTRY_64));
|
|
MemAddr = MemAddr - HeapEntry.PreviousSize*sizeof(_HEAP_ENTRY_64);
|
|
|
|
// 0x10 is LAST_ENTRY
|
|
while(HeapEntry.PreviousSize)
|
|
{
|
|
if (ReadMemory(MemAddr,&HeapEntry,sizeof(HeapEntry),NULL))
|
|
{
|
|
dprintf(" %p: %04x - %04x [%02x] (%x)\n",MemAddr,HeapEntry.Size,HeapEntry.PreviousSize,HeapEntry.Flags,HeapEntry.Size*sizeof(_HEAP_ENTRY_64)-HeapEntry.UnusedBytes);
|
|
GetVTable(MemAddr + sizeof(_HEAP_ENTRY_64));
|
|
MemAddr = MemAddr - HeapEntry.PreviousSize*sizeof(_HEAP_ENTRY_64);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",MemAddr);
|
|
break;
|
|
}
|
|
if (CheckControlC())
|
|
break;
|
|
}
|
|
|
|
dprintf("last %p\n",MemAddr);
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",MemAddr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("unable to resolve %s\n",args);
|
|
}
|
|
|
|
#endif /*KDEXT_64BIT*/
|
|
}
|
|
|
|
|
|
DECLARE_API(hps64)
|
|
{
|
|
INIT_API();
|
|
#ifdef KDEXT_64BIT
|
|
|
|
ULONG64 Peb = GetExpression(args);
|
|
|
|
if (!Peb)
|
|
{
|
|
Peb = 0x6fbfffde000;
|
|
}
|
|
|
|
ULONG NumberOfHeapsOffset;
|
|
ULONG HeapsOffset;
|
|
|
|
ULONG SegmentsOffset;
|
|
|
|
if ( Peb &&
|
|
(0 == GetFieldOffset("ntdll!_PEB","NumberOfHeaps",&NumberOfHeapsOffset)) &&
|
|
(0 == GetFieldOffset("ntdll!_PEB","ProcessHeaps",&HeapsOffset)) &&
|
|
(0 == GetFieldOffset("ntdll!_HEAP","Segments",&SegmentsOffset)))
|
|
{
|
|
//dprintf(" %x %x\n",NumberOfHeapsOffset,HeapsOffset);
|
|
ULONG nHeaps;
|
|
ULONG64 MemAddr;
|
|
if (ReadMemory(Peb+NumberOfHeapsOffset,&nHeaps,sizeof(ULONG),NULL))
|
|
{
|
|
//dprintf("nHeaps %08x\n",nHeaps);
|
|
ReadMemory(Peb+HeapsOffset,&MemAddr,sizeof(ULONG64),NULL);
|
|
ULONG64 * pHeaps = (ULONG64 *)_alloca(sizeof(ULONG64)*(DWORD)nHeaps);
|
|
ReadMemory(MemAddr,pHeaps,sizeof(ULONG64)*(DWORD)nHeaps,NULL);
|
|
|
|
// +0x0a0 Segments : [64] 0x000006fb`f9fa0c50
|
|
|
|
ULONG64 Segments[64];
|
|
|
|
for(ULONG i=0;i<nHeaps;i++)
|
|
{
|
|
if (ReadMemory(pHeaps[i]+SegmentsOffset,Segments,sizeof(Segments),NULL))
|
|
{
|
|
for (DWORD j=0;j<64;j++)
|
|
{
|
|
if (Segments[j])
|
|
{
|
|
dprintf(" S %p\n",Segments[j]);
|
|
}
|
|
if (CheckControlC())
|
|
break;
|
|
}
|
|
}
|
|
dprintf(" %p\n",pHeaps[i]);
|
|
|
|
if (CheckControlC())
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("RM %p\n",Peb+NumberOfHeapsOffset);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("check symbols for ntdll.dll or validate %p as PEB\n",Peb);
|
|
}
|
|
#endif /*KDEXT_64BIT*/
}
|