Call IoCompleteRequest while holding a spinlock

MS KB Q186775:《Tips for Windows NT Driver Developers -- Things to Avoid》一文(中译版)罗列了很多驱动开发过程中的注意点。微软仅仅提到了这些注意点,但是没有解释其背后的原因,只能结合网上的资料+自己分析其原因。我在osronline上看到有人针对item 20:"Never call IoCompleteRequest while holding a spin lock. It can deadlock your system."提问,所以就记录自此。

问题描述:

"I'm done exactly this inside my DPC to complete pending IRPs which I store in a list (which is protected by a spinlock). It's not too much of a problem to rewrite, but I'd still like to know what could possibly happen. "

相比于回复2,回复1的解释不具有很强的说服力,但还是有一定的参考价值.

回复1:

"There are two issues here. One: You're holding a spinlock, most commonly your IRP queue spinlock, and you call IoCR. (Or IoCallDriver, which could result in an immediate IoCR.) Completion of the IRP could cause an upper layer's IO completion routine to send you another IRP, before your call to IoCompleteRequest returns -- i.e., before you can drop the spinlock in that path. Your dispatch routine for the new IRP has no idea that it's been called "within" your call to IoCR, so tries to acquire the same spinlock to synch with your IRP state info... deadlock. This is difficult (no, not impossible) to cover via "I know what I'm doing". (One way to cover it, of course, is to build your own "VMS-style spinlock" that can tolerate multiple acquisitions by the same CPU. :-) Two - and this is much softer - the doc actually goes much farther, claiming you should never *call out of the driver* while holding ANY spinlock(特意加粗并翻译一下:回复者意思是不要带着自旋锁离开自己的驱动程序). Now before you say "I don't want to hear it", I agree, this is obviously bogus -- the counterexample of KeReleaseSpinLock comes immediately to mind! The real issue is that they don't want us doing things that can take an unknown, unpredictable, possibly-a-lot-longer-than-expected, etc., period of time, while we hold a spinlock. This seems to me to be a valid concern, and IoCompleteRequest, cruising as it does through any number of drivers' I/O completion routines, seems to me to be in that category. "

回复2:

"IoCompleteRequest will call some arbitrary completion routines of the upper drivers. These routines can be complex, and calling complex code while holding a spinlock is a bad idea. Also - these routines can decide to submit some IRP (maybe the same IRP) to your driver's dispatch entry points. If these entry points also acquire the same spinlock - you will have a deadlock on the SMP kernel."

一般在IOCancel回调函数中容易出现这样的问题,如下:

VOID PtDriverCancelIRP(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) 
{ 
    UNREFERENCED_PARAMETER(DeviceObject);

    KdPrint(( "[WENZ] User Message Cancel Irp....\n" ));

    if ( Irp == PeddingIRP)
        PeddingIRP = NULL; 

    Irp->IoStatus.Status = STATUS_CANCELLED; 
    Irp->IoStatus.Information = 0; 
    IoCompleteRequest(Irp,IO_NO_INCREMENT); 
}

...

NTSTATUS
DeviceControl( PDEVICE_OBJECT DeviceObject, PIRP Irp )
{
    ...
    switch ( irpSp->Parameters.DeviceIoControl.IoControlCode )
    {
        ...
    case IOCTL_NOTIFY_STATE:
    Irp->IoStatus.Information = 0;
    Irp->IoStatus.Status = STATUS_PENDING;
    IoMarkIrpPending(Irp); 
    PeddingIRP = Irp;
        IoSetCancelRoutine(Irp, PtDriverCancelIRP);

    return STATUS_PENDING;
        ...
    }
    ...
}

这段代码可能会触发蓝屏错误:DRIVER_RETURNED_HOLDING_CANCEL_LOCK。cancel回调要尽快释放自旋锁,然而,代码中没有这样做,并调用了IoCompleteRequest,需要改为如下:

