天天看點

Wdf架構中WdfDriverGlobals對象的建立

    前面寫過一篇<WDF基本對象和句柄定義>,反響一般,不過這不會成為阻擋我繼續寫下去的絆腳石~本篇我們繼續來分析Wdf架構。

    WdfDriverGlobals對象的身影活躍在wdf架構的各個角落,幾乎每個DDI接口内部都會使用它:

_Must_inspect_result_
__drv_maxIRQL(PASSIVE_LEVEL)
NTSTATUS
WDFEXPORT(WdfDriverCreate)(
    PWDF_DRIVER_GLOBALS DriverGlobals,
    MdDriverObject DriverObject,
    PCUNICODE_STRING RegistryPath,
    PWDF_OBJECT_ATTRIBUTES DriverAttributes,
    PWDF_DRIVER_CONFIG DriverConfig,
    WDFDRIVER* Driver
    )
{
   ...
    pFxDriverGlobals = GetFxDriverGlobals(DriverGlobals);
    ...
}      

不得不說,WdfDriverGlobals有着舉足輕重的作用。在好奇心的驅使下,我查找了該對象的配置設定和建立過程:

NTSTATUS
FxLibraryCommonRegisterClient(
    __inout PWDF_BIND_INFO        Info,
    __deref_out PWDF_DRIVER_GLOBALS *WdfDriverGlobals,
    __in_opt PCLIENT_INFO          ClientInfo
    )
{
    NTSTATUS           status;
    UNICODE_STRING serviceName = { 0 };

    ASSERT(Info->FuncCount);
    *WdfDriverGlobals = NULL;
    ...
    *WdfDriverGlobals = FxAllocateDriverGlobals();

    if (*WdfDriverGlobals) { ...      

    雖然SourceInsight中可以找到大量對WdfDriverGlobals的引用,但唯有這處比較特别:

FxLibraryCommonRegisterClient函數的第二個參數是一個二級指針類型。這涉及到每個C語言入門者都會遇到的難題:如何在函數内部為指針配置設定記憶體并回傳給調用者,答案之一是通過二級指針實作。是以,我比較确定這個函數的作用之一就是調用FxAllocateDriverGlobals函數為WdfDriverGlobals配置設定記憶體并初始化。

PWDF_DRIVER_GLOBALS
FxAllocateDriverGlobals(
    VOID
    )
{
    PFX_DRIVER_GLOBALS  pFxDriverGlobals;
    KIRQL               irql;
    NTSTATUS            status;
    //MxAllocatePoolWithTag内部調用ExAllocatePoolWithTag,配置設定非分頁記憶體
    pFxDriverGlobals = (PFX_DRIVER_GLOBALS)
        MxMemory::MxAllocatePoolWithTag(NonPagedPool, sizeof(FX_DRIVER_GLOBALS), FX_TAG);

    if (pFxDriverGlobals == NULL) {
        return NULL;
    }
    //準備初始化
    RtlZeroMemory(pFxDriverGlobals, sizeof(FX_DRIVER_GLOBALS));
    ...
    return &pFxDriverGlobals->Public; //<----敲黑闆 劃重點 請各位注意函數的傳回值
}      

    FxAllocateDriverGlobals函數本身不是很複雜,除了函數傳回值,其他并不值得用大篇段落介紹。請注意代碼中pFxDriverGlobals的類型為PFX_DRIVER_GLOBALS,而函數的傳回類型為PWDF_DRIVER_GLOBALS。WDF_DRIVER_GLOBALS作為FX_DRIVER_GLOBALS結構的最後一部分,儲存在其Public域内:

typedef struct _FX_DRIVER_GLOBALS {
public:
    ULONG
    __inline
    AddRef(
        __in_opt   PVOID Tag = NULL,
        __in       LONG Line = 0,
        __in_opt   PSTR File = NULL
        );
...
    PFX_TELEMETRY_CONTEXT TelemetryContext;

    DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) WDF_DRIVER_GLOBALS  Public;

} FX_DRIVER_GLOBALS, *PFX_DRIVER_GLOBALS;      

    我們可能以WDF_DRIVER_GLOBALS*作為參數調用DDI接口,而DDI接口内部會通過GetFxDriverGlobals宏(确切的說是CONTAINING_RECORD宏),根據DriverGlobals的位址以及DriverGlobals對應的Public域在結構中的偏移計算出調用FxAllocateDriverGlobals時配置設定的對象的起始位址。不過,真不知道MS為什麼要大費周章的這麼折騰...

__inline
PFX_DRIVER_GLOBALS
GetFxDriverGlobals(
    __in PWDF_DRIVER_GLOBALS DriverGlobals
    )
{
    return CONTAINING_RECORD( DriverGlobals, FX_DRIVER_GLOBALS, Public );
}      

    前面說了函數FxLibraryCommonRegisterClient調用FxAllocateDriverGlobals配置設定記憶體,但縱覽整份WDF架構的源碼都沒有發現哪裡調用FxLibraryCommonRegisterClient這個函數。Wdf架構總不可能一直在傳遞空的DriverGlobals對象,要不然OS早崩潰了。是以,我初步斷定,很可能MS并沒有把調用FxLibraryCommonRegisterClient函數的代碼開源出來,原因不詳。雖然MS不開源,但是根據調用棧來反推調用函數得源碼應該不難。這就得靠windbg動态調試來做到了:

kd> x Wdf01000!FxLibraryCommonRegisterClient ;查找符号
fffff80e`55b25a64 Wdf01000!FxLibraryCommonRegisterClient (struct _WDF_BIND_INFO *, struct _WDF_DRIVER_GLOBALS **, struct _CLIENT_INFO *)
kd> bu Wdf01000!FxLibraryCommonRegisterClient ;系統啟動時,Wdf01000.sys還沒加載,是以要下延遲斷點
kd> g
Breakpoint 0 hit
Wdf01000!FxLibraryCommonRegisterClient:
fffff80e`55b25a64 4c8bdc          mov     r11,rsp
kd> kb 
RetAddr           : Args to Child                                                           : Call Site
fffff80e`55b25a26 : 00000010`00000b32 ffffbe0a`67727453 00000000`00000002 fffff803`f6ed529d : Wdf01000!FxLibraryCommonRegisterClient [minkernel\wdf\framework\kmdf\src\librarycommon\fxlibrarycommon.cpp @ 344]
fffff80e`55c0d3a5 : 00000000`00000000 fffff80e`57704148 fffff80e`57704160 fffff803`f6a21832 : Wdf01000!LibraryRegisterClient+0x56 [minkernel\wdf\framework\kmdf\src\dynamic\version\version.cpp @ 517]
fffff80e`57702634 : ffffe201`0425e230 00000000`00000000 00000000`00000078 00000000`00000000 : WDFLDR!WdfVersionBind+0xd5 [minkernel\wdf\framework\kmdf\src\dynamic\loader\wdfldr.cpp @ 1954]
fffff803`f6e6257a : 00000000`00000000 ffffbe0a`add31440 ffffae09`6c7f6e60 ffffffff`800000f0 : CompositeBus!FxDriverEntryWorker+0x74
fffff803`f6e64c8b : 00000000`00000000 00000000`00000000 00000000`00000004 ffffe201`00000004 : nt!IopLoadDriver+0x4da
fffff803`f6e652a8 : ffffffff`80000001 ffffbe0a`add316d0 ffffffff`800000ec 00000000`00000000 : nt!PipCallDriverAddDeviceQueryRoutine+0x1b3
fffff803`f6e68009 : 00000000`00000000 ffffbe0a`add316b0 00000000`6e657050 00000000`0000001a : nt!PnpCallDriverQueryServiceHelper+0xcc
fffff803`f6e718b8 : ffffae09`6ada0d20 ffffbe0a`add318f0 ffffae09`6ada0d20 ffffae09`6ad9fd20 : nt!PipCallDriverAddDevice+0x385
fffff803`f6fdd495 : ffffae09`6c775b30 ffffbe0a`add31b19 ffffae09`6c775b30 ffffae09`6c775b80 : nt!PipProcessDevNodeTree+0x164
fffff803`f6b17f94 : ffffae01`00000003 ffffae09`00000000 ffffae09`00000000 00000000`00000000 : nt!PiProcessStartSystemDevices+0x59
fffff803`f6a5a645 : ffffae09`6acde700 ffffae09`6ac7b6e0 fffff803`f6d7a960 ffffae09`0000000c : nt!PnpDeviceActionWorker+0x474
fffff803`f6ae33a7 : 00000000`00000000 00000000`00000080 ffffae09`6ac84480 ffffae09`6acde700 : nt!ExpWorkerThread+0xf5
fffff803`f6b68d66 : fffff803`f5e90180 ffffae09`6acde700 fffff803`f6ae3360 00000000`00000000 : nt!PspSystemThreadStartup+0x47
00000000`00000000 : ffffbe0a`add32000 ffffbe0a`add2c000 00000000`00000000 00000000`00000000 : nt!KiStartSystemThread+0x16      

最底部的nt!KiStartSystemThread顯示目前系統還在啟動階段,在此階段中,Wdfldr.sys!WdfVersionBind調用函數FxLibraryCommonRegisterClient,并最終配置設定

全局(全局這個詞有待考證,

每個OS中已加載的driver都有各自的WdfDriverGlobals執行個體<----這句話我仍在調研中,如果有結果,會在後續的文章中更新)對象WdfDriverGlobals。正是這WdfLdr的代碼,MS沒有公開。好在有pdb檔案,可以用IDA逆向分析。加載WdfLdr.sys并設定pdb路徑後,F5(Generate pseudocode),可以得到如下僞代碼:

__int64 __fastcall WdfVersionBind(_DRIVER_OBJECT *DriverObject, _UNICODE_STRING *RegistryPath, _WDF_BIND_INFO *Info, void ***Globals)
{
  void ***v4; // rsi@1
  _WDF_BIND_INFO *v5; // rdi@1
  _UNICODE_STRING *v6; // rbp@1
  int (__cdecl *v7)(_WDF_BIND_INFO *, void ***, void **); // rax@3
  _CLIENT_MODULE *v8; // rcx@3
  __int64 result; // rax@3
  _LIBRARY_MODULE *Module; // [sp+30h] [bp-38h]@1
  _CLIENT_MODULE *v11; // [sp+38h] [bp-30h]@3
  int v12; // [sp+40h] [bp-28h]@1
  _UNICODE_STRING *v13; // [sp+48h] [bp-20h]@1

  v12 = 16;
  v4 = Globals;
  Module = 0i64;
  v5 = Info;
  v13 = 0i64;
  v6 = RegistryPath;
  if ( WdfLdrDiags & 1 )
  {
    DbgPrint("WdfLdr: WdfVersionBind - ");
    DbgPrint("WdfLdr: WdfVersionBind: enter\n");
  }
  v11 = 0i64;
  JUMPOUT(v4, 0i64, sub_1C000D920);
  JUMPOUT(v5, 0i64, sub_1C000D920);
  JUMPOUT(v5->FuncTable, 0i64, sub_1C000D920);
  JUMPOUT(ReferenceVersion(v5, &Module), 0, sub_1C000D8DC);
  JUMPOUT(LibraryLinkInClient(v5->Module, v6, v5, 0i64, &v11), 0, sub_1C000D87C);
  v13 = v6;
  v7 = Module->LibraryInfo->LibraryRegisterClient;
  LODWORD(result) = _guard_dispatch_icall_fptr(v5, v4);
  JUMPOUT(result, 0, sub_1C000D8AC);
  v8 = v11;
  v11->Globals = *v4;
  v8->Context = &v12;
  JUMPOUT(result, 0, &loc_1C000D8E1);
  JUMPOUT(WdfLdrDiags & 1, 0, sub_1C000D8FE);
  return (unsigned int)result;
}      

    僞代碼行中

v7 = Module->LibraryInfo->LibraryRegisterClient;      

LibraryRegisterClient應該就是我們的FxLibraryCommonRegisterClient函數,如果是這樣的話,Module可能包含了Wdf01000.sys的資訊。讓我麼來驗證一下:

根據上面WdfVersionBind的僞代碼,大概可以猜到Wdf01000.sys的子產品資訊存放在變量_LIBRARY_MODULE *Module;中。是以,我們可以在調用LibraryLinkInClient時,檢視Module資訊(fastcall,參數Library存放在rcx中)。

IDA分析得出的LibraryLinkInClient函數原型:

__int64 __fastcall LibraryLinkInClient(_LIBRARY_MODULE *Library, _UNICODE_STRING *RegistryPath, _WDF_BIND_INFO *Info, void *Context, _CLIENT_MODULE **Client)      

    接下來,一起驗證一下IDA僞代碼中的v7 = Module->LibraryInfo->LibraryRegisterClient;調用的的确是Wdf01000.sys中的FxLibraryCommonRegisterClient

kd> bp WDFLDR!LibraryLinkInClient
kd> g
Breakpoint 2 hit
WDFLDR!LibraryLinkInClient:
fffff80e`55c024a0 48895c2408      mov     qword ptr [rsp+8],rbx

kd> dt WDFLDR!_LIBRARY_MODULE @rcx ;檢視LibraryLinkInClient的第一個參數。參數類型為LIBRARY_MODULE,儲存在rcx中
   +0x000 LibraryRefCount  : 0n1
   +0x008 LibraryListEntry : _LIST_ENTRY [ 0xfffff80e`55c0a238 - 0xfffff80e`55c0a238 ]
   +0x018 ClientRefCount   : 0n10
   +0x01c IsBootDriver     : 0x1 ''
   +0x01d ImplicitlyLoaded : 0x1 ''
   +0x020 Service          : _UNICODE_STRING "\Registry\Machine\System\CurrentControlSet\Services\Wdf01000"
   +0x030 ImageName        : _UNICODE_STRING "Wdf01000.sys" ;子產品資訊為Wdf01000.sys
   +0x040 ImageAddress     : 0xfffff80e`55b10000 Void
   +0x048 ImageSize        : 0xe3000
   +0x050 LibraryFileObject : 0xffffae09`6ac7a210 _FILE_OBJECT
   +0x058 LibraryDriverObject : 0xffffae09`6b9b4b20 _DRIVER_OBJECT
   +0x060 LibraryInfo      : 0xfffff80e`55bcf140 _WDF_LIBRARY_INFO ;Wdf01000.sys設定的回調函數的位址
   +0x068 ClientsListHead  : _LIST_ENTRY [ 0xffffae09`6c767110 - 0xffffae09`6af091b0 ]
   +0x078 ClientsListLock  : _ERESOURCE
   +0x0e0 Version          : _WDF_VERSION
   +0x0f0 LoaderEvent      : _KEVENT
   +0x108 LoaderThread     : (null) 
   +0x110 ClassListHead    : _LIST_ENTRY [ 0xffffae09`6ba1bd08 - 0xffffae09`6ba1bd08 ]
;顯示Wdf01000.sys設定的回調函數
kd> dt _WDF_LIBRARY_INFO 0xfffff80e`55bcf140 
Wdf01000!_WDF_LIBRARY_INFO
   +0x000 Size             : 0x38
   +0x008 LibraryCommission : 0xfffff80e`55b46870     long  Wdf01000!LibraryCommission+0
   +0x010 LibraryDecommission : 0xfffff80e`55b759c0     long  Wdf01000!LibraryDecommission+0
   +0x018 LibraryRegisterClient : 0xfffff80e`55b259d0     long  Wdf01000!LibraryRegisterClient+0 
   ;這是我們上文分析的建立WdfDriverGlobals的函數入口
   +0x020 LibraryUnregisterClient : 0xfffff80e`55b759e0     long  Wdf01000!LibraryUnregisterClient+0
   +0x028 Version          : _WDF_VERSION

kd> .srcfix C:\srcfix\Windows-Driver-Frameworks-master\src\framework ;設定Wdf架構源碼路徑
Source search path is: SRV*;C:\srcfix\Windows-Driver-Frameworks-master\src\framework
kd> lsa Wdf01000!LibraryRegisterClient ;顯示源碼,WDFLDR!_LIBRARY_MODULE->_WDF_LIBRARY_INFO->LibraryRegisterClient指向Wdf01000!LibraryRegisterClient
   458:     clientInfo = (PCLIENT_INFO)*Context;
   459:     *Context = NULL;
   460: 
   461:     ASSERT(Info->Version.Major == WdfLibraryInfo.Version.Major);
>  462:      

    windbg顯示的資訊和前面我的猜測有點出路,看來要結合SourceInsight看看是不是遺漏什麼。原來Wdf01000.sys在DriverEntry中通過WdfLdr!WdfRegisterLibrary注冊了WdfLibraryInfo結構:

extern "C" {
#pragma prefast(suppress:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "kernel component.");
WDF_LIBRARY_INFO  WdfLibraryInfo = {
    sizeof(WDF_LIBRARY_INFO),
    (PFNLIBRARYCOMMISSION)        WDF_LIBRARY_COMMISSION,
    (PFNLIBRARYDECOMMISSION)      WDF_LIBRARY_DECOMMISSION,
    (PFNLIBRARYREGISTERCLIENT)    WDF_LIBRARY_REGISTER_CLIENT,
    (PFNLIBRARYUNREGISTERCLIENT)  WDF_LIBRARY_UNREGISTER_CLIENT,
    { __WDF_MAJOR_VERSION, __WDF_MINOR_VERSION, __WDF_BUILD_NUMBER }
};      

這個結構體變量初始化看着有點怪怪的,各個成員更像是一段宏,而不是某個函數位址。那就根據這些宏的名字,按圖索骥,找到定義他們的地方;

#define WDF_LIBRARY_COMMISSION          LibraryCommission
#define WDF_LIBRARY_DECOMMISSION        LibraryDecommission
#define WDF_LIBRARY_REGISTER_CLIENT     LibraryRegisterClient
#define WDF_LIBRARY_UNREGISTER_CLIENT   LibraryUnregisterClient      

恩,我們順利找到了宏WDF_LIBRARY_REGISTER_CLIENT對應的函數名:LibraryRegisterClient,而這個函數名就是前面windbg lsa指令顯示的源碼,由它調用了FxLibraryCommonRegisterClient去配置設定和初始化WdfDriverGlobals對象,供以後其他DDI使用:

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
    )
{
    NTSTATUS           status = STATUS_INVALID_PARAMETER;
    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    WCHAR              insertString[EVTLOG_MESSAGE_SIZE];
    ULONG              rawData[RAW_DATA_SIZE];
    PCLIENT_INFO       clientInfo = NULL;
    ...
    status = FxLibraryCommonRegisterClient(Info,
                                           WdfDriverGlobals,
                                           clientInfo);      

繼續閱讀