Sync to trunk head(r38096)
[reactos.git] / reactos / ntoskrnl / mm / virtual.c
index f5fd854..8c15e35 100644 (file)
@@ -1,5 +1,4 @@
-/* $Id$
- *
+/*
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS kernel
  * FILE:            ntoskrnl/mm/virtual.c
  * PROGRAMMERS:     David Welch
  */
 
-/* INCLUDE *****************************************************************/
+/* INCLUDE ********************************************************************/
 
 #include <ntoskrnl.h>
-
 #define NDEBUG
-#include <internal/debug.h>
+#include <debug.h>
 
-/* FUNCTIONS *****************************************************************/
+#define MI_MAPPED_COPY_PAGES  16
+#define MI_POOL_COPY_BYTES    512
+#define MI_MAX_TRANSFER_SIZE  64 * 1024
+#define TAG_VM TAG('V', 'm', 'R', 'w')
 
-NTSTATUS STDCALL
-NtFlushVirtualMemory(IN HANDLE ProcessHandle,
-                     IN PVOID BaseAddress,
-                     IN ULONG NumberOfBytesToFlush,
-                     OUT PULONG NumberOfBytesFlushed OPTIONAL)
-/*
- * FUNCTION: Flushes virtual memory to file
- * ARGUMENTS:
- *        ProcessHandle = Points to the process that allocated the virtual
- *                        memory
- *        BaseAddress = Points to the memory address
- *        NumberOfBytesToFlush = Limits the range to flush,
- *        NumberOfBytesFlushed = Actual number of bytes flushed
- * RETURNS: Status
- */
+/* PRIVATE FUNCTIONS **********************************************************/
+
+static
+int
+MiGetExceptionInfo(EXCEPTION_POINTERS *ExceptionInfo, BOOLEAN * HaveBadAddress, ULONG_PTR * BadAddress)
 {
-  /* This should be implemented once we support network filesystems */
-  DPRINT("NtFlushVirtualMemory is UNIMPLEMENTED\n");
-  return(STATUS_SUCCESS);
-}
+    PEXCEPTION_RECORD ExceptionRecord;
+    PAGED_CODE();
 
+    /* Assume default */
+    *HaveBadAddress = FALSE;
 
-NTSTATUS STDCALL
-MiLockVirtualMemory(HANDLE ProcessHandle,
-  PVOID BaseAddress,
-  ULONG NumberOfBytesToLock,
-  PULONG NumberOfBytesLocked,
-  PObReferenceObjectByHandle pObReferenceObjectByHandle,
-  PMmCreateMdl pMmCreateMdl,
-  PObDereferenceObject pObDereferenceObject,
-  PMmProbeAndLockPages pMmProbeAndLockPages,
-  PExFreePool pExFreePool)
-{
-  PEPROCESS Process;
-  NTSTATUS Status;
-  PMDL Mdl;
-
-  Status = pObReferenceObjectByHandle(ProcessHandle,
-    PROCESS_VM_WRITE,
-    NULL,
-    UserMode,
-    (PVOID*)(&Process),
-    NULL);
-  if (!NT_SUCCESS(Status))
-    return(Status);
+    /* Get the exception record */
+    ExceptionRecord = ExceptionInfo->ExceptionRecord;
 
-  Mdl = pMmCreateMdl(NULL,
-    BaseAddress,
-    NumberOfBytesToLock);
-  if (Mdl == NULL)
+    /* Look at the exception code */
+    if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
+        (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
+        (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
     {
-      pObDereferenceObject(Process);
-      return(STATUS_NO_MEMORY);
+        /* 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];
+        }
     }
 
-  pMmProbeAndLockPages(Mdl,
-    UserMode,
-    IoWriteAccess);
+    /* Continue executing the next handler */
+    return EXCEPTION_EXECUTE_HANDLER;
+}
 
-  pExFreePool(Mdl);
+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 - 2) * 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;
 
-  pObDereferenceObject(Process);
+        /* Attach to the source address space */
+        KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
 
-  *NumberOfBytesLocked = NumberOfBytesToLock;
-  return(STATUS_SUCCESS);
-}
+        /* 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;
 
-NTSTATUS STDCALL
-NtLockVirtualMemory(HANDLE ProcessHandle,
-  PVOID BaseAddress,
-  ULONG NumberOfBytesToLock,
-  PULONG NumberOfBytesLocked)
-{
-  DPRINT("NtLockVirtualMemory(ProcessHandle %x, BaseAddress %x, "
-    "NumberOfBytesToLock %d, NumberOfBytesLocked %x)\n",
-    ProcessHandle,
-    BaseAddress,
-    NumberOfBytesToLock,
-    NumberOfBytesLocked);
-
-  return MiLockVirtualMemory(ProcessHandle,
-    BaseAddress,
-    NumberOfBytesToLock,
-    NumberOfBytesLocked,
-    ObReferenceObjectByHandle,
-    MmCreateMdl,
-    ObfDereferenceObject,
-    MmProbeAndLockPages,
-    ExFreePool);
-}
+                /* Do the probe */
+                ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
 
+                /* Passed */
+                FailedInProbe = FALSE;
+            }
 