VOID PtDriverCancelIRP(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) 
{ 
    UNREFERENCED_PARAMETER(DeviceObject);

    KdPrint(( "[WENZ] User Message Cancel Irp....\n" ));

    if ( Irp == PeddingIRP)
        PeddingIRP = NULL; 

    IoReleaseCancelSpinLock(); // release the cancel spinlock

    Irp->IoStatus.Status = STATUS_CANCELLED; 
    Irp->IoStatus.Information = 0; 
    IoCompleteRequest(Irp,IO_NO_INCREMENT); 
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
郁金香VC++过驱动保护全套 免key版 天異赤提供 教程下载地址获取方法: 第一步:打开下方链接,填写QQ邮箱,系统会往QQ邮箱发一封确认订阅邮件 第二步:打开QQ邮箱查看邮件,确认订阅,订阅成功后系统会自动把下载地址和解压密码一起发送到你QQ邮箱http://list.qq.com/cgi-bin/qf_invite?id=585e150c59f30e1213af9a9352367711b2e45c217582cf35 最近时间有些多,一时对网络游戏的保护机制感兴趣了,来研究了一下,听说QQ系列的TesSafe.sys 有些强,于是拿来看看驱动都做了些什 么.以下是对DNF和QQffo(自由幻想)研究结果(xp sp2) 在网上找了些TesSafe的资料,说TesSafe并不怎么样 现在这个版本保护的结果为:任务管理器中可以看到游戏进程,但OD和CE看不见,更不用说什么调试了,iceword可以 看到EPROCSS,但WSysCheck看 郁金香驱动 不见,自己写程序,也不能注入受保护的游戏进程. 可见,NtOpenProcess被Hook了,恢复SSDT后,没有任何效果,可见是inline hook , 用一般的软件检测一下,没有发现inline hook,看来hook得比较深,在网上一找资料才发现,原来的确够隐藏的 郁金香驱动 以下是上一个TesSafe版本的分析结果 从网上找出来的资料,TesSafe.sys保护原理(DNF的保护,我听说,QQ系列游戏的保护机制都是一样的) ================================================================= 保护得比较没有意思,强度也不高.可能隐藏性稍好一些. 用IDA反汇编TesSafe.sys可以看到: 这个驱动一加载,ExAllocPoolWithTag分配了一块内存,然后将一个函数写进这块内存,接着做好保护,然后 PsCreateSystemThread()创建的 郁金香驱动 线程调用ZwUnloadDriver将驱动卸载。虽 然驱动被卸载了,但是ExAllocPoolWithTag分配的内存仍然在起作用。 具体来看它如何进行保护: 郁金香驱动 先是得到了ObOpenObjectByPointer的地址,然后在 NtOpenProcess中搜索0xe8 ,也就是跳转指令,直到遇见RET为止。 一但得到0xe8,比较后面的四个字节,如果转换后为 ObOpenObjectByPointer的地址,就把这个地址用自己代理函数的地址转换后替换掉,达到 保护自己的目的。 郁金香驱动 用WinDbg看了看,果然在我的机器上:0x80570e41这个在 NtOpenProcess中的区域被TesSafe.sys修改,原来这里是:80570e41 e87c8dffff call nt!ObOpenObjectByPointer (80569bc2) 系统通过ObOpenObjectByPointer来通过 EPROCESS得到Handle返回给调用者。TENCENT在这里下了一个跳转:(TesSafe.sys加载后。)80570e41 e826542a78 call f881626c 很明显,系统执行到这里就会调用0x6881626c的函数,也就是 TesSafe.sys的 ObOpenObjectByPointer代理函数。这 样,Client.exe就会在这里被过滤掉,从而让R3程序无法得到QQT的句柄,从而保护进程。 郁金香驱动 ================================================================================= 我手头拿到的版本是2008年8月5号的,把TesSafe逆出来一看,比上个版本有所加强. 在这个新版本中,TesSafe一共InLine Hook了六个函数,我只逆出并恢复了五个 其中: 1. KeAttachProcess NtOpenProcess NtOpenThread 这三个函数的HOOK方式与上一个版本一样,就是上面蓝色字体的方法,把本应该call ObOpenObjectByPointe的代码修改成了call他自己的代码, 然后在他自己的代码中处理保护的进程 上面三个函数,原来正常的代码为 80581ce3 e8a658ffff call nt!ObOpenObjectByPointer (8057758e) 被HOOK后的代码变成了 call a8724af4(TesSafe自己搞出来的函数) lkd> u a8724af
很抱歉,作为AI语言模型,我无法提供完整的驱动程序代码,但是我可以为您提供一个简单的驱动程序示例,供您参考。 示例代码如下: ```c #include <ntddk.h> // 驱动程序入口函数 NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); // 初始化驱动程序对象 DriverObject->DriverUnload = Example_Unload; // 注册设备对象 UNICODE_STRING DeviceName; RtlInitUnicodeString(&DeviceName, L"\\Device\\ExampleDevice"); PDEVICE_OBJECT DeviceObject = NULL; NTSTATUS Status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &DeviceObject); if (!NT_SUCCESS(Status)) { return Status; } // 注册设备对象处理函数 DriverObject->MajorFunction[IRP_MJ_CREATE] = Example_Create; DriverObject->MajorFunction[IRP_MJ_CLOSE] = Example_Close; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Example_IoControl; // 设置设备对象的标志 DeviceObject->Flags |= DO_DIRECT_IO; DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; } // 设备对象创建处理函数 NTSTATUS Example_Create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { UNREFERENCED_PARAMETER(DeviceObject); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } // 设备对象关闭处理函数 NTSTATUS Example_Close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { UNREFERENCED_PARAMETER(DeviceObject); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } // 设备对象IO控制处理函数 NTSTATUS Example_IoControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { UNREFERENCED_PARAMETER(DeviceObject); PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp); ULONG ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode; switch (ControlCode) { case IOCTL_EXAMPLE_SET_DATA: // 处理设置数据的操作 break; case IOCTL_EXAMPLE_GET_DATA: // 处理获取数据的操作 break; default: Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_INVALID_DEVICE_REQUEST; } Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } // 驱动程序卸载处理函数 VOID Example_Unload(IN PDRIVER_OBJECT DriverObject) { UNICODE_STRING DeviceName; RtlInitUnicodeString(&DeviceName, L"\\Device\\ExampleDevice"); // 删除设备对象 IoDeleteDevice(DriverObject->DeviceObject); // 卸载驱动程序 DriverObject->DriverUnload = NULL; } ``` 该示例驱动程序是一个基本的设备驱动程序,包含设备对象创建、关闭、IO控制等处理函数。您可以根据实际需要进行修改和添加。请注意,驱动程序开发需要熟悉Windows内核编程,同时需要小心谨慎,以避免对系统产生负面影响。建议您参考相关的教材或者网上的资源,以帮助您完成驱动程序的编写。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值