This post introduces how one can start reverse engineering UEFI-based BIOS modules. Taking Absolute as an example, this post serves as a tutorial of BIOS module reverse engineering with free tools and approachable steps for beginners.
This post is not to explain how to disable or discover issues in Absolute.
In this post, terms "BIOS", "UEFI" and "firmware" all refer to UEFI-based host firmware and are interchangeable.
Background Story
You can skip this section.
Last week, I got a Dell laptop with activated Absolute.
Absolute, formally known as Computrace, is popular data and device security software with an interesting persistent technology as explained in Wikipedia.Absolute's flagship product is the Absolute Platform, formerly known as Data and Device Security (DDS). Absolute relies on patented Persistence technology, which is embedded into the firmware of most computers, tablets, and smartphones at the factory.[25]
The Persistence module is activated once the Absolute agent is installed. If the software client is removed from a device through flashing the firmware, replacing the hard drive, reimaging the device, or resetting the device back to factory settings, Persistence technology will trigger an automatic reinstallation of the software client.[25] Persistence technology is embedded in more than half a billion devices worldwide.
I read multiple articles about its internals in the past but did not know much about the modules embedded in the firmware. Out of curiosity, I started to reverse engineer it, then decided to write up the steps I took because I believed this area needed more engineers' scrutiny and tutorials for it.
Getting a BIOS image
There are two easy ways to get a BIOS image to analyze:
extracting from an update package or using CHIPSEC.
BIOS images may be extracted from BIOS update packages OEMs
publish. For example, any recent Dell's BIOS images can be extracted with a script in found in @platomaniac's
As a handy sample, here is the Dell BIOS image we will analyze in this post.
Alternatively, with CHIPSEC, one can dump the BIOS image of the current system with this command after installation if the system is supported.
$ python chipsec_util.py spi dump rom.bin
Identifying Absolute's module
UEFI modules are normally OS-agnostics. It is, however, not the case for the modules that need to interact with OS environment to establish OS-level persistency, for example. We can find out such peculiar modules by searching OS-specific strings such as "System32" and "NtOpen." Let us do this.
Open the the extracted image with UEFITool and search "System32". This will list 821ACA26-29EA-4993-839F-597FC021708D.
Note that UEFI modules are identified by GUIDs and not
human-friendly names. Names may be specified but are optional and unused by the
platform software. Take 821ACA26-29EA-4993-839F-597FC021708D as an example, it
is unnamed in our image, but in other image, it is named as
"efiinstnats". The internet also suggests it may be named as
"AbsoluteAbtInstaller".
Reverse Engineering a UEFI module
As the UEFITools shows, UEFI modules are vastly in the PF format and can be analyzed with existing tools.
To reserves engineer 821ACA26-29EA-4993-839F-597FC021708D, on UEFITool, right click the file and extract its body. Then, install Ghidra and the efiSeek plugin as a free option. The other popular option is IDA and efiXplorer, though the free version of IDA is not usable for our scenario.
Open the extracted file
(821ACA26-29EA-4993-839F-597FC021708D) with Ghidra and make sure efiSeek is
checked for auto analysis. Ignore a warning about PDB if it appears.
Strings contained in the module is VERY interesting.
What on earth a UEFI module has to do with SystemRoot. Either way, the string “Computrace” indicates this is Absolute’s component.
By peeking at functions called from the entry point, we can find an interesting function calling InstallAcpiTable().
As we can read from the API name it installs… an ACPI table, but what is it? With little bit of clean up, we can find some string literal looking values are assigned to the table variable, in particular, WPBT at the offset 0 looks interesting.With some google, we can find the Microsoft’s document explaining the table: Windows Platform Binary Table (WPBT) (DOCX)
In short, this type of ACPI table lets a UEFI module instruct Windows’ Session Manager to launch a specified executable on startup. We can see the use of the table in the code.
Let us just verify what is being registered. The handoff memory appears to be initialized with the data at 0x80013178, which are coming from 0x80001138 containing the MZ header.
From here, you could investigate smss.exe to see how the program
is executed and wpbbin.exe to see the contents of the program. In this post, we are going to further
look into BIOS, however.
Tracking inter-module dependencies
We have understood how the module installs auto startup mechanism for Windows, but how 821ACA26-29EA-4993-839F-597FC021708D gets executed? The file is an “Application” that is not automatically loaded the platform software.
The short answer: another module starts it.
Finding the parent module requires UEFI specific knowledge that starting
an application in the firmware image is done with those steps.
- Locating the application file via GUID through EFI_LOADED_IMAGE_PROTOCOL
- Calling LoadImage() and StartImage()
EDK2's UEFI Driver Writer's Guide shows example code that looks about like this:
With this knowledge, we can search the GUID of the application (821ACA26-29EA-4993-839F-597FC021708D) and locate the parent module.
As show above, the module 8B778A74-C275-49D5-93ED-4D709A129CB1 is found and is a DXE driver, meaning it is executed automatically by the platform software.
Note that this module does not have the name in our image but other images had
it as AbtDxe and DellAbtDxeBin.
Open the image with Ghidra and search the GUID through
memory search. We can find the GUID at 0x800050e0 as shown below.
By cross-referencing 0x800050e0, we can find the function using the GUID and calling StartImage().
As we inspect the FUN_80002fe8 called above, it becomes obvious that the function calls LoadImage() with the GUID as input, and then, StartImage() is called, which launches the application.
When are those functions called? By cross referencing the
function, we can find that the pointer of the function is passed to the CreateEventEx() with EFI_EVENT_READY_TO_BOOT_GUID.
We can make better sense of this with the UEFI specification (PDF).
As highlighted, the function is set as a callback for the
event that is called right before the OS boot loader starts.
To summarize the flow:
- The driver 8B778A74-C275-49D5-93ED-4D709A129CB1 is loaded by the platform software.
- The driver 8B778A74-C275-49D5-93ED-4D709A129CB1 registers the event notification.
- When the system is about to start the boot loader (eg, bootmgfw.efi and grub.efi), the event is signaled.
- The driver 8B778A74-C275-49D5-93ED-4D709A129CB1 starts the application 821ACA26-29EA-4993-839F-597FC021708D.
- The application 821ACA26-29EA-4993-839F-597FC021708D installs the WPBT ACPI table.
- If Windows is booted, smss.exe creates wpbbin.exe from the table and executes it.
More inter-module interactions
An astute reader might notice we have not looked into how the above flow can be activated, or skipped in case Absolute is not enabled by the user. Answering to this question requires further analysis of UEFI variables and additional OEM-specific modules.
On the UEFI variables, Absolute uses few named as Abt* in the a0b1889e-00eb-445b-8ca9-e91ce43c907d namespace. They can be found as Unicode strings easily.
On the additional modules, the below snippet indicates that the condition
to launch the applications is either:
- LocateProtocol() failed, or
- LocateProtocol() succeeded and the bit 0 of the retrieved data is set
The first case does not appear to happen. The second case depends
on whether another module that installed the “unknownProtocol_fa02fb02” protocol
sets the bit on their side. Meaning that we would have to reverse engineer the
other module to determine the exact condition.
One can find the additional module by searching the protocol GUID
on UEFITool again. Those are modules I found on my laptops:
- 0FEBE434-A6AF-4166-BC2F-DE2C5952C87D (DellAbsoluteDxe) in Dell laptops
- A81DD68E-F878-49FF-8309-798444A9C035 (AbtSmm) in an Acer laptop
- 458034FD-DE82-44F1-8398-6D941F85F473 and 22E6FAB5-A6C4-4FF6-AE8C-C16939911BCD in a HP laptop
Analysis of the UEFI variable and OEM-specific modules is left as an exercise for readers, as they differ across OEMs.
Conclusion
Through this exercise, we studied:
- How a BIOS image file can be extracted
- How modules with tight dependency on Windows can be located
- How the WBPT ACPI table may be used to establish persistency on Windows
- How events can be used for differed execution
- How dependent modules may be located in the BIOS image
- Using only freely available, cross-platform software
Hopefully, you found reverse engineering BIOS images was interesting and more approachable than previously you thought.
Hey Satoshi! This is Josiah, I had emailed you several months ago about your AMD hypervisor.
ReplyDeleteJust wanted to say I hope you never stop writing to this blog, I'm finding every article super fascinating and it's been really helpful to me