Process Hacker
memprv.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * memory provider
4  *
5  * Copyright (C) 2010-2015 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <phapp.h>
24 #include <heapstruct.h>
25 
26 #define MAX_HEAPS 1000
27 #define WS_REQUEST_COUNT (PAGE_SIZE / sizeof(MEMORY_WORKING_SET_EX_INFORMATION))
28 
30  _In_ PVOID Object,
31  _In_ ULONG Flags
32  );
33 
35 
37  VOID
38  )
39 {
40  PhMemoryItemType = PhCreateObjectType(L"MemoryItem", 0, PhpMemoryItemDeleteProcedure);
41 
42  return TRUE;
43 }
44 
46  _In_ ULONG Protection,
47  _Out_writes_(17) PWSTR String
48  )
49 {
50  PWSTR string;
51  PH_STRINGREF base;
52 
53  if (!Protection)
54  {
55  String[0] = 0;
56  return;
57  }
58 
59  if (Protection & PAGE_NOACCESS)
60  PhInitializeStringRef(&base, L"NA");
61  else if (Protection & PAGE_READONLY)
62  PhInitializeStringRef(&base, L"R");
63  else if (Protection & PAGE_READWRITE)
64  PhInitializeStringRef(&base, L"RW");
65  else if (Protection & PAGE_WRITECOPY)
66  PhInitializeStringRef(&base, L"WC");
67  else if (Protection & PAGE_EXECUTE)
68  PhInitializeStringRef(&base, L"X");
69  else if (Protection & PAGE_EXECUTE_READ)
70  PhInitializeStringRef(&base, L"RX");
71  else if (Protection & PAGE_EXECUTE_READWRITE)
72  PhInitializeStringRef(&base, L"RWX");
73  else if (Protection & PAGE_EXECUTE_WRITECOPY)
74  PhInitializeStringRef(&base, L"WCX");
75  else
76  PhInitializeStringRef(&base, L"?");
77 
78  string = String;
79 
80  memcpy(string, base.Buffer, base.Length);
81  string += base.Length / sizeof(WCHAR);
82 
83  if (Protection & PAGE_GUARD)
84  {
85  memcpy(string, L"+G", 2 * 2);
86  string += 2;
87  }
88 
89  if (Protection & PAGE_NOCACHE)
90  {
91  memcpy(string, L"+NC", 3 * 2);
92  string += 3;
93  }
94 
95  if (Protection & PAGE_WRITECOMBINE)
96  {
97  memcpy(string, L"+WCM", 4 * 2);
98  string += 4;
99  }
100 
101  *string = 0;
102 }
103 
105  _In_ ULONG State
106  )
107 {
108  if (State & MEM_COMMIT)
109  return L"Commit";
110  else if (State & MEM_RESERVE)
111  return L"Reserved";
112  else if (State & MEM_FREE)
113  return L"Free";
114  else
115  return L"Unknown";
116 }
117 
119  _In_ ULONG Type
120  )
121 {
122  if (Type & MEM_PRIVATE)
123  return L"Private";
124  else if (Type & MEM_MAPPED)
125  return L"Mapped";
126  else if (Type & MEM_IMAGE)
127  return L"Image";
128  else
129  return L"Unknown";
130 }
131 
133  VOID
134  )
135 {
136  PPH_MEMORY_ITEM memoryItem;
137 
138  memoryItem = PhCreateObject(sizeof(PH_MEMORY_ITEM), PhMemoryItemType);
139  memset(memoryItem, 0, sizeof(PH_MEMORY_ITEM));
140 
141  return memoryItem;
142 }
143 
145  _In_ PVOID Object,
146  _In_ ULONG Flags
147  )
148 {
149  PPH_MEMORY_ITEM memoryItem = Object;
150 
151  switch (memoryItem->RegionType)
152  {
153  case CustomRegion:
154  PhClearReference(&memoryItem->u.Custom.Text);
155  break;
156  case MappedFileRegion:
157  PhClearReference(&memoryItem->u.MappedFile.FileName);
158  break;
159  }
160 }
161 
162 static LONG NTAPI PhpMemoryItemCompareFunction(
163  _In_ PPH_AVL_LINKS Links1,
164  _In_ PPH_AVL_LINKS Links2
165  )
166 {
167  PPH_MEMORY_ITEM memoryItem1 = CONTAINING_RECORD(Links1, PH_MEMORY_ITEM, Links);
168  PPH_MEMORY_ITEM memoryItem2 = CONTAINING_RECORD(Links2, PH_MEMORY_ITEM, Links);
169 
170  return uintptrcmp((ULONG_PTR)memoryItem1->BaseAddress, (ULONG_PTR)memoryItem2->BaseAddress);
171 }
172 
174  _In_ PPH_MEMORY_ITEM_LIST List
175  )
176 {
177  PLIST_ENTRY listEntry;
178  PPH_MEMORY_ITEM memoryItem;
179 
180  listEntry = List->ListHead.Flink;
181 
182  while (listEntry != &List->ListHead)
183  {
184  memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry);
185  listEntry = listEntry->Flink;
186 
187  PhDereferenceObject(memoryItem);
188  }
189 }
190 
192  _In_ PPH_MEMORY_ITEM_LIST List,
193  _In_ PVOID Address
194  )
195 {
196  PH_MEMORY_ITEM lookupMemoryItem;
197  PPH_AVL_LINKS links;
198  PPH_MEMORY_ITEM memoryItem;
199  LONG result;
200 
201  // Do an approximate search on the set to locate the memory item with the largest
202  // base address that is still smaller than the given address.
203  lookupMemoryItem.BaseAddress = Address;
204  links = PhFindElementAvlTree2(&List->Set, &lookupMemoryItem.Links, &result);
205  memoryItem = NULL;
206 
207  if (links)
208  {
209  if (result == 0)
210  {
211  // Exact match.
212  }
213  else if (result < 0)
214  {
215  // The base of the closest known memory region is larger than our address. Assume the
216  // preceding element (which is going to be smaller than our address) is the
217  // one we're looking for.
218 
219  links = PhPredecessorElementAvlTree(links);
220  }
221  else
222  {
223  // The base of the closest known memory region is smaller than our address. Assume this
224  // is the element we're looking for.
225  }
226 
227  if (links)
228  {
229  memoryItem = CONTAINING_RECORD(links, PH_MEMORY_ITEM, Links);
230  }
231  }
232  else
233  {
234  // No modules loaded.
235  }
236 
237  if (memoryItem && (ULONG_PTR)Address < (ULONG_PTR)memoryItem->BaseAddress + memoryItem->RegionSize)
238  return memoryItem;
239  else
240  return NULL;
241 }
242 
244  _In_ PPH_MEMORY_ITEM_LIST List,
245  _In_ PVOID Address,
246  _In_ BOOLEAN GoToAllocationBase,
247  _In_ PH_MEMORY_REGION_TYPE RegionType
248  )
249 {
250  PPH_MEMORY_ITEM memoryItem;
251 
252  memoryItem = PhLookupMemoryItemList(List, Address);
253 
254  if (!memoryItem)
255  return NULL;
256 
257  if (GoToAllocationBase && memoryItem->AllocationBaseItem)
258  memoryItem = memoryItem->AllocationBaseItem;
259 
260  if (memoryItem->RegionType != UnknownRegion)
261  return NULL;
262 
263  memoryItem->RegionType = RegionType;
264 
265  return memoryItem;
266 }
267 
269  _In_ PPH_MEMORY_ITEM_LIST List,
270  _In_ HANDLE ProcessHandle
271  )
272 {
273  NTSTATUS status;
274  PVOID processes;
276  ULONG i;
277 #ifdef _WIN64
278  BOOLEAN isWow64 = FALSE;
279 #endif
280  PPH_MEMORY_ITEM memoryItem;
281  PLIST_ENTRY listEntry;
282 
284  return status;
285 
286  process = PhFindProcessInformation(processes, List->ProcessId);
287 
288  if (!process)
289  {
290  PhFree(processes);
291  return STATUS_NOT_FOUND;
292  }
293 
294  // USER_SHARED_DATA
295  PhpSetMemoryRegionType(List, USER_SHARED_DATA, TRUE, UserSharedDataRegion);
296 
297  // PEB, heap
298  {
299  PROCESS_BASIC_INFORMATION basicInfo;
300  ULONG numberOfHeaps;
301  PVOID processHeapsPtr;
302  PVOID *processHeaps;
303  ULONG i;
304 #ifdef _WIN64
305  PVOID peb32;
306  ULONG processHeapsPtr32;
307  ULONG *processHeaps32;
308 #endif
309 
310  if (NT_SUCCESS(PhGetProcessBasicInformation(ProcessHandle, &basicInfo)))
311  {
312  PhpSetMemoryRegionType(List, basicInfo.PebBaseAddress, TRUE, PebRegion);
313 
314  if (NT_SUCCESS(PhReadVirtualMemory(ProcessHandle,
315  PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, NumberOfHeaps)),
316  &numberOfHeaps, sizeof(ULONG), NULL)) && numberOfHeaps < MAX_HEAPS)
317  {
318  processHeaps = PhAllocate(numberOfHeaps * sizeof(PVOID));
319 
320  if (NT_SUCCESS(PhReadVirtualMemory(ProcessHandle,
321  PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ProcessHeaps)),
322  &processHeapsPtr, sizeof(PVOID), NULL)) &&
323  NT_SUCCESS(PhReadVirtualMemory(ProcessHandle,
324  processHeapsPtr,
325  processHeaps, numberOfHeaps * sizeof(PVOID), NULL)))
326  {
327  for (i = 0; i < numberOfHeaps; i++)
328  {
329  if (memoryItem = PhpSetMemoryRegionType(List, processHeaps[i], TRUE, HeapRegion))
330  memoryItem->u.Heap.Index = i;
331  }
332  }
333 
334  PhFree(processHeaps);
335  }
336  }
337 #ifdef _WIN64
338 
339  if (NT_SUCCESS(PhGetProcessPeb32(ProcessHandle, &peb32)) && peb32 != 0)
340  {
341  isWow64 = TRUE;
342  PhpSetMemoryRegionType(List, peb32, TRUE, Peb32Region);
343 
344  if (NT_SUCCESS(PhReadVirtualMemory(ProcessHandle,
345  PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, NumberOfHeaps)),
346  &numberOfHeaps, sizeof(ULONG), NULL)) && numberOfHeaps < MAX_HEAPS)
347  {
348  processHeaps32 = PhAllocate(numberOfHeaps * sizeof(ULONG));
349 
350  if (NT_SUCCESS(PhReadVirtualMemory(ProcessHandle,
351  PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, ProcessHeaps)),
352  &processHeapsPtr32, sizeof(ULONG), NULL)) &&
353  NT_SUCCESS(PhReadVirtualMemory(ProcessHandle,
354  (PVOID)processHeapsPtr32,
355  processHeaps32, numberOfHeaps * sizeof(ULONG), NULL)))
356  {
357  for (i = 0; i < numberOfHeaps; i++)
358  {
359  if (memoryItem = PhpSetMemoryRegionType(List, (PVOID)processHeaps32[i], TRUE, Heap32Region))
360  memoryItem->u.Heap.Index = i;
361  }
362  }
363 
364  PhFree(processHeaps32);
365  }
366  }
367 #endif
368  }
369 
370  // TEB, stack
371  for (i = 0; i < process->NumberOfThreads; i++)
372  {
374 
376  {
377  HANDLE threadHandle;
378  THREAD_BASIC_INFORMATION basicInfo;
379 
381  {
382  if (NT_SUCCESS(PhGetThreadBasicInformation(threadHandle, &basicInfo)))
383  thread->TebBase = basicInfo.TebBaseAddress;
384 
385  NtClose(threadHandle);
386  }
387  }
388 
389  if (thread->TebBase)
390  {
391  NT_TIB ntTib;
392  SIZE_T bytesRead;
393 
394  if (memoryItem = PhpSetMemoryRegionType(List, thread->TebBase, TRUE, TebRegion))
395  memoryItem->u.Teb.ThreadId = thread->ThreadInfo.ClientId.UniqueThread;
396 
397  if (NT_SUCCESS(PhReadVirtualMemory(ProcessHandle, thread->TebBase, &ntTib, sizeof(NT_TIB), &bytesRead)) &&
398  bytesRead == sizeof(NT_TIB))
399  {
400  if ((ULONG_PTR)ntTib.StackLimit < (ULONG_PTR)ntTib.StackBase)
401  {
402  if (memoryItem = PhpSetMemoryRegionType(List, ntTib.StackLimit, TRUE, StackRegion))
403  memoryItem->u.Stack.ThreadId = thread->ThreadInfo.ClientId.UniqueThread;
404  }
405 #ifdef _WIN64
406 
407  if (isWow64 && ntTib.ExceptionList)
408  {
409  ULONG teb32 = (ULONG)ntTib.ExceptionList;
410  NT_TIB32 ntTib32;
411 
412  // 64-bit and 32-bit TEBs usually share the same memory region, so don't do anything for the 32-bit
413  // TEB.
414 
415  if (NT_SUCCESS(PhReadVirtualMemory(ProcessHandle, (PVOID)teb32, &ntTib32, sizeof(NT_TIB32), &bytesRead)) &&
416  bytesRead == sizeof(NT_TIB32))
417  {
418  if (ntTib32.StackLimit < ntTib32.StackBase)
419  {
420  if (memoryItem = PhpSetMemoryRegionType(List, (PVOID)ntTib32.StackLimit, TRUE, Stack32Region))
421  memoryItem->u.Stack.ThreadId = thread->ThreadInfo.ClientId.UniqueThread;
422  }
423  }
424  }
425 #endif
426  }
427  }
428  }
429 
430  // Mapped file, heap segment, unusable
431  for (listEntry = List->ListHead.Flink; listEntry != &List->ListHead; listEntry = listEntry->Flink)
432  {
433  memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry);
434 
435  if (memoryItem->RegionType != UnknownRegion)
436  continue;
437 
438  if ((memoryItem->Type & (MEM_MAPPED | MEM_IMAGE)) && memoryItem->AllocationBaseItem == memoryItem)
439  {
440  PPH_STRING fileName;
441 
442  if (NT_SUCCESS(PhGetProcessMappedFileName(ProcessHandle, memoryItem->BaseAddress, &fileName)))
443  {
444  PPH_STRING newFileName = PhResolveDevicePrefix(fileName);
445 
446  if (newFileName)
447  PhMoveReference(&fileName, newFileName);
448 
449  memoryItem->RegionType = MappedFileRegion;
450  memoryItem->u.MappedFile.FileName = fileName;
451  continue;
452  }
453  }
454 
455  if (memoryItem->State & MEM_COMMIT)
456  {
457  UCHAR buffer[HEAP_SEGMENT_MAX_SIZE];
458 
459  if (NT_SUCCESS(PhReadVirtualMemory(ProcessHandle, memoryItem->BaseAddress,
460  buffer, sizeof(buffer), NULL)))
461  {
462  PVOID candidateHeap = NULL;
463  ULONG candidateHeap32 = 0;
464  PPH_MEMORY_ITEM heapMemoryItem;
465 
467  {
468  PHEAP_SEGMENT heapSegment = (PHEAP_SEGMENT)buffer;
469  PHEAP_SEGMENT32 heapSegment32 = (PHEAP_SEGMENT32)buffer;
470 
471  if (heapSegment->SegmentSignature == HEAP_SEGMENT_SIGNATURE)
472  candidateHeap = heapSegment->Heap;
473  if (heapSegment32->SegmentSignature == HEAP_SEGMENT_SIGNATURE)
474  candidateHeap32 = heapSegment32->Heap;
475  }
476  else
477  {
478  PHEAP_SEGMENT_OLD heapSegment = (PHEAP_SEGMENT_OLD)buffer;
479  PHEAP_SEGMENT_OLD32 heapSegment32 = (PHEAP_SEGMENT_OLD32)buffer;
480 
481  if (heapSegment->Signature == HEAP_SEGMENT_SIGNATURE)
482  candidateHeap = heapSegment->Heap;
483  if (heapSegment32->Signature == HEAP_SEGMENT_SIGNATURE)
484  candidateHeap32 = heapSegment32->Heap;
485  }
486 
487  if (candidateHeap)
488  {
489  heapMemoryItem = PhLookupMemoryItemList(List, candidateHeap);
490 
491  if (heapMemoryItem && heapMemoryItem->BaseAddress == candidateHeap &&
492  heapMemoryItem->RegionType == HeapRegion)
493  {
494  memoryItem->RegionType = HeapSegmentRegion;
495  memoryItem->u.HeapSegment.HeapItem = heapMemoryItem;
496  continue;
497  }
498  }
499  else if (candidateHeap32)
500  {
501  heapMemoryItem = PhLookupMemoryItemList(List, (PVOID)candidateHeap32);
502 
503  if (heapMemoryItem && heapMemoryItem->BaseAddress == (PVOID)candidateHeap32 &&
504  heapMemoryItem->RegionType == Heap32Region)
505  {
506  memoryItem->RegionType = HeapSegment32Region;
507  memoryItem->u.HeapSegment.HeapItem = heapMemoryItem;
508  continue;
509  }
510  }
511  }
512  }
513  }
514 
515  PhFree(processes);
516 
517  return STATUS_SUCCESS;
518 }
519 
521  _In_ PPH_MEMORY_ITEM_LIST List,
522  _In_ HANDLE ProcessHandle
523  )
524 {
525  PLIST_ENTRY listEntry;
527 
529 
530  if (!info)
531  return STATUS_NO_MEMORY;
532 
533  for (listEntry = List->ListHead.Flink; listEntry != &List->ListHead; listEntry = listEntry->Flink)
534  {
535  PPH_MEMORY_ITEM memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry);
536  ULONG_PTR virtualAddress;
537  SIZE_T remainingPages;
538  SIZE_T requestPages;
539  SIZE_T i;
540 
541  if (!(memoryItem->State & MEM_COMMIT))
542  continue;
543 
544  virtualAddress = (ULONG_PTR)memoryItem->BaseAddress;
545  remainingPages = memoryItem->RegionSize / PAGE_SIZE;
546 
547  while (remainingPages != 0)
548  {
549  requestPages = min(remainingPages, WS_REQUEST_COUNT);
550 
551  for (i = 0; i < requestPages; i++)
552  {
553  info[i].VirtualAddress = (PVOID)virtualAddress;
554  virtualAddress += PAGE_SIZE;
555  }
556 
558  ProcessHandle,
559  NULL,
561  info,
562  requestPages * sizeof(MEMORY_WORKING_SET_EX_INFORMATION),
563  NULL
564  )))
565  {
566  for (i = 0; i < requestPages; i++)
567  {
569 
570  if (block->Valid)
571  {
572  memoryItem->TotalWorkingSetPages++;
573 
574  if (block->ShareCount > 1)
575  memoryItem->SharedWorkingSetPages++;
576  if (block->ShareCount == 0)
577  memoryItem->PrivateWorkingSetPages++;
578  if (block->Shared)
579  memoryItem->ShareableWorkingSetPages++;
580  if (block->Locked)
581  memoryItem->LockedWorkingSetPages++;
582  }
583  }
584  }
585 
586  remainingPages -= requestPages;
587  }
588  }
589 
590  PhFreePage(info);
591 
592  return STATUS_SUCCESS;
593 }
594 
596  _In_ PPH_MEMORY_ITEM_LIST List,
597  _In_ HANDLE ProcessHandle
598  )
599 {
600  NTSTATUS status;
602  PPH_MEMORY_ITEM memoryItem = NULL;
603  ULONG_PTR i;
604 
605  if (!NT_SUCCESS(status = PhGetProcessWorkingSetInformation(ProcessHandle, &info)))
606  return status;
607 
608  for (i = 0; i < info->NumberOfEntries; i++)
609  {
610  PMEMORY_WORKING_SET_BLOCK block = &info->WorkingSetInfo[i];
611  ULONG_PTR virtualAddress = block->VirtualPage * PAGE_SIZE;
612 
613  if (!memoryItem || virtualAddress < (ULONG_PTR)memoryItem->BaseAddress ||
614  virtualAddress >= (ULONG_PTR)memoryItem->BaseAddress + memoryItem->RegionSize)
615  {
616  memoryItem = PhLookupMemoryItemList(List, (PVOID)virtualAddress);
617  }
618 
619  if (memoryItem)
620  {
621  memoryItem->TotalWorkingSetPages++;
622 
623  if (block->ShareCount > 1)
624  memoryItem->SharedWorkingSetPages++;
625  if (block->ShareCount == 0)
626  memoryItem->PrivateWorkingSetPages++;
627  if (block->Shared)
628  memoryItem->ShareableWorkingSetPages++;
629  }
630  }
631 
632  PhFree(info);
633 
634  return STATUS_SUCCESS;
635 }
636 
638  _In_ HANDLE ProcessId,
639  _In_ ULONG Flags,
640  _Out_ PPH_MEMORY_ITEM_LIST List
641  )
642 {
643  NTSTATUS status;
644  HANDLE processHandle;
645  ULONG_PTR allocationGranularity;
646  PVOID baseAddress = (PVOID)0;
647  MEMORY_BASIC_INFORMATION basicInfo;
648  PPH_MEMORY_ITEM allocationBaseItem = NULL;
649  PPH_MEMORY_ITEM previousMemoryItem = NULL;
650 
651  if (!NT_SUCCESS(status = PhOpenProcess(
652  &processHandle,
654  ProcessId
655  )))
656  {
657  if (!NT_SUCCESS(status = PhOpenProcess(
658  &processHandle,
660  ProcessId
661  )))
662  {
663  return status;
664  }
665  }
666 
667  List->ProcessId = ProcessId;
668  PhInitializeAvlTree(&List->Set, PhpMemoryItemCompareFunction);
669  InitializeListHead(&List->ListHead);
670 
671  allocationGranularity = PhSystemBasicInformation.AllocationGranularity;
672 
674  processHandle,
675  baseAddress,
677  &basicInfo,
678  sizeof(MEMORY_BASIC_INFORMATION),
679  NULL
680  )))
681  {
682  PPH_MEMORY_ITEM memoryItem;
683 
684  if (basicInfo.State & MEM_FREE)
685  {
686  if (Flags & PH_QUERY_MEMORY_IGNORE_FREE)
687  goto ContinueLoop;
688 
689  basicInfo.AllocationBase = basicInfo.BaseAddress;
690  }
691 
692  memoryItem = PhCreateMemoryItem();
693  memoryItem->BasicInfo = basicInfo;
694 
695  if (basicInfo.AllocationBase == basicInfo.BaseAddress)
696  allocationBaseItem = memoryItem;
697  if (allocationBaseItem && basicInfo.AllocationBase == allocationBaseItem->BaseAddress)
698  memoryItem->AllocationBaseItem = allocationBaseItem;
699 
700  if (basicInfo.State & MEM_COMMIT)
701  {
702  memoryItem->CommittedSize = memoryItem->RegionSize;
703 
704  if (basicInfo.Type & MEM_PRIVATE)
705  memoryItem->PrivateSize = memoryItem->RegionSize;
706  }
707 
708  PhAddElementAvlTree(&List->Set, &memoryItem->Links);
709  InsertTailList(&List->ListHead, &memoryItem->ListEntry);
710 
711  if (basicInfo.State & MEM_FREE)
712  {
713  if ((ULONG_PTR)basicInfo.BaseAddress & (allocationGranularity - 1))
714  {
715  ULONG_PTR nextAllocationBase;
716  ULONG_PTR potentialUnusableSize;
717 
718  // Split this free region into an unusable and a (possibly empty) usable region.
719 
720  nextAllocationBase = ALIGN_UP_BY(basicInfo.BaseAddress, allocationGranularity);
721  potentialUnusableSize = nextAllocationBase - (ULONG_PTR)basicInfo.BaseAddress;
722 
723  memoryItem->RegionType = UnusableRegion;
724 
725  // VMMap does this, but is it correct?
726  //if (previousMemoryItem && (previousMemoryItem->State & MEM_COMMIT))
727  // memoryItem->CommittedSize = min(potentialUnusableSize, basicInfo.RegionSize);
728 
729  if (nextAllocationBase < (ULONG_PTR)basicInfo.BaseAddress + basicInfo.RegionSize)
730  {
731  PPH_MEMORY_ITEM otherMemoryItem;
732 
733  memoryItem->RegionSize = potentialUnusableSize;
734 
735  otherMemoryItem = PhCreateMemoryItem();
736  otherMemoryItem->BasicInfo = basicInfo;
737  otherMemoryItem->BaseAddress = (PVOID)nextAllocationBase;
738  otherMemoryItem->AllocationBase = otherMemoryItem->BaseAddress;
739  otherMemoryItem->RegionSize = basicInfo.RegionSize - potentialUnusableSize;
740  otherMemoryItem->AllocationBaseItem = otherMemoryItem;
741 
742  PhAddElementAvlTree(&List->Set, &otherMemoryItem->Links);
743  InsertTailList(&List->ListHead, &otherMemoryItem->ListEntry);
744 
745  previousMemoryItem = otherMemoryItem;
746  goto ContinueLoop;
747  }
748  }
749  }
750 
751  previousMemoryItem = memoryItem;
752 
753 ContinueLoop:
754  baseAddress = PTR_ADD_OFFSET(baseAddress, basicInfo.RegionSize);
755  }
756 
757  if (Flags & PH_QUERY_MEMORY_REGION_TYPE)
758  PhpUpdateMemoryRegionTypes(List, processHandle);
759 
760  if (Flags & PH_QUERY_MEMORY_WS_COUNTERS)
761  {
763  PhpUpdateMemoryWsCounters(List, processHandle);
764  else
765  PhpUpdateMemoryWsCountersOld(List, processHandle);
766  }
767 
768  NtClose(processHandle);
769 
770  return STATUS_SUCCESS;
771 }