https://jbeekman.nl/Reverse Engineering – Technology & Policy2015-03-09T00:00:00ZJethro Beekmanhttps://jbeekman.nl/bloghttps://jbeekman.nl/favicon.icotag:jbeekman.nl,2015-03-08:/blog/2015/03/reverse-engineering-uefi-firmware/Reverse Engineering UEFI Firmware – Technology & Policy2015-03-09T00:00:00Z2015-03-09T00:00:00Z<p>In order to <a href="/blog/2015/03/lenovo-thinkpad-hdd-password/">figure out how my BIOS drive password
worked,</a> I had to
reverse-engineer the firmware that comes with my laptop. You can find the
binary blobs on the update CD that Lenovo provides, and it turns out these
blobs are actually UEFI images. UEFI firmware is made up of many different
loadable modules (drivers, shared libraries, etc.), which are stored in the
Portable Executable (PE) image format. These modules can be extracted from the
image using Nikolaj Schlej’s excellent UEFIExtract (from
<a href="https://github.com/LongSoft/UEFITool">UEFITool</a>). Once you have all the PE
modules, the real reversing can begin.</p>
<p>It helps to understand how UEFI works. The Internet contains a wealth of
information, and here are two articles to get you started: <a href="http://mjg59.dreamwidth.org/18773.html">Getting started
with UEFI development</a> and <a href="http://x86asm.net/articles/uefi-programming-first-steps/">UEFI
Programming - First
Steps</a>. The main
problem that makes reverse engineering hard is that while the firmware consists
of over 300 loadable modules, there is no dynamic linker. Instead, the entry
point of a module gets passed an pointer to a “protocol” registry. A protocol
is basically an interface, or in other words a struct of function pointers. The
registry is keyed by Globally unique identifiers (GUIDs). To call into another
module, you need to lookup a GUID in the registry and then call some function
returned in the interface.</p>
<p>My first strategy to get some insight into the firmware was to collect GUIDs
from images and build a dependency graph. This turned out to be useless. The
UEFI image contains <em>PEI dependency sections</em> for each image, but the GUIDs
that are listed seem to have no relation to actually required protocols.
Furthermore, identifying GUIDs (also known as 16 random bytes) in binaries is
hard, and even when I manged to identify a section that seemed to store GUIDs,
there would be many GUIDs in such a section that were never referenced from
code in that image.</p>
<p>To figure out the dependencies, I decided to actually run the modules and see
which protocols they lookup and which ones they register. <em>Wait what, run UEFI
PE modules?</em> Yes, <a href="https://github.com/jethrogb/uefireverse/tree/master/efiperun">I wrote a tool called
<code>efiperun</code></a> that
can load PE modules into memory and simulate enough of what an UEFI environment
is supposed to look like to actually run them. Most modules will upon entry
lookup some standard protocols, do some initialization, and register one or
more protocols that other modules can use.</p>
<p>With this information in hand, you can do more targeted reversing, trying to
identify interfaces and function signatures. For example,
<code>LenovoTranslateService.efi</code> installs a protocol
<code>e3abb023-b8b1-4696-98e1-8eedc3d3c63d</code>. This protocol turns out to have the
following interface:</p>
<div class="language-c highlighter-coderay"><div class="CodeRay">
<div class="code"><pre><span style="color:#080;font-weight:bold">struct</span> interface_e3abb023_b8b1_4696_98e1_8eedc3d3c63d
{
<span style="color:#088;font-weight:bold">void</span>(EFIAPI *translate)(<span style="color:#088;font-weight:bold">void</span>* _this, <span style="color:#088;font-weight:bold">const</span> <span style="color:#0a8;font-weight:bold">char</span>* input, <span style="color:#0a8;font-weight:bold">char</span>* output, size_t length);
}
</pre></div>
</div>
</div>
<p>With <code>efiperun</code> you can actually write code that calls into loaded EFI modules,
which makes it easy to test installed interfaces. Utilizing this functionality,
I was able to determine that the <code>translate</code> function above actually translates
an ASCII string to keyboard scan codes.</p>
<p>When doing reverse engineering, you always end up exploring branches that turn
out to be less fruitful. But the knowledge obtained exploring such a branch can
be useful in exploring other ideas. Now that I’ve setup the stage with the
tools I’m going to use, I will describe the path that lead to the discovery of
the algorithm. Keep in mind that this is a reconstruction and the order in
which I actually figured parts out is different.</p>
<h4 id="graphical-entry-point">Graphical entry point</h4>
<p><img src="/img/blog/reverse-engineering-uefi-firmware/hdp-prompt.png" alt="Lenovo HDP Prompt" align="right" /></p>
<p>The Lenovo firmware does not make heavy use of graphical elements, but the Hard
Drive Password prompt actually does display a small pictogram, pictured on the
right. Now, judging by the filenames, there are only a few modules that deal with graphics:</p>
<ul>
<li><code>SystemGraphicsConsoleDxe.efi</code></li>
<li><code>SystemHiiImageDisplayDxe.efi</code></li>
<li><code>SystemImageDecoderDxe.efi</code></li>
<li><code>SystemImageDisplayDxe.efi</code></li>
</ul>
<p>All these modules install a single protocol that don’t use a <a href="https://github.com/jethrogb/uefireverse/blob/master/guiddb/efi_guid.c">well-known
GUID</a>,
so let’s see what modules call them. As it turns out, only
<code>SystemSplashDxe.efi</code> calls <code>SystemHiiImageDisplayDxe.efi</code>
(96ce4c12-55e4-4a1c-bbf3-73a5055fb364) and only <code>LenovoPromptService.efi</code> calls
<code>SystemImageDisplayDxe.efi</code> (71583a77-2789-4213-a83b-eef42afe85e0).
<code>SystemSplashDxe.efi</code> pretty much seems to be as advertised and even contains a
GIF file with the ThinkPad splash image. Upon further inspection,
<code>LenovoPromptService.efi</code> contains 21 BMP files, all related to displaying
password prompts. Bingo!</p>
<h4 id="password-control-program">Password control program</h4>
<p>The Prompt service installs a single protocol
56350810-2cb2-4aa0-96d2-66d1b8e1aac2 which is only called by
<code>LenovoPasswordCp.efi</code>. This module contains key code connecting various
password-related modules, and I’ll assume Cp means “control progam”. Besides
the prompt service (for text input), it also calls into
<code>LenovoSoundService.efi</code> (e01fc710-ba41-493b-a919-53583368f6d9, for beeping
noises when you press an invalid key), <code>LenovoTranslateService.efi</code> (described
above) and <code>LenovoCryptService.efi</code> (73e47354-b0c5-4e00-a714-9d0d5a4fdbfd,
supposedly a crypto module—see next section).</p>
<p>The password control program has an interesting function at offset <code>0x8cc</code> that
calls only <code>SetMem</code>, <code>CopyMem</code> and the Crypto and Translate services. Here’s
roughly the code for this function:</p>
<div class="language-c highlighter-coderay"><div class="CodeRay">
<div class="code"><pre><span style="color:#088;font-weight:bold">void</span> _0x8cc(<span style="color:#088;font-weight:bold">const</span> CHAR16 in[<span style="color:#00D">64</span>], UINT8 out[<span style="color:#00D">16</span>])
{
UINT8 ascii[<span style="color:#00D">64</span>], scancode[<span style="color:#00D">64</span>], hash[<span style="color:#00D">32</span>];
BootServices->SetMem(out,<span style="color:#00D">16</span>,<span style="color:#00D">0</span>);
BootServices->SetMem(ascii,<span style="color:#00D">64</span>,<span style="color:#00D">0</span>);
BootServices->SetMem(scancode,<span style="color:#00D">64</span>,<span style="color:#00D">0</span>);
BootServices->SetMem(hash,<span style="color:#00D">32</span>,<span style="color:#00D">0</span>);
<span style="color:#080;font-weight:bold">for</span> (<span style="color:#0a8;font-weight:bold">int</span> i=<span style="color:#00D">0</span>;i<<span style="color:#00D">64</span>;i++)
{
ascii[i]=in[i];
}
<span style="color:#080;font-weight:bold">if</span> (TranslateService)
{
TranslateService->Translate(TranslateService,ascii,scancode,<span style="color:#00D">64</span>);
<span style="color:#080;font-weight:bold">if</span> (CryptService)
{
CryptService->SHA256(CryptService,scancode,<span style="color:#00D">64</span>,hash);
BootServices->CopyMem(out,hash,<span style="color:#00D">16</span>);
}
BootServices->SetMem(ascii,<span style="color:#00D">64</span>,<span style="color:#00D">0</span>);
BootServices->SetMem(scancode,<span style="color:#00D">64</span>,<span style="color:#00D">0</span>);
BootServices->SetMem(hash,<span style="color:#00D">32</span>,<span style="color:#00D">0</span>);
}
<span style="color:#080;font-weight:bold">else</span>
{
BootServices->SetMem(ascii,<span style="color:#00D">64</span>,<span style="color:#00D">0</span>);
}
}
</pre></div>
</div>
</div>
<p>I’ll assume that this function is used to hash a password input by the user.
There’s another interesting function at offset <code>0xa30</code>, which checks whether the
input <code>CHAR16</code> is in the character class <code>[0-9A-Za-z ;]</code>, which is used to limit
the possible characters in the password input.</p>
<p>I’ve made good progress identifying part of the path from password input to
security unlock command, but here I’ve hit a dead end. It’s not really clear
from where the password control program gets called and what happens to the
hash it outputs. I’ll try a different approach next, but first let’s talk about
the crypto service.</p>
<h4 id="crypto-service">Crypto service</h4>
<p>The password control program calls a function in the Crypto service at offset
<code>0x26e0</code>, which references three GUIDs that I hadn’t seen before:</p>
<ul>
<li>69188a5f-6bbd-46c7-9c16-55f194befcdf</li>
<li>d0b3d668-16cf-4feb-95f5-1ca3693cfe56</li>
<li>6c48f74a-b4df-461f-80c4-5cae8a85b7ee</li>
</ul>
<p>These GUIDs do not appear in any <code>efiperun</code> output. Instead, I just searched
all images for appearances of these GUIDs, and they appear in 10 other images.
A noteworthy appearance is in <code>SystemCryptSvcRt.efi</code> at offset <code>0x1c70</code>. Offset
<code>0x1c70</code> is referenced at offset <code>0x330</code>, where it is immediately followed by
the unicode string “SHA256”. This is followed by a jump table at offset
<code>0x370</code>, which points to 3 jumps at offset <code>0x33c0</code> that jump to 3 functions at
offsets <code>0x753c</code>, <code>0x7570</code> and <code>0x760c</code>. The function at offset <code>0x753c</code>
references offset <code>0x2258</code>, <em>which stores the <a href="https://en.wikipedia.org/wiki/SHA-2#Pseudocode">hash initialization constants
for SHA256</a></em>! The rest of the
<code>SystemCryptSvcRt.efi</code> module also contains SHA256 round constants, and similar
strings and constants for other algorithms.</p>
<p>All in all this suggests that the Crypto service is a front for the
cryptographic routines in <code>SystemCryptSvcRt.efi</code> and that the password control
program calls SHA256. I wrote a small test program for the <a href="http://tianocore.sourceforge.net/wiki/Efi-shell">EFI shell</a> that
tests this:</p>
<div class="language-c highlighter-coderay"><div class="CodeRay">
<div class="code"><pre><span style="color:#088;font-weight:bold">void</span> buf2hexstr(VOID*buf,CHAR16*str,UINTN len)
{
UINTN i;
<span style="color:#088;font-weight:bold">static</span> CHAR16 hchars[<span style="color:#00D">16</span>]={<span style="color:#D20">'0'</span>,<span style="color:#D20">'1'</span>,<span style="color:#D20">'2'</span>,<span style="color:#D20">'3'</span>,<span style="color:#D20">'4'</span>,<span style="color:#D20">'5'</span>,<span style="color:#D20">'6'</span>,<span style="color:#D20">'7'</span>,<span style="color:#D20">'8'</span>,<span style="color:#D20">'9'</span>,<span style="color:#D20">'a'</span>,<span style="color:#D20">'b'</span>,<span style="color:#D20">'c'</span>,<span style="color:#D20">'d'</span>,<span style="color:#D20">'e'</span>,<span style="color:#D20">'f'</span>};
UINT8* buf_=(UINT8*)buf;
<span style="color:#080;font-weight:bold">for</span> (i=<span style="color:#00D">0</span>;i<len;i++)
{
*(str++)=hchars[*(buf_) >><span style="color:#00D">4</span>];
*(str++)=hchars[*(buf_++)&<span style="color:#02b">0xf</span>];
}
}
EFI_STATUS Initialize(...)
{
...
EFI_GUID guid={<span style="color:#02b">0x73e47354</span>,<span style="color:#02b">0xb0c5</span>,<span style="color:#02b">0x4e00</span>,{<span style="color:#02b">0xa7</span>,<span style="color:#02b">0x14</span>,<span style="color:#02b">0x9d</span>,<span style="color:#02b">0x0d</span>,<span style="color:#02b">0x5a</span>,<span style="color:#02b">0x4f</span>,<span style="color:#02b">0xdb</span>,<span style="color:#02b">0xfd</span>}};
<span style="color:#088;font-weight:bold">void</span>* intf;
<span style="color:#080;font-weight:bold">if</span> (SystemTable->BootServices->LocateProtocol(&guid,<span style="color:#069">NULL</span>,&intf)==EFI_SUCCESS)
{
<span style="color:#088;font-weight:bold">const</span> <span style="color:#0a8;font-weight:bold">char</span>* in=<span style="background-color:hsla(0,100%,50%,0.05)"><span style="color:#710">"</span><span style="color:#D20">TEST</span><span style="color:#710">"</span></span>;
<span style="color:#0a8;font-weight:bold">char</span> out[<span style="color:#00D">32</span>]={};
CHAR16 str[<span style="color:#00D">13</span>+(<span style="color:#00D">32</span>*<span style="color:#00D">2</span>)+<span style="color:#00D">2</span>]=L<span style="background-color:hsla(0,100%,50%,0.05)"><span style="color:#710">"</span><span style="color:#D20">SHA256 test: </span><span style="color:#710">"</span></span>;
((<span style="color:#088;font-weight:bold">void</span>(*)(<span style="color:#088;font-weight:bold">void</span>*,<span style="color:#088;font-weight:bold">const</span> <span style="color:#0a8;font-weight:bold">char</span>*,UINTN,<span style="color:#0a8;font-weight:bold">char</span>*))(*(<span style="color:#088;font-weight:bold">void</span>**)intf))(intf,in,<span style="color:#00D">4</span>,out);
buf2hexstr(out,str+<span style="color:#00D">13</span>,<span style="color:#00D">32</span>);
str[<span style="color:#00D">13</span>+(<span style="color:#00D">32</span>*<span style="color:#00D">2</span>)]=<span style="color:#D20">'\n'</span>;
SystemTable->ConOut->OutputString(SystemTable->ConOut, str);
SystemTable->ConOut->OutputString(SystemTable->ConOut,
L<span style="background-color:hsla(0,100%,50%,0.05)"><span style="color:#710">"</span><span style="color:#D20">Expected: 94ee059335e587e501cc4bf90613e0814f00a7b08bc7c648fd865a2af6a22cc2</span><span style="color:#b0b">\n</span><span style="color:#710">"</span></span>);
}
<span style="color:#080;font-weight:bold">else</span>
{
SystemTable->ConOut->OutputString(SystemTable->ConOut,
L<span style="background-color:hsla(0,100%,50%,0.05)"><span style="color:#710">"</span><span style="color:#D20">Unable to load CryptService protocol</span><span style="color:#b0b">\n</span><span style="color:#710">"</span></span>);
}
...
}
</pre></div>
</div>
</div>
<p>Outputs:</p>
<pre><code>SHA256 test: 94ee059335e587e501cc4bf90613e0814f00a7b08bc7c648fd865a2af6a22cc2
Expected: 94ee059335e587e501cc4bf90613e0814f00a7b08bc7c648fd865a2af6a22cc2
</code></pre>
<p>Success!</p>
<h4 id="hard-drive-communication">Hard-drive communication</h4>
<p>As mentioned, I discovered how the input password got hashed, but it still
needs to be sent to the drive. The UEFI standard defines the <em>ATA Pass Thru
Protocol</em>, which can be used to send raw ATA commands to a drive. This protocol
is very likely to be used for sending ATA security commands. This protocol is
not loaded upon initialization by any modules, but the GUID does appear in the
following modules:</p>
<ul>
<li><code>FdiskOem.efi</code></li>
<li><code>LenovoHdpManagerDxe.efi</code></li>
<li><code>LenovoMfgBenchEventDxe.efi</code></li>
<li><code>SystemAhciAtaAtapiPassThruDxe.efi</code></li>
<li><code>SystemAhciBusDxe.efi</code></li>
<li><code>SystemAhciBusSmm.efi</code></li>
<li><code>SystemIdeAtaPassThruDxe.efi</code></li>
<li><code>SystemIdeBusDxe.efi</code></li>
</ul>
<p>Wait a minute, is that second module called <em>Lenovo Hard Drive Password
Manager</em>? Why yes, it is. There’s a bunch of code in this module, but I found
an interesting function call chain for you:</p>
<ul>
<li>offset <code>0xce0</code>
<ul>
<li>offset <code>0x8a0</code>
<ul>
<li>CryptService.SHA256</li>
</ul>
</li>
<li>offset <code>0x144c</code>
<ul>
<li>offset <code>0x232c</code>
<ul>
<li>EFI_ATA_PASS_THRU_PROTOCOL.PassThru</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>The input to the SHA256 function is a parameter to the function at offset
<code>0xce0</code>, and data from an EFI runtime variable “LenovoHddSecInfoVar”. The
PassThru function is called with a ATA_OP_SECURITY_UNLOCK command block
including the hash generated just before. I assume the input to the function at
offset <code>0xce0</code> is the password hash from the password control program, but what
is the data in “LenovoHddSecInfoVar”? The <code>dmpstore</code> utility in the <a href="http://tianocore.sourceforge.net/wiki/Efi-shell">EFI shell</a>
that will dump runtime variables. Here’s mine:</p>
<pre><code>Variable BS '2D8FBE63-3A04-4EF8-A8A4-77321DB5A9AB:LenovoHddSecInfoVar' DataSize = 8
00000000: 98 7D BC B7 00 00 00 00- *........*
</code></pre>
<p>From the code I know that the value is being used as a memory address, so let’s
use the <code>mem</code> utility to dump that:</p>
<pre><code> B7BC7D98: .. .. .. .. .. .. .. ..-18 E0 0F B8 00 00 00 00 ........*
B7BC7DA8: 98 DF 0F B8 00 00 00 00-.. .. .. .. .. .. .. .. *........
</code></pre>
<p>Those are two more memory addresses, let’s see what’s there:</p>
<pre><code> B80FDF98: 61 53 73 6D 6E 75 20 67-53 53 20 44 34 38 20 30 *aSsmnu gSS D48 0*
B80FDFA8: 56 45 20 4F 30 35 47 30-20 42 20 20 20 20 20 20 *VE O05G0 B *
B80FDFB8: 20 20 20 20 20 20 20 20-.. .. .. .. .. .. .. .. *
B80FE018: 31 53 48 44 53 4E 46 41-30 42 38 35 39 34 20 45 *1SHDSNFA0B8594 E*
B80FE028: 20 20 20 20 .. .. .. ..-.. .. .. .. .. .. .. .. *
</code></pre>
<p>If you squint your eyes just right, those kind of read <code>Samsung SSD 840 EVO
500GB</code> and <code>S1DHNSAFB05849E</code>, the Model Number and Serial Number for my SSD,
respectively. Piecing all this together, you get the algorithm described in <a href="/blog/2015/03/lenovo-thinkpad-hdd-password/">my
other blog post</a>.</p>
<h4 id="conclusion">Conclusion</h4>
<p>As I mentioned, this story is the abridged version of how I found the password
hashing algorithm. In reality, I looked at many other modules, including many
hours spent looking at useless things. In the end though, I prevailed and found
what I was looking for, developing <a href="https://github.com/jethrogb/uefireverse">a bunch of
tools</a> in the process:</p>
<dl>
<dt>efiperun:</dt>
<dd>Load and run EFI PE image files in a regular OS environment.</dd>
<dt>guiddb:</dt>
<dd>Scan files for GUIDs and output them in C-source file format.</dd>
<dt>memdmp:</dt>
<dd>Dump UEFI memory using <a href="http://tianocore.sourceforge.net/wiki/Efi-shell">EFI shell</a>.</dd>
<dt>tree:</dt>
<dd>A Ruby abstraction for a firmware tree on your filesystem previously
extracted by <a href="https://github.com/LongSoft/UEFITool">UEFIExtract</a>.</dd>
</dl>
<p>I hope these tools are of use to anyone. Patches welcome. ☺️</p>
<p>In order to <a href="/blog/2015/03/lenovo-thinkpad-hdd-password/">figure out how my BIOS drive password
worked,</a> I had to
reverse-engineer the firmware that comes with my laptop. You can find the
binary blobs on the update CD that Lenovo provides, and it turns out these
blobs are actually UEFI images. UEFI firmware is made up of many different
loadable modules (drivers, shared libraries, etc.), which are stored in the
Portable Executable (PE) image format. These modules can be extracted from the
image using Nikolaj Schlej’s excellent UEFIExtract (from
<a href="https://github.com/LongSoft/UEFITool">UEFITool</a>). Once you have all the PE
modules, the real reversing can begin.</p>