-NTSTATUS FASTCALL
-MiQueryVirtualMemory (IN HANDLE ProcessHandle,
-                      IN PVOID Address,
-                      IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
-                      OUT PVOID VirtualMemoryInformation,
-                      IN ULONG Length,
-                      OUT PULONG ResultLength)
-{
-   NTSTATUS Status;
-   PEPROCESS Process;
-   MEMORY_AREA* MemoryArea;
-   PMADDRESS_SPACE AddressSpace;
-
-   if (Address < MmSystemRangeStart)
-   {
-      Status = ObReferenceObjectByHandle(ProcessHandle,
-                                         PROCESS_QUERY_INFORMATION,
-                                         NULL,
-                                         UserMode,
-                                         (PVOID*)(&Process),
-                                         NULL);
-
-      if (!NT_SUCCESS(Status))
-      {
-         DPRINT("NtQueryVirtualMemory() = %x\n",Status);
-         return(Status);
-      }
-      AddressSpace = (PMADDRESS_SPACE)&Process->VadRoot;
-   }
-   else
-   {
-      AddressSpace = MmGetKernelAddressSpace();
-   }
-   MmLockAddressSpace(AddressSpace);
-   MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
-   switch(VirtualMemoryInformationClass)
-   {
-      case MemoryBasicInformation:
-         {
-           PMEMORY_BASIC_INFORMATION Info =
-               (PMEMORY_BASIC_INFORMATION)VirtualMemoryInformation;
-            if (Length != sizeof(MEMORY_BASIC_INFORMATION))
+            /* 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)
             {
-               MmUnlockAddressSpace(AddressSpace);
-               ObDereferenceObject(Process);
-               return(STATUS_INFO_LENGTH_MISMATCH);
+                /* Use our SEH handler to pick this up */
+                FailedInMapping = TRUE;
+                ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
             }
 
-            if (MemoryArea == NULL)
+            /* 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))
             {
-              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:
-                  case MEMORY_AREA_PEB_OR_TEB:
-                     Status = MmQueryAnonMem(MemoryArea, Address, Info,
-                                             ResultLength);
-                    break;
-                 case MEMORY_AREA_SECTION_VIEW:
-                     Status = MmQuerySectionView(MemoryArea, Address, Info,
-                                                 ResultLength);
-                     break;
-                 case MEMORY_AREA_NO_ACCESS:
-                    Info->Type = MEM_PRIVATE;
-                     Info->State = MEM_RESERVE;
-                    Info->Protect = MemoryArea->Protect;
-                    Info->AllocationProtect = MemoryArea->Protect;
-                    Info->BaseAddress = MemoryArea->StartingAddress;
-                    Info->AllocationBase = MemoryArea->StartingAddress;
-                    Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
-                                       (ULONG_PTR)MemoryArea->StartingAddress;
-                     Status = STATUS_SUCCESS;
-                     *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
-                    break;
-                 case MEMORY_AREA_SHARED_DATA:
-                    Info->Type = MEM_PRIVATE;
-                     Info->State = MEM_COMMIT;
-                    Info->Protect = MemoryArea->Protect;
-                    Info->AllocationProtect = MemoryArea->Protect;
-                    Info->BaseAddress = MemoryArea->StartingAddress;
-                    Info->AllocationBase = MemoryArea->StartingAddress;
-                    Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
-                                       (ULONG_PTR)MemoryArea->StartingAddress;
-                     Status = STATUS_SUCCESS;
-                     *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
-                    break;
-                 case MEMORY_AREA_SYSTEM:
-                    Info->Type = 0;
-                     Info->State = MEM_COMMIT;
-                    Info->Protect = MemoryArea->Protect;
-                    Info->AllocationProtect = MemoryArea->Protect;
-                    Info->BaseAddress = MemoryArea->StartingAddress;
-                    Info->AllocationBase = MemoryArea->StartingAddress;
-                    Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
-                                       (ULONG_PTR)MemoryArea->StartingAddress;
-                     Status = STATUS_SUCCESS;
-                     *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
-                    break;
-                 case MEMORY_AREA_KERNEL_STACK:
-                    Info->Type = 0;
-                     Info->State = MEM_COMMIT;
-                    Info->Protect = MemoryArea->Protect;
-                    Info->AllocationProtect = MemoryArea->Protect;
-                    Info->BaseAddress = MemoryArea->StartingAddress;
-                    Info->AllocationBase = MemoryArea->StartingAddress;
-                    Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
-                                       (ULONG_PTR)MemoryArea->StartingAddress;
-                     Status = STATUS_SUCCESS;
-                     *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
-                    break;
-                  case MEMORY_AREA_PAGED_POOL:
-                    Info->Type = 0;
-                     Info->State = MEM_COMMIT;
-                    Info->Protect = MemoryArea->Protect;
-                    Info->AllocationProtect = MemoryArea->Protect;
-                    Info->BaseAddress = MemoryArea->StartingAddress;
-                    Info->AllocationBase = MemoryArea->StartingAddress;
-                    Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
-                                       (ULONG_PTR)MemoryArea->StartingAddress;
-                     Status = STATUS_SUCCESS;
-                     *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
-                    break;
-                 default:
-                    DPRINT1("unhandled memory area type: 0x%x\n", MemoryArea->Type);
-                    Status = STATUS_UNSUCCESSFUL;
-                     *ResultLength = 0;
-              }
-           }
-            break;
-         }
+                /* Catch a failure here */
+                FailedInProbe = TRUE;
 
-      default:
-         {
-            Status = STATUS_INVALID_INFO_CLASS;
-            *ResultLength = 0;
-            break;
-         }
-   }
+                /* 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);
 
-   MmUnlockAddressSpace(AddressSpace);
-   if (Address < MmSystemRangeStart)
-   {
-      ObDereferenceObject(Process);
-   }
+            /* 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;
 
-   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;
 }
 
-/* (tMk 2004.II.4)
- * FUNCTION:
- * Called from VirtualQueryEx (lib\kernel32\mem\virtual.c)
- *
- */
-NTSTATUS STDCALL
-NtQueryVirtualMemory (IN HANDLE ProcessHandle,
-                      IN PVOID Address,
-                      IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
-                      OUT PVOID VirtualMemoryInformation,
-                      IN ULONG Length,
-                      OUT PULONG UnsafeResultLength)
+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)
 {
-   NTSTATUS Status = STATUS_SUCCESS;
-   ULONG ResultLength = 0;
-   KPROCESSOR_MODE PreviousMode;
-   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 && UnsafeResultLength != NULL)
-     {
-       _SEH_TRY
-         {
-           ProbeForWriteUlong(UnsafeResultLength);
-         }
-       _SEH_HANDLE
-         {
-           Status = _SEH_GetExceptionCode();
-         }
-       _SEH_END;
-       
-       if (!NT_SUCCESS(Status))
-         {
-           return Status;
-         }
-     }
-
-   if (Address >= MmSystemRangeStart)
-   {
-      DPRINT1("Invalid parameter\n");
-      return STATUS_INVALID_PARAMETER;
-   }
-
-   Status = MiQueryVirtualMemory ( ProcessHandle,
-       Address,
-       VirtualMemoryInformationClass,
-       &VirtualMemoryInfo,
-       Length,
-       &ResultLength );
-
-   if (NT_SUCCESS(Status))
-   {
-      if (PreviousMode != KernelMode)
+    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, TAG_VM);
+        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
         {
-          _SEH_TRY
+            /* If this is our first time, probe the buffer */
+            if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
             {
-              if (ResultLength > 0)
-                {
-                  ProbeForWrite(VirtualMemoryInformation,
-                                ResultLength,
-                                1);
-                  RtlCopyMemory(VirtualMemoryInformation,
-                                &VirtualMemoryInfo,
-                                ResultLength);
-                }
-              if (UnsafeResultLength != NULL)
-                {
-                  *UnsafeResultLength = ResultLength;
-                }
+                /* Catch a failure here */
+                FailedInProbe = TRUE;
+
+                /* Do the probe */
+                ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
+
+                /* Passed */
+                FailedInProbe = FALSE;
             }
-          _SEH_HANDLE
+
+            /* 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))
             {
-              Status = _SEH_GetExceptionCode();
+                /* Catch a failure here */
+                FailedInProbe = TRUE;
+
+                /* Do the probe */
+                ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
+
+                /* Passed */
+                FailedInProbe = FALSE;
             }
