AlexF
AlexF

Reputation: 497

Accessing kernel memory from user mode (Windows)

I'm writing a driver that needs to allocate a Non Paged pool of memory and this memory, for performance sake, must be directly accessible from a usermode program.

In the driver entry I've allocated some memory with these two type of methods:

pMdl = IoAllocateMdl(NULL,
                     4096,
                     FALSE,
                     FALSE,
                     NULL);
if(!pMdl) {
    DbgPrintEx(DPFLTR_IHVVIDEO_ID, DPFLTR_INFO_LEVEL, "Error on IoAllocateMdl. Returning from driver early.\n");
    return STATUS_INSUFFICIENT_RESOURCES;
}
MmBuildMdlForNonPagedPool(pMdl);
userMemory = (void *)MmMapLockedPagesSpecifyCache(pMdl, UserMode, MmWriteCombined, NULL, FALSE, LowPagePriority);

and

userMemory = ExAllocatePoolWithTag(
                NonPagedPool,
                4096,
                POOL_TAG);

Now I don't want to issue a DeviceIoControl every time I need to write/read from this memory, but instead I want to do something like this:

char* sharedMem;
.....
transactionResult = DeviceIoControl ( hDevice,
                        (DWORD) IOCTL_MMAP,
                        NULL,
                        0,
                        sharedMem,
                        sizeof(int),
                        &bRetur,
                        NULL
                        );
.....
sharedMem[0]='c';

Using a DeviceIoControl to get the address in kernel memory and then using it directly, like it were an mmap under Linux.

Is there some kind of way to do this in Windows?

I've done this:

hMapFile = OpenFileMapping(
                FILE_MAP_ALL_ACCESS,     // Read/write access
                TRUE,
                "Global\\SharedMemory"); // Name of mapping object

lastError = GetLastError();
if (hMapFile == NULL)
{
    printf("Could not create file mapping object (%d).\n" ,GetLastError());
    return 1;
}
pBuf = (char*)MapViewOfFile(hMapFile,            // Handle to map object
                            FILE_MAP_ALL_ACCESS, // Read/write permission
                            0,
                            0,
                            4096);

if (pBuf == NULL)
{
    printf("Could not map view of file (%d).\n", GetLastError());
    CloseHandle(hMapFile);
    return 1;
}
pBuf[0] = 'c';
pBuf[1] = '\n';
CloseHandle(hMapFile);

And I've created the view in Kernel like this:

RtlInitUnicodeString(&name, L"\\BaseNamedObjects\\SharedMemory");
InitializeObjectAttributes(&oa, &name, 0, 0, NULL);
ZwCreateSection(&hsection, SECTION_ALL_ACCESS, &oa, &Li, PAGE_READWRITE, SEC_COMMIT, NULL);

ZwMapViewOfSection(hsection, NtCurrentProcess(),
                   &userMem, 0, MEM_WIDTH, NULL,
                   &j, ViewShare, 0, PAGE_READWRITE);

But in the kernel when I read the memory it's empty: how can it be?

Upvotes: 4

Views: 5502

Answers (1)

AlexF
AlexF

Reputation: 497

I finally understood how this needs to work.

First I've created a structure like the following.

typedef struct _MEMORY_ENTRY
{
    PVOID pBuffer;
} MEMORY_ENTRY, *PMEMORY_ENTRY;

This will be used to return the virtual address from the kernel space to the user space.

In the DriverEntry I used

userMem = ExAllocatePoolWithTag(NonPagedPool,
                                MEM_WIDTH,
                                POOL_TAG );

to set up the NonPaged memory.

Then I've created an IOCTL working in DIRECT_OUT mode that does the following snippet:

...
PMDL         mdl = NULL;
PVOID        buffer = NULL;
MEMORY_ENTRY returnedValue;
 void*       UserVirtualAddress = NULL;
...
buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); // Gets safely the pointer for the output in the IRP
mdl = IoAllocateMdl(userMem, MEM_WIDTH, FALSE, FALSE, NULL); // Allocate the memory descriptor list
MmBuildMdlForNonPagedPool(mdl); // This is needed when we're managing NonPaged memory
UserVirtualAddress = MmMapLockedPagesSpecifyCache(
                         mdl,
                         UserMode,
                         MmNonCached,
                         NULL,
                         FALSE,
                         NormalPagePriority); // Return the virtual address in the context of
                                              // the user space program who called the IOCTL

returnedValue.pBuffer = UserVirtualAddress;
RtlCopyMemory(buffer,
            &returnedValue,
            sizeof(PVOID)); // I copy the virtual address in the structure that will
                            // be returned to the user mode program by the IRP

In the user mode program I just needed to to this

transactionResult = DeviceIoControl(
                        hDevice,
                        (DWORD) IOCTL_MMAP,
                        NULL,
                        0,
                        sharedMem,
                        sizeof(void*),
                        &bRetur,
                        NULL
                        );

In (MEMORY_ENTRY*)sharedMem->pBuffer we will find the memory area created and shared by the kernel space directly accessible by the kernel and by the user program.

I haven't wrote it but we need to remember to wrap the entire MmGetSystemAddressForMdlSafe(...)----->RtlCopyMemory(...) in a Try...Except block because we can encounter various problems here that may eventually cause a BugCheck so better be safe than sorry. Anyway, if you're compiling this kind of code in a checked environment the Microsoft AutocodeReview will be pointing this out.

If someone needs more clarifications, or if I wrote something wrong just let me know and I will be happy to modify this post.

Upvotes: 5

Related Questions