Windows内核驱动与DLL通信时模块遍历因Name成员触发蓝屏的问题排查
Windows内核驱动模块通信蓝屏问题记录
开发场景
正在开发Windows内核驱动,实现内核驱动与用户态DLL的双向通信,同时编写了模块快照snapshot、模块遍历walk两个功能模块函数,联调过程中出现随机蓝屏问题。
核心数据结构
通信交互使用的结构体定义如下:
struct SM_MSI { wchar_t* Name; SIZE_T Size; uintptr_t Address; };
功能函数实现
模块快照函数
快照函数核心执行逻辑:
- 根据预估的模块数量,申请
PagedPool内存作为模块列表的连续存储区 - 遍历进程PEB的加载顺序模块链表,逐一解析模块项
- 对符合长度要求的模块名,单独申请256宽字符长度的
PagedPool内存存储模块名称 - 填充模块大小、模块基地址字段后,将整条模块信息拷贝到连续存储区
对应代码片段:
ModulesList = ExAllocatePool(PagedPool, sizeof(SM_MSI) * index); if (ModulesList == NULL) // 校验内存分配是否成功 { return STATUS_UNSUCCESSFUL; } index = 0; for (PLIST_ENTRY pListEntry = (PLIST_ENTRY)((PPEB_LDR_DATA)pPeb->Ldr)->InLoadOrderModuleList.Flink; pListEntry != &((PPEB_LDR_DATA)pPeb->Ldr)->InLoadOrderModuleList; pListEntry = (PLIST_ENTRY)pListEntry->Flink) { PLDR_DATA_TABLE_ENTRY pEntry = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); if (pEntry->BaseDllName.Buffer > 0 && pEntry->BaseDllName.Length < 256) { ModulesListCount = ModulesListCount + 1; SM_MSI temp = { 0 }; temp.Name = (wchar_t*)ExAllocatePool(PagedPool, sizeof(wchar_t) * 256); if (temp.Name != NULL) wcscpy_s(temp.Name, 256, pEntry->BaseDllName.Buffer); temp.Size = pEntry->SizeOfImage; temp.Address = (uintptr_t)pEntry->DllBase; memcpy((PVOID)((ULONG_PTR)ModulesList + ((index++) * sizeof(SM_MSI))), &temp, sizeof(SM_MSI)); } }
模块遍历函数
遍历函数核心执行逻辑:
- 校验模块列表计数为0时,直接返回拒绝访问状态
- 按当前遍历索引,从连续存储区拷贝单条模块信息到用户态传出缓冲区
- 当索引遍历到列表末尾时,重置计数、索引值,释放连续存储区内存
对应代码片段:
NTSTATUS ProcModulesWalk(OUT SM_MSI* module_sys_info) { if (ModulesListCount == 0) return STATUS_ACCESS_DENIED; memcpy(module_sys_info, (SM_MSI*)((ULONG_PTR)ModulesList) + ModulesListIndex++, sizeof(SM_MSI)); if (ModulesListIndex >= ModulesListCount) { ModulesListCount = 0; ModulesListIndex = 0; ExFreePool(ModulesList); // 释放内存 DbgPrintEx(0, 0, "free memory\n"); } return STATUS_SUCCESS; }
问题表现
- 在驱动加载阶段的
DriverEntry中直接调用这组函数,运行完全正常,无蓝屏 - 改为响应用户态DLL的调用请求时,仅能正常运行2秒左右就触发蓝屏
- 临时将
SM_MSI结构体中的Name成员移除后,程序可完全正常运行,无蓝屏 - 初步将问题范围锁定在结构体中
wchar_t* Name成员的处理逻辑上
问题根因
经自行排查定位,触发蓝屏的核心原因是内核与用户态交互传递结构体时,遗漏了指针成员对应的内存空间分配与拷贝:
- 快照函数存储的
SM_MSI结构体中,Name是指向内核态内存的指针,当通过IO请求将结构体拷贝到用户态缓冲区时,内核地址会被直接传递到用户态,用户态尝试访问这个内核地址就会触发非法内存访问,直接导致蓝屏 - 连续列表内存释放时,仅释放了结构体数组本身的内存,没有单独释放每个
Name成员申请的内核内存,长期运行会造成内存池损坏,进一步触发蓝屏
内容的提问来源于stack exchange,提问作者doronazu
相关产品推荐
相关产品推荐

