mirror of https://github.com/tongzx/nt5src
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.
2420 lines
73 KiB
2420 lines
73 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
pool.c
|
|
|
|
Abstract:
|
|
|
|
WinDbg Extension Api
|
|
|
|
Author:
|
|
|
|
Lou Perazzoli (Loup) 5-Nov-1993
|
|
|
|
Environment:
|
|
|
|
User Mode.
|
|
|
|
Revision History:
|
|
|
|
Kshitiz K. Sharma (kksharma)
|
|
|
|
Using debugger type info.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
typedef struct _POOL_BLOCK_HEAD {
|
|
// POOL_HEADER Header;
|
|
LIST_ENTRY List;
|
|
} POOL_BLOCK_HEAD, *PPOOL_BLOCK_HEADER;
|
|
|
|
typedef struct _POOL_HACKER {
|
|
// POOL_HEADER Header;
|
|
ULONG Contents[8];
|
|
} POOL_HACKER;
|
|
|
|
|
|
#define TAG 0
|
|
#define NONPAGED_ALLOC 1
|
|
#define NONPAGED_FREE 2
|
|
#define PAGED_ALLOC 3
|
|
#define PAGED_FREE 4
|
|
#define NONPAGED_USED 5
|
|
#define PAGED_USED 6
|
|
|
|
BOOL NewPool;
|
|
ULONG SortBy;
|
|
|
|
typedef struct _FILTER {
|
|
ULONG Tag;
|
|
BOOLEAN Exclude;
|
|
} FILTER, *PFILTER;
|
|
|
|
#define MAX_FILTER 64
|
|
FILTER Filter[MAX_FILTER];
|
|
|
|
ULONG64 SpecialPoolStart;
|
|
ULONG64 SpecialPoolEnd;
|
|
ULONG64 PoolBigTableAddress;
|
|
|
|
|
|
#define DecodeLink(Pool) ( (ULONG64) (Pool & (ULONG64) ~1))
|
|
|
|
//
|
|
// Size of a pool page.
|
|
//
|
|
// This must be greater than or equal to the page size.
|
|
//
|
|
|
|
#define POOL_PAGE_SIZE PageSize
|
|
|
|
//
|
|
// The smallest pool block size must be a multiple of the page size.
|
|
//
|
|
// Define the block size as 32.
|
|
//
|
|
|
|
|
|
#define POOL_LIST_HEADS (POOL_PAGE_SIZE / (1 << POOL_BLOCK_SHIFT))
|
|
|
|
|
|
|
|
#define SPECIAL_POOL_BLOCK_SIZE(PoolHeader_Ulong1) (PoolHeader_Ulong1 & (MI_SPECIAL_POOL_VERIFIER - 1))
|
|
|
|
#ifndef _EXTFNS_H
|
|
// GetPoolTagDescription
|
|
typedef HRESULT
|
|
(WINAPI *PGET_POOL_TAG_DESCRIPTION)(
|
|
ULONG PoolTag,
|
|
PSTR *pDescription
|
|
);
|
|
#endif
|
|
|
|
ULONG64
|
|
GetSpecialPoolHeader (
|
|
IN PVOID DataPage,
|
|
IN ULONG64 RealDataPage,
|
|
OUT PULONG64 ReturnedDataStart
|
|
);
|
|
|
|
int __cdecl
|
|
ulcomp(const void *e1,const void *e2)
|
|
{
|
|
ULONG u1;
|
|
LONG64 diff;
|
|
ULONG64 ValE1, ValE2;
|
|
|
|
switch (SortBy) {
|
|
case TAG:
|
|
|
|
GetFieldValue(*((PULONG64) e1), "nt!_POOL_TRACKER_TABLE", "Key", ValE1);
|
|
GetFieldValue(*((PULONG64) e2), "nt!_POOL_TRACKER_TABLE", "Key", ValE2);
|
|
|
|
u1 = ((PUCHAR)&ValE1)[0] - ((PUCHAR)&ValE2)[0];
|
|
if (u1 != 0) {
|
|
return u1;
|
|
}
|
|
u1 = ((PUCHAR)&ValE1)[1] - ((PUCHAR)&ValE2)[1];
|
|
if (u1 != 0) {
|
|
return u1;
|
|
}
|
|
u1 = ((PUCHAR)&ValE1)[2] - ((PUCHAR)&ValE2)[2];
|
|
if (u1 != 0) {
|
|
return u1;
|
|
}
|
|
u1 = ((PUCHAR)&ValE1)[3] - ((PUCHAR)&ValE2)[3];
|
|
return u1;
|
|
break;
|
|
|
|
case NONPAGED_ALLOC:
|
|
GetFieldValue(*((PULONG64) e1), "nt!_POOL_TRACKER_TABLE", "NonPagedAllocs", ValE1);
|
|
GetFieldValue(*((PULONG64) e2), "nt!_POOL_TRACKER_TABLE", "NonPagedAllocs", ValE2);
|
|
diff = ValE2 - ValE1;
|
|
return( diff ? ( diff > 0 ? 1 : -1 ) : 0 );
|
|
break;
|
|
|
|
case NONPAGED_FREE:
|
|
GetFieldValue(*((PULONG64) e1), "nt!_POOL_TRACKER_TABLE", "NonPagedFrees", ValE1);
|
|
GetFieldValue(*((PULONG64) e2), "nt!_POOL_TRACKER_TABLE", "NonPagedFrees", ValE2);
|
|
diff = ValE2 - ValE1;
|
|
return( diff ? ( diff > 0 ? 1 : -1 ) : 0 );
|
|
break;
|
|
|
|
case NONPAGED_USED:
|
|
GetFieldValue(*((PULONG64) e1), "nt!_POOL_TRACKER_TABLE", "NonPagedBytes", ValE1);
|
|
GetFieldValue(*((PULONG64) e2), "nt!_POOL_TRACKER_TABLE", "NonPagedBytes", ValE2);
|
|
diff = ValE2 - ValE1;
|
|
return( diff ? ( diff > 0 ? 1 : -1 ) : 0 );
|
|
break;
|
|
|
|
case PAGED_USED:
|
|
GetFieldValue(*((PULONG64) e1), "nt!_POOL_TRACKER_TABLE", "PagedBytes", ValE1);
|
|
GetFieldValue(*((PULONG64) e2), "nt!_POOL_TRACKER_TABLE", "PagedBytes", ValE2);
|
|
diff = ValE2 - ValE1;
|
|
return( diff ? ( diff > 0 ? 1 : -1 ) : 0 );
|
|
break;
|
|
|
|
default:
|
|
return(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets up generally useful pool globals.
|
|
|
|
Must be called in every DECLARE_API interface that uses pool.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
LOGICAL PoolInitialized = FALSE;
|
|
|
|
LOGICAL
|
|
PoolInitializeGlobals(
|
|
VOID
|
|
)
|
|
{
|
|
if (PoolInitialized == TRUE) {
|
|
return TRUE;
|
|
}
|
|
|
|
SpecialPoolStart = GetPointerValue("nt!MmSpecialPoolStart");
|
|
SpecialPoolEnd = GetPointerValue("nt!MmSpecialPoolEnd");
|
|
|
|
|
|
if (PageSize < 0x1000 || (PageSize & (ULONG)0xFFF)) {
|
|
dprintf ("unable to get MmPageSize (0x%x) - probably bad symbols\n", PageSize);
|
|
return FALSE;
|
|
}
|
|
|
|
PoolInitialized = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DECLARE_API( frag )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump pool fragmentation
|
|
|
|
Arguments:
|
|
|
|
args - Flags
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Flags;
|
|
ULONG result;
|
|
ULONG i;
|
|
ULONG count;
|
|
ULONG64 Pool;
|
|
ULONG64 PoolLoc1;
|
|
ULONG TotalFrag;
|
|
ULONG TotalCount;
|
|
ULONG Frag;
|
|
ULONG64 PoolStart;
|
|
ULONG PoolOverhead;
|
|
ULONG64 PoolLoc;
|
|
ULONG PoolTag, BlockSize, PreviousSize, PoolIndex;
|
|
ULONG TotalPages, TotalBigPages;
|
|
ULONG64 Flink, Blink;
|
|
PCHAR pc;
|
|
ULONG64 tmp;
|
|
|
|
#define PoolBlk(F,V) GetFieldValue(Pool, "nt!_POOL_BLOCK_HEAD", #F, V)
|
|
|
|
if (PoolInitializeGlobals() == FALSE) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
dprintf("\n NonPaged Pool Fragmentation\n\n");
|
|
Flags = 0;
|
|
PoolStart = 0;
|
|
|
|
if (GetExpressionEx(args, &tmp, &args)) {
|
|
Flags = (ULONG) tmp;
|
|
PoolStart = GetExpression (args);
|
|
}
|
|
|
|
PoolOverhead = GetTypeSize("nt!_POOL_HEADER");
|
|
if (PoolStart != 0) {
|
|
PoolStart += PoolOverhead;
|
|
|
|
Pool = DecodeLink(PoolStart);
|
|
do {
|
|
|
|
Pool = Pool - PoolOverhead;
|
|
if ( PoolBlk(Header.PoolTag, PoolTag) ) {
|
|
dprintf("%08p: Unable to get contents of pool block\n", Pool );
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
PoolBlk(Header.BlockSize,BlockSize);
|
|
PoolBlk(Header.PreviousSize,PreviousSize);
|
|
PoolBlk(List.Flink,Flink);
|
|
PoolBlk(List.Blink,Blink);
|
|
|
|
dprintf(" %p size: %4lx previous size: %4lx %c%c%c%c links: %8p %8p\n",
|
|
Pool,
|
|
(ULONG)BlockSize << POOL_BLOCK_SHIFT,
|
|
(ULONG)PreviousSize << POOL_BLOCK_SHIFT,
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(PoolTag),
|
|
PP(PoolTag >> 8),
|
|
PP(PoolTag >> 16),
|
|
PP((PoolTag&~PROTECTED_POOL) >> 24),
|
|
#undef PP
|
|
Flink,
|
|
Blink);
|
|
|
|
if (Flags != 3) {
|
|
Pool = Flink;
|
|
} else {
|
|
Pool = Blink;
|
|
}
|
|
|
|
Pool = DecodeLink(Pool);
|
|
|
|
if (CheckControlC()) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
} while ( (Pool & (ULONG64) ~1) != (PoolStart & (ULONG64) ~1) );
|
|
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
PoolLoc1 = GetNtDebuggerData( NonPagedPoolDescriptor );
|
|
|
|
if (PoolLoc1 == 0) {
|
|
dprintf ("unable to get nonpaged pool head\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
PoolLoc = PoolLoc1;
|
|
|
|
TotalFrag = 0;
|
|
TotalCount = 0;
|
|
|
|
for (i = 0; i < POOL_LIST_HEADS; i += 1) {
|
|
CHAR Buffer[40];
|
|
ULONG ListOffset;
|
|
|
|
sprintf(Buffer, "ListHeads[%d].Flink", i);
|
|
Frag = 0;
|
|
count = 0;
|
|
|
|
if (GetFieldValue(PoolLoc, "nt!_POOL_DESCRIPTOR", Buffer, Pool)) {
|
|
dprintf ("%08p: Unable to get pool descriptor\n", PoolLoc1);
|
|
return E_INVALIDARG;
|
|
}
|
|
GetFieldOffset("nt!_POOL_DESCRIPTOR", Buffer, &ListOffset);
|
|
|
|
// Pool = (PUCHAR)PoolDesc.ListHeads[i].Flink;
|
|
Pool = DecodeLink(Pool);
|
|
|
|
while (Pool != (ListOffset + PoolLoc)) {
|
|
|
|
Pool = Pool - PoolOverhead;
|
|
if ( PoolBlk(Header.PoolTag, PoolTag) ) {
|
|
dprintf("%08p: Unable to get contents of pool block\n", Pool );
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
PoolBlk(Header.BlockSize,BlockSize);
|
|
PoolBlk(Header.PreviousSize,PreviousSize);
|
|
PoolBlk(List.Flink,Flink);
|
|
|
|
Frag += BlockSize << POOL_BLOCK_SHIFT;
|
|
count += 1;
|
|
|
|
if (Flags & 2) {
|
|
dprintf(" ListHead[%x]: %p size: %4lx previous size: %4lx %c%c%c%c\n",
|
|
i,
|
|
(ULONG)Pool,
|
|
(ULONG)BlockSize << POOL_BLOCK_SHIFT,
|
|
(ULONG)PreviousSize << POOL_BLOCK_SHIFT,
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(PoolTag),
|
|
PP(PoolTag >> 8),
|
|
PP(PoolTag >> 16),
|
|
PP((PoolTag&~PROTECTED_POOL) >> 24));
|
|
#undef PP
|
|
}
|
|
Pool = Flink;
|
|
Pool = DecodeLink(Pool);
|
|
|
|
if (CheckControlC()) {
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
if (Flags & 1) {
|
|
dprintf("index: %2ld number of fragments: %5ld bytes: %6ld\n",
|
|
i,count,Frag);
|
|
}
|
|
TotalFrag += Frag;
|
|
TotalCount += count;
|
|
}
|
|
|
|
dprintf("\n Number of fragments: %7ld consuming %7ld bytes\n",
|
|
TotalCount,TotalFrag);
|
|
GetFieldValue(PoolLoc, "nt!_POOL_DESCRIPTOR", "TotalPages",TotalPages);
|
|
GetFieldValue(PoolLoc, "nt!_POOL_DESCRIPTOR", "TotalBigPages", TotalBigPages);
|
|
|
|
dprintf( " NonPagedPool Usage: %7ld bytes\n",(TotalPages + TotalBigPages)*PageSize);
|
|
return S_OK;
|
|
#undef PoolBlk
|
|
}
|
|
|
|
|
|
PRTL_BITMAP
|
|
GetBitmap(
|
|
ULONG64 pBitmap
|
|
)
|
|
{
|
|
PRTL_BITMAP p;
|
|
ULONG Size, Result;
|
|
ULONG64 Buffer=0;
|
|
|
|
if ( GetFieldValue(pBitmap, "nt!_RTL_BITMAP", "Buffer", Buffer)) {
|
|
dprintf("%08p: Unable to get contents of bitmap\n", pBitmap );
|
|
return 0;
|
|
}
|
|
GetFieldValue(pBitmap, "nt!_RTL_BITMAP", "SizeOfBitMap", Size);
|
|
|
|
p = HeapAlloc( GetProcessHeap(), 0, sizeof( *p ) + (Size / 8) );
|
|
if (p) {
|
|
p->SizeOfBitMap = Size;
|
|
p->Buffer = (PULONG)(p + 1);
|
|
if ( !ReadMemory( Buffer,
|
|
p->Buffer,
|
|
Size / 8,
|
|
&Result) ) {
|
|
dprintf("%08p: Unable to get contents of bitmap buffer\n", Buffer );
|
|
HeapFree( GetProcessHeap(), 0, p );
|
|
p = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
return p;
|
|
}
|
|
|
|
VOID
|
|
DumpPool(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG64 p, pStart;
|
|
ULONG64 Size;
|
|
ULONG BusyFlag;
|
|
ULONG CurrentPage, NumberOfPages;
|
|
PRTL_BITMAP StartMap;
|
|
PRTL_BITMAP EndMap;
|
|
ULONG64 PagedPoolStart;
|
|
ULONG64 PagedPoolEnd;
|
|
ULONG Result;
|
|
UCHAR PgPool[] = "nt!_MM_PAGED_POOL_INFO";
|
|
ULONG64 PagedPoolInfoPointer;
|
|
ULONG64 PagedPoolAllocationMap=0, EndOfPagedPoolBitmap=0;
|
|
|
|
if (PoolInitializeGlobals() == FALSE) {
|
|
return;
|
|
}
|
|
|
|
PagedPoolInfoPointer = GetNtDebuggerData( MmPagedPoolInformation );
|
|
|
|
if ( GetFieldValue( PagedPoolInfoPointer,
|
|
PgPool,
|
|
"PagedPoolAllocationMap",
|
|
PagedPoolAllocationMap)) {
|
|
dprintf("%08p: Unable to get contents of paged pool information\n",
|
|
PagedPoolInfoPointer );
|
|
return;
|
|
}
|
|
|
|
GetFieldValue( PagedPoolInfoPointer, PgPool, "EndOfPagedPoolBitmap", EndOfPagedPoolBitmap);
|
|
|
|
|
|
StartMap = GetBitmap( PagedPoolAllocationMap );
|
|
EndMap = GetBitmap( EndOfPagedPoolBitmap );
|
|
|
|
PagedPoolStart = GetNtDebuggerDataPtrValue( MmPagedPoolStart );
|
|
PagedPoolEnd = GetNtDebuggerDataPtrValue( MmPagedPoolEnd );
|
|
|
|
if (StartMap && EndMap) {
|
|
p = PagedPoolStart;
|
|
CurrentPage = 0;
|
|
dprintf( "Paged Pool: %p .. %p\n", PagedPoolStart, PagedPoolEnd );
|
|
|
|
while (p < PagedPoolEnd) {
|
|
if ( CheckControlC() ) {
|
|
return;
|
|
}
|
|
pStart = p;
|
|
BusyFlag = RtlCheckBit( StartMap, CurrentPage );
|
|
while ( ~(BusyFlag ^ RtlCheckBit( StartMap, CurrentPage )) ) {
|
|
p += PageSize;
|
|
if (RtlCheckBit( EndMap, CurrentPage )) {
|
|
CurrentPage++;
|
|
break;
|
|
}
|
|
|
|
CurrentPage++;
|
|
if (p > PagedPoolEnd) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Size = p - pStart;
|
|
dprintf( "%p: %I64x - %s\n", pStart, Size, BusyFlag ? "busy" : "free" );
|
|
}
|
|
}
|
|
|
|
HeapFree( GetProcessHeap(), 0, StartMap );
|
|
HeapFree( GetProcessHeap(), 0, EndMap );
|
|
}
|
|
|
|
void
|
|
PrintPoolTagComponent(
|
|
ULONG PoolTag
|
|
)
|
|
{
|
|
PGET_POOL_TAG_DESCRIPTION GetPoolTagDescription;
|
|
PSTR TagComponent;
|
|
#ifdef _EXTFNS_H
|
|
DEBUG_POOLTAG_DESCRIPTION Desc = {0};
|
|
Desc.SizeOfStruct = sizeof(DEBUG_POOLTAG_DESCRIPTION);
|
|
GetPoolTagDescription = NULL;
|
|
if ((GetExtensionFunction("GetPoolTagDescription", (FARPROC*) &GetPoolTagDescription) != S_OK) ||
|
|
!GetPoolTagDescription) {
|
|
return;
|
|
}
|
|
|
|
(*GetPoolTagDescription)(PoolTag, &Desc);
|
|
|
|
if (Desc.Description[0]) {
|
|
dprintf("\t\tPooltag %4.4s : %s", &PoolTag, Desc.Description);
|
|
if (Desc.Binary[0]) {
|
|
dprintf(", Binary : %s",Desc.Binary);
|
|
}
|
|
if (Desc.Owner[0]) {
|
|
dprintf(", Owner : %s", Desc.Owner);
|
|
}
|
|
dprintf("\n");
|
|
} else {
|
|
dprintf("\t\tOwning component : Unknown (update pooltag.txt)\n");
|
|
}
|
|
|
|
#else
|
|
GetPoolTagDescription = NULL;
|
|
if ((GetExtensionFunction("GetPoolTagDescription", (FARPROC*) &GetPoolTagDescription) != S_OK) ||
|
|
!GetPoolTagDescription) {
|
|
return;
|
|
}
|
|
|
|
(*GetPoolTagDescription)(PoolTag, &TagComponent);
|
|
if (TagComponent && (100 < (ULONG64) TagComponent)) {
|
|
dprintf("\t\tOwning component : %s\n", TagComponent);
|
|
} else {
|
|
dprintf("\t\tOwning component : Unknown (update pooltag.txt)\n");
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
PSTR g_PoolRegion[DbgPoolRegionMax] = {
|
|
"Unknown", // DbgPoolRegionUnknown,
|
|
"Special pool", // DbgPoolRegionSpecial,
|
|
"Paged pool", // DbgPoolRegionPaged,
|
|
"Nonpaged pool", // DbgPoolRegionNonPaged,
|
|
"Pool code", // DbgPoolRegionCode,
|
|
"Nonpaged pool expansion", // DbgPoolRegionNonPagedExpansion,
|
|
};
|
|
|
|
DEBUG_POOL_REGION
|
|
GetPoolRegion(
|
|
ULONG64 Pool
|
|
)
|
|
{
|
|
static ULONG64 PoolCodeEnd;
|
|
static ULONG64 SpecialPoolEnd;
|
|
static ULONG64 PagedPoolEnd;
|
|
static ULONG64 NonPagedPoolEnd;
|
|
static ULONG64 NonPagedPoolStart;
|
|
static ULONG64 SpecialPoolStart;
|
|
static ULONG64 PagedPoolStart;
|
|
static ULONG64 NonPagedPoolExpansionStart;
|
|
static ULONG64 PoolCodeStart;
|
|
static BOOL GotAll = FALSE;
|
|
|
|
if (!GotAll) {
|
|
PoolCodeEnd = GetPointerValue("nt!MmPoolCodeEnd");
|
|
SpecialPoolEnd = GetPointerValue("nt!MmSpecialPoolEnd");
|
|
PagedPoolEnd = GetPointerValue("nt!MmPagedPoolEnd");
|
|
NonPagedPoolEnd = GetPointerValue("nt!MmNonPagedPoolEnd");
|
|
NonPagedPoolStart = GetPointerValue("nt!MmNonPagedPoolStart");
|
|
SpecialPoolStart = GetPointerValue("nt!MmSpecialPoolStart");
|
|
PagedPoolStart = GetPointerValue("nt!MmPagedPoolStart");
|
|
NonPagedPoolExpansionStart = GetPointerValue("nt!MmNonPagedPoolExpansionStart");
|
|
PoolCodeStart = GetPointerValue("nt!MmPoolCodeStart");
|
|
GotAll = TRUE;
|
|
}
|
|
if (!(PoolCodeStart || SpecialPoolStart || SpecialPoolEnd || PoolCodeEnd ||
|
|
NonPagedPoolExpansionStart || NonPagedPoolStart || NonPagedPoolEnd ||
|
|
PagedPoolStart || PagedPoolEnd)) {
|
|
GotAll = FALSE;
|
|
return DbgPoolRegionUnknown;
|
|
}
|
|
if ( Pool >= SpecialPoolStart && Pool < SpecialPoolEnd) {
|
|
return DbgPoolRegionSpecial;
|
|
} else if ( Pool >= PagedPoolStart && Pool < PagedPoolEnd) {
|
|
return DbgPoolRegionPaged;
|
|
} else if ( Pool >= NonPagedPoolStart && Pool < NonPagedPoolEnd) {
|
|
return DbgPoolRegionNonPaged;
|
|
} else if ( Pool >= PoolCodeStart && Pool < PoolCodeEnd) {
|
|
return DbgPoolRegionCode;
|
|
} else if ( Pool >= NonPagedPoolExpansionStart) {
|
|
return DbgPoolRegionNonPagedExpansion;
|
|
} else {
|
|
return DbgPoolRegionUnknown;
|
|
}
|
|
return DbgPoolRegionUnknown;
|
|
}
|
|
|
|
void
|
|
PrintPoolRegion(
|
|
ULONG64 Pool
|
|
)
|
|
{
|
|
PSTR pszRegion;
|
|
DEBUG_POOL_REGION Region;
|
|
|
|
|
|
Region = GetPoolRegion(Pool);
|
|
|
|
pszRegion = g_PoolRegion[Region];
|
|
if (pszRegion) {
|
|
dprintf(pszRegion);
|
|
dprintf("\n");
|
|
} else {
|
|
dprintf("Region unkown\n", Pool);
|
|
}
|
|
|
|
}
|
|
HRESULT
|
|
ListPoolPage(
|
|
ULONG64 PoolPageToDump,
|
|
ULONG Flags,
|
|
PDEBUG_POOL_DATA PoolData
|
|
)
|
|
{
|
|
ULONG64 PoolTableAddress;
|
|
ULONG result;
|
|
ULONG PoolTag;
|
|
ULONG Result;
|
|
ULONG64 StartPage;
|
|
ULONG64 Pool;
|
|
ULONG PoolBlockSize;
|
|
ULONG PoolHeaderSize;
|
|
ULONG64 PoolHeader;
|
|
ULONG Previous;
|
|
UCHAR c;
|
|
PUCHAR p;
|
|
ULONG64 PoolDataEnd;
|
|
UCHAR PoolBlockPattern;
|
|
UCHAR DataPage[0x5000];
|
|
PUCHAR DataStart;
|
|
ULONG64 RealDataStart;
|
|
LOGICAL Pagable;
|
|
LOGICAL FirstBlock;
|
|
ULONG BlockType;
|
|
ULONG PoolWhere;
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG ct;
|
|
ULONG start;
|
|
ULONG PoolBigPageTableSize;
|
|
ULONG SizeOfPoolHdr=GetTypeSize("nt!_POOL_HEADER");
|
|
|
|
if (!SpecialPoolStart) {
|
|
SpecialPoolStart = GetPointerValue("nt!MmSpecialPoolStart");
|
|
SpecialPoolEnd = GetPointerValue("nt!MmSpecialPoolEnd");
|
|
}
|
|
|
|
Pool = PAGE_ALIGN64 (PoolPageToDump);
|
|
StartPage = Pool;
|
|
Previous = 0;
|
|
|
|
if (PoolData) {
|
|
ZeroMemory(PoolData, sizeof(DEBUG_POOL_DATA));
|
|
}
|
|
if (!(Flags & 0x80000000)) {
|
|
dprintf("Pool page %p region is ", PoolPageToDump);
|
|
PrintPoolRegion(PoolPageToDump);
|
|
}
|
|
|
|
|
|
if ( Pool >= SpecialPoolStart && Pool < SpecialPoolEnd) {
|
|
ULONG Hdr_Ulong=0;
|
|
|
|
// LWFIX: this is not ported yet.
|
|
// dprintf("reading %I64x datapage %x\n", Pool, min(PageSize, sizeof(DataPage)));
|
|
// Pre read the pool
|
|
if ( !ReadMemory( Pool,
|
|
&DataPage[0],
|
|
min(PageSize, sizeof(DataPage)),
|
|
&Result) ) {
|
|
dprintf("%08p: Unable to get contents of special pool block\n", Pool );
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if ( GetFieldValue( Pool, "nt!_POOL_HEADER", "Ulong1", Hdr_Ulong)) {
|
|
dprintf("%08p: Unable to get nt!_POOL_HEADER\n", Pool );
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// Determine whether the data is at the start or end of the page.
|
|
// Start off by assuming the data is at the end, in this case the
|
|
// header will be at the start.
|
|
//
|
|
|
|
PoolHeader = GetSpecialPoolHeader((PVOID) &DataPage[0], Pool, &RealDataStart);
|
|
|
|
if (PoolHeader == 0) {
|
|
dprintf("Block %p is a corrupted special pool allocation\n",
|
|
PoolPageToDump);
|
|
return E_INVALIDARG;
|
|
}
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "Ulong1", Hdr_Ulong);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolTag", PoolTag);
|
|
PoolBlockSize = SPECIAL_POOL_BLOCK_SIZE(Hdr_Ulong);
|
|
|
|
if (Hdr_Ulong & MI_SPECIAL_POOL_PAGABLE) {
|
|
Pagable = TRUE;
|
|
} else {
|
|
Pagable = FALSE;
|
|
}
|
|
|
|
if (PoolData) {
|
|
PoolData->Pool = RealDataStart;
|
|
PoolData->PoolBlock = PoolPageToDump;
|
|
PoolData->SpecialPool = 1;
|
|
PoolData->Pageable = Hdr_Ulong & 0x8000 ? 1 : 0;
|
|
PoolData->Size = PoolBlockSize;
|
|
if (Flags & 0x80000000) {
|
|
// do not print anything
|
|
return S_OK;
|
|
}
|
|
}
|
|
dprintf("*%p size: %4lx %s special pool, Tag is %c%c%c%c\n",
|
|
RealDataStart,
|
|
PoolBlockSize,
|
|
Hdr_Ulong & 0x8000 ? "pagable" : "non-paged",
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(PoolTag),
|
|
PP(PoolTag >> 8),
|
|
PP(PoolTag >> 16),
|
|
PP(PoolTag >> 24)
|
|
);
|
|
#undef PP
|
|
PrintPoolTagComponent(PoolTag);
|
|
|
|
//
|
|
// Add code to validate whole block.
|
|
//
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
FirstBlock = TRUE;
|
|
|
|
while (PAGE_ALIGN64(Pool) == StartPage) {
|
|
ULONG BlockSize=0, PreviousSize=0, PoolType=0, AllocatorBackTraceIndex=0;
|
|
ULONG PoolTagHash=0, PoolIndex=0;
|
|
ULONG64 ProcessBilled=0;
|
|
|
|
if ( CheckControlC() ) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if ( GetFieldValue( Pool, "nt!_POOL_HEADER", "BlockSize", BlockSize) ) {
|
|
dprintf("%08p: Unable to get contents of pool block\n", Pool );
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (PoolPageToDump >= Pool &&
|
|
PoolPageToDump < (Pool + (BlockSize << POOL_BLOCK_SHIFT))
|
|
) {
|
|
c = '*';
|
|
} else {
|
|
c = ' ';
|
|
}
|
|
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "PreviousSize", PreviousSize);
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "PoolType", PoolType);
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "PoolTag", PoolTag);
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "PoolTagHash", PoolTagHash);
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "PoolIndex", PoolIndex);
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "AllocatorBackTraceIndex", AllocatorBackTraceIndex);
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "ProcessBilled", ProcessBilled);
|
|
|
|
BlockType = 0;
|
|
|
|
if ((BlockSize << POOL_BLOCK_SHIFT) >= POOL_PAGE_SIZE) {
|
|
BlockType = 1;
|
|
} else if (BlockSize == 0) {
|
|
BlockType = 2;
|
|
} else if (PreviousSize != Previous) {
|
|
BlockType = 3;
|
|
}
|
|
|
|
if (BlockType != 0) {
|
|
ULONG BigPageSize = GetTypeSize ("nt!_POOL_TRACKER_BIG_PAGES");
|
|
|
|
if (!BigPageSize) {
|
|
dprintf("Cannot get _POOL_TRACKER_BIG_PAGES type size\n");
|
|
break;
|
|
}
|
|
|
|
//
|
|
// See if this is a big block allocation. Iff we have not parsed
|
|
// any other small blocks in here already.
|
|
//
|
|
|
|
if (FirstBlock == TRUE) {
|
|
|
|
if (!PoolBigTableAddress) {
|
|
PoolBigTableAddress = GetPointerValue ("PoolBigPageTable");
|
|
}
|
|
|
|
PoolTableAddress = PoolBigTableAddress;
|
|
|
|
if (PoolTableAddress) {
|
|
|
|
dprintf ("%p is not a valid small pool allocation, checking large pool...\n", Pool);
|
|
|
|
PoolBigPageTableSize = GetUlongValue ("PoolBigPageTableSize");
|
|
//
|
|
// Scan the table looking for a match.
|
|
//
|
|
|
|
i = 0;
|
|
ct = PageSize / BigPageSize;
|
|
|
|
while (i < PoolBigPageTableSize) {
|
|
ULONG64 Va=0;
|
|
ULONG Key=0, NumberOfPages=0;
|
|
|
|
if (PoolBigPageTableSize - i < ct) {
|
|
ct = PoolBigPageTableSize - i;
|
|
}
|
|
|
|
if ( GetFieldValue( PoolTableAddress,
|
|
"nt!_POOL_TRACKER_BIG_PAGES",
|
|
"Va",
|
|
Va) ) {
|
|
dprintf("%08p: Unable to get contents of pool block\n", PoolTableAddress );
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
for (j = 0; j < ct; j += 1) {
|
|
|
|
if ( GetFieldValue( PoolTableAddress + BigPageSize*j,
|
|
"nt!_POOL_TRACKER_BIG_PAGES",
|
|
"Va",
|
|
Va) ) {
|
|
dprintf("%08p: Unable to get contents of pool block\n", PoolTableAddress );
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (Va == PAGE_ALIGN64(Pool)) {
|
|
|
|
//
|
|
// Match !
|
|
//
|
|
GetFieldValue( PoolTableAddress + BigPageSize*j,
|
|
"nt!_POOL_TRACKER_BIG_PAGES",
|
|
"Key",
|
|
Key);
|
|
GetFieldValue( PoolTableAddress + BigPageSize*j,
|
|
"nt!_POOL_TRACKER_BIG_PAGES",
|
|
"NumberOfPages",
|
|
NumberOfPages);
|
|
PoolTag = Key;
|
|
|
|
if (PoolData) {
|
|
PoolData->Pool = PoolPageToDump;
|
|
PoolData->Size = NumberOfPages*PageSize;
|
|
PoolData->PoolTag = PoolTag;
|
|
PoolData->LargePool = 1;
|
|
PoolData->Free = (Pool & POOL_BIG_TABLE_ENTRY_FREE) ? 1 : 0;
|
|
if (Flags & 0x80000000) {
|
|
// do not print anything
|
|
return S_OK;
|
|
}
|
|
}
|
|
dprintf("*%p :%s large page allocation, Tag is %c%c%c%c, size is 0x%x bytes\n",
|
|
(Pool & ~POOL_BIG_TABLE_ENTRY_FREE),
|
|
(Pool & POOL_BIG_TABLE_ENTRY_FREE) ? "free " : "",
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(PoolTag),
|
|
PP(PoolTag >> 8),
|
|
PP(PoolTag >> 16),
|
|
PP(PoolTag >> 24),
|
|
NumberOfPages * PageSize
|
|
);
|
|
#undef PP
|
|
PrintPoolTagComponent(PoolTag);
|
|
return S_OK;
|
|
}
|
|
}
|
|
i += ct;
|
|
PoolTableAddress += (ct * BigPageSize);
|
|
}
|
|
|
|
//
|
|
// No match in small or large pool, must be
|
|
// freed or corrupt pool
|
|
//
|
|
|
|
dprintf("%p is freed (or corrupt) pool\n", Pool);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
dprintf("unable to get pool big page table - either wrong symbols or pool tagging is disabled\n");
|
|
}
|
|
|
|
if (BlockType == 1) {
|
|
dprintf("Bad allocation size @%p, too large\n", Pool);
|
|
return E_INVALIDARG;
|
|
} else if (BlockType == 2) {
|
|
dprintf("Bad allocation size @%p, zero is invalid\n", Pool);
|
|
return E_INVALIDARG;
|
|
} else if (BlockType == 3) {
|
|
dprintf("Bad previous allocation size @%p, last size was %lx\n",
|
|
Pool, Previous);
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "PoolTag", PoolTag);
|
|
|
|
|
|
if (!(Flags & 2) || c == '*') {
|
|
if (PoolData) {
|
|
PoolData->Pool = Pool;
|
|
PoolData->PoolBlock = PoolPageToDump;
|
|
PoolData->PoolTag = PoolTag & ~PROTECTED_POOL;
|
|
PoolData->ProcessBilled = ProcessBilled;
|
|
PoolData->PreviousSize = PreviousSize << POOL_BLOCK_SHIFT;
|
|
PoolData->Size = BlockSize << POOL_BLOCK_SHIFT;
|
|
PoolData->Free = ((PoolType != 0) && (!NewPool ?
|
|
(PoolIndex & 0x80) : (PoolType & 0x04))) ? 0 : 1;
|
|
PoolData->Protected = (PoolTag&PROTECTED_POOL) ? 1 : 0;
|
|
if (Flags & 0x80000000) {
|
|
// do not print anything
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
dprintf("%c%p size: %4lx previous size: %4lx ",
|
|
c,
|
|
Pool,
|
|
BlockSize << POOL_BLOCK_SHIFT,
|
|
PreviousSize << POOL_BLOCK_SHIFT);
|
|
|
|
if (PoolType == 0) {
|
|
|
|
//
|
|
// "Free " with a space after it before the parentheses means
|
|
// it's been freed to a (pool manager internal) lookaside list.
|
|
// We used to print "Lookaside" but that just confused driver
|
|
// writers because they didn't know if this meant in use or not
|
|
// and many would say "but I don't use lookaside lists - the
|
|
// extension or kernel is broken".
|
|
//
|
|
// "Free" with no space after it before the parentheses means
|
|
// it is not on a pool manager internal lookaside list and is
|
|
// instead on the regular pool manager internal flink/blink
|
|
// chains.
|
|
//
|
|
// Note to anyone using the pool package, these 2 terms are
|
|
// equivalent. The fine distinction is only for those actually
|
|
// writing pool internal code.
|
|
//
|
|
|
|
dprintf(" (Free)");
|
|
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
dprintf(" %c%c%c%c%c\n",
|
|
c,
|
|
PP(PoolTag),
|
|
PP(PoolTag >> 8),
|
|
PP(PoolTag >> 16),
|
|
PP((PoolTag&~PROTECTED_POOL) >> 24)
|
|
);
|
|
#undef PP
|
|
if (c=='*') {
|
|
PrintPoolTagComponent(PoolTag & ~PROTECTED_POOL);
|
|
}
|
|
} else {
|
|
|
|
if (!NewPool ? (PoolIndex & 0x80) : (PoolType & 0x04)) {
|
|
dprintf(" (Allocated)");
|
|
} else {
|
|
//
|
|
// "Free " with a space after it before the parentheses means
|
|
// it's been freed to a (pool manager internal) lookaside list.
|
|
// We used to print "Lookaside" but that just confused driver
|
|
// writers because they didn't know if this meant in use or not
|
|
// and many would say "but I don't use lookaside lists - the
|
|
// extension or kernel is broken".
|
|
//
|
|
// "Free" with no space after it before the parentheses means
|
|
// it is not on a pool manager internal lookaside list and is
|
|
// instead on the regular pool manager internal flink/blink
|
|
// chains.
|
|
//
|
|
// Note to anyone using the pool package, these 2 terms are
|
|
// equivalent. The fine distinction is only for those actually
|
|
// writing pool internal code.
|
|
//
|
|
dprintf(" (Free )");
|
|
}
|
|
if ((PoolType & POOL_QUOTA_MASK) == 0) {
|
|
/*
|
|
ULONG Key=0;
|
|
if (AllocatorBackTraceIndex != 0 &&
|
|
AllocatorBackTraceIndex & POOL_BACKTRACEINDEX_PRESENT
|
|
) {
|
|
if ( GetFieldValue( PoolTrackTable + ( PoolTagHash&~(PROTECTED_POOL >> 16) )*GetTypeSize("nt!_POOL_TRACKER_TABLE"),
|
|
"nt!_POOL_TRACKER_TABLE",
|
|
"Key",
|
|
Key) ) {
|
|
PoolTag = 0;
|
|
} else {
|
|
PoolTag = Key;
|
|
}
|
|
|
|
if (PoolTagHash & (PROTECTED_POOL >> 16)) {
|
|
PoolTag |= PROTECTED_POOL;
|
|
}
|
|
|
|
} else {
|
|
PoolTag = PoolTag;
|
|
}*/
|
|
|
|
dprintf(" %c%c%c%c%c%s\n",
|
|
c,
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(PoolTag),
|
|
PP(PoolTag >> 8),
|
|
PP(PoolTag >> 16),
|
|
PP((PoolTag&~PROTECTED_POOL) >> 24),
|
|
(PoolTag&PROTECTED_POOL) ? " (Protected)" : ""
|
|
#undef PP
|
|
);
|
|
|
|
if (c=='*') {
|
|
PrintPoolTagComponent(PoolTag & ~PROTECTED_POOL);
|
|
}
|
|
} else {
|
|
if (ProcessBilled != 0) {
|
|
dprintf(" Process: %0p\n", ProcessBilled );
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if (Flags & 1) {
|
|
ULONG i, Contents[8];
|
|
|
|
// BUG if Contents have different size than 32 bits
|
|
ReadMemory(Pool + SizeOfPoolHdr,
|
|
&Contents,
|
|
sizeof(Contents),
|
|
&i);
|
|
dprintf(" %08lx %08lx %08lx %08lx %08lx\n",
|
|
Pool+SizeOfPoolHdr,
|
|
Contents[0],
|
|
Contents[1],
|
|
Contents[2],
|
|
Contents[3]);
|
|
|
|
dprintf(" %08lx %08lx %08lx %08lx %08lx\n",
|
|
Pool+SizeOfPoolHdr+16,
|
|
Contents[4],
|
|
Contents[5],
|
|
Contents[6],
|
|
Contents[7]);
|
|
dprintf("\n");
|
|
}
|
|
|
|
Previous = BlockSize;
|
|
Pool += (Previous << POOL_BLOCK_SHIFT);
|
|
FirstBlock = FALSE;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
DECLARE_API( pool )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump kernel mode heap
|
|
|
|
Arguments:
|
|
|
|
args - Page Flags
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG64 PoolPageToDump;
|
|
ULONG Flags;
|
|
HRESULT Hr;
|
|
|
|
INIT_API();
|
|
if (PoolInitializeGlobals() == FALSE) {
|
|
Hr = E_INVALIDARG;
|
|
} else {
|
|
PoolPageToDump = 0;
|
|
Flags = 0;
|
|
if (GetExpressionEx(args, &PoolPageToDump, &args)) {
|
|
Flags = (ULONG) GetExpression (args);
|
|
}
|
|
|
|
if (PoolPageToDump == 0) {
|
|
DumpPool();
|
|
Hr = S_OK;;
|
|
} else {
|
|
Hr = ListPoolPage(PoolPageToDump, Flags, NULL);
|
|
}
|
|
|
|
}
|
|
EXIT_API();
|
|
|
|
return Hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
DECLARE_API( poolused )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump usage by pool tag
|
|
|
|
Arguments:
|
|
|
|
args -
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG PoolTrackTableSize;
|
|
ULONG PoolTrackTableSizeInBytes;
|
|
PULONG64 p;
|
|
PUCHAR PoolTrackTableData;
|
|
ULONG Flags;
|
|
ULONG i;
|
|
ULONG result;
|
|
ULONG ct;
|
|
ULONG TagName;
|
|
CHAR TagNameX[4] = {'*','*','*','*'};
|
|
ULONG SizeOfPoolTarker;
|
|
ULONG64 PoolTableAddress;
|
|
ULONG64 PoolTrackTable;
|
|
ULONG NonPagedAllocsTotal,NonPagedFreesTotal,PagedAllocsTotal,PagedFreesTotal;
|
|
ULONG64 NonPagedBytesTotal, PagedBytesTotal;
|
|
|
|
if (PoolInitializeGlobals() == FALSE) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
Flags = 0;
|
|
if (!sscanf(args,"%lx %c%c%c%c", &Flags, &TagNameX[0],
|
|
&TagNameX[1], &TagNameX[2], &TagNameX[3])) {
|
|
Flags = 0;
|
|
}
|
|
|
|
TagName = TagNameX[0] | (TagNameX[1] << 8) | (TagNameX[2] << 16) | (TagNameX[3] << 24);
|
|
|
|
PoolTrackTableSize = GetUlongValue ("PoolTrackTableSize");
|
|
|
|
if (!(SizeOfPoolTarker = GetTypeSize("nt!_POOL_TRACKER_TABLE"))) {
|
|
dprintf("Unable to get _POOL_TRACKER_TABLE : probably wrong symbols.\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
PoolTrackTable = GetNtDebuggerDataPtrValue( PoolTrackTable );
|
|
|
|
if (PoolTrackTable == 0) {
|
|
dprintf ("unable to get PoolTrackTable - ");
|
|
if (GetExpression("nt!PoolTrackTable")) {
|
|
dprintf ("pool tagging is disabled, enable it to use this command\n");
|
|
dprintf ("Use gflags.exe and check the box that says \"Enable pool tagging\".\n");
|
|
} else {
|
|
dprintf ("symbols could be worng\n");
|
|
}
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
|
|
PoolTrackTableSizeInBytes = PoolTrackTableSize * SizeOfPoolTarker;
|
|
|
|
PoolTrackTableData = malloc (PoolTrackTableSizeInBytes);
|
|
if (PoolTrackTableData == NULL) {
|
|
dprintf("unable to allocate memory for tag table.\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// KD is going to cache the data
|
|
//
|
|
PoolTableAddress = PoolTrackTable;
|
|
if ( !ReadMemory( PoolTableAddress,
|
|
&PoolTrackTableData[0],
|
|
PoolTrackTableSizeInBytes,
|
|
&result) ) {
|
|
dprintf("%08p: Unable to get contents of pool block\n", PoolTableAddress );
|
|
free (PoolTrackTableData);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (Flags & 2) {
|
|
SortBy = NONPAGED_USED;
|
|
dprintf(" Sorting by NonPaged Pool Consumed\n");
|
|
} else if (Flags & 4) {
|
|
SortBy = PAGED_USED;
|
|
dprintf(" Sorting by Paged Pool Consumed\n");
|
|
} else {
|
|
SortBy = TAG;
|
|
dprintf(" Sorting by Tag\n");
|
|
}
|
|
|
|
dprintf("\n Pool Used:\n");
|
|
if (!(Flags & 1)) {
|
|
dprintf(" NonPaged Paged\n");
|
|
dprintf(" Tag Allocs Used Allocs Used\n");
|
|
|
|
} else {
|
|
dprintf(" NonPaged Paged\n");
|
|
dprintf(" Tag Allocs Frees Diff Used Allocs Frees Diff Used\n");
|
|
}
|
|
|
|
ct = PageSize / SizeOfPoolTarker;
|
|
i = 0;
|
|
PoolTableAddress = PoolTrackTable;
|
|
|
|
free (PoolTrackTableData);
|
|
//
|
|
// Create array of POOL_TRACKER_TABLE addresses and sort the addresses
|
|
//
|
|
PoolTrackTableData = malloc (PoolTrackTableSize * sizeof(ULONG64));
|
|
if (PoolTrackTableData == NULL) {
|
|
dprintf("unable to allocate memory for tag table.\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
while (i < PoolTrackTableSize) {
|
|
|
|
if ( CheckControlC() ) {
|
|
free (PoolTrackTableData);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
((PULONG64) PoolTrackTableData)[i] = PoolTableAddress + i * SizeOfPoolTarker;
|
|
i++;
|
|
}
|
|
|
|
qsort((void *)PoolTrackTableData,
|
|
(size_t)PoolTrackTableSize,
|
|
(size_t)sizeof(ULONG64),
|
|
ulcomp);
|
|
|
|
i = 0;
|
|
p = (PULONG64) &PoolTrackTableData[i];
|
|
|
|
NonPagedAllocsTotal = 0;
|
|
NonPagedFreesTotal = 0;
|
|
NonPagedBytesTotal = 0;
|
|
|
|
PagedAllocsTotal = 0;
|
|
PagedFreesTotal = 0;
|
|
PagedBytesTotal = 0;
|
|
|
|
for ( ; i < PoolTrackTableSize; i += 1, p += 1) {
|
|
ULONG Key,NonPagedAllocs,NonPagedFrees,PagedAllocs,PagedFrees;
|
|
ULONG64 NonPagedBytes, PagedBytes;
|
|
|
|
#define TrackFld(F) GetFieldValue(*p, "nt!_POOL_TRACKER_TABLE", #F, F)
|
|
|
|
TrackFld(Key); TrackFld(NonPagedAllocs); TrackFld(NonPagedBytes);
|
|
TrackFld(PagedBytes); TrackFld(NonPagedFrees); TrackFld(PagedAllocs);
|
|
TrackFld(PagedFrees);
|
|
|
|
#undef TrackFld
|
|
|
|
if ((Key != 0) &&
|
|
(CheckSingleFilter ((PCHAR)&Key, (PCHAR)&TagName))) {
|
|
|
|
if (!(Flags & 1)) {
|
|
|
|
if ((NonPagedBytes != 0) || (PagedBytes != 0)) {
|
|
|
|
NonPagedAllocsTotal += NonPagedAllocs;
|
|
NonPagedFreesTotal += NonPagedFrees;
|
|
NonPagedBytesTotal += NonPagedBytes;
|
|
|
|
PagedAllocsTotal += PagedAllocs;
|
|
PagedFreesTotal += PagedFrees;
|
|
PagedBytesTotal += PagedBytes;
|
|
|
|
dprintf(" %c%c%c%c %8ld %8I64ld %8ld %8I64ld\n",
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(Key),
|
|
PP(Key >> 8),
|
|
PP(Key >> 16),
|
|
PP(Key >> 24),
|
|
NonPagedAllocs - NonPagedFrees,
|
|
NonPagedBytes,
|
|
PagedAllocs - PagedFrees,
|
|
PagedBytes);
|
|
}
|
|
|
|
} else {
|
|
|
|
NonPagedAllocsTotal += NonPagedAllocs;
|
|
NonPagedFreesTotal += NonPagedFrees;
|
|
NonPagedBytesTotal += NonPagedBytes;
|
|
|
|
PagedAllocsTotal += PagedAllocs;
|
|
PagedFreesTotal += PagedFrees;
|
|
PagedBytesTotal += PagedBytes;
|
|
|
|
dprintf(" %c%c%c%c %8ld %8ld %8ld %8I64ld %8ld %8ld %8ld %8I64ld\n",
|
|
PP(Key),
|
|
PP(Key >> 8),
|
|
PP(Key >> 16),
|
|
PP(Key >> 24),
|
|
NonPagedAllocs,
|
|
NonPagedFrees,
|
|
NonPagedAllocs - NonPagedFrees,
|
|
NonPagedBytes,
|
|
PagedAllocs,
|
|
PagedFrees,
|
|
PagedAllocs - PagedFrees,
|
|
PagedBytes);
|
|
#undef PP
|
|
}
|
|
}
|
|
|
|
}
|
|
if (!(Flags & 1)) {
|
|
dprintf(" TOTAL %8ld %8I64ld %8ld %8I64ld\n",
|
|
NonPagedAllocsTotal - NonPagedFreesTotal,
|
|
NonPagedBytesTotal,
|
|
PagedAllocsTotal - PagedFreesTotal,
|
|
PagedBytesTotal);
|
|
} else {
|
|
dprintf(" TOTAL %8ld %8ld %8ld %8I64ld %8ld %8ld %8ld %8I64ld\n",
|
|
NonPagedAllocsTotal,
|
|
NonPagedFreesTotal,
|
|
NonPagedAllocsTotal - NonPagedFreesTotal,
|
|
NonPagedBytesTotal,
|
|
PagedAllocsTotal,
|
|
PagedFreesTotal,
|
|
PagedAllocsTotal - PagedFreesTotal,
|
|
PagedBytesTotal);
|
|
}
|
|
|
|
free (PoolTrackTableData);
|
|
return S_OK;
|
|
}
|
|
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
|
|
BOOLEAN WINAPI
|
|
CheckSingleFilterAndPrint (
|
|
PCHAR Tag,
|
|
PCHAR Filter,
|
|
ULONG Flags,
|
|
ULONG64 PoolHeader,
|
|
ULONG BlockSize,
|
|
ULONG64 Data,
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback to check a piece of pool and print out information about it
|
|
if it matches the specified tag.
|
|
|
|
Arguments:
|
|
|
|
Tag - Supplies the tag to search for.
|
|
|
|
Filter - Supplies the filter string to match against.
|
|
|
|
Flags - Supplies 0 if a nonpaged pool search is desired.
|
|
Supplies 1 if a paged pool search is desired.
|
|
Supplies 2 if a special pool search is desired.
|
|
Supplies 4 if a pool is a large pool
|
|
|
|
PoolHeader - Supplies the pool header.
|
|
|
|
BlockSize - Supplies the size of the pool block in bytes.
|
|
|
|
Data - Supplies the address of the pool block.
|
|
|
|
Context - Unused.
|
|
|
|
Return Value:
|
|
|
|
TRUE for a match, FALSE if not.
|
|
|
|
--*/
|
|
{
|
|
ULONG UTag = *((PULONG)Tag);
|
|
ULONG HdrUlong1=0, HdrPoolSize ;
|
|
|
|
UNREFERENCED_PARAMETER (Context);
|
|
|
|
if (CheckSingleFilter (Tag, Filter) == FALSE) {
|
|
return FALSE;
|
|
}
|
|
|
|
HdrPoolSize = GetTypeSize("nt!_POOL_HEADER");
|
|
if ((BlockSize >= (PageSize-2*HdrPoolSize)) || (Flags & 0x4)) {
|
|
dprintf("*%p :%slarge page allocation, Tag %3s %c%c%c%c, size %3s 0x%x bytes\n",
|
|
(Data & ~POOL_BIG_TABLE_ENTRY_FREE),
|
|
(Data & POOL_BIG_TABLE_ENTRY_FREE) ? "free " : "",
|
|
(Data & POOL_BIG_TABLE_ENTRY_FREE) ? "was" : "is",
|
|
PP(UTag),
|
|
PP(UTag >> 8),
|
|
PP(UTag >> 16),
|
|
PP(UTag >> 24),
|
|
(Data & POOL_BIG_TABLE_ENTRY_FREE) ? "was" : "is",
|
|
BlockSize
|
|
);
|
|
} else if (Flags & 0x2) {
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "Ulong1", HdrUlong1);
|
|
dprintf("*%p size: %4lx %s special pool, Tag is %c%c%c%c\n",
|
|
Data,
|
|
BlockSize,
|
|
HdrUlong1 & MI_SPECIAL_POOL_PAGABLE ? "pagable" : "non-paged",
|
|
PP(UTag),
|
|
PP(UTag >> 8),
|
|
PP(UTag >> 16),
|
|
PP(UTag >> 24)
|
|
);
|
|
} else {
|
|
ULONG BlockSize, PreviousSize, PoolType, PoolIndex, AllocatorBackTraceIndex;
|
|
ULONG PoolTagHash, PoolTag;
|
|
ULONG64 ProcessBilled;
|
|
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "BlockSize", BlockSize);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolType", PoolType);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolTagHash", PoolTagHash);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolTag", PoolTag);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolIndex", PoolIndex);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PreviousSize", PreviousSize);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "ProcessBilled", ProcessBilled);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "AllocatorBackTraceIndex", AllocatorBackTraceIndex);
|
|
|
|
dprintf("%p size: %4lx previous size: %4lx ",
|
|
Data - HdrPoolSize,
|
|
BlockSize << POOL_BLOCK_SHIFT,
|
|
PreviousSize << POOL_BLOCK_SHIFT);
|
|
|
|
if (PoolType == 0) {
|
|
//
|
|
// "Free " with a space after it before the parentheses means
|
|
// it's been freed to a (pool manager internal) lookaside list.
|
|
// We used to print "Lookaside" but that just confused driver
|
|
// writers because they didn't know if this meant in use or not
|
|
// and many would say "but I don't use lookaside lists - the
|
|
// extension or kernel is broken".
|
|
//
|
|
// "Free" with no space after it before the parentheses means
|
|
// it is not on a pool manager internal lookaside list and is
|
|
// instead on the regular pool manager internal flink/blink
|
|
// chains.
|
|
//
|
|
// Note to anyone using the pool package, these 2 terms are
|
|
// equivalent. The fine distinction is only for those actually
|
|
// writing pool internal code.
|
|
//
|
|
dprintf(" (Free)");
|
|
dprintf(" %c%c%c%c\n",
|
|
PP(UTag),
|
|
PP(UTag >> 8),
|
|
PP(UTag >> 16),
|
|
PP(UTag >> 24)
|
|
);
|
|
} else {
|
|
|
|
if (!NewPool ? (PoolIndex & 0x80) : (PoolType & 0x04)) {
|
|
dprintf(" (Allocated)");
|
|
} else {
|
|
//
|
|
// "Free " with a space after it before the parentheses means
|
|
// it's been freed to a (pool manager internal) lookaside list.
|
|
// We used to print "Lookaside" but that just confused driver
|
|
// writers because they didn't know if this meant in use or not
|
|
// and many would say "but I don't use lookaside lists - the
|
|
// extension or kernel is broken".
|
|
//
|
|
// "Free" with no space after it before the parentheses means
|
|
// it is not on a pool manager internal lookaside list and is
|
|
// instead on the regular pool manager internal flink/blink
|
|
// chains.
|
|
//
|
|
// Note to anyone using the pool package, these 2 terms are
|
|
// equivalent. The fine distinction is only for those actually
|
|
// writing pool internal code.
|
|
//
|
|
dprintf(" (Free )");
|
|
}
|
|
if ((PoolType & POOL_QUOTA_MASK) == 0) {
|
|
|
|
UTag = PoolTag;
|
|
|
|
dprintf(" %c%c%c%c%s\n",
|
|
PP(UTag),
|
|
PP(UTag >> 8),
|
|
PP(UTag >> 16),
|
|
PP((UTag &~PROTECTED_POOL) >> 24),
|
|
(UTag & PROTECTED_POOL) ? " (Protected)" : ""
|
|
);
|
|
|
|
} else {
|
|
if (ProcessBilled != 0) {
|
|
dprintf(" Process: %08p\n", ProcessBilled );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
} // CheckSingleFilterAndPrint
|
|
|
|
#undef PP
|
|
|
|
ULONG64
|
|
GetNextResidentAddress (
|
|
ULONG64 VirtualAddress,
|
|
ULONG64 MaximumVirtualAddress
|
|
)
|
|
{
|
|
ULONG64 PointerPde;
|
|
ULONG64 PointerPte;
|
|
ULONG SizeOfPte;
|
|
ULONG Valid;
|
|
|
|
//
|
|
// Note this code will need to handle one more level of indirection for
|
|
// WIN64.
|
|
//
|
|
|
|
if (!(SizeOfPte=GetTypeSize("nt!_MMPTE"))) {
|
|
dprintf("Cannot get MMPTE type.\n");
|
|
return 0;
|
|
}
|
|
|
|
top:
|
|
|
|
PointerPde = DbgGetPdeAddress (VirtualAddress);
|
|
|
|
while (GetFieldValue(PointerPde,
|
|
"nt!_MMPTE",
|
|
"u.Hard.Valid",
|
|
Valid) ||
|
|
(Valid == 0)) {
|
|
|
|
//
|
|
// Note that on 32-bit systems, the PDE should always be readable.
|
|
// If the PDE is not valid then increment to the next PDE's VA.
|
|
//
|
|
|
|
PointerPde = (PointerPde + SizeOfPte);
|
|
|
|
VirtualAddress = DbgGetVirtualAddressMappedByPte (PointerPde);
|
|
VirtualAddress = DbgGetVirtualAddressMappedByPte (VirtualAddress);
|
|
|
|
if (VirtualAddress >= MaximumVirtualAddress) {
|
|
return VirtualAddress;
|
|
}
|
|
|
|
if (CheckControlC()) {
|
|
return VirtualAddress;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
PointerPte = DbgGetPteAddress (VirtualAddress);
|
|
|
|
while (GetFieldValue(PointerPde,
|
|
"nt!_MMPTE",
|
|
"u.Hard.Valid",
|
|
Valid) ||
|
|
(Valid == 0)) {
|
|
|
|
//
|
|
// If the PTE cannot be read then increment by PAGE_SIZE.
|
|
//
|
|
|
|
VirtualAddress = (VirtualAddress + PageSize);
|
|
|
|
if (CheckControlC()) {
|
|
return VirtualAddress;
|
|
}
|
|
|
|
PointerPte = (PointerPte + SizeOfPte);
|
|
if ((PointerPte & (PageSize - 1)) == 0) {
|
|
goto top;
|
|
}
|
|
|
|
if (VirtualAddress >= MaximumVirtualAddress) {
|
|
return VirtualAddress;
|
|
}
|
|
}
|
|
|
|
return VirtualAddress;
|
|
}
|
|
|
|
|
|
VOID
|
|
SearchPool(
|
|
ULONG TagName,
|
|
ULONG Flags,
|
|
ULONG64 RestartAddr,
|
|
POOLFILTER Filter,
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Engine to search the pool.
|
|
|
|
Arguments:
|
|
|
|
TagName - Supplies the tag to search for.
|
|
|
|
Flags - Supplies 0 if a nonpaged pool search is desired.
|
|
Supplies 1 if a paged pool search is desired.
|
|
Supplies 2 if a special pool search is desired.
|
|
|
|
RestartAddr - Supplies the address to restart the search from.
|
|
|
|
Filter - Supplies the filter routine to use.
|
|
|
|
Context - Supplies the user defined context blob.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
LOGICAL PhysicallyContiguous;
|
|
ULONG PoolBlockSize;
|
|
ULONG64 PoolHeader;
|
|
ULONG PoolTag;
|
|
ULONG Result;
|
|
ULONG64 PoolPage;
|
|
ULONG64 StartPage;
|
|
ULONG64 Pool;
|
|
ULONG Previous;
|
|
ULONG64 PoolStart;
|
|
ULONG64 PoolPteAddress;
|
|
ULONG64 PoolEnd;
|
|
ULONG64 ExpandedPoolStart;
|
|
ULONG64 ExpandedPoolEnd;
|
|
ULONG InitialPoolSize;
|
|
ULONG SkipSize;
|
|
BOOLEAN TwoPools;
|
|
UCHAR DataPage[0x4000]; // MAX pzger size
|
|
ULONG64 DataPageReal;
|
|
ULONG64 DataStartReal;
|
|
LOGICAL Found;
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG ct;
|
|
ULONG PoolBigPageTableSize;
|
|
ULONG64 PoolTableAddress;
|
|
UCHAR FastTag[4];
|
|
ULONG TagLength;
|
|
ULONG SizeOfBigPages;
|
|
ULONG PoolTypeFlags = Flags & 0x3;
|
|
ULONG Ulong1;
|
|
ULONG HdrSize;
|
|
|
|
if (PoolInitializeGlobals() == FALSE) {
|
|
return;
|
|
}
|
|
|
|
if (PoolTypeFlags == 2) {
|
|
|
|
if (RestartAddr && (RestartAddr >= SpecialPoolStart) && (RestartAddr <= SpecialPoolEnd)) {
|
|
Pool = RestartAddr;
|
|
} else {
|
|
Pool = SpecialPoolStart;
|
|
}
|
|
|
|
dprintf("\nSearching special pool (%p : %p) for Tag: %c%c%c%c\r\n\n",
|
|
Pool,
|
|
SpecialPoolEnd,
|
|
TagName,
|
|
TagName >> 8,
|
|
TagName >> 16,
|
|
TagName >> 24);
|
|
|
|
Found = FALSE;
|
|
SkipSize = PageSize;
|
|
|
|
if (SpecialPoolStart && SpecialPoolEnd) {
|
|
|
|
//
|
|
// Search special pool for the tag.
|
|
//
|
|
|
|
while (Pool < SpecialPoolEnd) {
|
|
|
|
if ( CheckControlC() ) {
|
|
dprintf("\n...terminating - searched pool to %p\n",
|
|
Pool);
|
|
return;
|
|
}
|
|
|
|
DataStartReal = Pool;
|
|
DataPageReal = Pool;
|
|
if ( !ReadMemory( Pool,
|
|
&DataPage[0],
|
|
min(PageSize, sizeof(DataPage)),
|
|
&Result) ) {
|
|
ULONG64 PteLong=0, PageFileHigh;
|
|
|
|
if (SkipSize != 2 * PageSize) {
|
|
|
|
// dprintf("SP skip %x", Pool);
|
|
PoolPteAddress = DbgGetPteAddress (Pool);
|
|
|
|
if (!GetFieldValue(PoolPteAddress,
|
|
"nt!_MMPTE",
|
|
"u.Soft.PageFileHigh",
|
|
PageFileHigh) ) {
|
|
|
|
if ((PageFileHigh == 0) ||
|
|
(PageFileHigh == MI_SPECIAL_POOL_PTE_PAGABLE) ||
|
|
(PageFileHigh == MI_SPECIAL_POOL_PTE_NONPAGABLE)) {
|
|
|
|
//
|
|
// Found a NO ACCESS PTE - skip these from
|
|
// here on to speed up the search.
|
|
//
|
|
|
|
// dprintf("SP skip double %p", PoolPteAddress);
|
|
SkipSize = 2 * PageSize;
|
|
Pool += PageSize;
|
|
// dprintf("SP skip final %p", Pool);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
Pool += SkipSize;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Determine whether this is a valid special pool block.
|
|
//
|
|
|
|
PoolHeader = GetSpecialPoolHeader (DataPage,
|
|
DataPageReal,
|
|
&DataStartReal);
|
|
|
|
if (PoolHeader != 0) {
|
|
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolTag", PoolTag);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "Ulong1", Ulong1);
|
|
PoolBlockSize = (ULONG) SPECIAL_POOL_BLOCK_SIZE(Ulong1);
|
|
|
|
Found = Filter( (PCHAR)&PoolTag,
|
|
(PCHAR)&TagName,
|
|
Flags,
|
|
PoolHeader,
|
|
PoolBlockSize,
|
|
DataStartReal,
|
|
Context );
|
|
} else {
|
|
dprintf( "No pool header for page: 0x%p\n", Pool );
|
|
}
|
|
Pool += SkipSize;
|
|
}
|
|
}
|
|
|
|
if (Found == FALSE) {
|
|
dprintf("The %c%c%c%c tag could not be found in special pool.\n",
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(TagName),
|
|
PP(TagName >> 8),
|
|
PP(TagName >> 16),
|
|
PP(TagName >> 24)
|
|
);
|
|
#undef PP
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (PoolTypeFlags == 0) {
|
|
PhysicallyContiguous = TRUE;
|
|
} else {
|
|
PhysicallyContiguous = FALSE;
|
|
}
|
|
|
|
__try {
|
|
TwoPools = FALSE;
|
|
|
|
if (!PoolBigTableAddress) {
|
|
PoolBigTableAddress = GetPointerValue ("PoolBigPageTable");
|
|
}
|
|
|
|
PoolTableAddress = PoolBigTableAddress;
|
|
|
|
if (PoolTableAddress) {
|
|
|
|
ULONG VaOffset;
|
|
ULONG NumPagesOffset;
|
|
ULONG PtrSize;
|
|
ULONG KeyOffset;
|
|
|
|
PoolBigPageTableSize = GetUlongValue ("PoolBigPageTableSize");
|
|
|
|
//
|
|
// Scan the table looking for a match. We read close to a page at a time
|
|
// physical page / sizeof ( pool_tracker_big_page ) * sizeof ( pool_tracker_big_page )
|
|
// on x86 this works out to ffc
|
|
//
|
|
|
|
i = 0;
|
|
SizeOfBigPages = GetTypeSize ("nt!_POOL_TRACKER_BIG_PAGES");
|
|
if (!SizeOfBigPages) {
|
|
dprintf("Cannot get _POOL_TRACKER_BIG_PAGES type size\n");
|
|
return;
|
|
}
|
|
ct = PageSize / SizeOfBigPages;
|
|
|
|
dprintf( "\nScanning large pool allocation table for Tag: %c%c%c%c (%p : %p)\n\n\r",
|
|
TagName,
|
|
TagName >> 8,
|
|
TagName >> 16,
|
|
TagName >> 24,
|
|
PoolBigTableAddress,
|
|
PoolBigTableAddress + PoolBigPageTableSize * SizeOfBigPages );
|
|
|
|
GetFieldOffset( "nt!_POOL_TRACKER_BIG_PAGES", "Va", &VaOffset );
|
|
GetFieldOffset( "nt!_POOL_TRACKER_BIG_PAGES", "NumberOfPages", &NumPagesOffset );
|
|
GetFieldOffset( "nt!_POOL_TRACKER_BIG_PAGES", "Key", &KeyOffset );
|
|
PtrSize = IsPtr64() ? 8 : 4;
|
|
|
|
while (i < PoolBigPageTableSize) {
|
|
|
|
if (PoolBigPageTableSize - i < ct) {
|
|
ct = PoolBigPageTableSize - i;
|
|
}
|
|
|
|
if ( !ReadMemory( PoolTableAddress,
|
|
&DataPage[0],
|
|
ct * SizeOfBigPages,
|
|
&Result) ) {
|
|
|
|
dprintf( "%08lx: Unable to get contents of big pool block\r\n", PoolTableAddress );
|
|
break;
|
|
}
|
|
|
|
for (j = 0; j < ct; j += 1) {
|
|
ULONG64 Va = 0;
|
|
|
|
memcpy( &Va, (PCHAR)DataPage + (SizeOfBigPages * j) + VaOffset, PtrSize );
|
|
|
|
Filter( ((PCHAR)DataPage + (SizeOfBigPages * j) + KeyOffset),
|
|
(PCHAR)&TagName,
|
|
Flags | 0x4, // To assist filter routine to recognize this as large pool
|
|
PoolTableAddress + SizeOfBigPages * j,
|
|
(*((PULONG)((PCHAR)DataPage + (SizeOfBigPages * j) + NumPagesOffset))) * PageSize,
|
|
Va,
|
|
Context );
|
|
if ( CheckControlC() ) {
|
|
dprintf("\n...terminating - searched pool to %p\n",
|
|
PoolTableAddress + j * SizeOfBigPages);
|
|
return;
|
|
}
|
|
}
|
|
i += ct;
|
|
PoolTableAddress += (ct * SizeOfBigPages);
|
|
if ( CheckControlC() ) {
|
|
dprintf("\n...terminating - searched pool to %p\n",
|
|
PoolTableAddress);
|
|
return;
|
|
}
|
|
|
|
}
|
|
} else {
|
|
dprintf("unable to get large pool allocation table - either wrong symbols or pool tagging is disabled\n");
|
|
}
|
|
|
|
if (PoolTypeFlags == 0) {
|
|
PoolStart = GetNtDebuggerDataPtrValue( MmNonPagedPoolStart );
|
|
|
|
if (0 == PoolStart) {
|
|
dprintf( "Unable to get MmNonPagedPoolStart\n" );
|
|
}
|
|
|
|
PoolEnd =
|
|
PoolStart + GetNtDebuggerDataValue( MmMaximumNonPagedPoolInBytes );
|
|
|
|
ExpandedPoolEnd = GetNtDebuggerDataPtrValue( MmNonPagedPoolEnd );
|
|
|
|
if (PoolEnd != ExpandedPoolEnd) {
|
|
InitialPoolSize = (ULONG)GetUlongValue( "MmSizeOfNonPagedPoolInBytes" );
|
|
PoolEnd = PoolStart + InitialPoolSize;
|
|
|
|
ExpandedPoolStart = GetPointerValue( "MmNonPagedPoolExpansionStart" );
|
|
TwoPools = TRUE;
|
|
}
|
|
for (TagLength = 0;TagLength < 3; TagLength++) {
|
|
if ((*(((PCHAR)&TagName)+TagLength) == '?') ||
|
|
(*(((PCHAR)&TagName)+TagLength) == '*')) {
|
|
break;
|
|
}
|
|
FastTag[TagLength] = *(((PCHAR)&TagName)+TagLength);
|
|
}
|
|
|
|
} else {
|
|
PoolStart = GetNtDebuggerDataPtrValue( MmPagedPoolStart );
|
|
PoolEnd =
|
|
PoolStart + GetNtDebuggerDataValue( MmSizeOfPagedPoolInBytes );
|
|
}
|
|
|
|
if (RestartAddr) {
|
|
PoolStart = RestartAddr;
|
|
if (TwoPools == TRUE) {
|
|
if (PoolStart > PoolEnd) {
|
|
TwoPools = FALSE;
|
|
PoolStart = RestartAddr;
|
|
PoolEnd = ExpandedPoolEnd;
|
|
}
|
|
}
|
|
}
|
|
|
|
dprintf("\nSearching %s pool (%p : %p) for Tag: %c%c%c%c\r\n\n",
|
|
(PoolTypeFlags == 0) ? "NonPaged" : "Paged",
|
|
PoolStart,
|
|
PoolEnd,
|
|
TagName,
|
|
TagName >> 8,
|
|
TagName >> 16,
|
|
TagName >> 24);
|
|
|
|
PoolPage = PoolStart;
|
|
HdrSize = GetTypeSize("nt!_POOL_HEADER");
|
|
while (PoolPage < PoolEnd) {
|
|
|
|
//
|
|
// Optimize things by ioctl'ing over to the other side to
|
|
// do a fast search and start with that page.
|
|
//
|
|
if ((PoolTypeFlags == 0) &&
|
|
PhysicallyContiguous &&
|
|
(TagLength > 0)) {
|
|
SEARCHMEMORY Search;
|
|
|
|
Search.SearchAddress = PoolPage;
|
|
Search.SearchLength = PoolEnd-PoolPage;
|
|
Search.PatternLength = TagLength;
|
|
Search.Pattern = &FastTag;
|
|
Search.FoundAddress = 0;
|
|
if ((Ioctl(IG_SEARCH_MEMORY, &Search, sizeof(Search))) &&
|
|
(Search.FoundAddress != 0)) {
|
|
//
|
|
// Got a hit, search the whole page
|
|
//
|
|
PoolPage = PAGE_ALIGN64(Search.FoundAddress);
|
|
} else {
|
|
//
|
|
// The tag was not found at all, so we can just skip
|
|
// this chunk entirely.
|
|
//
|
|
PoolPage = PoolEnd;
|
|
goto skiprange;
|
|
}
|
|
}
|
|
|
|
Pool = PAGE_ALIGN64 (PoolPage);
|
|
StartPage = Pool;
|
|
Previous = 0;
|
|
|
|
while (PAGE_ALIGN64(Pool) == StartPage) {
|
|
|
|
ULONG HdrPoolTag, BlockSize, PreviousSize, AllocatorBackTraceIndex, PoolTagHash;
|
|
ULONG PoolType;
|
|
|
|
if ( GetFieldValue(Pool,
|
|
"nt!_POOL_HEADER",
|
|
"PoolTag",
|
|
HdrPoolTag) ) {
|
|
|
|
PoolPage = GetNextResidentAddress (Pool, PoolEnd);
|
|
|
|
//
|
|
// If we're half resident - half non-res then we'll get back
|
|
// that are starting address is the next resident page. In that
|
|
// case just go on to the next page
|
|
//
|
|
|
|
if (PoolPage == Pool) {
|
|
PoolPage = PoolPage + PageSize;
|
|
}
|
|
|
|
goto nextpage;
|
|
}
|
|
|
|
GetFieldValue(Pool,"nt!_POOL_HEADER","PoolTag",HdrPoolTag);
|
|
GetFieldValue(Pool,"nt!_POOL_HEADER","PoolType", PoolType);
|
|
GetFieldValue(Pool,"nt!_POOL_HEADER","BlockSize",BlockSize);
|
|
GetFieldValue(Pool,"nt!_POOL_HEADER","PoolTagHash",PoolTagHash);
|
|
GetFieldValue(Pool,"nt!_POOL_HEADER","PreviousSize",PreviousSize);
|
|
GetFieldValue(Pool,"nt!_POOL_HEADER","AllocatorBackTraceIndex",AllocatorBackTraceIndex);
|
|
|
|
if ((BlockSize << POOL_BLOCK_SHIFT) > POOL_PAGE_SIZE) {
|
|
//dprintf("Bad allocation size @%lx, too large\n", Pool);
|
|
break;
|
|
}
|
|
|
|
if (BlockSize == 0) {
|
|
//dprintf("Bad allocation size @%lx, zero is invalid\n", Pool);
|
|
break;
|
|
}
|
|
|
|
if (PreviousSize != Previous) {
|
|
//dprintf("Bad previous allocation size @%lx, last size was %lx\n",Pool, Previous);
|
|
break;
|
|
}
|
|
|
|
PoolTag = HdrPoolTag;
|
|
|
|
Filter((PCHAR)&PoolTag,
|
|
(PCHAR)&TagName,
|
|
Flags,
|
|
Pool,
|
|
BlockSize << POOL_BLOCK_SHIFT,
|
|
Pool + HdrSize,
|
|
Context );
|
|
|
|
Previous = BlockSize;
|
|
Pool += (Previous << POOL_BLOCK_SHIFT);
|
|
if ( CheckControlC() ) {
|
|
dprintf("\n...terminating - searched pool to %p\n",
|
|
PoolPage);
|
|
return;
|
|
}
|
|
}
|
|
PoolPage = (PoolPage + PageSize);
|
|
nextpage:
|
|
if ( CheckControlC() ) {
|
|
dprintf("\n...terminating - searched pool to %p\n",
|
|
PoolPage);
|
|
return;
|
|
}
|
|
skiprange:
|
|
if (TwoPools == TRUE) {
|
|
if (PoolPage == PoolEnd) {
|
|
TwoPools = FALSE;
|
|
PoolStart = ExpandedPoolStart;
|
|
PoolEnd = ExpandedPoolEnd;
|
|
PoolPage = PoolStart;
|
|
PhysicallyContiguous = FALSE;
|
|
|
|
dprintf("\nSearching %s pool (%p : %p) for Tag: %c%c%c%c\n\n",
|
|
"NonPaged",
|
|
PoolStart,
|
|
PoolEnd,
|
|
TagName,
|
|
TagName >> 8,
|
|
TagName >> 16,
|
|
TagName >> 24);
|
|
}
|
|
}
|
|
}
|
|
} __finally {
|
|
}
|
|
|
|
return;
|
|
} // SearchPool
|
|
|
|
|
|
|
|
DECLARE_API( poolfind )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
flags == 0 means finds a tag in nonpaged pool.
|
|
flags == 1 means finds a tag in paged pool.
|
|
flags == 2 means finds a tag in special pool.
|
|
|
|
Arguments:
|
|
|
|
args -
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Flags;
|
|
CHAR TagNameX[4] = {' ',' ',' ',' '};
|
|
ULONG TagName;
|
|
ULONG64 PoolTrackTable;
|
|
|
|
Flags = 0;
|
|
if (!sscanf(args,"%c%c%c%c %lx", &TagNameX[0],
|
|
&TagNameX[1], &TagNameX[2], &TagNameX[3], &Flags)) {
|
|
Flags = 0;
|
|
}
|
|
|
|
if (TagNameX[0] == '0' && TagNameX[1] == 'x') {
|
|
if (!sscanf( args, "%lx %lx", &TagName, &Flags )) {
|
|
TagName = 0;
|
|
}
|
|
} else {
|
|
TagName = TagNameX[0] | (TagNameX[1] << 8) | (TagNameX[2] << 16) | (TagNameX[3] << 24);
|
|
}
|
|
|
|
PoolTrackTable = GetNtDebuggerDataPtrValue( PoolTrackTable );
|
|
if (PoolTrackTable == 0) {
|
|
dprintf ("unable to get PoolTrackTable - probably pool tagging disabled or wrong symbols\n");
|
|
}
|
|
|
|
|
|
SearchPool( TagName, Flags, 0, CheckSingleFilterAndPrint, NULL );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CheckSingleFilter (
|
|
PCHAR Tag,
|
|
PCHAR Filter
|
|
)
|
|
{
|
|
ULONG i;
|
|
UCHAR tc;
|
|
UCHAR fc;
|
|
|
|
for ( i = 0; i < 4; i++ ) {
|
|
tc = (UCHAR) *Tag++;
|
|
fc = (UCHAR) *Filter++;
|
|
if ( fc == '*' ) return TRUE;
|
|
if ( fc == '?' ) continue;
|
|
if (i == 3 && (tc & ~(PROTECTED_POOL >> 24)) == fc) continue;
|
|
if ( tc != fc ) return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
ULONG64
|
|
GetSpecialPoolHeader (
|
|
IN PVOID DataPage,
|
|
IN ULONG64 RealDataPage,
|
|
OUT PULONG64 ReturnedDataStart
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Examine a page of data to determine if it is a special pool block.
|
|
|
|
Arguments:
|
|
|
|
DataPage - Supplies a pointer to a page of data to examine.
|
|
|
|
ReturnedDataStart - Supplies a pointer to return the start of the data.
|
|
Only valid if this routine returns non-NULL.
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to the pool header for this special pool block or
|
|
NULL if the block is not valid special pool.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG PoolBlockSize;
|
|
ULONG PoolHeaderSize;
|
|
ULONG PoolBlockPattern;
|
|
PUCHAR p;
|
|
PUCHAR PoolDataEnd;
|
|
PUCHAR DataStart;
|
|
ULONG64 PoolHeader;
|
|
ULONG HdrUlong1;
|
|
|
|
PoolHeader = RealDataPage;
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "Ulong1", HdrUlong1);
|
|
//
|
|
// Determine whether the data is at the start or end of the page.
|
|
// Start off by assuming the data is at the end, in this case the
|
|
// header will be at the start.
|
|
//
|
|
|
|
PoolBlockSize = SPECIAL_POOL_BLOCK_SIZE(HdrUlong1);
|
|
|
|
if ((PoolBlockSize != 0) && (PoolBlockSize < PageSize - POOL_OVERHEAD)) {
|
|
|
|
PoolHeaderSize = POOL_OVERHEAD;
|
|
if (HdrUlong1 & MI_SPECIAL_POOL_VERIFIER) {
|
|
PoolHeaderSize += GetTypeSize ("nt!_MI_VERIFIER_POOL_HEADER");
|
|
}
|
|
|
|
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "BlockSize", PoolBlockPattern);
|
|
|
|
DataStart = (PUCHAR)DataPage + PageSize - PoolBlockSize;
|
|
p = (PUCHAR)DataPage + PoolHeaderSize;
|
|
|
|
for ( ; p < DataStart; p += 1) {
|
|
if (*p != PoolBlockPattern) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (p == DataStart || p >= (PUCHAR)DataPage + PoolHeaderSize + 0x10) {
|
|
|
|
//
|
|
// For this page, the data is at the end of the block.
|
|
// The 0x10 is just to give corrupt blocks some slack.
|
|
// All pool allocations are quadword aligned.
|
|
//
|
|
|
|
DataStart = (PUCHAR)DataPage + ((PageSize - PoolBlockSize) & ~(sizeof(QUAD)-1));
|
|
|
|
*ReturnedDataStart = RealDataPage + (ULONG64) ((PUCHAR) DataStart - (PUCHAR) DataPage);
|
|
return PoolHeader;
|
|
}
|
|
|
|
//
|
|
// The data must be at the front or the block is corrupt.
|
|
//
|
|
}
|
|
|
|
//
|
|
// Try for the data at the front. Checks are necessary as
|
|
// the page could be corrupt on both ends.
|
|
//
|
|
|
|
PoolHeader = (RealDataPage + PageSize - POOL_OVERHEAD);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "Ulong1", HdrUlong1);
|
|
PoolBlockSize = SPECIAL_POOL_BLOCK_SIZE(HdrUlong1);
|
|
|
|
if ((PoolBlockSize != 0) && (PoolBlockSize < PageSize - POOL_OVERHEAD)) {
|
|
PoolDataEnd = (PUCHAR)PoolHeader;
|
|
|
|
if (HdrUlong1 & MI_SPECIAL_POOL_VERIFIER) {
|
|
PoolDataEnd -= GetTypeSize ("nt!_MI_VERIFIER_POOL_HEADER");
|
|
}
|
|
|
|
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "BlockSize", PoolBlockPattern);
|
|
DataStart = (PUCHAR)DataPage;
|
|
|
|
p = DataStart + PoolBlockSize;
|
|
for ( ; p < PoolDataEnd; p += 1) {
|
|
if (*p != PoolBlockPattern) {
|
|
break;
|
|
}
|
|
}
|
|
if (p == (PUCHAR)PoolDataEnd || p > (PUCHAR)DataPage + PoolBlockSize + 0x10) {
|
|
//
|
|
// For this page, the data is at the front of the block.
|
|
// The 0x10 is just to give corrupt blocks some slack.
|
|
// All pool allocations are quadword aligned.
|
|
//
|
|
|
|
*ReturnedDataStart = RealDataPage + (ULONG64)( (PUCHAR)DataStart - (PUCHAR) DataPage);
|
|
return PoolHeader;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Not valid special pool.
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
#define BYTE(u,n) ((u & (0xff << 8*n)) >> 8*n)
|
|
#define LOCHAR_BYTE(u,n) (tolower(BYTE(u,n)) & 0xff)
|
|
#define REVERSE_ULONGBYTES(u) (LOCHAR_BYTE(u,3) | (LOCHAR_BYTE(u,2) << 8) | (LOCHAR_BYTE(u,1) << 16) | (LOCHAR_BYTE(u,0) << 24))
|
|
|
|
|
|
PSTR
|
|
GetNextLine(
|
|
HANDLE hFile
|
|
)
|
|
// Returns next line in the file hFile
|
|
// Returns NULL if EOF is reached
|
|
{
|
|
static CHAR FileLines1[MAX_PATH] = {0}, FileLines2[MAX_PATH] = {0};
|
|
static CHAR FileLine[MAX_PATH];
|
|
PCHAR pEOL;
|
|
ULONG BytesRead;
|
|
PCHAR pEndOfBuff;
|
|
ULONG BuffLen, ReadLen;
|
|
|
|
pEOL = NULL;
|
|
if (!(pEOL = strchr(FileLines1, '\n'))) {
|
|
// We have something that was already read but it isn't enough for a whole line
|
|
// We need to read the data
|
|
|
|
BuffLen = strlen(FileLines1);
|
|
|
|
// sanity check
|
|
if (BuffLen >= sizeof(FileLines1)) {
|
|
return NULL;
|
|
}
|
|
|
|
pEndOfBuff = &FileLines1[0] + BuffLen;
|
|
ReadLen = sizeof(FileLines1) - BuffLen;
|
|
|
|
ZeroMemory(pEndOfBuff, ReadLen);
|
|
if (ReadFile(hFile, pEndOfBuff, ReadLen - 1, &BytesRead, NULL)) {
|
|
pEOL = strchr(FileLines1, '\n');
|
|
}
|
|
}
|
|
|
|
if (pEOL) {
|
|
FileLine[0] = 0;
|
|
|
|
strncat(FileLine,FileLines1, (ULONG) (pEOL - &FileLines1[0]));
|
|
strcpy(FileLines2, pEOL+1);
|
|
strcpy(FileLines1, FileLines2);
|
|
return FileLine;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
EXTENSION_API ( GetPoolRegion )(
|
|
PDEBUG_CLIENT Client,
|
|
ULONG64 Pool,
|
|
DEBUG_POOL_REGION *PoolData
|
|
)
|
|
{
|
|
INIT_API();
|
|
|
|
*PoolData = GetPoolRegion(Pool);
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
}
|
|
|
|
EXTENSION_API ( GetPoolData )(
|
|
PDEBUG_CLIENT Client,
|
|
ULONG64 Pool,
|
|
PDEBUG_POOL_DATA PoolData
|
|
)
|
|
{
|
|
PCHAR Desc;
|
|
HRESULT Hr;
|
|
PGET_POOL_TAG_DESCRIPTION GetPoolTagDescription;
|
|
|
|
INIT_API();
|
|
|
|
if (!PoolInitializeGlobals()) {
|
|
EXIT_API();
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
Hr = ListPoolPage(Pool, 0x80000002, PoolData);
|
|
|
|
if (Hr != S_OK) {
|
|
EXIT_API();
|
|
return Hr;
|
|
}
|
|
|
|
GetPoolTagDescription = NULL;
|
|
#ifndef _EXTFNS_H
|
|
if (!GetExtensionFunction("GetPoolTagDescription", (FARPROC*) &GetPoolTagDescription)) {
|
|
EXIT_API();
|
|
return E_INVALIDARG;
|
|
}
|
|
(*GetPoolTagDescription)(PoolData->PoolTag, &Desc);
|
|
if (Desc) {
|
|
ULONG strsize = strlen(Desc);
|
|
if (strsize > sizeof(PoolData->PoolTagDescription)) {
|
|
strsize = sizeof(PoolData->PoolTagDescription);
|
|
}
|
|
strncpy(PoolData->PoolTagDescription, Desc, strsize);
|
|
PoolData->PoolTagDescription[strsize] = 0;
|
|
}
|
|
#endif
|
|
EXIT_API();
|
|
return Hr;
|
|
}
|