-          _SEH_END;
+
+            /* Now do the actual move */
+            FailedInMoving = TRUE;
+            RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
         }
-      else
+        _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), &HaveBadAddress, &BadAddress))
         {
-          if (ResultLength > 0)
+            /* 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)
             {
-              RtlCopyMemory(VirtualMemoryInformation,
-                            &VirtualMemoryInfo,
-                            ResultLength);
+                /* Exit */
+                Status = _SEH2_GetExceptionCode();
+                _SEH2_YIELD(return Status);
             }
 
-          if (UnsafeResultLength != NULL)
+            /* Otherwise, we failed  probably during the move */
+            *ReturnSize = BufferSize - RemainingSize;
+            if (FailedInMoving)
             {
-              *UnsafeResultLength = ResultLength;
+                /* 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;
 
-   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 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;
+    PMM_AVL_TABLE 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->VadRoot;
+
+    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:
+                    case MEMORY_AREA_PEB_OR_TEB:
+                        Status = MmQueryAnonMem(MemoryArea, Address, Info,
+                                                ResultLength);
+                        break;
+
+                    case MEMORY_AREA_SECTION_VIEW:
+                        Status = MmQuerySectionView(MemoryArea, Address, Info,
+                                                    ResultLength);
+                        break;
+
+                    case MEMORY_AREA_NO_ACCESS:
+                        Info->Type = MEM_PRIVATE;
+                        Info->State = MEM_RESERVE;
+                        Info->Protect = MemoryArea->Protect;
+                        Info->AllocationProtect = MemoryArea->Protect;
+                        Info->BaseAddress = MemoryArea->StartingAddress;
+                        Info->AllocationBase = MemoryArea->StartingAddress;
+                        Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
+                                           (ULONG_PTR)MemoryArea->StartingAddress;
+                        Status = STATUS_SUCCESS;
+                        *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
+                        break;
+
+                    case MEMORY_AREA_SHARED_DATA:
+                        Info->Type = MEM_PRIVATE;
+                        Info->State = MEM_COMMIT;
+                        Info->Protect = MemoryArea->Protect;
+                        Info->AllocationProtect = MemoryArea->Protect;
+                        Info->BaseAddress = MemoryArea->StartingAddress;
+                        Info->AllocationBase = MemoryArea->StartingAddress;
+                        Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
+                                           (ULONG_PTR)MemoryArea->StartingAddress;
+                        Status = STATUS_SUCCESS;
+                        *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
+                        break;
+
+                    case MEMORY_AREA_SYSTEM:
+                        Info->Type = 0;
+                        Info->State = MEM_COMMIT;
+                        Info->Protect = MemoryArea->Protect;
+                        Info->AllocationProtect = MemoryArea->Protect;
+                        Info->BaseAddress = MemoryArea->StartingAddress;
+                        Info->AllocationBase = MemoryArea->StartingAddress;
+                        Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
+                                           (ULONG_PTR)MemoryArea->StartingAddress;
+                        Status = STATUS_SUCCESS;
+                        *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
+                        break;
+
+                    case MEMORY_AREA_KERNEL_STACK:
+                        Info->Type = 0;
+                        Info->State = MEM_COMMIT;
+                        Info->Protect = MemoryArea->Protect;
+                        Info->AllocationProtect = MemoryArea->Protect;
+                        Info->BaseAddress = MemoryArea->StartingAddress;
+                        Info->AllocationBase = MemoryArea->StartingAddress;
+                        Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
+                                           (ULONG_PTR)MemoryArea->StartingAddress;
+                        Status = STATUS_SUCCESS;
+                        *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
+                        break;
+
+                    case MEMORY_AREA_PAGED_POOL:
+                        Info->Type = 0;
+                        Info->State = MEM_COMMIT;
+                        Info->Protect = MemoryArea->Protect;
+                        Info->AllocationProtect = MemoryArea->Protect;
+                        Info->BaseAddress = MemoryArea->StartingAddress;
+                        Info->AllocationBase = MemoryArea->StartingAddress;
+                        Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
+                                           (ULONG_PTR)MemoryArea->StartingAddress;
+                        Status = STATUS_SUCCESS;
+                        *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
+                        break;
+
+                    default:
+                        DPRINT1("unhandled memory area type: 0x%x\n", MemoryArea->Type);
+                        Status = STATUS_UNSUCCESSFUL;
+                        *ResultLength = 0;
+                }
+            }
+            break;
+        }
+
+        default:
+        {
+            Status = STATUS_INVALID_INFO_CLASS;
+            *ResultLength = 0;
+            break;
+        }
+    }
+
+    MmUnlockAddressSpace(AddressSpace);
+    ObDereferenceObject(Process);
+
+    return Status;
+}
 
-NTSTATUS STDCALL
+NTSTATUS NTAPI
 MiProtectVirtualMemory(IN PEPROCESS Process,
                        IN OUT PVOID *BaseAddress,
-                       IN OUT PULONG NumberOfBytesToProtect,
+                       IN OUT PSIZE_T NumberOfBytesToProtect,
                        IN ULONG NewAccessProtection,
                        OUT PULONG OldAccessProtection  OPTIONAL)
 {
-   PMEMORY_AREA MemoryArea;
-   PMADDRESS_SPACE AddressSpace;
-   ULONG OldAccessProtection_;
-   NTSTATUS Status;
-
-   *NumberOfBytesToProtect =
-      PAGE_ROUND_UP((*BaseAddress) + (*NumberOfBytesToProtect)) -
-      PAGE_ROUND_DOWN(*BaseAddress);
-   *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
-
-   AddressSpace = (PMADDRESS_SPACE)&(Process)->VadRoot;
-
-   MmLockAddressSpace(AddressSpace);
-   MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
-   if (MemoryArea == NULL)
-   {
-      MmUnlockAddressSpace(AddressSpace);
-      return STATUS_UNSUCCESSFUL;
-   }
-
-   if (OldAccessProtection == NULL)
-      OldAccessProtection = &OldAccessProtection_;
-
-   if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
-   {
-      Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
-                                *NumberOfBytesToProtect, NewAccessProtection,
-                                OldAccessProtection);
-   }
-   else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
-   {
-      Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
-                                    *NumberOfBytesToProtect,
-                                    NewAccessProtection,
-                                    OldAccessProtection);
-   }
-   else
-   {
-      /* FIXME: Should we return failure or success in this case? */
-      Status = STATUS_CONFLICTING_ADDRESSES;
-   }
+    PMEMORY_AREA MemoryArea;
+    PMM_AVL_TABLE AddressSpace;
+    ULONG OldAccessProtection_;
+    NTSTATUS Status;
+
+    *NumberOfBytesToProtect =
+    PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) -
+    PAGE_ROUND_DOWN(*BaseAddress);
+    *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
 
