您好,登錄后才能下訂單哦!
如何解析Windows內核擴展機制,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
近期,我自己寫了一個內核模式驅動器,跟我平時寫的很多代碼一樣,這個驅動器也存在Bug。 確切來說,這個驅動器可以防止其他驅動器正常加載,并導致系統崩潰。
實際上,很多啟動器會默認它們的初始化進程(DriverEntry)能夠成功執行,因此在出現問題時它們是無法正確處理的。j00ru曾在幾年前發布的博文中提過幾種案例,而且其中有很多都跟目前的Windows版本有關,而我只對其中的一個驅動器比較感興趣。這里最吸引我的還是“Windows內核主機擴展機制”。
這個驅動器是Bam.sys(Background Activity Moderator)- Windows 10 v1709(RS3)中新引入的一種驅動器。當它的DriverEntry中途出現問題時,調用棧會導致系統發生崩潰:
從崩潰信息中我們可以看到,Bam.sys注冊了一個進程創建回調,并且在卸載之前忘記注銷它了。接下來,當一個進程被創建/終止時,系統會嘗試調用這個回調,并引用舊指針,然后導致系統崩潰。
有趣的并不是系統發生崩潰,而是Bam.sys到底是如何注冊這個回調的。一般來說,進程創建回調是通過nt!PsSetCreateProcessNotifyRoutine(Ex)注冊的,它會向nt!PspCreateProcessNotifyRoutine數組中添加這個回調。接下來,當進程被創建/終止時,nt!PspCallProcessNotifyRoutines會迭代循環這個數組,并調用所有已注冊的回調。然而,如果我們在WinDbg中運行“!wdbgark.wa_systemcb /typeprocess“的話,我們將會看到這個數組中并不存在Bam.sys所使用的回調:
原來,Bam.sys使用了另一種機制來注冊它的回調:
查看nt!PspCallProcessNotifyRoutines后我們發現,其中有一個指向nt!PspBamExtensionHost的顯示引用。它會使用這種“擴展主機”來檢索所謂的“擴展表”,并調用擴展表中的第一個函數,即bam!BampCreateProcessCallback。
在IDA中打開Bam.sys,我們可以直接看到bam!BampCreateProcessCallback,并搜索它的xrefs。幸好它只有一個,就在bam!BampRegisterKernelExtension中:
我懷疑Bam!BampCreateProcessCallback并沒有通過常規的回調注冊機制來進行注冊。實際上,它被存在了一個名叫Bam!BampKernelCalloutTable的函數表中,而這個表會和其他參數一起傳遞給nt!ExRegisterExtension函數(文檔中未記載)。
唯一一個我找到的跟這個函數相關的就是ntosifs.h頭文件,其中包含了nt!ExRegisterExtension函數的原型以及_EX_EXTENSION_REGISTRATION_1結構體。
nt!ExRegisterExtension函數原型和_EX_EXTENSION_REGISTRATION_1:
NTKERNELAPINTSTATUS ExRegisterExtension ( _Outptr_ PEX_EXTENSION *Extension, _In_ ULONG RegistrationVersion, _In_ PVOID RegistrationInfo); typedefstruct _EX_EXTENSION_REGISTRATION_1 { USHORT ExtensionId; USHORT ExtensionVersion; USHORT FunctionCount; VOID *FunctionTable; PVOID *HostInterface; PVOID DriverObject;}EX_EXTENSION_REGISTRATION_1, *PEX_EXTENSION_REGISTRATION_1;
注意:nt!ExRegisterExtension的偽代碼可在附錄B中找到。
我們可以看到,nt!ExRegisterExtension會搜索名稱匹配RegistrationInfo的主機。那么問題來了,這些主機是從哪兒來的呢?
在初始化的過程中,NTOS會多次調用nt!ExRegisterHost。每次調用時,它都會傳遞一個結構體來識別列表中預定義的驅動器(完整列表可在附錄A中找到)。比如說,Bam.sys會使用下列調用來初始化一臺主機:
nt!ExRegisterHost會分配一個type_HOST_LIST_ENTRY結構體,并使用調用方提供的參數來進行初始化,然后將其添加到nt!ExpHostList結尾。_HOST_LIST_ENTRY結構體文檔未記載,它的大致結構如下:
struct_HOST_LIST_ENTRY{ _LIST_ENTRY List; DWORD RefCount; USHORT ExtensionId; USHORT ExtensionVersion; USHORT FunctionCount; // number ofcallbacks that the extension // contains POOL_TYPE PoolType; // where this host is allocated PVOID HostInterface; // table of unexportednt functions, // to be used by thedriver to which // this extensionbelongs PVOID FunctionAddress; // optional, rarelyused. // This callback iscalled before // and after anextension for this // host isregistered / unregistered PVOID ArgForFunction; // will be sent tothe function saved here _EX_RUNDOWN_REF RundownRef; _EX_PUSH_LOCK Lock; PVOID FunctionTable; // a table of thecallbacks that the // driver “registers” DWORD Flags; // Only uses one bit. // Not sure about itsmeaning.}HOST_LIST_ENTRY, *PHOST_LIST_ENTRY;
當其中一個預定義的驅動器加載時,它會使用nt!ExRegisterExtension注冊一個擴展,并提供一個RegistrationInfo結構體,其中包括一個函數表。這個函數表會被置入匹配主機的FunctionTable成員中。這些函數隨后會被NTOS在特定的場景下調用,這也就是所謂的回調了。
之前我們看到了nt!ExRegisterExtension的部分功能會被設置為RegistrationInfo->HostInterface(包含一個全局變量),并指向主機結構體中的某些數據。
每一個注冊了擴展的驅動器都有一個NTOS設置的初始化主機,這種主機包含一個HostInterface,它指向HTOS函數預定一個一個表,不同的驅動器會收到不同的HostInterface,但有些則不會收到。
比如說,Bam.sys收到的就是下面這個HostInterface:
實際上,這里所謂的“內核擴展機制”即使一種雙向通信端口:驅動器提供一個“回調”列表,以供在不同場景下調用,然后在各自內部使用場景中接收不同的函數集。
下面給出的是Bam.sys的回調樣本:
BampCreateProcessCallbackBampSetThrottleStateCallbackBampGetThrottleStateCallbackBampSetUserSettingsBampGetUserSettingsHandle
負責Bam.sys初始化的主機預先“知道”它需要接收包含上面這5個函數的函數表。這些函數必須按照上述順序提供,因此這里是通過索引來調用的。我們可以看到,nt!PspBamExtensionHost->FunctionTable[4]調用情況:
總而言之,Windows中存在一種機制,可以用來“擴展”NTOS,具體過程是先注冊某些回調函數,然后接收驅動可以使用的未導出函數。我并不清楚這個知識點是否能發揮實際作用,但覺得這方面內容比較有趣,值得與大家分享。希望可以給大家提供幫助,感謝閱讀!
NTSTATUSExRegisterExtension(_Outptr_ PEX_EXTENSION *Extension, _In_ ULONGRegistrationVersion, _In_ PREGISTRATION_INFO RegistrationInfo){ // Validate that version is ok and thatFunctionTable is not sent without FunctionCount or vise-versa. if ( (RegistrationVersion &0xFFFF0000 != 0x10000) || (RegistrationInfo->FunctionTable == nullptr&& RegistrationInfo->FunctionCount != 0) ) { return STATUS_INVALID_PARAMETER; } // Skipping over some lock-related stuff, // Find the host with the matchingversion and id. PHOST_LIST_ENTRY pHostListEntry; pHostListEntry =ExpFindHost(RegistrationInfo->ExtensionId,RegistrationInfo->ExtensionVersion); // More lock-related stuff. if (!pHostListEntry) { return STATUS_NOT_FOUND; } // Verify that the FunctionCount in thehost doesn't exceed the FunctionCount supplied by the caller. if (RegistrationInfo->FunctionCount< pHostListEntry->FunctionCount) { ExpDereferenceHost(pHostListEntry); return STATUS_INVALID_PARAMETER; } // Check that the number of functions inFunctionTable matches the amount in FunctionCount. PVOID FunctionTable =RegistrationInfo->FunctionTable; for (int i = 0; i <RegistrationInfo->FunctionCount; i++) { if (RegistrationInfo->FunctionTable[i] == nullptr ) { ExpDereferenceHost(pHostListEntry); returnSTATUS_ACCESS_DENIED; } } // skipping over some more lock-relatedstuff // Check if there is already an extensionregistered for this host. if (pHostListEntry->FunctionTable !=nullptr || FlagOn(pHostListEntry->Flags, 1) ) { // There is something related tolocks here ExpDereferenceHost(pHostListEntry); returnSTATUS_OBJECT_NAME_COLLISION; } // If there is a callback function forthis host, call it before registering the extension, with 0 as the firstparameter. if (pHostListEntry->FunctionAddress) { pHostListEntry->FunctionAddress(0,pHostListEntry->ArgForFunction); } // Set the FunctionTable in the host tothe table supplied by the caller, or to MmBadPointer if a table wasn'tsupplied. if (RegistrationInfo->FunctionTable ==nullptr) { pHostListEntry->FunctionTable= nt!MmBadPointer; } else { pHostListEntry->FunctionTable= RegistrationInfo->FunctionTable; } pHostListEntry->RundownRef = 0; // If there is a callback function forthis host, call it after registering the extension, with 1 as the firstparameter. if (pHostListEntry->FunctionAddress) { pHostListEntry->FunctionAddress(1,pHostListEntry->ArgForFunction); } // Here there is some more lock-relatedstuff // Set the HostTable of the callingdriver to the table of functions listed in the host. if (RegistrationInfo->HostTable !=nullptr) { *(PVOID)RegistrationInfo->HostTable= pHostListEntry->hostInterface; } // Return the initialized host to thecaller in the output Extension parameter. *Extension = pHostListEntry; return STATUS_SUCCESS;}
NTSTATUSExRegisterHost(_Out_ PHOST_LIST_ENTRY ExtensionHost, _In_ ULONG Unused, _In_PHOST_INFORMATION HostInformation){ NTSTATUS Status = STATUS_SUCCESS; // Allocate memory for a newHOST_LIST_ENTRY PHOST_LIST_ENTRY p =ExAllocatePoolWithTag(HostInformation->PoolType, 0x60, 'HExE'); if (p == nullptr) { returnSTATUS_INSUFFICIENT_RESOURCES; } // // Initialize a new HOST_LIST_ENTRY // p->Flags &= 0xFE; p->RefCount = 1; p->FunctionTable = 0; p->ExtensionId =HostInformation->ExtensionId; p->ExtensionVersion =HostInformation->ExtensionVersion; p->hostInterface =HostInformation->hostInterface; p->FunctionAddress =HostInformation->FunctionAddress; p->ArgForFunction =HostInformation->ArgForFunction; p->Lock = 0; p->RundownRef = 0; // Search for an existing listEntry withthe same version and id. PHOST_LIST_ENTRY listEntry = ExpFindHost(HostInformation->ExtensionId,HostInformation->ExtensionVersion); if (listEntry) { Status =STATUS_OBJECT_NAME_COLLISION; ExpDereferenceHost(p); ExpDereferenceHost(listEntry); } else { // Insert the new HOST_LIST_ENTRYto the end of ExpHostList. if ( *lastHostListEntry !=&firstHostListEntry ) { __fastfail(); } firstHostListEntry->Prev =&p; p->Next = firstHostListEntry; lastHostListEntry = p; ExtensionHost = p; } return Status;}
PHOST_LIST_ENTRYExpFindHost(USHORT ExtensionId, USHORT ExtensionVersion){ PHOST_LIST_ENTRY entry; for (entry == ExpHostList; ; entry =entry->Next) { if (entry == &ExpHostList) { return 0; } if ( *(entry->ExtensionId) ==ExtensionId && *(entry->ExtensionVersion) == ExtensionVersion ) { break; } }InterlockedIncrement(entry->RefCount); return entry;}
voidExpDereferenceHost(PHOST_LIST_ENTRY Host){ if (InterlockedExchangeAdd(Host.RefCount, 0xFFFFFFFF) == 1 ) { ExFreePoolWithTag(Host, 0); }}
struct_HOST_INFORMATION{ USHORT ExtensionId; USHORT ExtensionVersion; DWORD FunctionCount; POOL_TYPE PoolType; PVOID HostInterface; PVOID FunctionAddress; PVOID ArgForFunction; PVOID unk;}HOST_INFORMATION, *PHOST_INFORMATION; struct_HOST_LIST_ENTRY{ _LIST_ENTRY List; DWORD RefCount; USHORT ExtensionId; USHORT ExtensionVersion; USHORT FunctionCount; // number ofcallbacks that the // extension contains POOL_TYPE PoolType; // where this host is allocated PVOID HostInterface; // table of unexported nt functions, // to be used by thedriver to which // this extensionbelongs PVOID FunctionAddress; // optional, rarelyused. // This callback iscalled before and // after anextension for this host // is registered /unregistered PVOID ArgForFunction; // will be sent tothe function saved here _EX_RUNDOWN_REF RundownRef; _EX_PUSH_LOCK Lock; PVOID FunctionTable; // a table of the callbacks that // the driver“registers”DWORDFlags; // Only uses oneflag. // Not sure about its meaning.}HOST_LIST_ENTRY, *PHOST_LIST_ENTRY;; struct_EX_EXTENSION_REGISTRATION_1{ USHORT ExtensionId; USHORT ExtensionVersion; USHORT FunctionCount; PVOID FunctionTable; PVOID *HostTable; PVOID DriverObject;}EX_EXTENSION_REGISTRATION_1,*PEX_EXTENSION_REGISTRATION_1;
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。