Early Launch Antimalware Drivers
Chapter 10
Drivers can register callback routines called shutdown handlers that let them perform some action when the system is shutting down. This is abused by rootkits and bootkits for persistence.
To address this weakness, Microsoft introduced a new antimalware feature in Windows 8 that allows certain special drivers to load before all other boot-start drivers. Today, nearly all EDR vendors leverage this capability, called Early Launch Antimalware (ELAM).
How ELAMs Protect The Boot Process
The primary function of an ELAM driver is to receive notifications when another driver attempts to load during the boot process, then decide whether to allow it to load. This validation process is part of Trusted Boot and only vetted antimalware vendors can participate in it.
To publish an ELAM driver, developers must be part of the Microsoft Virus Initiative (MVI), a program open to antimalware companies that produce security software for the Windows operating system.
To create a production driver, Microsoft must countersign the driver. This counter signature uses a special certificate, visible in the ELAM driver’s digital signature information under Microsoft Windows Early Launch Anti-malware Publisher.
Developing ELAM Drivers
Registering Callback Routines
The first ELAM-specific action the driver takes is to register its callback routines. ELAM drivers commonly use both registry and boot-start callbacks. The registry callback functions, registered with nt!CmRegisterCallbackEx(), validate the configuration data of the drivers being loaded in the registry.
The boot-start callback routine is registered with nt!IoRegisterBootDriverCallback(). This callback provides the ELAM driver with updates about the status of the boot process, as well as information about each boot-start driver being loaded. Boot-start callback functions are passed to the registration function as a PBOOT_DRIVER_CALLBACK _FUNCTION and must have a signature matching the one shown below:
PVOID IoRegisterBootDriverCallback(
[in] PBOOT_DRIVER_CALLBACK_FUNCTION CallbackFunction,
[in, optional] PVOID CallbackContext
);
void BootDriverCallbackFunction(
PVOID CallbackContext,
BDCB_CALLBACK_TYPE Classification,
PBDCB_IMAGE_INFORMATION ImageInformation
)
During the boot process, this callback routine receives two different types of events, dictated by the value in the Classification input parameter. These are defined in the BDCB_CALLBACK_TYPE enum.
typedef enum _BDCB_CALLBACK_TYPE {
BdCbStatusUpdate,
BdCbInitializeImage,
} BDCB_CALLBACK_TYPE, *PBDCB_CALLBACK_TYPE;
The BdCbStatusUpdate events tell the ELAM driver how far the system has gotten in the process of loading boot-start drivers so that the driver may act appropriately. It can report any of three states:
typedef enum _BDCB_STATUS_UPDATE_TYPE {
BdCbStatusPrepareForDependencyLoad,
BdCbStatusPrepareForDriverLoad,
BdCbStatusPrepareForUnload
} BDCB_STATUS_UPDATE_TYPE, *PBDCB_STATUS_UPDATE_TYPE;
The first of these values indicates that the system is about to load driver dependencies. The second indicates that the system is about to load bootstart drivers. The last indicates that all boot-start drivers have been loaded, so the ELAM driver should prepare to be unloaded.
During the first two states, the ELAM driver will receive another type of event that correlates to the loading of a boot-start driver’s image. This event is passed to the callback as a pointer to a BDCB_IMAGE_INFORMATION structure.
typedef struct _BDCB_IMAGE_INFORMATION {
BDCB_CLASSIFICATION Classification;
ULONG ImageFlags;
UNICODE_STRING ImageName;
UNICODE_STRING RegistryPath;
UNICODE_STRING CertificatePublisher;
UNICODE_STRING CertificateIssuer;
PVOID ImageHash;
PVOID CertificateThumbprint;
ULONG ImageHashAlgorithm;
ULONG ThumbprintHashAlgorithm;
ULONG ImageHashLength;
ULONG CertificateThumbprintLength;
} BDCB_IMAGE_INFORMATION, *PBDCB_IMAGE_INFORMATION;
This structure contains the bulk of the information used to decide whether some driver is a rootkit. Most of it relates to the image’s digital signature.
It notably omits a few fields you might expect to see, such as a pointer to the contents of the image on disk. This is due in part to the performance requirements imposed on ELAM drivers. Because they can affect system boot times, Microsoft imposes a time limit of 0.5 ms for the evaluation of each boot-start driver and 50 ms for the evaluation of all boot-start drivers together, within a 128KB memory footprint. Therefore, developers typically rely on static signatures to identify rootkits.
During the boot process, the operating system loads the signatures in use by ELAM drivers into an early-launch drivers registry hive under HKLM:\ELAM\, followed by the vendor’s name. This hive is unloaded later in the boot process and is not present in the registry by the time users start their sessions.
If the vendor wishes to update signatures in this hive, they may do so from user mode by mounting the hive containing the signatures from %SystemRoot%\System32\config\ELAM and modifying their key.
Applying Detection Logic
Once the ELAM driver has received the BdCbStatusPrepareForDriverLoad status update and pointers to BDCB_IMAGE_INFORMATION structures for each bootload driver, it applies its detection logic using the information provided in the structure.
Once it has made a determination, the driver updates the Classification member of the current image-information structure (not to be confused with the Classification input parameter passed to the callback function) with a value from the BDCB_CLASSIFICATION enumeration
typedef enum _BDCB_CLASSIFICATION {
BdCbClassificationUnknownImage,
BdCbClassificationKnownGoodImage,
BdCbClassificationKnownBadImage,
BdCbClassificationKnownBadImageBootCritical,
BdCbClassificationEnd
} BDCB_CLASSIFICATION, *PBDCB_CLASSIFICATION;