-   MmUnlockAddressSpace(AddressSpace);
+    AddressSpace = &Process->VadRoot;
 
-   return Status;
+    MmLockAddressSpace(AddressSpace);
+    MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
+    if (MemoryArea == NULL)
+    {
+        MmUnlockAddressSpace(AddressSpace);
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    if (OldAccessProtection == NULL)
+        OldAccessProtection = &OldAccessProtection_;
+
+    if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
+    {
+        Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
+                                  *NumberOfBytesToProtect, NewAccessProtection,
+                                  OldAccessProtection);
+    }
+    else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
+    {
+        Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
+                                      *NumberOfBytesToProtect,
+                                      NewAccessProtection,
+                                      OldAccessProtection);
+    }
+    else
+    {
+        /* FIXME: Should we return failure or success in this case? */
+        Status = STATUS_CONFLICTING_ADDRESSES;
+    }
+
+    MmUnlockAddressSpace(AddressSpace);
+
+    return Status;
 }
 
+/* PUBLIC FUNCTIONS ***********************************************************/
 
-/* (tMk 2004.II.5)
- * FUNCTION:
- * Called from VirtualProtectEx (lib\kernel32\mem\virtual.c)
- *
+/*
+ * @unimplemented
  */
-NTSTATUS STDCALL
-NtProtectVirtualMemory(IN HANDLE ProcessHandle,
-                       IN OUT PVOID *UnsafeBaseAddress,
-                       IN OUT ULONG *UnsafeNumberOfBytesToProtect,
-                       IN ULONG NewAccessProtection,
-                       OUT PULONG UnsafeOldAccessProtection)
+PVOID
+NTAPI
+MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
 {
-   PEPROCESS Process;
-   ULONG OldAccessProtection;
-   PVOID BaseAddress = NULL;
-   ULONG NumberOfBytesToProtect = 0;
-   KPROCESSOR_MODE PreviousMode;
-   NTSTATUS Status = STATUS_SUCCESS;
-   
-   PreviousMode = ExGetPreviousMode();
-   
-   if (PreviousMode != KernelMode)
-     {
-       _SEH_TRY
-         {
-           ProbeForWritePointer(UnsafeBaseAddress);
-           ProbeForWriteUlong(UnsafeNumberOfBytesToProtect);
-           ProbeForWriteUlong(UnsafeOldAccessProtection);
-
-           BaseAddress = *UnsafeBaseAddress;
-           NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
-         }
-       _SEH_HANDLE
-         {
-           Status = _SEH_GetExceptionCode();
-         }
-       _SEH_END;
-       
-       if (!NT_SUCCESS(Status))
-         {
-           return Status;
-         }
-     }
-   else
-     {
-       BaseAddress = *UnsafeBaseAddress;
-       NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
-     }
-
-   if ((ULONG_PTR)BaseAddress + NumberOfBytesToProtect - 1 < (ULONG_PTR)BaseAddress ||
-       (ULONG_PTR)BaseAddress + NumberOfBytesToProtect - 1 >= MmUserProbeAddress)
-     {
-       /* Don't allow to change the protection of a kernel mode address */
-       return STATUS_INVALID_PARAMETER_2;
-     }
-
-   /* (tMk 2004.II.5) in Microsoft SDK I read:
-    * 'if this parameter is NULL or does not point to a valid variable, the function fails'
-    */
-   if(UnsafeOldAccessProtection == NULL)
-   {
-      return(STATUS_INVALID_PARAMETER);
-   }
-
-   Status = ObReferenceObjectByHandle(ProcessHandle,
-                                      PROCESS_VM_OPERATION,
-                                      PsProcessType,
-                                      UserMode,
-                                      (PVOID*)(&Process),
-                                      NULL);
-   if (!NT_SUCCESS(Status))
-   {
-      DPRINT("NtProtectVirtualMemory() = %x\n",Status);
-      return(Status);
-   }
-
-   Status = MiProtectVirtualMemory(Process,
-                                   &BaseAddress,
-                                   &NumberOfBytesToProtect,
-                                   NewAccessProtection,
-                                   &OldAccessProtection);
-
-   ObDereferenceObject(Process);
-
-   if (PreviousMode != KernelMode)
-     {
-       _SEH_TRY
-         {
-           *UnsafeOldAccessProtection = OldAccessProtection;
-           *UnsafeBaseAddress = BaseAddress;
-           *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
-         }
-       _SEH_HANDLE
-         {
-           Status = _SEH_GetExceptionCode();
-         }
-       _SEH_END;
-     }
-   else
-     {
-       *UnsafeOldAccessProtection = OldAccessProtection;
-       *UnsafeBaseAddress = BaseAddress;
-       *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
-     }
-
-   return(Status);
+    UNIMPLEMENTED;
+    return 0;
 }
 
