# Everything and a kitchensink - what to find in a modern x86_64 image
---
# Firmware?
- Mostly RE knowledge
- HexDump knowledge
- Started as `ifdtool` rewrite
- Dense. 69 Slides in 60 Minutes
---
# When you boot
```
007fe880 ff ff 00 00 00 93 cf 00 ff ff 00 00 00 9b af 00 |................|
007fe890 00 00 00 00 00 00 00 00 47 00 50 e8 ff ff 8b ff |........G.P.....|
007fe8a0 36 e0 ff ff 10 00 8d a4 24 00 00 00 00 8d 49 00 |6.......$.....I.|
007fe8b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
007fff80 50 45 49 45 4b a8 f5 ff 10 00 00 00 00 00 00 00 |PEIEK...........|
007fff90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
007ffff0 e9 0d e0 ff ff 00 00 10 00 00 78 56 00 00 f0 ff |..........xV....|
```
---
- CPU jumps to -16
```e9 0d e0 [ff ff]```
- ShortJump backwards
- 16 bit mode
- Load UEFI from here
- How is UEFI organized?
---
# UEFI
---
# Tooling
- UEFITool
![](https://www.thomas-krenn.com/de/wikiDE/images/c/c0/UEFITool-03.png)
---
- Google Fiano
- [https://github.com/linuxboot/fiano]
---
# Whats inside UEFI?
- UEFI Image / Region contain a filesystem
- Volume
- Region
- File
- Header contains GUID with the respected Information
---
# UEFI Volume header
(copied from pheonix wiki)
```golang
typedef struct {
UINT8 ZeroVector[16];
EFI_GUID FileSystemGuid;
UINT64 FvLength;
UINT32 Signature;
EFI_FVB_ATTRIBUTES Attributes;
UINT16 HeaderLength;
UINT16 Checksum;
UINT8 Reserved[3];
UINT8 Revision;
EFI_FV_BLOCK_MAP_ENTRY FvBlockMap[1];
} EFI_FIRMWARE_VOLUME_HEADER;
```
---
- Signature is "_FVH"
- FvBlockMap "An array of run-length encoded FvBlockMapEntry structures. The array is terminated with an entry of {0,0}."
- FvBlockMapEntry
- NumBlocks
- BlockLength
---
# UEFI Stages
- Three Stages
- SEC
- CPU init
- Ram init
- Device init
- PEI
- Pre-Efi /Platform Initialization
- DXE prepare
- DXE
---
# DXE
- Driver stage
- Mount drives
- UEFI runtimes / interfaces
- Configuratoin interface
- Load OS
---
# Update Capsules
- Special UEFI Volumes
```
$EFI {d954937a68044a4481ce0bf617d890df}
$EFIv2 {78e58c8c3d8a1c4f9935896185c32dd3}
$Apple {adeead04ff61314db6ba64f8bf901f5a}
$Applev2 {8c1b00bd716a7b48a14f0c2a2dcf7a5d}
$Intelv1 {ffff3fad8bd2c4449f139ea98a97f9f0}
$Intelv2 {70cda1d6334b9449a6ea375f2ccc5437}
$Sonyv1 {5641494fd6ae644da537b8a5557bceec}
```
---
## EFIFilesystem
```golang
type EfiCapsule struct {
HeaderSize uint32
Flags uint32
CapsuleImageSize uint32
SequenceNumber uint32
InstanceId [16]uint8
OffsetToSplitInformation uint32
OffsetToCapsuleBody uint32
OffsetToOemDefinedHeader uint32
OffsetToAuthorInformation uint32
OffsetToRevisionInformation uint32
OffsetToShortDescription uint32
OffsetToLongDescription uint32
OffsetToApplicableDevices uint32
}
```
---
## EFI2Filesystem
```golang
type EFI2Capsule struct {
HeaderSize uint32
Flags uint32
CapsuleImageSize uint32
FwImageOffset uint16
OemHdrOffset uint16
}
```
---
```
00000800 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000810 78 e5 8c 8c 3d 8a 1c 4f 99 35 89 61 85 c3 2d d3 |x...=..O.5.a..-.|
00000820 00 00 04 00 00 00 00 00 5f 46 56 48 ff fe 03 00 |........_FVH....|
00000830 48 00 8c e6 00 00 00 02 40 00 00 00 00 10 00 00 |H.......@.......|
00000840 00 00 00 00 00 00 00 00 a3 b9 f5 ce 6d 47 7f 49 |............mG.I|
00000850 9f dc e9 81 43 e0 42 2c 34 aa 01 00 b8 ff 03 f8 |....C.B,4.......|
```
---
# Intel specifics
Intel Image is partitioned into
- IFD
- BIOS region
- ME region
- Gigabit Ethernet region
- Platform Data
---
## IFD
- THE Intel Header
- Located at the begin of the flashimage (0x00 or 0x10)
- Contains information about the segments of the flashimage
- `ifdtool`
- Addressbits need to be shifted 4 bits to the left
---
- starts with signature `5AA5F00F`
- IFD-Header
---
```
Flvalsig uint32
Flmap0 uint32
Flmap1 uint32
Flmap2 uint32
Flmap3 uint32 // added with coffee lake
OEMReserved [3800]uint8
Flumap1 uint32 // added with ICH9
```
---
# Flmap 0 (Chip and Region config)
- ComponentBaseAddress : 8 Bits
- Different configurations for several SPI flashchips
```golang
type ComponentSection struct {
FLCOMP ComponentSectionFLCOMP : 32 Bits
FLILL ComponentSectionFLILL : 32 Bits
FLPB ComponentSectionFLPB : 16 Bits
}
```
---
```golang
type ComponentSectionFLCOMP struct {
DualOutputFastReadSupport bool
ReadIDStatusClockFrequency uint32
WriteEraseClockFrequency uint32
FastReadClockFrequency uint32
FastReadSupport bool
ReadClockFrequency uint32
Component1Density uint32
Component2Density uint32
}
```
---
```golang
type ComponentSectionFLILL struct {
InvalidInstruction0 string
InvalidInstruction1 string
InvalidInstruction2 string
InvalidInstruction3 string
}
```
---
- NumberOfFlashChips : 2 Bits
- Reserved: 6 Bits
- RegionBaseAddress : 8 Bits
- Contains uint32 Array
- Depending on the Version with 0x7fff or 0xfff masked start and end addresses
- Regions always in the same order
- NumberOfRegions : 3 Bits
- Only way to determine the version before coffeelake
- Reserved: 5 Bits
---
# Flmap 1 (SPI and PCH Config)
- MasterBaseAddress : 8 Bits
- Region Access rights
- NumberOfMasters : 2 Bits
---
```golang
type MasterSectionEntry struct {
FlashDescriptorReadAccess bool
FlashDescriptorWriteAccess bool
HostCPUBIOSRegionReadAccess bool
HostCPUBIOSRegionWriteAccess bool
IntelMERegionReadAccess bool
IntelMERegionWriteAccess bool
GbERegionReadAccess bool
GbERegionWriteAccess bool
PlatformDataRegionReadAccess bool
PlatformDataRegionWriteAccess bool
ECRegionReadAccess bool
ECRegionWriteAccess bool
}
```
---
- Reserved : 6 Bits
- PchStrapsBase : 8 Bits
- NumberOfPchStraps : 8 Bits
---
# Flmap 2 (CPU Straps)
- ProcStrapsBase : 8 Bits
- NumberOfProcStraps : 8 Bits
- Reserved: 16 Bits
---
# Flmap 3
- DescriptorVersion : 32 Bits
---
# Flumap 1 (ME Config)
- MEVSCCBaseAdress : 8 Bits
- MEVSCCLength : 8 Bits
- Reserved 16 Bits
---
# ME
- Organized into partitions
- Starts with an header
---
```golang
type MEHeader struct {
Reserved [16]uint8
Signature uint32 // "$FPT"
Entries uint32
Version uint8
Type uint8
Len uint8
Checksum uint8
LIFE uint16
Limit uint16
UMASize uint32
Flags uint32
Reserved uint64
}
```
---
```golang
type MEPartitionHeader struct {
Name uint32
Owner uint32
Offset uint32
Length uint32
StartTokens uint32
MaxTokens uint32
ScratchSectors uint32
Flags uint32
}
```
---
- Partitions contain Containers
- Container contain Modules
- Signed `$MCP` container contain config
- `$MAN` and `$MN2` modules contain compressed code
---
# FSP
- Intel specific binary blobs to start the platform
- eg. Raminit
- Distributed as UEFI Volumes
- GUID `BE40279184223447B97184B027353F0C` in the first file of first volume
---
# AMD specifics
---
## AMD Firmware Entry Table
- PSP looks at offset `0x20000` in the flash.
- Flashmapping for 16MB is 0xFF000000
- Mapping depends on Flashsize
- Finds the "Firmware Entry Table"
---
```golang
type AMDFirmwareEntryTable struct {
Signature uint32
ImcRomBase uint32
GecRomBase uint32
XHCRomBase uint32
PSPDirBase uint32
NewPSPDirBase uint32
BDHDirBase uint32
Unknown1 uint32
}
```
---
## Signature
0x55AA55AA
---
## ImcRomBase
- _AMD_IMC_C magic
- AMDs embedded controller implementation
- Found in most CPUs
- Almost never used
---
## GecRomBase
- Gigabit ethernet
- very hardware specific
- ignored
---
## XHCI
- Two versions
- Old and new
- Running on V850E1
---
### OLD XHCI
- quoting coreboot wiki:
```
0x0000 2 Signature 0x55aa
0x0002 2 Offset to the BCD (type0?)
0x0004 2 BCD size
0x0006 2 Offset to Main FW (type1?)
0x0008 2 Main FW size
0x000a 2 Offset to the ACD (type2?)
0x000c 2 Offset to the ACD
```
- Main FW
```
Offset Size in bytes What
0x0000 2 Firmware version - 0x3032 means 3.0.3.2
0x0002 - Firmware data, most likely V850E1 controller
```
---
### NEW XHCI
```
00000000 80 07 f8 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000010 e0 07 40 01 00 00 00 00 00 00 00 00 00 00 00 00 |..@.............|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000040 80 07 3a 03 e0 07 40 01 00 00 00 00 00 00 00 00 |..:...@.........|
00000050 e0 07 40 01 00 00 00 00 00 00 00 00 00 00 00 00 |..@.............|
00000060 80 07 34 01 e0 07 40 01 00 00 00 00 00 00 00 00 |..4...@.........|
00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000080 80 07 4a 01 e0 07 40 01 00 00 00 00 00 00 00 00 |..J...@.........|
00000090 80 07 70 01 e0 07 40 01 00 00 00 00 00 00 00 00 |..p...@.........|
000000a0 80 07 96 01 e0 07 40 01 00 00 00 00 00 00 00 00 |......@.........|
000000b0 80 07 bc 01 e0 07 40 01 00 00 00 00 00 00 00 00 |......@.........|
000000c0 80 07 e2 01 e0 07 40 01 00 00 00 00 00 00 00 00 |......@.........|
000000d0 80 07 08 02 e0 07 40 01 00 00 00 00 00 00 00 00 |......@.........|
000000e0 80 07 2e 02 e0 07 40 01 00 00 00 00 00 00 00 00 |......@.........|
000000f0 80 07 54 02 e0 07 40 01 00 08 00 10 00 18 00 20 |..T...@........ |
```
---
### NEW XHCI
```
.data:00000000 8007f800 jr 0x000000f8
```
- We found ourself the vector table
- reset vector first
- Ghidra next?
---
## AGESA
- All binary blobs are shipped as "AGESA Bundle"
- Only with NDA
- Format is documented
- AMD Generic Encapsulated Software Architecture (AGESA™)Interface Specification for Arch2008
---
## AGESA
### What we have:
- Binary blobs needed to boot
- AGESA header
```yara
rule AGESA {
strings:
$AMDGESA = {41 4d 44 21 47 45 53 41 ?? ?? ?? ??}
$AGESA = /AGESA![0-9a-zA-Z]{0,10}\x00{0,1}[0-9a-zA-Z .\-]*/
$AAGESA = /!!AGESA[0-9a-zA-Z .\-]*/
$AMD_PI = /\$AMD[A-Z][0-9a-zA-Z]*[PIVpiv][0-9a-zA-Z.\-]*/
condition:
any of them
}
```
---
```
!!AGESABristolPI V1.0.0.1X
!!AGESACarrizoFM2r2PI V1.3.0.1X
!!AGESACarrizoLitePI V1.0.0.4
!!AGESACarrizoPI V1.1.0.0
!!AGESA DanNiPI V0.0.9.2
!!AGESA DanNiPI V1.0.0.0
!!AGESAGodavariPI V0.0.9.1
!!AGESAKabiniPI V1.0.0.5
!!AGESAKabiniPI V1.0.0.6
!!AGESAKaveriPI V1.1.0.5
!!AGESA LlanoPI V1.0.0.6
!!AGESA MarG34PIV1.1.0.4
!!AGESAMullinsPI V1.0.0.2
!!AGESA OntaroPIV1.2.0.0
!!AGESA OrochiPIV0.0.7.3X
!!AGESARichlandPI V1.1.0.1
!!AGESAStoneyPI V1.0.0.2
!!AGESAStoneyPI V1.3.0.3
!!AGESA TrinyPI V1.1.0.2
!!AGESA V3.1.1.0
!!AGESA V4.6.1.0
!!AGESA V6.0.9.0
AGESA!V9\x00NaplesPI-SP3 1.0.0.B
AGESA!V9\x00PinnaclePI-AM4 1.0.0.0a
AGESA!V9\x00RavenPI-FP5-AM4 1.1.0.2
AGESA!V9\x00SummitPI-SP3r2-1.1.0.1
AGESA!V9\x00ThreadRipperPI-SP3r2-0.0.6.0
AGESA!V9\x00UnkownPI 0.0.0.0
!!AGESA X3.5.2.0
```
---
## PSP
- AMDs security processor
- Boots before the main system
- Most of the time
- Is required to boot
- Most of the time
- Only runs signed code
- Most of the time
---
- PSPTool released thursday!
- https://github.com/cwerling/psptool
---
### Boot Process
---
![](https://4.bp.blogspot.com/-G2jIAokGNB8/WusHOkog9-I/AAAAAAAAACc/XXw3DKgL8PAlE4pqUmel4B40kwwpClfXgCLcBGAs/s1600/img1.png)
---
#### Overview
```
Type GUID/Name Size
BIOS 0x1000000
BIOS Pad 0x40000
FV 8C8CE578-8A3D-4F1C-9935-896185C32DD3 0x20000
BIOS Pad 0x5b0000
FV 8C8CE578-8A3D-4F1C-9935-896185C32DD3 0x40000
FV 8C8CE578-8A3D-4F1C-9935-896185C32DD3 0x6b0000
FV 8C8CE578-8A3D-4F1C-9935-896185C32DD3 0x300000
```
---
## PSP Directories
- Found at PSPDirBase and/or NewPSPDirBase
```
PSPDirBase uint32
NewPSPDirBase uint32
BDHDirBase uint32
```
- BDH is build similiar with different magic values
- ignored
- Starts with $PSPCOOCKIE
---
## PSP Header
```golang
type AMDPSPDirectoryHeader struct {
PspCookie [4]byte
Checksum uint32
TotalEntries uint32
Reserved uint32
}
```
- $PSP
- 2PSP
- Only two entries
- Two pointer to $PSP directories
---
## PSP Entries
- $TotalEntries entries
```golang
type AMDPSPDirectoryBinaryEntry struct {
Type uint32
Size uint32
Location uint32
Reserved uint32
}
```
---
## PSP Entry Type
- Type - duh
- Credit@TU Berlin
```golang
var AMDPSPDirectoryEntries = []AMDPSPDirectoryEntryType{
{0x00, "AMD_PUBLIC_KEY", "AMD public key"},
{0x01, "PSP_FW_BOOT_LOADER", "PSP boot loader in SPI space"},
{0x02, "PSP_FW_TRUSTED_OS", "PSP Firmware region in SPI space"},
{0x03, "PSP_FW_RECOVERY_BOOT_LOADER", "PSP recovery region"},
[...]
{0x60, "FW_IMC", ""},
{0x61, "FW_GEC", ""},
{0x62, "FW_XHCI", ""},
{0x63, "FW_INVALID", ""}
}
```
---
```
+---------+-------+-------+------+----------------+
| Directory | Addr | Type | Magic | Secondary Directory |
+---------+-------+-------+------+----------------+
| 2 | 0xd1000 | PSP_NEW | $PSP | 0x261000 |
+---------+-------+-------+------+----------------+
+---+------+--------+-------+---------------------------+---------+------------------+---------+
| | Entry | Address | Size | Type (Magic) | Version | Signed by | Info |
+---+------+--------+-------+---------------------------+---------+------------------+---------+
| | 0 | 0xd1400 | 0x240 | AMD_PUBLIC_KEY | | AMD_PUBLIC_KEY | pubkey |
| | | | | | | [not verified] | |
| | 1 | 0x261400 | 0x20000 | PSP_FW_BOOT_LOADER ($PS1) | 0.9.3.34 | AMD_PUBLIC_KEY | |
| | | | | | | [not verified] | |
| | 2 | 0xd1700 | 0xbb40 | PSP_FW_RECOVERY_BOOT_LOADER ($PS1) | FF.9.3.34 | AMD_PUBLIC_KEY | |
| | | | | | | [not verified] | |
| | 3 | 0xdd300 | 0x1d300 | SMU_OFFCHIP_FW (SMUR) | 0.19.4D.0 | AMD_PUBLIC_KEY | compressed |
| | | | | | | [not verified] | |
| | 4 | 0xfa600 | 0x340 | OEM_PSP_FW_PUBLIC_KEY | | AMD_PUBLIC_KEY | pubkey |
| | | | | | | [not verified] | |
| | 5 | 0xfaa00 | 0x2740 | SMU_OFF_CHIP_FW_2 (SMUR) | 0.19.4D.0 | AMD_PUBLIC_KEY | compressed |
| | | | | | | [not verified] | |
| | 6 | 0xfd200 | 0x10 | 0x21 | | | |
| | 7 | 0xfd300 | 0x4d0 | 0x30 (0BAR) | 17.5.15.1 | OEM_PSP_FW_PUBLIC_KEY | compressed |
| | | | | | | [not verified] | |
| | 8 | 0xfd800 | 0xbac0 | 0x31~ABL_ARM_CODE~ (AR1B) | 17.5.15.1 | OEM_PSP_FW_PUBLIC_KEY | compressed |
| | | | | | | [not verified] | |
| | 9 | 0x109300 | 0x96d0 | 0x32 (AR2B) | 17.5.15.1 | OEM_PSP_FW_PUBLIC_KEY | compressed |
| | | | | | | [not verified] | |
| | 10 | 0x112a00 | 0xd610 | 0x33 (AR3B) | 17.5.15.1 | OEM_PSP_FW_PUBLIC_KEY | compressed |
| | | | | | | [not verified] | |
| | 11 | 0x120100 | 0x9960 | 0x34 (AR4B) | 17.5.15.1 | OEM_PSP_FW_PUBLIC_KEY | compressed |
| | | | | | | [not verified] | |
| | 12 | 0x129b00 | 0xe800 | 0x35 (AR5B) | 17.5.15.1 | OEM_PSP_FW_PUBLIC_KEY | compressed |
| | | | | | | [not verified] | |
| | 13 | 0x138300 | 0xa7c0 | 0x36 (AR6B) | 17.5.15.1 | OEM_PSP_FW_PUBLIC_KEY | compressed |
| | | | | | | [not verified] | |
| | 14 | 0x142b00 | 0xb90 | 0x24 ($PS1) | 0.7.0.1 | AMD_PUBLIC_KEY | compressed |
| | | | | | | [not verified] | |
| | 15 | 0x261000 | 0x400 | !PL2_SECONDARY_DIRECTORY | | | |
+---+------+--------+-------+---------------------------+---------+------------------+---------+
```
---
## Security?
- Directory starts with public key
- Bootloader next
- Was encrypted end of 2018
- New 0x21 Type: "Encrypted Key"
- AES
---
![](https://1.bp.blogspot.com/-euM-RJEmMVU/WusK5zvEUgI/AAAAAAAAADg/1cXJuaq9Cfw0iIDjWGPk-TkpD5jfmnB2QCLcBGAs/s1600/AES_vertical.jpg)
---
![](https://2.bp.blogspot.com/-UzBO84jaTc0/WusIaJ__46I/AAAAAAAAAC8/PvYg1MXg-ycptjkwfQRwD9Bpt4hBseL_gCLcBGAs/s1600/img6.png)
---
## Data Entries
- Undocumented 0x100 Byte header
- Partly reverse engeneeried
- Signed
---
Header is verified
```go
type AMDPSPEntryBinaryHeader struct {
Unknown1 [16]byte
ID uint32
SizeSigned uint32
Unknown2 [0x18]byte
AlwaysOne uint32 // Could be a type?
Unknown3 [4]byte
SigFingerprint [16]byte
IsCompressed uint32
Unknown4 uint32
FullSize uint32
Unknown5 [12]byte
Version [4]byte
Unknown6 [4]byte
Unknown7 [4]byte
SizePacked uint32
}
```
---
<!-- .slide: data-background="http://i.giphy.com/90F8aUepslB84.gif" -->
## ... Data was not!
-> Ryzenfall
---
# Microcode ?
- Microcode has a header
- But no magic
- Neither with AMD or with Intel
- Detecting MC is hard
- Loaded to a specified address and passed to the processor
- Can lie anywhere inside the firmware
- Format is partly documented
- MCExtractor
- https://github.com/platomav/MCExtractor
---
# Questions?