Files
wj32 f4ad8a85da Upgrade all projects to VS 2015 (almost all by dmex)
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@6179 21ef857c-d57f-4fe0-8362-d861dc6d29cd
2015-08-16 06:10:56 +00:00

772 lines
24 KiB
C

/*
* Process Hacker -
* memory provider
*
* Copyright (C) 2010-2015 wj32
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
*/
#include <phapp.h>
#include <heapstruct.h>
#define MAX_HEAPS 1000
#define WS_REQUEST_COUNT (PAGE_SIZE / sizeof(MEMORY_WORKING_SET_EX_INFORMATION))
VOID PhpMemoryItemDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
);
PPH_OBJECT_TYPE PhMemoryItemType;
BOOLEAN PhMemoryProviderInitialization(
VOID
)
{
PhMemoryItemType = PhCreateObjectType(L"MemoryItem", 0, PhpMemoryItemDeleteProcedure);
return TRUE;
}
VOID PhGetMemoryProtectionString(
_In_ ULONG Protection,
_Out_writes_(17) PWSTR String
)
{
PWSTR string;
PH_STRINGREF base;
if (!Protection)
{
String[0] = 0;
return;
}
if (Protection & PAGE_NOACCESS)
PhInitializeStringRef(&base, L"NA");
else if (Protection & PAGE_READONLY)
PhInitializeStringRef(&base, L"R");
else if (Protection & PAGE_READWRITE)
PhInitializeStringRef(&base, L"RW");
else if (Protection & PAGE_WRITECOPY)
PhInitializeStringRef(&base, L"WC");
else if (Protection & PAGE_EXECUTE)
PhInitializeStringRef(&base, L"X");
else if (Protection & PAGE_EXECUTE_READ)
PhInitializeStringRef(&base, L"RX");
else if (Protection & PAGE_EXECUTE_READWRITE)
PhInitializeStringRef(&base, L"RWX");
else if (Protection & PAGE_EXECUTE_WRITECOPY)
PhInitializeStringRef(&base, L"WCX");
else
PhInitializeStringRef(&base, L"?");
string = String;
memcpy(string, base.Buffer, base.Length);
string += base.Length / sizeof(WCHAR);
if (Protection & PAGE_GUARD)
{
memcpy(string, L"+G", 2 * 2);
string += 2;
}
if (Protection & PAGE_NOCACHE)
{
memcpy(string, L"+NC", 3 * 2);
string += 3;
}
if (Protection & PAGE_WRITECOMBINE)
{
memcpy(string, L"+WCM", 4 * 2);
string += 4;
}
*string = 0;
}
PWSTR PhGetMemoryStateString(
_In_ ULONG State
)
{
if (State & MEM_COMMIT)
return L"Commit";
else if (State & MEM_RESERVE)
return L"Reserved";
else if (State & MEM_FREE)
return L"Free";
else
return L"Unknown";
}
PWSTR PhGetMemoryTypeString(
_In_ ULONG Type
)
{
if (Type & MEM_PRIVATE)
return L"Private";
else if (Type & MEM_MAPPED)
return L"Mapped";
else if (Type & MEM_IMAGE)
return L"Image";
else
return L"Unknown";
}
PPH_MEMORY_ITEM PhCreateMemoryItem(
VOID
)
{
PPH_MEMORY_ITEM memoryItem;
memoryItem = PhCreateObject(sizeof(PH_MEMORY_ITEM), PhMemoryItemType);
memset(memoryItem, 0, sizeof(PH_MEMORY_ITEM));
return memoryItem;
}
VOID PhpMemoryItemDeleteProcedure(
_In_ PVOID Object,
_In_ ULONG Flags
)
{
PPH_MEMORY_ITEM memoryItem = Object;
switch (memoryItem->RegionType)
{
case CustomRegion:
PhClearReference(&memoryItem->u.Custom.Text);
break;
case MappedFileRegion:
PhClearReference(&memoryItem->u.MappedFile.FileName);
break;
}
}
static LONG NTAPI PhpMemoryItemCompareFunction(
_In_ PPH_AVL_LINKS Links1,
_In_ PPH_AVL_LINKS Links2
)
{
PPH_MEMORY_ITEM memoryItem1 = CONTAINING_RECORD(Links1, PH_MEMORY_ITEM, Links);
PPH_MEMORY_ITEM memoryItem2 = CONTAINING_RECORD(Links2, PH_MEMORY_ITEM, Links);
return uintptrcmp((ULONG_PTR)memoryItem1->BaseAddress, (ULONG_PTR)memoryItem2->BaseAddress);
}
VOID PhDeleteMemoryItemList(
_In_ PPH_MEMORY_ITEM_LIST List
)
{
PLIST_ENTRY listEntry;
PPH_MEMORY_ITEM memoryItem;
listEntry = List->ListHead.Flink;
while (listEntry != &List->ListHead)
{
memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry);
listEntry = listEntry->Flink;
PhDereferenceObject(memoryItem);
}
}
PPH_MEMORY_ITEM PhLookupMemoryItemList(
_In_ PPH_MEMORY_ITEM_LIST List,
_In_ PVOID Address
)
{
PH_MEMORY_ITEM lookupMemoryItem;
PPH_AVL_LINKS links;
PPH_MEMORY_ITEM memoryItem;
LONG result;
// Do an approximate search on the set to locate the memory item with the largest
// base address that is still smaller than the given address.
lookupMemoryItem.BaseAddress = Address;
links = PhFindElementAvlTree2(&List->Set, &lookupMemoryItem.Links, &result);
memoryItem = NULL;
if (links)
{
if (result == 0)
{
// Exact match.
}
else if (result < 0)
{
// The base of the closest known memory region is larger than our address. Assume the
// preceding element (which is going to be smaller than our address) is the
// one we're looking for.
links = PhPredecessorElementAvlTree(links);
}
else
{
// The base of the closest known memory region is smaller than our address. Assume this
// is the element we're looking for.
}
if (links)
{
memoryItem = CONTAINING_RECORD(links, PH_MEMORY_ITEM, Links);
}
}
else
{
// No modules loaded.
}
if (memoryItem && (ULONG_PTR)Address < (ULONG_PTR)memoryItem->BaseAddress + memoryItem->RegionSize)
return memoryItem;
else
return NULL;
}
PPH_MEMORY_ITEM PhpSetMemoryRegionType(
_In_ PPH_MEMORY_ITEM_LIST List,
_In_ PVOID Address,
_In_ BOOLEAN GoToAllocationBase,
_In_ PH_MEMORY_REGION_TYPE RegionType
)
{
PPH_MEMORY_ITEM memoryItem;
memoryItem = PhLookupMemoryItemList(List, Address);
if (!memoryItem)
return NULL;
if (GoToAllocationBase && memoryItem->AllocationBaseItem)
memoryItem = memoryItem->AllocationBaseItem;
if (memoryItem->RegionType != UnknownRegion)
return NULL;
memoryItem->RegionType = RegionType;
return memoryItem;
}
NTSTATUS PhpUpdateMemoryRegionTypes(
_In_ PPH_MEMORY_ITEM_LIST List,
_In_ HANDLE ProcessHandle
)
{
NTSTATUS status;
PVOID processes;
PSYSTEM_PROCESS_INFORMATION process;
ULONG i;
#ifdef _WIN64
BOOLEAN isWow64 = FALSE;
#endif
PPH_MEMORY_ITEM memoryItem;
PLIST_ENTRY listEntry;
if (!NT_SUCCESS(status = PhEnumProcessesEx(&processes, SystemExtendedProcessInformation)))
return status;
process = PhFindProcessInformation(processes, List->ProcessId);
if (!process)
{
PhFree(processes);
return STATUS_NOT_FOUND;
}
// USER_SHARED_DATA
PhpSetMemoryRegionType(List, USER_SHARED_DATA, TRUE, UserSharedDataRegion);
// PEB, heap
{
PROCESS_BASIC_INFORMATION basicInfo;
ULONG numberOfHeaps;
PVOID processHeapsPtr;
PVOID *processHeaps;
ULONG i;
#ifdef _WIN64
PVOID peb32;
ULONG processHeapsPtr32;
ULONG *processHeaps32;
#endif
if (NT_SUCCESS(PhGetProcessBasicInformation(ProcessHandle, &basicInfo)))
{
PhpSetMemoryRegionType(List, basicInfo.PebBaseAddress, TRUE, PebRegion);
if (NT_SUCCESS(PhReadVirtualMemory(ProcessHandle,
PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, NumberOfHeaps)),
&numberOfHeaps, sizeof(ULONG), NULL)) && numberOfHeaps < MAX_HEAPS)
{
processHeaps = PhAllocate(numberOfHeaps * sizeof(PVOID));
if (NT_SUCCESS(PhReadVirtualMemory(ProcessHandle,
PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ProcessHeaps)),
&processHeapsPtr, sizeof(PVOID), NULL)) &&
NT_SUCCESS(PhReadVirtualMemory(ProcessHandle,
processHeapsPtr,
processHeaps, numberOfHeaps * sizeof(PVOID), NULL)))
{
for (i = 0; i < numberOfHeaps; i++)
{
if (memoryItem = PhpSetMemoryRegionType(List, processHeaps[i], TRUE, HeapRegion))
memoryItem->u.Heap.Index = i;
}
}
PhFree(processHeaps);
}
}
#ifdef _WIN64
if (NT_SUCCESS(PhGetProcessPeb32(ProcessHandle, &peb32)) && peb32 != 0)
{
isWow64 = TRUE;
PhpSetMemoryRegionType(List, peb32, TRUE, Peb32Region);
if (NT_SUCCESS(PhReadVirtualMemory(ProcessHandle,
PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, NumberOfHeaps)),
&numberOfHeaps, sizeof(ULONG), NULL)) && numberOfHeaps < MAX_HEAPS)
{
processHeaps32 = PhAllocate(numberOfHeaps * sizeof(ULONG));
if (NT_SUCCESS(PhReadVirtualMemory(ProcessHandle,
PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, ProcessHeaps)),
&processHeapsPtr32, sizeof(ULONG), NULL)) &&
NT_SUCCESS(PhReadVirtualMemory(ProcessHandle,
UlongToPtr(processHeapsPtr32),
processHeaps32, numberOfHeaps * sizeof(ULONG), NULL)))
{
for (i = 0; i < numberOfHeaps; i++)
{
if (memoryItem = PhpSetMemoryRegionType(List, UlongToPtr(processHeaps32[i]), TRUE, Heap32Region))
memoryItem->u.Heap.Index = i;
}
}
PhFree(processHeaps32);
}
}
#endif
}
// TEB, stack
for (i = 0; i < process->NumberOfThreads; i++)
{
PSYSTEM_EXTENDED_THREAD_INFORMATION thread = (PSYSTEM_EXTENDED_THREAD_INFORMATION)process->Threads + i;
if (WindowsVersion < WINDOWS_VISTA)
{
HANDLE threadHandle;
THREAD_BASIC_INFORMATION basicInfo;
if (NT_SUCCESS(PhOpenThread(&threadHandle, ThreadQueryAccess, thread->ThreadInfo.ClientId.UniqueThread)))
{
if (NT_SUCCESS(PhGetThreadBasicInformation(threadHandle, &basicInfo)))
thread->TebBase = basicInfo.TebBaseAddress;
NtClose(threadHandle);
}
}
if (thread->TebBase)
{
NT_TIB ntTib;
SIZE_T bytesRead;
if (memoryItem = PhpSetMemoryRegionType(List, thread->TebBase, TRUE, TebRegion))
memoryItem->u.Teb.ThreadId = thread->ThreadInfo.ClientId.UniqueThread;
if (NT_SUCCESS(PhReadVirtualMemory(ProcessHandle, thread->TebBase, &ntTib, sizeof(NT_TIB), &bytesRead)) &&
bytesRead == sizeof(NT_TIB))
{
if ((ULONG_PTR)ntTib.StackLimit < (ULONG_PTR)ntTib.StackBase)
{
if (memoryItem = PhpSetMemoryRegionType(List, ntTib.StackLimit, TRUE, StackRegion))
memoryItem->u.Stack.ThreadId = thread->ThreadInfo.ClientId.UniqueThread;
}
#ifdef _WIN64
if (isWow64 && ntTib.ExceptionList)
{
ULONG teb32 = PtrToUlong(ntTib.ExceptionList);
NT_TIB32 ntTib32;
// 64-bit and 32-bit TEBs usually share the same memory region, so don't do anything for the 32-bit
// TEB.
if (NT_SUCCESS(PhReadVirtualMemory(ProcessHandle, UlongToPtr(teb32), &ntTib32, sizeof(NT_TIB32), &bytesRead)) &&
bytesRead == sizeof(NT_TIB32))
{
if (ntTib32.StackLimit < ntTib32.StackBase)
{
if (memoryItem = PhpSetMemoryRegionType(List, UlongToPtr(ntTib32.StackLimit), TRUE, Stack32Region))
memoryItem->u.Stack.ThreadId = thread->ThreadInfo.ClientId.UniqueThread;
}
}
}
#endif
}
}
}
// Mapped file, heap segment, unusable
for (listEntry = List->ListHead.Flink; listEntry != &List->ListHead; listEntry = listEntry->Flink)
{
memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry);
if (memoryItem->RegionType != UnknownRegion)
continue;
if ((memoryItem->Type & (MEM_MAPPED | MEM_IMAGE)) && memoryItem->AllocationBaseItem == memoryItem)
{
PPH_STRING fileName;
if (NT_SUCCESS(PhGetProcessMappedFileName(ProcessHandle, memoryItem->BaseAddress, &fileName)))
{
PPH_STRING newFileName = PhResolveDevicePrefix(fileName);
if (newFileName)
PhMoveReference(&fileName, newFileName);
memoryItem->RegionType = MappedFileRegion;
memoryItem->u.MappedFile.FileName = fileName;
continue;
}
}
if (memoryItem->State & MEM_COMMIT)
{
UCHAR buffer[HEAP_SEGMENT_MAX_SIZE];
if (NT_SUCCESS(PhReadVirtualMemory(ProcessHandle, memoryItem->BaseAddress,
buffer, sizeof(buffer), NULL)))
{
PVOID candidateHeap = NULL;
ULONG candidateHeap32 = 0;
PPH_MEMORY_ITEM heapMemoryItem;
if (WindowsVersion >= WINDOWS_VISTA)
{
PHEAP_SEGMENT heapSegment = (PHEAP_SEGMENT)buffer;
PHEAP_SEGMENT32 heapSegment32 = (PHEAP_SEGMENT32)buffer;
if (heapSegment->SegmentSignature == HEAP_SEGMENT_SIGNATURE)
candidateHeap = heapSegment->Heap;
if (heapSegment32->SegmentSignature == HEAP_SEGMENT_SIGNATURE)
candidateHeap32 = heapSegment32->Heap;
}
else
{
PHEAP_SEGMENT_OLD heapSegment = (PHEAP_SEGMENT_OLD)buffer;
PHEAP_SEGMENT_OLD32 heapSegment32 = (PHEAP_SEGMENT_OLD32)buffer;
if (heapSegment->Signature == HEAP_SEGMENT_SIGNATURE)
candidateHeap = heapSegment->Heap;
if (heapSegment32->Signature == HEAP_SEGMENT_SIGNATURE)
candidateHeap32 = heapSegment32->Heap;
}
if (candidateHeap)
{
heapMemoryItem = PhLookupMemoryItemList(List, candidateHeap);
if (heapMemoryItem && heapMemoryItem->BaseAddress == candidateHeap &&
heapMemoryItem->RegionType == HeapRegion)
{
memoryItem->RegionType = HeapSegmentRegion;
memoryItem->u.HeapSegment.HeapItem = heapMemoryItem;
continue;
}
}
else if (candidateHeap32)
{
heapMemoryItem = PhLookupMemoryItemList(List, UlongToPtr(candidateHeap32));
if (heapMemoryItem && heapMemoryItem->BaseAddress == UlongToPtr(candidateHeap32) &&
heapMemoryItem->RegionType == Heap32Region)
{
memoryItem->RegionType = HeapSegment32Region;
memoryItem->u.HeapSegment.HeapItem = heapMemoryItem;
continue;
}
}
}
}
}
PhFree(processes);
return STATUS_SUCCESS;
}
NTSTATUS PhpUpdateMemoryWsCounters(
_In_ PPH_MEMORY_ITEM_LIST List,
_In_ HANDLE ProcessHandle
)
{
PLIST_ENTRY listEntry;
PMEMORY_WORKING_SET_EX_INFORMATION info;
info = PhAllocatePage(WS_REQUEST_COUNT * sizeof(MEMORY_WORKING_SET_EX_INFORMATION), NULL);
if (!info)
return STATUS_NO_MEMORY;
for (listEntry = List->ListHead.Flink; listEntry != &List->ListHead; listEntry = listEntry->Flink)
{
PPH_MEMORY_ITEM memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry);
ULONG_PTR virtualAddress;
SIZE_T remainingPages;
SIZE_T requestPages;
SIZE_T i;
if (!(memoryItem->State & MEM_COMMIT))
continue;
virtualAddress = (ULONG_PTR)memoryItem->BaseAddress;
remainingPages = memoryItem->RegionSize / PAGE_SIZE;
while (remainingPages != 0)
{
requestPages = min(remainingPages, WS_REQUEST_COUNT);
for (i = 0; i < requestPages; i++)
{
info[i].VirtualAddress = (PVOID)virtualAddress;
virtualAddress += PAGE_SIZE;
}
if (NT_SUCCESS(NtQueryVirtualMemory(
ProcessHandle,
NULL,
MemoryWorkingSetExInformation,
info,
requestPages * sizeof(MEMORY_WORKING_SET_EX_INFORMATION),
NULL
)))
{
for (i = 0; i < requestPages; i++)
{
PMEMORY_WORKING_SET_EX_BLOCK block = &info[i].u1.VirtualAttributes;
if (block->Valid)
{
memoryItem->TotalWorkingSetPages++;
if (block->ShareCount > 1)
memoryItem->SharedWorkingSetPages++;
if (block->ShareCount == 0)
memoryItem->PrivateWorkingSetPages++;
if (block->Shared)
memoryItem->ShareableWorkingSetPages++;
if (block->Locked)
memoryItem->LockedWorkingSetPages++;
}
}
}
remainingPages -= requestPages;
}
}
PhFreePage(info);
return STATUS_SUCCESS;
}
NTSTATUS PhpUpdateMemoryWsCountersOld(
_In_ PPH_MEMORY_ITEM_LIST List,
_In_ HANDLE ProcessHandle
)
{
NTSTATUS status;
PMEMORY_WORKING_SET_INFORMATION info;
PPH_MEMORY_ITEM memoryItem = NULL;
ULONG_PTR i;
if (!NT_SUCCESS(status = PhGetProcessWorkingSetInformation(ProcessHandle, &info)))
return status;
for (i = 0; i < info->NumberOfEntries; i++)
{
PMEMORY_WORKING_SET_BLOCK block = &info->WorkingSetInfo[i];
ULONG_PTR virtualAddress = block->VirtualPage * PAGE_SIZE;
if (!memoryItem || virtualAddress < (ULONG_PTR)memoryItem->BaseAddress ||
virtualAddress >= (ULONG_PTR)memoryItem->BaseAddress + memoryItem->RegionSize)
{
memoryItem = PhLookupMemoryItemList(List, (PVOID)virtualAddress);
}
if (memoryItem)
{
memoryItem->TotalWorkingSetPages++;
if (block->ShareCount > 1)
memoryItem->SharedWorkingSetPages++;
if (block->ShareCount == 0)
memoryItem->PrivateWorkingSetPages++;
if (block->Shared)
memoryItem->ShareableWorkingSetPages++;
}
}
PhFree(info);
return STATUS_SUCCESS;
}
NTSTATUS PhQueryMemoryItemList(
_In_ HANDLE ProcessId,
_In_ ULONG Flags,
_Out_ PPH_MEMORY_ITEM_LIST List
)
{
NTSTATUS status;
HANDLE processHandle;
ULONG_PTR allocationGranularity;
PVOID baseAddress = (PVOID)0;
MEMORY_BASIC_INFORMATION basicInfo;
PPH_MEMORY_ITEM allocationBaseItem = NULL;
PPH_MEMORY_ITEM previousMemoryItem = NULL;
if (!NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
ProcessId
)))
{
if (!NT_SUCCESS(status = PhOpenProcess(
&processHandle,
PROCESS_QUERY_INFORMATION,
ProcessId
)))
{
return status;
}
}
List->ProcessId = ProcessId;
PhInitializeAvlTree(&List->Set, PhpMemoryItemCompareFunction);
InitializeListHead(&List->ListHead);
allocationGranularity = PhSystemBasicInformation.AllocationGranularity;
while (NT_SUCCESS(NtQueryVirtualMemory(
processHandle,
baseAddress,
MemoryBasicInformation,
&basicInfo,
sizeof(MEMORY_BASIC_INFORMATION),
NULL
)))
{
PPH_MEMORY_ITEM memoryItem;
if (basicInfo.State & MEM_FREE)
{
if (Flags & PH_QUERY_MEMORY_IGNORE_FREE)
goto ContinueLoop;
basicInfo.AllocationBase = basicInfo.BaseAddress;
}
memoryItem = PhCreateMemoryItem();
memoryItem->BasicInfo = basicInfo;
if (basicInfo.AllocationBase == basicInfo.BaseAddress)
allocationBaseItem = memoryItem;
if (allocationBaseItem && basicInfo.AllocationBase == allocationBaseItem->BaseAddress)
memoryItem->AllocationBaseItem = allocationBaseItem;
if (basicInfo.State & MEM_COMMIT)
{
memoryItem->CommittedSize = memoryItem->RegionSize;
if (basicInfo.Type & MEM_PRIVATE)
memoryItem->PrivateSize = memoryItem->RegionSize;
}
PhAddElementAvlTree(&List->Set, &memoryItem->Links);
InsertTailList(&List->ListHead, &memoryItem->ListEntry);
if (basicInfo.State & MEM_FREE)
{
if ((ULONG_PTR)basicInfo.BaseAddress & (allocationGranularity - 1))
{
ULONG_PTR nextAllocationBase;
ULONG_PTR potentialUnusableSize;
// Split this free region into an unusable and a (possibly empty) usable region.
nextAllocationBase = ALIGN_UP_BY(basicInfo.BaseAddress, allocationGranularity);
potentialUnusableSize = nextAllocationBase - (ULONG_PTR)basicInfo.BaseAddress;
memoryItem->RegionType = UnusableRegion;
// VMMap does this, but is it correct?
//if (previousMemoryItem && (previousMemoryItem->State & MEM_COMMIT))
// memoryItem->CommittedSize = min(potentialUnusableSize, basicInfo.RegionSize);
if (nextAllocationBase < (ULONG_PTR)basicInfo.BaseAddress + basicInfo.RegionSize)
{
PPH_MEMORY_ITEM otherMemoryItem;
memoryItem->RegionSize = potentialUnusableSize;
otherMemoryItem = PhCreateMemoryItem();
otherMemoryItem->BasicInfo = basicInfo;
otherMemoryItem->BaseAddress = (PVOID)nextAllocationBase;
otherMemoryItem->AllocationBase = otherMemoryItem->BaseAddress;
otherMemoryItem->RegionSize = basicInfo.RegionSize - potentialUnusableSize;
otherMemoryItem->AllocationBaseItem = otherMemoryItem;
PhAddElementAvlTree(&List->Set, &otherMemoryItem->Links);
InsertTailList(&List->ListHead, &otherMemoryItem->ListEntry);
previousMemoryItem = otherMemoryItem;
goto ContinueLoop;
}
}
}
previousMemoryItem = memoryItem;
ContinueLoop:
baseAddress = PTR_ADD_OFFSET(baseAddress, basicInfo.RegionSize);
}
if (Flags & PH_QUERY_MEMORY_REGION_TYPE)
PhpUpdateMemoryRegionTypes(List, processHandle);
if (Flags & PH_QUERY_MEMORY_WS_COUNTERS)
{
if (WindowsVersion >= WINDOWS_SERVER_2003)
PhpUpdateMemoryWsCounters(List, processHandle);
else
PhpUpdateMemoryWsCountersOld(List, processHandle);
}
NtClose(processHandle);
return STATUS_SUCCESS;
}