2009-10-15 16:50:49 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: ReactOS Kernel
|
|
|
|
* LICENSE: BSD - See COPYING.ARM in the top level directory
|
|
|
|
* FILE: ntoskrnl/mm/ARM3/virtual.c
|
|
|
|
* PURPOSE: ARM Memory Manager Virtual Memory Management
|
|
|
|
* PROGRAMMERS: ReactOS Portable Systems Group
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
|
|
|
|
#include <ntoskrnl.h>
|
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
|
|
|
#line 15 "ARM³::VIRTUAL"
|
|
|
|
#define MODULE_INVOLVED_IN_ARM3
|
|
|
|
#include "../ARM3/miarm.h"
|
|
|
|
|
|
|
|
#define MI_MAPPED_COPY_PAGES 14
|
|
|
|
#define MI_POOL_COPY_BYTES 512
|
|
|
|
#define MI_MAX_TRANSFER_SIZE 64 * 1024
|
|
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
|
|
MiProtectVirtualMemory(IN PEPROCESS Process,
|
|
|
|
IN OUT PVOID *BaseAddress,
|
|
|
|
IN OUT PSIZE_T NumberOfBytesToProtect,
|
|
|
|
IN ULONG NewAccessProtection,
|
|
|
|
OUT PULONG OldAccessProtection OPTIONAL);
|
|
|
|
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
|
2010-10-17 20:02:17 +00:00
|
|
|
ULONG
|
|
|
|
NTAPI
|
|
|
|
MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress,
|
|
|
|
IN PEPROCESS CurrentProcess)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
BOOLEAN LockChange = FALSE;
|
|
|
|
|
|
|
|
/* Must be a non-pool page table, since those are double-mapped already */
|
|
|
|
ASSERT(PageTableVirtualAddress > MM_HIGHEST_USER_ADDRESS);
|
|
|
|
ASSERT((PageTableVirtualAddress < MmPagedPoolStart) ||
|
|
|
|
(PageTableVirtualAddress > MmPagedPoolEnd));
|
|
|
|
|
|
|
|
/* Working set lock or PFN lock should be held */
|
|
|
|
ASSERT(KeAreAllApcsDisabled() == TRUE);
|
|
|
|
|
|
|
|
/* Check if the page table is valid */
|
|
|
|
while (!MmIsAddressValid(PageTableVirtualAddress))
|
|
|
|
{
|
|
|
|
/* Fault it in */
|
|
|
|
Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* This should not fail */
|
|
|
|
KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
|
|
|
|
1,
|
|
|
|
Status,
|
|
|
|
(ULONG_PTR)CurrentProcess,
|
|
|
|
(ULONG_PTR)PageTableVirtualAddress);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This flag will be useful later when we do better locking */
|
|
|
|
LockChange = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Let caller know what the lock state is */
|
|
|
|
return LockChange;
|
|
|
|
}
|
|
|
|
|
2010-10-19 17:07:11 +00:00
|
|
|
ULONG
|
|
|
|
NTAPI
|
|
|
|
MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress,
|
|
|
|
IN KIRQL OldIrql)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
BOOLEAN LockChange = FALSE;
|
|
|
|
|
|
|
|
/* Must be e kernel address */
|
|
|
|
ASSERT(VirtualAddress > MM_HIGHEST_USER_ADDRESS);
|
|
|
|
|
|
|
|
/* Check if the page is valid */
|
|
|
|
while (!MmIsAddressValid(VirtualAddress))
|
|
|
|
{
|
|
|
|
/* Release the PFN database */
|
|
|
|
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
|
|
|
|
|
|
|
|
/* Fault it in */
|
|
|
|
Status = MmAccessFault(FALSE, VirtualAddress, KernelMode, NULL);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* This should not fail */
|
|
|
|
KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
|
|
|
|
3,
|
|
|
|
Status,
|
|
|
|
0,
|
|
|
|
(ULONG_PTR)VirtualAddress);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This flag will be useful later when we do better locking */
|
|
|
|
LockChange = TRUE;
|
|
|
|
|
|
|
|
/* Lock the PFN database */
|
|
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Let caller know what the lock state is */
|
|
|
|
return LockChange;
|
|
|
|
}
|
|
|
|
|
2010-06-05 04:16:46 +00:00
|
|
|
PFN_NUMBER
|
|
|
|
NTAPI
|
|
|
|
MiDeleteSystemPageableVm(IN PMMPTE PointerPte,
|
|
|
|
IN PFN_NUMBER PageCount,
|
|
|
|
IN ULONG Flags,
|
|
|
|
OUT PPFN_NUMBER ValidPages)
|
|
|
|
{
|
|
|
|
PFN_NUMBER ActualPages = 0;
|
2010-07-22 18:26:04 +00:00
|
|
|
PETHREAD CurrentThread = PsGetCurrentThread();
|
2010-06-05 04:16:46 +00:00
|
|
|
PMMPFN Pfn1, Pfn2;
|
|
|
|
PFN_NUMBER PageFrameIndex, PageTableIndex;
|
2010-07-22 18:26:04 +00:00
|
|
|
KIRQL OldIrql;
|
2010-06-05 04:16:46 +00:00
|
|
|
ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
|
|
|
|
|
2010-07-22 18:26:04 +00:00
|
|
|
/* Lock the system working set */
|
|
|
|
MiLockWorkingSet(CurrentThread, &MmSystemCacheWs);
|
2010-06-05 04:16:46 +00:00
|
|
|
|
|
|
|
/* Loop all pages */
|
|
|
|
while (PageCount)
|
|
|
|
{
|
|
|
|
/* Make sure there's some data about the page */
|
|
|
|
if (PointerPte->u.Long)
|
|
|
|
{
|
|
|
|
/* As always, only handle current ARM3 scenarios */
|
|
|
|
ASSERT(PointerPte->u.Soft.Prototype == 0);
|
|
|
|
ASSERT(PointerPte->u.Soft.Transition == 0);
|
|
|
|
|
|
|
|
/* Normally this is one possibility -- freeing a valid page */
|
|
|
|
if (PointerPte->u.Hard.Valid)
|
|
|
|
{
|
|
|
|
/* Get the page PFN */
|
|
|
|
PageFrameIndex = PFN_FROM_PTE(PointerPte);
|
|
|
|
Pfn1 = MiGetPfnEntry(PageFrameIndex);
|
|
|
|
|
|
|
|
/* Should not have any working set data yet */
|
|
|
|
ASSERT(Pfn1->u1.WsIndex == 0);
|
|
|
|
|
|
|
|
/* Actual valid, legitimate, pages */
|
|
|
|
if (ValidPages) *ValidPages++;
|
|
|
|
|
|
|
|
/* Get the page table entry */
|
|
|
|
PageTableIndex = Pfn1->u4.PteFrame;
|
|
|
|
Pfn2 = MiGetPfnEntry(PageTableIndex);
|
|
|
|
|
|
|
|
/* Lock the PFN database */
|
|
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
|
|
|
|
|
|
|
|
/* Delete it the page */
|
|
|
|
MI_SET_PFN_DELETED(Pfn1);
|
|
|
|
MiDecrementShareCount(Pfn1, PageFrameIndex);
|
|
|
|
|
|
|
|
/* Decrement the page table too */
|
[NTOS]: Remove useless variables in kernel code that were set, but never actually used (dead code, tests, copy/pasters). If a variable was set but not used because of missing/#if'ed out code, a note was added instead.
[NTOS]: In the process, fix bugs in the Event dispatcher code that used Win32 EVENT_TYPE instead of NT KOBJECTS enumeration.
[NTOS]: Fix a bug in ObpInsertHandleCount, where the object access check was being done with the previous mode, instead of honoring the probe mode, which is defined by OBJ_FORCE_ACCESS_CHECK.
[NTOS]: Fix a bug in a section function which was always returning STATUS_SUCCESS, now it returns the result of the previous Status = function assignment. If this isn't desired, then don't check for the Status anymore.
[NTOS]: Note that MDL code does not support SkipBytes argument. If it is used, MDL could be invalid.
[NTOS]: Add checks for VerifierAllocation and set it when needed (WIP).
[NTOS]: Clarify what _WORKING_LINKER_ is, and the legal risks in continuing to use a linker that builds non-Microsoft drivers when used with headers whose EULA specify that they can only be used for Microsoft drivers.
svn path=/trunk/; revision=48692
2010-09-04 08:17:17 +00:00
|
|
|
DPRINT("FIXME: ARM3 should decrement the PT refcount for: %p\n", Pfn2);
|
2010-06-05 04:16:46 +00:00
|
|
|
#if 0 // ARM3: Dont't trust this yet
|
|
|
|
MiDecrementShareCount(Pfn2, PageTableIndex);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Release the PFN database */
|
|
|
|
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
|
|
|
|
|
|
|
|
/* Destroy the PTE */
|
|
|
|
PointerPte->u.Long = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Actual legitimate pages */
|
|
|
|
ActualPages++;
|
|
|
|
}
|
2010-06-05 14:54:26 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The only other ARM3 possibility is a demand zero page, which would
|
|
|
|
* mean freeing some of the paged pool pages that haven't even been
|
|
|
|
* touched yet, as part of a larger allocation.
|
|
|
|
*
|
|
|
|
* Right now, we shouldn't expect any page file information in the PTE
|
|
|
|
*/
|
|
|
|
ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
|
|
|
|
|
|
|
|
/* Destroy the PTE */
|
|
|
|
PointerPte->u.Long = 0;
|
|
|
|
}
|
2010-06-05 04:16:46 +00:00
|
|
|
|
|
|
|
/* Keep going */
|
|
|
|
PointerPte++;
|
|
|
|
PageCount--;
|
|
|
|
}
|
|
|
|
|
2010-07-22 18:26:04 +00:00
|
|
|
/* Release the working set */
|
|
|
|
MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs);
|
2010-06-05 04:16:46 +00:00
|
|
|
|
|
|
|
/* Flush the entire TLB */
|
|
|
|
KeFlushEntireTb(TRUE, TRUE);
|
|
|
|
|
|
|
|
/* Done */
|
|
|
|
return ActualPages;
|
|
|
|
}
|
|
|
|
|
2010-10-17 20:02:17 +00:00
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
MiDeletePte(IN PMMPTE PointerPte,
|
|
|
|
IN PVOID VirtualAddress,
|
2010-10-19 17:07:11 +00:00
|
|
|
IN PEPROCESS CurrentProcess,
|
|
|
|
IN PMMPTE PrototypePte)
|
2010-10-17 20:02:17 +00:00
|
|
|
{
|
|
|
|
PMMPFN Pfn1;
|
2010-10-18 14:25:33 +00:00
|
|
|
MMPTE TempPte;
|
2010-10-17 20:02:17 +00:00
|
|
|
PFN_NUMBER PageFrameIndex;
|
2010-10-19 17:07:11 +00:00
|
|
|
PMMPDE PointerPde;
|
2010-10-17 20:02:17 +00:00
|
|
|
|
|
|
|
/* PFN lock must be held */
|
|
|
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
|
|
|
|
|
|
/* Capture the PTE */
|
2010-10-18 14:25:33 +00:00
|
|
|
TempPte = *PointerPte;
|
2010-10-17 20:02:17 +00:00
|
|
|
|
|
|
|
/* We only support valid PTEs for now */
|
2010-10-18 14:25:33 +00:00
|
|
|
ASSERT(TempPte.u.Hard.Valid == 1);
|
2010-10-19 17:07:11 +00:00
|
|
|
if (TempPte.u.Hard.Valid == 0)
|
|
|
|
{
|
|
|
|
/* Invalid PTEs not supported yet */
|
|
|
|
ASSERT(TempPte.u.Soft.Prototype == 0);
|
|
|
|
ASSERT(TempPte.u.Soft.Transition == 0);
|
|
|
|
}
|
2010-10-17 20:02:17 +00:00
|
|
|
|
|
|
|
/* Get the PFN entry */
|
2010-10-18 14:25:33 +00:00
|
|
|
PageFrameIndex = PFN_FROM_PTE(&TempPte);
|
2010-10-17 20:02:17 +00:00
|
|
|
Pfn1 = MiGetPfnEntry(PageFrameIndex);
|
|
|
|
|
2010-10-19 17:07:11 +00:00
|
|
|
/* Check if this is a valid, prototype PTE */
|
|
|
|
if (Pfn1->u3.e1.PrototypePte == 1)
|
2010-10-17 20:02:17 +00:00
|
|
|
{
|
2010-10-19 17:07:11 +00:00
|
|
|
/* Get the PDE and make sure it's faulted in */
|
|
|
|
PointerPde = MiAddressToPde(PointerPte);
|
|
|
|
if (PointerPde->u.Hard.Valid == 0)
|
|
|
|
{
|
|
|
|
#if (_MI_PAGING_LEVELS == 2)
|
|
|
|
/* Could be paged pool access from a new process -- synchronize the page directories */
|
|
|
|
if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress)))
|
|
|
|
{
|
|
|
|
#endif
|
|
|
|
/* The PDE must be valid at this point */
|
|
|
|
KeBugCheckEx(MEMORY_MANAGEMENT,
|
|
|
|
0x61940,
|
|
|
|
(ULONG_PTR)PointerPte,
|
|
|
|
PointerPte->u.Long,
|
|
|
|
(ULONG_PTR)VirtualAddress);
|
|
|
|
}
|
|
|
|
#if (_MI_PAGING_LEVELS == 2)
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
|
|
|
|
//MiDecrementShareCount(MiGetPfnEntry(PFN_FROM_PTE(PointerPde)), PFN_FROM_PDE(PointerPde));
|
|
|
|
|
|
|
|
/* Drop the share count */
|
|
|
|
MiDecrementShareCount(Pfn1, PageFrameIndex);
|
|
|
|
|
|
|
|
/* No fork yet */
|
|
|
|
if (PointerPte <= MiHighestUserPte) ASSERT(PrototypePte == Pfn1->PteAddress);
|
2010-10-17 20:02:17 +00:00
|
|
|
}
|
2010-10-19 17:07:11 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Make sure the saved PTE address is valid */
|
|
|
|
if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte)
|
|
|
|
{
|
|
|
|
/* The PFN entry is illegal, or invalid */
|
|
|
|
KeBugCheckEx(MEMORY_MANAGEMENT,
|
|
|
|
0x401,
|
|
|
|
(ULONG_PTR)PointerPte,
|
|
|
|
PointerPte->u.Long,
|
|
|
|
(ULONG_PTR)Pfn1->PteAddress);
|
|
|
|
}
|
2010-10-17 20:02:17 +00:00
|
|
|
|
2010-10-19 17:07:11 +00:00
|
|
|
/* There should only be 1 shared reference count */
|
|
|
|
ASSERT(Pfn1->u2.ShareCount == 1);
|
2010-10-17 20:02:17 +00:00
|
|
|
|
2010-10-19 17:07:11 +00:00
|
|
|
/* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
|
|
|
|
//MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
|
2010-10-17 20:02:17 +00:00
|
|
|
|
2010-10-19 17:07:11 +00:00
|
|
|
/* Mark the PFN for deletion and dereference what should be the last ref */
|
|
|
|
MI_SET_PFN_DELETED(Pfn1);
|
|
|
|
MiDecrementShareCount(Pfn1, PageFrameIndex);
|
2010-10-17 20:02:17 +00:00
|
|
|
|
2010-10-19 17:07:11 +00:00
|
|
|
/* We should eventually do this */
|
|
|
|
//CurrentProcess->NumberOfPrivatePages--;
|
|
|
|
}
|
2010-10-17 20:02:17 +00:00
|
|
|
|
|
|
|
/* Destroy the PTE and flush the TLB */
|
|
|
|
PointerPte->u.Long = 0;
|
|
|
|
KeFlushCurrentTb();
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
MiDeleteVirtualAddresses(IN ULONG_PTR Va,
|
|
|
|
IN ULONG_PTR EndingAddress,
|
|
|
|
IN PMMVAD Vad)
|
|
|
|
{
|
2010-10-19 17:07:11 +00:00
|
|
|
PMMPTE PointerPte, PointerPde, PrototypePte, LastPrototypePte;
|
2010-10-17 20:02:17 +00:00
|
|
|
MMPTE TempPte;
|
|
|
|
PEPROCESS CurrentProcess;
|
|
|
|
KIRQL OldIrql;
|
2010-10-19 17:07:11 +00:00
|
|
|
BOOLEAN AddressGap = FALSE;
|
|
|
|
PSUBSECTION Subsection;
|
|
|
|
|
|
|
|
/* Get out if this is a fake VAD, RosMm will free the marea pages */
|
|
|
|
if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return;
|
2010-10-17 20:02:17 +00:00
|
|
|
|
|
|
|
/* Grab the process and PTE/PDE for the address being deleted */
|
|
|
|
CurrentProcess = PsGetCurrentProcess();
|
|
|
|
PointerPde = MiAddressToPde(Va);
|
|
|
|
PointerPte = MiAddressToPte(Va);
|
|
|
|
|
2010-10-19 17:07:11 +00:00
|
|
|
/* Check if this is a section VAD or a VM VAD */
|
|
|
|
if (!(Vad) || (Vad->u.VadFlags.PrivateMemory) || !(Vad->FirstPrototypePte))
|
2010-10-17 20:02:17 +00:00
|
|
|
{
|
2010-10-19 17:07:11 +00:00
|
|
|
/* Don't worry about prototypes */
|
|
|
|
PrototypePte = LastPrototypePte = NULL;
|
2010-10-17 20:02:17 +00:00
|
|
|
}
|
2010-10-19 17:07:11 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Get the prototype PTE */
|
|
|
|
PrototypePte = Vad->FirstPrototypePte;
|
|
|
|
LastPrototypePte = Vad->FirstPrototypePte + 1;
|
|
|
|
}
|
|
|
|
|
2010-10-17 20:02:17 +00:00
|
|
|
/* In all cases, we don't support fork() yet */
|
|
|
|
ASSERT(CurrentProcess->CloneRoot == NULL);
|
|
|
|
|
|
|
|
/* Loop the PTE for each VA */
|
|
|
|
while (TRUE)
|
|
|
|
{
|
|
|
|
/* First keep going until we find a valid PDE */
|
2010-10-19 17:07:11 +00:00
|
|
|
while (!PointerPde->u.Long)
|
2010-10-17 20:02:17 +00:00
|
|
|
{
|
2010-10-19 17:07:11 +00:00
|
|
|
/* There are gaps in the address space */
|
|
|
|
AddressGap = TRUE;
|
|
|
|
|
2010-10-17 20:02:17 +00:00
|
|
|
/* Still no valid PDE, try the next 4MB (or whatever) */
|
|
|
|
PointerPde++;
|
|
|
|
|
|
|
|
/* Update the PTE on this new boundary */
|
|
|
|
PointerPte = MiPteToAddress(PointerPde);
|
|
|
|
|
|
|
|
/* Check if all the PDEs are invalid, so there's nothing to free */
|
|
|
|
Va = (ULONG_PTR)MiPteToAddress(PointerPte);
|
|
|
|
if (Va > EndingAddress) return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now check if the PDE is mapped in */
|
2010-10-19 17:07:11 +00:00
|
|
|
if (!PointerPde->u.Hard.Valid)
|
2010-10-17 20:02:17 +00:00
|
|
|
{
|
|
|
|
/* It isn't, so map it in */
|
|
|
|
PointerPte = MiPteToAddress(PointerPde);
|
|
|
|
MiMakeSystemAddressValid(PointerPte, CurrentProcess);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now we should have a valid PDE, mapped in, and still have some VA */
|
|
|
|
ASSERT(PointerPde->u.Hard.Valid == 1);
|
|
|
|
ASSERT(Va <= EndingAddress);
|
|
|
|
|
2010-10-19 17:07:11 +00:00
|
|
|
/* Check if this is a section VAD with gaps in it */
|
|
|
|
if ((AddressGap) && (LastPrototypePte))
|
|
|
|
{
|
|
|
|
/* We need to skip to the next correct prototype PTE */
|
|
|
|
PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
|
|
|
|
|
|
|
|
/* And we need the subsection to skip to the next last prototype PTE */
|
|
|
|
Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
|
|
|
|
if (Subsection)
|
|
|
|
{
|
|
|
|
/* Found it! */
|
|
|
|
LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* No more subsections, we are done with prototype PTEs */
|
|
|
|
PrototypePte = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-17 20:02:17 +00:00
|
|
|
/* Lock the PFN Database while we delete the PTEs */
|
|
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
|
|
|
|
do
|
|
|
|
{
|
|
|
|
/* Capture the PDE and make sure it exists */
|
|
|
|
TempPte = *PointerPte;
|
|
|
|
if (TempPte.u.Long)
|
|
|
|
{
|
|
|
|
/* Check if the PTE is actually mapped in */
|
|
|
|
if (TempPte.u.Long & 0xFFFFFC01)
|
|
|
|
{
|
2010-10-19 17:07:11 +00:00
|
|
|
/* Are we dealing with section VAD? */
|
|
|
|
if ((LastPrototypePte) && (PrototypePte > LastPrototypePte))
|
|
|
|
{
|
|
|
|
/* We need to skip to the next correct prototype PTE */
|
|
|
|
PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
|
|
|
|
|
|
|
|
/* And we need the subsection to skip to the next last prototype PTE */
|
|
|
|
Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
|
|
|
|
if (Subsection)
|
|
|
|
{
|
|
|
|
/* Found it! */
|
|
|
|
LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* No more subsections, we are done with prototype PTEs */
|
|
|
|
PrototypePte = NULL;
|
|
|
|
}
|
|
|
|
}
|
2010-10-17 20:02:17 +00:00
|
|
|
|
2010-10-19 17:07:11 +00:00
|
|
|
/* Check for prototype PTE */
|
|
|
|
if ((TempPte.u.Hard.Valid == 0) &&
|
|
|
|
(TempPte.u.Soft.Prototype == 1))
|
|
|
|
{
|
|
|
|
/* Just nuke it */
|
|
|
|
PointerPte->u.Long = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Delete the PTE proper */
|
|
|
|
MiDeletePte(PointerPte,
|
|
|
|
(PVOID)Va,
|
|
|
|
CurrentProcess,
|
|
|
|
PrototypePte);
|
|
|
|
}
|
2010-10-17 20:02:17 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* The PTE was never mapped, just nuke it here */
|
|
|
|
PointerPte->u.Long = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update the address and PTE for it */
|
|
|
|
Va += PAGE_SIZE;
|
|
|
|
PointerPte++;
|
2010-10-19 17:07:11 +00:00
|
|
|
PrototypePte++;
|
2010-10-17 20:02:17 +00:00
|
|
|
|
|
|
|
/* Making sure the PDE is still valid */
|
|
|
|
ASSERT(PointerPde->u.Hard.Valid == 1);
|
|
|
|
}
|
|
|
|
while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress));
|
|
|
|
|
|
|
|
/* The PDE should still be valid at this point */
|
|
|
|
ASSERT(PointerPde->u.Hard.Valid == 1);
|
|
|
|
|
|
|
|
/* Release the lock and get out if we're done */
|
|
|
|
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
|
|
|
|
if (Va > EndingAddress) return;
|
|
|
|
|
|
|
|
/* Otherwise, we exited because we hit a new PDE boundary, so start over */
|
|
|
|
PointerPde = MiAddressToPde(Va);
|
2010-10-19 17:07:11 +00:00
|
|
|
AddressGap = FALSE;
|
2010-10-17 20:11:04 +00:00
|
|
|
}
|
2010-10-17 20:02:17 +00:00
|
|
|
}
|
|
|
|
|
2009-10-15 16:50:49 +00:00
|
|
|
LONG
|
|
|
|
MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo,
|
|
|
|
OUT PBOOLEAN HaveBadAddress,
|
|
|
|
OUT PULONG_PTR BadAddress)
|
|
|
|
{
|
|
|
|
PEXCEPTION_RECORD ExceptionRecord;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
//
|
|
|
|
// Assume default
|
|
|
|
//
|
|
|
|
*HaveBadAddress = FALSE;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Get the exception record
|
|
|
|
//
|
|
|
|
ExceptionRecord = ExceptionInfo->ExceptionRecord;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Look at the exception code
|
|
|
|
//
|
|
|
|
if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
|
|
|
|
(ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
|
|
|
|
(ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// We can tell the address if we have more than one parameter
|
|
|
|
//
|
|
|
|
if (ExceptionRecord->NumberParameters > 1)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Return the address
|
|
|
|
//
|
|
|
|
*HaveBadAddress = TRUE;
|
|
|
|
*BadAddress = ExceptionRecord->ExceptionInformation[1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Continue executing the next handler
|
|
|
|
//
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
MiDoMappedCopy(IN PEPROCESS SourceProcess,
|
|
|
|
IN PVOID SourceAddress,
|
|
|
|
IN PEPROCESS TargetProcess,
|
|
|
|
OUT PVOID TargetAddress,
|
|
|
|
IN SIZE_T BufferSize,
|
|
|
|
IN KPROCESSOR_MODE PreviousMode,
|
|
|
|
OUT PSIZE_T ReturnSize)
|
|
|
|
{
|
|
|
|
PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
|
|
|
|
PMDL Mdl = (PMDL)MdlBuffer;
|
|
|
|
SIZE_T TotalSize, CurrentSize, RemainingSize;
|
|
|
|
volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving;
|
|
|
|
volatile BOOLEAN PagesLocked;
|
|
|
|
PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
|
|
|
|
volatile PVOID MdlAddress;
|
|
|
|
KAPC_STATE ApcState;
|
|
|
|
BOOLEAN HaveBadAddress;
|
|
|
|
ULONG_PTR BadAddress;
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
//
|
|
|
|
// Calculate the maximum amount of data to move
|
|
|
|
//
|
|
|
|
TotalSize = MI_MAPPED_COPY_PAGES * PAGE_SIZE;
|
|
|
|
if (BufferSize <= TotalSize) TotalSize = BufferSize;
|
|
|
|
CurrentSize = TotalSize;
|
|
|
|
RemainingSize = BufferSize;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Loop as long as there is still data
|
|
|
|
//
|
|
|
|
while (RemainingSize > 0)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Check if this transfer will finish everything off
|
|
|
|
//
|
|
|
|
if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Attach to the source address space
|
|
|
|
//
|
|
|
|
KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Reset state for this pass
|
|
|
|
//
|
|
|
|
MdlAddress = NULL;
|
|
|
|
PagesLocked = FALSE;
|
|
|
|
FailedInMoving = FALSE;
|
|
|
|
ASSERT(FailedInProbe == FALSE);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Protect user-mode copy
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// If this is our first time, probe the buffer
|
|
|
|
//
|
|
|
|
if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Catch a failure here
|
|
|
|
//
|
|
|
|
FailedInProbe = TRUE;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Do the probe
|
|
|
|
//
|
|
|
|
ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
|
|
|
|
|
|
|
|
//
|
|
|
|
// Passed
|
|
|
|
//
|
|
|
|
FailedInProbe = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Initialize and probe and lock the MDL
|
|
|
|
//
|
|
|
|
MmInitializeMdl(Mdl, CurrentAddress, CurrentSize);
|
|
|
|
MmProbeAndLockPages(Mdl, PreviousMode, IoReadAccess);
|
|
|
|
PagesLocked = TRUE;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Now map the pages
|
|
|
|
//
|
|
|
|
MdlAddress = MmMapLockedPagesSpecifyCache(Mdl,
|
|
|
|
KernelMode,
|
|
|
|
MmCached,
|
|
|
|
NULL,
|
|
|
|
FALSE,
|
|
|
|
HighPagePriority);
|
|
|
|
if (!MdlAddress)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Use our SEH handler to pick this up
|
|
|
|
//
|
|
|
|
FailedInMapping = TRUE;
|
|
|
|
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Now let go of the source and grab to the target process
|
|
|
|
//
|
|
|
|
KeUnstackDetachProcess(&ApcState);
|
|
|
|
KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if this is our first time through
|
|
|
|
//
|
|
|
|
if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Catch a failure here
|
|
|
|
//
|
|
|
|
FailedInProbe = TRUE;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Do the probe
|
|
|
|
//
|
|
|
|
ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
|
|
|
|
|
|
|
|
//
|
|
|
|
// Passed
|
|
|
|
//
|
|
|
|
FailedInProbe = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Now do the actual move
|
|
|
|
//
|
|
|
|
FailedInMoving = TRUE;
|
|
|
|
RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
|
|
|
|
&HaveBadAddress,
|
|
|
|
&BadAddress))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Detach from whoever we may be attached to
|
|
|
|
//
|
|
|
|
KeUnstackDetachProcess(&ApcState);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we had mapped the pages
|
|
|
|
//
|
|
|
|
if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we had locked the pages
|
|
|
|
//
|
|
|
|
if (PagesLocked) MmUnlockPages(Mdl);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we hit working set quota
|
|
|
|
//
|
|
|
|
if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Return the error
|
|
|
|
//
|
|
|
|
return STATUS_WORKING_SET_QUOTA;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we failed during the probe or mapping
|
|
|
|
//
|
|
|
|
if ((FailedInProbe) || (FailedInMapping))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Exit
|
|
|
|
//
|
|
|
|
Status = _SEH2_GetExceptionCode();
|
|
|
|
_SEH2_YIELD(return Status);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Otherwise, we failed probably during the move
|
|
|
|
//
|
|
|
|
*ReturnSize = BufferSize - RemainingSize;
|
|
|
|
if (FailedInMoving)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Check if we know exactly where we stopped copying
|
|
|
|
//
|
|
|
|
if (HaveBadAddress)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Return the exact number of bytes copied
|
|
|
|
//
|
|
|
|
*ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return partial copy
|
|
|
|
//
|
|
|
|
Status = STATUS_PARTIAL_COPY;
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check for SEH status
|
|
|
|
//
|
|
|
|
if (Status != STATUS_SUCCESS) return Status;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Detach from target
|
|
|
|
//
|
|
|
|
KeUnstackDetachProcess(&ApcState);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Unmap and unlock
|
|
|
|
//
|
|
|
|
MmUnmapLockedPages(MdlAddress, Mdl);
|
|
|
|
MmUnlockPages(Mdl);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Update location and size
|
|
|
|
//
|
|
|
|
RemainingSize -= CurrentSize;
|
|
|
|
CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
|
|
|
|
CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// All bytes read
|
|
|
|
//
|
|
|
|
*ReturnSize = BufferSize;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
MiDoPoolCopy(IN PEPROCESS SourceProcess,
|
|
|
|
IN PVOID SourceAddress,
|
|
|
|
IN PEPROCESS TargetProcess,
|
|
|
|
OUT PVOID TargetAddress,
|
|
|
|
IN SIZE_T BufferSize,
|
|
|
|
IN KPROCESSOR_MODE PreviousMode,
|
|
|
|
OUT PSIZE_T ReturnSize)
|
|
|
|
{
|
|
|
|
UCHAR StackBuffer[MI_POOL_COPY_BYTES];
|
|
|
|
SIZE_T TotalSize, CurrentSize, RemainingSize;
|
|
|
|
volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE;
|
|
|
|
PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
|
|
|
|
PVOID PoolAddress;
|
|
|
|
KAPC_STATE ApcState;
|
|
|
|
BOOLEAN HaveBadAddress;
|
|
|
|
ULONG_PTR BadAddress;
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
//
|
|
|
|
// Calculate the maximum amount of data to move
|
|
|
|
//
|
|
|
|
TotalSize = MI_MAX_TRANSFER_SIZE;
|
|
|
|
if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize;
|
|
|
|
CurrentSize = TotalSize;
|
|
|
|
RemainingSize = BufferSize;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we can use the stack
|
|
|
|
//
|
|
|
|
if (BufferSize <= MI_POOL_COPY_BYTES)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Use it
|
|
|
|
//
|
|
|
|
PoolAddress = (PVOID)StackBuffer;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Allocate pool
|
|
|
|
//
|
|
|
|
PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, 'VmRw');
|
|
|
|
if (!PoolAddress) ASSERT(FALSE);
|
|
|
|
HavePoolAddress = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Loop as long as there is still data
|
|
|
|
//
|
|
|
|
while (RemainingSize > 0)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Check if this transfer will finish everything off
|
|
|
|
//
|
|
|
|
if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Attach to the source address space
|
|
|
|
//
|
|
|
|
KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Reset state for this pass
|
|
|
|
//
|
|
|
|
FailedInMoving = FALSE;
|
|
|
|
ASSERT(FailedInProbe == FALSE);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Protect user-mode copy
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// If this is our first time, probe the buffer
|
|
|
|
//
|
|
|
|
if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Catch a failure here
|
|
|
|
//
|
|
|
|
FailedInProbe = TRUE;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Do the probe
|
|
|
|
//
|
|
|
|
ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
|
|
|
|
|
|
|
|
//
|
|
|
|
// Passed
|
|
|
|
//
|
|
|
|
FailedInProbe = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Do the copy
|
|
|
|
//
|
|
|
|
RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Now let go of the source and grab to the target process
|
|
|
|
//
|
|
|
|
KeUnstackDetachProcess(&ApcState);
|
|
|
|
KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if this is our first time through
|
|
|
|
//
|
|
|
|
if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Catch a failure here
|
|
|
|
//
|
|
|
|
FailedInProbe = TRUE;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Do the probe
|
|
|
|
//
|
|
|
|
ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
|
|
|
|
|
|
|
|
//
|
|
|
|
// Passed
|
|
|
|
//
|
|
|
|
FailedInProbe = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Now do the actual move
|
|
|
|
//
|
|
|
|
FailedInMoving = TRUE;
|
|
|
|
RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
|
|
|
|
&HaveBadAddress,
|
|
|
|
&BadAddress))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Detach from whoever we may be attached to
|
|
|
|
//
|
|
|
|
KeUnstackDetachProcess(&ApcState);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we had allocated pool
|
|
|
|
//
|
|
|
|
if (HavePoolAddress) ExFreePool(PoolAddress);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we failed during the probe
|
|
|
|
//
|
|
|
|
if (FailedInProbe)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Exit
|
|
|
|
//
|
|
|
|
Status = _SEH2_GetExceptionCode();
|
|
|
|
_SEH2_YIELD(return Status);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Otherwise, we failed, probably during the move
|
|
|
|
//
|
|
|
|
*ReturnSize = BufferSize - RemainingSize;
|
|
|
|
if (FailedInMoving)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Check if we know exactly where we stopped copying
|
|
|
|
//
|
|
|
|
if (HaveBadAddress)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Return the exact number of bytes copied
|
|
|
|
//
|
|
|
|
*ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return partial copy
|
|
|
|
//
|
|
|
|
Status = STATUS_PARTIAL_COPY;
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check for SEH status
|
|
|
|
//
|
|
|
|
if (Status != STATUS_SUCCESS) return Status;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Detach from target
|
|
|
|
//
|
|
|
|
KeUnstackDetachProcess(&ApcState);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Update location and size
|
|
|
|
//
|
|
|
|
RemainingSize -= CurrentSize;
|
|
|
|
CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
|
|
|
|
CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress +
|
|
|
|
CurrentSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we had allocated pool
|
|
|
|
//
|
|
|
|
if (HavePoolAddress) ExFreePool(PoolAddress);
|
|
|
|
|
|
|
|
//
|
|
|
|
// All bytes read
|
|
|
|
//
|
|
|
|
*ReturnSize = BufferSize;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
MmCopyVirtualMemory(IN PEPROCESS SourceProcess,
|
|
|
|
IN PVOID SourceAddress,
|
|
|
|
IN PEPROCESS TargetProcess,
|
|
|
|
OUT PVOID TargetAddress,
|
|
|
|
IN SIZE_T BufferSize,
|
|
|
|
IN KPROCESSOR_MODE PreviousMode,
|
|
|
|
OUT PSIZE_T ReturnSize)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
PEPROCESS Process = SourceProcess;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Don't accept zero-sized buffers
|
|
|
|
//
|
|
|
|
if (!BufferSize) return STATUS_SUCCESS;
|
|
|
|
|
|
|
|
//
|
|
|
|
// If we are copying from ourselves, lock the target instead
|
|
|
|
//
|
|
|
|
if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Acquire rundown protection
|
|
|
|
//
|
|
|
|
if (!ExAcquireRundownProtection(&Process->RundownProtect))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Fail
|
|
|
|
//
|
|
|
|
return STATUS_PROCESS_IS_TERMINATING;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// See if we should use the pool copy
|
|
|
|
//
|
|
|
|
if (BufferSize > MI_POOL_COPY_BYTES)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Use MDL-copy
|
|
|
|
//
|
|
|
|
Status = MiDoMappedCopy(SourceProcess,
|
|
|
|
SourceAddress,
|
|
|
|
TargetProcess,
|
|
|
|
TargetAddress,
|
|
|
|
BufferSize,
|
|
|
|
PreviousMode,
|
|
|
|
ReturnSize);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Do pool copy
|
|
|
|
//
|
|
|
|
Status = MiDoPoolCopy(SourceProcess,
|
|
|
|
SourceAddress,
|
|
|
|
TargetProcess,
|
|
|
|
TargetAddress,
|
|
|
|
BufferSize,
|
|
|
|
PreviousMode,
|
|
|
|
ReturnSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Release the lock
|
|
|
|
//
|
|
|
|
ExReleaseRundownProtection(&Process->RundownProtect);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
MmFlushVirtualMemory(IN PEPROCESS Process,
|
|
|
|
IN OUT PVOID *BaseAddress,
|
|
|
|
IN OUT PSIZE_T RegionSize,
|
|
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock)
|
|
|
|
{
|
|
|
|
PAGED_CODE();
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Fake success
|
|
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2010-10-26 21:34:52 +00:00
|
|
|
ULONG
|
|
|
|
NTAPI
|
|
|
|
MiGetPageProtection(IN PMMPTE PointerPte)
|
|
|
|
{
|
|
|
|
MMPTE TempPte;
|
|
|
|
PMMPFN Pfn;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
/* Copy this PTE's contents */
|
|
|
|
TempPte = *PointerPte;
|
|
|
|
|
|
|
|
/* Assure it's not totally zero */
|
|
|
|
ASSERT(TempPte.u.Long);
|
|
|
|
|
|
|
|
/* Check for a special prototype format */
|
|
|
|
if (TempPte.u.Soft.Valid == 0 &&
|
|
|
|
TempPte.u.Soft.Prototype == 1)
|
|
|
|
{
|
|
|
|
/* Unsupported now */
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
ASSERT(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* In the easy case of transition or demand zero PTE just return its protection */
|
|
|
|
if (!TempPte.u.Hard.Valid) return MmProtectToValue[TempPte.u.Soft.Protection];
|
|
|
|
|
|
|
|
/* If we get here, the PTE is valid, so look up the page in PFN database */
|
2010-10-27 09:58:18 +00:00
|
|
|
Pfn = MiGetPfnEntry(TempPte.u.Hard.PageFrameNumber);
|
2010-10-26 21:34:52 +00:00
|
|
|
|
|
|
|
if (!Pfn->u3.e1.PrototypePte)
|
|
|
|
{
|
|
|
|
/* Return protection of the original pte */
|
|
|
|
return MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is hardware PTE */
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
ASSERT(FALSE);
|
|
|
|
|
|
|
|
return PAGE_NOACCESS;
|
|
|
|
}
|
|
|
|
|
2010-10-18 14:25:33 +00:00
|
|
|
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;
|
2010-10-26 21:34:52 +00:00
|
|
|
|
|
|
|
/* Get protection state of this page */
|
|
|
|
Protect = MiGetPageProtection(PointerPte);
|
2010-10-18 14:25:33 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-10-15 16:50:49 +00:00
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @unimplemented
|
|
|
|
*/
|
|
|
|
PVOID
|
|
|
|
NTAPI
|
|
|
|
MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
|
|
|
|
{
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @unimplemented
|
|
|
|
*/
|
|
|
|
PVOID
|
|
|
|
NTAPI
|
|
|
|
MmSecureVirtualMemory(IN PVOID Address,
|
|
|
|
IN SIZE_T Length,
|
|
|
|
IN ULONG Mode)
|
|
|
|
{
|
2010-06-27 21:18:59 +00:00
|
|
|
static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
|
2010-01-02 01:32:43 +00:00
|
|
|
return Address;
|
2009-10-15 16:50:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @unimplemented
|
|
|
|
*/
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
MmUnsecureVirtualMemory(IN PVOID SecureMem)
|
|
|
|
{
|
2010-06-27 21:18:59 +00:00
|
|
|
static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
|
2009-10-15 16:50:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* SYSTEM CALLS ***************************************************************/
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
NtReadVirtualMemory(IN HANDLE ProcessHandle,
|
|
|
|
IN PVOID BaseAddress,
|
|
|
|
OUT PVOID Buffer,
|
|
|
|
IN SIZE_T NumberOfBytesToRead,
|
|
|
|
OUT PSIZE_T NumberOfBytesRead OPTIONAL)
|
|
|
|
{
|
|
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
|
|
PEPROCESS Process;
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
SIZE_T BytesRead = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we came from user mode
|
|
|
|
//
|
|
|
|
if (PreviousMode != KernelMode)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Validate the read addresses
|
|
|
|
//
|
|
|
|
if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
|
|
|
|
(((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
|
|
|
|
(((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
|
|
|
|
(((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Don't allow to write into kernel space
|
|
|
|
//
|
|
|
|
return STATUS_ACCESS_VIOLATION;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Enter SEH for probe
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Probe the output value
|
|
|
|
//
|
|
|
|
if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Get exception code
|
|
|
|
//
|
|
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Don't do zero-byte transfers
|
|
|
|
//
|
|
|
|
if (NumberOfBytesToRead)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Reference the process
|
|
|
|
//
|
|
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
|
|
PROCESS_VM_READ,
|
|
|
|
PsProcessType,
|
|
|
|
PreviousMode,
|
|
|
|
(PVOID*)(&Process),
|
|
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Do the copy
|
|
|
|
//
|
|
|
|
Status = MmCopyVirtualMemory(Process,
|
|
|
|
BaseAddress,
|
|
|
|
PsGetCurrentProcess(),
|
|
|
|
Buffer,
|
|
|
|
NumberOfBytesToRead,
|
|
|
|
PreviousMode,
|
|
|
|
&BytesRead);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Dereference the process
|
|
|
|
//
|
|
|
|
ObDereferenceObject(Process);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if the caller sent this parameter
|
|
|
|
//
|
|
|
|
if (NumberOfBytesRead)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Enter SEH to guard write
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Return the number of bytes read
|
|
|
|
//
|
|
|
|
*NumberOfBytesRead = BytesRead;
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return status
|
|
|
|
//
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
NtWriteVirtualMemory(IN HANDLE ProcessHandle,
|
|
|
|
IN PVOID BaseAddress,
|
|
|
|
IN PVOID Buffer,
|
|
|
|
IN SIZE_T NumberOfBytesToWrite,
|
|
|
|
OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
|
|
|
|
{
|
|
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
|
|
PEPROCESS Process;
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
2010-07-16 00:34:26 +00:00
|
|
|
SIZE_T BytesWritten = 0;
|
2009-10-15 16:50:49 +00:00
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we came from user mode
|
|
|
|
//
|
|
|
|
if (PreviousMode != KernelMode)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Validate the read addresses
|
|
|
|
//
|
|
|
|
if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
|
|
|
|
(((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
|
|
|
|
(((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
|
|
|
|
(((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Don't allow to write into kernel space
|
|
|
|
//
|
|
|
|
return STATUS_ACCESS_VIOLATION;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Enter SEH for probe
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Probe the output value
|
|
|
|
//
|
|
|
|
if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Get exception code
|
|
|
|
//
|
|
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Don't do zero-byte transfers
|
|
|
|
//
|
|
|
|
if (NumberOfBytesToWrite)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Reference the process
|
|
|
|
//
|
|
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
|
|
PROCESS_VM_WRITE,
|
|
|
|
PsProcessType,
|
|
|
|
PreviousMode,
|
|
|
|
(PVOID*)&Process,
|
|
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Do the copy
|
|
|
|
//
|
|
|
|
Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
|
|
|
|
Buffer,
|
|
|
|
Process,
|
|
|
|
BaseAddress,
|
|
|
|
NumberOfBytesToWrite,
|
|
|
|
PreviousMode,
|
|
|
|
&BytesWritten);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Dereference the process
|
|
|
|
//
|
|
|
|
ObDereferenceObject(Process);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if the caller sent this parameter
|
|
|
|
//
|
|
|
|
if (NumberOfBytesWritten)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Enter SEH to guard write
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Return the number of bytes written
|
|
|
|
//
|
|
|
|
*NumberOfBytesWritten = BytesWritten;
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return status
|
|
|
|
//
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
NtProtectVirtualMemory(IN HANDLE ProcessHandle,
|
|
|
|
IN OUT PVOID *UnsafeBaseAddress,
|
|
|
|
IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
|
|
|
|
IN ULONG NewAccessProtection,
|
|
|
|
OUT PULONG UnsafeOldAccessProtection)
|
|
|
|
{
|
|
|
|
PEPROCESS Process;
|
|
|
|
ULONG OldAccessProtection;
|
|
|
|
ULONG Protection;
|
|
|
|
PEPROCESS CurrentProcess = PsGetCurrentProcess();
|
|
|
|
PVOID BaseAddress = NULL;
|
|
|
|
SIZE_T NumberOfBytesToProtect = 0;
|
|
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
|
|
NTSTATUS Status;
|
|
|
|
BOOLEAN Attached = FALSE;
|
|
|
|
KAPC_STATE ApcState;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check for valid protection flags
|
|
|
|
//
|
|
|
|
Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
|
|
|
|
if (Protection != PAGE_NOACCESS &&
|
|
|
|
Protection != PAGE_READONLY &&
|
|
|
|
Protection != PAGE_READWRITE &&
|
|
|
|
Protection != PAGE_WRITECOPY &&
|
|
|
|
Protection != PAGE_EXECUTE &&
|
|
|
|
Protection != PAGE_EXECUTE_READ &&
|
|
|
|
Protection != PAGE_EXECUTE_READWRITE &&
|
|
|
|
Protection != PAGE_EXECUTE_WRITECOPY)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Fail
|
|
|
|
//
|
|
|
|
return STATUS_INVALID_PAGE_PROTECTION;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we came from user mode
|
|
|
|
//
|
|
|
|
if (PreviousMode != KernelMode)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Enter SEH for probing
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Validate all outputs
|
|
|
|
//
|
|
|
|
ProbeForWritePointer(UnsafeBaseAddress);
|
|
|
|
ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
|
|
|
|
ProbeForWriteUlong(UnsafeOldAccessProtection);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Capture them
|
|
|
|
//
|
|
|
|
BaseAddress = *UnsafeBaseAddress;
|
|
|
|
NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Get exception code
|
|
|
|
//
|
|
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Capture directly
|
|
|
|
//
|
|
|
|
BaseAddress = *UnsafeBaseAddress;
|
|
|
|
NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Catch illegal base address
|
|
|
|
//
|
|
|
|
if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Catch illegal region size
|
|
|
|
//
|
|
|
|
if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Fail
|
|
|
|
//
|
|
|
|
return STATUS_INVALID_PARAMETER_3;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 0 is also illegal
|
|
|
|
//
|
|
|
|
if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Get a reference to the process
|
|
|
|
//
|
|
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
|
|
PROCESS_VM_OPERATION,
|
|
|
|
PsProcessType,
|
|
|
|
PreviousMode,
|
|
|
|
(PVOID*)(&Process),
|
|
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we should attach
|
|
|
|
//
|
|
|
|
if (CurrentProcess != Process)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Do it
|
|
|
|
//
|
|
|
|
KeStackAttachProcess(&Process->Pcb, &ApcState);
|
|
|
|
Attached = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Do the actual work
|
|
|
|
//
|
|
|
|
Status = MiProtectVirtualMemory(Process,
|
|
|
|
&BaseAddress,
|
|
|
|
&NumberOfBytesToProtect,
|
|
|
|
NewAccessProtection,
|
|
|
|
&OldAccessProtection);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Detach if needed
|
|
|
|
//
|
|
|
|
if (Attached) KeUnstackDetachProcess(&ApcState);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Release reference
|
|
|
|
//
|
|
|
|
ObDereferenceObject(Process);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Enter SEH to return data
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Return data to user
|
|
|
|
//
|
|
|
|
*UnsafeOldAccessProtection = OldAccessProtection;
|
|
|
|
*UnsafeBaseAddress = BaseAddress;
|
|
|
|
*UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return status
|
|
|
|
//
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
NtLockVirtualMemory(IN HANDLE ProcessHandle,
|
|
|
|
IN OUT PVOID *BaseAddress,
|
|
|
|
IN OUT PSIZE_T NumberOfBytesToLock,
|
|
|
|
IN ULONG MapType)
|
|
|
|
{
|
|
|
|
PEPROCESS Process;
|
|
|
|
PEPROCESS CurrentProcess = PsGetCurrentProcess();
|
|
|
|
NTSTATUS Status;
|
|
|
|
BOOLEAN Attached = FALSE;
|
|
|
|
KAPC_STATE ApcState;
|
|
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
|
|
PVOID CapturedBaseAddress;
|
|
|
|
SIZE_T CapturedBytesToLock;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
//
|
|
|
|
// Validate flags
|
|
|
|
//
|
|
|
|
if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Invalid set of flags
|
|
|
|
//
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// At least one flag must be specified
|
|
|
|
//
|
|
|
|
if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// No flag given
|
|
|
|
//
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Enter SEH for probing
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Validate output data
|
|
|
|
//
|
|
|
|
ProbeForWritePointer(BaseAddress);
|
|
|
|
ProbeForWriteSize_t(NumberOfBytesToLock);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Capture it
|
|
|
|
//
|
|
|
|
CapturedBaseAddress = *BaseAddress;
|
|
|
|
CapturedBytesToLock = *NumberOfBytesToLock;
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Get exception code
|
|
|
|
//
|
|
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Catch illegal base address
|
|
|
|
//
|
|
|
|
if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Catch illegal region size
|
|
|
|
//
|
|
|
|
if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Fail
|
|
|
|
//
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 0 is also illegal
|
|
|
|
//
|
|
|
|
if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Get a reference to the process
|
|
|
|
//
|
|
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
|
|
PROCESS_VM_OPERATION,
|
|
|
|
PsProcessType,
|
|
|
|
PreviousMode,
|
|
|
|
(PVOID*)(&Process),
|
|
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if this is is system-mapped
|
|
|
|
//
|
|
|
|
if (MapType & MAP_SYSTEM)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Check for required privilege
|
|
|
|
//
|
|
|
|
if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Fail: Don't have it
|
|
|
|
//
|
|
|
|
ObDereferenceObject(Process);
|
|
|
|
return STATUS_PRIVILEGE_NOT_HELD;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we should attach
|
|
|
|
//
|
|
|
|
if (CurrentProcess != Process)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Do it
|
|
|
|
//
|
|
|
|
KeStackAttachProcess(&Process->Pcb, &ApcState);
|
|
|
|
Attached = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Oops :(
|
|
|
|
//
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Detach if needed
|
|
|
|
//
|
|
|
|
if (Attached) KeUnstackDetachProcess(&ApcState);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Release reference
|
|
|
|
//
|
|
|
|
ObDereferenceObject(Process);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Enter SEH to return data
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Return data to user
|
|
|
|
//
|
|
|
|
*BaseAddress = CapturedBaseAddress;
|
|
|
|
*NumberOfBytesToLock = 0;
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Get exception code
|
|
|
|
//
|
|
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return status
|
|
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
|
|
|
|
IN OUT PVOID *BaseAddress,
|
|
|
|
IN OUT PSIZE_T NumberOfBytesToUnlock,
|
|
|
|
IN ULONG MapType)
|
|
|
|
{
|
|
|
|
PEPROCESS Process;
|
|
|
|
PEPROCESS CurrentProcess = PsGetCurrentProcess();
|
|
|
|
NTSTATUS Status;
|
|
|
|
BOOLEAN Attached = FALSE;
|
|
|
|
KAPC_STATE ApcState;
|
|
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
|
|
PVOID CapturedBaseAddress;
|
|
|
|
SIZE_T CapturedBytesToUnlock;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
//
|
|
|
|
// Validate flags
|
|
|
|
//
|
|
|
|
if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Invalid set of flags
|
|
|
|
//
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// At least one flag must be specified
|
|
|
|
//
|
|
|
|
if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// No flag given
|
|
|
|
//
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Enter SEH for probing
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Validate output data
|
|
|
|
//
|
|
|
|
ProbeForWritePointer(BaseAddress);
|
|
|
|
ProbeForWriteSize_t(NumberOfBytesToUnlock);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Capture it
|
|
|
|
//
|
|
|
|
CapturedBaseAddress = *BaseAddress;
|
|
|
|
CapturedBytesToUnlock = *NumberOfBytesToUnlock;
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Get exception code
|
|
|
|
//
|
|
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Catch illegal base address
|
|
|
|
//
|
|
|
|
if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Catch illegal region size
|
|
|
|
//
|
|
|
|
if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Fail
|
|
|
|
//
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 0 is also illegal
|
|
|
|
//
|
|
|
|
if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Get a reference to the process
|
|
|
|
//
|
|
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
|
|
PROCESS_VM_OPERATION,
|
|
|
|
PsProcessType,
|
|
|
|
PreviousMode,
|
|
|
|
(PVOID*)(&Process),
|
|
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if this is is system-mapped
|
|
|
|
//
|
|
|
|
if (MapType & MAP_SYSTEM)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Check for required privilege
|
|
|
|
//
|
|
|
|
if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Fail: Don't have it
|
|
|
|
//
|
|
|
|
ObDereferenceObject(Process);
|
|
|
|
return STATUS_PRIVILEGE_NOT_HELD;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we should attach
|
|
|
|
//
|
|
|
|
if (CurrentProcess != Process)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Do it
|
|
|
|
//
|
|
|
|
KeStackAttachProcess(&Process->Pcb, &ApcState);
|
|
|
|
Attached = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Oops :(
|
|
|
|
//
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Detach if needed
|
|
|
|
//
|
|
|
|
if (Attached) KeUnstackDetachProcess(&ApcState);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Release reference
|
|
|
|
//
|
|
|
|
ObDereferenceObject(Process);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Enter SEH to return data
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Return data to user
|
|
|
|
//
|
|
|
|
*BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
|
|
|
|
*NumberOfBytesToUnlock = 0;
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Get exception code
|
|
|
|
//
|
|
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return status
|
|
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
NtFlushVirtualMemory(IN HANDLE ProcessHandle,
|
|
|
|
IN OUT PVOID *BaseAddress,
|
|
|
|
IN OUT PSIZE_T NumberOfBytesToFlush,
|
|
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock)
|
|
|
|
{
|
|
|
|
PEPROCESS Process;
|
|
|
|
NTSTATUS Status;
|
|
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
|
|
PVOID CapturedBaseAddress;
|
|
|
|
SIZE_T CapturedBytesToFlush;
|
|
|
|
IO_STATUS_BLOCK LocalStatusBlock;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we came from user mode
|
|
|
|
//
|
|
|
|
if (PreviousMode != KernelMode)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Enter SEH for probing
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Validate all outputs
|
|
|
|
//
|
|
|
|
ProbeForWritePointer(BaseAddress);
|
|
|
|
ProbeForWriteSize_t(NumberOfBytesToFlush);
|
|
|
|
ProbeForWriteIoStatusBlock(IoStatusBlock);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Capture them
|
|
|
|
//
|
|
|
|
CapturedBaseAddress = *BaseAddress;
|
|
|
|
CapturedBytesToFlush = *NumberOfBytesToFlush;
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Get exception code
|
|
|
|
//
|
|
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Capture directly
|
|
|
|
//
|
|
|
|
CapturedBaseAddress = *BaseAddress;
|
|
|
|
CapturedBytesToFlush = *NumberOfBytesToFlush;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Catch illegal base address
|
|
|
|
//
|
|
|
|
if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Catch illegal region size
|
|
|
|
//
|
|
|
|
if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Fail
|
|
|
|
//
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Get a reference to the process
|
|
|
|
//
|
|
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
|
|
PROCESS_VM_OPERATION,
|
|
|
|
PsProcessType,
|
|
|
|
PreviousMode,
|
|
|
|
(PVOID*)(&Process),
|
|
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Do it
|
|
|
|
//
|
|
|
|
Status = MmFlushVirtualMemory(Process,
|
|
|
|
&CapturedBaseAddress,
|
|
|
|
&CapturedBytesToFlush,
|
|
|
|
&LocalStatusBlock);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Release reference
|
|
|
|
//
|
|
|
|
ObDereferenceObject(Process);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Enter SEH to return data
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Return data to user
|
|
|
|
//
|
|
|
|
*BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
|
|
|
|
*NumberOfBytesToFlush = 0;
|
|
|
|
*IoStatusBlock = LocalStatusBlock;
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return status
|
|
|
|
//
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @unimplemented
|
|
|
|
*/
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
NtGetWriteWatch(IN HANDLE ProcessHandle,
|
|
|
|
IN ULONG Flags,
|
|
|
|
IN PVOID BaseAddress,
|
|
|
|
IN SIZE_T RegionSize,
|
|
|
|
IN PVOID *UserAddressArray,
|
|
|
|
OUT PULONG_PTR EntriesInUserAddressArray,
|
|
|
|
OUT PULONG Granularity)
|
|
|
|
{
|
|
|
|
PEPROCESS Process;
|
|
|
|
NTSTATUS Status;
|
|
|
|
PVOID EndAddress;
|
|
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
|
|
ULONG_PTR CapturedEntryCount;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if we came from user mode
|
|
|
|
//
|
|
|
|
if (PreviousMode != KernelMode)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Enter SEH for probing
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Catch illegal base address
|
|
|
|
//
|
|
|
|
if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Catch illegal region size
|
|
|
|
//
|
|
|
|
if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Fail
|
|
|
|
//
|
|
|
|
return STATUS_INVALID_PARAMETER_3;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Validate all data
|
|
|
|
//
|
|
|
|
ProbeForWriteSize_t(EntriesInUserAddressArray);
|
|
|
|
ProbeForWriteUlong(Granularity);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Capture them
|
|
|
|
//
|
|
|
|
CapturedEntryCount = *EntriesInUserAddressArray;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Must have a count
|
|
|
|
//
|
|
|
|
if (CapturedEntryCount == 0) return STATUS_INVALID_PARAMETER_5;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Can't be larger than the maximum
|
|
|
|
//
|
|
|
|
if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR)))
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Fail
|
|
|
|
//
|
|
|
|
return STATUS_INVALID_PARAMETER_5;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Probe the actual array
|
|
|
|
//
|
|
|
|
ProbeForWrite(UserAddressArray,
|
|
|
|
CapturedEntryCount * sizeof(PVOID),
|
|
|
|
sizeof(PVOID));
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Get exception code
|
|
|
|
//
|
|
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Capture directly
|
|
|
|
//
|
|
|
|
CapturedEntryCount = *EntriesInUserAddressArray;
|
|
|
|
ASSERT(CapturedEntryCount != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if this is a local request
|
|
|
|
//
|
|
|
|
if (ProcessHandle == NtCurrentProcess())
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// No need to reference the process
|
|
|
|
//
|
|
|
|
Process = PsGetCurrentProcess();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Reference the target
|
|
|
|
//
|
|
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
|
|
PROCESS_VM_OPERATION,
|
|
|
|
PsProcessType,
|
|
|
|
PreviousMode,
|
|
|
|
(PVOID *)&Process,
|
|
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Compute the last address and validate it
|
|
|
|
//
|
|
|
|
EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
|
|
|
|
if (BaseAddress > EndAddress)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Fail
|
|
|
|
//
|
|
|
|
if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
|
|
|
|
return STATUS_INVALID_PARAMETER_4;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Oops :(
|
|
|
|
//
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Dereference if needed
|
|
|
|
//
|
|
|
|
if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Enter SEH to return data
|
|
|
|
//
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Return data to user
|
|
|
|
//
|
|
|
|
*EntriesInUserAddressArray = 0;
|
|
|
|
*Granularity = PAGE_SIZE;
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Get exception code
|
|
|
|
//
|
|
|
|
Status = _SEH2_GetExceptionCode();
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return success
|
|
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @unimplemented
|
|
|
|
*/
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
NtResetWriteWatch(IN HANDLE ProcessHandle,
|
|
|
|
IN PVOID BaseAddress,
|
|
|
|
IN SIZE_T RegionSize)
|
|
|
|
{
|
|
|
|
PVOID EndAddress;
|
|
|
|
PEPROCESS Process;
|
|
|
|
NTSTATUS Status;
|
|
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
|
|
ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Catch illegal base address
|
|
|
|
//
|
|
|
|
if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Catch illegal region size
|
|
|
|
//
|
|
|
|
if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Fail
|
|
|
|
//
|
|
|
|
return STATUS_INVALID_PARAMETER_3;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if this is a local request
|
|
|
|
//
|
|
|
|
if (ProcessHandle == NtCurrentProcess())
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// No need to reference the process
|
|
|
|
//
|
|
|
|
Process = PsGetCurrentProcess();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Reference the target
|
|
|
|
//
|
|
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
|
|
PROCESS_VM_OPERATION,
|
|
|
|
PsProcessType,
|
|
|
|
PreviousMode,
|
|
|
|
(PVOID *)&Process,
|
|
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Compute the last address and validate it
|
|
|
|
//
|
|
|
|
EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
|
|
|
|
if (BaseAddress > EndAddress)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Fail
|
|
|
|
//
|
|
|
|
if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
|
|
|
|
return STATUS_INVALID_PARAMETER_3;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Oops :(
|
|
|
|
//
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Dereference if needed
|
|
|
|
//
|
|
|
|
if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return success
|
|
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2010-10-18 14:25:33 +00:00
|
|
|
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;
|
2010-10-24 20:02:04 +00:00
|
|
|
BOOLEAN Found = FALSE;
|
2010-10-18 14:25:33 +00:00
|
|
|
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))
|
|
|
|
{
|
2010-10-25 20:50:45 +00:00
|
|
|
Address = PAGE_ALIGN(BaseAddress);
|
|
|
|
|
|
|
|
/* Make up an info structure describing this range */
|
|
|
|
MemoryInfo.BaseAddress = Address;
|
|
|
|
MemoryInfo.AllocationProtect = PAGE_READONLY;
|
|
|
|
MemoryInfo.Type = MEM_PRIVATE;
|
|
|
|
|
|
|
|
/* Special case for shared data */
|
|
|
|
if (Address == (PVOID)USER_SHARED_DATA)
|
|
|
|
{
|
|
|
|
MemoryInfo.AllocationBase = (PVOID)USER_SHARED_DATA;
|
|
|
|
MemoryInfo.State = MEM_COMMIT;
|
|
|
|
MemoryInfo.Protect = PAGE_READONLY;
|
|
|
|
MemoryInfo.RegionSize = PAGE_SIZE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MemoryInfo.AllocationBase = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1;
|
|
|
|
MemoryInfo.State = MEM_RESERVE;
|
|
|
|
MemoryInfo.Protect = PAGE_NOACCESS;
|
|
|
|
MemoryInfo.RegionSize = (ULONG_PTR)MemoryInfo.AllocationBase - (ULONG_PTR)Address;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the data (FIXME: Use SEH) */
|
|
|
|
*(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
|
|
|
|
if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
2010-10-18 14:25:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
{
|
2010-10-24 20:02:04 +00:00
|
|
|
Address = PAGE_ALIGN(BaseAddress);
|
|
|
|
|
|
|
|
/* Calculate region size */
|
|
|
|
if (Vad)
|
|
|
|
{
|
2010-10-25 20:50:45 +00:00
|
|
|
if (Vad->StartingVpn >= BaseVpn)
|
|
|
|
{
|
|
|
|
/* Region size is the free space till the start of that VAD */
|
|
|
|
MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Get the next VAD */
|
|
|
|
Vad = (PMMVAD)MiGetNextNode((PMMADDRESS_NODE)Vad);
|
|
|
|
if (Vad)
|
|
|
|
{
|
|
|
|
/* Region size is the free space till the start of that VAD */
|
|
|
|
MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Maximum possible region size with that base address */
|
|
|
|
MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
|
|
|
|
}
|
|
|
|
}
|
2010-10-24 20:02:04 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Maximum possible region size with that base address */
|
|
|
|
MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if we were attached */
|
|
|
|
if (ProcessHandle != NtCurrentProcess())
|
|
|
|
{
|
|
|
|
/* Detach and derefernece the process */
|
|
|
|
KeUnstackDetachProcess(&ApcState);
|
|
|
|
ObDereferenceObject(TargetProcess);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Build the rest of the initial information block */
|
|
|
|
MemoryInfo.BaseAddress = Address;
|
|
|
|
MemoryInfo.AllocationBase = NULL;
|
|
|
|
MemoryInfo.AllocationProtect = 0;
|
|
|
|
MemoryInfo.State = MEM_FREE;
|
|
|
|
MemoryInfo.Protect = PAGE_NOACCESS;
|
|
|
|
MemoryInfo.Type = 0;
|
|
|
|
|
|
|
|
/* Return the data (FIXME: Use SEH) */
|
|
|
|
*(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
|
|
|
|
if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
2010-10-18 14:25:33 +00:00
|
|
|
}
|
2010-10-24 20:02:04 +00:00
|
|
|
|
2010-10-18 14:25:33 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2009-10-15 16:50:49 +00:00
|
|
|
/* EOF */
|