1501 lines
35 KiB
C
1501 lines
35 KiB
C
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
heapleak.c
|
|
|
|
Abstract:
|
|
|
|
WinDbg Extension Api
|
|
|
|
Author:
|
|
|
|
Adrian Marinescu (adrmarin) 04/17/2000
|
|
|
|
Environment:
|
|
|
|
User Mode.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "heap.h"
|
|
#pragma hdrstop
|
|
|
|
ULONG PageSize;
|
|
ULONG HeapEntrySize;
|
|
ULONG PointerSize;
|
|
ULONG64 HeapLargestAddress;
|
|
BOOLEAN Is64BitArchitecture;
|
|
ULONG FrontEndHeapType;
|
|
ULONG CrtSegmentIndex;
|
|
|
|
ULONG ScanLevel;
|
|
|
|
ULONG LoopLimit = 100000;
|
|
|
|
|
|
BOOLEAN
|
|
ReadHeapSubSegment(
|
|
ULONG64 HeapAddress,
|
|
ULONG64 SegmentAddress,
|
|
ULONG64 SubSegmentAddress,
|
|
HEAP_ITERATOR_CALLBACK HeapCallback
|
|
)
|
|
{
|
|
ULONG64 SubSegmentDescriptor;
|
|
ULONG64 BlockCount = 0, BlockSize;
|
|
ULONG i;
|
|
ULONG64 CrtAddress;
|
|
ULONG64 EntryAddress;
|
|
|
|
GetFieldValue(SubSegmentAddress, "ntdll!_HEAP_USERDATA_HEADER", "SubSegment", SubSegmentDescriptor);
|
|
|
|
if (!(*HeapCallback)( CONTEXT_START_SUBSEGMENT,
|
|
HeapAddress,
|
|
SubSegmentAddress,
|
|
SubSegmentDescriptor,
|
|
0
|
|
)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
GetFieldValue(SubSegmentDescriptor, "ntdll!_HEAP_SUBSEGMENT", "BlockCount", BlockCount);
|
|
|
|
if (GetFieldValue(SubSegmentDescriptor, "ntdll!_HEAP_SUBSEGMENT", "BlockSize", BlockSize)) {
|
|
|
|
(*HeapCallback)( CONTEXT_ERROR,
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
SubSegmentAddress,
|
|
(ULONG64)(&"subsegment cannot access the block size\n")
|
|
);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (BlockSize <= 1) {
|
|
|
|
(*HeapCallback)( CONTEXT_ERROR,
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
SubSegmentAddress,
|
|
(ULONG64)(&"invalid block size\n")
|
|
);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
CrtAddress = SubSegmentAddress + GetTypeSize("ntdll!_HEAP_USERDATA_HEADER");
|
|
|
|
for (i = 0; i < BlockCount; i++) {
|
|
|
|
ULONG64 SmallTagIndex, BlockSegIndex;
|
|
|
|
EntryAddress = CrtAddress + i * BlockSize * HeapEntrySize;
|
|
|
|
GetFieldValue(EntryAddress, "ntdll!_HEAP_ENTRY", "SegmentIndex", BlockSegIndex);
|
|
|
|
if (BlockSegIndex != 0xFF) {
|
|
|
|
(*HeapCallback)( CONTEXT_ERROR,
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
EntryAddress,
|
|
(ULONG64)(&"SegmentIndex field corrupted\n")
|
|
);
|
|
}
|
|
|
|
GetFieldValue(EntryAddress, "ntdll!_HEAP_ENTRY", "SmallTagIndex", SmallTagIndex);
|
|
|
|
if (SmallTagIndex) {
|
|
|
|
(*HeapCallback)( CONTEXT_BUSY_BLOCK,
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
EntryAddress,
|
|
BlockSize * HeapEntrySize
|
|
);
|
|
} else {
|
|
|
|
(*HeapCallback)( CONTEXT_FREE_BLOCK,
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
EntryAddress,
|
|
BlockSize * HeapEntrySize
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!(*HeapCallback)( CONTEXT_END_SUBSEGMENT,
|
|
HeapAddress,
|
|
SubSegmentAddress,
|
|
SubSegmentDescriptor,
|
|
0
|
|
)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Walking heap routines
|
|
//
|
|
|
|
BOOLEAN
|
|
ReadHeapSegment(
|
|
ULONG64 HeapAddress,
|
|
ULONG SegmentIndex,
|
|
ULONG64 SegmentAddress,
|
|
HEAP_ITERATOR_CALLBACK HeapCallback
|
|
)
|
|
{
|
|
ULONG64 SegmentBaseAddress;
|
|
ULONG64 PrevEntryAddress, EntryAddress, NextEntryAddress;
|
|
ULONG64 EntrySize, EntryFlags, BlockSegIndex;
|
|
ULONG64 SegmentLastValidEntry;
|
|
ULONG64 UnCommittedRange, UnCommittedRangeAddress = 0, UnCommittedRangeSize = 0;
|
|
ULONG LoopCount;
|
|
BOOLEAN IsSubsegment;
|
|
|
|
ScanLevel = SCANSEGMENT;
|
|
|
|
if (!(*HeapCallback)( CONTEXT_START_SEGMENT,
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
0,
|
|
0
|
|
)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
CrtSegmentIndex = SegmentIndex;
|
|
|
|
GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "BaseAddress", SegmentBaseAddress);
|
|
GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "LastValidEntry", SegmentLastValidEntry);
|
|
GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "UnCommittedRanges", UnCommittedRange);
|
|
|
|
if (UnCommittedRange) {
|
|
|
|
GetFieldValue(UnCommittedRange, "ntdll!_HEAP_UNCOMMMTTED_RANGE", "Address", UnCommittedRangeAddress);
|
|
GetFieldValue(UnCommittedRange, "ntdll!_HEAP_UNCOMMMTTED_RANGE", "Size", UnCommittedRangeSize);
|
|
}
|
|
|
|
// dprintf("Uncommitted: %p %p %p\n", UnCommittedRange, UnCommittedRangeAddress, UnCommittedRangeSize);
|
|
|
|
if (SegmentBaseAddress == HeapAddress) {
|
|
|
|
EntryAddress = HeapAddress;
|
|
|
|
} else {
|
|
|
|
EntryAddress = SegmentAddress;
|
|
}
|
|
|
|
PrevEntryAddress = 0;
|
|
LoopCount = 0;
|
|
|
|
while (EntryAddress < SegmentLastValidEntry) {
|
|
|
|
if (++LoopCount >= LoopLimit) {
|
|
|
|
dprintf("Walking the segment exceeded the %ld limit\n", LoopLimit);
|
|
|
|
break;
|
|
}
|
|
|
|
if (ScanLevel < SCANSEGMENT) {
|
|
|
|
break;
|
|
}
|
|
|
|
if (GetFieldValue(EntryAddress, "ntdll!_HEAP_ENTRY", "Size", EntrySize)) {
|
|
|
|
(*HeapCallback)( CONTEXT_ERROR,
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
EntryAddress,
|
|
(ULONG64)(&"unable to read uncommited range structure at\n")
|
|
);
|
|
break;
|
|
}
|
|
|
|
if (EntrySize <= 1) {
|
|
|
|
(*HeapCallback)( CONTEXT_ERROR,
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
EntryAddress,
|
|
(ULONG64)(&"invalid block size\n")
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
EntrySize *= HeapEntrySize;
|
|
|
|
NextEntryAddress = EntryAddress + EntrySize;
|
|
|
|
GetFieldValue(EntryAddress, "ntdll!_HEAP_ENTRY", "Flags", EntryFlags);
|
|
|
|
GetFieldValue(EntryAddress, "ntdll!_HEAP_ENTRY", "SegmentIndex", BlockSegIndex);
|
|
|
|
if (BlockSegIndex != CrtSegmentIndex) {
|
|
|
|
(*HeapCallback)( CONTEXT_ERROR,
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
EntryAddress,
|
|
(ULONG64)(&"SegmentIndex field corrupted\n")
|
|
);
|
|
}
|
|
|
|
IsSubsegment = FALSE;
|
|
|
|
if (FrontEndHeapType == 2) {
|
|
|
|
ULONG64 Signature;
|
|
GetFieldValue(EntryAddress + HeapEntrySize, "ntdll!_HEAP_USERDATA_HEADER", "Signature", Signature);
|
|
|
|
if ((ULONG)Signature == 0xF0E0D0C0) {
|
|
|
|
ReadHeapSubSegment( HeapAddress,
|
|
SegmentAddress,
|
|
EntryAddress + HeapEntrySize,
|
|
HeapCallback );
|
|
|
|
IsSubsegment = TRUE;
|
|
|
|
if (CheckControlC()) {
|
|
|
|
ScanLevel = 0;
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!IsSubsegment) {
|
|
if (EntryFlags & HEAP_ENTRY_BUSY) {
|
|
|
|
(*HeapCallback)( CONTEXT_BUSY_BLOCK,
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
EntryAddress,
|
|
EntrySize
|
|
);
|
|
} else {
|
|
|
|
(*HeapCallback)( CONTEXT_FREE_BLOCK,
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
EntryAddress,
|
|
EntrySize
|
|
);
|
|
}
|
|
}
|
|
|
|
PrevEntryAddress = EntryAddress;
|
|
EntryAddress = NextEntryAddress;
|
|
|
|
if (EntryFlags & HEAP_ENTRY_LAST_ENTRY) {
|
|
|
|
if (CheckControlC()) {
|
|
|
|
ScanLevel = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
if (EntryAddress == UnCommittedRangeAddress) {
|
|
|
|
PrevEntryAddress = 0;
|
|
EntryAddress = UnCommittedRangeAddress + UnCommittedRangeSize;
|
|
|
|
GetFieldValue(UnCommittedRange, "ntdll!_HEAP_UNCOMMMTTED_RANGE", "Next", UnCommittedRange);
|
|
|
|
if (UnCommittedRange) {
|
|
|
|
GetFieldValue(UnCommittedRange, "ntdll!_HEAP_UNCOMMMTTED_RANGE", "Address", UnCommittedRangeAddress);
|
|
GetFieldValue(UnCommittedRange, "ntdll!_HEAP_UNCOMMMTTED_RANGE", "Size", UnCommittedRangeSize);
|
|
}
|
|
|
|
} else {
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(*HeapCallback)( CONTEXT_END_SEGMENT,
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
0,
|
|
0
|
|
)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
ReadHeapData(ULONG64 HeapAddress, HEAP_ITERATOR_CALLBACK HeapCallback)
|
|
{
|
|
ULONG SegmentCount = 0;
|
|
ULONG64 Head;
|
|
ULONG64 Next;
|
|
ULONG i;
|
|
ULONG PtrSize;
|
|
ULONG SegmentsOffset;
|
|
ULONG VirtualBlockOffset;
|
|
ULONG64 Segment;
|
|
ULONG64 LookasideAddress;
|
|
ULONG64 LFHAddress;
|
|
ULONG LoopCount;
|
|
|
|
ScanLevel = SCANHEAP;
|
|
|
|
if (!(*HeapCallback)( CONTEXT_START_HEAP,
|
|
HeapAddress,
|
|
0,
|
|
0,
|
|
0
|
|
)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
PtrSize = IsPtr64() ? 8 : 4;
|
|
|
|
LookasideAddress = 0;
|
|
LFHAddress = 0;
|
|
FrontEndHeapType = 0;
|
|
|
|
if (GetFieldValue(HeapAddress, "ntdll!_HEAP", "Lookaside", LookasideAddress)) {
|
|
|
|
|
|
if (GetFieldValue(HeapAddress, "ntdll!_HEAP", "FrontEndHeapType", FrontEndHeapType)) {
|
|
|
|
dprintf("Front-end heap type info is not available\n");
|
|
}
|
|
|
|
switch (FrontEndHeapType){
|
|
case 1:
|
|
GetFieldValue(HeapAddress, "ntdll!_HEAP", "FrontEndHeap", LookasideAddress);
|
|
break;
|
|
case 2:
|
|
GetFieldValue(HeapAddress, "ntdll!_HEAP", "FrontEndHeap", LFHAddress);
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (LookasideAddress) {
|
|
|
|
FrontEndHeapType = 1;
|
|
}
|
|
}
|
|
|
|
GetFieldOffset("ntdll!_HEAP", "Segments", &SegmentsOffset);
|
|
|
|
do {
|
|
|
|
if (ScanLevel < SCANHEAP) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ReadPointer( HeapAddress + SegmentsOffset + SegmentCount*PtrSize,
|
|
&Segment ) ) {
|
|
|
|
break;
|
|
}
|
|
|
|
if (Segment) {
|
|
|
|
ReadHeapSegment( HeapAddress,
|
|
SegmentCount,
|
|
Segment,
|
|
HeapCallback
|
|
);
|
|
|
|
SegmentCount += 1;
|
|
|
|
if (CheckControlC()) {
|
|
|
|
ScanLevel = 0;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
} while ( Segment );
|
|
|
|
GetFieldOffset("_HEAP", "VirtualAllocdBlocks", &VirtualBlockOffset);
|
|
|
|
Head = HeapAddress + VirtualBlockOffset;
|
|
GetFieldValue(HeapAddress, "ntdll!_HEAP", "VirtualAllocdBlocks.Flink", Next);
|
|
|
|
LoopCount = 0;
|
|
|
|
while (Next != Head) {
|
|
|
|
ULONG64 VBlockSize;
|
|
|
|
if (++LoopCount >= LoopLimit) {
|
|
|
|
dprintf("Walking the virtual block list exceeded the %ld limit\n", LoopLimit);
|
|
|
|
break;
|
|
}
|
|
|
|
if (ScanLevel < SCANHEAP) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
GetFieldValue(Next, "ntdll!_HEAP_VIRTUAL_ALLOC_ENTRY", "CommitSize", VBlockSize);
|
|
|
|
(*HeapCallback)( CONTEXT_VIRTUAL_BLOCK,
|
|
HeapAddress,
|
|
0,
|
|
Next,
|
|
VBlockSize
|
|
);
|
|
|
|
if (!ReadPointer(Next, &Next)) {
|
|
|
|
(*HeapCallback)( CONTEXT_ERROR,
|
|
HeapAddress,
|
|
0,
|
|
Next,
|
|
(ULONG64)(&"Unable to read virtual block\n")
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!(*HeapCallback)( CONTEXT_END_BLOCKS,
|
|
HeapAddress,
|
|
0,
|
|
0,
|
|
0
|
|
)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// dprintf("Scanning lookasides\n");
|
|
|
|
|
|
if (LookasideAddress) {
|
|
ULONG LookasideSize;
|
|
PVOID Lookaside;
|
|
ULONG HeapEntrySize;
|
|
|
|
HeapEntrySize = GetTypeSize("ntdll!_HEAP_ENTRY");
|
|
LookasideSize = GetTypeSize("ntdll!_HEAP_LOOKASIDE");
|
|
|
|
for (i = 0; i < HEAP_MAXIMUM_FREELISTS; i++) {
|
|
|
|
if (ScanLevel < SCANHEAP) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
GetFieldValue(LookasideAddress, "ntdll!_HEAP_LOOKASIDE", "ListHead.Next", Next);
|
|
|
|
if (Is64BitArchitecture) {
|
|
|
|
Next <<= 3;
|
|
}
|
|
|
|
LoopCount = 0;
|
|
|
|
while (Next) {
|
|
|
|
if (++LoopCount >= LoopLimit) {
|
|
|
|
dprintf("Walking the lookaside block list index %ld exceeded the %ld limit\n", i, LoopLimit);
|
|
|
|
break;
|
|
}
|
|
|
|
(*HeapCallback)( CONTEXT_LOOKASIDE_BLOCK,
|
|
HeapAddress,
|
|
0,
|
|
Next - HeapEntrySize,
|
|
i*HeapEntrySize
|
|
);
|
|
|
|
if (!ReadPointer(Next, &Next)) {
|
|
|
|
(*HeapCallback)( CONTEXT_ERROR,
|
|
HeapAddress,
|
|
0,
|
|
Next,
|
|
(ULONG64)(&"Unable to read lookaside block\n")
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
LookasideAddress += LookasideSize;
|
|
}
|
|
}
|
|
|
|
if (LFHAddress) {
|
|
(*HeapCallback)( CONTEXT_LFH_HEAP,
|
|
HeapAddress,
|
|
LFHAddress,
|
|
0,
|
|
0
|
|
);
|
|
}
|
|
|
|
(*HeapCallback)( CONTEXT_END_HEAP,
|
|
HeapAddress,
|
|
0,
|
|
0,
|
|
0
|
|
);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
ScanProcessHeaps(
|
|
IN ULONG64 AddressToDump,
|
|
IN ULONG64 ProcessPeb,
|
|
HEAP_ITERATOR_CALLBACK HeapCallback
|
|
)
|
|
{
|
|
ULONG NumberOfHeaps;
|
|
ULONG64 pHeapsList;
|
|
ULONG64 * Heaps;
|
|
ULONG PtrSize;
|
|
ULONG HeapNumber;
|
|
|
|
if (AddressToDump) {
|
|
|
|
ReadHeapData ( AddressToDump, HeapCallback);
|
|
return;
|
|
}
|
|
|
|
if (!(*HeapCallback)( CONTEXT_START_GLOBALS,
|
|
0,
|
|
0,
|
|
0,
|
|
ProcessPeb
|
|
)) {
|
|
|
|
return;
|
|
}
|
|
|
|
ScanLevel = SCANPROCESS;
|
|
|
|
GetFieldValue(ProcessPeb, "ntdll!_PEB", "NumberOfHeaps", NumberOfHeaps);
|
|
GetFieldValue(ProcessPeb, "ntdll!_PEB", "ProcessHeaps", pHeapsList);
|
|
|
|
if (NumberOfHeaps == 0) {
|
|
|
|
dprintf( "No heaps to display.\n" );
|
|
|
|
return;
|
|
}
|
|
|
|
if (!pHeapsList) {
|
|
|
|
dprintf( "Unable to get address of ProcessHeaps array\n" );
|
|
return;
|
|
|
|
}
|
|
|
|
Heaps = malloc( NumberOfHeaps * sizeof(ULONG64) );
|
|
|
|
if (!Heaps) {
|
|
|
|
dprintf( "Unable to allocate memory to hold ProcessHeaps array\n" );
|
|
|
|
return;
|
|
}
|
|
|
|
PtrSize = IsPtr64() ? 8 : 4;
|
|
|
|
for (HeapNumber=0; HeapNumber<NumberOfHeaps ; HeapNumber++) {
|
|
|
|
if (!ReadPointer( pHeapsList + HeapNumber*PtrSize,
|
|
&Heaps[HeapNumber] ) ) {
|
|
|
|
dprintf( "%08p: Unable to read ProcessHeaps array\n", pHeapsList );
|
|
|
|
free(Heaps);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for ( HeapNumber = 0; HeapNumber < NumberOfHeaps; HeapNumber++ ) {
|
|
|
|
if (ScanLevel < SCANPROCESS) {
|
|
|
|
free(Heaps);
|
|
return;
|
|
}
|
|
|
|
if ((AddressToDump == 0)
|
|
||
|
|
(AddressToDump == Heaps[HeapNumber])) {
|
|
|
|
ReadHeapData ( Heaps[HeapNumber], HeapCallback);
|
|
}
|
|
}
|
|
|
|
free(Heaps);
|
|
}
|
|
|
|
//
|
|
// Allocation routines
|
|
//
|
|
|
|
HANDLE TempHeap;
|
|
|
|
#define AllocateBlock(Size) HeapAlloc(TempHeap, 0, Size)
|
|
#define FreeBlock(P) HeapFree(TempHeap, 0, P)
|
|
|
|
//
|
|
// Leak detector code
|
|
//
|
|
|
|
typedef enum _USAGE_TYPE {
|
|
|
|
UsageUnknown,
|
|
UsageModule,
|
|
UsageHeap,
|
|
UsageOther
|
|
|
|
} USAGE_TYPE;
|
|
|
|
typedef struct _HEAP_BLOCK {
|
|
|
|
LIST_ENTRY Entry;
|
|
ULONG64 BlockAddress;
|
|
ULONG64 Size;
|
|
LONG Count;
|
|
} HEAP_BLOCK, *PHEAP_BLOCK;
|
|
|
|
typedef struct _BLOCK_DESCR {
|
|
USAGE_TYPE Type;
|
|
ULONG64 Heap;
|
|
LONG Count;
|
|
HEAP_BLOCK Blocks[1];
|
|
}BLOCK_DESCR, *PBLOCK_DESCR;
|
|
|
|
typedef struct _MEMORY_MAP {
|
|
|
|
ULONG64 Granularity;
|
|
ULONG64 Offset;
|
|
ULONG64 MaxAddress;
|
|
|
|
CHAR FlagsBitmap[256 / 8];
|
|
|
|
union{
|
|
|
|
struct _MEMORY_MAP * Details[ 256 ];
|
|
PBLOCK_DESCR Usage[ 256 ];
|
|
};
|
|
|
|
struct _MEMORY_MAP * Parent;
|
|
|
|
} MEMORY_MAP, *PMEMORY_MAP;
|
|
|
|
MEMORY_MAP ProcessMemory;
|
|
ULONG LeaksCount = 0;
|
|
ULONG64 PreviousPage = 0;
|
|
ULONG64 CrtPage = 0;
|
|
LONG NumBlocks = 0;
|
|
PHEAP_BLOCK TempBlocks;
|
|
ULONG64 LastHeapAddress = 0;
|
|
ULONG64 RtlpPreviousStartAddress = 0;
|
|
|
|
LIST_ENTRY HeapBusyList;
|
|
LIST_ENTRY HeapLeakList;
|
|
|
|
|
|
void InitializeMap(PMEMORY_MAP MemMap, PMEMORY_MAP Parent)
|
|
{
|
|
memset(MemMap, 0, sizeof(*MemMap));
|
|
MemMap->Parent = Parent;
|
|
if (Parent) {
|
|
MemMap->Granularity = Parent->Granularity / 256;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SetBlockInfo(PMEMORY_MAP MemMap, ULONG64 Base, ULONG64 Size, PBLOCK_DESCR BlockDescr)
|
|
{
|
|
ULONG64 Start, End;
|
|
ULONG64 i;
|
|
|
|
if (((Base + Size - 1) < MemMap->Offset) ||
|
|
(Base > MemMap->MaxAddress)
|
|
) {
|
|
|
|
return;
|
|
}
|
|
|
|
if (Base > MemMap->Offset) {
|
|
Start = (Base - MemMap->Offset) / MemMap->Granularity;
|
|
} else {
|
|
Start = 0;
|
|
}
|
|
|
|
End = (Base - MemMap->Offset + Size - 1) / MemMap->Granularity;
|
|
|
|
if (End > 255) {
|
|
|
|
End = 255;
|
|
}
|
|
|
|
for (i = Start; i <= End; i++) {
|
|
|
|
if (MemMap->Granularity == PageSize) {
|
|
|
|
if (BlockDescr) {
|
|
if (MemMap->Usage[i] != NULL) {
|
|
if (MemMap->Usage[i] != BlockDescr) {
|
|
|
|
dprintf("Error\n");
|
|
}
|
|
}
|
|
|
|
MemMap->Usage[i] = BlockDescr;
|
|
} else {
|
|
|
|
MemMap->FlagsBitmap[i / 8] |= 1 << (i % 8);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!MemMap->Details[i]) {
|
|
|
|
MemMap->Details[i] = AllocateBlock(sizeof(*MemMap));
|
|
|
|
if (!MemMap->Details[i]) {
|
|
dprintf("Error allocate\n");
|
|
return;
|
|
}
|
|
|
|
InitializeMap(MemMap->Details[i], MemMap);
|
|
MemMap->Details[i]->Offset = MemMap->Offset + MemMap->Granularity * i;
|
|
MemMap->Details[i]->MaxAddress = MemMap->Offset + MemMap->Granularity * (i+1) - 1;
|
|
}
|
|
|
|
SetBlockInfo(MemMap->Details[i], Base, Size, BlockDescr);
|
|
}
|
|
}
|
|
}
|
|
|
|
PBLOCK_DESCR
|
|
GetBlockInfo(PMEMORY_MAP MemMap, ULONG64 Base)
|
|
{
|
|
ULONG64 Start;
|
|
PBLOCK_DESCR BlockDescr = NULL;
|
|
|
|
if ((Base < MemMap->Offset) ||
|
|
(Base > MemMap->MaxAddress)
|
|
) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (Base > MemMap->Offset) {
|
|
Start = (Base - MemMap->Offset) / MemMap->Granularity;
|
|
} else {
|
|
Start = 0;
|
|
}
|
|
|
|
if (MemMap->Granularity == PageSize) {
|
|
|
|
return MemMap->Usage[Start];
|
|
|
|
} else {
|
|
|
|
if (MemMap->Details[Start]) {
|
|
|
|
return GetBlockInfo(MemMap->Details[Start], Base);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
BOOLEAN
|
|
GetFlag(PMEMORY_MAP MemMap, ULONG64 Base)
|
|
{
|
|
ULONG64 Start;
|
|
PBLOCK_DESCR BlockDescr = NULL;
|
|
/*
|
|
dprintf("GetFlag %p %p %p\n",
|
|
MemMap->Offset,
|
|
MemMap->MaxAddress,
|
|
MemMap->Granularity
|
|
);
|
|
*/
|
|
if ((Base < MemMap->Offset) ||
|
|
(Base > MemMap->MaxAddress)
|
|
) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (Base > MemMap->Offset) {
|
|
Start = (Base - MemMap->Offset) / MemMap->Granularity;
|
|
} else {
|
|
Start = 0;
|
|
}
|
|
|
|
if (MemMap->Granularity == PageSize) {
|
|
|
|
ULONG Flag = (MemMap->FlagsBitmap[Start / 8] & (1 << (Start % 8))) != 0;
|
|
|
|
return (MemMap->FlagsBitmap[Start / 8] & (1 << (Start % 8))) != 0;
|
|
|
|
} else {
|
|
|
|
if (MemMap->Details[Start]) {
|
|
|
|
return GetFlag(MemMap->Details[Start], Base);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void InitializeSystem()
|
|
{
|
|
ULONG64 AddressRange = PageSize;
|
|
ULONG64 PreviousAddressRange = PageSize;
|
|
|
|
InitializeMap(&ProcessMemory, NULL);
|
|
|
|
InitializeListHead( &HeapBusyList );
|
|
InitializeListHead( &HeapLeakList );
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
AddressRange = AddressRange * 256;
|
|
|
|
if ((AddressRange < PreviousAddressRange)
|
|
||
|
|
(AddressRange > HeapLargestAddress)
|
|
) {
|
|
|
|
ProcessMemory.MaxAddress = HeapLargestAddress;
|
|
|
|
ProcessMemory.Granularity = PreviousAddressRange;
|
|
|
|
break;
|
|
}
|
|
|
|
PreviousAddressRange = AddressRange;
|
|
}
|
|
|
|
TempBlocks = AllocateBlock(PageSize);
|
|
|
|
if (TempBlocks == NULL) {
|
|
|
|
dprintf("Cannot allocate temp buffer\n");
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
PushPageDescriptor(ULONG64 Page, ULONG64 NumPages)
|
|
{
|
|
PBLOCK_DESCR PBlockDescr;
|
|
PBLOCK_DESCR PreviousDescr;
|
|
LONG i;
|
|
|
|
PreviousDescr = GetBlockInfo(&ProcessMemory, Page * PageSize);
|
|
|
|
if (PreviousDescr) {
|
|
|
|
dprintf("Conflicting descriptors %08lx\n", PreviousDescr);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
PBlockDescr = (PBLOCK_DESCR)AllocateBlock(sizeof(BLOCK_DESCR) + (NumBlocks - 1) * sizeof(HEAP_BLOCK));
|
|
|
|
if (!PBlockDescr) {
|
|
|
|
dprintf("Unable to allocate page descriptor\n");
|
|
|
|
return FALSE;
|
|
}
|
|
PBlockDescr->Type = UsageHeap;
|
|
PBlockDescr->Count = NumBlocks;
|
|
PBlockDescr->Heap = LastHeapAddress;
|
|
|
|
memcpy(PBlockDescr->Blocks, TempBlocks, NumBlocks * sizeof(HEAP_BLOCK));
|
|
|
|
for (i = 0; i < NumBlocks; i++) {
|
|
|
|
InitializeListHead( &PBlockDescr->Blocks[i].Entry );
|
|
|
|
if (PBlockDescr->Blocks[i].BlockAddress != RtlpPreviousStartAddress) {
|
|
|
|
InsertTailList(&HeapLeakList, &PBlockDescr->Blocks[i].Entry);
|
|
|
|
PBlockDescr->Blocks[i].Count = 0;
|
|
|
|
RtlpPreviousStartAddress = PBlockDescr->Blocks[i].BlockAddress;
|
|
}
|
|
}
|
|
|
|
SetBlockInfo(&ProcessMemory, Page * PageSize, NumPages * PageSize, PBlockDescr);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN RegisterHeapBlocks(
|
|
IN ULONG Context,
|
|
IN ULONG64 HeapAddress,
|
|
IN ULONG64 SegmentAddress,
|
|
IN ULONG64 EntryAddress,
|
|
IN ULONG64 Data
|
|
)
|
|
{
|
|
if (Context == CONTEXT_START_HEAP) {
|
|
|
|
dprintf("Heap %p\n", HeapAddress);
|
|
|
|
LastHeapAddress = HeapAddress;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if (Context == CONTEXT_START_SEGMENT) {
|
|
|
|
ULONG64 NumberOfPages;
|
|
ULONG64 SegmentBaseAddress;
|
|
|
|
GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "NumberOfPages", NumberOfPages);
|
|
GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "BaseAddress", SegmentBaseAddress);
|
|
|
|
SetBlockInfo(&ProcessMemory, SegmentBaseAddress, NumberOfPages * PageSize, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if (Context == CONTEXT_ERROR) {
|
|
|
|
dprintf("HEAP %p (Seg %p) At %p Error: %s\n",
|
|
HeapAddress,
|
|
SegmentAddress,
|
|
EntryAddress,
|
|
Data
|
|
);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if (Context == CONTEXT_END_BLOCKS) {
|
|
if (PreviousPage) {
|
|
|
|
PushPageDescriptor(PreviousPage, 1);
|
|
}
|
|
|
|
PreviousPage = 0;
|
|
NumBlocks = 0;
|
|
|
|
} else if (Context == CONTEXT_BUSY_BLOCK) {
|
|
|
|
ULONG EntrySize;
|
|
ULONG64 EndPage;
|
|
|
|
EntrySize = (ULONG)Data;
|
|
|
|
EndPage = (EntryAddress + (EntrySize - 1)) / PageSize;
|
|
|
|
if (!GetFlag(&ProcessMemory, EntryAddress)) {
|
|
|
|
dprintf("CONTEXT_BUSY_BLOCK %p address isn't from the heap\n", EntryAddress);
|
|
}
|
|
|
|
CrtPage = (EntryAddress) / PageSize;
|
|
|
|
if (CrtPage != PreviousPage) {
|
|
|
|
if (PreviousPage) {
|
|
|
|
PushPageDescriptor(PreviousPage, 1);
|
|
}
|
|
|
|
PreviousPage = CrtPage;
|
|
NumBlocks = 0;
|
|
}
|
|
|
|
TempBlocks[NumBlocks].BlockAddress = EntryAddress;
|
|
TempBlocks[NumBlocks].Count = 0;
|
|
TempBlocks[NumBlocks].Size = EntrySize;
|
|
|
|
NumBlocks++;
|
|
|
|
if (EndPage != CrtPage) {
|
|
|
|
PushPageDescriptor(CrtPage, 1);
|
|
|
|
NumBlocks = 0;
|
|
|
|
TempBlocks[NumBlocks].BlockAddress = (ULONG_PTR)EntryAddress;
|
|
TempBlocks[NumBlocks].Count = 0;
|
|
TempBlocks[NumBlocks].Size = EntrySize;
|
|
|
|
NumBlocks = 1;
|
|
|
|
if (EndPage - CrtPage > 1) {
|
|
|
|
PushPageDescriptor(CrtPage + 1, EndPage - CrtPage - 1);
|
|
}
|
|
|
|
PreviousPage = EndPage;
|
|
}
|
|
} else if (Context == CONTEXT_VIRTUAL_BLOCK) {
|
|
|
|
ULONG64 EndPage;
|
|
|
|
EndPage = (EntryAddress + Data - 1) / PageSize;
|
|
|
|
CrtPage = (EntryAddress) / PageSize;
|
|
|
|
if (CrtPage != PreviousPage) {
|
|
|
|
if (PreviousPage) {
|
|
|
|
PushPageDescriptor(PreviousPage, 1);
|
|
}
|
|
|
|
PreviousPage = CrtPage;
|
|
NumBlocks = 0;
|
|
} else {
|
|
dprintf("Error in large block address\n");
|
|
}
|
|
|
|
TempBlocks[NumBlocks].BlockAddress = EntryAddress;
|
|
TempBlocks[NumBlocks].Count = 0;
|
|
TempBlocks[NumBlocks].Size = Data * HeapEntrySize;
|
|
|
|
NumBlocks++;
|
|
|
|
PushPageDescriptor(CrtPage, EndPage - CrtPage + 1);
|
|
|
|
PreviousPage = 0;
|
|
|
|
} else if ( Context == CONTEXT_LOOKASIDE_BLOCK ) {
|
|
|
|
PBLOCK_DESCR PBlockDescr;
|
|
LONG i;
|
|
|
|
|
|
if (!GetFlag(&ProcessMemory, EntryAddress)) {
|
|
|
|
dprintf("CONTEXT_LOOKASIDE_BLOCK %p address isn't from the heap\n", EntryAddress);
|
|
}
|
|
|
|
PBlockDescr = GetBlockInfo(&ProcessMemory, EntryAddress);
|
|
|
|
if (!PBlockDescr) {
|
|
|
|
dprintf("Error finding block from lookaside %p\n", EntryAddress);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < PBlockDescr->Count; i++) {
|
|
|
|
if ((PBlockDescr->Blocks[i].BlockAddress <= (ULONG_PTR)EntryAddress) &&
|
|
(PBlockDescr->Blocks[i].BlockAddress + PBlockDescr->Blocks[i].Size > (ULONG_PTR)EntryAddress)) {
|
|
|
|
PBlockDescr->Blocks[i].Count = -10000;
|
|
RemoveEntryList(&PBlockDescr->Blocks[i].Entry);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
dprintf("Error, block %p from lookaside not found in allocated block list\n", EntryAddress);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PHEAP_BLOCK
|
|
GetHeapBlock(ULONG64 Address)
|
|
{
|
|
PBLOCK_DESCR PBlockDescr;
|
|
LONG i;
|
|
|
|
PBlockDescr = GetBlockInfo(&ProcessMemory, Address);
|
|
|
|
if (PBlockDescr) {
|
|
for (i = 0; i < PBlockDescr->Count; i++) {
|
|
|
|
if ((PBlockDescr->Blocks[i].BlockAddress <= Address) &&
|
|
(PBlockDescr->Blocks[i].BlockAddress + PBlockDescr->Blocks[i].Size > Address)) {
|
|
|
|
if (PBlockDescr->Blocks[i].BlockAddress != Address) {
|
|
|
|
return GetHeapBlock(PBlockDescr->Blocks[i].BlockAddress);
|
|
}
|
|
|
|
return &(PBlockDescr->Blocks[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
BOOLEAN
|
|
ScanHeapAllocBlocks()
|
|
{
|
|
|
|
PLIST_ENTRY Next;
|
|
|
|
Next = HeapBusyList.Flink;
|
|
|
|
while (Next != &HeapBusyList) {
|
|
|
|
PHEAP_BLOCK Block = CONTAINING_RECORD(Next, HEAP_BLOCK, Entry);
|
|
|
|
PULONG_PTR CrtAddress = (PULONG_PTR)(Block->BlockAddress + HeapEntrySize);
|
|
|
|
Next = Next->Flink;
|
|
|
|
while ((ULONG_PTR)CrtAddress < Block->BlockAddress + Block->Size) {
|
|
|
|
ULONG_PTR Pointer;
|
|
|
|
if (ReadMemory( (ULONG64)(CrtAddress),
|
|
&Pointer,
|
|
sizeof(Pointer),
|
|
NULL
|
|
)) {
|
|
|
|
PHEAP_BLOCK pBlock = GetHeapBlock( Pointer );
|
|
|
|
if (pBlock) {
|
|
|
|
//
|
|
// We found a block. we increment then the reference count
|
|
//
|
|
|
|
if (pBlock->Count == 0) {
|
|
|
|
RemoveEntryList(&pBlock->Entry);
|
|
InsertTailList(&HeapBusyList, &pBlock->Entry);
|
|
}
|
|
|
|
pBlock->Count += 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Go to the next possible pointer
|
|
//
|
|
|
|
CrtAddress++;
|
|
}
|
|
}
|
|
|
|
Next = HeapLeakList.Flink;
|
|
|
|
while (Next != &HeapLeakList) {
|
|
|
|
PHEAP_BLOCK Block = CONTAINING_RECORD(Next, HEAP_BLOCK, Entry);
|
|
PBLOCK_DESCR PBlockDescr = GetBlockInfo( &ProcessMemory, Block->BlockAddress );
|
|
PULONG_PTR CrtAddress = (PULONG_PTR)(Block->BlockAddress + HeapEntrySize);
|
|
|
|
//
|
|
// First time we need to display the header
|
|
//
|
|
|
|
if (LeaksCount == 0) {
|
|
|
|
dprintf("\n");
|
|
DumpEntryHeader();
|
|
}
|
|
|
|
//
|
|
// Display the information for this block
|
|
//
|
|
|
|
DumpEntryInfo(PBlockDescr->Heap, 0, Block->BlockAddress);
|
|
|
|
LeaksCount += 1;
|
|
|
|
//
|
|
// Go to the next item from the leak list
|
|
//
|
|
|
|
Next = Next->Flink;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
ScanProcessVM (
|
|
HANDLE hProcess
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
SIZE_T BufferLen;
|
|
ULONG_PTR lpAddress = 0;
|
|
MEMORY_BASIC_INFORMATION Buffer;
|
|
PVOID MemoryBuffer;
|
|
|
|
if ( hProcess ) {
|
|
|
|
PROCESS_BASIC_INFORMATION BasicInfo;
|
|
|
|
dprintf("Scanning VM ...");
|
|
|
|
Status = NtQueryInformationProcess(
|
|
hProcess,
|
|
ProcessBasicInformation,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
NULL
|
|
);
|
|
|
|
// dprintf("PEB %p\n", BasicInfo.PebBaseAddress);
|
|
|
|
MemoryBuffer = AllocateBlock(PageSize);
|
|
|
|
if (!MemoryBuffer) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BufferLen = sizeof(Buffer);
|
|
|
|
while (BufferLen) {
|
|
|
|
BufferLen = VirtualQueryEx( hProcess,
|
|
(LPVOID)lpAddress,
|
|
&Buffer,
|
|
sizeof(Buffer)
|
|
);
|
|
|
|
if (BufferLen) {
|
|
|
|
if (( Buffer.AllocationProtect &
|
|
(PAGE_READWRITE | PAGE_EXECUTE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY))
|
|
) {
|
|
|
|
ULONG64 NumPages;
|
|
ULONG i, j;
|
|
|
|
NumPages = Buffer.RegionSize / PageSize;
|
|
|
|
for (i = 0; i < NumPages; i++) {
|
|
|
|
if (ReadMemory( (ULONG64)(lpAddress + i * PageSize),
|
|
MemoryBuffer,
|
|
PageSize,
|
|
NULL )
|
|
&&
|
|
!GetFlag(&ProcessMemory, lpAddress)
|
|
) {
|
|
|
|
ULONG_PTR * Pointers = (ULONG_PTR *)MemoryBuffer;
|
|
|
|
for (j = 0; j < PageSize/sizeof(ULONG_PTR); j++) {
|
|
|
|
ULONG_PTR Address = lpAddress + i * PageSize + j * sizeof(ULONG_PTR);
|
|
|
|
PHEAP_BLOCK pBlock = GetHeapBlock(*Pointers);
|
|
|
|
if (pBlock) {
|
|
|
|
if (pBlock->Count == 0) {
|
|
|
|
RemoveEntryList(&pBlock->Entry);
|
|
InsertTailList(&HeapBusyList, &pBlock->Entry);
|
|
}
|
|
|
|
pBlock->Count += 1;
|
|
}
|
|
|
|
Pointers += 1;
|
|
}
|
|
}
|
|
|
|
if (CheckControlC()) {
|
|
FreeBlock(MemoryBuffer);
|
|
ScanLevel = 0;
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
lpAddress += Buffer.RegionSize;
|
|
}
|
|
}
|
|
|
|
//
|
|
// First scan will mark all used blocks
|
|
//
|
|
|
|
ScanHeapAllocBlocks();
|
|
|
|
FreeBlock(MemoryBuffer);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void InspectLeaks(
|
|
IN ULONG64 AddressToDump,
|
|
IN ULONG64 ProcessPeb
|
|
)
|
|
{
|
|
HANDLE hProcess;
|
|
|
|
LeaksCount = 0;
|
|
|
|
InitializeSystem();
|
|
|
|
if (TempBlocks) {
|
|
ScanProcessHeaps( 0,
|
|
ProcessPeb,
|
|
RegisterHeapBlocks
|
|
);
|
|
|
|
GetCurrentProcessHandle( &hProcess );
|
|
|
|
if (hProcess){
|
|
|
|
ScanProcessVM(hProcess);
|
|
|
|
if (LeaksCount) {
|
|
|
|
dprintf("%ld leaks detected.\n", LeaksCount);
|
|
|
|
} else {
|
|
|
|
dprintf( "No leaks detected.\n");
|
|
}
|
|
|
|
} else {
|
|
|
|
dprintf("Unable to get the process handle\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
HeapDetectLeaks()
|
|
{
|
|
ULONG64 Process;
|
|
ULONG64 ThePeb;
|
|
ULONG64 PageHeapAddress;
|
|
BOOLEAN PageHeapEnabled = FALSE;
|
|
ULONG PageHeapFlags = 0;
|
|
|
|
if (!InitializeHeapExtension()) {
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Return immediately if full page heap is enabled
|
|
//
|
|
|
|
PageHeapAddress = GetExpression ("ntdll!RtlpDebugPageHeap");
|
|
|
|
ReadMemory (PageHeapAddress,
|
|
&PageHeapEnabled,
|
|
sizeof (BOOLEAN),
|
|
NULL);
|
|
|
|
PageHeapAddress = GetExpression ("ntdll!RtlpDphGlobalFlags");
|
|
|
|
ReadMemory (PageHeapAddress,
|
|
&PageHeapFlags,
|
|
sizeof (ULONG),
|
|
NULL);
|
|
|
|
if (PageHeapEnabled == TRUE && (PageHeapFlags & 0x01)) {
|
|
dprintf ("!heap -l does not work if full page heap is enabled for the process \n");
|
|
return;
|
|
}
|
|
|
|
|
|
GetPebAddress( 0, &ThePeb);
|
|
|
|
TempHeap = HeapCreate(HEAP_NO_SERIALIZE | HEAP_GROWABLE, 0, 0);
|
|
if (!TempHeap) {
|
|
|
|
dprintf("Unable to create temporary heap\n");
|
|
return;
|
|
}
|
|
|
|
InspectLeaks( 0, ThePeb);
|
|
HeapDestroy(TempHeap);
|
|
|
|
TempHeap = NULL;
|
|
}
|
|
|
|
BOOLEAN
|
|
InitializeHeapExtension()
|
|
{
|
|
PointerSize = IsPtr64() ? 8 : 4;
|
|
HeapEntrySize = GetTypeSize("ntdll!_HEAP_ENTRY");
|
|
|
|
if ((HeapEntrySize == 0)
|
|
||
|
|
(PointerSize == 0)) {
|
|
|
|
dprintf("Invalid type information\n");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Issue adrmarin 04/28/00: The page size should be available in the new interface
|
|
// IDebugControl::GetPageSize
|
|
//
|
|
|
|
if (PointerSize == 4) {
|
|
|
|
PageSize = 0x1000;
|
|
HeapLargestAddress = (ULONG)-1;
|
|
Is64BitArchitecture = FALSE;
|
|
|
|
} else {
|
|
|
|
PageSize = 0x2000;
|
|
HeapLargestAddress = (ULONGLONG)-1;
|
|
Is64BitArchitecture = TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|