3
votes

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?

1
Ok, I've tried with the "ZwCreateSection" etc, and in user I've used the "MapViewOfFile". The map is opened in the user program, I can write in it but when I read it from kernel there is nothing of what I wrote. I've edited the question.AlexF
Are you mapping the view, and reading the memory, from a system thread? (Note also that since you wanted non-paged memory, using a section may not be appropriate. You might want to use MmMapLockedPagesSpecifyCache instead.)Harry Johnston
It's just that you said you needed non-paged memory, and a section is paged memory by definition. Also, the fact that you asked for non-paged memory suggests that your driver may need access to the memory in an arbitrary context, but using a section means your driver can only access it in the context of a system thread.Harry Johnston
I think you will need MmBuildMdlForNonPagedPool as well, but yes, that's the basic idea. I've never tried it myself, mind you.Harry Johnston

1 Answers

4
votes

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.