+/*
+ * @unimplemented
+ */
+PVOID
+NTAPI
+MmSecureVirtualMemory(IN PVOID Address,
+                      IN SIZE_T Length,
+                      IN ULONG Mode)
+{
+    UNIMPLEMENTED;
+    return NULL;
+}
 
-/* (tMk 2004.II.05)
- * FUNCTION:
- * Called from ReadProcessMemory (lib\kernel32\mem\procmem.c) and KlInitPeb(lib\kernel32\process\create.c)
- *
- * NOTE: This function will be correct if MmProbeAndLockPages() would be fully IMPLEMENTED.
+/*
+ * @unimplemented
  */
-NTSTATUS STDCALL
+VOID
+NTAPI
+MmUnsecureVirtualMemory(IN PVOID SecureMem)
+{
+    UNIMPLEMENTED;
+}
+
+/* SYSTEM CALLS ***************************************************************/
+
+NTSTATUS
+NTAPI
 NtReadVirtualMemory(IN HANDLE ProcessHandle,
                     IN PVOID BaseAddress,
                     OUT PVOID Buffer,
-                    IN ULONG NumberOfBytesToRead,
-                    OUT PULONG NumberOfBytesRead OPTIONAL)
+                    IN SIZE_T NumberOfBytesToRead,
+                    OUT PSIZE_T NumberOfBytesRead OPTIONAL)
 {
-   PMDL Mdl;
-   PVOID SystemAddress;
-   KPROCESSOR_MODE PreviousMode;
-   PEPROCESS Process, CurrentProcess;
-   NTSTATUS Status = STATUS_SUCCESS;
-
-   PAGED_CODE();
-
-   DPRINT("NtReadVirtualMemory(ProcessHandle %x, BaseAddress %x, "
-          "Buffer %x, NumberOfBytesToRead %d)\n",ProcessHandle,BaseAddress,
-          Buffer,NumberOfBytesToRead);
-
-   if ((ULONG_PTR)BaseAddress + NumberOfBytesToRead - 1 < (ULONG_PTR)BaseAddress ||
-       (ULONG_PTR)BaseAddress + NumberOfBytesToRead - 1 >= MmUserProbeAddress)
-     {
-       /* Don't allow to read from kernel space */
-       return STATUS_ACCESS_VIOLATION;
-     }
-
-   PreviousMode = ExGetPreviousMode();
-
-   if (PreviousMode != KernelMode)
-     {
-       if ((ULONG_PTR)Buffer + NumberOfBytesToRead - 1 < (ULONG_PTR)Buffer ||
-           (ULONG_PTR)Buffer + NumberOfBytesToRead - 1 >= MmUserProbeAddress)
-         {
-           /* Don't allow to write into kernel space */
-           return STATUS_ACCESS_VIOLATION;
-         }
-     }
-
-   Status = ObReferenceObjectByHandle(ProcessHandle,
-                                      PROCESS_VM_READ,
-                                      NULL,
-                                      PreviousMode,
-                                      (PVOID*)(&Process),
-                                      NULL);
-   if (!NT_SUCCESS(Status))
-   {
-      return(Status);
-   }
-
-   CurrentProcess = PsGetCurrentProcess();
-
-   if(PreviousMode != KernelMode)
-   {
-     _SEH_TRY
-     {
-       if(NumberOfBytesRead != NULL)
-       {
-         ProbeForWriteUlong(NumberOfBytesRead);
-       }
-     }
-     _SEH_HANDLE
-     {
-       Status = _SEH_GetExceptionCode();
-     }
-     _SEH_END;
-
-     if(!NT_SUCCESS(Status))
-     {
-       return Status;
-     }
-   }
-
-
-   if (Process == CurrentProcess)
-   {
-      _SEH_TRY
-      {
-        RtlCopyMemory(Buffer, BaseAddress, NumberOfBytesToRead);
-      }
-      _SEH_HANDLE
-      {
-        Status = _SEH_GetExceptionCode();
-      }
-      _SEH_END;
-   }
-   else
-   {
-      Mdl = MmCreateMdl(NULL,
-                        Buffer,
-                        NumberOfBytesToRead);
-      if(Mdl == NULL)
-      {
-         ObDereferenceObject(Process);
-         return(STATUS_NO_MEMORY);
-      }
-      _SEH_TRY
-      {
-        MmProbeAndLockPages(Mdl,
-                            PreviousMode,
-                            IoWriteAccess);
-      }
-      _SEH_HANDLE
-      {
-        Status = _SEH_GetExceptionCode();
-      }
-      _SEH_END;
-
-      if(NT_SUCCESS(Status))
-      {
-        KeAttachProcess(&Process->Pcb);
-
-        SystemAddress = MmGetSystemAddressForMdl(Mdl);
-
-          Status = STATUS_SUCCESS;
-          _SEH_TRY {
-              Status = STATUS_PARTIAL_COPY;
-              RtlCopyMemory(SystemAddress, BaseAddress, NumberOfBytesToRead);
-              Status = STATUS_SUCCESS;
-          } _SEH_HANDLE {
-              if(Status != STATUS_PARTIAL_COPY)
-                  Status = _SEH_GetExceptionCode();
-          } _SEH_END;
-
-        KeDetachProcess();
-
-        if (Mdl->MappedSystemVa != NULL)
+    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))
         {
-           MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
+            /* Don't allow to write into kernel space */
+            return STATUS_ACCESS_VIOLATION;
         }
-        MmUnlockPages(Mdl);
-      }
-      ExFreePool(Mdl);
-   }
-
-   ObDereferenceObject(Process);
-
-   if((NT_SUCCESS(Status) || Status == STATUS_PARTIAL_COPY) &&
-      NumberOfBytesRead != NULL)
-   {
-     _SEH_TRY
-     {
-       *NumberOfBytesRead = NumberOfBytesToRead;
-     }
-     _SEH_HANDLE
-     {
-       Status = _SEH_GetExceptionCode();
-     }
-     _SEH_END;
-   }
-
-   return(Status);
-}
 