The ELAM driver sets one of these classifications for each boot-start driver until it receives the BdCbStatusPrepareForUnload status update instructing it to clean up. The ELAM driver is then unloaded.
Next, the operating system evaluates the classifications returned by each ELAM driver and takes action if needed. To determine which action to take, Windows consults the registry key HKLM:\System\CurrentControlSet\Control\EarlyLaunch\DriverLoadPolicy, which defines the drivers allowed to run on the system. This value is read by nt!IopInitializeBootDrivers().
The kernel (specifically, the Plug and Play manager) uses the classification specified by the ELAM driver to prevent any banned drivers from loading. All other drivers are allowed to load, and system boot continues as normal.
If the ELAM driver identifies a known malicious boot-start driver and is running on a system that leverages Measured Boot, developers must call tbs!Tbsi_Revoke_ Attestation(). What this function does is, it extends a platform configuration register bank in the TPM, specifically PCR[12], by an unspecified value and then increments the TPM’s event counter, breaking trust in the security state of the system.
Loading An ELAM Driver
Signing The Driver
First the driver needs to be signed as per Microsoft’s requirements for loading on the system. Even when operating in test-signing mode, the driver must have specific certificate attributes.
Microsoft says - Early Launch drivers are required to be signed with a codesigning certificate that also contains the Early Launch EKU “1.3.6.1.4.1.311.61.4.1” [. . .] and the “1.3.6.1.5.5.7.3.3” Code Signing EKU. Once a certificate of this form has been created, signtool.exe can be used to sign [the ELAM driver].
An testing certificate can be made using makecert utility as shown below:
PS > & 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.19042.0\x64\makecert.exe'
>> -a SHA256 -r -pe
>> -ss PrivateCertStore
>> -n "CN=DevElamCert"
>> -sr localmachine
>> -eku 1.3.6.1.4.1.311.61.4.1,1.3.6.1.5.5.7.3.3
>> C:\Users\dev\Desktop\DevElamCert.cer
Next, you can use signtool.exe, another tool from the Windows SDK, to sign the compiled ELAM driver.
PS > & 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe'
>> sign
>> /fd SHA256
>> /a
>> /ph
>> /s "PrivateCertStore"
>> /n "MyElamCert"
>> /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp
>> .\elamdriver.sys
To function properly, the ELAM driver must load very early in the boot process. This is where the concept of load-order grouping comes into play.
Setting The Load Order
When creating a boot-start service on Windows, the developer can specify when it should be loaded in the boot order. This is useful in cases when the driver depends on the availability of another service or otherwise needs to load at a specific time.
The developer can’t specify any arbitrary string for the load-order group, however. Microsoft keeps a list containing most of the groups available in the registry at HKLM:\SYSTEM\CurrentControlSet\ Control\ServiceGroupOrder.
PS> (Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\ServiceGroupOrder).List
System Reserved
EMS
WdfLoadGroup
Boot Bus Extender
System Bus Extender
SCSI miniport
Port
Primary Disk
SCSI Class
SCSI CDROM Class
FSFilter Infrastructure
FSFilter System
FSFilter Bottom
FSFilter Copy Protection
--snip--
Microsoft instructs ELAM driver developers to use the Early-Launch load-order group, which is notably missing from the ServiceGroupOrder key. No other special loading requirements exist, and you can do it simply by using sc.exe or advapi32!CreateService().
The process of loading the ELAM drivers is primarily the responsibility of the Windows bootloader, winload.efi. The bootloader searches the registry for all boot-start drivers on the system in the Early-Launch group and adds them to a list. Next, it loads core drivers, such as the System Guard Runtime Monitor (sgrmagent.sys) and the Security Events Component Minifilter (mssecflt.sys). Finally, it goes over its list of ELAM drivers, performing some integrity checking and eventually loading the drivers. Once the Early-Launch drivers are loaded, the boot process continues, and the ELAM vetting process is executed.
Last updated