Yes, More Callbacks — The Kernel Extension Mechanism

The only useful resource I found was the leaked ntosifs.h header file, which contains the prototype for nt!ExRegisterExtension as well as the layout of the _EX_EXTENSION_REGISTRATION_1 structure.Prototype for nt!ExRegisterExtension and _EX_EXTENSION_REGISTRATION_1, as supplied in ntosifs.h:NTKERNELAPI NTSTATUS ExRegisterExtension ( _Outptr_ PEX_EXTENSION *Extension, _In_ ULONG RegistrationVersion, _In_ PVOID RegistrationInfo);typedef struct _EX_EXTENSION_REGISTRATION_1 { USHORT ExtensionId; USHORT ExtensionVersion; USHORT FunctionCount; VOID *FunctionTable; PVOID *HostInterface; PVOID DriverObject;} EX_EXTENSION_REGISTRATION_1, *PEX_EXTENSION_REGISTRATION_1;After a bit of reverse engineering, I figured that the formal input parameter “PVOID RegistrationInfo” is actually of type PEX_EXTENSION_REGISTRATION_1.The pseudo-code of nt!ExRegisterExtension is shown in appendix B, but here are the main points:nt!ExRegisterExtension extracts the ExtensionId and ExtensionVersion members of the RegistrationInfo structure and uses them to locate a matching host in nt!ExpHostList (using the nt!ExpFindHost function, whose pseudo-code appears in appendix B).Then, the function verifies that the amount of functions supplied in RegistrationInfo->FunctionCount matches the expected amount set in the host’s structure..It also makes sure that the host’s FunctionTable field has not already been initialized..Basically, this check means that an extension cannot be registered twice.If everything seems OK, the host’s FunctionTable field is set to point to the FunctionTable supplied in RegistrationInfo.Additionally, RegistrationInfo->HostInterface is set to point to some data found in the host structure..This data is interesting, and we’ll discuss it soon.Eventually, the fully initialized host is returned to the caller via an output parameter.We saw that nt!ExRegisterExtension searches for a host that matches RegistrationInfo..The question now is, where do these hosts come from?During its initialization, NTOS performs several calls to nt!ExRegisterHost..In every call it passes a structure identifying a single driver from a list of predetermined drivers (full list in appendix A)..For example, here is the call which initializes a host for Bam.sys:nt!ExRegisterHost allocates a structure of type _HOST_LIST_ENTRY (unofficial name, coined by me), initializes it with data supplied by the caller, and adds it to the end of nt!ExpHostList..The _HOST_LIST_ENTRY structure is undocumented, and looks something like this:struct _HOST_LIST_ENTRY{ _LIST_ENTRY List; DWORD RefCount; USHORT ExtensionId; USHORT ExtensionVersion; USHORT FunctionCount; // number of callbacks that the extension // contains POOL_TYPE PoolType; // where this host is allocated PVOID HostInterface; // table of unexported nt functions, // to be used by the driver to which // this extension belongs PVOID FunctionAddress; // optional, rarely used..// This callback is called before // and after an extension for this // host is registered / unregistered PVOID ArgForFunction; // will be sent to the function saved here _EX_RUNDOWN_REF RundownRef; _EX_PUSH_LOCK Lock; PVOID FunctionTable; // a table of the callbacks that the // driver “registers” DWORD Flags; // Only uses one bit..// Not sure about its meaning.} HOST_LIST_ENTRY, *PHOST_LIST_ENTRY;When one of the predetermined drivers loads, it registers an extension using nt!ExRegisterExtension and supplies a RegistrationInfo structure, containing a table of functions (as we saw Bam.sys doing)..This table of functions will be placed in the FunctionTable member of the matching host..These functions will be called by NTOS in certain occasions, which makes them some kind of callbacks.Earlier we saw that part of nt!ExRegisterExtension functionality is to set RegistrationInfo->HostInterface (which contains a global variable in the calling driver) to point to some data found in the host structure. Let’s get back to that.Every driver which registers an extension has a host initialized for it by NTOS. This host contains, among other things, a HostInterface, pointing to a predetermined table of unexported NTOS functions. Different drivers receive different HostInterfaces, and some don’t receive one at all.For example, this is the HostInterface that Bam.sys receives:So the “kernel extensions” mechanism is actually a bi-directional communication port: The driver supplies a list of “callbacks”, to be called on different occasions, and receives a set of functions for its own internal use.To stick with the example of Bam.sys, let’s take a look at the callbacks that it supplies:BampCreateProcessCallbackBampSetThrottleStateCallbackBampGetThrottleStateCallbackBampSetUserSettingsBampGetUserSettingsHandleThe host initialized for Bam.sys “knows” in advance that it should receive a table of 5 functions. These functions must be laid-out in the exact order presented here, since they are called according to their index. As we can see in this case, where the function found in nt!PspBamExtensionHost->FunctionTable[4] is called:To conclude, there exists a mechanism to “extend” NTOS by means of registering specific callbacks and retrieving unexported functions to be used by certain predetermined drivers.I don’t know if there is any practical use for this knowledge, but I thought it was interesting enough to share. If you find anything useful / interesting to do with this mechanism, I’d love to know :)Appendix A — Extension hosts initialized by NTOS:Appendix B — functions pseudo-code:Appendix C — structures definitions: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 of callbacks that the // extension contains POOL_TYPE PoolType; // where this host is allocated PVOID HostInterface; // table of unexported nt functions, // to be used by the driver to which // this extension belongs PVOID FunctionAddress; // optional, rarely used.. More details

Leave a Reply