
Greetings to all!
This small article will discuss one way to create shared memory, which can be accessed from both kernel mode and user mode. I will give examples of functions for allocating and freeing memory, and there will also be links to the sources so that anyone can try.
The driver was going under a 32-bit Windows XP OS (I also checked its operation and
in 32-bit Windows 7).')
I will not fully describe the development of the driver, starting with the installation of the WDK (DDK), the selection of development tools, the writing of standard driver functions, etc. (if you wish, you can read
here and
there , although there is also not much information). So that the article does not turn out to be too bloated, I will describe only the way to implement shared memory.
A bit of theory
The driver does not create a special program flow to execute its code, but runs in the context of the thread that is currently active. Therefore, it is considered that the driver is executed in the
context of an arbitrary thread (
Context Switching ). It is very important that when mapping the allocated memory to the user address space, we are in the context of the application thread that will manage our driver. In this case, this rule is observed, since the driver is single-level and we access it using the
IRP_MJ_DEVICE_CONTROL request, hence the flow context will not switch and we will have access to the address space of our application.
Memory allocation
#pragma LOCKEDCODE NTSTATUS AllocateSharedMemory(PDEVICE_EXTENSION pdx, PIRP Irp) {
parsing function in parts:Save the pointer, with which we pass the pointer to the allocated memory to our application:
pdx->vaReturned = (unsigned short **) GenericGetSystemAddressForMdl(Irp->MdlAddress);
The next step is to allocate unmovable physical memory with a size of memory_size and build on its basis an MDL (
Memory Descriptor List ) structure, the pointer to which is stored in the variable pdx-> mdl:
pdx->mdl = MmAllocatePagesForMdl(pstart, pstop, pskip, memory_size);

As you can see from the image, we need the MDL structure to describe the fixed physical pages.
Then we get the range of virtual addresses for MDL in the system address space and store a pointer to these addresses in the variable pdx-> kernel_va:
pdx->kernel_va = (unsigned short*) MmGetSystemAddressForMdlSafe(pdx->mdl, NormalPagePriority);
This function will return a pointer by which we can access the allocated memory in the driver (and regardless of the current thread context, since the addresses are obtained from the system address space).
In the cycle, we write the first 10 memory cells with numbers from 10 to 1, so that we can check the availability of the allocated memory from the user mode:
for (int i = 0; i < 10; ++i) { pdx->kernel_va[i] = 10 - i;
Now you need to display the allocated memory in the address space of the application that addressed the driver:
pdx->user_va = (unsigned short*) MmMapLockedPagesSpecifyCache(pdx->mdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority);
The variable pdx-> vaReturned is a pointer to a pointer and is declared in the pdx structure (see driver.h in the source_driver folder). Using it, we pass the pointer pdx-> user_va to the application:
*pdx->vaReturned = pdx->user_va;
Memory release
#pragma LOCKEDCODE NTSTATUS ReleaseSharedMemory(PDEVICE_EXTENSION pdx, PIRP Irp) {
This is where the address space of the application is released:
MmUnmapLockedPages(pdx->user_va, pdx->mdl);
system address space:
MmUnmapLockedPages(pdx->kernel_va, pdx->mdl);
then the physical pages are released:
MmFreePagesFromMdl(pdx->mdl);
and kill MDL:
IoFreeMdl(pdx->mdl);
We are accessing the driver from user mode
(The entire application code, see the attached materials)The first thing to do is get the device handle (handle) using the
CreateFile () function:
hDevice = CreateFile(L"\\\\.\\SharedMemory", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
Then you need to send an I / O request to the driver using the
DeviceIoControl () function:
unsigned short** vaReturned = new unsigned short*(); ioCtlCode = IOCTL_ALLOCATE_SHARED_MEMORY; checker = DeviceIoControl(hDevice, ioCtlCode, NULL, 0, vaReturned, sizeof(int), &bytesReturned, NULL);
The function call is converted to an IRP packet, which will be processed in the dispatcher function of the driver (see DispatchControl () in the driver's control.cpp file). Those. when you call DeviceIoControl (), the control is transferred to the driver function, the code of which was described above. Also, when you call the DeviceIoControl () function in the DebugView program (you need to check the box so that it catches kernel-mode events), we see the following:

Upon returning control to the application, the vaReturned variable will point to shared memory (more precisely, it will point to a pointer that will already point to memory). Let's make a slight simplification to get the usual pointer to memory:
unsigned short* data = *vaReturned;
Now, by the
data pointer, we have access to shared memory from the application:

When you click on the “Allocate memory” button, the application transfers control to the driver, which performs all the actions described above, and returns a pointer to the allocated memory, which can be accessed from the application through the
data pointer. Using the “Fill TextEdit” button, we display the contents of the first 10 elements that were filled in the driver in QTextEdit and we see successful access to shared memory.
When you click on the "Release memory" button, the memory is released and the created MDL structure is deleted.
Sources
1.
source_driver.rar .
2.
source_app.rar .
3.
source_generic_oney .
For the basis of the driver (source_driver) I took one of the examples from Walter They (examples are attached to his book "Using the Microsoft Windows Driver Model"). It is also necessary to download the Generic core library, since This library is needed both when building and when the driver is running.
Those who want to try it themselves
Create a directory (eg, C: \ Drivers) and unpack the source files there (source_driver, source_generic_oney and source_app). If you do not rebuild the driver, it is enough to install the new hardware manually (specifying the inf file: sharedmemory.inf) through the Control Panel — installing the new hardware (for Windows XP). Then you need to run habr_app.exe (source_app / release).
If you decide to rebuild, then:
1. You need to install the
WDK .
2. First you need to rebuild the Generic library, since Depending on the OS version, folders with output files can be named differently (for example, for XP - objchk_wxp_x86, for Win7 - objchk_win7_x86).
3. After points 1 and 2, you can try to build the driver with the “build” command using x86 Checked Build Environment, included in the WDK.
Sources
- Articles from wasm.ru
- Walter They “Using the Microsoft Windows Driver Model” (ISBN 978-5-91180-057-4, 0735618038).
- Jeffrey Richter "Windows for professionals. Creating effective Win32 applications ”(ISBN 5-272-00384-5, 1-57231-996-8).
- msdn
UPD: Habrayuzer
DZhon provided the link
shared memory by means of Qt .