# 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?
{"slideOptions":{"transition":"slide"}}