-/* (tMk 2004.II.05)
- * FUNCTION:  THIS function doesn't make a sense...
- * Called from VirtualUnlock (lib\kernel32\mem\virtual.c)
- */
-NTSTATUS STDCALL
-NtUnlockVirtualMemory(HANDLE ProcessHandle,
-                      PVOID BaseAddress,
-                      ULONG NumberOfBytesToUnlock,
-                      PULONG NumberOfBytesUnlocked OPTIONAL)
-{
-   // AG [08-20-03] : I have *no* idea if this is correct, I just used the
-   // other functions as a template and made a few intelligent guesses...
-
-   NTSTATUS Status;
-   PMDL Mdl;
-   PEPROCESS Process;
-
-   DPRINT("NtUnlockVirtualMemory(ProcessHandle %x, BaseAddress %x, "
-          "NumberOfBytesToUnlock %d), NumberOfBytesUnlocked %x\n",ProcessHandle,BaseAddress,
-          NumberOfBytesToUnlock, NumberOfBytesUnlocked);
-
-   Status = ObReferenceObjectByHandle(ProcessHandle,
-                                      PROCESS_VM_WRITE,
-                                      NULL,
-                                      UserMode,
-                                      (PVOID*)(&Process),
-                                      NULL);
-   if (!NT_SUCCESS(Status))
-   {
-      return(Status);
-   }
-
-   Mdl = MmCreateMdl(NULL,
-                     BaseAddress,
-                     NumberOfBytesToUnlock);
-   if(Mdl == NULL)
-   {
-      ObDereferenceObject(Process);
-      return(STATUS_NO_MEMORY);
-   }
-
-   ObDereferenceObject(Process);
-
-   if (Mdl->MappedSystemVa != NULL)
-   {
-      MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
-   }
-   MmUnlockPages(Mdl);
-   ExFreePool(Mdl);
-
-   *NumberOfBytesUnlocked = NumberOfBytesToUnlock;
-
-   return(STATUS_SUCCESS);
-}
+        /* Enter SEH for probe */
+        _SEH2_TRY
+        {
+            /* Probe the output value */
+            if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            /* Get exception code */
+            Status = _SEH2_GetExceptionCode();
+        }
+        _SEH2_END;
 
+        /* Return if we failed */
+        if (!NT_SUCCESS(Status)) return Status;
+    }
 
-/* (tMk 2004.II.05)
- * FUNCTION:
- * Called from WriteProcessMemory (lib\kernel32\mem\procmem.c) and KlInitPeb(lib\kernel32\process\create.c)
- *
- * NOTE: This function will be correct if MmProbeAndLockPages() would be fully IMPLEMENTED.
- */
-NTSTATUS STDCALL
+    /* 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)
+        {
+            /* Handle exception */
+            Status = _SEH2_GetExceptionCode();
+        }
+        _SEH2_END;
+    }
+
+    /* Return status */
+    return Status;
+}
+
+NTSTATUS
+NTAPI
 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
                      IN PVOID BaseAddress,
                      IN PVOID Buffer,
-                     IN ULONG NumberOfBytesToWrite,
-                     OUT PULONG NumberOfBytesWritten  OPTIONAL)
+                     IN SIZE_T NumberOfBytesToWrite,
+                     OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
 {
-   PMDL Mdl;
-   PVOID SystemAddress;
-   PEPROCESS Process;
-   KPROCESSOR_MODE PreviousMode;
-   NTSTATUS CopyStatus, Status = STATUS_SUCCESS;
-
-   DPRINT("NtWriteVirtualMemory(ProcessHandle %x, BaseAddress %x, "
-          "Buffer %x, NumberOfBytesToWrite %d)\n",ProcessHandle,BaseAddress,
-          Buffer,NumberOfBytesToWrite);
-
-   if ((ULONG_PTR)BaseAddress + NumberOfBytesToWrite - 1 < (ULONG_PTR)BaseAddress ||
-       (ULONG_PTR)BaseAddress + NumberOfBytesToWrite - 1 >= MmUserProbeAddress)
-     {
-       /* Don't allow to write into kernel space */
-       return STATUS_ACCESS_VIOLATION;
-     }
-
-   PreviousMode = ExGetPreviousMode();
-   
-   if (PreviousMode != KernelMode)
-     {
-       if ((ULONG_PTR)Buffer + NumberOfBytesToWrite - 1 < (ULONG_PTR)Buffer ||
-           (ULONG_PTR)Buffer + NumberOfBytesToWrite - 1 >= MmUserProbeAddress)
-         {
-           /* Don't allow to read from kernel space */
-           return STATUS_ACCESS_VIOLATION;
-         }
-       if (NumberOfBytesWritten != NULL)
-         {
-           _SEH_TRY
-             {
-               ProbeForWriteUlong(NumberOfBytesWritten);
-             }
-           _SEH_HANDLE
-             {
-               Status = _SEH_GetExceptionCode();
-             }
-           _SEH_END;
-       
-           if (!NT_SUCCESS(Status))
-             {
-               return Status;
-             }
-          }
-     }
-
-   Status = ObReferenceObjectByHandle(ProcessHandle,
-                                      PROCESS_VM_WRITE,
-                                      NULL,
-                                      UserMode,
-                                      (PVOID*)(&Process),
-                                      NULL);
-   if (!NT_SUCCESS(Status))
-   {
-      return(Status);
-   }
-
-   CopyStatus = STATUS_SUCCESS;
-
-   /* Write memory */
-   if (Process == PsGetCurrentProcess())
-   {
-      if (PreviousMode != KernelMode)
+    KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
+    PEPROCESS Process;
+    NTSTATUS Status = STATUS_SUCCESS;
+    SIZE_T BytesWritten = 0;
+    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))
         {
-         _SEH_TRY
-            {
-              memcpy(BaseAddress, Buffer, NumberOfBytesToWrite);
-            }
-          _SEH_HANDLE
-            {
-              CopyStatus = _SEH_GetExceptionCode();
-            }
-          _SEH_END;
+            /* Don't allow to write into kernel space */
+            return STATUS_ACCESS_VIOLATION;
         }
