设为首页 加入收藏

TOP

驱动开发:通过MDL映射实现多次通信(一)
2023-07-23 13:31:54 】 浏览:111
Tags:通过 MDL 通信

在前几篇文章中LyShark通过多种方式实现了驱动程序与应用层之间的通信,这其中就包括了通过运用SystemBuf缓冲区通信,运用ReadFile读写通信,运用PIPE管道通信,以及运用ASYNC反向通信,这些通信方式在应对一收一发模式的时候效率极高,但往往我们需要实现一次性吐出多种数据,例如ARK工具中当我们枚举内核模块时,往往应用层例程中可以返回几条甚至是几十条结果,如下案例所示,这对于开发一款ARK反内核工具是必须要有的功能。

  • 那么如何实现如上述功能呢?

其实,实现这类功能可以从两个方面入手,但不论使用哪一种方式本质上都是预留一段缓冲区以此来给内核与应用层共享的区域,该区域内可用于交换数据,实现方式有两种要么在应用层分配空间,要么在内核中分配,LyShark先带大家在内核层实现,通过巧妙地运用MDL映射机制来实现通信需求。

  • MDL是什么呢?

MDL内存读写是最常用的一种读写模式,是用于描述物理地址页面的一个结构,简单的官方解释;内存描述符列表 (MDL) 是一个系统定义的结构,通过一系列物理地址描述缓冲区。执行直接I/O的驱动程序从I/O管理器接收一个MDL的指针,并通过MDL读写数据。一些驱动程序在执行直接I/O来满足设备I/O控制请求时也使用MDL。

通过运用MDL的方式对同一块物理内存同时映射到R0和R3,这样我们只需要使用DeviceIoControl向驱动发送一个指针,通过对指针进行读写就可以实现数据的交换,本人在网络上找到了如下两段被转载的烂大街的片段,这两段代码明显是存在缺陷的如果你也在寻找映射方法那么不要被这两段代码坑了,多数人也根本没有能力将其变为可用的,也就只能转载,不知道哪个大哥挖的坑。

用户态进程分配空间,内核态去映射。

// assume uva is a virtual address in user space, uva_size is its size
MDL * mdl = IoAllocateMdl(uva, uva_size, FALSE, FALSE, NULL);
ASSERT(mdl);
__try {
	MmProbeAndLockPages(mdl, UserMode, IoReadAccess);
} __except(EXCEPTION_EXECUTE_HANDLER) {
	DbgPrint("error code = %d", GetExceptionCode);
}
PVOID kva = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority);
// use kva 
// …
 
MmUnlockPages(mdl);
IoFreeMdl(mdl);

内核态分配空间,用户态进程去映射。

PVOID kva = ExAllocatePoolWithTag(NonPagedPool, 1024, (ULONG)'PMET');
MDL * mdl = IoAllocateMdl(uva, uva_size, FALSE, FALSE, NULL);
ASSERT(mdl);
__try {
	MmBuildMdlForNonPagedPool(mdl);
} __except(EXCEPTION_EXECUTE_HANDLER) {
	DbgPrint("error code = %d", GetExceptionCode);
}
 
PVOID uva = MmMapLockedPagesSpecifyCache(mdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority); 

如上的代码看看就好摘出来只是要提醒大家这个是无法使用的,如下将进入本篇文章的正题。

以内核中开辟空间为例,首先在代码中要做的就是定义一段非分页内存#define FILE_DEVICE_EXTENSION 4096这段区域用于给全局变量使用,其次我们需要传输结构体那么结构体中的成员就要事先定义好,例如此处使用StructAll来定义结构结构体成员变量如下所示,通过使用static将结构体定义为静态,预先空出1024的内存空间并初始化为0,当然了这种方式是存在弊端的,例如最大只支持1024个结构如果超过了则可能会溢出,当然最好的办法是用户空间开辟,在下次章节中再介绍。

// -------------------------------------------------
// MDL数据传递变量
// -------------------------------------------------

// 保存一段非分页内存,用于给全局变量使用
#define FILE_DEVICE_EXTENSION 4096

// 定义重复结构(循环传递)
typedef struct
{
  char username[256];
  char password[256];
  int count;
}StructAll;

static StructAll ptr[1024] = { 0 };

为了能够达到输出结构体的效果这里我定义一个ShowProcess用于模拟当前系统内进程数,并自动填充为特定的数据,此处结构体内部count成员则用于标注当前共有多少个结构体,用于在用户层读取判断,当然了这种方式的另一个弊端就是浪费空间,因为每一个结构体中都存在一个被填充为0的整数类型。但如果只是实现功能的话其实也不是那么重要。

// 模拟进程列表赋值测试
int ShowProcess(int process_count)
{
  memset(ptr, 0, sizeof(StructAll) * process_count);
  int x = 0;

  for (; x < process_count + 1; x++)
  {
    strcpy_s(ptr[x].username, 256, "lyshark");
    strcpy_s(ptr[x].password, 256, "123456");
  }

  // 设置总共有多少个结构体,并返回结构体个数
  ptr[0].count = x;
  return x;
}

内核态映射: 当定义好如上这些方法时,接下来就是最重要的驱动映射部分了,如下代码所示,首先当用户调用派遣时第一个执行的函数是ShowProcess()它用于获取到当前系统中有多少个进程,接着通过sizeof(MyData) * count计算出当前MyData需要分配的内存池大小并返回给pool_size,调用ExAllocatePool分配一块非分页内核空间,创建IoAllocateMdlMDL映射,将数据MmMapLockedPagesSpecifyCache映射到用户空间,最后将指针pShareMM_User返回给用户态。

  • ShowProcess(715) 获取当前进程数,并返回数量
  • sizeof(MyData) * count 计算得到结构体长度
  • ExAllocatePool(NonPagedPool, pool_size) 分配非分页内存,长度是pool_size
  • IoAllocateMdl() 分配MDL空间,并放入内核态
  • MmMapLockedPagesSpecifyCache() 将内核态指针映射
首页 上一页 1 2 3 4 5 6 下一页 尾页 1/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇P6818 [PA2013]Dzia?ka 题解 下一篇C++容器(vector、deque、list、m..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目