Windows应用与内核内存共享

我们知道,进程之间如果要进行数据,是可以通过共享内存来实现的。
其原理就是将同一片物理内存,分别进行不同的线性映射,这样就可以得到2个线性地址,而这两个线地址址对应的空间就是物理内存的空间。
这样,如果我们在各自的进程分别读写内存,另一个进程中对应的线性地址空间的数据同时也会变化。
原理如下图:

映射原理

相同的进程是由于地址空间的隔离,所以进程之间传递数据使用共享内存的方式。

在驱动中,我们进行应用与驱动层是通过函数ReadFile,WriteFile,DeviceIoControl进行应用与驱动层的传递的。但数据之间的拷贝或地址映射对于我们来说是不可见的,由系统自动完成,而我们只需要在创建设备或者设置IOCTL_CODE码时指定相应的方式即可,如BUFFER,DIRECT_IO或NEITHER方式。
但由于每次进行数据传递都需要进行一次系统调用,那么有没有像共享内存的方式不进行系统调用呢?
答案是肯定的。

原理

原理上和共享内存一样,我们只需要对同一片物理地址空间进行不同的线性地址映射即可,即分别进行该进程的应用层地址映射和内核层映射即可。

所以进程的内核层是共享的。

方法如下:

第一步就是先在内存中创建一片地址空间,这个我们可以通过内存分配函数ExAllocatePoolWithTag或ExAllocatePool来实现。这样就有了内核层地址空间。
第二步是对刚创建的内存分配MDL,我们可以通过函数IoAllocateMdl来实现,并使用函数MmBuildMdlForNonPagedPool内存关联。

在Windows内核中,内存的管理是通过MDL来实现的。

第三步使用函数MmMapLockedPagesSpecifyCache对指定的MDL进行线性映射,这里我们可以指定为应用层。

返回的这个地址是应用层的地址,在内核层是不可用的,如果不在进程空间中,会出现蓝屏的。
如果指定为内核,即对同一步物理地址空间进行了不同的内核映射,这样可以对一些只读的内存空间进行读写,这如果说开了就是内核的IAT或者PATCH。

最后,我们可以通过一个DeviceIoControl将创建的地址返回给应用层。这样应用层就可以使用这个地址进行数据读写了。
假如我们创建一个700k的共享内存空间,代码如下:

deviceExtension->nShareMemoryLen = 1024 * 700;
deviceExtension->pKernelShareMemory = ExAllocatePoolWithTag(NonPagedPool, deviceExtension->nShareMemoryLen, 'byte');

...

 deviceExtension->pShareMdl = IoAllocateMdl(deviceExtension->pKernelShareMemory, deviceExtension->nShareMemoryLen, FALSE, FALSE, NULL);
...

MmBuildMdlForNonPagedPool(deviceExtension->pShareMdl);

deviceExtension->pUserShareMemory = MmMapLockedPagesSpecifyCache(deviceExtension->pShareMdl, UserMode, MmNonCached, NULL, FALSE, NormalPagePriority);
...

释放

当不再需要进行共享内存时,就需要释放。
释放时只需要将新创建的MDL和内存空间释放即可。

 IoFreeMdl(deviceExtension->pShareMdl);
 ExFreePoolWithTag(deviceExtension->pKernelShareMemory,'byte')
  • USB基础
  • USB摄像头UVC
  • USB人机交互HID
  • USB音频UAC
  • Windows基础
  • 磁盘与文件系统
  • Windows编程
  • Windows驱动
  • 开发模块
  • Windows运维
  • Linux相关
  • C语言学习
  • 高级语言
  • 前端开发
  • 服务器开发
  • 数据库
  • 字节流笔记
  • 字节流
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

    打开支付宝扫一扫,即可进行扫码打赏哦

    Powered by bytekits.com,汇天下文字,成非凡梦想!!!