From 68df5a64a32c5283e0c939ba57b7bae62fff6de1 Mon Sep 17 00:00:00 2001 From: Alex Matrosov Date: Wed, 11 Oct 2017 22:59:23 -0700 Subject: [PATCH] NE Alpha 43 add visual validation of Intel Boot Guard coverage --- UEFIDump/CMakeLists.txt | 2 + UEFIExtract/uefiextract.pro | 4 +- UEFIFind/uefifind.pro | 5 +- UEFITool/uefitool.cpp | 35 +- UEFITool/uefitool.pro | 4 + UEFITool/uefitool.ui | 44 +- common/basetypes.h | 6 + common/bootguard.h | 192 +++++++ common/descriptor.h | 2 +- common/ffs.h | 44 +- common/ffsparser.cpp | 1083 ++++++++++++++++++++++++++++------- common/ffsparser.h | 47 +- common/fit.h | 10 +- common/gbe.h | 2 +- common/me.h | 2 +- common/nvram.h | 33 +- common/nvramparser.cpp | 40 +- common/parsingdata.h | 3 +- common/sha256.c | 215 +++++++ common/sha256.h | 27 + common/treeitem.cpp | 1 + common/treeitem.h | 4 + common/treemodel.cpp | 58 +- common/treemodel.h | 4 + common/types.cpp | 1 + common/types.h | 3 +- common/utility.cpp | 2 +- common/utility.h | 2 +- 28 files changed, 1591 insertions(+), 284 deletions(-) create mode 100644 common/bootguard.h create mode 100644 common/sha256.c create mode 100644 common/sha256.h diff --git a/UEFIDump/CMakeLists.txt b/UEFIDump/CMakeLists.txt index ef7ae4a..067bf8c 100644 --- a/UEFIDump/CMakeLists.txt +++ b/UEFIDump/CMakeLists.txt @@ -45,6 +45,8 @@ SET(PROJECT_HEADERS ../common/Tiano/EfiTianoDecompress.h ../common/ubytearray.h ../common/ustring.h + ../common/bootguard.h + ../common/sha256.h ../common/bstrlib/bstrlib.h ../common/bstrlib/bstrwrap.h ) diff --git a/UEFIExtract/uefiextract.pro b/UEFIExtract/uefiextract.pro index 68ffbab..e383dfa 100644 --- a/UEFIExtract/uefiextract.pro +++ b/UEFIExtract/uefiextract.pro @@ -47,5 +47,7 @@ HEADERS += \ ../common/LZMA/LzmaDecompress.h \ ../common/Tiano/EfiTianoDecompress.h \ ../common/ubytearray.h \ - ../common/ustring.h + ../common/ustring.h \ + ../common/bootguard.h \ + ../common/sha256.h diff --git a/UEFIFind/uefifind.pro b/UEFIFind/uefifind.pro index 19cd519..3f5643a 100644 --- a/UEFIFind/uefifind.pro +++ b/UEFIFind/uefifind.pro @@ -40,5 +40,6 @@ HEADERS += uefifind.h \ ../common/LZMA/LzmaDecompress.h \ ../common/Tiano/EfiTianoDecompress.h \ ../common/ustring.h \ - ../common/ubytearray.h - + ../common/ubytearray.h \ + ../common/bootguard.h \ + ../common/sha256.h diff --git a/UEFITool/uefitool.cpp b/UEFITool/uefitool.cpp index cf0dae8..076d220 100644 --- a/UEFITool/uefitool.cpp +++ b/UEFITool/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("NE alpha 42")) +version(tr("NE alpha 44")) { clipboard = QApplication::clipboard(); @@ -80,6 +80,7 @@ version(tr("NE alpha 42")) ui->finderMessagesListWidget->setFont(font); ui->builderMessagesListWidget->setFont(font); ui->fitTableWidget->setFont(font); + ui->bootGuardEdit->setFont(font); ui->structureTreeView->setFont(font); searchDialog->ui->guidEdit->setFont(font); searchDialog->ui->hexEdit->setFont(font); @@ -118,7 +119,9 @@ void UEFITool::init() ui->fitTableWidget->setRowCount(0); ui->fitTableWidget->setColumnCount(0); ui->infoEdit->clear(); + ui->bootGuardEdit->clear(); ui->messagesTabWidget->setTabEnabled(1, false); + ui->messagesTabWidget->setTabEnabled(2, false); // Set window title setWindowTitle(tr("UEFITool %1").arg(version)); @@ -863,9 +866,9 @@ void UEFITool::copyMessage() clipboard->clear(); if (ui->messagesTabWidget->currentIndex() == 0) // Parser tab clipboard->setText(ui->parserMessagesListWidget->currentItem()->text()); - else if (ui->messagesTabWidget->currentIndex() == 2) // Search tab + else if (ui->messagesTabWidget->currentIndex() == 3) // Search tab clipboard->setText(ui->finderMessagesListWidget->currentItem()->text()); - else if (ui->messagesTabWidget->currentIndex() == 3) // Builder tab + else if (ui->messagesTabWidget->currentIndex() == 4) // Builder tab clipboard->setText(ui->builderMessagesListWidget->currentItem()->text()); } @@ -878,12 +881,12 @@ void UEFITool::copyAllMessages() text.append(ui->parserMessagesListWidget->item(i)->text()).append("\n"); clipboard->setText(text); } - else if (ui->messagesTabWidget->currentIndex() == 2) { // Search tab + else if (ui->messagesTabWidget->currentIndex() == 3) { // Search tab for (INT32 i = 0; i < ui->finderMessagesListWidget->count(); i++) text.append(ui->finderMessagesListWidget->item(i)->text()).append("\n"); clipboard->setText(text); } - else if (ui->messagesTabWidget->currentIndex() == 3) { // Builder tab + else if (ui->messagesTabWidget->currentIndex() == 4) { // Builder tab for (INT32 i = 0; i < ui->builderMessagesListWidget->count(); i++) text.append(ui->builderMessagesListWidget->item(i)->text()).append("\n"); clipboard->setText(text); @@ -896,11 +899,11 @@ void UEFITool::clearMessages() if (ffsParser) ffsParser->clearMessages(); ui->parserMessagesListWidget->clear(); } - else if (ui->messagesTabWidget->currentIndex() == 2) { // Search tab + else if (ui->messagesTabWidget->currentIndex() == 3) { // Search tab if (ffsFinder) ffsFinder->clearMessages(); ui->finderMessagesListWidget->clear(); } - else if (ui->messagesTabWidget->currentIndex() == 3) { // Builder tab + else if (ui->messagesTabWidget->currentIndex() == 4) { // Builder tab if (ffsBuilder) ffsBuilder->clearMessages(); ui->builderMessagesListWidget->clear(); } @@ -955,7 +958,7 @@ void UEFITool::showFinderMessages() ui->finderMessagesListWidget->addItem(item); } - ui->messagesTabWidget->setCurrentIndex(2); + ui->messagesTabWidget->setCurrentIndex(3); ui->finderMessagesListWidget->scrollToBottom(); } @@ -973,7 +976,7 @@ void UEFITool::showBuilderMessages() ui->builderMessagesListWidget->addItem(item); } - ui->messagesTabWidget->setCurrentIndex(3); + ui->messagesTabWidget->setCurrentIndex(4); ui->builderMessagesListWidget->scrollToBottom(); } @@ -1079,6 +1082,8 @@ void UEFITool::showFitTable() if (fitTable.empty()) { // Disable FIT tab ui->messagesTabWidget->setTabEnabled(1, false); + // Disable BootGuard tab + ui->messagesTabWidget->setTabEnabled(2, false); return; } @@ -1107,6 +1112,18 @@ void UEFITool::showFitTable() ui->fitTableWidget->resizeColumnsToContents(); ui->fitTableWidget->resizeRowsToContents(); ui->messagesTabWidget->setCurrentIndex(1); + + // Get BootGuard info + UString bgInfo = ffsParser->getBootGuardInfo(); + if (bgInfo.isEmpty()) { + // Disable BootGuard tab + ui->messagesTabWidget->setTabEnabled(2, false); + return; + } + + ui->messagesTabWidget->setTabEnabled(2, true); + ui->bootGuardEdit->setPlainText(bgInfo); + ui->messagesTabWidget->setCurrentIndex(2); } void UEFITool::currentTabChanged(int index) diff --git a/UEFITool/uefitool.pro b/UEFITool/uefitool.pro index 1f8f918..2acb10a 100644 --- a/UEFITool/uefitool.pro +++ b/UEFITool/uefitool.pro @@ -3,6 +3,7 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = UEFITool TEMPLATE = app +DEFINES += "U_ENABLE_FIT_PARSING_SUPPORT" DEFINES += "U_ENABLE_NVRAM_PARSING_SUPPORT" DEFINES += "U_ENABLE_GUID_DATABASE_SUPPORT" @@ -39,6 +40,8 @@ HEADERS += uefitool.h \ ../common/Tiano/EfiTianoCompress.h \ ../common/ustring.h \ ../common/ubytearray.h \ + ../common/bootguard.h \ + ../common/sha256.h \ qhexedit2/qhexedit.h \ qhexedit2/chunks.h \ qhexedit2/commands.h @@ -72,6 +75,7 @@ SOURCES += uefitool_main.cpp \ ../common/Tiano/EfiTianoCompress.c \ ../common/Tiano/EfiTianoCompressLegacy.c \ ../common/ustring.cpp \ + ../common/sha256.c \ qhexedit2/qhexedit.cpp \ qhexedit2/chunks.cpp \ qhexedit2/commands.cpp diff --git a/UEFITool/uefitool.ui b/UEFITool/uefitool.ui index 3450b41..306e159 100644 --- a/UEFITool/uefitool.ui +++ b/UEFITool/uefitool.ui @@ -132,15 +132,15 @@ true - - false - + + true + 0 @@ -203,6 +203,44 @@ + + + true + + + BootGuard + + + + 0 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + false + + + false + + + true + + + + + Search diff --git a/common/basetypes.h b/common/basetypes.h index bafb800..51d4ee4 100644 --- a/common/basetypes.h +++ b/common/basetypes.h @@ -59,6 +59,12 @@ typedef uint8_t USTATUS; #define U_STORES_NOT_FOUND 38 #define U_INVALID_IMAGE 39 #define U_INVALID_RAW_AREA 40 +#define U_INVALID_FIT 41 +#define U_INVALID_MICROCODE 42 +#define U_INVALID_ACM 43 +#define U_INVALID_BG_KEY_MANIFEST 44 +#define U_INVALID_BG_BOOT_POLICY 45 +#define U_ELEMENTS_NOT_FOUND 46 #define U_NOT_IMPLEMENTED 0xFF // UDK porting definitions diff --git a/common/bootguard.h b/common/bootguard.h new file mode 100644 index 0000000..d2520ea --- /dev/null +++ b/common/bootguard.h @@ -0,0 +1,192 @@ +/* bootguard.h + +Copyright (c) 2017, LongSoft. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ + +#ifndef BOOTGUARD_H +#define BOOTGUARD_H + +#include "basetypes.h" +#include "sha256.h" + +#pragma pack(push, 1) + +const UByteArray BG_VENDOR_HASH_FILE_GUID_PHOENIX // 389CC6F2-1EA8-467B-AB8A-78E769AE2A15 +("\xF2\xC6\x9C\x38\xA8\x1E\x7B\x46\xAB\x8A\x78\xE7\x69\xAE\x2A\x15", 16); + +#define BG_VENDOR_HASH_FILE_SIGNATURE_PHOENIX (*(UINT64 *)"$HASHTBL") + +const UByteArray BG_VENDOR_HASH_FILE_GUID_AMI // CBC91F44-A4BC-4A5B-8696-703451D0B053 +("\x44\x1F\xC9\xCB\xBC\xA4\x5B\x4A\x86\x96\x70\x34\x51\xD0\xB0\x53", 16); + +typedef struct BG_VENDOR_HASH_FILE_ENTRY +{ + UINT8 Hash[SHA256_DIGEST_SIZE]; + UINT32 Offset; + UINT32 Size; +} BG_VENDOR_HASH_FILE_ENTRY; + +typedef struct BG_VENDOR_HASH_FILE_HEADER_PHOENIX_ +{ + UINT64 Signature; + UINT32 NumEntries; + //BG_VENDOR_HASH_FILE_ENTRY Entries[]; +} BG_VENDOR_HASH_FILE_HEADER_PHOENIX; + +typedef struct BG_VENDOR_HASH_FILE_HEADER_AMI_NEW_ +{ + BG_VENDOR_HASH_FILE_ENTRY Entries[2]; +} BG_VENDOR_HASH_FILE_HEADER_AMI_NEW; + +typedef struct BG_VENDOR_HASH_FILE_HEADER_AMI_OLD_ +{ + UINT8 Hash[SHA256_DIGEST_SIZE]; + UINT32 Size; + // Offset is derived from flash map, will be detected as root volume with DXE core +} BG_VENDOR_HASH_FILE_HEADER_AMI_OLD; + + +// +// Intel ACM +// + +#define INTEL_ACM_MODULE_TYPE 0x00030002 +#define INTEL_ACM_MODULE_VENDOR 0x8086 + +typedef struct INTEL_ACM_HEADER_ { + UINT32 ModuleType; + UINT32 HeaderType; + UINT32 HeaderVersion; + UINT16 ChipsetId; + UINT16 Unknown; + UINT32 ModuleVendor; + UINT8 DateDay; + UINT8 DateMonth; + UINT16 DateYear; + UINT32 ModuleSize; + UINT16 AcmSvn; + UINT16 : 16; + UINT32 Unknown1; + UINT32 Unknown2; + UINT32 GdtMax; + UINT32 GdtBase; + UINT32 SegmentSel; + UINT32 EntryPoint; + UINT8 Unknown3[64]; + UINT32 KeySize; + UINT32 Unknown4; + UINT8 RsaPubKey[256]; + UINT32 RsaPubExp; + UINT8 RsaSig[256]; +} INTEL_ACM_HEADER; + +// +// Intel BootGuard Key Manifest +// +#define BG_BOOT_POLICY_MANIFEST_HEADER_TAG (*(UINT64 *)"__ACBP__") +typedef struct BG_BOOT_POLICY_MANIFEST_HEADER_ { + UINT64 Tag; + UINT8 Version; + UINT8 HeaderVersion; + UINT8 PMBPMVersion; + UINT8 BPSVN; + UINT8 ACMSVN; + UINT8 : 8; + UINT16 NEMDataSize; +} BG_BOOT_POLICY_MANIFEST_HEADER; + +typedef struct SHA256_HASH_ { + UINT16 HashAlgorithmId; + UINT16 Size; + UINT8 HashBuffer[32]; +} SHA256_HASH; + +typedef struct RSA_PUBLIC_KEY_ { + UINT8 Version; + UINT16 KeySize; + UINT32 Exponent; + UINT8 Modulus[256]; +} RSA_PUBLIC_KEY; + +typedef struct RSA_SIGNATURE_ { + UINT8 Version; + UINT16 KeySize; + UINT16 HashId; + UINT8 Signature[256]; +} RSA_SIGNATURE; + +typedef struct KEY_SIGNATURE_ { + UINT8 Version; + UINT16 KeyId; + RSA_PUBLIC_KEY PubKey; + UINT16 SigScheme; + RSA_SIGNATURE Signature; +} BG_KEY_SIGNATURE; + +#define BG_IBB_SEGMENT_FLAG_IBB 0x0 +#define BG_IBB_SEGMENT_FLAG_NON_IBB 0x1 +typedef struct BG_IBB_SEGMENT_ELEMENT_ { +UINT16: 16; + UINT16 Flags; + UINT32 Base; + UINT32 Size; +} BG_IBB_SEGMENT_ELEMENT; + +#define BG_BOOT_POLICY_MANIFEST_IBB_ELEMENT_TAG (*(UINT64 *)"__IBBS__") +#define BG_IBB_FLAG_AUTHORITY_MEASURE 0x4 + +typedef struct BG_IBB_ELEMENT_ { + UINT64 Tag; + UINT8 Version; + UINT16 : 16; + UINT8 Unknown; + UINT32 Flags; + UINT64 IbbMchBar; + UINT64 VtdBar; + UINT32 Unknown1; + UINT32 Unknown2; + UINT64 Unknown3; + UINT64 Unknown4; + SHA256_HASH IbbHash; + UINT32 EntryPoint; + SHA256_HASH Digest; + UINT8 IbbSegCount; + // BG_IBB_SEGMENT_ELEMENT IbbSegment[]; +} BG_IBB_ELEMENT; + +#define BG_BOOT_POLICY_MANIFEST_PLATFORM_MANUFACTURER_ELEMENT_TAG (*(UINT64 *)"__PMDA__") +typedef struct BG_PLATFORM_MANUFACTURER_ELEMENT_ { + UINT64 Tag; + UINT8 Version; + UINT16 DataSize; +} BG_PLATFORM_MANUFACTURER_ELEMENT; + +#define BG_BOOT_POLICY_MANIFEST_SIGNATURE_ELEMENT_TAG (*(UINT64 *)"__PMSG__") +typedef struct BG_BOOT_POLICY_MANIFEST_SIGNATURE_ELEMENT_ { + UINT64 Tag; + UINT8 Version; + BG_KEY_SIGNATURE KeySignature; +} BG_BOOT_POLICY_MANIFEST_SIGNATURE_ELEMENT; + +#define BG_KEY_MANIFEST_TAG (*(UINT64 *)"__KEYM__") +typedef struct BG_KEY_MANIFEST_ { + UINT64 Tag; + UINT8 Version; + UINT8 KmVersion; + UINT8 KmSvn; + UINT8 KmId; + SHA256_HASH BpKeyHash; + BG_KEY_SIGNATURE KeyManifestSignature; +} BG_KEY_MANIFEST; + +#pragma pack(pop) + +#endif // BOOTGUARD_H \ No newline at end of file diff --git a/common/descriptor.h b/common/descriptor.h index 06011f3..1aa076f 100644 --- a/common/descriptor.h +++ b/common/descriptor.h @@ -63,7 +63,7 @@ typedef struct FLASH_PARAMETERS_ { UINT8 SecondChipDensity : 4; UINT8 : 8; UINT8 : 1; - UINT8 ReadClockFrequency : 3; // Hardcoded value of 20 Mhz (000b) in v1 descriptors and 17 Mhz (110b) in v2 ones + UINT8 ReadClockFrequency : 3; // Hardcoded value of 20 Mhz (000b) in v1 descriptors UINT8 FastReadEnabled : 1; UINT8 FastReadFrequency : 3; UINT8 FlashWriteFrequency : 3; diff --git a/common/ffs.h b/common/ffs.h index 5a18e2e..61e1e84 100644 --- a/common/ffs.h +++ b/common/ffs.h @@ -149,17 +149,17 @@ const UByteArray EFI_FV_SIGNATURE("_FVH", 4); // Firmware volume attributes // Revision 1 -#define EFI_FVB_READ_DISABLED_CAP 0x00000001 -#define EFI_FVB_READ_ENABLED_CAP 0x00000002 -#define EFI_FVB_READ_STATUS 0x00000004 -#define EFI_FVB_WRITE_DISABLED_CAP 0x00000008 -#define EFI_FVB_WRITE_ENABLED_CAP 0x00000010 -#define EFI_FVB_WRITE_STATUS 0x00000020 -#define EFI_FVB_LOCK_CAP 0x00000040 -#define EFI_FVB_LOCK_STATUS 0x00000080 -#define EFI_FVB_STICKY_WRITE 0x00000200 -#define EFI_FVB_MEMORY_MAPPED 0x00000400 -#define EFI_FVB_ERASE_POLARITY 0x00000800 +#define EFI_FVB_READ_DISABLED_CAP 0x00000001 +#define EFI_FVB_READ_ENABLED_CAP 0x00000002 +#define EFI_FVB_READ_STATUS 0x00000004 +#define EFI_FVB_WRITE_DISABLED_CAP 0x00000008 +#define EFI_FVB_WRITE_ENABLED_CAP 0x00000010 +#define EFI_FVB_WRITE_STATUS 0x00000020 +#define EFI_FVB_LOCK_CAP 0x00000040 +#define EFI_FVB_LOCK_STATUS 0x00000080 +#define EFI_FVB_STICKY_WRITE 0x00000200 +#define EFI_FVB_MEMORY_MAPPED 0x00000400 +#define EFI_FVB_ERASE_POLARITY 0x00000800 #define EFI_FVB_ALIGNMENT_CAP 0x00008000 #define EFI_FVB_ALIGNMENT_2 0x00010000 #define EFI_FVB_ALIGNMENT_4 0x00020000 @@ -248,7 +248,7 @@ typedef struct EFI_FIRMWARE_VOLUME_EXT_ENTRY_ { typedef struct EFI_FIRMWARE_VOLUME_EXT_HEADER_OEM_TYPE_ { EFI_FIRMWARE_VOLUME_EXT_ENTRY Header; UINT32 TypeMask; - //EFI_GUID Types[1]; + //EFI_GUID Types[]; } EFI_FIRMWARE_VOLUME_EXT_HEADER_OEM_TYPE; #define EFI_FV_EXT_TYPE_GUID_TYPE 0x0002 @@ -339,6 +339,7 @@ extern const UINT8 ffsAlignmentTable[]; #define EFI_FILE_DELETED 0x10 #define EFI_FILE_HEADER_INVALID 0x20 #define EFI_FILE_ERASE_POLARITY 0x80 // Defined as "all other bits must be set to ERASE_POLARITY" in UEFI PI Vol3 + // PEI apriori file const UByteArray EFI_PEI_APRIORI_FILE_GUID ("\x0A\xCC\x45\x1B\x6A\x15\x8A\x42\xAF\x62\x49\x86\x4D\xA0\xE6\xE6", 16); @@ -355,6 +356,10 @@ const UByteArray EFI_FFS_VOLUME_TOP_FILE_GUID const UByteArray EFI_FFS_PAD_FILE_GUID ("\x85\x65\x53\xE4\x09\x79\x60\x4A\xB5\xC6\xEC\xDE\xA6\xEB\xFB\x54", 16); +// DXE core file +const UByteArray EFI_DXE_CORE_GUID // 5AE3F37E-4EAE-41AE-8240-35465B5E81EB +("\x7E\xF3\xE3\x5A\xAE\x4E\xAE\x41\x82\x40\x35\x46\x5B\x5E\x81\xEB", 16); + // FFS size conversion routines extern VOID uint32ToUint24(UINT32 size, UINT8* ffsSize); extern UINT32 uint24ToUint32(const UINT8* ffsSize); @@ -479,15 +484,18 @@ typedef struct WIN_CERTIFICATE_UEFI_GUID_ { } WIN_CERTIFICATE_UEFI_GUID; // WIN_CERTIFICATE_UEFI_GUID.CertType -const UByteArray EFI_CERT_TYPE_RSA2048_SHA256_GUID +const UByteArray EFI_CERT_TYPE_RSA2048_SHA256_GUID // A7717414-C616-4977-9420-844712A735BF ("\x14\x74\x71\xA7\x16\xC6\x77\x49\x94\x20\x84\x47\x12\xA7\x35\xBF"); // WIN_CERTIFICATE_UEFI_GUID.CertData -typedef struct EFI_CERT_BLOCK_RSA_2048_SHA256_ { - UINT32 HashType; - UINT8 PublicKey[256]; - UINT8 Signature[256]; -} EFI_CERT_BLOCK_RSA_2048_SHA256; +typedef struct EFI_CERT_BLOCK_RSA2048_SHA256_ { + EFI_GUID HashType; + UINT8 PublicKey[256]; + UINT8 Signature[256]; +} EFI_CERT_BLOCK_RSA2048_SHA256; + +const UByteArray EFI_HASH_ALGORITHM_SHA256_GUID // 51aa59de-fdf2-4ea3-bc63-875fb7842ee9 +("\xde\x59\xAA\x51\xF2\xFD\xA3\x4E\xBC\x63\x87\x5F\xB7\x84\x2E\xE9"); // Version section typedef struct EFI_VERSION_SECTION_ { diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index 613604a..8eab609 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -28,6 +28,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "parsingdata.h" #include "types.h" + // Region info structure definition struct REGION_INFO { UINT32 offset; @@ -42,6 +43,7 @@ USTATUS FfsParser::parse(const UByteArray & buffer) { UModelIndex root; + openedImage = buffer; USTATUS result = performFirstPass(buffer, root); addOffsetsRecursive(root); if (result) @@ -265,12 +267,8 @@ USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 l UINT8 descriptorVersion = 0; if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_20MHZ) // Old descriptor descriptorVersion = 1; - else if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_17MHZ) // Skylake+ descriptor + else // Skylake+ descriptor descriptorVersion = 2; - else { - msg(usprintf("parseIntelImage: unknown descriptor version with ReadClockFrequency %02Xh", componentSection->FlashParameters.ReadClockFrequency)); - return U_INVALID_FLASH_DESCRIPTOR; - } // Regions std::vector regions; @@ -784,6 +782,9 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) if (result) return result; + if (bgFirstVolumeOffset == 0x100000000ULL) + bgFirstVolumeOffset = model->offset(index) + prevVolumeOffset; + // First volume is not at the beginning of RAW area UString name; UString info; @@ -993,23 +994,17 @@ USTATUS FfsParser::parseVolumeHeader(const UByteArray & volume, const UINT32 loc // Determine value of empty byte UINT8 emptyByte = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? '\xFF' : '\x00'; - // Check for AppleCRC32 and AppleFreeSpaceOffset in ZeroVector + // Check for AppleCRC32 and UsedSpace in ZeroVector bool hasAppleCrc32 = false; - bool hasAppleFSO = false; UINT32 volumeSize = volume.size(); UINT32 appleCrc32 = *(UINT32*)(volume.constData() + 8); - UINT32 appleFSO = *(UINT32*)(volume.constData() + 12); + UINT32 usedSpace = *(UINT32*)(volume.constData() + 12); if (appleCrc32 != 0) { // Calculate CRC32 of the volume body UINT32 crc = crc32(0, (const UINT8*)(volume.constData() + volumeHeader->HeaderLength), volumeSize - volumeHeader->HeaderLength); if (crc == appleCrc32) { hasAppleCrc32 = true; } - - // Check if FreeSpaceOffset is non-zero - if (appleFSO != 0) { - hasAppleFSO = true; - } } // Check header checksum by recalculating it @@ -1053,8 +1048,6 @@ USTATUS FfsParser::parseVolumeHeader(const UByteArray & volume, const UINT32 loc UString text; if (hasAppleCrc32) text += UString("AppleCRC32 "); - if (hasAppleFSO) - text += UString("AppleFSO "); // Add tree item UINT8 subtype = Subtypes::UnknownVolume; @@ -1077,7 +1070,8 @@ USTATUS FfsParser::parseVolumeHeader(const UByteArray & volume, const UINT32 loc pdata.alignment = alignment; pdata.revision = volumeHeader->Revision; pdata.hasAppleCrc32 = hasAppleCrc32; - pdata.hasAppleFSO = hasAppleFSO; + pdata.hasValidUsedSpace = FALSE; // Will be updated later, if needed + pdata.usedSpace = usedSpace; pdata.isWeakAligned = (volumeHeader->Revision > 1 && (volumeHeader->Attributes & EFI_FVB2_WEAK_ALIGNMENT)); model->setParsingData(index, UByteArray((const char*)&pdata, sizeof(pdata))); @@ -1195,11 +1189,13 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) // Get required values from parsing data UINT8 emptyByte = 0xFF; UINT8 ffsVersion = 2; + UINT32 usedSpace = 0; if (model->hasEmptyParsingData(index) == false) { UByteArray data = model->parsingData(index); const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData(); emptyByte = pdata->emptyByte; ffsVersion = pdata->ffsVersion; + usedSpace = pdata->usedSpace; } // Check for unknown FFS version @@ -1217,6 +1213,17 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) // Check that we are at the empty space UByteArray header = volumeBody.mid(fileOffset, sizeof(EFI_FFS_FILE_HEADER)); if (header.count(emptyByte) == header.size()) { //Empty space + // Check volume usedSpace entry to be valid + if (usedSpace > 0 && usedSpace == fileOffset + volumeHeaderSize) { + if (model->hasEmptyParsingData(index) == false) { + UByteArray data = model->parsingData(index); + VOLUME_PARSING_DATA* pdata = (VOLUME_PARSING_DATA*)data.data(); + pdata->hasValidUsedSpace = TRUE; + model->setParsingData(index, data); + model->setText(index, model->text(index) + "UsedSpace "); + } + } + // Check free space to be actually free UByteArray freeSpace = volumeBody.mid(fileOffset); if (freeSpace.count(emptyByte) != freeSpace.size()) { @@ -1366,7 +1373,7 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf bool isWeakAligned = false; UINT32 volumeAlignment = 0xFFFFFFFF; UINT8 volumeRevision = 2; - UModelIndex parentVolumeIndex = model->findParentOfType(parent, Types::Volume); + UModelIndex parentVolumeIndex = model->type(parent) == Types::Volume ? parent : model->findParentOfType(parent, Types::Volume); if (parentVolumeIndex.isValid() && model->hasEmptyParsingData(parentVolumeIndex) == false) { UByteArray data = model->parsingData(parentVolumeIndex); const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData(); @@ -1474,14 +1481,22 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf UString text; bool isVtf = false; + bool isDxeCore = false; // Check if the file is a Volume Top File - if (UByteArray((const char*)&fileHeader->Name, sizeof(EFI_GUID)) == EFI_FFS_VOLUME_TOP_FILE_GUID) { + UByteArray fileGuid = UByteArray((const char*)&fileHeader->Name, sizeof(EFI_GUID)); + if (fileGuid == EFI_FFS_VOLUME_TOP_FILE_GUID) { // Mark it as the last VTF // This information will later be used to determine memory addresses of uncompressed image elements // Because the last byte of the last VFT is mapped to 0xFFFFFFFF physical memory address isVtf = true; text = UString("Volume Top File"); } + // Check if the file is the first DXE Core + else if (fileGuid == EFI_DXE_CORE_GUID) { + // Mark is as first DXE code + // This information may be used to determine DXE volume offset for old AMI protected ranges + isDxeCore = true; + } // Construct fixed state ItemFixedState fixed = (ItemFixedState)((fileHeader->Attributes & FFS_ATTRIB_FIXED) != 0); @@ -1500,6 +1515,11 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf lastVtf = index; } + // Override first DXE core index, if needed + if (isDxeCore && !bgDxeCoreIndex.isValid()) { + bgDxeCoreIndex = index; + } + // Show messages if (msgUnalignedFile) msg(UString("parseFileHeader: unaligned file"), index); @@ -1555,10 +1575,16 @@ USTATUS FfsParser::parseFileBody(const UModelIndex & index) // Parse raw files as raw areas if (model->subtype(index) == EFI_FV_FILETYPE_RAW || model->subtype(index) == EFI_FV_FILETYPE_ALL) { + UByteArray fileGuid = UByteArray(model->header(index).constData(), sizeof(EFI_GUID)); + // Parse NVAR store - if (UByteArray(model->header(index).constData(), sizeof(EFI_GUID)) == NVRAM_NVAR_STORE_FILE_GUID) + if (fileGuid == NVRAM_NVAR_STORE_FILE_GUID) return nvramParser.parseNvarStore(index); + // Parse vendor hash file + else if (fileGuid == BG_VENDOR_HASH_FILE_GUID_PHOENIX) + return parseVendorHashFile(fileGuid, index); + return parseRawArea(index); } @@ -1967,6 +1993,20 @@ USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UI } // No need to change dataOffset here } + else if (baGuid == EFI_CERT_TYPE_RSA2048_SHA256_GUID) { + if ((attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == 0) { // Check that ProcessingRequired attribute is set on signed GUIDed sections + msgNoProcessingRequiredAttributeSigned = true; + } + + // Get certificate type and length + if ((UINT32)section.size() < headerSize + sizeof(EFI_CERT_BLOCK_RSA2048_SHA256)) + return U_INVALID_SECTION; + + // Adjust dataOffset + dataOffset += sizeof(EFI_CERT_BLOCK_RSA2048_SHA256); + additionalInfo += UString("\nCertificate type: RSA2048/SHA256"); + msgSignedSectionFound = true; + } else if (baGuid == EFI_FIRMWARE_CONTENTS_SIGNED_GUID) { if ((attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == 0) { // Check that ProcessingRequired attribute is set on signed GUIDed sections msgNoProcessingRequiredAttributeSigned = true; @@ -2009,6 +2049,7 @@ USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UI } msgSignedSectionFound = true; } + UByteArray header = section.left(dataOffset); UByteArray body = section.mid(dataOffset); @@ -2658,11 +2699,16 @@ USTATUS FfsParser::parseRawSectionBody(const UModelIndex & index) } else if (parentFileGuid == NVRAM_NVAR_EXTERNAL_DEFAULTS_FILE_GUID) { // AMI NVRAM external defaults // Parse NVAR area - nvramParser.parseNvarStore(index); + nvramParser.parseNvarStore(index); // Set parent file text model->setText(parentFile, UString("NVRAM external defaults")); } + else if (parentFileGuid == BG_VENDOR_HASH_FILE_GUID_AMI) { // AMI vendor hash file + // Parse AMI vendor hash file + parseVendorHashFile(parentFileGuid, index); + } + // Parse as raw area return parseRawArea(index); @@ -2822,7 +2868,10 @@ USTATUS FfsParser::performSecondPass(const UModelIndex & index) // Find and parse FIT parseFit(index); - + + // Check protected ranges + checkProtectedRanges(index); + // Apply address information to index and all it's child items addMemoryAddressesRecursive(index); @@ -2832,203 +2881,12 @@ USTATUS FfsParser::performSecondPass(const UModelIndex & index) return U_SUCCESS; } -USTATUS FfsParser::addFixedAndCompressedRecursive(const UModelIndex & index) { - // Sanity check - if (!index.isValid()) - return U_INVALID_PARAMETER; - - // Add fixed and compressed info - model->addInfo(index, usprintf("\nCompressed: %s", model->compressed(index) ? "Yes" : "No")); - model->addInfo(index, usprintf("\nFixed: %s", model->fixed(index) ? "Yes" : "No")); - - // Process child items - for (int i = 0; i < model->rowCount(index); i++) { - addFixedAndCompressedRecursive(index.child(i, 0)); - } - - return U_SUCCESS; -} - -USTATUS FfsParser::parseFit(const UModelIndex & index) -{ - // Check sanity - if (!index.isValid()) - return EFI_INVALID_PARAMETER; - - // Search for FIT - UModelIndex fitIndex; - UINT32 fitOffset; - USTATUS result = findFitRecursive(index, fitIndex, fitOffset); - if (result) - return result; - - // FIT not found - if (!fitIndex.isValid()) - return U_SUCCESS; - - // Explicitly set the item as fixed - model->setFixed(fitIndex, true); - - // Special case of FIT header - UByteArray fitBody = model->body(fitIndex); - const FIT_ENTRY* fitHeader = (const FIT_ENTRY*)(fitBody.constData() + fitOffset); - - // Check FIT checksum, if present - UINT32 fitSize = (fitHeader->Size & 0xFFFFFF) << 4; - if (fitHeader->Type & 0x80) { - // Calculate FIT entry checksum - UByteArray tempFIT = model->body(fitIndex).mid(fitOffset, fitSize); - FIT_ENTRY* tempFitHeader = (FIT_ENTRY*)tempFIT.data(); - tempFitHeader->Type &= 0x7F; // Remove ChecksumValid bit before calculating the checksum - tempFitHeader->Checksum = 0; - UINT8 calculated = calculateChecksum8((const UINT8*)tempFitHeader, fitSize); - if (calculated != fitHeader->Checksum) { - msg(usprintf("parseFit: invalid FIT table checksum %02Xh, should be %02Xh", fitHeader->Checksum, calculated), fitIndex); - } - } - - // Check fit header type - if ((fitHeader->Type & 0x7F) != FIT_TYPE_HEADER) - msg(UString("Invalid FIT header type"), fitIndex); - - // Add FIT header - std::vector currentStrings; - currentStrings.push_back(UString("_FIT_ ")); - currentStrings.push_back(usprintf("%08Xh", fitSize)); - currentStrings.push_back(usprintf("%04Xh", fitHeader->Version)); - currentStrings.push_back(usprintf("%02Xh", fitHeader->Checksum)); - currentStrings.push_back(fitEntryTypeToUString(fitHeader->Type)); - currentStrings.push_back(UString("")); // Empty info for FIT header - fitTable.push_back(std::pair, UModelIndex>(currentStrings, fitIndex)); - - // Process all other entries - bool msgModifiedImageMayNotWork = false; - for (UINT32 i = 1; i < fitHeader->Size; i++) { - currentStrings.clear(); - UString info; - UModelIndex itemIndex; - const FIT_ENTRY* currentEntry = fitHeader + i; - UINT32 currentEntrySize = currentEntry->Size; - - // Check entry type - switch (currentEntry->Type & 0x7F) { - case FIT_TYPE_HEADER: - msg(UString("parseFit: second FIT header found, the table is damaged"), fitIndex); - break; - - case FIT_TYPE_EMPTY: - break; - - case FIT_TYPE_MICROCODE: { - //TODO: refactor into function with error reporting - if (currentEntry->Address > addressDiff && currentEntry->Address < 0xFFFFFFFFUL) { - UINT32 offset = (UINT32)(currentEntry->Address - addressDiff); - UModelIndex mcIndex = model->findByOffset(offset); - if (mcIndex.isValid()) { - UByteArray mcFile = model->header(mcIndex) + model->body(mcIndex) + model->tail(mcIndex); - UINT32 mcOffset = offset - model->offset(mcIndex); - if (mcOffset + sizeof(INTEL_MICROCODE_HEADER) <= (UINT32)mcFile.size()) { - const INTEL_MICROCODE_HEADER* header = (const INTEL_MICROCODE_HEADER*)(mcFile.constData() + mcOffset); - if (header->Version == INTEL_MICROCODE_HEADER_VERSION) { - bool reservedBytesValid = true; - for (UINT8 i = 0; i < sizeof(header->Reserved); i++) - if (header->Reserved[i] != INTEL_MICROCODE_HEADER_RESERVED_BYTE) { - reservedBytesValid = false; - break; - } - if (reservedBytesValid) { - UINT32 mcSize = header->TotalSize; - if (mcOffset + mcSize <= (UINT32)mcFile.size()) { - // Valid microcode found - info = usprintf("LocalOffset %08Xh, CPUID %08Xh, Revision %08Xh, Date %08Xh", - mcOffset, - header->CpuSignature, - header->Revision, - header->Date); - currentEntrySize = header->TotalSize; - itemIndex = mcIndex; - } - } - } - } - } - } - } break; - - case FIT_TYPE_BIOS_AC_MODULE: - case FIT_TYPE_BIOS_INIT_MODULE: - case FIT_TYPE_TPM_POLICY: - case FIT_TYPE_BIOS_POLICY_DATA: - case FIT_TYPE_TXT_CONF_POLICY: - case FIT_TYPE_AC_KEY_MANIFEST: - case FIT_TYPE_AC_BOOT_POLICY: - default: - if (currentEntry->Address > addressDiff && currentEntry->Address < 0xFFFFFFFFUL) { - itemIndex = model->findByOffset((UINT32)(currentEntry->Address - addressDiff)); - } - - msgModifiedImageMayNotWork = true; - break; - } - - // Add entry to fitTable - currentStrings.push_back(usprintf("%016" PRIX64, currentEntry->Address)); - currentStrings.push_back(usprintf("%08Xh", currentEntrySize, currentEntrySize)); - currentStrings.push_back(usprintf("%04Xh", currentEntry->Version)); - currentStrings.push_back(usprintf("%02Xh", currentEntry->Checksum)); - currentStrings.push_back(fitEntryTypeToUString(currentEntry->Type)); - currentStrings.push_back(info); - fitTable.push_back(std::pair, UModelIndex>(currentStrings, itemIndex)); - } - - if (msgModifiedImageMayNotWork) - msg(UString("parseFit: opened image may not work after any modification"), fitIndex); - - return U_SUCCESS; -} - -USTATUS FfsParser::findFitRecursive(const UModelIndex & index, UModelIndex & found, UINT32 & fitOffset) -{ - // Sanity check - if (!index.isValid()) - return U_SUCCESS; - - // Process child items - for (int i = 0; i < model->rowCount(index); i++) { - findFitRecursive(index.child(i, 0), found, fitOffset); - if (found.isValid()) - return U_SUCCESS; - } - - // Check for all FIT signatures in item's body - UByteArray lastVtfBody = model->body(lastVtf); - UINT32 storedFitAddress = *(const UINT32*)(lastVtfBody.constData() + lastVtfBody.size() - FIT_POINTER_OFFSET); - for (INT32 offset = model->body(index).indexOf(FIT_SIGNATURE); - offset >= 0; - offset = model->body(index).indexOf(FIT_SIGNATURE, offset + 1)) { - // FIT candidate found, calculate it's physical address - UINT32 fitAddress = model->offset(index) + addressDiff + model->header(index).size() + (UINT32)offset; - - // Check FIT address to be stored in the last VTF - if (fitAddress == storedFitAddress) { - found = index; - fitOffset = offset; - msg(usprintf("findFitRecursive: real FIT table found at physical address %08Xh", fitAddress), found); - return U_SUCCESS; - } - else if (model->rowCount(index) == 0) // Show messages only to leaf items - msg(UString("findFitRecursive: FIT table candidate found, but not referenced from the last VTF"), index); - } - - return U_SUCCESS; -} - USTATUS FfsParser::addMemoryAddressesRecursive(const UModelIndex & index) { // Sanity check if (!index.isValid()) return U_SUCCESS; - + // Set address value for non-compressed data if (!model->compressed(index)) { // Check address sanity @@ -3109,7 +2967,7 @@ USTATUS FfsParser::addOffsetsRecursive(const UModelIndex & index) // Sanity check if (!index.isValid()) return U_INVALID_PARAMETER; - + // Add current offset if the element is not compressed // or it's compressed, but it's parent isn't if ((!model->compressed(index)) || (index.parent().isValid() && !model->compressed(index.parent()))) { @@ -3124,3 +2982,800 @@ USTATUS FfsParser::addOffsetsRecursive(const UModelIndex & index) return U_SUCCESS; } +USTATUS FfsParser::addFixedAndCompressedRecursive(const UModelIndex & index) { + // Sanity check + if (!index.isValid()) + return U_INVALID_PARAMETER; + + // Add fixed and compressed info + model->addInfo(index, usprintf("\nCompressed: %s", model->compressed(index) ? "Yes" : "No")); + model->addInfo(index, usprintf("\nFixed: %s", model->fixed(index) ? "Yes" : "No")); + + // Process child items + for (int i = 0; i < model->rowCount(index); i++) { + addFixedAndCompressedRecursive(index.child(i, 0)); + } + + return U_SUCCESS; +} + +USTATUS FfsParser::checkProtectedRanges(const UModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return U_INVALID_PARAMETER; + + // Calculate digest for BG-protected ranges + UByteArray protectedParts; + bool bgProtectedRangeFound = false; + for (UINT32 i = 0; i < (UINT32)bgProtectedRanges.size(); i++) { + if (bgProtectedRanges[i].Type == BG_PROTECTED_RANGE_INTEL_BOOT_GUARD) { + bgProtectedRangeFound = true; + protectedParts += openedImage.mid(bgProtectedRanges[i].Offset, bgProtectedRanges[i].Size); + markProtectedRangeRecursive(index, bgProtectedRanges[i]); + } + } + + if (bgProtectedRangeFound) { + UByteArray digest(SHA256_DIGEST_SIZE, '\x00'); + sha256(protectedParts.constData(), protectedParts.length(), digest.data()); + + if (digest != bgBpDigest) { + msg(UString("checkProtectedRanges: BG-protected ranges hash mismatch, opened image may refuse to boot"), index); + } + } + else if (bgBootPolicyFound) { + msg(usprintf("checkProtectedRanges: BootPolicy doesn't define any BG-protected ranges"), index); + } + + // Calculate digests for vendor-protected ranges + for (UINT32 i = 0; i < (UINT32)bgProtectedRanges.size(); i++) { + if (bgProtectedRanges[i].Type == BG_PROTECTED_RANGE_VENDOR_HASH_AMI_OLD) { + if (!bgDxeCoreIndex.isValid()) { + msg(UString("checkProtectedRanges: can't determine DXE volume offset, old AMI protected range hash can't be checked"), index); + } + else { + // Offset will be determined as the offset of root volume with first DXE core + UModelIndex dxeRootVolumeIndex = model->findLastParentOfType(bgDxeCoreIndex, Types::Volume); + if (!dxeRootVolumeIndex.isValid()) { + msg(UString("checkProtectedRanges: can't determine DXE volume offset, old AMI protected range hash can't be checked"), index); + } + else { + bgProtectedRanges[i].Offset = model->offset(dxeRootVolumeIndex); + protectedParts = openedImage.mid(bgProtectedRanges[i].Offset, bgProtectedRanges[i].Size); + + UByteArray digest(SHA256_DIGEST_SIZE, '\x00'); + sha256(protectedParts.constData(), protectedParts.length(), digest.data()); + + if (digest != bgProtectedRanges[i].Hash) { + msg(usprintf("checkProtectedRanges: AMI protected range [%Xh:%Xh] hash mismatch, opened image may refuse to boot", + bgProtectedRanges[i].Offset, bgProtectedRanges[i].Offset + bgProtectedRanges[i].Size), + model->findByOffset(bgProtectedRanges[i].Offset)); + } + + markProtectedRangeRecursive(index, bgProtectedRanges[i]); + } + } + } + else if (bgProtectedRanges[i].Type == BG_PROTECTED_RANGE_VENDOR_HASH_AMI_NEW) { + bgProtectedRanges[i].Offset -= addressDiff; + protectedParts = openedImage.mid(bgProtectedRanges[i].Offset, bgProtectedRanges[i].Size); + + UByteArray digest(SHA256_DIGEST_SIZE, '\x00'); + sha256(protectedParts.constData(), protectedParts.length(), digest.data()); + + if (digest != bgProtectedRanges[i].Hash) { + msg(usprintf("checkProtectedRanges: AMI protected range [%Xh:%Xh] hash mismatch, opened image may refuse to boot", + bgProtectedRanges[i].Offset, bgProtectedRanges[i].Offset + bgProtectedRanges[i].Size), + model->findByOffset(bgProtectedRanges[i].Offset)); + } + + markProtectedRangeRecursive(index, bgProtectedRanges[i]); + } + else if (bgProtectedRanges[i].Type == BG_PROTECTED_RANGE_VENDOR_HASH_PHOENIX) { + bgProtectedRanges[i].Offset += bgFirstVolumeOffset; + protectedParts = openedImage.mid(bgProtectedRanges[i].Offset, bgProtectedRanges[i].Size); + + UByteArray digest(SHA256_DIGEST_SIZE, '\x00'); + sha256(protectedParts.constData(), protectedParts.length(), digest.data()); + + if (digest != bgProtectedRanges[i].Hash) { + msg(usprintf("checkProtectedRanges: Phoenix protected range [%Xh:%Xh] hash mismatch, opened image may refuse to boot", + bgProtectedRanges[i].Offset, bgProtectedRanges[i].Offset + bgProtectedRanges[i].Size), + model->findByOffset(bgProtectedRanges[i].Offset)); + } + + markProtectedRangeRecursive(index, bgProtectedRanges[i]); + } + } + + return U_SUCCESS; +} + +USTATUS FfsParser::markProtectedRangeRecursive(const UModelIndex & index, const BG_PROTECTED_RANGE & range) +{ + if (!index.isValid()) + return U_SUCCESS; + + // Mark compressed items + UModelIndex parentIndex = model->parent(index); + if (parentIndex.isValid() && model->compressed(index) && model->compressed(parentIndex)) { + model->setMarking(index, model->marking(parentIndex)); + } + // Mark normal items + else { + UINT32 currentOffset = model->offset(index); + UINT32 currentSize = model->header(index).size() + model->body(index).size() + model->tail(index).size(); + + if (std::min(currentOffset + currentSize, range.Offset + range.Size) > std::max(currentOffset, range.Offset)) { + if (range.Offset <= currentOffset && currentOffset + currentSize <= range.Offset + range.Size) { // Mark as fully in range + model->setMarking(index, range.Type == BG_PROTECTED_RANGE_INTEL_BOOT_GUARD ? Qt::red : Qt::cyan); + } + else { // Mark as partially in range + model->setMarking(index, Qt::yellow); + } + } + } + + for (int i = 0; i < model->rowCount(index); i++) { + markProtectedRangeRecursive(index.child(i, 0), range); + } + + return U_SUCCESS; +} + +USTATUS FfsParser::parseVendorHashFile(const UByteArray & fileGuid, const UModelIndex & index) +{ + if (!index.isValid()) + return EFI_INVALID_PARAMETER; + + if (fileGuid == BG_VENDOR_HASH_FILE_GUID_PHOENIX) { + // File too small to have even a signature + if (model->body(index).size() < sizeof(BG_VENDOR_HASH_FILE_SIGNATURE_PHOENIX)) { + msg(UString("parseVendorHashFile: unknown or corrupted Phoenix hash file found"), index); + model->setText(index, UString("Phoenix hash file")); + return U_INVALID_FILE; + } + + const BG_VENDOR_HASH_FILE_HEADER_PHOENIX* header = (const BG_VENDOR_HASH_FILE_HEADER_PHOENIX*)model->body(index).constData(); + if (header->Signature == BG_VENDOR_HASH_FILE_SIGNATURE_PHOENIX) { + if ((UINT32)model->body(index).size() < sizeof(BG_VENDOR_HASH_FILE_HEADER_PHOENIX) || + (UINT32)model->body(index).size() < sizeof(BG_VENDOR_HASH_FILE_HEADER_PHOENIX) + header->NumEntries * sizeof(BG_VENDOR_HASH_FILE_ENTRY)) { + msg(UString("parseVendorHashFile: unknown or corrupted Phoenix hash file found"), index); + model->setText(index, UString("Phoenix hash file")); + return U_INVALID_FILE; + } + + if (header->NumEntries > 0) { + bool protectedRangesFound = false; + for (UINT32 i = 0; i < header->NumEntries; i++) { + protectedRangesFound = true; + const BG_VENDOR_HASH_FILE_ENTRY* entry = (const BG_VENDOR_HASH_FILE_ENTRY*)(header + 1) + i; + BG_PROTECTED_RANGE range; + range.Offset = entry->Offset; + range.Size = entry->Size; + range.Hash = UByteArray((const char*)entry->Hash, sizeof(entry->Hash)); + range.Type = BG_PROTECTED_RANGE_VENDOR_HASH_PHOENIX; + bgProtectedRanges.push_back(range); + } + + if (protectedRangesFound) { + bootGuardInfo += usprintf("Phoenix hash file found at offset %Xh\nProtected ranges:", model->offset(index)); + for (UINT32 i = 0; i < header->NumEntries; i++) { + const BG_VENDOR_HASH_FILE_ENTRY* entry = (const BG_VENDOR_HASH_FILE_ENTRY*)(header + 1) + i; + bootGuardInfo += usprintf("\nRelativeOffset: %08Xh Size: %Xh\nHash: ", entry->Offset, entry->Size); + for (int i = 0; i < sizeof(entry->Hash); i++) { + bootGuardInfo += usprintf("%02X", entry->Hash[i]); + } + } + bootGuardInfo += UString("\n------------------------------------------------------------------------\n\n"); + } + + msg(UString("parseVendorHashFile: Phoenix hash file found"), index); + } + else { + msg(UString("parseVendorHashFile: empty Phoenix hash file found"), index); + } + + model->setText(index, UString("Phoenix hash file")); + } + } + else if (fileGuid == BG_VENDOR_HASH_FILE_GUID_AMI) { + UModelIndex fileIndex = model->parent(index); + UINT32 size = model->body(index).size(); + if (size != model->body(index).count('\xFF')) { + if (size == sizeof(BG_VENDOR_HASH_FILE_HEADER_AMI_NEW)) { + bool protectedRangesFound = false; + UINT32 NumEntries = (UINT32)model->body(index).size() / sizeof(BG_VENDOR_HASH_FILE_ENTRY); + for (UINT32 i = 0; i < NumEntries; i++) { + protectedRangesFound = true; + const BG_VENDOR_HASH_FILE_ENTRY* entry = (const BG_VENDOR_HASH_FILE_ENTRY*)(model->body(index).constData()) + i; + BG_PROTECTED_RANGE range; + range.Offset = entry->Offset; + range.Size = entry->Size; + range.Hash = UByteArray((const char*)entry->Hash, sizeof(entry->Hash)); + range.Type = BG_PROTECTED_RANGE_VENDOR_HASH_AMI_NEW; + bgProtectedRanges.push_back(range); + } + + if (protectedRangesFound) { + bootGuardInfo += usprintf("New AMI hash file found at offset %Xh\nProtected ranges:", model->offset(fileIndex)); + for (UINT32 i = 0; i < NumEntries; i++) { + const BG_VENDOR_HASH_FILE_ENTRY* entry = (const BG_VENDOR_HASH_FILE_ENTRY*)(model->body(index).constData()) + i; + bootGuardInfo += usprintf("\nAddress: %08Xh Size: %Xh\nHash: ", entry->Offset, entry->Size); + for (int i = 0; i < sizeof(entry->Hash); i++) { + bootGuardInfo += usprintf("%02X", entry->Hash[i]); + } + } + bootGuardInfo += UString("\n------------------------------------------------------------------------\n\n"); + } + + msg(UString("parseVendorHashFile: new AMI hash file found"), fileIndex); + } + else if (size == sizeof(BG_VENDOR_HASH_FILE_HEADER_AMI_OLD)) { + bootGuardInfo += usprintf("Old AMI hash file found at offset %Xh\nProtected range:", model->offset(fileIndex)); + const BG_VENDOR_HASH_FILE_HEADER_AMI_OLD* entry = (const BG_VENDOR_HASH_FILE_HEADER_AMI_OLD*)(model->body(index).constData()); + bootGuardInfo += usprintf("\nSize: %Xh\nHash: ", entry->Size); + for (int i = 0; i < sizeof(entry->Hash); i++) { + bootGuardInfo += usprintf("%02X", entry->Hash[i]); + } + bootGuardInfo += UString("\n------------------------------------------------------------------------\n\n"); + + BG_PROTECTED_RANGE range; + range.Offset = 0; + range.Size = entry->Size; + range.Hash = UByteArray((const char*)entry->Hash, sizeof(entry->Hash)); + range.Type = BG_PROTECTED_RANGE_VENDOR_HASH_AMI_OLD; + bgProtectedRanges.push_back(range); + + msg(UString("parseVendorHashFile: old AMI hash file found"), fileIndex); + } + else { + msg(UString("parseVendorHashFile: unknown or corrupted AMI hash file found"), index); + } + } + else { + msg(UString("parseVendorHashFile: empty AMI hash file found"), fileIndex); + } + + model->setText(fileIndex, UString("AMI hash file")); + } + + return U_SUCCESS; +} + +#ifndef U_ENABLE_FIT_PARSING_SUPPORT +USTATUS FfsParser::parseFit(const UModelIndex & index) +{ + U_UNUSED_PARAMETER(index); + return U_SUCCESS; +} + +#else +USTATUS FfsParser::parseFit(const UModelIndex & index) +{ + // Check sanity + if (!index.isValid()) + return EFI_INVALID_PARAMETER; + + // Search for FIT + UModelIndex fitIndex; + UINT32 fitOffset; + USTATUS result = findFitRecursive(index, fitIndex, fitOffset); + if (result) + return result; + + // FIT not found + if (!fitIndex.isValid()) + return U_SUCCESS; + + // Explicitly set the item as fixed + model->setFixed(fitIndex, true); + + // Special case of FIT header + UByteArray fitBody = model->body(fitIndex); + const FIT_ENTRY* fitHeader = (const FIT_ENTRY*)(fitBody.constData() + fitOffset); + + // Check FIT checksum, if present + UINT32 fitSize = fitHeader->Size * sizeof(FIT_ENTRY); + if (fitHeader->CsFlag) { + // Calculate FIT entry checksum + UByteArray tempFIT = model->body(fitIndex).mid(fitOffset, fitSize); + FIT_ENTRY* tempFitHeader = (FIT_ENTRY*)tempFIT.data(); + tempFitHeader->CsFlag = 0; + tempFitHeader->Checksum = 0; + UINT8 calculated = calculateChecksum8((const UINT8*)tempFitHeader, fitSize); + if (calculated != fitHeader->Checksum) { + msg(usprintf("parseFit: invalid FIT table checksum %02Xh, should be %02Xh", fitHeader->Checksum, calculated), fitIndex); + } + } + + // Check fit header type + if (fitHeader->Type != FIT_TYPE_HEADER) { + msg(UString("Invalid FIT header type"), fitIndex); + return U_INVALID_FIT; + } + + // Add FIT header + std::vector currentStrings; + currentStrings.push_back(UString("_FIT_ ")); + currentStrings.push_back(usprintf("%08Xh", fitSize)); + currentStrings.push_back(usprintf("%04Xh", fitHeader->Version)); + currentStrings.push_back(usprintf("%02Xh", fitHeader->Checksum)); + currentStrings.push_back(fitEntryTypeToUString(fitHeader->Type)); + currentStrings.push_back(UString("")); // Empty info for FIT header + fitTable.push_back(std::pair, UModelIndex>(currentStrings, fitIndex)); + + // Process all other entries + UModelIndex acmIndex; + UModelIndex kmIndex; + UModelIndex bpIndex; + for (UINT32 i = 1; i < fitHeader->Size; i++) { + currentStrings.clear(); + UString info; + UModelIndex itemIndex; + const FIT_ENTRY* currentEntry = fitHeader + i; + UINT32 currentEntrySize = currentEntry->Size; + UINT32 currentEntryOffset; + + // Check sanity + if (currentEntry->Type == FIT_TYPE_HEADER) { + msg(UString("parseFit: second FIT header found, the table is damaged"), fitIndex); + return U_INVALID_FIT; + } + + // Set item index + if (currentEntry->Address > addressDiff && currentEntry->Address < 0xFFFFFFFFUL) { // Only elements in the image need to be parsed + currentEntryOffset = (UINT32)(currentEntry->Address - addressDiff); + itemIndex = model->findByOffset(currentEntryOffset); + if (itemIndex.isValid()) { + USTATUS status = U_INVALID_FIT; + UByteArray item = model->header(itemIndex) + model->body(itemIndex) + model->tail(itemIndex); + UINT32 localOffset = currentEntryOffset - model->offset(itemIndex); + + switch (currentEntry->Type) { + case FIT_TYPE_MICROCODE: + status = parseIntelMicrocode(item, localOffset, itemIndex, info, currentEntrySize); + break; + + case FIT_TYPE_BIOS_AC_MODULE: + status = parseIntelAcm(item, localOffset, itemIndex, info, currentEntrySize); + acmIndex = itemIndex; + break; + + case FIT_TYPE_AC_KEY_MANIFEST: + status = parseIntelBootGuardKeyManifest(item, localOffset, itemIndex, info, currentEntrySize); + kmIndex = itemIndex; + break; + + case FIT_TYPE_AC_BOOT_POLICY: + status = parseIntelBootGuardBootPolicy(item, localOffset, itemIndex, info, currentEntrySize); + bpIndex = itemIndex; + break; + + default: + // Do nothing + status = U_SUCCESS; + break; + } + + if (status != U_SUCCESS) + itemIndex = UModelIndex(); + } + else + msg(usprintf("parseFit: FIT entry #%d not found in the image", i), fitIndex); + } + + // Add entry to fitTable + currentStrings.push_back(usprintf("%016" PRIX64, currentEntry->Address)); + currentStrings.push_back(usprintf("%08Xh", currentEntrySize, currentEntrySize)); + currentStrings.push_back(usprintf("%04Xh", currentEntry->Version)); + currentStrings.push_back(usprintf("%02Xh", currentEntry->Checksum)); + currentStrings.push_back(fitEntryTypeToUString(currentEntry->Type)); + currentStrings.push_back(info); + fitTable.push_back(std::pair, UModelIndex>(currentStrings, itemIndex)); + } + + // Perform validation of BootGuard stuff + if (bgAcmFound) { + if (!bgKeyManifestFound) { + msg(usprintf("parseBootGuardData: ACM found, but KeyManifest isn't"), acmIndex); + } + else if (!bgBootPolicyFound) { + msg(usprintf("parseBootGuardData: ACM and KeyManifest found, BootPolicy isn't"), kmIndex); + } + else { + // Check key hashes + if (!bgKmHash.isEmpty() && bgBpHash.isEmpty() && bgKmHash != bgBpHash) { + msg(usprintf("parseBootGuardData: BootPolicy key hash stored in KeyManifest differs from the hash of public key stored in BootPolicy"), bpIndex); + return U_SUCCESS; + } + } + } + + return U_SUCCESS; +} + +USTATUS FfsParser::findFitRecursive(const UModelIndex & index, UModelIndex & found, UINT32 & fitOffset) +{ + // Sanity check + if (!index.isValid()) + return U_SUCCESS; + + // Process child items + for (int i = 0; i < model->rowCount(index); i++) { + findFitRecursive(index.child(i, 0), found, fitOffset); + if (found.isValid()) + return U_SUCCESS; + } + + // Check for all FIT signatures in item's body + UByteArray lastVtfBody = model->body(lastVtf); + UINT32 storedFitAddress = *(const UINT32*)(lastVtfBody.constData() + lastVtfBody.size() - FIT_POINTER_OFFSET); + for (INT32 offset = model->body(index).indexOf(FIT_SIGNATURE); + offset >= 0; + offset = model->body(index).indexOf(FIT_SIGNATURE, offset + 1)) { + // FIT candidate found, calculate it's physical address + UINT32 fitAddress = model->offset(index) + addressDiff + model->header(index).size() + (UINT32)offset; + + // Check FIT address to be stored in the last VTF + if (fitAddress == storedFitAddress) { + found = index; + fitOffset = offset; + msg(usprintf("findFitRecursive: real FIT table found at physical address %08Xh", fitAddress), found); + return U_SUCCESS; + } + else if (model->rowCount(index) == 0) // Show messages only to leaf items + msg(UString("findFitRecursive: FIT table candidate found, but not referenced from the last VTF"), index); + } + + return U_SUCCESS; +} + +USTATUS FfsParser::parseIntelMicrocode(const UByteArray & microcode, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize) +{ + U_UNUSED_PARAMETER(parent); + if (localOffset + sizeof(INTEL_MICROCODE_HEADER) <= (UINT32)microcode.size()) { + const INTEL_MICROCODE_HEADER* header = (const INTEL_MICROCODE_HEADER*)(microcode.constData() + localOffset); + if (header->Version == INTEL_MICROCODE_HEADER_VERSION) { + bool reservedBytesValid = true; + for (UINT8 i = 0; i < sizeof(header->Reserved); i++) + if (header->Reserved[i] != INTEL_MICROCODE_HEADER_RESERVED_BYTE) { + reservedBytesValid = false; + break; + } + if (reservedBytesValid) { + UINT32 mcSize = header->TotalSize; + if (localOffset + mcSize <= (UINT32)microcode.size()) { + // Valid microcode found + info = usprintf("LocalOffset %08Xh, CPUID %08Xh, Revision %08Xh, Date %02X.%02X.%04X", + localOffset, + header->CpuSignature, + header->Revision, + header->DateDay, + header->DateMonth, + header->DateYear + ); + realSize = mcSize; + return U_SUCCESS; + } + } + } + } + + return U_INVALID_MICROCODE; +} + +USTATUS FfsParser::parseIntelAcm(const UByteArray & acm, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize) +{ + if (localOffset + sizeof(INTEL_ACM_HEADER) <= (UINT32)acm.size()) { + const INTEL_ACM_HEADER* header = (const INTEL_ACM_HEADER*)(acm.constData() + localOffset); + if (header->ModuleType == INTEL_ACM_MODULE_TYPE && header->ModuleVendor == INTEL_ACM_MODULE_VENDOR) { + UINT32 acmSize = header->ModuleSize * sizeof(UINT32); + if (localOffset + acmSize <= (UINT32)acm.size()) { + // Valid ACM found + info = usprintf("LocalOffset %08Xh, EntryPoint %08Xh, ACM SVN %04Xh, Date %02X.%02X.%04X", + localOffset, + header->EntryPoint, + header->AcmSvn, + header->DateDay, + header->DateMonth, + header->DateYear + ); + realSize = acmSize; + + // Add ACM header info + bootGuardInfo += usprintf( + "Intel ACM found at offset %Xh\n" + "ModuleType: %08Xh HeaderLength: %08Xh HeaderVersion: %08Xh\n" + "ChipsetId: %04Xh Unknown: %04Xh ModuleVendor: %04Xh\n" + "Date: %02X.%02X.%04X ModuleSize: %08Xh EntryPoint: %08Xh\n" + "AcmSvn: %04Xh Unknown1: %08Xh Unknown2: %08Xh\n" + "GdtBase: %08Xh GdtMax: %08Xh SegSel: %08Xh\n" + "KeySize: %08Xh Unknown3: %08Xh", + model->offset(parent) + localOffset, + header->ModuleType, + header->ModuleSize * sizeof(UINT32), + header->HeaderVersion, + header->ChipsetId, + header->Unknown, + header->ModuleVendor, + header->DateDay, header->DateMonth, header->DateYear, + header->ModuleSize * sizeof(UINT32), + header->EntryPoint, + header->AcmSvn, + header->Unknown1, + header->Unknown2, + header->GdtBase, + header->GdtMax, + header->SegmentSel, + header->KeySize * sizeof(UINT32), + header->Unknown4 * sizeof(UINT32) + ); + // Add PubKey + bootGuardInfo += usprintf("\n\nACM RSA Public Key (Exponent: %Xh):", header->RsaPubExp); + for (int i = 0; i < sizeof(header->RsaPubKey); i++) { + if (i % 32 == 0) + bootGuardInfo += UString("\n"); + bootGuardInfo += usprintf("%02X", header->RsaPubKey[i]); + } + // Add RsaSig + bootGuardInfo += UString("\n\nACM RSA Signature:"); + for (int i = 0; i < sizeof(header->RsaSig); i++) { + if (i % 32 == 0) + bootGuardInfo += UString("\n"); + bootGuardInfo += usprintf("%02X", header->RsaSig[i]); + } + bootGuardInfo += UString("\n------------------------------------------------------------------------\n\n"); + bgAcmFound = true; + return U_SUCCESS; + } + } + } + + return U_INVALID_ACM; +} + +USTATUS FfsParser::parseIntelBootGuardKeyManifest(const UByteArray & keyManifest, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize) +{ + U_UNUSED_PARAMETER(parent); + U_UNUSED_PARAMETER(realSize); + if (localOffset + sizeof(BG_KEY_MANIFEST) <= (UINT32)keyManifest.size()) { + const BG_KEY_MANIFEST* header = (const BG_KEY_MANIFEST*)(keyManifest.constData() + localOffset); + if (header->Tag == BG_KEY_MANIFEST_TAG) { + UINT32 kmSize = sizeof(BG_KEY_MANIFEST); + if (localOffset + kmSize <= (UINT32)keyManifest.size()) { + // Valid KM found + info = usprintf("LocalOffset %08Xh, KM Version %02Xh, KM SVN: %02Xh, KM ID %02Xh", + localOffset, + header->KmVersion, + header->KmSvn, + header->KmId + ); + + // Add KM header info + bootGuardInfo += usprintf( + "Intel BootGuard Key manifest found at offset %Xh\n" + "Tag: __KEYM__ Version: %02Xh KmVersion: %02Xh KmSvn: %02Xh KmId: %02Xh", + model->offset(parent) + localOffset, + header->Version, + header->KmVersion, + header->KmSvn, + header->KmId + ); + // Add BpKeyHash + bootGuardInfo += UString("\n\nBoot Policy RSA Public Key Hash:\n"); + for (int i = 0; i < sizeof(header->BpKeyHash.HashBuffer); i++) { + bootGuardInfo += usprintf("%02X", header->BpKeyHash.HashBuffer[i]); + } + bgKmHash = UByteArray((const char*)header->BpKeyHash.HashBuffer, sizeof(header->BpKeyHash.HashBuffer)); + + // Add Key Manifest PubKey + bootGuardInfo += usprintf("\n\nKey Manifest RSA Public Key (Exponent: %Xh):", + header->KeyManifestSignature.PubKey.Exponent); + for (int i = 0; i < sizeof(header->KeyManifestSignature.PubKey.Modulus); i++) { + if (i % 32 == 0) + bootGuardInfo += UString("\n"); + bootGuardInfo += usprintf("%02X", header->KeyManifestSignature.PubKey.Modulus[i]); + } + // Add Key Manifest Signature + bootGuardInfo += UString("\n\nKey Manifest RSA Signature:"); + for (int i = 0; i < sizeof(header->KeyManifestSignature.Signature.Signature); i++) { + if (i % 32 == 0) + bootGuardInfo += UString("\n"); + bootGuardInfo += usprintf("%02X", header->KeyManifestSignature.Signature.Signature[i]); + } + bootGuardInfo += UString("\n------------------------------------------------------------------------\n\n"); + bgKeyManifestFound = true; + return U_SUCCESS; + } + } + } + + return U_INVALID_BG_KEY_MANIFEST; +} + +USTATUS FfsParser::findNextElement(const UByteArray & bootPolicy, const UINT32 localOffset, const UINT32 elementOffset, UINT32 & nextElementOffset, UINT32 & nextElementSize) +{ + UINT32 dataSize = bootPolicy.size(); + + if (dataSize < sizeof(UINT64)) + return U_ELEMENTS_NOT_FOUND; + + UINT32 offset = elementOffset; + for (; offset < dataSize - sizeof(UINT64); offset++) { + const UINT64* currentPos = (const UINT64*)(bootPolicy.constData() + offset); + if (*currentPos == BG_BOOT_POLICY_MANIFEST_IBB_ELEMENT_TAG && offset + sizeof(BG_IBB_ELEMENT) < dataSize) { + const BG_IBB_ELEMENT* header = (const BG_IBB_ELEMENT*)currentPos; + // Check that all segments are present + if (offset + sizeof(BG_IBB_ELEMENT) + sizeof(BG_IBB_SEGMENT_ELEMENT) * header->IbbSegCount < dataSize) { + nextElementOffset = offset; + nextElementSize = sizeof(BG_IBB_ELEMENT) + sizeof(BG_IBB_SEGMENT_ELEMENT) * header->IbbSegCount; + return U_SUCCESS; + } + } + else if (*currentPos == BG_BOOT_POLICY_MANIFEST_PLATFORM_MANUFACTURER_ELEMENT_TAG && offset + sizeof(BG_PLATFORM_MANUFACTURER_ELEMENT) < dataSize) { + const BG_PLATFORM_MANUFACTURER_ELEMENT* header = (const BG_PLATFORM_MANUFACTURER_ELEMENT*)(bootPolicy.constData() + localOffset + elementOffset); + // Check that data is present + if (offset + sizeof(BG_PLATFORM_MANUFACTURER_ELEMENT) + header->DataSize < dataSize) { + nextElementOffset = offset; + nextElementSize = sizeof(BG_PLATFORM_MANUFACTURER_ELEMENT) + header->DataSize; + return U_SUCCESS; + } + } + else if (*currentPos == BG_BOOT_POLICY_MANIFEST_SIGNATURE_ELEMENT_TAG && offset + sizeof(BG_BOOT_POLICY_MANIFEST_SIGNATURE_ELEMENT) < dataSize) { + nextElementOffset = offset; + nextElementSize = sizeof(BG_BOOT_POLICY_MANIFEST_SIGNATURE_ELEMENT); + return U_SUCCESS; + } + } + + return U_ELEMENTS_NOT_FOUND; +} + +USTATUS FfsParser::parseIntelBootGuardBootPolicy(const UByteArray & bootPolicy, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize) +{ + U_UNUSED_PARAMETER(realSize); + if (localOffset + sizeof(BG_BOOT_POLICY_MANIFEST_HEADER) <= (UINT32)bootPolicy.size()) { + const BG_BOOT_POLICY_MANIFEST_HEADER* header = (const BG_BOOT_POLICY_MANIFEST_HEADER*)(bootPolicy.constData() + localOffset); + if (header->Tag == BG_BOOT_POLICY_MANIFEST_HEADER_TAG) { + UINT32 bmSize = sizeof(BG_BOOT_POLICY_MANIFEST_HEADER); + if (localOffset + bmSize <= (UINT32)bootPolicy.size()) { + // Valid BPM found + info = usprintf("LocalOffset %08Xh, BP SVN %02Xh, ACM SVN %02Xh", + localOffset, + header->BPSVN, + header->ACMSVN + ); + + // Add BP header info + bootGuardInfo += usprintf( + "Intel BootGuard Boot Policy Manifest found at offset %Xh\n" + "Tag: __ACBP__ Version: %02Xh HeaderVersion: %02Xh\n" + "PMBPMVersion: %02Xh PBSVN: %02Xh ACMSVN: %02Xh NEMDataStack: %04Xh\n", + model->offset(parent) + localOffset, + header->Version, + header->HeaderVersion, + header->PMBPMVersion, + header->BPSVN, + header->ACMSVN, + header->NEMDataSize + ); + + // Iterate over elements to get them all + UINT32 elementOffset = 0; + UINT32 elementSize = 0; + USTATUS status = findNextElement(bootPolicy, localOffset, localOffset + sizeof(BG_BOOT_POLICY_MANIFEST_HEADER), elementOffset, elementSize); + while (status == U_SUCCESS) { + const UINT64* currentPos = (const UINT64*)(bootPolicy.constData() + elementOffset); + if (*currentPos == BG_BOOT_POLICY_MANIFEST_IBB_ELEMENT_TAG) { + const BG_IBB_ELEMENT* elementHeader = (const BG_IBB_ELEMENT*)currentPos; + // Valid IBB element found + bootGuardInfo += usprintf( + "\nInitial Boot Block Element found at offset %Xh\n" + "Tag: __IBBS__ Version: %02Xh Unknown: %02Xh\n" + "Flags: %08Xh IbbMchBar: %08Xh VtdBar: %08Xh\n" + "Unknown1: %08Xh Unknown2: %08Xh EntryPoint: %08Xh", + model->offset(parent) + localOffset + elementOffset, + elementHeader->Version, + elementHeader->Unknown, + elementHeader->Flags, + elementHeader->IbbMchBar, + elementHeader->VtdBar, + elementHeader->Unknown1, + elementHeader->Unknown2, + elementHeader->EntryPoint + ); + // Add PostIbbHash + bootGuardInfo += UString("\n\nPost IBB Hash:\n"); + for (int i = 0; i < sizeof(elementHeader->IbbHash.HashBuffer); i++) { + bootGuardInfo += usprintf("%02X", elementHeader->IbbHash.HashBuffer[i]); + } + // Add Digest + bgBpDigest = UByteArray((const char*)elementHeader->Digest.HashBuffer, sizeof(elementHeader->Digest.HashBuffer)); + bootGuardInfo += UString("\n\nIBB Digest:\n"); + for (int i = 0; i < bgBpDigest.size(); i++) { + bootGuardInfo += usprintf("%02X", (UINT8)bgBpDigest.at(i)); + } + + // Add all IBB segments + bootGuardInfo += UString("\n\nIBB Segments:\n"); + const BG_IBB_SEGMENT_ELEMENT* segments = (const BG_IBB_SEGMENT_ELEMENT*)(elementHeader + 1); + for (int i = 0; i < elementHeader->IbbSegCount; i++) { + bootGuardInfo += usprintf("Flags: %04Xh Address: %08Xh Size: %08Xh\n", + segments[i].Flags, segments[i].Base, segments[i].Size); + if (segments[i].Flags == BG_IBB_SEGMENT_FLAG_IBB) { + BG_PROTECTED_RANGE range; + range.Offset = segments[i].Base - addressDiff; + range.Size = segments[i].Size; + range.Type = BG_PROTECTED_RANGE_INTEL_BOOT_GUARD; + bgProtectedRanges.push_back(range); + } + } + } + else if (*currentPos == BG_BOOT_POLICY_MANIFEST_PLATFORM_MANUFACTURER_ELEMENT_TAG) { + const BG_PLATFORM_MANUFACTURER_ELEMENT* elementHeader = (const BG_PLATFORM_MANUFACTURER_ELEMENT*)currentPos; + bootGuardInfo += usprintf( + "\nPlatform Manufacturer Data Element found at offset %Xh\n" + "Tag: __PMDA__ Version: %02Xh DataSize: %02Xh\n", + model->offset(parent) + localOffset + elementOffset, + elementHeader->Version, + elementHeader->DataSize + ); + // Add data + UINT8* data = (UINT8*)(elementHeader + 1); + for (int i = 0; i < elementHeader->DataSize; i++) { + if (i % 32 == 0) + bootGuardInfo += UString("\n"); + bootGuardInfo += usprintf("%02X", data[i]); + } + } + else if (*currentPos == BG_BOOT_POLICY_MANIFEST_SIGNATURE_ELEMENT_TAG) { + const BG_BOOT_POLICY_MANIFEST_SIGNATURE_ELEMENT* elementHeader = (const BG_BOOT_POLICY_MANIFEST_SIGNATURE_ELEMENT*)currentPos; + bootGuardInfo += usprintf( + "\nBoot Policy Signature Element found at offset %Xh\n" + "Tag: __PMSG__ Version: %02Xh", + model->offset(parent) + localOffset + elementOffset, + elementHeader->Version + ); + // Add PubKey + bootGuardInfo += usprintf("\n\nBoot Policy RSA Public Key (Exponent: %Xh):", elementHeader->KeySignature.PubKey.Exponent); + for (int i = 0; i < sizeof(elementHeader->KeySignature.PubKey.Modulus); i++) { + if (i % 32 == 0) + bootGuardInfo += UString("\n"); + bootGuardInfo += usprintf("%02X", elementHeader->KeySignature.PubKey.Modulus[i]); + } + + // Calculate and add PubKey hash + UINT8 hash[SHA256_DIGEST_SIZE]; + sha256(&elementHeader->KeySignature.PubKey.Modulus, sizeof(elementHeader->KeySignature.PubKey.Modulus), hash); + bootGuardInfo += UString("\n\nBoot Policy RSA Public Key Hash:"); + for (int i = 0; i < sizeof(hash); i++) { + if (i % 32 == 0) + bootGuardInfo += UString("\n"); + bootGuardInfo += usprintf("%02X", hash[i]); + } + bgBpHash = UByteArray((const char*)hash, sizeof(hash)); + + // Add Signature + bootGuardInfo += UString("\n\nBoot Policy RSA Signature:"); + for (int i = 0; i < sizeof(elementHeader->KeySignature.Signature.Signature); i++) { + if (i % 32 == 0) + bootGuardInfo += UString("\n"); + bootGuardInfo += usprintf("%02X", elementHeader->KeySignature.Signature.Signature[i]); + } + } + status = findNextElement(bootPolicy, localOffset, elementOffset + elementSize, elementOffset, elementSize); + } + + bootGuardInfo += UString("\n------------------------------------------------------------------------\n\n"); + bgBootPolicyFound = true; + return U_SUCCESS; + } + } + } + + return U_INVALID_BG_BOOT_POLICY; +} + +#endif diff --git a/common/ffsparser.h b/common/ffsparser.h index 340406e..007bed6 100644 --- a/common/ffsparser.h +++ b/common/ffsparser.h @@ -21,12 +21,28 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "treemodel.h" #include "nvramparser.h" #include "meparser.h" +#include "bootguard.h" + +typedef struct BG_PROTECTED_RANGE_ +{ + UINT32 Offset; + UINT32 Size; + UINT8 Type; + UByteArray Hash; +} BG_PROTECTED_RANGE; + +#define BG_PROTECTED_RANGE_INTEL_BOOT_GUARD 0x01 +#define BG_PROTECTED_RANGE_VENDOR_HASH_PHOENIX 0x02 +#define BG_PROTECTED_RANGE_VENDOR_HASH_AMI_OLD 0x03 +#define BG_PROTECTED_RANGE_VENDOR_HASH_AMI_NEW 0x04 class FfsParser { public: // Default constructor and destructor - FfsParser(TreeModel* treeModel) : model(treeModel), nvramParser(treeModel), meParser(treeModel), capsuleOffsetFixup(0), addressDiff(0x100000000ULL) {} + FfsParser(TreeModel* treeModel) : model(treeModel), nvramParser(treeModel), meParser(treeModel), + capsuleOffsetFixup(0), addressDiff(0x100000000ULL), + bgAcmFound(false), bgKeyManifestFound(false), bgBootPolicyFound(false), bgFirstVolumeOffset(0x100000000ULL) {} ~FfsParser() {} // Obtain parser messages @@ -48,6 +64,9 @@ public: // Obtain parsed FIT table std::vector, UModelIndex> > getFitTable() const { return fitTable; } + // Obtain BootGuardInfo + UString getBootGuardInfo() const { return bootGuardInfo; } + // Obtain offset/address difference UINT64 getAddressDiff() { return addressDiff; } @@ -61,10 +80,22 @@ private: NvramParser nvramParser; MeParser meParser; + UByteArray openedImage; UModelIndex lastVtf; UINT32 capsuleOffsetFixup; UINT64 addressDiff; std::vector, UModelIndex> > fitTable; + + UString bootGuardInfo; + bool bgAcmFound; + bool bgKeyManifestFound; + bool bgBootPolicyFound; + UByteArray bgKmHash; + UByteArray bgBpHash; + UByteArray bgBpDigest; + std::vector bgProtectedRanges; + UINT64 bgFirstVolumeOffset; + UModelIndex bgDxeCoreIndex; // First pass USTATUS performFirstPass(const UByteArray & imageFile, UModelIndex & index); @@ -115,8 +146,22 @@ private: USTATUS addOffsetsRecursive(const UModelIndex & index); USTATUS addMemoryAddressesRecursive(const UModelIndex & index); USTATUS addFixedAndCompressedRecursive(const UModelIndex & index); + USTATUS checkProtectedRanges(const UModelIndex & index); + USTATUS markProtectedRangeRecursive(const UModelIndex & index, const BG_PROTECTED_RANGE & range); + USTATUS parseFit(const UModelIndex & index); + USTATUS parseVendorHashFile(const UByteArray & fileGuid, const UModelIndex & index); + +#ifdef U_ENABLE_FIT_PARSING_SUPPORT USTATUS findFitRecursive(const UModelIndex & index, UModelIndex & found, UINT32 & fitOffset); + + // FIT entries + USTATUS parseIntelMicrocode(const UByteArray & microcode, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize); + USTATUS parseIntelAcm(const UByteArray & acm, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize); + USTATUS parseIntelBootGuardKeyManifest(const UByteArray & keyManifest, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize); + USTATUS parseIntelBootGuardBootPolicy(const UByteArray & bootPolicy, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize); + USTATUS findNextElement(const UByteArray & bootPolicy, const UINT32 localOffset, const UINT32 elementOffset, UINT32 & nextElementOffset, UINT32 & nextElementSize); +#endif }; #endif // FFSPARSER_H diff --git a/common/fit.h b/common/fit.h index 81d73de..564296a 100644 --- a/common/fit.h +++ b/common/fit.h @@ -42,16 +42,20 @@ const UByteArray FIT_SIGNATURE typedef struct FIT_ENTRY_ { UINT64 Address; - UINT32 Size; + UINT32 Size : 24; + UINT32 : 8; UINT16 Version; - UINT8 Type; + UINT8 Type : 7; + UINT8 CsFlag : 1; UINT8 Checksum; } FIT_ENTRY; typedef struct INTEL_MICROCODE_HEADER_ { UINT32 Version; UINT32 Revision; - UINT32 Date; + UINT16 DateYear; + UINT8 DateDay; + UINT8 DateMonth; UINT32 CpuSignature; UINT32 Checksum; UINT32 LoaderRevision; diff --git a/common/gbe.h b/common/gbe.h index d11230d..0130d83 100644 --- a/common/gbe.h +++ b/common/gbe.h @@ -26,7 +26,7 @@ typedef struct GBE_MAC_ADDRESS_ { #define GBE_VERSION_OFFSET 10 typedef struct GBE_VERSION_ { - UINT8 id : 4; + UINT8 id : 4; UINT8 minor : 4; UINT8 major; } GBE_VERSION; diff --git a/common/me.h b/common/me.h index 60b8245..dbcca81 100644 --- a/common/me.h +++ b/common/me.h @@ -23,7 +23,7 @@ const UByteArray ME_VERSION_SIGNATURE2("\x24\x4D\x4E\x32", 4); //$MN2 typedef struct ME_VERSION_ { UINT32 signature; - UINT32 reserved; // Unknown for me + UINT32 reserved; UINT16 major; UINT16 minor; UINT16 bugfix; diff --git a/common/nvram.h b/common/nvram.h index 8845c65..4cfb7c5 100644 --- a/common/nvram.h +++ b/common/nvram.h @@ -106,18 +106,28 @@ typedef struct VSS_VARIABLE_STORE_HEADER_ { typedef struct VSS_VARIABLE_HEADER_ { UINT16 StartId; // Variable start marker AA55 UINT8 State; // Variable state - UINT8 : 8; + UINT8 Reserved; UINT32 Attributes; // Variable attributes UINT32 NameSize; // Size of variable name, stored as null-terminated UCS2 string UINT32 DataSize; // Size of variable data without header and name EFI_GUID VendorGuid; // Variable vendor GUID } VSS_VARIABLE_HEADER; +// Intel variable header +typedef struct VSS_INTEL_VARIABLE_HEADER_ { + UINT16 StartId; // Variable start marker AA55 + UINT8 State; // Variable state + UINT8 Reserved; + UINT32 Attributes; // Variable attributes + UINT32 TotalSize; // Size of variable including header + EFI_GUID VendorGuid; // Variable vendor GUID +} VSS_INTEL_VARIABLE_HEADER; + // Apple variation of normal variable header, with one new field typedef struct VSS_APPLE_VARIABLE_HEADER_ { UINT16 StartId; // Variable start marker AA55 UINT8 State; // Variable state - UINT8 : 8; + UINT8 Reserved; UINT32 Attributes; // Variable attributes UINT32 NameSize; // Size of variable name, stored as null-terminated UCS2 string UINT32 DataSize; // Size of variable data without header and name @@ -129,7 +139,7 @@ typedef struct VSS_APPLE_VARIABLE_HEADER_ { typedef struct VSS_AUTH_VARIABLE_HEADER_ { UINT16 StartId; // Variable start marker AA55 UINT8 State; // Variable state - UINT8 : 8; + UINT8 Reserved; UINT32 Attributes; // Variable attributes UINT64 MonotonicCounter; // Monotonic counter against replay attack EFI_TIME Timestamp; // Time stamp against replay attack @@ -144,7 +154,8 @@ typedef struct VSS_AUTH_VARIABLE_HEADER_ { #define NVRAM_VSS_VARIABLE_DELETED 0xfd // Variable is obsolete #define NVRAM_VSS_VARIABLE_HEADER_VALID 0x7f // Variable has valid header #define NVRAM_VSS_VARIABLE_ADDED 0x3f // Variable has been completely added -#define NVRAM_VSS_IS_VARIABLE_STATE(_c, _Mask) (BOOLEAN) (((~_c) & (~_Mask)) != 0) +#define NVRAM_VSS_INTEL_VARIABLE_VALID 0xfc // Intel special variable valid +#define NVRAM_VSS_INTEL_VARIABLE_INVALID 0xf8 // Intel special variable invalid // VSS variable attributes #define NVRAM_VSS_VARIABLE_NON_VOLATILE 0x00000001 @@ -175,11 +186,11 @@ const UByteArray LENOVO_VSS_STORE_GUID // Variable store header typedef struct LENOVO_VSS_VARIABLE_STORE_HEADER_ { EFI_GUID Signature; - UINT32 Size; // Size of variable store, including store header - UINT8 Format; // Store format state - UINT8 State; // Store health state - UINT16 Unknown; - UINT32 : 32; + UINT32 Size; // Size of variable store, including store header + UINT8 Format; // Store format state + UINT8 State; // Store health state + UINT16 Unknown; + UINT32 : 32; } LENOVO_VSS_VARIABLE_STORE_HEADER; // VSS entries are 4-bytes aligned in Lenovo stores @@ -212,7 +223,7 @@ const UByteArray EDKII_WORKING_BLOCK_SIGNATURE_GUID const UByteArray LENOVO_WORKING_BLOCK_SIGNATURE_GUID ("\x2B\x29\x58\x9E\x68\x7C\x7D\x49\xA0\xCE\x65\x00\xFD\x9F\x1B\x95", 16); -#define NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 0xFFF12B8D +#define NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 0xFFF12B8D #define EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1 0x9E58292B typedef struct EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32_ { @@ -328,7 +339,7 @@ extern UString evsaAttributesToUString(const UINT32 attributes); // Phoenix SCT Flash Map // -#define NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1 0x414C465F +#define NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1 0x414C465F #define NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_LENGTH 10 // _FLASH_MAP diff --git a/common/nvramparser.cpp b/common/nvramparser.cpp index 05db593..d71b790 100644 --- a/common/nvramparser.cpp +++ b/common/nvramparser.cpp @@ -1250,11 +1250,13 @@ USTATUS NvramParser::parseIntelMicrocodeHeader(const UByteArray & store, const U // Add info UString name("Intel microcode"); UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\n" - "Date: %08Xh\nCPU signature: %08Xh\nRevision: %08Xh\nChecksum: %08Xh\nLoader revision: %08Xh\nCPU flags: %08Xh", + "Date: %02X.%02X.%04x\nCPU signature: %08Xh\nRevision: %08Xh\nChecksum: %08Xh\nLoader revision: %08Xh\nCPU flags: %08Xh", ucodeHeader->TotalSize, ucodeHeader->TotalSize, header.size(), header.size(), body.size(), body.size(), - ucodeHeader->Date, + ucodeHeader->DateDay, + ucodeHeader->DateMonth, + ucodeHeader->DateYear, ucodeHeader->CpuSignature, ucodeHeader->Revision, ucodeHeader->Checksum, @@ -1352,6 +1354,7 @@ USTATUS NvramParser::parseVssStoreBody(const UModelIndex & index, UINT8 alignmen bool isInvalid = false; bool isAuthenticated = false; bool isAppleCrc32 = false; + bool isIntelSpecial = false; UINT32 storedCrc32 = 0; UINT32 calculatedCrc32 = 0; @@ -1374,9 +1377,8 @@ USTATUS NvramParser::parseVssStoreBody(const UModelIndex & index, UINT8 alignmen // Check variable header to fit in still unparsed data UINT32 variableSize = 0; - if (unparsedSize >= sizeof(VSS_VARIABLE_HEADER) + if (unparsedSize >= sizeof(VSS_VARIABLE_HEADER) && variableHeader->StartId == NVRAM_VSS_VARIABLE_START_ID) { - // Apple VSS variable with CRC32 of the data if (variableHeader->Attributes & NVRAM_VSS_VARIABLE_APPLE_DATA_CHECKSUM) { isAppleCrc32 = true; @@ -1421,9 +1423,27 @@ USTATUS NvramParser::parseVssStoreBody(const UModelIndex & index, UINT8 alignmen pubKeyIndex = authVariableHeader->PubKeyIndex; } } - + + // Intel special variable + else if (variableHeader->State == NVRAM_VSS_INTEL_VARIABLE_VALID || variableHeader->State == NVRAM_VSS_INTEL_VARIABLE_INVALID) { + isIntelSpecial = true; + const VSS_INTEL_VARIABLE_HEADER* intelVariableHeader = (const VSS_INTEL_VARIABLE_HEADER*)variableHeader; + variableSize = intelVariableHeader->TotalSize; + variableGuid = (EFI_GUID*)&intelVariableHeader->VendorGuid; + variableName = (CHAR16*)(intelVariableHeader + 1); + + UINT32 i = 0; + while (variableName[i] != 0) ++i; + + i = sizeof(VSS_INTEL_VARIABLE_HEADER) + 2 * (i + 1); + i = i < variableSize ? i : variableSize; + + header = data.mid(offset, i); + body = data.mid(offset + header.size(), variableSize - i); + } + // Normal VSS variable - if (!isAuthenticated && !isAppleCrc32) { + if (!isAuthenticated && !isAppleCrc32 && !isIntelSpecial) { variableSize = sizeof(VSS_VARIABLE_HEADER) + variableHeader->NameSize + variableHeader->DataSize; variableGuid = (EFI_GUID*)&variableHeader->VendorGuid; variableName = (CHAR16*)(variableHeader + 1); @@ -1433,7 +1453,7 @@ USTATUS NvramParser::parseVssStoreBody(const UModelIndex & index, UINT8 alignmen } // Check variable state - if (variableHeader->State != NVRAM_VSS_VARIABLE_ADDED && variableHeader->State != NVRAM_VSS_VARIABLE_HEADER_VALID) { + if (variableHeader->State != NVRAM_VSS_INTEL_VARIABLE_VALID && variableHeader->State != NVRAM_VSS_VARIABLE_ADDED && variableHeader->State != NVRAM_VSS_VARIABLE_HEADER_VALID) { isInvalid = true; } @@ -1481,11 +1501,12 @@ USTATUS NvramParser::parseVssStoreBody(const UModelIndex & index, UINT8 alignmen } // Add info - info += usprintf("Full size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nAttributes: %08Xh (", + info += usprintf("Full size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nReserved: %02Xh\nAttributes: %08Xh (", variableSize, variableSize, header.size(), header.size(), body.size(), body.size(), variableHeader->State, + variableHeader->Reserved, variableHeader->Attributes) + vssAttributesToUString(variableHeader->Attributes) + UString(")"); // Set subtype and add related info @@ -1501,6 +1522,9 @@ USTATUS NvramParser::parseVssStoreBody(const UModelIndex & index, UINT8 alignmen info += usprintf("\nData checksum: %08Xh", storedCrc32) + (storedCrc32 != calculatedCrc32 ? usprintf(", invalid, should be %08Xh", calculatedCrc32) : UString(", valid")); } + else if (isIntelSpecial) { + subtype = Subtypes::IntelVssEntry; + } else subtype = Subtypes::StandardVssEntry; diff --git a/common/parsingdata.h b/common/parsingdata.h index 622968e..b7d17b5 100644 --- a/common/parsingdata.h +++ b/common/parsingdata.h @@ -27,8 +27,9 @@ typedef struct VOLUME_PARSING_DATA_ { UINT8 revision; BOOLEAN hasExtendedHeader; BOOLEAN hasAppleCrc32; - BOOLEAN hasAppleFSO; BOOLEAN isWeakAligned; + BOOLEAN hasValidUsedSpace; + UINT32 usedSpace; } VOLUME_PARSING_DATA; typedef struct FILE_PARSING_DATA_ { diff --git a/common/sha256.c b/common/sha256.c new file mode 100644 index 0000000..3850a8a --- /dev/null +++ b/common/sha256.c @@ -0,0 +1,215 @@ +/* sha256.c + +Copyright (c) 2017, LongSoft. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ + +#include "sha256.h" +#include +#include + +struct sha256_state { + uint64_t length; + uint32_t state[8], curlen; + uint8_t buf[SHA256_DIGEST_SIZE*2]; +}; + +void sha256_init(struct sha256_state *md); +int sha256_process(struct sha256_state *md, const unsigned char *in, unsigned long inlen); +int sha256_done(struct sha256_state *md, uint8_t *out); + +#define GET_BE32(a) ((((uint32_t) (a)[0]) << 24) | (((uint32_t) (a)[1]) << 16) | \ + (((uint32_t) (a)[2]) << 8) | ((uint32_t) (a)[3])) + +#define PUT_BE32(a, val) \ + do { \ + (a)[0] = (uint8_t) ((((uint32_t) (val)) >> 24) & 0xff); \ + (a)[1] = (uint8_t) ((((uint32_t) (val)) >> 16) & 0xff); \ + (a)[2] = (uint8_t) ((((uint32_t) (val)) >> 8) & 0xff); \ + (a)[3] = (uint8_t) (((uint32_t) (val)) & 0xff); \ + } while (0) + +#define PUT_BE64(a, val) \ + do { \ + (a)[0] = (uint8_t) (((uint64_t) (val)) >> 56); \ + (a)[1] = (uint8_t) (((uint64_t) (val)) >> 48); \ + (a)[2] = (uint8_t) (((uint64_t) (val)) >> 40); \ + (a)[3] = (uint8_t) (((uint64_t) (val)) >> 32); \ + (a)[4] = (uint8_t) (((uint64_t) (val)) >> 24); \ + (a)[5] = (uint8_t) (((uint64_t) (val)) >> 16); \ + (a)[6] = (uint8_t) (((uint64_t) (val)) >> 8); \ + (a)[7] = (uint8_t) (((uint64_t) (val)) & 0xff); \ + } while (0) + +void sha256(const void *in, unsigned long inlen, void* out) +{ + struct sha256_state ctx; + sha256_init(&ctx); + sha256_process(&ctx, (const unsigned char*)in, inlen); + sha256_done(&ctx, (unsigned char *)out); +} + +/* This is based on SHA256 implementation in LibTomCrypt that was released into + * public domain by Tom St Denis. */ +/* the K array */ +static const unsigned long K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; +/* Various logical functions */ +#define RORc(x, y) \ +( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \ +((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL) +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) RORc((x), (n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif +/* compress 512-bits */ +static int sha256_compress(struct sha256_state *md, unsigned char *buf) +{ + uint32_t S[8], W[64], t0, t1; + uint32_t t; + int i; + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) + W[i] = GET_BE32(buf + (4 * i)); + /* fill W[16..63] */ + for (i = 16; i < 64; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + + W[i - 16]; + } + /* Compress */ +#define RND(a,b,c,d,e,f,g,h,i) \ +t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ +t1 = Sigma0(a) + Maj(a, b, c); \ +d += t0; \ +h = t0 + t1; + for (i = 0; i < 64; ++i) { + RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; + } + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + return 0; +} +/* Initialize the hash state */ +void sha256_init(struct sha256_state *md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = 0x6A09E667UL; + md->state[1] = 0xBB67AE85UL; + md->state[2] = 0x3C6EF372UL; + md->state[3] = 0xA54FF53AUL; + md->state[4] = 0x510E527FUL; + md->state[5] = 0x9B05688CUL; + md->state[6] = 0x1F83D9ABUL; + md->state[7] = 0x5BE0CD19UL; +} +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful + */ +int sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen) +{ + unsigned long n; +#define block_size 64 + if (md->curlen > sizeof(md->buf)) + return -1; + while (inlen > 0) { + if (md->curlen == 0 && inlen >= block_size) { + if (sha256_compress(md, (unsigned char *) in) < 0) + return -1; + md->length += block_size * 8; + in += block_size; + inlen -= block_size; + } else { + n = MIN(inlen, (block_size - md->curlen)); + memcpy(md->buf + md->curlen, in, n); + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == block_size) { + if (sha256_compress(md, md->buf) < 0) + return -1; + md->length += 8 * block_size; + md->curlen = 0; + } + } + } + return 0; +} +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (32 bytes) + @return CRYPT_OK if successful + */ +int sha256_done(struct sha256_state *md, unsigned char *out) +{ + int i; + if (md->curlen >= sizeof(md->buf)) + return -1; + /* increase the length of the message */ + md->length += md->curlen * 8; + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char) 0x80; + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 56) { + while (md->curlen < 64) { + md->buf[md->curlen++] = (unsigned char) 0; + } + sha256_compress(md, md->buf); + md->curlen = 0; + } + /* pad upto 56 bytes of zeroes */ + while (md->curlen < 56) { + md->buf[md->curlen++] = (unsigned char) 0; + } + /* store length */ + PUT_BE64(md->buf + 56, md->length); + sha256_compress(md, md->buf); + /* copy output */ + for (i = 0; i < 8; i++) + PUT_BE32(out + (4 * i), md->state[i]); + return 0; +} diff --git a/common/sha256.h b/common/sha256.h new file mode 100644 index 0000000..7ebe423 --- /dev/null +++ b/common/sha256.h @@ -0,0 +1,27 @@ +/* sha256.h + +Copyright (c) 2017, LongSoft. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ + +#ifndef SHA256_H +#define SHA256_H +#ifdef __cplusplus +extern "C" { +#endif + +#define SHA256_DIGEST_SIZE 32 + +void sha256(const void *in, unsigned long inlen, void* out); + +#ifdef __cplusplus +} +#endif +#endif // SHA256_H diff --git a/common/treeitem.cpp b/common/treeitem.cpp index e18b142..939a6f9 100644 --- a/common/treeitem.cpp +++ b/common/treeitem.cpp @@ -31,6 +31,7 @@ TreeItem::TreeItem(const UINT32 offset, const UINT8 type, const UINT8 subtype, itemTail(tail), itemFixed(fixed), itemCompressed(compressed), + itemMarking(0), parentItem(parent) { } diff --git a/common/treeitem.h b/common/treeitem.h index a18e9b2..880e94a 100644 --- a/common/treeitem.h +++ b/common/treeitem.h @@ -86,12 +86,16 @@ public: bool hasEmptyParsingData() const { return itemParsingData.isEmpty(); } void setParsingData(const UByteArray & pdata) { itemParsingData = pdata; } + UINT8 marking() const { return itemMarking; } + void setMarking(const UINT8 marking) { itemMarking = marking; } + private: std::list childItems; UINT32 itemOffset; UINT8 itemAction; UINT8 itemType; UINT8 itemSubtype; + UINT8 itemMarking; UString itemName; UString itemText; UString itemInfo; diff --git a/common/treemodel.cpp b/common/treemodel.cpp index 1f69c28..251d6a0 100644 --- a/common/treemodel.cpp +++ b/common/treemodel.cpp @@ -21,15 +21,21 @@ QVariant TreeModel::data(const UModelIndex &index, int role) const if (!index.isValid()) return QVariant(); - if (role != Qt::DisplayRole && role != Qt::UserRole) - return QVariant(); - TreeItem *item = static_cast(index.internalPointer()); - if (role == Qt::DisplayRole) + if (role == Qt::DisplayRole) { return (const char*)item->data(index.column()).toLocal8Bit(); - else + } + else if (role == Qt::BackgroundRole) { + if (marking(index) > 0) { + return QBrush((Qt::GlobalColor)marking(index)); + } + } + else if (role == Qt::UserRole) { return (const char*)item->info().toLocal8Bit(); + } + + return QVariant(); } Qt::ItemFlags TreeModel::flags(const UModelIndex &index) const @@ -183,6 +189,14 @@ UINT8 TreeModel::subtype(const UModelIndex &index) const return item->subtype(); } +UINT8 TreeModel::marking(const UModelIndex &index) const +{ + if (!index.isValid()) + return 0; + TreeItem *item = static_cast(index.internalPointer()); + return item->marking(); +} + UByteArray TreeModel::header(const UModelIndex &index) const { if (!index.isValid()) @@ -314,6 +328,17 @@ void TreeModel::setCompressed(const UModelIndex &index, const bool compressed) emit dataChanged(index, index); } +void TreeModel::setMarking(const UModelIndex &index, const UINT8 marking) +{ + if (!index.isValid()) + return; + + TreeItem *item = static_cast(index.internalPointer()); + item->setMarking(marking); + + emit dataChanged(index, index); +} + void TreeModel::setOffset(const UModelIndex &index, const UINT32 offset) { if (!index.isValid()) @@ -479,11 +504,11 @@ UModelIndex TreeModel::addItem(const UINT32 offset, const UINT8 type, const UINT UModelIndex TreeModel::findParentOfType(const UModelIndex& index, UINT8 type) const { - if (!index.isValid()) + if (!index.isValid() || !index.parent().isValid()) return UModelIndex(); TreeItem *item; - UModelIndex parent = index; + UModelIndex parent = index.parent(); for (item = static_cast(parent.internalPointer()); item != NULL && item != rootItem && item->type() != type; @@ -495,6 +520,25 @@ UModelIndex TreeModel::findParentOfType(const UModelIndex& index, UINT8 type) co return UModelIndex(); } +UModelIndex TreeModel::findLastParentOfType(const UModelIndex& index, UINT8 type) const +{ + if (!index.isValid()) + return UModelIndex(); + + UModelIndex lastParentOfType = findParentOfType(index, type); + + if (!lastParentOfType.isValid()) + return UModelIndex(); + + UModelIndex currentParentOfType = findParentOfType(lastParentOfType, type); + while (currentParentOfType.isValid()) { + lastParentOfType = currentParentOfType; + currentParentOfType = findParentOfType(lastParentOfType, type); + } + + return lastParentOfType; +} + UModelIndex TreeModel::findByOffset(UINT32 offset) const { UModelIndex parentIndex = index(0,0); diff --git a/common/treemodel.h b/common/treemodel.h index b76860f..209ca7a 100644 --- a/common/treemodel.h +++ b/common/treemodel.h @@ -25,6 +25,7 @@ enum ItemFixedState { #include #include #include +#include #include "ustring.h" #include "ubytearray.h" @@ -143,6 +144,7 @@ public: void addInfo(const UModelIndex &index, const UString &info, const bool append = TRUE); void setFixed(const UModelIndex &index, const bool fixed); void setCompressed(const UModelIndex &index, const bool compressed); + void setMarking(const UModelIndex &index, const UINT8 marking); UINT32 offset(const UModelIndex &index) const; UINT8 type(const UModelIndex &index) const; @@ -158,6 +160,7 @@ public: bool hasEmptyTail(const UModelIndex &index) const; bool fixed(const UModelIndex &index) const; bool compressed(const UModelIndex &index) const; + UINT8 marking(const UModelIndex &index) const; UINT8 action(const UModelIndex &index) const; @@ -172,6 +175,7 @@ public: const UModelIndex & parent = UModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); UModelIndex findParentOfType(const UModelIndex & index, UINT8 type) const; + UModelIndex findLastParentOfType(const UModelIndex & index, UINT8 type) const; UModelIndex findByOffset(UINT32 offset) const; }; diff --git a/common/types.cpp b/common/types.cpp index 6d2fa39..dee411f 100644 --- a/common/types.cpp +++ b/common/types.cpp @@ -115,6 +115,7 @@ UString itemSubtypeToUString(const UINT8 type, const UINT8 subtype) if (subtype == Subtypes::StandardVssEntry) return UString("Standard"); if (subtype == Subtypes::AppleVssEntry) return UString("Apple"); if (subtype == Subtypes::AuthVssEntry) return UString("Auth"); + if (subtype == Subtypes::IntelVssEntry) return UString("Intel"); break; case Types::FsysEntry: if (subtype == Subtypes::InvalidFsysEntry) return UString("Invalid"); diff --git a/common/types.h b/common/types.h index 5f55ffc..41268af 100644 --- a/common/types.h +++ b/common/types.h @@ -112,7 +112,8 @@ namespace Subtypes { InvalidVssEntry = 140, StandardVssEntry, AppleVssEntry, - AuthVssEntry + AuthVssEntry, + IntelVssEntry }; enum FsysEntrySubtypes { diff --git a/common/utility.cpp b/common/utility.cpp index 90d8710..c3e726c 100644 --- a/common/utility.cpp +++ b/common/utility.cpp @@ -24,7 +24,7 @@ UString uniqueItemName(const UModelIndex & index) { // Sanity check if (!index.isValid()) - return UString("Invalid index"); + return UString("Invalid_index"); // Get model from index const TreeModel* model = (const TreeModel*)index.model(); diff --git a/common/utility.h b/common/utility.h index 6c15b72..3a7994d 100644 --- a/common/utility.h +++ b/common/utility.h @@ -19,7 +19,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "treemodel.h" #include "parsingdata.h" -// Returns unique name string based for tree item +// Returns unique name for tree item UString uniqueItemName(const UModelIndex & index); // Converts error code to UString