From 90ff19692d209c1d26af0a77266f990351e60c08 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Mon, 19 Aug 2019 11:36:02 -0700 Subject: [PATCH] Add support for IFWI 1.7 and 2.0 layouts, improve ME parser, fix small issues spotted by static analysis --- UEFIExtract/CMakeLists.txt | 3 +- UEFITool/uefitool_main.cpp | 35 +- common/bstrlib/bstrlib.c | 12 +- common/descriptor.h | 1 + common/ffs.cpp | 142 +++-- common/ffs.h | 218 ++++++- common/ffsparser.cpp | 840 ++++++++++++++++++++++++- common/ffsparser.h | 9 + common/me.h | 70 ++- common/meparser.cpp | 1218 +++++++++++------------------------- common/meparser.h | 209 +------ common/ubytearray.h | 8 +- 12 files changed, 1627 insertions(+), 1138 deletions(-) diff --git a/UEFIExtract/CMakeLists.txt b/UEFIExtract/CMakeLists.txt index fabaec1..5cf1a01 100644 --- a/UEFIExtract/CMakeLists.txt +++ b/UEFIExtract/CMakeLists.txt @@ -12,6 +12,7 @@ SET(PROJECT_SOURCES ../common/ffs.cpp ../common/nvram.cpp ../common/nvramparser.cpp + ../common/meparser.cpp ../common/ffsparser.cpp ../common/ffsreport.cpp ../common/peimage.cpp @@ -83,6 +84,6 @@ SET(PROJECT_HEADERS ../version.h ) -ADD_DEFINITIONS(-DU_ENABLE_NVRAM_PARSING_SUPPORT -DU_ENABLE_FIT_PARSING_SUPPORT -DU_ENABLE_GUID_DATABASE_SUPPORT) +ADD_DEFINITIONS(-DU_ENABLE_NVRAM_PARSING_SUPPORT -DU_ENABLE_ME_PARSING_SUPPORT -DU_ENABLE_FIT_PARSING_SUPPORT -DU_ENABLE_GUID_DATABASE_SUPPORT) ADD_EXECUTABLE(UEFIExtract ${PROJECT_SOURCES} ${PROJECT_HEADERS}) \ No newline at end of file diff --git a/UEFITool/uefitool_main.cpp b/UEFITool/uefitool_main.cpp index 952e77d..7114935 100644 --- a/UEFITool/uefitool_main.cpp +++ b/UEFITool/uefitool_main.cpp @@ -17,35 +17,42 @@ class UEFIToolApplication : public QApplication { - UEFITool tool; + UEFITool* tool; + public: UEFIToolApplication(int &argc, char **argv) : QApplication(argc, argv) { setOrganizationName("LongSoft"); setOrganizationDomain("longsoft.org"); - setApplicationName("UEFITool"); + setApplicationName("UEFITool NE"); + + tool = new UEFITool(); } - - int startup() - { - tool.setProgramPath(arguments().at(0)); - if (arguments().length() > 1) - tool.openImageFile(arguments().at(1)); - tool.show(); - - return exec(); + + virtual ~UEFIToolApplication() { + delete tool; } - - bool event(QEvent *event) + + virtual bool event(QEvent *event) { if (event->type() == QEvent::FileOpen) { QFileOpenEvent *openEvent = static_cast(event); - tool.openImageFile(openEvent->file()); + tool->openImageFile(openEvent->file()); } return QApplication::event(event); } + + int startup() + { + tool->setProgramPath(arguments().at(0)); + if (arguments().length() > 1) + tool->openImageFile(arguments().at(1)); + tool->show(); + + return exec(); + } }; int main(int argc, char *argv[]) diff --git a/common/bstrlib/bstrlib.c b/common/bstrlib/bstrlib.c index 0c051f6..2b729a9 100644 --- a/common/bstrlib/bstrlib.c +++ b/common/bstrlib/bstrlib.c @@ -102,7 +102,7 @@ static int snapUpSize (int i) { /* int balloc (bstring b, int len) * - * Increase the size of the memory backing the bstring b to at least len. + * Increase the size of the memory backing the bstring b to at least olen + 1. */ int balloc (bstring b, int olen) { int len; @@ -124,14 +124,14 @@ int balloc (bstring b, int olen) { reallocStrategy:; - x = (unsigned char *) bstr__realloc (b->data, (size_t) len); + x = (unsigned char *) bstr__realloc (b->data, (size_t) len + 1); if (x == NULL) { /* Since we failed, try allocating the tighest possible allocation */ len = olen; - x = (unsigned char *) bstr__realloc (b->data, (size_t) olen); + x = (unsigned char *) bstr__realloc (b->data, (size_t) len + 1); if (NULL == x) { return BSTR_ERR; } @@ -142,7 +142,7 @@ int balloc (bstring b, int olen) { the extra bytes that are allocated, but not considered part of the string */ - if (NULL == (x = (unsigned char *) bstr__alloc ((size_t) len))) { + if (NULL == (x = (unsigned char *) bstr__alloc ((size_t) len + 1))) { /* Perhaps there is no available memory for the two allocations to be in memory at once */ @@ -2398,13 +2398,13 @@ int i, c, v; } else { v = (bl->qty - 1) * len; if ((bl->qty > 512 || len > 127) && - v / len != bl->qty - 1) { + v / len != bl->qty - 1) { bstr__free (b); return NULL; /* Overflow */ } if (v > INT_MAX - c) { bstr__free (b); - return NULL; /* Overflow */ + return NULL; /* Overflow */ } c += v; p = b->data = (unsigned char *) bstr__alloc (c); diff --git a/common/descriptor.h b/common/descriptor.h index dfca7fc..c4e04c7 100644 --- a/common/descriptor.h +++ b/common/descriptor.h @@ -15,6 +15,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "basetypes.h" #include "ustring.h" +#include "ubytearray.h" // Make sure we use right packing rules #pragma pack(push,1) diff --git a/common/ffs.cpp b/common/ffs.cpp index de9a274..3549989 100644 --- a/common/ffs.cpp +++ b/common/ffs.cpp @@ -101,50 +101,114 @@ bool ustringToGuid(const UString & str, EFI_GUID & guid) UString fileTypeToUString(const UINT8 type) { - switch (type) - { - case EFI_FV_FILETYPE_RAW: return UString("Raw"); - case EFI_FV_FILETYPE_FREEFORM: return UString("Freeform"); - case EFI_FV_FILETYPE_SECURITY_CORE: return UString("SEC core"); - case EFI_FV_FILETYPE_PEI_CORE: return UString("PEI core"); - case EFI_FV_FILETYPE_DXE_CORE: return UString("DXE core"); - case EFI_FV_FILETYPE_PEIM: return UString("PEI module"); - case EFI_FV_FILETYPE_DRIVER: return UString("DXE driver"); - case EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER: return UString("Combined PEI/DXE"); - case EFI_FV_FILETYPE_APPLICATION: return UString("Application"); - case EFI_FV_FILETYPE_MM: return UString("SMM module"); - case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: return UString("Volume image"); - case EFI_FV_FILETYPE_COMBINED_MM_DXE: return UString("Combined SMM/DXE"); - case EFI_FV_FILETYPE_MM_CORE: return UString("SMM core"); - case EFI_FV_FILETYPE_MM_STANDALONE: return UString("MM standalone module"); - case EFI_FV_FILETYPE_MM_CORE_STANDALONE: return UString("MM standalone core"); - case EFI_FV_FILETYPE_PAD: return UString("Pad"); - default: return UString("Unknown"); + switch (type) { + case EFI_FV_FILETYPE_RAW: return UString("Raw"); + case EFI_FV_FILETYPE_FREEFORM: return UString("Freeform"); + case EFI_FV_FILETYPE_SECURITY_CORE: return UString("SEC core"); + case EFI_FV_FILETYPE_PEI_CORE: return UString("PEI core"); + case EFI_FV_FILETYPE_DXE_CORE: return UString("DXE core"); + case EFI_FV_FILETYPE_PEIM: return UString("PEI module"); + case EFI_FV_FILETYPE_DRIVER: return UString("DXE driver"); + case EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER: return UString("Combined PEI/DXE"); + case EFI_FV_FILETYPE_APPLICATION: return UString("Application"); + case EFI_FV_FILETYPE_MM: return UString("SMM module"); + case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: return UString("Volume image"); + case EFI_FV_FILETYPE_COMBINED_MM_DXE: return UString("Combined SMM/DXE"); + case EFI_FV_FILETYPE_MM_CORE: return UString("SMM core"); + case EFI_FV_FILETYPE_MM_STANDALONE: return UString("MM standalone module"); + case EFI_FV_FILETYPE_MM_CORE_STANDALONE: return UString("MM standalone core"); + case EFI_FV_FILETYPE_PAD: return UString("Pad"); + default: return usprintf("Unknown %u", type); }; } UString sectionTypeToUString(const UINT8 type) { - switch (type) - { - case EFI_SECTION_COMPRESSION: return UString("Compressed"); - case EFI_SECTION_GUID_DEFINED: return UString("GUID defined"); - case EFI_SECTION_DISPOSABLE: return UString("Disposable"); - case EFI_SECTION_PE32: return UString("PE32 image"); - case EFI_SECTION_PIC: return UString("PIC image"); - case EFI_SECTION_TE: return UString("TE image"); - case EFI_SECTION_DXE_DEPEX: return UString("DXE dependency"); - case EFI_SECTION_VERSION: return UString("Version"); - case EFI_SECTION_USER_INTERFACE: return UString("UI"); - case EFI_SECTION_COMPATIBILITY16: return UString("16-bit image"); - case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: return UString("Volume image"); - case EFI_SECTION_FREEFORM_SUBTYPE_GUID: return UString("Freeform subtype GUID"); - case EFI_SECTION_RAW: return UString("Raw"); - case EFI_SECTION_PEI_DEPEX: return UString("PEI dependency"); - case EFI_SECTION_MM_DEPEX: return UString("MM dependency"); - case INSYDE_SECTION_POSTCODE: return UString("Insyde postcode"); - case PHOENIX_SECTION_POSTCODE: return UString("Phoenix postcode"); - default: return UString("Unknown"); + switch (type) { + case EFI_SECTION_COMPRESSION: return UString("Compressed"); + case EFI_SECTION_GUID_DEFINED: return UString("GUID defined"); + case EFI_SECTION_DISPOSABLE: return UString("Disposable"); + case EFI_SECTION_PE32: return UString("PE32 image"); + case EFI_SECTION_PIC: return UString("PIC image"); + case EFI_SECTION_TE: return UString("TE image"); + case EFI_SECTION_DXE_DEPEX: return UString("DXE dependency"); + case EFI_SECTION_VERSION: return UString("Version"); + case EFI_SECTION_USER_INTERFACE: return UString("UI"); + case EFI_SECTION_COMPATIBILITY16: return UString("16-bit image"); + case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: return UString("Volume image"); + case EFI_SECTION_FREEFORM_SUBTYPE_GUID: return UString("Freeform subtype GUID"); + case EFI_SECTION_RAW: return UString("Raw"); + case EFI_SECTION_PEI_DEPEX: return UString("PEI dependency"); + case EFI_SECTION_MM_DEPEX: return UString("MM dependency"); + case INSYDE_SECTION_POSTCODE: return UString("Insyde postcode"); + case PHOENIX_SECTION_POSTCODE: return UString("Phoenix postcode"); + default: return usprintf("Unknown %u", type); } } +UString bpdtEntryTypeToUString(const UINT16 type) +{ + switch (type) { + case BPDT_ENTRY_TYPE_OEM_SMIP: return UString("OEM SMIP"); + case BPDT_ENTRY_TYPE_OEM_RBE: return UString("CSE RBE"); + case BPDT_ENTRY_TYPE_CSE_BUP: return UString("CSE BUP"); + case BPDT_ENTRY_TYPE_UCODE: return UString("uCode"); + case BPDT_ENTRY_TYPE_IBB: return UString("IBB"); + case BPDT_ENTRY_TYPE_SBPDT: return UString("S-BPDT"); + case BPDT_ENTRY_TYPE_OBB: return UString("OBB"); + case BPDT_ENTRY_TYPE_CSE_MAIN: return UString("CSE Main"); + case BPDT_ENTRY_TYPE_ISH: return UString("ISH"); + case BPDT_ENTRY_TYPE_CSE_IDLM: return UString("CSE IDLM"); + case BPDT_ENTRY_TYPE_IFP_OVERRIDE: return UString("IFP Override"); + case BPDT_ENTRY_TYPE_DEBUG_TOKENS: return UString("Debug Tokens"); + case BPDT_ENTRY_TYPE_USF_PHY_CONFIG: return UString("USF Phy Config"); + case BPDT_ENTRY_TYPE_USB_GPP_LUN_ID: return UString("USF GPP LUN ID"); + case BPDT_ENTRY_TYPE_PMC: return UString("PMC"); + case BPDT_ENTRY_TYPE_IUNIT: return UString("iUnit"); + case BPDT_ENTRY_TYPE_NVM_CONFIG: return UString("NVM Config"); + case BPDT_ENTRY_TYPE_UEP: return UString("UEP"); + case BPDT_ENTRY_TYPE_WLAN_UCODE: return UString("WLAN uCode"); + case BPDT_ENTRY_TYPE_LOCL_SPRITES: return UString("LOCL Sprites"); + case BPDT_ENTRY_TYPE_OEM_KEY_MANIFEST: return UString("OEM Key Manifest"); + case BPDT_ENTRY_TYPE_DEFAULTS: return UString("Defaults"); + case BPDT_ENTRY_TYPE_PAVP: return UString("PAVP"); + case BPDT_ENTRY_TYPE_TCSS_FW_IOM: return UString("TCSS FW IOM"); + case BPDT_ENTRY_TYPE_TCSS_FW_PHY: return UString("TCSS FW PHY"); + case BPDT_ENTRY_TYPE_TBT: return UString("TCSS TBT"); + default: return usprintf("Unknown %u", type); + } +} + +UString cpdExtensionTypeToUstring(const UINT32 type) +{ + switch (type) { + case CPD_EXT_TYPE_SYSTEM_INFO: return UString("System Info"); + case CPD_EXT_TYPE_INIT_SCRIPT: return UString("Init Script"); + case CPD_EXT_TYPE_FEATURE_PERMISSIONS: return UString("Feature Permissions"); + case CPD_EXT_TYPE_PARTITION_INFO: return UString("Partition Info"); + case CPD_EXT_TYPE_SHARED_LIB_ATTRIBUTES: return UString("Shared Lib Attributes"); + case CPD_EXT_TYPE_PROCESS_ATTRIBUTES: return UString("Process Attributes"); + case CPD_EXT_TYPE_THREAD_ATTRIBUTES: return UString("Thread Attributes"); + case CPD_EXT_TYPE_DEVICE_TYPE: return UString("Device Type"); + case CPD_EXT_TYPE_MMIO_RANGE: return UString("MMIO Range"); + case CPD_EXT_TYPE_SPEC_FILE_PRODUCER: return UString("Spec File Producer"); + case CPD_EXT_TYPE_MODULE_ATTRIBUTES: return UString("Module Attributes"); + case CPD_EXT_TYPE_LOCKED_RANGES: return UString("Locked Ranges"); + case CPD_EXT_TYPE_CLIENT_SYSTEM_INFO: return UString("Client System Info"); + case CPD_EXT_TYPE_USER_INFO: return UString("User Info"); + case CPD_EXT_TYPE_KEY_MANIFEST: return UString("Key Manifest"); + case CPD_EXT_TYPE_SIGNED_PACKAGE_INFO: return UString("Signed Package Info"); + case CPD_EXT_TYPE_ANTI_CLONING_SKU_ID: return UString("Anti-cloning SKU ID"); + case CPD_EXT_TYPE_CAVS: return UString("cAVS"); + case CPD_EXT_TYPE_IMR_INFO: return UString("IMR Info"); + case CPD_EXT_TYPE_RCIP_INFO: return UString("RCIP Info"); + case CPD_EXT_TYPE_BOOT_POLICY: return UString("Boot Policy"); + case CPD_EXT_TYPE_SECURE_TOKEN: return UString("Secure Token"); + case CPD_EXT_TYPE_IFWI_PARTITION_MANIFEST: return UString("IFWI Partition Manifest"); + case CPD_EXT_TYPE_FD_HASH: return UString("FD Hash"); + case CPD_EXT_TYPE_IOM_METADATA: return UString("IOM Metadata"); + case CPD_EXT_TYPE_MGP_METADATA: return UString("MGP Metadata"); + case CPD_EXT_TYPE_TBT_METADATA: return UString("TBT Metadata"); + default: return usprintf("Unknown %u", type); + } +} diff --git a/common/ffs.h b/common/ffs.h index 4a7ccae..441e09a 100644 --- a/common/ffs.h +++ b/common/ffs.h @@ -26,8 +26,8 @@ extern UString guidToUString(const EFI_GUID& guid, bool convertToString = true); extern bool ustringToGuid(const UString& str, EFI_GUID& guid); extern UString fileTypeToUString(const UINT8 type); extern UString sectionTypeToUString(const UINT8 type); - - +extern UString bpdtEntryTypeToUString(const UINT16 type); +extern UString cpdExtensionTypeToUstring(const UINT32 type); //***************************************************************************** // EFI Capsule //***************************************************************************** @@ -586,6 +586,220 @@ typedef struct X86_RESET_VECTOR_DATA_ { #define X86_RESET_VECTOR_DATA_UNPOPULATED 0x12345678 +//***************************************************************************** +// IFWI +//***************************************************************************** + +// BPDT +#define BPDT_GREEN_SIGNATURE 0x000055AA +#define BPDT_YELLOW_SIGNATURE 0x00AA55AA + +typedef struct BPDT_HEADER_ { + UINT32 Signature; + UINT16 NumEntries; + UINT8 HeaderVersion; + UINT8 RedundancyFlag; // Reserved zero in version 1 + UINT32 Checksum; + UINT32 IfwiVersion; + UINT16 FitcMajor; + UINT16 FitcMinor; + UINT16 FitcHotfix; + UINT16 FitcBuild; +} BPDT_HEADER; + +#define BPDT_HEADER_VERSION_1 1 +#define BPDT_HEADER_VERSION_2 2 + +typedef struct BPDT_ENTRY_ { + UINT32 Type : 16; + UINT32 SplitSubPartitionFirstPart : 1; + UINT32 SplitSubPartitionSecondPart : 1; + UINT32 CodeSubPartition : 1; + UINT32 UmaCachable : 1; + UINT32 Reserved: 12; + UINT32 Offset; + UINT32 Size; +} BPDT_ENTRY; + +#define BPDT_ENTRY_TYPE_OEM_SMIP 0 +#define BPDT_ENTRY_TYPE_OEM_RBE 1 +#define BPDT_ENTRY_TYPE_CSE_BUP 2 +#define BPDT_ENTRY_TYPE_UCODE 3 +#define BPDT_ENTRY_TYPE_IBB 4 +#define BPDT_ENTRY_TYPE_SBPDT 5 +#define BPDT_ENTRY_TYPE_OBB 6 +#define BPDT_ENTRY_TYPE_CSE_MAIN 7 +#define BPDT_ENTRY_TYPE_ISH 8 +#define BPDT_ENTRY_TYPE_CSE_IDLM 9 +#define BPDT_ENTRY_TYPE_IFP_OVERRIDE 10 +#define BPDT_ENTRY_TYPE_DEBUG_TOKENS 11 +#define BPDT_ENTRY_TYPE_USF_PHY_CONFIG 12 +#define BPDT_ENTRY_TYPE_USB_GPP_LUN_ID 13 +#define BPDT_ENTRY_TYPE_PMC 14 +#define BPDT_ENTRY_TYPE_IUNIT 15 +#define BPDT_ENTRY_TYPE_NVM_CONFIG 16 +#define BPDT_ENTRY_TYPE_UEP 17 +#define BPDT_ENTRY_TYPE_WLAN_UCODE 18 +#define BPDT_ENTRY_TYPE_LOCL_SPRITES 19 +#define BPDT_ENTRY_TYPE_OEM_KEY_MANIFEST 20 +#define BPDT_ENTRY_TYPE_DEFAULTS 21 +#define BPDT_ENTRY_TYPE_PAVP 22 +#define BPDT_ENTRY_TYPE_TCSS_FW_IOM 23 +#define BPDT_ENTRY_TYPE_TCSS_FW_PHY 24 +#define BPDT_ENTRY_TYPE_TBT 25 +#define BPDT_LAST_KNOWN_ENTRY_TYPE BPDT_ENTRY_TYPE_TBT + +// CPD +#define CPD_SIGNATURE 0x44504324 //$CPD + +typedef struct CPD_REV1_HEADER_ { + UINT32 Signature; + UINT32 NumEntries; + UINT8 HeaderVersion; // 1 + UINT8 EntryVersion; + UINT8 HeaderLength; + UINT8 HeaderChecksum; + UINT8 ShortName[4]; +} CPD_REV1_HEADER; + +typedef struct CPD_REV2_HEADER_ { + UINT32 Signature; + UINT32 NumEntries; + UINT8 HeaderVersion; // 2 + UINT8 EntryVersion; + UINT8 HeaderLength; + UINT8 Reserved; + UINT8 ShortName[4]; + UINT32 Checksum; +} CPD_REV2_HEADER; + +typedef struct CPD_ENTRY_ { + UINT8 EntryName[12]; + struct { + UINT32 Offset : 25; + UINT32 HuffmanCompressed : 1; + UINT32 Reserved : 6; + } Offset; + UINT32 Length; + UINT32 Reserved; +} CPD_ENTRY; + +typedef struct CPD_MANIFEST_HEADER_ { + UINT32 HeaderType; + UINT32 HeaderLength; + UINT32 HeaderVersion; + UINT32 Flags; + UINT32 Vendor; + UINT32 Date; + UINT32 Size; + UINT32 HeaderId; + UINT32 Reserved1; + UINT16 VersionMajor; + UINT16 VersionMinor; + UINT16 VersionBugfix; + UINT16 VersionBuild; + UINT32 SecurityVersion; + UINT8 Reserved2[8]; + UINT8 Reserved3[64]; + UINT32 ModulusSize; + UINT32 ExponentSize; + //manifest_rsa_key_t public_key; + //manifest_signature_t signature; +} CPD_MANIFEST_HEADER; + +typedef struct CPD_EXTENTION_HEADER_ { + UINT32 Type; + UINT32 Length; +} CPD_EXTENTION_HEADER; + +#define CPD_EXT_TYPE_SYSTEM_INFO 0 +#define CPD_EXT_TYPE_INIT_SCRIPT 1 +#define CPD_EXT_TYPE_FEATURE_PERMISSIONS 2 +#define CPD_EXT_TYPE_PARTITION_INFO 3 +#define CPD_EXT_TYPE_SHARED_LIB_ATTRIBUTES 4 +#define CPD_EXT_TYPE_PROCESS_ATTRIBUTES 5 +#define CPD_EXT_TYPE_THREAD_ATTRIBUTES 6 +#define CPD_EXT_TYPE_DEVICE_TYPE 7 +#define CPD_EXT_TYPE_MMIO_RANGE 8 +#define CPD_EXT_TYPE_SPEC_FILE_PRODUCER 9 +#define CPD_EXT_TYPE_MODULE_ATTRIBUTES 10 +#define CPD_EXT_TYPE_LOCKED_RANGES 11 +#define CPD_EXT_TYPE_CLIENT_SYSTEM_INFO 12 +#define CPD_EXT_TYPE_USER_INFO 13 +#define CPD_EXT_TYPE_KEY_MANIFEST 14 +#define CPD_EXT_TYPE_SIGNED_PACKAGE_INFO 15 +#define CPD_EXT_TYPE_ANTI_CLONING_SKU_ID 16 +#define CPD_EXT_TYPE_CAVS 17 +#define CPD_EXT_TYPE_IMR_INFO 18 +#define CPD_EXT_TYPE_BOOT_POLICY 19 +#define CPD_EXT_TYPE_RCIP_INFO 20 +#define CPD_EXT_TYPE_SECURE_TOKEN 21 +#define CPD_EXT_TYPE_IFWI_PARTITION_MANIFEST 22 +#define CPD_EXT_TYPE_FD_HASH 23 +#define CPD_EXT_TYPE_IOM_METADATA 24 +#define CPD_EXT_TYPE_MGP_METADATA 25 +#define CPD_EXT_TYPE_TBT_METADATA 26 +#define CPD_LAST_KNOWN_EXT_TYPE CPD_EXT_TYPE_TBT_METADATA + +typedef struct CPD_EXT_SIGNED_PACKAGE_INFO_MODULE_ { + UINT8 Name[12]; + UINT8 Type; + UINT8 HashAlgorithm; + UINT16 HashSize; + UINT32 MetadataSize; + UINT8 MetadataHash[32]; +} CPD_EXT_SIGNED_PACKAGE_INFO_MODULE; + +typedef struct CPD_EXT_SIGNED_PACKAGE_INFO_ { + UINT32 ExtensionType; + UINT32 ExtensionLength; + UINT8 PackageName[4]; + UINT32 Vcn; + UINT8 UsageBitmap[16]; + UINT32 Svn; + UINT8 Reserved[16]; + // EXT_SIGNED_PACKAGE_INFO_MODULE Modules[]; +} CPD_EXT_SIGNED_PACKAGE_INFO; + +typedef struct CPD_EXT_MODULE_ATTRIBUTES_ { + UINT32 ExtensionType; + UINT32 ExtensionLength; + UINT8 CompressionType; + UINT8 Reserved[3]; + UINT32 UncompressedSize; + UINT32 CompressedSize; + UINT32 GlobalModuleId; + UINT8 ImageHash[32]; +} CPD_EXT_MODULE_ATTRIBUTES; + +#define CPD_EXT_MODULE_COMPRESSION_TYPE_UNCOMPRESSED 0 +#define CPD_EXT_MODULE_COMPRESSION_TYPE_HUFFMAN 1 +#define CPD_EXT_MODULE_COMPRESSION_TYPE_LZMA 2 + +typedef struct CPD_EXT_IFWI_PARTITION_MANIFEST_ { + UINT32 ExtensionType; + UINT32 ExtensionLength; + UINT8 PartitionName[4]; + UINT32 CompletePartitionLength; + UINT16 PartitionVersionMinor; + UINT16 PartitionVersionMajor; + UINT32 DataFormatVersion; + UINT32 InstanceId; + UINT32 SupportMultipleInstances : 1; + UINT32 SupportApiVersionBasedUpdate : 1; + UINT32 ActionOnUpdate : 2; + UINT32 ObeyFullUpdateRules : 1; + UINT32 IfrEnableOnly : 1; + UINT32 AllowCrossPointUpdate : 1; + UINT32 AllowCrossHotfixUpdate : 1; + UINT32 PartialUpdateOnly : 1; + UINT32 ReservedFlags : 23; + UINT32 HashAlgorithm : 8; + UINT32 HashSize : 24; + UINT8 CompletePartitionHash[32]; + UINT8 Reserved[20]; +} CPD_EXT_IFWI_PARTITION_MANIFEST; + // Restore previous packing rules #pragma pack(pop) diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index 7432f0e..3a15d15 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -39,13 +39,14 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. namespace Qt { enum GlobalColor { red = 7, + green = 8, cyan = 10, yellow = 12, }; } #endif -// Region info structure definition +// Region info struct REGION_INFO { UINT32 offset; UINT32 length; @@ -54,12 +55,28 @@ struct REGION_INFO { friend bool operator< (const REGION_INFO & lhs, const REGION_INFO & rhs){ return lhs.offset < rhs.offset; } }; +// BPDT partition info +struct BPDT_PARTITION_INFO { + BPDT_ENTRY ptEntry; + UINT8 type; + UModelIndex index; + friend bool operator< (const BPDT_PARTITION_INFO & lhs, const BPDT_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset < rhs.ptEntry.Offset; } +}; + +// CPD partition info +struct CPD_PARTITION_INFO { + CPD_ENTRY ptEntry; + UINT8 type; + UModelIndex index; + friend bool operator< (const CPD_PARTITION_INFO & lhs, const CPD_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset.Offset < rhs.ptEntry.Offset.Offset; } +}; + // Constructor FfsParser::FfsParser(TreeModel* treeModel) : model(treeModel), imageBase(0), addressDiff(0x100000000ULL), bgAcmFound(false), bgKeyManifestFound(false), bgBootPolicyFound(false), bgProtectedRegionsBase(0) { nvramParser = new NvramParser(treeModel, this); - meParser = new MeParser(treeModel); + meParser = new MeParser(treeModel, this); } // Destructor @@ -790,7 +807,7 @@ USTATUS FfsParser::parseBiosRegion(const UByteArray & bios, const UINT32 localOf // Add tree item index = model->addItem(localOffset, Types::Region, Subtypes::BiosRegion, name, UString(), info, UByteArray(), bios, UByteArray(), Fixed, parent); - + return parseRawArea(index); } @@ -900,6 +917,23 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) msg(usprintf("%s: microcode header parsing failed with error ", __FUNCTION__) + errorCodeToUString(result), index); } } + else if (itemType == Types::BpdtStore) { + UByteArray bpdtStore = data.mid(itemOffset, itemSize); + + // Get info + name = UString("BPDT region"); + info = usprintf("Full size: %Xh (%u)", bpdtStore.size(), bpdtStore.size()); + + // Add tree item + UModelIndex bpdtIndex = model->addItem(headerSize + itemOffset, Types::BpdtStore, 0, name, UString(), info, UByteArray(), bpdtStore, UByteArray(), Fixed, index); + + // Parse BPDT region + UModelIndex bpdtPtIndex; + result = parseBpdtRegion(bpdtStore, 0, 0, bpdtIndex, bpdtPtIndex); + if (result) { + msg(usprintf("%s: BPDT store parsing failed with error ", __FUNCTION__) + errorCodeToUString(result), index); + } + } else { return U_UNKNOWN_ITEM_TYPE; } @@ -937,6 +971,12 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) case Types::Microcode: // Parsing already done break; + case Types::BpdtStore: + // Parsing already done + break; + case Types::BpdtPartition: + // Parsing already done + break; case Types::Padding: // No parsing required break; @@ -1226,6 +1266,48 @@ USTATUS FfsParser::findNextRawAreaItem(const UModelIndex & index, const UINT32 l nextItemOffset = offset - EFI_FV_SIGNATURE_OFFSET; break; } + else if (readUnaligned(currentPos) == BPDT_GREEN_SIGNATURE || readUnaligned(currentPos) == BPDT_YELLOW_SIGNATURE) { + // Check data size + if (restSize < sizeof(BPDT_HEADER)) + continue; + + const BPDT_HEADER *bpdtHeader = (const BPDT_HEADER *)currentPos; + // Check version + if (bpdtHeader->HeaderVersion != BPDT_HEADER_VERSION_1) // IFWI 2.0 only for now + continue; + + UINT32 ptBodySize = bpdtHeader->NumEntries * sizeof(BPDT_ENTRY); + UINT32 ptSize = sizeof(BPDT_HEADER) + ptBodySize; + // Check data size again + if (restSize < ptSize) + continue; + + UINT32 sizeCandidate = 0; + // Parse partition table + const BPDT_ENTRY* firstPtEntry = (const BPDT_ENTRY*)((const UINT8*)bpdtHeader + sizeof(BPDT_HEADER)); + for (UINT16 i = 0; i < bpdtHeader->NumEntries; i++) { + // Populate entry header + const BPDT_ENTRY* ptEntry = firstPtEntry + i; + // Check that entry is present in the image + if (ptEntry->Offset != 0 + && ptEntry->Offset != 0xFFFFFFFF + && ptEntry->Size != 0 + && sizeCandidate < ptEntry->Offset + ptEntry->Size) { + sizeCandidate = ptEntry->Offset + ptEntry->Size; + } + } + + // Check size candidate + if (sizeCandidate == 0) + continue; + + // All checks passed, BPDT found + nextItemType = Types::BpdtStore; + nextItemSize = sizeCandidate; + nextItemAlternativeSize = sizeCandidate; + nextItemOffset = offset; + break; + } } // No more stores found @@ -1294,7 +1376,7 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) UINT32 fileSize = getFileSize(volumeBody, fileOffset, ffsVersion); // Check that we are at the empty space - UByteArray header = volumeBody.mid(fileOffset, std::min(sizeof(EFI_FFS_FILE_HEADER), (size_t)volumeBodySize - fileOffset)); + UByteArray header = volumeBody.mid(fileOffset, (int)std::min(sizeof(EFI_FFS_FILE_HEADER), (size_t)volumeBodySize - fileOffset)); if (header.count(emptyByte) == header.size()) { //Empty space // Check volume usedSpace entry to be valid if (usedSpace > 0 && usedSpace == fileOffset + volumeHeaderSize) { @@ -3316,7 +3398,12 @@ USTATUS FfsParser::markProtectedRangeRecursive(const UModelIndex & index, const 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_IBB ? Qt::red : Qt::cyan); + if (range.Type == BG_PROTECTED_RANGE_INTEL_BOOT_GUARD_IBB) { + model->setMarking(index, Qt::red); + } + else { + model->setMarking(index, Qt::cyan); + } } else { // Mark as partially in range model->setMarking(index, Qt::yellow); @@ -4251,3 +4338,746 @@ USTATUS FfsParser::parseIntelMicrocodeHeader(const UByteArray & microcode, const // No need to parse the body further for now return U_SUCCESS; } + +USTATUS FfsParser::parseBpdtRegion(const UByteArray & region, const UINT32 localOffset, const UINT32 sbpdtOffsetFixup, const UModelIndex & parent, UModelIndex & index) +{ + UINT32 regionSize = (UINT32)region.size(); + + // Check region size + if (regionSize < sizeof(BPDT_HEADER)) { + msg(usprintf("%s: BPDT region too small to fit BPDT partition table header", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + + // Populate partition table header + const BPDT_HEADER* ptHeader = (const BPDT_HEADER*)(region.constData()); + + // Check region size again + UINT32 ptBodySize = ptHeader->NumEntries * sizeof(BPDT_ENTRY); + UINT32 ptSize = sizeof(BPDT_HEADER) + ptBodySize; + if (regionSize < ptSize) { + msg(usprintf("%s: BPDT region too small to fit BPDT partition table", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + + // Get info + UByteArray header = region.left(sizeof(BPDT_HEADER)); + UByteArray body = region.mid(sizeof(BPDT_HEADER), ptBodySize); + + UString name = UString("BPDT partition table"); + UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nNumber of entries: %u\nVersion: %2Xh\n" + "IFWI version: %Xh\nFITC version: %u.%u.%u.%u", + ptSize, ptSize, + header.size(), header.size(), + ptBodySize, ptBodySize, + ptHeader->NumEntries, + ptHeader->HeaderVersion, + ptHeader->IfwiVersion, + ptHeader->FitcMajor, ptHeader->FitcMinor, ptHeader->FitcHotfix, ptHeader->FitcBuild); + + // Add tree item + index = model->addItem(localOffset, Types::BpdtStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + + // Adjust offset + UINT32 offset = sizeof(BPDT_HEADER); + + // Add partition table entries + std::vector partitions; + const BPDT_ENTRY* firstPtEntry = (const BPDT_ENTRY*)((const UINT8*)ptHeader + sizeof(BPDT_HEADER)); + for (UINT16 i = 0; i < ptHeader->NumEntries; i++) { + // Populate entry header + const BPDT_ENTRY* ptEntry = firstPtEntry + i; + + // Get info + name = bpdtEntryTypeToUString(ptEntry->Type); + info = usprintf("Full size: %Xh (%u)\nType: %Xh\nPartition offset: %Xh\nPartition length: %Xh", + sizeof(BPDT_ENTRY), sizeof(BPDT_ENTRY), + ptEntry->Type, + ptEntry->Offset, + ptEntry->Size) + + UString("\nSplit sub-partition first part: ") + (ptEntry->SplitSubPartitionFirstPart ? "Yes" : "No") + + UString("\nSplit sub-partition second part: ") + (ptEntry->SplitSubPartitionSecondPart ? "Yes" : "No") + + UString("\nCode sub-partition: ") + (ptEntry->CodeSubPartition ? "Yes" : "No") + + UString("\nUMA cachable: ") + (ptEntry->UmaCachable ? "Yes" : "No"); + + // Add tree item + UModelIndex entryIndex = model->addItem(localOffset + offset, Types::BpdtEntry, 0, name, UString(), info, UByteArray(), UByteArray((const char*)ptEntry, sizeof(BPDT_ENTRY)), UByteArray(), Fixed, index); + + // Adjust offset + offset += sizeof(BPDT_ENTRY); + + if (ptEntry->Offset != 0 && ptEntry->Offset != 0xFFFFFFFF && ptEntry->Size != 0) { + // Add to partitions vector + BPDT_PARTITION_INFO partition; + partition.type = Types::BpdtPartition; + partition.ptEntry = *ptEntry; + partition.ptEntry.Offset -= sbpdtOffsetFixup; + partition.index = entryIndex; + partitions.push_back(partition); + } + } + + // Add padding if there's no partions to add + if (partitions.size() == 0) { + UByteArray partition = region.mid(ptSize); + + // Get info + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", + partition.size(), partition.size()); + + // Add tree item + model->addItem(localOffset + ptSize, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + return U_SUCCESS; + } + +make_partition_table_consistent: + // Sort partitions by offset + std::sort(partitions.begin(), partitions.end()); + + // Check for intersections and paddings between partitions + BPDT_PARTITION_INFO padding; + + // Check intersection with the partition table header + if (partitions.front().ptEntry.Offset < ptSize) { + msg(usprintf("%s: BPDT partition has intersection with BPDT partition table, skipped", __FUNCTION__), + partitions.front().index); + partitions.erase(partitions.begin()); + goto make_partition_table_consistent; + } + // Check for padding between partition table and the first partition + else if (partitions.front().ptEntry.Offset > ptSize) { + padding.ptEntry.Offset = ptSize; + padding.ptEntry.Size = partitions.front().ptEntry.Offset - padding.ptEntry.Offset; + padding.type = Types::Padding; + partitions.insert(partitions.begin(), padding); + } + // Check for intersections/paddings between partitions + for (size_t i = 1; i < partitions.size(); i++) { + UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Size; + + // Check that partition is fully present in the image + if ((UINT64)partitions[i].ptEntry.Offset + (UINT64)partitions[i].ptEntry.Size > regionSize) { + if ((UINT64)partitions[i].ptEntry.Offset >= (UINT64)region.size()) { + msg(usprintf("%s: BPDT partition is located outside of the opened image, skipped", __FUNCTION__), partitions[i].index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + else { + msg(usprintf("%s: BPDT partition can't fit into it's region, truncated", __FUNCTION__), partitions[i].index); + partitions[i].ptEntry.Size = regionSize - (UINT32)partitions[i].ptEntry.Offset; + } + } + + // Check for intersection with previous partition + if (partitions[i].ptEntry.Offset < previousPartitionEnd) { + // Check if current partition is located inside previous one + if (partitions[i].ptEntry.Offset + partitions[i].ptEntry.Size <= previousPartitionEnd) { + msg(usprintf("%s: BPDT partition is located inside another BPDT partition, skipped", __FUNCTION__), + partitions[i].index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + else { + msg(usprintf("%s: BPDT partition intersects with prevous one, skipped", __FUNCTION__), + partitions[i].index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + } + + // Check for padding between current and previous partitions + else if (partitions[i].ptEntry.Offset > previousPartitionEnd) { + padding.ptEntry.Offset = previousPartitionEnd; + padding.ptEntry.Size = partitions[i].ptEntry.Offset - previousPartitionEnd; + padding.type = Types::Padding; + std::vector::iterator iter = partitions.begin(); + std::advance(iter, i); + partitions.insert(iter, padding); + } + } + + // Partition map is consistent + for (size_t i = 0; i < partitions.size(); i++) { + if (partitions[i].type == Types::BpdtPartition) { + // Get info + UString name = bpdtEntryTypeToUString(partitions[i].ptEntry.Type); + UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Size); + UByteArray signature = partition.left(sizeof(UINT32)); + + UString info = usprintf("Full size: %Xh (%u)\nType: %Xh", + partition.size(), partition.size(), + partitions[i].ptEntry.Type) + + UString("\nSplit sub-partition first part: ") + (partitions[i].ptEntry.SplitSubPartitionFirstPart ? "Yes" : "No") + + UString("\nSplit sub-partition second part: ") + (partitions[i].ptEntry.SplitSubPartitionSecondPart ? "Yes" : "No") + + UString("\nCode sub-partition: ") + (partitions[i].ptEntry.CodeSubPartition ? "Yes" : "No") + + UString("\nUMA cachable: ") + (partitions[i].ptEntry.UmaCachable ? "Yes" : "No"); + + UString text = bpdtEntryTypeToUString(partitions[i].ptEntry.Type); + + // Add tree item + UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset, Types::BpdtPartition, 0, name, text, info, UByteArray(), partition, UByteArray(), Fixed, parent); + + // Special case of S-BPDT + if (partitions[i].ptEntry.Type == BPDT_ENTRY_TYPE_SBPDT) { + UModelIndex sbpdtIndex; + parseBpdtRegion(partition, 0, partitions[i].ptEntry.Offset, partitionIndex, sbpdtIndex); // Third parameter is a fixup for S-BPDT offset entries, because they are calculated from the start of BIOS region + } + + // Parse code partitions + if (readUnaligned((const UINT32*)partition.constData()) == CPD_SIGNATURE) { + // Parse code partition contents + UModelIndex cpdIndex; + parseCpdRegion(partition, localOffset, partitionIndex, cpdIndex); + } + + if (partitions[i].ptEntry.Type > BPDT_LAST_KNOWN_ENTRY_TYPE) { + msg(usprintf("%s: BPDT entry of unknown type found", __FUNCTION__), partitionIndex); + } + } + else if (partitions[i].type == Types::Padding) { + UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Size); + + // Get info + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", + partition.size(), partition.size()); + + // Add tree item + model->addItem(localOffset + partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + } + } + + // Add padding after the last region + if ((UINT64)partitions.back().ptEntry.Offset + (UINT64)partitions.back().ptEntry.Size < regionSize) { + UByteArray partition = region.mid(partitions.back().ptEntry.Offset + partitions.back().ptEntry.Size, regionSize - padding.ptEntry.Offset); + + // Get info + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", + partition.size(), partition.size()); + + // Add tree item + model->addItem(localOffset + partitions.back().ptEntry.Offset + partitions.back().ptEntry.Size, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + } + + return U_SUCCESS; +} + +USTATUS FfsParser::parseCpdRegion(const UByteArray & region, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +{ + // Check directory size + if ((UINT32)region.size() < sizeof(CPD_REV1_HEADER)) { + msg(usprintf("%s: CPD too small to fit rev1 partition table header", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + + // Populate partition table header + const CPD_REV1_HEADER* cpdHeader = (const CPD_REV1_HEADER*)region.constData(); + + // Check header version to be known + UINT32 ptHeaderSize = 0; + if (cpdHeader->HeaderVersion == 2) { + if ((UINT32)region.size() < sizeof(CPD_REV2_HEADER)) { + msg(usprintf("%s: CPD too small to fit rev2 partition table header", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + + ptHeaderSize = sizeof(CPD_REV2_HEADER); + } + else if (cpdHeader->HeaderVersion == 1) { + ptHeaderSize = sizeof(CPD_REV1_HEADER); + } + + // Check directory size again + UINT32 ptBodySize = cpdHeader->NumEntries * sizeof(CPD_ENTRY); + UINT32 ptSize = ptHeaderSize + ptBodySize; + if ((UINT32)region.size() < ptSize) { + msg(usprintf("%s: CPD too small to fit the whole partition table", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + + // Get info + UByteArray header = region.left(ptHeaderSize); + UByteArray body = region.mid(ptHeaderSize); + UString name = usprintf("CPD partition table"); + UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nNumber of entries: %u\n" + "Header version: %02X\nEntry version: %02X", + region.size(), region.size(), + header.size(), header.size(), + body.size(), body.size(), + cpdHeader->NumEntries, + cpdHeader->HeaderVersion, + cpdHeader->EntryVersion); + + // Add tree item + index = model->addItem(localOffset, Types::CpdStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + + // Add partition table entries + std::vector partitions; + UINT32 offset = ptHeaderSize; + const CPD_ENTRY* firstCpdEntry = (const CPD_ENTRY*)(body.constData()); + for (UINT32 i = 0; i < cpdHeader->NumEntries; i++) { + // Populate entry header + const CPD_ENTRY* cpdEntry = firstCpdEntry + i; + UByteArray entry((const char*)cpdEntry, sizeof(CPD_ENTRY)); + + // Get info + name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c", + cpdEntry->EntryName[0], cpdEntry->EntryName[1], cpdEntry->EntryName[2], cpdEntry->EntryName[3], + cpdEntry->EntryName[4], cpdEntry->EntryName[5], cpdEntry->EntryName[6], cpdEntry->EntryName[7], + cpdEntry->EntryName[8], cpdEntry->EntryName[9], cpdEntry->EntryName[10], cpdEntry->EntryName[11]); + info = usprintf("Full size: %Xh (%u)\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ", + entry.size(), entry.size(), + cpdEntry->Offset.Offset, + cpdEntry->Length) + + (cpdEntry->Offset.HuffmanCompressed ? "Yes" : "No"); + + // Add tree item + UModelIndex entryIndex = model->addItem(offset, Types::CpdEntry, 0, name, UString(), info, UByteArray(), entry, UByteArray(), Fixed, index); + + // Adjust offset + offset += sizeof(CPD_ENTRY); + + if (cpdEntry->Offset.Offset != 0 && cpdEntry->Length != 0) { + // Add to partitions vector + CPD_PARTITION_INFO partition; + partition.type = Types::CpdPartition; + partition.ptEntry = *cpdEntry; + partition.index = entryIndex; + partitions.push_back(partition); + } + } + + // Add padding if there's no partions to add + if (partitions.size() == 0) { + UByteArray partition = region.mid(ptSize); + + // Get info + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", + partition.size(), partition.size()); + + // Add tree item + model->addItem(localOffset + ptSize, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + + return U_SUCCESS; + } + + // Sort partitions by offset + std::sort(partitions.begin(), partitions.end()); + + // Because lenghts for all Huffmann-compressed partitions mean nothing at all, we need to split all partitions into 2 classes: + // 1. CPD manifest (should be the first) + // 2. Metadata entries (should begin right after partition manifest and end before any code partition) + UINT32 i = 1; + while (i < partitions.size()) { + name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c", + partitions[i].ptEntry.EntryName[0], partitions[i].ptEntry.EntryName[1], partitions[i].ptEntry.EntryName[2], partitions[i].ptEntry.EntryName[3], + partitions[i].ptEntry.EntryName[4], partitions[i].ptEntry.EntryName[5], partitions[i].ptEntry.EntryName[6], partitions[i].ptEntry.EntryName[7], + partitions[i].ptEntry.EntryName[8], partitions[i].ptEntry.EntryName[9], partitions[i].ptEntry.EntryName[10], partitions[i].ptEntry.EntryName[11]); + + // Check if the current entry is metadata entry + if (!name.contains(".met")) { + // No need to parse further, all metadata partitions are parsed + break; + } + + // Parse into data block, find Module Attributes extension, and get compressed size from there + UINT32 offset = 0; + UINT32 length = 0xFFFFFFFF; // Special guardian value + UByteArray partition = region.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length); + while (offset < (UINT32)partition.size()) { + const CPD_EXTENTION_HEADER* extHeader = (const CPD_EXTENTION_HEADER*) (partition.constData() + offset); + if (extHeader->Length <= ((UINT32)partition.size() - offset)) { + if (extHeader->Type == CPD_EXT_TYPE_MODULE_ATTRIBUTES) { + const CPD_EXT_MODULE_ATTRIBUTES* attrHeader = (const CPD_EXT_MODULE_ATTRIBUTES*)(partition.constData() + offset); + length = attrHeader->CompressedSize; + } + offset += extHeader->Length; + } + else break; + } + + // Search down for corresponding code partition + // Construct it's name by replacing last 4 non-zero butes of the name with zeros + UINT32 j = 0; + for (UINT32 k = 11; k > 0 && j < 4; k--) { + if (name[k] != '\x00') { + name[k] = '\x00'; + j++; + } + } + + // Search + j = i + 1; + while (j < partitions.size()) { + if (name == usprintf("%c%c%c%c%c%c%c%c%c%c%c%c", + partitions[j].ptEntry.EntryName[0], partitions[j].ptEntry.EntryName[1], partitions[j].ptEntry.EntryName[2], partitions[j].ptEntry.EntryName[3], + partitions[j].ptEntry.EntryName[4], partitions[j].ptEntry.EntryName[5], partitions[j].ptEntry.EntryName[6], partitions[j].ptEntry.EntryName[7], + partitions[j].ptEntry.EntryName[8], partitions[j].ptEntry.EntryName[9], partitions[j].ptEntry.EntryName[10], partitions[j].ptEntry.EntryName[11])) { + // Found it, update it's Length if needed + if (partitions[j].ptEntry.Offset.HuffmanCompressed) { + partitions[j].ptEntry.Length = length; + } + else if (length != 0xFFFFFFFF && partitions[j].ptEntry.Length != length) { + msg(usprintf("%s: partition size mismatch between partition table (%Xh) and partition metadata (%Xh)", __FUNCTION__, + partitions[j].ptEntry.Length, length), partitions[j].index); + partitions[j].ptEntry.Length = length; // Believe metadata + } + // No need to search further + break; + } + // Check the next partition + j++; + } + // Check the next partition + i++; + } + +make_partition_table_consistent: + // Sort partitions by offset + std::sort(partitions.begin(), partitions.end()); + + // Check for intersections and paddings between partitions + CPD_PARTITION_INFO padding; + + // Check intersection with the partition table header + if (partitions.front().ptEntry.Offset.Offset < ptSize) { + msg(usprintf("%s: CPD partition has intersection with CPD partition table, skipped", __FUNCTION__), + partitions.front().index); + partitions.erase(partitions.begin()); + goto make_partition_table_consistent; + } + // Check for padding between partition table and the first partition + else if (partitions.front().ptEntry.Offset.Offset > ptSize) { + padding.ptEntry.Offset.Offset = ptSize; + padding.ptEntry.Length = partitions.front().ptEntry.Offset.Offset - padding.ptEntry.Offset.Offset; + padding.type = Types::Padding; + partitions.insert(partitions.begin(), padding); + } + // Check for intersections/paddings between partitions + for (size_t i = 1; i < partitions.size(); i++) { + UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset.Offset + partitions[i - 1].ptEntry.Length; + + // Check that current region is fully present in the image + if ((UINT64)partitions[i].ptEntry.Offset.Offset + (UINT64)partitions[i].ptEntry.Length > (UINT64)region.size()) { + if ((UINT64)partitions[i].ptEntry.Offset.Offset >= (UINT64)region.size()) { + msg(usprintf("%s: CPD partition is located outside of the opened image, skipped", __FUNCTION__), partitions[i].index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + else { + msg(usprintf("%s: CPD partition can't fit into it's region, truncated", __FUNCTION__), partitions[i].index); + partitions[i].ptEntry.Length = (UINT32)region.size() - (UINT32)partitions[i].ptEntry.Offset.Offset; + } + } + + // Check for intersection with previous partition + if (partitions[i].ptEntry.Offset.Offset < previousPartitionEnd) { + // Check if current partition is located inside previous one + if (partitions[i].ptEntry.Offset.Offset + partitions[i].ptEntry.Length <= previousPartitionEnd) { + msg(usprintf("%s: CPD partition is located inside another CPD partition, skipped", __FUNCTION__), + partitions[i].index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + else { + msg(usprintf("%s: CPD partition intersects with previous one, skipped", __FUNCTION__), + partitions[i].index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + } + // Check for padding between current and previous partitions + else if (partitions[i].ptEntry.Offset.Offset > previousPartitionEnd) { + padding.ptEntry.Offset.Offset = previousPartitionEnd; + padding.ptEntry.Length = partitions[i].ptEntry.Offset.Offset - previousPartitionEnd; + padding.type = Types::Padding; + std::vector::iterator iter = partitions.begin(); + std::advance(iter, i); + partitions.insert(iter, padding); + } + } + // Check for padding after the last region + if ((UINT64)partitions.back().ptEntry.Offset.Offset + (UINT64)partitions.back().ptEntry.Length < (UINT64)region.size()) { + padding.ptEntry.Offset.Offset = partitions.back().ptEntry.Offset.Offset + partitions.back().ptEntry.Length; + padding.ptEntry.Length = (UINT32)region.size() - padding.ptEntry.Offset.Offset; + padding.type = Types::Padding; + partitions.push_back(padding); + } + + // Partition map is consistent + for (size_t i = 0; i < partitions.size(); i++) { + if (partitions[i].type == Types::CpdPartition) { + UByteArray partition = region.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length); + + // Get info + name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c", + partitions[i].ptEntry.EntryName[0], partitions[i].ptEntry.EntryName[1], partitions[i].ptEntry.EntryName[2], partitions[i].ptEntry.EntryName[3], + partitions[i].ptEntry.EntryName[4], partitions[i].ptEntry.EntryName[5], partitions[i].ptEntry.EntryName[6], partitions[i].ptEntry.EntryName[7], + partitions[i].ptEntry.EntryName[8], partitions[i].ptEntry.EntryName[9], partitions[i].ptEntry.EntryName[10], partitions[i].ptEntry.EntryName[11]); + + // It's a manifest + if (name.contains(".man")) { + if (!partitions[i].ptEntry.Offset.HuffmanCompressed + && partitions[i].ptEntry.Length >= sizeof(CPD_MANIFEST_HEADER)) { + const CPD_MANIFEST_HEADER* manifestHeader = (const CPD_MANIFEST_HEADER*) partition.constData(); + if (manifestHeader->HeaderId == ME_MANIFEST_HEADER_ID) { + UByteArray header = partition.left(manifestHeader->HeaderLength * sizeof(UINT32)); + UByteArray body = partition.mid(header.size()); + + info += usprintf( + "\nHeader type: %u\nHeader length: %Xh (%u)\nHeader version: %Xh\nFlags: %08Xh\nVendor: %Xh\n" + "Date: %Xh\nSize: %Xh (%u)\nVersion: %u.%u.%u.%u\nSecurity version number: %u\nModulus size: %Xh (%u)\nExponent size: %Xh (%u)", + manifestHeader->HeaderType, + manifestHeader->HeaderLength * sizeof(UINT32), manifestHeader->HeaderLength * sizeof(UINT32), + manifestHeader->HeaderVersion, + manifestHeader->Flags, + manifestHeader->Vendor, + manifestHeader->Date, + manifestHeader->Size * sizeof(UINT32), manifestHeader->Size * sizeof(UINT32), + manifestHeader->VersionMajor, manifestHeader->VersionMinor, manifestHeader->VersionBugfix, manifestHeader->VersionBuild, + manifestHeader->SecurityVersion, + manifestHeader->ModulusSize * sizeof(UINT32), manifestHeader->ModulusSize * sizeof(UINT32), + manifestHeader->ExponentSize * sizeof(UINT32), manifestHeader->ExponentSize * sizeof(UINT32)); + + // Add tree item + UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::ManifestCpdPartition, name, UString(), info, header, body, UByteArray(), Fixed, parent); + + // Parse data as extensions area + parseCpdExtensionsArea(partitionIndex); + } + } + } + // It's a metadata + else if (name.contains(".met")) { + info = usprintf("Full size: %Xh (%u)\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ", + partition.size(), partition.size(), + partitions[i].ptEntry.Offset.Offset, + partitions[i].ptEntry.Length) + + (partitions[i].ptEntry.Offset.HuffmanCompressed ? "Yes" : "No"); + + // Calculate SHA256 hash over the metadata and add it to it's info + UByteArray hash(SHA256_DIGEST_SIZE, '\x00'); + sha256(partition.constData(), partition.size(), hash.data()); + info += UString("\nMetadata hash: ") + UString(hash.toHex().constData()); + + // Add three item + UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::MetadataCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + + // Parse data as extensions area + parseCpdExtensionsArea(partitionIndex); + } + // It's a key + else if (name.contains(".key")) { + info = usprintf("Full size: %Xh (%u)\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ", + partition.size(), partition.size(), + partitions[i].ptEntry.Offset.Offset, + partitions[i].ptEntry.Length) + + (partitions[i].ptEntry.Offset.HuffmanCompressed ? "Yes" : "No"); + + // Calculate SHA256 hash over the key and add it to it's info + UByteArray hash(SHA256_DIGEST_SIZE, '\x00'); + sha256(partition.constData(), partition.size(), hash.data()); + info += UString("\nHash: ") + UString(hash.toHex().constData()); + + // Add three item + UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::KeyCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + + // Parse data as extensions area + parseCpdExtensionsArea(partitionIndex); + } + // It's a code + else { + info = usprintf("Full size: %Xh (%u)\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ", + partition.size(), partition.size(), + partitions[i].ptEntry.Offset.Offset, + partitions[i].ptEntry.Length) + + (partitions[i].ptEntry.Offset.HuffmanCompressed ? "Yes" : "No"); + + // Calculate SHA256 hash over the code and add it to it's info + UByteArray hash(SHA256_DIGEST_SIZE, '\x00'); + sha256(partition.constData(), partition.size(), hash.data()); + info += UString("\nHash: ") + UString(hash.toHex().constData()); + + UModelIndex codeIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::CodeCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + parseRawArea(codeIndex); + } + } + else if (partitions[i].type == Types::Padding) { + UByteArray partition = region.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length); + + // Get info + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", partition.size(), partition.size()); + + // Add tree item + model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + } + else { + msg(usprintf("%s: CPD partition of unknown type found", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + } + + return U_SUCCESS; +} + +USTATUS FfsParser::parseCpdExtensionsArea(const UModelIndex & index) +{ + if (!index.isValid()) { + return U_INVALID_PARAMETER; + } + + UByteArray body = model->body(index); + UINT32 offset = 0; + while (offset < (UINT32)body.size()) { + const CPD_EXTENTION_HEADER* extHeader = (const CPD_EXTENTION_HEADER*) (body.constData() + offset); + if (extHeader->Length <= ((UINT32)body.size() - offset)) { + UByteArray partition = body.mid(offset, extHeader->Length); + + UString name = cpdExtensionTypeToUstring(extHeader->Type); + UString info = usprintf("Full size: %Xh (%u)\nType: %Xh", partition.size(), partition.size(), extHeader->Type); + + // Parse Signed Package Info a bit further + UModelIndex extIndex; + if (extHeader->Type == CPD_EXT_TYPE_SIGNED_PACKAGE_INFO) { + UByteArray header = partition.left(sizeof(CPD_EXT_SIGNED_PACKAGE_INFO)); + UByteArray data = partition.mid(header.size()); + + const CPD_EXT_SIGNED_PACKAGE_INFO* infoHeader = (const CPD_EXT_SIGNED_PACKAGE_INFO*)header.constData(); + + info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %Xh\n" + "Package name: %c%c%c%c\nVersion control number: %Xh\nSecurity version number: %Xh\n" + "Usage bitmap: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + partition.size(), partition.size(), + header.size(), header.size(), + body.size(), body.size(), + infoHeader->ExtensionType, + infoHeader->PackageName[0], infoHeader->PackageName[1], infoHeader->PackageName[2], infoHeader->PackageName[3], + infoHeader->Vcn, + infoHeader->Svn, + infoHeader->UsageBitmap[0], infoHeader->UsageBitmap[1], infoHeader->UsageBitmap[2], infoHeader->UsageBitmap[3], + infoHeader->UsageBitmap[4], infoHeader->UsageBitmap[5], infoHeader->UsageBitmap[6], infoHeader->UsageBitmap[7], + infoHeader->UsageBitmap[8], infoHeader->UsageBitmap[9], infoHeader->UsageBitmap[10], infoHeader->UsageBitmap[11], + infoHeader->UsageBitmap[12], infoHeader->UsageBitmap[13], infoHeader->UsageBitmap[14], infoHeader->UsageBitmap[15]); + + // Add tree item + extIndex = model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, header, data, UByteArray(), Fixed, index); + parseSignedPackageInfoData(extIndex); + } + // Parse IFWI Partition Manifest a bit further + else if (extHeader->Type == CPD_EXT_TYPE_IFWI_PARTITION_MANIFEST) { + const CPD_EXT_IFWI_PARTITION_MANIFEST* attrHeader = (const CPD_EXT_IFWI_PARTITION_MANIFEST*)partition.constData(); + + // This hash is stored reversed + // Need to reverse it back to normal + UByteArray hash((const char*)&attrHeader->CompletePartitionHash, sizeof(attrHeader->CompletePartitionHash)); + std::reverse(hash.begin(), hash.end()); + + info = usprintf("Full size: %Xh (%u)\nType: %Xh\n" + "Partition name: %c%c%c%c\nPartition length: %Xh\nPartition version major: %Xh\nPartition version minor: %Xh\n" + "Data format version: %Xh\nInstance ID: %Xh\nHash algorithm: %Xh\nHash size: %Xh\nAction on update: %Xh", + partition.size(), partition.size(), + attrHeader->ExtensionType, + attrHeader->PartitionName[0], attrHeader->PartitionName[1], attrHeader->PartitionName[2], attrHeader->PartitionName[3], + attrHeader->CompletePartitionLength, + attrHeader->PartitionVersionMajor, attrHeader->PartitionVersionMinor, + attrHeader->DataFormatVersion, + attrHeader->InstanceId, + attrHeader->HashAlgorithm, + attrHeader->HashSize, + attrHeader->ActionOnUpdate) + + UString("\nSupport multiple instances: ") + (attrHeader->SupportMultipleInstances ? "Yes" : "No") + + UString("\nSupport API version based update: ") + (attrHeader->SupportApiVersionBasedUpdate ? "Yes" : "No") + + UString("\nObey full update rules: ") + (attrHeader->ObeyFullUpdateRules ? "Yes" : "No") + + UString("\nIFR enable only: ") + (attrHeader->IfrEnableOnly ? "Yes" : "No") + + UString("\nAllow cross point update: ") + (attrHeader->AllowCrossPointUpdate ? "Yes" : "No") + + UString("\nAllow cross hotfix update: ") + (attrHeader->AllowCrossHotfixUpdate ? "Yes" : "No") + + UString("\nPartial update only: ") + (attrHeader->PartialUpdateOnly ? "Yes" : "No") + + UString("\nPartition hash: ") + UString(hash.toHex().constData()); + + // Add tree item + extIndex = model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index); + } + // Parse Module Attributes a bit further + else if (extHeader->Type == CPD_EXT_TYPE_MODULE_ATTRIBUTES) { + const CPD_EXT_MODULE_ATTRIBUTES* attrHeader = (const CPD_EXT_MODULE_ATTRIBUTES*)partition.constData(); + + // This hash is stored reversed + // Need to reverse it back to normal + UByteArray hash((const char*)&attrHeader->ImageHash, sizeof(attrHeader->ImageHash)); + std::reverse(hash.begin(), hash.end()); + + info = usprintf("Full size: %Xh (%u)\nType: %Xh\n" + "Compression type: %Xh\nUncompressed size: %Xh (%u)\nCompressed size: %Xh (%u)\nGlobal module ID: %Xh\nImage hash: ", + partition.size(), partition.size(), + attrHeader->ExtensionType, + attrHeader->CompressionType, + attrHeader->UncompressedSize, attrHeader->UncompressedSize, + attrHeader->CompressedSize, attrHeader->CompressedSize, + attrHeader->GlobalModuleId) + UString(hash.toHex().constData()); + + // Add tree item + extIndex = model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index); + } + // Parse everything else + else { + // Add tree item, if needed + extIndex = model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index); + } + + if (extHeader->Type > CPD_LAST_KNOWN_EXT_TYPE) { + msg(usprintf("%s: CPD extention of unknown type found", __FUNCTION__), extIndex); + } + + offset += extHeader->Length; + } + else break; + // TODO: add padding at the end + } + + return U_SUCCESS; +} + +USTATUS FfsParser::parseSignedPackageInfoData(const UModelIndex & index) +{ + if (!index.isValid()) { + return U_INVALID_PARAMETER; + } + + UByteArray body = model->body(index); + UINT32 offset = 0; + while (offset < (UINT32)body.size()) { + const CPD_EXT_SIGNED_PACKAGE_INFO_MODULE* moduleHeader = (const CPD_EXT_SIGNED_PACKAGE_INFO_MODULE*)(body.constData() + offset); + if (sizeof(CPD_EXT_SIGNED_PACKAGE_INFO_MODULE) <= ((UINT32)body.size() - offset)) { + UByteArray module((const char*)moduleHeader, sizeof(CPD_EXT_SIGNED_PACKAGE_INFO_MODULE)); + + UString name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c", + moduleHeader->Name[0], moduleHeader->Name[1], moduleHeader->Name[2], moduleHeader->Name[3], + moduleHeader->Name[4], moduleHeader->Name[5], moduleHeader->Name[6], moduleHeader->Name[7], + moduleHeader->Name[8], moduleHeader->Name[9], moduleHeader->Name[10],moduleHeader->Name[11]); + + // This hash is stored reversed + // Need to reverse it back to normal + UByteArray hash((const char*)&moduleHeader->MetadataHash, sizeof(moduleHeader->MetadataHash)); + std::reverse(hash.begin(), hash.end()); + + UString info = usprintf("Full size: %X (%u)\nType: %Xh\nHash algorithm: %Xh\nHash size: %Xh (%u)\nMetadata size: %Xh (%u)\nMetadata hash: ", + module.size(), module.size(), + moduleHeader->Type, + moduleHeader->HashAlgorithm, + moduleHeader->HashSize, moduleHeader->HashSize, + moduleHeader->MetadataSize, moduleHeader->MetadataSize) + UString(hash.toHex().constData()); + // Add tree otem + model->addItem(offset, Types::CpdSpiEntry, 0, name, UString(), info, UByteArray(), module, UByteArray(), Fixed, index); + + offset += module.size(); + } + else break; + // TODO: add padding at the end + } + + return U_SUCCESS; +} diff --git a/common/ffsparser.h b/common/ffsparser.h index 74febfc..dc8f6a9 100644 --- a/common/ffsparser.h +++ b/common/ffsparser.h @@ -97,6 +97,11 @@ private: USTATUS parseIntelImage(const UByteArray & intelImage, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); USTATUS parseGenericImage(const UByteArray & intelImage, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseBpdtRegion(const UByteArray & region, const UINT32 localOffset, const UINT32 sbpdtOffsetFixup, const UModelIndex & parent, UModelIndex & index); + USTATUS parseCpdRegion(const UByteArray & region, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseCpdExtensionsArea(const UModelIndex & index); + USTATUS parseSignedPackageInfoData(const UModelIndex & index); + USTATUS parseRawArea(const UModelIndex & index); USTATUS parseVolumeHeader(const UByteArray & volume, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); USTATUS parseVolumeBody(const UModelIndex & index); @@ -163,6 +168,10 @@ private: #ifdef U_ENABLE_NVRAM_PARSING_SUPPORT friend class NvramParser; // Make FFS parsing routines accessible to NvramParser #endif + +#ifdef U_ENABLE_ME_PARSING_SUPPORT + friend class MeParser; // Make FFS parsing routines accessible to MeParser +#endif }; #endif // FFSPARSER_H diff --git a/common/me.h b/common/me.h index c3de13a..560a40c 100644 --- a/common/me.h +++ b/common/me.h @@ -18,9 +18,6 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. // Make sure we use right packing rules #pragma pack(push, 1) -const UByteArray ME_VERSION_SIGNATURE("\x24\x4D\x41\x4E", 4); //$MAN -const UByteArray ME_VERSION_SIGNATURE2("\x24\x4D\x4E\x32", 4); //$MN2 - typedef struct ME_VERSION_ { UINT32 Signature; UINT32 Reserved; @@ -30,6 +27,73 @@ typedef struct ME_VERSION_ { UINT16 Build; } ME_VERSION; +const UByteArray ME_VERSION_SIGNATURE("\x24\x4D\x41\x4E", 4); //$MAN +const UByteArray ME_VERSION_SIGNATURE2("\x24\x4D\x4E\x32", 4); //$MN2 + +// FPT +#define ME_ROM_BYPASS_VECTOR_SIZE 0x10 +const UByteArray FPT_HEADER_SIGNATURE("\x24\x46\x50\x54", 4); //$FPT + +typedef struct FPT_HEADER_ { + UINT32 Signature; + UINT32 NumEntries; + UINT8 HeaderVersion; + UINT8 EntryVersion; + UINT8 HeaderLength; + UINT8 Checksum; // One bit for Redundant before IFWI + UINT16 TicksToAdd; + UINT16 TokensToAdd; + UINT32 UmaSize; // Flags in SPS + UINT32 FlashLayout; // Crc32 before IFWI + UINT16 FitcMajor; + UINT16 FitcMinor; + UINT16 FitcHotfix; + UINT16 FitcBuild; +} FPT_HEADER; + +typedef struct FPT_HEADER_ENTRY_{ + CHAR8 Name[4]; + CHAR8 Owner[4]; + UINT32 Offset; + UINT32 Size; + UINT32 Reserved[3]; + UINT8 Type : 7; + UINT8 CopyToDramCache : 1; + UINT8 Reserved1 : 7; + UINT8 BuiltWithLength1 : 1; + UINT8 BuiltWithLength2 : 1; + UINT8 Reserved2 : 7; + UINT8 EntryValid; +} FPT_HEADER_ENTRY; + +// IFWI +typedef struct IFWI_HEADER_ENTRY_ { + UINT32 Offset; + UINT32 Size; +} IFWI_HEADER_ENTRY; + +// IFWI 1.6 (ME), 2.0 (BIOS) +typedef struct IFWI_16_LAYOUT_HEADER_ { + UINT8 RomBypassVector[16]; + IFWI_HEADER_ENTRY DataPartition; + IFWI_HEADER_ENTRY BootPartition[5]; + UINT64 Checksum; +} IFWI_16_LAYOUT_HEADER; + +// IFWI 1.7 (ME) +typedef struct IFWI_17_LAYOUT_HEADER_ { + UINT8 RomBypassVector[16]; + UINT16 HeaderSize; + UINT8 Flags; + UINT8 Reserved; + UINT32 Checksum; + IFWI_HEADER_ENTRY DataPartition; + IFWI_HEADER_ENTRY BootPartition[5]; + IFWI_HEADER_ENTRY TempPage; +} IFWI_17_LAYOUT_HEADER; + +#define ME_MANIFEST_HEADER_ID 0x324E4D24 //$MN2 + // Restore previous packing rules #pragma pack(pop) diff --git a/common/meparser.cpp b/common/meparser.cpp index b7ae12f..25d1380 100755 --- a/common/meparser.cpp +++ b/common/meparser.cpp @@ -1,6 +1,6 @@ /* meparser.cpp -Copyright (c) 2016, Nikolaj Schlej. All rights reserved. +Copyright (c) 2019, Nikolaj Schlej. All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -15,90 +15,26 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include +#include "ffs.h" +#include "me.h" #include "meparser.h" #include "parsingdata.h" #include "utility.h" #ifdef U_ENABLE_ME_PARSING_SUPPORT -UString meBpdtEntryTypeToUString(UINT16 type) { - switch (type) { - case 0: return UString("OEM SMIP"); - case 1: return UString("CSE RBE"); - case 2: return UString("CSE BUP"); - case 3: return UString("uCode"); - case 4: return UString("IBB"); - case 5: return UString("S-BPDT"); - case 6: return UString("OBB"); - case 7: return UString("CSE Main"); - case 8: return UString("ISH"); - case 9: return UString("CSE IDLM"); - case 10: return UString("IFP Override"); - case 11: return UString("Debug Tokens"); - case 12: return UString("USF Phy Config"); - case 13: return UString("USF GPP LUN ID"); - case 14: return UString("PMC"); - case 15: return UString("iUnit"); - case 16: return UString("NVM Config"); - case 17: return UString("UEP"); - case 18: return UString("WLAN uCode"); - case 19: return UString("LOCL Sprites"); - case 20: return UString("OEM Key Manifest"); - case 21: return UString("Defaults/FITC.cfg"); - case 22: return UString("PAVP"); - case 23: return UString("TCSS FW IOM"); - case 24: return UString("TCSS FW PHY"); - case 25: return UString("TCSS TBT"); - default: return usprintf("Unknown %u", type); - } -} - -UString meExtensionTypeToUstring(UINT32 type) { - switch (type) { - case 0: return UString("System Info"); - case 1: return UString("Init Script"); - case 2: return UString("Feature Permissions"); - case 3: return UString("Partition Info"); - case 4: return UString("Shared Lib Attributes"); - case 5: return UString("Process Attributes"); - case 6: return UString("Thread Attributes"); - case 7: return UString("Device Type"); - case 8: return UString("MMIO Range"); - case 9: return UString("Spec File Producer"); - case 10: return UString("Module Attributes"); - case 11: return UString("Locked Ranges"); - case 12: return UString("Client System Info"); - case 13: return UString("User Info"); - case 14: return UString("Key Manifest"); - case 15: return UString("Signed Package Info"); - case 16: return UString("Anto-cloning SKU ID"); - case 18: return UString("Intel IMR Info"); - case 20: return UString("RCIP Info"); - case 21: return UString("Secure Token"); - case 22: return UString("IFWI Partition Manifest"); - default: return usprintf("Unknown %u", type); - } -} - -struct ME_FPT_PARTITION_INFO { - ME_FPT_ENTRY ptEntry; +struct FPT_PARTITION_INFO { + FPT_HEADER_ENTRY ptEntry; UINT8 type; UModelIndex index; - friend bool operator< (const ME_FPT_PARTITION_INFO & lhs, const ME_FPT_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset < rhs.ptEntry.Offset; } + friend bool operator< (const FPT_PARTITION_INFO & lhs, const FPT_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset < rhs.ptEntry.Offset; } }; -struct ME_BPDT_PARTITION_INFO { - ME_BPDT_ENTRY ptEntry; +struct IFWI_PARTITION_INFO { + IFWI_HEADER_ENTRY ptEntry; UINT8 type; - UModelIndex index; - friend bool operator< (const ME_BPDT_PARTITION_INFO & lhs, const ME_BPDT_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset < rhs.ptEntry.Offset; } -}; - -struct ME_CPD_PARTITION_INFO { - ME_BPDT_CPD_ENTRY ptEntry; - UINT8 type; - UModelIndex index; - friend bool operator< (const ME_CPD_PARTITION_INFO & lhs, const ME_CPD_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset.Offset < rhs.ptEntry.Offset.Offset; } + UINT8 subtype; + friend bool operator< (const IFWI_PARTITION_INFO & lhs, const IFWI_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset < rhs.ptEntry.Offset; } }; USTATUS MeParser::parseMeRegionBody(const UModelIndex & index) @@ -118,28 +54,46 @@ USTATUS MeParser::parseMeRegionBody(const UModelIndex & index) // Check ME signature to determine it's version // ME v11 and older layout - if (meRegion.left(sizeof(UINT32)) == ME_FPT_HEADER_SIGNATURE || meRegion.mid(ME_ROM_BYPASS_VECTOR_SIZE, sizeof(UINT32)) == ME_FPT_HEADER_SIGNATURE) { + if (meRegion.left(sizeof(UINT32)) == FPT_HEADER_SIGNATURE || meRegion.mid(ME_ROM_BYPASS_VECTOR_SIZE, sizeof(UINT32)) == FPT_HEADER_SIGNATURE) { UModelIndex ptIndex; return parseFptRegion(meRegion, index, ptIndex); } - // CannonLake 1.6+ layout (IFWI) + // IFWI 1.6 // Check region size - if ((UINT32)meRegion.size() < sizeof(ME_IFWI_LAYOUT_HEADER)) { - msg(usprintf("%s: ME region too small to fit IFWI layout header", __FUNCTION__), index); + if ((UINT32)meRegion.size() < sizeof(IFWI_16_LAYOUT_HEADER)) { + msg(usprintf("%s: ME region too small to fit IFWI 1.6 layout header", __FUNCTION__), index); return U_INVALID_ME_PARTITION_TABLE; } - const ME_IFWI_LAYOUT_HEADER* regionHeader = (const ME_IFWI_LAYOUT_HEADER*)meRegion.constData(); + const IFWI_16_LAYOUT_HEADER* ifwi16Header = (const IFWI_16_LAYOUT_HEADER*)meRegion.constData(); // Check region size - if ((UINT32)meRegion.size() < regionHeader->DataPartitionOffset + sizeof(UINT32)) { - msg(usprintf("%s: ME region too small to fit IFWI layout header", __FUNCTION__), index); + if ((UINT32)meRegion.size() < ifwi16Header->DataPartition.Offset + sizeof(UINT32)) { + msg(usprintf("%s: ME region too small to fit IFWI 1.6 data partition", __FUNCTION__), index); return U_INVALID_ME_PARTITION_TABLE; } // Data partition always points to FPT header - if (meRegion.mid(regionHeader->DataPartitionOffset, sizeof(UINT32)) == ME_FPT_HEADER_SIGNATURE) { + if (meRegion.mid(ifwi16Header->DataPartition.Offset, sizeof(UINT32)) == FPT_HEADER_SIGNATURE) { UModelIndex ptIndex; - return parseIfwiRegion(meRegion, index, ptIndex); + return parseIfwi16Region(meRegion, index, ptIndex); + } + + // IFWI 1.7 + if ((UINT32)meRegion.size() < sizeof(IFWI_17_LAYOUT_HEADER)) { + msg(usprintf("%s: ME region too small to fit IFWI 1.7 layout header", __FUNCTION__), index); + return U_INVALID_ME_PARTITION_TABLE; + } + + const IFWI_17_LAYOUT_HEADER* ifwi17Header = (const IFWI_17_LAYOUT_HEADER*)meRegion.constData(); + // Check region size + if ((UINT32)meRegion.size() < ifwi17Header->DataPartition.Offset + sizeof(UINT32)) { + msg(usprintf("%s: ME region too small to fit IFWI 1.7 data partition", __FUNCTION__), index); + return U_INVALID_ME_PARTITION_TABLE; + } + // Data partition always points to FPT header + if (meRegion.mid(ifwi17Header->DataPartition.Offset, sizeof(UINT32)) == FPT_HEADER_SIGNATURE) { + UModelIndex ptIndex; + return parseIfwi17Region(meRegion, index, ptIndex); } // Something else entirely @@ -147,138 +101,40 @@ USTATUS MeParser::parseMeRegionBody(const UModelIndex & index) return U_INVALID_ME_PARTITION_TABLE; } -USTATUS MeParser::parseIfwiRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index) -{ - // Add header - UByteArray header = region.left(sizeof(ME_IFWI_LAYOUT_HEADER)); - const ME_IFWI_LAYOUT_HEADER* ifwiHeader = (const ME_IFWI_LAYOUT_HEADER*)region.constData(); - - UString name = UString("IFWI header"); - UString info = usprintf("Full size: %Xh (%u)\n" - "Data partition offset: %Xh\nData partition length: %Xh\n" - "Boot1 partition offset: %Xh\nBoot1 partition length: %Xh\n" - "Boot2 partition offset: %Xh\nBoot2 partition length: %Xh\n" - "Boot3 partition offset: %Xh\nBoot3 partition length: %Xh", - header.size(), header.size(), - ifwiHeader->DataPartitionOffset, ifwiHeader->DataPartitionSize, - ifwiHeader->Boot1Offset, ifwiHeader->Boot1Size, - ifwiHeader->Boot2Offset, ifwiHeader->Boot2Size, - ifwiHeader->Boot3Offset, ifwiHeader->Boot3Size); - // Add tree item - index = model->addItem(0, Types::IfwiHeader, 0, name, UString(), info, UByteArray(), header, UByteArray(), Fixed, parent); - - // TODO: this requires better parsing using a similar approach as in other things: get all, sort, check for paddings/intersections - - // Add padding after header - if (ifwiHeader->DataPartitionOffset > sizeof(ME_IFWI_LAYOUT_HEADER)) { - UByteArray padding = region.mid(sizeof(ME_IFWI_LAYOUT_HEADER), ifwiHeader->DataPartitionOffset - sizeof(ME_IFWI_LAYOUT_HEADER)); - name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); - // Add tree item - model->addItem(sizeof(ME_IFWI_LAYOUT_HEADER), Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent); - } - - // Add data partition - UByteArray dataPartition = region.mid(ifwiHeader->DataPartitionOffset, ifwiHeader->DataPartitionSize); - name = UString("Data partition"); - info = usprintf("Full size: %Xh (%u)", dataPartition.size(), dataPartition.size()); - UModelIndex dataPartitionIndex = model->addItem(ifwiHeader->DataPartitionOffset, Types::IfwiPartition, Subtypes::DataIfwiPartition, name, UString(), info, UByteArray(), dataPartition, UByteArray(), Fixed, parent); - UModelIndex dataPartitionFptRegionIndex; - parseFptRegion(dataPartition, dataPartitionIndex, dataPartitionFptRegionIndex); - - // Add padding after data partition - if (ifwiHeader->Boot1Offset > ifwiHeader->DataPartitionOffset + ifwiHeader->DataPartitionSize) { - UByteArray padding = region.mid(ifwiHeader->DataPartitionOffset + ifwiHeader->DataPartitionSize, ifwiHeader->Boot1Offset - ifwiHeader->DataPartitionOffset + ifwiHeader->DataPartitionSize); - name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); - // Add tree item - model->addItem(ifwiHeader->DataPartitionOffset + ifwiHeader->DataPartitionSize, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent); - } - else if (ifwiHeader->Boot1Offset < ifwiHeader->DataPartitionOffset + ifwiHeader->DataPartitionSize) { - msg(usprintf("%s: invalid Boot1 partition offset", __FUNCTION__), parent); - return U_INVALID_ME_PARTITION_TABLE; - } - - // Add Boot1 partition - UByteArray bpdt1Partition = region.mid(ifwiHeader->Boot1Offset, ifwiHeader->Boot1Size); - name = UString("Boot1 partition"); - info = usprintf("Full size: %Xh (%u)", bpdt1Partition.size(), bpdt1Partition.size()); - UModelIndex bpdt1PartitionIndex = model->addItem(ifwiHeader->Boot1Offset, Types::IfwiPartition, Subtypes::BootIfwiPartition, name, UString(), info, UByteArray(), bpdt1Partition, UByteArray(), Fixed, parent); - UModelIndex bpdt1PartitionBpdtRegionIndex; - parseBpdtRegion(bpdt1Partition, bpdt1PartitionIndex, bpdt1PartitionBpdtRegionIndex); - - // Add padding after Boot1 partition - if (ifwiHeader->Boot2Offset > ifwiHeader->Boot1Offset + ifwiHeader->Boot1Size) { - UByteArray padding = region.mid(ifwiHeader->Boot1Offset + ifwiHeader->Boot1Size, ifwiHeader->Boot2Offset - ifwiHeader->Boot1Offset + ifwiHeader->Boot1Size); - name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); - // Add tree item - model->addItem(ifwiHeader->Boot1Offset + ifwiHeader->Boot1Size, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent); - } - else if (ifwiHeader->Boot2Offset < ifwiHeader->Boot1Offset + ifwiHeader->Boot1Size) { - msg(usprintf("%s: invalid Boot2 partition offset", __FUNCTION__), parent); - return U_INVALID_ME_PARTITION_TABLE; - } - - // Add Boot2 partition - UByteArray bpdt2Partition = region.mid(ifwiHeader->Boot2Offset, ifwiHeader->Boot2Size); - name = UString("Boot2 partition"); - info = usprintf("Full size: %Xh (%u)", bpdt2Partition.size(), bpdt2Partition.size()); - UModelIndex bpdt2PartitionIndex = model->addItem(ifwiHeader->Boot2Offset, Types::IfwiPartition, Subtypes::BootIfwiPartition, name, UString(), info, UByteArray(), bpdt2Partition, UByteArray(), Fixed, parent); - UModelIndex bpdt2PartitionBpdtRegionIndex; - parseBpdtRegion(bpdt2Partition, bpdt2PartitionIndex, bpdt2PartitionBpdtRegionIndex); - - // TODO: add Boot3 if needed - // Add padding at the end - if ((UINT32)region.size() > ifwiHeader->Boot2Offset + ifwiHeader->Boot2Size) { - UByteArray padding = region.mid(ifwiHeader->Boot2Offset + ifwiHeader->Boot2Size, (UINT32)region.size() - ifwiHeader->Boot2Offset + ifwiHeader->Boot2Size); - name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); - // Add tree item - model->addItem(ifwiHeader->Boot2Offset + ifwiHeader->Boot2Size, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent); - } - else if ((UINT32)region.size() < ifwiHeader->Boot2Offset + ifwiHeader->Boot2Size) { - msg(usprintf("%s: Boot2 partition is located outside of the region", __FUNCTION__), parent); - return U_INVALID_ME_PARTITION_TABLE; - } - - return U_SUCCESS; -} - USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index) { // Check region size - if ((UINT32)region.size() < sizeof(ME_FPT_HEADER)) { + if ((UINT32)region.size() < sizeof(FPT_HEADER)) { msg(usprintf("%s: region too small to fit FPT header", __FUNCTION__), parent); return U_INVALID_ME_PARTITION_TABLE; } // Populate partition table header - const ME_FPT_HEADER* ptHeader = (const ME_FPT_HEADER*)region.constData(); + const FPT_HEADER* ptHeader = (const FPT_HEADER*)region.constData(); UINT32 romBypassVectorSize = 0; - if (region.left(sizeof(UINT32)) != ME_FPT_HEADER_SIGNATURE) { + if (region.left(sizeof(UINT32)) != FPT_HEADER_SIGNATURE) { // Adjust the header to skip ROM bypass vector romBypassVectorSize = ME_ROM_BYPASS_VECTOR_SIZE; - ptHeader = (const ME_FPT_HEADER*)(region.constData() + romBypassVectorSize); + ptHeader = (const FPT_HEADER*)(region.constData() + romBypassVectorSize); } // Check region size again - UINT32 ptBodySize = ptHeader->NumEntries * sizeof(ME_FPT_ENTRY); - UINT32 ptSize = romBypassVectorSize + sizeof(ME_FPT_HEADER) + ptBodySize; + UINT32 ptBodySize = ptHeader->NumEntries * sizeof(FPT_HEADER_ENTRY); + UINT32 ptSize = romBypassVectorSize + sizeof(FPT_HEADER) + ptBodySize; if ((UINT32)region.size() < ptSize) { msg(usprintf("%s: ME region too small to fit partition table", __FUNCTION__), parent); return U_INVALID_ME_PARTITION_TABLE; } // Recalculate checksum - UByteArray tempHeader = UByteArray((const char*)ptHeader, sizeof(ME_FPT_HEADER)); - ME_FPT_HEADER* tempPtHeader = (ME_FPT_HEADER*)tempHeader.data(); + UByteArray tempHeader = UByteArray((const char*)ptHeader, sizeof(FPT_HEADER)); + FPT_HEADER* tempPtHeader = (FPT_HEADER*)tempHeader.data(); tempPtHeader->Checksum = 0; - UINT8 calculated = calculateChecksum8((const UINT8*)tempPtHeader, sizeof(ME_FPT_HEADER)); + UINT8 calculated = calculateChecksum8((const UINT8*)tempPtHeader, sizeof(FPT_HEADER)); bool msgInvalidPtHeaderChecksum = (calculated != ptHeader->Checksum); // Get info - UByteArray header = region.left(romBypassVectorSize + sizeof(ME_FPT_HEADER)); + UByteArray header = region.left(romBypassVectorSize + sizeof(FPT_HEADER)); UByteArray body = region.mid(header.size(), ptBodySize); UString name = UString("FPT partition table"); @@ -307,32 +163,32 @@ USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex & } // Add partition table entries - std::vector partitions; + std::vector partitions; UINT32 offset = header.size(); - const ME_FPT_ENTRY* firstPtEntry = (const ME_FPT_ENTRY*)(region.constData() + offset); + const FPT_HEADER_ENTRY* firstPtEntry = (const FPT_HEADER_ENTRY*)(region.constData() + offset); for (UINT8 i = 0; i < ptHeader->NumEntries; i++) { // Populate entry header - const ME_FPT_ENTRY* ptEntry = firstPtEntry + i; + const FPT_HEADER_ENTRY* ptEntry = firstPtEntry + i; // Get info - name = usprintf("%c%c%c%c", ptEntry->PartitionName[0], ptEntry->PartitionName[1], ptEntry->PartitionName[2], ptEntry->PartitionName[3]); + name = usprintf("%c%c%c%c", ptEntry->Name[0], ptEntry->Name[1], ptEntry->Name[2], ptEntry->Name[3]); info = usprintf("Full size: %Xh (%u)\nPartition offset: %Xh\nPartition length: %Xh\nPartition type: %02Xh", - sizeof(ME_FPT_ENTRY), sizeof(ME_FPT_ENTRY), + sizeof(FPT_HEADER_ENTRY), sizeof(FPT_HEADER_ENTRY), ptEntry->Offset, - ptEntry->Length, - ptEntry->PartitionType); + ptEntry->Size, + ptEntry->Type); // Add tree item - const UINT8 type = (ptEntry->Offset != 0 && ptEntry->Offset != 0xFFFFFFFF && ptEntry->Length != 0 && ptEntry->EntryValid != 0xFF) ? Subtypes::ValidFptEntry : Subtypes::InvalidFptEntry; - UModelIndex entryIndex = model->addItem(offset, Types::FptEntry, type, name, UString(), info, UByteArray(), UByteArray((const char*)ptEntry, sizeof(ME_FPT_ENTRY)), UByteArray(), Fixed, index); + const UINT8 type = (ptEntry->Offset != 0 && ptEntry->Offset != 0xFFFFFFFF && ptEntry->Size != 0 && ptEntry->EntryValid != 0xFF) ? Subtypes::ValidFptEntry : Subtypes::InvalidFptEntry; + UModelIndex entryIndex = model->addItem(offset, Types::FptEntry, type, name, UString(), info, UByteArray(), UByteArray((const char*)ptEntry, sizeof(FPT_HEADER_ENTRY)), UByteArray(), Fixed, index); // Adjust offset - offset += sizeof(ME_FPT_ENTRY); + offset += sizeof(FPT_HEADER_ENTRY); // Add valid partitions if (type == Subtypes::ValidFptEntry) { // Skip absent and invalid partitions // Add to partitions vector - ME_FPT_PARTITION_INFO partition; + FPT_PARTITION_INFO partition; partition.type = Types::FptPartition; partition.ptEntry = *ptEntry; partition.index = entryIndex; @@ -345,7 +201,7 @@ make_partition_table_consistent: std::sort(partitions.begin(), partitions.end()); // Check for intersections and paddings between partitions - ME_FPT_PARTITION_INFO padding; + FPT_PARTITION_INFO padding; // Check intersection with the partition table header if (partitions.front().ptEntry.Offset < ptSize) { @@ -357,16 +213,16 @@ make_partition_table_consistent: // Check for padding between partition table and the first partition else if (partitions.front().ptEntry.Offset > ptSize) { padding.ptEntry.Offset = ptSize; - padding.ptEntry.Length = partitions.front().ptEntry.Offset - ptSize; + padding.ptEntry.Size = partitions.front().ptEntry.Offset - ptSize; padding.type = Types::Padding; partitions.insert(partitions.begin(), padding); } // Check for intersections/paddings between partitions for (size_t i = 1; i < partitions.size(); i++) { - UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Length; + UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Size; // Check that current region is fully present in the image - if ((UINT32)partitions[i].ptEntry.Offset + (UINT32)partitions[i].ptEntry.Length > (UINT32)region.size()) { + if ((UINT32)partitions[i].ptEntry.Offset + (UINT32)partitions[i].ptEntry.Size > (UINT32)region.size()) { if ((UINT32)partitions[i].ptEntry.Offset >= (UINT32)region.size()) { msg(usprintf("%s: FPT partition is located outside of the opened image, skipped", __FUNCTION__), partitions[i].index); partitions.erase(partitions.begin() + i); @@ -374,21 +230,21 @@ make_partition_table_consistent: } else { msg(usprintf("%s: FPT partition can't fit into the region, truncated", __FUNCTION__), partitions[i].index); - partitions[i].ptEntry.Length = (UINT32)region.size() - (UINT32)partitions[i].ptEntry.Offset; + partitions[i].ptEntry.Size = (UINT32)region.size() - (UINT32)partitions[i].ptEntry.Offset; } } // Check for intersection with previous partition if (partitions[i].ptEntry.Offset < previousPartitionEnd) { // Check if current partition is located inside previous one - if (partitions[i].ptEntry.Offset + partitions[i].ptEntry.Length <= previousPartitionEnd) { + if (partitions[i].ptEntry.Offset + partitions[i].ptEntry.Size <= previousPartitionEnd) { msg(usprintf("%s: FPT partition is located inside another FPT partition, skipped", __FUNCTION__), partitions[i].index); partitions.erase(partitions.begin() + i); goto make_partition_table_consistent; } else { - msg(usprintf("%s: FPT partition intersects with prevous one, skipped", __FUNCTION__), + msg(usprintf("%s: FPT partition intersects with previous one, skipped", __FUNCTION__), partitions[i].index); partitions.erase(partitions.begin() + i); goto make_partition_table_consistent; @@ -398,39 +254,39 @@ make_partition_table_consistent: // Check for padding between current and previous partitions else if (partitions[i].ptEntry.Offset > previousPartitionEnd) { padding.ptEntry.Offset = previousPartitionEnd; - padding.ptEntry.Length = partitions[i].ptEntry.Offset - previousPartitionEnd; + padding.ptEntry.Size = partitions[i].ptEntry.Offset - previousPartitionEnd; padding.type = Types::Padding; - std::vector::iterator iter = partitions.begin(); + std::vector::iterator iter = partitions.begin(); std::advance(iter, i); partitions.insert(iter, padding); } } // Check for padding after the last region - if ((UINT32)partitions.back().ptEntry.Offset + (UINT32)partitions.back().ptEntry.Length < (UINT32)region.size()) { - padding.ptEntry.Offset = partitions.back().ptEntry.Offset + partitions.back().ptEntry.Length; - padding.ptEntry.Length = region.size() - padding.ptEntry.Offset; + if ((UINT32)partitions.back().ptEntry.Offset + (UINT32)partitions.back().ptEntry.Size < (UINT32)region.size()) { + padding.ptEntry.Offset = partitions.back().ptEntry.Offset + partitions.back().ptEntry.Size; + padding.ptEntry.Size = region.size() - padding.ptEntry.Offset; padding.type = Types::Padding; partitions.push_back(padding); } // Partition map is consistent for (size_t i = 0; i < partitions.size(); i++) { - UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Length); + UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Size); if (partitions[i].type == Types::FptPartition) { UModelIndex partitionIndex; // Get info - name = usprintf("%c%c%c%c", partitions[i].ptEntry.PartitionName[0], partitions[i].ptEntry.PartitionName[1], partitions[i].ptEntry.PartitionName[2], partitions[i].ptEntry.PartitionName[3]); + name = usprintf("%c%c%c%c", partitions[i].ptEntry.Name[0], partitions[i].ptEntry.Name[1], partitions[i].ptEntry.Name[2], partitions[i].ptEntry.Name[3]); info = usprintf("Full size: %Xh (%u)\nPartition type: %02Xh\n", partition.size(), partition.size(), - partitions[i].ptEntry.PartitionType); + partitions[i].ptEntry.Type); // Add tree item - UINT8 type = Subtypes::CodeFptPartition + partitions[i].ptEntry.PartitionType; + UINT8 type = Subtypes::CodeFptPartition + partitions[i].ptEntry.Type; partitionIndex = model->addItem(partitions[i].ptEntry.Offset, Types::FptPartition, type, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); - if (type == Subtypes::CodeFptPartition && partition.left(sizeof(UINT32)) == ME_CPD_SIGNATURE) { - // Parse conde partition contents - UModelIndex ptIndex; - parseCodePartitionDirectory(partition, partitions[i].ptEntry.Offset, partitionIndex, ptIndex); + if (type == Subtypes::CodeFptPartition) { + // Parse code partition contents + UModelIndex cpdIndex; + ffsParser->parseCpdRegion(partition, partitions[i].ptEntry.Offset, partitionIndex, cpdIndex); } } else if (partitions[i].type == Types::Padding) { @@ -446,211 +302,351 @@ make_partition_table_consistent: return U_SUCCESS; } -USTATUS MeParser::parseBpdtRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index) +USTATUS MeParser::parseIfwi16Region(const UByteArray & region, const UModelIndex & parent, UModelIndex & index) { - // Check region size - if ((UINT32)region.size() < sizeof(ME_BPDT_HEADER)) { - msg(usprintf("%s: BPDT region too small to fit partition table header", __FUNCTION__), parent); - return U_INVALID_ME_PARTITION_TABLE; - } - - // Populate partition table header - const ME_BPDT_HEADER* ptHeader = (const ME_BPDT_HEADER*)region.constData(); - // Check region size again - UINT32 ptBodySize = ptHeader->NumEntries * sizeof(ME_BPDT_ENTRY); - UINT32 ptSize = sizeof(ME_BPDT_HEADER) + ptBodySize; - if ((UINT32)region.size() < ptSize) { - msg(usprintf("%s: BPDT region too small to fit BPDT partition table", __FUNCTION__), parent); + if ((UINT32)region.size() < sizeof(IFWI_16_LAYOUT_HEADER)) { + msg(usprintf("%s: ME region too small to fit IFWI 1.6 layout header", __FUNCTION__), parent); return U_INVALID_ME_PARTITION_TABLE; } - // Get info - UByteArray header = region.left(sizeof(ME_BPDT_HEADER)); - UByteArray body = region.mid(sizeof(ME_BPDT_HEADER), ptBodySize); + const IFWI_16_LAYOUT_HEADER* ifwiHeader = (const IFWI_16_LAYOUT_HEADER*)region.constData(); - UString name = UString("BPDT partition table"); - UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nNumber of entries: %u\nVersion: %Xh\n" - "IFWI version: %Xh\nFITC version: %u.%u.%u.%u", - ptSize, ptSize, + // Add header + UINT32 ptSize = sizeof(IFWI_16_LAYOUT_HEADER); + UByteArray header = region.left(ptSize); + + UString name = UString("IFWI 1.6 header"); + UString info = usprintf("Full size: %Xh (%u)\n" + "Data partition offset: %Xh\nData partition size: %Xh\n" + "Boot1 partition offset: %Xh\nBoot1 partition size: %Xh\n" + "Boot2 partition offset: %Xh\nBoot2 partition size: %Xh\n" + "Boot3 partition offset: %Xh\nBoot3 partition size: %Xh\n" + "Boot4 partition offset: %Xh\nBoot4 partition size: %Xh\n" + "Boot5 partition offset: %Xh\nBoot5 partition size: %Xh\n" + "Checksum: %Xh", header.size(), header.size(), - ptBodySize, ptBodySize, - ptHeader->NumEntries, - ptHeader->Version, - ptHeader->IfwiVersion, - ptHeader->FitcMajor, ptHeader->FitcMinor, ptHeader->FitcHotfix, ptHeader->FitcBuild); - + ifwiHeader->DataPartition.Offset, ifwiHeader->DataPartition.Size, + ifwiHeader->BootPartition[0].Offset, ifwiHeader->BootPartition[0].Size, + ifwiHeader->BootPartition[1].Offset, ifwiHeader->BootPartition[1].Size, + ifwiHeader->BootPartition[2].Offset, ifwiHeader->BootPartition[2].Size, + ifwiHeader->BootPartition[3].Offset, ifwiHeader->BootPartition[3].Size, + ifwiHeader->BootPartition[4].Offset, ifwiHeader->BootPartition[4].Size, + ifwiHeader->Checksum); // Add tree item - index = model->addItem(0, Types::BpdtStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem(0, Types::IfwiHeader, 0, name, UString(), info, UByteArray(), header, UByteArray(), Fixed, parent); - // Adjust offset - UINT32 offset = sizeof(ME_FPT_HEADER); - - // Add partition table entries - std::vector partitions; - const ME_BPDT_ENTRY* firstPtEntry = (const ME_BPDT_ENTRY*)(region.constData() + sizeof(ME_BPDT_HEADER)); - for (UINT8 i = 0; i < ptHeader->NumEntries; i++) { - // Populate entry header - const ME_BPDT_ENTRY* ptEntry = firstPtEntry + i; - - // Get info - name = meBpdtEntryTypeToUString(ptEntry->Type); - info = usprintf("Full size: %Xh (%u)\nType: %Xh\nPartition offset: %Xh\nPartition length: %Xh", - sizeof(ME_BPDT_ENTRY), sizeof(ME_BPDT_ENTRY), - ptEntry->Type, - ptEntry->Offset, - ptEntry->Length) + - UString("\nSplit sub-partition first part: ") + (ptEntry->SplitSubPartitionFirstPart ? "Yes" : "No") + - UString("\nSplit sub-partition second part: ") + (ptEntry->SplitSubPartitionSecondPart ? "Yes" : "No") + - UString("\nCode sub-partition: ") + (ptEntry->CodeSubPartition ? "Yes" : "No") + - UString("\nUMA cachable: ") + (ptEntry->UmaCachable ? "Yes" : "No"); - - // Add tree item - UModelIndex entryIndex = model->addItem(offset, Types::BpdtEntry, 0, name, UString(), info, UByteArray(), UByteArray((const char*)ptEntry, sizeof(ME_BPDT_ENTRY)), UByteArray(), Fixed, index); - - // Adjust offset - offset += sizeof(ME_BPDT_ENTRY); - - if (ptEntry->Offset != 0 && ptEntry->Offset != 0xFFFFFFFF && ptEntry->Length != 0) { - // Add to partitions vector - ME_BPDT_PARTITION_INFO partition; - partition.type = Types::FptPartition; - partition.ptEntry = *ptEntry; - partition.index = entryIndex; + std::vector partitions; + // Add data partition + { + IFWI_PARTITION_INFO partition; + partition.type = Types::IfwiPartition; + partition.subtype = Subtypes::DataIfwiPartition; + partition.ptEntry = ifwiHeader->DataPartition; + partitions.push_back(partition); + } + // Add boot partitions + for (UINT8 i = 0 ; i < 4; i++) { + if (ifwiHeader->BootPartition[i].Offset != 0 && ifwiHeader->BootPartition[i].Offset != 0xFFFFFFFF) { + IFWI_PARTITION_INFO partition; + partition.type = Types::IfwiPartition; + partition.subtype = Subtypes::BootIfwiPartition; + partition.ptEntry = ifwiHeader->BootPartition[i]; partitions.push_back(partition); } } - - // Add padding if there's no partions to add - if (partitions.size() == 0) { - UByteArray partition = region.mid(ptSize); - - // Get info - name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", - partition.size(), partition.size()); - - // Add tree item - model->addItem(ptSize, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); - - return U_SUCCESS; - } make_partition_table_consistent: // Sort partitions by offset std::sort(partitions.begin(), partitions.end()); - + // Check for intersections and paddings between partitions - ME_BPDT_PARTITION_INFO padding; - + IFWI_PARTITION_INFO padding; + // Check intersection with the partition table header if (partitions.front().ptEntry.Offset < ptSize) { - msg(usprintf("%s: BPDT partition has intersection with BPDT partition table, skipped", __FUNCTION__), - partitions.front().index); + msg(usprintf("%s: IFWI partition has intersection with IFWI layout header, skipped", __FUNCTION__), index); partitions.erase(partitions.begin()); goto make_partition_table_consistent; } // Check for padding between partition table and the first partition else if (partitions.front().ptEntry.Offset > ptSize) { padding.ptEntry.Offset = ptSize; - padding.ptEntry.Length = partitions.front().ptEntry.Offset - padding.ptEntry.Offset; + padding.ptEntry.Size = partitions.front().ptEntry.Offset - ptSize; padding.type = Types::Padding; partitions.insert(partitions.begin(), padding); } // Check for intersections/paddings between partitions for (size_t i = 1; i < partitions.size(); i++) { - UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Length; - - // Check that partition is fully present in the image - if ((UINT64)partitions[i].ptEntry.Offset + (UINT64)partitions[i].ptEntry.Length > (UINT64)region.size()) { - if ((UINT64)partitions[i].ptEntry.Offset >= (UINT64)region.size()) { - msg(usprintf("%s: BPDT partition is located outside of the opened image, skipped", __FUNCTION__), partitions[i].index); + UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Size; + + // Check that current region is fully present in the image + if ((UINT32)partitions[i].ptEntry.Offset + (UINT32)partitions[i].ptEntry.Size > (UINT32)region.size()) { + if ((UINT32)partitions[i].ptEntry.Offset >= (UINT32)region.size()) { + msg(usprintf("%s: IFWI partition is located outside of the opened image, skipped", __FUNCTION__), index); partitions.erase(partitions.begin() + i); goto make_partition_table_consistent; } else { - msg(usprintf("%s: BPDT partition can't fit into it's region, truncated", __FUNCTION__), partitions[i].index); - partitions[i].ptEntry.Length = (UINT32)region.size() - (UINT32)partitions[i].ptEntry.Offset; + msg(usprintf("%s: IFWI partition can't fit into the region, truncated", __FUNCTION__), index); + partitions[i].ptEntry.Size = (UINT32)region.size() - (UINT32)partitions[i].ptEntry.Offset; } } - + // Check for intersection with previous partition if (partitions[i].ptEntry.Offset < previousPartitionEnd) { // Check if current partition is located inside previous one - if (partitions[i].ptEntry.Offset + partitions[i].ptEntry.Length <= previousPartitionEnd) { - msg(usprintf("%s: BPDT partition is located inside another BPDT partition, skipped", __FUNCTION__), - partitions[i].index); + if (partitions[i].ptEntry.Offset + partitions[i].ptEntry.Size <= previousPartitionEnd) { + msg(usprintf("%s: IFWI partition is located inside another IFWI partition, skipped", __FUNCTION__), index); partitions.erase(partitions.begin() + i); goto make_partition_table_consistent; } else { - msg(usprintf("%s: BPDT partition intersects with prevous one, skipped", __FUNCTION__), - partitions[i].index); + msg(usprintf("%s: IFWI partition intersects with previous one, skipped", __FUNCTION__), index); partitions.erase(partitions.begin() + i); goto make_partition_table_consistent; } } - + // Check for padding between current and previous partitions else if (partitions[i].ptEntry.Offset > previousPartitionEnd) { padding.ptEntry.Offset = previousPartitionEnd; - padding.ptEntry.Length = partitions[i].ptEntry.Offset - previousPartitionEnd; + padding.ptEntry.Size = partitions[i].ptEntry.Offset - previousPartitionEnd; padding.type = Types::Padding; - std::vector::iterator iter = partitions.begin(); + std::vector::iterator iter = partitions.begin(); std::advance(iter, i); partitions.insert(iter, padding); } } - // Check for padding after the last region - if ((UINT64)partitions.back().ptEntry.Offset + (UINT64)partitions.back().ptEntry.Length < (UINT64)region.size()) { - padding.ptEntry.Offset = partitions.back().ptEntry.Offset + partitions.back().ptEntry.Length; - padding.ptEntry.Length = region.size() - padding.ptEntry.Offset; + if ((UINT32)partitions.back().ptEntry.Offset + (UINT32)partitions.back().ptEntry.Size < (UINT32)region.size()) { + padding.ptEntry.Offset = partitions.back().ptEntry.Offset + partitions.back().ptEntry.Size; + padding.ptEntry.Size = region.size() - padding.ptEntry.Offset; padding.type = Types::Padding; partitions.push_back(padding); } - + // Partition map is consistent for (size_t i = 0; i < partitions.size(); i++) { - if (partitions[i].type == Types::FptPartition) { - // Get info - UString name = meBpdtEntryTypeToUString(partitions[i].ptEntry.Type); - UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Length); - UByteArray signature = partition.left(sizeof(UINT32)); - - UString info = usprintf("Full size: %Xh (%u)\nType: %Xh", - partition.size(), partition.size(), - partitions[i].ptEntry.Type) + - UString("\nSplit sub-partition first part: ") + (partitions[i].ptEntry.SplitSubPartitionFirstPart ? "Yes" : "No") + - UString("\nSplit sub-partition second part: ") + (partitions[i].ptEntry.SplitSubPartitionSecondPart ? "Yes" : "No") + - UString("\nCode sub-partition: ") + (partitions[i].ptEntry.CodeSubPartition ? "Yes" : "No") + - UString("\nUMA cachable: ") + (partitions[i].ptEntry.UmaCachable ? "Yes" : "No"); - - if (signature == ME_CPD_SIGNATURE) { - const ME_CPD_HEADER* cpdHeader = (const ME_CPD_HEADER*)partition.constData(); - name = usprintf("%c%c%c%c", cpdHeader->ShortName[0], cpdHeader->ShortName[1], cpdHeader->ShortName[2], cpdHeader->ShortName[3]); - UString text = meBpdtEntryTypeToUString(partitions[i].ptEntry.Type); - - // Add tree item - UModelIndex ptIndex = model->addItem(partitions[i].ptEntry.Offset, Types::BpdtPartition, 0, name, text, info, UByteArray(), partition, UByteArray(), Fixed, parent); - - // Parse contents - UModelIndex cpdIndex; - parseCodePartitionDirectory(partition, partitions[i].ptEntry.Offset, ptIndex, cpdIndex); + UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Size); + if (partitions[i].type == Types::IfwiPartition) { + UModelIndex partitionIndex; + if (partitions[i].subtype == Subtypes::DataIfwiPartition) { + name = "Data partition"; } - else { - // Add tree item - model->addItem(partitions[i].ptEntry.Offset, Types::BpdtEntry, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + else if (partitions[i].subtype == Subtypes::BootIfwiPartition) { + name = "Boot partition"; + } + + // Get info + info = usprintf("Full size: %Xh (%u)\n", + partition.size(), partition.size()); + + // Add tree item + partitionIndex = model->addItem(partitions[i].ptEntry.Offset, partitions[i].type, partitions[i].subtype, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + + // Parse partition further + if (partitions[i].subtype == Subtypes::DataIfwiPartition) { + UModelIndex dataPartitionFptRegionIndex; + parseFptRegion(partition, partitionIndex, dataPartitionFptRegionIndex); + } + else if (partitions[i].subtype == Subtypes::BootIfwiPartition) { + // Parse code partition contents + UModelIndex bootPartitionBpdtRegionIndex; + ffsParser->parseBpdtRegion(partition, 0, 0, partitionIndex, bootPartitionBpdtRegionIndex); } } else if (partitions[i].type == Types::Padding) { - UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Length); - // Get info name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", + info = usprintf("Full size: %Xh (%u)", partition.size(), partition.size()); + + // Add tree item + model->addItem(partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + } + } + + return U_SUCCESS; +} + +USTATUS MeParser::parseIfwi17Region(const UByteArray & region, const UModelIndex & parent, UModelIndex & index) +{ + // Check region size again + if ((UINT32)region.size() < sizeof(IFWI_17_LAYOUT_HEADER)) { + msg(usprintf("%s: ME region too small to fit IFWI 1.7 layout header", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + + const IFWI_17_LAYOUT_HEADER* ifwiHeader = (const IFWI_17_LAYOUT_HEADER*)region.constData(); + // TODO: add check for HeaderSize to be 0x40 + + // Add header + UINT32 ptSize = sizeof(IFWI_17_LAYOUT_HEADER); + UByteArray header = region.left(ptSize); + + UString name = UString("IFWI 1.7 header"); + UString info = usprintf("Full size: %Xh (%u)\n" + "Flags: %02Xh\n" + "Reserved: %02Xh\n" + "Checksum: %Xh\n" + "Data partition offset: %Xh\nData partition size: %Xh\n" + "Boot1 partition offset: %Xh\nBoot1 partition size: %Xh\n" + "Boot2 partition offset: %Xh\nBoot2 partition size: %Xh\n" + "Boot3 partition offset: %Xh\nBoot3 partition size: %Xh\n" + "Boot4 partition offset: %Xh\nBoot4 partition size: %Xh\n" + "Boot5 partition offset: %Xh\nBoot5 partition size: %Xh\n" + "Temp page offset: %Xh\nTemp page size: %Xh\n", + header.size(), header.size(), + ifwiHeader->Flags, + ifwiHeader->Reserved, + ifwiHeader->Checksum, + ifwiHeader->DataPartition.Offset, ifwiHeader->DataPartition.Size, + ifwiHeader->BootPartition[0].Offset, ifwiHeader->BootPartition[0].Size, + ifwiHeader->BootPartition[1].Offset, ifwiHeader->BootPartition[1].Size, + ifwiHeader->BootPartition[2].Offset, ifwiHeader->BootPartition[2].Size, + ifwiHeader->BootPartition[3].Offset, ifwiHeader->BootPartition[3].Size, + ifwiHeader->BootPartition[4].Offset, ifwiHeader->BootPartition[4].Size, + ifwiHeader->TempPage.Offset, ifwiHeader->TempPage.Size); + // Add tree item + index = model->addItem(0, Types::IfwiHeader, 0, name, UString(), info, UByteArray(), header, UByteArray(), Fixed, parent); + + std::vector partitions; + // Add data partition + { + IFWI_PARTITION_INFO partition; + partition.type = Types::IfwiPartition; + partition.subtype = Subtypes::DataIfwiPartition; + partition.ptEntry = ifwiHeader->DataPartition; + partitions.push_back(partition); + } + // Add boot partitions + for (UINT8 i = 0 ; i < 4; i++) { + if (ifwiHeader->BootPartition[i].Offset != 0 && ifwiHeader->BootPartition[i].Offset != 0xFFFFFFFF) { + IFWI_PARTITION_INFO partition; + partition.type = Types::IfwiPartition; + partition.subtype = Subtypes::BootIfwiPartition; + partition.ptEntry = ifwiHeader->BootPartition[i]; + partitions.push_back(partition); + } + } + // Add temp page + if (ifwiHeader->TempPage.Offset != 0 && ifwiHeader->TempPage.Offset != 0xFFFFFFFF) { + IFWI_PARTITION_INFO partition; + partition.type = Types::IfwiPartition; + partition.subtype = Subtypes::DataPadding; + partition.ptEntry = ifwiHeader->TempPage; + partitions.push_back(partition); + } + +make_partition_table_consistent: + // Sort partitions by offset + std::sort(partitions.begin(), partitions.end()); + + // Check for intersections and paddings between partitions + IFWI_PARTITION_INFO padding; + + // Check intersection with the partition table header + if (partitions.front().ptEntry.Offset < ptSize) { + msg(usprintf("%s: IFWI partition has intersection with IFWI layout header, skipped", __FUNCTION__), index); + partitions.erase(partitions.begin()); + goto make_partition_table_consistent; + } + // Check for padding between partition table and the first partition + else if (partitions.front().ptEntry.Offset > ptSize) { + padding.ptEntry.Offset = ptSize; + padding.ptEntry.Size = partitions.front().ptEntry.Offset - ptSize; + padding.type = Types::Padding; + partitions.insert(partitions.begin(), padding); + } + // Check for intersections/paddings between partitions + for (size_t i = 1; i < partitions.size(); i++) { + UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Size; + + // Check that current region is fully present in the image + if ((UINT32)partitions[i].ptEntry.Offset + (UINT32)partitions[i].ptEntry.Size > (UINT32)region.size()) { + if ((UINT32)partitions[i].ptEntry.Offset >= (UINT32)region.size()) { + msg(usprintf("%s: IFWI partition is located outside of the opened image, skipped", __FUNCTION__), index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + else { + msg(usprintf("%s: IFWI partition can't fit into the region, truncated", __FUNCTION__), index); + partitions[i].ptEntry.Size = (UINT32)region.size() - (UINT32)partitions[i].ptEntry.Offset; + } + } + + // Check for intersection with previous partition + if (partitions[i].ptEntry.Offset < previousPartitionEnd) { + // Check if current partition is located inside previous one + if (partitions[i].ptEntry.Offset + partitions[i].ptEntry.Size <= previousPartitionEnd) { + msg(usprintf("%s: IFWI partition is located inside another IFWI partition, skipped", __FUNCTION__), index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + else { + msg(usprintf("%s: IFWI partition intersects with previous one, skipped", __FUNCTION__), index); + partitions.erase(partitions.begin() + i); + goto make_partition_table_consistent; + } + } + + // Check for padding between current and previous partitions + else if (partitions[i].ptEntry.Offset > previousPartitionEnd) { + padding.ptEntry.Offset = previousPartitionEnd; + padding.ptEntry.Size = partitions[i].ptEntry.Offset - previousPartitionEnd; + padding.type = Types::Padding; + std::vector::iterator iter = partitions.begin(); + std::advance(iter, i); + partitions.insert(iter, padding); + } + } + // Check for padding after the last region + if ((UINT32)partitions.back().ptEntry.Offset + (UINT32)partitions.back().ptEntry.Size < (UINT32)region.size()) { + padding.ptEntry.Offset = partitions.back().ptEntry.Offset + partitions.back().ptEntry.Size; + padding.ptEntry.Size = region.size() - padding.ptEntry.Offset; + padding.type = Types::Padding; + partitions.push_back(padding); + } + + // Partition map is consistent + for (size_t i = 0; i < partitions.size(); i++) { + UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Size); + if (partitions[i].type == Types::IfwiPartition) { + UModelIndex partitionIndex; + if (partitions[i].subtype == Subtypes::DataIfwiPartition) { + name = "Data partition"; + + } + else if (partitions[i].subtype == Subtypes::BootIfwiPartition){ + name = "Boot partition"; + } + else { + name = "Temp page"; + } + + // Get info + info = usprintf("Full size: %Xh (%u)\n", partition.size(), partition.size()); + // Add tree item + partitionIndex = model->addItem(partitions[i].ptEntry.Offset, partitions[i].type, partitions[i].subtype, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + + // Parse partition further + if (partitions[i].subtype == Subtypes::DataIfwiPartition) { + UModelIndex dataPartitionFptRegionIndex; + parseFptRegion(partition, partitionIndex, dataPartitionFptRegionIndex); + } + else if (partitions[i].subtype == Subtypes::BootIfwiPartition) { + // Parse code partition contents + UModelIndex bootPartitionBpdtRegionIndex; + ffsParser->parseBpdtRegion(partition, 0, 0, partitionIndex, bootPartitionBpdtRegionIndex); + } + } + else if (partitions[i].type == Types::Padding) { + // Get info + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", partition.size(), partition.size()); + // Add tree item model->addItem(partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); } @@ -659,511 +655,5 @@ make_partition_table_consistent: return U_SUCCESS; } - -USTATUS MeParser::parseCodePartitionDirectory(const UByteArray & directory, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) -{ - // Check directory size - if ((UINT32)directory.size() < sizeof(ME_CPD_HEADER)) { - msg(usprintf("%s: CPD too small to fit partition table header", __FUNCTION__), parent); - return U_INVALID_ME_PARTITION_TABLE; - } - - // Populate partition table header - const ME_CPD_HEADER* cpdHeader = (const ME_CPD_HEADER*)directory.constData(); - - // Check directory size again - UINT32 ptBodySize = cpdHeader->NumEntries * sizeof(ME_BPDT_CPD_ENTRY); - UINT32 ptSize = sizeof(ME_CPD_HEADER) + ptBodySize; - if ((UINT32)directory.size() < ptSize) { - msg(usprintf("%s: CPD too small to fit partition table", __FUNCTION__), parent); - return U_INVALID_ME_PARTITION_TABLE; - } - - // Get info - UByteArray header = directory.left(sizeof(ME_CPD_HEADER)); - UByteArray body = directory.mid(sizeof(ME_CPD_HEADER)); - UString name = usprintf("CPD partition table"); - UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nNumber of entries: %u\n" - "Header version: %u\nEntry version: %u\nHeader checksum: %02Xh", - directory.size(), directory.size(), - header.size(), header.size(), - body.size(), body.size(), - cpdHeader->NumEntries, - cpdHeader->HeaderVersion, - cpdHeader->EntryVersion, - cpdHeader->HeaderChecksum); - - // Add tree item - index = model->addItem(localOffset, Types::CpdStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); - - // Add partition table entries - std::vector partitions; - UINT32 offset = sizeof(ME_CPD_HEADER); - const ME_BPDT_CPD_ENTRY* firstCpdEntry = (const ME_BPDT_CPD_ENTRY*)(body.constData()); - for (UINT32 i = 0; i < cpdHeader->NumEntries; i++) { - // Populate entry header - const ME_BPDT_CPD_ENTRY* cpdEntry = firstCpdEntry + i; - UByteArray entry((const char*)cpdEntry, sizeof(ME_BPDT_CPD_ENTRY)); - - // Get info - name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c", - cpdEntry->EntryName[0], cpdEntry->EntryName[1], cpdEntry->EntryName[2], cpdEntry->EntryName[3], - cpdEntry->EntryName[4], cpdEntry->EntryName[5], cpdEntry->EntryName[6], cpdEntry->EntryName[7], - cpdEntry->EntryName[8], cpdEntry->EntryName[9], cpdEntry->EntryName[10], cpdEntry->EntryName[11]); - info = usprintf("Full size: %Xh (%u)\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ", - entry.size(), entry.size(), - cpdEntry->Offset.Offset, - cpdEntry->Length) - + (cpdEntry->Offset.HuffmanCompressed ? "Yes" : "No"); - - // Add tree item - UModelIndex entryIndex = model->addItem(offset, Types::CpdEntry, 0, name, UString(), info, UByteArray(), entry, UByteArray(), Fixed, index); - - // Adjust offset - offset += sizeof(ME_BPDT_CPD_ENTRY); - - if (cpdEntry->Offset.Offset != 0 && cpdEntry->Length != 0) { - // Add to partitions vector - ME_CPD_PARTITION_INFO partition; - partition.type = Types::CpdPartition; - partition.ptEntry = *cpdEntry; - partition.index = entryIndex; - partitions.push_back(partition); - } - } - - // Add padding if there's no partions to add - if (partitions.size() == 0) { - UByteArray partition = directory.mid(ptSize); - - // Get info - name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", - partition.size(), partition.size()); - - // Add tree item - model->addItem(localOffset + ptSize, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); - - return U_SUCCESS; - } - - // Sort partitions by offset - std::sort(partitions.begin(), partitions.end()); - - // Because lenghts for all Huffmann-compressed partitions mean nothing at all, we need to split all partitions into 2 classes: - // 1. CPD manifest (should be the first) - // 2. Metadata entries (should begin right after partition manifest and end before any code partition) - UINT32 i = 1; - while (i < partitions.size()) { - name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c", - partitions[i].ptEntry.EntryName[0], partitions[i].ptEntry.EntryName[1], partitions[i].ptEntry.EntryName[2], partitions[i].ptEntry.EntryName[3], - partitions[i].ptEntry.EntryName[4], partitions[i].ptEntry.EntryName[5], partitions[i].ptEntry.EntryName[6], partitions[i].ptEntry.EntryName[7], - partitions[i].ptEntry.EntryName[8], partitions[i].ptEntry.EntryName[9], partitions[i].ptEntry.EntryName[10], partitions[i].ptEntry.EntryName[11]); - - // Check if the current entry is metadata entry - if (!name.contains(".met")) { - // No need to parse further, all metadata partitions are parsed - break; - } - - // Parse into data block, find Module Attributes extension, and get compressed size from there - UINT32 offset = 0; - UINT32 length = 0xFFFFFFFF; // Special guardian value - UByteArray partition = directory.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length); - while (offset < (UINT32)partition.size()) { - const ME_CPD_EXTENTION_HEADER* extHeader = (const ME_CPD_EXTENTION_HEADER*) (partition.constData() + offset); - if (extHeader->Length <= ((UINT32)partition.size() - offset)) { - if (extHeader->Type == 10) { //TODO: replace with defines - const ME_CPD_EXT_MODULE_ATTRIBUTES* attrHeader = (const ME_CPD_EXT_MODULE_ATTRIBUTES*)(partition.constData() + offset); - length = attrHeader->CompressedSize; - } - offset += extHeader->Length; - } - else break; - } - - // Search down for corresponding code partition - // Construct it's name by replacing last 4 non-zero butes of the name with zeros - UINT32 j = 0; - for (UINT32 k = 11; k > 0 && j < 4; k--) { - if (name[k] != '\x00') { - name[k] = '\x00'; - j++; - } - } - - // Search - j = i + 1; - while (j < partitions.size()) { - if (name == usprintf("%c%c%c%c%c%c%c%c%c%c%c%c", - partitions[j].ptEntry.EntryName[0], partitions[j].ptEntry.EntryName[1], partitions[j].ptEntry.EntryName[2], partitions[j].ptEntry.EntryName[3], - partitions[j].ptEntry.EntryName[4], partitions[j].ptEntry.EntryName[5], partitions[j].ptEntry.EntryName[6], partitions[j].ptEntry.EntryName[7], - partitions[j].ptEntry.EntryName[8], partitions[j].ptEntry.EntryName[9], partitions[j].ptEntry.EntryName[10], partitions[j].ptEntry.EntryName[11])) { - // Found it, update it's Length if needed - if (partitions[j].ptEntry.Offset.HuffmanCompressed) { - partitions[j].ptEntry.Length = length; - } - else if (length != 0xFFFFFFFF && partitions[j].ptEntry.Length != length) { - msg(usprintf("%s: partition size mismatch between partition table (%Xh) and partition metadata (%Xh)", __FUNCTION__, - partitions[j].ptEntry.Length, length), partitions[j].index); - partitions[j].ptEntry.Length = length; // Believe metadata - } - // No need to search further - break; - } - // Check the next partition - j++; - } - // Check the next partition - i++; - } - -make_partition_table_consistent: - // Sort partitions by offset - std::sort(partitions.begin(), partitions.end()); - - // Check for intersections and paddings between partitions - ME_CPD_PARTITION_INFO padding; - - // Check intersection with the partition table header - if (partitions.front().ptEntry.Offset.Offset < ptSize) { - msg(usprintf("%s: CPD partition has intersection with CPD partition table, skipped", __FUNCTION__), - partitions.front().index); - partitions.erase(partitions.begin()); - goto make_partition_table_consistent; - } - // Check for padding between partition table and the first partition - else if (partitions.front().ptEntry.Offset.Offset > ptSize) { - padding.ptEntry.Offset.Offset = ptSize; - padding.ptEntry.Length = partitions.front().ptEntry.Offset.Offset - padding.ptEntry.Offset.Offset; - padding.type = Types::Padding; - partitions.insert(partitions.begin(), padding); - } - // Check for intersections/paddings between partitions - for (size_t i = 1; i < partitions.size(); i++) { - UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset.Offset + partitions[i - 1].ptEntry.Length; - - // Check that current region is fully present in the image - if ((UINT64)partitions[i].ptEntry.Offset.Offset + (UINT64)partitions[i].ptEntry.Length > (UINT64)directory.size()) { - if ((UINT64)partitions[i].ptEntry.Offset.Offset >= (UINT64)directory.size()) { - msg(usprintf("%s: CPD partition is located outside of the opened image, skipped", __FUNCTION__), partitions[i].index); - partitions.erase(partitions.begin() + i); - goto make_partition_table_consistent; - } - else { - msg(usprintf("%s: CPD partition can't fit into it's region, truncated", __FUNCTION__), partitions[i].index); - partitions[i].ptEntry.Length = (UINT32)directory.size() - (UINT32)partitions[i].ptEntry.Offset.Offset; - } - } - - // Check for intersection with previous partition - if (partitions[i].ptEntry.Offset.Offset < previousPartitionEnd) { - // Check if current partition is located inside previous one - if (partitions[i].ptEntry.Offset.Offset + partitions[i].ptEntry.Length <= previousPartitionEnd) { - msg(usprintf("%s: CPD partition is located inside another CPD partition, skipped", __FUNCTION__), - partitions[i].index); - partitions.erase(partitions.begin() + i); - goto make_partition_table_consistent; - } - else { - msg(usprintf("%s: CPD partition intersects with prevous one, skipped", __FUNCTION__), - partitions[i].index); - partitions.erase(partitions.begin() + i); - goto make_partition_table_consistent; - } - } - // Check for padding between current and previous partitions - else if (partitions[i].ptEntry.Offset.Offset > previousPartitionEnd) { - padding.ptEntry.Offset.Offset = previousPartitionEnd; - padding.ptEntry.Length = partitions[i].ptEntry.Offset.Offset - previousPartitionEnd; - padding.type = Types::Padding; - std::vector::iterator iter = partitions.begin(); - std::advance(iter, i); - partitions.insert(iter, padding); - } - } - // Check for padding after the last region - if ((UINT64)partitions.back().ptEntry.Offset.Offset + (UINT64)partitions.back().ptEntry.Length < (UINT64)directory.size()) { - padding.ptEntry.Offset.Offset = partitions.back().ptEntry.Offset.Offset + partitions.back().ptEntry.Length; - padding.ptEntry.Length = (UINT32)directory.size() - padding.ptEntry.Offset.Offset; - padding.type = Types::Padding; - partitions.push_back(padding); - } - - // Partition map is consistent - for (size_t i = 0; i < partitions.size(); i++) { - if (partitions[i].type == Types::CpdPartition) { - UByteArray partition = directory.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length); - - // Get info - name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c", - partitions[i].ptEntry.EntryName[0], partitions[i].ptEntry.EntryName[1], partitions[i].ptEntry.EntryName[2], partitions[i].ptEntry.EntryName[3], - partitions[i].ptEntry.EntryName[4], partitions[i].ptEntry.EntryName[5], partitions[i].ptEntry.EntryName[6], partitions[i].ptEntry.EntryName[7], - partitions[i].ptEntry.EntryName[8], partitions[i].ptEntry.EntryName[9], partitions[i].ptEntry.EntryName[10], partitions[i].ptEntry.EntryName[11]); - - // It's a manifest - if (name.contains(".man")) { - if (!partitions[i].ptEntry.Offset.HuffmanCompressed - && partitions[i].ptEntry.Length >= sizeof(ME_CPD_MANIFEST_HEADER)) { - const ME_CPD_MANIFEST_HEADER* manifestHeader = (const ME_CPD_MANIFEST_HEADER*) partition.constData(); - if (manifestHeader->HeaderId == ME_MANIFEST_HEADER_ID) { - UByteArray header = partition.left(manifestHeader->HeaderLength * sizeof(UINT32)); - UByteArray body = partition.mid(header.size()); - - info += usprintf( - "\nHeader type: %u\nHeader length: %Xh (%u)\nHeader version: %Xh\nFlags: %08Xh\nVendor: %Xh\n" - "Date: %Xh\nSize: %Xh (%u)\nVersion: %u.%u.%u.%u\nSecurity version number: %u\nModulus size: %Xh (%u)\nExponent size: %Xh (%u)", - manifestHeader->HeaderType, - manifestHeader->HeaderLength * sizeof(UINT32), manifestHeader->HeaderLength * sizeof(UINT32), - manifestHeader->HeaderVersion, - manifestHeader->Flags, - manifestHeader->Vendor, - manifestHeader->Date, - manifestHeader->Size * sizeof(UINT32), manifestHeader->Size * sizeof(UINT32), - manifestHeader->VersionMajor, manifestHeader->VersionMinor, manifestHeader->VersionBugfix, manifestHeader->VersionBuild, - manifestHeader->SecurityVersion, - manifestHeader->ModulusSize * sizeof(UINT32), manifestHeader->ModulusSize * sizeof(UINT32), - manifestHeader->ExponentSize * sizeof(UINT32), manifestHeader->ExponentSize * sizeof(UINT32)); - - // Add tree item - UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::ManifestCpdPartition, name, UString(), info, header, body, UByteArray(), Fixed, parent); - - // Parse data as extensions area - parseExtensionsArea(partitionIndex); - } - } - } - // It's a metadata - else if (name.contains(".met")) { - info = usprintf("Full size: %Xh (%u)\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ", - partition.size(), partition.size(), - partitions[i].ptEntry.Offset.Offset, - partitions[i].ptEntry.Length) - + (partitions[i].ptEntry.Offset.HuffmanCompressed ? "Yes" : "No"); - - // Calculate SHA256 hash over the metadata and add it to it's info - UByteArray hash(SHA256_DIGEST_SIZE, '\x00'); - sha256(partition.constData(), partition.size(), hash.data()); - info += UString("\nMetadata hash: ") + UString(hash.toHex().constData()); - - // Add three item - UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::MetadataCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); - - // Parse data as extensions area - parseExtensionsArea(partitionIndex); - } - // It's a key - else if (name.contains(".key")) { - info = usprintf("Full size: %Xh (%u)\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ", - partition.size(), partition.size(), - partitions[i].ptEntry.Offset.Offset, - partitions[i].ptEntry.Length) - + (partitions[i].ptEntry.Offset.HuffmanCompressed ? "Yes" : "No"); - - // Calculate SHA256 hash over the key and add it to it's info - UByteArray hash(SHA256_DIGEST_SIZE, '\x00'); - sha256(partition.constData(), partition.size(), hash.data()); - info += UString("\nHash: ") + UString(hash.toHex().constData()); - - // Add three item - UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::KeyCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); - - // Parse data as extensions area - parseExtensionsArea(partitionIndex); - } - // It's a code - else { - info = usprintf("Full size: %Xh (%u)\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ", - partition.size(), partition.size(), - partitions[i].ptEntry.Offset.Offset, - partitions[i].ptEntry.Length) - + (partitions[i].ptEntry.Offset.HuffmanCompressed ? "Yes" : "No"); - - // Calculate SHA256 hash over the code and add it to it's info - UByteArray hash(SHA256_DIGEST_SIZE, '\x00'); - sha256(partition.constData(), partition.size(), hash.data()); - info += UString("\nHash: ") + UString(hash.toHex().constData()); - - model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::CodeCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); - } - } - else if (partitions[i].type == Types::Padding) { - UByteArray partition = directory.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length); - - // Get info - name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", partition.size(), partition.size()); - - // Add tree item - model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); - } - else { - msg(usprintf("%s: CPD partition of unknown type found", __FUNCTION__), parent); - return U_INVALID_ME_PARTITION_TABLE; - } - } - - return U_SUCCESS; -} - -USTATUS MeParser::parseExtensionsArea(const UModelIndex & index) -{ - if (!index.isValid()) { - return U_INVALID_PARAMETER; - } - - UByteArray body = model->body(index); - UINT32 offset = 0; - while (offset < (UINT32)body.size()) { - const ME_CPD_EXTENTION_HEADER* extHeader = (const ME_CPD_EXTENTION_HEADER*) (body.constData() + offset); - if (extHeader->Length <= ((UINT32)body.size() - offset)) { - UByteArray partition = body.mid(offset, extHeader->Length); - - UString name = meExtensionTypeToUstring(extHeader->Type); - UString info = usprintf("Full size: %Xh (%u)\nType: %Xh", partition.size(), partition.size(), extHeader->Type); - - // Parse Signed Package Info a bit further - bool parsed = false; - if (extHeader->Type == 15) { - UByteArray header = partition.left(sizeof(ME_CPD_EXT_SIGNED_PACKAGE_INFO)); - UByteArray data = partition.mid(header.size()); - - const ME_CPD_EXT_SIGNED_PACKAGE_INFO* infoHeader = (const ME_CPD_EXT_SIGNED_PACKAGE_INFO*)header.constData(); - - info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %Xh\n" - "Package name: %c%c%c%c\nVersion control number: %Xh\nSecurity version number: %Xh\n" - "Usage bitmap: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", - partition.size(), partition.size(), - header.size(), header.size(), - body.size(), body.size(), - infoHeader->ExtensionType, - infoHeader->PackageName[0], infoHeader->PackageName[1], infoHeader->PackageName[2], infoHeader->PackageName[3], - infoHeader->Vcn, - infoHeader->Svn, - infoHeader->UsageBitmap[0], infoHeader->UsageBitmap[1], infoHeader->UsageBitmap[2], infoHeader->UsageBitmap[3], - infoHeader->UsageBitmap[4], infoHeader->UsageBitmap[5], infoHeader->UsageBitmap[6], infoHeader->UsageBitmap[7], - infoHeader->UsageBitmap[8], infoHeader->UsageBitmap[9], infoHeader->UsageBitmap[10], infoHeader->UsageBitmap[11], - infoHeader->UsageBitmap[12], infoHeader->UsageBitmap[13], infoHeader->UsageBitmap[14], infoHeader->UsageBitmap[15]); - - // Add tree item - UModelIndex infoIndex = model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, header, data, UByteArray(), Fixed, index); - parseSignedPackageInfoData(infoIndex); - parsed = true; - } - - // Parse IFWI Partition Manifest a bit further - else if (extHeader->Type == 22) { - const ME_CPD_EXT_IFWI_PARTITION_MANIFEST* attrHeader = (const ME_CPD_EXT_IFWI_PARTITION_MANIFEST*)partition.constData(); - - // This hash is stored reversed, because why the hell not - // Need to reverse it back to normal - UByteArray hash((const char*)&attrHeader->CompletePartitionHash, sizeof(attrHeader->CompletePartitionHash)); - std::reverse(hash.begin(), hash.end()); - - info = usprintf("Full size: %Xh (%u)\nType: %Xh\n" - "Partition name: %c%c%c%c\nPartition length: %Xh\nPartition version major: %Xh\nPartition version minor: %Xh\n" - "Data format version: %Xh\nInstance ID: %Xh\nHash algorithm: %Xh\nHash size: %Xh\nAction on update: %Xh", - partition.size(), partition.size(), - attrHeader->ExtensionType, - attrHeader->PartitionName[0], attrHeader->PartitionName[1], attrHeader->PartitionName[2], attrHeader->PartitionName[3], - attrHeader->CompletePartitionLength, - attrHeader->PartitionVersionMajor, attrHeader->PartitionVersionMinor, - attrHeader->DataFormatVersion, - attrHeader->InstanceId, - attrHeader->HashAlgorithm, - attrHeader->HashSize, - attrHeader->ActionOnUpdate) - + UString("\nSupport multiple instances: ") + (attrHeader->SupportMultipleInstances ? "Yes" : "No") - + UString("\nSupport API version based update: ") + (attrHeader->SupportApiVersionBasedUpdate ? "Yes" : "No") - + UString("\nObey full update rules: ") + (attrHeader->ObeyFullUpdateRules ? "Yes" : "No") - + UString("\nIFR enable only: ") + (attrHeader->IfrEnableOnly ? "Yes" : "No") - + UString("\nAllow cross point update: ") + (attrHeader->AllowCrossPointUpdate ? "Yes" : "No") - + UString("\nAllow cross hotfix update: ") + (attrHeader->AllowCrossHotfixUpdate ? "Yes" : "No") - + UString("\nPartial update only: ") + (attrHeader->PartialUpdateOnly ? "Yes" : "No") - + UString("\nPartition hash: ") + UString(hash.toHex().constData()); - - // Add tree item - model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index); - parsed = true; - } - - // Parse Module Attributes a bit further - else if (extHeader->Type == 10) { - const ME_CPD_EXT_MODULE_ATTRIBUTES* attrHeader = (const ME_CPD_EXT_MODULE_ATTRIBUTES*)partition.constData(); - - // This hash is stored reversed, because why the hell not - // Need to reverse it back to normal - UByteArray hash((const char*)&attrHeader->ImageHash, sizeof(attrHeader->ImageHash)); - std::reverse(hash.begin(), hash.end()); - - info = usprintf("Full size: %Xh (%u)\nType: %Xh\n" - "Compression type: %Xh\nUncompressed size: %Xh (%u)\nCompressed size: %Xh (%u)\nGlobal module ID: %Xh\nImage hash: ", - partition.size(), partition.size(), - attrHeader->ExtensionType, - attrHeader->CompressionType, - attrHeader->UncompressedSize, attrHeader->UncompressedSize, - attrHeader->CompressedSize, attrHeader->CompressedSize, - attrHeader->GlobalModuleId) + UString(hash.toHex().constData()); - - // Add tree item - model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index); - parsed = true; - } - - if (!parsed) { - // Add tree item, if needed - model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index); - } - - offset += extHeader->Length; - } - else break; - // TODO: add padding at the end - } - - return U_SUCCESS; -} - -USTATUS MeParser::parseSignedPackageInfoData(const UModelIndex & index) -{ - if (!index.isValid()) { - return U_INVALID_PARAMETER; - } - - UByteArray body = model->body(index); - UINT32 offset = 0; - while (offset < (UINT32)body.size()) { - const ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES* moduleHeader = (const ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES*)(body.constData() + offset); - if (sizeof(ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES) <= ((UINT32)body.size() - offset)) { - UByteArray module((const char*)moduleHeader,sizeof(ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES)); - - UString name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c", - moduleHeader->Name[0], moduleHeader->Name[1], moduleHeader->Name[2], moduleHeader->Name[3], - moduleHeader->Name[4], moduleHeader->Name[5], moduleHeader->Name[6], moduleHeader->Name[7], - moduleHeader->Name[8], moduleHeader->Name[9], moduleHeader->Name[10],moduleHeader->Name[11]); - - // This hash is stored reversed, because why the hell not - // Need to reverse it back to normal - UByteArray hash((const char*)&moduleHeader->MetadataHash, sizeof(moduleHeader->MetadataHash)); - std::reverse(hash.begin(), hash.end()); - - UString info = usprintf("Full size: %X (%u)\nType: %Xh\nHash algorithm: %Xh\nHash size: %Xh (%u)\nMetadata size: %Xh (%u)\nMetadata hash: ", - module.size(), module.size(), - moduleHeader->Type, - moduleHeader->HashAlgorithm, - moduleHeader->HashSize, moduleHeader->HashSize, - moduleHeader->MetadataSize, moduleHeader->MetadataSize) + UString(hash.toHex().constData()); - // Add tree otem - model->addItem(offset, Types::CpdSpiEntry, 0, name, UString(), info, UByteArray(), module, UByteArray(), Fixed, index); - - offset += module.size(); - } - else break; - // TODO: add padding at the end - } - - return U_SUCCESS; -} - #endif // U_ENABLE_ME_PARSING_SUPPORT diff --git a/common/meparser.h b/common/meparser.h index cc69c64..07a28e4 100755 --- a/common/meparser.h +++ b/common/meparser.h @@ -21,208 +21,15 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "ustring.h" #include "ubytearray.h" #include "treemodel.h" +#include "ffsparser.h" #include "sha256.h" #ifdef U_ENABLE_ME_PARSING_SUPPORT - -// FPT -#define ME_ROM_BYPASS_VECTOR_SIZE 0x10 - -const UByteArray ME_FPT_HEADER_SIGNATURE("\x24\x46\x50\x54", 4); //$FPT - -typedef struct ME_FPT_HEADER_ { - UINT32 Signature; - UINT32 NumEntries; - UINT8 HeaderVersion; - UINT8 EntryVersion; - UINT8 HeaderLength; - UINT8 Checksum; // One bit for Redundant before IFWI - UINT16 TicksToAdd; - UINT16 TokensToAdd; - UINT32 UmaSize; // Flags in SPS - UINT32 FlashLayout; // Crc32 before IFWI - UINT16 FitcMajor; - UINT16 FitcMinor; - UINT16 FitcHotfix; - UINT16 FitcBuild; -} ME_FPT_HEADER; - -typedef struct ME_FPT_ENTRY_{ - CHAR8 PartitionName[4]; - UINT8 Reserved1; - UINT32 Offset; - UINT32 Length; - UINT8 Reserved2[12]; - UINT32 PartitionType : 7; - UINT32 CopyToDramCache : 1; - UINT32 Reserved3 : 7; - UINT32 BuiltWithLength1 : 1; - UINT32 BuiltWithLength2 : 1; - UINT32 Reserved4 : 7; - UINT32 EntryValid : 8; -} ME_FPT_ENTRY; - - -// IFWI -typedef struct ME_IFWI_LAYOUT_HEADER_ { - UINT8 RomBypassVector[16]; - UINT32 DataPartitionOffset; - UINT32 DataPartitionSize; - UINT32 Boot1Offset; - UINT32 Boot1Size; - UINT32 Boot2Offset; - UINT32 Boot2Size; - UINT32 Boot3Offset; - UINT32 Boot3Size; -} ME_IFWI_LAYOUT_HEADER; - - -// BPDT -const UByteArray ME_BPDT_GREEN_SIGNATURE("\xAA\x55\x00\x00", 4); //0x000055AA -const UByteArray ME_BPDT_YELLOW_SIGNATURE("\xAA\x55\xAA\x00", 4); //0x00AA55AA - -typedef struct ME_BPDT_HEADER_ { - UINT32 Signature; - UINT16 NumEntries; - UINT16 Version; - UINT32 Checksum; - UINT32 IfwiVersion; - UINT16 FitcMajor; - UINT16 FitcMinor; - UINT16 FitcHotfix; - UINT16 FitcBuild; -} ME_BPDT_HEADER ; - -typedef struct ME_BPDT_ENTRY_ { - UINT32 Type : 16; - UINT32 SplitSubPartitionFirstPart : 1; - UINT32 SplitSubPartitionSecondPart : 1; - UINT32 CodeSubPartition : 1; - UINT32 UmaCachable : 1; - UINT32 Reserved: 12; - UINT32 Offset; - UINT32 Length; -} ME_BPDT_ENTRY; - -// CPD -const UByteArray ME_CPD_SIGNATURE("\x24\x43\x50\x44", 4); //$CPD - -typedef struct ME_CPD_HEADER_ { - UINT32 Signature; - UINT32 NumEntries; - UINT8 HeaderVersion; - UINT8 EntryVersion; - UINT8 HeaderLength; - UINT8 HeaderChecksum; - UINT8 ShortName[4]; -} ME_CPD_HEADER; - -typedef struct ME_BPDT_CPD_ENTRY_ { - UINT8 EntryName[12]; - struct { - UINT32 Offset : 25; - UINT32 HuffmanCompressed : 1; - UINT32 Reserved : 6; - } Offset; - UINT32 Length; - UINT32 Reserved; -} ME_BPDT_CPD_ENTRY; - -typedef struct ME_CPD_MANIFEST_HEADER_ { - UINT32 HeaderType; - UINT32 HeaderLength; - UINT32 HeaderVersion; - UINT32 Flags; - UINT32 Vendor; - UINT32 Date; - UINT32 Size; - UINT32 HeaderId; - UINT32 Reserved1; - UINT16 VersionMajor; - UINT16 VersionMinor; - UINT16 VersionBugfix; - UINT16 VersionBuild; - UINT32 SecurityVersion; - UINT8 Reserved2[8]; - UINT8 Reserved3[64]; - UINT32 ModulusSize; - UINT32 ExponentSize; - //manifest_rsa_key_t public_key; - //manifest_signature_t signature; -} ME_CPD_MANIFEST_HEADER; - -typedef struct ME_CPD_EXTENTION_HEADER_ { - UINT32 Type; - UINT32 Length; -} ME_CPD_EXTENTION_HEADER; - -typedef struct ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES_ { - UINT8 Name[12]; - UINT8 Type; - UINT8 HashAlgorithm; - UINT16 HashSize; - UINT32 MetadataSize; - UINT8 MetadataHash[32]; -} ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES; - -typedef struct ME_CPD_EXT_SIGNED_PACKAGE_INFO_ { - UINT32 ExtensionType; - UINT32 ExtensionLength; - UINT8 PackageName[4]; - UINT32 Vcn; - UINT8 UsageBitmap[16]; - UINT32 Svn; - UINT8 Reserved[16]; - // ME_EXT_SIGNED_PACKAGE_INFO_MODULES Modules[]; -} ME_CPD_EXT_SIGNED_PACKAGE_INFO; - -typedef struct ME_CPD_EXT_MODULE_ATTRIBUTES_ { - UINT32 ExtensionType; - UINT32 ExtensionLength; - UINT8 CompressionType; - UINT8 Reserved[3]; - UINT32 UncompressedSize; - UINT32 CompressedSize; - UINT32 GlobalModuleId; - UINT8 ImageHash[32]; -} ME_CPD_EXT_MODULE_ATTRIBUTES; - -typedef struct ME_CPD_EXT_IFWI_PARTITION_MANIFEST_ { - UINT32 ExtensionType; - UINT32 ExtensionLength; - UINT8 PartitionName[4]; - UINT32 CompletePartitionLength; - UINT16 PartitionVersionMinor; - UINT16 PartitionVersionMajor; - UINT32 DataFormatVersion; - UINT32 InstanceId; - UINT32 SupportMultipleInstances : 1; - UINT32 SupportApiVersionBasedUpdate : 1; - UINT32 ActionOnUpdate : 2; - UINT32 ObeyFullUpdateRules : 1; - UINT32 IfrEnableOnly : 1; - UINT32 AllowCrossPointUpdate : 1; - UINT32 AllowCrossHotfixUpdate : 1; - UINT32 PartialUpdateOnly : 1; - UINT32 ReservedFlags : 23; - UINT32 HashAlgorithm : 8; - UINT32 HashSize : 24; - UINT8 CompletePartitionHash[32]; - UINT8 Reserved[20]; -} ME_CPD_EXT_IFWI_PARTITION_MANIFEST; - -#define ME_MODULE_COMPRESSION_TYPE_UNCOMPRESSED 0 -#define ME_MODULE_COMPRESSION_TYPE_HUFFMAN 1 -#define ME_MODULE_COMPRESSION_TYPE_LZMA 2 - -#define ME_MANIFEST_HEADER_ID 0x324E4D24 //$MN2 - - class MeParser { public: // Default constructor and destructor - MeParser(TreeModel* treeModel) : model(treeModel) {} + MeParser(TreeModel* treeModel, FfsParser* parser) : model(treeModel), ffsParser(parser) {} ~MeParser() {} // Returns messages @@ -232,9 +39,9 @@ public: // ME parsing USTATUS parseMeRegionBody(const UModelIndex & index); - private: TreeModel *model; + FfsParser *ffsParser; std::vector > messagesVector; void msg(const UString message, const UModelIndex index = UModelIndex()) { @@ -242,19 +49,15 @@ private: } USTATUS parseFptRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index); - USTATUS parseIfwiRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index); - USTATUS parseBpdtRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index); - - USTATUS parseCodePartitionDirectory(const UByteArray & directory, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseExtensionsArea(const UModelIndex & index); - USTATUS parseSignedPackageInfoData(const UModelIndex & index); + USTATUS parseIfwi16Region(const UByteArray & region, const UModelIndex & parent, UModelIndex & index); + USTATUS parseIfwi17Region(const UByteArray & region, const UModelIndex & parent, UModelIndex & index); }; #else class MeParser { public: // Default constructor and destructor - MeParser(TreeModel* treeModel) { U_UNUSED_PARAMETER(treeModel); } + MeParser(TreeModel* treeModel, FfsParser* parser) { U_UNUSED_PARAMETER(treeModel); U_UNUSED_PARAMETER(parser); } ~MeParser() {} // Returns messages diff --git a/common/ubytearray.h b/common/ubytearray.h index 531f4a5..6cf2710 100644 --- a/common/ubytearray.h +++ b/common/ubytearray.h @@ -86,6 +86,11 @@ public: return UByteArray(hex); } + std::basic_string::iterator begin() {return d.begin();} + std::basic_string::iterator end() {return d.end();} + std::basic_string::const_iterator begin() const {return d.begin();} + std::basic_string::const_iterator end() const {return d.end();} + private: std::basic_string d; }; @@ -96,4 +101,5 @@ inline const UByteArray operator+(const UByteArray &a1, const UByteArray &a2) } #endif // QT_CORE_LIB -#endif // UBYTEARRAY_H \ No newline at end of file +#endif // UBYTEARRAY_H +