From cb430456bf374dcd5f37cf8782ac41bb7f0a2cbb Mon Sep 17 00:00:00 2001 From: Alex Matrosov Date: Sun, 9 Oct 2016 23:05:04 -0700 Subject: [PATCH] NE Alpha 33 - human readable JEDEC ID - NVRAM parser separated from FFS parser - added support for LZMAF86 sections - solved a bug with parsing of VSS variables with invalid sizes --- README.md | 2 +- UEFIDump/CMakeLists.txt | 8 +- UEFIDump/uefidump_main.cpp | 2 +- UEFIExtract/uefiextract.pro | 3 + UEFIExtract/uefiextract_main.cpp | 2 +- UEFITool/uefitool.cpp | 2 +- UEFITool/uefitool.pro | 3 + UEFITool/uefitool_main.cpp | 4 +- common/LZMA/LzmaCompress.c | 4 +- common/descriptor.cpp | 155 ++- common/descriptor.h | 4 + common/ffs.cpp | 1 - common/ffs.h | 5 +- common/ffsbuilder.cpp | 5 + common/ffsbuilder.h | 6 +- common/ffsops.cpp | 2 + common/ffsops.h | 4 +- common/ffsparser.cpp | 1933 +----------------------------- common/ffsparser.h | 52 +- common/ffsreport.cpp | 3 + common/ffsreport.h | 9 +- common/nvram.h | 2 +- common/nvramparser.cpp | 1914 +++++++++++++++++++++++++++++ common/nvramparser.h | 90 ++ common/peimage.h | 2 +- common/treeitem.h | 4 +- common/treemodel.cpp | 1 - common/treemodel.h | 2 +- common/types.cpp | 1 - common/types.h | 3 +- common/ubytearray.h | 20 +- common/ustring.cpp | 2 +- common/utility.cpp | 17 +- common/utility.h | 5 +- qhexedit2/qhexedit.cpp | 4 +- 35 files changed, 2282 insertions(+), 1994 deletions(-) create mode 100644 common/nvramparser.cpp create mode 100644 common/nvramparser.h diff --git a/README.md b/README.md index 3854eef..964f2dc 100644 --- a/README.md +++ b/README.md @@ -53,4 +53,4 @@ You can either use [pre-built binaries for Windows and OSX](https://github.com/L * Some vendor-specific firmware update files can be opened incorrectly or can't be opened at all. This includes encrypted HP update files, Dell HDR and EXE files, some InsydeFlash FD files and so on. Enabling support for such files will require massive amount of reverse-engineering which is almost pointless because the updated image can be obtained from BIOS chip where it's already decrypted and unpacked. * Intel Firmware Interface Table (FIT) editing is not supported right now. FIT contains pointers to various image components that must be loaded before executing the first CPU instruction from the BIOS chip. Those components include CPU microcode updates, binaries and settings used by BIOS Guard and Boot Guard technologies and some other stuff. More information on FIT can be obtained [here](http://downloadmirror.intel.com/18931/eng/Intel%20TXT%20LAB%20Handout.pdf). -* Builder code is still not ready, but I'm working hard on it. +* Builder code is still not ready. diff --git a/UEFIDump/CMakeLists.txt b/UEFIDump/CMakeLists.txt index 723e0af..9054749 100644 --- a/UEFIDump/CMakeLists.txt +++ b/UEFIDump/CMakeLists.txt @@ -7,6 +7,7 @@ SET(PROJECT_SOURCES ../common/descriptor.cpp ../common/ffs.cpp ../common/nvram.cpp + ../common/nvramparser.cpp ../common/ffsparser.cpp ../common/ffsreport.cpp ../common/peimage.cpp @@ -16,7 +17,7 @@ SET(PROJECT_SOURCES ../common/LZMA/LzmaDecompress.c ../common/LZMA/SDK/C/LzmaDec.c ../common/Tiano/EfiTianoDecompress.c - ../common/ustring.cpp + ../common/ustring.cpp ../bstrlib/bstrlib.c ../bstrlib/bstrwrap.cpp ) @@ -29,7 +30,8 @@ SET(PROJECT_HEADERS ../common/me.h ../common/ffs.h ../common/fit.h - ../common/nvram.h + ../common/nvram.h + ../common/nvramparser.h ../common/ffsparser.h ../common/ffsreport.h ../common/peimage.h @@ -45,4 +47,6 @@ SET(PROJECT_HEADERS ../bstrlib/bstrwrap.h ) +ADD_DEFINITIONS(-DU_ENABLE_NVRAM_PARSING_SUPPORT) + ADD_EXECUTABLE(UEFIDump ${PROJECT_SOURCES} ${PROJECT_HEADERS}) \ No newline at end of file diff --git a/UEFIDump/uefidump_main.cpp b/UEFIDump/uefidump_main.cpp index 4fcbedf..be1a153 100644 --- a/UEFIDump/uefidump_main.cpp +++ b/UEFIDump/uefidump_main.cpp @@ -29,7 +29,7 @@ int main(int argc, char *argv[]) return (uefidumper.dump(buffer, UString(argv[1])) != U_SUCCESS); } - std::cout << "UEFIDump 0.1.1" << std::endl << std::endl + std::cout << "UEFIDump 0.1.2" << std::endl << std::endl << "Usage: UEFIDump imagefile" << std::endl; return 0; } diff --git a/UEFIExtract/uefiextract.pro b/UEFIExtract/uefiextract.pro index c9a86f7..b66f38e 100644 --- a/UEFIExtract/uefiextract.pro +++ b/UEFIExtract/uefiextract.pro @@ -5,6 +5,7 @@ TARGET = UEFIExtract TEMPLATE = app CONFIG += console CONFIG -= app_bundle +DEFINES += "U_ENABLE_NVRAM_PARSING_SUPPORT" SOURCES += \ uefiextract_main.cpp \ @@ -13,6 +14,7 @@ SOURCES += \ ../common/descriptor.cpp \ ../common/ffs.cpp \ ../common/nvram.cpp \ + ../common/nvramparser.cpp \ ../common/ffsparser.cpp \ ../common/ffsreport.cpp \ ../common/peimage.cpp \ @@ -32,6 +34,7 @@ HEADERS += \ ../common/me.h \ ../common/ffs.h \ ../common/nvram.h \ + ../common/nvramparser.h \ ../common/ffsparser.h \ ../common/ffsreport.h \ ../common/peimage.h \ diff --git a/UEFIExtract/uefiextract_main.cpp b/UEFIExtract/uefiextract_main.cpp index 1263ff7..4bdc732 100644 --- a/UEFIExtract/uefiextract_main.cpp +++ b/UEFIExtract/uefiextract_main.cpp @@ -120,7 +120,7 @@ int main(int argc, char *argv[]) } } // If parameters are different, show version and usage information - std::cout << "UEFIExtract 0.13.0" << std::endl << std::endl + std::cout << "UEFIExtract 0.13.1" << std::endl << std::endl << "Usage: UEFIExtract imagefile - generate report and dump only leaf tree items into .dump folder." << std::endl << " UEFIExtract imagefile all - generate report and dump all tree items." << std::endl << " UEFIExtract imagefile dump - only generate dump, no report needed." << std::endl diff --git a/UEFITool/uefitool.cpp b/UEFITool/uefitool.cpp index 574312e..191acfe 100644 --- a/UEFITool/uefitool.cpp +++ b/UEFITool/uefitool.cpp @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("NE Alpha32")) +version(tr("NE Alpha33")) { clipboard = QApplication::clipboard(); diff --git a/UEFITool/uefitool.pro b/UEFITool/uefitool.pro index 1e15a6e..b2832db 100644 --- a/UEFITool/uefitool.pro +++ b/UEFITool/uefitool.pro @@ -3,6 +3,7 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = UEFITool TEMPLATE = app +DEFINES += "U_ENABLE_NVRAM_PARSING_SUPPORT" HEADERS += uefitool.h \ searchdialog.h \ @@ -11,6 +12,7 @@ HEADERS += uefitool.h \ guidlineedit.h \ ffsfinder.h \ ../common/nvram.h \ + ../common/nvramparser.h \ ../common/ffsops.h \ ../common/basetypes.h \ ../common/descriptor.h \ @@ -44,6 +46,7 @@ SOURCES += uefitool_main.cpp \ guidlineedit.cpp \ ffsfinder.cpp \ ../common/nvram.cpp \ + ../common/nvramparser.cpp \ ../common/ffsops.cpp \ ../common/types.cpp \ ../common/descriptor.cpp \ diff --git a/UEFITool/uefitool_main.cpp b/UEFITool/uefitool_main.cpp index a9ac03a..ca81d24 100644 --- a/UEFITool/uefitool_main.cpp +++ b/UEFITool/uefitool_main.cpp @@ -18,8 +18,8 @@ int main(int argc, char *argv[]) { QApplication a(argc, argv); - a.setOrganizationName("CodeRush"); - a.setOrganizationDomain("coderush.me"); + a.setOrganizationName("LongSoft"); + a.setOrganizationDomain("longsoft.org"); a.setApplicationName("UEFITool"); UEFITool w; diff --git a/common/LZMA/LzmaCompress.c b/common/LZMA/LzmaCompress.c index 8ae345c..f796cfd 100644 --- a/common/LZMA/LzmaCompress.c +++ b/common/LZMA/LzmaCompress.c @@ -74,7 +74,7 @@ UINT32 *DestinationSize if (*DestinationSize < destLen) { - *DestinationSize = destLen; + *DestinationSize = (UINT32)destLen; return EFI_BUFFER_TOO_SMALL; } @@ -96,7 +96,7 @@ UINT32 *DestinationSize &SzAllocForLzma, &SzAllocForLzma); - *DestinationSize = destLen + LZMA_HEADER_SIZE; + *DestinationSize = (UINT32)(destLen + LZMA_HEADER_SIZE); SetEncodedSizeOfBuf((UINT64)SourceSize, Destination); diff --git a/common/descriptor.cpp b/common/descriptor.cpp index 5082875..c289f76 100644 --- a/common/descriptor.cpp +++ b/common/descriptor.cpp @@ -37,4 +37,157 @@ UINT32 calculateRegionSize(const UINT16 base, const UINT16 limit) if (limit) return (limit + 1 - base) * 0x1000; return 0; -} \ No newline at end of file +} + +// Return human-readable chip name for given JEDEC ID +UString jedecIdToUString(UINT8 vendorId, UINT8 deviceId0, UINT8 deviceId1) +{ + UINT32 jedecId = (UINT32)deviceId1 + ((UINT32)deviceId0 << 8) + ((UINT32)vendorId << 16); + switch (jedecId) { + // Winbond + case 0xEF3013: return UString("Winbond W25X40"); + case 0xEF3014: return UString("Winbond W25X80"); + case 0xEF3015: return UString("Winbond W25X16"); + case 0xEF3016: return UString("Winbond W25X32"); + case 0xEF3017: return UString("Winbond W25X64"); + case 0xEF4013: return UString("Winbond W25Q40"); + case 0xEF4014: return UString("Winbond W25Q80"); + case 0xEF4015: return UString("Winbond W25Q16"); + case 0xEF4016: return UString("Winbond W25Q32"); + case 0xEF4017: return UString("Winbond W25Q64"); + case 0xEF4018: return UString("Winbond W25Q128"); + case 0xEF4019: return UString("Winbond W25Q256"); + case 0xEF6015: return UString("Winbond W25Q16 1.8v"); + case 0xEF6016: return UString("Winbond W25Q32 1.8v"); + case 0xEF6017: return UString("Winbond W25Q64 1.8v"); + case 0xEF6018: return UString("Winbond W25Q128 1.8v"); + + // Macronix + case 0xC22013: return UString("Macronix MX25L40"); + case 0xC22014: return UString("Macronix MX25L80"); + case 0xC22015: + case 0xC22415: + case 0xC22515: return UString("Macronix MX25L16"); + case 0xC22016: + case 0xC22535: return UString("Macronix MX25U16"); + case 0xC22536: return UString("Macronix MX25U32"); + case 0xC22537: return UString("Macronix MX25U64"); + case 0xC22538: return UString("Macronix MX25U128"); + case 0xC22539: return UString("Macronix MX25U256"); + case 0xC25E16: return UString("Macronix MX25L32"); + case 0xC22017: + case 0xC29517: return UString("Macronix MX25L64"); + case 0xC22018: return UString("Macronix MX25L128"); + case 0xC22019: return UString("Macronix MX25L256"); + + // Micron + case 0x202014: return UString("Micron M25P80"); + case 0x202015: return UString("Micron M25P16"); + case 0x202016: return UString("Micron M25P32"); + case 0x202017: return UString("Micron M25P64"); + case 0x202018: return UString("Micron M25P128"); + case 0x204011: return UString("Micron M45PE10"); + case 0x204012: return UString("Micron M45PE20"); + case 0x204013: return UString("Micron M45PE40"); + case 0x204014: return UString("Micron M45PE80"); + case 0x204015: return UString("Micron M45PE16"); + case 0x207114: return UString("Micron M25PX80"); + case 0x207115: return UString("Micron M25PX16"); + case 0x207116: return UString("Micron M25PX32"); + case 0x207117: return UString("Micron M25PX64"); + case 0x208011: return UString("Micron M25PE10"); + case 0x208012: return UString("Micron M25PE20"); + case 0x208013: return UString("Micron M25PE40"); + case 0x208014: return UString("Micron M25PE80"); + case 0x208015: return UString("Micron M25PE16"); + case 0x20BA15: return UString("Micron N25Q016"); + case 0x20BA16: return UString("Micron N25Q032"); + case 0x20BA17: return UString("Micron N25Q064"); + case 0x20BA18: return UString("Micron N25Q128"); + case 0x20BA19: return UString("Micron N25Q256"); + case 0x20BA20: return UString("Micron N25Q512"); + case 0x20BA21: return UString("Micron N25Q00A"); + case 0x20BB15: return UString("Micron N25Q016 1.8v"); + case 0x20BB16: return UString("Micron N25Q032 1.8v"); + case 0x20BB17: return UString("Micron N25Q064 1.8v"); + case 0x20BB18: return UString("Micron MT25Q128 1.8v"); + case 0x20BB19: return UString("Micron MT25Q256 1.8v"); + case 0x20BB20: return UString("Micron MT25Q512 1.8v"); + + // Atmel + case 0x1F4500: return UString("Atmel AT26DF081"); + case 0x1F4501: return UString("Atmel AT26DF081A"); + case 0x1F4502: return UString("Atmel AT25DF081"); + case 0x1F4600: return UString("Atmel AT26DF161"); + case 0x1F4601: return UString("Atmel AT26DF161A"); + case 0x1F4602: return UString("Atmel AT25DF161"); + case 0x1F8600: return UString("Atmel AT25DQ161"); + case 0x1F4700: return UString("Atmel AT25DF321"); + case 0x1F4701: return UString("Atmel AT25DF321A"); + case 0x1F4800: return UString("Atmel AT25DF641"); + case 0x1F8800: return UString("Atmel AT25DQ641"); + + // Microchip + case 0xBF2541: return UString("Microchip SST25VF016B"); + case 0xBF254A: return UString("Microchip SST25VF032B"); + case 0xBF258D: return UString("Microchip SST25VF040B"); + case 0xBF258E: return UString("Microchip SST25VF080B"); + case 0xBF254B: return UString("Microchip SST25VF064C"); + + // EON + case 0x1C3013: return UString("EON EN25Q40"); + case 0x1C3014: return UString("EON EN25Q80"); + case 0x1C3015: return UString("EON EN25Q16"); + case 0x1C3016: return UString("EON EN25Q32"); + case 0x1C3017: return UString("EON EN25Q64"); + case 0x1C3018: return UString("EON EN25Q128"); + case 0x1C3114: return UString("EON EN25F80"); + case 0x1C3115: return UString("EON EN25F16"); + case 0x1C3116: return UString("EON EN25F32"); + case 0x1C3117: return UString("EON EN25F64"); + case 0x1C7015: return UString("EON EN25QH16"); + case 0x1C7016: return UString("EON EN25QH32"); + case 0x1C7017: return UString("EON EN25QH64"); + case 0x1C7018: return UString("EON EN25QH128"); + case 0x1C7019: return UString("EON EN25QH256"); + + // GigaDevice + case 0xC84014: return UString("GigaDevice GD25x80"); + case 0xC84015: return UString("GigaDevice GD25x16"); + case 0xC84016: return UString("GigaDevice GD25x32"); + case 0xC84017: return UString("GigaDevice GD25x64"); + case 0xC84018: return UString("GigaDevice GD25x128"); + case 0xC86017: return UString("GigaDevice GD25Lx64"); + case 0xC86018: return UString("GigaDevice GD25Lx128"); + + // Fidelix + case 0xF83215: return UString("Fidelix FM25Q16"); + case 0xF83216: return UString("Fidelix FM25Q32"); + case 0xF83217: return UString("Fidelix FM25Q64"); + case 0xF83218: return UString("Fidelix FM25Q128"); + + // Spansion + case 0x014015: return UString("Spansion S25FL116K"); + case 0x014016: return UString("Spansion S25FL132K"); + case 0x014017: return UString("Spansion S25FL164K"); + + // Amic + case 0x373015: return UString("Amic A25L016"); + case 0x373016: return UString("Amic A25L032"); + case 0x374016: return UString("Amic A25L032A"); + + // PMC + case 0x7F9D13: return UString("PMC Pm25LV080B"); + case 0x7F9D14: return UString("PMC Pm25LV016B"); + case 0x7F9D44: return UString("PMC Pm25LQ080C"); + case 0x7F9D45: return UString("PMC Pm25LQ016C"); + case 0x7F9D46: return UString("PMC Pm25LQ032C"); + + // ISSI + case 0x9D6017: return UString("ISSI Ix25LP064"); + case 0x9D6018: return UString("ISSI Ix25LP128"); + case 0x9D7018: return UString("ISSI Ix25WP128"); + } + + return UString("Unknown"); +} diff --git a/common/descriptor.h b/common/descriptor.h index fa77101..06011f3 100644 --- a/common/descriptor.h +++ b/common/descriptor.h @@ -14,6 +14,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define DESCRIPTOR_H #include "basetypes.h" +#include "ustring.h" // Make sure we use right packing rules #pragma pack(push,1) @@ -201,4 +202,7 @@ extern UINT32 calculateRegionOffset(const UINT16 base); // Calculate size of region using it's base and limit extern UINT32 calculateRegionSize(const UINT16 base, const UINT16 limit); +// Return human-readable chip name for given JEDEC ID +extern UString jedecIdToUString(UINT8 vendorId, UINT8 deviceId0, UINT8 deviceId1); + #endif // DESCRIPTOR_H diff --git a/common/ffs.cpp b/common/ffs.cpp index 29ed276..1197193 100644 --- a/common/ffs.cpp +++ b/common/ffs.cpp @@ -10,7 +10,6 @@ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ -#include "ustring.h" #include "ffs.h" // This is a workaround for the lack of static std::vector initializer before C++11 diff --git a/common/ffs.h b/common/ffs.h index 34f8141..64d46cf 100644 --- a/common/ffs.h +++ b/common/ffs.h @@ -15,9 +15,9 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include +#include "basetypes.h" #include "ubytearray.h" #include "ustring.h" -#include "basetypes.h" // Make sure we use right packing rules #pragma pack(push,1) @@ -454,6 +454,9 @@ const UByteArray EFI_GUIDED_SECTION_TIANO // A31280AD-481E-41B6-95E8-127F4C98477 const UByteArray EFI_GUIDED_SECTION_LZMA // EE4E5898-3914-4259-9D6E-DC7BD79403CF ("\x98\x58\x4E\xEE\x14\x39\x59\x42\x9D\x6E\xDC\x7B\xD7\x94\x03\xCF", 16); +const UByteArray EFI_GUIDED_SECTION_LZMAF86 // D42AE6BD-1352-4BFB-909A-CA72A6EAE889 +("\xBD\xE6\x2A\xD4\x52\x13\xFB\x4B\x90\x9A\xCA\x72\xA6\xEA\xE8\x89", 16); + const UByteArray EFI_FIRMWARE_CONTENTS_SIGNED_GUID // 0F9D89E8-9259-4F76-A5AF-0C89E34023DF ("\xE8\x89\x9D\x0F\x59\x92\x76\x4F\xA5\xAF\x0C\x89\xE3\x40\x23\xDF", 16); diff --git a/common/ffsbuilder.cpp b/common/ffsbuilder.cpp index 1b4b9a1..e2d6d4c 100644 --- a/common/ffsbuilder.cpp +++ b/common/ffsbuilder.cpp @@ -12,6 +12,11 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #include "ffsbuilder.h" +#include "descriptor.h" +#include "ffs.h" +#include "peimage.h" +#include "utility.h" + USTATUS FfsBuilder::erase(const UModelIndex & index, UByteArray & erased) { // Sanity check diff --git a/common/ffsbuilder.h b/common/ffsbuilder.h index 0669ec2..70f126e 100644 --- a/common/ffsbuilder.h +++ b/common/ffsbuilder.h @@ -16,14 +16,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include +#include "basetypes.h" #include "ubytearray.h" #include "ustring.h" -#include "basetypes.h" #include "treemodel.h" -#include "descriptor.h" -#include "ffs.h" -#include "peimage.h" -#include "utility.h" class FfsBuilder { diff --git a/common/ffsops.cpp b/common/ffsops.cpp index 27cd0f4..f34bb65 100644 --- a/common/ffsops.cpp +++ b/common/ffsops.cpp @@ -12,6 +12,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #include "ffsops.h" +#include "ffs.h" +#include "utility.h" USTATUS FfsOperations::extract(const UModelIndex & index, UString & name, UByteArray & extracted, const UINT8 mode) { diff --git a/common/ffsops.h b/common/ffsops.h index b9a7862..4208cef 100644 --- a/common/ffsops.h +++ b/common/ffsops.h @@ -16,12 +16,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include +#include "basetypes.h" #include "ubytearray.h" #include "ustring.h" -#include "basetypes.h" #include "treemodel.h" -#include "ffs.h" -#include "utility.h" class FfsOperations { diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index 9a1e17d..5c92a1a 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -13,9 +13,20 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "ffsparser.h" #include -#include #include -#include +#include +#include + +#include "descriptor.h" +#include "ffs.h" +#include "gbe.h" +#include "me.h" +#include "fit.h" +#include "nvram.h" +#include "utility.h" +#include "peimage.h" +#include "parsingdata.h" +#include "types.h" // Region info structure definition struct REGION_INFO { @@ -556,8 +567,10 @@ USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 p info += UString("\nFlash chips in VSCC table:"); UINT8 vsscTableSize = upperMap->VsccTableSize * sizeof(UINT32) / sizeof(VSCC_TABLE_ENTRY); for (int i = 0; i < vsscTableSize; i++) { - info += usprintf("\n%02X%02X%02Xh", - vsccTableEntry->VendorId, vsccTableEntry->DeviceId0, vsccTableEntry->DeviceId1); + info += usprintf("\n%02X%02X%02X (", + vsccTableEntry->VendorId, vsccTableEntry->DeviceId0, vsccTableEntry->DeviceId1) + + jedecIdToUString(vsccTableEntry->VendorId, vsccTableEntry->DeviceId0, vsccTableEntry->DeviceId1) + + UString(")"); vsccTableEntry++; } @@ -790,15 +803,6 @@ USTATUS FfsParser::parseBiosRegion(const UByteArray & bios, const UINT32 parentO return parseRawArea(index); } -UINT8 FfsParser::getPaddingType(const UByteArray & padding) -{ - if (padding.count('\x00') == padding.size()) - return Subtypes::ZeroPadding; - if (padding.count('\xFF') == padding.size()) - return Subtypes::OnePadding; - return Subtypes::DataPadding; -} - USTATUS FfsParser::parseRawArea(const UModelIndex & index) { // Sanity check @@ -1166,12 +1170,6 @@ USTATUS FfsParser::findNextVolume(const UModelIndex & index, const UByteArray & volumeHeader->FvLength), index); continue; } - if (volumeHeader->Reserved != 0xFF && volumeHeader->Reserved != 0x00) { - msg(usprintf("findNextVolume: volume candidate at offset %Xh skipped, has invalid Reserved byte value %02Xh", - parentOffset + (nextIndex - EFI_FV_SIGNATURE_OFFSET), - volumeHeader->Reserved), index); - continue; - } if (volumeHeader->Revision != 1 && volumeHeader->Revision != 2) { msg(usprintf("findNextVolume: volume candidate at offset %Xh skipped, has invalid Revision byte value %02Xh", parentOffset + (nextIndex - EFI_FV_SIGNATURE_OFFSET) @@ -1261,7 +1259,7 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) // Parse VSS NVRAM volumes with a dedicated function if (model->subtype(index) == Subtypes::NvramVolume) - return parseNvramVolumeBody(index); + return nvramParser.parseNvramVolumeBody(index); if (pdata.ffsVersion != 2 && pdata.ffsVersion != 3) // Don't parse unknown volumes return U_SUCCESS; @@ -1616,7 +1614,7 @@ USTATUS FfsParser::parseFileBody(const UModelIndex & index) // Parse NVAR store if (UByteArray((const char*)&pdata.file.guid, sizeof(EFI_GUID)) == NVRAM_NVAR_STORE_FILE_GUID) - return parseNvarStore(index); + return nvramParser.parseNvarStore(index); return parseRawArea(index); } @@ -1996,7 +1994,7 @@ USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UI } // No need to change dataOffset here } - else if (baGuid == EFI_GUIDED_SECTION_LZMA || baGuid == EFI_GUIDED_SECTION_TIANO) { + else if (baGuid == EFI_GUIDED_SECTION_LZMA || baGuid == EFI_GUIDED_SECTION_LZMAF86 || baGuid == EFI_GUIDED_SECTION_TIANO) { if ((attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == 0) { // Check that ProcessingRequired attribute is set on compressed GUIDed sections msgNoProcessingRequiredAttributeCompressed = true; } @@ -2393,8 +2391,9 @@ USTATUS FfsParser::parseGuidedSectionBody(const UModelIndex & index) UString info; bool parseCurrentSection = true; UINT8 algorithm = COMPRESSION_ALGORITHM_NONE; + UByteArray baGuid = UByteArray((const char*)&guid, sizeof(EFI_GUID)); // Tiano compressed section - if (UByteArray((const char*)&guid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_TIANO) { + if (baGuid == EFI_GUIDED_SECTION_TIANO) { algorithm = EFI_STANDARD_COMPRESSION; USTATUS result = decompress(model->body(index), algorithm, processed, efiDecompressed); if (result) { @@ -2424,7 +2423,7 @@ USTATUS FfsParser::parseGuidedSectionBody(const UModelIndex & index) info += usprintf("\nDecompressed size: %Xh (%u)", processed.size(), processed.size()); } // LZMA compressed section - else if (UByteArray((const char*)&guid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_LZMA) { + else if (baGuid == EFI_GUIDED_SECTION_LZMA || baGuid == EFI_GUIDED_SECTION_LZMAF86) { algorithm = EFI_CUSTOMIZED_COMPRESSION; USTATUS result = decompress(model->body(index), algorithm, processed, efiDecompressed); if (result) { @@ -2669,7 +2668,7 @@ USTATUS FfsParser::parseRawSectionBody(const UModelIndex & index) } else if (parentFileGuid == NVRAM_NVAR_EXTERNAL_DEFAULTS_FILE_GUID) { // Parse NVAR area - parseNvarStore(index); + nvramParser.parseNvarStore(index); // Set parent file text model->setText(parentFile, UString("NVRAM external defaults")); @@ -3088,1887 +3087,3 @@ USTATUS FfsParser::addOffsetsRecursive(const UModelIndex & index) return U_SUCCESS; } -USTATUS FfsParser::parseNvarStore(const UModelIndex & index) -{ - // Sanity check - if (!index.isValid()) - return U_INVALID_PARAMETER; - - // Get parsing data for the current item - PARSING_DATA pdata = parsingDataFromUModelIndex(index); - UINT32 parentOffset = pdata.offset + model->header(index).size(); - - // Get item data - const UByteArray data = model->body(index); - - // Rename parent file - model->setText(model->findParentOfType(index, Types::File), UString("NVAR store")); - - UINT32 offset = 0; - UINT32 guidsInStore = 0; - const UINT8 emptyByte = pdata.emptyByte; - // Parse all entries - while (1) { - bool msgUnknownExtDataFormat = false; - bool msgExtHeaderTooLong = false; - bool msgExtDataTooShort = false; - - bool isInvalid = false; - bool isInvalidLink = false; - //bool isDataOnly = false; - bool hasExtendedHeader = false; - bool hasChecksum = false; - bool hasTimestamp = false; - bool hasHash = false; - bool hasGuidIndex = false; - - UINT32 guidIndex = 0; - UINT8 storedChecksum = 0; - UINT8 calculatedChecksum = 0; - UINT32 extendedHeaderSize = 0; - UINT8 extendedAttributes = 0; - UINT64 timestamp = 0; - UByteArray hash; - - UINT8 subtype = Subtypes::FullNvarEntry; - UString name; - UString text; - UByteArray header; - UByteArray body; - UByteArray tail; - - UINT32 guidAreaSize = guidsInStore * sizeof(EFI_GUID); - UINT32 unparsedSize = (UINT32)data.size() - offset - guidAreaSize; - - // Get entry header - const NVAR_ENTRY_HEADER* entryHeader = (const NVAR_ENTRY_HEADER*)(data.constData() + offset); - - // Check header size and signature - if (unparsedSize < sizeof(NVAR_ENTRY_HEADER) || - entryHeader->Signature != NVRAM_NVAR_ENTRY_SIGNATURE || - unparsedSize < entryHeader->Size) { - - // Check if the data left is a free space or a padding - UByteArray padding = data.mid(offset, unparsedSize); - UINT8 type; - - if ((UINT32)padding.count(emptyByte) == unparsedSize) { - // It's a free space - name = ("Free space"); - type = Types::FreeSpace; - subtype = 0; - } - else { - // Nothing is parsed yet, but the file is not empty - if (!offset) { - msg(UString("parseNvarStore: file can't be parsed as NVAR variables store"), index); - return U_SUCCESS; - } - - // It's a padding - name = UString("Padding"); - type = Types::Padding; - subtype = getPaddingType(padding); - } - // Get info - UString info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); - // Construct parsing data - pdata.offset = parentOffset + offset; - // Add tree item - model->addItem(type, subtype, name, UString(), info, UByteArray(), padding, UByteArray(), false, parsingDataToUByteArray(pdata), index); - - // Add GUID store area - UByteArray guidArea = data.right(guidAreaSize); - // Get info - name = UString("GUID store area"); - info = usprintf("Full size: %Xh (%u)\nGUIDs in store: %u", - guidArea.size(), guidArea.size(), - guidsInStore); - // Construct parsing data - pdata.offset = parentOffset + offset + padding.size(); - // Add tree item - model->addItem(Types::Padding, getPaddingType(guidArea), name, UString(), info, UByteArray(), guidArea, UByteArray(), false, parsingDataToUByteArray(pdata), index); - - return U_SUCCESS; - } - - // Contruct generic header and body - header = data.mid(offset, sizeof(NVAR_ENTRY_HEADER)); - body = data.mid(offset + sizeof(NVAR_ENTRY_HEADER), entryHeader->Size - sizeof(NVAR_ENTRY_HEADER)); - - UINT32 lastVariableFlag = pdata.emptyByte ? 0xFFFFFF : 0; - - // Set default next to predefined last value - pdata.nvar.next = lastVariableFlag; - - // Entry is marked as invalid - if ((entryHeader->Attributes & NVRAM_NVAR_ENTRY_VALID) == 0) { // Valid attribute is not set - isInvalid = true; - // Do not parse further - goto parsing_done; - } - - // Add next node information to parsing data - if (entryHeader->Next != lastVariableFlag) { - subtype = Subtypes::LinkNvarEntry; - pdata.nvar.next = entryHeader->Next; - } - - // Entry with extended header - if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_EXT_HEADER) { - hasExtendedHeader = true; - msgUnknownExtDataFormat = true; - - extendedHeaderSize = *(UINT16*)(body.constData() + body.size() - sizeof(UINT16)); - if (extendedHeaderSize > (UINT32)body.size()) { - msgExtHeaderTooLong = true; - isInvalid = true; - // Do not parse further - goto parsing_done; - } - - extendedAttributes = *(UINT8*)(body.constData() + body.size() - extendedHeaderSize); - - // Variable with checksum - if (extendedAttributes & NVRAM_NVAR_ENTRY_EXT_CHECKSUM) { - // Get stored checksum - storedChecksum = *(UINT8*)(body.constData() + body.size() - sizeof(UINT16) - sizeof(UINT8)); - - // Recalculate checksum for the variable - calculatedChecksum = 0; - // Include entry data - UINT8* start = (UINT8*)(entryHeader + 1); - for (UINT8* p = start; p < start + entryHeader->Size - sizeof(NVAR_ENTRY_HEADER); p++) { - calculatedChecksum += *p; - } - // Include entry size and flags - start = (UINT8*)&entryHeader->Size; - for (UINT8*p = start; p < start + sizeof(UINT16); p++) { - calculatedChecksum += *p; - } - // Include entry attributes - calculatedChecksum += entryHeader->Attributes; - - hasChecksum = true; - msgUnknownExtDataFormat = false; - } - - tail = body.mid(body.size() - extendedHeaderSize); - body = body.left(body.size() - extendedHeaderSize); - - // Entry with authenticated write (for SecureBoot) - if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_AUTH_WRITE) { - if ((entryHeader->Attributes & NVRAM_NVAR_ENTRY_DATA_ONLY)) {// Data only auth. variables has no hash - if ((UINT32)tail.size() < sizeof(UINT64)) { - msgExtDataTooShort = true; - isInvalid = true; - // Do not parse further - goto parsing_done; - } - - timestamp = *(UINT64*)(tail.constData() + sizeof(UINT8)); - hasTimestamp = true; - msgUnknownExtDataFormat = false; - } - else { // Full or link variable have hash - if ((UINT32)tail.size() < sizeof(UINT64) + SHA256_HASH_SIZE) { - msgExtDataTooShort = true; - isInvalid = true; - // Do not parse further - goto parsing_done; - } - - timestamp = *(UINT64*)(tail.constData() + sizeof(UINT8)); - hash = tail.mid(sizeof(UINT64) + sizeof(UINT8), SHA256_HASH_SIZE); - hasTimestamp = true; - hasHash = true; - msgUnknownExtDataFormat = false; - } - } - } - - // Entry is data-only (nameless and GUIDless entry or link) - if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_DATA_ONLY) { // Data-only attribute is set - isInvalidLink = true; - UModelIndex nvarIndex; - // Search prevously added entries for a link to this variable - for (int i = 0; i < model->rowCount(index); i++) { - nvarIndex = index.child(i, 0); - PARSING_DATA nvarPdata = parsingDataFromUModelIndex(nvarIndex); - if (nvarPdata.nvar.isValid && nvarPdata.nvar.next + nvarPdata.offset - parentOffset == offset) { // Previous link is present and valid - isInvalidLink = false; - break; - } - } - // Check if the link is valid - if (!isInvalidLink) { - // Use the name and text of the previous link - name = model->name(nvarIndex); - text = model->text(nvarIndex); - - if (entryHeader->Next == lastVariableFlag) - subtype = Subtypes::DataNvarEntry; - } - - //isDataOnly = true; - // Do not parse further - goto parsing_done; - } - - // Get entry name - { - UINT32 nameOffset = (entryHeader->Attributes & NVRAM_NVAR_ENTRY_GUID) ? sizeof(EFI_GUID) : sizeof(UINT8); // GUID can be stored with the variable or in a separate store, so there will only be an index of it - CHAR8* namePtr = (CHAR8*)(entryHeader + 1) + nameOffset; - UINT32 nameSize = 0; - if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_ASCII_NAME) { // Name is stored as ASCII string of CHAR8s - text = UString(namePtr); - nameSize = text.length() + 1; - } - else { // Name is stored as UCS2 string of CHAR16s - text = UString::fromUtf16((CHAR16*)namePtr); - nameSize = (text.length() + 1) * 2; - } - - // Get entry GUID - if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_GUID) { // GUID is strored in the variable itself - name = guidToUString(*(EFI_GUID*)(entryHeader + 1)); - } - // GUID is stored in GUID list at the end of the store - else { - guidIndex = *(UINT8*)(entryHeader + 1); - if (guidsInStore < guidIndex + 1) - guidsInStore = guidIndex + 1; - - // The list begins at the end of the store and goes backwards - const EFI_GUID* guidPtr = (const EFI_GUID*)(data.constData() + data.size()) - 1 - guidIndex; - name = guidToUString(*guidPtr); - hasGuidIndex = true; - } - - // Include name and GUID into the header and remove them from body - header = data.mid(offset, sizeof(NVAR_ENTRY_HEADER) + nameOffset + nameSize); - body = body.mid(nameOffset + nameSize); - } -parsing_done: - UString info; - - // Rename invalid entries according to their types - pdata.nvar.isValid = TRUE; - if (isInvalid) { - name = UString("Invalid"); - subtype = Subtypes::InvalidNvarEntry; - pdata.nvar.isValid = FALSE; - } - else if (isInvalidLink) { - name = UString("Invalid link"); - subtype = Subtypes::InvalidLinkNvarEntry; - pdata.nvar.isValid = FALSE; - } - else // Add GUID info for valid entries - info += UString("Variable GUID: ") + name + UString("\n"); - - // Add GUID index information - if (hasGuidIndex) - info += usprintf("GUID index: %u\n", guidIndex); - - // Add header, body and extended data info - info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)", - entryHeader->Size, entryHeader->Size, - header.size(), header.size(), - body.size(), body.size()); - - // Add attributes info - info += usprintf("\nAttributes: %02Xh", entryHeader->Attributes); - // Translate attributes to text - if (entryHeader->Attributes && entryHeader->Attributes != 0xFF) - info += UString(" (") + nvarAttributesToUString(entryHeader->Attributes) + UString(")"); - - // Add next node info - if (!isInvalid && entryHeader->Next != lastVariableFlag) - info += usprintf("\nNext node at offset: %Xh", parentOffset + offset + entryHeader->Next); - - // Add extended header info - if (hasExtendedHeader) { - info += usprintf("\nExtended header size: %Xh (%u)\nExtended attributes: %Xh (", - extendedHeaderSize, extendedHeaderSize, - extendedAttributes) + nvarExtendedAttributesToUString(extendedAttributes) + UString(")"); - - // Add checksum - if (hasChecksum) - info += usprintf("\nChecksum: %02Xh", storedChecksum) + - (calculatedChecksum ? usprintf(", invalid, should be %02Xh", 0x100 - calculatedChecksum) : UString(", valid")); - - // Add timestamp - if (hasTimestamp) - info += usprintf("\nTimestamp: %" PRIX64 "h", timestamp); - - // Add hash - if (hasHash) - info += UString("\nHash: ") + UString(hash.toHex().constData()); - } - - // Add correct offset to parsing data - pdata.offset = parentOffset + offset; - - // Add tree item - UModelIndex varIndex = model->addItem(Types::NvarEntry, subtype, name, text, info, header, body, tail, false, parsingDataToUByteArray(pdata), index); - - // Show messages - if (msgUnknownExtDataFormat) msg(UString("parseNvarStore: unknown extended data format"), varIndex); - if (msgExtHeaderTooLong) msg(usprintf("parseNvarStore: extended header size (%Xh) is greater than body size (%Xh)", - extendedHeaderSize, body.size()), varIndex); - if (msgExtDataTooShort) msg(usprintf("parseNvarStore: extended header size (%Xh) is too small for timestamp and hash", - tail.size()), varIndex); - - // Try parsing the entry data as NVAR storage if it begins with NVAR signature - if ((subtype == Subtypes::DataNvarEntry || subtype == Subtypes::FullNvarEntry) - && *(const UINT32*)body.constData() == NVRAM_NVAR_ENTRY_SIGNATURE) - parseNvarStore(varIndex); - - // Move to next exntry - offset += entryHeader->Size; - } - - return U_SUCCESS; -} - -USTATUS FfsParser::parseNvramVolumeBody(const UModelIndex & index) -{ - // Sanity check - if (!index.isValid()) - return U_INVALID_PARAMETER; - - // Get parsing data - PARSING_DATA pdata = parsingDataFromUModelIndex(index); - UINT32 parentOffset = pdata.offset + model->header(index).size(); - - // Get item data - UByteArray data = model->body(index); - - // Search for first store - USTATUS result; - UINT32 prevStoreOffset; - result = findNextStore(index, data, parentOffset, 0, prevStoreOffset); - if (result) - return result; - - // First store is not at the beginning of volume body - UString name; - UString info; - if (prevStoreOffset > 0) { - // Get info - UByteArray padding = data.left(prevStoreOffset); - name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); - - // Construct parsing data - pdata.offset = parentOffset; - - // Add tree item - model->addItem(Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), true, parsingDataToUByteArray(pdata), index); - } - - // Search for and parse all stores - UINT32 storeOffset = prevStoreOffset; - UINT32 prevStoreSize = 0; - - while (!result) - { - // Padding between stores - if (storeOffset > prevStoreOffset + prevStoreSize) { - UINT32 paddingOffset = prevStoreOffset + prevStoreSize; - UINT32 paddingSize = storeOffset - paddingOffset; - UByteArray padding = data.mid(paddingOffset, paddingSize); - - // Get info - name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); - - // Construct parsing data - pdata.offset = parentOffset + paddingOffset; - - // Add tree item - model->addItem(Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), true, parsingDataToUByteArray(pdata), index); - } - - // Get store size - UINT32 storeSize = 0; - result = getStoreSize(data, storeOffset, storeSize); - if (result) { - msg(UString("parseNvramVolumeBody: getStoreSize failed with error ") + errorCodeToUString(result), index); - return result; - } - - // Check that current store is fully present in input - if (storeSize > (UINT32)data.size() || storeOffset + storeSize > (UINT32)data.size()) { - // Mark the rest as padding and finish parsing - UByteArray padding = data.mid(storeOffset); - - // Get info - name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); - - // Construct parsing data - pdata.offset = parentOffset + storeOffset; - - // Add tree item - UModelIndex paddingIndex = model->addItem(Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), true, parsingDataToUByteArray(pdata), index); - msg(UString("parseNvramVolumeBody: one of stores inside overlaps the end of data"), paddingIndex); - - // Update variables - prevStoreOffset = storeOffset; - prevStoreSize = padding.size(); - break; - } - - UByteArray store = data.mid(storeOffset, storeSize); - // Parse current store header - UModelIndex storeIndex; - result = parseStoreHeader(store, parentOffset + storeOffset, index, storeIndex); - if (result) - msg(UString("parseNvramVolumeBody: store header parsing failed with error ") + errorCodeToUString(result), index); - - // Go to next store - prevStoreOffset = storeOffset; - prevStoreSize = storeSize; - result = findNextStore(index, data, parentOffset, storeOffset + prevStoreSize, storeOffset); - } - - // Padding/free space at the end - storeOffset = prevStoreOffset + prevStoreSize; - if ((UINT32)data.size() > storeOffset) { - UByteArray padding = data.mid(storeOffset); - UINT8 type; - UINT8 subtype; - if (padding.count(pdata.emptyByte) == padding.size()) { - // It's a free space - name = UString("Free space"); - type = Types::FreeSpace; - subtype = 0; - } - else { - // Nothing is parsed yet, but the file is not empty - if (!storeOffset) { - msg(UString("parseNvramVolumeBody: can't be parsed as NVRAM volume"), index); - return U_SUCCESS; - } - - // It's a padding - name = UString("Padding"); - type = Types::Padding; - subtype = getPaddingType(padding); - } - - // Add info - info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); - - // Construct parsing data - pdata.offset = parentOffset + storeOffset; - - // Add tree item - model->addItem(type, subtype, name, UString(), info, UByteArray(), padding, UByteArray(), true, parsingDataToUByteArray(pdata), index); - } - - // Parse bodies - for (int i = 0; i < model->rowCount(index); i++) { - UModelIndex current = index.child(i, 0); - switch (model->type(current)) { - case Types::VssStore: - case Types::FdcStore: parseVssStoreBody(current); break; - case Types::FsysStore: parseFsysStoreBody(current); break; - case Types::EvsaStore: parseEvsaStoreBody(current); break; - case Types::FlashMapStore: parseFlashMapBody(current); break; - } - } - - return U_SUCCESS; -} - -USTATUS FfsParser::findNextStore(const UModelIndex & index, const UByteArray & volume, const UINT32 parentOffset, const UINT32 storeOffset, UINT32 & nextStoreOffset) -{ - UINT32 dataSize = volume.size(); - - if (dataSize < sizeof(UINT32)) - return U_STORES_NOT_FOUND; - - UINT32 offset = storeOffset; - for (; offset < dataSize - sizeof(UINT32); offset++) { - const UINT32* currentPos = (const UINT32*)(volume.constData() + offset); - if (*currentPos == NVRAM_VSS_STORE_SIGNATURE || *currentPos == NVRAM_APPLE_SVS_STORE_SIGNATURE) { //$VSS or $SVS signatures found, perform checks - const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)currentPos; - if (vssHeader->Format != NVRAM_VSS_VARIABLE_STORE_FORMATTED) { - msg(usprintf("findNextStore: VSS store candidate at offset %Xh skipped, has invalid format %02Xh", parentOffset + offset, vssHeader->Format), index); - continue; - } - if (vssHeader->Size == 0 || vssHeader->Size == 0xFFFFFFFF) { - msg(usprintf("findNextStore: VSS store candidate at offset %Xh skipped, has invalid size %Xh", parentOffset + offset, vssHeader->Size), index); - continue; - } - // All checks passed, store found - break; - } - else if (*currentPos == NVRAM_FDC_VOLUME_SIGNATURE) { //FDC signature found - const FDC_VOLUME_HEADER* fdcHeader = (const FDC_VOLUME_HEADER*)currentPos; - if (fdcHeader->Size == 0 || fdcHeader->Size == 0xFFFFFFFF) { - msg(usprintf("findNextStore: FDC store candidate at offset %Xh skipped, has invalid size %Xh", parentOffset + offset, fdcHeader->Size), index); - continue; - } - // All checks passed, store found - break; - } - else if (*currentPos == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *currentPos == NVRAM_APPLE_GAID_STORE_SIGNATURE) { //Fsys or Gaid signature found - const APPLE_FSYS_STORE_HEADER* fsysHeader = (const APPLE_FSYS_STORE_HEADER*)currentPos; - if (fsysHeader->Size == 0 || fsysHeader->Size == 0xFFFF) { - msg(usprintf("findNextStore: Fsys store candidate at offset %Xh skipped, has invalid size %Xh", parentOffset + offset, fsysHeader->Size), index); - continue; - } - // All checks passed, store found - break; - } - else if (*currentPos == NVRAM_EVSA_STORE_SIGNATURE) { //EVSA signature found - if (offset < sizeof(UINT32)) - continue; - - const EVSA_STORE_ENTRY* evsaHeader = (const EVSA_STORE_ENTRY*)(currentPos - 1); - if (evsaHeader->Header.Type != NVRAM_EVSA_ENTRY_TYPE_STORE) { - msg(usprintf("findNextStore: EVSA store candidate at offset %Xh skipped, has invalid type %02Xh", parentOffset + offset - 4, evsaHeader->Header.Type), index); - continue; - } - if (evsaHeader->StoreSize == 0 || evsaHeader->StoreSize == 0xFFFFFFFF) { - msg(usprintf("findNextStore: EVSA store candidate at offset %Xh skipped, has invalid size %Xh", parentOffset + offset, evsaHeader->StoreSize), index); - continue; - } - // All checks passed, store found - offset -= sizeof(UINT32); - break; - } - else if (*currentPos == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *currentPos == EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1) { //Possible FTW block signature found - UByteArray guid = UByteArray(volume.constData() + offset, sizeof(EFI_GUID)); - if (guid != NVRAM_MAIN_STORE_VOLUME_GUID && guid != EDKII_WORKING_BLOCK_SIGNATURE_GUID) // Check the whole signature - continue; - - // Detect header variant based on WriteQueueSize - const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftwHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)currentPos; - if (ftwHeader->WriteQueueSize % 0x10 == 0x04) { // Header with 32 bit WriteQueueSize - if (ftwHeader->WriteQueueSize == 0 || ftwHeader->WriteQueueSize == 0xFFFFFFFF) { - msg(usprintf("findNextStore: FTW block candidate at offset %Xh skipped, has invalid body size %Xh", parentOffset + offset, ftwHeader->WriteQueueSize), index); - continue; - } - } - else if (ftwHeader->WriteQueueSize % 0x10 == 0x00) { // Header with 64 bit WriteQueueSize - const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64Header = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)currentPos; - if (ftw64Header->WriteQueueSize == 0 || ftw64Header->WriteQueueSize >= 0xFFFFFFFF) { - msg(usprintf("findNextStore: FTW block candidate at offset %Xh skipped, has invalid body size %Xh", parentOffset + offset, ftw64Header->WriteQueueSize), index); - continue; - } - } - else // Unknown header - continue; - - // All checks passed, store found - break; - } - else if (*currentPos == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1) {// Phoenix SCT flash map - UByteArray signature = UByteArray(volume.constData() + offset, NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_LENGTH); - if (signature != NVRAM_PHOENIX_FLASH_MAP_SIGNATURE) // Check the whole signature - continue; - - // All checks passed, store found - break; - } - else if (*currentPos == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE) { // Phoenix SCT CMDB store - const PHOENIX_CMDB_HEADER* cmdbHeader = (const PHOENIX_CMDB_HEADER*)currentPos; - - // Check size - if (cmdbHeader->HeaderSize != sizeof(PHOENIX_CMDB_HEADER)) - continue; - - // All checks passed, store found - break; - } - else if (*currentPos == INTEL_MICROCODE_HEADER_VERSION) {// Intel microcode - if (!INTEL_MICROCODE_HEADER_SIZES_VALID(currentPos)) // Check header sizes - continue; - - // Check reserved bytes - const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)currentPos; - bool reservedBytesValid = true; - for (UINT32 i = 0; i < sizeof(ucodeHeader->Reserved); i++) - if (ucodeHeader->Reserved[i] != INTEL_MICROCODE_HEADER_RESERVED_BYTE) { - reservedBytesValid = false; - break; - } - if (!reservedBytesValid) - continue; - - // All checks passed, store found - break; - } - else if (*currentPos == OEM_ACTIVATION_PUBKEY_MAGIC) { // SLIC pubkey - if (offset < 4 * sizeof(UINT32)) - continue; - - const OEM_ACTIVATION_PUBKEY* pubkeyHeader = (const OEM_ACTIVATION_PUBKEY*)(currentPos - 4); - // Check type - if (pubkeyHeader->Type != OEM_ACTIVATION_PUBKEY_TYPE) - continue; - - // All checks passed, store found - offset -= 4 * sizeof(UINT32); - break; - } - else if (*currentPos == OEM_ACTIVATION_MARKER_WINDOWS_FLAG_PART1) { // SLIC marker - if (offset >= dataSize - sizeof(UINT64) || - *(const UINT64*)currentPos != OEM_ACTIVATION_MARKER_WINDOWS_FLAG || - offset < 26) // Check full windows flag and structure size - continue; - - const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)(volume.constData() + offset - 26); - // Check reserved bytes - bool reservedBytesValid = true; - for (UINT32 i = 0; i < sizeof(markerHeader->Reserved); i++) - if (markerHeader->Reserved[i] != OEM_ACTIVATION_MARKER_RESERVED_BYTE) { - reservedBytesValid = false; - break; - } - if (!reservedBytesValid) - continue; - - // All checks passed, store found - offset -= 26; - break; - } - } - // No more stores found - if (offset >= dataSize - sizeof(UINT32)) - return U_STORES_NOT_FOUND; - - nextStoreOffset = offset; - - return U_SUCCESS; -} - -USTATUS FfsParser::getStoreSize(const UByteArray & data, const UINT32 storeOffset, UINT32 & storeSize) -{ - const UINT32* signature = (const UINT32*)(data.constData() + storeOffset); - if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) { - const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)signature; - storeSize = vssHeader->Size; - } - else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE) { - const FDC_VOLUME_HEADER* fdcHeader = (const FDC_VOLUME_HEADER*)signature; - storeSize = fdcHeader->Size; - } - else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *signature == NVRAM_APPLE_GAID_STORE_SIGNATURE) { - const APPLE_FSYS_STORE_HEADER* fsysHeader = (const APPLE_FSYS_STORE_HEADER*)signature; - storeSize = fsysHeader->Size; - } - else if (*(signature + 1) == NVRAM_EVSA_STORE_SIGNATURE) { - const EVSA_STORE_ENTRY* evsaHeader = (const EVSA_STORE_ENTRY*)signature; - storeSize = evsaHeader->StoreSize; - } - else if (*signature == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *signature == EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1) { - const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftwHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)signature; - if (ftwHeader->WriteQueueSize % 0x10 == 0x04) { // Header with 32 bit WriteQueueSize - storeSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) + ftwHeader->WriteQueueSize; - } - else { // Header with 64 bit WriteQueueSize - const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64Header = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)signature; - storeSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64) + (UINT32)ftw64Header->WriteQueueSize; - } - } - else if (*signature == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1) { // Phoenix SCT flash map - const PHOENIX_FLASH_MAP_HEADER* flashMapHeader = (const PHOENIX_FLASH_MAP_HEADER*)signature; - storeSize = sizeof(PHOENIX_FLASH_MAP_HEADER) + sizeof(PHOENIX_FLASH_MAP_ENTRY) * flashMapHeader->NumEntries; - } - else if (*signature == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE) { // Phoenix SCT CMDB store - storeSize = NVRAM_PHOENIX_CMDB_SIZE; // It's a predefined max size, no need to calculate - } - else if (*(signature + 4) == OEM_ACTIVATION_PUBKEY_MAGIC) { // SLIC pubkey - const OEM_ACTIVATION_PUBKEY* pubkeyHeader = (const OEM_ACTIVATION_PUBKEY*)signature; - storeSize = pubkeyHeader->Size; - } - else if (*(const UINT64*)(data.constData() + storeOffset + 26) == OEM_ACTIVATION_MARKER_WINDOWS_FLAG) { // SLIC marker - const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)signature; - storeSize = markerHeader->Size; - } - else if (*signature == INTEL_MICROCODE_HEADER_VERSION) { // Intel microcode, must be checked after SLIC marker because of the same *signature values - const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)signature; - storeSize = ucodeHeader->TotalSize; - } - return U_SUCCESS; -} - -USTATUS FfsParser::parseVssStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) -{ - const UINT32 dataSize = (const UINT32)store.size(); - - // Check store size - if (dataSize < sizeof(VSS_VARIABLE_STORE_HEADER)) { - msg(UString("parseVssStoreHeader: volume body is too small even for VSS store header"), parent); - return U_SUCCESS; - } - - // Get VSS store header - const VSS_VARIABLE_STORE_HEADER* vssStoreHeader = (const VSS_VARIABLE_STORE_HEADER*)store.constData(); - - // Check store size - if (dataSize < vssStoreHeader->Size) { - msg(usprintf("parseVssStoreHeader: VSS store size %Xh (%u) is greater than volume body size %Xh (%u)", - vssStoreHeader->Size, vssStoreHeader->Size, - dataSize, dataSize), parent); - return U_SUCCESS; - } - - // Get parsing data - PARSING_DATA pdata = parsingDataFromUModelIndex(parent); - - // Construct header and body - UByteArray header = store.left(sizeof(VSS_VARIABLE_STORE_HEADER)); - UByteArray body = store.mid(sizeof(VSS_VARIABLE_STORE_HEADER), vssStoreHeader->Size - sizeof(VSS_VARIABLE_STORE_HEADER)); - - // Add info - bool isSvsStore = (vssStoreHeader->Signature == NVRAM_APPLE_SVS_STORE_SIGNATURE); - UString name = isSvsStore ? UString("SVS store") : UString("VSS store"); - UString info = usprintf("Signature: %s\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nFormat: %02Xh\nState: %02Xh\nUnknown: %04Xh", - isSvsStore ? "$SVS" : "$VSS", - vssStoreHeader->Size, vssStoreHeader->Size, - header.size(), header.size(), - body.size(), body.size(), - vssStoreHeader->Format, - vssStoreHeader->State, - vssStoreHeader->Unknown); - - // Add correct offset - pdata.offset = parentOffset; - - // Add tree item - index = model->addItem(Types::VssStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); - - return U_SUCCESS; -} - -USTATUS FfsParser::parseFtwStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) -{ - const UINT32 dataSize = (const UINT32)store.size(); - - // Check store size - if (dataSize < sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64)) { - msg(UString("parseFtwStoreHeader: volume body is too small even for FTW store header"), parent); - return U_SUCCESS; - } - - // Get FTW block headers - const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftw32BlockHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)store.constData(); - const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64BlockHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)store.constData(); - - // Check store size - UINT32 ftwBlockSize; - bool has32bitHeader; - if (ftw32BlockHeader->WriteQueueSize % 0x10 == 0x04) { // Header with 32 bit WriteQueueSize - ftwBlockSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) + ftw32BlockHeader->WriteQueueSize; - has32bitHeader = true; - } - else { // Header with 64 bit WriteQueueSize - ftwBlockSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64) + (UINT32)ftw64BlockHeader->WriteQueueSize; - has32bitHeader = false; - } - if (dataSize < ftwBlockSize) { - msg(usprintf("parseFtwStoreHeader: FTW store size %Xh (%u) is greater than volume body size %Xh (%u)", - ftwBlockSize, ftwBlockSize, - dataSize, dataSize), parent); - return U_SUCCESS; - } - - // Get parsing data - PARSING_DATA pdata = parsingDataFromUModelIndex(parent); - - // Construct header and body - UINT32 headerSize = has32bitHeader ? sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) : sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64); - UByteArray header = store.left(headerSize); - UByteArray body = store.mid(headerSize, ftwBlockSize - headerSize); - - // Check block header checksum - UByteArray crcHeader = header; - EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* crcFtwBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)header.data(); - crcFtwBlockHeader->Crc = pdata.emptyByte ? 0xFFFFFFFF : 0; - crcFtwBlockHeader->State = pdata.emptyByte ? 0xFF : 0; - UINT32 calculatedCrc = crc32(0, (const UINT8*)crcFtwBlockHeader, headerSize); - - // Add info - UString name("FTW store"); - UString info = UString("Signature: ") + guidToUString(ftw32BlockHeader->Signature) + - usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nHeader CRC32: %08Xh", - ftwBlockSize, ftwBlockSize, - headerSize, headerSize, - body.size(), body.size(), - ftw32BlockHeader->State, - ftw32BlockHeader->Crc) + - (ftw32BlockHeader->Crc != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid")); - - // Add correct offset - pdata.offset = parentOffset; - - // Add tree item - index = model->addItem(Types::FtwStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); - - return U_SUCCESS; -} - -USTATUS FfsParser::parseFdcStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) -{ - const UINT32 dataSize = (const UINT32)store.size(); - - // Check store size - if (dataSize < sizeof(FDC_VOLUME_HEADER)) { - msg(UString("parseFdcStoreHeader: volume body is too small even for FDC store header"), parent); - return U_SUCCESS; - } - - // Get Fdc store header - const FDC_VOLUME_HEADER* fdcStoreHeader = (const FDC_VOLUME_HEADER*)store.constData(); - - // Check store size - if (dataSize < fdcStoreHeader->Size) { - msg(usprintf("parseFdcStoreHeader: FDC store size %Xh (%u) is greater than volume body size %Xh (%u)", - fdcStoreHeader->Size, fdcStoreHeader->Size, - dataSize, dataSize), parent); - return U_SUCCESS; - } - - // Determine internal volume header size - const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(fdcStoreHeader + 1); - UINT32 headerSize; - if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) { - const EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (const EFI_FIRMWARE_VOLUME_EXT_HEADER*)((const UINT8*)volumeHeader + volumeHeader->ExtHeaderOffset); - headerSize = volumeHeader->ExtHeaderOffset + extendedHeader->ExtHeaderSize; - } - else - headerSize = volumeHeader->HeaderLength; - - // Extended header end can be unaligned - headerSize = ALIGN8(headerSize); - - // Add VSS store header - headerSize += sizeof(VSS_VARIABLE_STORE_HEADER); - - // Add FDC header - headerSize += sizeof(FDC_VOLUME_HEADER); - - // Check sanity of combined header size - if (dataSize < headerSize) { - msg(usprintf("parseFdcStoreHeader: FDC store header size %Xh (%u) is greater than volume body size %Xh (%u)", - fdcStoreHeader->Size,fdcStoreHeader->Size, - dataSize, dataSize), parent); - return U_SUCCESS; - } - - // Get parsing data - PARSING_DATA pdata = parsingDataFromUModelIndex(parent); - - // Construct header and body - UByteArray header = store.left(headerSize); - UByteArray body = store.mid(headerSize, fdcStoreHeader->Size - headerSize); - - // Add info - UString name("FDC store"); - UString info = usprintf("Signature: _FDC\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)", - fdcStoreHeader->Size, fdcStoreHeader->Size, - header.size(), header.size(), - body.size(), body.size()); - - // TODO: add internal headers info - - // Add correct offset - pdata.offset = parentOffset; - - // Add tree item - index = model->addItem(Types::FdcStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); - - return U_SUCCESS; -} - -USTATUS FfsParser::parseFsysStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) -{ - const UINT32 dataSize = (const UINT32)store.size(); - - // Check store size - if (dataSize < sizeof(APPLE_FSYS_STORE_HEADER)) { - msg(UString("parseFsysStoreHeader: volume body is too small even for Fsys store header"), parent); - return U_SUCCESS; - } - - // Get Fsys store header - const APPLE_FSYS_STORE_HEADER* fsysStoreHeader = (const APPLE_FSYS_STORE_HEADER*)store.constData(); - - // Check store size - if (dataSize < fsysStoreHeader->Size) { - msg(usprintf("parseFsysStoreHeader: Fsys store size %Xh (%u) is greater than volume body size %Xh (%u)", - fsysStoreHeader->Size, fsysStoreHeader->Size, - dataSize, dataSize), parent); - return U_SUCCESS; - } - - // Get parsing data - PARSING_DATA pdata = parsingDataFromUModelIndex(parent); - - // Construct header and body - UByteArray header = store.left(sizeof(APPLE_FSYS_STORE_HEADER)); - UByteArray body = store.mid(sizeof(APPLE_FSYS_STORE_HEADER), fsysStoreHeader->Size - sizeof(APPLE_FSYS_STORE_HEADER) - sizeof(UINT32)); - - // Check store checksum - UINT32 storedCrc = *(UINT32*)store.right(sizeof(UINT32)).constData(); - UINT32 calculatedCrc = crc32(0, (const UINT8*)store.constData(), (const UINT32)store.size() - sizeof(UINT32)); - - // Add info - bool isGaidStore = (fsysStoreHeader->Signature == NVRAM_APPLE_GAID_STORE_SIGNATURE); - UString name = isGaidStore ? UString("Gaid store") : UString("Fsys store"); - UString info = usprintf("Signature: %s\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nUnknown0: %02Xh\nUnknown1: %08Xh\nCRC32: %08Xh", - isGaidStore ? "Gaid" : "Fsys", - fsysStoreHeader->Size, fsysStoreHeader->Size, - header.size(), header.size(), - body.size(), body.size(), - fsysStoreHeader->Unknown0, - fsysStoreHeader->Unknown1) - + (storedCrc != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid")); - - // Add correct offset - pdata.offset = parentOffset; - - // Add tree item - index = model->addItem(Types::FsysStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); - - return U_SUCCESS; -} - -USTATUS FfsParser::parseEvsaStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) -{ - const UINT32 dataSize = (const UINT32)store.size(); - - // Check dataSize - if (dataSize < sizeof(EVSA_STORE_ENTRY)) { - msg(UString("parseEvsaStoreHeader: volume body is too small even for EVSA store header"), parent); - return U_SUCCESS; - } - - // Get EVSA store header - const EVSA_STORE_ENTRY* evsaStoreHeader = (const EVSA_STORE_ENTRY*)store.constData(); - - // Check store size - if (dataSize < evsaStoreHeader->StoreSize) { - msg(usprintf("parseEvsaStoreHeader: EVSA store size %Xh (%u) is greater than volume body size %Xh (%u)", - evsaStoreHeader->StoreSize, evsaStoreHeader->StoreSize, - dataSize, dataSize), parent); - return U_SUCCESS; - } - - // Get parsing data - PARSING_DATA pdata = parsingDataFromUModelIndex(parent); - - // Construct header and body - UByteArray header = store.left(evsaStoreHeader->Header.Size); - UByteArray body = store.mid(evsaStoreHeader->Header.Size, evsaStoreHeader->StoreSize - evsaStoreHeader->Header.Size); - - // Recalculate checksum - UINT8 calculated = calculateChecksum8(((const UINT8*)evsaStoreHeader) + 2, evsaStoreHeader->Header.Size - 2); - - // Add info - UString name("EVSA store"); - UString info = usprintf("Signature: EVSA\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nAttributes: %08Xh\nChecksum: %02Xh", - evsaStoreHeader->StoreSize, evsaStoreHeader->StoreSize, - header.size(), header.size(), - body.size(), body.size(), - evsaStoreHeader->Header.Type, - evsaStoreHeader->Attributes, - evsaStoreHeader->Header.Checksum) + - (evsaStoreHeader->Header.Checksum != calculated ? usprintf("%, invalid, should be %02Xh", calculated) : UString(", valid")); - - // Add correct offset - pdata.offset = parentOffset; - - // Add tree item - index = model->addItem(Types::EvsaStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); - - return U_SUCCESS; -} - -USTATUS FfsParser::parseFlashMapStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) -{ - const UINT32 dataSize = (const UINT32)store.size(); - - // Check data size - if (dataSize < sizeof(PHOENIX_FLASH_MAP_HEADER)) { - msg(UString("parseFlashMapStoreHeader: volume body is too small even for FlashMap block header"), parent); - return U_SUCCESS; - } - - // Get FlashMap block header - const PHOENIX_FLASH_MAP_HEADER* flashMapHeader = (const PHOENIX_FLASH_MAP_HEADER*)store.constData(); - - // Check store size - UINT32 flashMapSize = sizeof(PHOENIX_FLASH_MAP_HEADER) + flashMapHeader->NumEntries * sizeof(PHOENIX_FLASH_MAP_ENTRY); - if (dataSize < flashMapSize) { - msg(usprintf("parseFlashMapStoreHeader: FlashMap block size %Xh (%u) is greater than volume body size %Xh (%u)", - flashMapSize, flashMapSize, - dataSize, dataSize), parent); - return U_SUCCESS; - } - - // Get parsing data - PARSING_DATA pdata = parsingDataFromUModelIndex(parent); - - // Construct header and body - UByteArray header = store.left(sizeof(PHOENIX_FLASH_MAP_HEADER)); - UByteArray body = store.mid(sizeof(PHOENIX_FLASH_MAP_HEADER), flashMapSize - sizeof(PHOENIX_FLASH_MAP_HEADER)); - - // Add info - UString name("Phoenix SCT flash map"); - UString info = usprintf("Signature: _FLASH_MAP\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nNumber of entries: %u", - flashMapSize, flashMapSize, - header.size(), header.size(), - body.size(), body.size(), - flashMapHeader->NumEntries); - - // Add correct offset - pdata.offset = parentOffset; - - // Add tree item - index = model->addItem(Types::FlashMapStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); - - return U_SUCCESS; -} - -USTATUS FfsParser::parseCmdbStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) -{ - const UINT32 dataSize = (const UINT32)store.size(); - - // Check store size - if (dataSize < sizeof(PHOENIX_CMDB_HEADER)) { - msg(UString("parseCmdbStoreHeader: volume body is too small even for CMDB store header"), parent); - return U_SUCCESS; - } - - UINT32 cmdbSize = NVRAM_PHOENIX_CMDB_SIZE; - if (dataSize < cmdbSize) { - msg(usprintf("parseCmdbStoreHeader: CMDB store size %Xh (%u) is greater than volume body size %Xh (%u)", - cmdbSize, cmdbSize, - dataSize, dataSize), parent); - return U_SUCCESS; - } - - // Get store header - const PHOENIX_CMDB_HEADER* cmdbHeader = (const PHOENIX_CMDB_HEADER*)store.constData(); - - // Get parsing data - PARSING_DATA pdata = parsingDataFromUModelIndex(parent); - - // Construct header and body - UByteArray header = store.left(cmdbHeader->TotalSize); - UByteArray body = store.mid(cmdbHeader->TotalSize, cmdbSize - cmdbHeader->TotalSize); - - // Add info - UString name("CMDB store"); - UString info = usprintf("Signature: CMDB\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)", - cmdbSize, cmdbSize, - header.size(), header.size(), - body.size(), body.size()); - - // Add correct offset - pdata.offset = parentOffset; - - // Add tree item - index = model->addItem(Types::CmdbStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); - - return U_SUCCESS; -} - -USTATUS FfsParser::parseSlicPubkeyHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) -{ - const UINT32 dataSize = (const UINT32)store.size(); - - // Check data size - if (dataSize < sizeof(OEM_ACTIVATION_PUBKEY)) { - msg(UString("parseSlicPubkeyHeader: volume body is too small even for SLIC pubkey header"), parent); - return U_SUCCESS; - } - - // Get SLIC pubkey header - const OEM_ACTIVATION_PUBKEY* pubkeyHeader = (const OEM_ACTIVATION_PUBKEY*)store.constData(); - - // Check store size - if (dataSize < pubkeyHeader->Size) { - msg(usprintf("parseSlicPubkeyHeader: SLIC pubkey size %Xh (%u) is greater than volume body size %Xh (%u)", - pubkeyHeader->Size, pubkeyHeader->Size, - dataSize, dataSize), parent); - return U_SUCCESS; - } - - // Get parsing data - PARSING_DATA pdata = parsingDataFromUModelIndex(parent); - - // Construct header and body - UByteArray header = store.left(sizeof(OEM_ACTIVATION_PUBKEY)); - - // Add info - UString name("SLIC pubkey"); - UString info = usprintf("Type: 0h\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: 0h (0)\n" - "Key type: %02Xh\nVersion: %02Xh\nAlgorithm: %08Xh\nMagic: RSA1\nBit length: %08Xh\nExponent: %08Xh", - pubkeyHeader->Size, pubkeyHeader->Size, - header.size(), header.size(), - pubkeyHeader->KeyType, - pubkeyHeader->Version, - pubkeyHeader->Algorithm, - pubkeyHeader->BitLength, - pubkeyHeader->Exponent); - - // Add correct offset - pdata.offset = parentOffset; - - // Add tree item - index = model->addItem(Types::SlicData, Subtypes::PubkeySlicData, name, UString(), info, header, UByteArray(), UByteArray(), true, parsingDataToUByteArray(pdata), parent); - - return U_SUCCESS; -} - -USTATUS FfsParser::parseSlicMarkerHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) -{ - const UINT32 dataSize = (const UINT32)store.size(); - - // Check data size - if (dataSize < sizeof(OEM_ACTIVATION_MARKER)) { - msg(UString("parseSlicMarkerHeader: volume body is too small even for SLIC marker header"), parent); - return U_SUCCESS; - } - - // Get SLIC marker header - const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)store.constData(); - - // Check store size - if (dataSize < markerHeader->Size) { - msg(usprintf("parseSlicMarkerHeader: SLIC marker size %Xh (%u) is greater than volume body size %Xh (%u)", - markerHeader->Size, markerHeader->Size, - dataSize, dataSize), parent); - return U_SUCCESS; - } - - // Get parsing data - PARSING_DATA pdata = parsingDataFromUModelIndex(parent); - - // Construct header and body - UByteArray header = store.left(sizeof(OEM_ACTIVATION_MARKER)); - - // Add info - UString name("SLIC marker"); - UString info = usprintf("Type: 1h\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: 0h (0)\n" - "Version: %08Xh\nOEM ID: %s\nOEM table ID: %s\nWindows flag: WINDOWS\nSLIC version: %08Xh", - markerHeader->Size, markerHeader->Size, - header.size(), header.size(), - markerHeader->Version, - (const char*)UString((const char*)&(markerHeader->OemId)).left(6).toLocal8Bit(), - (const char*)UString((const char*)&(markerHeader->OemTableId)).left(8).toLocal8Bit(), - markerHeader->SlicVersion); - - // Add correct offset - pdata.offset = parentOffset; - - // Add tree item - index = model->addItem(Types::SlicData, Subtypes::MarkerSlicData, name, UString(), info, header, UByteArray(), UByteArray(), true, parsingDataToUByteArray(pdata), parent); - - return U_SUCCESS; -} - -USTATUS FfsParser::parseIntelMicrocodeHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) -{ - const UINT32 dataSize = (const UINT32)store.size(); - - // Check data size - if (dataSize < sizeof(INTEL_MICROCODE_HEADER)) { - msg(UString("parseIntelMicrocodeHeader: volume body is too small even for Intel microcode header"), parent); - return U_SUCCESS; - } - - // Get Intel microcode header - const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)store.constData(); - - // Check store size - if (dataSize < ucodeHeader->TotalSize) { - msg(usprintf("parseIntelMicrocodeHeader: Intel microcode size %Xh (%u) is greater than volume body size %Xh (%u)", - ucodeHeader->TotalSize, ucodeHeader->TotalSize, - dataSize, dataSize), parent); - return U_SUCCESS; - } - - // Get parsing data - PARSING_DATA pdata = parsingDataFromUModelIndex(parent); - - // Construct header and body - UByteArray header = store.left(sizeof(INTEL_MICROCODE_HEADER)); - UByteArray body = store.mid(sizeof(INTEL_MICROCODE_HEADER), ucodeHeader->DataSize); - - //TODO: recalculate checksum - - // Add info - UString name("Intel microcode"); - UString info = usprintf("Revision: 1h\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\n" - "Date: %08Xh\nCPU signature: %08Xh\nChecksum: %08Xh\nLoader revision: %08Xh\nCPU flags: %08Xh", - ucodeHeader->TotalSize, ucodeHeader->TotalSize, - header.size(), header.size(), - body.size(), body.size(), - ucodeHeader->Date, - ucodeHeader->CpuSignature, - ucodeHeader->Checksum, - ucodeHeader->LoaderRevision, - ucodeHeader->CpuFlags); - - // Add correct offset - pdata.offset = parentOffset; - - // Add tree item - index = model->addItem(Types::Microcode, Subtypes::IntelMicrocode, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); - - return U_SUCCESS; -} - -USTATUS FfsParser::parseStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) -{ - const UINT32 dataSize = (const UINT32)store.size(); - const UINT32* signature = (const UINT32*)store.constData(); - // Check store size - if (dataSize < sizeof(UINT32)) { - msg(UString("parseStoreHeader: volume body is too small even for store signature"), parent); - return U_SUCCESS; - } - - // Check signature and run parser function needed - // VSS/SVS store - if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) - return parseVssStoreHeader(store, parentOffset, parent, index); - // FTW store - else if (*signature == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *signature == EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1) - return parseFtwStoreHeader(store, parentOffset, parent, index); - // FDC store - else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE) - return parseFdcStoreHeader(store, parentOffset, parent, index); - // Apple Fsys/Gaid store - else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *signature == NVRAM_APPLE_GAID_STORE_SIGNATURE) - return parseFsysStoreHeader(store, parentOffset, parent, index); - // EVSA store - else if (*(signature + 1) == NVRAM_EVSA_STORE_SIGNATURE) - return parseEvsaStoreHeader(store, parentOffset, parent, index); - // Phoenix SCT flash map - else if (*signature == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1) - return parseFlashMapStoreHeader(store, parentOffset, parent, index); - // Phoenix CMDB store - else if (*signature == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE) - return parseCmdbStoreHeader(store, parentOffset, parent, index); - // SLIC pubkey - else if (*(signature + 4) == OEM_ACTIVATION_PUBKEY_MAGIC) - return parseSlicPubkeyHeader(store, parentOffset, parent, index); - // SLIC marker - else if (*(const UINT64*)(store.constData() + 26) == OEM_ACTIVATION_MARKER_WINDOWS_FLAG) - return parseSlicMarkerHeader(store, parentOffset, parent, index); - // Intel microcode - // Must be checked after SLIC marker because of the same *signature values - else if (*signature == INTEL_MICROCODE_HEADER_VERSION) - return parseIntelMicrocodeHeader(store, parentOffset, parent, index); - - msg(usprintf("parseStoreHeader: don't know how to parse a header with signature %08Xh", *signature), parent); - return U_SUCCESS; -} - -USTATUS FfsParser::parseVssStoreBody(const UModelIndex & index) -{ - // Sanity check - if (!index.isValid()) - return U_INVALID_PARAMETER; - - // Get parsing data for the current item - PARSING_DATA pdata = parsingDataFromUModelIndex(index); - UINT32 parentOffset = pdata.offset + model->header(index).size(); - const UByteArray data = model->body(index); - - // Check that the is enough space for variable header - const UINT32 dataSize = (UINT32)data.size(); - if (dataSize < sizeof(VSS_VARIABLE_HEADER)) { - msg(UString("parseVssStoreBody: store body is too small even for VSS variable header"), index); - return U_SUCCESS; - } - - UINT32 offset = 0; - - // Parse all variables - while (1) { - bool isInvalid = false; - bool isAuthenticated = false; - bool isAppleCrc32 = false; - - UINT32 storedCrc32 = 0; - UINT32 calculatedCrc32 = 0; - UINT64 monotonicCounter = 0; - EFI_TIME timestamp = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - UINT32 pubKeyIndex = 0; - - UINT8 subtype = 0; - UString name; - UString text; - EFI_GUID* variableGuid; - CHAR16* variableName = (CHAR16*)L""; - UByteArray header; - UByteArray body; - - UINT32 unparsedSize = dataSize - offset; - - // Get variable header - const VSS_VARIABLE_HEADER* variableHeader = (const VSS_VARIABLE_HEADER*)(data.constData() + offset); - - // Check variable header to fit in still unparsed data - UINT32 variableSize = 0; - if (unparsedSize >= sizeof(VSS_VARIABLE_HEADER) - && variableHeader->StartId == NVRAM_VSS_VARIABLE_START_ID) { - - // Apple VSS variable with CRC32 of the data - if (variableHeader->Attributes & NVRAM_VSS_VARIABLE_APPLE_DATA_CHECKSUM) { - isAppleCrc32 = true; - if (unparsedSize < sizeof(VSS_APPLE_VARIABLE_HEADER)) { - variableSize = 0; - } - else { - const VSS_APPLE_VARIABLE_HEADER* appleVariableHeader = (const VSS_APPLE_VARIABLE_HEADER*)variableHeader; - variableSize = sizeof(VSS_APPLE_VARIABLE_HEADER) + appleVariableHeader->NameSize + appleVariableHeader->DataSize; - variableGuid = (EFI_GUID*)&appleVariableHeader->VendorGuid; - variableName = (CHAR16*)(appleVariableHeader + 1); - - header = data.mid(offset, sizeof(VSS_APPLE_VARIABLE_HEADER) + appleVariableHeader->NameSize); - body = data.mid(offset + header.size(), appleVariableHeader->DataSize); - - // Calculate CRC32 of the variable data - storedCrc32 = appleVariableHeader->DataCrc32; - calculatedCrc32 = crc32(0, (const UINT8*)body.constData(), body.size()); - } - } - - // Authenticated variable - else if ((variableHeader->Attributes & NVRAM_VSS_VARIABLE_AUTHENTICATED_WRITE_ACCESS) - || (variableHeader->Attributes & NVRAM_VSS_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) - || (variableHeader->Attributes & NVRAM_VSS_VARIABLE_APPEND_WRITE) - || (variableHeader->NameSize == 0 && variableHeader->DataSize == 0)) { // If both NameSize and DataSize are zeros, it's auth variable with zero montonic counter - isAuthenticated = true; - if (unparsedSize < sizeof(VSS_AUTH_VARIABLE_HEADER)) { - variableSize = 0; - } - else { - const VSS_AUTH_VARIABLE_HEADER* authVariableHeader = (const VSS_AUTH_VARIABLE_HEADER*)variableHeader; - variableSize = sizeof(VSS_AUTH_VARIABLE_HEADER) + authVariableHeader->NameSize + authVariableHeader->DataSize; - variableGuid = (EFI_GUID*)&authVariableHeader->VendorGuid; - variableName = (CHAR16*)(authVariableHeader + 1); - - header = data.mid(offset, sizeof(VSS_AUTH_VARIABLE_HEADER) + authVariableHeader->NameSize); - body = data.mid(offset + header.size(), authVariableHeader->DataSize); - - monotonicCounter = authVariableHeader->MonotonicCounter; - timestamp = authVariableHeader->Timestamp; - pubKeyIndex = authVariableHeader->PubKeyIndex; - } - } - - // Normal VSS variable - if (!isAuthenticated && !isAppleCrc32) { - variableSize = sizeof(VSS_VARIABLE_HEADER) + variableHeader->NameSize + variableHeader->DataSize; - variableGuid = (EFI_GUID*)&variableHeader->VendorGuid; - variableName = (CHAR16*)(variableHeader + 1); - - header = data.mid(offset, sizeof(VSS_VARIABLE_HEADER) + variableHeader->NameSize); - body = data.mid(offset + header.size(), variableHeader->DataSize); - } - - // There is also a case of authenticated Apple variables, but I haven't seen one yet - - // Check variable state - if (variableHeader->State != NVRAM_VSS_VARIABLE_ADDED && variableHeader->State != NVRAM_VSS_VARIABLE_HEADER_VALID) { - isInvalid = true; - } - } - - // Can't parse further, add the last element and break the loop - if (!variableSize) { - // Check if the data left is a free space or a padding - UByteArray padding = data.mid(offset, unparsedSize); - UINT8 type; - - if (padding.count(pdata.emptyByte) == padding.size()) { - // It's a free space - name = UString("Free space"); - type = Types::FreeSpace; - subtype = 0; - } - else { - // Nothing is parsed yet, but the store is not empty - if (!offset) { - msg(UString("parseVssStoreBody: store can't be parsed as VSS store"), index); - return U_SUCCESS; - } - - // It's a padding - name = UString("Padding"); - type = Types::Padding; - subtype = getPaddingType(padding); - } - - // Get info - UString info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); - - // Construct parsing data - pdata.offset = parentOffset + offset; - - // Add tree item - model->addItem(type, subtype, name, UString(), info, UByteArray(), padding, UByteArray(), false, parsingDataToUByteArray(pdata), index); - - return U_SUCCESS; - } - - UString info; - - // Rename invalid variables - if (isInvalid) { - name = UString("Invalid"); - } - else { // Add GUID and text for valid variables - name = guidToUString(*variableGuid); - info += UString("Variable GUID: ") + name + UString("\n"); - text = UString::fromUtf16(variableName); - } - - // Add info - info += usprintf("Full size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nAttributes: %08Xh (", - variableSize, variableSize, - header.size(), header.size(), - body.size(), body.size(), - variableHeader->State, - variableHeader->Attributes) + vssAttributesToUString(variableHeader->Attributes) + UString(")"); - - // Set subtype and add related info - if (isInvalid) - subtype = Subtypes::InvalidVssEntry; - else if (isAuthenticated) { - subtype = Subtypes::AuthVssEntry; - info += usprintf("\nMonotonic counter: %" PRIX64 "h\nTimestamp: ", monotonicCounter) + efiTimeToUString(timestamp) - + usprintf("\nPubKey index: %u", pubKeyIndex); - } - else if (isAppleCrc32) { - subtype = Subtypes::AppleVssEntry; - info += usprintf("\nData checksum: %08Xh", storedCrc32) + - (storedCrc32 != calculatedCrc32 ? usprintf(", invalid, should be %08Xh", calculatedCrc32) : UString(", valid")); - } - else - subtype = Subtypes::StandardVssEntry; - - // Add correct offset to parsing data - pdata.offset = parentOffset + offset; - - // Add tree item - model->addItem(Types::VssEntry, subtype, name, text, info, header, body, UByteArray(), false, parsingDataToUByteArray(pdata), index); - - // Move to next variable - offset += variableSize; - } - - return U_SUCCESS; -} - -USTATUS FfsParser::parseFsysStoreBody(const UModelIndex & index) -{ - // Sanity check - if (!index.isValid()) - return U_INVALID_PARAMETER; - - // Get parsing data for the current item - PARSING_DATA pdata = parsingDataFromUModelIndex(index); - UINT32 parentOffset = pdata.offset + model->header(index).size(); - const UByteArray data = model->body(index); - - // Check that the is enough space for variable header - const UINT32 dataSize = (UINT32)data.size(); - UINT32 offset = 0; - - // Parse all variables - while (1) { - UINT32 unparsedSize = dataSize - offset; - UINT32 variableSize = 0; - - // Get nameSize and name of the variable - const UINT8 nameSize = *(UINT8*)(data.constData() + offset); - // Check sanity - if (unparsedSize >= nameSize + sizeof(UINT8)) { - variableSize = nameSize + sizeof(UINT8); - } - - UByteArray name; - if (variableSize) { - name = data.mid(offset + sizeof(UINT8), nameSize); - // Check for EOF variable - if (nameSize == 3 && name[0] == 'E' && name[1] == 'O' && name[2] == 'F') { - // There is no data afterward, add EOF variable and free space and return - UByteArray header = data.mid(offset, sizeof(UINT8) + nameSize); - UString info = usprintf("Full size: %Xh (%u)", header.size(), header.size()); - - // Add correct offset to parsing data - pdata.offset = parentOffset + offset; - - // Add EOF tree item - model->addItem(Types::FsysEntry, 0, UString("EOF"), UString(), info, header, UByteArray(), UByteArray(), false, parsingDataToUByteArray(pdata), index); - - // Add free space - offset += header.size(); - unparsedSize = dataSize - offset; - UByteArray body = data.mid(offset); - info = usprintf("Full size: %Xh (%u)", body.size(), body.size()); - - // Add correct offset to parsing data - pdata.offset = parentOffset + offset; - - // Add free space tree item - model->addItem(Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), body, UByteArray(), false, parsingDataToUByteArray(pdata), index); - - return U_SUCCESS; - } - } - - // Get dataSize and data of the variable - const UINT16 dataSize = *(UINT16*)(data.constData() + offset + sizeof(UINT8) + nameSize); - if (unparsedSize >= sizeof(UINT8) + nameSize + sizeof(UINT16) + dataSize) { - variableSize = sizeof(UINT8) + nameSize + sizeof(UINT16) + dataSize; - } - else { - // Last variable is bad, add the rest as padding and return - UByteArray body = data.mid(offset); - UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size()); - - // Add correct offset to parsing data - pdata.offset = parentOffset + offset; - - // Add free space tree item - model->addItem(Types::Padding, getPaddingType(body), UString("Padding"), UString(), info, UByteArray(), body, UByteArray(), false, parsingDataToUByteArray(pdata), index); - - // Show message - msg(UString("parseFsysStoreBody: next variable appears too big, added as padding"), index); - - return U_SUCCESS; - } - - // Construct header and body - UByteArray header = data.mid(offset, sizeof(UINT8) + nameSize + sizeof(UINT16)); - UByteArray body = data.mid(offset + sizeof(UINT8) + nameSize + sizeof(UINT16), dataSize); - - // Add info - UString info = usprintf("Full size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)", - variableSize, variableSize, - header.size(), header.size(), - body.size(), body.size()); - - // Add correct offset to parsing data - pdata.offset = parentOffset + offset; - - // Add tree item - model->addItem(Types::FsysEntry, 0, UString(name.constData()), UString(), info, header, body, UByteArray(), false, parsingDataToUByteArray(pdata), index); - - // Move to next variable - offset += variableSize; - } - - return U_SUCCESS; -} - -USTATUS FfsParser::parseEvsaStoreBody(const UModelIndex & index) -{ - // Sanity check - if (!index.isValid()) - return U_INVALID_PARAMETER; - - // Get parsing data for the current item - PARSING_DATA pdata = parsingDataFromUModelIndex(index); - UINT32 parentOffset = pdata.offset + model->header(index).size(); - const UByteArray data = model->body(index); - - // Check that the is enough space for entry header - const UINT32 dataSize = (UINT32)data.size(); - UINT32 offset = 0; - - std::map guidMap; - std::map nameMap; - - // Parse all entries - UINT32 unparsedSize = dataSize; - while (unparsedSize) { - UINT32 variableSize = 0; - UString name; - UString info; - UByteArray header; - UByteArray body; - UINT8 subtype; - UINT8 calculated; - - const EVSA_ENTRY_HEADER* entryHeader = (const EVSA_ENTRY_HEADER*)(data.constData() + offset); - - // Check entry size - variableSize = sizeof(EVSA_ENTRY_HEADER); - if (unparsedSize < variableSize || unparsedSize < entryHeader->Size) { - UByteArray body = data.mid(offset); - UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size()); - - // Checke type - UString name("Free space"); - UINT8 type = Types::FreeSpace; - UINT8 subtype = 0; - if (getPaddingType(body) == Subtypes::DataPadding) { - name = UString("Padding"); - type = Types::Padding; - subtype = Subtypes::DataPadding; - } - - // Add correct offset to parsing data - pdata.offset = parentOffset + offset; - - // Add free space tree item - UModelIndex itemIndex = model->addItem(type, subtype, name, UString(), info, UByteArray(), body, UByteArray(), false, parsingDataToUByteArray(pdata), index); - - // Show message - if (type == Types::Padding) - msg(UString("parseEvsaStoreBody: variable parsing failed, rest of unparsed store added as padding"), itemIndex); - - break; - } - variableSize = entryHeader->Size; - - // Recalculate entry checksum - calculated = calculateChecksum8(((const UINT8*)entryHeader) + 2, entryHeader->Size - 2); - - // GUID entry - if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_GUID1 || - entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_GUID2) { - const EVSA_GUID_ENTRY* guidHeader = (const EVSA_GUID_ENTRY*)entryHeader; - header = data.mid(offset, sizeof(EVSA_GUID_ENTRY)); - body = data.mid(offset + sizeof(EVSA_GUID_ENTRY), guidHeader->Header.Size - sizeof(EVSA_GUID_ENTRY)); - EFI_GUID guid = *(EFI_GUID*)body.constData(); - name = guidToUString(guid); - info = UString("GUID: ") + name + usprintf("\nFull size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh", - variableSize, variableSize, - header.size(), header.size(), - body.size(), body.size(), - guidHeader->Header.Type, - guidHeader->Header.Checksum) - + (guidHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid")) - + usprintf("\nGuidId: %04Xh", guidHeader->GuidId); - subtype = Subtypes::GuidEvsaEntry; - guidMap.insert(std::pair(guidHeader->GuidId, guid)); - } - // Name entry - else if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_NAME1 || - entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_NAME2) { - const EVSA_NAME_ENTRY* nameHeader = (const EVSA_NAME_ENTRY*)entryHeader; - header = data.mid(offset, sizeof(EVSA_NAME_ENTRY)); - body = data.mid(offset + sizeof(EVSA_NAME_ENTRY), nameHeader->Header.Size - sizeof(EVSA_NAME_ENTRY)); - name = UString::fromUtf16((const CHAR16*)body.constData()); - info = UString("GUID: ") + name + usprintf("\nFull size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh", - variableSize, variableSize, - header.size(), header.size(), - body.size(), body.size(), - nameHeader->Header.Type, - nameHeader->Header.Checksum) - + (nameHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid")) - + usprintf("\nVarId: %04Xh", nameHeader->VarId); - subtype = Subtypes::NameEvsaEntry; - nameMap.insert(std::pair(nameHeader->VarId, name)); - } - // Data entry - else if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA1 || - entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA2 || - entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA_INVALID) { - const EVSA_DATA_ENTRY* dataHeader = (const EVSA_DATA_ENTRY*)entryHeader; - // Check for extended header - UINT32 headerSize = sizeof(EVSA_DATA_ENTRY); - UINT32 dataSize = dataHeader->Header.Size - sizeof(EVSA_DATA_ENTRY); - if (dataHeader->Attributes & NVRAM_EVSA_DATA_EXTENDED_HEADER) { - const EVSA_DATA_ENTRY_EXTENDED* dataHeaderExtended = (const EVSA_DATA_ENTRY_EXTENDED*)entryHeader; - headerSize = sizeof(EVSA_DATA_ENTRY_EXTENDED); - dataSize = dataHeaderExtended->DataSize; - variableSize = headerSize + dataSize; - } - - header = data.mid(offset, headerSize); - body = data.mid(offset + headerSize, dataSize); - name = UString("Data"); - info = usprintf("Full size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh", - variableSize, variableSize, - headerSize, headerSize, - dataSize, dataSize, - dataHeader->Header.Type, - dataHeader->Header.Checksum) - + (dataHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid")) - + usprintf("\nVarId: %04Xh\nGuidId: %04Xh\nAttributes: %08Xh (", - dataHeader->VarId, - dataHeader->GuidId, - dataHeader->Attributes) - + evsaAttributesToUString(dataHeader->Attributes) + UString(")"); - subtype = Subtypes::DataEvsaEntry; - } - // Unknown entry or free space - else { - UByteArray body = data.mid(offset); - UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size()); - - // Check type - UString name("Free space"); - UINT8 type = Types::FreeSpace; - UINT8 subtype = 0; - if (getPaddingType(body) == Subtypes::DataPadding) { - name = UString("Padding"); - type = Types::Padding; - subtype = Subtypes::DataPadding; - } - - // Add correct offset to parsing data - pdata.offset = parentOffset + offset; - - // Add free space tree item - UModelIndex itemIndex = model->addItem(type, subtype, name, UString(), info, UByteArray(), body, UByteArray(), false, parsingDataToUByteArray(pdata), index); - - // Show message - if (type == Types::Padding) - msg(usprintf("parseEvsaStoreBody: unknown variable of type %02Xh found at offset %Xh, the rest of unparsed store added as padding",entryHeader->Type, offset), itemIndex); - break; - } - - // Add correct offset to parsing data - pdata.offset = parentOffset + offset; - - // Add tree item - model->addItem(Types::EvsaEntry, subtype, name, UString(), info, header, body, UByteArray(), false, parsingDataToUByteArray(pdata), index); - - // Move to next variable - offset += variableSize; - unparsedSize = dataSize - offset; - } - - // Reparse all data variables to detect invalid ones and assign name and test to valid ones - for (int i = 0; i < model->rowCount(index); i++) { - UModelIndex current = index.child(i, 0); - if (model->subtype(current) == Subtypes::DataEvsaEntry) { - UByteArray header = model->header(current); - const EVSA_DATA_ENTRY* dataHeader = (const EVSA_DATA_ENTRY*)header.constData(); - UString guid; - if (guidMap.count(dataHeader->GuidId)) - guid = guidToUString(guidMap[dataHeader->GuidId]); - UString name; - if (nameMap.count(dataHeader->VarId)) - name = nameMap[dataHeader->VarId]; - - // Check for variable validity - if (guid.isEmpty() && name.isEmpty()) { // Both name and guid aren't found - model->setSubtype(current, Subtypes::InvalidEvsaEntry); - model->setName(current, UString("Invalid")); - msg(UString("parseEvsaStoreBody: data variable with invalid GuidId and invalid VarId"), current); - } - else if (guid.isEmpty()) { // Guid not found - model->setSubtype(current, Subtypes::InvalidEvsaEntry); - model->setName(current, UString("Invalid")); - msg(UString("parseEvsaStoreBody: data variable with invalid GuidId"), current); - } - else if (name.isEmpty()) { // Name not found - model->setSubtype(current, Subtypes::InvalidEvsaEntry); - model->setName(current, UString("Invalid")); - msg(UString("parseEvsaStoreBody: data variable with invalid VarId"), current); - } - else { // Variable is OK, rename it - if (dataHeader->Header.Type == NVRAM_EVSA_ENTRY_TYPE_DATA_INVALID) { - model->setSubtype(current, Subtypes::InvalidEvsaEntry); - model->setName(current, UString("Invalid")); - } - else { - model->setName(current, guid); - } - model->setText(current, name); - model->addInfo(current, UString("GUID: ") + guid + UString("\nName: ") + name + UString("\n"), false); - } - } - } - - return U_SUCCESS; -} - - -USTATUS FfsParser::parseFlashMapBody(const UModelIndex & index) -{ - // Sanity check - if (!index.isValid()) - return U_INVALID_PARAMETER; - - // Get parsing data for the current item - PARSING_DATA pdata = parsingDataFromUModelIndex(index); - UINT32 parentOffset = pdata.offset + model->header(index).size(); - const UByteArray data = model->body(index); - - - const UINT32 dataSize = (UINT32)data.size(); - UINT32 offset = 0; - UINT32 unparsedSize = dataSize; - // Parse all entries - while (unparsedSize) { - const PHOENIX_FLASH_MAP_ENTRY* entryHeader = (const PHOENIX_FLASH_MAP_ENTRY*)(data.constData() + offset); - - // Check entry size - if (unparsedSize < sizeof(PHOENIX_FLASH_MAP_ENTRY)) { - // Last variable is bad, add the rest as padding and return - UByteArray body = data.mid(offset); - UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size()); - - // Add correct offset to parsing data - pdata.offset = parentOffset + offset; - - // Add free space tree item - model->addItem(Types::Padding, getPaddingType(body), UString("Padding"), UString(), info, UByteArray(), body, UByteArray(), false, parsingDataToUByteArray(pdata), index); - - // Show message - if (unparsedSize < entryHeader->Size) - msg(UString("parseFlashMapBody: next entry appears too big, added as padding"), index); - - break; - } - - UString name = guidToUString(entryHeader->Guid); - - // Construct header - UByteArray header = data.mid(offset, sizeof(PHOENIX_FLASH_MAP_ENTRY)); - - // Add info - UString info = UString("Entry GUID: ") + name + usprintf("\nFull size: 24h (36)\nHeader size: 24h (36)\nBody size: 0h (0)\n" - "Entry type: %04Xh\nData type: %04Xh\nMemory address: %08Xh\nSize: %08Xh\nOffset: %08Xh", - entryHeader->EntryType, - entryHeader->DataType, - entryHeader->PhysicalAddress, - entryHeader->Size, - entryHeader->Offset); - - // Add correct offset to parsing data - pdata.offset = parentOffset + offset; - - // Determine subtype - UINT8 subtype = 0; - switch (entryHeader->DataType) { - case NVRAM_PHOENIX_FLASH_MAP_ENTRY_TYPE_VOLUME: - subtype = Subtypes::VolumeFlashMapEntry; - break; - case NVRAM_PHOENIX_FLASH_MAP_ENTRY_TYPE_DATA_BLOCK: - subtype = Subtypes::DataFlashMapEntry; - break; - } - - // Add tree item - model->addItem(Types::FlashMapEntry, subtype, name, flashMapGuidToUString(entryHeader->Guid), info, header, UByteArray(), UByteArray(), true, parsingDataToUByteArray(pdata), index); - - // Move to next variable - offset += sizeof(PHOENIX_FLASH_MAP_ENTRY); - unparsedSize = dataSize - offset; - } - - return U_SUCCESS; -} diff --git a/common/ffsparser.h b/common/ffsparser.h index 258cb53..7ac6543 100644 --- a/common/ffsparser.h +++ b/common/ffsparser.h @@ -15,33 +15,27 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include +#include "basetypes.h" #include "ustring.h" #include "ubytearray.h" -#include "basetypes.h" #include "treemodel.h" -#include "utility.h" -#include "peimage.h" -#include "parsingdata.h" -#include "types.h" -#include "treemodel.h" -#include "descriptor.h" -#include "ffs.h" -#include "gbe.h" -#include "me.h" -#include "fit.h" -#include "nvram.h" - -class TreeModel; +#include "nvramparser.h" class FfsParser { public: // Default constructor and destructor - FfsParser(TreeModel* treeModel) : model(treeModel), capsuleOffsetFixup(0) {} + FfsParser(TreeModel* treeModel) : model(treeModel), nvramParser(treeModel), capsuleOffsetFixup(0) {} ~FfsParser() {} // Returns messages - std::vector > getMessages() const { return messagesVector; } + std::vector > getMessages() const { + std::vector > nvramVector = nvramParser.getMessages(); + std::vector > resultVector = messagesVector; + resultVector.insert(std::end(resultVector), std::begin(nvramVector), std::end(nvramVector)); + return resultVector; + } + // Clears messages void clearMessages() { messagesVector.clear(); } @@ -62,6 +56,8 @@ private: UINT32 capsuleOffsetFixup; std::vector > fitTable; + NvramParser nvramParser; + // First pass USTATUS performFirstPass(const UByteArray & imageFile, UModelIndex & index); @@ -100,36 +96,12 @@ private: USTATUS parsePeImageSectionBody(const UModelIndex & index); USTATUS parseTeImageSectionBody(const UModelIndex & index); - UINT8 getPaddingType(const UByteArray & padding); USTATUS parseAprioriRawSection(const UByteArray & body, UString & parsed); USTATUS findNextVolume(const UModelIndex & index, const UByteArray & bios, const UINT32 parentOffset, const UINT32 volumeOffset, UINT32 & nextVolumeOffset); USTATUS getVolumeSize(const UByteArray & bios, const UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize); UINT32 getFileSize(const UByteArray & volume, const UINT32 fileOffset, const UINT8 ffsVersion); UINT32 getSectionSize(const UByteArray & file, const UINT32 sectionOffset, const UINT8 ffsVersion); - // NVRAM parsing - USTATUS parseNvramVolumeBody(const UModelIndex & index); - USTATUS findNextStore(const UModelIndex & index, const UByteArray & volume, const UINT32 parentOffset, const UINT32 storeOffset, UINT32 & nextStoreOffset); - USTATUS getStoreSize(const UByteArray & data, const UINT32 storeOffset, UINT32 & storeSize); - USTATUS parseStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); - - USTATUS parseNvarStore(const UModelIndex & index); - USTATUS parseVssStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseFtwStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseFdcStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseFsysStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseEvsaStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseFlashMapStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseCmdbStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseSlicPubkeyHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseSlicMarkerHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseIntelMicrocodeHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); - - USTATUS parseVssStoreBody(const UModelIndex & index); - USTATUS parseFsysStoreBody(const UModelIndex & index); - USTATUS parseEvsaStoreBody(const UModelIndex & index); - USTATUS parseFlashMapBody(const UModelIndex & index); - // Second pass USTATUS performSecondPass(const UModelIndex & index); USTATUS addOffsetsRecursive(const UModelIndex & index); diff --git a/common/ffsreport.cpp b/common/ffsreport.cpp index 58f9775..f3628e3 100644 --- a/common/ffsreport.cpp +++ b/common/ffsreport.cpp @@ -12,6 +12,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #include "ffsreport.h" +#include "ffs.h" +#include "utility.h" std::vector FfsReport::generate() { @@ -65,3 +67,4 @@ USTATUS FfsReport::generateRecursive(std::vector & report, UModelIndex return U_SUCCESS; } + diff --git a/common/ffsreport.h b/common/ffsreport.h index c7415f9..9e43a17 100644 --- a/common/ffsreport.h +++ b/common/ffsreport.h @@ -16,12 +16,11 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include -#include "../common/ubytearray.h" -#include "../common/ustring.h" #include "basetypes.h" +#include "ubytearray.h" +#include "ustring.h" #include "treemodel.h" -#include "ffs.h" -#include "utility.h" + class FfsReport { @@ -36,7 +35,7 @@ private: TreeModel* model; USTATUS generateRecursive(std::vector & report, UModelIndex index, UINT32 level = 0); - }; #endif // FFSREPORT_H + diff --git a/common/nvram.h b/common/nvram.h index 7a26f74..9b85971 100644 --- a/common/nvram.h +++ b/common/nvram.h @@ -15,9 +15,9 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #ifndef NVRAM_H #define NVRAM_H +#include "basetypes.h" #include "ubytearray.h" #include "ustring.h" -#include "basetypes.h" // Make sure we use right packing rules #pragma pack(push, 1) diff --git a/common/nvramparser.cpp b/common/nvramparser.cpp new file mode 100644 index 0000000..70557e7 --- /dev/null +++ b/common/nvramparser.cpp @@ -0,0 +1,1914 @@ +/* nvramparser.cpp + +Copyright (c) 2016, 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 +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ + +#include + +#include "nvramparser.h" +#include "parsingdata.h" +#include "utility.h" +#include "nvram.h" +#include "ffs.h" +#include "fit.h" + +#ifdef U_ENABLE_NVRAM_PARSING_SUPPORT +USTATUS NvramParser::parseNvarStore(const UModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return U_INVALID_PARAMETER; + + // Get parsing data for the current item + PARSING_DATA pdata = parsingDataFromUModelIndex(index); + UINT32 parentOffset = pdata.offset + model->header(index).size(); + + // Get item data + const UByteArray data = model->body(index); + + // Rename parent file + model->setText(model->findParentOfType(index, Types::File), UString("NVAR store")); + + UINT32 offset = 0; + UINT32 guidsInStore = 0; + const UINT8 emptyByte = pdata.emptyByte; + // Parse all entries + while (1) { + bool msgUnknownExtDataFormat = false; + bool msgExtHeaderTooLong = false; + bool msgExtDataTooShort = false; + + bool isInvalid = false; + bool isInvalidLink = false; + //bool isDataOnly = false; + bool hasExtendedHeader = false; + bool hasChecksum = false; + bool hasTimestamp = false; + bool hasHash = false; + bool hasGuidIndex = false; + + UINT32 guidIndex = 0; + UINT8 storedChecksum = 0; + UINT8 calculatedChecksum = 0; + UINT32 extendedHeaderSize = 0; + UINT8 extendedAttributes = 0; + UINT64 timestamp = 0; + UByteArray hash; + + UINT8 subtype = Subtypes::FullNvarEntry; + UString name; + UString text; + UByteArray header; + UByteArray body; + UByteArray tail; + + UINT32 guidAreaSize = guidsInStore * sizeof(EFI_GUID); + UINT32 unparsedSize = (UINT32)data.size() - offset - guidAreaSize; + + // Get entry header + const NVAR_ENTRY_HEADER* entryHeader = (const NVAR_ENTRY_HEADER*)(data.constData() + offset); + + // Check header size and signature + if (unparsedSize < sizeof(NVAR_ENTRY_HEADER) || + entryHeader->Signature != NVRAM_NVAR_ENTRY_SIGNATURE || + unparsedSize < entryHeader->Size) { + + // Check if the data left is a free space or a padding + UByteArray padding = data.mid(offset, unparsedSize); + UINT8 type; + + if ((UINT32)padding.count(emptyByte) == unparsedSize) { + // It's a free space + name = ("Free space"); + type = Types::FreeSpace; + subtype = 0; + } + else { + // Nothing is parsed yet, but the file is not empty + if (!offset) { + msg(UString("parseNvarStore: file can't be parsed as NVAR variables store"), index); + return U_SUCCESS; + } + + // It's a padding + name = UString("Padding"); + type = Types::Padding; + subtype = getPaddingType(padding); + } + // Get info + UString info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); + // Construct parsing data + pdata.offset = parentOffset + offset; + // Add tree item + model->addItem(type, subtype, name, UString(), info, UByteArray(), padding, UByteArray(), false, parsingDataToUByteArray(pdata), index); + + // Add GUID store area + UByteArray guidArea = data.right(guidAreaSize); + // Get info + name = UString("GUID store area"); + info = usprintf("Full size: %Xh (%u)\nGUIDs in store: %u", + guidArea.size(), guidArea.size(), + guidsInStore); + // Construct parsing data + pdata.offset = parentOffset + offset + padding.size(); + // Add tree item + model->addItem(Types::Padding, getPaddingType(guidArea), name, UString(), info, UByteArray(), guidArea, UByteArray(), false, parsingDataToUByteArray(pdata), index); + + return U_SUCCESS; + } + + // Contruct generic header and body + header = data.mid(offset, sizeof(NVAR_ENTRY_HEADER)); + body = data.mid(offset + sizeof(NVAR_ENTRY_HEADER), entryHeader->Size - sizeof(NVAR_ENTRY_HEADER)); + + UINT32 lastVariableFlag = pdata.emptyByte ? 0xFFFFFF : 0; + + // Set default next to predefined last value + pdata.nvar.next = lastVariableFlag; + + // Entry is marked as invalid + if ((entryHeader->Attributes & NVRAM_NVAR_ENTRY_VALID) == 0) { // Valid attribute is not set + isInvalid = true; + // Do not parse further + goto parsing_done; + } + + // Add next node information to parsing data + if (entryHeader->Next != lastVariableFlag) { + subtype = Subtypes::LinkNvarEntry; + pdata.nvar.next = entryHeader->Next; + } + + // Entry with extended header + if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_EXT_HEADER) { + hasExtendedHeader = true; + msgUnknownExtDataFormat = true; + + extendedHeaderSize = *(UINT16*)(body.constData() + body.size() - sizeof(UINT16)); + if (extendedHeaderSize > (UINT32)body.size()) { + msgExtHeaderTooLong = true; + isInvalid = true; + // Do not parse further + goto parsing_done; + } + + extendedAttributes = *(UINT8*)(body.constData() + body.size() - extendedHeaderSize); + + // Variable with checksum + if (extendedAttributes & NVRAM_NVAR_ENTRY_EXT_CHECKSUM) { + // Get stored checksum + storedChecksum = *(UINT8*)(body.constData() + body.size() - sizeof(UINT16) - sizeof(UINT8)); + + // Recalculate checksum for the variable + calculatedChecksum = 0; + // Include entry data + UINT8* start = (UINT8*)(entryHeader + 1); + for (UINT8* p = start; p < start + entryHeader->Size - sizeof(NVAR_ENTRY_HEADER); p++) { + calculatedChecksum += *p; + } + // Include entry size and flags + start = (UINT8*)&entryHeader->Size; + for (UINT8*p = start; p < start + sizeof(UINT16); p++) { + calculatedChecksum += *p; + } + // Include entry attributes + calculatedChecksum += entryHeader->Attributes; + + hasChecksum = true; + msgUnknownExtDataFormat = false; + } + + tail = body.mid(body.size() - extendedHeaderSize); + body = body.left(body.size() - extendedHeaderSize); + + // Entry with authenticated write (for SecureBoot) + if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_AUTH_WRITE) { + if ((entryHeader->Attributes & NVRAM_NVAR_ENTRY_DATA_ONLY)) {// Data only auth. variables has no hash + if ((UINT32)tail.size() < sizeof(UINT64)) { + msgExtDataTooShort = true; + isInvalid = true; + // Do not parse further + goto parsing_done; + } + + timestamp = *(UINT64*)(tail.constData() + sizeof(UINT8)); + hasTimestamp = true; + msgUnknownExtDataFormat = false; + } + else { // Full or link variable have hash + if ((UINT32)tail.size() < sizeof(UINT64) + SHA256_HASH_SIZE) { + msgExtDataTooShort = true; + isInvalid = true; + // Do not parse further + goto parsing_done; + } + + timestamp = *(UINT64*)(tail.constData() + sizeof(UINT8)); + hash = tail.mid(sizeof(UINT64) + sizeof(UINT8), SHA256_HASH_SIZE); + hasTimestamp = true; + hasHash = true; + msgUnknownExtDataFormat = false; + } + } + } + + // Entry is data-only (nameless and GUIDless entry or link) + if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_DATA_ONLY) { // Data-only attribute is set + isInvalidLink = true; + UModelIndex nvarIndex; + // Search prevously added entries for a link to this variable + for (int i = 0; i < model->rowCount(index); i++) { + nvarIndex = index.child(i, 0); + PARSING_DATA nvarPdata = parsingDataFromUModelIndex(nvarIndex); + if (nvarPdata.nvar.isValid && nvarPdata.nvar.next + nvarPdata.offset - parentOffset == offset) { // Previous link is present and valid + isInvalidLink = false; + break; + } + } + // Check if the link is valid + if (!isInvalidLink) { + // Use the name and text of the previous link + name = model->name(nvarIndex); + text = model->text(nvarIndex); + + if (entryHeader->Next == lastVariableFlag) + subtype = Subtypes::DataNvarEntry; + } + + //isDataOnly = true; + // Do not parse further + goto parsing_done; + } + + // Get entry name + { + UINT32 nameOffset = (entryHeader->Attributes & NVRAM_NVAR_ENTRY_GUID) ? sizeof(EFI_GUID) : sizeof(UINT8); // GUID can be stored with the variable or in a separate store, so there will only be an index of it + CHAR8* namePtr = (CHAR8*)(entryHeader + 1) + nameOffset; + UINT32 nameSize = 0; + if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_ASCII_NAME) { // Name is stored as ASCII string of CHAR8s + text = UString(namePtr); + nameSize = text.length() + 1; + } + else { // Name is stored as UCS2 string of CHAR16s + text = UString::fromUtf16((CHAR16*)namePtr); + nameSize = (text.length() + 1) * 2; + } + + // Get entry GUID + if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_GUID) { // GUID is strored in the variable itself + name = guidToUString(*(EFI_GUID*)(entryHeader + 1)); + } + // GUID is stored in GUID list at the end of the store + else { + guidIndex = *(UINT8*)(entryHeader + 1); + if (guidsInStore < guidIndex + 1) + guidsInStore = guidIndex + 1; + + // The list begins at the end of the store and goes backwards + const EFI_GUID* guidPtr = (const EFI_GUID*)(data.constData() + data.size()) - 1 - guidIndex; + name = guidToUString(*guidPtr); + hasGuidIndex = true; + } + + // Include name and GUID into the header and remove them from body + header = data.mid(offset, sizeof(NVAR_ENTRY_HEADER) + nameOffset + nameSize); + body = body.mid(nameOffset + nameSize); + } +parsing_done: + UString info; + + // Rename invalid entries according to their types + pdata.nvar.isValid = TRUE; + if (isInvalid) { + name = UString("Invalid"); + subtype = Subtypes::InvalidNvarEntry; + pdata.nvar.isValid = FALSE; + } + else if (isInvalidLink) { + name = UString("Invalid link"); + subtype = Subtypes::InvalidLinkNvarEntry; + pdata.nvar.isValid = FALSE; + } + else // Add GUID info for valid entries + info += UString("Variable GUID: ") + name + UString("\n"); + + // Add GUID index information + if (hasGuidIndex) + info += usprintf("GUID index: %u\n", guidIndex); + + // Add header, body and extended data info + info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)", + entryHeader->Size, entryHeader->Size, + header.size(), header.size(), + body.size(), body.size()); + + // Add attributes info + info += usprintf("\nAttributes: %02Xh", entryHeader->Attributes); + // Translate attributes to text + if (entryHeader->Attributes && entryHeader->Attributes != 0xFF) + info += UString(" (") + nvarAttributesToUString(entryHeader->Attributes) + UString(")"); + + // Add next node info + if (!isInvalid && entryHeader->Next != lastVariableFlag) + info += usprintf("\nNext node at offset: %Xh", parentOffset + offset + entryHeader->Next); + + // Add extended header info + if (hasExtendedHeader) { + info += usprintf("\nExtended header size: %Xh (%u)\nExtended attributes: %Xh (", + extendedHeaderSize, extendedHeaderSize, + extendedAttributes) + nvarExtendedAttributesToUString(extendedAttributes) + UString(")"); + + // Add checksum + if (hasChecksum) + info += usprintf("\nChecksum: %02Xh", storedChecksum) + + (calculatedChecksum ? usprintf(", invalid, should be %02Xh", 0x100 - calculatedChecksum) : UString(", valid")); + + // Add timestamp + if (hasTimestamp) + info += usprintf("\nTimestamp: %" PRIX64 "h", timestamp); + + // Add hash + if (hasHash) + info += UString("\nHash: ") + UString(hash.toHex().constData()); + } + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Add tree item + UModelIndex varIndex = model->addItem(Types::NvarEntry, subtype, name, text, info, header, body, tail, false, parsingDataToUByteArray(pdata), index); + + // Show messages + if (msgUnknownExtDataFormat) msg(UString("parseNvarStore: unknown extended data format"), varIndex); + if (msgExtHeaderTooLong) msg(usprintf("parseNvarStore: extended header size (%Xh) is greater than body size (%Xh)", + extendedHeaderSize, body.size()), varIndex); + if (msgExtDataTooShort) msg(usprintf("parseNvarStore: extended header size (%Xh) is too small for timestamp and hash", + tail.size()), varIndex); + + // Try parsing the entry data as NVAR storage if it begins with NVAR signature + if ((subtype == Subtypes::DataNvarEntry || subtype == Subtypes::FullNvarEntry) + && *(const UINT32*)body.constData() == NVRAM_NVAR_ENTRY_SIGNATURE) + parseNvarStore(varIndex); + + // Move to next exntry + offset += entryHeader->Size; + } + + return U_SUCCESS; +} + +USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return U_INVALID_PARAMETER; + + // Get parsing data + PARSING_DATA pdata = parsingDataFromUModelIndex(index); + UINT32 parentOffset = pdata.offset + model->header(index).size(); + + // Get item data + UByteArray data = model->body(index); + + // Search for first store + USTATUS result; + UINT32 prevStoreOffset; + result = findNextStore(index, data, parentOffset, 0, prevStoreOffset); + if (result) + return result; + + // First store is not at the beginning of volume body + UString name; + UString info; + if (prevStoreOffset > 0) { + // Get info + UByteArray padding = data.left(prevStoreOffset); + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); + + // Construct parsing data + pdata.offset = parentOffset; + + // Add tree item + model->addItem(Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), true, parsingDataToUByteArray(pdata), index); + } + + // Search for and parse all stores + UINT32 storeOffset = prevStoreOffset; + UINT32 prevStoreSize = 0; + + while (!result) + { + // Padding between stores + if (storeOffset > prevStoreOffset + prevStoreSize) { + UINT32 paddingOffset = prevStoreOffset + prevStoreSize; + UINT32 paddingSize = storeOffset - paddingOffset; + UByteArray padding = data.mid(paddingOffset, paddingSize); + + // Get info + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); + + // Construct parsing data + pdata.offset = parentOffset + paddingOffset; + + // Add tree item + model->addItem(Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), true, parsingDataToUByteArray(pdata), index); + } + + // Get store size + UINT32 storeSize = 0; + result = getStoreSize(data, storeOffset, storeSize); + if (result) { + msg(UString("parseNvramVolumeBody: getStoreSize failed with error ") + errorCodeToUString(result), index); + return result; + } + + // Check that current store is fully present in input + if (storeSize > (UINT32)data.size() || storeOffset + storeSize > (UINT32)data.size()) { + // Mark the rest as padding and finish parsing + UByteArray padding = data.mid(storeOffset); + + // Get info + name = UString("Padding"); + info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); + + // Construct parsing data + pdata.offset = parentOffset + storeOffset; + + // Add tree item + UModelIndex paddingIndex = model->addItem(Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), true, parsingDataToUByteArray(pdata), index); + msg(UString("parseNvramVolumeBody: one of stores inside overlaps the end of data"), paddingIndex); + + // Update variables + prevStoreOffset = storeOffset; + prevStoreSize = padding.size(); + break; + } + + UByteArray store = data.mid(storeOffset, storeSize); + // Parse current store header + UModelIndex storeIndex; + result = parseStoreHeader(store, parentOffset + storeOffset, index, storeIndex); + if (result) + msg(UString("parseNvramVolumeBody: store header parsing failed with error ") + errorCodeToUString(result), index); + + // Go to next store + prevStoreOffset = storeOffset; + prevStoreSize = storeSize; + result = findNextStore(index, data, parentOffset, storeOffset + prevStoreSize, storeOffset); + } + + // Padding/free space at the end + storeOffset = prevStoreOffset + prevStoreSize; + if ((UINT32)data.size() > storeOffset) { + UByteArray padding = data.mid(storeOffset); + UINT8 type; + UINT8 subtype; + if (padding.count(pdata.emptyByte) == padding.size()) { + // It's a free space + name = UString("Free space"); + type = Types::FreeSpace; + subtype = 0; + } + else { + // Nothing is parsed yet, but the file is not empty + if (!storeOffset) { + msg(UString("parseNvramVolumeBody: can't be parsed as NVRAM volume"), index); + return U_SUCCESS; + } + + // It's a padding + name = UString("Padding"); + type = Types::Padding; + subtype = getPaddingType(padding); + } + + // Add info + info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); + + // Construct parsing data + pdata.offset = parentOffset + storeOffset; + + // Add tree item + model->addItem(type, subtype, name, UString(), info, UByteArray(), padding, UByteArray(), true, parsingDataToUByteArray(pdata), index); + } + + // Parse bodies + for (int i = 0; i < model->rowCount(index); i++) { + UModelIndex current = index.child(i, 0); + switch (model->type(current)) { + case Types::VssStore: + case Types::FdcStore: parseVssStoreBody(current); break; + case Types::FsysStore: parseFsysStoreBody(current); break; + case Types::EvsaStore: parseEvsaStoreBody(current); break; + case Types::FlashMapStore: parseFlashMapBody(current); break; + } + } + + return U_SUCCESS; +} + +USTATUS NvramParser::findNextStore(const UModelIndex & index, const UByteArray & volume, const UINT32 parentOffset, const UINT32 storeOffset, UINT32 & nextStoreOffset) +{ + UINT32 dataSize = volume.size(); + + if (dataSize < sizeof(UINT32)) + return U_STORES_NOT_FOUND; + + UINT32 offset = storeOffset; + for (; offset < dataSize - sizeof(UINT32); offset++) { + const UINT32* currentPos = (const UINT32*)(volume.constData() + offset); + if (*currentPos == NVRAM_VSS_STORE_SIGNATURE || *currentPos == NVRAM_APPLE_SVS_STORE_SIGNATURE) { //$VSS or $SVS signatures found, perform checks + const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)currentPos; + if (vssHeader->Format != NVRAM_VSS_VARIABLE_STORE_FORMATTED) { + msg(usprintf("findNextStore: VSS store candidate at offset %Xh skipped, has invalid format %02Xh", parentOffset + offset, vssHeader->Format), index); + continue; + } + if (vssHeader->Size == 0 || vssHeader->Size == 0xFFFFFFFF) { + msg(usprintf("findNextStore: VSS store candidate at offset %Xh skipped, has invalid size %Xh", parentOffset + offset, vssHeader->Size), index); + continue; + } + // All checks passed, store found + break; + } + else if (*currentPos == NVRAM_FDC_VOLUME_SIGNATURE) { //FDC signature found + const FDC_VOLUME_HEADER* fdcHeader = (const FDC_VOLUME_HEADER*)currentPos; + if (fdcHeader->Size == 0 || fdcHeader->Size == 0xFFFFFFFF) { + msg(usprintf("findNextStore: FDC store candidate at offset %Xh skipped, has invalid size %Xh", parentOffset + offset, fdcHeader->Size), index); + continue; + } + // All checks passed, store found + break; + } + else if (*currentPos == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *currentPos == NVRAM_APPLE_GAID_STORE_SIGNATURE) { //Fsys or Gaid signature found + const APPLE_FSYS_STORE_HEADER* fsysHeader = (const APPLE_FSYS_STORE_HEADER*)currentPos; + if (fsysHeader->Size == 0 || fsysHeader->Size == 0xFFFF) { + msg(usprintf("findNextStore: Fsys store candidate at offset %Xh skipped, has invalid size %Xh", parentOffset + offset, fsysHeader->Size), index); + continue; + } + // All checks passed, store found + break; + } + else if (*currentPos == NVRAM_EVSA_STORE_SIGNATURE) { //EVSA signature found + if (offset < sizeof(UINT32)) + continue; + + const EVSA_STORE_ENTRY* evsaHeader = (const EVSA_STORE_ENTRY*)(currentPos - 1); + if (evsaHeader->Header.Type != NVRAM_EVSA_ENTRY_TYPE_STORE) { + msg(usprintf("findNextStore: EVSA store candidate at offset %Xh skipped, has invalid type %02Xh", parentOffset + offset - 4, evsaHeader->Header.Type), index); + continue; + } + if (evsaHeader->StoreSize == 0 || evsaHeader->StoreSize == 0xFFFFFFFF) { + msg(usprintf("findNextStore: EVSA store candidate at offset %Xh skipped, has invalid size %Xh", parentOffset + offset, evsaHeader->StoreSize), index); + continue; + } + // All checks passed, store found + offset -= sizeof(UINT32); + break; + } + else if (*currentPos == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *currentPos == EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1) { //Possible FTW block signature found + UByteArray guid = UByteArray(volume.constData() + offset, sizeof(EFI_GUID)); + if (guid != NVRAM_MAIN_STORE_VOLUME_GUID && guid != EDKII_WORKING_BLOCK_SIGNATURE_GUID) // Check the whole signature + continue; + + // Detect header variant based on WriteQueueSize + const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftwHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)currentPos; + if (ftwHeader->WriteQueueSize % 0x10 == 0x04) { // Header with 32 bit WriteQueueSize + if (ftwHeader->WriteQueueSize == 0 || ftwHeader->WriteQueueSize == 0xFFFFFFFF) { + msg(usprintf("findNextStore: FTW block candidate at offset %Xh skipped, has invalid body size %Xh", parentOffset + offset, ftwHeader->WriteQueueSize), index); + continue; + } + } + else if (ftwHeader->WriteQueueSize % 0x10 == 0x00) { // Header with 64 bit WriteQueueSize + const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64Header = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)currentPos; + if (ftw64Header->WriteQueueSize == 0 || ftw64Header->WriteQueueSize >= 0xFFFFFFFF) { + msg(usprintf("findNextStore: FTW block candidate at offset %Xh skipped, has invalid body size %Xh", parentOffset + offset, ftw64Header->WriteQueueSize), index); + continue; + } + } + else // Unknown header + continue; + + // All checks passed, store found + break; + } + else if (*currentPos == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1) {// Phoenix SCT flash map + UByteArray signature = UByteArray(volume.constData() + offset, NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_LENGTH); + if (signature != NVRAM_PHOENIX_FLASH_MAP_SIGNATURE) // Check the whole signature + continue; + + // All checks passed, store found + break; + } + else if (*currentPos == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE) { // Phoenix SCT CMDB store + const PHOENIX_CMDB_HEADER* cmdbHeader = (const PHOENIX_CMDB_HEADER*)currentPos; + + // Check size + if (cmdbHeader->HeaderSize != sizeof(PHOENIX_CMDB_HEADER)) + continue; + + // All checks passed, store found + break; + } + else if (*currentPos == INTEL_MICROCODE_HEADER_VERSION) {// Intel microcode + if (!INTEL_MICROCODE_HEADER_SIZES_VALID(currentPos)) // Check header sizes + continue; + + // Check reserved bytes + const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)currentPos; + bool reservedBytesValid = true; + for (UINT32 i = 0; i < sizeof(ucodeHeader->Reserved); i++) + if (ucodeHeader->Reserved[i] != INTEL_MICROCODE_HEADER_RESERVED_BYTE) { + reservedBytesValid = false; + break; + } + if (!reservedBytesValid) + continue; + + // All checks passed, store found + break; + } + else if (*currentPos == OEM_ACTIVATION_PUBKEY_MAGIC) { // SLIC pubkey + if (offset < 4 * sizeof(UINT32)) + continue; + + const OEM_ACTIVATION_PUBKEY* pubkeyHeader = (const OEM_ACTIVATION_PUBKEY*)(currentPos - 4); + // Check type + if (pubkeyHeader->Type != OEM_ACTIVATION_PUBKEY_TYPE) + continue; + + // All checks passed, store found + offset -= 4 * sizeof(UINT32); + break; + } + else if (*currentPos == OEM_ACTIVATION_MARKER_WINDOWS_FLAG_PART1) { // SLIC marker + if (offset >= dataSize - sizeof(UINT64) || + *(const UINT64*)currentPos != OEM_ACTIVATION_MARKER_WINDOWS_FLAG || + offset < 26) // Check full windows flag and structure size + continue; + + const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)(volume.constData() + offset - 26); + // Check reserved bytes + bool reservedBytesValid = true; + for (UINT32 i = 0; i < sizeof(markerHeader->Reserved); i++) + if (markerHeader->Reserved[i] != OEM_ACTIVATION_MARKER_RESERVED_BYTE) { + reservedBytesValid = false; + break; + } + if (!reservedBytesValid) + continue; + + // All checks passed, store found + offset -= 26; + break; + } + } + // No more stores found + if (offset >= dataSize - sizeof(UINT32)) + return U_STORES_NOT_FOUND; + + nextStoreOffset = offset; + + return U_SUCCESS; +} + +USTATUS NvramParser::getStoreSize(const UByteArray & data, const UINT32 storeOffset, UINT32 & storeSize) +{ + const UINT32* signature = (const UINT32*)(data.constData() + storeOffset); + if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) { + const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)signature; + storeSize = vssHeader->Size; + } + else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE) { + const FDC_VOLUME_HEADER* fdcHeader = (const FDC_VOLUME_HEADER*)signature; + storeSize = fdcHeader->Size; + } + else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *signature == NVRAM_APPLE_GAID_STORE_SIGNATURE) { + const APPLE_FSYS_STORE_HEADER* fsysHeader = (const APPLE_FSYS_STORE_HEADER*)signature; + storeSize = fsysHeader->Size; + } + else if (*(signature + 1) == NVRAM_EVSA_STORE_SIGNATURE) { + const EVSA_STORE_ENTRY* evsaHeader = (const EVSA_STORE_ENTRY*)signature; + storeSize = evsaHeader->StoreSize; + } + else if (*signature == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *signature == EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1) { + const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftwHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)signature; + if (ftwHeader->WriteQueueSize % 0x10 == 0x04) { // Header with 32 bit WriteQueueSize + storeSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) + ftwHeader->WriteQueueSize; + } + else { // Header with 64 bit WriteQueueSize + const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64Header = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)signature; + storeSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64) + (UINT32)ftw64Header->WriteQueueSize; + } + } + else if (*signature == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1) { // Phoenix SCT flash map + const PHOENIX_FLASH_MAP_HEADER* flashMapHeader = (const PHOENIX_FLASH_MAP_HEADER*)signature; + storeSize = sizeof(PHOENIX_FLASH_MAP_HEADER) + sizeof(PHOENIX_FLASH_MAP_ENTRY) * flashMapHeader->NumEntries; + } + else if (*signature == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE) { // Phoenix SCT CMDB store + storeSize = NVRAM_PHOENIX_CMDB_SIZE; // It's a predefined max size, no need to calculate + } + else if (*(signature + 4) == OEM_ACTIVATION_PUBKEY_MAGIC) { // SLIC pubkey + const OEM_ACTIVATION_PUBKEY* pubkeyHeader = (const OEM_ACTIVATION_PUBKEY*)signature; + storeSize = pubkeyHeader->Size; + } + else if (*(const UINT64*)(data.constData() + storeOffset + 26) == OEM_ACTIVATION_MARKER_WINDOWS_FLAG) { // SLIC marker + const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)signature; + storeSize = markerHeader->Size; + } + else if (*signature == INTEL_MICROCODE_HEADER_VERSION) { // Intel microcode, must be checked after SLIC marker because of the same *signature values + const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)signature; + storeSize = ucodeHeader->TotalSize; + } + return U_SUCCESS; +} + +USTATUS NvramParser::parseVssStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) +{ + const UINT32 dataSize = (const UINT32)store.size(); + + // Check store size + if (dataSize < sizeof(VSS_VARIABLE_STORE_HEADER)) { + msg(UString("parseVssStoreHeader: volume body is too small even for VSS store header"), parent); + return U_SUCCESS; + } + + // Get VSS store header + const VSS_VARIABLE_STORE_HEADER* vssStoreHeader = (const VSS_VARIABLE_STORE_HEADER*)store.constData(); + + // Check store size + if (dataSize < vssStoreHeader->Size) { + msg(usprintf("parseVssStoreHeader: VSS store size %Xh (%u) is greater than volume body size %Xh (%u)", + vssStoreHeader->Size, vssStoreHeader->Size, + dataSize, dataSize), parent); + return U_SUCCESS; + } + + // Get parsing data + PARSING_DATA pdata = parsingDataFromUModelIndex(parent); + + // Construct header and body + UByteArray header = store.left(sizeof(VSS_VARIABLE_STORE_HEADER)); + UByteArray body = store.mid(sizeof(VSS_VARIABLE_STORE_HEADER), vssStoreHeader->Size - sizeof(VSS_VARIABLE_STORE_HEADER)); + + // Add info + bool isSvsStore = (vssStoreHeader->Signature == NVRAM_APPLE_SVS_STORE_SIGNATURE); + UString name = isSvsStore ? UString("SVS store") : UString("VSS store"); + UString info = usprintf("Signature: %s\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nFormat: %02Xh\nState: %02Xh\nUnknown: %04Xh", + isSvsStore ? "$SVS" : "$VSS", + vssStoreHeader->Size, vssStoreHeader->Size, + header.size(), header.size(), + body.size(), body.size(), + vssStoreHeader->Format, + vssStoreHeader->State, + vssStoreHeader->Unknown); + + // Add correct offset + pdata.offset = parentOffset; + + // Add tree item + index = model->addItem(Types::VssStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); + + return U_SUCCESS; +} + +USTATUS NvramParser::parseFtwStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) +{ + const UINT32 dataSize = (const UINT32)store.size(); + + // Check store size + if (dataSize < sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64)) { + msg(UString("parseFtwStoreHeader: volume body is too small even for FTW store header"), parent); + return U_SUCCESS; + } + + // Get FTW block headers + const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftw32BlockHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)store.constData(); + const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64BlockHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)store.constData(); + + // Check store size + UINT32 ftwBlockSize; + bool has32bitHeader; + if (ftw32BlockHeader->WriteQueueSize % 0x10 == 0x04) { // Header with 32 bit WriteQueueSize + ftwBlockSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) + ftw32BlockHeader->WriteQueueSize; + has32bitHeader = true; + } + else { // Header with 64 bit WriteQueueSize + ftwBlockSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64) + (UINT32)ftw64BlockHeader->WriteQueueSize; + has32bitHeader = false; + } + if (dataSize < ftwBlockSize) { + msg(usprintf("parseFtwStoreHeader: FTW store size %Xh (%u) is greater than volume body size %Xh (%u)", + ftwBlockSize, ftwBlockSize, + dataSize, dataSize), parent); + return U_SUCCESS; + } + + // Get parsing data + PARSING_DATA pdata = parsingDataFromUModelIndex(parent); + + // Construct header and body + UINT32 headerSize = has32bitHeader ? sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) : sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64); + UByteArray header = store.left(headerSize); + UByteArray body = store.mid(headerSize, ftwBlockSize - headerSize); + + // Check block header checksum + UByteArray crcHeader = header; + EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* crcFtwBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)header.data(); + crcFtwBlockHeader->Crc = pdata.emptyByte ? 0xFFFFFFFF : 0; + crcFtwBlockHeader->State = pdata.emptyByte ? 0xFF : 0; + UINT32 calculatedCrc = crc32(0, (const UINT8*)crcFtwBlockHeader, headerSize); + + // Add info + UString name("FTW store"); + UString info = UString("Signature: ") + guidToUString(ftw32BlockHeader->Signature) + + usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nHeader CRC32: %08Xh", + ftwBlockSize, ftwBlockSize, + headerSize, headerSize, + body.size(), body.size(), + ftw32BlockHeader->State, + ftw32BlockHeader->Crc) + + (ftw32BlockHeader->Crc != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid")); + + // Add correct offset + pdata.offset = parentOffset; + + // Add tree item + index = model->addItem(Types::FtwStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); + + return U_SUCCESS; +} + +USTATUS NvramParser::parseFdcStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) +{ + const UINT32 dataSize = (const UINT32)store.size(); + + // Check store size + if (dataSize < sizeof(FDC_VOLUME_HEADER)) { + msg(UString("parseFdcStoreHeader: volume body is too small even for FDC store header"), parent); + return U_SUCCESS; + } + + // Get Fdc store header + const FDC_VOLUME_HEADER* fdcStoreHeader = (const FDC_VOLUME_HEADER*)store.constData(); + + // Check store size + if (dataSize < fdcStoreHeader->Size) { + msg(usprintf("parseFdcStoreHeader: FDC store size %Xh (%u) is greater than volume body size %Xh (%u)", + fdcStoreHeader->Size, fdcStoreHeader->Size, + dataSize, dataSize), parent); + return U_SUCCESS; + } + + // Determine internal volume header size + const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(fdcStoreHeader + 1); + UINT32 headerSize; + if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) { + const EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (const EFI_FIRMWARE_VOLUME_EXT_HEADER*)((const UINT8*)volumeHeader + volumeHeader->ExtHeaderOffset); + headerSize = volumeHeader->ExtHeaderOffset + extendedHeader->ExtHeaderSize; + } + else + headerSize = volumeHeader->HeaderLength; + + // Extended header end can be unaligned + headerSize = ALIGN8(headerSize); + + // Add VSS store header + headerSize += sizeof(VSS_VARIABLE_STORE_HEADER); + + // Add FDC header + headerSize += sizeof(FDC_VOLUME_HEADER); + + // Check sanity of combined header size + if (dataSize < headerSize) { + msg(usprintf("parseFdcStoreHeader: FDC store header size %Xh (%u) is greater than volume body size %Xh (%u)", + fdcStoreHeader->Size,fdcStoreHeader->Size, + dataSize, dataSize), parent); + return U_SUCCESS; + } + + // Get parsing data + PARSING_DATA pdata = parsingDataFromUModelIndex(parent); + + // Construct header and body + UByteArray header = store.left(headerSize); + UByteArray body = store.mid(headerSize, fdcStoreHeader->Size - headerSize); + + // Add info + UString name("FDC store"); + UString info = usprintf("Signature: _FDC\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)", + fdcStoreHeader->Size, fdcStoreHeader->Size, + header.size(), header.size(), + body.size(), body.size()); + + // TODO: add internal headers info + + // Add correct offset + pdata.offset = parentOffset; + + // Add tree item + index = model->addItem(Types::FdcStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); + + return U_SUCCESS; +} + +USTATUS NvramParser::parseFsysStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) +{ + const UINT32 dataSize = (const UINT32)store.size(); + + // Check store size + if (dataSize < sizeof(APPLE_FSYS_STORE_HEADER)) { + msg(UString("parseFsysStoreHeader: volume body is too small even for Fsys store header"), parent); + return U_SUCCESS; + } + + // Get Fsys store header + const APPLE_FSYS_STORE_HEADER* fsysStoreHeader = (const APPLE_FSYS_STORE_HEADER*)store.constData(); + + // Check store size + if (dataSize < fsysStoreHeader->Size) { + msg(usprintf("parseFsysStoreHeader: Fsys store size %Xh (%u) is greater than volume body size %Xh (%u)", + fsysStoreHeader->Size, fsysStoreHeader->Size, + dataSize, dataSize), parent); + return U_SUCCESS; + } + + // Get parsing data + PARSING_DATA pdata = parsingDataFromUModelIndex(parent); + + // Construct header and body + UByteArray header = store.left(sizeof(APPLE_FSYS_STORE_HEADER)); + UByteArray body = store.mid(sizeof(APPLE_FSYS_STORE_HEADER), fsysStoreHeader->Size - sizeof(APPLE_FSYS_STORE_HEADER) - sizeof(UINT32)); + + // Check store checksum + UINT32 storedCrc = *(UINT32*)store.right(sizeof(UINT32)).constData(); + UINT32 calculatedCrc = crc32(0, (const UINT8*)store.constData(), (const UINT32)store.size() - sizeof(UINT32)); + + // Add info + bool isGaidStore = (fsysStoreHeader->Signature == NVRAM_APPLE_GAID_STORE_SIGNATURE); + UString name = isGaidStore ? UString("Gaid store") : UString("Fsys store"); + UString info = usprintf("Signature: %s\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nUnknown0: %02Xh\nUnknown1: %08Xh\nCRC32: %08Xh", + isGaidStore ? "Gaid" : "Fsys", + fsysStoreHeader->Size, fsysStoreHeader->Size, + header.size(), header.size(), + body.size(), body.size(), + fsysStoreHeader->Unknown0, + fsysStoreHeader->Unknown1) + + (storedCrc != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid")); + + // Add correct offset + pdata.offset = parentOffset; + + // Add tree item + index = model->addItem(Types::FsysStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); + + return U_SUCCESS; +} + +USTATUS NvramParser::parseEvsaStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) +{ + const UINT32 dataSize = (const UINT32)store.size(); + + // Check dataSize + if (dataSize < sizeof(EVSA_STORE_ENTRY)) { + msg(UString("parseEvsaStoreHeader: volume body is too small even for EVSA store header"), parent); + return U_SUCCESS; + } + + // Get EVSA store header + const EVSA_STORE_ENTRY* evsaStoreHeader = (const EVSA_STORE_ENTRY*)store.constData(); + + // Check store size + if (dataSize < evsaStoreHeader->StoreSize) { + msg(usprintf("parseEvsaStoreHeader: EVSA store size %Xh (%u) is greater than volume body size %Xh (%u)", + evsaStoreHeader->StoreSize, evsaStoreHeader->StoreSize, + dataSize, dataSize), parent); + return U_SUCCESS; + } + + // Get parsing data + PARSING_DATA pdata = parsingDataFromUModelIndex(parent); + + // Construct header and body + UByteArray header = store.left(evsaStoreHeader->Header.Size); + UByteArray body = store.mid(evsaStoreHeader->Header.Size, evsaStoreHeader->StoreSize - evsaStoreHeader->Header.Size); + + // Recalculate checksum + UINT8 calculated = calculateChecksum8(((const UINT8*)evsaStoreHeader) + 2, evsaStoreHeader->Header.Size - 2); + + // Add info + UString name("EVSA store"); + UString info = usprintf("Signature: EVSA\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nAttributes: %08Xh\nChecksum: %02Xh", + evsaStoreHeader->StoreSize, evsaStoreHeader->StoreSize, + header.size(), header.size(), + body.size(), body.size(), + evsaStoreHeader->Header.Type, + evsaStoreHeader->Attributes, + evsaStoreHeader->Header.Checksum) + + (evsaStoreHeader->Header.Checksum != calculated ? usprintf("%, invalid, should be %02Xh", calculated) : UString(", valid")); + + // Add correct offset + pdata.offset = parentOffset; + + // Add tree item + index = model->addItem(Types::EvsaStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); + + return U_SUCCESS; +} + +USTATUS NvramParser::parseFlashMapStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) +{ + const UINT32 dataSize = (const UINT32)store.size(); + + // Check data size + if (dataSize < sizeof(PHOENIX_FLASH_MAP_HEADER)) { + msg(UString("parseFlashMapStoreHeader: volume body is too small even for FlashMap block header"), parent); + return U_SUCCESS; + } + + // Get FlashMap block header + const PHOENIX_FLASH_MAP_HEADER* flashMapHeader = (const PHOENIX_FLASH_MAP_HEADER*)store.constData(); + + // Check store size + UINT32 flashMapSize = sizeof(PHOENIX_FLASH_MAP_HEADER) + flashMapHeader->NumEntries * sizeof(PHOENIX_FLASH_MAP_ENTRY); + if (dataSize < flashMapSize) { + msg(usprintf("parseFlashMapStoreHeader: FlashMap block size %Xh (%u) is greater than volume body size %Xh (%u)", + flashMapSize, flashMapSize, + dataSize, dataSize), parent); + return U_SUCCESS; + } + + // Get parsing data + PARSING_DATA pdata = parsingDataFromUModelIndex(parent); + + // Construct header and body + UByteArray header = store.left(sizeof(PHOENIX_FLASH_MAP_HEADER)); + UByteArray body = store.mid(sizeof(PHOENIX_FLASH_MAP_HEADER), flashMapSize - sizeof(PHOENIX_FLASH_MAP_HEADER)); + + // Add info + UString name("Phoenix SCT flash map"); + UString info = usprintf("Signature: _FLASH_MAP\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nNumber of entries: %u", + flashMapSize, flashMapSize, + header.size(), header.size(), + body.size(), body.size(), + flashMapHeader->NumEntries); + + // Add correct offset + pdata.offset = parentOffset; + + // Add tree item + index = model->addItem(Types::FlashMapStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); + + return U_SUCCESS; +} + +USTATUS NvramParser::parseCmdbStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) +{ + const UINT32 dataSize = (const UINT32)store.size(); + + // Check store size + if (dataSize < sizeof(PHOENIX_CMDB_HEADER)) { + msg(UString("parseCmdbStoreHeader: volume body is too small even for CMDB store header"), parent); + return U_SUCCESS; + } + + UINT32 cmdbSize = NVRAM_PHOENIX_CMDB_SIZE; + if (dataSize < cmdbSize) { + msg(usprintf("parseCmdbStoreHeader: CMDB store size %Xh (%u) is greater than volume body size %Xh (%u)", + cmdbSize, cmdbSize, + dataSize, dataSize), parent); + return U_SUCCESS; + } + + // Get store header + const PHOENIX_CMDB_HEADER* cmdbHeader = (const PHOENIX_CMDB_HEADER*)store.constData(); + + // Get parsing data + PARSING_DATA pdata = parsingDataFromUModelIndex(parent); + + // Construct header and body + UByteArray header = store.left(cmdbHeader->TotalSize); + UByteArray body = store.mid(cmdbHeader->TotalSize, cmdbSize - cmdbHeader->TotalSize); + + // Add info + UString name("CMDB store"); + UString info = usprintf("Signature: CMDB\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)", + cmdbSize, cmdbSize, + header.size(), header.size(), + body.size(), body.size()); + + // Add correct offset + pdata.offset = parentOffset; + + // Add tree item + index = model->addItem(Types::CmdbStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); + + return U_SUCCESS; +} + +USTATUS NvramParser::parseSlicPubkeyHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) +{ + const UINT32 dataSize = (const UINT32)store.size(); + + // Check data size + if (dataSize < sizeof(OEM_ACTIVATION_PUBKEY)) { + msg(UString("parseSlicPubkeyHeader: volume body is too small even for SLIC pubkey header"), parent); + return U_SUCCESS; + } + + // Get SLIC pubkey header + const OEM_ACTIVATION_PUBKEY* pubkeyHeader = (const OEM_ACTIVATION_PUBKEY*)store.constData(); + + // Check store size + if (dataSize < pubkeyHeader->Size) { + msg(usprintf("parseSlicPubkeyHeader: SLIC pubkey size %Xh (%u) is greater than volume body size %Xh (%u)", + pubkeyHeader->Size, pubkeyHeader->Size, + dataSize, dataSize), parent); + return U_SUCCESS; + } + + // Get parsing data + PARSING_DATA pdata = parsingDataFromUModelIndex(parent); + + // Construct header and body + UByteArray header = store.left(sizeof(OEM_ACTIVATION_PUBKEY)); + + // Add info + UString name("SLIC pubkey"); + UString info = usprintf("Type: 0h\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: 0h (0)\n" + "Key type: %02Xh\nVersion: %02Xh\nAlgorithm: %08Xh\nMagic: RSA1\nBit length: %08Xh\nExponent: %08Xh", + pubkeyHeader->Size, pubkeyHeader->Size, + header.size(), header.size(), + pubkeyHeader->KeyType, + pubkeyHeader->Version, + pubkeyHeader->Algorithm, + pubkeyHeader->BitLength, + pubkeyHeader->Exponent); + + // Add correct offset + pdata.offset = parentOffset; + + // Add tree item + index = model->addItem(Types::SlicData, Subtypes::PubkeySlicData, name, UString(), info, header, UByteArray(), UByteArray(), true, parsingDataToUByteArray(pdata), parent); + + return U_SUCCESS; +} + +USTATUS NvramParser::parseSlicMarkerHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) +{ + const UINT32 dataSize = (const UINT32)store.size(); + + // Check data size + if (dataSize < sizeof(OEM_ACTIVATION_MARKER)) { + msg(UString("parseSlicMarkerHeader: volume body is too small even for SLIC marker header"), parent); + return U_SUCCESS; + } + + // Get SLIC marker header + const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)store.constData(); + + // Check store size + if (dataSize < markerHeader->Size) { + msg(usprintf("parseSlicMarkerHeader: SLIC marker size %Xh (%u) is greater than volume body size %Xh (%u)", + markerHeader->Size, markerHeader->Size, + dataSize, dataSize), parent); + return U_SUCCESS; + } + + // Get parsing data + PARSING_DATA pdata = parsingDataFromUModelIndex(parent); + + // Construct header and body + UByteArray header = store.left(sizeof(OEM_ACTIVATION_MARKER)); + + // Add info + UString name("SLIC marker"); + UString info = usprintf("Type: 1h\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: 0h (0)\n" + "Version: %08Xh\nOEM ID: %s\nOEM table ID: %s\nWindows flag: WINDOWS\nSLIC version: %08Xh", + markerHeader->Size, markerHeader->Size, + header.size(), header.size(), + markerHeader->Version, + (const char*)UString((const char*)&(markerHeader->OemId)).left(6).toLocal8Bit(), + (const char*)UString((const char*)&(markerHeader->OemTableId)).left(8).toLocal8Bit(), + markerHeader->SlicVersion); + + // Add correct offset + pdata.offset = parentOffset; + + // Add tree item + index = model->addItem(Types::SlicData, Subtypes::MarkerSlicData, name, UString(), info, header, UByteArray(), UByteArray(), true, parsingDataToUByteArray(pdata), parent); + + return U_SUCCESS; +} + +USTATUS NvramParser::parseIntelMicrocodeHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) +{ + const UINT32 dataSize = (const UINT32)store.size(); + + // Check data size + if (dataSize < sizeof(INTEL_MICROCODE_HEADER)) { + msg(UString("parseIntelMicrocodeHeader: volume body is too small even for Intel microcode header"), parent); + return U_SUCCESS; + } + + // Get Intel microcode header + const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)store.constData(); + + // Check store size + if (dataSize < ucodeHeader->TotalSize) { + msg(usprintf("parseIntelMicrocodeHeader: Intel microcode size %Xh (%u) is greater than volume body size %Xh (%u)", + ucodeHeader->TotalSize, ucodeHeader->TotalSize, + dataSize, dataSize), parent); + return U_SUCCESS; + } + + // Get parsing data + PARSING_DATA pdata = parsingDataFromUModelIndex(parent); + + // Construct header and body + UByteArray header = store.left(sizeof(INTEL_MICROCODE_HEADER)); + UByteArray body = store.mid(sizeof(INTEL_MICROCODE_HEADER), ucodeHeader->DataSize); + + //TODO: recalculate checksum + + // Add info + UString name("Intel microcode"); + UString info = usprintf("Revision: 1h\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\n" + "Date: %08Xh\nCPU signature: %08Xh\nChecksum: %08Xh\nLoader revision: %08Xh\nCPU flags: %08Xh", + ucodeHeader->TotalSize, ucodeHeader->TotalSize, + header.size(), header.size(), + body.size(), body.size(), + ucodeHeader->Date, + ucodeHeader->CpuSignature, + ucodeHeader->Checksum, + ucodeHeader->LoaderRevision, + ucodeHeader->CpuFlags); + + // Add correct offset + pdata.offset = parentOffset; + + // Add tree item + index = model->addItem(Types::Microcode, Subtypes::IntelMicrocode, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent); + + return U_SUCCESS; +} + +USTATUS NvramParser::parseStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index) +{ + const UINT32 dataSize = (const UINT32)store.size(); + const UINT32* signature = (const UINT32*)store.constData(); + // Check store size + if (dataSize < sizeof(UINT32)) { + msg(UString("parseStoreHeader: volume body is too small even for store signature"), parent); + return U_SUCCESS; + } + + // Check signature and run parser function needed + // VSS/SVS store + if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) + return parseVssStoreHeader(store, parentOffset, parent, index); + // FTW store + else if (*signature == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *signature == EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1) + return parseFtwStoreHeader(store, parentOffset, parent, index); + // FDC store + else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE) + return parseFdcStoreHeader(store, parentOffset, parent, index); + // Apple Fsys/Gaid store + else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *signature == NVRAM_APPLE_GAID_STORE_SIGNATURE) + return parseFsysStoreHeader(store, parentOffset, parent, index); + // EVSA store + else if (*(signature + 1) == NVRAM_EVSA_STORE_SIGNATURE) + return parseEvsaStoreHeader(store, parentOffset, parent, index); + // Phoenix SCT flash map + else if (*signature == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1) + return parseFlashMapStoreHeader(store, parentOffset, parent, index); + // Phoenix CMDB store + else if (*signature == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE) + return parseCmdbStoreHeader(store, parentOffset, parent, index); + // SLIC pubkey + else if (*(signature + 4) == OEM_ACTIVATION_PUBKEY_MAGIC) + return parseSlicPubkeyHeader(store, parentOffset, parent, index); + // SLIC marker + else if (*(const UINT64*)(store.constData() + 26) == OEM_ACTIVATION_MARKER_WINDOWS_FLAG) + return parseSlicMarkerHeader(store, parentOffset, parent, index); + // Intel microcode + // Must be checked after SLIC marker because of the same *signature values + else if (*signature == INTEL_MICROCODE_HEADER_VERSION) + return parseIntelMicrocodeHeader(store, parentOffset, parent, index); + + msg(usprintf("parseStoreHeader: don't know how to parse a header with signature %08Xh", *signature), parent); + return U_SUCCESS; +} + +USTATUS NvramParser::parseVssStoreBody(const UModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return U_INVALID_PARAMETER; + + // Get parsing data for the current item + PARSING_DATA pdata = parsingDataFromUModelIndex(index); + UINT32 parentOffset = pdata.offset + model->header(index).size(); + const UByteArray data = model->body(index); + + // Check that the is enough space for variable header + const UINT32 dataSize = (UINT32)data.size(); + if (dataSize < sizeof(VSS_VARIABLE_HEADER)) { + msg(UString("parseVssStoreBody: store body is too small even for VSS variable header"), index); + return U_SUCCESS; + } + + UINT32 offset = 0; + + // Parse all variables + while (1) { + bool isInvalid = false; + bool isAuthenticated = false; + bool isAppleCrc32 = false; + + UINT32 storedCrc32 = 0; + UINT32 calculatedCrc32 = 0; + UINT64 monotonicCounter = 0; + EFI_TIME timestamp = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + UINT32 pubKeyIndex = 0; + + UINT8 subtype = 0; + UString name; + UString text; + EFI_GUID* variableGuid; + CHAR16* variableName = (CHAR16*)L""; + UByteArray header; + UByteArray body; + + UINT32 unparsedSize = dataSize - offset; + + // Get variable header + const VSS_VARIABLE_HEADER* variableHeader = (const VSS_VARIABLE_HEADER*)(data.constData() + offset); + + // Check variable header to fit in still unparsed data + UINT32 variableSize = 0; + if (unparsedSize >= sizeof(VSS_VARIABLE_HEADER) + && variableHeader->StartId == NVRAM_VSS_VARIABLE_START_ID) { + + // Apple VSS variable with CRC32 of the data + if (variableHeader->Attributes & NVRAM_VSS_VARIABLE_APPLE_DATA_CHECKSUM) { + isAppleCrc32 = true; + if (unparsedSize < sizeof(VSS_APPLE_VARIABLE_HEADER)) { + variableSize = 0; + } + else { + const VSS_APPLE_VARIABLE_HEADER* appleVariableHeader = (const VSS_APPLE_VARIABLE_HEADER*)variableHeader; + variableSize = sizeof(VSS_APPLE_VARIABLE_HEADER) + appleVariableHeader->NameSize + appleVariableHeader->DataSize; + variableGuid = (EFI_GUID*)&appleVariableHeader->VendorGuid; + variableName = (CHAR16*)(appleVariableHeader + 1); + + header = data.mid(offset, sizeof(VSS_APPLE_VARIABLE_HEADER) + appleVariableHeader->NameSize); + body = data.mid(offset + header.size(), appleVariableHeader->DataSize); + + // Calculate CRC32 of the variable data + storedCrc32 = appleVariableHeader->DataCrc32; + calculatedCrc32 = crc32(0, (const UINT8*)body.constData(), body.size()); + } + } + + // Authenticated variable + else if ((variableHeader->Attributes & NVRAM_VSS_VARIABLE_AUTHENTICATED_WRITE_ACCESS) + || (variableHeader->Attributes & NVRAM_VSS_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) + || (variableHeader->Attributes & NVRAM_VSS_VARIABLE_APPEND_WRITE) + || (variableHeader->NameSize == 0 && variableHeader->DataSize == 0)) { // If both NameSize and DataSize are zeros, it's auth variable with zero montonic counter + isAuthenticated = true; + if (unparsedSize < sizeof(VSS_AUTH_VARIABLE_HEADER)) { + variableSize = 0; + } + else { + const VSS_AUTH_VARIABLE_HEADER* authVariableHeader = (const VSS_AUTH_VARIABLE_HEADER*)variableHeader; + variableSize = sizeof(VSS_AUTH_VARIABLE_HEADER) + authVariableHeader->NameSize + authVariableHeader->DataSize; + variableGuid = (EFI_GUID*)&authVariableHeader->VendorGuid; + variableName = (CHAR16*)(authVariableHeader + 1); + + header = data.mid(offset, sizeof(VSS_AUTH_VARIABLE_HEADER) + authVariableHeader->NameSize); + body = data.mid(offset + header.size(), authVariableHeader->DataSize); + + monotonicCounter = authVariableHeader->MonotonicCounter; + timestamp = authVariableHeader->Timestamp; + pubKeyIndex = authVariableHeader->PubKeyIndex; + } + } + + // Normal VSS variable + if (!isAuthenticated && !isAppleCrc32) { + variableSize = sizeof(VSS_VARIABLE_HEADER) + variableHeader->NameSize + variableHeader->DataSize; + variableGuid = (EFI_GUID*)&variableHeader->VendorGuid; + variableName = (CHAR16*)(variableHeader + 1); + + header = data.mid(offset, sizeof(VSS_VARIABLE_HEADER) + variableHeader->NameSize); + body = data.mid(offset + header.size(), variableHeader->DataSize); + } + + // There is also a case of authenticated Apple variables, but I haven't seen one yet + + // Check variable state + if (variableHeader->State != NVRAM_VSS_VARIABLE_ADDED && variableHeader->State != NVRAM_VSS_VARIABLE_HEADER_VALID) { + isInvalid = true; + } + + // Check variable size + if (variableSize > unparsedSize) { + unparsedSize = 0; + } + } + + // Can't parse further, add the last element and break the loop + if (!variableSize) { + // Check if the data left is a free space or a padding + UByteArray padding = data.mid(offset, unparsedSize); + UINT8 type; + + if (padding.count(pdata.emptyByte) == padding.size()) { + // It's a free space + name = UString("Free space"); + type = Types::FreeSpace; + subtype = 0; + } + else { + // Nothing is parsed yet, but the store is not empty + if (!offset) { + msg(UString("parseVssStoreBody: store can't be parsed as VSS store"), index); + return U_SUCCESS; + } + + // It's a padding + name = UString("Padding"); + type = Types::Padding; + subtype = getPaddingType(padding); + } + + // Get info + UString info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size()); + + // Construct parsing data + pdata.offset = parentOffset + offset; + + // Add tree item + model->addItem(type, subtype, name, UString(), info, UByteArray(), padding, UByteArray(), false, parsingDataToUByteArray(pdata), index); + + return U_SUCCESS; + } + + UString info; + + // Rename invalid variables + if (isInvalid) { + name = UString("Invalid"); + } + else { // Add GUID and text for valid variables + name = guidToUString(*variableGuid); + info += UString("Variable GUID: ") + name + UString("\n"); + text = UString::fromUtf16(variableName); + } + + // Add info + info += usprintf("Full size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nAttributes: %08Xh (", + variableSize, variableSize, + header.size(), header.size(), + body.size(), body.size(), + variableHeader->State, + variableHeader->Attributes) + vssAttributesToUString(variableHeader->Attributes) + UString(")"); + + // Set subtype and add related info + if (isInvalid) + subtype = Subtypes::InvalidVssEntry; + else if (isAuthenticated) { + subtype = Subtypes::AuthVssEntry; + info += usprintf("\nMonotonic counter: %" PRIX64 "h\nTimestamp: ", monotonicCounter) + efiTimeToUString(timestamp) + + usprintf("\nPubKey index: %u", pubKeyIndex); + } + else if (isAppleCrc32) { + subtype = Subtypes::AppleVssEntry; + info += usprintf("\nData checksum: %08Xh", storedCrc32) + + (storedCrc32 != calculatedCrc32 ? usprintf(", invalid, should be %08Xh", calculatedCrc32) : UString(", valid")); + } + else + subtype = Subtypes::StandardVssEntry; + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Add tree item + model->addItem(Types::VssEntry, subtype, name, text, info, header, body, UByteArray(), false, parsingDataToUByteArray(pdata), index); + + // Move to next variable + offset += variableSize; + } + + return U_SUCCESS; +} + +USTATUS NvramParser::parseFsysStoreBody(const UModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return U_INVALID_PARAMETER; + + // Get parsing data for the current item + PARSING_DATA pdata = parsingDataFromUModelIndex(index); + UINT32 parentOffset = pdata.offset + model->header(index).size(); + const UByteArray data = model->body(index); + + // Check that the is enough space for variable header + const UINT32 dataSize = (UINT32)data.size(); + UINT32 offset = 0; + + // Parse all variables + while (1) { + UINT32 unparsedSize = dataSize - offset; + UINT32 variableSize = 0; + + // Get nameSize and name of the variable + const UINT8 nameSize = *(UINT8*)(data.constData() + offset); + // Check sanity + if (unparsedSize >= nameSize + sizeof(UINT8)) { + variableSize = nameSize + sizeof(UINT8); + } + + UByteArray name; + if (variableSize) { + name = data.mid(offset + sizeof(UINT8), nameSize); + // Check for EOF variable + if (nameSize == 3 && name[0] == 'E' && name[1] == 'O' && name[2] == 'F') { + // There is no data afterward, add EOF variable and free space and return + UByteArray header = data.mid(offset, sizeof(UINT8) + nameSize); + UString info = usprintf("Full size: %Xh (%u)", header.size(), header.size()); + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Add EOF tree item + model->addItem(Types::FsysEntry, 0, UString("EOF"), UString(), info, header, UByteArray(), UByteArray(), false, parsingDataToUByteArray(pdata), index); + + // Add free space + offset += header.size(); + unparsedSize = dataSize - offset; + UByteArray body = data.mid(offset); + info = usprintf("Full size: %Xh (%u)", body.size(), body.size()); + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Add free space tree item + model->addItem(Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), body, UByteArray(), false, parsingDataToUByteArray(pdata), index); + + return U_SUCCESS; + } + } + + // Get dataSize and data of the variable + const UINT16 dataSize = *(UINT16*)(data.constData() + offset + sizeof(UINT8) + nameSize); + if (unparsedSize >= sizeof(UINT8) + nameSize + sizeof(UINT16) + dataSize) { + variableSize = sizeof(UINT8) + nameSize + sizeof(UINT16) + dataSize; + } + else { + // Last variable is bad, add the rest as padding and return + UByteArray body = data.mid(offset); + UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size()); + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Add free space tree item + model->addItem(Types::Padding, getPaddingType(body), UString("Padding"), UString(), info, UByteArray(), body, UByteArray(), false, parsingDataToUByteArray(pdata), index); + + // Show message + msg(UString("parseFsysStoreBody: next variable appears too big, added as padding"), index); + + return U_SUCCESS; + } + + // Construct header and body + UByteArray header = data.mid(offset, sizeof(UINT8) + nameSize + sizeof(UINT16)); + UByteArray body = data.mid(offset + sizeof(UINT8) + nameSize + sizeof(UINT16), dataSize); + + // Add info + UString info = usprintf("Full size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)", + variableSize, variableSize, + header.size(), header.size(), + body.size(), body.size()); + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Add tree item + model->addItem(Types::FsysEntry, 0, UString(name.constData()), UString(), info, header, body, UByteArray(), false, parsingDataToUByteArray(pdata), index); + + // Move to next variable + offset += variableSize; + } + + return U_SUCCESS; +} + +USTATUS NvramParser::parseEvsaStoreBody(const UModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return U_INVALID_PARAMETER; + + // Get parsing data for the current item + PARSING_DATA pdata = parsingDataFromUModelIndex(index); + UINT32 parentOffset = pdata.offset + model->header(index).size(); + const UByteArray data = model->body(index); + + // Check that the is enough space for entry header + const UINT32 dataSize = (UINT32)data.size(); + UINT32 offset = 0; + + std::map guidMap; + std::map nameMap; + + // Parse all entries + UINT32 unparsedSize = dataSize; + while (unparsedSize) { + UINT32 variableSize = 0; + UString name; + UString info; + UByteArray header; + UByteArray body; + UINT8 subtype; + UINT8 calculated; + + const EVSA_ENTRY_HEADER* entryHeader = (const EVSA_ENTRY_HEADER*)(data.constData() + offset); + + // Check entry size + variableSize = sizeof(EVSA_ENTRY_HEADER); + if (unparsedSize < variableSize || unparsedSize < entryHeader->Size) { + UByteArray body = data.mid(offset); + UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size()); + + // Checke type + UString name("Free space"); + UINT8 type = Types::FreeSpace; + UINT8 subtype = 0; + if (getPaddingType(body) == Subtypes::DataPadding) { + name = UString("Padding"); + type = Types::Padding; + subtype = Subtypes::DataPadding; + } + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Add free space tree item + UModelIndex itemIndex = model->addItem(type, subtype, name, UString(), info, UByteArray(), body, UByteArray(), false, parsingDataToUByteArray(pdata), index); + + // Show message + if (type == Types::Padding) + msg(UString("parseEvsaStoreBody: variable parsing failed, the rest of unparsed store added as padding"), itemIndex); + + break; + } + variableSize = entryHeader->Size; + + // Recalculate entry checksum + calculated = calculateChecksum8(((const UINT8*)entryHeader) + 2, entryHeader->Size - 2); + + // GUID entry + if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_GUID1 || + entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_GUID2) { + const EVSA_GUID_ENTRY* guidHeader = (const EVSA_GUID_ENTRY*)entryHeader; + header = data.mid(offset, sizeof(EVSA_GUID_ENTRY)); + body = data.mid(offset + sizeof(EVSA_GUID_ENTRY), guidHeader->Header.Size - sizeof(EVSA_GUID_ENTRY)); + EFI_GUID guid = *(EFI_GUID*)body.constData(); + name = guidToUString(guid); + info = UString("GUID: ") + name + usprintf("\nFull size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh", + variableSize, variableSize, + header.size(), header.size(), + body.size(), body.size(), + guidHeader->Header.Type, + guidHeader->Header.Checksum) + + (guidHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid")) + + usprintf("\nGuidId: %04Xh", guidHeader->GuidId); + subtype = Subtypes::GuidEvsaEntry; + guidMap.insert(std::pair(guidHeader->GuidId, guid)); + } + // Name entry + else if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_NAME1 || + entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_NAME2) { + const EVSA_NAME_ENTRY* nameHeader = (const EVSA_NAME_ENTRY*)entryHeader; + header = data.mid(offset, sizeof(EVSA_NAME_ENTRY)); + body = data.mid(offset + sizeof(EVSA_NAME_ENTRY), nameHeader->Header.Size - sizeof(EVSA_NAME_ENTRY)); + name = UString::fromUtf16((const CHAR16*)body.constData()); + info = UString("GUID: ") + name + usprintf("\nFull size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh", + variableSize, variableSize, + header.size(), header.size(), + body.size(), body.size(), + nameHeader->Header.Type, + nameHeader->Header.Checksum) + + (nameHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid")) + + usprintf("\nVarId: %04Xh", nameHeader->VarId); + subtype = Subtypes::NameEvsaEntry; + nameMap.insert(std::pair(nameHeader->VarId, name)); + } + // Data entry + else if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA1 || + entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA2 || + entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA_INVALID) { + const EVSA_DATA_ENTRY* dataHeader = (const EVSA_DATA_ENTRY*)entryHeader; + // Check for extended header + UINT32 headerSize = sizeof(EVSA_DATA_ENTRY); + UINT32 dataSize = dataHeader->Header.Size - sizeof(EVSA_DATA_ENTRY); + if (dataHeader->Attributes & NVRAM_EVSA_DATA_EXTENDED_HEADER) { + const EVSA_DATA_ENTRY_EXTENDED* dataHeaderExtended = (const EVSA_DATA_ENTRY_EXTENDED*)entryHeader; + headerSize = sizeof(EVSA_DATA_ENTRY_EXTENDED); + dataSize = dataHeaderExtended->DataSize; + variableSize = headerSize + dataSize; + } + + header = data.mid(offset, headerSize); + body = data.mid(offset + headerSize, dataSize); + name = UString("Data"); + info = usprintf("Full size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh", + variableSize, variableSize, + headerSize, headerSize, + dataSize, dataSize, + dataHeader->Header.Type, + dataHeader->Header.Checksum) + + (dataHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid")) + + usprintf("\nVarId: %04Xh\nGuidId: %04Xh\nAttributes: %08Xh (", + dataHeader->VarId, + dataHeader->GuidId, + dataHeader->Attributes) + + evsaAttributesToUString(dataHeader->Attributes) + UString(")"); + subtype = Subtypes::DataEvsaEntry; + } + // Unknown entry or free space + else { + UByteArray body = data.mid(offset); + UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size()); + + // Check type + UString name("Free space"); + UINT8 type = Types::FreeSpace; + UINT8 subtype = 0; + if (getPaddingType(body) == Subtypes::DataPadding) { + name = UString("Padding"); + type = Types::Padding; + subtype = Subtypes::DataPadding; + } + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Add free space tree item + UModelIndex itemIndex = model->addItem(type, subtype, name, UString(), info, UByteArray(), body, UByteArray(), false, parsingDataToUByteArray(pdata), index); + + // Show message + if (type == Types::Padding) + msg(usprintf("parseEvsaStoreBody: unknown variable of type %02Xh found at offset %Xh, the rest of unparsed store added as padding",entryHeader->Type, offset), itemIndex); + break; + } + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Add tree item + model->addItem(Types::EvsaEntry, subtype, name, UString(), info, header, body, UByteArray(), false, parsingDataToUByteArray(pdata), index); + + // Move to next variable + offset += variableSize; + unparsedSize = dataSize - offset; + } + + // Reparse all data variables to detect invalid ones and assign name and test to valid ones + for (int i = 0; i < model->rowCount(index); i++) { + UModelIndex current = index.child(i, 0); + if (model->subtype(current) == Subtypes::DataEvsaEntry) { + UByteArray header = model->header(current); + const EVSA_DATA_ENTRY* dataHeader = (const EVSA_DATA_ENTRY*)header.constData(); + UString guid; + if (guidMap.count(dataHeader->GuidId)) + guid = guidToUString(guidMap[dataHeader->GuidId]); + UString name; + if (nameMap.count(dataHeader->VarId)) + name = nameMap[dataHeader->VarId]; + + // Check for variable validity + if (guid.isEmpty() && name.isEmpty()) { // Both name and guid aren't found + model->setSubtype(current, Subtypes::InvalidEvsaEntry); + model->setName(current, UString("Invalid")); + msg(UString("parseEvsaStoreBody: data variable with invalid GuidId and invalid VarId"), current); + } + else if (guid.isEmpty()) { // Guid not found + model->setSubtype(current, Subtypes::InvalidEvsaEntry); + model->setName(current, UString("Invalid")); + msg(UString("parseEvsaStoreBody: data variable with invalid GuidId"), current); + } + else if (name.isEmpty()) { // Name not found + model->setSubtype(current, Subtypes::InvalidEvsaEntry); + model->setName(current, UString("Invalid")); + msg(UString("parseEvsaStoreBody: data variable with invalid VarId"), current); + } + else { // Variable is OK, rename it + if (dataHeader->Header.Type == NVRAM_EVSA_ENTRY_TYPE_DATA_INVALID) { + model->setSubtype(current, Subtypes::InvalidEvsaEntry); + model->setName(current, UString("Invalid")); + } + else { + model->setName(current, guid); + } + model->setText(current, name); + model->addInfo(current, UString("GUID: ") + guid + UString("\nName: ") + name + UString("\n"), false); + } + } + } + + return U_SUCCESS; +} + + +USTATUS NvramParser::parseFlashMapBody(const UModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return U_INVALID_PARAMETER; + + // Get parsing data for the current item + PARSING_DATA pdata = parsingDataFromUModelIndex(index); + UINT32 parentOffset = pdata.offset + model->header(index).size(); + const UByteArray data = model->body(index); + + + const UINT32 dataSize = (UINT32)data.size(); + UINT32 offset = 0; + UINT32 unparsedSize = dataSize; + // Parse all entries + while (unparsedSize) { + const PHOENIX_FLASH_MAP_ENTRY* entryHeader = (const PHOENIX_FLASH_MAP_ENTRY*)(data.constData() + offset); + + // Check entry size + if (unparsedSize < sizeof(PHOENIX_FLASH_MAP_ENTRY)) { + // Last variable is bad, add the rest as padding and return + UByteArray body = data.mid(offset); + UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size()); + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Add free space tree item + model->addItem(Types::Padding, getPaddingType(body), UString("Padding"), UString(), info, UByteArray(), body, UByteArray(), false, parsingDataToUByteArray(pdata), index); + + // Show message + if (unparsedSize < entryHeader->Size) + msg(UString("parseFlashMapBody: next entry appears too big, added as padding"), index); + + break; + } + + UString name = guidToUString(entryHeader->Guid); + + // Construct header + UByteArray header = data.mid(offset, sizeof(PHOENIX_FLASH_MAP_ENTRY)); + + // Add info + UString info = UString("Entry GUID: ") + name + usprintf("\nFull size: 24h (36)\nHeader size: 24h (36)\nBody size: 0h (0)\n" + "Entry type: %04Xh\nData type: %04Xh\nMemory address: %08Xh\nSize: %08Xh\nOffset: %08Xh", + entryHeader->EntryType, + entryHeader->DataType, + entryHeader->PhysicalAddress, + entryHeader->Size, + entryHeader->Offset); + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Determine subtype + UINT8 subtype = 0; + switch (entryHeader->DataType) { + case NVRAM_PHOENIX_FLASH_MAP_ENTRY_TYPE_VOLUME: + subtype = Subtypes::VolumeFlashMapEntry; + break; + case NVRAM_PHOENIX_FLASH_MAP_ENTRY_TYPE_DATA_BLOCK: + subtype = Subtypes::DataFlashMapEntry; + break; + } + + // Add tree item + model->addItem(Types::FlashMapEntry, subtype, name, flashMapGuidToUString(entryHeader->Guid), info, header, UByteArray(), UByteArray(), true, parsingDataToUByteArray(pdata), index); + + // Move to next variable + offset += sizeof(PHOENIX_FLASH_MAP_ENTRY); + unparsedSize = dataSize - offset; + } + + return U_SUCCESS; +} +#endif // U_ENABLE_NVRAM_PARSING_SUPPORT diff --git a/common/nvramparser.h b/common/nvramparser.h new file mode 100644 index 0000000..10678c4 --- /dev/null +++ b/common/nvramparser.h @@ -0,0 +1,90 @@ +/* nvramparser.h + +Copyright (c) 2016, 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 +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ + +#ifndef NVRAMPARSER_H +#define NVRAMPARSER_H + +#include + +#include "basetypes.h" +#include "ustring.h" +#include "ubytearray.h" +#include "treemodel.h" + +#ifdef U_ENABLE_NVRAM_PARSING_SUPPORT +class NvramParser +{ +public: + // Default constructor and destructor + NvramParser(TreeModel* treeModel) : model(treeModel) {} + ~NvramParser() {} + + // Returns messages + std::vector > getMessages() const { return messagesVector; } + // Clears messages + void clearMessages() { messagesVector.clear(); } + + // NVRAM parsing + USTATUS parseNvramVolumeBody(const UModelIndex & index); + USTATUS parseNvarStore(const UModelIndex & index); + +private: + TreeModel *model; + std::vector > messagesVector; + void msg(const UString message, const UModelIndex index = UModelIndex()) { + messagesVector.push_back(std::pair(message, index)); + }; + + USTATUS findNextStore(const UModelIndex & index, const UByteArray & volume, const UINT32 parentOffset, const UINT32 storeOffset, UINT32 & nextStoreOffset); + USTATUS getStoreSize(const UByteArray & data, const UINT32 storeOffset, UINT32 & storeSize); + USTATUS parseStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); + + USTATUS parseVssStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseFtwStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseFdcStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseFsysStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseEvsaStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseFlashMapStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseCmdbStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseSlicPubkeyHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseSlicMarkerHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseIntelMicrocodeHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index); + + USTATUS parseVssStoreBody(const UModelIndex & index); + USTATUS parseFsysStoreBody(const UModelIndex & index); + USTATUS parseEvsaStoreBody(const UModelIndex & index); + USTATUS parseFlashMapBody(const UModelIndex & index); +}; +#else +class NvramParser +{ +public: + // Default constructor and destructor + NvramParser(TreeModel* treeModel) : model(treeModel) {} + ~NvramParser() {} + + // Returns messages + std::vector > getMessages() const { return messagesVector; } + // Clears messages + void clearMessages() { messagesVector.clear(); } + + // NVRAM parsing + USTATUS parseNvramVolumeBody(const UModelIndex &) { return U_SUCCESS; } + USTATUS parseNvarStore(const UModelIndex &) { return U_SUCCESS; } +private: + TreeModel *model; + std::vector > messagesVector; +}; +#endif // U_ENABLE_NVRAM_PARSING_SUPPORT +#endif // NVRAMPARSER_H diff --git a/common/peimage.h b/common/peimage.h index 6c14d9c..91dea3e 100644 --- a/common/peimage.h +++ b/common/peimage.h @@ -16,8 +16,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #ifndef PEIMAGE_H #define PEIMAGE_H -#include "ustring.h" #include "basetypes.h" +#include "ustring.h" extern UString machineTypeToUString(UINT16 machineType); diff --git a/common/treeitem.h b/common/treeitem.h index b4971b8..17c57a4 100644 --- a/common/treeitem.h +++ b/common/treeitem.h @@ -17,9 +17,9 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include +#include "basetypes.h" #include "ubytearray.h" #include "ustring.h" -#include "basetypes.h" class TreeItem { @@ -38,7 +38,7 @@ public: // Model support operations TreeItem *child(int row); // Non-trivial implementation in CPP file - int childCount() const {return childItems.size(); } + int childCount() const {return (int)childItems.size(); } int columnCount() const { return 5; } UString data(int column) const; // Non-trivial implementation in CPP file int row() const; // Non-trivial implementation in CPP file diff --git a/common/treemodel.cpp b/common/treemodel.cpp index 8ec8450..7a7e106 100644 --- a/common/treemodel.cpp +++ b/common/treemodel.cpp @@ -11,7 +11,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ -#include "treeitem.h" #include "treemodel.h" #if defined(QT_CORE_LIB) diff --git a/common/treemodel.h b/common/treemodel.h index 84ee141..fcc64a3 100644 --- a/common/treemodel.h +++ b/common/treemodel.h @@ -1,6 +1,6 @@ /* treemodel.h -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2016, 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 which accompanies this distribution. The full text of the license may be found at diff --git a/common/types.cpp b/common/types.cpp index 4f503d7..d56efe0 100644 --- a/common/types.cpp +++ b/common/types.cpp @@ -10,7 +10,6 @@ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ -#include "ustring.h" #include "types.h" #include "ffs.h" #include "fit.h" diff --git a/common/types.h b/common/types.h index 8480dbe..a998014 100644 --- a/common/types.h +++ b/common/types.h @@ -15,6 +15,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define TYPES_H #include "basetypes.h" +#include "ustring.h" // Actions namespace Actions @@ -136,7 +137,7 @@ namespace Subtypes { PubkeySlicData = 180, MarkerSlicData }; -}; +} // *ToUString conversion routines extern UString actionTypeToUString(const UINT8 action); diff --git a/common/ubytearray.h b/common/ubytearray.h index 82a6ff3..647b53c 100644 --- a/common/ubytearray.h +++ b/common/ubytearray.h @@ -19,7 +19,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define UByteArray QByteArray #else // Use own implementation -#include +#include #include #include #include @@ -32,6 +32,7 @@ public: UByteArray(const std::basic_string & bs) : d(bs) {} UByteArray(const std::vector & bc) : d(bc.data(), bc.size()) {} UByteArray(const char* bytes, int32_t size) : d(bytes, size) {} + UByteArray(const size_t n, char c) : d(n, c) {} ~UByteArray() {} bool isEmpty() const { return d.length() == 0; } @@ -41,6 +42,9 @@ public: const char* constData() const { return d.c_str(); } void clear() { d.clear(); } + UByteArray toUpper() { std::basic_string s = d; std::transform(s.begin(), s.end(), s.begin(), ::toupper); return UByteArray(s); } + uint32_t toUInt(bool* ok = NULL, const uint8_t base = 10) { return (uint32_t)std::strtoul(d.c_str(), NULL, base); } + int32_t size() const { return d.size(); } int32_t count(char ch) const { return std::count(d.begin(), d.end(), ch); } char at(uint32_t i) const { return d.at(i); } @@ -49,11 +53,19 @@ public: bool startsWith(const UByteArray & ba) const { return 0 == d.find(ba.d, 0); } int indexOf(const UByteArray & ba, int from = 0) const { return d.find(ba.d, from); } - int lastIndexOf(const UByteArray & ba, int from = 0) const { return d.rfind(ba.d, from); } + int lastIndexOf(const UByteArray & ba, int from = 0) const { + size_t old_index = d.npos; + size_t index = d.find(ba.d, from); + while (index != d.npos) { + old_index = index; + index = d.find(ba.d, index + 1); + } + return old_index; + } UByteArray left(int32_t len) const { return d.substr(0, len); } - UByteArray right(int32_t len) const { return d.substr(d.size() - 1 - len, len); }; - UByteArray mid(int32_t pos, int32_t len = -1) const { return d.substr(pos, len); }; + UByteArray right(int32_t len) const { return d.substr(d.size() - 1 - len, len); } + UByteArray mid(int32_t pos, int32_t len = -1) const { return d.substr(pos, len); } UByteArray & operator=(const UByteArray & ba) { d = ba.d; return *this; } UByteArray & operator+=(const UByteArray & ba) { d += ba.d; return *this; } diff --git a/common/ustring.cpp b/common/ustring.cpp index 6591d26..4f15701 100644 --- a/common/ustring.cpp +++ b/common/ustring.cpp @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #include "ustring.h" -#include +#include #if defined(QT_CORE_LIB) UString usprintf(const char* fmt, ...) diff --git a/common/utility.cpp b/common/utility.cpp index a678200..9b9e4dd 100644 --- a/common/utility.cpp +++ b/common/utility.cpp @@ -243,9 +243,9 @@ USTATUS decompress(const UByteArray & compressedData, UINT8 & algorithm, UByteAr efiDecompressed = (UINT8*)malloc(decompressedSize); scratch = (UINT8*)malloc(scratchSize); if (!decompressed || !efiDecompressed || !scratch) { - if (decompressed) free(decompressed); - if (efiDecompressed) free(efiDecompressed); - if (scratch) free(scratch); + free(decompressed); + free(efiDecompressed); + free(scratch); return U_STANDARD_DECOMPRESSION_FAILED; } @@ -361,4 +361,13 @@ UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize) } return (UINT16)(0x10000 - counter); -} \ No newline at end of file +} + +UINT8 getPaddingType(const UByteArray & padding) +{ + if (padding.count('\x00') == padding.size()) + return Subtypes::ZeroPadding; + if (padding.count('\xFF') == padding.size()) + return Subtypes::OnePadding; + return Subtypes::DataPadding; +} diff --git a/common/utility.h b/common/utility.h index dcde3e5..18e1e96 100644 --- a/common/utility.h +++ b/common/utility.h @@ -14,9 +14,9 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #ifndef UTILITY_H #define UTILITY_H +#include "basetypes.h" #include "ustring.h" #include "treemodel.h" -#include "basetypes.h" #include "parsingdata.h" // Returns either new parsing data instance or obtains it from index @@ -46,4 +46,7 @@ UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize); // 16bit checksum calculation routine UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize); +// Return padding type from it's contents +UINT8 getPaddingType(const UByteArray & padding); + #endif // UTILITY_H diff --git a/qhexedit2/qhexedit.cpp b/qhexedit2/qhexedit.cpp index 8f101ed..43e7f54 100644 --- a/qhexedit2/qhexedit.cpp +++ b/qhexedit2/qhexedit.cpp @@ -676,8 +676,10 @@ void QHexEdit::keyPressEvent(QKeyEvent *event) if (event->matches(QKeySequence::Copy)) { QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()).toHex(); - for (qint64 idx = 32; idx < ba.size(); idx +=33) + for (qint64 idx = 32; idx < ba.size(); idx += 33) ba.insert(idx, "\n"); + if(_upperCase) + ba = ba.toUpper(); QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(ba); }