[NTOS]: Store the protection mask in the Fake VADs as well, we can read/convert this from the MAREA protection value.

[NTOS]: Reimplement NtQueryVirtualMemory to use VAD information instead. Even though Alloc/Free are still MAREA-based, the fake VADs we build ought to be enough to make the query API work for certain limited scenarios. Only some paths are implemented and it's lacking SEH, but it's good enough for the install/boot requirements. If there are any regressions, please file bugs.

svn path=/trunk/; revision=49194
This commit is contained in:
Sir Richard 2010-10-18 14:25:33 +00:00
parent 26c20f3857
commit a81f92c1cb
3 changed files with 267 additions and 264 deletions

View file

@ -172,22 +172,22 @@ MiDeletePte(IN PMMPTE PointerPte,
IN PEPROCESS CurrentProcess)
{
PMMPFN Pfn1;
MMPTE PteContents;
MMPTE TempPte;
PFN_NUMBER PageFrameIndex;
/* PFN lock must be held */
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
/* Capture the PTE */
PteContents = *PointerPte;
TempPte = *PointerPte;
/* We only support valid PTEs for now */
ASSERT(PteContents.u.Hard.Valid == 1);
ASSERT(PteContents.u.Soft.Prototype == 0);
ASSERT(PteContents.u.Soft.Transition == 0);
ASSERT(TempPte.u.Hard.Valid == 1);
ASSERT(TempPte.u.Soft.Prototype == 0);
ASSERT(TempPte.u.Soft.Transition == 0);
/* Get the PFN entry */
PageFrameIndex = PFN_FROM_PTE(&PteContents);
PageFrameIndex = PFN_FROM_PTE(&TempPte);
Pfn1 = MiGetPfnEntry(PageFrameIndex);
/* We don't support deleting prototype PTEs for now */
@ -909,6 +909,109 @@ MmFlushVirtualMemory(IN PEPROCESS Process,
return STATUS_SUCCESS;
}
ULONG
NTAPI
MiQueryAddressState(IN PVOID Va,
IN PMMVAD Vad,
IN PEPROCESS TargetProcess,
OUT PULONG ReturnedProtect,
OUT PVOID *NextVa)
{
PMMPTE PointerPte, PointerPde;
MMPTE TempPte;
BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
ULONG State = MEM_RESERVE, Protect = 0, LockChange;
ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) &&
(Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT)));
/* Only normal VADs supported */
ASSERT(Vad->u.VadFlags.VadType == VadNone);
/* Get the PDE and PTE for the address */
PointerPde = MiAddressToPde(Va);
PointerPte = MiAddressToPte(Va);
/* Return the next range */
*NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
/* Loop to make sure the PDE is valid */
do
{
/* Try again */
LockChange = 0;
/* Is the PDE empty? */
if (!PointerPde->u.Long)
{
/* No address in this range used yet, move to the next PDE range */
*NextVa = MiPteToAddress(MiPteToAddress(PointerPde + 1));
break;
}
/* The PDE is empty, but is it faulted in? */
if (!PointerPde->u.Hard.Valid)
{
/* It isn't, go ahead and do the fault */
LockChange = MiMakeSystemAddressValid(MiPteToAddress(PointerPde),
TargetProcess);
}
/* Check if the PDE was faulted in, making the PTE readable */
if (!LockChange) ValidPte = TRUE;
} while (LockChange);
/* Is it safe to try reading the PTE? */
if (ValidPte)
{
/* FIXME: watch out for large pages */
/* Capture the PTE */
TempPte = *PointerPte;
if (TempPte.u.Long)
{
/* The PTE is valid, so it's not zeroed out */
DemandZeroPte = FALSE;
/* Check if it's valid or has a valid protection mask */
ASSERT(TempPte.u.Soft.Prototype == 0);
if ((TempPte.u.Soft.Protection != MM_DECOMMIT) ||
(TempPte.u.Hard.Valid == 1))
{
/* This means it's committed */
State = MEM_COMMIT;
/* For now, we lie about the protection */
Protect = PAGE_EXECUTE_READWRITE;
}
else
{
/* Otherwise our defaults should hold */
ASSERT(Protect == 0);
ASSERT(State == MEM_RESERVE);
}
}
}
/* Check if this was a demand-zero PTE, since we need to find the state */
if (DemandZeroPte)
{
/* Check if the VAD is for committed memory */
if (Vad->u.VadFlags.MemCommit)
{
/* This is committed memory */
State = MEM_COMMIT;
/* Convert the protection */
Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
}
}
/* Return the protection code */
*ReturnedProtect = Protect;
return State;
}
/* PUBLIC FUNCTIONS ***********************************************************/
/*
@ -2051,4 +2154,155 @@ NtResetWriteWatch(IN HANDLE ProcessHandle,
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
NtQueryVirtualMemory(IN HANDLE ProcessHandle,
IN PVOID BaseAddress,
IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
OUT PVOID MemoryInformation,
IN SIZE_T MemoryInformationLength,
OUT PSIZE_T ReturnLength)
{
PEPROCESS TargetProcess;
NTSTATUS Status;
PMMVAD Vad = NULL;
PVOID Address, NextAddress;
BOOLEAN Found;
ULONG NewProtect, NewState, BaseVpn;
MEMORY_BASIC_INFORMATION MemoryInfo;
KAPC_STATE ApcState;
DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress);
/* Only this class is supported for now */
ASSERT(MemoryInformationClass == MemoryBasicInformation);
/* Validate the size information of the class */
if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION))
{
/* The size is invalid */
return STATUS_INFO_LENGTH_MISMATCH;
}
/* Bail out if the address is invalid */
if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
/* Check for illegal addresses in user-space, or the shared memory area */
if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) ||
(PAGE_ALIGN(BaseAddress) == (PVOID)USER_SHARED_DATA))
{
/* FIXME: We should return some bogus info structure */
UNIMPLEMENTED;
while (TRUE);
}
/* Check if this is for a local or remote process */
if (ProcessHandle == NtCurrentProcess())
{
TargetProcess = PsGetCurrentProcess();
}
else
{
/* Reference the target process */
Status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_QUERY_INFORMATION,
PsProcessType,
ExGetPreviousMode(),
(PVOID*)&TargetProcess,
NULL);
if (!NT_SUCCESS(Status)) return Status;
/* Attach to it now */
KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
}
/* Loop the VADs */
ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
if (TargetProcess->VadRoot.NumberGenericTableElements)
{
/* Scan on the right */
Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild;
BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
while (Vad)
{
/* Check if this VAD covers the allocation range */
if ((BaseVpn >= Vad->StartingVpn) &&
(BaseVpn <= Vad->EndingVpn))
{
/* We're done */
Found = TRUE;
break;
}
/* Check if this VAD is too high */
if (BaseVpn < Vad->StartingVpn)
{
/* Search on the left next */
Vad = Vad->LeftChild;
}
else
{
/* Then this VAD is too low, keep searching on the right */
ASSERT(BaseVpn > Vad->EndingVpn);
Vad = Vad->RightChild;
}
}
}
/* Was a VAD found? */
if (!Found)
{
/* We don't handle this yet */
UNIMPLEMENTED;
while (TRUE);
}
/* This must be a VM VAD */
ASSERT(Vad->u.VadFlags.PrivateMemory);
/* Build the initial information block */
Address = PAGE_ALIGN(BaseAddress);
MemoryInfo.BaseAddress = Address;
MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
MemoryInfo.Type = MEM_PRIVATE;
/* Find the largest chunk of memory which has the same state and protection mask */
MemoryInfo.State = MiQueryAddressState(Address,
Vad,
TargetProcess,
&MemoryInfo.Protect,
&NextAddress);
Address = NextAddress;
while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
{
/* Keep going unless the state or protection mask changed */
NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
Address = NextAddress;
}
/* Now that we know the last VA address, calculate hte region size */
MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
/* Check if we were attached */
if (ProcessHandle != NtCurrentProcess())
{
/* Detach and derefernece the process */
KeUnstackDetachProcess(&ApcState);
ObDereferenceObject(TargetProcess);
}
/* Return the data (FIXME: Use SEH) */
*(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
/* All went well */
DPRINT("Base: %p AllocBase: %p Protect: %lx AllocProtect: %lx "
"State: %lx Type: %lx Size: %lx\n",
MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
MemoryInfo.AllocationProtect, MemoryInfo.Protect,
MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
return STATUS_SUCCESS;
}
/* EOF */

View file

@ -516,99 +516,6 @@ MmModifyAttributes(PMMSUPPORT AddressSpace,
}
}
NTSTATUS FASTCALL
MiQueryVirtualMemory(IN HANDLE ProcessHandle,
IN PVOID Address,
IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
OUT PVOID VirtualMemoryInformation,
IN SIZE_T Length,
OUT PSIZE_T ResultLength)
{
NTSTATUS Status;
PEPROCESS Process;
MEMORY_AREA* MemoryArea;
PMMSUPPORT AddressSpace;
Status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_QUERY_INFORMATION,
NULL,
UserMode,
(PVOID*)(&Process),
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT("NtQueryVirtualMemory() = %x\n",Status);
return(Status);
}
AddressSpace = &Process->Vm;
MmLockAddressSpace(AddressSpace);
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
switch(VirtualMemoryInformationClass)
{
case MemoryBasicInformation:
{
PMEMORY_BASIC_INFORMATION Info =
(PMEMORY_BASIC_INFORMATION)VirtualMemoryInformation;
if (Length != sizeof(MEMORY_BASIC_INFORMATION))
{
MmUnlockAddressSpace(AddressSpace);
ObDereferenceObject(Process);
return(STATUS_INFO_LENGTH_MISMATCH);
}
if (MemoryArea == NULL)
{
Info->Type = 0;
Info->State = MEM_FREE;
Info->Protect = PAGE_NOACCESS;
Info->AllocationProtect = 0;
Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
Info->AllocationBase = NULL;
Info->RegionSize = MmFindGapAtAddress(AddressSpace, Info->BaseAddress);
Status = STATUS_SUCCESS;
*ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
}
else
{
switch(MemoryArea->Type)
{
case MEMORY_AREA_VIRTUAL_MEMORY:
Status = MmQueryAnonMem(MemoryArea, Address, Info,
ResultLength);
break;
case MEMORY_AREA_SECTION_VIEW:
Status = MmQuerySectionView(MemoryArea, Address, Info,
ResultLength);
break;
default:
DPRINT1("unhandled memory area type: 0x%x\n", MemoryArea->Type);
Status = STATUS_UNSUCCESSFUL;
*ResultLength = 0;
}
}
break;
}
default:
{
DPRINT1("Unsupported or unimplemented class: %lx\n", VirtualMemoryInformationClass);
Status = STATUS_INVALID_INFO_CLASS;
*ResultLength = 0;
break;
}
}
MmUnlockAddressSpace(AddressSpace);
ObDereferenceObject(Process);
return Status;
}
NTSTATUS NTAPI
MiProtectVirtualMemory(IN PEPROCESS Process,
IN OUT PVOID *BaseAddress,
@ -1032,171 +939,6 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
return(STATUS_SUCCESS);
}
NTSTATUS NTAPI
NtQueryVirtualMemory(IN HANDLE ProcessHandle,
IN PVOID Address,
IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
OUT PVOID VirtualMemoryInformation,
IN SIZE_T Length,
OUT PSIZE_T UnsafeResultLength)
{
NTSTATUS Status;
SIZE_T ResultLength = 0;
KPROCESSOR_MODE PreviousMode;
WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
UNICODE_STRING ModuleFileName;
PMEMORY_SECTION_NAME SectionName = NULL;
PEPROCESS Process;
union
{
MEMORY_BASIC_INFORMATION BasicInfo;
}
VirtualMemoryInfo;
DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
"VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
"Length %lu ResultLength %x)\n",ProcessHandle,Address,
VirtualMemoryInformationClass,VirtualMemoryInformation,
Length,ResultLength);
PreviousMode = ExGetPreviousMode();
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
ProbeForWrite(VirtualMemoryInformation,
Length,
sizeof(ULONG_PTR));
if (UnsafeResultLength) ProbeForWriteSize_t(UnsafeResultLength);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
if (Address >= MmSystemRangeStart)
{
DPRINT1("Invalid parameter\n");
return STATUS_INVALID_PARAMETER;
}
/* FIXME: Move this inside MiQueryVirtualMemory */
if (VirtualMemoryInformationClass == MemorySectionName)
{
Status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_QUERY_INFORMATION,
NULL,
PreviousMode,
(PVOID*)(&Process),
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT("NtQueryVirtualMemory() = %x\n",Status);
return(Status);
}
RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
Status = MmGetFileNameForAddress(Address, &ModuleFileName);
if (NT_SUCCESS(Status))
{
SectionName = VirtualMemoryInformation;
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
SectionName->SectionFileName.MaximumLength = Length;
RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
if (UnsafeResultLength != NULL)
{
*UnsafeResultLength = ModuleFileName.Length;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
else
{
RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
SectionName->SectionFileName.MaximumLength = Length;
RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
if (UnsafeResultLength != NULL)
{
*UnsafeResultLength = ModuleFileName.Length;
}
}
}
ObDereferenceObject(Process);
return Status;
}
else
{
Status = MiQueryVirtualMemory(ProcessHandle,
Address,
VirtualMemoryInformationClass,
&VirtualMemoryInfo,
Length,
&ResultLength);
}
if (NT_SUCCESS(Status))
{
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
if (ResultLength > 0)
{
ProbeForWrite(VirtualMemoryInformation,
ResultLength,
1);
RtlCopyMemory(VirtualMemoryInformation,
&VirtualMemoryInfo,
ResultLength);
}
if (UnsafeResultLength != NULL)
{
*UnsafeResultLength = ResultLength;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
else
{
if (ResultLength > 0)
{
RtlCopyMemory(VirtualMemoryInformation,
&VirtualMemoryInfo,
ResultLength);
}
if (UnsafeResultLength != NULL)
{
*UnsafeResultLength = ResultLength;
}
}
}
return(Status);
}
static VOID
MmFreeVirtualMemoryPage(PVOID Context,
MEMORY_AREA* MemoryArea,

View file

@ -356,6 +356,12 @@ NTAPI
MiInsertVad(IN PMMVAD Vad,
IN PEPROCESS Process);
ULONG
NTAPI
MiMakeProtectionMask(
IN ULONG Protect
);
static VOID
MmInsertMemoryArea(
PMMSUPPORT AddressSpace,
@ -389,6 +395,7 @@ MmInsertMemoryArea(
}
Vad->u.VadFlags.Spare = 1;
Vad->u.VadFlags.PrivateMemory = 1;
Vad->u.VadFlags.Protection = MiMakeProtectionMask(marea->Protect);
MiInsertVad(Vad, MmGetAddressSpaceOwner(AddressSpace));
marea->Vad = Vad;
}