summaryrefslogtreecommitdiffstats
path: root/private/ntos/mm/readwrt.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/mm/readwrt.c')
-rw-r--r--private/ntos/mm/readwrt.c971
1 files changed, 971 insertions, 0 deletions
diff --git a/private/ntos/mm/readwrt.c b/private/ntos/mm/readwrt.c
new file mode 100644
index 000000000..31b02996a
--- /dev/null
+++ b/private/ntos/mm/readwrt.c
@@ -0,0 +1,971 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ readwrt.c
+
+Abstract:
+
+ This module contains the routines which implement the capability
+ to read and write the virtual memory of a target process.
+
+Author:
+
+ Lou Perazzoli (loup) 22-May-1989
+
+Revision History:
+
+--*/
+
+#include "mi.h"
+
+//
+// The maximum amount to try to Probe and Lock is 14 pages, this
+// way it always fits in a 16 page allocation.
+//
+
+#define MAX_LOCK_SIZE ((ULONG)(14 * PAGE_SIZE))
+
+//
+// The maximum to move in a single block is 64k bytes.
+//
+
+#define MAX_MOVE_SIZE (LONG)0x10000
+
+//
+// The minimum to move is a single block is 128 bytes.
+//
+
+#define MINIMUM_ALLOCATION (LONG)128
+
+//
+// Define the pool move threshold value.
+//
+
+#define POOL_MOVE_THRESHOLD 511
+
+//
+// Define foreward referenced procedure prototypes.
+//
+
+ULONG
+MiGetExceptionInfo (
+ IN PEXCEPTION_POINTERS ExceptionPointers,
+ IN PULONG BadVa
+ );
+
+NTSTATUS
+MiDoMappedCopy (
+ IN PEPROCESS FromProcess,
+ IN PVOID FromAddress,
+ IN PEPROCESS ToProcess,
+ OUT PVOID ToAddress,
+ IN ULONG BufferSize,
+ IN KPROCESSOR_MODE PreviousMode,
+ OUT PULONG NumberOfBytesRead
+ );
+
+NTSTATUS
+MiDoPoolCopy (
+ IN PEPROCESS FromProcess,
+ IN PVOID FromAddress,
+ IN PEPROCESS ToProcess,
+ OUT PVOID ToAddress,
+ IN ULONG BufferSize,
+ OUT PULONG NumberOfBytesRead
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,MiGetExceptionInfo)
+#pragma alloc_text(PAGE,NtReadVirtualMemory)
+#pragma alloc_text(PAGE,NtWriteVirtualMemory)
+#pragma alloc_text(PAGE,MiDoMappedCopy)
+#pragma alloc_text(PAGE,MiDoPoolCopy)
+#endif
+
+#define COPY_STACK_SIZE 64
+
+
+NTSTATUS
+NtReadVirtualMemory(
+ IN HANDLE ProcessHandle,
+ IN PVOID BaseAddress,
+ OUT PVOID Buffer,
+ IN ULONG BufferSize,
+ OUT PULONG NumberOfBytesRead OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function copies the specified address range from the specified
+ process into the specified address range of the current process.
+
+Arguments:
+
+ ProcessHandle - Supplies an open handle to a process object.
+
+ BaseAddress - Supplies the base address in the specified process
+ to be read.
+
+ Buffer - Supplies the address of a buffer which receives the
+ contents from the specified process address space.
+
+ BufferSize - Supplies the requested number of bytes to read from
+ the specified process.
+
+ NumberOfBytesRead - Receives the actual number of bytes
+ transferred into the specified buffer.
+
+Return Value:
+
+ TBS
+
+--*/
+
+{
+
+ ULONG BytesCopied;
+ KPROCESSOR_MODE PreviousMode;
+ PEPROCESS Process;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ //
+ // Get the previous mode and probe output argument if necessary.
+ //
+
+ PreviousMode = KeGetPreviousMode();
+ if (PreviousMode != KernelMode) {
+
+#ifdef MIPS
+
+ //
+ // Handle the PCR case for mips.
+ //
+
+ if (((ULONG)BaseAddress >= KSEG0_BASE) ||
+ (((ULONG)BaseAddress + BufferSize) > (ULONG)KSEG0_BASE) ||
+ (((ULONG)BaseAddress + BufferSize) < (ULONG)BaseAddress)) {
+ return STATUS_ACCESS_VIOLATION;
+ }
+ if (((ULONG)Buffer >= KSEG0_BASE) ||
+ (((ULONG)Buffer + BufferSize) > (ULONG)KSEG0_BASE) ||
+ (((ULONG)Buffer + BufferSize) < (ULONG)Buffer)) {
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+#elif defined(_PPC_)
+
+ //
+ // Handle the PCR case for PPC.
+ //
+
+ if (((ULONG)BaseAddress >= KIPCR) &&
+ ((ULONG)BaseAddress < (KIPCR2 + PAGE_SIZE)) &&
+ (((ULONG)BaseAddress + BufferSize) < (KIPCR2 + PAGE_SIZE)) &&
+ (((ULONG)BaseAddress + BufferSize) >= (ULONG)BaseAddress)) {
+ ;
+ } else if (BaseAddress > MM_HIGHEST_USER_ADDRESS) {
+ return STATUS_ACCESS_VIOLATION;
+ }
+ if (Buffer > MM_HIGHEST_USER_ADDRESS) {
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+#else
+
+ if ((BaseAddress > MM_HIGHEST_USER_ADDRESS) ||
+ (Buffer > MM_HIGHEST_USER_ADDRESS)) {
+ return STATUS_ACCESS_VIOLATION;
+ }
+#endif
+
+ if (ARGUMENT_PRESENT(NumberOfBytesRead)) {
+ try {
+ ProbeForWriteUlong(NumberOfBytesRead);
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+ }
+ }
+
+ //
+ // If the buffer size is not zero, then attempt to read data from the
+ // specified process address space into the current process address
+ // space.
+ //
+
+ BytesCopied = 0;
+ Status = STATUS_SUCCESS;
+ if (BufferSize != 0) {
+
+ //
+ // Reference the target process.
+ //
+
+ Status = ObReferenceObjectByHandle(ProcessHandle,
+ PROCESS_VM_READ,
+ PsProcessType,
+ PreviousMode,
+ (PVOID *)&Process,
+ NULL);
+
+ //
+ // If the process was successfully referenced, then attempt to
+ // read the specified memory either by direct mapping or copying
+ // through nonpaged pool.
+ //
+
+ if (Status == STATUS_SUCCESS) {
+
+ Status = MmCopyVirtualMemory(Process,
+ BaseAddress,
+ PsGetCurrentProcess(),
+ Buffer,
+ BufferSize,
+ PreviousMode,
+ &BytesCopied);
+
+ //
+ // Dereference the target process.
+ //
+
+ ObDereferenceObject(Process);
+ }
+ }
+
+ //
+ // If requested, return the number of bytes read.
+ //
+
+ if (ARGUMENT_PRESENT(NumberOfBytesRead)) {
+ try {
+ *NumberOfBytesRead = BytesCopied;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ NOTHING;
+ }
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NtWriteVirtualMemory(
+ IN HANDLE ProcessHandle,
+ OUT PVOID BaseAddress,
+ IN PVOID Buffer,
+ IN ULONG BufferSize,
+ OUT PULONG NumberOfBytesWritten OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function copies the specified address range from the current
+ process into the specified address range of the specified process.
+
+Arguments:
+
+ ProcessHandle - Supplies an open handle to a process object.
+
+ BaseAddress - Supplies the base address to be written to in the
+ specified process.
+
+ Buffer - Supplies the address of a buffer which contains the
+ contents to be written into the specified process
+ address space.
+
+ BufferSize - Supplies the requested number of bytes to write
+ into the specified process.
+
+ NumberOfBytesWritten - Receives the actual number of
+ bytes transferred into the specified address
+ space.
+
+Return Value:
+
+ TBS
+
+--*/
+
+{
+ ULONG BytesCopied;
+ KPROCESSOR_MODE PreviousMode;
+ PEPROCESS Process;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ //
+ // Get the previous mode and probe output argument if necessary.
+ //
+
+ PreviousMode = KeGetPreviousMode();
+ if (PreviousMode != KernelMode) {
+
+ if ((BaseAddress > MM_HIGHEST_USER_ADDRESS) ||
+ (Buffer > MM_HIGHEST_USER_ADDRESS)) {
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ if (ARGUMENT_PRESENT(NumberOfBytesWritten)) {
+ try {
+ ProbeForWriteUlong(NumberOfBytesWritten);
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+ }
+ }
+
+ //
+ // If the buffer size is not zero, then attempt to write data from the
+ // current process address space into the target process address space.
+ //
+
+ BytesCopied = 0;
+ Status = STATUS_SUCCESS;
+ if (BufferSize != 0) {
+
+ //
+ // Reference the target process.
+ //
+
+ Status = ObReferenceObjectByHandle(ProcessHandle,
+ PROCESS_VM_WRITE,
+ PsProcessType,
+ PreviousMode,
+ (PVOID *)&Process,
+ NULL);
+
+ //
+ // If the process was successfully referenced, then attempt to
+ // write the specified memory either by direct mapping or copying
+ // through nonpaged pool.
+ //
+
+ if (Status == STATUS_SUCCESS) {
+
+ Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
+ Buffer,
+ Process,
+ BaseAddress,
+ BufferSize,
+ PreviousMode,
+ &BytesCopied);
+
+ //
+ // Dereference the target process.
+ //
+
+ ObDereferenceObject(Process);
+ }
+ }
+
+ //
+ // If requested, return the number of bytes read.
+ //
+
+ if (ARGUMENT_PRESENT(NumberOfBytesWritten)) {
+ try {
+ *NumberOfBytesWritten = BytesCopied;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ NOTHING;
+ }
+ }
+
+ return Status;
+}
+
+
+
+NTSTATUS
+MmCopyVirtualMemory(
+ IN PEPROCESS FromProcess,
+ IN PVOID FromAddress,
+ IN PEPROCESS ToProcess,
+ OUT PVOID ToAddress,
+ IN ULONG BufferSize,
+ IN KPROCESSOR_MODE PreviousMode,
+ OUT PULONG NumberOfBytesCopied
+ )
+{
+ NTSTATUS Status;
+ KIRQL OldIrql;
+ PEPROCESS ProcessToLock;
+
+
+ ProcessToLock = FromProcess;
+ if (FromProcess == PsGetCurrentProcess()) {
+ ProcessToLock = ToProcess;
+ }
+
+ //
+ // Make sure the process still has an address space.
+ //
+
+ ExAcquireSpinLock (&MmSystemSpaceLock, &OldIrql);
+ if (ProcessToLock->AddressSpaceDeleted != 0) {
+ ExReleaseSpinLock ( &MmSystemSpaceLock, OldIrql );
+ return STATUS_PROCESS_IS_TERMINATING;
+ }
+ ProcessToLock->VmOperation += 1;
+ ExReleaseSpinLock ( &MmSystemSpaceLock, OldIrql );
+
+
+ //
+ // If the buffer size is greater than the pool move threshold,
+ // then attempt to write the memory via direct mapping.
+ //
+
+ if (BufferSize > POOL_MOVE_THRESHOLD) {
+ Status = MiDoMappedCopy(FromProcess,
+ FromAddress,
+ ToProcess,
+ ToAddress,
+ BufferSize,
+ PreviousMode,
+ NumberOfBytesCopied);
+
+ //
+ // If the completion status is not a working quota problem,
+ // then finish the service. Otherwise, attempt to write the
+ // memory through nonpaged pool.
+ //
+
+ if (Status != STATUS_WORKING_SET_QUOTA) {
+ goto CompleteService;
+ }
+
+ *NumberOfBytesCopied = 0;
+ }
+
+ //
+ // There was not enough working set quota to write the memory via
+ // direct mapping or the size of the write was below the pool move
+ // threshold. Attempt to write the specified memory through nonpaged
+ // pool.
+ //
+
+ Status = MiDoPoolCopy(FromProcess,
+ FromAddress,
+ ToProcess,
+ ToAddress,
+ BufferSize,
+ NumberOfBytesCopied);
+
+ //
+ // Dereference the target process.
+ //
+
+CompleteService:
+
+ //
+ // Indicate that the vm operation is complete.
+ //
+
+ ExAcquireSpinLock (&MmSystemSpaceLock, &OldIrql);
+ ProcessToLock->VmOperation -= 1;
+ if ((ProcessToLock->VmOperation == 0) &&
+ (ProcessToLock->VmOperationEvent != NULL)) {
+ KeSetEvent (ProcessToLock->VmOperationEvent, 0, FALSE);
+ }
+ ExReleaseSpinLock ( &MmSystemSpaceLock, OldIrql );
+
+ return Status;
+}
+
+
+ULONG
+MiGetExceptionInfo (
+ IN PEXCEPTION_POINTERS ExceptionPointers,
+ IN PULONG BadVa
+ )
+
+/*++
+
+Routine Description:
+
+ This routine examines a exception record and extracts the virtual
+ address of an access violation, guard page violation, or in-page error.
+
+Arguments:
+
+ ExceptionPointers - Supplies a pointer to the exception record.
+
+ BadVa - Receives the virtual address which caused the access violation.
+
+Return Value:
+
+ EXECUTE_EXCEPTION_HANDLER
+
+--*/
+
+{
+ PEXCEPTION_RECORD ExceptionRecord;
+
+ PAGED_CODE();
+
+ //
+ // If the exception code is an access violation, guard page violation,
+ // or an in-page read error, then return the faulting address. Otherwise.
+ // return a special address value.
+ //
+
+ ExceptionRecord = ExceptionPointers->ExceptionRecord;
+ if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
+ (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
+ (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR)) {
+
+ //
+ // The virtual address which caused the exception is the 2nd
+ // parameter in the exception information array.
+ //
+
+ *BadVa = ExceptionRecord->ExceptionInformation[1];
+
+ } else {
+
+ //
+ // Unexpected exception - set the number of bytes copied to zero.
+ //
+
+ *BadVa = 0xFFFFFFFF;
+ }
+
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+NTSTATUS
+MiDoMappedCopy (
+ IN PEPROCESS FromProcess,
+ IN PVOID FromAddress,
+ IN PEPROCESS ToProcess,
+ OUT PVOID ToAddress,
+ IN ULONG BufferSize,
+ IN KPROCESSOR_MODE PreviousMode,
+ OUT PULONG NumberOfBytesRead
+ )
+
+/*++
+
+Routine Description:
+
+ This function copies the specified address range from the specified
+ process into the specified address range of the current process.
+
+Arguments:
+
+ FromProcess - Supplies an open handle to a process object.
+
+ FromAddress - Supplies the base address in the specified process
+ to be read.
+
+ ToProcess - Supplies an open handle to a process object.
+
+ ToAddress - Supplies the address of a buffer which receives the
+ contents from the specified process address space.
+
+ BufferSize - Supplies the requested number of bytes to read from
+ the specified process.
+
+ PreviousMode - Supplies the previous processor mode.
+
+ NumberOfBytesRead - Receives the actual number of bytes
+ transferred into the specified buffer.
+
+Return Value:
+
+ TBS
+
+--*/
+
+{
+
+ ULONG AmountToMove;
+ ULONG BadVa;
+ PEPROCESS CurrentProcess;
+ BOOLEAN FailedMove;
+ BOOLEAN FailedProbe;
+ PULONG InVa;
+ ULONG LeftToMove;
+ PULONG MappedAddress;
+ ULONG MaximumMoved;
+ PMDL Mdl;
+ ULONG MdlHack[(sizeof(MDL)/4) + (MAX_LOCK_SIZE >> PAGE_SHIFT) + 1];
+ PULONG OutVa;
+
+ PAGED_CODE();
+
+ //
+ // Get the address of the current process object and initialize copy
+ // parameters.
+ //
+
+ CurrentProcess = PsGetCurrentProcess();
+
+ InVa = FromAddress;
+ OutVa = ToAddress;
+
+ MaximumMoved = MAX_LOCK_SIZE;
+ if (BufferSize <= MAX_LOCK_SIZE) {
+ MaximumMoved = BufferSize;
+ }
+
+ Mdl = (PMDL)&MdlHack[0];
+
+ //
+ // Map the data into the system part of the address space, then copy it.
+ //
+
+ LeftToMove = BufferSize;
+ AmountToMove = MaximumMoved;
+ while (LeftToMove > 0) {
+
+ if (LeftToMove < AmountToMove) {
+
+ //
+ // Set to move the remaining bytes.
+ //
+
+ AmountToMove = LeftToMove;
+ }
+
+ KeDetachProcess();
+ KeAttachProcess (&FromProcess->Pcb);
+
+ //
+ // We may be touching a user's memory which could be invalid,
+ // declare an exception handler.
+ //
+
+ try {
+
+ //
+ // Probe to make sure that the specified buffer is accessable in
+ // the target process.
+ //
+
+ MappedAddress = NULL;
+
+ if (((PVOID)InVa == FromAddress) &&
+ ((PVOID)InVa <= MM_HIGHEST_USER_ADDRESS)) {
+ FailedProbe = TRUE;
+ ProbeForRead (FromAddress, BufferSize, sizeof(CHAR));
+ }
+
+ //
+ // Initialize MDL for request.
+ //
+
+ MmInitializeMdl(Mdl,
+ InVa,
+ AmountToMove);
+
+ FailedMove = TRUE;
+ MmProbeAndLockPages (Mdl, PreviousMode, IoReadAccess);
+ FailedMove = FALSE;
+
+ MappedAddress = MmMapLockedPages (Mdl, KernelMode);
+
+ //
+ // Deattach from the FromProcess and attach to the ToProcess.
+ //
+
+ KeDetachProcess();
+ KeAttachProcess (&ToProcess->Pcb);
+
+ //
+ // Now operating in the context of the ToProcess.
+ //
+
+ if (((PVOID)InVa == FromAddress)
+ && (ToAddress <= MM_HIGHEST_USER_ADDRESS)) {
+ ProbeForWrite (ToAddress, BufferSize, sizeof(CHAR));
+ FailedProbe = FALSE;
+ }
+
+ RtlCopyMemory (OutVa, MappedAddress, AmountToMove);
+ } except (MiGetExceptionInfo (GetExceptionInformation(), &BadVa)) {
+
+
+ //
+ // If an exception occurs during the move operation or probe,
+ // return the exception code as the status value.
+ //
+
+ KeDetachProcess();
+ if (MappedAddress != NULL) {
+ MmUnmapLockedPages (MappedAddress, Mdl);
+ MmUnlockPages (Mdl);
+ }
+
+ if (GetExceptionCode() == STATUS_WORKING_SET_QUOTA) {
+ return STATUS_WORKING_SET_QUOTA;
+ }
+
+ if (FailedProbe) {
+ return GetExceptionCode();
+
+ } else {
+
+ //
+ // The failure occurred during the move operation, determine
+ // which move failed, and calculate the number of bytes
+ // actually moved.
+ //
+
+ if (FailedMove) {
+ if (BadVa != 0xFFFFFFFF) {
+ *NumberOfBytesRead = BadVa - (ULONG)FromAddress;
+ }
+
+ } else {
+ *NumberOfBytesRead = BadVa - (ULONG)ToAddress;
+ }
+ }
+
+ return STATUS_PARTIAL_COPY;
+ }
+ MmUnmapLockedPages (MappedAddress, Mdl);
+ MmUnlockPages (Mdl);
+
+ LeftToMove -= AmountToMove;
+ InVa = (PVOID)((ULONG)InVa + AmountToMove);
+ OutVa = (PVOID)((ULONG)OutVa + AmountToMove);
+ }
+
+ KeDetachProcess();
+
+ //
+ // Set number of bytes moved.
+ //
+
+ *NumberOfBytesRead = BufferSize;
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+MiDoPoolCopy (
+ IN PEPROCESS FromProcess,
+ IN PVOID FromAddress,
+ IN PEPROCESS ToProcess,
+ OUT PVOID ToAddress,
+ IN ULONG BufferSize,
+ OUT PULONG NumberOfBytesRead
+ )
+
+/*++
+
+Routine Description:
+
+ This function copies the specified address range from the specified
+ process into the specified address range of the current process.
+
+Arguments:
+
+ ProcessHandle - Supplies an open handle to a process object.
+
+ BaseAddress - Supplies the base address in the specified process
+ to be read.
+
+ Buffer - Supplies the address of a buffer which receives the
+ contents from the specified process address space.
+
+ BufferSize - Supplies the requested number of bytes to read from
+ the specified process.
+
+ NumberOfBytesRead - Receives the actual number of bytes
+ transferred into the specified buffer.
+
+Return Value:
+
+ TBS
+
+--*/
+
+{
+
+ ULONG AmountToMove;
+ ULONG BadVa;
+ PEPROCESS CurrentProcess;
+ BOOLEAN FailedMove;
+ BOOLEAN FailedProbe;
+ PULONG InVa;
+ ULONG LeftToMove;
+ ULONG MaximumMoved;
+ PULONG OutVa;
+ PULONG PoolArea;
+ LONGLONG StackArray[COPY_STACK_SIZE];
+ ULONG FreePool;
+
+ PAGED_CODE();
+
+ //
+ // Get the address of the current process object and initialize copy
+ // parameters.
+ //
+
+ CurrentProcess = PsGetCurrentProcess();
+
+ InVa = FromAddress;
+ OutVa = ToAddress;
+
+ //
+ // Allocate non-paged memory to copy in and out of.
+ //
+
+ MaximumMoved = MAX_MOVE_SIZE;
+ if (BufferSize <= MAX_MOVE_SIZE) {
+ MaximumMoved = BufferSize;
+ }
+
+ if (BufferSize <= (COPY_STACK_SIZE * sizeof(LONGLONG))) {
+ PoolArea = (PULONG)&StackArray[0];
+ FreePool = FALSE;
+ } else {
+ PoolArea = ExAllocatePoolWithTag (NonPagedPool, MaximumMoved, 'wRmM');
+
+ while (PoolArea == NULL) {
+ if (MaximumMoved <= MINIMUM_ALLOCATION) {
+ PoolArea = ExAllocatePoolWithTag (NonPagedPoolMustSucceed,
+ MaximumMoved, 'wRmM');
+
+ } else {
+ MaximumMoved = MaximumMoved >> 1;
+ PoolArea = ExAllocatePoolWithTag (NonPagedPool, MaximumMoved, 'wRmM');
+ }
+ }
+ FreePool = TRUE;
+ }
+
+ //
+ // Copy the data into pool, then copy back into the ToProcess.
+ //
+
+ LeftToMove = BufferSize;
+ AmountToMove = MaximumMoved;
+ while (LeftToMove > 0) {
+
+ if (LeftToMove < AmountToMove) {
+
+ //
+ // Set to move the remaining bytes.
+ //
+
+ AmountToMove = LeftToMove;
+ }
+
+ KeDetachProcess();
+ KeAttachProcess (&FromProcess->Pcb);
+
+ //
+ // We may be touching a user's memory which could be invalid,
+ // declare an exception handler.
+ //
+
+ try {
+
+ //
+ // Probe to make sure that the specified buffer is accessable in
+ // the target process.
+ //
+
+ if (((PVOID)InVa == FromAddress) &&
+ ((PVOID)InVa <= MM_HIGHEST_USER_ADDRESS)) {
+ FailedProbe = TRUE;
+ ProbeForRead (FromAddress, BufferSize, sizeof(CHAR));
+ }
+
+ FailedMove = TRUE;
+ RtlCopyMemory (PoolArea, InVa, AmountToMove);
+ FailedMove = FALSE;
+
+ KeDetachProcess();
+ KeAttachProcess (&ToProcess->Pcb);
+
+ //
+ // Now operating in the context of the ToProcess.
+ //
+
+ if (((PVOID)InVa == FromAddress)
+ && (ToAddress <= MM_HIGHEST_USER_ADDRESS)) {
+ ProbeForWrite (ToAddress, BufferSize, sizeof(CHAR));
+ FailedProbe = FALSE;
+ }
+
+ RtlCopyMemory (OutVa, PoolArea, AmountToMove);
+
+ } except (MiGetExceptionInfo (GetExceptionInformation(), &BadVa)) {
+
+ //
+ // If an exception occurs during the move operation or probe,
+ // return the exception code as the status value.
+ //
+
+ KeDetachProcess();
+
+ if (FreePool) {
+ ExFreePool (PoolArea);
+ }
+ if (FailedProbe) {
+ return GetExceptionCode();
+
+ } else {
+
+ //
+ // The failure occurred during the move operation, determine
+ // which move failed, and calculate the number of bytes
+ // actually moved.
+ //
+
+ if (FailedMove) {
+
+ //
+ // The failure occurred getting the data.
+ //
+
+ if (BadVa != 0xFFFFFFFF) {
+ *NumberOfBytesRead = BadVa - (ULONG)FromAddress;
+ }
+
+ } else {
+
+ //
+ // The failure occurred writing the data.
+ //
+
+ *NumberOfBytesRead = BadVa - (ULONG)ToAddress;
+ }
+ }
+
+ return STATUS_PARTIAL_COPY;
+ }
+
+ LeftToMove -= AmountToMove;
+ InVa = (PVOID)((ULONG)InVa + AmountToMove);
+ OutVa = (PVOID)((ULONG)OutVa + AmountToMove);
+ }
+
+ if (FreePool) {
+ ExFreePool (PoolArea);
+ }
+ KeDetachProcess();
+
+ //
+ // Set number of bytes moved.
+ //
+
+ *NumberOfBytesRead = BufferSize;
+ return STATUS_SUCCESS;
+}