最近学习驱动开发,参照深入浅出Windows驱动开发第一章节HelloWorld驱动敲了代码,在停止驱动时虚拟机蓝屏,本次解决这个问题,并进行记录。
开发环境:vs2013 WDK8.1
调试环境:虚拟机VMware 12、 系统Windows7 sp1 x86、VirtualKD-Redux-2020.4、Windbg 10
安装驱动的软件:InstDrv.exe 这款软件可以安装驱动服务、启动、停止、卸载驱动服务,非常方便。
截取一部分代码如下:
#define DRIVER_SYMBOLLINK_NAME L"\??\MySymbolLinkName"
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, DefaultDispatch)
#pragma alloc_text(PAGE, DriverUnload)
DriverEntry函数部分代码:
UNICODE_STRING symbolicLink;
deviceExtension = (PDEVICE_EXTENSION)deviceObject->DeviceExtension;//扩展设备内存
RtlInitUnicodeString(&symbolicLink, DRIVER_SYMBOLLINK_NAME);
deviceExtension->SymbolicLink = symbolicLink;//浅拷贝
DriverUnload函数部分代码如下:
while (NULL != deviceObject)
{
PDEVICE_EXTENSION deviceExtesion = \
(PDEVICE_EXTENSION)deviceObject->DeviceExtension;
// 删除符号链接与设备
linkName = deviceExtesion->SymbolicLink;
IoDeleteSymbolicLink(&linkName);//此处导致崩溃
deviceObject = deviceObject->NextDevice;
IoDeleteDevice(deviceExtesion->DeviceObject);
}
使用VirtualKD-Redux-2020.4 搭建虚拟机双机调试内核环境,成功启动后,设置好windbg符号文件路径,自己写的驱动文件名为 HelloWorldWDM.sys,debug模式生成的文件,生成sys时并且本机生成pdb文件。
1. 使用CTRL+BREAK中断虚拟机系统,输入命令 sxe ld:HelloWorldWDM,输入命令 g 回车 运行。
2. 以管理员权限运行InstDrv.exe 安装驱动服务,启动服务,会中断到调试器中。
3. 使用命令 !reload /f HelloWorldWDM.sys可以加载本地磁盘中驱动模块的PDB文件。
4. 使用lm命令查看 pdb文件是否加载成功
5. 输入命令 bp HelloWorldWDM!DriverEntry打下int 3断点,输入g运行,自动打开源码调试
6.动态调试分别在DriverEntry和DriverUnload函数下断点,IoDeleteSymbolicLink(&linkName)函数导致崩溃,这个linkName是符号链接名,初始化及使用流程如下:
(1) 在DriverEntry函数中调用RtlInitUnicodeString(&symbolicLink, DRIVER_SYMBOLLINK_NAME)对symbolicLink初始化,symbolicLink.Buffer指向字符串L"\??\MySymbolLinkName"
(2)在DriverEntry函数中使用语句deviceExtension->SymbolicLink = symbolicLink; 进行复制,这个可以理解为浅拷贝,所以当前deviceExtension->SymbolicLink中的Buffer变量与symbolicLink.Buffer指向同一块内存
(3)在 DriverUnload函数中查看linkName = deviceExtesion->SymbolicLink, 动态调试可知 linkName指向的内存区域此时不可访问。
7. 所以现在的问题在于为什么在DriverEntry函数字符串区域可以访问,在DriverUnload中字符串区域不可访问?
从上面两幅图中可以看到调用RtlInitUnicodeString函数并使用符号链接名来初始化变量,而且看最左侧还有INIT标记。
之前有#pragma alloc_text(INIT, DriverEntry),表明DriverEntry函数执行完毕后INIT内存页面会被释放,变量字符串\??\MySymbolLinkName也是INIT标记,也会释放。
kd> u nt!RtlInitUnicodeString l30
nt!RtlInitUnicodeString:
83e73ed8 57 push edi
83e73ed9 8b7c240c mov edi,dword ptr [esp+0Ch] //字符串指针
83e73edd 8b542408 mov edx,dword ptr [esp+8]
83e73ee1 c70200000000 mov dword ptr [edx],0
83e73ee7 897a04 mov dword ptr [edx+4],edi //复制Buffer为字符串指针
83e73eea 0bff or edi,edi
83e73eec 7422 je nt!RtlInitUnicodeString+0x38 (83e73f10)
83e73eee 83c9ff or ecx,0FFFFFFFFh
83e73ef1 33c0 xor eax,eax
83e73ef3 f266af repne scas word ptr es:[edi]
83e73ef6 f7d1 not ecx
83e73ef8 d1e1 shl ecx,1 //计算 字符串长度
83e73efa 81f9feff0000 cmp ecx,0FFFEh
83e73f00 7605 jbe nt!RtlInitUnicodeString+0x2f (83e73f07)
83e73f02 b9feff0000 mov ecx,0FFFEh
83e73f07 66894a02 mov word ptr [edx+2],cx
83e73f0b 49 dec ecx
83e73f0c 49 dec ecx
83e73f0d 66890a mov word ptr [edx],cx
83e73f10 5f pop edi
83e73f11 c20800 ret 8
由上面可知RtlInitUnicodeString函数是通过设置Buffer变量指向字符串区域,并没有再次拷贝一份字符串,所以当DriverEntry函数所在内存页面被释放后,调用DriverUnload函数时访问Buffer指向的内存区域无法访问导致蓝屏。
笔者修改DriverUnload部分源码如下:
UNICODE_STRING SymbolLinkName;
RtlInitUnicodeString(&SymbolLinkName, DRIVER_SYMBOLLINK_NAME);
IoDeleteSymbolicLink(&SymbolLinkName);
停止驱动时不会崩溃,查看这部分代码的静态反汇编:
上面两幅图可以看到 调用RtlInitUnicodeString函数时使用的字符串是位于PAGE页面,PAGE节的特点是位于可以进行分页交换的内存空间,这些空间在内存紧张时可以被交换到硬盘上以节省内存。
1. 本次学习了内核双机调试、源码调试、符号配置。
2. 用户态编程时使用上面#define定义字符串的形式,字符串位于PE文件的中可读不可写的区域,程序内部多次用到此字符串时,始终指向的是此区域的那一份数据,这与内核态编程不一样,目前来看,字符串内容虽然相同,但是在INIT页面与PAGE页面中使用的还是不同内存地址的字符串,即存在两份相同的字符串数据。
3. 学习了RtlInitUnicodeString函数初始化UNICODE_STRING的方式,RtlInitUnicodeString函数不会分配内存
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2021-7-27 22:42
被0346954编辑
,原因: