/*****************************************************************************
代码架构
*****************************************************************************/
1、 用户通过deviceuicontrol发送请求到驱动
2、 驱动收到用户的request(WDFREQUEST)后, 为该请求设置取消例程,同时创建子请求subrequest,为子请求设置完成例程
3、 驱动的requst的取消例程中,我首先结束subrequest,然后以STATUS_CANCELLED状态结束主request
4、 在subrequest的完成例程中,我代码大致如下
VOID EvtXFerCompletionRoutine (...)
{
if ( WdfRequestUnmarkCancelable(pParentRequest) != STATUS_CANCELLED ) // 此处在开启vierifier后,系统睡眠,触发int3中断
{
完成主请求 request
}
// reuse subrequest
}
/*****************************************************************************
问题出现
*****************************************************************************/
代码架构描述清楚了,出现步骤如下
1、 开启程序,使数据开始传输,意味着源源不断的request发送给驱动,驱动又源源不断的产生subrequest
2、 此时,点击使设备睡眠(非长时间不操作后设备休眠)
3、 系统蓝屏
/*****************************************************************************
windbg分析
*****************************************************************************/
KERNEL_MODE_EXCEPTION_NOT_HANDLED_M (1000008e)
This is a very common bugcheck. Usually the exception address pinpoints
the driver/function that caused the problem. Always note this address
as well as the link date of the driver/image that contains this address.
Some common problems are exception code 0x80000003. This means a hard
coded breakpoint or assertion was hit, but this system was booted
/NODEBUG. This is not supposed to happen as developers should never have
hardcoded breakpoints in retail code, but ...
If this happens, make sure a debugger gets connected, and the
system is booted /DEBUG. This will let us see why this breakpoint is
happening.
Arguments:
Arg1: 80000003, The exception code that was not handled
Arg2: 83eab4bc, The address that the exception occurred at
Arg3: 807e5ca8, Trap Frame
Arg4: 00000000
STACK_TEXT:
807e5d18 8603737f 8ceb97fc 8cec4eb0 00000000 nt!DbgBreakPoint+0x1
807e5d2c 9bd0117a 8cecceb0 00000000 73133148 Wdf01000!imp_WdfRequestUnmarkCancelable+0xa5
807e5d44 86047317 7313b148 796adfb0 8ceb97fc XXXXXXXX!EvtBulkRWURBCompletionRoutine+0x32 [f:\010_driver\cyioctl.c @ 1862]
807e5d70 8602bc36 a7cd8ed8 86952048 00000000 Wdf01000!FxRequestBase::CompleteSubmitted+0xf6
807e5d8c 8602bcde 01ec4eb0 8ca589b0 807e5dc4 Wdf01000!FxIoTarget::RequestCompletionRoutine+0x12d
807e5d9c 83ec99be 00000000 a7cd8ed8 8cec4eb0 Wdf01000!FxIoTarget::_RequestCompletionRoutine+0x35
807e5dc4 8416fcd4 00000000 a7cd8ed8 8ca589b0 nt!IopUnloadSafeCompletion+0x45
807e5df4 83ea8c53 00000000 a7cd8ed8 807e5e68 nt!IovpLocalCompletionRoutine+0x14b
807e5e38 8416fb64 8ea890f0 8cf2c008 8ea89028 nt!IopfCompleteRequest+0x128
807e5ea0 8ed15868 83e80314 8cf2c008 00000000 nt!IovCompleteRequest+0x133
807e5ed0 8ed16178 98f031d0 a7cd8ed8 8ceb9844 USBPORT!USBPORT_Core_iCompleteDoneTransfer+0x6e0
807e5efc 8ed199af 8ea89028 8ea890f0 8ea89a98 USBPORT!USBPORT_Core_iIrpCsqCompleteDoneTransfer+0x33b
807e5f24 8ed13d18 8ea89028 8ea89a98 8ea89002 USBPORT!USBPORT_Core_UsbIocDpc_Worker+0xbc
807e5f48 83ea84f5 8ea89aa4 8ea89002 00000000 USBPORT!USBPORT_Xdpc_Worker+0x173
807e5fa4 83ea8358 807c5120 8cbd3020 00000000 nt!KiExecuteAllDpcs+0xf9
807e5ff4 83ea7b1c 9e9b18fc 00000000 00000000 nt!KiRetireDpcList+0xd5
807e5ff8 9e9b18fc 00000000 00000000 00000000 nt!KiDispatchInterrupt+0x2c
/*****************************************************************************
调试过程
*****************************************************************************/
1、 定位WdfRequestUnmarkCancelable会触发dbgbreakpoint
2、 还原WdfRequestUnmarkCancelable代码大致如下: 其中主要由于积累不足,无法获知相关形参。猜测为主
int __stdcall imp_WdfRequestUnmarkCancelable_252DA(int WdfDriverGlobals, ULONG_PTR Request)
{
ULONG_PTR v2; // edi@1
int v3; // ST14_4@3
int v4; // eax@3
int v5; // esi@6
int result; // eax@11
v2 = Request;
if ( !Request )
FxVerifierBugCheck_50F7A(WdfDriverGlobals - 200, 5u, 0, 0x1008u);
v3 = Request;
Request = 0;
v4 = FxObject::_GetObjectFromHandle_127F7(v3, (int)&Request); // 内核知识浅薄,不明白为何如此转换。 该代码实现请参考最后部分
// 此处省略我认为不关键代码
v5 = *(_DWORD *)(v4 + 0xC); // 这个是_FX_DRIVER_GLOBALS地址,巧合下,dt wdf01000!FxRequest 发现该结构体地址偏移c可以得到_FX_DRIVER_GLOBALS
// 根据V5的判断, v4可以理解为 dtwdf01000!FxRequest类型 发现如下代码可以翻译为如下
// if(!Request.m_Completed && Request.m_ioQueue), 理解为该请求尚未完成,或者仍在队列中m_ioQueue不为空,则执行真正的取消取消例程
// 由于休眠使得主请求request被以STATUS_CANCELLED方式完成,因此走else分支
if ( !*(_BYTE *)(v4 + 0x6A) && *(_DWORD *)(v4 + 0x88) )
{
result = FxIoQueue::RequestCancelable_4179F(v4, 0, 0, 0);
}
else
{
// 出现该bug正式由于开启了verifier
if ( /* *(_BYTE *)(v5 + 150) */ request.m_Globals.FxVerifierDbgBreakOnError == true )
DbgBreakPoint();
else
FxRequestOutputBuffer::Delete(
"Turn on framework verifier for %.sys to automatically break into the debugger next time it happens.\n",
v5 + 212);
result = 0xC0000010;
}
return result;
}
出现问题的是在开启verifier后,正在传输数据时系统睡眠引起的蓝屏, 原因可能是上层传递的请求在休眠时调用了取消例程
但是在该取消例程中,我以取消状态完成了这个请求, 同时迫使子请求调用完成例程,在完成例程中,我调用了WdfRequestUnmarkCancelable尝试取消 取消例程,因此引发了breakpoint调用。
因此我认为这个是正常现象。 不用搭理这个错误, 不知道理解的对不对。 只期望不是隐患
//????? 为啥request句柄转对象要这么恶心的操作,不了解。 那位高手可以解答一下呢?
unsigned int __stdcall FxObject::_GetObjectFromHandle_127F7(int a1, int a2)
{
unsigned int result; // eax@1
unsigned __int16 v3; // cx@2
result = ~a1 & 0xFFFFFFF8;
if ( a1 & 1 )
{
v3 = *(_WORD *)result;
*(_WORD *)a2 = *(_WORD *)result;
result -= v3;
}
return result;
}
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课