mirror of
https://github.com/reactos/reactos.git
synced 2025-04-21 20:50:29 +00:00
- Multiple Virtual Memory API fixes:
- MiDoMappedcopy: The MDL should have 16 pages total, so MI_MAPPED_COPY_PAGES should be 14, not 16, to account for the MDL page itself, and the extra add-on page. - MiDoMappedCopy: Check for working set quota exception. - NtRead/WriteVirtualMemory: Do not attempt to do any work if the size is 0. - NtRead/WriteVirtualMemory: Do not return SEH status if we fail to write the number of bytes read/written -- return the function status. - NtProtectVirtualMemory: Protect the memory while attached to the target process. - NtProtectVirtualMemory: Do not return SEH status if we fail to write the number of bytes protected -- return the function status. - NtLock/UnlockVirtualMemory: Fix incorrect function definition. The last parameter is a bitfield. The middle two parameters are pointers, not values. - VirtualLock/Unlock: Fix calling NtLock/UnlockVirtualMemory with new correct function definitions. Call with MAP_PROCESS. - NtLock/UnlockVirtualMemory: Apply SEH. Validate flags. Validate parameters. - NtLock/UnlockVirtualMemory: Attach to the process while doing the operation. Reference the process. - NtLock/UnlockVirtualMemory: Check for SE_LOCK_MEMORY_PRIVILEGE if MAP_SYSTEM is specified. - Move MAP_SYSTEM and MAP_PROCESS from ntifs.h to mmtypes.h in NDK. - NtLock/UnlockVirtualMemory: Return success and semi-legitimate return values saying nothing was actually done. - NtFlushVirtualMemory: Apply SEH. Validate flags. Validate parameters. Call MmFlushVirtualMemory. - NtFlushVirtualMemory: Reference the process. - NtFlushVirtualMemory: Return success and semi-legitimate return values indicating nothing was flushed. - NtGetWriteWatch: Fix function prototype. - NtGet/ResetWriteWatch: Apply SEH instead of hacked parameter checks. Validate parameters. - NtGet/ResetWriteWatch: Reference the process. - NtGet/ResetWriteWatch: Return semi-legitimate return values indicating nothing was written to. - These APIs are now owned by ARM3. svn path=/trunk/; revision=43480
This commit is contained in:
parent
e3d56ce220
commit
c04d1d038e
7 changed files with 1790 additions and 813 deletions
|
@ -170,14 +170,15 @@ NTAPI
|
||||||
VirtualLock(IN LPVOID lpAddress,
|
VirtualLock(IN LPVOID lpAddress,
|
||||||
IN SIZE_T dwSize)
|
IN SIZE_T dwSize)
|
||||||
{
|
{
|
||||||
ULONG BytesLocked;
|
|
||||||
NTSTATUS Status;
|
NTSTATUS Status;
|
||||||
|
ULONG RegionSize = dwSize;
|
||||||
|
PVOID BaseAddress = lpAddress;
|
||||||
|
|
||||||
/* Lock the memory */
|
/* Lock the memory */
|
||||||
Status = NtLockVirtualMemory(NtCurrentProcess(),
|
Status = NtLockVirtualMemory(NtCurrentProcess(),
|
||||||
lpAddress,
|
&BaseAddress,
|
||||||
dwSize,
|
&RegionSize,
|
||||||
&BytesLocked);
|
MAP_PROCESS);
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
{
|
{
|
||||||
/* We failed */
|
/* We failed */
|
||||||
|
@ -244,14 +245,15 @@ NTAPI
|
||||||
VirtualUnlock(IN LPVOID lpAddress,
|
VirtualUnlock(IN LPVOID lpAddress,
|
||||||
IN SIZE_T dwSize)
|
IN SIZE_T dwSize)
|
||||||
{
|
{
|
||||||
ULONG BytesLocked;
|
|
||||||
NTSTATUS Status;
|
NTSTATUS Status;
|
||||||
|
ULONG RegionSize = dwSize;
|
||||||
|
PVOID BaseAddress = lpAddress;
|
||||||
|
|
||||||
/* Unlock the memory */
|
/* Lock the memory */
|
||||||
Status = NtUnlockVirtualMemory(NtCurrentProcess(),
|
Status = NtUnlockVirtualMemory(NtCurrentProcess(),
|
||||||
lpAddress,
|
&BaseAddress,
|
||||||
dwSize,
|
&RegionSize,
|
||||||
&BytesLocked);
|
MAP_PROCESS);
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
{
|
{
|
||||||
/* We failed */
|
/* We failed */
|
||||||
|
|
|
@ -329,8 +329,6 @@ typedef enum _SECURITY_LOGON_TYPE
|
||||||
|
|
||||||
#define MAILSLOT_SIZE_AUTO 0
|
#define MAILSLOT_SIZE_AUTO 0
|
||||||
|
|
||||||
#define MAP_PROCESS 1L
|
|
||||||
#define MAP_SYSTEM 2L
|
|
||||||
#define MEM_DOS_LIM 0x40000000
|
#define MEM_DOS_LIM 0x40000000
|
||||||
|
|
||||||
#define MCB_FLAG_RAISE_ON_ALLOCATION_FAILURE 1
|
#define MCB_FLAG_RAISE_ON_ALLOCATION_FAILURE 1
|
||||||
|
|
|
@ -173,10 +173,10 @@ NTSYSCALLAPI
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
NTAPI
|
NTAPI
|
||||||
NtLockVirtualMemory(
|
NtLockVirtualMemory(
|
||||||
HANDLE ProcessHandle,
|
IN HANDLE ProcessHandle,
|
||||||
PVOID BaseAddress,
|
IN OUT PVOID *BaseAddress,
|
||||||
SIZE_T NumberOfBytesToLock,
|
IN OUT PSIZE_T NumberOfBytesToLock,
|
||||||
PSIZE_T NumberOfBytesLocked
|
IN ULONG MapType
|
||||||
);
|
);
|
||||||
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
|
@ -278,9 +278,9 @@ NTSTATUS
|
||||||
NTAPI
|
NTAPI
|
||||||
NtUnlockVirtualMemory(
|
NtUnlockVirtualMemory(
|
||||||
IN HANDLE ProcessHandle,
|
IN HANDLE ProcessHandle,
|
||||||
IN PVOID BaseAddress,
|
IN OUT PVOID *BaseAddress,
|
||||||
IN SIZE_T NumberOfBytesToUnlock,
|
IN OUT PSIZE_T NumberOfBytesToUnlock,
|
||||||
OUT PSIZE_T NumberOfBytesUnlocked OPTIONAL
|
IN ULONG MapType
|
||||||
);
|
);
|
||||||
|
|
||||||
NTSYSCALLAPI
|
NTSYSCALLAPI
|
||||||
|
@ -367,10 +367,10 @@ NTSYSAPI
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
NTAPI
|
NTAPI
|
||||||
ZwLockVirtualMemory(
|
ZwLockVirtualMemory(
|
||||||
HANDLE ProcessHandle,
|
IN HANDLE ProcessHandle,
|
||||||
PVOID BaseAddress,
|
IN OUT PVOID *BaseAddress,
|
||||||
SIZE_T NumberOfBytesToLock,
|
IN OUT PSIZE_T NumberOfBytesToLock,
|
||||||
PSIZE_T NumberOfBytesLocked
|
IN ULONG MapType
|
||||||
);
|
);
|
||||||
|
|
||||||
NTSYSAPI
|
NTSYSAPI
|
||||||
|
@ -448,9 +448,9 @@ NTSTATUS
|
||||||
NTAPI
|
NTAPI
|
||||||
ZwUnlockVirtualMemory(
|
ZwUnlockVirtualMemory(
|
||||||
IN HANDLE ProcessHandle,
|
IN HANDLE ProcessHandle,
|
||||||
IN PVOID BaseAddress,
|
IN OUT PVOID *BaseAddress,
|
||||||
IN SIZE_T NumberOfBytesToUnlock,
|
IN OUT PSIZE_T NumberOfBytesToUnlock,
|
||||||
OUT PSIZE_T NumberOfBytesUnlocked OPTIONAL
|
IN ULONG MapType
|
||||||
);
|
);
|
||||||
|
|
||||||
NTSYSAPI
|
NTSYSAPI
|
||||||
|
|
|
@ -57,6 +57,12 @@ Author:
|
||||||
#define MMPFNUSE_DRIVERLOCKPAGE 10
|
#define MMPFNUSE_DRIVERLOCKPAGE 10
|
||||||
#define MMPFNUSE_KERNELSTACK 11
|
#define MMPFNUSE_KERNELSTACK 11
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lock/Unlock Virtuam Memory Flags
|
||||||
|
//
|
||||||
|
#define MAP_PROCESS 1
|
||||||
|
#define MAP_SYSTEM 2
|
||||||
|
|
||||||
#ifndef NTOS_MODE_USER
|
#ifndef NTOS_MODE_USER
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
1757
reactos/ntoskrnl/mm/ARM3/virtual.c
Normal file
1757
reactos/ntoskrnl/mm/ARM3/virtual.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -13,392 +13,8 @@
|
||||||
#define NDEBUG
|
#define NDEBUG
|
||||||
#include <debug.h>
|
#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 'wRmV'
|
|
||||||
|
|
||||||
/* PRIVATE FUNCTIONS **********************************************************/
|
/* PRIVATE FUNCTIONS **********************************************************/
|
||||||
|
|
||||||
LONG
|
|
||||||
MiGetExceptionInfo(EXCEPTION_POINTERS *ExceptionInfo, BOOLEAN * HaveBadAddress, ULONG_PTR * BadAddress)
|
|
||||||
{
|
|
||||||
PEXCEPTION_RECORD ExceptionRecord;
|
|
||||||
PAGED_CODE();
|
|
||||||
|
|
||||||
/* Assume default */
|
|
||||||
*HaveBadAddress = FALSE;
|
|
||||||
|
|
||||||
/* Get the exception record */
|
|
||||||
ExceptionRecord = ExceptionInfo->ExceptionRecord;
|
|
||||||
|
|
||||||
/* Look at the exception code */
|
|
||||||
if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
|
|
||||||
(ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
|
|
||||||
(ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
|
|
||||||
{
|
|
||||||
/* We can tell the address if we have more than one parameter */
|
|
||||||
if (ExceptionRecord->NumberParameters > 1)
|
|
||||||
{
|
|
||||||
/* Return the address */
|
|
||||||
*HaveBadAddress = TRUE;
|
|
||||||
*BadAddress = ExceptionRecord->ExceptionInformation[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Continue executing the next handler */
|
|
||||||
return EXCEPTION_EXECUTE_HANDLER;
|
|
||||||
}
|
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
MiDoMappedCopy(IN PEPROCESS SourceProcess,
|
|
||||||
IN PVOID SourceAddress,
|
|
||||||
IN PEPROCESS TargetProcess,
|
|
||||||
OUT PVOID TargetAddress,
|
|
||||||
IN SIZE_T BufferSize,
|
|
||||||
IN KPROCESSOR_MODE PreviousMode,
|
|
||||||
OUT PSIZE_T ReturnSize)
|
|
||||||
{
|
|
||||||
PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
|
|
||||||
PMDL Mdl = (PMDL)MdlBuffer;
|
|
||||||
SIZE_T TotalSize, CurrentSize, RemainingSize;
|
|
||||||
volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving;
|
|
||||||
volatile BOOLEAN PagesLocked;
|
|
||||||
PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
|
|
||||||
volatile PVOID MdlAddress;
|
|
||||||
KAPC_STATE ApcState;
|
|
||||||
BOOLEAN HaveBadAddress;
|
|
||||||
ULONG_PTR BadAddress;
|
|
||||||
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 */
|
|
||||||
_SEH2_TRY
|
|
||||||
{
|
|
||||||
/* If this is our first time, probe the buffer */
|
|
||||||
if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
|
|
||||||
{
|
|
||||||
/* Catch a failure here */
|
|
||||||
FailedInProbe = TRUE;
|
|
||||||
|
|
||||||
/* Do the probe */
|
|
||||||
ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
|
|
||||||
|
|
||||||
/* Passed */
|
|
||||||
FailedInProbe = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize and probe and lock the MDL */
|
|
||||||
MmInitializeMdl (Mdl, CurrentAddress, CurrentSize);
|
|
||||||
MmProbeAndLockPages (Mdl, PreviousMode, IoReadAccess);
|
|
||||||
PagesLocked = TRUE;
|
|
||||||
|
|
||||||
/* Now map the pages */
|
|
||||||
MdlAddress = MmMapLockedPagesSpecifyCache(Mdl,
|
|
||||||
KernelMode,
|
|
||||||
MmCached,
|
|
||||||
NULL,
|
|
||||||
FALSE,
|
|
||||||
HighPagePriority);
|
|
||||||
if (!MdlAddress)
|
|
||||||
{
|
|
||||||
/* Use our SEH handler to pick this up */
|
|
||||||
FailedInMapping = TRUE;
|
|
||||||
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now let go of the source and grab to the target process */
|
|
||||||
KeUnstackDetachProcess(&ApcState);
|
|
||||||
KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
|
|
||||||
|
|
||||||
/* Check if this is our first time through */
|
|
||||||
if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
|
|
||||||
{
|
|
||||||
/* Catch a failure here */
|
|
||||||
FailedInProbe = TRUE;
|
|
||||||
|
|
||||||
/* Do the probe */
|
|
||||||
ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
|
|
||||||
|
|
||||||
/* Passed */
|
|
||||||
FailedInProbe = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now do the actual move */
|
|
||||||
FailedInMoving = TRUE;
|
|
||||||
RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
|
|
||||||
}
|
|
||||||
_SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), &HaveBadAddress, &BadAddress))
|
|
||||||
{
|
|
||||||
/* Detach from whoever we may be attached to */
|
|
||||||
KeUnstackDetachProcess(&ApcState);
|
|
||||||
|
|
||||||
/* Check if we had mapped the pages */
|
|
||||||
if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl);
|
|
||||||
|
|
||||||
/* Check if we had locked the pages */
|
|
||||||
if (PagesLocked) MmUnlockPages(Mdl);
|
|
||||||
|
|
||||||
/* Check if we failed during the probe or mapping */
|
|
||||||
if ((FailedInProbe) || (FailedInMapping))
|
|
||||||
{
|
|
||||||
/* Exit */
|
|
||||||
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
_SEH2_YIELD(return STATUS_PARTIAL_COPY);
|
|
||||||
}
|
|
||||||
_SEH2_END;
|
|
||||||
|
|
||||||
/* Detach from target */
|
|
||||||
KeUnstackDetachProcess(&ApcState);
|
|
||||||
|
|
||||||
/* Unmap and unlock */
|
|
||||||
MmUnmapLockedPages(MdlAddress, Mdl);
|
|
||||||
MmUnlockPages(Mdl);
|
|
||||||
|
|
||||||
/* Update location and size */
|
|
||||||
RemainingSize -= CurrentSize;
|
|
||||||
CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
|
|
||||||
CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* All bytes read */
|
|
||||||
*ReturnSize = BufferSize;
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
MiDoPoolCopy(IN PEPROCESS SourceProcess,
|
|
||||||
IN PVOID SourceAddress,
|
|
||||||
IN PEPROCESS TargetProcess,
|
|
||||||
OUT PVOID TargetAddress,
|
|
||||||
IN SIZE_T BufferSize,
|
|
||||||
IN KPROCESSOR_MODE PreviousMode,
|
|
||||||
OUT PSIZE_T ReturnSize)
|
|
||||||
{
|
|
||||||
UCHAR StackBuffer[MI_POOL_COPY_BYTES];
|
|
||||||
SIZE_T TotalSize, CurrentSize, RemainingSize;
|
|
||||||
volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE;
|
|
||||||
PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
|
|
||||||
PVOID PoolAddress;
|
|
||||||
KAPC_STATE ApcState;
|
|
||||||
BOOLEAN HaveBadAddress;
|
|
||||||
ULONG_PTR BadAddress;
|
|
||||||
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
|
|
||||||
{
|
|
||||||
/* If this is our first time, probe the buffer */
|
|
||||||
if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
|
|
||||||
{
|
|
||||||
/* Catch a failure here */
|
|
||||||
FailedInProbe = TRUE;
|
|
||||||
|
|
||||||
/* Do the probe */
|
|
||||||
ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
|
|
||||||
|
|
||||||
/* Passed */
|
|
||||||
FailedInProbe = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do the copy */
|
|
||||||
RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
|
|
||||||
|
|
||||||
/* Now let go of the source and grab to the target process */
|
|
||||||
KeUnstackDetachProcess(&ApcState);
|
|
||||||
KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
|
|
||||||
|
|
||||||
/* Check if this is our first time through */
|
|
||||||
if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
|
|
||||||
{
|
|
||||||
/* Catch a failure here */
|
|
||||||
FailedInProbe = TRUE;
|
|
||||||
|
|
||||||
/* Do the probe */
|
|
||||||
ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
|
|
||||||
|
|
||||||
/* Passed */
|
|
||||||
FailedInProbe = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now do the actual move */
|
|
||||||
FailedInMoving = TRUE;
|
|
||||||
RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
|
|
||||||
}
|
|
||||||
_SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), &HaveBadAddress, &BadAddress))
|
|
||||||
{
|
|
||||||
/* Detach from whoever we may be attached to */
|
|
||||||
KeUnstackDetachProcess(&ApcState);
|
|
||||||
|
|
||||||
/* Check if we had allocated pool */
|
|
||||||
if (HavePoolAddress) ExFreePool(PoolAddress);
|
|
||||||
|
|
||||||
/* Check if we failed during the probe */
|
|
||||||
if (FailedInProbe)
|
|
||||||
{
|
|
||||||
/* Exit */
|
|
||||||
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
_SEH2_YIELD(return STATUS_PARTIAL_COPY);
|
|
||||||
}
|
|
||||||
_SEH2_END;
|
|
||||||
|
|
||||||
/* Detach from target */
|
|
||||||
KeUnstackDetachProcess(&ApcState);
|
|
||||||
|
|
||||||
/* Update location and size */
|
|
||||||
RemainingSize -= CurrentSize;
|
|
||||||
CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
|
|
||||||
CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we had allocated pool */
|
|
||||||
if (HavePoolAddress) ExFreePool(PoolAddress);
|
|
||||||
|
|
||||||
/* All bytes read */
|
|
||||||
*ReturnSize = BufferSize;
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
MmCopyVirtualMemory(IN PEPROCESS SourceProcess,
|
|
||||||
IN PVOID SourceAddress,
|
|
||||||
IN PEPROCESS TargetProcess,
|
|
||||||
OUT PVOID TargetAddress,
|
|
||||||
IN SIZE_T BufferSize,
|
|
||||||
IN KPROCESSOR_MODE PreviousMode,
|
|
||||||
OUT PSIZE_T ReturnSize)
|
|
||||||
{
|
|
||||||
NTSTATUS Status;
|
|
||||||
PEPROCESS Process = SourceProcess;
|
|
||||||
|
|
||||||
/* Don't accept zero-sized buffers */
|
|
||||||
if (!BufferSize) return STATUS_SUCCESS;
|
|
||||||
|
|
||||||
/* If we are copying from ourselves, lock the target instead */
|
|
||||||
if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess;
|
|
||||||
|
|
||||||
/* Acquire rundown protection */
|
|
||||||
if (!ExAcquireRundownProtection(&Process->RundownProtect))
|
|
||||||
{
|
|
||||||
/* Fail */
|
|
||||||
return STATUS_PROCESS_IS_TERMINATING;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* See if we should use the pool copy */
|
|
||||||
if (BufferSize > MI_POOL_COPY_BYTES)
|
|
||||||
{
|
|
||||||
/* Use MDL-copy */
|
|
||||||
Status = MiDoMappedCopy(SourceProcess,
|
|
||||||
SourceAddress,
|
|
||||||
TargetProcess,
|
|
||||||
TargetAddress,
|
|
||||||
BufferSize,
|
|
||||||
PreviousMode,
|
|
||||||
ReturnSize);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Do pool copy */
|
|
||||||
Status = MiDoPoolCopy(SourceProcess,
|
|
||||||
SourceAddress,
|
|
||||||
TargetProcess,
|
|
||||||
TargetAddress,
|
|
||||||
BufferSize,
|
|
||||||
PreviousMode,
|
|
||||||
ReturnSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Release the lock */
|
|
||||||
ExReleaseRundownProtection(&Process->RundownProtect);
|
|
||||||
return Status;
|
|
||||||
}
|
|
||||||
|
|
||||||
NTSTATUS FASTCALL
|
NTSTATUS FASTCALL
|
||||||
MiQueryVirtualMemory(IN HANDLE ProcessHandle,
|
MiQueryVirtualMemory(IN HANDLE ProcessHandle,
|
||||||
IN PVOID Address,
|
IN PVOID Address,
|
||||||
|
@ -721,323 +337,8 @@ MiUnmapLockedPagesInUserSpace(IN PVOID BaseAddress,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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 ***************************************************************/
|
/* SYSTEM CALLS ***************************************************************/
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
NtReadVirtualMemory(IN HANDLE ProcessHandle,
|
|
||||||
IN PVOID BaseAddress,
|
|
||||||
OUT PVOID Buffer,
|
|
||||||
IN SIZE_T NumberOfBytesToRead,
|
|
||||||
OUT PSIZE_T NumberOfBytesRead OPTIONAL)
|
|
||||||
{
|
|
||||||
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
||||||
PEPROCESS Process;
|
|
||||||
NTSTATUS Status;
|
|
||||||
SIZE_T BytesRead = 0;
|
|
||||||
PAGED_CODE();
|
|
||||||
|
|
||||||
/* Check if we came from user mode */
|
|
||||||
if (PreviousMode != KernelMode)
|
|
||||||
{
|
|
||||||
/* Validate the read addresses */
|
|
||||||
if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
|
|
||||||
(((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
|
|
||||||
(((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
|
|
||||||
(((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
|
|
||||||
{
|
|
||||||
/* Don't allow to write into kernel space */
|
|
||||||
return STATUS_ACCESS_VIOLATION;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enter SEH for probe */
|
|
||||||
_SEH2_TRY
|
|
||||||
{
|
|
||||||
/* Probe the output value */
|
|
||||||
if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
|
|
||||||
}
|
|
||||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
||||||
{
|
|
||||||
/* Return the exception code */
|
|
||||||
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
||||||
}
|
|
||||||
_SEH2_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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 SIZE_T NumberOfBytesToWrite,
|
|
||||||
OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
|
|
||||||
{
|
|
||||||
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
||||||
PEPROCESS Process;
|
|
||||||
NTSTATUS Status;
|
|
||||||
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 */
|
|
||||||
_SEH2_TRY
|
|
||||||
{
|
|
||||||
/* Probe the output value */
|
|
||||||
if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
|
|
||||||
}
|
|
||||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
||||||
{
|
|
||||||
/* Return the exception code */
|
|
||||||
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
||||||
}
|
|
||||||
_SEH2_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
||||||
{
|
|
||||||
/* Handle exception */
|
|
||||||
Status = _SEH2_GetExceptionCode();
|
|
||||||
}
|
|
||||||
_SEH2_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return status */
|
|
||||||
return Status;
|
|
||||||
}
|
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
NtProtectVirtualMemory(IN HANDLE ProcessHandle,
|
|
||||||
IN OUT PVOID *UnsafeBaseAddress,
|
|
||||||
IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
|
|
||||||
IN ULONG NewAccessProtection,
|
|
||||||
OUT PULONG UnsafeOldAccessProtection)
|
|
||||||
{
|
|
||||||
PEPROCESS Process;
|
|
||||||
ULONG OldAccessProtection;
|
|
||||||
ULONG Protection;
|
|
||||||
PVOID BaseAddress = NULL;
|
|
||||||
SIZE_T NumberOfBytesToProtect = 0;
|
|
||||||
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
||||||
NTSTATUS Status;
|
|
||||||
PAGED_CODE();
|
|
||||||
|
|
||||||
/* Check for valid protection flags */
|
|
||||||
Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
|
|
||||||
if (Protection != PAGE_NOACCESS &&
|
|
||||||
Protection != PAGE_READONLY &&
|
|
||||||
Protection != PAGE_READWRITE &&
|
|
||||||
Protection != PAGE_WRITECOPY &&
|
|
||||||
Protection != PAGE_EXECUTE &&
|
|
||||||
Protection != PAGE_EXECUTE_READ &&
|
|
||||||
Protection != PAGE_EXECUTE_READWRITE &&
|
|
||||||
Protection != PAGE_EXECUTE_WRITECOPY)
|
|
||||||
{
|
|
||||||
return STATUS_INVALID_PAGE_PROTECTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we came from user mode */
|
|
||||||
if (PreviousMode != KernelMode)
|
|
||||||
{
|
|
||||||
/* Enter SEH for probing */
|
|
||||||
_SEH2_TRY
|
|
||||||
{
|
|
||||||
/* Validate all outputs */
|
|
||||||
ProbeForWritePointer(UnsafeBaseAddress);
|
|
||||||
ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
|
|
||||||
ProbeForWriteUlong(UnsafeOldAccessProtection);
|
|
||||||
|
|
||||||
/* Capture them */
|
|
||||||
BaseAddress = *UnsafeBaseAddress;
|
|
||||||
NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
|
|
||||||
}
|
|
||||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
||||||
{
|
|
||||||
/* Return the exception code */
|
|
||||||
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
||||||
}
|
|
||||||
_SEH2_END;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
NTSTATUS NTAPI
|
NTSTATUS NTAPI
|
||||||
NtQueryVirtualMemory(IN HANDLE ProcessHandle,
|
NtQueryVirtualMemory(IN HANDLE ProcessHandle,
|
||||||
IN PVOID Address,
|
IN PVOID Address,
|
||||||
|
@ -1202,92 +503,4 @@ NtQueryVirtualMemory(IN HANDLE ProcessHandle,
|
||||||
return(Status);
|
return(Status);
|
||||||
}
|
}
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
NtLockVirtualMemory(IN HANDLE ProcessHandle,
|
|
||||||
IN PVOID BaseAddress,
|
|
||||||
IN SIZE_T NumberOfBytesToLock,
|
|
||||||
OUT PSIZE_T NumberOfBytesLocked OPTIONAL)
|
|
||||||
{
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
if (NumberOfBytesLocked) *NumberOfBytesLocked = 0;
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
|
|
||||||
IN PVOID BaseAddress,
|
|
||||||
IN SIZE_T NumberOfBytesToUnlock,
|
|
||||||
OUT PSIZE_T NumberOfBytesUnlocked OPTIONAL)
|
|
||||||
{
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
if (NumberOfBytesUnlocked) *NumberOfBytesUnlocked = 0;
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
NtFlushVirtualMemory(IN HANDLE ProcessHandle,
|
|
||||||
IN OUT PVOID *BaseAddress,
|
|
||||||
IN OUT PSIZE_T NumberOfBytesToFlush,
|
|
||||||
OUT PIO_STATUS_BLOCK IoStatusBlock)
|
|
||||||
{
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @unimplemented
|
|
||||||
*/
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
NtGetWriteWatch(IN HANDLE ProcessHandle,
|
|
||||||
IN ULONG Flags,
|
|
||||||
IN PVOID BaseAddress,
|
|
||||||
IN ULONG RegionSize,
|
|
||||||
IN PVOID *UserAddressArray,
|
|
||||||
OUT PULONG EntriesInUserAddressArray,
|
|
||||||
OUT PULONG Granularity)
|
|
||||||
{
|
|
||||||
if (!EntriesInUserAddressArray || !Granularity)
|
|
||||||
{
|
|
||||||
return STATUS_ACCESS_VIOLATION;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!*EntriesInUserAddressArray || !RegionSize)
|
|
||||||
{
|
|
||||||
return STATUS_INVALID_PARAMETER;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!UserAddressArray)
|
|
||||||
{
|
|
||||||
return STATUS_ACCESS_VIOLATION;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* HACK: Set granularity to PAGE_SIZE */
|
|
||||||
*Granularity = PAGE_SIZE;
|
|
||||||
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
return STATUS_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @unimplemented
|
|
||||||
*/
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
NtResetWriteWatch(IN HANDLE ProcessHandle,
|
|
||||||
IN PVOID BaseAddress,
|
|
||||||
IN ULONG RegionSize)
|
|
||||||
{
|
|
||||||
if (!RegionSize)
|
|
||||||
{
|
|
||||||
return STATUS_INVALID_PARAMETER;
|
|
||||||
}
|
|
||||||
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
return STATUS_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* EOF */
|
/* EOF */
|
||||||
|
|
|
@ -394,6 +394,7 @@
|
||||||
<file>pool.c</file>
|
<file>pool.c</file>
|
||||||
<file>procsup.c</file>
|
<file>procsup.c</file>
|
||||||
<file>syspte.c</file>
|
<file>syspte.c</file>
|
||||||
|
<file>virtual.c</file>
|
||||||
</directory>
|
</directory>
|
||||||
<file>anonmem.c</file>
|
<file>anonmem.c</file>
|
||||||
<file>balance.c</file>
|
<file>balance.c</file>
|
||||||
|
|
Loading…
Reference in a new issue