Herald: So, in this talk we will hear about
what UEFI is, how it can be used,
and how it can be executed in userspace,
and our speaker is Jethro Beekman,
and... the stage is yours.
Jethro Beekman: Thank you for the introduction.
*applause*
Alright, thank you for the introduction.
So, I'm a PhD student at UC Berkeley,
and in my copious amounts of free time,
I like to reverse-engineer things.
In particular, this time,
I reverse-engineered UEFI,
which is the modern BIOS replacement.
And, in this talk, I will discuss some tools
that you can use - that you too can use
to reverse-engineer UEFI,
including some tools that I wrote,
and tools that other people wrote.
So, this whole project started
when I bought a new SSD for my laptop
and, as you might know, modern SSDs have
built-in encryption capabilities,
whether this is secure or not is a question,
is a good question, but really,
reverse-engineering SSD firmware
is a talk for another day.
So, I decided to use this encryption,
and use it using the built-in
hardware password option
of my UEFI firmware.
This is the password I chose, 64 characters,
"correct horse battery staple galaxy piece position require house".
It's very secure, obviously.
So this all seemed to be working fine,
and I was trusting that my hard drive
was encrypted properly,
but once you start to think about
how it actually works,
there's a small discrepancy, really,
because the way this password's input
to the hard drive is using
the ATA security feature set,
the security-unlock command.
And if you look at the unlock command,
you clearly see that the password
is supposed to be 32 bytes.
How are these 64 characters turned into 32 bytes?
That is my question,
because if my laptop dies
but my SSD is still functional,
I want to be able to take my SSD
and put it into another computer
to get access to my data.
I tried all the standard things,
like truncating it, or using a standard
hash function like SHA-256
that has 32-byte output,
but these things all didn't work.
So then I decided to, you know,
really dive into the firmware
to figure out how it works.
So this talk will also be called
"How to turn 64 characters into 32 bytes".
*applause*
So what are some challenges
when reverse-engineering UEFI?
So first of all, this is the first thing
that runs when your computer is booted up.
So that means you will not be able
to use a standard debugger.
Surely people who develop firmware for a living
have some kind of hardware debugger
but that's unlikely to work on a commodity system
such as this laptop, which is all I got from the store.
Maybe you think you can emulate the firmware
using qemu or something like that,
but the hardware that the firmware
is designed to support
is unlikely to be correctly emulated by qemu.
So that is also probably not a viable way
to debug this.
Also, because UEFI is basically one big process,
using one address space,
there's no operating system
so there's no system calls.
Also there's no dynamic linker,
so there's no dynamic symbols,
there's no symbol table that you can use
as a starting point in your reverse-engineering.
You know, if you were reverse-engineering
a standard password utility in userspace
or something, you might start at
the read system call for,
that would be displayed to the user
to enter the password.
But in UEFI, no, that is not an option.
And even though there's no dynamic linker,
the whole firmware consists
of 281 different modules, in my case,
and it could be similar numbers on your UEFI laptop.
And these modules all need to interact in some way.
So, let's take a look at these different modules.
There's a tool called UEFITool,
written by Nikolaj Schlej, and
this really should be in your bag of tricks
if you're interested in doing anything
with UEFI firmware.
So, here I'll just go and use UEFIExtract,
which is a command line utility
included with UEFITool
that allows you to extract a firmware blob,
so in this case I took the firmware blob
from the Lenovo firmware update CD
and we're going to extract that.
After extracting it, we get this nice directory structure
with one subdirectory per module
and as you can see, there's quite a few of them.
Here's, there's system management mode control module,
timer module, things like that.
And as you can see,
each module has a bunch of subdirectories
for the different sections
that are included in that module.
And one that appears a lot is the PE32+ image.
This is a portable execution image,
this is a format that Windows uses for executables.
So, you might think you might be able to run
these modules, and that is true.
But first, let's take a look at
what happens when you run a module like that.
Each module has an entry point, main,
and the main function gets passed
a pointer to the system table.
The system table is just...
contains more pointers to other structures.
For example, for the terminals,
cons in, cons out, standard error,
the standard text inputs and outputs,
and also the boot services structure.
The boot services structure contains
a bunch of function pointers,
including these install protocol interface
and locate protocol functions.
The install protocol interface allows
a module to install a protocol interface
using, specified by a particular GUID
and the interface is specified by just some pointer.
Then, later, another module can call
a locate protocol function with the same GUID
and it will receive a pointer
to this interface that was previously installed
by the other modules.
So, most modules in their main function
start by loading a bunch of protocols
and then, after that, installing one more
of their own protocols.
This is all done at runtime.
So there's really no static way of
viewing the dependencies
between the different modules.
Luckily, we might be able to
execute these modules,
as I was alluding to before.
These modules, they're written
for the hardware you're currently using,
with your current operating system.
So they have a compatible instruction set.
So in order to execute them,
you just need a compatible
application binary interface, or ABI.
This is what I've written,
with the efiperun tool.
You can think of efiperun as WINE for UEFI.
Just like WINE allows you to run
Windows applications on Linux,
efiperun allows you to run EFI modules
on Linux.
*applause*
So, efiperun has a bunch of features,
it includes many of the standard EFI APIs
and it's very easy to add implementations
for missing APIs.
Also, at runtime, a missing API
will be generated automatically
with some stub functions.
It supports memory map annotations,
so that you can see which parts of memory
have been allocated by which module
and you can all run these in your standard
debugger environments, like gdb.
Also, as an interesting aside,
I think this the only project
I could find online
that uses the cross-stdarg header
which is used for calling
variable-argument functions
across calling conventions.
So, let's do a little demo of efiperun.
In this demo, I will just
run efiperun on each different module
to see which other modules it interacts with.
So, I just wrote this little Ruby script
which traverses the directory tree
that we just saw from the UEFIExtract utility
and then it executes efiperun
for each different module.
Whee!
Assertion error that I've never seen before,
that's always fun.
As you can see, 281 processes are launched,
most modules return
from the main function, normally,
but some of them get stuck
in an infinite loop,
so efiperun will automatically terminate
after 10 seconds in this case.
Let's look at the output of
all these different efiperun modules.
You can see a bunch of them segfault,
which, you know, can be, is understandable
because they might be expecting
some memory setup from the early boot
that is not existent anymore,
but there are a bunch of modules that do work
such as system boot manager.
You can see that it prints out a bunch of stuff,
version information, copyright information,
then it requests a protocol,
this protocol has a GUID that is specified
by the EFI specification,
so we can interpret that GUID
and then it calls some stub functions
that we have not implemented
and then afterwards,
it installs its own protocol,
which is also a protocol specified
by the EFI specification.
Another interesting module
is the system splash module,
which we see over here.
As you can see, it actually requests
a bunch of protocols that are not implemented
by efiperun and you will see
it will automatically generate
a dummy interface for that purpose.
And then you will see that it calls
a function in this dummy interface
that was created here,
namely function number 2,
and because we are unable to handle this function
we just abort.
Okay, so now that I've shown you
that we can run these different modules,
we really need to get started with
the reverse-engineering of my BIOS
to figure how to turn those 64 characters
into 32 bytes.
You might remember this picture from slide 2.
You can see that there's a little graphic
displayed in the password prompt.
So this graphic needs to be stored
somewhere in the BIOS,
and it needs to be coded to display
this graphic to the user at some point.
So, let's take a look at the different modules
that might have something to do with images
and graphics and things like that.
Turns out there's only 4 of the 281
that have a file names that seems to correspond
to something with graphics or images.
And if this, the first module is called
by another module,
which is Lenovo prompt service.
And Lenovo prompt service contains
in one of its data sections,
this image over here.
So now we know that we've found something
that has to do with the password prompt.
This prompt module is called by only one
other module, which is
the Lenovo password CP module,
which probably means something like
password control panel or something like that.
The password CP module also calls
into 3 other modules, namely
the sound service module, presumably to
play beeps if the user does something wrong
while entering the password,
the translate service module, which is used
to translate characters,
which I've reverse-engineered,
I've figured out that it's used to translate
ascii characters back into keyboard scan codes.
Keyboard scan codes are codes that are assigned
to each different key on your keyboard.
It's the way the hardware keyboard
communicates with your computer.
And then there's the Lenovo crypt service module,
which turns out to be
standard SHA-256 hash function.
Right, so let's see and have a demo,
in which we are going to call
one of the functions in
this Lenovo password CP module.
So, efiperun allows you to write code
to interact with the EFI modules
that are loaded at runtime.
Here I've written this Lenovo-specific
debug module and you can specify
two functions that will be called.
The first function in it will be called
before all the EFI modules are loaded,
the second function will be called after
all the EFI functions are loaded.
So in this case, we'll first call
the install something function, and then
call, after loading the EFI modules,
the call something function.
The install function installs a stub
Lenovo crypt service protocol.
This is necessary because the standard
Lenovo crypt service calls into system
management mode to do the hashing,
which is currently not possible from
Linux userspace.
The second function, call_LenovoPasswordCp_8cc,
will determine the address of the function
in the Lenovo password CP module
at offset 8cc and then call it.
It will take two parameters, in and out.
In will use this unicode string, my password,
and for the output,
we'll just pass this array buffer.
Then we'll just print the output
of the buffer after calling the function.
So, as you can see here,
the Lenovo translate service module
installs this protocol E3AB-etc.
and then later Lenovo password CP module
requests the same protocol E3AB-etc.
Also, you will see that
the Lenovo password CP module
requests the protocol C73E4-etc.
which is the Lenovo crypt service protocol
that we installed earlier.
Then it does a bunch of memory operations,
and at the end, we get the output.
So I reverse-engineered this function
at offset 8cc, and it turns out that
it is, in fact, the following function:
it takes the input password and
converts it into scan codes,
and then it pads it to 64 characters,
and then it takes a SHA-256 hash
and then it displays the first half of that.
Alright, here we go, so...
*applause*
We have reverse-engineered to
the first half of the password algorithm.
Took me about three weeks to decode
the entire algorithm,
and here it is.
So this is the hash that we just saw
of the password, and then this hash is
concatenated with the serial number and
model number of the hard drive.
That all is then hashed again,
and that is then passed to the SATA drive
over the ATA protocol.
Now this is actually quite a good idea
because it means that if you sniff
the password on the SATA bus
you will only be able to then later
unlock the same drive.
Because other drives, even though
they might be using the same password,
will have a different serial number
and model number.
So this hash will not work for them.
Unfortunately, the algorithm is
a little more complex than this.
The password hash, as I said, actually
uses the scan codes of the password, which
means that there is no distinguishing in
case of the letters.
Also, after hashing it, it truncates it
to only 12 bytes. Which means that
there's a maximum of 96 bits of entropy
in this password hash.
This is quite unfortunate.
But most human passwords have less than
96 bits of entropy to start with,
so it's probably not that big of a deal.
Okay, then again this part of the hash is
concatenated with the serial number
and the model number,
except the bytes are swapped.
And I really can't figure out why
the bytes are swapped here.
But it probably has to do something with
the fact that the ATA protocol uses
16-bit words while this model number and
serial number are 8-bit character strings.
So maybe they did some endianness mess-up
or something like that.
Alright, so in this talk, I've shown you
how you too can reverse-engineer UEFI
with some tools that you can use
in this including the efiperun tool
which allows you to run EFI modules
in linux userspace.
The code of this is of course available
under GPL license on github,
and if you want to read about
the second part of reverse-engineering
the algorithm, you can find more
information on my blog.
Thank you very much.
*applause*
H: So, thank you, Jethro, and we have
more than ten minutes' time for Q&A.
Is there a question from the Internet,
signal angel?
Signal: Not right now.
H: And do we have questions
from the audience?
Please line up at the microphones.
Then we'll start with microphone 2, please.
Q: Thanks a lot for making your tools available,
as someone else who does a lot of
UEFI reversing, I've been through similar
rabbit holes of trying to track this down.
You mentioned that SMM is not supported
and I assumed also the real mode
in the transition into long mode
is not supported.
Is that on your roadmap or something,
that you're interested in continuing
development on?
A: Yeah, I'm interested in it, but
I'm not quite sure how to do it,
especially calling into system management mode,
protected mode should probably,
you know, also be possible,
but currently I'm also doing 64-bit mode
but, yeah, if anyone wants to help me
implement it, obviously I welcome your support.
H: Okay, and from the Internet, please.
Q: Yes. The Internet wants to know
what is the advantage of UEFI compared to coreboot?
A: I'm sorry, I didn't...
Q: The Internet wants to know
what is the advantage of UEFI compared
with coreboot, for example?
A: The advantage of UEFI compared to what?
Q: To coreboot, coreboot, this open source
BIOS replacement.
A: Oh okay. coreboot is UEFI,
coreboot is Intel's open source
implementation of UEFI.
Q: Actually, well, actually it's not.
A: Oh, it's not, no, that's tianocore,
you're right.
The only advantage is,
I don't know what the advantage is,
this is just what's supported on my laptop.
So that's what I was interested in.
*laughter, applause*
H: Okay. Microphone 3, please.
Q: Hi. Thank you for your talk.
Have you looked at any other firmware
besides your Lenovo?
A: I've not personally, but
I know other people have.
H: Okay. 4, please.
Q: Thank you for the talk. How did you
come out how to get the password, the hash
with the combination of the serial number
and the module number?
A: So, the full details of this
are in my blog post, but in short,
there is a EFI protocol for talking
to the hard drive, which is
the ATA support protocol,
if I remember correctly,
there's a special module,
there are only a few modules,
that invoke this protocol,
so I just disassembled those, and looked
at what they did, and what commands
they sent to the ATA drive.
Q: Thank you.
H: And there is another question from the chat.
Q: Yes. The chat wants to know if you found
any other unexpected bugs and
if it is possible to check UEFI code,
for example, when running it with valgrind
or something.
A: I've not really run into any other
unexpected bugs, but I must say I also
really wasn't looking for them.
H: Okay. 2, please.
Q: I have little understanding of EFI, but
I had the idea to look into trying to get
boot ROMs from PC graphic cards
to be running on a MacOS computer,
old Mac Pros when they still were having cards,
and I was wondering, I wanted to figure out...
these cards run, apparently, the old BIOS,
real mode, and I wanted to figure out
if I can write a stub that gets loaded
by the EFI system and then that...
my code would then invoke the initialization boot ROM
for the graphics card and provide the functions.
Would your toolset, would that help me in
tracing that while my Mac is running,
because I can't do it while it's booting?
A: I don't... it sounds like a, quite a complex setup,
I think it would be possible to write
an EFI module to do what you're saying,
but I'm not sure if you'll be able
to run that while your operating system is also running.
So... I'm not sure, I'll have to talk more
offline to discuss your specific situation.
H: Okay, and microphone 3, please.
Q: Hi. Have you looked into using SerialICE for emulation of the UEFI
which is essentially qemu but it has, does forward all of the hardware accesses
to real hardware, so you can just trace it
and run arbitrary BIOS or UEFI code in it?
A: I've not looked into that, but that
sounds a very interesting avenue to explore.
Q: Thank you.
H: Okay. I see no more questions.
Thank you to our speaker.
*applause*