Geoff Chappell, Software Analyst
This function registers a shim for eventual application to one or more drivers.
NTSTATUS KseRegisterShimEx ( KSE_SHIM *Shim, PVOID Ignored, ULONG Flags, PVOID Object);
Except for KSE_SHIM, the names and types in the preceding declaration are confected for this article, Microsoft’s being unknown.
The Shim argument is the address of a KSE_SHIM structure that mostly provides input to the function but also receives some output. As input, it describes the shim, including to specify a GUID and to point to arrays of KSE_COLLECTION structures that have pointers to arrays of KSE_HOOK structures. On output, it receives a pointer to a table of callback routines. The structure may be further edited if the shim ever is applied to any driver.
The Ignored argument appears to be ignored.
The Flags argument seems intended to offer some configurability.
The Object argument is the address of an object for the kernel to keep referenced while the shim is applied. This argument can be NULL (and is when the function is called from KseRegisterShim).
The function returns STATUS_SUCCESS if successful, else a negative error code.
The KseRegisterShimEx function is exported by name from the kernel in version 6.2 and higher.
The KseRegisterShimEx function is not documented. Nor is it declared in any header from any known Windows Driver Kit (WDK).
The function must be given a shim to register else it has nothing to do: if instead Shim is NULL, the function returns STATUS_INVALID_PARAMETER.
If some problem occurred while initialising the Kernel Shim Engine (KSE) such that it is not operational, there is no point trying to proceed, and the function returns STATUS_UNSUCCESSFUL.
The function checks the KSE_SHIM for plausibility. Among the intentions to this validation is that all hooks to which a shimmed driver’s execution might be diverted must be within the shim provider, i.e., the caller of this function. As preparation, the function needs a list of loaded modules (for the base address and size of each module’s executable image in memory). If the function cannot get the list, it fails, returning whatever error it ran into. If the return address of this function is not in an executable image as known from the list, the function returns STATUS_NOT_FOUND.
The KSE_SHIM must have a non-NULL pointer to an array of KSE_COLLECTION structures. Each KSE_COLLECTION structure must have a non-NULL pointer to an array of KSE_HOOK structures. Each KSE_HOOK structure must have a non-NULL pointer to a hook routine. Each hook routine must lie inside the shim provider, or so seems to be the intention. Failure anywhere in this hierarchy is failure for the function, which returns STATUS_UNSUCCESSFUL. Note though, that the last test for each hook is presently ineffective: what’s coded is to reject a hook that is below the shim provider’s base and not below the shim provider’s base plus size.
All being well, the KSE will keep a small structure—which text that can be logged on errors refers to as a shim object—to represent this shim in a linked list of all registered shims. If the function can’t get memory (from paged pool) for this structure, it fails, returning STATUS_INSUFFICIENT_RESOURCES. If the list already has a structure for a registered shim with the same GUID as given in the KSE_SHIM that’s being registered now, then the function fails, returning STATUS_OBJECT_NAME_COLLISION. Ordinarily, however, the structure is inserted into the list and the shim thus becomes registered.
Before returning, the function edits the KSE_SHIM to tell the shim provider of two callback routines that may help should the shim ever be applied to a driver.
How a shim ever does get applied, and to what drivers, is not the business of this function but does on one count have some implication for the function’s use. Whenever a driver is loaded, the KSE consults a database to learn what shims, if any, should be applied. Each shim is indicated by its GUID and an expected shim provider. If no shim with that GUID is yet registered, the KSE loads the corresponding shim provider, plainly expecting that the shim will then get registered. The strong suggestion is that shim providers call this function while initialising.