 So, in this talk, we will hear about what UEFI is, how it can be used, and how it can be executed in user space. And our speaker is Jeff Beekman, and the stage is yours. Thank you for the introduction. All right. 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 bias 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 description and use it using the built-in hard drive password option of my UEFI firmware. This is the password I chose, 64 characters, correct horse, battery, staple, galaxy, that piece position require house, it's very secure, obviously. So this all seems to be working fine, and trusting that my hard drive was encrypted properly. But once you start to think about how this actually works, there's, you know, a small discrepancy, really, because the way this password is input into 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, you know, 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 shot to 56 that has 32 bytes output, but these things all didn't work. So then I decided to, you know, really dive into the firmware and to figure out how it works. So this talk could also be called how to turn 64 characters into 32 bytes. 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 this 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 how it got from the store. Maybe you think you can emulate the firmware using QMU or something like that, but hardware that the firmware is designed to support is unlikely to be correctly emulated by QMU. So that is also probably not a viable way to debug this. Also because UEFI is basically one big process, one address space, there is no operating system. So there are no system calls. Also, there is no dynamic linker, so there are no dynamic symbols, there is no symbol table that you can use as a starting point in your reverse engineering. If you were reverse engineering a standard password utility in user space or something, you might start at the read system call that would be displayed to the user to enter their password. But in UEFI, that is not an option. Even though there is 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 the different modules. There is this tool called UEFI tool written by Nikolai Schley, and this really should be in your bag of tricks if you are interested in doing anything with UEFI firmware. So here we will just go and use UEFI extracts, which is a command line utility included with UEFI tool 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 are going to extract that. After extracting it, you get this nice directory structure with one subdirectory per module, and as you can see, there is quite a few of them. Here, there is system management, mode control module, some 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 occurs a lot is the PE32 plus image. This is the portable execution image. This is the same 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 in this pointer to the system table. The system table contains more pointers to other structures. For example, for the terminals, console in, console out, standard error, just for standard text input and output, 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 specified by a particular GUID, and the interface is then specified by just some pointer. Then later, another module can call a locate protocol function with the same GUID, and they 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 done after that, installing one or 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 alluded to before. These modules are written for the hardware that 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. And this is what I've written with the EFI PE Run tool. You can think of EFI PE Run as wine for EFI, just like wine allows you to run Windows applications on Linux, EFI PE Run allows you to run EFI modules on Linux. So EFI PE Run has a bunch of features, includes many of the standard EFI APIs, and it's very easy to add implementations for missing APIs. Also at runtime, missing APIs 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 this in standard debugger environments like GDB. Also as an interesting aside, I think this is the only project I could find online that uses the cross-standard arc header, which is used for calling variable argument functions across calling conventions. So let's do a little demo of EFI PE Run. In this demo, I will just run EFI PE Run 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 UEFI extract utility, and then it executes EFI PE Run on each different module. We. It's a search and error that I've never seen before. That's always fun. As you can see, 281 processes are launched. Most modules return from their main function normally, but some of them get stuck in an infinite loop, so EFI PE Run will automatically terminate after 10 seconds in this case. Let's look at the output of all these different EFI PE Run modules. You can see a bunch of them sac fault, which can be 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 stuff 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 EFI PE Run, 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 two, and because we aren't able to handle that 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 out how to turn those 64 characters into 32 bytes. You might remember this picture from slide two. You can see that there is a little graphic displayed in the password prompt. So this graphic needs to be stored somewhere in the BIOS, and there needs to be code 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. It turns out there's only four of the 281 that have a file name that seems to correspond to something with graphics or images. Now these, 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 found something that has to do with the password prompt. This prompt service 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 three other modules, namely the sound service module, presumably to play beeps if the user does something wrong while entering a password. The translate service module, which is used to translate characters or which I reverse engineered and figure out that it's used to translate keyboard or ASCII characters back into keyboard scan codes. Keyboard scan codes are codes that are assigned to each different key on your keyboard is the way that hardware, your hardware keyboard communicates with your computer. And then there is the Lenovo script service module, which turns out to be a standard SHA-256 hash function. All right, so let's do another demo in which we are going to call one of the functions in this Lenovo password CP module. So EFI peron allows you to write code to interact with the EFI modules that are loaded at one time. So 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 the after loading the EFI module to call something function. The install function installs a stub Lenovo script service protocol. This is necessary because the standard Lenovo script service calls into system management mode to do the hashing, which is currently not possible from Linux user space. The second function call Lenovo password CP8CC 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 we'll use this Unicode string MyPassword and for the output we'll just pass this array buffer. Then we'll just print the output of this buffer after calling the function. So as you can see here, the Lenovo translate service module installs this protocol E3AB, etc. And then later the Lenovo password CP module requests the same protocol E3AB, etc. Also you will see that the Lenovo password CP module requests the protocol 7, 3, E4, etc., which is the Lenovo script 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 code and then it pads it to 64 characters and then it takes the shot to 56 hash and then it displays the first half of that. All right, here we go. So we have reverse engineered to the first half of the password algorithm. It 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 solid drive over the ADAP protocol. Now this is actually quite a good idea because this means that if you sniff the password over 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 is a maximum of 96 bits of entropy in this password hash. So 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. Then, again, this part of the hash is concatenated with a serial number and a model number, except the bytes are swapped. I really can't figure out why the bytes are swapped here, but it probably has to do something with the fact that the ADAP protocol uses 16-bit words while these model number and serial number are 8-bit character streams, so maybe they did some and in this mess up or something like that. All right, so in this talk, I've shown you how U2 can reverse engineer UEFI with some tools that you can use in this included EFI PE run tool, which allows you to run EFI modules in Linux user space. The code of this is, of course, available on their GPL license on GitHub, and if you want to read about the second part of the reverse engineering of the algorithm, you can find more information on my blog. Thank you very much. So, thank you, Jethro, and we have more than 10 minutes of time for Q&A. Is there a question from the internet? Signal Angel. Not right now. And do we have questions in the audience? Please line up at the microphones. Then we start with microphone two, please. 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 assume also the real mode and the transition into long mode is not supported. Is that on your roadmap or something that you're interested in continuing development on? Yeah, I'm interested in it, but I'm not quite sure how to do it, especially calling it a system management mode. Protected mode should probably be also possible. Currently, I'm also doing a 64-bit mode. But yeah, if anyone wants to help me implement it, obviously I welcome your support. OK, and from the internet, please. Yes, the internet wants to know, what is the advantage of UEFI compared to Coreboot? I'm sorry, I didn't. The internet wants to know, what is the advantage of UEFI compared with Coreboot, for example? The advantage of UEFI compared to what? Coreboot is UEFI. Coreboot is the Intel's open source implementation of UEFI. Actually, well, actually it's not. Oh, it is not. No, that's TianoCore. You're right. Well, the 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. OK, Markathon 3, please. Hi, thank you for your talk. Have you looked at any other firmwares besides your Lenovo? I have not personally, but I have no other people who have. OK, four, please. Thank you for your talk. How did you came out? How to get the password, the hash, with the combination of the serial number and the module number? 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 ADA support protocol, if I remember correctly. 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 ADA drive. Thank you. And there is another question from the chat. Yes, the chat wants to know if you found any other like unexpected bugs and if it is possible to check you if the code, for example, will, when running it with Belgrind or something. I've not really run into any other unexpected bugs, but I must say I also really wasn't looking for them. OK, two, please. 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 Mac OS computer, old Mac Pros when they still were having cards. And I was wondering, I wanted to figure out these cards running, 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 my code would then invoke the initialization boot ROM for the graphics card and provide the functions. Would your tool set, would that help me in tracing that while my Mac is running, because I can't do it while it's booting? 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 will be able to run that while the operating system is also running. So I'm not sure, I'm happy to talk more offline to discuss your specific situation. OK, and microphone three, please. Hi, have you looked into using serial ICE for emulation of the UEFI, which is essentially QEMU, but it has does forward all the hardware accesses to real hardware, so you can just trace it and run arbitrary BIOS or UEFI code in it? I have not looked into that, but that sounds very interesting avenue to explore. Thank you. OK, I see no more questions. Thank you to our speaker.