首页
论坛
课程
招聘
[原创]竹林蹊径-深入浅出Windows驱动开发第一章HelloWorld疑问解决
2021-7-26 18:19 2326

[原创]竹林蹊径-深入浅出Windows驱动开发第一章HelloWorld疑问解决

2021-7-26 18:19
2326

  最近学习驱动开发,参照深入浅出Windows驱动开发第一章节HelloWorld驱动敲了代码,在停止驱动时虚拟机蓝屏,本次解决这个问题,并进行记录。
  开发环境:vs2013 WDK8.1
  调试环境:虚拟机VMware 12、 系统Windows7 sp1 x86、VirtualKD-Redux-2020.4、Windbg 10
  安装驱动的软件:InstDrv.exe 这款软件可以安装驱动服务、启动、停止、卸载驱动服务,非常方便。

问题:在驱动启动后,使用InstDrv软件停止驱动,会导致win7虚拟机蓝屏。

  截取一部分代码如下:
  #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标记,也会释放。

1
查看 RtlInitUnicodeString函数,没有申请空间存放字符串

  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函数不会分配内存

1
4. 这个问题的暴露应该与给虚拟机分配了1G内存有关,内存过小,导致驱动入口函数执行完毕后内存页面就被释放了。

[注意] 欢迎加入看雪团队!base上海,招聘CTF安全工程师,将兴趣和工作融合在一起!看雪20年安全圈的口碑,助你快速成长!

最后于 2021-7-27 22:42 被0346954编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (2)
雪    币: 2
活跃值: 活跃值 (1017)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
咖啡_741298 活跃值 2021-7-27 18:20
2
0
INIT 不是换出而是PE初始化后就被卸载了吧。  都是system空间,即使换出也没事
雪    币: 2775
活跃值: 活跃值 (1281)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
0346954 活跃值 1 2021-7-27 22:32
3
0
咖啡_741298 INIT 不是换出而是PE初始化后就被卸载了吧。 都是system空间,即使换出也没事
嗯 是了  我表达有误   应该是:  INIT节是在执行完毕之后就被释放,不再占用内存空间,不是内存被换出
游客
登录 | 注册 方可回帖
返回