DRIVR_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS 蓝屏分析

最近在写windows驱动的时候发现有一个偶发蓝屏现象,打了一个full dump。记录分析的流程,便于以后查阅。

蓝屏

1 由上面的图片可以见到,蓝屏错误码是DRIVR_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS。查询 微软文档,可知该错误表示:驱动程序未能在卸载前取消挂起的操作。初步方向就是:程序卸载的时候有一些操作正在执行中,没等操作执行完驱动就退出了。
chatgpt回答如下:

DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS 是一个 Windows 操作系统的蓝屏错误代码,表示驱动程序卸载时存在未取消挂起操作的错误。
通常情况下,在卸载驱动程序之前,需要确保所有正在进行的操作都已经完成或被取消。否则,可能会导致错误的操作或系统崩溃等问题。如果驱动程序卸载时存在未取消挂起操作,就会触发 DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS 错误,并导致系统崩溃。
为了避免这种错误,驱动程序在卸载之前必须正确处理所有正在进行的操作,并将它们取消或等待它们完成。在取消挂起操作时,应该使用正确的同步机制,以避免竞态条件和其他同步问题。
如果发生了 DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS 错误,可以尝试以下几种方法来解决问题:
检查驱动程序的代码,确保在卸载之前正确处理所有正在进行的操作,并将它们取消或等待它们完成。需要检查所有可能导致挂起操作的代码路径,并确保在适当的时间取消或等待这些操作。
使用正确的同步机制来避免竞态条件和其他同步问题。在取消或等待挂起操作时,应该使用适当的同步机制,例如互斥量、信号量、事件等,以确保线程安全和正确性。
使用调试工具来分析错误。可以使用 Microsoft 的调试工具来分析 DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS 错误,以确定错误的原因和位置。例如,可以使用 Windows 调试器 (WinDbg) 或 Visual Studio 调试器来分析错误。
更新驱动程序或操作系统。如果 DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS 错误是由驱动程序或操作系统的 Bug 导致的,可以尝试更新驱动程序或操作系统来解决问题。
需要注意的是,DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS 错误可能会导致系统崩溃,因此需要尽快解决问题,以确保系统的稳定性和安全性。

2 用windbg打开memory.dmp,执行!analyze -v,可以看到错误堆栈如下所示。
在这里插入图片描述

可以看到此时驱动已经被卸载掉(Unloaded状态)了,遂利用ida计算驱动基址+偏移可得如下
在这里插入图片描述
查看源码可得崩溃函数是KernelApcInjectCallback,也就是apc的回调函数。
KeInitializeApc(
pInjectApc, (PKTHREAD)pThread,
OriginalApcEnvironment, &KernelApcInjectCallback,
NULL, (PKNORMAL_ROUTINE)(ULONG_PTR)pUserFunc, UserMode, Arg1);

3 利用!process查看当前进程的信息,可以看到是git.exe。
进程信息
再!process bc18查看详细信息,观察堆栈,正是上面!analyze -v分析出来的异常堆栈。
在这里插入图片描述

4 根据上面信息就可以基本可以认为是,当git做apc注入的时候,用户做了手动卸载驱动的操作。此时由于apc注入的操作已经被挂起了,所以直接退出驱动会导致异常。至于为什么是偶发,是因为卸载驱动不是一个经常性操作,而且刚好碰上有进程注入未执行完的操作的概率较低。

5 基于这个结论,尝试复现该问题。在KeInitializeApc之前 和 KeInsertQueueApc之后分别执行休眠5秒的操作(目的是为了阻塞这个操作)。重新安装驱动,执行一个git clone操作,然后卸载驱动,等待一会之后发现,蓝屏了。重复多次,稳定复现。如此可得出结论:是因为该线程操作未正确取消导致的问题。
复现蓝屏
6 解决方法:添加计数器机制(或者其它同步机制)。在DriverUnload的时候判断该计数器的值,如果不满足,则等待条件满足之后再继续往下执行。

7 总结:驱动和进程不同,进程崩溃只会影响进程本身,而且也非常好调试。驱动则是难调试,问题难复现,所以在写驱动时一定要小心。openedr是个不错的参考对象,edrdrv用C++风格封装了一层,对这种同步问题,以及内存管理问题都做了一层兜底。

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值