/*++ 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; }