mirror of
https://github.com/reactos/reactos.git
synced 2024-09-19 09:09:53 +00:00
c501d8112c
svn path=/branches/aicom-network-fixes/; revision=34994
1085 lines
35 KiB
C
1085 lines
35 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: ntoskrnl/mm/virtual.c
|
|
* PURPOSE: Implementing operations on virtual memory.
|
|
*
|
|
* PROGRAMMERS: David Welch
|
|
*/
|
|
|
|
/* INCLUDE ********************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#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')
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
_SEH_DEFINE_LOCALS(MiGetExceptionInfo)
|
|
{
|
|
volatile BOOLEAN HaveBadAddress;
|
|
volatile ULONG_PTR BadAddress;
|
|
};
|
|
|
|
_SEH_FILTER(MiGetExceptionInfo)
|
|
{
|
|
_SEH_ACCESS_LOCALS(MiGetExceptionInfo);
|
|
EXCEPTION_POINTERS *ExceptionInfo = _SEH_GetExceptionPointers();
|
|
PEXCEPTION_RECORD ExceptionRecord;
|
|
PAGED_CODE();
|
|
|
|
/* Assume default */
|
|
_SEH_VAR(HaveBadAddress) = FALSE;
|
|
|
|
/* Get the exception record */
|
|
ExceptionRecord = ExceptionInfo->ExceptionRecord;
|
|
|
|
/* Look at the exception code */
|
|
if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
|
|
(ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
|
|
(ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
|
|
{
|
|
/* We can tell the address if we have more than one parameter */
|
|
if (ExceptionRecord->NumberParameters > 1)
|
|
{
|
|
/* Return the address */
|
|
_SEH_VAR(HaveBadAddress) = TRUE;
|
|
_SEH_VAR(BadAddress) = ExceptionRecord->ExceptionInformation[1];
|
|
}
|
|
}
|
|
|
|
/* Continue executing the next handler */
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MiDoMappedCopy(IN PEPROCESS SourceProcess,
|
|
IN PVOID SourceAddress,
|
|
IN PEPROCESS TargetProcess,
|
|
OUT PVOID TargetAddress,
|
|
IN ULONG BufferSize,
|
|
IN KPROCESSOR_MODE PreviousMode,
|
|
OUT PULONG ReturnSize)
|
|
{
|
|
PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
|
|
PMDL Mdl = (PMDL)MdlBuffer;
|
|
ULONG TotalSize, CurrentSize, RemainingSize;
|
|
volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving;
|
|
BOOLEAN PagesLocked;
|
|
PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
|
|
PVOID MdlAddress;
|
|
KAPC_STATE ApcState;
|
|
_SEH_DECLARE_LOCALS(MiGetExceptionInfo);
|
|
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;
|
|
|
|
/* Attach to the source address space */
|
|
KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
|
|
|
|
/* Reset state for this pass */
|
|
MdlAddress = NULL;
|
|
PagesLocked = FALSE;
|
|
FailedInMoving = FALSE;
|
|
ASSERT(FailedInProbe == FALSE);
|
|
|
|
/* Protect user-mode copy */
|
|
_SEH_TRY
|
|
{
|
|
/* If this is our first time, probe the buffer */
|
|
if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
|
|
{
|
|
/* Catch a failure here */
|
|
FailedInProbe = TRUE;
|
|
|
|
/* Do the probe */
|
|
ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
|
|
|
|
/* Passed */
|
|
FailedInProbe = FALSE;
|
|
}
|
|
|
|
/* Initialize and probe and lock the MDL */
|
|
MmInitializeMdl (Mdl, CurrentAddress, CurrentSize);
|
|
MmProbeAndLockPages (Mdl, PreviousMode, IoReadAccess);
|
|
PagesLocked = TRUE;
|
|
|
|
/* Now map the pages */
|
|
MdlAddress = MmMapLockedPagesSpecifyCache(Mdl,
|
|
KernelMode,
|
|
MmCached,
|
|
NULL,
|
|
FALSE,
|
|
HighPagePriority);
|
|
if (!MdlAddress)
|
|
{
|
|
/* Use our SEH handler to pick this up */
|
|
FailedInMapping = TRUE;
|
|
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
/* Now let go of the source and grab to the target process */
|
|
KeUnstackDetachProcess(&ApcState);
|
|
KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
|
|
|
|
/* Check if this is our first time through */
|
|
if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
|
|
{
|
|
/* Catch a failure here */
|
|
FailedInProbe = TRUE;
|
|
|
|
/* Do the probe */
|
|
ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
|
|
|
|
/* Passed */
|
|
FailedInProbe = FALSE;
|
|
}
|
|
|
|
/* Now do the actual move */
|
|
FailedInMoving = TRUE;
|
|
RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
|
|
}
|
|
_SEH_EXCEPT(MiGetExceptionInfo)
|
|
{
|
|
/* Detach from whoever we may be attached to */
|
|
KeUnstackDetachProcess(&ApcState);
|
|
|
|
/* Check if we had mapped the pages */
|
|
if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl);
|
|
|
|
/* Check if we had locked the pages */
|
|
if (PagesLocked) MmUnlockPages(Mdl);
|
|
|
|
/* Check if we failed during the probe or mapping */
|
|
if ((FailedInProbe) || (FailedInMapping))
|
|
{
|
|
/* Exit */
|
|
Status = _SEH_GetExceptionCode();
|
|
_SEH_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 (_SEH_VAR(HaveBadAddress))
|
|
{
|
|
/* Return the exact number of bytes copied */
|
|
*ReturnSize = _SEH_VAR(BadAddress) - (ULONG_PTR)SourceAddress;
|
|
}
|
|
}
|
|
|
|
/* Return partial copy */
|
|
Status = STATUS_PARTIAL_COPY;
|
|
}
|
|
_SEH_END;
|
|
|
|
/* Check for SEH status */
|
|
if (Status != STATUS_SUCCESS) return Status;
|
|
|
|
/* Detach from target */
|
|
KeUnstackDetachProcess(&ApcState);
|
|
|
|
/* Unmap and unlock */
|
|
MmUnmapLockedPages(MdlAddress, Mdl);
|
|
MmUnlockPages(Mdl);
|
|
|
|
/* Update location and size */
|
|
RemainingSize -= CurrentSize;
|
|
CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
|
|
CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
|
|
}
|
|
|
|
/* All bytes read */
|
|
*ReturnSize = BufferSize;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MiDoPoolCopy(IN PEPROCESS SourceProcess,
|
|
IN PVOID SourceAddress,
|
|
IN PEPROCESS TargetProcess,
|
|
OUT PVOID TargetAddress,
|
|
IN ULONG BufferSize,
|
|
IN KPROCESSOR_MODE PreviousMode,
|
|
OUT PULONG ReturnSize)
|
|
{
|
|
UCHAR StackBuffer[MI_POOL_COPY_BYTES];
|
|
ULONG TotalSize, CurrentSize, RemainingSize;
|
|
volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE;
|
|
PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
|
|
PVOID PoolAddress;
|
|
KAPC_STATE ApcState;
|
|
_SEH_DECLARE_LOCALS(MiGetExceptionInfo);
|
|
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 */
|
|
_SEH_TRY
|
|
{
|
|
/* If this is our first time, probe the buffer */
|
|
if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
|
|
{
|
|
/* Catch a failure here */
|
|
FailedInProbe = TRUE;
|
|
|
|
/* Do the probe */
|
|
ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
|
|
|
|
/* Passed */
|
|
FailedInProbe = FALSE;
|
|
}
|
|
|
|
/* Do the copy */
|
|
RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
|
|
|
|
/* Now let go of the source and grab to the target process */
|
|
KeUnstackDetachProcess(&ApcState);
|
|
KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
|
|
|
|
/* Check if this is our first time through */
|
|
if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
|
|
{
|
|
/* Catch a failure here */
|
|
FailedInProbe = TRUE;
|
|
|
|
/* Do the probe */
|
|
ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
|
|
|
|
/* Passed */
|
|
FailedInProbe = FALSE;
|
|
}
|
|
|
|
/* Now do the actual move */
|
|
FailedInMoving = TRUE;
|
|
RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
|
|
}
|
|
_SEH_EXCEPT(MiGetExceptionInfo)
|
|
{
|
|
/* Detach from whoever we may be attached to */
|
|
KeUnstackDetachProcess(&ApcState);
|
|
|
|
/* Check if we had allocated pool */
|
|
if (HavePoolAddress) ExFreePool(PoolAddress);
|
|
|
|
/* Check if we failed during the probe */
|
|
if (FailedInProbe)
|
|
{
|
|
/* Exit */
|
|
Status = _SEH_GetExceptionCode();
|
|
_SEH_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 (_SEH_VAR(HaveBadAddress))
|
|
{
|
|
/* Return the exact number of bytes copied */
|
|
*ReturnSize = _SEH_VAR(BadAddress) - (ULONG_PTR)SourceAddress;
|
|
}
|
|
}
|
|
|
|
/* Return partial copy */
|
|
Status = STATUS_PARTIAL_COPY;
|
|
}
|
|
_SEH_END;
|
|
|
|
/* Check for SEH status */
|
|
if (Status != STATUS_SUCCESS) return Status;
|
|
|
|
/* Detach from target */
|
|
KeUnstackDetachProcess(&ApcState);
|
|
|
|
/* Update location and size */
|
|
RemainingSize -= CurrentSize;
|
|
CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
|
|
CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
|
|
}
|
|
|
|
/* Check if we had allocated pool */
|
|
if (HavePoolAddress) ExFreePool(PoolAddress);
|
|
|
|
/* All bytes read */
|
|
*ReturnSize = BufferSize;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmCopyVirtualMemory(IN PEPROCESS SourceProcess,
|
|
IN PVOID SourceAddress,
|
|
IN PEPROCESS TargetProcess,
|
|
OUT PVOID TargetAddress,
|
|
IN ULONG BufferSize,
|
|
IN KPROCESSOR_MODE PreviousMode,
|
|
OUT PULONG 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 ULONG Length,
|
|
OUT PULONG ResultLength)
|
|
{
|
|
NTSTATUS Status;
|
|
PEPROCESS Process;
|
|
MEMORY_AREA* MemoryArea;
|
|
PMM_AVL_TABLE 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->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))
|
|
{
|
|
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);
|
|
if (Address < MmSystemRangeStart)
|
|
{
|
|
ASSERT(Process);
|
|
ObDereferenceObject(Process);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS STDCALL
|
|
MiProtectVirtualMemory(IN PEPROCESS Process,
|
|
IN OUT PVOID *BaseAddress,
|
|
IN OUT PULONG NumberOfBytesToProtect,
|
|
IN ULONG NewAccessProtection,
|
|
OUT PULONG OldAccessProtection OPTIONAL)
|
|
{
|
|
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;
|
|
|
|
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 ***********************************************************/
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
PVOID
|
|
NTAPI
|
|
MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
PVOID
|
|
NTAPI
|
|
MmSecureVirtualMemory(IN PVOID Address,
|
|
IN SIZE_T Length,
|
|
IN ULONG Mode)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
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)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
PEPROCESS Process;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG BytesRead = 0;
|
|
PAGED_CODE();
|
|
|
|
/* Check if we came from user mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Validate the read addresses */
|
|
if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
|
|
(((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
|
|
(((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
|
|
(((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
|
|
{
|
|
/* Don't allow to write into kernel space */
|
|
return STATUS_ACCESS_VIOLATION;
|
|
}
|
|
|
|
/* Enter SEH for probe */
|
|
_SEH_TRY
|
|
{
|
|
/* Probe the output value */
|
|
if (NumberOfBytesRead) ProbeForWriteUlong(NumberOfBytesRead);
|
|
}
|
|
_SEH_HANDLE
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH_GetExceptionCode();
|
|
}
|
|
_SEH_END;
|
|
|
|
/* Return if we failed */
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
}
|
|
|
|
/* 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);
|
|
|
|
/* Derefernece the process */
|
|
ObDereferenceObject(Process);
|
|
}
|
|
|
|
/* Check if the caller sent this parameter */
|
|
if (NumberOfBytesRead)
|
|
{
|
|
/* Enter SEH to guard write */
|
|
_SEH_TRY
|
|
{
|
|
/* Return the number of bytes read */
|
|
*NumberOfBytesRead = BytesRead;
|
|
}
|
|
_SEH_HANDLE
|
|
{
|
|
/* Handle exception */
|
|
Status = _SEH_GetExceptionCode();
|
|
}
|
|
_SEH_END;
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtWriteVirtualMemory(IN HANDLE ProcessHandle,
|
|
IN PVOID BaseAddress,
|
|
IN PVOID Buffer,
|
|
IN ULONG NumberOfBytesToWrite,
|
|
OUT PULONG NumberOfBytesWritten OPTIONAL)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
PEPROCESS Process;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG 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))
|
|
{
|
|
/* Don't allow to write into kernel space */
|
|
return STATUS_ACCESS_VIOLATION;
|
|
}
|
|
|
|
/* Enter SEH for probe */
|
|
_SEH_TRY
|
|
{
|
|
/* Probe the output value */
|
|
if (NumberOfBytesWritten) ProbeForWriteUlong(NumberOfBytesWritten);
|
|
}
|
|
_SEH_HANDLE
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH_GetExceptionCode();
|
|
}
|
|
_SEH_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);
|
|
|
|
/* Derefernece the process */
|
|
ObDereferenceObject(Process);
|
|
}
|
|
|
|
/* Check if the caller sent this parameter */
|
|
if (NumberOfBytesWritten)
|
|
{
|
|
/* Enter SEH to guard write */
|
|
_SEH_TRY
|
|
{
|
|
/* Return the number of bytes read */
|
|
*NumberOfBytesWritten = BytesWritten;
|
|
}
|
|
_SEH_HANDLE
|
|
{
|
|
/* Handle exception */
|
|
Status = _SEH_GetExceptionCode();
|
|
}
|
|
_SEH_END;
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtProtectVirtualMemory(IN HANDLE ProcessHandle,
|
|
IN OUT PVOID *UnsafeBaseAddress,
|
|
IN OUT ULONG *UnsafeNumberOfBytesToProtect,
|
|
IN ULONG NewAccessProtection,
|
|
OUT PULONG UnsafeOldAccessProtection)
|
|
{
|
|
PEPROCESS Process;
|
|
ULONG OldAccessProtection;
|
|
PVOID BaseAddress = NULL;
|
|
ULONG NumberOfBytesToProtect = 0;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
/* Check if we came from user mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Enter SEH for probing */
|
|
_SEH_TRY
|
|
{
|
|
/* Validate all outputs */
|
|
ProbeForWritePointer(UnsafeBaseAddress);
|
|
ProbeForWriteUlong(UnsafeNumberOfBytesToProtect);
|
|
ProbeForWriteUlong(UnsafeOldAccessProtection);
|
|
|
|
/* Capture them */
|
|
BaseAddress = *UnsafeBaseAddress;
|
|
NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
|
|
}
|
|
_SEH_HANDLE
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH_GetExceptionCode();
|
|
}
|
|
_SEH_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 */
|
|
_SEH_TRY
|
|
{
|
|
/* Return data to user */
|
|
*UnsafeOldAccessProtection = OldAccessProtection;
|
|
*UnsafeBaseAddress = BaseAddress;
|
|
*UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
|
|
}
|
|
_SEH_HANDLE
|
|
{
|
|
/* Catch exception */
|
|
Status = _SEH_GetExceptionCode();
|
|
}
|
|
_SEH_END;
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS STDCALL
|
|
NtQueryVirtualMemory(IN HANDLE ProcessHandle,
|
|
IN PVOID Address,
|
|
IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
|
|
OUT PVOID VirtualMemoryInformation,
|
|
IN ULONG Length,
|
|
OUT PULONG UnsafeResultLength)
|
|
{
|
|
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)
|
|
{
|
|
_SEH_TRY
|
|
{
|
|
if (ResultLength > 0)
|
|
{
|
|
ProbeForWrite(VirtualMemoryInformation,
|
|
ResultLength,
|
|
1);
|
|
RtlCopyMemory(VirtualMemoryInformation,
|
|
&VirtualMemoryInfo,
|
|
ResultLength);
|
|
}
|
|
if (UnsafeResultLength != NULL)
|
|
{
|
|
*UnsafeResultLength = ResultLength;
|
|
}
|
|
}
|
|
_SEH_HANDLE
|
|
{
|
|
Status = _SEH_GetExceptionCode();
|
|
}
|
|
_SEH_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 ULONG NumberOfBytesToLock,
|
|
OUT PULONG NumberOfBytesLocked OPTIONAL)
|
|
{
|
|
UNIMPLEMENTED;
|
|
if (NumberOfBytesLocked) *NumberOfBytesLocked = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
|
|
IN PVOID BaseAddress,
|
|
IN ULONG NumberOfBytesToUnlock,
|
|
OUT PULONG 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 */
|