接前篇,这篇一起来看下WdfVersionBind函数的第4个参数:WdfDriverGlobals。
经过前面一番波折,WdfVersionBind总算找到了Wdf01000.sys!_WDF_LIBRARY_INFO结构。接下去,它要调用_WDF_LIBRARY_INFO!LibraryCommission函数,为WdfDriverGlobals分配空间并初始化。因为_WDF_LIBRARY_INFO!LibraryCommission中保存的是函数指针,因此直接看到调试器以call eax的方式进行调用,如下表:
WdfVersionBind(x,x,x,x)+9B 024 mov eax, [ebp+WDFLDR_LIBRARY_MODULE_Ptr]
WdfVersionBind(x,x,x,x)+9E 024 push ecx ; prepare to call WDF_LIBRARY_REGISTER_CLIENT
WdfVersionBind(x,x,x,x)+9F 028 push ebx
WdfVersionBind(x,x,x,x)+A0 02C push esi
WdfVersionBind(x,x,x,x)+A1 030 mov eax, [eax+WDFLDR_LIBRARY_MODULE.LibraryInfo]
WdfVersionBind(x,x,x,x)+A4 030 mov eax, [eax+_WDF_LIBRARY_INFO.LibraryRegisterClient]
WdfVersionBind(x,x,x,x)+A7 030 call eax ; call Wdf01000!LibraryRegisterClient
WdfVersionBind(x,x,x,x)+A7 ;
WdfVersionBind(x,x,x,x)+A7 ; NTSTATUS
WdfVersionBind(x,x,x,x)+A7 ; WDF_LIBRARY_REGISTER_CLIENT(
WdfVersionBind(x,x,x,x)+A7 ; PWDF_BIND_INFO,PWDF_DRIVER_GLOBALS*,PCLIENT_INFO);
WdfVersionBind(x,x,x,x)+A9 024 mov edi, eax
既然是以call eax的方式调用,那么寄存器eax中必然保存着跳转目标的地址,可以借助windbg查看该地址对应的符号(也就是函数名。附注,wdf01000.sys将自己实现的函数地址填写到WdfLdr.sys:_WDF_LIBRARY_INFO结构中,那么一般来说,wdf01000.sys必然会导出这些函数----便于根据函数名查找地址。所以,这些导出函数一定会被windbg精确的匹配到):
kd> bp WDFLDR!WdfVersionBind+0xa7 ;在call eax处下断点
kd> g
Breakpoint 2 hit
WDFLDR!WdfVersionBind+0xa7:
8b40a317 ffd0 call eax
kd> r eax ;查看eax中保存的转移地址
eax=8b975110
kd> ln 8b975110 ;查看该地址对应的符号
d:\th\minkernel\wdf\framework\kmdf\src\dynamic\version\version.cpp(449)
SrcSrv Command:
(8b975110) Wdf01000!LibraryRegisterClient | (8b97519c) Wdf01000!FxLibraryCommonRegisterClient
Exact matches:
Wdf01000!LibraryRegisterClient (void)
上面的输出分明指出WdfVersionBind会跳转到Wdf01000.sys!LibraryRegisterClient中。正好MS公开了这个函数的原型,真是得来全不费工夫:
extern "C"
_Must_inspect_result_
NTSTATUS
WDF_LIBRARY_REGISTER_CLIENT(
__in PWDF_BIND_INFO Info,
__deref_out PWDF_DRIVER_GLOBALS * WdfDriverGlobals,
__deref_inout PVOID * Context
);
调用这个函数时(call eax),第二个参数(二级指针)指向空,即WdfDriverGlobals还没有分配到内存,
push ecx ; prepare to call WDF_LIBRARY_REGISTER_CLIENT
push ebx ;WDF_LIBRARY_REGISTER_CLIENT函数的第二个参数
push esi
mov eax, [eax+WDFLDR_LIBRARY_MODULE.LibraryInfo]
mov eax, [eax+_WDF_LIBRARY_INFO.LibraryRegisterClient]
call eax
...
kd> r ebx
ebx=abd03070
kd> dd @ebx L1
abd03070 00000000 ;指针指向的的值为空
直到从FxLibraryCommonRegisterClient进入FxAllocateDriverGlobals才调用MxMemory::MxAllocatePoolWithTag真正分配内存。在以前的文章中也说过,WdfDriverGlobals只是作为结构体PFX_DRIVER_GLOBALS中最后一个域,返回给驱动,供其在整个驱动程序生命周期中使用。所以,我们在关注WdfDriverGlobals变量之余,还需要顺带关注一下WdfDriverGlobals变量的属主----PFX_DRIVER_GLOBALS pFxDriverGlobals的内容。哎,写到这,我就想感叹一下MS:虽说它把WDF框架的代码(大部分)开源出来,但是,不支持编译!!!所以,在调试过程中即使有pdb文件做源码行索引,但依然无法与二进制文件一一对应起来,它这样的开源实在很影响学习!不过,好在有IDA,结合源码还是可以艰难的调试下去。比如查看pFxDriverGlobals的内容,倒也不是不可完成的任务,查看代码,可以看到这样的内容:
PWDF_DRIVER_GLOBALS
FxAllocateDriverGlobals(
VOID
)
{
...
return &pFxDriverGlobals->Public; //Public即为WdfDriverGlobals变量
}
借助这段代码,在IDA中可以找到对应的汇编代码,阅读后了解到pFxDriverGlobals的地址保存在edi中,因此,在蓝线处下断点,从中获取pFxDriverGlobals的内存分布
kd> bp Wdf01000!FxAllocateDriverGlobals+FB ;在lea eax,[edi+0F8h]处下断
kd> g
Breakpoint 4 hit
Wdf01000!FxAllocateDriverGlobals+0xfb:
8b975561 8d87f8000000 lea eax,[edi+0F8h]
kd> r edi ;查看pFxDriverGlobals的地址值及内容
edi=a63884d0
kd> dt PFX_DRIVER_GLOBALS @edi ;此时,大部分结构体域还没有完成初始化工作
Wdf01000!PFX_DRIVER_GLOBALS
0xa3b7ac68
+0x000 Linkage : _LIST_ENTRY [ 0xa94a1ce0 - 0xa63884d0 ] ;Linkage用来将所有的Wdf Client模块加入系统双向链表FxLibraryGlobals.FxDriverGlobalsList
+0x008 Refcnt : 0n1
+0x00c DestroyEvent : MxEvent
+0x020 WdfHandleMask : 0xfffffff8
+0x024 WdfVerifierAllocateFailCount : 0n-1
+0x028 Tag : 0x75414550
+0x02c Driver : 0xa3b7a5d8 FxDriver
+0x030 DebugExtension : (null)
+0x034 LibraryGlobals : 0x8b9e3578 FxLibraryGlobalsType
+0x038 WdfLogHeader : 0xa5c40000 Void
+0x03c FxPoolFrameworks : FX_POOL
+0x098 FxPoolTrackingOn : 0 ''
+0x09c ThreadTableLock : MxLock
+0x0a4 ThreadTable : (null)
+0x0a8 WdfBindInfo : 0xabbd1000 _WDF_BIND_INFO
+0x0ac ImageAddress : 0xabbb0000 Void
+0x0b0 ImageSize : 0xb6000
+0x0b4 FxVerifierOn : 0 ''
+0x0b5 FxVerifyDownlevel : 0 ''
+0x0b6 FxVerifierDbgBreakOnError : 0 ''
+0x0b7 FxVerifierDbgBreakOnDeviceStateError : 0 ''
+0x0b8 FxVerifierHandle : 0 ''
+0x0b9 FxVerifierIO : 0 ''
+0x0ba FxVerifierLock : 0 ''
+0x0bb FxVerifyOn : 0 ''
+0x0bc FxVerboseOn : 0 ''
+0x0bd FxRequestParentOptimizationOn : 0x1 ''
+0x0be FxDsfOn : 0 ''
+0x0bf FxForceLogsInMiniDump : 0 ''
+0x0c0 FxTrackDriverForMiniDumpLog : 0x1 ''
+0x0c1 IsUserModeDriver : 0 ''
+0x0c4 RemoveLockOptionFlags : 0
+0x0c8 BugCheckDriverInfoIndex : 0x14
+0x0cc BugCheckCallbackRecord : _KBUGCHECK_REASON_CALLBACK_RECORD
+0x0e8 FxEnhancedVerifierOptions : 0
+0x0ec FxVerifierDbgWaitForSignalTimeoutInSec : 0x3c
+0x0f0 DbgWaitForWakeInterruptIsrTimeoutInSec : 0x3c
+0x0f4 TelemetryContext : 0x93b7eed8 _FX_TELEMETRY_CONTEXT
+0x0f8 Public : _WDF_DRIVER_GLOBALS
kd> dt _WDF_DRIVER_GLOBALS @edi+0xf8
Wdf01000!_WDF_DRIVER_GLOBALS
+0x000 Driver : (null)
+0x004 DriverFlags : 0
+0x008 DriverTag : 0
+0x00c DriverName : [32] ""
+0x02c DisplaceDriverUnload : 0 ''
等到执行流从FxAllocateDriverGlobals返回,FxLibraryCommonRegisterClient对Wdf01000!_WDF_DRIVER_GLOBALS进一步初始化,最终返回给WdfVersionBind。然后由WdfVersionBind调用我们自己的DriverEntry入口。