-      else
+
+        /* Enter SEH for probe */
+        _SEH2_TRY
         {
-          memcpy(BaseAddress, Buffer, NumberOfBytesToWrite);
+            /* Probe the output value */
+            if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
         }
-   }
-   else
-   {
-      /* Create MDL describing the source buffer. */
-      Mdl = MmCreateMdl(NULL,
-                        Buffer,
-                        NumberOfBytesToWrite);
-      if(Mdl == NULL)
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
-          ObDereferenceObject(Process);
-          return(STATUS_NO_MEMORY);
+            /* Get exception code */
+            Status = _SEH2_GetExceptionCode();
         }
-      _SEH_TRY
+        _SEH2_END;
+
+        /* Return if we failed */
+        if (!NT_SUCCESS(Status)) return Status;
+    }
+
+    /* 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
         {
-          /* Map the MDL. */
-          MmProbeAndLockPages(Mdl,
-                              UserMode,
-                              IoReadAccess);
-       }
-      _SEH_HANDLE
+            /* Return the number of bytes read */
+            *NumberOfBytesWritten = BytesWritten;
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
-         CopyStatus = _SEH_GetExceptionCode();
+            /* Handle exception */
+            Status = _SEH2_GetExceptionCode();
         }
-      _SEH_END;
+        _SEH2_END;
+    }
+
+    /* Return status */
+    return Status;
+}
 
-      if (NT_SUCCESS(CopyStatus))
+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;
+    PVOID BaseAddress = NULL;
+    SIZE_T NumberOfBytesToProtect = 0;
+    KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    /* 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)
         {
-          /* Copy memory from the mapped MDL into the target buffer. */
-          KeAttachProcess(&Process->Pcb);
+            /* Get exception code */
+            Status = _SEH2_GetExceptionCode();
+        }
+        _SEH2_END;
 
-          SystemAddress = MmGetSystemAddressForMdl(Mdl);
-          if (PreviousMode != KernelMode)
-            {
-              _SEH_TRY
-                {
-                  memcpy(BaseAddress, SystemAddress, NumberOfBytesToWrite);
-                }
-              _SEH_HANDLE
-                {
-                  CopyStatus = _SEH_GetExceptionCode();
-                }
-              _SEH_END;
-            }
-          else
-            {
-              memcpy(BaseAddress, SystemAddress, NumberOfBytesToWrite);
-            }
+        /* Return on exception */
+        if (!NT_SUCCESS(Status)) return Status;
+    }
+    else
+    {
+        /* Capture directly */
+        BaseAddress = *UnsafeBaseAddress;
+        NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
+    }
 
-          KeDetachProcess();
-       }
-
-      /* Free the MDL. */
-      if (Mdl->MappedSystemVa != NULL)
-      {
-         MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
-      }
-      MmUnlockPages(Mdl);
-      ExFreePool(Mdl);
-   }
-   ObDereferenceObject(Process);
-
-   if (NT_SUCCESS(CopyStatus) && NumberOfBytesWritten != NULL)
-     {
-       if (PreviousMode != KernelMode)
-         {
-           _SEH_TRY
-             {
-               *NumberOfBytesWritten = NumberOfBytesToWrite;
-             }
-           _SEH_HANDLE
-             {
-               Status = _SEH_GetExceptionCode();
-             }
-           _SEH_END;
-         }
-       else
-         {
-           *NumberOfBytesWritten = NumberOfBytesToWrite;
-         }
-     }
-
-   return(NT_SUCCESS(CopyStatus) ? Status : CopyStatus);
-}
+    /* Catch illegal base address */
+    if (BaseAddress > (PVOID)MmUserProbeAddress) return STATUS_INVALID_PARAMETER_2;
 
-/*
- * @unimplemented
- */
+    /* Catch illegal region size  */
+    if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
+    {
+        /* Fail */
+        return STATUS_INVALID_PARAMETER_3;
+    }
 
-PVOID
-STDCALL
-MmGetVirtualForPhysical (
-    IN PHYSICAL_ADDRESS PhysicalAddress
-    )
-{
-       UNIMPLEMENTED;
-       return 0;
-}
+    /* 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;
+
+    /* Do the actual work */
+    Status = MiProtectVirtualMemory(Process,
+                                    &BaseAddress,
+                                    &NumberOfBytesToProtect,
+                                    NewAccessProtection,
+                                    &OldAccessProtection);
 
-/* FUNCTION:
- * Called from EngSecureMem (subsys\win32k\eng\mem.c)
- * @unimplemented
- */
-PVOID STDCALL
-MmSecureVirtualMemory (PVOID  Address,
-                       SIZE_T Length,
-                       ULONG  Mode)
-{
-   /* Only works for user space */
-   if (MmHighestUserAddress < Address)
-   {
-      return NULL;
-   }
+    /* Release reference */
+    ObDereferenceObject(Process);
 
-   UNIMPLEMENTED;
+    /* Enter SEH to return data */
+    _SEH2_TRY
+    {
+        /* Return data to user */
+        *UnsafeOldAccessProtection = OldAccessProtection;
+        *UnsafeBaseAddress = BaseAddress;
+        *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        /* Catch exception */
+        Status = _SEH2_GetExceptionCode();
+    }
+    _SEH2_END;
 
-   return 0;
+    /* Return status */
+    return Status;
 }
 
