Copyright (c) Microsoft Corporation. All rights reserved.
You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.
Module Name:
readwrt.c
Abstract:
This module contains the routines which implement the capability
to read and write the virtual memory of a target process.
--*/
#include "mi.h"
//
// The maximum amount to try to Probe and Lock is 14 pages, this
// way it always fits in a 16 page allocation.
//
#define MAX_LOCK_SIZE ((ULONG)(14 * PAGE_SIZE))
//
// The maximum to move in a single block is 64k bytes.
//
#define MAX_MOVE_SIZE (LONG)0x10000
//
// The minimum to move is a single block is 128 bytes.
//
ULONG
MiGetExceptionInfo (
IN PEXCEPTION_POINTERS ExceptionPointers,
IN PLOGICAL ExceptionAddressConfirmed,
IN PULONG_PTR BadVa
);
NTSTATUS
MiDoMappedCopy (
IN PEPROCESS FromProcess,
IN CONST VOID *FromAddress,
IN PEPROCESS ToProcess,
OUT PVOID ToAddress,
IN SIZE_T BufferSize,
IN KPROCESSOR_MODE PreviousMode,
OUT PSIZE_T NumberOfBytesRead
);
NTSTATUS
MiDoPoolCopy (
IN PEPROCESS FromProcess,
IN CONST VOID *FromAddress,
IN PEPROCESS ToProcess,
OUT PVOID ToAddress,
IN SIZE_T BufferSize,
IN KPROCESSOR_MODE PreviousMode,
OUT PSIZE_T NumberOfBytesRead
);
//
// If the buffer size is not zero, then attempt to read data from the
// specified process address space into the current process address
// space.
//
BytesCopied = 0;
Status = STATUS_SUCCESS;
if (BufferSize != 0) {
//
// Reference the target process.
//
Status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_VM_READ,
PsProcessType,
PreviousMode,
(PVOID *)&Process,
NULL);
//
// If the process was successfully referenced, then attempt to
// read the specified memory either by direct mapping or copying
// through nonpaged pool.
//
if (Status == STATUS_SUCCESS) {
Status = MmCopyVirtualMemory (Process,
BaseAddress,
PsGetCurrentProcessByThread(CurrentThread),
Buffer,
BufferSize,
PreviousMode,
&BytesCopied);
//
// Dereference the target process.
//
ObDereferenceObject(Process);
}
}
//
// If requested, return the number of bytes read.
//
if (ARGUMENT_PRESENT(NumberOfBytesRead)) {
try {
*NumberOfBytesRead = BytesCopied;
//
// If the buffer size is not zero, then attempt to write data from the
// current process address space into the target process address space.
//
BytesCopied = 0;
Status = STATUS_SUCCESS;
if (BufferSize != 0) {
//
// Reference the target process.
//
Status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_VM_WRITE,
PsProcessType,
PreviousMode,
(PVOID *)&Process,
NULL);
//
// If the process was successfully referenced, then attempt to
// write the specified memory either by direct mapping or copying
// through nonpaged pool.
//
if (Status == STATUS_SUCCESS) {
Status = MmCopyVirtualMemory (PsGetCurrentProcessByThread(CurrentThread),
Buffer,
Process,
BaseAddress,
BufferSize,
PreviousMode,
&BytesCopied);
//
// Dereference the target process.
//
ObDereferenceObject(Process);
}
}
//
// If requested, return the number of bytes read.
//
if (ARGUMENT_PRESENT(NumberOfBytesWritten)) {
try {
*NumberOfBytesWritten = BytesCopied;
NTSTATUS
MmCopyVirtualMemory(
IN PEPROCESS FromProcess,
IN CONST VOID *FromAddress,
IN PEPROCESS ToProcess,
OUT PVOID ToAddress,
IN SIZE_T BufferSize,
IN KPROCESSOR_MODE PreviousMode,
OUT PSIZE_T NumberOfBytesCopied
)
{
NTSTATUS Status;
PEPROCESS ProcessToLock;
if (BufferSize == 0) {
ASSERT (FALSE); // No one should call with a zero size.
return STATUS_SUCCESS;
}
//
// Make sure the process still has an address space.
//
if (ExAcquireRundownProtection (&ProcessToLock->RundownProtect) == FALSE) {
return STATUS_PROCESS_IS_TERMINATING;
}
//
// If the buffer size is greater than the pool move threshold,
// then attempt to write the memory via direct mapping.
//
if (BufferSize > POOL_MOVE_THRESHOLD) {
Status = MiDoMappedCopy(FromProcess,
FromAddress,
ToProcess,
ToAddress,
BufferSize,
PreviousMode,
NumberOfBytesCopied);
//
// If the completion status is not a working quota problem,
// then finish the service. Otherwise, attempt to write the
// memory through nonpaged pool.
//
if (Status != STATUS_WORKING_SET_QUOTA) {
goto CompleteService;
}
*NumberOfBytesCopied = 0;
}
//
// There was not enough working set quota to write the memory via
// direct mapping or the size of the write was below the pool move
// threshold. Attempt to write the specified memory through nonpaged
// pool.
//
Status = MiDoPoolCopy(FromProcess,
FromAddress,
ToProcess,
ToAddress,
BufferSize,
PreviousMode,
NumberOfBytesCopied);
//
// Dereference the target process.
//
CompleteService:
//
// Indicate that the vm operation is complete.
//
ULONG
MiGetExceptionInfo (
IN PEXCEPTION_POINTERS ExceptionPointers,
IN OUT PLOGICAL ExceptionAddressConfirmed,
IN OUT PULONG_PTR BadVa
)
/*++
Routine Description:
This routine examines a exception record and extracts the virtual
address of an access violation, guard page violation, or in-page error.
Arguments:
ExceptionPointers - Supplies a pointer to the exception record.
ExceptionAddressConfirmed - Receives TRUE if the exception address was
reliably detected, FALSE if not.
BadVa - Receives the virtual address which caused the access violation.
Return Value:
EXECUTE_EXCEPTION_HANDLER
--*/
{
PEXCEPTION_RECORD ExceptionRecord;
PAGED_CODE();
//
// If the exception code is an access violation, guard page violation,
// or an in-page read error, then return the faulting address. Otherwise.
// return a special address value.
//
//
// The virtual address which caused the exception is the 2nd
// parameter in the exception information array.
//
// The number of parameters will be zero if an exception handler
// above us (like the one in MmProbeAndLockPages) caught the
// original exception and subsequently just raised status.
// This means the number of bytes copied is zero.
//
NTSTATUS
MiDoMappedCopy (
IN PEPROCESS FromProcess,
IN CONST VOID *FromAddress,
IN PEPROCESS ToProcess,
OUT PVOID ToAddress,
IN SIZE_T BufferSize,
IN KPROCESSOR_MODE PreviousMode,
OUT PSIZE_T NumberOfBytesRead
)
/*++
Routine Description:
This function copies the specified address range from the specified
process into the specified address range of the current process.
Arguments:
FromProcess - Supplies an open handle to a process object.
FromAddress - Supplies the base address in the specified process
to be read.
ToProcess - Supplies an open handle to a process object.
ToAddress - Supplies the address of a buffer which receives the
contents from the specified process address space.
BufferSize - Supplies the requested number of bytes to read from
the specified process.
PreviousMode - Supplies the previous processor mode.
NumberOfBytesRead - Receives the actual number of bytes
transferred into the specified buffer.
//
// Initializing BadVa & ExceptionAddressConfirmed is not needed for
// correctness but without it the compiler cannot compile this code
// W4 to check for use of uninitialized variables.
//
//
// Now operating in the context of the ToProcess.
//
if ((InVa == FromAddress) && (PreviousMode != KernelMode)){
Probing = TRUE;
ProbeForWrite (ToAddress, BufferSize, sizeof(CHAR));
Probing = FALSE;
}
NTSTATUS
MiDoPoolCopy (
IN PEPROCESS FromProcess,
IN CONST VOID *FromAddress,
IN PEPROCESS ToProcess,
OUT PVOID ToAddress,
IN SIZE_T BufferSize,
IN KPROCESSOR_MODE PreviousMode,
OUT PSIZE_T NumberOfBytesRead
)
/*++
Routine Description:
This function copies the specified address range from the specified
process into the specified address range of the current process.
Arguments:
ProcessHandle - Supplies an open handle to a process object.
BaseAddress - Supplies the base address in the specified process
to be read.
Buffer - Supplies the address of a buffer which receives the
contents from the specified process address space.
BufferSize - Supplies the requested number of bytes to read from
the specified process.
PreviousMode - Supplies the previous processor mode.
NumberOfBytesRead - Receives the actual number of bytes
transferred into the specified buffer.
MaximumMoved = MaximumMoved >> 1;
if (MaximumMoved <= sizeof(StackArray)) {
PoolArea = (PVOID)&StackArray[0];
break;
}
} while (TRUE);
}
//
// Initializing BadVa & ExceptionAddressConfirmed is not needed for
// correctness but without it the compiler cannot compile this code
// W4 to check for use of uninitialized variables.
//
BadVa = 0;
ExceptionAddressConfirmed = FALSE;
//
// Copy the data into pool, then copy back into the ToProcess.
//
对了,我差点忘了,你的程序逻辑上目前没有发现什么问题,但是有一点,你的这个程序通过 status = PsLookupProcessByProcessId(ProcessId, &Process);直接取得进程结构的指针,意味着,攻击者可以直接通过你的驱动访问任意权限的进程,这已经是一个权限提升漏洞,当然,如果这个程序是学习用的,也可以这样做,只不过如果以后是要编写发布版本的,你就不能这样做了!建议你的程序改为使用句柄来取得进程结构这样可以避免这个问题,我这只是个建议,要看看你是怎么玩的啦!当然,我给你的代码也有这个问题,哈哈!学习而已!