Reading User-Mode Process Memory from Kernel-Mode Code in Windows

By | April 19, 2025

One of the popular techniques is based on the usage of a pair of functions:

  • PsLookupProcessByProcessId (for retrieving EPROCESS structure)
  • KeStackAttachProcess with KeUnstackDetachProcess

This technique is used by both Antiviruses and Rootkits. So the final snippet code could be as follows

NTSTATUS
ReadProcessMemorySafe(
    HANDLE targetProcessId,
    LARGE_INTEGER memoryOffset,
    PVOID destinationBuffer,
    ULONG bufferLength,
    PULONG bytesRead
)
{
    if (!targetProcessId || !destinationBuffer || bufferLength == 0)
        return STATUS_INVALID_PARAMETER;

    if (bytesRead)
        *bytesRead = 0;

    PEPROCESS targetProcess = NULL;
    NTSTATUS status = PsLookupProcessByProcessId(targetProcessId, &targetProcess);
    if (!NT_SUCCESS(status))
        return status;

    SIZE_T localBytesRead = 0;

    status = MmCopyVirtualMemory(
        targetProcess,                          // Source process
        (PVOID)(memoryOffset.QuadPart),         // Source address
        PsGetCurrentProcess(),                  // Target is the kernel-mode caller
        destinationBuffer,                      // Target buffer
        bufferLength,                           // Number of bytes to read
        KernelMode,                             // Running in kernel mode
        &localBytesRead                         // Out: actual number of bytes copied
    );

    ObDereferenceObject(targetProcess);

    if (NT_SUCCESS(status) && bytesRead)
        *bytesRead = (ULONG)localBytesRead;

    return status;
}



The careful reader could notice that, unlike ReadProcessMemory(), which is subject to user-mode process protections and access control, kernel-mode code operates at a higher privilege level and can read any address in a user-mode process.

Leave a Reply