1128 lines
32 KiB
C
1128 lines
32 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
queryvm.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the routines which implement the
|
||
NtQueryVirtualMemory service.
|
||
|
||
Author:
|
||
|
||
Lou Perazzoli (loup) 21-Aug-1989
|
||
Landy Wang (landyw) 02-June-1997
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "mi.h"
|
||
|
||
extern POBJECT_TYPE IoFileObjectType;
|
||
|
||
NTSTATUS
|
||
MiGetWorkingSetInfo (
|
||
IN PMEMORY_WORKING_SET_INFORMATION WorkingSetInfo,
|
||
IN SIZE_T Length,
|
||
IN PEPROCESS Process
|
||
);
|
||
|
||
MMPTE
|
||
MiCaptureSystemPte (
|
||
IN PMMPTE PointerProtoPte,
|
||
IN PEPROCESS Process
|
||
);
|
||
|
||
#if DBG
|
||
PEPROCESS MmWatchProcess;
|
||
#endif // DBG
|
||
|
||
ULONG
|
||
MiQueryAddressState (
|
||
IN PVOID Va,
|
||
IN PMMVAD Vad,
|
||
IN PEPROCESS TargetProcess,
|
||
OUT PULONG ReturnedProtect,
|
||
OUT PVOID *NextVaToQuery
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE,NtQueryVirtualMemory)
|
||
#pragma alloc_text(PAGE,MiQueryAddressState)
|
||
#pragma alloc_text(PAGE,MiGetWorkingSetInfo)
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
NtQueryVirtualMemory (
|
||
IN HANDLE ProcessHandle,
|
||
IN PVOID BaseAddress,
|
||
IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
|
||
OUT PVOID MemoryInformation,
|
||
IN SIZE_T MemoryInformationLength,
|
||
OUT PSIZE_T ReturnLength OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function provides the capability to determine the state,
|
||
protection, and type of a region of pages within the virtual address
|
||
space of the subject process.
|
||
|
||
The state of the first page within the region is determined and then
|
||
subsequent entries in the process address map are scanned from the
|
||
base address upward until either the entire range of pages has been
|
||
scanned or until a page with a nonmatching set of attributes is
|
||
encountered. The region attributes, the length of the region of pages
|
||
with matching attributes, and an appropriate status value are
|
||
returned.
|
||
|
||
If the entire region of pages does not have a matching set of
|
||
attributes, then the returned length parameter value can be used to
|
||
calculate the address and length of the region of pages that was not
|
||
scanned.
|
||
|
||
Arguments:
|
||
|
||
|
||
ProcessHandle - An open handle to a process object.
|
||
|
||
BaseAddress - The base address of the region of pages to be
|
||
queried. This value is rounded down to the next host-page-
|
||
address boundary.
|
||
|
||
MemoryInformationClass - The memory information class about which
|
||
to retrieve information.
|
||
|
||
MemoryInformation - A pointer to a buffer that receives the specified
|
||
information. The format and content of the buffer
|
||
depend on the specified information class.
|
||
|
||
|
||
MemoryBasicInformation - Data type is PMEMORY_BASIC_INFORMATION.
|
||
|
||
MEMORY_BASIC_INFORMATION Structure
|
||
|
||
|
||
ULONG RegionSize - The size of the region in bytes beginning at
|
||
the base address in which all pages have
|
||
identical attributes.
|
||
|
||
ULONG State - The state of the pages within the region.
|
||
|
||
State Values
|
||
|
||
MEM_COMMIT - The state of the pages within the region
|
||
is committed.
|
||
|
||
MEM_FREE - The state of the pages within the region
|
||
is free.
|
||
|
||
MEM_RESERVE - The state of the pages within the
|
||
region is reserved.
|
||
|
||
ULONG Protect - The protection of the pages within the region.
|
||
|
||
Protect Values
|
||
|
||
PAGE_NOACCESS - No access to the region of pages is allowed.
|
||
An attempt to read, write, or execute within
|
||
the region results in an access violation.
|
||
|
||
PAGE_EXECUTE - Execute access to the region of pages
|
||
is allowed. An attempt to read or write within
|
||
the region results in an access violation.
|
||
|
||
PAGE_READONLY - Read-only and execute access to the region
|
||
of pages is allowed. An attempt to write within
|
||
the region results in an access violation.
|
||
|
||
PAGE_READWRITE - Read, write, and execute access to the region
|
||
of pages is allowed. If write access to the
|
||
underlying section is allowed, then a single
|
||
copy of the pages are shared. Otherwise,
|
||
the pages are shared read-only/copy-on-write.
|
||
|
||
PAGE_GUARD - Read, write, and execute access to the
|
||
region of pages is allowed; however, access to
|
||
the region causes a "guard region entered"
|
||
condition to be raised in the subject process.
|
||
|
||
PAGE_NOCACHE - Disable the placement of committed
|
||
pages into the data cache.
|
||
|
||
ULONG Type - The type of pages within the region.
|
||
|
||
Type Values
|
||
|
||
MEM_PRIVATE - The pages within the region are private.
|
||
|
||
MEM_MAPPED - The pages within the region are mapped
|
||
into the view of a section.
|
||
|
||
MEM_IMAGE - The pages within the region are mapped
|
||
into the view of an image section.
|
||
|
||
MemoryInformationLength - Specifies the length in bytes of
|
||
the memory information buffer.
|
||
|
||
ReturnLength - An optional pointer which, if specified, receives the
|
||
number of bytes placed in the process information buffer.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS.
|
||
|
||
Environment:
|
||
|
||
Kernel mode.
|
||
|
||
--*/
|
||
|
||
{
|
||
KPROCESSOR_MODE PreviousMode;
|
||
PEPROCESS TargetProcess;
|
||
NTSTATUS Status;
|
||
PMMVAD Vad;
|
||
PVOID Va;
|
||
PVOID NextVaToQuery;
|
||
LOGICAL Found;
|
||
SIZE_T TheRegionSize;
|
||
ULONG NewProtect;
|
||
ULONG NewState;
|
||
PVOID FilePointer;
|
||
ULONG_PTR BaseVpn;
|
||
MEMORY_BASIC_INFORMATION Info;
|
||
PMEMORY_BASIC_INFORMATION BasicInfo;
|
||
LOGICAL Attached;
|
||
LOGICAL Leaped;
|
||
ULONG MemoryInformationLengthUlong;
|
||
KAPC_STATE ApcState;
|
||
PETHREAD CurrentThread;
|
||
|
||
Found = FALSE;
|
||
Leaped = TRUE;
|
||
FilePointer = NULL;
|
||
|
||
//
|
||
// Make sure the user's buffer is large enough for the requested operation.
|
||
//
|
||
// Check argument validity.
|
||
//
|
||
|
||
switch (MemoryInformationClass) {
|
||
case MemoryBasicInformation:
|
||
if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION)) {
|
||
return STATUS_INFO_LENGTH_MISMATCH;
|
||
}
|
||
break;
|
||
|
||
case MemoryWorkingSetInformation:
|
||
if (MemoryInformationLength < sizeof(ULONG_PTR)) {
|
||
return STATUS_INFO_LENGTH_MISMATCH;
|
||
}
|
||
break;
|
||
|
||
case MemoryMappedFilenameInformation:
|
||
break;
|
||
|
||
default:
|
||
return STATUS_INVALID_INFO_CLASS;
|
||
}
|
||
|
||
CurrentThread = PsGetCurrentThread ();
|
||
PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Check arguments.
|
||
//
|
||
|
||
try {
|
||
|
||
ProbeForWrite(MemoryInformation,
|
||
MemoryInformationLength,
|
||
sizeof(ULONG_PTR));
|
||
|
||
if (ARGUMENT_PRESENT(ReturnLength)) {
|
||
ProbeForWriteUlong_ptr(ReturnLength);
|
||
}
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
//
|
||
// If an exception occurs during the probe or capture
|
||
// of the initial values, then handle the exception and
|
||
// return the exception code as the status value.
|
||
//
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
}
|
||
|
||
if (BaseAddress > MM_HIGHEST_USER_ADDRESS) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
if ((BaseAddress >= MM_HIGHEST_VAD_ADDRESS)
|
||
#if defined(MM_SHARED_USER_DATA_VA)
|
||
||
|
||
(PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA)
|
||
#endif
|
||
) {
|
||
|
||
//
|
||
// Indicate a reserved area from this point on.
|
||
//
|
||
|
||
if (MemoryInformationClass == MemoryBasicInformation) {
|
||
|
||
try {
|
||
((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationBase =
|
||
(PCHAR) MM_HIGHEST_VAD_ADDRESS + 1;
|
||
((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationProtect =
|
||
PAGE_READONLY;
|
||
((PMEMORY_BASIC_INFORMATION)MemoryInformation)->BaseAddress =
|
||
PAGE_ALIGN(BaseAddress);
|
||
((PMEMORY_BASIC_INFORMATION)MemoryInformation)->RegionSize =
|
||
((PCHAR)MM_HIGHEST_USER_ADDRESS + 1) -
|
||
(PCHAR)PAGE_ALIGN(BaseAddress);
|
||
((PMEMORY_BASIC_INFORMATION)MemoryInformation)->State = MEM_RESERVE;
|
||
((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Protect = PAGE_NOACCESS;
|
||
((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Type = MEM_PRIVATE;
|
||
|
||
if (ARGUMENT_PRESENT(ReturnLength)) {
|
||
*ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
|
||
}
|
||
|
||
#if defined(MM_SHARED_USER_DATA_VA)
|
||
if (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA) {
|
||
|
||
//
|
||
// This is the page that is double mapped between
|
||
// user mode and kernel mode.
|
||
//
|
||
|
||
((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationBase =
|
||
(PVOID)MM_SHARED_USER_DATA_VA;
|
||
((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Protect =
|
||
PAGE_READONLY;
|
||
((PMEMORY_BASIC_INFORMATION)MemoryInformation)->RegionSize =
|
||
PAGE_SIZE;
|
||
((PMEMORY_BASIC_INFORMATION)MemoryInformation)->State =
|
||
MEM_COMMIT;
|
||
}
|
||
#endif
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
//
|
||
// Just return success.
|
||
//
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
else {
|
||
return STATUS_INVALID_ADDRESS;
|
||
}
|
||
}
|
||
|
||
if (ProcessHandle == NtCurrentProcess()) {
|
||
TargetProcess = PsGetCurrentProcessByThread(CurrentThread);
|
||
}
|
||
else {
|
||
Status = ObReferenceObjectByHandle (ProcessHandle,
|
||
PROCESS_QUERY_INFORMATION,
|
||
PsProcessType,
|
||
PreviousMode,
|
||
(PVOID *)&TargetProcess,
|
||
NULL);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
}
|
||
|
||
if (MemoryInformationClass == MemoryWorkingSetInformation) {
|
||
|
||
Status = MiGetWorkingSetInfo (
|
||
(PMEMORY_WORKING_SET_INFORMATION) MemoryInformation,
|
||
MemoryInformationLength,
|
||
TargetProcess);
|
||
|
||
if (ProcessHandle != NtCurrentProcess()) {
|
||
ObDereferenceObject (TargetProcess);
|
||
}
|
||
|
||
//
|
||
// If MiGetWorkingSetInfo failed then inform the caller.
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
try {
|
||
|
||
if (ARGUMENT_PRESENT(ReturnLength)) {
|
||
*ReturnLength = ((((PMEMORY_WORKING_SET_INFORMATION)
|
||
MemoryInformation)->NumberOfEntries - 1) *
|
||
sizeof(ULONG)) +
|
||
sizeof(MEMORY_WORKING_SET_INFORMATION);
|
||
}
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// If the specified process is not the current process, attach
|
||
// to the specified process.
|
||
//
|
||
|
||
if (ProcessHandle != NtCurrentProcess()) {
|
||
KeStackAttachProcess (&TargetProcess->Pcb, &ApcState);
|
||
Attached = TRUE;
|
||
}
|
||
else {
|
||
Attached = FALSE;
|
||
}
|
||
|
||
//
|
||
// Get working set mutex and block APCs.
|
||
//
|
||
|
||
LOCK_ADDRESS_SPACE (TargetProcess);
|
||
|
||
//
|
||
// Make sure the address space was not deleted, if so, return an error.
|
||
//
|
||
|
||
if (TargetProcess->Flags & PS_PROCESS_FLAGS_VM_DELETED) {
|
||
UNLOCK_ADDRESS_SPACE (TargetProcess);
|
||
if (Attached == TRUE) {
|
||
KeUnstackDetachProcess (&ApcState);
|
||
ObDereferenceObject (TargetProcess);
|
||
}
|
||
return STATUS_PROCESS_IS_TERMINATING;
|
||
}
|
||
|
||
//
|
||
// Locate the VAD that contains the base address or the VAD
|
||
// which follows the base address.
|
||
//
|
||
|
||
Vad = TargetProcess->VadRoot;
|
||
BaseVpn = MI_VA_TO_VPN (BaseAddress);
|
||
|
||
for (;;) {
|
||
|
||
if (Vad == NULL) {
|
||
break;
|
||
}
|
||
|
||
if ((BaseVpn >= Vad->StartingVpn) &&
|
||
(BaseVpn <= Vad->EndingVpn)) {
|
||
Found = TRUE;
|
||
break;
|
||
}
|
||
|
||
if (BaseVpn < Vad->StartingVpn) {
|
||
if (Vad->LeftChild == NULL) {
|
||
break;
|
||
}
|
||
Vad = Vad->LeftChild;
|
||
|
||
}
|
||
else {
|
||
if (BaseVpn < Vad->EndingVpn) {
|
||
break;
|
||
}
|
||
if (Vad->RightChild == NULL) {
|
||
break;
|
||
}
|
||
Vad = Vad->RightChild;
|
||
}
|
||
}
|
||
|
||
if (!Found) {
|
||
|
||
//
|
||
// There is no virtual address allocated at the base
|
||
// address. Return the size of the hole starting at
|
||
// the base address.
|
||
//
|
||
|
||
if (Vad == NULL) {
|
||
TheRegionSize = ((PCHAR)MM_HIGHEST_VAD_ADDRESS + 1) -
|
||
(PCHAR)PAGE_ALIGN(BaseAddress);
|
||
}
|
||
else {
|
||
if (Vad->StartingVpn < BaseVpn) {
|
||
|
||
//
|
||
// We are looking at the Vad which occupies the range
|
||
// just before the desired range. Get the next Vad.
|
||
//
|
||
|
||
Vad = MiGetNextVad (Vad);
|
||
if (Vad == NULL) {
|
||
TheRegionSize = ((PCHAR)MM_HIGHEST_VAD_ADDRESS + 1) -
|
||
(PCHAR)PAGE_ALIGN(BaseAddress);
|
||
}
|
||
else {
|
||
TheRegionSize = (PCHAR)MI_VPN_TO_VA (Vad->StartingVpn) -
|
||
(PCHAR)PAGE_ALIGN(BaseAddress);
|
||
}
|
||
}
|
||
else {
|
||
TheRegionSize = (PCHAR)MI_VPN_TO_VA (Vad->StartingVpn) -
|
||
(PCHAR)PAGE_ALIGN(BaseAddress);
|
||
}
|
||
}
|
||
|
||
UNLOCK_ADDRESS_SPACE (TargetProcess);
|
||
|
||
if (Attached == TRUE) {
|
||
KeUnstackDetachProcess (&ApcState);
|
||
ObDereferenceObject (TargetProcess);
|
||
}
|
||
|
||
//
|
||
// Establish an exception handler and write the information and
|
||
// returned length.
|
||
//
|
||
|
||
if (MemoryInformationClass == MemoryBasicInformation) {
|
||
BasicInfo = (PMEMORY_BASIC_INFORMATION) MemoryInformation;
|
||
Found = FALSE;
|
||
try {
|
||
|
||
BasicInfo->AllocationBase = NULL;
|
||
BasicInfo->AllocationProtect = 0;
|
||
BasicInfo->BaseAddress = PAGE_ALIGN(BaseAddress);
|
||
BasicInfo->RegionSize = TheRegionSize;
|
||
BasicInfo->State = MEM_FREE;
|
||
BasicInfo->Protect = PAGE_NOACCESS;
|
||
BasicInfo->Type = 0;
|
||
|
||
Found = TRUE;
|
||
if (ARGUMENT_PRESENT(ReturnLength)) {
|
||
*ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
|
||
}
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
//
|
||
// Just return success if the BasicInfo was successfully
|
||
// filled in.
|
||
//
|
||
|
||
if (Found == FALSE) {
|
||
return GetExceptionCode ();
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
return STATUS_INVALID_ADDRESS;
|
||
}
|
||
|
||
//
|
||
// Found a VAD.
|
||
//
|
||
|
||
Va = PAGE_ALIGN(BaseAddress);
|
||
Info.BaseAddress = Va;
|
||
|
||
//
|
||
// There is a page mapped at the base address.
|
||
//
|
||
|
||
if (Vad->u.VadFlags.PrivateMemory) {
|
||
Info.Type = MEM_PRIVATE;
|
||
}
|
||
else {
|
||
if (Vad->u.VadFlags.ImageMap == 1) {
|
||
Info.Type = MEM_IMAGE;
|
||
}
|
||
else {
|
||
Info.Type = MEM_MAPPED;
|
||
}
|
||
|
||
if (MemoryInformationClass == MemoryMappedFilenameInformation) {
|
||
|
||
if (Vad->ControlArea) {
|
||
FilePointer = Vad->ControlArea->FilePointer;
|
||
}
|
||
if (FilePointer == NULL) {
|
||
FilePointer = (PVOID)1;
|
||
}
|
||
else {
|
||
ObReferenceObject(FilePointer);
|
||
}
|
||
}
|
||
}
|
||
|
||
LOCK_WS_UNSAFE (TargetProcess);
|
||
|
||
Info.State = MiQueryAddressState (Va,
|
||
Vad,
|
||
TargetProcess,
|
||
&Info.Protect,
|
||
&NextVaToQuery);
|
||
|
||
Va = NextVaToQuery;
|
||
|
||
while (MI_VA_TO_VPN (Va) <= Vad->EndingVpn) {
|
||
|
||
NewState = MiQueryAddressState (Va,
|
||
Vad,
|
||
TargetProcess,
|
||
&NewProtect,
|
||
&NextVaToQuery);
|
||
|
||
if ((NewState != Info.State) || (NewProtect != Info.Protect)) {
|
||
|
||
//
|
||
// The state for this address does not match, calculate
|
||
// size and return.
|
||
//
|
||
|
||
Leaped = FALSE;
|
||
break;
|
||
}
|
||
Va = NextVaToQuery;
|
||
}
|
||
|
||
UNLOCK_WS_UNSAFE (TargetProcess);
|
||
|
||
//
|
||
// We may have aggressively leaped past the end of the VAD. Shorten the
|
||
// Va here if we did.
|
||
//
|
||
|
||
if (Leaped == TRUE) {
|
||
Va = MI_VPN_TO_VA (Vad->EndingVpn + 1);
|
||
}
|
||
|
||
Info.RegionSize = ((PCHAR)Va - (PCHAR)Info.BaseAddress);
|
||
Info.AllocationBase = MI_VPN_TO_VA (Vad->StartingVpn);
|
||
Info.AllocationProtect = MI_CONVERT_FROM_PTE_PROTECTION (
|
||
Vad->u.VadFlags.Protection);
|
||
|
||
//
|
||
// A range has been found, release the mutexes, detach from the
|
||
// target process and return the information.
|
||
//
|
||
|
||
#if defined(_MIALT4K_)
|
||
|
||
if (TargetProcess->Wow64Process != NULL) {
|
||
|
||
Info.BaseAddress = PAGE_4K_ALIGN(BaseAddress);
|
||
|
||
MiQueryRegionFor4kPage (Info.BaseAddress,
|
||
MI_VPN_TO_VA_ENDING(Vad->EndingVpn),
|
||
&Info.RegionSize,
|
||
&Info.State,
|
||
&Info.Protect,
|
||
TargetProcess);
|
||
}
|
||
|
||
#endif
|
||
|
||
UNLOCK_ADDRESS_SPACE (TargetProcess);
|
||
|
||
if (Attached == TRUE) {
|
||
KeUnstackDetachProcess (&ApcState);
|
||
ObDereferenceObject (TargetProcess);
|
||
}
|
||
|
||
if (MemoryInformationClass == MemoryBasicInformation) {
|
||
Found = FALSE;
|
||
try {
|
||
|
||
*(PMEMORY_BASIC_INFORMATION)MemoryInformation = Info;
|
||
|
||
Found = TRUE;
|
||
if (ARGUMENT_PRESENT(ReturnLength)) {
|
||
*ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
|
||
}
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
//
|
||
// Just return success if the BasicInfo was successfully
|
||
// filled in.
|
||
//
|
||
|
||
if (Found == FALSE) {
|
||
return GetExceptionCode ();
|
||
}
|
||
}
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Try to return the name of the file that is mapped.
|
||
//
|
||
|
||
if (FilePointer == NULL) {
|
||
return STATUS_INVALID_ADDRESS;
|
||
}
|
||
|
||
if (FilePointer == (PVOID)1) {
|
||
return STATUS_FILE_INVALID;
|
||
}
|
||
|
||
MemoryInformationLengthUlong = (ULONG)MemoryInformationLength;
|
||
|
||
if ((SIZE_T)MemoryInformationLengthUlong < MemoryInformationLength) {
|
||
return STATUS_INVALID_PARAMETER_5;
|
||
}
|
||
|
||
//
|
||
// We have a referenced pointer to the file. Call ObQueryNameString
|
||
// and get the file name.
|
||
//
|
||
|
||
Status = ObQueryNameString (FilePointer,
|
||
(POBJECT_NAME_INFORMATION) MemoryInformation,
|
||
MemoryInformationLengthUlong,
|
||
(PULONG)ReturnLength);
|
||
|
||
ObDereferenceObject (FilePointer);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
ULONG
|
||
MiQueryAddressState (
|
||
IN PVOID Va,
|
||
IN PMMVAD Vad,
|
||
IN PEPROCESS TargetProcess,
|
||
OUT PULONG ReturnedProtect,
|
||
OUT PVOID *NextVaToQuery
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
Returns the state (MEM_COMMIT, MEM_RESERVE, MEM_PRIVATE).
|
||
|
||
Environment:
|
||
|
||
Kernel mode. Working set lock and address creation lock held.
|
||
|
||
--*/
|
||
|
||
{
|
||
PMMPTE PointerPte;
|
||
PMMPTE PointerPde;
|
||
PMMPTE PointerPpe;
|
||
PMMPTE PointerPxe;
|
||
MMPTE CapturedProtoPte;
|
||
PMMPTE ProtoPte;
|
||
LOGICAL PteIsZero;
|
||
ULONG State;
|
||
ULONG Protect;
|
||
ULONG Waited;
|
||
LOGICAL PteDetected;
|
||
PVOID NextVa;
|
||
|
||
State = MEM_RESERVE;
|
||
Protect = 0;
|
||
|
||
#ifdef LARGE_PAGES
|
||
if (Vad->u.VadFlags.LargePages) {
|
||
*ReturnedProtect = MI_CONVERT_FROM_PTE_PROTECTION (
|
||
Vad->u.VadFlags.Protection);
|
||
return MEM_COMMIT;
|
||
}
|
||
#endif //LARGE_PAGES
|
||
|
||
PointerPxe = MiGetPxeAddress (Va);
|
||
PointerPpe = MiGetPpeAddress (Va);
|
||
PointerPde = MiGetPdeAddress (Va);
|
||
PointerPte = MiGetPteAddress (Va);
|
||
|
||
ASSERT ((Vad->StartingVpn <= MI_VA_TO_VPN (Va)) &&
|
||
(Vad->EndingVpn >= MI_VA_TO_VPN (Va)));
|
||
|
||
PteIsZero = TRUE;
|
||
PteDetected = FALSE;
|
||
|
||
*NextVaToQuery = (PVOID)((PCHAR)Va + PAGE_SIZE);
|
||
|
||
do {
|
||
|
||
if (!MiDoesPxeExistAndMakeValid (PointerPxe,
|
||
TargetProcess,
|
||
FALSE,
|
||
&Waited)) {
|
||
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
NextVa = MiGetVirtualAddressMappedByPte (PointerPxe + 1);
|
||
NextVa = MiGetVirtualAddressMappedByPte (NextVa);
|
||
NextVa = MiGetVirtualAddressMappedByPte (NextVa);
|
||
*NextVaToQuery = MiGetVirtualAddressMappedByPte (NextVa);
|
||
#endif
|
||
break;
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
if (!MiDoesPpeExistAndMakeValid (PointerPpe,
|
||
TargetProcess,
|
||
FALSE,
|
||
&Waited)) {
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
NextVa = MiGetVirtualAddressMappedByPte (PointerPpe + 1);
|
||
NextVa = MiGetVirtualAddressMappedByPte (NextVa);
|
||
*NextVaToQuery = MiGetVirtualAddressMappedByPte (NextVa);
|
||
#endif
|
||
break;
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS < 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
if (!MiDoesPdeExistAndMakeValid (PointerPde,
|
||
TargetProcess,
|
||
FALSE,
|
||
&Waited)) {
|
||
NextVa = MiGetVirtualAddressMappedByPte (PointerPde + 1);
|
||
*NextVaToQuery = MiGetVirtualAddressMappedByPte (NextVa);
|
||
break;
|
||
}
|
||
|
||
if (Waited == 0) {
|
||
PteDetected = TRUE;
|
||
}
|
||
|
||
} while (Waited != 0);
|
||
|
||
if (PteDetected == TRUE) {
|
||
|
||
//
|
||
// A PTE exists at this address, see if it is zero.
|
||
//
|
||
|
||
if (PointerPte->u.Long != 0) {
|
||
|
||
PteIsZero = FALSE;
|
||
|
||
//
|
||
// There is a non-zero PTE at this address, use
|
||
// it to build the information block.
|
||
//
|
||
|
||
if (MiIsPteDecommittedPage (PointerPte)) {
|
||
ASSERT (Protect == 0);
|
||
ASSERT (State == MEM_RESERVE);
|
||
}
|
||
else {
|
||
State = MEM_COMMIT;
|
||
if (Vad->u.VadFlags.PhysicalMapping == 1) {
|
||
|
||
//
|
||
// Physical mapping, there is no corresponding
|
||
// PFN element to get the page protection from.
|
||
//
|
||
|
||
Protect = MI_CONVERT_FROM_PTE_PROTECTION (
|
||
Vad->u.VadFlags.Protection);
|
||
}
|
||
else {
|
||
Protect = MiGetPageProtection (PointerPte,
|
||
TargetProcess,
|
||
FALSE);
|
||
|
||
if ((PointerPte->u.Soft.Valid == 0) &&
|
||
(PointerPte->u.Soft.Prototype == 1) &&
|
||
(Vad->u.VadFlags.PrivateMemory == 0) &&
|
||
(Vad->ControlArea != (PCONTROL_AREA)NULL)) {
|
||
|
||
//
|
||
// Make sure the protoPTE is committed.
|
||
//
|
||
|
||
ProtoPte = MiGetProtoPteAddress(Vad,
|
||
MI_VA_TO_VPN (Va));
|
||
CapturedProtoPte.u.Long = 0;
|
||
if (ProtoPte) {
|
||
CapturedProtoPte = MiCaptureSystemPte (ProtoPte,
|
||
TargetProcess);
|
||
}
|
||
if (CapturedProtoPte.u.Long == 0) {
|
||
State = MEM_RESERVE;
|
||
Protect = 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (PteIsZero) {
|
||
|
||
//
|
||
// There is no PDE at this address, the template from
|
||
// the VAD supplies the information unless the VAD is
|
||
// for an image file. For image files the individual
|
||
// protection is on the prototype PTE.
|
||
//
|
||
|
||
//
|
||
// Get the default protection information.
|
||
//
|
||
|
||
State = MEM_RESERVE;
|
||
Protect = 0;
|
||
|
||
if (Vad->u.VadFlags.PhysicalMapping == 1) {
|
||
|
||
//
|
||
// Must be banked memory, just return reserved.
|
||
//
|
||
|
||
NOTHING;
|
||
|
||
} else if ((Vad->u.VadFlags.PrivateMemory == 0) &&
|
||
(Vad->ControlArea != (PCONTROL_AREA)NULL)) {
|
||
|
||
//
|
||
// This VAD refers to a section. Even though the PTE is
|
||
// zero, the actual page may be committed in the section.
|
||
//
|
||
|
||
*NextVaToQuery = (PVOID)((PCHAR)Va + PAGE_SIZE);
|
||
|
||
ProtoPte = MiGetProtoPteAddress(Vad, MI_VA_TO_VPN (Va));
|
||
|
||
CapturedProtoPte.u.Long = 0;
|
||
if (ProtoPte) {
|
||
CapturedProtoPte = MiCaptureSystemPte (ProtoPte,
|
||
TargetProcess);
|
||
}
|
||
|
||
if (CapturedProtoPte.u.Long != 0) {
|
||
State = MEM_COMMIT;
|
||
|
||
if (Vad->u.VadFlags.ImageMap == 0) {
|
||
Protect = MI_CONVERT_FROM_PTE_PROTECTION (
|
||
Vad->u.VadFlags.Protection);
|
||
}
|
||
else {
|
||
|
||
//
|
||
// This is an image file, the protection is in the
|
||
// prototype PTE.
|
||
//
|
||
|
||
Protect = MiGetPageProtection (&CapturedProtoPte,
|
||
TargetProcess,
|
||
TRUE);
|
||
}
|
||
}
|
||
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Get the protection from the corresponding VAD.
|
||
//
|
||
|
||
if (Vad->u.VadFlags.MemCommit) {
|
||
State = MEM_COMMIT;
|
||
Protect = MI_CONVERT_FROM_PTE_PROTECTION (
|
||
Vad->u.VadFlags.Protection);
|
||
}
|
||
}
|
||
}
|
||
|
||
*ReturnedProtect = Protect;
|
||
return State;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
MiGetWorkingSetInfo (
|
||
IN PMEMORY_WORKING_SET_INFORMATION WorkingSetInfo,
|
||
IN SIZE_T Length,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
{
|
||
PMDL Mdl;
|
||
PMEMORY_WORKING_SET_INFORMATION Info;
|
||
PMEMORY_WORKING_SET_BLOCK Entry;
|
||
#if DBG
|
||
PMEMORY_WORKING_SET_BLOCK LastEntry;
|
||
#endif
|
||
PMMWSLE Wsle;
|
||
PMMWSLE LastWsle;
|
||
WSLE_NUMBER WsSize;
|
||
PMMPTE PointerPte;
|
||
PMMPFN Pfn1;
|
||
NTSTATUS status;
|
||
LOGICAL Attached;
|
||
KAPC_STATE ApcState;
|
||
PETHREAD CurrentThread;
|
||
|
||
//
|
||
// Allocate an MDL to map the request.
|
||
//
|
||
|
||
Mdl = ExAllocatePoolWithTag (NonPagedPool,
|
||
sizeof(MDL) + sizeof(PFN_NUMBER) +
|
||
BYTES_TO_PAGES (Length) * sizeof(PFN_NUMBER),
|
||
' mM');
|
||
|
||
if (Mdl == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Initialize the MDL for the request.
|
||
//
|
||
|
||
MmInitializeMdl(Mdl, WorkingSetInfo, Length);
|
||
|
||
CurrentThread = PsGetCurrentThread ();
|
||
|
||
try {
|
||
MmProbeAndLockPages (Mdl,
|
||
KeGetPreviousModeByThread (&CurrentThread->Tcb),
|
||
IoWriteAccess);
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
ExFreePool (Mdl);
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
Info = MmGetSystemAddressForMdlSafe (Mdl, NormalPagePriority);
|
||
|
||
if (Info == NULL) {
|
||
MmUnlockPages (Mdl);
|
||
ExFreePool (Mdl);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (PsGetCurrentProcessByThread (CurrentThread) != Process) {
|
||
KeStackAttachProcess (&Process->Pcb, &ApcState);
|
||
Attached = TRUE;
|
||
}
|
||
else {
|
||
Attached = FALSE;
|
||
}
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
LOCK_WS (Process);
|
||
|
||
if (Process->Flags & PS_PROCESS_FLAGS_VM_DELETED) {
|
||
status = STATUS_PROCESS_IS_TERMINATING;
|
||
}
|
||
else {
|
||
WsSize = Process->Vm.WorkingSetSize;
|
||
ASSERT (WsSize != 0);
|
||
Info->NumberOfEntries = WsSize;
|
||
if (sizeof(MEMORY_WORKING_SET_INFORMATION) + (WsSize-1) * sizeof(ULONG_PTR) > Length) {
|
||
status = STATUS_INFO_LENGTH_MISMATCH;
|
||
}
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
UNLOCK_WS (Process);
|
||
|
||
if (Attached == TRUE) {
|
||
KeUnstackDetachProcess (&ApcState);
|
||
}
|
||
MmUnlockPages (Mdl);
|
||
ExFreePool (Mdl);
|
||
return status;
|
||
}
|
||
|
||
Wsle = MmWsle;
|
||
LastWsle = &MmWsle[MmWorkingSetList->LastEntry];
|
||
Entry = &Info->WorkingSetInfo[0];
|
||
|
||
#if DBG
|
||
LastEntry = (PMEMORY_WORKING_SET_BLOCK)(
|
||
(PCHAR)Info + (Length & (~(sizeof(ULONG_PTR) - 1))));
|
||
#endif
|
||
|
||
do {
|
||
if (Wsle->u1.e1.Valid == 1) {
|
||
Entry->VirtualPage = Wsle->u1.e1.VirtualPageNumber;
|
||
PointerPte = MiGetPteAddress (Wsle->u1.VirtualAddress);
|
||
ASSERT (PointerPte->u.Hard.Valid == 1);
|
||
Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
|
||
|
||
#if defined(MI_MULTINODE)
|
||
Entry->Node = Pfn1->u3.e1.PageColor;
|
||
#else
|
||
Entry->Node = 0;
|
||
#endif
|
||
Entry->Shared = Pfn1->u3.e1.PrototypePte;
|
||
if (Pfn1->u3.e1.PrototypePte == 0) {
|
||
Entry->ShareCount = 0;
|
||
Entry->Protection = MI_GET_PROTECTION_FROM_SOFT_PTE(&Pfn1->OriginalPte);
|
||
}
|
||
else {
|
||
if (Pfn1->u2.ShareCount <= 7) {
|
||
Entry->ShareCount = Pfn1->u2.ShareCount;
|
||
}
|
||
else {
|
||
Entry->ShareCount = 7;
|
||
}
|
||
if (Wsle->u1.e1.SameProtectAsProto == 1) {
|
||
Entry->Protection = MI_GET_PROTECTION_FROM_SOFT_PTE(&Pfn1->OriginalPte);
|
||
}
|
||
else {
|
||
Entry->Protection = Wsle->u1.e1.Protection;
|
||
}
|
||
}
|
||
Entry += 1;
|
||
}
|
||
Wsle += 1;
|
||
#if DBG
|
||
ASSERT ((Entry < LastEntry) || (Wsle > LastWsle));
|
||
#endif
|
||
} while (Wsle <= LastWsle);
|
||
|
||
UNLOCK_WS (Process);
|
||
|
||
if (Attached == TRUE) {
|
||
KeUnstackDetachProcess (&ApcState);
|
||
}
|
||
MmUnlockPages (Mdl);
|
||
ExFreePool (Mdl);
|
||
return STATUS_SUCCESS;
|
||
}
|