在前面的文章<Wdf框架中WdfDriverGlobals对象的创建>中简单的提到过WdfVersionBind函数的作用,但是没有来得及分析这个函数的调用处。今天得空,借这篇文章写下WdfVersionBind函数的调用者:FxDriverEntry。
在写这篇文章前,我被WdfLdr.sys的名字误导,以为这个sys文件是内核的加载器,用于加载整个内核启动(Ldr是Loader的缩写)。所以,当时我很粗浅的认为这个驱动只在系统引导阶段才被调用,之后就靠边站了。然而事实却是,当系统启动后,只要加载基于WDF框架的驱动,就会中断在WdfVersionBind函数上。以登录到系统后,加载WinDDK中WdfSimple.sys驱动(toast驱动的WDF版本)为例:
kd> bp WDFLDR!WdfVersionBind
kd> g
Breakpoint 1 hit
WDFLDR!WdfVersionBind:
fffff802`9598d2d0 488bc4 mov rax,rsp
kd> kb
RetAddr : Args to Child : Call Site
fffff802`98a414a3 : ffff8881`fc86f000 ffff8881`fbf2c750 00000000`00000000 ffff327f`24cdc304 : WDFLDR!WdfVersionBind
fffff802`5ab053e9 : 00000000`00000000 ffffd381`a819d460 ffff8881`fbf2c750 ffffffff`00000000 : wdfsimple!FxDriverEntryWorker+0x7f
fffff802`5abab13e : 00000000`00000000 00000000`00000000 00000000`00000004 ffffc285`00000004 : nt!IopLoadDriver+0x521
fffff802`5ab04495 : ffffd381`a819da01 ffffd381`a819d6c8 00000000`c0000000 ffffd381`a819d6a4 : nt!PipCallDriverAddDeviceQueryRoutine+0x1b6
fffff802`5ab03e93 : 00000000`00000000 00000000`00000014 00000000`c0000034 ffff8881`fbd6cd30 : nt!PnpCallDriverQueryServiceHelper+0xbd
fffff802`5ab0323d : ffff8881`fbd6cd30 ffffd381`a819d8e0 ffff8881`fbd6cd30 00000000`00000000 : nt!PipCallDriverAddDevice+0x317
fffff802`5acd5eda : ffff8881`fbd6cd30 00000000`00000001 ffffd381`a819db19 fffff802`5ab0397b : nt!PipProcessDevNodeTree+0x1cd
fffff802`5a824d9e : 00000001`00000003 00000000`00000000 ffffd381`a819daf0 00000000`00000000 : nt!PiRestartDevice+0xba
fffff802`5a6e0d79 : ffff8881`f97af040 fffff802`5a9a5320 fffff802`5aa46280 fffff802`5aa46280 : nt! ?? ::FNODOBFM::`string'+0x42bde
fffff802`5a7254bd : fffff802`5a9cb180 00000000`00000080 ffff8881`f88af6c0 ffff8881`f97af040 : nt!ExpWorkerThread+0xe9
fffff802`5a7d8456 : fffff802`5a9cb180 ffff8881`f97af040 fffff802`5a72547c 43ffff41`04080003 : nt!PspSystemThreadStartup+0x41
00000000`00000000 : ffffd381`a819e000 ffffd381`a8198000 00000000`00000000 00000000`00000000 : nt!KxStartSystemThread+0x16
图1:
图1结合调试输出显示,在登录系统后,加载驱动也会中断到WdfVersionBind。由此可见,OS会为每个Wdf驱动设置WDF框架的版本号等信息。这些内容属于上一篇文章的延伸,我要借助它引出这篇文章的主题:FxDriverEntry。
上面的函数调用栈中只有frame 1与我们的驱动模块有关,但名字却是FxDriverEntryWorker。整份Sample源码中没有调用这个函数,可见,它是由编译器插入的。这就又引出2个疑问:
1.驱动入口DriverEntry在FxDriverEntryWorker函数执行完后调用;
2.反之,驱动入口DriverEntry在FxDriverEntryWorker函数之前执行。
如果属于情况2,可能我要重启或者重新加载驱动。怕麻烦的我最终选择用IDA加载wdfsimple.sys分析了函数流程。下图是在IDA中对DriverEntry函数执行"XRefs graph to"后生成的调用图,调用次序依次为FxDriverEntry->FxDriverEntryWorker->DriverEntry:
FxDriverEntry只是一个包装函数,真正的工作全交由FxDriverEntryWorker完成。下面的代码是IDA对FxDriverEntryWorker函数反汇编后生成的伪代码。FxDriverEntryWorker首先为WdfSimple驱动对象DriverObject调用WdfVersionBind,设置Wdf框架信息并关联WdfDriverGlobals这个重要的全局变量。如果WdfVersionBind调用成功,则会调用DriverEntry进入我们程序的入口。这才是原来WDM模型中驱动程序的入口。
int __fastcall FxDriverEntryWorker(_DRIVER_OBJECT *DriverObject, _UNICODE_STRING *RegistryPath)
{
_UNICODE_STRING *v2; // rsi@1
_DRIVER_OBJECT *v3; // rdi@1
int result; // eax@2
int v5; // ebx@4
void (__cdecl *v6)(_DRIVER_OBJECT *); // rax@7
v2 = RegistryPath;
v3 = DriverObject;
if ( DriverObject )
{
result = WdfVersionBind_0(v3, &WdfDriverStubRegistryPath, &WdfBindInfo, &WdfDriverGlobals);
if ( result >= 0 )
{
v5 = FxStubBindClasses(&WdfBindInfo);
if ( v5 < 0 || (FxStubInitTypes(), v5 = DriverEntry(v3, v2), v5 < 0) ) //WdfVersionBind执行成功后,调用DrvierEntry
{
}
本篇完