-
-/* FUNCTION:
- * Called from EngUnsecureMem (subsys\win32k\eng\mem.c)
- * @unimplemented
- */
-VOID STDCALL
-MmUnsecureVirtualMemory(PVOID SecureMem)
+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)
 {
-   if (NULL == SecureMem)
-   {
-      return;
-   }
+    NTSTATUS Status = STATUS_SUCCESS;
+    SIZE_T ResultLength = 0;
+    KPROCESSOR_MODE PreviousMode;
+    WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
+    UNICODE_STRING ModuleFileName;
+    PMEMORY_SECTION_NAME SectionName = NULL;
+    union
+    {
+        MEMORY_BASIC_INFORMATION BasicInfo;
+    }
+    VirtualMemoryInfo;
 
-   UNIMPLEMENTED;
-}
+    DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
+           "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
+           "Length %lu ResultLength %x)\n",ProcessHandle,Address,
+           VirtualMemoryInformationClass,VirtualMemoryInformation,
+           Length,ResultLength);
 
+    PreviousMode =  ExGetPreviousMode();
 
-/*
- * @implemented
- */
-VOID STDCALL
-ProbeForRead (IN CONST VOID *Address,
-              IN ULONG Length,
-              IN ULONG Alignment)
-{
-    if (Length != 0)
+    if (PreviousMode != KernelMode && UnsafeResultLength != NULL)
     {
-        ASSERT(Alignment == 1 || Alignment == 2 || Alignment == 4 || Alignment == 8);
-
-        if (((ULONG_PTR)Address & (Alignment - 1)) != 0)
+        _SEH2_TRY
         {
-            ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT);
+            ProbeForWriteSize_t(UnsafeResultLength);
         }
-        else if ((ULONG_PTR)Address + Length - 1 < (ULONG_PTR)Address ||
-                 (ULONG_PTR)Address + Length - 1 >= (ULONG_PTR)MmUserProbeAddress)
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
-            ExRaiseStatus (STATUS_ACCESS_VIOLATION);
+            Status = _SEH2_GetExceptionCode();
         }
-    }
-}
+        _SEH2_END;
 
+        if (!NT_SUCCESS(Status))
+        {
+            return Status;
+        }
+    }
 
-/*
- * @implemented
- */
-VOID STDCALL
-ProbeForWrite (IN PVOID Address,
-               IN ULONG Length,
-               IN ULONG Alignment)
-{
-    volatile CHAR *Current;
-    PCHAR Last;
+    if (Address >= MmSystemRangeStart)
+    {
+        DPRINT1("Invalid parameter\n");
+        return STATUS_INVALID_PARAMETER;
+    }
 
-    if (Length != 0)
+    /* FIXME: Move this inside MiQueryVirtualMemory */
+    if (VirtualMemoryInformationClass == MemorySectionName)
     {
-        ASSERT(Alignment == 1 || Alignment == 2 || Alignment == 4 || Alignment == 8);
+        RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
+        Status = MmGetFileNameForAddress(Address, &ModuleFileName);
 
-        if (((ULONG_PTR)Address & (Alignment - 1)) != 0)
+        if (NT_SUCCESS(Status))
         {
-            ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT);
+            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;
+                }
+            }
         }
+        return Status;
+    }
+    else
+    {
+        Status = MiQueryVirtualMemory(ProcessHandle,
+                                      Address,
+                                      VirtualMemoryInformationClass,
+                                      &VirtualMemoryInfo,
+                                      Length,
+                                      &ResultLength);
+    }
 
-        Last = (PCHAR)((ULONG_PTR)Address + Length - 1);
-        if ((ULONG_PTR)Last < (ULONG_PTR)Address ||
-            (ULONG_PTR)Last >= (ULONG_PTR)MmUserProbeAddress)
+    if (NT_SUCCESS(Status))
+    {
+        if (PreviousMode != KernelMode)
         {
-            ExRaiseStatus (STATUS_ACCESS_VIOLATION);
+            _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;
         }
-
-        /* Check for accessible pages, do *not* touch any memory outside of the
-           range!*/
-        Current = (volatile CHAR*)Address;
-        Last = (PCHAR)(PAGE_ROUND_DOWN(Last));
-        do
+        else
         {
-            *Current = *Current;
-            Current = (volatile CHAR*)(PAGE_ROUND_DOWN(Current) + PAGE_SIZE);
-        } while (Current <= Last);
+            if (ResultLength > 0)
+            {
+                RtlCopyMemory(VirtualMemoryInformation,
+                              &VirtualMemoryInfo,
+                              ResultLength);
+            }
+
+            if (UnsafeResultLength != NULL)
+            {
+                *UnsafeResultLength = ResultLength;
+            }
+        }
     }
+
+    return(Status);
+}
+
+NTSTATUS
+NTAPI
+NtLockVirtualMemory(IN HANDLE ProcessHandle,
+                    IN PVOID BaseAddress,
+                    IN SIZE_T NumberOfBytesToLock,
+                    OUT PSIZE_T NumberOfBytesLocked OPTIONAL)
+{
+    UNIMPLEMENTED;
+    if (NumberOfBytesLocked) *NumberOfBytesLocked = 0;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
+                      IN PVOID BaseAddress,
+                      IN SIZE_T NumberOfBytesToUnlock,
+                      OUT PSIZE_T NumberOfBytesUnlocked OPTIONAL)
+{
+    UNIMPLEMENTED;
+    if (NumberOfBytesUnlocked) *NumberOfBytesUnlocked = 0;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+NtFlushVirtualMemory(IN HANDLE ProcessHandle,
+                     IN OUT PVOID *BaseAddress,
+                     IN OUT PSIZE_T NumberOfBytesToFlush,
+                     OUT PIO_STATUS_BLOCK IoStatusBlock)
+{
+    UNIMPLEMENTED;
+    return STATUS_SUCCESS;
 }
 
 /* EOF */