-/* $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 CINT 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 = &Process->AddressSpace;
- }
- 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 = 0;
- Info->State = MEM_FREE;
- Info->Protect = MemoryArea->Attributes;
- Info->AllocationProtect = MemoryArea->Attributes;
- 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 = 0;
- Info->State = MEM_COMMIT;
- Info->Protect = MemoryArea->Attributes;
- Info->AllocationProtect = MemoryArea->Attributes;
- 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->Attributes;
- Info->AllocationProtect = MemoryArea->Attributes;
- 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->Attributes;
- Info->AllocationProtect = MemoryArea->Attributes;
- 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->Attributes;
- Info->AllocationProtect = MemoryArea->Attributes;
- 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);
- MmUnlockAddressSpace(AddressSpace);
- if (Address < MmSystemRangeStart)
- {
- ObDereferenceObject(Process);
- }
+ /* Check if we had locked the pages */
+ if (PagesLocked) MmUnlockPages(Mdl);
- return Status;
+ /* 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;
}
-/* (tMk 2004.II.4)
- * FUNCTION:
- * Called from VirtualQueryEx (lib\kernel32\mem\virtual.c)
- *
- */
-NTSTATUS STDCALL
-NtQueryVirtualMemory (IN HANDLE ProcessHandle,
- IN PVOID Address,
- IN CINT 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
- {
- ProbeForWrite(UnsafeResultLength,
- sizeof(ULONG),
- sizeof(ULONG));
- }
- _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;
+
+ /* Detach from target */
+ KeUnstackDetachProcess(&ApcState);
- return(Status);
+ /* 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 = &Process->AddressSpace;
-
- 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_SUCCESS;
- }
+ 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);
+
+ AddressSpace = &Process->VadRoot;
- MmUnlockAddressSpace(AddressSpace);
+ MmLockAddressSpace(AddressSpace);
+ MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
+ if (MemoryArea == NULL)
+ {
+ MmUnlockAddressSpace(AddressSpace);
+ return STATUS_UNSUCCESSFUL;
+ }
- return Status;
+ 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
- {
- ProbeForWrite(UnsafeBaseAddress,
- sizeof(PVOID),
- sizeof(ULONG));
- ProbeForWrite(UnsafeBaseAddress,
- sizeof(ULONG),
- sizeof(ULONG));
- ProbeForWrite(UnsafeOldAccessProtection,
- sizeof(ULONG),
- sizeof(ULONG));
-
- BaseAddress = *UnsafeBaseAddress;
- NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
- }
- _SEH_HANDLE
- {
- Status = _SEH_GetExceptionCode();
- }
- _SEH_END;
-
- if (!NT_SUCCESS(Status))
- {
- return Status;
- }
- }
- else
- {
- BaseAddress = *UnsafeBaseAddress;
- NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
- }
-
- /* (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();
-
- PreviousMode = ExGetPreviousMode();
-
- if(PreviousMode != KernelMode)
- {
- _SEH_TRY
- {
- ProbeForWrite(Buffer,
- NumberOfBytesToRead,
- 1);
- if(NumberOfBytesRead != NULL)
- {
- ProbeForWrite(NumberOfBytesRead,
- sizeof(ULONG),
- sizeof(ULONG));
- }
- }
- _SEH_HANDLE
- {
- Status = _SEH_GetExceptionCode();
- }
- _SEH_END;
-
- if(!NT_SUCCESS(Status))
- {
- return Status;
- }
- }
-
- DPRINT("NtReadVirtualMemory(ProcessHandle %x, BaseAddress %x, "
- "Buffer %x, NumberOfBytesToRead %d)\n",ProcessHandle,BaseAddress,
- Buffer,NumberOfBytesToRead);
-
- Status = ObReferenceObjectByHandle(ProcessHandle,
- PROCESS_VM_READ,
- NULL,
- PreviousMode,
- (PVOID*)(&Process),
- NULL);
- if (!NT_SUCCESS(Status))
- {
- return(Status);
- }
-
- CurrentProcess = PsGetCurrentProcess();
-
- 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 {
- ProbeForRead(BaseAddress, NumberOfBytesToRead, 1);
- 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;
- ULONG OldProtection = 0;
- PVOID ProtectBaseAddress;
- ULONG ProtectNumberOfBytes;
- KPROCESSOR_MODE PreviousMode;
- NTSTATUS CopyStatus, Status = STATUS_SUCCESS;
-
- DPRINT("NtWriteVirtualMemory(ProcessHandle %x, BaseAddress %x, "
- "Buffer %x, NumberOfBytesToWrite %d)\n",ProcessHandle,BaseAddress,
- Buffer,NumberOfBytesToWrite);
-
- PreviousMode = ExGetPreviousMode();
-
- if (PreviousMode != KernelMode && NumberOfBytesWritten != NULL)
- {
- _SEH_TRY
- {
- ProbeForWrite(NumberOfBytesWritten,
- sizeof(ULONG),
- sizeof(ULONG));
- }
- _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);
- }
-
- /* We have to make sure the target memory is writable.
- *
- * I am not sure if it is correct to do this in any case, but it has to be
- * done at least in some cases because you can use WriteProcessMemory to
- * write into the .text section of a module where memcpy() would crash.
- * -blight (2005/01/09)
- */
- ProtectBaseAddress = BaseAddress;
- ProtectNumberOfBytes = NumberOfBytesToWrite;
-
- CopyStatus = STATUS_SUCCESS;
-
- /* Write memory */
- if (Process == PsGetCurrentProcess())
- {
- Status = MiProtectVirtualMemory(Process,
- &ProtectBaseAddress,
- &ProtectNumberOfBytes,
- PAGE_READWRITE,
- &OldProtection);
- if (!NT_SUCCESS(Status))
- {
- ObDereferenceObject(Process);
- return Status;
- }
-
- 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)
- {
- ObDereferenceObject(Process);
- return(STATUS_NO_MEMORY);
- }
-
- /* Make the target area writable. */
- Status = MiProtectVirtualMemory(Process,
- &ProtectBaseAddress,
- &ProtectNumberOfBytes,
- PAGE_READWRITE,
- &OldProtection);
- if (!NT_SUCCESS(Status))
- {
- ObDereferenceObject(Process);
- ExFreePool(Mdl);
- return Status;
- }
-
- /* Map the MDL. */
- MmProbeAndLockPages(Mdl,
- UserMode,
- IoReadAccess);
-
- /* Copy memory from the mapped MDL into the target buffer. */
- KeAttachProcess(&Process->Pcb);
-
- SystemAddress = MmGetSystemAddressForMdl(Mdl);
- if (PreviousMode != KernelMode)
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
- _SEH_TRY
- {
- memcpy(BaseAddress, SystemAddress, NumberOfBytesToWrite);
- }
- _SEH_HANDLE
- {
- CopyStatus = _SEH_GetExceptionCode();
- }
- _SEH_END;
+ /* Get exception code */
+ Status = _SEH2_GetExceptionCode();
+ }
+ _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
+ {
+ /* Return the number of bytes read */
+ *NumberOfBytesWritten = BytesWritten;
}
- else
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
- memcpy(BaseAddress, SystemAddress, NumberOfBytesToWrite);
+ /* Handle exception */
+ Status = _SEH2_GetExceptionCode();
}
+ _SEH2_END;
+ }
- KeDetachProcess();
-
- /* Free the MDL. */
- if (Mdl->MappedSystemVa != NULL)
- {
- MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
- }
- MmUnlockPages(Mdl);
- ExFreePool(Mdl);
- }
-
- /* Reset the protection of the target memory. */
- Status = MiProtectVirtualMemory(Process,
- &ProtectBaseAddress,
- &ProtectNumberOfBytes,
- OldProtection,
- &OldProtection);
- if (!NT_SUCCESS(Status))
- {
- DPRINT1("Failed to reset protection of the target memory! (Status 0x%x)\n", Status);
- /* FIXME: Should we bugcheck here? */
- }
-
- ObDereferenceObject(Process);
-
- if (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);
+ /* Return status */
+ return Status;
}
-/*
- * @unimplemented
- */
-
-PVOID
-STDCALL
-MmGetVirtualForPhysical (
- IN PHYSICAL_ADDRESS PhysicalAddress
- )
+NTSTATUS
+NTAPI
+NtProtectVirtualMemory(IN HANDLE ProcessHandle,
+ IN OUT PVOID *UnsafeBaseAddress,
+ IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
+ IN ULONG NewAccessProtection,
+ OUT PULONG UnsafeOldAccessProtection)
{
- UNIMPLEMENTED;
- return 0;
+ 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)
+ {
+ /* Get exception code */
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+
+ /* Return on exception */
+ if (!NT_SUCCESS(Status)) return Status;
+ }
+ else
+ {
+ /* Capture directly */
+ BaseAddress = *UnsafeBaseAddress;
+ NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
+ }
+
+ /* Catch illegal base address */
+ if (BaseAddress > (PVOID)MmUserProbeAddress) 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;
+
+ /* Do the actual work */
+ Status = MiProtectVirtualMemory(Process,
+ &BaseAddress,
+ &NumberOfBytesToProtect,
+ NewAccessProtection,
+ &OldAccessProtection);
+
+ /* 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)
+ {
+ /* Catch exception */
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+
+ /* Return status */
+ return Status;
}
-/* FUNCTION:
- * Called from EngSecureMem (subsys\win32k\eng\mem.c)
- * @unimplemented
- */
-PVOID STDCALL
-MmSecureVirtualMemory (PVOID Address,
- SIZE_T Length,
- ULONG Mode)
+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)
{
- /* Only works for user space */
- if (MmHighestUserAddress < Address)
- {
- return NULL;
- }
+ 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);
- return 0;
-}
+ PreviousMode = ExGetPreviousMode();
+ if (PreviousMode != KernelMode && UnsafeResultLength != NULL)
+ {
+ _SEH2_TRY
+ {
+ ProbeForWriteSize_t(UnsafeResultLength);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
-/* FUNCTION:
- * Called from EngUnsecureMem (subsys\win32k\eng\mem.c)
- * @unimplemented
- */
-VOID STDCALL
-MmUnsecureVirtualMemory(PVOID SecureMem)
-{
- if (NULL == SecureMem)
- {
- return;
- }
+ if (!NT_SUCCESS(Status))
+ {
+ return Status;
+ }
+ }
- UNIMPLEMENTED;
-}
+ if (Address >= MmSystemRangeStart)
+ {
+ DPRINT1("Invalid parameter\n");
+ return STATUS_INVALID_PARAMETER;
+ }
+ /* FIXME: Move this inside MiQueryVirtualMemory */
+ if (VirtualMemoryInformationClass == MemorySectionName)
+ {
+ RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
+ Status = MmGetFileNameForAddress(Address, &ModuleFileName);
-/*
- * @implemented
- */
-VOID STDCALL
-ProbeForRead (IN CONST VOID *Address,
- IN ULONG Length,
- IN ULONG Alignment)
+ if (NT_SUCCESS(Status))
+ {
+ SectionName = VirtualMemoryInformation;
+ if (PreviousMode != KernelMode)
+ {
+ _SEH2_TRY
+ {
+ RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
+ SectionName->SectionFileName.MaximumLength = Length;
+ RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
+
+ if (UnsafeResultLength != NULL)
+ {
+ *UnsafeResultLength = ModuleFileName.Length;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+ }
+ else
+ {
+ RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
+ SectionName->SectionFileName.MaximumLength = Length;
+ RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
+
+ if (UnsafeResultLength != NULL)
+ {
+ *UnsafeResultLength = ModuleFileName.Length;
+ }
+ }
+ }
+ return Status;
+ }
+ else
+ {
+ Status = MiQueryVirtualMemory(ProcessHandle,
+ Address,
+ VirtualMemoryInformationClass,
+ &VirtualMemoryInfo,
+ Length,
+ &ResultLength);
+ }
+
+ if (NT_SUCCESS(Status))
+ {
+ if (PreviousMode != KernelMode)
+ {
+ _SEH2_TRY
+ {
+ if (ResultLength > 0)
+ {
+ ProbeForWrite(VirtualMemoryInformation,
+ ResultLength,
+ 1);
+ RtlCopyMemory(VirtualMemoryInformation,
+ &VirtualMemoryInfo,
+ ResultLength);
+ }
+ if (UnsafeResultLength != NULL)
+ {
+ *UnsafeResultLength = ResultLength;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+ }
+ else
+ {
+ if (ResultLength > 0)
+ {
+ RtlCopyMemory(VirtualMemoryInformation,
+ &VirtualMemoryInfo,
+ ResultLength);
+ }
+
+ if (UnsafeResultLength != NULL)
+ {
+ *UnsafeResultLength = ResultLength;
+ }
+ }
+ }
+
+ return(Status);
+}
+
+NTSTATUS
+NTAPI
+NtLockVirtualMemory(IN HANDLE ProcessHandle,
+ IN PVOID BaseAddress,
+ IN SIZE_T NumberOfBytesToLock,
+ OUT PSIZE_T NumberOfBytesLocked OPTIONAL)
{
- ASSERT(Alignment == 1 || Alignment == 2 || Alignment == 4 || Alignment == 8);
-
- if (Length == 0)
- return;
-
- if (((ULONG_PTR)Address & (Alignment - 1)) != 0)
- {
- ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT);
- }
- else if ((ULONG_PTR)Address + Length - 1 < (ULONG_PTR)Address ||
- (ULONG_PTR)Address + Length - 1 >= (ULONG_PTR)MmUserProbeAddress)
- {
- ExRaiseStatus (STATUS_ACCESS_VIOLATION);
- }
+ 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;
+}
-/*
- * @implemented
- */
-VOID STDCALL
-ProbeForWrite (IN CONST VOID *Address,
- IN ULONG Length,
- IN ULONG Alignment)
+NTSTATUS
+NTAPI
+NtFlushVirtualMemory(IN HANDLE ProcessHandle,
+ IN OUT PVOID *BaseAddress,
+ IN OUT PSIZE_T NumberOfBytesToFlush,
+ OUT PIO_STATUS_BLOCK IoStatusBlock)
{
- volatile CHAR *Current;
- PCHAR Last;
-
- ASSERT(Alignment == 1 || Alignment == 2 || Alignment == 4 || Alignment == 8);
-
- if (Length == 0)
- return;
-
- if (((ULONG_PTR)Address & (Alignment - 1)) != 0)
- {
- ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT);
- }
-
- Last = (PCHAR)((ULONG_PTR)Address + Length - 1);
- if ((ULONG_PTR)Last < (ULONG_PTR)Address ||
- (ULONG_PTR)Last >= (ULONG_PTR)MmUserProbeAddress)
- {
- ExRaiseStatus (STATUS_ACCESS_VIOLATION);
- }
-
- /* Check for accessible pages */
- Current = (CHAR*)Address;
- do
- {
- *Current = *Current;
- Current = (CHAR*)((ULONG_PTR)Current + PAGE_SIZE);
- } while (Current <= Last);
+ UNIMPLEMENTED;
+ return STATUS_SUCCESS;
}
/* EOF */