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