From 5b240296fb2082f148258765bed3b8b6d9c4f22d Mon Sep 17 00:00:00 2001 From: Allan Date: Thu, 18 Dec 2025 14:21:17 -0600 Subject: [PATCH] Added the capability to save MFS partitions as binaries during unpack Hashes matched in ME Image Tool. Maybe the additions could be implemented with the mfs_write sub since there's a delay when writing files 6 & 7. --- MEA.py | 4710 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 2358 insertions(+), 2352 deletions(-) diff --git a/MEA.py b/MEA.py index 3563a14..1dac14b 100644 --- a/MEA.py +++ b/MEA.py @@ -94,11 +94,11 @@ def mea_help() : '-html : Writes parsable HTML info files during MEA operation\n' '-json : Writes parsable JSON info files during MEA operation' ) - + print(col_g + '\nCopyright (C) 2014-2025 Plato Mavropoulos' + col_e) - + if getattr(sys, 'frozen', False) : print(col_c + '\nRunning in frozen state!' + col_e) - + mea_exit(0) # Process MEA Parameters @@ -106,7 +106,7 @@ class MEA_Param: def __init__(self, source): self.val = ['-?','-skip','-unp86','-ver86','-bug86','-html','-json','-pdb','-dbn', '-mass','-dfpt','-exit','-ftbl','-rcfg','-chk','-byp','-duc','-dcm','-out'] - + self.help_scr = False self.skip_intro = False self.cse_unpack = False @@ -126,7 +126,7 @@ def __init__(self, source): self.upd_dis = False self.copy_dis = False self.out_dir = None - + if '-?' in source : self.help_scr = True if '-skip' in source : self.skip_intro = True if '-unp86' in source : self.cse_unpack = True @@ -145,13 +145,13 @@ def __init__(self, source): if '-byp' in source : self.bypass = True # Hidden if '-duc' in source : self.upd_dis = True if '-dcm' in source : self.copy_dis = True - + if '-out' in source: out_dir_idx = source.index('-out') + 1 - + if len(source) > out_dir_idx: self.out_dir = source.pop(out_dir_idx).strip('"').strip("'") - + if self.mass_scan or self.db_print_new or self.out_dir: self.skip_intro = True @@ -165,7 +165,7 @@ def function() : self.result = target(*args, **kwargs) super().__init__(group=group, target=function, name=name, daemon=daemon) - + # Engine/Graphics/Independent Structures class FPT_Pre_Header(ctypes.LittleEndianStructure) : # (ROM_BYPASS) _pack_ = 1 @@ -176,18 +176,18 @@ class FPT_Pre_Header(ctypes.LittleEndianStructure) : # (ROM_BYPASS) ('ROMB_Instr_3', uint32_t), # 0x0C # 0x10 ] - + def hdr_print_cse(self) : NA = [0,0xFFFFFFFF] # Non-ROMB or IFWI EXTR - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Flash Partition Table ROM-Bypass' + col_e pt.add_row(['Instruction 0', 'N/A' if self.ROMB_Instr_0 in NA else '0x%0.8X' % self.ROMB_Instr_0]) pt.add_row(['Instruction 1', 'N/A' if self.ROMB_Instr_1 in NA else '0x%0.8X' % self.ROMB_Instr_1]) pt.add_row(['Instruction 2', 'N/A' if self.ROMB_Instr_2 in NA else '0x%0.8X' % self.ROMB_Instr_2]) pt.add_row(['Instruction 3', 'N/A' if self.ROMB_Instr_3 in NA else '0x%0.8X' % self.ROMB_Instr_3]) - + return pt class FPT_Header(ctypes.LittleEndianStructure) : # Flash Partition Table v1.0 & v2.0 (FPT_HEADER) @@ -209,14 +209,14 @@ class FPT_Header(ctypes.LittleEndianStructure) : # Flash Partition Table v1.0 & ('FitBuild', uint16_t), # 0x1E # 0x20 ] - + def hdr_print_cse(self) : NA = 0xFFFFFFFF # IFWI EXTR - + fit_ver = '%d.%d.%d.%d' % (self.FitMajor,self.FitMinor,self.FitHotfix,self.FitBuild) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Flash Partition Table 2.0 Header' + col_e pt.add_row(['Tag', '%s' % self.Tag.decode('utf-8')]) pt.add_row(['Partition Count', '%d' % self.NumPartitions]) @@ -229,9 +229,9 @@ def hdr_print_cse(self) : pt.add_row(['Reserved', 'N/A' if self.UMASize == NA else '0x%X' % self.UMASize]) pt.add_row(['Flash Layout', 'N/A' if self.Flags == NA else sector_types[self.Flags]]) pt.add_row(['Flash Image Tool', 'N/A' if self.FitMajor in [0,0xFFFF] else fit_ver]) - + return pt - + class FPT_Header_21(ctypes.LittleEndianStructure) : # Flash Partition Table v2.1 (FPT_HEADER) _pack_ = 1 _fields_ = [ @@ -251,16 +251,16 @@ class FPT_Header_21(ctypes.LittleEndianStructure) : # Flash Partition Table v2.1 ('FitBuild', uint16_t), # 0x1E # 0x20 ] - + # When $FPT Redundancy is set, a backup of $FPT is kept at 0x1000 - + def hdr_print_cse(self) : f1,f2 = self.get_flags() - + fit_ver = '%d.%d.%d.%d' % (self.FitMajor,self.FitMinor,self.FitHotfix,self.FitBuild) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Flash Partition Table 2.1 Header' + col_e pt.add_row(['Tag', '%s' % self.Tag.decode('utf-8')]) pt.add_row(['Partition Count', '%d' % self.NumPartitions]) @@ -274,13 +274,13 @@ def hdr_print_cse(self) : pt.add_row(['SPS Flags', '0x%X' % self.SPSFlags]) pt.add_row(['Checksum', '0x%X' % self.HeaderChecksum]) pt.add_row(['Flash Image Tool', 'N/A' if self.FitMajor in [0,0xFFFF] else fit_ver]) - + return pt - + def get_flags(self) : flags = FPT_Header_21_GetFlags() flags.asbytes = self.Flags - + return flags.b.Redundancy, flags.b.Reserved class FPT_Header_21_Flags(ctypes.LittleEndianStructure): @@ -308,12 +308,12 @@ class FPT_Entry(ctypes.LittleEndianStructure) : # (FPT_ENTRY) ('Flags', uint32_t), # 0x1C (FPT_ENTRY_ATTRIBUTES) # 0x20 ] - + def hdr_print_cse(self) : f1,f2,f3,f4,f5,f6,f7 = self.get_flags() - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Flash Partition Table Entry' + col_e pt.add_row(['Name', '%s' % self.Name.decode('utf-8')]) pt.add_row(['Reserved 0', '0x%X' % int.from_bytes(self.Owner, 'little')]) @@ -328,13 +328,13 @@ def hdr_print_cse(self) : pt.add_row(['Built With Length 2', '0x%X' % f5]) pt.add_row(['Reserved 5', '0x%X' % f6]) pt.add_row(['Entry Valid', 'No' if f7 == 0xFF else 'Yes']) - + return pt - + def get_flags(self) : flags = FPT_Entry_GetFlags() flags.asbytes = self.Flags - + return flags.b.Type, flags.b.CopyToDramCache, flags.b.Reserved0, flags.b.BuiltWithLength1, flags.b.BuiltWithLength2, \ flags.b.Reserved1, flags.b.EntryValid @@ -354,7 +354,7 @@ class FPT_Entry_GetFlags(ctypes.Union): ('b', FPT_Entry_Flags), ('asbytes', uint32_t) ] - + class GSC_Info_FWI(ctypes.LittleEndianStructure) : # GSC Firmware Image Info (igsc_system.h > gsc_fwu_fw_image_data) _pack_ = 1 _fields_ = [ @@ -373,24 +373,24 @@ class GSC_Info_FWI(ctypes.LittleEndianStructure) : # GSC Firmware Image Info (ig ('VCN', uint32_t), # 0x1C # 0x20 ] - + # FWType & FWSKU are also used by CSE_Ext_0F_R2 & CSE_Ext_23, remember to change them as well! - + def get_flags(self) : fw_type = CSE_Ext_0F_R2_GetFWType() fw_type.asbytes = self.FWType fw_sub_type = CSE_Ext_0F_R2_GetFWSKU() fw_sub_type.asbytes = self.FWSKU - + return fw_type.b.FWType, fw_type.b.Reserved, fw_sub_type.b.FWSKU, fw_sub_type.b.Reserved - + def gsc_print(self) : f1,f2,f3,f4 = self.get_flags() - + gsc_ver = '%d.%d.%d.%d' % (self.GSCMajor,self.GSCMinor,self.GSCHotfix,self.GSCBuild) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'GSC Firmware Image Info' + col_e pt.add_row(['Project Name', self.Project.decode('utf-8')]) pt.add_row(['Project Hotfix', self.Hotfix]) @@ -404,9 +404,9 @@ def gsc_print(self) : pt.add_row(['ARB SVN', self.ARBSVN]) pt.add_row(['TCB SVN', self.TCBSVN]) pt.add_row(['VCN', self.VCN]) - + return pt - + class GSC_Info_IUP(ctypes.LittleEndianStructure) : # GSC Independent Update Partition Info (igsc_system.h > gsc_fwu_iup_data) _pack_ = 1 _fields_ = [ @@ -417,19 +417,19 @@ class GSC_Info_IUP(ctypes.LittleEndianStructure) : # GSC Independent Update Part ('VCN', uint32_t), # 0x0C # 0x10 ] - + def gsc_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'GSC Independent Update Partition Info' + col_e pt.add_row(['Name', self.Name.decode('utf-8')]) pt.add_row(['Flags', '0x%X' % self.Flags]) pt.add_row(['Reserved', '0x%X' % self.Reserved]) pt.add_row(['SVN', self.SVN]) pt.add_row(['VCN', self.VCN]) - + return pt - + class GSC_OROM_Header(ctypes.LittleEndianStructure) : # GSC Option ROM Image Header (igsc_oprom.h > oprom_header_ext_v2) _pack_ = 1 _fields_ = [ @@ -445,10 +445,10 @@ class GSC_OROM_Header(ctypes.LittleEndianStructure) : # GSC Option ROM Image Hea ('OROMPayloadOff', uint16_t), # 0x1A # 0x1C ] - + def gsc_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'GSC Option ROM Image Header' + col_e pt.add_row(['Signature', '%0.4X' % self.Signature]) pt.add_row(['Image Size', '0x%X' % (self.ImageSize * 512)]) @@ -460,9 +460,9 @@ def gsc_print(self) : pt.add_row(['EFI Image Offset', '0x%X' % self.EFIImageOffset]) pt.add_row(['PCI Data Header Offset', '0x%X' % self.PCIDataHdrOff]) pt.add_row(['OROM Payload Offset', '0x%X' % self.OROMPayloadOff]) - + return pt - + class GSC_OROM_PCI_Data(ctypes.LittleEndianStructure) : # GSC Option ROM Image Data Header (igsc_oprom.h > oprom_pci_data) _pack_ = 1 _fields_ = [ @@ -482,10 +482,10 @@ class GSC_OROM_PCI_Data(ctypes.LittleEndianStructure) : # GSC Option ROM Image D ('DMTFCLPEntPntPtr',uint16_t), # 0x1A DMTF CLP Entry Point Pointer # 0x1C ] - + def gsc_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'GSC Option ROM Image Data' + col_e pt.add_row(['Signature', self.Signature.decode('utf-8')]) pt.add_row(['Vendor ID', '%0.4X' % self.VEN_ID]) @@ -501,7 +501,7 @@ def gsc_print(self) : pt.add_row(['Maximum Runtime Image Size', '0x%X' % self.MaxRuntimeImgLen]) pt.add_row(['Config Utility Code Header Pointer', '0x%X' % self.ConfUtlCodHdrPtr]) pt.add_row(['DMTF CLP Entry Point Pointer', '0x%X' % self.DMTFCLPEntPntPtr]) - + return pt class CSE_Layout_Table_16(ctypes.LittleEndianStructure) : # IFWI 1.6 (CseLayoutTable, IfwiRegionData) @@ -526,12 +526,12 @@ class CSE_Layout_Table_16(ctypes.LittleEndianStructure) : # IFWI 1.6 (CseLayoutT ('Checksum', uint64_t), # 0x40 2's complement of CSE Layout Table (w/o ROMB), sum of the CSE LT + Checksum = 0 # 0x48 ] - + def hdr_print(self) : NA = [0,0xFFFFFFFF] # Non-ROMB or IFWI EXTR - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'CSE Layout Table 1.6 & 2.0' + col_e pt.add_row(['ROMB Instruction 0', 'N/A' if self.ROMBInstr0 in NA else '0x%0.8X' % self.ROMBInstr0]) pt.add_row(['ROMB Instruction 1', 'N/A' if self.ROMBInstr1 in NA else '0x%0.8X' % self.ROMBInstr1]) @@ -550,7 +550,7 @@ def hdr_print(self) : pt.add_row(['Boot Partition 5 Offset', '0x%X' % self.BP5Offset]) pt.add_row(['Boot Partition 5 Size', '0x%X' % self.BP5Size]) pt.add_row(['Checksum', '0x%X' % self.Checksum]) - + return pt class CSE_Layout_Table_17(ctypes.LittleEndianStructure) : # IFWI 1.7 (CseLayoutTable, IfwiRegionData) @@ -582,15 +582,15 @@ class CSE_Layout_Table_17(ctypes.LittleEndianStructure) : # IFWI 1.7 (CseLayoutT ('ELogSize', uint32_t), # 0x54 # 0x58 ] - + # When CSE Redundancy is set, a backup of BP1 is stored in (the otherwise empty) BP2 - + def hdr_print(self) : f1,f2 = self.get_flags() NA = [0,0xFFFFFFFF] # Non-ROMB or IFWI EXTR - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'CSE Layout Table 1.7' + col_e pt.add_row(['ROMB Instruction 0', 'N/A' if self.ROMBInstr0 in NA else '0x%0.8X' % self.ROMBInstr0]) pt.add_row(['ROMB Instruction 1', 'N/A' if self.ROMBInstr1 in NA else '0x%0.8X' % self.ROMBInstr1]) @@ -617,15 +617,15 @@ def hdr_print(self) : pt.add_row(['Temporary Pages Size', '0x%X' % self.TempPagesSize]) if self.Size >= 0x48 : pt.add_row(['External Fatal Error Log Offset', '0x%X' % self.ELogOffset]) if self.Size >= 0x48 : pt.add_row(['External Fatal Error Log Size', '0x%X' % self.ELogSize]) - + return pt - + def get_flags(self) : flags = CSE_Layout_Table_17_GetFlags() flags.asbytes = self.Flags - + return flags.b.Redundancy, flags.b.Reserved - + class CSE_Layout_Table_17_Flags(ctypes.LittleEndianStructure): _fields_ = [ ('Redundancy', uint8_t, 1), @@ -637,7 +637,7 @@ class CSE_Layout_Table_17_GetFlags(ctypes.Union): ('b', CSE_Layout_Table_17_Flags), ('asbytes', uint8_t) ] - + class BPDT_Header_1(ctypes.LittleEndianStructure) : # Boot Partition Descriptor Table 1.6 & 2.0 (PrimaryBootPartition, SecondaryBootPartition, PrimaryBootPartitionNC, BootPartitionLayout) _pack_ = 1 _fields_ = [ @@ -653,23 +653,23 @@ class BPDT_Header_1(ctypes.LittleEndianStructure) : # Boot Partition Descriptor ('FitBuild', uint16_t), # 0x16 # 0x18 (0x200 <= Header + Entries <= 0x1000) ] - + # Used at IFWI 1.6 & 2.0 platforms - + # XOR Checksum of the redundant block (from the beginning of the BPDT structure, up to and including the S-BPDT) such that # the XOR Checksum of the redundant block including Checksum field is 0. If no redundancy is supported, Checksum field is 0 - + # https://github.com/coreboot/coreboot/blob/master/util/cbfstool/ifwitool.c by coreboot - + # Remember to also update bpdt_match - + def hdr_print(self) : bpdt_ver = {1 : '1.6 & 2.0', 2 : '1.7'} - + fit_ver = '%d.%d.%d.%d' % (self.FitMajor,self.FitMinor,self.FitHotfix,self.FitBuild) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Boot Partition Descriptor Table 1.6 & 2.0 Header' + col_e pt.add_row(['Signature', '0x%0.8X' % self.Signature]) pt.add_row(['Descriptor Count', '%d' % self.DescCount]) @@ -678,9 +678,9 @@ def hdr_print(self) : pt.add_row(['Checksum', '0x%X' % self.Checksum]) pt.add_row(['IFWI Version', '%d' % self.IFWIVersion]) pt.add_row(['Flash Image Tool', 'N/A' if self.FitMajor in [0,0xFFFF] else fit_ver]) - + return pt - + class BPDT_Header_2(ctypes.LittleEndianStructure) : # Boot Partition Descriptor Table 1.7 (PrimaryBootPartition, PrimaryBootPartitionNC) _pack_ = 1 _fields_ = [ @@ -696,19 +696,19 @@ class BPDT_Header_2(ctypes.LittleEndianStructure) : # Boot Partition Descriptor ('FitBuild', uint16_t), # 0x16 # 0x18 (0x200 <= Header + Entries <= 0x1000) ] - + # Used at IFWI 1.7 platform - + # Remember to also update bpdt_match - + def hdr_print(self) : bpdt_ver = {1 : '1.6 & 2.0', 2 : '1.7'} f1,f2 = self.get_flags() - + fit_ver = '%d.%d.%d.%d' % (self.FitMajor,self.FitMinor,self.FitHotfix,self.FitBuild) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Boot Partition Descriptor Table 1.7 Header' + col_e pt.add_row(['Signature', '0x%0.8X' % self.Signature]) pt.add_row(['Descriptor Count', '%d' % self.DescCount]) @@ -718,15 +718,15 @@ def hdr_print(self) : pt.add_row(['Checksum', '0x%X' % self.Checksum]) pt.add_row(['IFWI Version', '0x%X' % self.IFWIVersion]) pt.add_row(['Flash Image Tool', 'N/A' if self.FitMajor in [0,0xFFFF] else fit_ver]) - + return pt - + def get_flags(self) : flags = BPDT_Header_2_GetFlags() flags.asbytes = self.BPDTConfig - + return flags.b.BPDT_R_S, flags.b.Reserved - + class BPDT_Header_2_Flags(ctypes.LittleEndianStructure): _fields_ = [ ('BPDT_R_S', uint8_t, 1), @@ -748,16 +748,16 @@ class BPDT_Entry(ctypes.LittleEndianStructure) : # (BpdtEntry) ("Size", uint32_t), # 0x08 # 0xC ] - + # It is probable that Flags field is relevant to APL IFWI 2.0 platform only # At the rest of IFWI 1.6, 2.0 & 1.7, Type is uint32_t without Flags - + def hdr_print(self) : fvalue = ['No','Yes'] f1,f2,f3,f4,f5 = self.get_flags() - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Boot Partition Descriptor Table Entry' + col_e pt.add_row(['Type', bpdt_dict[self.Type] if self.Type in bpdt_dict else 'Unknown']) pt.add_row(['Split Sub-Partition 1st Part', fvalue[f1]]) @@ -767,16 +767,16 @@ def hdr_print(self) : pt.add_row(['Flags Reserved', '0x%X' % f5]) pt.add_row(['Offset', '0x%X' % self.Offset]) pt.add_row(['Size', '0x%X' % self.Size]) - + return pt - + def get_flags(self) : flags = BPDT_Entry_GetFlags() flags.asbytes = self.Flags - + return flags.b.SplitSubPartitionFirstPart, flags.b.SplitSubPartitionSecondPart, flags.b.CodeSubPartition,\ flags.b.UMACachable, flags.b.Reserved - + class BPDT_Entry_Flags(ctypes.LittleEndianStructure): _fields_ = [ ('SplitSubPartitionFirstPart', uint16_t, 1), # 1st part in this LBP, 2nd part in other LBP (for up to 1 non-critical Sub-Partition) @@ -821,17 +821,17 @@ class MN2_Manifest_R0(ctypes.LittleEndianStructure) : # Manifest $MAN/$MN2 Pre-C ("RSASignature", uint32_t*64), # 0x184 2048-bit (PKCS #1 v1.5) # 0x284 ] - + def get_flags(self) : flags = MN2_Manifest_GetFlags() flags.asbytes = self.Flags - + return flags.b.PVBit, flags.b.Reserved, flags.b.PIDBound, flags.b.IntelOwned, flags.b.DebugSigned - + @staticmethod def hdr_print_cse() : pass # Placeholder for test ext_anl calls - + class MN2_Manifest_R1(ctypes.LittleEndianStructure) : # Manifest $MN2 CSE R1 (MANIFEST_HEADER) _pack_ = 1 _fields_ = [ @@ -873,20 +873,20 @@ class MN2_Manifest_R1(ctypes.LittleEndianStructure) : # Manifest $MN2 CSE R1 (MA ('RSASignature', uint32_t*64), # 0x184 2048-bit (PKCS #1 v1.5) # 0x284 ] - + def hdr_print_cse(self) : fvalue = ['No','Yes'] f1,f2,f3,f4,f5 = self.get_flags() - + version = '%d.%d.%d.%d' % (self.Major,self.Minor,self.Hotfix,self.Build) meu_version = '%d.%d.%d.%d' % (self.MEU_Major,self.MEU_Minor,self.MEU_Hotfix,self.MEU_Build) - + PublicKeySize = self.PublicKeySize * 4 RSAPublicKey = '%0.*X' % (PublicKeySize * 2, int.from_bytes(self.RSAPublicKey, 'little')) RSASignature = '%0.*X' % (PublicKeySize * 2, int.from_bytes(self.RSASignature, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Manifest Header' + col_e pt.add_row(['Header Type', '%d' % self.HeaderType]) pt.add_row(['Header Sub Type', '%d' % self.HeaderSubType]) @@ -920,13 +920,13 @@ def hdr_print_cse(self) : pt.add_row(['RSA Public Key', '%s [...]' % RSAPublicKey[:8]]) pt.add_row(['RSA Exponent', '0x%X' % self.RSAExponent]) pt.add_row(['RSA Signature', '%s [...]' % RSASignature[:8]]) - + return pt - + def get_flags(self) : flags = MN2_Manifest_GetFlags() flags.asbytes = self.Flags - + return flags.b.PVBit, flags.b.Reserved, flags.b.PIDBound, flags.b.IntelOwned, flags.b.DebugSigned class MN2_Manifest_R2(ctypes.LittleEndianStructure) : # Manifest $MN2 CSE R2 (MANIFEST_HEADER) @@ -970,20 +970,20 @@ class MN2_Manifest_R2(ctypes.LittleEndianStructure) : # Manifest $MN2 CSE R2 (MA ('RSASignature', uint32_t*96), # 0x204 3072-bit (SSA-PSS) # 0x384 ] - + def hdr_print_cse(self) : fvalue = ['No','Yes'] f1,f2,f3,f4,f5 = self.get_flags() - + version = '%d.%d.%d.%d' % (self.Major,self.Minor,self.Hotfix,self.Build) meu_version = '%d.%d.%d.%d' % (self.MEU_Major,self.MEU_Minor,self.MEU_Hotfix,self.MEU_Build) - + PublicKeySize = self.PublicKeySize * 4 RSAPublicKey = '%0.*X' % (PublicKeySize * 2, int.from_bytes(self.RSAPublicKey, 'little')) RSASignature = '%0.*X' % (PublicKeySize * 2, int.from_bytes(self.RSASignature, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Manifest Header' + col_e pt.add_row(['Header Type', '%d' % self.HeaderType]) pt.add_row(['Header Sub Type', '%d' % self.HeaderSubType]) @@ -1017,15 +1017,15 @@ def hdr_print_cse(self) : pt.add_row(['RSA Public Key', '%s [...]' % RSAPublicKey[:8]]) pt.add_row(['RSA Exponent', '0x%X' % self.RSAExponent]) pt.add_row(['RSA Signature', '%s [...]' % RSASignature[:8]]) - + return pt - + def get_flags(self) : flags = MN2_Manifest_GetFlags() flags.asbytes = self.Flags - + return flags.b.PVBit, flags.b.Reserved, flags.b.PIDBound, flags.b.IntelOwned, flags.b.DebugSigned - + class MN2_Manifest_Flags(ctypes.LittleEndianStructure): _fields_ = [ ('PVBit', uint32_t, 1), # CSE @@ -1034,13 +1034,13 @@ class MN2_Manifest_Flags(ctypes.LittleEndianStructure): ('IntelOwned', uint32_t, 1), # ME 9 - 10 ('DebugSigned', uint32_t, 1) ] - + class MN2_Manifest_GetFlags(ctypes.Union): _fields_ = [ ('b', MN2_Manifest_Flags), ('asbytes', uint32_t) ] - + class SKU_Attributes(ctypes.LittleEndianStructure) : # Pre-CSE $SKU _pack_ = 1 _fields_ = [ @@ -1049,12 +1049,12 @@ class SKU_Attributes(ctypes.LittleEndianStructure) : # Pre-CSE $SKU ('FWSKUAttrib', uint64_t), # 0x08 (uint32_t for ME 2-6 & SPS 1-3) # 0x10 (0xC for ME 2-6 & SPS 1-3) ] - + def hdr_print(self) : f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13 = self.get_flags() - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + '$SKU New' + col_e pt.add_row(['Tag', self.Tag.decode('utf-8')]) pt.add_row(['Size', '0x%X' % (self.Size * 4)]) @@ -1071,13 +1071,13 @@ def hdr_print(self) : pt.add_row(['SKU Type', ['Corporate','Consumer','Slim'][f11]]) pt.add_row(['SKU Size', '%0.1f MB' % (f12 * 0.5)]) pt.add_row(['Value 10', '0x%X' % f13]) - + return pt - + def get_flags(self) : flags = SKU_Attributes_GetFlags() flags.asbytes = self.FWSKUAttrib - + return flags.b.Value1, flags.b.Value2, flags.b.Value3, flags.b.Value4, flags.b.Value5, flags.b.Value6, \ flags.b.Value7, flags.b.Value8, flags.b.Value9, flags.b.Patsburg, flags.b.SKUType, flags.b.SKUSize, flags.b.Value10 @@ -1097,7 +1097,7 @@ class SKU_Attributes_Flags(ctypes.BigEndianStructure): ('SKUSize', uint64_t, 4), # Size * 0.5MB (ME 7-10, TXE 0-2) ('Value10', uint64_t, 24) ] - + class SKU_Attributes_GetFlags(ctypes.Union): _fields_ = [ ('b', SKU_Attributes_Flags), @@ -1170,10 +1170,10 @@ class CPD_Header_R1(ctypes.LittleEndianStructure) : # Code Partition Directory R ('PartitionName', char*4), # 0x0C # 0x10 ] - + def hdr_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Code Partition Directory Header' + col_e pt.add_row(['Tag', self.Tag.decode('utf-8')]) pt.add_row(['Module Count', '%d' % self.NumModules]) @@ -1182,7 +1182,7 @@ def hdr_print(self) : pt.add_row(['Header Size', '0x%X' % self.HeaderLength]) pt.add_row(['Checksum', '0x%X' % self.Checksum]) pt.add_row(['Partition Name', self.PartitionName.decode('utf-8')]) - + return pt class CPD_Header_R2(ctypes.LittleEndianStructure) : # Code Partition Directory R2 (CPD_HEADER) @@ -1198,10 +1198,10 @@ class CPD_Header_R2(ctypes.LittleEndianStructure) : # Code Partition Directory R ('Checksum', uint32_t), # 0x10 CRC-32 of Header + Entries with Checksum field = 0 # 0x14 ] - + def hdr_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Code Partition Directory Header' + col_e pt.add_row(['Tag', self.Tag.decode('utf-8')]) pt.add_row(['Module Count', '%d' % self.NumModules]) @@ -1211,9 +1211,9 @@ def hdr_print(self) : pt.add_row(['Reserved', '0x%X' % self.Reserved]) pt.add_row(['Partition Name', self.PartitionName.decode('utf-8')]) pt.add_row(['Checksum', '0x%X' % self.Checksum]) - + return pt - + class CPD_Entry(ctypes.LittleEndianStructure) : # (CPD_ENTRY) _pack_ = 1 _fields_ = [ @@ -1223,12 +1223,12 @@ class CPD_Entry(ctypes.LittleEndianStructure) : # (CPD_ENTRY) ("Reserved", uint32_t), # 0x14 # 0x18 ] - + def hdr_print(self) : f1,f2,f3 = self.get_flags() - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Code Partition Directory Entry' + col_e pt.add_row(['Name', self.Name.decode('utf-8')]) pt.add_row(['Offset from $CPD', '0x%X' % f1]) @@ -1236,13 +1236,13 @@ def hdr_print(self) : pt.add_row(['Offset Reserved', '0x%X' % f3]) pt.add_row(['Size Uncompressed', '0x%X' % self.Size]) pt.add_row(['Reserved', '0x%X' % self.Reserved]) - + return pt - + def get_flags(self) : flags = CPD_Entry_GetOffsetAttrib() flags.asbytes = self.OffsetAttrib - + return flags.b.OffsetCPD, flags.b.IsHuffman, flags.b.Reserved class CPD_Entry_OffsetAttrib(ctypes.LittleEndianStructure): @@ -1251,7 +1251,7 @@ class CPD_Entry_OffsetAttrib(ctypes.LittleEndianStructure): ('IsHuffman', uint32_t, 1), ('Reserved', uint32_t, 6) ] - + class CPD_Entry_GetOffsetAttrib(ctypes.Union): _fields_ = [ ('b', CPD_Entry_OffsetAttrib), @@ -1270,10 +1270,10 @@ class MFS_Page_Header(ctypes.LittleEndianStructure) : # MFS Page Header ('Reserved', uint8_t), # 0x11 # 0x12 ] - + def mfs_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'MFS Page Header' + col_e pt.add_row(['Signature', '%0.8X' % self.Signature]) pt.add_row(['Page Number', '%d' % self.PageNumber]) @@ -1282,9 +1282,9 @@ def mfs_print(self) : pt.add_row(['First Chunk Index', '%d' % self.FirstChunkIndex]) pt.add_row(['CRC-8', '0x%0.2X' % self.CRC8]) pt.add_row(['Reserved', '0x%X' % self.Reserved]) - + return pt - + class MFS_Volume_Header(ctypes.LittleEndianStructure) : # MFS Volume Header _pack_ = 1 _fields_ = [ @@ -1296,13 +1296,13 @@ class MFS_Volume_Header(ctypes.LittleEndianStructure) : # MFS Volume Header ('FileRecordCount', uint16_t), # 0x0C Supported by FAT # 0x0E ] - + def mfs_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + is_ftbl = not (self.FTBLDictionary,self.FTBLPlatform,self.FTBLReserved) == (1,0,0) plat_name = ftbl_efst_plat[self.FTBLPlatform] if self.FTBLPlatform in ftbl_efst_plat else 'Unknown' - + pt.title = col_y + 'MFS Volume Header' + col_e pt.add_row(['Signature', '%0.8X' % self.Signature]) if is_ftbl : @@ -1313,9 +1313,9 @@ def mfs_print(self) : pt.add_row(['Revision', '%d' % self.FTBLDictionary]) pt.add_row(['Volume Length', '0x%X' % self.VolumeSize]) pt.add_row(['File Record Count', '%d' % self.FileRecordCount]) - + return pt - + class MFS_Config_Record_0x1C(ctypes.LittleEndianStructure) : # MFS Configuration Record 0x1C _pack_ = 1 _fields_ = [ @@ -1329,13 +1329,13 @@ class MFS_Config_Record_0x1C(ctypes.LittleEndianStructure) : # MFS Configuration ('FileOffset', uint32_t), # 0x18 # 0x1C ] - + def mfs_print(self) : fvalue = ['No','Yes'] f1,f2,f3,f4,f5,f6,f7,f8,f9 = self.get_flags() - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'MFS Configuration Record' + col_e pt.add_row(['Name', self.FileName.decode('utf-8')]) pt.add_row(['Type', ['File','Folder'][f5]]) @@ -1352,24 +1352,24 @@ def mfs_print(self) : pt.add_row(['Access Mode Unknown', '{0:03b}b'.format(f6)]) pt.add_row(['Deploy Options Unknown', '{0:014b}b'.format(f9)]) pt.add_row(['Reserved', '0x%X' % self.Reserved]) - + return pt - + @staticmethod def get_rights(f1) : bits = format(f1, '09b') for i in range(len(bits)) : yield 'rwxrwxrwx'[i] if bits[i] == '1' else '-' - + def get_flags(self) : a_flags = MFS_Config_Record_GetAccess() a_flags.asbytes = self.AccessMode o_flags = MFS_Config_Record_GetOptions() o_flags.asbytes = self.DeployOptions - + return a_flags.b.UnixRights, a_flags.b.Integrity, a_flags.b.Encryption, a_flags.b.AntiReplay, a_flags.b.RecordType,\ a_flags.b.Unknown, o_flags.b.OEMConfigurable, o_flags.b.MCAConfigurable, o_flags.b.Unknown - + class MFS_Config_Record_Access(ctypes.LittleEndianStructure): _fields_ = [ ('UnixRights', uint16_t, 9), @@ -1379,26 +1379,26 @@ class MFS_Config_Record_Access(ctypes.LittleEndianStructure): ('RecordType', uint16_t, 1), # 0 File, 1 Folder ('Unknown', uint16_t, 3) ] - + class MFS_Config_Record_GetAccess(ctypes.Union): _fields_ = [ ('b', MFS_Config_Record_Access), ('asbytes', uint16_t) ] - + class MFS_Config_Record_Options(ctypes.LittleEndianStructure): _fields_ = [ ('OEMConfigurable', uint16_t, 1), # OEM fitc.cfg setting can overwrite Intel intl.cfg equivalent setting via Flash Image Tool ('MCAConfigurable', uint16_t, 1), # Manufacturing Configuration Architecture module can configure MFS CVARs in Manufacturing Mode ('Unknown', uint16_t, 14) ] - + class MFS_Config_Record_GetOptions(ctypes.Union): _fields_ = [ ('b', MFS_Config_Record_Options), ('asbytes', uint16_t) ] - + class MFS_Config_Record_0xC(ctypes.LittleEndianStructure) : # MFS Configuration Record 0xC _pack_ = 1 _fields_ = [ @@ -1408,40 +1408,40 @@ class MFS_Config_Record_0xC(ctypes.LittleEndianStructure) : # MFS Configuration ('Flags', uint16_t), # 0x0A # 0x0C ] - + def mfs_print(self) : fvalue = ['No','Yes'] f1,f2 = self.get_flags() - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'MFS Configuration Record' + col_e pt.add_row(['File ID', '0x%0.8X' % self.FileID]) #pt.add_row(['Offset', '0x%X' % self.FileOffset]) pt.add_row(['Size', '0x%X' % self.FileSize]) pt.add_row(['OEM Configurable', fvalue[f1]]) pt.add_row(['Reserved Flags', '{0:015b}b'.format(f2)]) - + return pt - + def get_flags(self) : flags = MFS_Config_Record_0xC_GetFlags() flags.asbytes = self.Flags - + return flags.b.OEMConfigurable, flags.b.Unknown - + class MFS_Config_Record_0xC_Flags(ctypes.LittleEndianStructure): _fields_ = [ ('OEMConfigurable', uint16_t, 1), # OEM fitc.cfg setting can overwrite Intel intl.cfg equivalent setting via Flash Image Tool ('Unknown', uint16_t, 15) ] - + class MFS_Config_Record_0xC_GetFlags(ctypes.Union): _fields_ = [ ('b', MFS_Config_Record_0xC_Flags), ('asbytes', uint16_t) ] - + class MFS_Home_Record_0x18(ctypes.LittleEndianStructure) : # MFS Home Directory Record 0x18 _pack_ = 1 _fields_ = [ @@ -1453,15 +1453,15 @@ class MFS_Home_Record_0x18(ctypes.LittleEndianStructure) : # MFS Home Directory ('FileName', char*12), # 0x0C # 0x18 ] - + # Remember to also adjust MFS_Home_Record_0x1C for common fields - + def mfs_print(self) : fvalue = ['No','Yes'] f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11 = self.get_flags() - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'MFS Home Record' + col_e pt.add_row(['Index', '%d' % f1]) pt.add_row(['Name', self.FileName.decode('utf-8')]) @@ -1478,24 +1478,24 @@ def mfs_print(self) : pt.add_row(['Access Mode Unknown 1', '{0:01b}b'.format(f11)]) pt.add_row(['Integrity Salt', '0x%0.4X' % f2]) pt.add_row(['Unknown Salt', '0x%X' % self.UnknownSalt]) - + return pt - + @staticmethod def get_rights(f4) : bits = format(f4, '09b') for i in range(len(bits)) : yield 'rwxrwxrwx'[i] if bits[i] == '1' else '-' - + def get_flags(self) : f_flags = MFS_Home_Record_GetFileInfo() f_flags.asbytes = self.FileInfo a_flags = MFS_Home_Record_GetAccess() a_flags.asbytes = self.AccessMode - + return f_flags.b.FileIndex, f_flags.b.IntegritySalt, f_flags.b.FileSystemID, a_flags.b.UnixRights, a_flags.b.Integrity, \ a_flags.b.Encryption, a_flags.b.AntiReplay, a_flags.b.Unknown0, a_flags.b.KeyType, a_flags.b.RecordType, a_flags.b.Unknown1 - + class MFS_Home_Record_0x1C(ctypes.LittleEndianStructure) : # MFS Home Directory Record 0x1C _pack_ = 1 _fields_ = [ @@ -1507,17 +1507,17 @@ class MFS_Home_Record_0x1C(ctypes.LittleEndianStructure) : # MFS Home Directory ('FileName', char*12), # 0x10 # 0x1C ] - + # Remember to also adjust MFS_Home_Record_0x18 for common fields - + def mfs_print(self) : fvalue = ['No','Yes'] f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11 = self.get_flags() - + UnknownSalt = '0x%0.*X' % (0x6 * 2, int.from_bytes(self.UnknownSalt, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'MFS Home Record' + col_e pt.add_row(['Index', '%d' % f1]) pt.add_row(['Name', self.FileName.decode('utf-8')]) @@ -1534,21 +1534,21 @@ def mfs_print(self) : pt.add_row(['Access Mode Unknown 1', '{0:01b}b'.format(f11)]) pt.add_row(['Integrity Salt', '0x%0.4X' % f2]) pt.add_row(['Unknown Salt', UnknownSalt]) - + return pt - + @staticmethod def get_rights(f4) : bits = format(f4, '09b') for i in range(len(bits)) : yield 'rwxrwxrwx'[i] if bits[i] == '1' else '-' - + def get_flags(self) : f_flags = MFS_Home_Record_GetFileInfo() f_flags.asbytes = self.FileInfo a_flags = MFS_Home_Record_GetAccess() a_flags.asbytes = self.AccessMode - + return f_flags.b.FileIndex, f_flags.b.IntegritySalt, f_flags.b.FileSystemID, a_flags.b.UnixRights, a_flags.b.Integrity, \ a_flags.b.Encryption, a_flags.b.AntiReplay, a_flags.b.Unknown0, a_flags.b.KeyType, a_flags.b.RecordType, a_flags.b.Unknown1 @@ -1558,13 +1558,13 @@ class MFS_Home_Record_FileInfo(ctypes.LittleEndianStructure): ('IntegritySalt', uint32_t, 16), # For MFS_Integrity_Table.HMAC ('FileSystemID', uint32_t, 4) # 0 root, 1 home, 2 bin, 3 susram, 4 fpf, 5 dev, 6 umafs ] - + class MFS_Home_Record_GetFileInfo(ctypes.Union): _fields_ = [ ('b', MFS_Home_Record_FileInfo), ('asbytes', uint32_t) ] - + class MFS_Home_Record_Access(ctypes.LittleEndianStructure): _fields_ = [ ('UnixRights', uint16_t, 9), @@ -1576,7 +1576,7 @@ class MFS_Home_Record_Access(ctypes.LittleEndianStructure): ('RecordType', uint16_t, 1), # 0 File, 1 Folder ('Unknown1', uint16_t, 1) ] - + class MFS_Home_Record_GetAccess(ctypes.Union): _fields_ = [ ('b', MFS_Home_Record_Access), @@ -1591,21 +1591,21 @@ class MFS_Integrity_Table_0x34(ctypes.LittleEndianStructure) : # MFS Integrity T ('ARValues_Nonce', uint32_t*4), # 0x2C Anti-Replay Random Value (32-bit) + Counter Value (32-bit) or AES-CTR Nonce (128-bit) # 0x34 ] - + # HMAC = File Contents + MFS_Integrity_Table with HMACSHA256 = 0, MFS_Home_Record.FileInfo.FileIndex + MFS_Home_Record.FileInfo.IntegritySalt (32-bit). # For MFS Low Level Files without MFS_Home_Record (2 Anti-Replay, 3 Anti-Replay, 8 Home): FileIndex = 0x10000000 + 2|3|8 and IntegritySalt = 0. # The MFS_Integrity_Table HMAC SHA-256 Integrity value cannot be verified by 3rd-party entities without Intel's Secret Key within the CSE. - + def mfs_print(self) : fvalue = ['No','Yes'] f1,f2,f3,f4,f5,f6,f7,f8 = self.get_flags() - + HMACSHA256 = '%0.*X' % (0x20 * 2, int.from_bytes(self.HMACSHA256, 'little')) ARValues_Nonce = '%0.*X' % (0x10 * 2, int.from_bytes(self.ARValues_Nonce, 'little')) ARRandom, ARCounter = struct.unpack_from('= 11.6.0.1109, 1 CSSPS, 3 CSME) # 0x58 ] - + # Used at $FPT size calculation as well, remember to change in case of new Extension Revision! - + # PartitionSize & Hash are valid for RGN firmware only with stock $CPD & Data, no FIT/OEM configurations. The latter, usually oem.key and fitc.cfg, # are added at the end of the PartitionSize so FIT adjusts $CPD and appends customization files accordingly. Thus, PartitionSize and Hash fields # must not be verified at FIT/OEM-customized images because they're not applicable anymore. - + def ext_print(self) : fvalue = ['No','Yes'] f1,f2,f3,f4,f5,f6,f7,f8,f9,f10 = self.get_flags() - + Hash = '%0.*X' % (0x20 * 2, int.from_bytes(self.Hash, 'little')) Reserved = '%0.*X' % (0x10 * 2, int.from_bytes(self.Reserved, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 3, Partition Information' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -2444,17 +2444,17 @@ def ext_print(self) : pt.add_row(['Flags Reserved', '0x%X' % f10]) pt.add_row(['Reserved', '0x' + 'FF * 16' if Reserved == 'FF' * 16 else Reserved]) pt.add_row(['Unknown', '0x%X' % self.Unknown]) - + return pt - + def get_flags(self) : flags = CSE_Ext_03_GetFlags() flags.asbytes = self.Flags - + return flags.b.SupportMultipleInstances, flags.b.SupportApiVersionBasedUpdate, flags.b.ActionOnUpdate, \ flags.b.ObeyFullUpdateRules, flags.b.IfrEnableOnly, flags.b.AllowCrossPointUpdate, flags.b.AllowCrossHotfixUpdate, \ flags.b.PartialUpdateOnly, flags.b.NotMeasured, flags.b.Reserved - + class CSE_Ext_03_R2(ctypes.LittleEndianStructure) : # R2 - Partition Information (MANIFEST_PARTITION_INFO_EXT) _pack_ = 1 _fields_ = [ @@ -2474,22 +2474,22 @@ class CSE_Ext_03_R2(ctypes.LittleEndianStructure) : # R2 - Partition Information ('Unknown', uint32_t), # 0x64 Unknown (>= 11.6.0.1109, 1 CSSPS, 3 CSME) # 0x68 ] - + # Used at $FPT size calculation as well, remember to change in case of new Extension Revision! - + # PartitionSize & Hash are valid for RGN firmware only with stock $CPD & Data, no FIT/OEM configurations. The latter, usually oem.key and fitc.cfg, # are added at the end of the PartitionSize so FIT adjusts $CPD and appends customization files accordingly. Thus, PartitionSize and Hash fields # must not be verified at FIT/OEM-customized images because they're not applicable anymore. - + def ext_print(self) : fvalue = ['No','Yes'] f1,f2,f3,f4,f5,f6,f7,f8,f9,f10 = self.get_flags() - + Hash = '%0.*X' % (0x30 * 2, int.from_bytes(self.Hash, 'little')) Reserved = '%0.*X' % (0x10 * 2, int.from_bytes(self.Reserved, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 3, Partition Information' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -2512,17 +2512,17 @@ def ext_print(self) : pt.add_row(['Flags Reserved', '0x%X' % f10]) pt.add_row(['Reserved', '0x' + 'FF * 16' if Reserved == 'FF' * 16 else Reserved]) pt.add_row(['Unknown', '0x%X' % self.Unknown]) - + return pt - + def get_flags(self) : flags = CSE_Ext_03_GetFlags() flags.asbytes = self.Flags - + return flags.b.SupportMultipleInstances, flags.b.SupportApiVersionBasedUpdate, flags.b.ActionOnUpdate, \ flags.b.ObeyFullUpdateRules, flags.b.IfrEnableOnly, flags.b.AllowCrossPointUpdate, flags.b.AllowCrossHotfixUpdate, \ flags.b.PartialUpdateOnly, flags.b.NotMeasured, flags.b.Reserved - + class CSE_Ext_03_Flags(ctypes.LittleEndianStructure): _fields_ = [ ('SupportMultipleInstances', uint32_t, 1), # For independently updated WCOD/LOCL partitions with multiple instances @@ -2554,13 +2554,13 @@ class CSE_Ext_03_Mod(ctypes.LittleEndianStructure) : # R1 - Module Information ( ("MetadataHash", uint32_t*8), # 0x14 # 0x34 ] - + def ext_print(self) : Type = cse_mod_type[self.Type] if self.Type in cse_mod_type else 'Unknown (%d)' % self.Type MetadataHash = '%0.*X' % (0x20 * 2, int.from_bytes(self.MetadataHash, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 3, Module Information' + col_e pt.add_row(['Name', self.Name.decode('utf-8')]) pt.add_row(['Type', Type]) @@ -2568,7 +2568,7 @@ def ext_print(self) : pt.add_row(['Reserved', '0x%X' % self.Reserved]) pt.add_row(['Metadata Size', '0x%X' % self.MetadataSize]) pt.add_row(['Metadata Hash', MetadataHash]) - + return pt class CSE_Ext_04(ctypes.LittleEndianStructure) : # R1 - Shared Library Attributes (SHARED_LIB_EXTENSION) @@ -2583,10 +2583,10 @@ class CSE_Ext_04(ctypes.LittleEndianStructure) : # R1 - Shared Library Attribute ("Reserved", uint32_t), # 0x18 # 0x1C ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 4, Shared Library Attributes' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -2595,7 +2595,7 @@ def ext_print(self) : pt.add_row(['Code Base Address', '0x%X' % self.CodeBaseAddress]) pt.add_row(['TLS Size', '0x%X' % self.TLSSize]) pt.add_row(['Reserved', '0x0' if self.Reserved == 0 else '0x%X' % self.Reserved]) - + return pt class CSE_Ext_05(ctypes.LittleEndianStructure) : # R1 - Process Attributes (MAN_PROCESS_EXTENSION) @@ -2618,16 +2618,16 @@ class CSE_Ext_05(ctypes.LittleEndianStructure) : # R1 - Process Attributes (MAN_ ("Reserved2", uint64_t), # 0x3C # 0x44 ] - + def ext_print(self) : fvalue = ['No','Yes'] f1value = ['Reset System','Terminate Process'] f1,f2,f3,f4,f5,f6,f7,f8 = self.get_flags() - + AllowedSysCalls = '%0.*X' % (0xC * 2, int.from_bytes(self.AllowedSysCalls, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 5, Process Attributes' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -2651,13 +2651,13 @@ def ext_print(self) : pt.add_row(['Reserved 0', '0x%X' % self.Reserved0]) pt.add_row(['Reserved 1', '0x%X' % self.Reserved1]) pt.add_row(['Reserved 2', '0x%X' % self.Reserved2]) - + return pt - + def get_flags(self) : flags = CSE_Ext_05_GetFlags() flags.asbytes = self.Flags - + return flags.b.FaultTolerant, flags.b.PermanentProcess, flags.b.SingleInstance, flags.b.TrustedSendReceiveSender,\ flags.b.TrustedNotifySender, flags.b.PublicSendReceiveReceiver, flags.b.PublicNotifyReceiver, flags.b.Reserved @@ -2667,13 +2667,13 @@ class CSE_Ext_05_Mod(ctypes.LittleEndianStructure) : # R1 - Group ID (PROCESS_GR ('GroupID', uint16_t), # 0x00 # 0x02 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 5, Group ID' + col_e pt.add_row(['Data', '0x%0.4X' % self.GroupID]) - + return pt class CSE_Ext_05_Flags(ctypes.LittleEndianStructure): @@ -2701,14 +2701,14 @@ class CSE_Ext_06(ctypes.LittleEndianStructure) : # R1 - Thread Attributes (Threa ("Size", uint32_t), # 0x04 # 0x08 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 6, Thread Attributes' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) - + return pt class CSE_Ext_06_Mod(ctypes.LittleEndianStructure) : # R1 - (Thread) @@ -2720,14 +2720,14 @@ class CSE_Ext_06_Mod(ctypes.LittleEndianStructure) : # R1 - (Thread) ("Reserved", uint32_t), # 0x0C # 0x10 ] - + def ext_print(self) : f1value = ['Live','CM0 UMA Only'] fvalue = ['No','Yes'] f1,f2,f3,f4,f5 = self.get_flags() - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 6, Thread' + col_e pt.add_row(['Stack Size', '0x%X' % self.StackSize]) pt.add_row(['Flags Type', f1value[f1]]) @@ -2736,18 +2736,18 @@ def ext_print(self) : pt.add_row(['Scheduling Policy Reserved', '0x%X' % f4]) pt.add_row(['Scheduling Attributes/Priority', '0x%X' % f5]) pt.add_row(['Reserved', '0x%X' % self.Reserved]) - + return pt - + def get_flags(self) : f_flags = CSE_Ext_06_GetFlags() s_flags = CSE_Ext_06_GetSchedulPolicy() f_flags.asbytes = self.Flags s_flags.asbytes = self.SchedulPolicy - + return f_flags.b.FlagsType, f_flags.b.FlagsReserved, s_flags.b.PolicyFixedPriority, s_flags.b.PolicyReserved,\ s_flags.b.AttributesORPriority - + class CSE_Ext_06_Flags(ctypes.LittleEndianStructure): _fields_ = [ ('FlagsType', uint32_t, 1), @@ -2759,14 +2759,14 @@ class CSE_Ext_06_GetFlags(ctypes.Union): ('b', CSE_Ext_06_Flags), ('asbytes', uint32_t) ] - + class CSE_Ext_06_SchedulPolicy(ctypes.LittleEndianStructure): _fields_ = [ ('PolicyFixedPriority', uint32_t, 1), ('PolicyReserved', uint32_t, 6), ('AttributesORPriority', uint32_t, 25) ] - + class CSE_Ext_06_GetSchedulPolicy(ctypes.Union): _fields_ = [ ('b', CSE_Ext_06_SchedulPolicy), @@ -2780,14 +2780,14 @@ class CSE_Ext_07(ctypes.LittleEndianStructure) : # R1 - Device Types (DeviceIds) ("Size", uint32_t), # 0x04 # 0x08 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 7, Devices' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) - + return pt class CSE_Ext_07_Mod(ctypes.LittleEndianStructure) : # R1 - (Device) @@ -2797,29 +2797,29 @@ class CSE_Ext_07_Mod(ctypes.LittleEndianStructure) : # R1 - (Device) ("Reserved", uint32_t), # 0x04 # 0x08 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 7, Device' + col_e pt.add_row(['Device ID', '0x%0.8X' % self.DeviceID]) pt.add_row(['Reserved', '0x%X' % self.Reserved]) - + return pt - + class CSE_Ext_07_Mod_R2(ctypes.LittleEndianStructure) : # R2 - (Device) _pack_ = 1 _fields_ = [ ("DeviceID", uint32_t), # 0x00 # 0x04 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 7, Device' + col_e pt.add_row(['Device ID', '0x%0.8X' % self.DeviceID]) - + return pt class CSE_Ext_08(ctypes.LittleEndianStructure) : # R1 - MMIO Ranges (MmioRanges) @@ -2829,14 +2829,14 @@ class CSE_Ext_08(ctypes.LittleEndianStructure) : # R1 - MMIO Ranges (MmioRanges) ("Size", uint32_t), # 0x04 # 0x08 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 8, MMIO Ranges' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) - + return pt class CSE_Ext_08_Mod(ctypes.LittleEndianStructure) : # R1 - (MmioRange) @@ -2847,15 +2847,15 @@ class CSE_Ext_08_Mod(ctypes.LittleEndianStructure) : # R1 - (MmioRange) ("Flags", uint32_t), # 0x08 (MmioAccess) # 0x0C ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 8, MMIO Range' + col_e pt.add_row(['Base Address', '0x%X' % self.BaseAddress]) pt.add_row(['Size Limit', '0x%X' % self.SizeLimit]) pt.add_row(['Access', '%s' % ['N/A','Read Only','Write Only','Read & Write'][self.Flags]]) - + return pt class CSE_Ext_09(ctypes.LittleEndianStructure) : # R1 - Special File Producer (SPECIAL_FILE_PRODUCER_EXTENSION) @@ -2867,16 +2867,16 @@ class CSE_Ext_09(ctypes.LittleEndianStructure) : # R1 - Special File Producer (S ("Flags", uint16_t), # 0x0A (Unknown/Unused) # 0x0C ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 9, Special File Producer' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) pt.add_row(['Major Number', '%d' % self.MajorNumber]) pt.add_row(['Flags', '0x%X' % self.Flags]) - + return pt class CSE_Ext_09_Mod(ctypes.LittleEndianStructure) : # R1 - (SPECIAL_FILE_DEF) @@ -2891,10 +2891,10 @@ class CSE_Ext_09_Mod(ctypes.LittleEndianStructure) : # R1 - (SPECIAL_FILE_DEF) ("Reserved1", uint32_t), # 0x14 # 0x18 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 9, Special File Definition' + col_e pt.add_row(['Name', self.Name.decode('utf-8')]) pt.add_row(['Access Mode', '0x%X' % self.AccessMode]) @@ -2903,7 +2903,7 @@ def ext_print(self) : pt.add_row(['Minor Number', '%d' % self.MinorNumber]) pt.add_row(['Reserved 0', '0x%X' % self.Reserved0]) pt.add_row(['Reserved 1', '0x%X' % self.Reserved1]) - + return pt class CSE_Ext_0A(ctypes.LittleEndianStructure) : # R1 - Module Attributes (MOD_ATTR_EXTENSION) @@ -2922,12 +2922,12 @@ class CSE_Ext_0A(ctypes.LittleEndianStructure) : # R1 - Module Attributes (MOD_A ("Hash", uint32_t*8), # 0x18 SHA-256 (Compressed for LZMA, Uncompressed for Huffman) # 0x38 ] - + def ext_print(self) : Hash = '%0.*X' % (0x20 * 2, int.from_bytes(self.Hash, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 10, Module Attributes' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -2940,9 +2940,9 @@ def ext_print(self) : pt.add_row(['Device ID', '0x%0.4X' % self.DEV_ID]) pt.add_row(['Vendor ID', '0x%0.4X' % self.VEN_ID]) pt.add_row(['Hash', Hash]) - + return pt - + class CSE_Ext_0A_R2(ctypes.LittleEndianStructure) : # R2 - Module Attributes (MOD_ATTR_EXTENSION) _pack_ = 1 _fields_ = [ @@ -2959,12 +2959,12 @@ class CSE_Ext_0A_R2(ctypes.LittleEndianStructure) : # R2 - Module Attributes (MO ('Hash', uint32_t*12), # 0x18 SHA-384 (Compressed for LZMA, Uncompressed for Huffman) # 0x48 ] - + def ext_print(self) : Hash = '%0.*X' % (0x30 * 2, int.from_bytes(self.Hash, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 10, Module Attributes' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -2977,7 +2977,7 @@ def ext_print(self) : pt.add_row(['Device ID', '0x%0.4X' % self.DEV_ID]) pt.add_row(['Vendor ID', '0x%0.4X' % self.VEN_ID]) pt.add_row(['Hash', Hash]) - + return pt class CSE_Ext_0B(ctypes.LittleEndianStructure) : # R1 - Locked Ranges (LockedRanges) @@ -2987,14 +2987,14 @@ class CSE_Ext_0B(ctypes.LittleEndianStructure) : # R1 - Locked Ranges (LockedRan ("Size", uint32_t), # 0x04 # 0x08 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 11, Locked Ranges' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) - + return pt class CSE_Ext_0B_Mod(ctypes.LittleEndianStructure) : # R1 - (LockedRange) @@ -3004,14 +3004,14 @@ class CSE_Ext_0B_Mod(ctypes.LittleEndianStructure) : # R1 - (LockedRange) ("RangeSize", uint32_t), # 0x04 # 0x08 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 11, Locked Range' + col_e pt.add_row(['Range Base', '0x%X' % self.RangeBase]) pt.add_row(['Range Size', '0x%X' % self.RangeSize]) - + return pt class CSE_Ext_0C(ctypes.LittleEndianStructure) : # R1 - Client System Information (CLIENT_SYSTEM_INFO_EXTENSION) @@ -3024,7 +3024,7 @@ class CSE_Ext_0C(ctypes.LittleEndianStructure) : # R1 - Client System Informatio ("FWSKUAttrib", uint64_t), # 0x28 # 0x30 ] - + def __init__(self, variant, major, minor, hotfix, build, year, month, variant_p, *args, **kwargs) : super().__init__(*args, **kwargs) self.variant = variant @@ -3035,15 +3035,15 @@ def __init__(self, variant, major, minor, hotfix, build, year, month, variant_p, self.year = year self.month = month self.variant_p = variant_p - + def ext_print(self) : fvalue = ['No','Yes'] f1,f2,f3,f4,f5,f6,f7,f8 = self.get_flags() sku_capabilities = self.get_skuc() sku_capabilities_pt = self.skuc_pt(sku_capabilities) - + FWSKUCapsRes = '%0.*X' % (0x1C * 2, int.from_bytes(self.FWSKUCapsRes, 'little')) - + if (self.variant,self.major) == ('CSME',11) and (self.minor > 0 or self.hotfix > 0 or (self.hotfix == 0 and self.build >= 1205 and self.build != 7101)) \ or (self.variant,self.major,self.minor,self.hotfix) == ('CSME',12,0,0) and self.build >= 7000 and self.year < 0x2018 and self.month < 0x8 : sku_value = ['H','LP','Reserved','Reserved'][f6] @@ -3051,9 +3051,9 @@ def ext_print(self) : else : sku_value = '0x%X' % f6 sku_field = 'Reserved' - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 12, Client System Information' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -3067,37 +3067,37 @@ def ext_print(self) : pt.add_row([sku_field, sku_value]) pt.add_row(['Si Class', '%d' % f7]) pt.add_row(['Reserved', '0x0' if f8 == 0 else '0x%X' % f8]) - + return pt - + def get_skuc(self) : sku_capabilities = [] - + skuc_bits = list(format(self.FWSKUCaps, '032b')) skuc_bits.reverse() - + for sku_bit in range(len(skuc_bits)) : if skuc_bits[sku_bit] == '1' : sku_capabilities.append(skuc_dict[sku_bit] if sku_bit in skuc_dict else 'Unknown (%d)' % sku_bit) - + return sku_capabilities - + @staticmethod def skuc_pt(sku_capabilities) : skuc_print = '' - + for cap_idx in range(len(sku_capabilities)) : skuc_print += ('%s, \n' if cap_idx > 0 and cap_idx % 10 == 0 else '%s, ') % sku_capabilities[cap_idx] - + return skuc_print.strip(', ').strip(', \n') # Strip last comma - + def get_flags(self) : flags = CSE_Ext_0C_GetFWSKUAttrib() flags.asbytes = self.FWSKUAttrib - + return flags.b.CSESize, flags.b.SKUType, flags.b.Workstation, flags.b.M3, flags.b.M0,\ flags.b.SKUPlatform, flags.b.SiClass, flags.b.Reserved - + class CSE_Ext_0C_FWSKUAttrib(ctypes.LittleEndianStructure): _fields_ = [ ('CSESize', uint64_t, 4), # CSESize * 0.5MB, always 0 @@ -3123,14 +3123,14 @@ class CSE_Ext_0D(ctypes.LittleEndianStructure) : # R1 - User Information (USER_I ("Size", uint32_t), # 0x04 # 0x08 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 13, User Information' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) - + return pt class CSE_Ext_0D_Mod(ctypes.LittleEndianStructure) : # R1 - (USER_INFO_ENTRY) @@ -3144,10 +3144,10 @@ class CSE_Ext_0D_Mod(ctypes.LittleEndianStructure) : # R1 - (USER_INFO_ENTRY) ("WorkingDir", char*36), # 0x10 # 0x34 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 13, Entry' + col_e pt.add_row(['User ID', '0x%0.4X' % self.UserID]) pt.add_row(['Reserved', '0x%X' % self.Reserved]) @@ -3155,7 +3155,7 @@ def ext_print(self) : pt.add_row(['RAM Storage Quota', '0x%X' % self.RAMStorageQuota]) pt.add_row(['WOP Quota', '0x%X' % self.WOPQuota]) pt.add_row(['Working Directory', self.WorkingDir.decode('utf-8')]) - + return pt class CSE_Ext_0D_Mod_R2(ctypes.LittleEndianStructure) : # R2 - (not in XML, Reverse Engineered) @@ -3168,17 +3168,17 @@ class CSE_Ext_0D_Mod_R2(ctypes.LittleEndianStructure) : # R2 - (not in XML, Reve ("WOPQuota", uint32_t), # 0x0C (Wear-out Prevention) # 0x10 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 13, Entry' + col_e pt.add_row(['User ID', '0x%0.4X' % self.UserID]) pt.add_row(['Reserved', '0x%X' % self.Reserved]) pt.add_row(['NV Storage Quota', '0x%X' % self.NVStorageQuota]) pt.add_row(['RAM Storage Quota', '0x%X' % self.RAMStorageQuota]) pt.add_row(['WOP Quota', '0x%X' % self.WOPQuota]) - + return pt class CSE_Ext_0E(ctypes.LittleEndianStructure) : # R1 - Key Manifest (KEY_MANIFEST_EXT) @@ -3194,10 +3194,10 @@ class CSE_Ext_0E(ctypes.LittleEndianStructure) : # R1 - Key Manifest (KEY_MANIFE ("Reserved1", uint32_t*4), # 0x14 # 0x24 ] - - def ext_print(self) : + + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 14, Key Manifest' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -3207,7 +3207,7 @@ def ext_print(self) : pt.add_row(['Key ID', '0x%0.2X' % self.KeyID]) pt.add_row(['Reserved 0', '0x%X' % self.Reserved0]) pt.add_row(['Reserved 1', '0x%X' % int.from_bytes(self.Reserved1, 'little')]) - + return pt class CSE_Ext_0E_Mod(ctypes.LittleEndianStructure) : # R1 - (KEY_MANIFEST_EXT_ENTRY) @@ -3221,16 +3221,16 @@ class CSE_Ext_0E_Mod(ctypes.LittleEndianStructure) : # R1 - (KEY_MANIFEST_EXT_EN ("Hash", uint32_t*8), # 0x24 SHA-256 (Big Endian, PKEY + EXP) # 0x44 ] - + def ext_print(self) : f1,f2 = self.get_flags() hash_usages = get_key_usages(self.UsageBitmap) - + Hash = '%0.*X' % (0x20 * 2, int.from_bytes(self.Hash, 'big')) HashAlgorithm = cse_hash_alg[self.HashAlgorithm] if self.HashAlgorithm in cse_hash_alg else 'Unknown (%d)' % self.HashAlgorithm - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 14, Entry' + col_e pt.add_row(['Hash Usages', ', '.join(map(str, hash_usages))]) pt.add_row(['Reserved 0', '0x%X' % int.from_bytes(self.Reserved0, 'little')]) @@ -3239,15 +3239,15 @@ def ext_print(self) : pt.add_row(['Hash Algorithm', HashAlgorithm]) pt.add_row(['Hash Size', '0x%X' % self.HashSize]) pt.add_row(['Modulus & Exponent Hash', Hash]) - + return pt - + def get_flags(self) : flags = CSE_Ext_0E_GetFlags() flags.asbytes = self.Flags - + return flags.b.IPIPolicy, flags.b.Reserved - + class CSE_Ext_0E_Mod_R2(ctypes.LittleEndianStructure) : # R2 - (KEY_MANIFEST_EXT_ENTRY) _pack_ = 1 _fields_ = [ @@ -3259,16 +3259,16 @@ class CSE_Ext_0E_Mod_R2(ctypes.LittleEndianStructure) : # R2 - (KEY_MANIFEST_EXT ("Hash", uint32_t*12), # 0x24 SHA-384 (Big Endian, PKEY + EXP) # 0x54 ] - + def ext_print(self) : f1,f2 = self.get_flags() hash_usages = get_key_usages(self.UsageBitmap) - + Hash = '%0.*X' % (0x30 * 2, int.from_bytes(self.Hash, 'big')) HashAlgorithm = cse_hash_alg[self.HashAlgorithm] if self.HashAlgorithm in cse_hash_alg else 'Unknown (%d)' % self.HashAlgorithm - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 14, Entry' + col_e pt.add_row(['Hash Usages', ', '.join(map(str, hash_usages))]) pt.add_row(['Reserved 0', '0x%X' % int.from_bytes(self.Reserved0, 'little')]) @@ -3277,15 +3277,15 @@ def ext_print(self) : pt.add_row(['Hash Algorithm', HashAlgorithm]) pt.add_row(['Hash Size', '0x%X' % self.HashSize]) pt.add_row(['Modulus & Exponent Hash', Hash]) - + return pt - + def get_flags(self) : flags = CSE_Ext_0E_GetFlags() flags.asbytes = self.Flags - + return flags.b.IPIPolicy, flags.b.Reserved - + class CSE_Ext_0E_Flags(ctypes.LittleEndianStructure): _fields_ = [ ('IPIPolicy', uint8_t, 1), # RoT (Root of Trust) Key Manifest @@ -3310,12 +3310,12 @@ class CSE_Ext_0F(ctypes.LittleEndianStructure) : # R1 - Signed Package Informati ("Reserved", uint32_t*4), # 0x24 # 0x34 ] - + def ext_print(self) : hash_usages = get_key_usages(self.UsageBitmap) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 15, Signed Package Information' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -3324,9 +3324,9 @@ def ext_print(self) : pt.add_row(['Hash Usages', ', '.join(map(str, hash_usages))]) pt.add_row(['ARB Security Version Number', '%d' % self.ARBSVN]) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) - + return pt - + class CSE_Ext_0F_R2(ctypes.LittleEndianStructure) : # R2 - Signed Package Information (SIGNED_PACKAGE_INFO_EXT) _pack_ = 1 _fields_ = [ @@ -3342,15 +3342,15 @@ class CSE_Ext_0F_R2(ctypes.LittleEndianStructure) : # R2 - Signed Package Inform ('Reserved', uint8_t*10), # 0x2A # 0x34 ] - + # FWType & FWSKU are also used by CSE_Ext_23 & GSC_Info_FWI, remember to change them as well! - + def ext_print(self) : f1,f2,f3,f4,f5,f6 = self.get_flags() hash_usages = get_key_usages(self.UsageBitmap) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 15, Signed Package Information' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -3365,9 +3365,9 @@ def ext_print(self) : pt.add_row(['NVM Compatibility', ext15_nvm_type[f5] if f5 in ext15_nvm_type else 'Unknown (%d)' % f5]) pt.add_row(['NVM Compatibility Reserved', '0x%X' % f6]) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) - + return pt - + def get_flags(self) : fw_type = CSE_Ext_0F_R2_GetFWType() fw_type.asbytes = self.FWType @@ -3375,7 +3375,7 @@ def get_flags(self) : fw_sub_type.asbytes = self.FWSKU nvm_compatibility = CSE_Ext_0F_R2_GetNVMCompatibility() nvm_compatibility.asbytes = self.NVMCompatibility - + return fw_type.b.FWType, fw_type.b.Reserved, fw_sub_type.b.FWSKU, fw_sub_type.b.Reserved, \ nvm_compatibility.b.NVMCompatibility, nvm_compatibility.b.Reserved @@ -3390,7 +3390,7 @@ class CSE_Ext_0F_R2_GetFWType(ctypes.Union): ('b', CSE_Ext_0F_R2_FWType), ('asbytes', uint8_t) ] - + class CSE_Ext_0F_R2_FWSKU(ctypes.LittleEndianStructure): _fields_ = [ ('FWSKU', uint8_t, 3), @@ -3402,7 +3402,7 @@ class CSE_Ext_0F_R2_GetFWSKU(ctypes.Union): ('b', CSE_Ext_0F_R2_FWSKU), ('asbytes', uint8_t) ] - + class CSE_Ext_0F_R2_NVMCompatibility(ctypes.LittleEndianStructure): _fields_ = [ ('NVMCompatibility', uint32_t, 2), @@ -3426,14 +3426,14 @@ class CSE_Ext_0F_Mod(ctypes.LittleEndianStructure) : # R1 - (SIGNED_PACKAGE_INFO ("MetadataHash", uint32_t*8), # 0x14 SHA-256 # 0x34 ] - + def ext_print(self) : Type = cse_mod_type[self.Type] if self.Type in cse_mod_type else 'Unknown (%d)' % self.Type HashAlgorithm = cse_hash_alg[self.HashAlgorithm] if self.HashAlgorithm in cse_hash_alg else 'Unknown (%d)' % self.HashAlgorithm MetadataHash = '%0.*X' % (0x20 * 2, int.from_bytes(self.MetadataHash, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 15, Entry' + col_e pt.add_row(['Name', self.Name.decode('utf-8')]) pt.add_row(['Type', Type]) @@ -3441,9 +3441,9 @@ def ext_print(self) : pt.add_row(['Hash Size', '0x%X' % self.HashSize]) pt.add_row(['Metadata Size', '0x%X' % self.MetadataSize]) pt.add_row(['Metadata Hash', MetadataHash]) - + return pt - + class CSE_Ext_0F_Mod_R2(ctypes.LittleEndianStructure) : # R2 - (SIGNED_PACKAGE_INFO_EXT_ENTRY, STRONG_SIGNED_PACKAGE_INFO_EXT_ENTRY) _pack_ = 1 _fields_ = [ @@ -3455,14 +3455,14 @@ class CSE_Ext_0F_Mod_R2(ctypes.LittleEndianStructure) : # R2 - (SIGNED_PACKAGE_I ('MetadataHash', uint32_t*12), # 0x14 SHA-384 # 0x44 ] - + def ext_print(self) : Type = cse_mod_type[self.Type] if self.Type in cse_mod_type else 'Unknown (%d)' % self.Type HashAlgorithm = cse_hash_alg[self.HashAlgorithm] if self.HashAlgorithm in cse_hash_alg else 'Unknown (%d)' % self.HashAlgorithm MetadataHash = '%0.*X' % (0x30 * 2, int.from_bytes(self.MetadataHash, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 15, Entry' + col_e pt.add_row(['Name', self.Name.decode('utf-8')]) pt.add_row(['Type', Type]) @@ -3470,9 +3470,9 @@ def ext_print(self) : pt.add_row(['Hash Size', '0x%X' % self.HashSize]) pt.add_row(['Metadata Size', '0x%X' % self.MetadataSize]) pt.add_row(['Metadata Hash', MetadataHash]) - + return pt - + class CSE_Ext_0F_Mod_R3(ctypes.LittleEndianStructure) : # R3 - (SIGNED_PACKAGE_INFO_EXT_ENTRY, STRONG_SIGNED_PACKAGE_INFO_EXT_ENTRY) _pack_ = 1 _fields_ = [ @@ -3484,13 +3484,13 @@ class CSE_Ext_0F_Mod_R3(ctypes.LittleEndianStructure) : # R3 - (SIGNED_PACKAGE_I ('MetadataHash', uint32_t*12), # 0x14 SHA-384 # 0x44 ] - + def ext_print(self) : Type = cse_mod_type[self.Type] if self.Type in cse_mod_type else 'Unknown (%d)' % self.Type MetadataHash = '%0.*X' % (0x30 * 2, int.from_bytes(self.MetadataHash, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 15, Entry' + col_e pt.add_row(['Name', self.Name.decode('utf-8')]) pt.add_row(['Type', Type]) @@ -3498,7 +3498,7 @@ def ext_print(self) : pt.add_row(['Hash Size', '0x%X' % self.HashSize]) pt.add_row(['Metadata Size', '0x%X' % self.MetadataSize]) pt.add_row(['Metadata Hash', MetadataHash]) - + return pt class CSE_Ext_10(ctypes.LittleEndianStructure) : # R1 - Anti-Cloning SKU ID (iUnit/IUNP/IUNM, not in XML, Reverse Engineered) @@ -3510,20 +3510,20 @@ class CSE_Ext_10(ctypes.LittleEndianStructure) : # R1 - Anti-Cloning SKU ID (iUn ('Reserved', uint32_t*4), # 0x0C # 0x1C ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + types = {1:'BootLoader', 2:'Firmware'} - + pt.title = col_y + 'Extension 16, Anti-Cloning SKU ID' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) pt.add_row(['Type', types[self.Type] if self.Type in types else 'Unknown']) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) - + return pt - + class CSE_Ext_10_Mod(ctypes.LittleEndianStructure) : # R1 - Anti-Cloning SKU ID Chunk (iUnit/IUNP/IUNM, not in XML, Reverse Engineered) _pack_ = 1 _fields_ = [ @@ -3538,13 +3538,13 @@ class CSE_Ext_10_Mod(ctypes.LittleEndianStructure) : # R1 - Anti-Cloning SKU ID ('Reserved', uint32_t*4), # 0x34 # 0x44 ] - + def ext_print(self) : Date = '%0.4X-%0.2X-%0.2X' % (self.Year, self.Month, self.Day) Hash = '%0.*X' % (0x20 * 2, int.from_bytes(self.Hash, 'big')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 16, Anti-Cloning SKU ID Chunk' + col_e pt.add_row(['Number', '%d' % self.Chunk]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -3553,9 +3553,9 @@ def ext_print(self) : pt.add_row(['Unknown 0', '0x%X' % self.Unknown0]) pt.add_row(['Unknown 1', '0x%X' % self.Unknown1]) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) - + return pt - + class CSE_Ext_10_Mod_R2(ctypes.LittleEndianStructure) : # R2 - Anti-Cloning SKU ID Chunk (iUnit/IUNP, not in XML, Reverse Engineered) _pack_ = 1 _fields_ = [ @@ -3570,13 +3570,13 @@ class CSE_Ext_10_Mod_R2(ctypes.LittleEndianStructure) : # R2 - Anti-Cloning SKU ('Reserved', uint32_t*4), # 0x44 # 0x54 ] - + def ext_print(self) : Date = '%0.4X-%0.2X-%0.2X' % (self.Year, self.Month, self.Day) Hash = '%0.*X' % (0x30 * 2, int.from_bytes(self.Hash, 'big')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 16, Anti-Cloning SKU ID Chunk' + col_e pt.add_row(['Number', '%d' % self.Chunk]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -3585,7 +3585,7 @@ def ext_print(self) : pt.add_row(['Unknown 0', '0x%X' % self.Unknown0]) pt.add_row(['Unknown 1', '0x%X' % self.Unknown1]) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) - + return pt class CSE_Ext_11(ctypes.LittleEndianStructure) : # R1 - cAVS (ADSP, sof_ri_info.py) @@ -3602,14 +3602,14 @@ class CSE_Ext_11(ctypes.LittleEndianStructure) : # R1 - cAVS (ADSP, sof_ri_info. ('Attributes', uint32_t*4), # 0x50 # 0x60 ] - + # https://thesofproject.github.io/latest/developer_guides/debugability/ri-info/index.html - + def ext_print(self) : Hash = '%0.*X' % (0x20 * 2, int.from_bytes(self.Hash, 'big')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 17, Clear Audio Voice Speech (aDSP)' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -3620,9 +3620,9 @@ def ext_print(self) : pt.add_row(['Offset Base', '0x%X' % self.OffsetBase]) pt.add_row(['Offset Limit', '0x%X' % self.OffsetLimit]) pt.add_row(['Attributes', '0x%X' % int.from_bytes(self.Attributes, 'little')]) - + return pt - + class CSE_Ext_11_R2(ctypes.LittleEndianStructure) : # R2 - cAVS (ADSP, sof_ri_info.py) _pack_ = 1 _fields_ = [ @@ -3637,14 +3637,14 @@ class CSE_Ext_11_R2(ctypes.LittleEndianStructure) : # R2 - cAVS (ADSP, sof_ri_in ('Attributes', uint32_t*4), # 0x60 # 0x70 ] - + # https://thesofproject.github.io/latest/developer_guides/debugability/ri-info/index.html - + def ext_print(self) : Hash = '%0.*X' % (0x30 * 2, int.from_bytes(self.Hash, 'big')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 17, Clear Audio Voice Speech (aDSP)' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -3655,9 +3655,9 @@ def ext_print(self) : pt.add_row(['Offset Base', '0x%X' % self.OffsetBase]) pt.add_row(['Offset Limit', '0x%X' % self.OffsetLimit]) pt.add_row(['Attributes', '0x%X' % int.from_bytes(self.Attributes, 'little')]) - + return pt - + class CSE_Ext_12(ctypes.LittleEndianStructure) : # R1 - Isolated Memory Ranges (FTPR, not in XML, Reverse Engineered) _pack_ = 1 _fields_ = [ @@ -3667,16 +3667,16 @@ class CSE_Ext_12(ctypes.LittleEndianStructure) : # R1 - Isolated Memory Ranges ( ("Reserved", uint32_t*4), # 0x0C # 0x1C ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 18, Isolated Memory Ranges' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) pt.add_row(['Module Count', '%d' % self.ModuleCount]) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) - + return pt class CSE_Ext_12_Mod(ctypes.LittleEndianStructure) : # R1 - (not in XML, Reverse Engineered) @@ -3695,10 +3695,10 @@ class CSE_Ext_12_Mod(ctypes.LittleEndianStructure) : # R1 - (not in XML, Reverse ('Unknown10', uint64_t), # 0x30 # 0x38 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 18, Isolated Memory Range' + col_e pt.add_row(['Unknown 0', '0x%X' % self.Unknown0]) pt.add_row(['Unknown 1', '0x%X' % self.Unknown1]) @@ -3711,7 +3711,7 @@ def ext_print(self) : pt.add_row(['Unknown 8', '0x%X' % self.Unknown8]) pt.add_row(['Unknown 9', '0x%X' % self.Unknown9]) pt.add_row(['Unknown 10', '0x%X' % self.Unknown10]) - + return pt class CSE_Ext_13(ctypes.LittleEndianStructure) : # R1 - Boot Policy (BOOT_POLICY_METADATA_EXT) @@ -3741,7 +3741,7 @@ class CSE_Ext_13(ctypes.LittleEndianStructure) : # R1 - Boot Policy (BOOT_POLICY ("VendorAttrSize", uint32_t), # 0xB0 # 0xB4 ] - + def ext_print(self) : IBBLHash = '%0.*X' % (0x20 * 2, int.from_bytes(self.IBBLHash, 'big')) IBBHash = '%0.*X' % (0x20 * 2, int.from_bytes(self.IBBHash, 'big')) @@ -3749,9 +3749,9 @@ def ext_print(self) : IBBLHashAlg = cse_hash_alg[self.IBBLHashAlg] if self.IBBLHashAlg in cse_hash_alg else 'Unknown (%d)' % self.IBBLHashAlg IBBHashAlg = cse_hash_alg[self.IBBHashAlg] if self.IBBHashAlg in cse_hash_alg else 'Unknown (%d)' % self.IBBHashAlg OBBHashAlg = cse_hash_alg[self.OBBHashAlg] if self.OBBHashAlg in cse_hash_alg else 'Unknown (%d)' % self.OBBHashAlg - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 19, Boot Policy' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -3775,9 +3775,9 @@ def ext_print(self) : pt.add_row(['IBB Entry Point', '0x%X' % self.IBBEntryPoint]) pt.add_row(['IBB Segment Count', '%d' % self.IBBSegmentCount]) pt.add_row(['Vendor Attributes Size', '0x%X' % self.VendorAttrSize]) - + return pt - + class CSE_Ext_13_R2(ctypes.LittleEndianStructure) : # R2 - Boot Policy (BOOT_POLICY_METADATA_EXT) _pack_ = 1 _fields_ = [ @@ -3805,7 +3805,7 @@ class CSE_Ext_13_R2(ctypes.LittleEndianStructure) : # R2 - Boot Policy (BOOT_POL ("VendorAttrSize", uint32_t), # 0xE0 # 0xE4 ] - + def ext_print(self) : IBBLHash = '%0.*X' % (0x30 * 2, int.from_bytes(self.IBBLHash, 'big')) IBBHash = '%0.*X' % (0x30 * 2, int.from_bytes(self.IBBHash, 'big')) @@ -3813,9 +3813,9 @@ def ext_print(self) : IBBLHashAlg = cse_hash_alg[self.IBBLHashAlg] if self.IBBLHashAlg in cse_hash_alg else 'Unknown (%d)' % self.IBBLHashAlg IBBHashAlg = cse_hash_alg[self.IBBHashAlg] if self.IBBHashAlg in cse_hash_alg else 'Unknown (%d)' % self.IBBHashAlg OBBHashAlg = cse_hash_alg[self.OBBHashAlg] if self.OBBHashAlg in cse_hash_alg else 'Unknown (%d)' % self.OBBHashAlg - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 19, Boot Policy' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -3839,7 +3839,7 @@ def ext_print(self) : pt.add_row(['IBB Entry Point', '0x%X' % self.IBBEntryPoint]) pt.add_row(['IBB Segment Count', '%d' % self.IBBSegmentCount]) pt.add_row(['Vendor Attributes Size', '0x%X' % self.VendorAttrSize]) - + return pt class CSE_Ext_14(ctypes.LittleEndianStructure) : # R1 - DnX Manifest (DnxManifestExtension) @@ -3867,12 +3867,12 @@ class CSE_Ext_14(ctypes.LittleEndianStructure) : # R1 - DnX Manifest (DnxManifes ("ChunkCount", uint32_t), # 0xA8 # 0xAC ] - - def ext_print(self) : + + def ext_print(self) : PublicKey = '%0.*X' % (0x100 * 2, int.from_bytes(self.PublicKey, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 20 R1, DnX Manifest' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -3894,7 +3894,7 @@ def ext_print(self) : pt.add_row(['Reserved 5', '0x%X' % self.Reserved5]) pt.add_row(['IFWI Chunk Data Size', '0x%X' % self.ChunkSize]) pt.add_row(['IFWI Chunk Count', '%d' % self.ChunkCount]) - + return pt class CSE_Ext_14_R2(ctypes.LittleEndianStructure) : # R2 - DnX Manifest (DnxManifestExtension_ver2) @@ -3934,14 +3934,14 @@ class CSE_Ext_14_R2(ctypes.LittleEndianStructure) : # R2 - DnX Manifest (DnxMani ("ChunkSize", uint32_t), # 0x144 0x10000 (64KB) # 0x148 ] - + def ext_print(self) : PublicKey = '%0.*X' % (0x100 * 2, int.from_bytes(self.PublicKey, 'little')) HashArrHashAlg = cse_hash_alg[self.HashArrHashAlg] if self.HashArrHashAlg in cse_hash_alg else 'Unknown (%d)' % self.HashArrHashAlg ChunkHashAlg = cse_hash_alg[self.ChunkHashAlg] if self.ChunkHashAlg in cse_hash_alg else 'Unknown (%d)' % self.ChunkHashAlg - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 20 R2, DnX Manifest' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -3975,9 +3975,9 @@ def ext_print(self) : pt.add_row(['Reserved 10', '0x%X' % self.Reserved10]) pt.add_row(['Reserved 11', '0x%X' % self.Reserved11]) pt.add_row(['IFWI Chunk Data Size', '0x%X' % self.ChunkSize]) - + return pt - + class CSE_Ext_14_R3(ctypes.LittleEndianStructure) : # R3 - DnX Manifest (DnxManifestExtension_ver2) _pack_ = 1 _fields_ = [ @@ -4015,14 +4015,14 @@ class CSE_Ext_14_R3(ctypes.LittleEndianStructure) : # R3 - DnX Manifest (DnxMani ('ChunkSize', uint32_t), # 0x1C4 0x10000 (64KB) # 0x1C8 ] - + def ext_print(self) : PublicKey = '%0.*X' % (0x180 * 2, int.from_bytes(self.PublicKey, 'little')) HashArrHashAlg = cse_hash_alg[self.HashArrHashAlg] if self.HashArrHashAlg in cse_hash_alg else 'Unknown (%d)' % self.HashArrHashAlg ChunkHashAlg = cse_hash_alg[self.ChunkHashAlg] if self.ChunkHashAlg in cse_hash_alg else 'Unknown (%d)' % self.ChunkHashAlg - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 20 R3, DnX Manifest' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -4056,7 +4056,7 @@ def ext_print(self) : pt.add_row(['Reserved 10', '0x%X' % self.Reserved10]) pt.add_row(['Reserved 11', '0x%X' % self.Reserved11]) pt.add_row(['IFWI Chunk Data Size', '0x%X' % self.ChunkSize]) - + return pt class CSE_Ext_14_HashArray(ctypes.LittleEndianStructure) : # R1 - DnX R2 Hashes Array (not in XML, Reverse Engineered) @@ -4066,18 +4066,18 @@ class CSE_Ext_14_HashArray(ctypes.LittleEndianStructure) : # R1 - DnX R2 Hashes ("HashArrHash", uint32_t*8), # 0x4 SHA-256 # 0x24 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + HashArrHash = '%0.*X' % (0x20 * 2, int.from_bytes(self.HashArrHash, 'little')) - + pt.title = col_y + 'Extension 20 R2, Hashes Array' + col_e pt.add_row(['Hashes Array Size', '0x%X' % (self.HashArrSize * 4)]) pt.add_row(['Hashes Array Hash', HashArrHash]) - + return pt - + class CSE_Ext_14_HashArray_R2(ctypes.LittleEndianStructure) : # R2 - DnX R2 Hashes Array (not in XML, Reverse Engineered) _pack_ = 1 _fields_ = [ @@ -4085,18 +4085,18 @@ class CSE_Ext_14_HashArray_R2(ctypes.LittleEndianStructure) : # R2 - DnX R2 Hash ("HashArrHash", uint32_t*12), # 0x4 SHA-384 # 0x34 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + HashArrHash = '%0.*X' % (0x30 * 2, int.from_bytes(self.HashArrHash, 'little')) - + pt.title = col_y + 'Extension 20 R2, Hashes Array' + col_e pt.add_row(['Hashes Array Size', '0x%X' % (self.HashArrSize * 4)]) pt.add_row(['Hashes Array Hash', HashArrHash]) - + return pt - + class CSE_Ext_14_RegionMap(ctypes.LittleEndianStructure) : # R1 - DnX R1/R2 Region Map (not in XML, Reverse Engineered) _pack_ = 1 _fields_ = [ @@ -4105,15 +4105,15 @@ class CSE_Ext_14_RegionMap(ctypes.LittleEndianStructure) : # R1 - DnX R1/R2 Regi ('RegionSize', uint32_t), # 0x08 # Size of region after rcipifwi start offset # 0xC ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 20 R1/R2, IFWI Region Map' + col_e pt.add_row(['Unknown', '0x%X' % self.Unknown]) pt.add_row(['IFWI Region Start', '0x%X' % self.RegionOffset]) pt.add_row(['IFWI Region Size', '0x%X' % self.RegionSize]) - + return pt class CSE_Ext_15(ctypes.LittleEndianStructure) : # R1 - Unlock/Secure Token UTOK/STKN (SECURE_TOKEN_EXT) @@ -4131,7 +4131,7 @@ class CSE_Ext_15(ctypes.LittleEndianStructure) : # R1 - Unlock/Secure Token UTOK ("Reserved", uint32_t*4), # 0x24 # 0x34 ] - + def ext_print(self) : fvalue = ['No','Yes'] frvalue = ['Yes','No'] @@ -4144,9 +4144,9 @@ def ext_print(self) : 8: 'Change Device Lifecycle' } f1,f2,f3,f4,f5,f6,f7 = self.get_flags() - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 21, Unlock/Secure Token' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -4164,16 +4164,16 @@ def ext_print(self) : pt.add_row(['Expiration Seconds', '%d' % self.ExpirationSec]) pt.add_row(['Manufacturing Lot', '0x%X' % self.ManufLot]) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) - + return pt - + def get_flags(self) : flags = CSE_Ext_15_GetFlags() flags.asbytes = self.Flags - + return flags.b.SingleBoot, flags.b.PartRestricted, flags.b.AntiReplay, flags.b.TimeLimited,\ flags.b.ManufacturingLotRestrict, flags.b.ManufacturingPartID, flags.b.Reserved - + class CSE_Ext_15_Flags(ctypes.LittleEndianStructure): _fields_ = [ ('SingleBoot', uint32_t, 1), @@ -4199,15 +4199,15 @@ class CSE_Ext_15_PartID(ctypes.LittleEndianStructure) : # After CSE_Ext_15 (SECU ("TimeBase", uint32_t), # 0x10 # 0x14 ] - - def ext_print(self) : + + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 21, Part ID' + col_e pt.add_row(['Part ID', '0x%X' % int.from_bytes(self.PartID, 'little')]) pt.add_row(['Nonce', '0x%X' % self.Nonce]) pt.add_row(['Time Base', '0x%X' % self.TimeBase]) - + return pt class CSE_Ext_15_Payload(ctypes.LittleEndianStructure) : # After CSE_Ext_15_PartID (SECURE_TOKEN_PAYLOAD) @@ -4216,13 +4216,13 @@ class CSE_Ext_15_Payload(ctypes.LittleEndianStructure) : # After CSE_Ext_15_Part ("KnobCount", uint32_t), # 0x00 # 0x04 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 21, Payload' + col_e pt.add_row(['Knob Count', '%d' % self.KnobCount]) - + return pt class CSE_Ext_15_Payload_Knob(ctypes.LittleEndianStructure) : # After CSE_Ext_15_Payload (SECURE_TOKEN_PAYLOAD_KNOB) @@ -4232,7 +4232,7 @@ class CSE_Ext_15_Payload_Knob(ctypes.LittleEndianStructure) : # After CSE_Ext_15 ("Data", uint32_t), # 0x04 # 0x08 ] - + def __init__(self, variant, major, minor, hotfix, build, year, month, variant_p, *args, **kwargs): super().__init__(*args, **kwargs) self.variant = variant @@ -4243,7 +4243,7 @@ def __init__(self, variant, major, minor, hotfix, build, year, month, variant_p, self.year = year self.month = month self.variant_p = variant_p - + def ext_print(self) : knob_ids = { 0x80860001 : ['Intel Unlock', ['None', 'Intel/RED', 'DAM']], @@ -4272,13 +4272,13 @@ def ext_print(self) : ['Change Device Lifecycle', ['No', 'Customer Care', 'RnD', 'Refurbish']], 0x80860201 : ['Co-Signing', ['Enabled', 'Disabled']] } - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 21, Payload Knob' + col_e pt.add_row(['ID', knob_ids[self.ID][0] if self.ID in knob_ids else 'Unknown: 0x%X' % self.ID]) pt.add_row(['Data', knob_ids[self.ID][1][self.Data] if self.ID in knob_ids else 'Unknown: 0x%X' % self.Data]) - + return pt class CSE_Ext_16(ctypes.LittleEndianStructure) : # R1 - IFWI Partition Information (IFWI_PARTITION_MANIFEST_EXTENSION) @@ -4301,20 +4301,20 @@ class CSE_Ext_16(ctypes.LittleEndianStructure) : # R1 - IFWI Partition Informati ('Reserved', uint32_t*4), # 0x48 # 0x58 ] - + # PartitionSize & Hash are valid for RGN firmware only with stock $CPD & Data, no FIT/OEM configurations. The latter, usually oem.key and fitc.cfg, # are added at the end of the PartitionSize so FIT adjusts $CPD and appends customization files accordingly. Thus, PartitionSize and Hash fields # must not be verified at FIT/OEM-customized images because they're not applicable anymore. - + def ext_print(self) : fvalue = ['No','Yes'] f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12 = self.get_flags() - + Hash = '%0.*X' % (0x20 * 2, int.from_bytes(self.Hash, 'little')) HashAlgorithm = cse_hash_alg[self.HashAlgorithm] if self.HashAlgorithm in cse_hash_alg else 'Unknown (%d)' % self.HashAlgorithm - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 22, IFWI Partition Information' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -4339,15 +4339,15 @@ def ext_print(self) : pt.add_row(['Ignore FWU Disable Policy', fvalue[f11]]) pt.add_row(['Flags Private Reserved', '0x%X' % f12]) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) - + return pt - + def get_flags(self) : flags = CSE_Ext_16_GetFlags() flags_p = CSE_Ext_16_GetFlagsPrivate() flags.asbytes = self.Flags flags_p.asbytes = self.FlagsPrivate - + return flags.b.SupportMultipleInstances, flags.b.SupportApiVersionBasedUpdate, flags.b.ActionOnUpdate, \ flags.b.ObeyFullUpdateRules, flags.b.IfrEnableOnly, flags.b.AllowCrossPointUpdate, flags.b.AllowCrossHotfixUpdate, \ flags.b.PartialUpdateOnly, flags.b.NotMeasured, flags.b.Reserved, flags_p.b.IgnoreFwuDisablePolicy, flags_p.b.Reserved @@ -4372,20 +4372,20 @@ class CSE_Ext_16_R2(ctypes.LittleEndianStructure) : # R2 - IFWI Partition Inform ('Reserved', uint32_t*4), # 0x58 # 0x68 ] - + # PartitionSize & Hash are valid for RGN firmware only with stock $CPD & Data, no FIT/OEM configurations. The latter, usually oem.key and fitc.cfg, # are added at the end of the PartitionSize so FIT adjusts $CPD and appends customization files accordingly. Thus, PartitionSize and Hash fields # must not be verified at FIT/OEM-customized images because they're not applicable anymore. - + def ext_print(self) : fvalue = ['No','Yes'] f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12 = self.get_flags() - + Hash = '%0.*X' % (0x30 * 2, int.from_bytes(self.Hash, 'little')) HashAlgorithm = cse_hash_alg[self.HashAlgorithm] if self.HashAlgorithm in cse_hash_alg else 'Unknown (%d)' % self.HashAlgorithm - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 22, IFWI Partition Information' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -4410,19 +4410,19 @@ def ext_print(self) : pt.add_row(['Ignore FWU Disable Policy', fvalue[f11]]) pt.add_row(['Flags Private Reserved', '0x%X' % f12]) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) - + return pt - + def get_flags(self) : flags = CSE_Ext_16_GetFlags() flags_p = CSE_Ext_16_GetFlagsPrivate() flags.asbytes = self.Flags flags_p.asbytes = self.FlagsPrivate - + return flags.b.SupportMultipleInstances, flags.b.SupportApiVersionBasedUpdate, flags.b.ActionOnUpdate, \ flags.b.ObeyFullUpdateRules, flags.b.IfrEnableOnly, flags.b.AllowCrossPointUpdate, flags.b.AllowCrossHotfixUpdate, \ flags.b.PartialUpdateOnly, flags.b.NotMeasured, flags.b.Reserved, flags_p.b.IgnoreFwuDisablePolicy, flags_p.b.Reserved - + class CSE_Ext_16_Flags(ctypes.LittleEndianStructure): _fields_ = [ ('SupportMultipleInstances', uint32_t, 1), # For independently updated WCOD/LOCL partitions with multiple instances @@ -4442,7 +4442,7 @@ class CSE_Ext_16_GetFlags(ctypes.Union): ('b', CSE_Ext_16_Flags), ('asbytes', uint32_t) ] - + class CSE_Ext_16_FlagsPrivate(ctypes.LittleEndianStructure): _fields_ = [ ('IgnoreFwuDisablePolicy', uint32_t, 1), @@ -4479,12 +4479,12 @@ class CSE_Ext_17(ctypes.LittleEndianStructure) : # R1 - Flash Descriptor Hash (n ('Hash', uint32_t*8), # 0x28 SHA-256 LE # 0x48 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + Hash = '%0.*X' % (0x20 * 2, int.from_bytes(self.Hash, 'little')) - + pt.title = col_y + 'Extension 23, Flash Descriptor Hash' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -4497,9 +4497,9 @@ def ext_print(self) : pt.add_row(['Range 7', '0x%X - 0x%X' % (self.Range7Start, self.Range7End)]) pt.add_row(['Range 8', '0x%X - 0x%X' % (self.Range8Start, self.Range8End)]) pt.add_row(['Hash', Hash]) - + return pt - + class CSE_Ext_17_R2(ctypes.LittleEndianStructure) : # R2 - Flash Descriptor Hash (not in XML, Reverse Engineered) _pack_ = 1 _fields_ = [ @@ -4524,12 +4524,12 @@ class CSE_Ext_17_R2(ctypes.LittleEndianStructure) : # R2 - Flash Descriptor Hash ('Hash', uint32_t*12), # 0x28 SHA-384 LE # 0x58 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + Hash = '%0.*X' % (0x30 * 2, int.from_bytes(self.Hash, 'little')) - + pt.title = col_y + 'Extension 23, Flash Descriptor Hash' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -4542,9 +4542,9 @@ def ext_print(self) : pt.add_row(['Range 7', '0x%X - 0x%X' % (self.Range7Start, self.Range7End)]) pt.add_row(['Range 8', '0x%X - 0x%X' % (self.Range8Start, self.Range8End)]) pt.add_row(['Hash', Hash]) - + return pt - + class CSE_Ext_18(ctypes.LittleEndianStructure) : # R1 - USB Type C IO Manageability (TCSS_METADATA_EXT) _pack_ = 1 _fields_ = [ @@ -4553,17 +4553,17 @@ class CSE_Ext_18(ctypes.LittleEndianStructure) : # R1 - USB Type C IO Manageabil ('Reserved', uint32_t), # 0x08 # 0x0C ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 24, USB Type C IO Manageability' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) pt.add_row(['Reserved', '0x%X' % self.Reserved]) - + return pt - + class CSE_Ext_18_R2(ctypes.LittleEndianStructure) : # R2 - GSC IUP Manifests (not in XML, Reverse Engineered) _pack_ = 1 _fields_ = [ @@ -4571,16 +4571,16 @@ class CSE_Ext_18_R2(ctypes.LittleEndianStructure) : # R2 - GSC IUP Manifests (no ('Size', uint32_t), # 0x04 # 0x08 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 24, GSC IUP Manifests' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) - + return pt - + class CSE_Ext_18_Mod(ctypes.LittleEndianStructure) : # R1 - USB Type C IO Manageability Hash (TCSS_HASH_METADATA) _pack_ = 1 _fields_ = [ @@ -4590,21 +4590,21 @@ class CSE_Ext_18_Mod(ctypes.LittleEndianStructure) : # R1 - USB Type C IO Manage ('Hash', uint32_t*8), # 0x0C SHA-256 Big Endian # 0x2C ] - + def ext_print(self) : Hash = '%0.*X' % (0x20 * 2, int.from_bytes(self.Hash, 'big')) HashAlgorithm = tcs_hash_alg[self.HashAlgorithm] if self.HashAlgorithm in tcs_hash_alg else 'Unknown (%d)' % self.HashAlgorithm - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 24, USB Type C IO Manageability Hash' + col_e pt.add_row(['Hash Type', '0x%X' % self.HashType]) pt.add_row(['Hash Algorithm', HashAlgorithm]) pt.add_row(['Hash Size', '0x%X' % self.HashSize]) pt.add_row(['Hash', Hash]) - + return pt - + class CSE_Ext_18_Mod_R2(ctypes.LittleEndianStructure) : # R2 - USB Type C IO Manageability Hash (TCSS_HASH_METADATA) _pack_ = 1 _fields_ = [ @@ -4614,21 +4614,21 @@ class CSE_Ext_18_Mod_R2(ctypes.LittleEndianStructure) : # R2 - USB Type C IO Man ('Hash', uint32_t*12), # 0x0C SHA-384 Big Endian # 0x3C ] - + def ext_print(self) : Hash = '%0.*X' % (0x30 * 2, int.from_bytes(self.Hash, 'big')) HashAlgorithm = cse_hash_alg[self.HashAlgorithm] if self.HashAlgorithm in cse_hash_alg else 'Unknown (%d)' % self.HashAlgorithm - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 24, USB Type C IO Manageability Hash' + col_e pt.add_row(['Hash Type', '0x%X' % self.HashType]) pt.add_row(['Hash Algorithm', HashAlgorithm]) pt.add_row(['Hash Size', '0x%X' % self.HashSize]) pt.add_row(['Hash', Hash]) - + return pt - + class CSE_Ext_18_Mod_R3(ctypes.LittleEndianStructure) : # R3 - GSC IUP Manifest Hash (not in XML, Reverse Engineered) _pack_ = 1 _fields_ = [ @@ -4636,16 +4636,16 @@ class CSE_Ext_18_Mod_R3(ctypes.LittleEndianStructure) : # R3 - GSC IUP Manifest ('Hash', uint32_t*12), # 0x0C SHA-384 BE (Manifest w/o RSA) # 0x34 ] - + def ext_print(self) : Hash = '%0.*X' % (0x30 * 2, int.from_bytes(self.Hash, 'big')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 24, GSC IUP Manifest Hash' + col_e pt.add_row(['Name', self.Name.decode('utf-8')]) pt.add_row(['Hash', Hash]) - + return pt class CSE_Ext_19(ctypes.LittleEndianStructure) : # R1 - USB Type C MG (TCSS_METADATA_EXT) @@ -4656,17 +4656,17 @@ class CSE_Ext_19(ctypes.LittleEndianStructure) : # R1 - USB Type C MG (TCSS_META ('Reserved', uint32_t), # 0x08 # 0x0C ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 25, USB Type C MG' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) pt.add_row(['Reserved', '0x%X' % self.Reserved]) - + return pt - + class CSE_Ext_19_R2(ctypes.LittleEndianStructure) : # R2 - GSC Project Info (gsc_fwu_external_version) _pack_ = 1 _fields_ = [ @@ -4677,21 +4677,21 @@ class CSE_Ext_19_R2(ctypes.LittleEndianStructure) : # R2 - GSC Project Info (gsc ('Build', uint16_t), # 0x0E Year & Week at DG1, Stepping/Build at DG2+ # 0x10 ] - + # The version of the overall IFWI image, i.e. the combination of IPs (igsc_system.h) - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 25, GSC Project Info' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) pt.add_row(['Project', self.Project.decode('utf-8')]) pt.add_row(['Hotfix', self.Hotfix]) pt.add_row(['Build', self.Build]) - + return pt - + class CSE_Ext_19_Mod(ctypes.LittleEndianStructure) : # R1 - USB Type C MG Hash (TCSS_HASH_METADATA) _pack_ = 1 _fields_ = [ @@ -4701,21 +4701,21 @@ class CSE_Ext_19_Mod(ctypes.LittleEndianStructure) : # R1 - USB Type C MG Hash ( ('Hash', uint32_t*8), # 0x0C SHA-256 Big Endian # 0x2C ] - + def ext_print(self) : Hash = '%0.*X' % (0x20 * 2, int.from_bytes(self.Hash, 'big')) HashAlgorithm = tcs_hash_alg[self.HashAlgorithm] if self.HashAlgorithm in tcs_hash_alg else 'Unknown (%d)' % self.HashAlgorithm - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 25, USB Type C MG Hash' + col_e pt.add_row(['Hash Type', '0x%X' % self.HashType]) pt.add_row(['Hash Algorithm', HashAlgorithm]) pt.add_row(['Hash Size', '0x%X' % self.HashSize]) pt.add_row(['Hash', Hash]) - + return pt - + class CSE_Ext_19_Mod_R2(ctypes.LittleEndianStructure) : # R2 - USB Type C MG Hash (TCSS_HASH_METADATA) _pack_ = 1 _fields_ = [ @@ -4725,21 +4725,21 @@ class CSE_Ext_19_Mod_R2(ctypes.LittleEndianStructure) : # R2 - USB Type C MG Has ('Hash', uint32_t*12), # 0x0C SHA-384 Big Endian # 0x3C ] - + def ext_print(self) : Hash = '%0.*X' % (0x30 * 2, int.from_bytes(self.Hash, 'big')) HashAlgorithm = cse_hash_alg[self.HashAlgorithm] if self.HashAlgorithm in cse_hash_alg else 'Unknown (%d)' % self.HashAlgorithm - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 25, USB Type C MG Hash' + col_e pt.add_row(['Hash Type', '0x%X' % self.HashType]) pt.add_row(['Hash Algorithm', HashAlgorithm]) pt.add_row(['Hash Size', '0x%X' % self.HashSize]) pt.add_row(['Hash', Hash]) - + return pt - + class CSE_Ext_1A(ctypes.LittleEndianStructure) : # R1 - USB Type C Thunderbolt (TCSS_METADATA_EXT) _pack_ = 1 _fields_ = [ @@ -4748,17 +4748,17 @@ class CSE_Ext_1A(ctypes.LittleEndianStructure) : # R1 - USB Type C Thunderbolt ( ('Reserved', uint32_t), # 0x08 # 0x0C ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 26, USB Type C Thunderbolt' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) pt.add_row(['Reserved', '0x%X' % self.Reserved]) - + return pt - + class CSE_Ext_1A_R2(ctypes.LittleEndianStructure) : # R2 - GSC FWI Manifests (not in XML, Reverse Engineered) _pack_ = 1 _fields_ = [ @@ -4766,16 +4766,16 @@ class CSE_Ext_1A_R2(ctypes.LittleEndianStructure) : # R2 - GSC FWI Manifests (no ('Size', uint32_t), # 0x04 # 0x08 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 26, GSC FWI Manifests' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) - + return pt - + class CSE_Ext_1A_Mod(ctypes.LittleEndianStructure) : # R1 - USB Type C Thunderbolt Hash (TCSS_HASH_METADATA) _pack_ = 1 _fields_ = [ @@ -4785,21 +4785,21 @@ class CSE_Ext_1A_Mod(ctypes.LittleEndianStructure) : # R1 - USB Type C Thunderbo ('Hash', uint32_t*8), # 0x0C SHA-256 Big Endian # 0x2C ] - + def ext_print(self) : Hash = '%0.*X' % (0x20 * 2, int.from_bytes(self.Hash, 'big')) HashAlgorithm = tcs_hash_alg[self.HashAlgorithm] if self.HashAlgorithm in tcs_hash_alg else 'Unknown (%d)' % self.HashAlgorithm - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 26, USB Type C Thunderbolt Hash' + col_e pt.add_row(['Hash Type', '0x%X' % self.HashType]) pt.add_row(['Hash Algorithm', HashAlgorithm]) pt.add_row(['Hash Size', '0x%X' % self.HashSize]) pt.add_row(['Hash', Hash]) - + return pt - + class CSE_Ext_1A_Mod_R2(ctypes.LittleEndianStructure) : # R2 - USB Type C Thunderbolt Hash (TCSS_HASH_METADATA) _pack_ = 1 _fields_ = [ @@ -4809,21 +4809,21 @@ class CSE_Ext_1A_Mod_R2(ctypes.LittleEndianStructure) : # R2 - USB Type C Thunde ('Hash', uint32_t*12), # 0x0C SHA-384 Big Endian # 0x3C ] - + def ext_print(self) : Hash = '%0.*X' % (0x30 * 2, int.from_bytes(self.Hash, 'big')) HashAlgorithm = cse_hash_alg[self.HashAlgorithm] if self.HashAlgorithm in cse_hash_alg else 'Unknown (%d)' % self.HashAlgorithm - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 26, USB Type C Thunderbolt Hash' + col_e pt.add_row(['Hash Type', '0x%X' % self.HashType]) pt.add_row(['Hash Algorithm', HashAlgorithm]) pt.add_row(['Hash Size', '0x%X' % self.HashSize]) pt.add_row(['Hash', Hash]) - + return pt - + class CSE_Ext_1A_Mod_R3(ctypes.LittleEndianStructure) : # R3 - GSC FWI Manifest Hash (not in XML, Reverse Engineered) _pack_ = 1 _fields_ = [ @@ -4831,18 +4831,18 @@ class CSE_Ext_1A_Mod_R3(ctypes.LittleEndianStructure) : # R3 - GSC FWI Manifest ('Hash', uint32_t*12), # 0x0C SHA-384 BE (Manifest w/o RSA) # 0x34 ] - + def ext_print(self) : Hash = '%0.*X' % (0x30 * 2, int.from_bytes(self.Hash, 'big')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 26, GSC FWI Manifest Hash' + col_e pt.add_row(['Name', self.Name.decode('utf-8')]) pt.add_row(['Hash', Hash]) - + return pt - + class CSE_Ext_1B(ctypes.LittleEndianStructure) : # R1 - GSC Alpha PCOD Initial Vector (not in XML, Reverse Engineered) _pack_ = 1 _fields_ = [ @@ -4851,17 +4851,17 @@ class CSE_Ext_1B(ctypes.LittleEndianStructure) : # R1 - GSC Alpha PCOD Initial V ('Nonce', uint32_t*4), # 0x08 AES-CTR 128-bit Nonce # 0x18 ] - + def ext_print(self) : Nonce = '%0.*X' % (0x10 * 2, int.from_bytes(self.Nonce, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 27, GSC Alpha PCOD Initial Vector' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) pt.add_row(['Nonce', Nonce]) - + return pt class CSE_Ext_1B_R2(ctypes.LittleEndianStructure) : # R2 - OEM Key Revocation Manifest (REVOCATION_EXTENSION) @@ -4871,18 +4871,18 @@ class CSE_Ext_1B_R2(ctypes.LittleEndianStructure) : # R2 - OEM Key Revocation Ma ('Size', uint32_t), # 0x04 # 0x08 ] - + # TODO: Should be followed by a MN2_Manifest_R2, sample required - - def ext_print(self) : + + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 27, OEM Key Revocation Manifest' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) - + return pt - + class CSE_Ext_1E(ctypes.LittleEndianStructure) : # R1 - Golden Measurements File Certificate (CERTIFICATE_EXTENSION) _pack_ = 1 _fields_ = [ @@ -4891,17 +4891,17 @@ class CSE_Ext_1E(ctypes.LittleEndianStructure) : # R1 - Golden Measurements File ('CertificateSize', uint32_t), # 0x08 # 0x0C ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 30, Golden Measurements File Certificate' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) pt.add_row(['Certificate Size', '0x%X' % self.CertificateSize]) - + return pt - + class CSE_Ext_1F(ctypes.LittleEndianStructure) : # R1 - Golden Measurements File Body (GMF_BODY_HEADER_EXTENSION) _pack_ = 1 _fields_ = [ @@ -4909,15 +4909,15 @@ class CSE_Ext_1F(ctypes.LittleEndianStructure) : # R1 - Golden Measurements File ('Size', uint32_t), # 0x04 # 0x08 ] - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 31, Golden Measurements File Body' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) pt.add_row(['Body Size (MEA)', '0x%X' % (self.Size - ctypes.sizeof(CSE_Ext_1F))]) # Calculated by MEA - + return pt class CSE_Ext_22(ctypes.LittleEndianStructure) : # R1 - Key Manifest v2 (KEY_MANIFEST_EXT, KeyManifestV2) @@ -4932,10 +4932,10 @@ class CSE_Ext_22(ctypes.LittleEndianStructure) : # R1 - Key Manifest v2 (KEY_MAN ('ModuleCount', uint8_t), # 0x1B Keys Number # 0x1C ] - - def ext_print(self) : + + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 34, Key Manifest' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -4944,7 +4944,7 @@ def ext_print(self) : pt.add_row(['Key ID', '0x%0.2X' % self.KeyID]) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) pt.add_row(['Key Count', self.ModuleCount]) - + return pt class CSE_Ext_22_Mod(ctypes.LittleEndianStructure) : # R1 - (KEY_MANIFEST_EXT_ENTRY) @@ -4957,14 +4957,14 @@ class CSE_Ext_22_Mod(ctypes.LittleEndianStructure) : # R1 - (KEY_MANIFEST_EXT_EN ('Reserved', uint8_t*3), # 0x05 # 0x08 ] - + def ext_print(self) : f1,f2 = self.get_flags() - + HashAlgorithm = cse_hash_alg[self.HashAlgorithm] if self.HashAlgorithm in cse_hash_alg else 'Unknown (%d)' % self.HashAlgorithm - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 34, Entry' + col_e pt.add_row(['Hash Usage', key_dict[self.Usage] if self.Usage in key_dict else 'Unknown (%d)' % self.Usage]) pt.add_row(['IPI Policy', ['OEM or Intel','Intel Only'][f1]]) @@ -4972,13 +4972,13 @@ def ext_print(self) : pt.add_row(['Hash Algorithm', HashAlgorithm]) pt.add_row(['Minimal SVN', self.MinimalSVN]) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) - + return pt - + def get_flags(self) : flags = CSE_Ext_0E_GetFlags() flags.asbytes = self.Flags - + return flags.b.IPIPolicy, flags.b.Reserved class CSE_Ext_23(ctypes.LittleEndianStructure) : # R1 - Signed Package Information v2 (SIGNED_PACKAGE_INFO_EXT, SignedPackageInfoV2) @@ -4996,14 +4996,14 @@ class CSE_Ext_23(ctypes.LittleEndianStructure) : # R1 - Signed Package Informati ('Reserved', uint8_t*15), # 0x19 # 0x28 ] - + # FWType & FWSKU are also used by CSE_Ext_0F_R2 & GSC_Info_FWI, remember to change them as well! - + def ext_print(self) : f1,f2,f3,f4 = self.get_flags() - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 35, Signed Package Information' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) @@ -5017,15 +5017,15 @@ def ext_print(self) : pt.add_row(['Firmware SKU Reserved', '0x%X' % f4]) pt.add_row(['Module Count', self.ModuleCount]) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) - + return pt - + def get_flags(self) : fw_type = CSE_Ext_0F_R2_GetFWType() fw_type.asbytes = self.FWType fw_sub_type = CSE_Ext_0F_R2_GetFWSKU() fw_sub_type.asbytes = self.FWSKU - + return fw_type.b.FWType, fw_type.b.Reserved, fw_sub_type.b.FWSKU, fw_sub_type.b.Reserved class CSE_Ext_23_Mod(ctypes.LittleEndianStructure) : # R1 - (SIGNED_PACKAGE_INFO_EXT_ENTRY) @@ -5039,14 +5039,14 @@ class CSE_Ext_23_Mod(ctypes.LittleEndianStructure) : # R1 - (SIGNED_PACKAGE_INFO ('MetadataHash', uint32_t*12), # 0x14 SHA-384 # 0x44 ] - + def ext_print(self) : Type = cse_mod_type[self.Type] if self.Type in cse_mod_type else 'Unknown (%d)' % self.Type HashAlgorithm = cse_hash_alg[self.HashAlgorithm] if self.HashAlgorithm in cse_hash_alg else 'Unknown (%d)' % self.HashAlgorithm MetadataHash = '%0.*X' % (0x30 * 2, int.from_bytes(self.MetadataHash, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 35, Entry' + col_e pt.add_row(['Name', self.Name.decode('utf-8')]) pt.add_row(['Type', Type]) @@ -5054,9 +5054,9 @@ def ext_print(self) : pt.add_row(['Hash Size', '0x%X' % self.HashSize]) pt.add_row(['Metadata Size', '0x%X' % self.MetadataSize]) pt.add_row(['Metadata Hash', MetadataHash]) - + return pt - + class CSE_Ext_25(ctypes.LittleEndianStructure) : # R1 - GSC DG2 OROM Unknown (not in XML, Reverse Engineered) _pack_ = 1 _fields_ = [ @@ -5065,19 +5065,19 @@ class CSE_Ext_25(ctypes.LittleEndianStructure) : # R1 - GSC DG2 OROM Unknown (no ('Unknown', uint32_t), # 0x08 # 0xC ] - + # Suspiciously, there is also an identical (but older) CSE_Ext_37 and 37 = 0x25. - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 37, GSC DG2 OROM Unknown' + col_e pt.add_row(['Tag', f'0x{self.Tag:02X}']) pt.add_row(['Size', f'0x{self.Size:X}']) pt.add_row(['Unknown', f'0x{self.Unknown:X}']) - + return pt - + class CSE_Ext_32(ctypes.LittleEndianStructure) : # R1 - SPS Platform ID (MFT_EXT_MANIFEST_PLATFORM_ID) _pack_ = 1 _fields_ = [ @@ -5088,20 +5088,20 @@ class CSE_Ext_32(ctypes.LittleEndianStructure) : # R1 - SPS Platform ID (MFT_EXT ("Reserved", uint32_t), # 0x0C # 0x10 ] - + def ext_print(self) : type_str = self.Type.decode('utf-8') platform_str = self.Platform.decode('utf-8') - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 50, CSSPS Platform ID' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) pt.add_row(['Type', 'Unknown' if type_str not in cssps_type_fw else cssps_type_fw[type_str]]) pt.add_row(['Platform', 'Unknown (%s)' % platform_str if platform_str not in cssps_platform else cssps_platform[platform_str]]) pt.add_row(['Reserved', '0x0' if self.Reserved == 0 else '0x%X' % self.Reserved]) - + return pt class CSE_Ext_37(ctypes.LittleEndianStructure) : # R1 - GSC DG2 OROM Unknown (not in XML, Reverse Engineered) @@ -5112,17 +5112,17 @@ class CSE_Ext_37(ctypes.LittleEndianStructure) : # R1 - GSC DG2 OROM Unknown (no ('Unknown', uint32_t), # 0x08 # 0xC ] - + # Suspiciously, there is also an identical (but newer) CSE_Ext_25 and 0x25 = 37. - + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'Extension 55, GSC DG2 OROM Unknown' + col_e pt.add_row(['Tag', f'0x{self.Tag:02X}']) pt.add_row(['Size', f'0x{self.Size:X}']) pt.add_row(['Unknown', f'0x{self.Unknown:X}']) - + return pt class CSE_Ext_544F4F46(ctypes.LittleEndianStructure) : # R1 - CPU Co-Signing 3K (Not in XML, Reverse Engineered) @@ -5138,14 +5138,14 @@ class CSE_Ext_544F4F46(ctypes.LittleEndianStructure) : # R1 - CPU Co-Signing 3K ('RSASignature', uint32_t*96), # 0x19C 3072-bits # 0x31C ] - - def ext_print(self) : + + def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) - + ModulusSize = self.ModulusSize * 4 RSAModulus = '%0.*X' % (ModulusSize * 2, int.from_bytes(self.RSAModulus, 'little')) RSASignature = '%0.*X' % (ModulusSize * 2, int.from_bytes(self.RSASignature, 'little')) - + pt.title = col_y + 'Extension FOOT, CPU Co-Signing' + col_e pt.add_row(['Tag', self.Tag.decode('utf-8')]) pt.add_row(['Total Size', '0x%X' % self.TotalSize]) @@ -5155,7 +5155,7 @@ def ext_print(self) : pt.add_row(['RSA Modulus', '%s [...]' % RSAModulus[:8]]) pt.add_row(['RSA Exponent', '0x%X' % self.RSAExponent]) pt.add_row(['RSA Signature', '%s [...]' % RSASignature[:8]]) - + return pt class RBE_PM_Metadata(ctypes.LittleEndianStructure) : # R1 - RBEP > rbe or FTPR > pm Module "Metadata" @@ -5175,12 +5175,12 @@ class RBE_PM_Metadata(ctypes.LittleEndianStructure) : # R1 - RBEP > rbe or FTPR ('Hash', uint32_t*8), # 0x28 SHA-256 LE # 0x48 ] - + def mod_print(self) : Hash = '%0.*X' % (0x20 * 2, int.from_bytes(self.Hash, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'RBE/PM Module "Metadata"' + col_e pt.add_row(['Unknown 0', '0x%X' % self.Unknown0]) pt.add_row(['Device ID', '0x%X' % self.DEV_ID]) @@ -5194,9 +5194,9 @@ def mod_print(self) : pt.add_row(['Unknown 1', '0x%X' % self.Unknown1]) pt.add_row(['Unknown 2', '0x%X' % self.Unknown2]) pt.add_row(['Hash', Hash]) - + return pt - + class RBE_PM_Metadata_R2(ctypes.LittleEndianStructure) : # R2 - RBEP > rbe or FTPR > pm Module "Metadata" _pack_ = 1 _fields_ = [ @@ -5208,12 +5208,12 @@ class RBE_PM_Metadata_R2(ctypes.LittleEndianStructure) : # R2 - RBEP > rbe or FT ('Hash', uint32_t*8), # 0x10 SHA-256 LE # 0x30 ] - + def mod_print(self) : Hash = '%0.*X' % (0x20 * 2, int.from_bytes(self.Hash, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'RBE/PM Module "Metadata"' + col_e pt.add_row(['Unknown 0', '0x%X' % self.Unknown0]) pt.add_row(['Device ID', '0x%X' % self.DEV_ID]) @@ -5221,9 +5221,9 @@ def mod_print(self) : pt.add_row(['Size Uncompressed', '0x%X' % self.SizeUncomp]) pt.add_row(['Size Compressed', '0x%X' % self.SizeComp]) pt.add_row(['Hash', Hash]) - + return pt - + class RBE_PM_Metadata_R3(ctypes.LittleEndianStructure) : # R3 - RBEP > rbe or FTPR > pm Module "Metadata" _pack_ = 1 _fields_ = [ @@ -5241,12 +5241,12 @@ class RBE_PM_Metadata_R3(ctypes.LittleEndianStructure) : # R3 - RBEP > rbe or FT ('Hash', uint32_t*12), # 0x28 SHA-384 LE # 0x58 ] - + def mod_print(self) : Hash = '%0.*X' % (0x30 * 2, int.from_bytes(self.Hash, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'RBE/PM Module "Metadata"' + col_e pt.add_row(['Unknown 0', '0x%X' % self.Unknown0]) pt.add_row(['Device ID', '0x%X' % self.DEV_ID]) @@ -5260,7 +5260,7 @@ def mod_print(self) : pt.add_row(['Unknown 1', '0x%X' % self.Unknown1]) pt.add_row(['Unknown 2', '0x%X' % self.Unknown2]) pt.add_row(['Hash', Hash]) - + return pt class RBE_PM_Metadata_R4(ctypes.LittleEndianStructure) : # R4 - RBEP > rbe or FTPR > pm Module "Metadata" @@ -5274,12 +5274,12 @@ class RBE_PM_Metadata_R4(ctypes.LittleEndianStructure) : # R4 - RBEP > rbe or FT ('Hash', uint32_t*12), # 0x10 SHA-384 LE # 0x40 ] - + def mod_print(self) : Hash = '%0.*X' % (0x30 * 2, int.from_bytes(self.Hash, 'little')) - + pt = ext_table(['Field', 'Value'], False, 1) - + pt.title = col_y + 'RBE/PM Module "Metadata"' + col_e pt.add_row(['Unknown 0', '0x%X' % self.Unknown0]) pt.add_row(['Device ID', '0x%X' % self.DEV_ID]) @@ -5287,9 +5287,9 @@ def mod_print(self) : pt.add_row(['Size Uncompressed', '0x%X' % self.SizeUncomp]) pt.add_row(['Size Compressed', '0x%X' % self.SizeComp]) pt.add_row(['Hash', Hash]) - + return pt - + # Unpack Engine/Graphics/Independent CSE firmware # noinspection PyUnusedLocal def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_chk_fail, cse_lt_chk_fail, cse_red_info, fdv_status, reading_msg, orom_hdr_all) : @@ -5309,26 +5309,26 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch len_orom_hdr_all = len(orom_hdr_all) config_rec_size = get_cfg_rec_size(variant,major,minor,hotfix,vol_ftbl_pl) huff_shape, huff_sym, huff_unk = cse_huffman_dictionary_load(variant, major, minor, 'error') # Load Huffman Dictionaries for rbe/pm Decompression - + # Create main Firmware Extraction Directory fw_name = 'Unpacked_' + os.path.basename(file_in) if os.path.isdir(os.path.join(out_dir, fw_name, '')) : shutil.rmtree(os.path.join(out_dir, fw_name, '')) os.mkdir(os.path.join(out_dir, fw_name, '')) - + # Print Input File Name file_pt = ext_table([], False, 1) file_pt.add_row([col_c + os.path.basename(file_in) + col_e]) print('\n%s\n' % file_pt) - + if reading_msg : print('%s\n' % reading_msg) - + # CSSPS 4.4 (WTL) is simply too terrible of a firmware to waste time and effort in order to add "proper" MEA parsing & unpacking if (variant,major,minor) == ('CSSPS',4,4): if param.cse_pause: input_col(col_m + 'Warning: CSE SPS 4.4 (Whitley) is partially supported only, errors are expected!\n' + col_e) # Debug else: print(col_m + 'Warning: CSE SPS 4.4 (Whitley) is partially supported only, errors are expected!\n' + col_e) - + # Show & Validate Flash Descriptor RSA Signature & Hash if fdv_status[0] : fdv_rsa_valid = fdv_status[1] # RSA Signature validity @@ -5336,29 +5336,29 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch fdv_hash_valid = fdv_status[3] # Hash validity fdv_print = fdv_status[4] # FDV Manifest/Extension Info fdv_path = os.path.join(out_dir, fw_name, 'CSE Flash Descriptor') # FDV Info File - + fdv_json_path = fdv_path + '.json' - + if fdv_json_path not in cse_unpack_jsons: cse_unpack_jsons[fdv_json_path] = [] - + # Print Flash Descriptor Manifest/Extension Info for index in range(0, len(fdv_print), 2) : # Only Name (index), skip Info (index + 1) if str(fdv_print[index]).startswith('FDV') : for ext in fdv_print[index + 1] : ext_str = ansi_escape.sub('', str(ext)) - + with open(fdv_path + '.txt', 'a', encoding = 'utf-8') as text_file : text_file.write('\n%s' % ext_str) - + if param.write_html: with open(fdv_path + '.html', 'a', encoding = 'utf-8') as text_file: text_file.write('\n
\n%s' % pt_html(ext)) - + if param.write_json: cse_unpack_jsons[fdv_json_path].append(pt_json(ext)) - + print(ext) # Print Flash Descriptor Manifest/Extension Info - + if 'Manifest' in ext.title : if fdv_rsa_valid : print(col_g + '\nFlash Descriptor RSA Signature is VALID\n' + col_e) @@ -5372,7 +5372,7 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch input_col(col_r + '\nFlash Descriptor RSA Signature is INVALID!\n' + col_e) # Debug else : print(col_r + '\nFlash Descriptor RSA Signature is INVALID!\n' + col_e) - + elif 'Hash' in ext.title : if fdv_hash_valid : print(col_g + '\nFlash Descriptor Hash is VALID\n' + col_e) @@ -5381,21 +5381,21 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch input_col(col_r + '\nFlash Descriptor Hash is INVALID!\n' + col_e) # Debug else : print(col_r + '\nFlash Descriptor Hash is INVALID!\n' + col_e) - + break - + # Show & Store CSE Layout Table info if cse_lt_struct : cse_lt_info = cse_lt_struct.hdr_print() cse_lt_fname = os.path.join(out_dir, fw_name, 'CSE LT [0x%0.6X]' % cse_lt_off) - + cse_lt_json_path = cse_lt_fname + '.json' - + if cse_lt_json_path not in cse_unpack_jsons: cse_unpack_jsons[cse_lt_json_path] = [] - + print('%s' % cse_lt_info) - + if not cse_lt_chk_fail : print(col_g + '\nCSE Layout Table CRC is VALID\n' + col_e) else : @@ -5403,7 +5403,7 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch input_col(col_r + '\nCSE Layout Table CRC is INVALID!\n' + col_e) # Debug else : print(col_r + '\nCSE Layout Table CRC is INVALID!\n' + col_e) - + if cse_red_info[0] and cse_red_info[1] : print(col_g + 'CSE Boot Partition Redundancy is VALID\n' + col_e) elif cse_red_info[0] and not cse_red_info[1] : @@ -5411,46 +5411,46 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch input_col(col_r + 'CSE Boot Partition Redundancy is INVALID!\n' + col_e) # Debug else : print(col_r + 'CSE Boot Partition Redundancy is INVALID!\n' + col_e) - + with open(cse_lt_fname + '.bin', 'w+b') as cse_lt_file : cse_lt_file.write(reading[cse_lt_off:cse_lt_off + cse_lt_size]) with open(cse_lt_fname + '.txt', 'a', encoding = 'utf-8') as cse_lt_file : cse_lt_file.write(ansi_escape.sub('', '\n%s' % cse_lt_info)) - + if param.write_html: with open(cse_lt_fname + '.html', 'a', encoding = 'utf-8') as cse_lt_file: cse_lt_file.write('\n
\n%s' % pt_html(cse_lt_info)) - + if param.write_json: cse_unpack_jsons[cse_lt_json_path].append(pt_json(cse_lt_info)) - + pt_dcselt.title = col_y + 'Detected %d Partition(s) at CSE LT [0x%0.6X]' % (len(cse_lt_part_all), cse_lt_off) + col_e print('%s\n' % pt_dcselt) # Local copy with different title for cse_unpack function - + cse_lt_hdr = ansi_escape.sub('', str(pt_dcselt)) with open(cse_lt_fname + '.txt', 'a', encoding = 'utf-8') as cse_lt_file : cse_lt_file.write('\n%s' % cse_lt_hdr) - + if param.write_html: with open(cse_lt_fname + '.html', 'a', encoding = 'utf-8') as cse_lt_file: cse_lt_file.write('\n
\n%s' % pt_html(pt_dcselt)) - + if param.write_json: cse_unpack_jsons[cse_lt_json_path].append(pt_json(pt_dcselt)) - + print(col_y + '--> Stored CSE Layout Table [0x%0.6X - 0x%0.6X]\n' % (cse_lt_off, cse_lt_off + cse_lt_size) + col_e) - + for part in cse_lt_part_all : part_name = part[0] part_start = part[1] part_end = part[3] part_empty = part[4] - + if not part_empty : # Skip Empty Partitions file_name = os.path.join(fw_name, 'CSE LT ' + part_name + ' [0x%0.6X].bin' % part_start) # Start offset covers any cases with duplicate name entries (CSE_Layout_Table_17) mod_fname = os.path.join(out_dir, file_name) - + with open(mod_fname, 'w+b') as part_file : part_file.write(reading[part_start:part_end]) - + print(col_y + '--> Stored CSE LT Partition "%s" [0x%0.6X - 0x%0.6X]\n' % (part_name, part_start, part_end) + col_e) - + # Parse all Flash Partition Table ($FPT) entries if len_fpt_part_all : if reading[fpt_start:fpt_start + 0x4] == b'$FPT' : @@ -5459,15 +5459,15 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch else : fpt_romb_exist = True fpt_hdr_1 = get_struct(reading, fpt_start + 0x10, get_fpt(reading, fpt_start + 0x10)[0]) - + if fpt_romb_exist : fpt_hdr_0 = get_struct(reading, fpt_start, FPT_Pre_Header) fpt_hdr_0_print = fpt_hdr_0.hdr_print_cse() print('%s\n' % fpt_hdr_0_print) - + fpt_hdr_1_print = fpt_hdr_1.hdr_print_cse() print('%s' % fpt_hdr_1_print) - + if not fpt_chk_fail : print(col_g + '\nFlash Partition Table Checksum is VALID\n' + col_e) else : @@ -5475,7 +5475,7 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch input_col(col_r + '\nFlash Partition Table Checksum is INVALID!\n' + col_e) # Debug else : print(col_r + '\nFlash Partition Table Checksum is INVALID!\n' + col_e) - + if cse_red_info[0] and cse_red_info[2] : print(col_g + 'CSE Data Partition Redundancy is VALID\n' + col_e) elif cse_red_info[0] and not cse_red_info[2] : @@ -5483,61 +5483,61 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch input_col(col_r + 'CSE Data Partition Redundancy is INVALID!\n' + col_e) # Debug else : print(col_r + 'CSE Data Partition Redundancy is INVALID!\n' + col_e) - + pt = ext_table([col_y + 'Name' + col_e, col_y + 'Start' + col_e, col_y + 'End' + col_e, col_y + 'ID' + col_e, col_y + 'Type' + col_e, col_y + 'Valid' + col_e, col_y + 'Empty' + col_e], True, 1) pt.title = col_y + 'Detected %d Partition(s) at $FPT [0x%0.6X]' % (len_fpt_part_all, fpt_start) + col_e - + for part in fpt_part_all : pt.add_row([part[0], '0x%0.6X' % part[1], '0x%0.6X' % part[2], '%0.4X' % part[3], part[4], part[5], part[6]]) # Store Partition details - + print(pt) # Show Partition details - + if cse_lt_struct : fpt_fname = os.path.join(out_dir, fw_name, 'CSE LT Data [0x%0.6X]' % fpt_start) else : fpt_fname = os.path.join(out_dir, fw_name, 'FPT [0x%0.6X]' % fpt_start) - + # Store Flash Partition Table ($FPT) Data if not cse_lt_struct : # Stored at CSE LT section too with open(fpt_fname + '.bin', 'w+b') as fpt_file : fpt_file.write(reading[fpt_start:fpt_start + 0x1000]) # $FPT size is 4K - + print(col_y + '\n--> Stored Flash Partition Table [0x%0.6X - 0x%0.6X]' % (fpt_start, fpt_start + 0x1000) + col_e) - + fpt_hdr_json_path = fpt_fname + '.json' - + if fpt_hdr_json_path not in cse_unpack_jsons: cse_unpack_jsons[fpt_hdr_json_path] = [] - + # Store Flash Partition Table ($FPT) Info # Ignore Colorama ANSI Escape Character Sequences if fpt_romb_exist : fpt_hdr_romb = ansi_escape.sub('', str(fpt_hdr_0_print)) with open(fpt_fname + '.txt', 'a', encoding = 'utf-8') as fpt_file : fpt_file.write('\n%s' % fpt_hdr_romb) - + if param.write_html: with open(fpt_fname + '.html', 'a', encoding = 'utf-8') as fpt_file: fpt_file.write('\n
\n%s' % pt_html(fpt_hdr_0_print)) - + if param.write_json: cse_unpack_jsons[fpt_hdr_json_path].append(pt_json(fpt_hdr_0_print)) - + fpt_hdr_main = ansi_escape.sub('', str(fpt_hdr_1_print)) fpt_hdr_part = ansi_escape.sub('', str(pt)) with open(fpt_fname + '.txt', 'a', encoding = 'utf-8') as fpt_file : fpt_file.write('\n%s\n%s' % (fpt_hdr_main, fpt_hdr_part)) - + if param.write_html: with open(fpt_fname + '.html', 'a', encoding = 'utf-8') as fpt_file: fpt_file.write('\n
\n%s\n
\n%s' % (pt_html(fpt_hdr_1_print), pt_html(pt))) - + if param.write_json: cse_unpack_jsons[fpt_hdr_json_path].append(pt_json(fpt_hdr_1_print)) - + # Place MFS first to validate FTPR > FTPR.man > 0x00 > Intel Configuration Hash # and get MFS FTBL ID & Record Size for FTPR/FITC Partition intl.cfg/fitc.cfg for i in range(len(fpt_part_all)) : if fpt_part_all[i][0] in ['MFS','AFSP'] : fpt_part_all.insert(0, fpt_part_all.pop(i)) break - + # Charted Partitions include fpt_start, Uncharted do not (RGN only, non-SPI) for part in fpt_part_all : part_name = part[0] @@ -5546,26 +5546,26 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch part_inid = part[3] part_type = part[4] part_empty = part[6] - + if not part_empty : # Skip Empty Partitions part_name_p = '%s %0.4X' % (part_name, part_inid) # Partition Name with Instance ID - + mod_f_path = os.path.join(out_dir, fw_name, part_name_p + ' [0x%0.6X].bin' % part_start) # Start offset covers any cases with duplicate name entries (Joule_C0-X64-Release) - + with open(mod_f_path, 'w+b') as part_file : part_file.write(reading[part_start:part_end]) - + print(col_y + '\n--> Stored $FPT %s Partition "%s" [0x%0.6X - 0x%0.6X]' % (part_type, part_name_p, part_start, part_end) + col_e) - + if part_name in ['UTOK','STKN'] : ext_print,mn2_signs,_ = ext_anl(reading[part_start:part_end], '$MN2', 0x1B, file_end, [variant,major,minor,hotfix,build,year,month,variant_p], part_name, [[],''], [[],-1,-1,-1]) # Retrieve & Store UTOK/STKN Extension Info - + # Print Manifest Info, when applicable (UTOK w/ UTFL or UTOK w/o UTFL) if man_pat.search(reading[part_start:part_end][:0x20]) : if param.cse_pause : print('\n MN2: %s' % mn2_signs[1]) # Debug print(' MEA: %s' % mn2_signs[2]) # Debug - + if mn2_signs[3] : if param.cse_pause : input_col(col_m + '\n RSA Signature of partition %s is UNKNOWN!' % part_name + col_e) # Debug @@ -5580,9 +5580,9 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch print(col_r + '\n RSA Signature of partition %s is INVALID (Known CSE Bad RSA Signature)!' % part_name + col_e) else : print(col_r + '\n RSA Signature of partition %s is INVALID!' % part_name + col_e) - + if not param.cse_verbose : print('\n%s' % ext_print[1][0]) # Print Manifest Info (already included in -ver86) - + # Print Manifest/Metadata/Key Extension Info (UTOK w/ UTFL or UTOK w/o UTFL or UTFL w/o UTOK) for index in range(0, len(ext_print), 2) : # Only Name (index), skip Info (index + 1) if str(ext_print[index]).startswith(part_name) : @@ -5590,105 +5590,105 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch for ext in ext_print[index + 1] : ext_str = ansi_escape.sub('', str(ext)) with open(mod_f_path[:-4] + '.txt', 'a', encoding = 'utf-8') as text_file : text_file.write('\n%s' % ext_str) - + if param.write_html: with open(mod_f_path[:-4] + '.html', 'a', encoding = 'utf-8') as text_file: text_file.write('\n
\n%s' % pt_html(ext)) - + if param.write_json: ext_json_path = mod_f_path[:-4] + '.json' - + if ext_json_path not in cse_unpack_jsons: cse_unpack_jsons[ext_json_path] = [] - + cse_unpack_jsons[ext_json_path].append(pt_json(ext)) if param.cse_verbose : print(ext) # Print Manifest/Metadata/Key Extension Info break - + if part_name in ['MFS','AFSP','MFSB'] : mfs_is_afs = part_name == 'AFSP' # Check if File System is MFS or AFS mfs_parsed_idx,intel_cfg_hash_mfs,mfs_info,pch_init_final,vol_ftbl_id,config_rec_size,vol_ftbl_pl \ = mfs_anl(os.path.join(mod_f_path[:-4], ''),part_start,part_end,variant,vol_ftbl_id,vol_ftbl_pl,mfs_is_afs) # Parse MFS for pt in mfs_info : mfs_txt(pt, os.path.join(mod_f_path[:-4], ''), mod_f_path[:-4], 'a', False) # Print MFS Structure Info - + # TODO: ADD CDMD Partition analysis - + if part_name == 'FITC' : fitc_anl(mod_f_path, part_start, part_end, config_rec_size, vol_ftbl_id, vol_ftbl_pl) - + if part_name == 'EFS' : _ = efs_anl(mod_f_path, part_start, part_end, vol_ftbl_id, vol_ftbl_pl) - + if part_name == 'INFO' : info_anl(mod_f_path, part_start, part_end) - + # Store RBEP > rbe and FTPR > pm "Metadata" within Module for Module w/o Metadata Hash validation if part_name in ['FTPR','RBEP'] : _,rbe_pm_mod_attr,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,fwi_iup_hashes,_ = ext_anl(reading, '$CPD', part_start, file_end, [variant,major,minor,hotfix,build,year,month,variant_p], None, [mfs_parsed_idx,intel_cfg_hash_mfs], [pch_init_final,config_rec_size,vol_ftbl_id,vol_ftbl_pl]) - + for mod in rbe_pm_mod_attr : if mod[0] in ['rbe','pm'] : rbe_pm_data = reading[mod[3]:mod[3] + mod[4]] # Store RBEP > rbe or FTPR > pm Module Compressed Huffman data try : rbe_pm_data_d, _ = cse_huffman_decompress(rbe_pm_data, mod[4], mod[5], huff_shape, huff_sym, huff_unk, 'none') # Huffman Decompress except : rbe_pm_data_d = rbe_pm_data - + rbe_pm_met_hashes = get_rbe_pm_met(rbe_pm_data_d, rbe_pm_met_hashes) - + if fwi_iup_hashes : rbe_man_hashes = fwi_iup_hashes - + # Parse all Boot Partition Description Table (BPDT/IFWI) entries if len_bpdt_part_all : if len_fpt_part_all : print() for hdr in bpdt_hdr_all : print('%s\n' % hdr) - + pt = ext_table([col_y + 'Name' + col_e, col_y + 'Type' + col_e, col_y + 'Partition' + col_e, col_y + 'ID' + col_e, col_y + 'Start' + col_e, col_y + 'End' + col_e, col_y + 'Empty' + col_e], True, 1) pt.title = col_y + 'Detected %d Partition(s) at %d BPDT(s)' % (len_bpdt_part_all, len(bpdt_hdr_all)) + col_e - + for part in bpdt_part_all : pt.add_row([part[0], '%0.2d' % part[3], part[5], '%0.4X' % part[6], '0x%0.6X' % part[1], '0x%0.6X' % part[2], part[4]]) # Store Entry details - + print('%s' % pt) # Show Entry details - + if cse_lt_struct : bpdt_fname = os.path.join(out_dir, fw_name, 'CSE LT Boot x [%d]' % len(bpdt_hdr_all)) else : bpdt_fname = os.path.join(out_dir, fw_name, 'BPDT [%d]' % len(bpdt_hdr_all)) - + # Store Boot Partition Description Table (BPDT/IFWI) Info in TXT with open(bpdt_fname + '.txt', 'a', encoding = 'utf-8') as bpdt_file : for hdr in bpdt_hdr_all : bpdt_file.write('\n%s' % ansi_escape.sub('', str(hdr))) bpdt_file.write('\n%s' % ansi_escape.sub('', str(pt))) - + # Store Boot Partition Description Table (BPDT/IFWI) Info in HTML if param.write_html: with open(bpdt_fname + '.html', 'a', encoding = 'utf-8') as bpdt_file: for hdr in bpdt_hdr_all: bpdt_file.write('\n
\n%s' % pt_html(hdr)) - + bpdt_file.write('\n
\n%s' % pt_html(pt)) - + # Store Boot Partition Description Table (BPDT/IFWI) Info in JSON if param.write_json: bpdt_json_path = bpdt_fname + '.json' - + if bpdt_json_path not in cse_unpack_jsons: cse_unpack_jsons[bpdt_json_path] = [] - + for hdr in bpdt_hdr_all: cse_unpack_jsons[bpdt_json_path].append(pt_json(hdr)) - + cse_unpack_jsons[bpdt_json_path].append(pt_json(pt)) - + # Store Boot Partition Descriptor Table (BPDT/IFWI) Data if not cse_lt_struct : # Stored at CSE LT section too with open(bpdt_fname + '.bin', 'w+b') as bpdt_file : for bpdt in bpdt_data_all : bpdt_file.write(bpdt) - + print(col_y + '\n--> Stored Boot Partition Descriptor Table(s) [%d]' % len(bpdt_hdr_all) + col_e) - + # Place MFS first to validate FTPR > FTPR.man > 0x00 > Intel Configuration Hash # and get MFS FTBL ID & Record Size for FTPR/FITC Partition intl.cfg/fitc.cfg for i in range(len(bpdt_part_all)) : if bpdt_part_all[i][0] in ['MFS','AFSP'] : bpdt_part_all.insert(0, bpdt_part_all.pop(i)) break - + for part in bpdt_part_all : part_name = part[0] part_start = part[1] @@ -5696,26 +5696,26 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch part_empty = part[4] part_order = part[5] part_inid = part[6] - + if not part_empty : # Skip Empty Partitions part_name_p = '%s %0.4X' % (part_name, part_inid) # Partition Name with Instance ID - + mod_f_path = os.path.join(out_dir, fw_name, part_name_p + ' [0x%0.6X].bin' % part_start) # Start offset covers any cases with duplicate name entries ("Unknown" etc) - + with open(mod_f_path, 'w+b') as part_file : part_file.write(reading[part_start:part_end]) - + print(col_y + '\n--> Stored BPDT %s Partition "%s" [0x%0.6X - 0x%0.6X]' % (part_order, part_name_p, part_start, part_end) + col_e) - + if part_name in ['UTOK'] : ext_print,mn2_signs,_ = ext_anl(reading[part_start:part_end], '$MN2', 0x1B, file_end, [variant,major,minor,hotfix,build,year,month,variant_p], part_name, [[],''], [[],-1,-1,-1]) # Retrieve & Store UTOK/STKN Extension Info - + # Print Manifest Info, when applicable (UTOK w/ UTFL or UTOK w/o UTFL) if man_pat.search(reading[part_start:part_end][:0x20]) : if param.cse_pause : print('\n MN2: %s' % mn2_signs[1]) # Debug print(' MEA: %s' % mn2_signs[2]) # Debug - + if mn2_signs[3] : if param.cse_pause : input_col(col_m + '\n RSA Signature of partition %s is UNKNOWN!' % part_name + col_e) # Debug @@ -5730,9 +5730,9 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch print(col_r + '\n RSA Signature of partition %s is INVALID (Known CSE Bad RSA Signature)!' % part_name + col_e) else : print(col_r + '\n RSA Signature of partition %s is INVALID!' % part_name + col_e) - + if not param.cse_verbose : print('\n%s' % ext_print[1][0]) # Print Manifest Info (already included in -ver86) - + # Print Manifest/Metadata/Key Extension Info (UTOK w/ UTFL or UTOK w/o UTFL or UTFL w/o UTOK) for index in range(0, len(ext_print), 2) : # Only Name (index), skip Info (index + 1) if str(ext_print[index]).startswith(part_name) : @@ -5740,51 +5740,51 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch for ext in ext_print[index + 1] : ext_str = ansi_escape.sub('', str(ext)) with open(mod_f_path[:-4] + '.txt', 'a', encoding = 'utf-8') as text_file : text_file.write('\n%s' % ext_str) - + if param.write_html: with open(mod_f_path[:-4] + '.html', 'a', encoding = 'utf-8') as text_file: text_file.write('\n
\n%s' % pt_html(ext)) - + if param.write_json: ext_json_path = mod_f_path[:-4] + '.json' - + if ext_json_path not in cse_unpack_jsons: cse_unpack_jsons[ext_json_path] = [] - + cse_unpack_jsons[ext_json_path].append(pt_json(ext)) - + if param.cse_verbose : print(ext) # Print Manifest/Metadata/Key Extension Info break - + if part_name in ['MFS','AFSP','MFSB'] : mfs_is_afs = part_name == 'AFSP' # Check if File System is MFS or AFS mfs_parsed_idx, intel_cfg_hash_mfs, mfs_info, pch_init_final,vol_ftbl_id,config_rec_size,vol_ftbl_pl \ = mfs_anl(os.path.join(mod_f_path[:-4], ''),part_start,part_end,variant,vol_ftbl_id,vol_ftbl_pl,mfs_is_afs) # Parse MFS - for pt in mfs_info : mfs_txt(pt, os.path.join(mod_f_path[:-4], ''), mod_f_path[:-4], 'a', False) # Print MFS Structure Info - + for pt in mfs_info : mfs_txt(pt, os.path.join(mod_f_path[:-4], ''), mod_f_path[:-4], 'a', False) # Print MFS Structure Info + # TODO: ADD CDMD Partition analysis - + if part_name == 'FITC' : fitc_anl(mod_f_path, part_start, part_end, config_rec_size, vol_ftbl_id, vol_ftbl_pl) - + if part_name == 'EFS' : _ = efs_anl(mod_f_path, part_start, part_end, vol_ftbl_id, vol_ftbl_pl) - + if part_name == 'INFO' : info_anl(mod_f_path, part_start, part_end) - + # Store RBEP > rbe and FTPR > pm "Metadata" within Module for Module w/o Metadata Hash validation if part_name in ['FTPR','RBEP'] : _,rbe_pm_mod_attr,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,fwi_iup_hashes,_ = ext_anl(reading, '$CPD', part_start, file_end, [variant,major,minor,hotfix,build,year,month,variant_p], None, [mfs_parsed_idx,intel_cfg_hash_mfs], [pch_init_final,config_rec_size,vol_ftbl_id,vol_ftbl_pl]) - + for mod in rbe_pm_mod_attr : if mod[0] in ['rbe','pm'] : rbe_pm_data = reading[mod[3]:mod[3] + mod[4]] # Store RBEP > rbe or FTPR > pm Module Compressed Huffman data try : rbe_pm_data_d, _ = cse_huffman_decompress(rbe_pm_data, mod[4], mod[5], huff_shape, huff_sym, huff_unk, 'none') # Huffman Decompress except : rbe_pm_data_d = rbe_pm_data - + rbe_pm_met_hashes = get_rbe_pm_met(rbe_pm_data_d, rbe_pm_met_hashes) - + if fwi_iup_hashes : rbe_man_hashes = fwi_iup_hashes - + # Print all Graphics System Controller Option ROM (OROM) entries if len_orom_hdr_all : if len_fpt_part_all or len_bpdt_part_all : print() @@ -5792,30 +5792,30 @@ def cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start, fpt_ch for hdr in orom_hdr_all : print('%s\n' % hdr) with open(orom_fname, 'a', encoding = 'utf-8') as orom_file : orom_file.write('%s\n' % ansi_escape.sub('', str(hdr))) - + # Parse all Code Partition Directory ($CPD) ranges/entries. Separate $CPD from $FPT/BPDT to avoid duplicate FTUP/NFTP ($FPT) issue. for cpdrange in list(cpd_pat.finditer(reading)) : # Store any Platform Data (PDR) Flash Descriptor Regions with Code Partition Directory ($CPD) structure (not in $FPT or BPDT) if fd_pdr_rgn_exist and reading[cpdrange.start() + 0xC:cpdrange.start() + 0x10] == b'PDRP' : mod_f_path = os.path.join(out_dir, fw_name, 'PDRP 0000 [0x%0.6X].bin' % cpdrange.start()) # Start offset covers any cases with multiple PDR (not POR, just in case) with open(mod_f_path, 'w+b') as part_file : part_file.write(reading[cpdrange.start():cpdrange.start() + pdr_fd_size]) - + print(col_y + '\n--> Stored Flash Descriptor Region "PDRP 0000" [0x%0.6X - 0x%0.6X]' % (cpdrange.start(), cpdrange.start() + pdr_fd_size) + col_e) - + cpd_offset_e,cpd_mod_attr_e,cpd_ext_attr_e,_,ext12_info,ext_print,_,_,ext_phval,ext_dnx_val,_,_,cpd_mn2_info,ext_iunit_val,_,_,gmf_blob_info,_,_ \ = ext_anl(reading, '$CPD', cpdrange.start(), file_end, [variant,major,minor,hotfix,build,year,month,variant_p], None, [mfs_parsed_idx,intel_cfg_hash_mfs], [pch_init_final,config_rec_size,vol_ftbl_id,vol_ftbl_pl]) - + if cse_lt_struct or len_fpt_part_all or len_bpdt_part_all or len_orom_hdr_all : print() # For visual purposes before $CPD info is shown - + rbe_pm_met_valid = mod_anl(cpd_offset_e, cpd_mod_attr_e, cpd_ext_attr_e, fw_name, ext_print, ext_phval, ext_dnx_val, ext_iunit_val, rbe_pm_met_hashes, rbe_pm_met_valid, ext12_info, vol_ftbl_id, config_rec_size, gmf_blob_info, vol_ftbl_pl, cpd_mn2_info, rbe_man_hashes) - + # Store all RBEP > rbe and FTPR > pm "Metadata" leftover Hashes for Huffman symbol reversing # The leftover Hashes for Huffman symbol reversing should be n+* if NFTP > pavp and/or PCOD > PCOD are encrypted if param.bypass : for l_hash in [l_hash for l_hash in rbe_pm_met_hashes if l_hash not in rbe_pm_met_valid] : print(l_hash) # Debug/Research - + if param.write_json: for cse_unpack_json_path, cse_unpack_json_lists in cse_unpack_jsons.items(): with open(cse_unpack_json_path, 'w', encoding='utf-8') as jo: @@ -5887,37 +5887,37 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man mfs_parsed_idx,intel_cfg_hash_mfs = mfs_idx_cfg pch_init_final,config_rec_size,vol_ftbl_id,vol_ftbl_pl = pch_init_input buffer_len = len(buffer) - + if input_type.startswith('$MN2') : start_man_match = input_offset end_man_match = start_man_match + 0x5 # .$MN2 - + # Scan backwards for $CPD (max $CPD size = 0x2000, .$MN2 Tag starts at 0x1B, works with both RGN --> $FPT & UPD --> 0x0) for offset in range(start_man_match + 2, start_man_match + 2 - 0x201D, -4) : # Search from MN2 (no .$) to find CPD (no $) at 1, before loop break at 0 if b'$CPD' in buffer[offset - 1:offset - 1 + 4] : cpd_offset = offset - 1 # Adjust $CPD to 0 (offset - 1 = 1 - 1 = 0) break # Stop at first detected $CPD - + elif input_type.startswith('$CPD') : cpd_offset = input_offset - + # Scan forward for .$MN2 (max $CPD size = 0x2000, .$MN2 Tag ends at 0x20, works with both RGN --> $FPT & UPD --> 0x0) mn2_pat = re.compile(br'\x00\$MN2').search(buffer[cpd_offset:cpd_offset + 0x2020]) # .$MN2 detection, 0x00 for extra sanity check if mn2_pat is not None : (start_man_match, end_man_match) = mn2_pat.span() start_man_match += cpd_offset end_man_match += cpd_offset - + # $MN2 existence not mandatory if start_man_match != -1 : mn2_hdr = get_struct(buffer, start_man_match - 0x1B, get_manifest(buffer, start_man_match - 0x1B)) - + if mn2_hdr.Tag == b'$MN2' : # Sanity Check (also UTOK w/o Manifest) mn2_offset = start_man_match - 0x1B # $MN2 Manifest Offset mn2_size = mn2_hdr.Size * 4 # $MN2 Manifest Size mn2_date = '%0.4X-%0.2X-%0.2X' % (mn2_hdr.Year,mn2_hdr.Month,mn2_hdr.Day) mn2_hdr_print = mn2_hdr.hdr_print_cse() - + mn2_rsa_key_len = mn2_hdr.PublicKeySize * 4 # RSA Key/Signature Length mn2_rsa_exp_len = mn2_hdr.ExponentSize * 4 # RSA Exponent Length mn2_rsa_key_start = mn2_offset + 0x80 # RSA Public Key Start @@ -5930,9 +5930,9 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man mn2_rsa_sig_hash = get_hash(mn2_rsa_sig_data, 0x20) # SHA-256 of RSA Signature Data mn2_wo_rsa_data = buffer[mn2_offset:mn2_rsa_key_start] + buffer[mn2_rsa_sig_end:mn2_offset + mn2_size] # $MN2 Manifest w/o RSA Block mn2_wo_rsa_hashes = [get_hash(mn2_wo_rsa_data, 0x30), get_hash(mn2_wo_rsa_data, 0x20)] # Hashes of $MN2 Manifest w/o RSA Block (RBEP) - + mn2_flags_pvbit,_,_,_,mn2_flags_debug = mn2_hdr.get_flags() - + if hasattr(mn2_hdr, 'MEU_Major') and mn2_hdr.MEU_Major not in (0,0xFFFF) : cpd_mn2_info = [mn2_hdr.Major, mn2_hdr.Minor, mn2_hdr.Hotfix, mn2_hdr.Build, ['Production','Debug'][mn2_flags_debug], mn2_rsa_key_hash, mn2_rsa_sig_hash, mn2_date, mn2_hdr.SVN, mn2_flags_pvbit, mn2_hdr.MEU_Major, mn2_hdr.MEU_Minor, @@ -5941,31 +5941,31 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man cpd_mn2_info = [mn2_hdr.Major, mn2_hdr.Minor, mn2_hdr.Hotfix, mn2_hdr.Build, ['Production','Debug'][mn2_flags_debug], mn2_rsa_key_hash, mn2_rsa_sig_hash, mn2_date, mn2_hdr.SVN, mn2_flags_pvbit, 0, 0, 0, 0, mn2_wo_rsa_hashes, mn2_hdr, start_man_match, end_man_match] - + # It is sometimes necessary to use the analyzed $MN2 info instead of the external ftpr_var_ver parameter anl_major,anl_minor,anl_hotfix,anl_build = cpd_mn2_info[0],cpd_mn2_info[1],cpd_mn2_info[2],cpd_mn2_info[3] anl_meu_major,anl_meu_minor,anl_meu_hotfix,anl_meu_build = cpd_mn2_info[10],cpd_mn2_info[11],cpd_mn2_info[12],cpd_mn2_info[13] - + mn2_sigs = rsa_sig_val(mn2_hdr, buffer, mn2_offset) # For each Partition else : mn2_hdr = None start_man_match = -1 - + # $CPD detected if cpd_offset > -1 : cpd_hdr_struct, cpd_hdr_size = get_cpd(buffer, cpd_offset) cpd_hdr = get_struct(buffer, cpd_offset, cpd_hdr_struct) cpd_num = cpd_entry_num_fix(buffer, cpd_offset, cpd_hdr.NumModules, cpd_hdr_size) cpd_name = cpd_hdr.PartitionName.strip(b'\x00').decode('utf-8') - + # Validate $CPD Checksum, skip at special _Stage1 mode (Variant/fptemp) to not see duplicate messages if not input_type.endswith('_Stage1') : cpd_chk_ok,cpd_chk_fw,cpd_chk_exp,cpd_chk_rslt = cpd_chk(buffer[cpd_offset:cpd_offset + cpd_hdr_size + cpd_num * 0x18], variant, major) cpd_chk_info = [cpd_chk_ok, cpd_chk_rslt] # Store $CPD Checksum Validity & Values - + if not cpd_chk_ok : cse_anl_err(col_r + 'Error: Wrong $CPD "%s" Checksum 0x%0.2X, expected 0x%0.2X' % (cpd_name, cpd_chk_fw, cpd_chk_exp) + col_e, cpd_chk_rslt) - + # Stage 1: Store $CPD Entry names to detect Partition attributes for MEA for entry in range(0, cpd_num) : cpd_entry_hdr = get_struct(buffer, cpd_offset + cpd_hdr_size + entry * 0x18, CPD_Entry) @@ -5975,31 +5975,31 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man cpd_entry_res0 = cpd_entry_hdr.Reserved cpd_entry_offset,cpd_entry_huff,cpd_entry_res1 = cpd_entry_hdr.get_flags() cpd_entry_offset += cpd_offset # Adjust $CPD Entry Offset based on $CPD start - + # Determine if Entry is Empty/Missing entry_data = buffer[cpd_entry_offset:cpd_entry_offset + cpd_entry_size] entry_empty = 1 if entry_data in (b'', b'\xFF' * cpd_entry_size) or cpd_entry_offset >= file_end else 0 - + # Detect if FTPR Partition is FWUpdate-customized to skip potential $FPT false positive at fptemp module if cpd_entry_name == 'fptemp' and entry_empty == 0 : # FWUpdate -save (fptemp not empty) fptemp_info = [True, cpd_entry_offset, cpd_entry_offset + cpd_entry_size] - + # Gathered any info for special _Stage1 mode (cpd_mod_names, fptemp_info) if input_type.endswith('_Stage1') : continue - + # Check if $CPD Entry Reserved field is zero, skip at special _Stage1 mode if (cpd_entry_res0,cpd_entry_res1) != (0,0) and not input_type.endswith('_Stage1') : cse_anl_err(col_m + 'Warning: Detected $CPD Entry with non-zero Reserved field at %s > %s' % (cpd_name, cpd_entry_name) + col_e, None) - + cpd_wo_met_info.append([cpd_entry_name,cpd_entry_offset,cpd_entry_size,cpd_entry_huff,cpd_entry_res0,cpd_entry_res1,entry_empty]) - + # Detect if FTPR Partition includes MFS Intel Configuration (intl.cfg) to validate FTPR Extension 0x00 Hash at Stage 2 # The FTPR intl.cfg Hash is stored separately from $FPT MFS Low Level File 6 Hash to validate both at Stage 2 (CSTXE, CSME 12 Alpha) if cpd_entry_name == 'intl.cfg' and entry_empty == 0 : intel_cfg_ftpr = True # Detected FTPR > intl.cfg module intel_cfg_data = buffer[cpd_entry_offset:cpd_entry_offset + cpd_entry_size] # FTPR > intl.cfg Contents intel_cfg_hash_ftpr = [get_hash(intel_cfg_data, 0x20), get_hash(intel_cfg_data, 0x30)] # Store FTPR MFS Intel Configuration Hashes - + # For Platform/Stepping analysis via the Chipset Initialization Table, prefer the FTPR Low Level File 6 (intl.cfg) # instead of the one from MFS, when possible (i.e. MFS & FTPR) or necessary (i.e. FTPR only, MFS empty). if not param.cse_unpack : @@ -6009,35 +6009,35 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man pch_init_final = pch_init_anl(pch_init_info) # Parse MFS Initialization Tables and store their Platforms/Steppings except : cse_anl_err(col_r + 'Error: Failed to analyze MFS Low Level File 6 (Intel Configuration) at %s > %s' % (cpd_name, cpd_entry_name) + col_e, None) - + # Detect if FTPR Partition is FIT/OEM-customized to skip Hash check at Stages 2 & 4 if cpd_entry_name == 'fitc.cfg' and entry_empty == 0 : oem_config = True # FIT OEM Configuration if cpd_entry_name == 'oem.key' and entry_empty == 0 and not bccb_pat.search(entry_data[:0x50]) : oem_signed = True # OEM RSA Signature, skip Intel "OEM" Placeholder Keys with VEN_ID 0xBCCB - + # Detect Recovery Image Partition (RCIP) if cpd_name == 'RCIP' : # Get DNX version 1 (R1 SHA-256) or 2 (R2 SHA-256, R3 SHA-384) if cpd_entry_name == 'version' : dnx_version = int.from_bytes(buffer[cpd_entry_offset:cpd_entry_offset + 0x4], 'little') - + # Get DNX R2 Hash Array offset elif cpd_entry_name == 'hash.array' : dnx_hash_arr_off = cpd_entry_offset - + # Get DNX R1/R2 RCIP IFWI offset elif cpd_entry_name == 'rcipifwi' : dnx_rcip_off = cpd_entry_offset dnx_rcip_len = cpd_entry_size # RCIP IFWI is uncompressed - + # Return only $CPD Module Names & fptemp info for special _Stage1 mode if input_type.endswith('_Stage1') : return cpd_mod_names, fptemp_info - + # Sort $CPD Entry Info based on Offset in ascending order cpd_wo_met_info = sorted(cpd_wo_met_info, key=lambda entry: entry[1]) cpd_wo_met_back = cpd_wo_met_info # Backup for adjustments validation - + # $CPD not found but special _Stage1 mode requires it, return null info elif input_type.endswith('_Stage1') : return cpd_mod_names, fptemp_info - + # Stage 2: Analyze Manifest & Metadata (must be before Module analysis) # Set cpd_num = 1 to analyze single $MN2 w/o $CPD (CSSPS MFS Low Level File 9) for entry in range(0, 1 if single_man_name else cpd_num) : @@ -6045,7 +6045,7 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man if not single_man_name : cpd_entry_hdr = get_struct(buffer, cpd_offset + cpd_hdr_size + entry * 0x18, CPD_Entry) cpd_mod_off,_,_ = cpd_entry_hdr.get_flags() - + cpd_entry_offset = cpd_offset + cpd_mod_off cpd_entry_size = cpd_entry_hdr.Size # Uncompressed only cpd_entry_name = cpd_entry_hdr.Name @@ -6058,59 +6058,59 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man dnx_rcip_off = 0 dnx_rcip_len = 0 cpd_chk_info = [True,['','']] - + ext_print_temp = [] cpd_ext_offset = 0 loop_break = 0 entry_empty = 0 - + if b'.man' in cpd_entry_name or b'.met' in cpd_entry_name or (single_man_name and start_man_match != -1) : # Set initial CSE Extension Offset if (b'.man' in cpd_entry_name or single_man_name) and start_man_match != -1 : cpd_ext_offset = cpd_entry_offset + mn2_hdr.HeaderLength * 4 # Skip $MN2 at .man elif b'.met' in cpd_entry_name : cpd_ext_offset = cpd_entry_offset # Metadata is always Uncompressed - + # Analyze all Manifest & Metadata Extensions ext_tag = int.from_bytes(buffer[cpd_ext_offset:cpd_ext_offset + 0x4], 'little') # Initial Extension Tag - + ext_print.append(cpd_entry_name.decode('utf-8')) # Store Manifest/Metadata name - + while True : # Parse all CSE Extensions and break at Manifest/Metadata end - + # Break loop just in case it becomes infinite loop_break += 1 if loop_break > 100 : cse_anl_err(col_r + 'Error: Forced CSE Extension Analysis break after 100 loops at %s > %s!' % (cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) break - + # Determine if Entry is Empty/Missing entry_data = buffer[cpd_entry_offset:cpd_entry_offset + cpd_entry_size] if entry_data in (b'', b'\xFF' * cpd_entry_size) or cpd_entry_offset >= file_end : entry_empty = 1 - + # Detect unknown CSE Extension & notify user if ext_tag not in ext_tag_all : cse_anl_err(col_r + 'Error: Detected unknown CSE Extension 0x%0.2X at %s > %s!\n Some modules may not be detected without adding 0x%0.2X support!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8'), ext_tag) + col_e, None) - + # Determine Extension Size & End Offset if ext_tag == 0x544F4F46 : cpd_ext_size = int.from_bytes(buffer[cpd_ext_offset + 0x8:cpd_ext_offset + 0xC], 'little') else : cpd_ext_size = int.from_bytes(buffer[cpd_ext_offset + 0x4:cpd_ext_offset + 0x8], 'little') cpd_ext_end = cpd_ext_offset + cpd_ext_size - + # Detect CSE Extension with null size & break loop if cpd_ext_size == 0 : cse_anl_err(col_r + 'Error: Detected CSE Extension 0x%0.2X with null size at %s > %s!\n Possible false positive, skipping rest of Manifest/Metadata!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) break # Break loop because it will become infinite with null size - + # Detect CSE Extension data overflow & notify user if entry_empty == 0 and (cpd_ext_end > cpd_entry_offset + cpd_entry_size) : # Manifest/Metadata Entry overflow cse_anl_err(col_r + 'Error: Detected CSE Extension 0x%0.2X data overflow at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) - + hdr_rev_tag = '' # CSE Extension Header Revision Tag mod_rev_tag = '' # CSE Extension Module Revision Tag - + if (variant,major) in [('GSC',100),('GSC',101)] or (variant_p,anl_meu_major) in [('PMC',100),('OROM',100),('PMC',101),('OROM',101)] : if ext_tag in ext_tag_rev_hdr_gsc100 : hdr_rev_tag = ext_tag_rev_hdr_gsc100[ext_tag] if ext_tag in ext_tag_rev_mod_gsc100 : mod_rev_tag = ext_tag_rev_mod_gsc100[ext_tag] @@ -6128,27 +6128,27 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man if ext_tag in ext_tag_rev_mod_cssps5 : mod_rev_tag = ext_tag_rev_mod_cssps5[ext_tag] else : pass # These CSE use the original Header/Module Structures - + ext_dict_name = 'CSE_Ext_%0.2X%s' % (ext_tag, hdr_rev_tag) ext_struct_name = ext_dict[ext_dict_name] if ext_dict_name in ext_dict else None ext_dict_mod = 'CSE_Ext_%0.2X_Mod%s' % (ext_tag, mod_rev_tag) ext_struct_mod = ext_dict[ext_dict_mod] if ext_dict_mod in ext_dict else None - + ext_length = ctypes.sizeof(ext_struct_name) if ext_struct_name else 0 mod_length = ctypes.sizeof(ext_struct_mod) if ext_struct_mod else 0 cpd_mod_offset = cpd_ext_offset + ext_length cpd_mod_area = cpd_ext_end - cpd_mod_offset - + ext_hdr_extra = ['CSE_Ext_0C'] # Extensions which require extra get_struct parameters - + # Detect CSE Extension without Modules different size & notify user if (ext_tag,hdr_rev_tag) in ext_tag_mod_none and cpd_ext_size != ext_length : cse_anl_err(col_r + 'Error: Detected CSE Extension 0x%0.2X w/o Modules size difference at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) - + # Check if Module data is divisible by Module size if mod_length and cpd_mod_area % mod_length != 0 : cse_anl_err(col_r + 'Error: Detected non-divisible CSE Extension 0x%0.2X at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) - + # Get Extension Structure when no extra get_struct parameters are required. The Extension Info storing for # Extensions which require extra get_struct parameters must occur after their own Structure initialization. if ext_struct_name and ext_dict_name not in ext_hdr_extra : @@ -6156,27 +6156,27 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man ext_print_temp.append(ext_hdr.ext_print()) # Store Extension Info for non ext_hdr_extra Extensions else : ext_hdr = None # Get Extension Structure for ext_hdr_extra Extensions later - + special_mod_anl = False # Mark all Extension Modules which require special/unique processing - + if ext_tag == 0x0 : intel_cfg_hash_len = len(ext_hdr.IMGDefaultHash) * 4 intel_cfg_hash_ext = '%0.*X' % (intel_cfg_hash_len * 2, int.from_bytes(ext_hdr.IMGDefaultHash, 'little')) - + # Validate CSME/CSSPS MFS Intel Configuration (Low Level File 6) Hash at Non-Initialized/Non-FWUpdated MFS. Ignore at (CS)SPS with different FTPR & OPR Versions. if intel_cfg_hash_mfs and mfs_found and mfs_parsed_idx and not any(idx in mfs_parsed_idx for idx in [0,1,2,3,4,5,8]) and not sps_extr_ignore and intel_cfg_hash_ext not in intel_cfg_hash_mfs : cse_anl_err(col_r + 'Error: Detected CSE Extension 0x%0.2X with wrong $FPT MFS Intel Configuration Hash at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, [intel_cfg_hash_ext,intel_cfg_hash_mfs]) - + # Validate CSTXE or CSME 12 Alpha MFS/AFS Intel Configuration (FTPR > intl.cfg) Hash if intel_cfg_hash_ftpr and intel_cfg_ftpr and intel_cfg_hash_ext not in intel_cfg_hash_ftpr : cse_anl_err(col_r + 'Error: Detected CSE Extension 0x%0.2X with wrong FTPR MFS Intel Configuration Hash at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, [intel_cfg_hash_ext,intel_cfg_hash_ftpr]) - + # Detect unexpected inability to validate Non-Initialized/Non-FWUpdated $FPT (Low Level File 6) or FTPR (intl.cfg) MFS/AFS Intel Configuration Hash if ((mfs_found and mfs_parsed_idx and 6 in mfs_parsed_idx and not any(idx in mfs_parsed_idx for idx in [0,1,2,3,4,5,8]) and not intel_cfg_hash_mfs) or (intel_cfg_ftpr and not intel_cfg_hash_ftpr)) and not param.cse_unpack : cse_anl_err(col_m + 'Warning: Could not validate CSE Extension 0x%0.2X MFS Intel Configuration Hash at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) - + elif ext_tag == 0x3 : ext_pname = ext_hdr.PartitionName.decode('utf-8') # Partition Name ext_psize = ext_hdr.PartitionSize # Partition Size @@ -6186,19 +6186,19 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man in_id = ext_hdr.InstanceID # LOCL/WCOD identifier if gmf_blob_info : gmf_blob_info[1] = in_id # Fill GMF Blobs Partition Instance ID (Not POR, just in case) special_mod_anl = True # CSE_Ext_03 requires special/unique Module processing - + # Verify Partition Hash ($CPD - $MN2 + Data) if start_man_match != -1 and not single_man_name and not oem_config and not oem_signed and not fptemp_info[0] : mea_pdata = buffer[cpd_offset:mn2_offset] + buffer[mn2_offset + mn2_size:cpd_offset + ext_psize] # $CPD + Data (no $MN2) mea_phash = get_hash(mea_pdata, len(ext_phash) // 2) # Hash for CSE_Ext_03 - + ext_phval = [True, ext_phash == mea_phash, ext_phash, mea_phash] if not ext_phval[1] and int(ext_phval[2], 16) != 0 : if (variant,major,minor,ext_psize) == ('CSME',11,8,0x88000) : (ext_phash, mea_phash) = ('IGNORE', 'IGNORE') # CSME 11.8 Slim Partition Hash is always wrong, ignore elif (variant,major) == ('CSSPS',1) : (ext_phash, mea_phash) = ('IGNORE', 'IGNORE') # CSSPS 1/IGN Partition Hash is always wrong, ignore elif (variant,major,minor) == ('CSSPS',4,2) : (ext_phash, mea_phash) = ('IGNORE', 'IGNORE') # CSSPS 4.2/IGN Partition Hash is always wrong, ignore cse_anl_err(col_r + 'Error: Detected CSE Extension 0x%0.2X with wrong Partition Hash at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, [ext_phash,mea_phash]) - + while cpd_mod_offset < cpd_ext_end : mod_hdr = get_struct(buffer, cpd_mod_offset, ext_struct_mod) met_name = mod_hdr.Name.decode('utf-8') + '.met' @@ -6206,13 +6206,13 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man if met_name.endswith('.met.met') : met_name = met_name[:-4] met_hash_len = len(mod_hdr.MetadataHash) * 4 # Metadata Hash Length met_hash = '%0.*X' % (met_hash_len * 2, int.from_bytes(mod_hdr.MetadataHash, 'little')) # Metadata Hash - + cpd_ext_hash.append([cpd_name, met_name, met_hash]) - + ext_print_temp.append(mod_hdr.ext_print()) - + cpd_mod_offset += mod_length - + elif ext_tag == 0xA : mod_comp_type = ext_hdr.Compression # Metadata's Module Compression Type (0-2) mod_encr_type = ext_hdr.Encryption # Metadata's Module Encryption Type (0-1) @@ -6220,40 +6220,40 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man mod_uncomp_size = ext_hdr.SizeUncomp # Metadata's Module Uncompressed Size (equal to $CPD Entry's Module Size) mod_hash_len = len(ext_hdr.Hash) * 4 # Metadata's Module Hash Length mod_hash = '%0.*X' % (mod_hash_len * 2, int.from_bytes(ext_hdr.Hash, 'little')) # Metadata's Module Hash - + cpd_mod_attr.append([cpd_entry_name.decode('utf-8')[:-4], mod_comp_type, mod_encr_type, 0, mod_comp_size, mod_uncomp_size, 0, mod_hash, cpd_name, 0, mn2_sigs, cpd_offset, cpd_chk_info]) - + elif ext_tag == 0xC : ext_hdr = get_struct(buffer, cpd_ext_offset, ext_struct_name, ftpr_var_ver) ext_print_temp.append(ext_hdr.ext_print()) # Store Extension 0C Info, requires extra get_struct parameters - + _,fw_0C_sku1,fw_0C_lbg,_,_,fw_0C_sku2,_,_ = ext_hdr.get_flags() fw_0C_sku0 = ext_hdr.get_skuc() # SKU Capabilities - + # Check if SKU Capabilities Reserved are actually reserved skuc_res_len = len(ext_hdr.FWSKUCapsRes) * 4 skuc_res = '%0.*X' % (skuc_res_len * 2, int.from_bytes(ext_hdr.FWSKUCapsRes, 'little')) if skuc_res != 'FF' * 28 : cse_anl_err(col_r + 'Error: Detected CSE Extension 0x%0.2X with new SKU Capabilities at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) - + ext12_sku1 = ext12_fw_sku[fw_0C_sku1] if fw_0C_sku1 in ext12_fw_sku else ('Unknown','UNK') # Firmware SKU (COR, CON, SLM, SVR) - + ext12_info = [fw_0C_sku0, ext12_sku1, fw_0C_lbg, fw_0C_sku2] - + elif ext_tag == 0xF : if ext_pname == '' : ext_pname = ext_hdr.PartitionName.decode('utf-8') # Partition Name (prefer CSE_Ext_03) if vcn == -1 : vcn = ext_hdr.VCN # Version Control Number (prefer CSE_Ext_03) arb_svn = ext_hdr.ARBSVN # FPF Anti-Rollback (ARB) Security Version Number ext15_info[0] = arb_svn # Adjust CSE Extension 15 Info with ARB SVN special_mod_anl = True # CSE_Ext_0F requires special/unique Module processing - + if hasattr(ext_hdr, 'NVMCompatibility') : # Parse CSE_Ext_0F_R2 fields ext15_type = ext15_fw_type[ext_hdr.FWType] if ext_hdr.FWType in ext15_fw_type else 'Unknown' # Firmware Type (Client, SPS etc) ext15_sku = ext15_fw_sku[ext_hdr.FWSKU] if ext_hdr.FWSKU in ext15_fw_sku else ('Unknown','UNK') # Firmware SKU (CON, COR, SLM, LIT, SVR etc) ext15_nvm = ext15_nvm_type[ext_hdr.NVMCompatibility] if ext_hdr.NVMCompatibility in ext15_nvm_type else 'Unknown' # NVM Compatibility (SPI, UFS etc) - + ext15_info[1:4] = ext15_type, ext15_sku, ext15_nvm # Adjust CSE Extension 15 Info with FW Type, FW SKU, NVM Type - + while cpd_mod_offset < cpd_ext_end : mod_hdr = get_struct(buffer, cpd_mod_offset, ext_struct_mod) met_name = mod_hdr.Name.decode('utf-8') + '.met' @@ -6261,22 +6261,22 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man if met_name.endswith('.met.met') : met_name = met_name[:-4] met_hash_len = len(mod_hdr.MetadataHash) * 4 # Metadata Hash Length met_hash = '%0.*X' % (met_hash_len * 2, int.from_bytes(mod_hdr.MetadataHash, 'little')) # Metadata Hash - + cpd_ext_hash.append([cpd_name, met_name, met_hash]) - + ext_print_temp.append(mod_hdr.ext_print()) - + cpd_mod_offset += mod_length - + elif ext_tag == 0x10 : CSE_Ext_10_Chunk_count = divmod(cpd_mod_area, mod_length) # Number of iUnit Entries/Chunks CSE_Ext_10_iUnit_offset = cpd_ext_end # iUnit Module data begin after iUnit Metadata while buffer[CSE_Ext_10_iUnit_offset] == 0xFF : CSE_Ext_10_iUnit_offset += 1 # Skip padding before iUnit Module data - + # Check if iUnit Entries/Chunks Area is divisible by Entry/Chunk Size size if CSE_Ext_10_Chunk_count[1] != 0 : cse_anl_err(col_r + 'Error: Detected non-divisible CSE Extension 0x%0.2X at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) - + # Parse all iUnit Module Chunks via their Extension Metadata for chunk in range(CSE_Ext_10_Chunk_count[0]) : chunk_hdr = get_struct(buffer, cpd_mod_offset + chunk * mod_length, ext_struct_mod) # iUnit Chunk Metadata @@ -6287,20 +6287,20 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man iunit_chunk_hash_mea = get_hash(buffer[iunit_chunk_start:iunit_chunk_start + iunit_chunk_size], len(iunit_chunk_hash_ext) // 2) # iUnit Module Chunk MEA Hash iunit_chunk_valid.append(iunit_chunk_hash_mea == iunit_chunk_hash_ext) # Store iUnit Module Chunk(s) Hash validation results iunit_chunk_start += iunit_chunk_size # Next iUnit Module Chunk starts at the previous plus its size - + # Verify that all iUnit Module data Chunks are valid if iunit_chunk_valid == [True] * len(iunit_chunk_valid) : ext_iunit_val[0] = True - + CSE_Ext_10_iUnit_size = iunit_chunk_start - CSE_Ext_10_iUnit_offset # iUnit Module full Size for CSE Unpacking cpd_mod_attr.append([cpd_entry_name.decode('utf-8')[:-4], 0, 0, 0, CSE_Ext_10_iUnit_size, CSE_Ext_10_iUnit_size, 0, 0, cpd_name, 0, mn2_sigs, cpd_offset, cpd_chk_info]) - + elif ext_tag == 0x11 : mod_size = ext_hdr.OffsetLimit - ext_hdr.OffsetBase # cAVS Module Size = OffsetLimit - OffsetBase (should match $CPD Entry) mod_hash_len = len(ext_hdr.Hash) * 4 # Metadata's Module Hash (BE) Length mod_hash = '%0.*X' % (mod_hash_len * 2, int.from_bytes(ext_hdr.Hash, 'big')) # Metadata's Module Hash (BE) - + cpd_mod_attr.append([cpd_entry_name.decode('utf-8')[:-4], 0, 0, 0, mod_size, mod_size, 0, mod_hash, cpd_name, 0, mn2_sigs, cpd_offset, cpd_chk_info]) - + elif ext_tag == 0x13 : hash_len = len(ext_hdr.IBBLHash) * 4 # IBBL/IBB/OBB Hash Length ibbl_hash = '%0.*X' % (hash_len * 2, int.from_bytes(ext_hdr.IBBLHash, 'big')) # IBBL Hash (BE) @@ -6309,11 +6309,11 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man if ibbl_hash not in ['00' * ext_hdr.IBBLHashSize, 'FF' * ext_hdr.IBBLHashSize] : cpd_mod_attr.append(['IBBL', 0, 0, 0, 0, 0, 0, ibbl_hash, cpd_name, 0, mn2_sigs, cpd_offset, cpd_chk_info]) if ibb_hash not in ['00' * ext_hdr.IBBHashSize, 'FF' * ext_hdr.IBBHashSize] : cpd_mod_attr.append(['IBB', 0, 0, 0, 0, 0, 0, ibb_hash, cpd_name, 0, mn2_sigs, cpd_offset, cpd_chk_info]) if obb_hash not in ['00' * ext_hdr.OBBHashSize, 'FF' * ext_hdr.OBBHashSize] : cpd_mod_attr.append(['OBB', 0, 0, 0, 0, 0, 0, obb_hash, cpd_name, 0, mn2_sigs, cpd_offset, cpd_chk_info]) - + elif ext_tag == 0x14 and dnx_version == 1 : # CSE_Ext_14 Revision 1 (R1) has a unique structure # For CSE_Ext_14_R1, all the processing is done at the Manifest Analysis level. All validation results # are transferred to mod_anl via ext_dnx_val list so that they can be displayed in logical -unp86 order. - + ext_dnx_val[0] = dnx_version # DnX Version 1 (R1 SHA-256) ifwi_rgn_hdr_step = 0 # Step to loop through IFWI Region Maps rcip_chunk_size = ext_hdr.ChunkSize # RCIP IFWI Chunk Size @@ -6321,43 +6321,43 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man rcip_chunk_count_mea = int(dnx_rcip_len / rcip_chunk_size) # RCIP IFWI Chunk Count from MEA ifwi_rgn_count = ext_hdr.IFWIRegionCount # IFWI Region Count (eMMC/UFS) special_mod_anl = True # CSE_Ext_14_R1 requires special/unique Module processing - + # Check if RCIP length is divisible by RCIP Chunk length and if RCIP Chunk count from EXT is the same as MEA's if (dnx_rcip_len % rcip_chunk_size != 0) or (rcip_chunk_count_ext != rcip_chunk_count_mea) : cse_anl_err(col_r + 'Error: Detected non-divisible CSE Extension 0x%0.2X at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) - + # Parse each IFWI Region Map for _ in range(ifwi_rgn_count) : ifwi_rgn_map = get_struct(buffer, cpd_mod_offset + ifwi_rgn_hdr_step, CSE_Ext_14_RegionMap) ext_print_temp.append(ifwi_rgn_map.ext_print()) - + ifwi_rgn_hdr_step += ctypes.sizeof(CSE_Ext_14_RegionMap) - + # Parse each RCIP IFWI Chunk for chunk in range(rcip_chunk_count_ext) : rcip_chunk_off = dnx_rcip_off + chunk * rcip_chunk_size chunk_hash_off = cpd_mod_offset + ifwi_rgn_hdr_step + chunk * 0x20 - + rcip_chunk_hash = get_hash(buffer[rcip_chunk_off:rcip_chunk_off + rcip_chunk_size], 0x20) # SHA-256 ext_chunk_hash = format(int.from_bytes(buffer[chunk_hash_off:chunk_hash_off + 0x20], 'little'), '064X') - + # Check if Extension Chunk Hash is equal to RCIP IFWI Chunk Hash if ext_chunk_hash == rcip_chunk_hash : chunk_hash_valid_count += 1 - + pt_14_R2 = ext_table(['Field', 'Value'], False, 1) pt_14_R2.title = col_y + 'Extension 20 R1 Chunk %d/%d' % (chunk + 1, rcip_chunk_count_ext) + col_e pt_14_R2.add_row(['Chunk EXT Hash', ext_chunk_hash]) pt_14_R2.add_row(['Chunk MEA Hash', rcip_chunk_hash]) - + ext_print_temp.append(pt_14_R2) - + # Check if all Extension Chunk Hashes and RCIP IFWI Chunk Hashes are Valid if chunk_hash_valid_count == rcip_chunk_count_ext : ext_dnx_val[2] = True - + elif ext_tag == 0x14 and dnx_version == 2 : # CSE_Ext_14 Revision 2 (R2-R3) has a unique structure # For CSE_Ext_14_R2, all the processing is done at the Manifest Analysis level. All validation results # are transferred to mod_anl via ext_dnx_val list so that they can be displayed in logical -unp86 order. - + ext_dnx_val[0] = dnx_version # DnX Version 2 (R2 SHA-256, R3 SHA-384) ifwi_rgn_hdr_step = 0 # Step to loop through IFWI Region Maps hash_arr_hdr_step = 0 # Step to loop through Hashes Array Headers @@ -6368,7 +6368,7 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man rcip_chunk_count = int(dnx_rcip_len / rcip_chunk_size) # RCIP IFWI Chunk Count ifwi_rgn_count = ext_hdr.IFWIRegionCount # IFWI Region Count (eMMC/UFS) special_mod_anl = True # CSE_Ext_14_R2/R3 requires special/unique Module processing - + # Parse each Hashes Array Header for header in range(hash_arr_hdr_count) : hash_arr_part_struct = CSE_Ext_14_HashArray if mn2_rsa_key_len == 0x100 else CSE_Ext_14_HashArray_R2 @@ -6376,43 +6376,43 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man hash_arr_part_size = hash_arr_part_hdr.HashArrSize * 4 # Hashes Array file section size hash_arr_part_hash_len = len(hash_arr_part_hdr.HashArrHash) * 4 # Hashes Array file section Hash Length hash_arr_part_hash = '%0.*X' % (hash_arr_part_hash_len * 2, int.from_bytes(hash_arr_part_hdr.HashArrHash, 'little')) # Hashes Array file section Hash - + hash_arr_part_data_off = dnx_hash_arr_off + hash_arr_prev_part_size # Hashes Array file section data offset hash_arr_part_data = buffer[hash_arr_part_data_off:hash_arr_part_data_off + hash_arr_part_size] # Hashes Array file section data hash_arr_part_data_hash = get_hash(hash_arr_part_data, chunk_hash_size) # Hashes Array file section data hash - + # Check if RCIP length is divisible by RCIP Chunk length and if Hashes Array file section length is divisible by its Size if (dnx_rcip_len % rcip_chunk_size != 0) or (len(hash_arr_part_data) % hash_arr_part_size != 0) : cse_anl_err(col_r + 'Error: Detected non-divisible CSE Extension 0x%0.2X at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) - + # Check if Hashes Array file section Hash is valid to Hashes Array file section Header if hash_arr_part_hash == hash_arr_part_data_hash : hash_arr_valid_count += 1 - + pt_14_R2 = ext_table(['Field', 'Value'], False, 1) pt_14_R2.title = col_y + 'Extension 20 R2/R3 Hashes Array %d/%d' % (header + 1, hash_arr_hdr_count) + col_e pt_14_R2.add_row(['Hashes Array EXT Hash', hash_arr_part_hash]) pt_14_R2.add_row(['Hashes Array MEA Hash', hash_arr_part_data_hash]) - + ext_print_temp.append(pt_14_R2) - + # Parse each RCIP IFWI Chunk for chunk in range(rcip_chunk_count) : rcip_chunk_off = dnx_rcip_off + chunk * rcip_chunk_size hash_arr_chunk_off = dnx_hash_arr_off + chunk * chunk_hash_size - + rcip_chunk_hash = get_hash(buffer[rcip_chunk_off:rcip_chunk_off + rcip_chunk_size], chunk_hash_size) # SHA-256 or SHA-384 hash_arr_chunk_hash = format(int.from_bytes(buffer[hash_arr_chunk_off:hash_arr_chunk_off + chunk_hash_size], 'little'), '0%dX' % (chunk_hash_size * 2)) - + # Check if Hashes Array Chunk Hash is equal to RCIP IFWI Chunk Hash if hash_arr_chunk_hash == rcip_chunk_hash : chunk_hash_valid_count += 1 - + pt_14_R2 = ext_table(['Field', 'Value'], False, 1) pt_14_R2.title = col_y + 'Extension 20 R2/R3 Chunk %d/%d' % (chunk + 1, rcip_chunk_count) + col_e pt_14_R2.add_row(['Chunk EXT Hash', hash_arr_chunk_hash]) pt_14_R2.add_row(['Chunk MEA Hash', rcip_chunk_hash]) - + ext_print_temp.append(pt_14_R2) - + hash_arr_prev_part_size += hash_arr_part_size hash_arr_hdr_step += ctypes.sizeof(hash_arr_part_struct) @@ -6420,48 +6420,48 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man for _ in range(ifwi_rgn_count) : ifwi_rgn_map = get_struct(buffer, cpd_mod_offset + hash_arr_hdr_step + ifwi_rgn_hdr_step, CSE_Ext_14_RegionMap) ext_print_temp.append(ifwi_rgn_map.ext_print()) - + ifwi_rgn_hdr_step += ctypes.sizeof(CSE_Ext_14_RegionMap) - + # Check if all Hashes Array Header Hashes and RCIP IFWI Chunk Hashes are Valid if hash_arr_valid_count == hash_arr_hdr_count : ext_dnx_val[1] = True if chunk_hash_valid_count == rcip_chunk_count * hash_arr_hdr_count : ext_dnx_val[2] = True - + elif ext_tag == 0x15 : # CSE_Ext_15 has a unique structure CSE_Ext_15_PartID_length = ctypes.sizeof(CSE_Ext_15_PartID) CSE_Ext_15_Payload_length = ctypes.sizeof(CSE_Ext_15_Payload) CSE_Ext_15_Payload_Knob_length = ctypes.sizeof(CSE_Ext_15_Payload_Knob) special_mod_anl = True # CSE_Ext_15 requires special/unique Module processing - + part_id_count = ext_hdr.PartIDCount cpd_part_id_offset = cpd_mod_offset # CSE_Ext_15 structure size (not entire Extension 15) cpd_payload_offset = cpd_part_id_offset + part_id_count * 0x14 cpd_payload_knob_offset = cpd_payload_offset + 0x4 - + for _ in range(part_id_count) : part_id_struct = get_struct(buffer, cpd_part_id_offset, CSE_Ext_15_PartID) ext_print_temp.append(part_id_struct.ext_print()) cpd_part_id_offset += 0x14 - + payload_struct = get_struct(buffer, cpd_payload_offset, CSE_Ext_15_Payload) ext_print_temp.append(payload_struct.ext_print()) payload_knob_count = payload_struct.KnobCount payload_knob_area = cpd_ext_end - cpd_payload_knob_offset - + # Check Extension full size when Module Counter exists if (ext_tag,hdr_rev_tag) in ext_tag_mod_count and (cpd_ext_size != ext_length + part_id_count * CSE_Ext_15_PartID_length + CSE_Ext_15_Payload_length + payload_knob_count * CSE_Ext_15_Payload_Knob_length) : cse_anl_err(col_r + 'Error: Detected CSE Extension 0x%0.2X with Module Count size difference at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) - + # Check if Knob data is divisible by Knob size if payload_knob_area % CSE_Ext_15_Payload_Knob_length != 0 : cse_anl_err(col_r + 'Error: Detected non-divisible CSE Extension 0x%0.2X at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) - + for _ in range(payload_knob_count) : payload_knob_struct = get_struct(buffer, cpd_payload_knob_offset, CSE_Ext_15_Payload_Knob, ftpr_var_ver) ext_print_temp.append(payload_knob_struct.ext_print()) cpd_payload_knob_offset += 0x08 - + elif ext_tag == 0x16 : ext_psize = ext_hdr.PartitionSize # Partition Size if ext_pname == '' : ext_pname = ext_hdr.PartitionName.decode('utf-8') # Partition Name (prefer CSE_Ext_03) @@ -6469,11 +6469,11 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man if gmf_blob_info : gmf_blob_info[1] = in_id # Fill GMF Blobs Partition Instance ID ext_phlen = int.from_bytes(ext_hdr.HashSize, 'little') # Partition Hash Length ext_phash = '%0.*X' % (ext_phlen * 2, int.from_bytes(ext_hdr.Hash, 'little')) # Partition Hash - + # Verify Partition Hash ($CPD - $MN2 + Data) if start_man_match != -1 and not single_man_name and not oem_config and not oem_signed and not fptemp_info[0] : mea_pdata = buffer[cpd_offset:mn2_offset] + buffer[mn2_offset + mn2_size:cpd_offset + ext_psize] # $CPD + Data (no $MN2) - + mea_phash = get_hash(mea_pdata, ext_phlen) ext_phval = [True, ext_phash == mea_phash, ext_phash, mea_phash] if not ext_phval[1] and int(ext_phval[2], 16) != 0 : @@ -6481,13 +6481,13 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man elif (variant,major,minor) == ('CSSPS',4,4) : (ext_phash, mea_phash) = ('IGNORE', 'IGNORE') # CSSPS 4.4 Partition Hash is always wrong, ignore elif (variant,anl_major,anl_meu_major) == ('PHY',12,0) : (ext_phash, mea_phash) = ('IGNORE', 'IGNORE') # PHYP EBG Partition Hash is always wrong, ignore cse_anl_err(col_r + 'Error: Detected CSE Extension 0x%0.2X with wrong Partition Hash at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, [ext_phash,mea_phash]) - + elif ext_tag == 0x17 : fd_ranges = [(ext_hdr.Range1Start,ext_hdr.Range1End),(ext_hdr.Range2Start,ext_hdr.Range2End),(ext_hdr.Range3Start,ext_hdr.Range3End),(ext_hdr.Range4Start,ext_hdr.Range4End), (ext_hdr.Range5Start,ext_hdr.Range5End),(ext_hdr.Range6Start,ext_hdr.Range6End),(ext_hdr.Range7Start,ext_hdr.Range7End),(ext_hdr.Range8Start,ext_hdr.Range8End)] fd_hash_len = len(ext_hdr.Hash) * 4 fd_hash = '%0.*X' % (fd_hash_len * 2, int.from_bytes(ext_hdr.Hash, 'little')) - + fd_info = [fd_hash,fd_ranges] # Store Flash Descriptor Verification Hash and Exclusion Ranges elif ext_tag in (0x18,0x19,0x1A) and hasattr(ext_hdr, 'Reserved') : @@ -6503,94 +6503,94 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man 11:(iom_names[cpd_name] if cpd_name in iom_names else 'iom') + '.hwcd' } special_mod_anl = True # CSE_Ext_18/19/1A require special/unique Module processing - + while cpd_mod_offset < cpd_ext_end : mod_hdr = get_struct(buffer, cpd_mod_offset, ext_struct_mod) - + tcss_type = mod_hdr.HashType # Numeric value which corresponds to specific TCSS module filename tcss_hash_len = len(mod_hdr.Hash) * 4 # Hash (BE) Length tcss_hash = '%0.*X' % (tcss_hash_len * 2, int.from_bytes(mod_hdr.Hash, 'big')) # Hash (BE) - + if tcss_type in tcss_types : tcss_name = tcss_types[tcss_type] # Get TCSS Module Name based on its Type and $CPD Name - + # Check if the generated TCSS Module Name is actually one of the $CPD Partition Modules Names if tcss_name not in cpd_mod_names : cse_anl_err(col_r + 'Error: Detected unknown CSE TCSS Name at %s > %s!' % (cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) - + cpd_mod_attr.append([tcss_name, 0, 0, 0, 0, 0, 0, tcss_hash, cpd_name, 0, mn2_sigs, cpd_offset, cpd_chk_info]) else : cse_anl_err(col_r + 'Error: Detected unknown CSE TCSS Type %d at %s > %s!' % (tcss_type, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) - + ext_print_temp.append(mod_hdr.ext_print()) - + cpd_mod_offset += mod_length elif ext_tag == 0x19 : gsc_project = ext_hdr.Project.decode('utf-8') # DG01, DG02 etc gsc_hotfix = ext_hdr.Hotfix # Hotfix for DG1 (e.g. 0), SKU at DG2+ (e.g. 1 = SOC1, 2 = SOC2) gsc_build = ext_hdr.Build # Year/Week at DG1 (2035 = 2020 W35), Stepping/Build at DG2 (e.g. 2054 = B) - + gsc_info = [gsc_project, gsc_hotfix, gsc_build] - + elif ext_tag in (0x18,0x1A) : special_mod_anl = True # CSE_Ext_18/1A require special/unique Module processing - + while cpd_mod_offset < cpd_ext_end : mod_hdr = get_struct(buffer, cpd_mod_offset, ext_struct_mod) - + fwi_iup_name = mod_hdr.Name.decode('utf-8') fwi_iup_hash_len = len(mod_hdr.Hash) * 4 fwi_iup_hash = '%0.*X' % (fwi_iup_hash_len * 2, int.from_bytes(mod_hdr.Hash, 'big')) - + fwi_iup_hashes.append([fwi_iup_name,fwi_iup_hash]) - + ext_print_temp.append(mod_hdr.ext_print()) - + cpd_mod_offset += mod_length - + elif ext_tag == 0x1E : # CSE_Ext_1E has a unique structure # At CSE_Ext_1E, the GMF Certificate file/blob is within the Extension so its data must be # transferred to mod_anl via gmf_blob_info so that it can be extracted during unpacking. - + gmf_cert_size = ext_hdr.CertificateSize gmf_cert_start = cpd_mod_offset gmf_cert_end = gmf_cert_start + gmf_cert_size gmf_cert_data = buffer[gmf_cert_start:gmf_cert_end] gmf_cert_padd = buffer[gmf_cert_end:cpd_ext_end] - + # Gather GMF Certificate Data for current Partition Name, Instance ID & Offset if gmf_blob_info : gmf_blob_info[3][0] = gmf_cert_data else : gmf_blob_info = [cpd_name, in_id, cpd_offset, [gmf_cert_data, b'']] - + # Check Extension padding after GMF Certificate if gmf_cert_padd != b'\xFF' * (cpd_ext_end - gmf_cert_end) : cse_anl_err(col_r + 'Error: Detected invalid CSE Extension 0x%0.2X padding at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) - + elif ext_tag == 0x1F : # CSE_Ext_1F has a unique structure # At CSE_Ext_1F, the GMF Body file/blob is within the Extension so its data must be # transferred to mod_anl via gmf_blob_info so that it can be extracted during unpacking. - + gmf_body_data = buffer[cpd_mod_offset:cpd_ext_end] - + # Gather GMF Body Data for current Partition Name, Instance ID & Offset if gmf_blob_info : gmf_blob_info[3][1] = gmf_body_data else : gmf_blob_info = [cpd_name, in_id, cpd_offset, [b'', gmf_body_data]] - + elif ext_tag == 0x23 : ext_pname = ext_hdr.PartitionName.decode('utf-8') # Partition Name vcn = ext_hdr.VCN # Version Control Number arb_svn = ext_hdr.ARBSVN # FPF Anti-Rollback (ARB) Security Version Number ext15_info[0] = arb_svn # Adjust CSE Extension 15/35 Info with ARB SVN special_mod_anl = True # CSE_Ext_23 requires special/unique Module processing - + ''' # Extension 35 FWType & FWSKU processing, waiting for first sample before implementation ext15_type = ext15_fw_type[ext_hdr.FWType] if ext_hdr.FWType in ext15_fw_type else 'Unknown' # Firmware Type (Client, SPS etc) ext15_sku = ext15_fw_sku[ext_hdr.FWSKU] if ext_hdr.FWSKU in ext15_fw_sku else ('Unknown','UNK') # Firmware SKU (CON, COR, SLM, LIT, SVR etc) - + ext15_info[1:3] = ext15_type, ext15_sku # Adjust CSE Extension 15 Info with FW Type, FW SKU ''' - + while cpd_mod_offset < cpd_ext_end : mod_hdr = get_struct(buffer, cpd_mod_offset, ext_struct_mod) met_name = mod_hdr.Name.decode('utf-8') + '.met' @@ -6598,51 +6598,51 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man if met_name.endswith('.met.met') : met_name = met_name[:-4] met_hash_len = len(mod_hdr.MetadataHash) * 4 # Metadata Hash Length met_hash = '%0.*X' % (met_hash_len * 2, int.from_bytes(mod_hdr.MetadataHash, 'little')) # Metadata Hash - + cpd_ext_hash.append([cpd_name, met_name, met_hash]) - + ext_print_temp.append(mod_hdr.ext_print()) - + cpd_mod_offset += mod_length - + elif ext_tag == 0x32 : ext50_type = ext_hdr.Type.decode('utf-8') # SPS Type (OP, RC) ext50_plat = ext_hdr.Platform.decode('utf-8') # SPS Platform (GE, HA, PU, PE etc) - + ext50_info = [ext50_type, ext50_plat] - + # Check Extension full size when Module Counter exists if (ext_tag,hdr_rev_tag) in ext_tag_mod_count and (cpd_ext_size != ext_length + ext_hdr.ModuleCount * mod_length) : cse_anl_err(col_r + 'Error: Detected CSE Extension 0x%0.2X with Module Count size difference at %s > %s!' % (ext_tag, cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) - + # Parse generic Extension Modules w/o special processing if ext_dict_mod in ext_dict and not special_mod_anl : while cpd_mod_offset < cpd_ext_end : mod_hdr = get_struct(buffer, cpd_mod_offset, ext_struct_mod) ext_print_temp.append(mod_hdr.ext_print()) - + cpd_mod_offset += mod_length - + cpd_ext_offset += cpd_ext_size # Next Extension Offset - + if cpd_ext_offset + 0x4 > cpd_entry_offset + cpd_entry_size : # End of Manifest/Metadata Entry reached (at least 0x4 needed for next ext_tag) cpd_ext_attr.append([cpd_entry_name.decode('utf-8'), 0, 0, cpd_entry_offset, cpd_entry_size, cpd_entry_size, entry_empty, 0, cpd_name, in_id, mn2_sigs, cpd_offset, cpd_chk_info]) cpd_ext_names.append(cpd_entry_name.decode('utf-8')[:-4]) # Store Module names which have Manifest/Metadata - + break # Stop Extension scanning at the end of Manifest/Metadata Entry - + ext_tag = int.from_bytes(buffer[cpd_ext_offset:cpd_ext_offset + 0x4], 'little') # Next Extension Tag - + # Detect last 0x20 of UTOK/STKN for Unlock Token Flags Structure (Optional) if buffer[buffer_len - 0x20:buffer_len - 0x1C] == b'UTFL' : utfl_hdr = get_struct(buffer, buffer_len - 0x20, UTFL_Header) ext_print_temp.append(utfl_hdr.hdr_print()) - + # Add $MN2 Info followed by Manifest/Metadata/UTFL Info if single_man_name and mn2_hdr_print : ext_print_temp = [mn2_hdr_print] + ext_print_temp - + ext_print.append(ext_print_temp) # Store Manifest/Metadata/UTFL Info - + # Actions when parsing UTOK/STKN without Manifest (a.k.a. UTFL only) if single_man_name and start_man_match == -1 : ext_print.append(cpd_entry_name.decode('utf-8')) # Store UTOK w/o $MN2 Partition Name @@ -6653,7 +6653,7 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man ext_print.append(ext_print_temp) # Store UTFL Info if single_man_name : return ext_print, mn2_sigs, fd_info # Stop Manifest/Metadata/UTFL analysis early when the input is a single Manifest - + # Stage 3: Calculate Module Compressed Size when no Metadata exists, thus treated as "Data" instead of "Module with Metadata" below # When the firmware lacks Module Metadata, the Compression Type, Encryption Yes/No, Compressed Size & Uncompressed Size are unknown # $CPD contains Huffman Yes/No and Uncompressed Size but Compressed Size is needed for Header parsing during Huffman decompression @@ -6665,7 +6665,7 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man continue # Do not adjust empty entries to skip them during unpacking (i.e. fitc.cfg or oem.key w/o Data) if oem_config or oem_signed : # Check if entry is FIT/OEM customized and thus outside Stock/RGN Partition continue # Do not adjust FIT/OEM-customized Partition entries (fitc.cfg, oem.key) since $CPD info is accurate - + if i < len(cpd_wo_met_info) - 1 : # For all entries, use the next module offset to find its size, if possible cpd_wo_met_info[i][2] = cpd_wo_met_info[i + 1][1] - cpd_wo_met_info[i][1] # Size is Next Start - Current Start elif ext_psize != -1 : # For the last entry, use CSE Extension 0x3/0x16 to find its size via the total Partition size @@ -6674,32 +6674,32 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man entry_size = buffer[cpd_wo_met_info[i][1]:].find(b'\xFF\xFF') # There is no Huffman codeword 0xFFFF if entry_size != -1 : cpd_wo_met_info[i][2] = entry_size # Size ends where the padding starts else : cse_anl_err(col_r + 'Error: Could not determine the size of Module %s > %s!' % (cpd_name,cpd_wo_met_info[i][0]) + col_e, None) - + if cpd_wo_met_info[i][2] > cpd_wo_met_back[i][2] or cpd_wo_met_info[i][2] < 0 : # Report obvious wrong Module Size adjustments cpd_wo_met_info[i][2] = cpd_wo_met_back[i][2] # Restore default Module Size from backup in case of wrong adjustment cse_anl_err(col_r + 'Error: Could not verify the size of Module %s > %s!' % (cpd_name,cpd_wo_met_info[i][0]) + col_e, None) - + # Stage 4: Fill Metadata Hash from Manifest for attr in cpd_ext_attr : for met_hash in cpd_ext_hash : if attr[8] == met_hash[0] and attr[0] == met_hash[1] : # Verify $CPD and Metadata name match attr[7] = met_hash[2] # Fill Metadata's Hash Attribute from Manifest Extension 0x3, 0xF, 0x16 or 0x23 break # To hopefully avoid some 03/0F/16/23 MetadataHash mismatch, assuming 1st has correct MetadataHash - + # Stage 5: Analyze Modules, Keys, Microcodes & Data (must be after all Manifest & Metadata Extension analysis) for entry in range(0, cpd_num) : cpd_entry_hdr = get_struct(buffer, cpd_offset + cpd_hdr_size + entry * 0x18, CPD_Entry) cpd_mod_off,_,_ = cpd_entry_hdr.get_flags() - + cpd_entry_name = cpd_entry_hdr.Name cpd_entry_size = cpd_entry_hdr.Size # Uncompressed only cpd_entry_offset = cpd_offset + cpd_mod_off mod_size = cpd_entry_size # Uncompressed initially, to replace with Compressed for Modules mod_empty = 0 # Assume that Module is not empty initially - + # Manifest & Metadata Skip if b'.man' in cpd_entry_name or b'.met' in cpd_entry_name : continue - + # Fill Module Attributes by single unified Metadata (BPM.met > [IBBL, IBB, OBB] or iom.met > [iom, iom.cd, iom.hwcd] etc...) if cpd_name in ('IBBP','IOMP','MGPP','NPHY','TBTP') : # MGPP = NPHY for mod in range(len(cpd_mod_attr)) : @@ -6707,29 +6707,29 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man cpd_mod_attr[mod][4] = cpd_entry_size # Fill Module Uncompressed Size from $CPD Entry cpd_mod_attr[mod][5] = cpd_entry_size # Fill Module Uncompressed Size from $CPD Entry cpd_ext_names.append(cpd_entry_name.decode('utf-8')) # To enter "Module with Metadata" section below - + break - + # Store all IBBP Module names to exclude those missing but with Hash at .met (GREAT WORK INTEL/OEMs...) if cpd_name == 'IBBP' : ibbp_all.append(cpd_entry_name.decode('utf-8')) - + # Module with Metadata if cpd_entry_name.decode('utf-8') in cpd_ext_names : for mod in range(len(cpd_mod_attr)) : if cpd_mod_attr[mod][0] == cpd_entry_name.decode('utf-8') : - + cpd_mod_attr[mod][3] = cpd_entry_offset # Fill Module Starting Offset from $CPD Entry if cpd_mod_attr[mod][4] == 0 : cpd_mod_attr[mod][4] = cpd_entry_size # Prefer Metadata info, if available (!= 0) if cpd_mod_attr[mod][5] == 0 : cpd_mod_attr[mod][5] = cpd_entry_size # Prefer Metadata info, if available (!= 0) cpd_mod_attr[mod][9] = in_id # Fill Module Instance ID from CSE_Ext_03 - + mod_comp_size = cpd_mod_attr[mod][4] # Store Module Compressed Size for Empty check mod_size = mod_comp_size # Store Module Compressed Size for Out of Partition Bounds check mod_data = buffer[cpd_entry_offset:cpd_entry_offset + mod_comp_size] # Store Module data for Empty check if mod_data in (b'', b'\xFF' * mod_comp_size) or cpd_entry_offset >= file_end : cpd_mod_attr[mod][6] = 1 # Determine if Module is Empty/Missing - + break - + # Detect $FPT Partition Size mismatch vs CSE_Ext_03/16 for part in fpt_part_all : # Verify that CSE_Ext_03/16.PartitionSize exists and that the same $CPD Partition was found at fpt_part_all @@ -6740,7 +6740,7 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man and part[1] == cpd_offset and part[3] == in_id and part[2] < (cpd_offset + ext_psize) : cse_anl_err(col_r + 'Error: Size of %s Partition at $FPT is smaller than CSE Extension 0x3/0x16!' % cpd_name + col_e, None) msg_shown = True # Partition related error, show only once - + # Detect BPDT Partition Size mismatch vs CSE_Ext_03/16 for part in bpdt_part_all : # Verify that CSE_Ext_03/16.PartitionSize exists and that the same $CPD Partition was found at bpdt_part_all @@ -6751,45 +6751,45 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man and part[1] == cpd_offset and part[6] == in_id and part[2] < (cpd_offset + ext_psize) : cse_anl_err(col_r + 'Error: Size of %s Partition at BPDT is smaller than CSE Extension 0x3/0x16!' % cpd_name + col_e, None) msg_shown = True # Partition related error, show only once - + # Key elif '.key' in cpd_entry_name.decode('utf-8') : mod_data = buffer[cpd_entry_offset:cpd_entry_offset + cpd_entry_size] if mod_data in (b'', b'\xFF' * cpd_entry_size) or cpd_entry_offset >= file_end : mod_empty = 1 # Determine if Key is Empty/Missing - + key_man_pat = man_pat.search(mod_data[:0x50]) or bccb_pat.search(mod_data[:0x50]) # Search for 0x8086 or 0xBCCB Key Manifest Pattern - + if not mod_empty and key_man_pat : key_man_off = key_man_pat.start() - 0x10 # Key Manifest Start Offset key_man_len = int.from_bytes(mod_data[key_man_pat.end() - 0x8:key_man_pat.end() - 0x4], 'little') * 4 # Key Manifest Size mod_data = mod_data[key_man_off:key_man_off + key_man_len] # Adjust Key Data to Manifest contents only (i.e. ignore $CPD) - + # Get Key Manifest/Extension Info when applicable (print at mod_anl only) key_print,mn2_signs,fd_info = ext_anl(mod_data, '$MN2', 0x1B, file_end, [variant,major,minor,hotfix,build,year,month,variant_p], cpd_entry_name.decode('utf-8'), [[],''], [[],-1,-1,-1]) # Retrieve & Store Key Manifest/Extension Info - + ext_print += key_print # Append Key Manifest/Extension Info (key_print is the ext_print of .key file, same structure) - + # noinspection PyUnboundLocalVariable cpd_mod_attr.append([cpd_entry_name.decode('utf-8'), 0, 0, cpd_entry_offset, cpd_entry_size, cpd_entry_size, mod_empty, 0, cpd_name, 0, mn2_signs, cpd_offset, cpd_chk_info]) - + # Microcode elif 'upatch' in cpd_entry_name.decode('utf-8') : mod_data = buffer[cpd_entry_offset:cpd_entry_offset + cpd_entry_size] if mod_data in (b'', b'\xFF' * cpd_entry_size) or cpd_entry_offset >= file_end : mod_empty = 1 # Determine if Microcode is Empty/Missing - + # Detect actual Microcode length mc_len = int.from_bytes(mod_data[0x20:0x24], 'little') mc_data = buffer[cpd_entry_offset:cpd_entry_offset + mc_len] - + cpd_mod_attr.append([cpd_entry_name.decode('utf-8'), 0, 0, cpd_entry_offset, cpd_entry_size, cpd_entry_size, mod_empty, mc_chk32(mc_data), cpd_name, 0, mn2_sigs, cpd_offset, cpd_chk_info]) - + # Data else : mod_comp_type = 0 # The Type is Uncompressed by default since "Data" shouldn't have Metadata mod_comp_size = cpd_entry_size # Compressed = Uncompressed (via $CPD) size by default since "Data" shouldn't have Metadata mod_uncomp_size = cpd_entry_size # The Uncompressed Size can be taken directly from $CPD - + # When the firmware lacks Huffman Module Metadata, we must manually fill the Compression Type via $CPD and calculated Compressed Size for i in range(len(cpd_wo_met_info)) : if (cpd_wo_met_info[i][0], cpd_wo_met_info[i][3]) == (cpd_entry_name.decode('utf-8'), 1) : @@ -6797,31 +6797,31 @@ def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man mod_comp_size = cpd_wo_met_info[i][2] # As calculated at Stage 3 of the analysis mod_size = mod_comp_size # Store calculated Compressed Size for Out of Partition Bounds check break - + mod_data = buffer[cpd_entry_offset:cpd_entry_offset + mod_size] - + # When the firmware lacks LZMA Module Metadata, we must manually fill the Compression Type and calculated Uncompressed Size if mod_data.startswith(b'\x36\x00\x40\x00\x00') and mod_data[0xE:0x11] == b'\x00\x00\x00' : mod_comp_type = 2 # Compression Type 2 is LZMA mod_uncomp_size = int.from_bytes(mod_data[0x5:0xD], 'little') # LZMA Header 0x5-0xD (uint64) is the Uncompressed Size in LE - + if mod_data in (b'', b'\xFF' * mod_size) or cpd_entry_offset >= file_end : mod_empty = 1 # Determine if Module is Empty/Missing - + cpd_mod_attr.append([cpd_entry_name.decode('utf-8'), mod_comp_type, 0, cpd_entry_offset, mod_comp_size, mod_uncomp_size, mod_empty, 0, cpd_name, 0, mn2_sigs, cpd_offset, cpd_chk_info]) - + # Detect Modules which exceed or are located at/after the end of RGN Partition size (CSE_Ext_03/16.PartitionSize) if not oem_config and not oem_signed and ext_psize != -1 and ((cpd_entry_offset >= cpd_offset + ext_psize) or (cpd_entry_offset + mod_size > cpd_offset + ext_psize)) : cse_anl_err(col_r + 'Error: Detected out of Partition bounds Module at %s > %s!' % (cpd_name, cpd_entry_name.decode('utf-8')) + col_e, None) - + # Stage 6: Remove missing APL IBBP Module Attributes if len(ibbp_all) : for ibbp in ibbp_bpm : if ibbp not in ibbp_all : # Module has hash at unified Metadata but is actually missing for mod_index in range(len(cpd_mod_attr)) : if cpd_mod_attr[mod_index][0] == ibbp : ibbp_del.append(mod_index) # Store missing Module's Attributes - + for mod_index in ibbp_del : del cpd_mod_attr[mod_index] # Delete missing Module's Attributes - + return cpd_offset,cpd_mod_attr,cpd_ext_attr,vcn,ext12_info,ext_print,ext_pname,ext50_info,ext_phval,ext_dnx_val,oem_config,oem_signed,cpd_mn2_info,ext_iunit_val,ext15_info,pch_init_final,gmf_blob_info,fwi_iup_hashes,gsc_info # Analyze & Store CSE Modules @@ -6834,34 +6834,34 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva comp = ['Uncompressed','Huffman','LZMA'] encr_type = ['None','AES-ECB','AES-CTR'] is_empty = ['No','Yes'] - + pt = ext_table([col_y + 'Name' + col_e, col_y + 'Compression' + col_e, col_y + 'Encryption' + col_e, col_y + 'Offset' + col_e, col_y + 'Compressed' + col_e, col_y + 'Uncompressed' + col_e, col_y + 'Empty' + col_e], True, 1) - + cpd_all_attr = cpd_ext_attr + cpd_mod_attr - + # $CPD validity verified if cpd_offset > -1 and cpd_all_attr : - + # Store Module details for mod in cpd_all_attr : comp_print = 'None' if mod[1] == 0 else comp[mod[1]] # Print Compression "None" instead of "Uncompressed" at Module details pt.add_row([mod[0],comp_print,encr_type[mod[2]],'0x%0.6X' % mod[3],'0x%0.6X' % mod[4],'0x%0.6X' % mod[5],is_empty[mod[6]]]) - + # Parent Partition Attributes (same for all cpd_all_attr list instance entries) cpd_pname = cpd_all_attr[0][8] # $CPD Name cpd_poffset = cpd_all_attr[0][11] # $CPD Offset, covers any cases with duplicate name entries (Joule_C0-X64-Release) cpd_chk_ok,cpd_chk_rslt = cpd_all_attr[0][12] # CPD Checksum Validity & Values ext_inid = cpd_all_attr[0][9] # Partition Instance ID - + pt.title = col_y + 'Detected %s Module(s) at %s %0.4X [0x%0.6X]' % (len(cpd_all_attr), cpd_pname, ext_inid, cpd_poffset) + col_e folder_name = os.path.join(out_dir, fw_name, '%s %0.4X [0x%0.6X]' % (cpd_pname, ext_inid, cpd_poffset), '') info_fname = os.path.join(out_dir, fw_name, '%s %0.4X [0x%0.6X].txt' % (cpd_pname, ext_inid, cpd_poffset)) - + cpd_hdr_struct, _ = get_cpd(reading, cpd_poffset) cpd_phdr = get_struct(reading, cpd_poffset, cpd_hdr_struct) if param.cse_unpack : print('%s' % cpd_phdr.hdr_print()) - + if cpd_chk_ok : print(col_g + '\n$CPD Checksum of partition "%s" is VALID\n' % cpd_pname + col_e) else : @@ -6871,32 +6871,32 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva print(col_r + '\n$CPD Checksum of partition "%s" is INVALID (Known CSE Bad Checksum)\n' % cpd_pname + col_e) else : print(col_r + '\n$CPD Checksum of partition "%s" is INVALID\n' % cpd_pname + col_e) - + print(pt) # Show Module details - + os.mkdir(folder_name) - + # Store Partition $CPD Header & Entry details in TXT with open(info_fname, 'a', encoding = 'utf-8') as info_file : info_file.write('\n%s\n%s' % (ansi_escape.sub('', str(cpd_phdr.hdr_print())), ansi_escape.sub('', str(pt)))) - + # Store Partition $CPD Header & Entry details in HTML if param.write_html: with open(info_fname[:-4] + '.html', 'a', encoding = 'utf-8') as info_file: info_file.write('\n
\n%s\n
\n%s' % (pt_html(cpd_phdr.hdr_print()), pt_html(pt))) - + # Store Partition $CPD Header & Entry details in JSON if param.write_json: info_json_path = info_fname[:-4] + '.json' - + if info_json_path not in mod_anl_jsons: mod_anl_jsons[info_json_path] = [] - + mod_anl_jsons[info_json_path].extend([pt_json(cpd_phdr.hdr_print()), pt_json(pt)]) - + # Load Huffman Dictionaries for Decompression huff_shape, huff_sym, huff_unk = cse_huffman_dictionary_load(variant, major, minor, 'error') - + # Parse all Modules based on their Metadata for mod in cpd_all_attr : mod_name = mod[0] # Name @@ -6913,46 +6913,46 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva mn2_sig_sha = mod[10][2] # RSA Signature Data Hash mn2_error = mod[10][3] # Check if RSA validation crashed (try-except) mn2_struct = mod[10][5] # Manifest Structure Object - + if mod_empty == 1 : continue # Skip Empty/Missing Modules - + if '.man' in mod_name or '.met' in mod_name : mod_fname = folder_name + mod_name mod_type = 'metadata' else : mod_fname = folder_name + mod_name mod_type = 'module' - + mod_data = reading[mod_start:mod_end] - + if not mod_encr : print(col_y + '\n--> Stored %s %s "%s" [0x%0.6X - 0x%0.6X]' % (comp[mod_comp], mod_type, mod_name, mod_start, mod_end - 0x1) + col_e) else : print(col_m + '\n--> Stored Encrypted %s %s "%s" [0x%0.6X - 0x%0.6X]' % (comp[mod_comp], mod_type, mod_name, mod_start, mod_end - 0x1) + col_e) - + # Store & Ignore Encrypted Data if mod_encr >= 1 : - + if param.cse_pause : # Debug print('\n MOD: %s' % mod_hash) print(col_m + '\n Hash of Encrypted %s "%s" cannot be verified' % (mod_type, mod_name) + col_e) - + with open(mod_fname, 'wb') as mod_file : mod_file.write(mod_data) # Store Encrypted Data, cannot validate - + # Store Uncompressed Data elif mod_comp == 0 : - + # Manifest if '.man' in mod_name : for rbep_man in rbe_man_hashes : rbe_man_name = rbep_man[0] - + if rbe_man_name == cpd_pname : rbe_man_hash = rbep_man[1] cpd_man_hash = cpd_mn2_info[14][{0x30: 0, 0x20: 1}[len(rbe_man_hash) // 2]] - + if param.cse_pause : print('\n %s: %s' % (cpd_pname, cpd_man_hash)) # Debug print(' RBEP: %s' % rbe_man_hash) # Debug - + if cpd_man_hash == rbe_man_hash : print(col_g + '\n Manifest of partition "%s" is VALID' % cpd_pname + col_e) else : @@ -6960,13 +6960,13 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva input_col(col_r + '\n Manifest of partition "%s" is INVALID' % cpd_pname + col_e) # Debug else : print(col_r + '\n Manifest of partition "%s" is INVALID' % cpd_pname + col_e) - + break - + if param.cse_pause : print('\n MN2: %s' % mn2_sig_dec) # Debug print(' MEA: %s' % mn2_sig_sha) # Debug - + if mn2_error : if param.cse_pause : input_col(col_m + '\n RSA Signature of partition "%s" is UNKNOWN' % cpd_pname + col_e) # Debug @@ -6981,21 +6981,21 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva print(col_r + '\n RSA Signature of partition "%s" is INVALID (Known CSE Bad RSA Signature)' % cpd_pname + col_e) else : print(col_r + '\n RSA Signature of partition "%s" is INVALID' % cpd_pname + col_e) - + mn2_hdr_print = mn2_struct.hdr_print_cse() if not param.cse_verbose : print('\n%s' % mn2_hdr_print) # Show $MN2 details (already included in -ver86) - + # Insert $MN2 Manifest details at Extension Info list (ext_print) ext_print_cur_len = len(ext_print) # Current length of Extension Info list for index in range(0, ext_print_cur_len, 2) : # Only Name (index), skip Info (index + 1) if str(ext_print[index]).startswith(mod_name) : ext_print[index + 1] = [mn2_hdr_print] + (ext_print[index + 1]) break - + if param.cse_pause and ext_phval[0] : print('\n EXT: %s' % ext_phval[2]) # Debug print(' MEA: %s' % ext_phval[3]) # Debug - + if ext_phval[0] and int(ext_phval[2], 16) == 0 : # Hash exists but is not used (0) print(col_m + '\n Hash of partition "%s" is UNKNOWN' % cpd_pname + col_e) elif ext_phval[0] and ext_phval[1] : # Hash exists and is Valid @@ -7013,7 +7013,7 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva print(col_r + '\n Hash of partition "%s" is INVALID (Known CSE Bad Hash)' % cpd_pname + col_e) else : print(col_r + '\n Hash of partition "%s" is INVALID' % cpd_pname + col_e) - + # Store Golden Measurements File (GMF) Blobs from RBEP.man > CSE_Ext_1E & CSE_Ext_1F if gmf_blob_info and (gmf_blob_info[0],gmf_blob_info[1],gmf_blob_info[2]) == (cpd_pname,ext_inid,cpd_poffset) : if gmf_blob_info[3][0] : @@ -7022,15 +7022,15 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva if gmf_blob_info[3][1] : gmf_body_path = os.path.join(folder_name, 'GMF_Body.bin') with open(gmf_body_path, 'wb') as gmf_body : gmf_body.write(gmf_blob_info[3][1]) - + # Metadata elif '.met' in mod_name : mea_hash = get_hash(mod_data, len(mod_hash) // 2) - + if param.cse_pause : print('\n MOD: %s' % mod_hash) # Debug print(' MEA: %s' % mea_hash) # Debug - + if mod_hash == mea_hash : print(col_g + '\n Hash of %s %s "%s" is VALID' % (comp[mod_comp], mod_type, mod_name) + col_e) else : @@ -7040,13 +7040,13 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva print(col_r + '\n Hash of %s %s "%s" is INVALID (Known CSE Bad Hash)' % (comp[mod_comp], mod_type, mod_name) + col_e) else : print(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) - + # Key elif '.key' in mod_name : if param.cse_pause : print('\n MN2: %s' % mn2_sig_dec) # Debug print(' MEA: %s' % mn2_sig_sha) # Debug - + if mn2_error : if param.cse_pause : input_col(col_m + '\n RSA Signature of %s %s "%s" is UNKNOWN!' % (comp[mod_comp], mod_type, mod_name)) # Debug @@ -7059,10 +7059,10 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva input_col(col_r + '\n RSA Signature of %s %s "%s" is INVALID!' % (comp[mod_comp], mod_type, mod_name) + col_e) # Debug else : print(col_r + '\n RSA Signature of %s %s "%s" is INVALID!' % (comp[mod_comp], mod_type, mod_name) + col_e) - + mn2_hdr_print = mn2_struct.hdr_print_cse() if not param.cse_verbose : print('\n%s' % mn2_hdr_print) # Show $MN2 details (already included in -ver86) - + # MFS Configuration elif mod_name in ('intl.cfg','fitc.cfg') : mfs_file_no = 6 if mod_name == 'intl.cfg' else 7 @@ -7077,15 +7077,15 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva input_col(col_r + '\n Failed to analyze MFS Low Level File %d (%s)' % (mfs_file_no, mfs_dict[mfs_file_no]) + col_e) # Debug else : print(col_r + '\n Failed to analyze MFS Low Level File %d (%s)' % (mfs_file_no, mfs_dict[mfs_file_no]) + col_e) - + # Only Intel MFS Configuration protected by Hash if mod_name == 'intl.cfg' : mea_hash = get_hash(mod_data, len(mod_hash) // 2) - + if param.cse_pause : print('\n MOD: %s' % mod_hash) # Debug print(' MEA: %s' % mea_hash) # Debug - + if mod_hash == mea_hash : print(col_g + '\n Hash of %s %s "%s" is VALID' % (comp[mod_comp], mod_type, mod_name) + col_e) else : @@ -7095,7 +7095,7 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva print(col_r + '\n Hash of %s %s "%s" is INVALID (Known CSE Bad Hash)' % (comp[mod_comp], mod_type, mod_name) + col_e) else : print(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) - + # Microcode elif 'upatch' in mod_name : if mod_hash == 0 : @@ -7105,10 +7105,10 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva input_col(col_r + '\n Checksum of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) # Debug else : print(col_r + '\n Checksum of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) - + # Data elif mod_hash == 0 : - + # CSE_Ext_14 R1/R2 has a unique structure if cpd_pname == 'RCIP' : if (mod_name,ext_dnx_val[1]) == ('hash.array',True) or (mod_name,ext_dnx_val[2]) == ('rcipifwi',True) : @@ -7129,11 +7129,11 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva elif mod_name != 'pavp' and rbe_pm_met_hashes : # Ignore PAVP w/o Metadata Hash check at rbe/pm as it's LZMA compressed and then AES encrypted mea_hash = get_hash(mod_data, len(rbe_pm_met_hashes[0]) // 2) - + if param.cse_pause : print('\n MOD: No Metadata, validation via RBEP > rbe and FTPR > pm Modules') # Debug print(' MEA: %s' % mea_hash) # Debug - + if mea_hash in rbe_pm_met_hashes : print(col_g + '\n Hash of %s %s "%s" is VALID' % (comp[mod_comp], mod_type, mod_name) + col_e) rbe_pm_met_valid.append(mea_hash) # Store valid RBEP > rbe or FTPR > pm Hash to single out leftovers @@ -7146,23 +7146,23 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva print(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) else : print(col_m + '\n Hash of %s %s "%s" is UNKNOWN' % (comp[mod_comp], mod_type, mod_name) + col_e) - + # Module else : mea_hash = get_hash(mod_data, len(mod_hash) // 2) - + if param.cse_pause : print('\n MOD: %s' % mod_hash) # Debug print(' MEA: %s' % mea_hash) # Debug - + # Detect & Cut Module OROM Headers if orom_pat.search(mod_data[:0x28]) : orom_hdr = get_struct(mod_data, 0, GSC_OROM_Header) # OROM Header Structure pcir_hdr = get_struct(mod_data, orom_hdr.PCIDataHdrOff, GSC_OROM_PCI_Data) # OROM PCIR Structure modp_off = max(orom_hdr.PCIDataHdrOff + pcir_hdr.PCIDataHdrLen, orom_hdr.EFIImageOffset, orom_hdr.OROMPayloadOff) # Payload Offset - + with open(mod_fname + '.bin', 'wb') as raw_file : raw_file.write(mod_data[modp_off:]) # Store Module Payload w/o OROM Headers - + if mod_hash == mea_hash : print(col_g + '\n Hash of %s %s "%s" is VALID' % (comp[mod_comp], mod_type, mod_name) + col_e) else : @@ -7172,29 +7172,29 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva print(col_r + '\n Hash of %s %s "%s" is INVALID (Known CSE Bad Hash)' % (comp[mod_comp], mod_type, mod_name) + col_e) else : print(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) - + with open(mod_fname, 'wb') as mod_file : mod_file.write(mod_data) # Store Metadata or Module # Store & Decompress Huffman Data elif mod_comp == 1 : - + try : if param.cse_pause : mod_data_d, huff_error = cse_huffman_decompress(mod_data, mod_size_comp, mod_size_uncomp, huff_shape, huff_sym, huff_unk, 'error') # Debug if (huff_error,mod_hash) == (True,0) : input() # Decompression incomplete, pause when no Module Metadata exist else : mod_data_d, huff_error = cse_huffman_decompress(mod_data, mod_size_comp, mod_size_uncomp, huff_shape, huff_sym, huff_unk, 'none') - + print(col_c + '\n Decompressed %s %s "%s"' % (comp[mod_comp], mod_type, mod_name) + col_e) - + # Open decompressed Huffman module for Hash validation, when Metadata info is available if mod_hash != 0 : mea_hash = get_hash(mod_data_d, len(mod_hash) // 2) - + if param.cse_pause : print('\n MOD: %s' % mod_hash) # Debug print(' MEA: %s' % mea_hash) # Debug - + if mod_hash == mea_hash : print(col_g + '\n Hash of %s %s "%s" is VALID' % (comp[mod_comp], mod_type, mod_name) + col_e) with open(mod_fname, 'wb') as mod_file: mod_file.write(mod_data_d) # Decompression complete, valid data @@ -7205,18 +7205,18 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva print(col_r + '\n Hash of %s %s "%s" is INVALID (Known CSE Bad Hash)' % (comp[mod_comp], mod_type, mod_name) + col_e) else : print(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) - + with open(mod_fname, 'wb') as mod_file: mod_file.write(mod_data_d) # Decompression complete, invalid data - + # Open decompressed Huffman module for Hash validation, when Metadata info is not available # When the firmware lacks Module Metadata, check RBEP > rbe and FTPR > pm Modules instead elif rbe_pm_met_hashes : mea_hash = get_hash(mod_data_d, len(rbe_pm_met_hashes[0]) // 2) - + if param.cse_pause : print('\n MOD: No Metadata, validation via RBEP > rbe and FTPR > pm Modules') # Debug print(' MEA: %s' % mea_hash) # Debug - + if mea_hash in rbe_pm_met_hashes : print(col_g + '\n Hash of %s %s "%s" is VALID' % (comp[mod_comp], mod_type, mod_name) + col_e) rbe_pm_met_valid.append(mea_hash) # Store valid RBEP > rbe or FTPR > pm Hash to single out leftovers @@ -7228,53 +7228,53 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva print(col_r + '\n Hash of %s %s "%s" is INVALID (Known CSE Bad Hash)' % (comp[mod_comp], mod_type, mod_name) + col_e) else : print(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) - + with open(mod_fname, 'wb') as mod_file: mod_file.write(mod_data_d) # Decompression complete, invalid data - + else : with open(mod_fname, 'wb') as mod_file: mod_file.write(mod_data_d) # Decompression complete, cannot validate - + except : if param.cse_pause : input_col(col_r + '\n Failed to decompress %s %s "%s"' % (comp[mod_comp], mod_type, mod_name) + col_e) # Debug else : print(col_r + '\n Failed to decompress %s %s "%s"' % (comp[mod_comp], mod_type, mod_name) + col_e) - + with open(mod_fname, 'wb') as mod_file: mod_file.write(mod_data) # Decompression failed - + # Store & Decompress LZMA Data elif mod_comp == 2 : - + mod_data_r = mod_data # Store raw LZMA Module contents before zeros removal, for hashing - + # Remove three extra zeros from LZMA Module header for proper decompression # https://github.com/skochinsky/me-tools/blob/master/me_unpack.py by Igor Skochinsky if mod_data.startswith(b'\x36\x00\x40\x00\x00') and mod_data[0xE:0x11] == b'\x00\x00\x00' : mod_data = mod_data[:0xE] + mod_data[0x11:] # Visually, mod_size_comp += -3 for compressed module - + try : # noinspection PyArgumentList mod_data_d = lzma.LZMADecompressor().decompress(mod_data) - + # Add missing EOF Padding when needed (usually at NFTP.ptt Module) data_size_uncomp = len(mod_data_d) if data_size_uncomp != mod_size_uncomp : mod_last_byte = struct.pack('B', mod_data_d[data_size_uncomp - 1]) # Determine padding type (0xFF or 0x00) mod_miss_padd = mod_size_uncomp - data_size_uncomp # Determine missing padding size mod_data_d += mod_last_byte * mod_miss_padd # Fill module with missing padding - + print(col_c + '\n Decompressed %s %s "%s"' % (comp[mod_comp], mod_type, mod_name) + col_e) - + # Open decompressed LZMA module for Hash validation, when Metadata info is available if mod_hash != 0 : # Calculate LZMA Module Hash mea_hash_c = get_hash(mod_data_r, len(mod_hash) // 2) # Compressed, Header zeros included (most LZMA Modules) - + mod_hash_c_ok = mod_hash == mea_hash_c # Check Compressed LZMA validity if not mod_hash_c_ok : # Skip Uncompressed LZMA hash if not needed mea_hash_u = get_hash(mod_data_d, len(mod_hash) // 2) # Uncompressed (few LZMA Modules) mod_hash_u_ok = mod_hash == mea_hash_u # Check Uncompressed LZMA validity - + if param.cse_pause : # Debug if mod_hash_c_ok : print('\n MOD: %s' % mod_hash) @@ -7286,7 +7286,7 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva print('\n MOD : %s' % mod_hash) print(' MEA C: %s' % mea_hash_c) print(' MEA U: %s' % mea_hash_u) - + if mod_hash_c_ok or mod_hash_u_ok : print(col_g + '\n Hash of %s %s "%s" is VALID' % (comp[mod_comp], mod_type, mod_name) + col_e) with open(mod_fname, 'wb') as mod_file : mod_file.write(mod_data_d) # Decompression complete, valid data @@ -7297,19 +7297,19 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva print(col_r + '\n Hash of %s %s "%s" is INVALID (Known CSE Bad Hash)' % (comp[mod_comp], mod_type, mod_name) + col_e) else : print(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) - + with open(mod_fname, 'wb') as mod_file : mod_file.write(mod_data_d) # Decompression complete, invalid data - + # Open decompressed LZMA module for Hash validation, when Metadata info is not available # When the firmware lacks Module Metadata, check RBEP > rbe and FTPR > pm Modules instead elif rbe_pm_met_hashes : mea_hash_c = get_hash(mod_data_r, len(rbe_pm_met_hashes[0]) // 2) # Compressed, Header zeros included (most LZMA Modules) - + mod_hash_c_ok = mea_hash_c in rbe_pm_met_hashes # Check Compressed LZMA validity if not mod_hash_c_ok : # Skip Uncompressed LZMA hash if not needed mea_hash_u = get_hash(mod_data_d, len(rbe_pm_met_hashes[0]) // 2) # Uncompressed (few LZMA Modules) mod_hash_u_ok = mea_hash_u in rbe_pm_met_hashes # Check Uncompressed LZMA validity - + if param.cse_pause : # Debug print('\n MOD: No Metadata, validation via RBEP > rbe and FTPR > pm Modules') # Debug if mod_hash_c_ok : @@ -7319,7 +7319,7 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva else : print(' MEA C: %s' % mea_hash_c) print(' MEA U: %s' % mea_hash_u) - + if mod_hash_c_ok : print(col_g + '\n Hash of %s %s "%s" is VALID' % (comp[mod_comp], mod_type, mod_name) + col_e) rbe_pm_met_valid.append(mea_hash_c) # Store valid RBEP > rbe or FTPR > pm Hash to single out leftovers @@ -7335,17 +7335,17 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva print(col_r + '\n Hash of %s %s "%s" is INVALID (Known CSE Bad Hash)' % (comp[mod_comp], mod_type, mod_name) + col_e) else : print(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) - + with open(mod_fname, 'wb') as mod_file: mod_file.write(mod_data_d) # Decompression complete, invalid data - + except : if param.cse_pause : input_col(col_r + '\n Failed to decompress %s %s "%s"' % (comp[mod_comp], mod_type, mod_name) + col_e) # Debug else : print(col_r + '\n Failed to decompress %s %s "%s"' % (comp[mod_comp], mod_type, mod_name) + col_e) - + with open(mod_fname, 'wb') as mod_file : mod_file.write(mod_data) # Decompression failed - + # Print Manifest/Metadata/Key Extension Info ext_print_len = len(ext_print) # Final length of Extension Info list (must be after Manifest & Key extraction) if mod_type == 'metadata' or '.key' in mod_name : @@ -7355,62 +7355,62 @@ def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, fw_name, ext_print, ext_phva for ext in ext_print[index + 1] : ext_str = ansi_escape.sub('', str(ext)) # Ignore Colorama ANSI Escape Character Sequences with open(mod_fname + '.txt', 'a', encoding = 'utf-8') as text_file : text_file.write('\n%s' % ext_str) - + if param.write_html: with open(mod_fname + '.html', 'a', encoding = 'utf-8') as text_file: text_file.write('\n
\n%s' % pt_html(ext)) - + if param.write_json: ext_json_path = mod_fname + '.json' - + if ext_json_path not in mod_anl_jsons: mod_anl_jsons[ext_json_path] = [] - + mod_anl_jsons[ext_json_path].append(pt_json(ext)) - + if param.cse_verbose : print(ext) # Print Manifest/Metadata/Key Extension Info break - + for mod_anl_json_path, mod_anl_json_lists in mod_anl_jsons.items(): with open(mod_anl_json_path, 'w', encoding='utf-8') as jo: json.dump(mod_anl_json_lists, jo, indent=4) - + return rbe_pm_met_valid # Get CSE Key Hash Usages def get_key_usages(key_bitmap) : hash_usages = [] - + usage_bits = list(format(int.from_bytes(key_bitmap, 'little'), '0128b')) usage_bits.reverse() - + for usage_bit in range(len(usage_bits)) : if usage_bits[usage_bit] == '1' : hash_usages.append(key_dict[usage_bit] if usage_bit in key_dict else 'Unknown (%d)' % usage_bit) - + return hash_usages - + # Store and show CSE Analysis Errors def cse_anl_err(ext_err_msg, checked_hashes) : if checked_hashes is None : checked_hashes = ['',''] - + copy_file = not checked_hashes in cse_known_bad_hashes err_stor.append([ext_err_msg, copy_file]) - + if param.cse_unpack : if copy_file and param.cse_pause : input_col('\n%s' % ext_err_msg) else : print('\n%s' % ext_err_msg) - + # Check if CSE File System FTBL/EFST Dictionary exists def check_ftbl_id(vol_ftbl_id, ftbl_dict, vol_ftbl_pl) : plat_name = ftbl_efst_plat[vol_ftbl_pl] if vol_ftbl_pl in ftbl_efst_plat else 'Unknown' - + if vol_ftbl_id == -1 : msg_pad = ' ' if param.cse_unpack else '' # Message "Tab" spacing during unpacking ftbl_id_msg = col_m + '%sWarning: Could not find any File System Dictionary, assuming 0x0A!' % msg_pad + col_e if param.cse_unpack : print('\n%s' % ftbl_id_msg) else : warn_stor.append([ftbl_id_msg, False]) - + vol_ftbl_id = 0xA # When MFS/AFS > Volume Header > vol_ftbl_id is missing, assume FTBL/EFST Dictionary of 0xA (CON) elif '%0.2X' % vol_ftbl_id not in ftbl_dict['%0.2X' % vol_ftbl_pl] : msg_pad = ' ' if param.cse_unpack else '' # Message "Tab" spacing during unpacking @@ -7418,21 +7418,21 @@ def check_ftbl_id(vol_ftbl_id, ftbl_dict, vol_ftbl_pl) : msg_pad, vol_ftbl_pl, plat_name, vol_ftbl_id) + col_e if param.cse_unpack : print('\n%s' % ftbl_id_msg) else : warn_stor.append([ftbl_id_msg, False]) - + vol_ftbl_id = 0xA # When FTBL/EFST > Platform > Dictionary is missing, assume FTBL/EFST Dictionary of 0xA (CON) - + return vol_ftbl_id - + # Check if CSE File System FTBL/EFST Platform exists def check_ftbl_pl(vol_ftbl_pl, ftbl_dict) : plat_name = ftbl_efst_plat[vol_ftbl_pl] if vol_ftbl_pl in ftbl_efst_plat else 'Unknown' - + if vol_ftbl_pl == -1 : msg_pad = ' ' if param.cse_unpack else '' # Message "Tab" spacing during unpacking ftbl_pl_msg = col_m + '%sWarning: Could not find any File System Platform, assuming 0x01 (ICP)!' % msg_pad + col_e if param.cse_unpack : print('\n%s' % ftbl_pl_msg) else : warn_stor.append([ftbl_pl_msg, False]) - + vol_ftbl_pl = 0x1 # When MFS/AFS > Volume Header > vol_ftbl_pl is missing, assume FTBL/EFST Platform of 0x1 (ICP) elif '%0.2X' % vol_ftbl_pl not in ftbl_dict : msg_pad = ' ' if param.cse_unpack else '' # Message "Tab" spacing during unpacking @@ -7440,9 +7440,9 @@ def check_ftbl_pl(vol_ftbl_pl, ftbl_dict) : msg_pad, vol_ftbl_pl, plat_name) + col_e if param.cse_unpack : print('\n%s' % ftbl_pl_msg) else : warn_stor.append([ftbl_pl_msg, False]) - + vol_ftbl_pl = 0x1 # When FTBL/EFST > Platform is missing, assume FTBL/EFST Platform of 0x1 (ICP) - + return vol_ftbl_pl # Get CSE File System Integrity Table Structure Size @@ -7451,7 +7451,7 @@ def get_sec_hdr_size(variant,major,minor,hotfix,vol_ftbl_pl) : if (variant,major,minor) == ('CSSPS',4,4) or (variant,major,vol_ftbl_pl) == ('CSSPS',5,10) : return 0x28 if (variant,major) in [('CSME',11),('CSTXE',3),('CSTXE',4),('CSSPS',4),('CSSPS',5)] : return 0x34 if (variant,major) in [('CSME',12),('CSME',13),('CSME',14),('CSME',15)] : return 0x28 - + return 0x28 # Get CSE File System Configuration Record Structure Size @@ -7459,7 +7459,7 @@ def get_cfg_rec_size(variant,major,minor,hotfix,vol_ftbl_pl) : if (variant,major,minor) == ('CSSPS',4,4) or (variant,major,vol_ftbl_pl) == ('CSSPS',5,10) : return 0xC if (variant,major) in [('CSME',11),('CSME',12),('CSTXE',3),('CSTXE',4),('CSSPS',4),('CSSPS',5)] : return 0x1C if (variant,major) in [('CSME',13),('CSME',14),('CSME',15),('CSME',16),('CSSPS',6)] : return 0xC - + return 0xC # Get CSE File System Files start at 0 or not @@ -7468,7 +7468,7 @@ def get_vfs_start_0(variant,major,minor,hotfix) : # pylint: disable=W0613 if (variant,major,minor) in [('CSME',13,30)] : return True if (variant,major) in [('CSME',15),('CSME',16)] : return True if (variant,major) in [('CSME',11),('CSME',12),('CSME',13),('CSME',14),('CSTXE',3),('CSTXE',4),('CSSPS',4),('CSSPS',5),('CSSPS',6)] : return False - + return True # Get CSE File System Attributes & Configuration State @@ -7476,22 +7476,22 @@ def get_mfs_anl(mfs_state, mfs_parsed_idx, intel_cfg_hash_mfs, mfs_info, pch_ini vol_ftbl_id = -0x1 vol_ftbl_pl = -0x1 config_rec_size = get_cfg_rec_size(variant,major,minor,hotfix,vol_ftbl_pl) # Get CSE File System Configuration Record Structure Size - + if mfs_found and not param.cse_unpack : try : # Get CSE File System Attributes mfs_parsed_idx,intel_cfg_hash_mfs,mfs_info,pch_init_final,vol_ftbl_id,config_rec_size,vol_ftbl_pl = mfs_anl('NA',mfs_start,mfs_start + mfs_size,variant,vol_ftbl_id,vol_ftbl_pl,mfs_is_afs) - + # Get CSE File System Backup Attributes when Main lacks Intel Configuration if mfsb_found and 6 not in mfs_parsed_idx : mfs_parsed_idx,intel_cfg_hash_mfs,_,pch_init_final,_,_,_ = mfs_anl('NA',mfsb_start,mfsb_start + mfsb_size,variant,vol_ftbl_id,vol_ftbl_pl,mfs_is_afs) - + # CSE File System exists, determine its Configuration State if any(idx in mfs_parsed_idx for idx in [0,1,2,3,4,5,8]) : mfs_state = 'Initialized' elif any(idx in mfs_parsed_idx for idx in [7,9]) : mfs_state = 'Configured' except : # CSE File System analysis failed, maybe corrupted mfs_state = col_r + 'Error' + col_e - + return mfs_state, mfs_parsed_idx, intel_cfg_hash_mfs, mfs_info, pch_init_final, vol_ftbl_id, config_rec_size, vol_ftbl_pl # Analyze & Extract CSE File Systems @@ -7523,24 +7523,24 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m mfs_signature1 = mfs_buffer_init[:0x4] # Store 1st MFS Signature Tag (MFS 0x877855AA or MFSB 0x4D465342 or Scratch) mfs_signature2 = mfs_buffer_init[page_size:page_size + 0x4] # Store 2nd MFS Signature Tag (MFS 0x877855AA or Scratch) mfsb_reserved = mfs_buffer_init[0x8:0x20] == b'\xFF' * 0x18 # Check MFSB Reserved area - + # Check if MFS is in MFS Backup R0 state if mfs_signature1 == b'\x4D\x46\x53\x42' and mfsb_reserved : # MFSB Tag & MFSB R0 Reserved = 0xFF * 24 mfsb_hdr = get_struct(mfs_buffer_init, 0, MFS_Backup_Header_R0) # MFSB Header R0 Structure if param.cse_unpack : print('\n%s' % mfsb_hdr.mfs_print()) # Print Structure Info during CSE Unpacking mfs_info.append(mfsb_hdr.mfs_print()) # Store Structure Info during CSE Unpacking - + mfsb_buffer = mfs_buffer_init[ctypes.sizeof(mfsb_hdr):] # MFS Backup Buffer without Header mfsb_crc32 = mfsb_hdr.CRC32 # Intel CRC-32 of MFS Backup Buffer mea_crc32 = ~crccheck.crc.Crc32.calc(mfsb_buffer, initvalue=0) & 0xFFFFFFFF # MEA CRC-32 of MFS Backup Buffer mfsb_patterns = re.compile(br'\x01\x03\x02\x04').finditer(mfsb_buffer) # Each MFS Backup Chunk ends with 0x01030204 mfsb_end = re.compile(br'\xFF{32}').search(mfsb_buffer).start() # MFS Backup Buffer ends where enough Padding (0xFF) is found - + if mfsb_crc32 != mea_crc32 : _ = mfs_anl_msg(col_r + 'Error: MFS Backup Header CRC-32 0x%0.8X is INVALID, expected 0x%0.8X!' % (mfsb_crc32, mea_crc32) + col_e, 'error', True, False, False, []) else : _ = mfs_anl_msg(col_g + 'MFS Backup Header CRC-32 is VALID' + col_e, '', True, False, False, []) - + data_start = 0 # Starting Offset of each MFS Backup Chunk mfs_buffer_init = b'' # Actual MFS Buffer from converted MFS Backup state for pattern in mfsb_patterns : # Iterate over all 0x01030204 chunk endings @@ -7549,96 +7549,96 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m data_start = pattern.end() + 0x4 # Adjust Starting Offset to 0x01030204 + Padding Size mfs_buffer_init += mfsb_buffer[data_start:mfsb_end] # Append Last MFS Backup Chunk Contents as has no 0x01030204 ending mfs_buffer_init += b'\xFF' * (- len(mfs_buffer_init) % 0x2000) # Append EOF Alignment Padding based on MFS Page Size of 0x2000 - + # Check if MFS is in MFS Backup R1 state elif mfs_signature1 == b'\x4D\x46\x53\x42' : # MFSB Tag & MFSB R0 Reserved != 0xFF * 24 mfsb_hdr = get_struct(mfs_buffer_init, 0, MFS_Backup_Header_R1) # MFSB Header R1 Structure if param.cse_unpack : print('\n%s' % mfsb_hdr.mfs_print()) # Print Structure Info during CSE Unpacking mfs_info.append(mfsb_hdr.mfs_print()) # Store Structure Info during CSE Unpacking - + mfsb_rev = mfsb_hdr.Revision # MFSB Header R1 Revision Tag if mfsb_rev != 1 : # Validate MFSB Header Revision, should be 1 _ = mfs_anl_msg(col_r + 'Error: Unknown MFS Backup Header Revision %d at 0x%X!' % (mfsb_rev,mfs_start) + col_e, 'error', True, False, False, []) - + mfsb_len = mfsb_hdr.Entry6Offset # MFSB Header R1 Size based on 1st Entry Offset mfsb_hdr_data = mfs_buffer_init[:0x8] + b'\x00' * 4 + mfs_buffer_init[0xC:mfsb_len] # MFS Backup Header Data mfsb_crc32 = mfsb_hdr.HeaderCRC32 # Intel CRC-32 of MFS Backup Header Data with HeaderCRC32 = 0 mea_crc32 = crccheck.crc.Crc32.calc(mfsb_hdr_data) # MEA CRC-32 of MFS Backup Header Data with HeaderCRC32 = 0 - + if mfsb_crc32 != mea_crc32 : _ = mfs_anl_msg(col_r + 'Error: MFS Backup Header CRC-32 0x%0.8X is INVALID, expected 0x%0.8X!' % (mfsb_crc32,mea_crc32) + col_e, 'error', True, False, False, []) else : _ = mfs_anl_msg(col_g + 'MFS Backup Header CRC-32 is VALID' + col_e, '', True, False, False, []) - + mfsb_entry_6 = [6, mfsb_hdr.Entry6Offset, mfsb_hdr.Entry6Size] # MFSB Entry 6 (Intel Configuration) Info mfsb_entry_7 = [7, mfsb_hdr.Entry7Offset, mfsb_hdr.Entry7Size] # MFSB Entry 7 (OEM Configuration) Info mfsb_entry_9 = [9, mfsb_hdr.Entry9Offset, mfsb_hdr.Entry9Size] # MFSB Entry 9 (Manifest Backup) Info mfsb_entries = [mfsb_entry_6, mfsb_entry_7, mfsb_entry_9] # MFSB Entries 6,7,9 Info Storage - + for entry in mfsb_entries : entry_file = entry[0] # MFSB R1 Entry Low Level File Index entry_data = mfs_buffer_init[entry[1]:entry[1] + entry[2]] # MFSB R1 Entry Data - + entry_hdr = get_struct(entry_data, 0, MFS_Backup_Entry) # MFSB R1 Entry Structure if param.cse_unpack : print('\n%s' % entry_hdr.mfs_print()) # Print Structure Info during CSE Unpacking mfs_info.append(entry_hdr.mfs_print()) # Store Structure Info during CSE Unpacking - + entry_rev = entry_hdr.Revision # MFSB R1 Entry Revision Tag if entry_rev != 1 : # Validate MFSB Entry Revision, should be 1 _ = mfs_anl_msg(col_r + 'Error: Unknown MFS Backup Entry %d Revision %d at 0x%X!' % (entry_file,entry_rev,mfs_start) + col_e, 'error', True, False, False, []) - + hdr_len = ctypes.sizeof(MFS_Backup_Entry) # MFSB R1 Entry Size based on Structure - + hdr_data_crc = entry_data[:0x4] + b'\x00' * 0x4 + entry_data[0x8:0xC] - + hdr_crc32_int = entry_hdr.EntryCRC32 # Intel CRC-32 of MFSB R1 Entry Header with EntryCRC32 = 0 w/o DataCRC32 hdr_crc32_mea = crccheck.crc.Crc32.calc(hdr_data_crc) # MEA CRC-32 of MFSB R1 Entry Header with EntryCRC32 = 0 w/o DataCRC32 - + if hdr_crc32_int != hdr_crc32_mea : _ = mfs_anl_msg(col_r + 'Error: MFS Backup Entry %d Header CRC-32 0x%0.8X is INVALID, expected 0x%0.8X!' % ( entry_file,hdr_crc32_int,hdr_crc32_mea) + col_e, 'error', True, False, False, []) else : _ = mfs_anl_msg(col_g + 'MFS Backup Entry %d Header CRC-32 is VALID' % entry_file + col_e, '', True, False, False, []) - + file_data = entry_data[hdr_len:hdr_len + entry_hdr.Size] - + file_crc32_int = entry_hdr.DataCRC32 # Intel CRC-32 of MFSB R1 Entry Data (Low Level File + DataCRC32 = 0) file_crc32_mea = crccheck.crc.Crc32.calc(file_data) # MEA CRC-32 of MFSB R1 Entry Data (Low Level File + DataCRC32 = 0) - + if file_crc32_int != file_crc32_mea : _ = mfs_anl_msg(col_r + 'Error: MFS Backup Entry %d Data CRC-32 0x%0.8X is INVALID, expected 0x%0.8X!' % ( entry_file,file_crc32_int,file_crc32_mea) + col_e, 'error', True, False, False, []) else : _ = mfs_anl_msg(col_g + 'MFS Backup Entry %d Data CRC-32 is VALID' % entry_file + col_e, '', True, False, False, []) - + if param.cse_unpack : print(col_g + '\n Analyzing MFS Low Level File %d (%s) ...' % (entry_file, mfs_dict[entry_file]) + col_e) - + if entry_file in (6,7) : rec_folder = os.path.join(mfs_folder, '%0.3d %s' % (entry_file, mfs_dict[entry_file]), '') root_folder = rec_folder # Store File Root Folder for Local Path printing mfs_parsed_idx.append(entry_file) # Set MFS Backup Low Level File as Parsed - + pch_init_info = mfs_cfg_anl(entry_file, file_data, rec_folder, root_folder, config_rec_size, pch_init_info, vol_ftbl_id, vol_ftbl_pl) # Parse MFSB Config Records pch_init_final = pch_init_anl(pch_init_info) # Parse MFSB Initialization Tables and store their Platforms/Steppings - + if entry_file == 6 : intel_cfg_hash_mfs = [get_hash(file_data, 0x20), get_hash(file_data, 0x30)] # Store MFSB Intel Configuration Hashes - + elif entry_file == 9 and man_pat.search(file_data[:0x20]) : file_9_folder = os.path.join(mfs_folder, '%0.3d %s' % (entry_file, mfs_dict[entry_file]), '') file_9_data_path = os.path.join(file_9_folder, 'FTPR.man') # MFS Manifest Backup Contents Path mfs_write(file_9_folder, file_9_data_path, file_data) # Store MFS Manifest Backup Contents mfs_parsed_idx.append(entry_file) # Set MFS Backup Low Level File as Parsed - + ext_print,mn2_signs,_ = ext_anl(file_data, '$MN2', 0x1B, file_end, [variant,major,minor,hotfix,build,year,month,variant_p], 'FTPR.man', [mfs_parsed_idx,intel_cfg_hash_mfs], [pch_init_final,config_rec_size,vol_ftbl_id,vol_ftbl_pl]) # Get Manifest Backup Extension Info - + if param.cse_unpack : if param.cse_pause : print('\n MN2: %s' % mn2_signs[1]) # Debug print(' MEA: %s' % mn2_signs[2]) # Debug - + if mn2_signs[3] : if param.cse_pause : input_col(col_m + '\n RSA Signature of %s is UNKNOWN!' % mfs_dict[entry_file] + col_e) # Debug @@ -7651,24 +7651,24 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m input_col(col_r + '\n RSA Signature of %s is INVALID!' % mfs_dict[entry_file] + col_e) # Debug else : print(col_r + '\n RSA Signature of %s is INVALID!' % mfs_dict[entry_file] + col_e) - + if not param.cse_verbose : print('\n%s' % ext_print[1][0]) # Print Manifest Backup Manifest Info else : print() - + for man_pt in ext_print[1] : if param.cse_verbose : print(man_pt) mfs_txt(man_pt, file_9_folder, os.path.join(file_9_folder + 'FTPR.man'), 'a', False) # Store MFS Manifest Backup Extension Info - + return mfs_parsed_idx, intel_cfg_hash_mfs, mfs_info, pch_init_final, vol_ftbl_id, config_rec_size, vol_ftbl_pl - + # Verify that MFS Partition can be parsed by mfs_anl elif b'\x87\x78\x55\xAA' not in (mfs_signature1, mfs_signature2) : # Check 1st & 2nd System Page MFS Signature Tag _ = mfs_anl_msg(col_r + 'Error: Skipped MFS partition at 0x%X, unrecognizable format!' % mfs_start + col_e, 'error', True, False, False, []) - + if not param.cse_unpack : raise Exception('BAD_MFS_FORMAT') # Try-Except used outside of unp86, trigger exception to show Error at MFS state - + return mfs_parsed_idx, intel_cfg_hash_mfs, mfs_info, pch_init_final, vol_ftbl_id, config_rec_size, vol_ftbl_pl - + # MFS Size related Variable Initialization (must be after MFSB R0) mfs_size = len(mfs_buffer_init) # MFS Total Length page_count = mfs_size // page_size # MFS Total Pages Count @@ -7676,7 +7676,7 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m dat_count = page_count - sys_count - 1 # MFS Data Pages Count #chunks_max_sys = sys_count * ((page_size - page_hdr_size - index_size_sys) // (index_size_sys + chunk_all_size)) # MFS Maximum System Chunks Count chunks_max_dat = dat_count * ((page_size - page_hdr_size) // (index_size_dat + chunk_all_size)) # MFS Maximum Data Chunks Count (= Actual) - + # Sort MFS System & Data Pages for page_index in range(page_count) : page_start = page_index * page_size # Page Offset @@ -7684,18 +7684,18 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m if page_hdr.FirstChunkIndex != 0 : chunks_count_sys = min(chunks_count_sys, page_hdr.FirstChunkIndex) # Store MFS Actual System Chunks Count # Page Number for System Page Sorting, Page First Chunk Index for Data Page Sorting, Page Contents mfs_page_init.append([page_hdr.PageNumber, page_hdr.FirstChunkIndex, mfs_buffer_init[page_start:page_start + page_size]]) - + for i in range(len(mfs_page_init)) : # Parse all MFS unsorted System & Data Pages if mfs_page_init[i][1] == 0 : sys_page_sorted.append([mfs_page_init[i][0], mfs_page_init[i][2]]) # System Pages are sorted via Page Number else : dat_page_sorted.append([mfs_page_init[i][1], mfs_page_init[i][2]]) # Data Pages are sorted via Page First Chunk Index sys_page_sorted = [i[1] for i in sorted(sys_page_sorted, key=lambda sys: sys[0])] # Store System Pages after Page Number sorting dat_page_sorted = [i[1] for i in sorted(dat_page_sorted, key=lambda dat: dat[0])] # Store Data Pages after Page First Chunk Index sorting mfs_sorted = sys_page_sorted + dat_page_sorted # Store total MFS sorted System & Data Pages - + mfs_pages_pt = ext_table([col_y + 'Type' + col_e, col_y + 'Signature' + col_e, col_y + 'Number' + col_e, col_y + 'Erase Count' + col_e, col_y + 'Next Erase' + col_e, col_y + 'First Chunk' + col_e, col_y + 'CRC-8' + col_e, col_y + 'Reserved' + col_e], True, 1) mfs_pages_pt.title = col_y + 'MFS Page Records' + col_e - + # Parse each MFS Page sequentially for mfs_page in mfs_sorted : page_hdr = get_struct(mfs_page, 0, MFS_Page_Header) # Page Header Structure @@ -7708,7 +7708,7 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m page_hdr_crc8_int = page_hdr.CRC8 # Intel CRC-8 of Page Header (0x12) with initial value of 1 page_reserved = page_hdr.Reserved # Page Reserved Data page_type = 'System' if page_chunk_first == 0 else 'Data' # Page System or Data Type - + # MEA CRC-8 of System/Data/Scratch Page Header (0x12) with initial value of 1 if page_tag == 0xAA557887 : page_hdr_crc8_mea = crccheck.crc.Crc8.calc(page_hdr_data[:-2] + bytes(page_hdr_data[-1]), initvalue = 1) @@ -7716,18 +7716,18 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m page_type = 'Scratch' # Only one Scratch Page initially exists at the MFS if not page_number : page_hdr_crc8_mea = 0 # Workaround only for Alpha CSME 11.0.0.1100 firmware (completely empty MFS Page Header) else : page_hdr_crc8_mea = crccheck.crc.Crc8.calc(b'\x87\x78\x55\xAA' + page_hdr_data[4:-2] + bytes(page_hdr_data[-1]), initvalue = 1) # Add MFS Signature - + mfs_pages_pt.add_row([page_type, '%0.8X' % page_tag, page_number, page_erase_count, page_erase_next, page_chunk_first, '0x%0.2X' % page_hdr_crc8_int, '0x%X' % page_reserved]) - + # Verify System/Data/Scratch Page CRC-8 if page_hdr_crc8_mea != page_hdr_crc8_int : mfs_tmp_page = mfs_anl_msg(col_r + 'Error: MFS %s Page %d Header CRC-8 0x%0.2X is INVALID, expected 0x%0.2X!' % (page_type, page_number, page_hdr_crc8_int, page_hdr_crc8_mea) + col_e, 'error', True, True, False, mfs_tmp_page) else : mfs_tmp_page = mfs_anl_msg(col_g + 'MFS %s Page %d Header CRC-8 is VALID' % (page_type, page_number) + col_e, '', True, True, False, mfs_tmp_page) - + if page_tag != 0xAA557887 : continue # Skip Scratch Page after CRC-8 check - + # MFS System Page if page_type == 'System' : chunk_count = (page_size - page_hdr_size - index_size_sys) // (index_size_sys + chunk_all_size) # System Page Chunks have a 2-byte Index after Page Header @@ -7735,7 +7735,7 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m index_data_obf = mfs_page[page_hdr_size:page_hdr_size + index_size] # System Page Total Obfuscated Chunk Indexes Buffer index_values_obf = struct.unpack('%dH' % (chunk_count + 1), index_data_obf) # System Page Total Obfuscated Chunk Indexes List, each Index is 2 bytes chunk_start = page_hdr_size + index_size # System Page First Chunk Offset - + # Calculate actual System Page Chunk Indexes chunk_index = 0 # Unobfuscated System Page Chunk Index chunk_indexes = [] # Unobfuscated System Page Chunk Indexes @@ -7744,7 +7744,7 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m if index_values_obf[i] & 0xC000 : break # Skip all the Unused System Page Chunks when Bits 0-1 = 1 (0xC000) = Unused Entry chunk_index = Crc16_14(chunk_index) ^ index_values_obf[i] # Unobfuscated System Page Chunk Index via reverse CRC-16 14-bit (no 0 and 1) chunk_indexes.append(chunk_index) # Store all Unobfuscated System Page Chunk Indexes (subset of index_values_obf when Unused Entries exist) - + # Parse all Used System Page Chunks chunk_healthy = 0 # System Page Healthy Chunks Count chunk_used_count = len(chunk_indexes) # System Page Total Used Chunks Count @@ -7753,10 +7753,10 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m chunk_all = mfs_page[chunk_start + chunk_all_size * i:chunk_start + chunk_all_size * i + chunk_all_size] # System Page Chunk with CRC-16 (0x42) chunk_raw = chunk_all[:-2] # System Page Chunk without CRC-16 (0x40) all_chunks_dict[chunk_index] = chunk_raw # Store System Page Chunk Index & Contents - + chunk_crc16_int = int.from_bytes(chunk_all[0x40:0x42], 'little') # Intel CRC-16 of Chunk (0x40) with initial value of 0xFFFF chunk_crc16_mea = crccheck.crc.Crc16.calc(chunk_raw + struct.pack(' Chunk %d CRC-16 0x%0.4X is INVALID, expected 0x%0.4X!' @@ -7764,10 +7764,10 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m else : #mfs_tmp_page = mfs_anl_msg(col_g + 'MFS %s Page %d > Chunk %d CRC-16 is VALID' % (page_type, page_number, chunk_index) + col_e, '', True, True, True, mfs_tmp_page) chunk_healthy += 1 - + if chunk_used_count and chunk_used_count == chunk_healthy : mfs_tmp_page = mfs_anl_msg(col_g + 'All MFS %s Page %d Chunks (%d) CRC-16 are VALID' % (page_type, page_number, chunk_used_count) + col_e, '', True, True, True, mfs_tmp_page) - + # MFS Data Page elif page_type == 'Data' : chunk_count = (page_size - page_hdr_size) // (index_size_dat + chunk_all_size) # Data Page Chunks have a 1-byte Index after Page Header @@ -7775,7 +7775,7 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m index_data = mfs_page[page_hdr_size:page_hdr_size + index_size] # Data Page Total Chunk Indexes Buffer index_values = struct.unpack('%dB' % chunk_count, index_data) # Data Page Total Chunk Indexes List, each index is 1 byte chunk_start = page_hdr_size + index_size # Data Page First Chunk Offset - + # Parse all Used Data Page Chunks chunk_healthy = 0 # Data Page Healthy Chunks Count chunk_used_count = 0 # Data Page Total Used Chunks Count @@ -7788,18 +7788,18 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m all_chunks_dict[chunk_index] = chunk_raw # Store Data Page Chunk Index & Contents chunk_crc16_int = int.from_bytes(chunk_all[0x40:0x42], 'little') # Intel CRC-16 of Chunk (0x40) with initial value of 0xFFFF chunk_crc16_mea = crccheck.crc.Crc16.calc(chunk_raw + struct.pack(' Chunk %d CRC-16 0x%0.4X is INVALID, expected 0x%0.4X!' % (page_type, page_number, chunk_index, chunk_crc16_int, chunk_crc16_mea) + col_e, 'error', True, True, True, mfs_tmp_page) else : #mfs_tmp_page = mfs_anl_msg(col_g + 'MFS %s Page %d > Chunk %d CRC-16 is VALID' % (page_type, page_number, chunk_index) + col_e, '', True, True, True, mfs_tmp_page) - chunk_healthy += 1 - + chunk_healthy += 1 + if chunk_used_count and chunk_used_count == chunk_healthy : mfs_tmp_page = mfs_anl_msg(col_g + 'All MFS %s Page %d Chunks (%d) CRC-16 are VALID' % (page_type, page_number, chunk_used_count) + col_e, '', True, True, True, mfs_tmp_page) - + # Print/Store MFS Page Records during CSE Unpacking if param.cse_unpack : print('\n%s' % mfs_pages_pt) # Show MFS Page Records Log before messages @@ -7807,13 +7807,13 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m if page_msg[1] == 'error' and param.cse_pause : input_col('\n%s' % page_msg[0]) else : print('\n%s' % page_msg[0]) mfs_info.append(mfs_pages_pt) # Store MFS Page Records Log during CSE Unpacking - + # Build MFS Total System Chunks Buffer all_mfs_sys = bytearray(chunks_count_sys * chunk_raw_size) # Empty System Area Buffer for i in range(chunks_count_sys) : # The final System Area Buffer must include all empty chunks for proper File Allocation Table parsing if i in all_chunks_dict : all_mfs_sys[i * chunk_raw_size:(i + 1) * chunk_raw_size] = bytearray(all_chunks_dict[i]) - + # Parse MFS System Volume Structure if not all_chunks_dict : _ = mfs_anl_msg(col_r + 'Error: MFS final System Area Buffer is empty!' + col_e, 'error', True, False, False, []) @@ -7835,11 +7835,11 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m mea_total_size = chunks_count_sys * chunk_raw_size + chunks_max_dat * chunk_raw_size # Size of MFS System & Data Volume via MEA if vol_total_size != mea_total_size : _ = mfs_anl_msg(col_r + 'Error: Detected MFS System Volume Size mismatch!' + col_e, 'error', True, False, False, []) else : _ = mfs_anl_msg(col_g + 'MFS System Volume Size is VALID' + col_e, '', True, False, False, []) - + # Re-calculate CSE File System Integrity Table and Configuration Record Structure Sizes once FTBL/EFST Platform is known sec_hdr_size = get_sec_hdr_size(variant,major,minor,hotfix,vol_ftbl_pl) # Get CSE File System Integrity Table Structure Size config_rec_size = get_cfg_rec_size(variant,major,minor,hotfix,vol_ftbl_pl) # Get CSE File System Configuration Record Structure Size - + # Parse MFS File Allocation Table fat_count = vol_file_rec + chunks_max_dat # MFS FAT Value Count (Low Level Files + their Data Chunks) fat_trail = len(all_mfs_sys) - fat_count * 2 - vol_hdr_size # MFS FAT Value End Trail Count @@ -7850,43 +7850,43 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m else : file_chunks = b'' # Initial MFS Low Level File Contents Buffer fat_value = fat_values[index] # Initial Used File FAT Value - + # Parse Data/Chunk FAT Values for each Used Low Level File while True : # Data FAT Values (Low Level File Chunks) start after Volume FAT Values (Low Level File Numbers/1st Chunk) if fat_value < vol_file_rec : _ = mfs_anl_msg(col_r + 'Error: Detected MFS File %d > FAT Value %d less than Volume Files Count %d!' % (index,fat_value,vol_file_rec) + col_e, 'error', True, False, False, []) break # Critical error while parsing Used File FAT Value - + # Data Page Chunks start after System Page Chunks and their Volume FAT Values file_chunk_index = chunks_count_sys + fat_value - vol_file_rec # Determine File Chunk Index for MFS Chunk Index & Data Dictionary use if file_chunk_index not in all_chunks_dict : # The File Chunk index/key must exist at the MFS Chunk Index & Data Dictionary _ = mfs_anl_msg(col_r + 'Error: Detected MFS File %d > Chunk %d not in Total Chunk Index/Data Area!' % (index,file_chunk_index) + col_e, 'error', True, False, False, []) break # Critical error while parsing Used File FAT Value - + file_chunk = all_chunks_dict[file_chunk_index] # Get File Chunk contents from the MFS Chunk Index & Data Dictionary fat_value = fat_values[fat_value] # Get Next Chunk FAT Value by using the current value as List index (starts from 0) - + # Small FAT Values (1 - 64) are markers for both EOF and Size of last Chunk if 1 <= fat_value <= chunk_raw_size : file_chunks += file_chunk[:fat_value] # Append the last File Chunk with its size adjusted based on the EOF FAT Value marker break # File ends when the Next FAT Value is between 1 and 64 (EOF marker) - + file_chunks += file_chunk # Append File Chunk Contents to the MFS Low Level File Contents Buffer - + mfs_files.append([index, file_chunks]) # Store MFS Low Level File Index & Contents - + # Check that the MFS FAT Trail Contents are empty/zeroes if all_mfs_sys[vol_hdr_size + fat_count * 2:] != b'\x00' * fat_trail : _ = mfs_anl_msg(col_r + 'Error: Detected additional MFS System Buffer contents after FAT ending!' + col_e, 'error', True, False, False, []) - + # Determine if the MFS has any Low Level Files based on their Contents mfs_has_files = [mfs_file[1] for mfs_file in mfs_files] != [None] * len(mfs_files) - + # Parse Reserved MFS Low Level Files for mfs_file in mfs_files : if vfs_starts_at_0 or not mfs_has_files : break # Skip at MFS which are empty or w/o Reserved Low Level Files - + # Parse MFS Low Level File 0 (Unknown) if mfs_file[1] and mfs_file[0] == 0 : if param.cse_unpack : print(col_g + '\n Analyzing MFS Low Level File %d (%s) ...' % (mfs_file[0], mfs_dict[mfs_file[0]]) + col_e) @@ -7894,7 +7894,9 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m file_folder = os.path.join(mfs_folder, '%0.3d %s' % (mfs_file[0], mfs_dict[mfs_file[0]]), '') file_path = os.path.join(file_folder, 'Contents.bin') # MFS Low Level File Path mfs_write(file_folder, file_path, mfs_file[1]) # Store MFS Low Level File - + with open(os.path.join(mfs_folder, '%0.3d %s%s' % (mfs_file[0], mfs_dict[mfs_file[0]], '.bin')),"wb") as mfs_bin: + mfs_bin.write(mfs_file[1]) # Store solid binary + # Parse MFS Low Level Files 1 (Unknown), 2-3 (Anti-Replay), 4 (SVN Migration) and 5 (Quota Storage) # At AFSP (CSTXE > AFS), MFS/AFS Low Level Files 6 & 7 are not Intel & OEM Configurations (Unknown) elif mfs_file[1] and ((mfs_file[0] in (1,2,3,4,5)) or (mfs_is_afs and mfs_file[0] in (6,7))) : @@ -7903,7 +7905,7 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m mfs_parsed_idx.append(mfs_file[0]) # Set MFS Low Level File as Parsed file_folder = os.path.join(mfs_folder, '%0.3d %s' % (mfs_file[0], mfs_file_name), '') file_data_path = os.path.join(file_folder, 'Contents.bin') # MFS Low Level File Contents Path - + # MFS Low Level File 5 Integrity is present only at CSME >= 12 # MFS Low Level File 4 Integrity is present only at non-CSTXE if (mfs_file[0] == 5 and not (variant == 'CSME' and major >= 12)) or (mfs_file[0] == 4 and mfs_is_afs) : @@ -7919,9 +7921,11 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m file_sec_path = os.path.join(file_folder, 'Integrity.bin') # MFS Low Level File Integrity Path mfs_write(file_folder, file_sec_path, file_sec) # Store MFS Low Level File Integrity mfs_txt(file_sec_hdr.mfs_print(), file_folder, file_sec_path, 'w', False) # Store/Print MFS Low Level File Integrity Info - + mfs_write(file_folder, file_data_path, file_data) # Store MFS Low Level File Contents - + with open(os.path.join(mfs_folder, '%0.3d %s%s' % (mfs_file[0], mfs_dict[mfs_file[0]], '.bin')),"wb") as mfs_bin: + mfs_bin.write(mfs_file[1]) # Store solid binary + # Parse MFS Low Level File 6 (Intel Configuration) and 7 (OEM Configuration) elif mfs_file[1] and mfs_file[0] in (6,7) : # Create copy of input firmware with clean/unconfigured MFS @@ -7931,7 +7935,7 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m mfstool_path = os.path.join(mea_dir, 'mfstool', '') mfs_tmpl_name = 'AFS_region_%sK.bin' % (mfs_size // 1024) mfs_tmpl_path = os.path.join(mfstool_path, mfs_tmpl_name) - + # MFS Templates depend on their Size (256K,400K,1272K), Volume File Record Count (256,512,1024,2048 etc) and # Total Volume Size (0x39240,0x58B80,0x58F80,0x11D900,0x11E100 etc). When the Volume File Record Count and/or # the Total Volume Size increase, a new template must be created with adjusted Volume Header Info but also @@ -7943,27 +7947,27 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m # Note that, at CSTXE, the Initialized AFS Size is variable as it expands during CSE operation at DevExp # SPI Region based on operational needs. That is OK because CSTXE does not need AFS cleaning either way # due to its use of FTPR > intl.cfg and fitc.cfg files as base even if its RGN includes the 256K MFS. - + if os.path.isfile(mfs_tmpl_path) : temp_dir = os.path.join(mfstool_path, 'temp', '') if os.path.isdir(temp_dir) : shutil.rmtree(temp_dir) os.mkdir(temp_dir) - + with open(os.path.join(temp_dir, 'intel.cfg'), 'wb') as o : # noinspection PyTypeChecker o.write(mfs_file[1]) - + temp_mfs_path = os.path.join(mfstool_path, 'MFS_TEMP.bin') if os.path.isfile(temp_mfs_path) : os.remove(temp_mfs_path) clean_mfs_path = os.path.join(mfstool_path, 'MFS_CLEAN.bin') if os.path.isfile(clean_mfs_path) : os.remove(clean_mfs_path) - + with open(mfs_tmpl_path, 'rb') as mfs_tmpl : mfs_tmpl_new = bytearray(mfs_tmpl.read()) - + tmpl_vol_size = int.from_bytes(mfs_tmpl_new[0x10C:0x110], 'little') # Get template MFS Volume Size - + start_diff = (vol_total_size - tmpl_vol_size) // chunk_raw_size # Calculate Data Page First Chunk difference - + # Parse template MFS and adjust all Data Pages First Chunk page_offset = 0 # First Page Offset (System) for i in range(page_count) : @@ -7972,49 +7976,51 @@ def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, m mfs_tmpl_new[page_offset + 0xE:page_offset + 0x10] = struct.pack('= 8 and fs_id != 1 : # File System ID for MFS Home Directory (Low Level File >= 8) is 1 (home) _ = mfs_anl_msg(col_r + 'Error: Detected bad File System ID %d at MFS Home Directory > %0.3d %s' % (fs_id, file_index, file_name) + col_e, 'error', True, False, False, []) - + # MFS Home Directory Record Nested Records Count file_records = divmod(len(file_data), home_rec_size)[0] - + # MFS Home Directory Record is a Folder Marker if file_name in ('.','..') : folder_path = os.path.normpath(os.path.join(root_folder, file_name, '')) # Set currently working MFS Home Directory Record/Folder Path rec_path = os.path.relpath(folder_path, start=init_folder) if file_index >= 8 else mfs_type[fs_id] # Set actual Record Path for printing - + if mfs_parsed_idx[-1] != 8 : continue # Skip logging & further parsing for Current (.) & Parent (..) directories of Low Level Files after 8 (home) - + # Append MFS Home Directory Record/Folder Info to Log if sec_hdr_size == 0x34 : # noinspection PyUnboundLocalVariable mfs_pt.add_row([file_index, rec_path, 'Folder', '', ['No','Yes'][integrity], integrity_salt, ['No','Yes'][encryption], sec_svn, sec_encr_nonce, ['No','Yes'][anti_replay], sec_ar_idx, sec_ar_random, sec_ar_counter, ['Intel','Other'][key_type], unix_rights, user_id, group_id, acc_unk_flags, unk_salt, sec_hmac, sec_unk_flags]) - + elif sec_hdr_size == 0x28 : # noinspection PyUnboundLocalVariable mfs_pt.add_row([file_index, rec_path, 'Folder', '', ['No','Yes'][integrity], integrity_salt, ['No','Yes'][encryption], sec_svn, ['No','Yes'][anti_replay], sec_ar_idx, sec_ar_random, sec_ar_counter, ['Intel','Other'][key_type], unix_rights, user_id, group_id, acc_unk_flags, unk_salt, sec_hmac, sec_aes_nonce, sec_unk_flags]) - + continue # Log but skip further parsing of Current (.) & Parent (..) Low Level File 8 (home) directories - + # MFS Home Directory Record is a File (Type 0) if rec_type == 0 : file_path = os.path.normpath(os.path.join(root_folder, file_name)) # Set MFS Home Directory Record/File Path @@ -8246,21 +8252,21 @@ def mfs_home_anl(mfs_files, file_buffer, file_records, root_folder, home_rec_siz file_rec_p = file_rec.mfs_print() # Get MFS Home Directory Record/File PLTable Object for printing adjustments file_rec_p.add_row(['Path', rec_path]) # Add MFS Home Directory Record/File Local Path for printing mfs_txt(file_rec_p, os.path.normpath(os.path.join(root_folder)), file_path, 'w', False) # Store/Print MFS Home Directory Record/File Info - + if integrity : # Store & Print MFS Home Directory Record/File Integrity sec_path = os.path.normpath(os.path.join(root_folder, file_name + '_integrity')) # Set MFS Home Directory Record/File Integrity Path mfs_write(os.path.normpath(os.path.join(root_folder)), sec_path, file_sec) # Store MFS Home Directory Record/File Integrity Contents mfs_txt(sec_hdr.mfs_print(), os.path.normpath(os.path.join(root_folder)), sec_path, 'w', False) # Store/Print MFS Home Directory Record/File Integrity Info - + # Append MFS Home Directory Record/File Info to Log if sec_hdr_size == 0x34 : mfs_pt.add_row([file_index, rec_path, 'File', '0x%X' % len(file_data), ['No','Yes'][integrity], integrity_salt, ['No','Yes'][encryption], sec_svn, sec_encr_nonce, ['No','Yes'][anti_replay], sec_ar_idx, sec_ar_random, sec_ar_counter, ['Intel','Other'][key_type], unix_rights, user_id, group_id, acc_unk_flags, unk_salt, sec_hmac, sec_unk_flags]) - + elif sec_hdr_size == 0x28 : mfs_pt.add_row([file_index, rec_path, 'File', '0x%X' % len(file_data), ['No','Yes'][integrity], integrity_salt, ['No','Yes'][encryption], sec_svn, ['No','Yes'][anti_replay], sec_ar_idx, sec_ar_random, sec_ar_counter, ['Intel','Other'][key_type], unix_rights, user_id, group_id, acc_unk_flags, unk_salt, sec_hmac, sec_aes_nonce, sec_unk_flags]) - + # MFS Home Directory Record is a Folder (Type 1) else : folder_path = os.path.normpath(os.path.join(root_folder, file_name, '')) # Set currently working MFS Home Directory Record/Folder Path @@ -8268,47 +8274,47 @@ def mfs_home_anl(mfs_files, file_buffer, file_records, root_folder, home_rec_siz file_rec_p = file_rec.mfs_print() # Get MFS Home Directory Record/Folder PLTable Object for printing adjustments file_rec_p.add_row(['Path', rec_path]) # Add MFS Home Directory Record/File Local Path for printing mfs_txt(file_rec_p, folder_path, folder_path, 'w', False) # Store/Print MFS Home Directory Record/Folder Info - + if integrity : # Store & Print MFS Home Directory Record/Folder Integrity sec_path = os.path.normpath(os.path.join(root_folder, file_name + '_integrity')) # Set MFS Home Directory Record/Folder Integrity Path mfs_write(os.path.normpath(os.path.join(root_folder)), sec_path, file_sec) # Store MFS Home Directory Record/Folder Integrity Contents mfs_txt(sec_hdr.mfs_print(), folder_path, folder_path + '_integrity', 'w', False) # Store/Print MFS Home Directory Record/Folder Integrity Info - + # Append MFS Home Directory Record/Folder Info to Log if sec_hdr_size == 0x34 : mfs_pt.add_row([file_index, rec_path, 'Folder', '', ['No','Yes'][integrity], integrity_salt, ['No','Yes'][encryption], sec_svn, sec_encr_nonce, ['No','Yes'][anti_replay], sec_ar_idx, sec_ar_random, sec_ar_counter, ['Intel','Other'][key_type], unix_rights, user_id, group_id, acc_unk_flags, unk_salt, sec_hmac, sec_unk_flags]) - + elif sec_hdr_size == 0x28 : mfs_pt.add_row([file_index, rec_path, 'Folder', '', ['No','Yes'][integrity], integrity_salt, ['No','Yes'][encryption], sec_svn, ['No','Yes'][anti_replay], sec_ar_idx, sec_ar_random, sec_ar_counter, ['Intel','Other'][key_type], unix_rights, user_id, group_id, acc_unk_flags, unk_salt, sec_hmac, sec_aes_nonce, sec_unk_flags]) - + mfs_home_anl(mfs_files, file_data, file_records, folder_path, home_rec_size, sec_hdr_size, mfs_parsed_idx, init_folder, mfs_pt) # Recursively parse all Folder Records - + # Parse all FTBL-based MFS Home Directory Low Level Files # noinspection PyUnusedLocal def mfs_home13_anl(mfs_file_idx, mfs_file_data, vol_ftbl_id, sec_hdr_size, mfs_home13_dir, mfs_parsed_idx, mfs_pt, ftbl_dict, vol_ftbl_pl) : fvalue = ['No','Yes'] - + file_data = mfs_file_data if mfs_file_data else b'' # MFS Home Directory File Contents vol_ftbl_pl = check_ftbl_pl(vol_ftbl_pl, ftbl_dict) # Check if MFS Volume FTBL Platform exists vol_ftbl_id = check_ftbl_id(vol_ftbl_id, ftbl_dict, vol_ftbl_pl) # Check if MFS Volume FTBL Dictionary exists ftbl_dict_id = '%0.2X' % vol_ftbl_id # FTBL Dictionary ID Tag ftbl_plat_id = '%0.2X' % vol_ftbl_pl # FTBL Platform ID Tag - + if ftbl_plat_id not in ftbl_dict or ftbl_dict_id not in ftbl_dict[ftbl_plat_id] or 'FTBL' not in ftbl_dict[ftbl_plat_id][ftbl_dict_id] : if ftbl_dict : _ = mfs_anl_msg(col_m + 'Warning: File Table %s > %s does not exist!' % (ftbl_plat_id,ftbl_dict_id) + col_e, '', True, False, False, []) rec_path = os.path.normpath(os.path.join('/Unknown', '%d.bin' % mfs_file_idx)) # Set generic/unknown File local path when warnings occur rec_file = os.path.normpath(mfs_home13_dir + rec_path) # Set generic/unknown File actual path when warnings occur rec_parent = os.path.normpath(os.path.join(mfs_home13_dir, 'Unknown')) # Set generic/unknown parent Folder actual path when warnings occur - + mfs_write(rec_parent, rec_file, file_data) # Store File to currently working Folder - + # Append MFS Home Directory File Info to Log if sec_hdr_size == 0x28 : mfs_pt.add_row(['%0.4d' % mfs_file_idx, rec_path, 'Unknown', '0x%X' % len(file_data), 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown']) - + elif sec_hdr_size == 0x34 : mfs_pt.add_row(['%0.4d' % mfs_file_idx, rec_path, 'Unknown', '0x%X' % len(file_data), 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown']) @@ -8317,10 +8323,10 @@ def mfs_home13_anl(mfs_file_idx, mfs_file_data, vol_ftbl_id, sec_hdr_size, mfs_h ftbl_entry = ftbl_dict[ftbl_plat_id][ftbl_dict_id]['FTBL'][ftbl_file_id].split(',') # Split FTBL Entry string data ftbl_entry = [ftbl_entry[0]] + [int(s) for s in ftbl_entry[1:]] # Convert FTBL Entry non-path string values to integers ftbl_path,ftbl_acc_int,ftbl_acc_enc,ftbl_acc_arp,ftbl_acc_unk,ftbl_group_id,ftbl_user_id,ftbl_vfs_id,ftbl_unk = ftbl_entry - + if ftbl_vfs_id == mfs_file_idx : mfs_parsed_idx.append(mfs_file_idx) - + # Remember to also adjust FTBL_Entry, param.mfs_ftbl & efs_anl ftbl_pt = ext_table(['Field', 'Value'], False, 1) ftbl_pt.title = col_y + 'File Table Entry' + col_e @@ -8334,13 +8340,13 @@ def mfs_home13_anl(mfs_file_idx, mfs_file_data, vol_ftbl_id, sec_hdr_size, mfs_h ftbl_pt.add_row(['User ID', '0x%0.4X' % ftbl_user_id]) ftbl_pt.add_row(['VFS ID', '%0.4d' % ftbl_vfs_id]) ftbl_pt.add_row(['Unknown', '{0:064b}b'.format(ftbl_unk)]) - + rec_path = os.path.normpath(ftbl_path + ' (%0.4d)' % mfs_file_idx) # Get File local path from FTBL Dictionary sec_path = os.path.normpath(ftbl_path + ' (%0.4d)' % mfs_file_idx + '_integrity') # Create File Integrity local path rec_file = os.path.normpath(mfs_home13_dir + rec_path) # Set File actual path from FTBL Dictionary sec_file = os.path.normpath(mfs_home13_dir + sec_path) # Set File Integrity actual path from FTBL Dictionary rec_parent = os.path.normpath(os.path.dirname(rec_file)) # Adjust parent Folder actual path from FTBL Dictionary - + # Initialize Integrity related variables sec_hmac, sec_ar_random, sec_ar_counter, sec_svn, sec_ar_idx, sec_aes_nonce, sec_unk_flags = [''] * 7 sec_unk0, sec_ar, sec_encr, sec_unk1, sec_unk2, sec_unk3, sec_unk4 = [0] * 7 @@ -8350,19 +8356,19 @@ def mfs_home13_anl(mfs_file_idx, mfs_file_data, vol_ftbl_id, sec_hdr_size, mfs_h file_sec = b'' sec_unk = '' sec_extra_size = 0 - + # Perform Integrity related actions if ftbl_acc_int : # Split MFS Home Directory Low Level File Contents & Integrity, if Integrity Protection is present file_data = mfs_file_data[:-sec_hdr_size] if mfs_file_data else b'' # MFS Home Directory Low Level File Contents without Integrity file_sec = mfs_file_data[-sec_hdr_size:] if mfs_file_data else b'' # MFS Home Directory Low Level File Integrity without Contents - + # Parse MFS Home Directory Low Level File Integrity Info if file_sec : - + if sec_hdr_size == 0x28 : sec_hdr = get_struct(file_sec, 0, sec_hdr_struct[sec_hdr_size]) # MFS Home Directory Low Level File Integrity Structure - + # Some files have extra 0x10 Unknown data at the end of the 0x28 Integrity Structure (0x38). # We need to know the exact size of the full Integrity Structure in order to split the file. # For the life of me I cannot find any indicator at FTBL or VFS that those extra 0x10 exist. @@ -8373,12 +8379,12 @@ def mfs_home13_anl(mfs_file_idx, mfs_file_data, vol_ftbl_id, sec_hdr_size, mfs_h file_sec = mfs_file_data[-(sec_hdr_size + sec_extra_size):] if mfs_file_data else b'' sec_hdr = get_struct(file_sec, 0, sec_hdr_struct[sec_hdr_size]) sec_unk = '0x%0.*X' % (sec_extra_size * 2, int.from_bytes(file_sec[-sec_extra_size:], 'little')) - + sec_unk0, sec_ar, sec_unk1, sec_encr, sec_unk2, sec_ar_idx, sec_unk3, sec_svn, sec_unk4 = sec_hdr.get_flags() - + log_encr = sec_encr # Always prefer Integrity Info > Encryption value, if it exists log_arpl = sec_ar # Always prefer Integrity Info > Anti-Replay value, if it exists - + sec_unk_flags = '{0:01b}b'.format(sec_unk0) + ' {0:01b}b'.format(sec_unk1) + ' {0:07b}b'.format(sec_unk2) + ' {0:01b}b'.format(sec_unk3) + ' {0:02b}b'.format(sec_unk4) sec_hmac = '%0.*X' % (0x10 * 2, int.from_bytes(sec_hdr.HMACMD5, 'little')) sec_aes_nonce = '%0.*X' % (0xC * 2, int.from_bytes(sec_hdr.AESGCMNonce, 'little')) @@ -8386,21 +8392,21 @@ def mfs_home13_anl(mfs_file_idx, mfs_file_data, vol_ftbl_id, sec_hdr_size, mfs_h sec_ar_counter = '0x%0.8X' % sec_hdr.ARCounter if sec_ar else '' if not sec_encr or sec_svn == 0 : sec_svn = '' if not sec_ar : sec_ar_idx = '' - + sec_hdr_pt = sec_hdr.mfs_print() # Save MFS Home Directory File Integrity Info if sec_extra_size : sec_hdr_pt.add_row(['Unknown', sec_unk]) # Append extra 0x10 Unknown data, if applicable - + mfs_write(os.path.normpath(os.path.join(rec_parent)), sec_file, file_sec) # Store MFS Home Directory File Integrity Contents mfs_txt(sec_hdr_pt, os.path.normpath(os.path.join(rec_parent)), sec_file, 'w', False) # Store/Print MFS Home Directory File Integrity Info - + elif sec_hdr_size == 0x34 : sec_hdr = get_struct(file_sec, 0, sec_hdr_struct[sec_hdr_size]) # MFS Home Directory Low Level File Integrity Structure - + sec_unk0, sec_ar, sec_encr, sec_unk1, sec_ar_idx, sec_unk2, sec_svn, sec_unk3 = sec_hdr.get_flags() - + log_encr = sec_encr # Always prefer Integrity Info > Encryption value, if it exists log_arpl = sec_ar # Always prefer Integrity Info > Anti-Replay value, if it exists - + sec_unk_flags = '{0:01b}b'.format(sec_unk0) + ' {0:07b}b'.format(sec_unk1) + ' {0:03b}b'.format(sec_unk2) + ' {0:01b}b'.format(sec_unk3) sec_hmac = '%0.*X' % (0x20 * 2, int.from_bytes(sec_hdr.HMACSHA256, 'little')) sec_aes_nonce = '%0.*X' % (0x10 * 2, int.from_bytes(sec_hdr.ARValues_Nonce, 'little')) if sec_encr else '' @@ -8408,54 +8414,54 @@ def mfs_home13_anl(mfs_file_idx, mfs_file_data, vol_ftbl_id, sec_hdr_size, mfs_h sec_ar_counter = '0x%0.8X' % struct.unpack_from(' %s does not contain VFS ID %d!' % (ftbl_plat_id,ftbl_dict_id,mfs_file_idx) + col_e, '', False, False, False, []) rec_path = os.path.normpath(os.path.join('/Unknown', '%d.bin' % mfs_file_idx)) # Set generic/unknown File local path when warnings occur rec_file = os.path.normpath(mfs_home13_dir + rec_path) # Set generic/unknown File actual path when warnings occur rec_parent = os.path.normpath(os.path.join(mfs_home13_dir, 'Unknown')) # Set generic/unknown parent Folder actual path when warnings occur - + mfs_write(rec_parent, rec_file, file_data) # Store File to currently working Folder - + # Append MFS Home Directory File Info to Log if sec_hdr_size == 0x28 : mfs_pt.add_row(['%0.4d' % mfs_file_idx, rec_path, 'Unknown', '0x%X' % len(file_data), 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown']) - + elif sec_hdr_size == 0x34 : mfs_pt.add_row(['%0.4d' % mfs_file_idx, rec_path, 'Unknown', '0x%X' % len(file_data), 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown']) - + return mfs_parsed_idx - + # Parse all MFS Configuration (Low Level Files 6 & 7) Records # noinspection PyUnusedLocal def mfs_cfg_anl(mfs_file, buffer, rec_folder, root_folder, config_rec_size, pch_init_info, vol_ftbl_id, vol_ftbl_pl) : mfs_pt = None ftbl_dict = {} ftbl_json = os.path.join(mea_dir, 'FileTable.dat') - + # Generate MFS Configuration Records Log if config_rec_size == 0x1C : mfs_pt = ext_table([col_y + 'Path' + col_e, col_y + 'Type' + col_e, col_y + 'Size' + col_e, col_y + 'Integrity' + col_e, col_y + 'Encryption' + col_e, @@ -8463,20 +8469,20 @@ def mfs_cfg_anl(mfs_file, buffer, rec_folder, root_folder, config_rec_size, pch_ col_y + 'MCA' + col_e, col_y + 'Reserved' + col_e, col_y + 'Unknown Access' + col_e, col_y + 'Unknown Options' + col_e], True, 1) elif config_rec_size == 0xC : mfs_pt = ext_table([col_y + 'Path' + col_e, col_y + 'File ID' + col_e, col_y + 'Size' + col_e, col_y + 'FIT' + col_e, col_y + 'Reserved Flags' + col_e], True, 1) - + # Check if MFS File Table Dictionary file exists if os.path.isfile(ftbl_json) : with open(ftbl_json, 'r', encoding='utf-8') as json_file : ftbl_dict = json.load(json_file) else : _ = mfs_anl_msg(col_r + 'Error: MFS File Table Dictionary file is missing!' + col_e, 'error', True, False, False, []) - + mfs_pt.title = col_y + 'MFS %s Configuration Records' % ('006 Intel' if mfs_file == 6 else '007 OEM') + col_e - + rec_count = int.from_bytes(buffer[:4], 'little') # MFS Configuration Records Count for rec in range(rec_count) : # Parse all MFS Configuration Records rec_hdr = get_struct(buffer[4:], rec * config_rec_size, config_rec_struct[config_rec_size]) # MFS Configuration Record Structure rec_hdr_pt = rec_hdr.mfs_print() # MFS Configuration Record PLTable Object - + if config_rec_size == 0x1C : rec_name = rec_hdr.FileName.decode('utf-8') # File or Folder Name rec_size = rec_hdr.FileSize # File Size @@ -8485,9 +8491,9 @@ def mfs_cfg_anl(mfs_file, buffer, rec_folder, root_folder, config_rec_size, pch_ rec_user_id = '0x%0.4X' % rec_hdr.OwnerUserID # Owner User ID rec_group_id = '0x%0.4X' % rec_hdr.OwnerGroupID # Owner Group ID unix_rights,integrity,encryption,anti_replay,record_type,acc_unk,fitc_cfg,mca_upd,opt_unk = rec_hdr.get_flags() # Get Record Flags - + rec_size_p = '' if (record_type,rec_size) == (1,0) else '0x%X' % rec_size # Set Folder/File Size value for printing - + if record_type == 1 : # Set currently working Folder (Name or ..) rec_folder = os.path.normpath(os.path.join(rec_folder, rec_name, '')) # Add Folder name to path and adjust it automatically at .. local_mfs_path = os.path.relpath(rec_folder, start=root_folder) # Create Local MFS Folder Path @@ -8500,30 +8506,30 @@ def mfs_cfg_anl(mfs_file, buffer, rec_folder, root_folder, config_rec_size, pch_ local_mfs_path = os.path.relpath(rec_file, start=root_folder) # Create Local MFS File Path rec_hdr_pt.add_row(['Path', local_mfs_path]) # Add Local MFS File Path to MFS Configuration Record Structure Info mfs_txt(rec_hdr_pt, rec_folder, rec_file, 'w', False) # Store/Print MFS Configuration Record Info - + # Get PCH info via MFS Intel Configuration > PCH Initialization Table if mfs_file == 6 and rec_name.startswith('mphytbl') and rec_data : pch_init_info = mphytbl(mfs_file, rec_data, pch_init_info) - + if rec_name == '..' : continue # Parse but skip logging of Parent (..) directory - + # Append MFS Configuration Record Info to Log mfs_pt.add_row([local_mfs_path, ['File','Folder'][record_type], rec_size_p, ['No','Yes'][integrity], ['No','Yes'][encryption], ['No','Yes'][anti_replay], ''.join(map(str, rec_hdr.get_rights(unix_rights))), rec_user_id, rec_group_id, ['No','Yes'][fitc_cfg], ['No','Yes'][mca_upd], rec_res, '{0:03b}b'.format(acc_unk), '{0:014b}b'.format(opt_unk)]) - + elif config_rec_size == 0xC : rec_id = rec_hdr.FileID # File ID relative to MFS System Volume FTBL Dictionary rec_offset = rec_hdr.FileOffset # File Offset relative to MFS Low Level File start rec_size = rec_hdr.FileSize # File Size fitc_cfg,flag_unk = rec_hdr.get_flags() # Get Record Flags - + vol_ftbl_pl = check_ftbl_pl(vol_ftbl_pl, ftbl_dict) # Check if MFS Volume FTBL Platform exists vol_ftbl_id = check_ftbl_id(vol_ftbl_id, ftbl_dict, vol_ftbl_pl) # Check if MFS Volume FTBL Dictionary exists - + ftbl_dict_id = '%0.2X' % vol_ftbl_id # FTBL Dictionary ID Tag ftbl_plat_id = '%0.2X' % vol_ftbl_pl # FTBL Platform ID Tag ftbl_rec_id = '%0.8X' % rec_id # FTBL File/Record ID (10002000, 10046A39, 12090300 etc) - + if ftbl_plat_id not in ftbl_dict or ftbl_dict_id not in ftbl_dict[ftbl_plat_id] or 'FTBL' not in ftbl_dict[ftbl_plat_id][ftbl_dict_id] : if ftbl_dict : _ = mfs_anl_msg(col_m + 'Warning: File Table %s > %s does not exist!' % (ftbl_plat_id,ftbl_dict_id) + col_e, '', True, False, False, []) rec_path = os.path.normpath(os.path.join('/Unknown', '%s.bin' % ftbl_rec_id)) # Set generic/unknown File local path when warnings occur @@ -8538,35 +8544,35 @@ def mfs_cfg_anl(mfs_file, buffer, rec_folder, root_folder, config_rec_size, pch_ rec_path = os.path.normpath(ftbl_dict[ftbl_plat_id][ftbl_dict_id]['FTBL'][ftbl_rec_id].split(',')[0]) # Get File local path from FTBL Dictionary rec_file = os.path.normpath(rec_folder + rec_path) # Set File actual path from FTBL Dictionary rec_parent = os.path.normpath(os.path.dirname(rec_file)) # Adjust parent Folder actual path from FTBL Dictionary - + rec_name = os.path.basename(rec_file) # Get File Name rec_data = buffer[rec_offset:rec_offset + rec_size] # Get File Contents from MFS Low Level File mfs_write(rec_parent, rec_file, rec_data) # Store File to currently working Folder rec_hdr_pt.add_row(['Path', rec_path]) # Add Local MFS File Path to MFS Configuration Record Structure Info mfs_txt(rec_hdr_pt, rec_parent, rec_file, 'w', False) # Store/Print MFS Configuration Record Info - + # Get PCH info via MFS Intel Configuration > PCH Initialization Table if mfs_file == 6 and rec_name.startswith('mphytbl') and rec_data : pch_init_info = mphytbl(mfs_file, rec_data, pch_init_info) - + # Append MFS Configuration Record Info to Log mfs_pt.add_row([rec_path, '0x%s' % ftbl_rec_id, '0x%0.4X' % rec_size, ['No','Yes'][fitc_cfg], '{0:015b}b'.format(flag_unk)]) - + mfs_txt(mfs_pt, root_folder, os.path.join(root_folder + 'home_records'), 'w', True) # Store/Print MFS Configuration Records Log - + return pch_init_info - + # Analyze CSE FITC Partition > fitc.cfg OEM Configuration File 7 def fitc_anl(mod_f_path, part_start, part_end, config_rec_size, vol_ftbl_id, vol_ftbl_pl) : print(col_g + '\n Analyzing MFS Low Level File 7 (OEM Configuration) ...' + col_e) - + fitc_part = reading[part_start:part_end] fitc_hdr = get_struct(fitc_part, 0, FITC_Header) fitc_rev = fitc_hdr.HeaderRevision - + if fitc_rev == 1 : fitc_hdr_data = fitc_part[:0x4] + b'\x00' * 4 + fitc_part[0x8:0xC] fitc_cfg_data = fitc_part[0x10:0x10 + fitc_hdr.DataLength] - + hdr_chk_int = fitc_hdr.HeaderChecksum hdr_chk_mea = crccheck.crc.Crc32.calc(fitc_hdr_data) if hdr_chk_int != hdr_chk_mea : @@ -8574,7 +8580,7 @@ def fitc_anl(mod_f_path, part_start, part_end, config_rec_size, vol_ftbl_id, vol input_col(col_r + '\n Error: Wrong FITC Header CRC-32 0x%0.8X, expected 0x%0.8X!' % (hdr_chk_int, hdr_chk_mea) + col_e) # Debug else : print(col_r + '\n Error: Wrong FITC Header CRC-32 0x%0.8X, expected 0x%0.8X!' % (hdr_chk_int, hdr_chk_mea) + col_e) - + data_chk_int = fitc_hdr.DataChecksum data_chk_mea = crccheck.crc.Crc32.calc(fitc_cfg_data) if data_chk_int != data_chk_mea : @@ -8582,18 +8588,18 @@ def fitc_anl(mod_f_path, part_start, part_end, config_rec_size, vol_ftbl_id, vol input_col(col_r + '\n Error: Wrong FITC Data CRC-32 0x%0.8X, expected 0x%0.8X!' % (data_chk_int, data_chk_mea) + col_e) # Debug else : print(col_r + '\n Error: Wrong FITC Data CRC-32 0x%0.8X, expected 0x%0.8X!' % (data_chk_int, data_chk_mea) + col_e) - + else : # CSME 15 (TGP) Alpha fitc_cfg_len = int.from_bytes(fitc_part[:0x4], 'little') fitc_cfg_data = fitc_part[0x4:0x4 + fitc_cfg_len] fitc_part_padd = fitc_part[0x4 + fitc_cfg_len:] - + if fitc_part_padd != len(fitc_part_padd) * b'\xFF' : if param.cse_pause : input_col(col_r + '\n Error: Data at FITC padding, possibly unknown Header revision %d!' % fitc_rev + col_e) # Debug else : print(col_r + '\n Error: Data at FITC padding, possibly unknown Header revision %d!' % fitc_rev + col_e) - + try : rec_folder = os.path.join(mod_f_path[:-4], 'OEM Configuration', '') # noinspection PyUnusedLocal @@ -8603,7 +8609,7 @@ def fitc_anl(mod_f_path, part_start, part_end, config_rec_size, vol_ftbl_id, vol input_col(col_r + '\n Error: Failed to analyze MFS Low Level File 7 (OEM Configuration)!' + col_e) # Debug else : print(col_r + '\n Error: Failed to analyze MFS Low Level File 7 (OEM Configuration)!' + col_e) - + # Analyze CSE EFS Partition def efs_anl(mod_f_path, part_start, part_end, vol_ftbl_id, vol_ftbl_pl) : page_size = 0x1000 @@ -8618,31 +8624,31 @@ def efs_anl(mod_f_path, part_start, part_end, vol_ftbl_id, vol_ftbl_pl) : file_data_all = b'' fvalue = ['No','Yes'] ftbl_dict = {} - + efs_part = reading[part_start:part_end] page_count = len(efs_part) // page_size page_hdr_size = ctypes.sizeof(EFS_Page_Header) page_ftr_size = ctypes.sizeof(EFS_Page_Footer) ftbl_json = os.path.join(mea_dir, 'FileTable.dat') efs_folder = os.path.join(os.path.join(mod_f_path[:-4]), '') - + # Verify that EFS Partition can be parsed by efs_anl if not re.compile(br'\x00.\x00.\x00{3}.{8}\x00\x01\x02\x03\x04\x05', re.DOTALL).search(efs_part[0x1:0x16]) : efs_anl_msg(col_r + 'Error: Skipped EFS partition at 0x%X, unrecognizable format!' % part_start + col_e, err_stor, True) - + return bool(file_data_all) - + # Initialize EFS Page Records Log efs_pt = ext_table([col_y + 'Type' + col_e, col_y + 'Unknown 0' + col_e, col_y + 'Table' + col_e, col_y + 'Revision' + col_e, col_y + 'Unknown 1' + col_e, col_y + 'Data Used' + col_e, col_y + 'Data Rest' + col_e, col_y + 'Table Revision' + col_e, col_y + 'CRC-32' + col_e], True, 1) efs_pt.title = col_y + 'EFS Page Records' + col_e - + # Parse EFS Pages to determine their Type (System, Data, Empty) for page_idx in range(page_count) : page_data = efs_part[page_idx * page_size:page_idx * page_size + page_size] page_hdr = get_struct(page_data[:page_hdr_size], 0, EFS_Page_Header) - + if page_hdr.Dictionary not in (0x0000,0xFFFF) : sys_page_all.append(page_data) # System Page page_type = 'System' @@ -8652,143 +8658,143 @@ def efs_anl(mod_f_path, part_start, part_end, vol_ftbl_id, vol_ftbl_pl) : else : emp_page_all += page_data # Empty/Scratch Page page_type = 'Scratch' - + if page_type == 'Scratch' : continue # Do not add Empty/Scratch Page(s) to Log - + # Append EFS Page Record Info to Log efs_pt.add_row([page_type, '0x%X' % page_hdr.Unknown0, '%0.2X' % page_hdr.Dictionary, page_hdr.Revision, '0x%X' % page_hdr.Unknown1, page_hdr.DataPagesCom, page_hdr.DataPagesRes, page_hdr.DictRevision, '0x%0.8X' % page_hdr.CRC32]) - + # Process EFS Page Records Log if param.cse_unpack : if not param.cse_verbose : print('\n%s' % efs_pt) # Print EFS Page Records Log (already included in -ver86) - + mfs_txt(efs_pt, os.path.join(mod_f_path[:-4], ''), mod_f_path[:-4], 'a', True) # Store EFS Page Records Log - + sys_count = len(sys_page_all) # Count detected System Pages dat_count = len(dat_page_all) # Count detected Data Pages - + # EFS seems to use 1 System Page if sys_count != 1 : efs_anl_msg(col_r + 'Error: Detected %d EFS System Page(s), expected %d!' % (sys_count, 1) + col_e, err_stor, True) - + # EFS Empty/Scratch Page(s) should be empty (0xFF) if emp_page_all != b'\xFF' * len(emp_page_all) : efs_anl_msg(col_r + 'Error: Detected data in EFS Empty/Scratch Page(s)!' + col_e, err_stor, True) - + sys_page_data = sys_page_all[0] # System Page Contents (assuming only 1 exists) sys_hdr_data = sys_page_data[:page_hdr_size] # System Page Header Contents sys_hdr = get_struct(sys_hdr_data, 0, EFS_Page_Header) # System Page Header Structure - + sys_hdr_dict = sys_hdr.Dictionary # System Page Header Dictionary/Table (0A = CON, 0B = COR, 0C = SLM etc) - + # EFS & MFS Dictionary IDs should match if sys_hdr_dict != vol_ftbl_id : efs_anl_msg(col_r + 'Error: Detected EFS (%0.2X) & MFS (%0.2X) File Table Dictionary mismatch!' % ( sys_hdr_dict, vol_ftbl_id) + col_e, err_stor, True) - + sys_hdr_rev = sys_hdr.Revision # System Page Header EFS Revision sys_hdr_unk1 = sys_hdr.Unknown1 # System Page Header Unknown1 field - + # Report any new/unexpected EFS Revision & Unknown1 field values if (sys_hdr_rev,sys_hdr_unk1) != (1,2) : efs_anl_msg(col_r + 'Error: EFS System Page Header Revision,Unknown1 = 0x%X,0x%X, expected 0x1,0x2!' % ( sys_hdr_rev, sys_hdr_unk1) + col_e, err_stor, True) - + sys_hdr_crc32_int = sys_hdr.CRC32 # System Page Header CRC-32 (Unknown0 - DictRevision, IV 0) sys_hdr_crc32_mea = ~crccheck.crc.Crc32.calc(sys_hdr_data[:-crc32_len], initvalue=crc32_iv) & 0xFFFFFFFF - + # Validate System Page Header CRC-32 if sys_hdr_crc32_int != sys_hdr_crc32_mea : efs_anl_msg(col_r + 'Error: Wrong EFS System Page Header CRC-32 0x%0.8X, expected 0x%0.8X!' % ( sys_hdr_crc32_int, sys_hdr_crc32_mea) + col_e, err_stor, True) elif param.cse_unpack : print(col_g + '\n EFS System Page Header CRC-32 0x%0.8X is VALID' % sys_hdr_crc32_int + col_e) - + sys_dat_count = sys_hdr.DataPagesCom + sys_hdr.DataPagesRes # Count Total Data Pages reported by System Page - + # Data Pages Count from System Page Header & manual detection should match if dat_count != sys_dat_count : efs_anl_msg(col_r + 'Error: Detected %d EFS Data Pages, expected %d!' % (dat_count, sys_dat_count) + col_e, err_stor, True) sys_idx_padd = sys_page_data[page_hdr_size + sys_dat_count:page_hdr_size + sys_dat_count + idx_padd_len] # 1st Index Padding - + # Report any unexpected data in System Page 1st Index Area Padding if sys_idx_padd != b'\x00' * idx_padd_len : efs_anl_msg(col_r + 'Error: Detected data in EFS System Page 1st Index Area Padding!' + col_e, err_stor, True) - + sys_idx_size = sys_dat_count + idx_padd_len + crc32_len # System Page Index Area Size sys_idx_offset = sys_page_data.find(b'\xFF' * sys_idx_size) - sys_idx_size # System Page Last/Current Index Area Offset sys_idx_values = struct.unpack_from('<%dB' % sys_dat_count, sys_page_data, sys_idx_offset) # System Page Last/Current Index Area Values sys_idx_data = sys_page_data[sys_idx_offset:sys_idx_offset + sys_dat_count + idx_padd_len + crc32_len] # System Page Last/Current Index Area Data - + sys_idx_crc32_int = int.from_bytes(sys_idx_data[-crc32_len:], 'little') # System Page Indexes CRC-32 (Indexes + Padding, IV 0) sys_idx_crc32_mea = ~crccheck.crc.Crc32.calc(sys_idx_data[:-crc32_len], initvalue=crc32_iv) & 0xFFFFFFFF - + # Validate System Page Indexes CRC-32 if sys_idx_crc32_int != sys_idx_crc32_mea : efs_anl_msg(col_r + 'Error: Wrong EFS System Page Indexes CRC-32 0x%0.8X, expected 0x%0.8X!' % ( sys_idx_crc32_int, sys_idx_crc32_mea) + col_e, err_stor, True) - + # Sort Data Pages based on the order of the System Page Index Values # For example: 0C 01 07 03 00 [...] --> 0C = 1st Page, 07 = 3rd Page, 03 = 4th Page etc dat_pages_final = [dat_page_all[v] for v in sys_idx_values] - + # Parse the ordered Data Pages for page_idx in range(sys_dat_count) : page_data_all = dat_pages_final[page_idx] # Data Page Entire Contents page_data_dat = page_data_all[page_hdr_size:-page_ftr_size] # Data Page Data/File Contents page_data_crc = page_data_all[page_hdr_size:-crc32_len] # Data Page CRC-32 checked Contents - + dat_hdr_data = page_data_all[:page_hdr_size] # Data Page Header Contents dat_hdr = get_struct(dat_hdr_data, 0, EFS_Page_Header) # Data Page Header Structure dat_hdr_crc32_int = dat_hdr.CRC32 # Data Page Header CRC-32 (Unknown0 - DictRevision, IV 0) dat_hdr_crc32_mea = ~crccheck.crc.Crc32.calc(dat_hdr_data[:-crc32_len], initvalue=crc32_iv) & 0xFFFFFFFF - + # Validate Data Page Header CRC-32 if dat_hdr_crc32_int != dat_hdr_crc32_mea : efs_anl_msg(col_r + 'Error: Wrong EFS Data Page %d Header CRC-32 0x%0.8X, expected 0x%0.8X!' % ( page_idx, dat_hdr_crc32_int, dat_hdr_crc32_mea) + col_e, err_stor, True) elif param.cse_unpack : print(col_g + '\n EFS Data Page %d Header CRC-32 0x%0.8X is VALID' % (page_idx, dat_hdr_crc32_int) + col_e) - + dat_ftr_data = page_data_all[-page_ftr_size:] # Data Page Footer Contents dat_ftr = get_struct(dat_ftr_data, 0, EFS_Page_Footer) # Data Page Footer Structure dat_ftr_crc32_int = dat_ftr.CRC32 # Data Page Footer CRC-32 (Header end - CRC32 start, IV 0) dat_ftr_crc32_mea = ~crccheck.crc.Crc32.calc(page_data_crc, initvalue=crc32_iv) & 0xFFFFFFFF dat_ftr_crc32_skip = bool(page_data_crc == b'\xFF' * len(page_data_crc) and dat_ftr_crc32_int == 0xFFFFFFFF) - + # Validate Data Page Footer CRC-32 (skip Reserved Data Pages) if not dat_ftr_crc32_skip and dat_ftr_crc32_int != dat_ftr_crc32_mea : efs_anl_msg(col_r + 'Error: Wrong EFS Data Page %d Footer CRC-32 0x%0.8X, expected 0x%0.8X!' % ( page_idx, dat_ftr_crc32_int, dat_ftr_crc32_mea) + col_e, err_stor, True) elif param.cse_unpack : print(col_g + '\n EFS Data Page %d Footer CRC-32 0x%0.8X is VALID' % (page_idx, dat_ftr_crc32_int) + col_e) - + efs_data_all += page_data_dat # Append Page/File Contents to Data Area Buffer - + efs_data_rest = bytearray(efs_data_all) # Initialize EFS Remaining Data Buffer to later detect wrong EFST - + # Check if EFS File Table Dictionary file exists if os.path.isfile(ftbl_json) : with open(ftbl_json, 'r', encoding='utf-8') as json_file : ftbl_dict = json.load(json_file) else : efs_anl_msg(col_r + 'Error: EFS File Table Dictionary file is missing!' + col_e, err_stor, True) - + vol_ftbl_pl = check_ftbl_pl(vol_ftbl_pl, ftbl_dict) # Get FTBL/EFST Platform from MFS Volume and check existence vol_ftbl_id = check_ftbl_id(vol_ftbl_id, ftbl_dict, vol_ftbl_pl) # Get FTBL/EFST Dictionary from MFS Volume and check existence - + plat_name = ftbl_efst_plat[vol_ftbl_pl] if vol_ftbl_pl in ftbl_efst_plat else 'Unknown' # Get FTBL/EFST Platform CodeName - + ftbl_dict_id = '%0.2X' % vol_ftbl_id # FTBL/EFST Dictionary ID Tag ftbl_plat_id = '%0.2X' % vol_ftbl_pl # FTBL/EFST Platform ID Tag efst_dict_rev = '%0.2X' % sys_hdr.DictRevision # FTBL/EFST Dictionary Revision Tag - + # Parse FTBL/EFST DB and extract EFS Files if 'EFST' in ftbl_dict[ftbl_plat_id][ftbl_dict_id] : if efst_dict_rev in ftbl_dict[ftbl_plat_id][ftbl_dict_id]['EFST'] : sec_hdr_size = get_sec_hdr_size(variant,major,minor,hotfix,vol_ftbl_pl) # Get CSE File System Integrity Table Structure Size - + # Initialize EFS File Records Log if sec_hdr_size == 0x28 : efs_pt = ext_table([col_y + 'VFS ID' + col_e, col_y + 'EFS Name' + col_e, col_y + 'VFS Path' + col_e, col_y + 'File ID' + col_e, @@ -8800,43 +8806,43 @@ def efs_anl(mod_f_path, part_start, part_end, vol_ftbl_id, vol_ftbl_pl) : efs_pt.title = col_y + 'EFS File Records' + col_e else : efs_pt = None - + for offset_id in ftbl_dict[ftbl_plat_id][ftbl_dict_id]['EFST'][efst_dict_rev] : efst_entry = ftbl_dict[ftbl_plat_id][ftbl_dict_id]['EFST'][efst_dict_rev][offset_id].split(',') # Split EFST Entry string data efst_entry = [int(s) for s in efst_entry[:-1]] + [efst_entry[-1]] # Convert EFST Entry non-name string values to integers _,_,file_length,file_id,reserved,file_name = efst_entry # EFST > Entry/File info file_path = os.path.join(efs_folder, '%s (%0.4d)' % (file_name, file_id)) # Generate Entry/File path efs_offset = int(offset_id, 16) # Actual EFS Data Area Buffer File Offset from FTBL/EFST DB - + file_data_met = efs_data_all[efs_offset:efs_offset + meta_size] # Entry/File Metadata file_met = get_struct(file_data_met, 0, EFS_File_Metadata) # EFS Entry/File Metadata Structure file_met_size = file_met.Size # EFS Entry/File Size via its Metadata (always prefer over EFST Size) file_min_size = min(file_met_size, file_length) # EFS Entry/File Minimum Size from EFST or Metadata file_met_unk = file_met.Unknown # EFS Entry/File Unknown via its Metadata - + # Replace EFS Entry/File full data at the EFS Remaining Data Buffer to check for leftover data later efs_data_rest[efs_offset:efs_offset + meta_size + file_min_size] = b'\xFF' * (meta_size + file_min_size) - + # Check for empty EFS Entry/File via its Metadata Size if file_met_size == 0xFFFF : continue # Skip storing/recording of empty/unused EFS files - + # Check for wrong EFST via EFS Table & EFS Metadata File Size Mismatch if file_met_size > file_length : efs_anl_msg(col_m + 'Warning: Detected EFS Table & File Size mismatch at %s, wrong EFST!' % file_name + col_e, warn_stor, False) continue # No point in storing/recording EFS "files" when EFST is wrong - + file_data_all = efs_data_all[efs_offset + meta_size:efs_offset + meta_size + file_met_size] # EFS Entry/File Contents - + if not param.cse_unpack : continue # No need to further analyze EFS when not unpacking - + # EFS Files can be Integrity protected. Integrity/Security info can only be retrieved from FTBL, not EFST or EFS. # Without knowing the presence of Integrity, file size cannot be determined so FTBL parsing is necessary, not optional. for ftbl_file_id in ftbl_dict[ftbl_plat_id][ftbl_dict_id]['FTBL'] : ftbl_entry = ftbl_dict[ftbl_plat_id][ftbl_dict_id]['FTBL'][ftbl_file_id].split(',') # Split FTBL Entry string data ftbl_entry = [ftbl_entry[0]] + [int(s) for s in ftbl_entry[1:]] # Convert FTBL Entry non-path string values to integers ftbl_path,ftbl_acc_int,ftbl_acc_enc,ftbl_acc_arp,ftbl_acc_unk,ftbl_group_id,ftbl_user_id,ftbl_vfs_id,ftbl_unk = ftbl_entry - + if ftbl_vfs_id == file_id : # Remember to also adjust FTBL_Entry, param.mfs_ftbl & mfs_home13_anl ftbl_pt = ext_table(['Field', 'Value'], False, 1) @@ -8852,25 +8858,25 @@ def efs_anl(mod_f_path, part_start, part_end, vol_ftbl_id, vol_ftbl_pl) : ftbl_pt.add_row(['User ID', '0x%0.4X' % ftbl_user_id]) ftbl_pt.add_row(['VFS ID', '%0.4d' % ftbl_vfs_id]) ftbl_pt.add_row(['Unknown', '{0:064b}b'.format(ftbl_unk)]) - + # Initialize Integrity related variables sec_hmac, sec_ar_random, sec_ar_counter, sec_svn, sec_ar_idx, sec_aes_nonce, sec_unk_flags = [''] * 7 log_encr = ftbl_acc_enc log_arpl = ftbl_acc_arp sec_unk = '' sec_extra_size = 0 - + # Perform Integrity related actions if ftbl_acc_int : # Split EFS File Contents & Integrity, if Integrity Protection is present file_data = file_data_all[:-sec_hdr_size] if file_data_all else b'' # EFS File Contents without Integrity file_sec = file_data_all[-sec_hdr_size:] if file_data_all else b'' # EFS File Integrity without Contents - + # Parse EFS File Integrity Info if file_sec : if sec_hdr_size == 0x28 : sec_hdr = get_struct(file_sec, 0, sec_hdr_struct[sec_hdr_size]) # EFS File Integrity Structure - + # Some files have extra 0x10 Unknown data at the end of the 0x28 Integrity Structure (0x38). # We need to know the exact size of the full Integrity Structure in order to split the file. # For the life of me I cannot find any indicator at FTBL or EFS that those extra 0x10 exist. @@ -8881,12 +8887,12 @@ def efs_anl(mod_f_path, part_start, part_end, vol_ftbl_id, vol_ftbl_pl) : file_sec = file_data_all[-(sec_hdr_size + sec_extra_size):] if file_data_all else b'' sec_hdr = get_struct(file_sec, 0, sec_hdr_struct[sec_hdr_size]) sec_unk = '0x%0.*X' % (sec_extra_size * 2, int.from_bytes(file_sec[-sec_extra_size:], 'little')) - + sec_unk0,sec_ar,sec_unk1,sec_encr,sec_unk2,sec_ar_idx,sec_unk3,sec_svn,sec_unk4 = sec_hdr.get_flags() - + log_encr = sec_encr # Always prefer Integrity Info > Encryption value, if it exists log_arpl = sec_ar # Always prefer Integrity Info > Anti-Replay value, if it exists - + sec_unk_flags = '{0:01b}b'.format(sec_unk0) + ' {0:01b}b'.format(sec_unk1) + ' {0:07b}b'.format(sec_unk2) + \ ' {0:01b}b'.format(sec_unk3) + ' {0:02b}b'.format(sec_unk4) sec_hmac = '%0.*X' % (0x10 * 2, int.from_bytes(sec_hdr.HMACMD5, 'little')) @@ -8895,34 +8901,34 @@ def efs_anl(mod_f_path, part_start, part_end, vol_ftbl_id, vol_ftbl_pl) : sec_ar_counter = '0x%0.8X' % sec_hdr.ARCounter if sec_ar else '' if not sec_encr or sec_svn == 0 : sec_svn = '' if not sec_ar : sec_ar_idx = '' - + sec_hdr_pt = sec_hdr.mfs_print() # Save EFS File Integrity Info sec_hdr_pt.title = col_y + 'EFS Integrity Table' + col_e # Adjust default title from MFS to EFS if sec_extra_size : sec_hdr_pt.add_row(['Unknown', sec_unk]) # Append extra 0x10 Unknown data, if applicable - + mfs_write(efs_folder, file_path + '_integrity', file_sec) # Store EFS File Integrity Contents mfs_txt(sec_hdr_pt, efs_folder, file_path + '_integrity', 'w', False) # Store EFS File Integrity Info else : file_data = file_data_all - + mfs_write(efs_folder, file_path, file_data) # Store EFS File Contents to currently working folder mfs_txt(ftbl_pt, efs_folder, file_path, 'w', False) # Store EFS File Metadata Info - + mfs_write(efs_folder, file_path + '_metadata', file_data_met) # Store EFS File Metadata to currently working folder mfs_txt(file_met.efs_print(), efs_folder, file_path + '_metadata', 'w', False) # Store EFS File Metadata Info - + if sec_hdr_size == 0x28 : # Append EFS File Record Info to Log efs_pt.add_row(['%0.4d' % file_id, file_name, ftbl_path, '0x%s' % ftbl_file_id, '0x%X' % len(file_data), '0x%0.4X' % file_met_unk, '0x%X' % reserved, fvalue[ftbl_acc_int], fvalue[log_encr], sec_svn, fvalue[log_arpl], sec_ar_idx, sec_ar_random, sec_ar_counter, '0x%0.4X' % ftbl_user_id, '0x%0.4X' % ftbl_group_id, '{0:013b}b'.format(ftbl_acc_unk), '{0:064b}b'.format(ftbl_unk), sec_hmac, sec_aes_nonce, sec_unk_flags, sec_unk]) - + break # Stop searching FTBL Dictionary at first VFS ID match else : efs_anl_msg(col_r + 'Error: Could not find File System Platform 0x%s (%s) > Dictionary 0x%s > FTBL > VFS ID %0.4d!' % ( ftbl_plat_id, plat_name, ftbl_dict_id, file_id) + col_e, err_stor, False) - + if file_data_all : # Perform actions depending on whether EFS Files exist or not mfs_txt(efs_pt, os.path.join(mod_f_path[:-4], ''), mod_f_path[:-4], 'a', True) # Store EFS File Records Log else : @@ -8935,14 +8941,14 @@ def efs_anl(mod_f_path, part_start, part_end, vol_ftbl_id, vol_ftbl_pl) : else : efs_anl_msg(col_r + 'Error: Could not find File System Platform 0x%s (%s) > Dictionary 0x%s > EFST!' % ( ftbl_plat_id, plat_name, ftbl_dict_id) + col_e, err_stor, False) - + # Remember to also update any prior function return statements return bool(file_data_all) - + # Analyze MFS Intel Configuration > Chipset Initialization Table def mphytbl(mfs_file, rec_data, pch_init_info) : pch_stp_val = {0:'A',1:'B',2:'C',3:'D',4:'E',5:'F',6:'G',7:'H',8:'I',9:'J',10:'K',11:'L',12:'M',13:'N',14:'O',15:'P'} - + if rec_data[0x4:0x6] == b'\xFF' * 2 : pch_init_plt = pch_dict[rec_data[7]] if rec_data[7] in pch_dict else 'Unknown' # Actual Chipset SKU Platform (ICP-LP, TGP-H etc) pch_init_stp = rec_data[8] >> 4 # Raw Chipset Stepping(s), Absolute or Bitfield depending on firmware @@ -8952,12 +8958,12 @@ def mphytbl(mfs_file, rec_data, pch_init_info) : pch_init_stp = rec_data[3] & 0xF # Raw Chipset Stepping(s), Absolute or Bitfield depending on firmware pch_init_rev = rec_data[2] # Chipset Initialization Table Revision pch_true_stp = '' # Actual Chipset Stepping(s) (A, B, C etc) - + # Detect Actual Chipset Stepping(s) for CSSPS 4.4 if (variant,major,minor) in [('CSSPS',4,4)] : pch_true_stp = pch_stp_val[pch_init_stp] # Absolute (0 = A, 1 = B, 2 = C, 3 = D etc) pch_init_plt = 'WTL' # Change from LBG-H to WTL (LBG-R) - + # Detect Actual Chipset Stepping(s) for CSME 11 & CSSPS 4 elif (variant,major) in [('CSME',11),('CSSPS',4)] : if mn2_ftpr_hdr.Year > 0x2015 or (mn2_ftpr_hdr.Year == 0x2015 and mn2_ftpr_hdr.Month > 0x05) \ @@ -8967,7 +8973,7 @@ def mphytbl(mfs_file, rec_data, pch_init_info) : else : # Unreliable for CSME ~< 11.0.0.1140 @ 2015-05-19 (always 80 --> SPT/KBP-LP A) pass - + # Detect Actual Chipset Stepping(s) for CSME 12 & CSSPS 5 elif (variant,major) in [('CSME',12),('CSSPS',5)] : if (mn2_ftpr_hdr.Year > 0x2018 or (mn2_ftpr_hdr.Year == 0x2018 and mn2_ftpr_hdr.Month > 0x01) @@ -8978,12 +8984,12 @@ def mphytbl(mfs_file, rec_data, pch_init_info) : else : # Absolute for CSME ~< 12.0.0.1058 @ 2018-01-25 (0 = A, 1 = B, 2 = C, 3 = D etc) pch_true_stp = pch_stp_val[pch_init_stp] - + # Detect Actual Chipset Stepping(s) for CSME 15.40 elif (variant,major,minor) in [('CSME',15,40)] : if 7000 > build >= 1000 : pch_true_stp = pch_stp_val[(build // 1000) - 1] # Build Number Yxxx else : pch_true_stp = pch_stp_val[pch_init_stp] # Fallback to Absolute value - + # Detect Actual Chipset Stepping(s) for CSME 13, CSME 15, CSME 16 & CSSPS 6 elif (variant,major) in [('CSME',13),('CSME',15),('CSME',16),('CSSPS',6)] : if rec_data[0x4:0x6] == b'\xFF' * 2 : @@ -8993,23 +8999,23 @@ def mphytbl(mfs_file, rec_data, pch_init_info) : # Bitfield for CSME ~< 13.0.0.1061 (0011 = --BA, 0110 = -CB-) for i in range(4) : pch_true_stp += 'DCBA'[i] if pch_init_stp & (1<<(4-1-i)) else '' if not pch_true_stp : pch_true_stp = 'A' # Fallback to A in case Bitfield is 0000 - + # Detect Actual Chipset Stepping(s) for CSME 14.5 elif (variant,major,minor) in [('CSME',14,5)] : # Absolute for CSME 14.5 (0 = A, 1 = B, 2 = C, 3 = D etc) pch_true_stp = pch_stp_val[pch_init_stp] pch_init_plt = 'CMP-V' # Change from KBP/BSF/GCF-H to CMP-V - + # Detect Actual Chipset Stepping(s) for CSME 14.0 elif (variant,major) in [('CSME',14)] : # Bitfield for CSME 14.0 & maybe CSME 15 (0011 = --BA, 0110 = -CB-) for i in range(4) : pch_true_stp += 'DCBA'[i] if pch_init_stp & (1<<(4-1-i)) else '' if not pch_true_stp : pch_true_stp = 'A' # Fallback to A in case Bitfield is 0000 - + pch_init_info.append([mfs_file, pch_init_plt, pch_true_stp, pch_init_rev]) # Output Chipset Initialization Table Info - + return pch_init_info - + # MFS 14-bit CRC-16 for System Page Chunk Indexes (from parseMFS by Dmitry Sklyarov) def Crc16_14(w, crc=0x3FFF) : CRC16tab = [0]*256 @@ -9017,136 +9023,136 @@ def Crc16_14(w, crc=0x3FFF) : r = i << 8 for _ in range(8): r = (r << 1) ^ (0x1021 if r & 0x8000 else 0) CRC16tab[i] = r & 0xFFFF - + for b in bytearray(struct.pack('> 8)] ^ (crc << 8)) & 0x3FFF - + return crc - + # Write/Print MFS Structures Information def mfs_txt(struct_print, folder_path, file_path_wo_ext, mode, is_log) : if param.cse_unpack : # Write Text File during CSE Unpacking struct_txt = ansi_escape.sub('', str(struct_print)) # Ignore Colorama ANSI Escape Character Sequences - + os.makedirs(folder_path, exist_ok=True) # Create the Text File's parent Folder, if needed - + if param.cse_verbose and is_log: print('\n%s' % struct_txt) # Print Structure Info - + with open(file_path_wo_ext + '.txt', mode, encoding = 'utf-8') as txt: txt.write('\n%s' % struct_txt) # Store Structure Info Text File - + if param.write_html: with open(file_path_wo_ext + '.html', mode, encoding = 'utf-8') as html: html.write('\n
\n%s' % pt_html(struct_print)) # Store Structure Info HTML File - + if param.write_json: mfs_txt_json_path = file_path_wo_ext + '.json' - + if not os.path.isfile(mfs_txt_json_path): with open(mfs_txt_json_path, 'w', encoding='utf-8') as jo: json.dump([], jo, indent=4) - + with open(mfs_txt_json_path, 'r', encoding='utf-8') as ji: mfs_txt_json_lists = json.load(ji) - + with open(mfs_txt_json_path, 'w', encoding='utf-8') as jo: json.dump(mfs_txt_json_lists + [pt_json(struct_print)], jo, indent=4) - + # Write MFS File Contents def mfs_write(folder_path, file_path, data) : if param.cse_unpack or param.cse_pause : # Write File during CSE Unpacking os.makedirs(folder_path, exist_ok=True) # Create the File's parent Folder, if needed - + with open(file_path, 'wb') as file : file.write(data) - + # Store and show MFS Analysis Errors def mfs_anl_msg(mfs_err_msg, msg_type, msg_copy, is_page, is_chunk_crc, mfs_tmp_page) : if msg_type == 'error' : err_stor.append([mfs_err_msg, msg_copy]) - + if param.cse_unpack and not is_page : if msg_type == 'error' and param.cse_pause : input_col('\n %s' % mfs_err_msg) else : print('\n %s' % mfs_err_msg) - + if is_page : if is_chunk_crc : mfs_err_msg = ' ' + mfs_err_msg # Extra Tab at Page Chunk CRC messages for visual purposes (-unp86) mfs_tmp_page.append((' ' + mfs_err_msg, msg_type)) # Pause on error (-bug86) handled by caller - + return mfs_tmp_page - + # Store and show EFS Analysis Messages def efs_anl_msg(efs_msg, msg_list, msg_copy) : msg_list.append([efs_msg, msg_copy]) - + if param.cse_unpack and param.cse_pause : input_col('\n %s' % efs_msg) # Debug elif param.cse_unpack : print('\n %s' % efs_msg) - + # Analyze CSE PCH Initialization Table Platforms/Steppings def pch_init_anl(pch_init_info) : pch_init_final = [] final_print = '' final_db = '' - + # pch_init_info = [[MFS File, Chipset, Stepping, Patch], etc] # pch_init_final = [[Chipset, Steppings], etc, [Total Platforms/Steppings, Total DB Steppings]] - + # Skip analysis if no Initialization Table or Stepping was detected if not pch_init_info or pch_init_info[0][2] == '' : return pch_init_final - + # Store each Chipset once for info in pch_init_info : skip = False for final in pch_init_final : if info[1] == final[0] : skip = True if not skip : pch_init_final.append([info[1], '']) - + # Store all Steppings for each Chipset for info in pch_init_info : for final in pch_init_final : if info[1] == final[0] : final[1] = final[1] + info[2] - + # Sort each Chipset Steppings in reverse order (e.g. DCBA) & build total Print values for final_idx in range(len(pch_init_final)) : pch_init_final[final_idx][1] = ''.join(sorted(list(dict.fromkeys(pch_init_final[final_idx][1])), reverse=True)) final_print += '%s %s' % (pch_init_final[final_idx][0], ','.join(map(str, list(pch_init_final[final_idx][1])))) if final_idx < len(pch_init_final) - 1 : final_print += '\n' # No new line after last print final_db += pch_init_final[final_idx][1] - + # Add total Platforms/Steppings and Steppings for printing at last list cell, pch_init_final[-1] pch_init_final.append([final_print, ''.join(sorted(list(dict.fromkeys(final_db)), reverse=True))]) - + return pch_init_final - + # Analyze GSC Information (INFO) $FPT Partition def info_anl(mod_f_path, part_start, part_end) : print(col_g + '\n Analyzing GSC Information Partition ...' + col_e) - + fwi_size = ctypes.sizeof(GSC_Info_FWI) iup_size = ctypes.sizeof(GSC_Info_IUP) rev_size = 0x4 - + info_rev = int.from_bytes(reading[part_start:part_start + rev_size], 'little') - + if info_rev != 1 : if param.cse_pause : input_col(col_r + '\n Error: Unknown GSC Information Partition Revision %d!' % info_rev + col_e) # Debug else : print(col_r + '\n Error: Unknown GSC Information Partition Revision %d!' % info_rev + col_e) - + info_data = reading[part_start + rev_size:part_end] fwi_data = info_data[:fwi_size] iup_data = info_data[fwi_size:] iup_count = len(iup_data) // iup_size - + fwi_hdr = get_struct(fwi_data, 0, GSC_Info_FWI) print('\n%s' % fwi_hdr.gsc_print()) mfs_txt(fwi_hdr.gsc_print(), os.path.join(mod_f_path[:-4], ''), mod_f_path[:-4], 'a', False) - + for iup_idx in range(iup_count) : iup_hdr = get_struct(iup_data, iup_idx * iup_size, GSC_Info_IUP) if param.cse_verbose : print(iup_hdr.gsc_print()) mfs_txt(iup_hdr.gsc_print(), os.path.join(mod_f_path[:-4], ''), mod_f_path[:-4], 'a', False) - + # Analyze CSE PMC firmware before parsing def pmc_anl(mn2_info) : pmc_pch_sku = 'Unknown' @@ -9155,59 +9161,59 @@ def pmc_anl(mn2_info) : pch_sku_old = {0:'H', 2:'LP'} pch_rev_val = {0:'A', 1:'B', 2:'C', 3:'D', 4:'E', 5:'F', 6:'G', 7:'H', 8:'I', 9:'J', 10:'K', 11:'L', 12:'M', 13:'N', 14:'O', 15:'P'} pmc_unsupported = False - + # mn2_info = [Major, Minor, Hotfix, Build, Release, RSA Key Hash, RSA Sig Hash, Date, SVN, PV bit, MEU Major, MEU Minor, MEU Hotfix, # MEU Build, MN2 w/o RSA Hashes, MN2 Struct, MN2 Match Start, MN2 Match End] - + # $MN2 Manifest SVN = CSE_Ext_0F ARBSVN. The value is used for Anti-Rollback (ARB) and not Trusted Computing Base (TCB) purposes. - + pmc_year,pmc_month,pmc_day = [int(info, 16) for info in mn2_info[7].split('-')] - + # Detect PMC Variant pmc_variant, _, _, _ = get_variant(reading, mn2_info[15], mn2_info[16], mn2_info[17], mn2_info[5], [pmc_year, pmc_month, pmc_day], [mn2_info[0], mn2_info[1], mn2_info[2], mn2_info[3]]) - + if not pmc_variant.startswith('PMC') : pmc_variant = 'Unknown' - + if pmc_variant != 'Unknown': pmc_platform = pmc_variant[-3:] - + pmc_pch_rev = '%s%d' % (pch_rev_val[min(mn2_info[2] // 10, 0xF)], mn2_info[2] % 10) # 21 = PCH C PMC 1 - + # Get/Set special PMC Platform, SKU and/or Steppings if pmc_variant == 'PMCCNP' and mn2_info[0] not in [300,30] : # CSME < 12.0.0.1033 --> 01.7.0.1022 = PCH Stepping A1 + PMC Hotfix 7 + PCH-H + PMC Build 1022 (Guess) # CSME < 12.0.0.1033 --> 10.0.2.1021 = PCH Stepping B0 + PMC Hotfix 0 + PCH-LP + PMC Build 1021 (Guess) if mn2_info[2] in pch_sku_old : pmc_pch_sku = pch_sku_old[mn2_info[2]] # 0 H, 2 LP pmc_pch_rev = '%s%d' % (pch_rev_val[min(mn2_info[0] // 10, 0xF)], mn2_info[0] % 10) # 21 = PCH C1 - + elif pmc_variant == 'PMCCMPV' : # CSME 14.5 is H instead of V, PMC is 140 LP instead of 145 V = GREAT WORK INTEL... pmc_pch_sku = 'V' # Value instead of Halo or Low Power pmc_platform = 'CMP-V' # To differentiate CMP-V (KBP) from actual CMP - + elif pmc_variant == 'PMCWTL' : # Whitley is a royal f**k up, no further explanation required = GREAT WORK INTEL... pmc_pch_sku = 'H' # Based on target use cases pmc_pch_rev = 'B' # Based on CSSPS > VFS > mphytbl pmc_platform = 'WTL' # Whitley (a.k.a. LBG-R, a.k.a. SHI-T) - + elif pmc_variant.startswith(('PMCAPL','PMCBXT','PMCGLK')) : pmc_platform = pmc_variant[3:6] pmc_pch_rev = pmc_variant[-1] - + elif pmc_variant != 'Unknown' and mn2_info[1] in pch_sku_val : # 130.1.10.1003 = ICP + LP + PCH Compatibility B + PMC Maintenance 0 + PMC Revision 1003 pmc_pch_sku = pch_sku_val[mn2_info[1]] # 0 SoC, 1 LP, 2 H, 3 N, 4 M - + pmc_pch_rev_p = pmc_pch_rev[0] if pmc_pch_rev != 'Unknown' else pmc_pch_rev - + pmc_mn2_signed = 'Pre-Production' if mn2_info[4] == 'Debug' else 'Production' pmc_mn2_signed_db = 'PRD' if pmc_mn2_signed == 'Production' else 'PRE' - + # Fix Release of PRE firmware which are wrongly reported as PRD pmc_mn2_signed, pmc_mn2_signed_db = release_fix(pmc_mn2_signed, pmc_mn2_signed_db, mn2_info[5]) - + if pmc_platform.startswith(('APL','GLK','BXT')) : pmc_fw_ver = '%s.%s.%s.%s' % (mn2_info[0], mn2_info[1], mn2_info[2], mn2_info[3]) pmc_meu_ver = '%d.%d.%d.%0.4d' % (mn2_info[10], mn2_info[11], mn2_info[12], mn2_info[13]) @@ -9224,7 +9230,7 @@ def pmc_anl(mn2_info) : pmc_fw_ver = '%s.%s.%0.2d.%0.4d' % (mn2_info[0], mn2_info[1], mn2_info[2], mn2_info[3]) pmc_meu_ver = '%d.%d.%d.%0.4d' % (mn2_info[10], mn2_info[11], mn2_info[12], mn2_info[13]) pmc_name_db = '%s_%s_%s_%s_%s_%s' % (pmc_platform[:3], pmc_fw_ver, pmc_pch_sku, pmc_pch_rev_p, pmc_mn2_signed_db, mn2_info[6]) - + # Search DB for PMC firmware if pmc_platform.startswith(('MCC','TGP','CMP','JSP','LKF','ICP','CNP','GLK','BXT','APL','DG1')): for line in mea_db_lines : @@ -9234,7 +9240,7 @@ def pmc_anl(mn2_info) : note_new_fw('PMC %s' % pmc_platform) else: pmc_unsupported = True - + # Detect PMC RSA Public Key Recognition for line in mea_db_lines : if mn2_info[5] in line : @@ -9242,7 +9248,7 @@ def pmc_anl(mn2_info) : else : err_msg = [col_r + 'Error: Unknown PMC %d.%d RSA Public Key!' % (mn2_info[0], mn2_info[1]) + col_e, True] if err_msg not in err_stor : err_stor.append(err_msg) # Do not store message twice at bare/non-stitched PMC firmware - + return pmc_fw_ver, mn2_info[0], pmc_pch_sku, pmc_pch_rev, mn2_info[3], pmc_mn2_signed, pmc_mn2_signed_db, pmc_platform, \ mn2_info[7], mn2_info[8], mn2_info[9], pmc_meu_ver, pmc_name_db, pmc_unsupported @@ -9250,46 +9256,46 @@ def pmc_anl(mn2_info) : def pmc_parse(pmc_all_init, pmc_all_anl) : for pmc in pmc_all_init : pmc_vcn,pmc_mn2_ver,pmc_ext15_info,pmc_size = pmc - + pmc_fw_ver,pmc_pch_gen,pmc_pch_sku,pmc_pch_rev,pmc_fw_rel,pmc_mn2_signed,pmc_mn2_signed_db,pmc_platform, \ pmc_date,pmc_svn,pmc_pvbit,pmc_meu_ver,pmc_name_db,pmc_unsupported = pmc_anl(pmc_mn2_ver) - + pmc_all_anl.append([pmc_fw_ver,pmc_pch_gen,pmc_pch_sku,pmc_pch_rev,pmc_fw_rel,pmc_mn2_signed,pmc_mn2_signed_db, pmc_platform,pmc_date,pmc_svn,pmc_pvbit,pmc_meu_ver,pmc_vcn,pmc_mn2_ver,pmc_ext15_info,pmc_size, pmc_name_db,pmc_unsupported]) - + return pmc_all_anl - + # Analyze CSE PCHC firmware before parsing def pchc_anl(mn2_info) : pchc_platform = 'Unknown' pchc_unsupported = False - + # mn2_info = [Major, Minor, Hotfix, Build, Release, RSA Key Hash, RSA Sig Hash, Date, SVN, PV bit, MEU Major, MEU Minor, MEU Hotfix, # MEU Build, MN2 w/o RSA Hashes, MN2 Struct, MN2 Match Start, MN2 Match End] - + # $MN2 Manifest SVN = CSE_Ext_0F ARBSVN. The value is used for Anti-Rollback (ARB) and not Trusted Computing Base (TCB) purposes. - + pchc_year,pchc_month,pchc_day = [int(info, 16) for info in mn2_info[7].split('-')] - + # Detect PCHC Variant pchc_variant, _, _, _ = get_variant(reading, mn2_info[15], mn2_info[16], mn2_info[17], mn2_info[5], [pchc_year, pchc_month, pchc_day], [mn2_info[0], mn2_info[1], mn2_info[2], mn2_info[3]]) - + if not pchc_variant.startswith('PCHC') : pchc_variant = 'Unknown' - + if pchc_variant != 'Unknown' : pchc_platform = 'CMP-V' if pchc_variant == 'PCHCCMPV' else pchc_variant[-3:] - + pchc_mn2_signed = 'Pre-Production' if mn2_info[4] == 'Debug' else 'Production' pchc_mn2_signed_db = 'PRD' if pchc_mn2_signed == 'Production' else 'PRE' - + # Fix Release of PRE firmware which are wrongly reported as PRD pchc_mn2_signed, pchc_mn2_signed_db = release_fix(pchc_mn2_signed, pchc_mn2_signed_db, mn2_info[5]) - + pchc_fw_ver = '%d.%d.%d.%0.4d' % (mn2_info[0], mn2_info[1], mn2_info[2], mn2_info[3]) pchc_meu_ver = '%d.%d.%d.%0.4d' % (mn2_info[10], mn2_info[11], mn2_info[12], mn2_info[13]) pchc_name_db = '%s_%s_%s_%s' % (pchc_platform[:3], pchc_fw_ver, pchc_mn2_signed_db, mn2_info[6]) - + # Search DB for PCHC firmware if pchc_platform.startswith(('MCC','TGP','CMP','JSP','LKF','ICP')): for line in mea_db_lines : @@ -9299,7 +9305,7 @@ def pchc_anl(mn2_info) : note_new_fw('PCHC %s' % pchc_platform) else: pchc_unsupported = True - + # Detect PCHC RSA Public Key Recognition for line in mea_db_lines : if mn2_info[5] in line : @@ -9307,7 +9313,7 @@ def pchc_anl(mn2_info) : else : err_msg = [col_r + 'Error: Unknown PCHC %d.%d RSA Public Key!' % (mn2_info[0], mn2_info[1]) + col_e, True] if err_msg not in err_stor : err_stor.append(err_msg) # Do not store message twice at bare/non-stitched PCHC firmware - + return pchc_fw_ver, mn2_info[0], mn2_info[1], mn2_info[3], pchc_mn2_signed, pchc_mn2_signed_db, pchc_platform, mn2_info[7], \ mn2_info[8], mn2_info[9], pchc_meu_ver, pchc_name_db, pchc_unsupported @@ -9315,14 +9321,14 @@ def pchc_anl(mn2_info) : def pchc_parse(pchc_all_init, pchc_all_anl) : for pchc in pchc_all_init : pchc_vcn,pchc_mn2_ver,pchc_ext15_info,pchc_size = pchc - + pchc_fw_ver,pchc_fw_major,pchc_fw_minor,pchc_fw_rel,pchc_mn2_signed,pchc_mn2_signed_db,pchc_platform,pchc_date,pchc_svn, \ pchc_pvbit,pchc_meu_ver,pchc_name_db,pchc_unsupported = pchc_anl(pchc_mn2_ver) - + pchc_all_anl.append([pchc_fw_ver,pchc_fw_major,pchc_fw_minor,pchc_fw_rel,pchc_mn2_signed,pchc_mn2_signed_db, pchc_platform, pchc_date,pchc_svn,pchc_pvbit,pchc_meu_ver,pchc_vcn,pchc_mn2_ver,pchc_ext15_info, pchc_size,pchc_name_db,pchc_unsupported]) - + return pchc_all_anl # Analyze CSE PHY firmware before parsing @@ -9330,37 +9336,37 @@ def phy_anl(mn2_info) : phy_platform = 'Unknown' phy_sku = 'Unknown' phy_unsupported = False - + # mn2_info = [Major, Minor, Hotfix, Build, Release, RSA Key Hash, RSA Sig Hash, Date, SVN, PV bit, MEU Major, MEU Minor, MEU Hotfix, # MEU Build, MN2 w/o RSA Hashes, MN2 Struct, MN2 Match Start, MN2 Match End] - + # $MN2 Manifest SVN = CSE_Ext_0F ARBSVN. The value is used for Anti-Rollback (ARB) and not Trusted Computing Base (TCB) purposes. - + phy_year,phy_month,phy_day = [int(info, 16) for info in mn2_info[7].split('-')] - + # Detect PHY Variant phy_variant, _, _, _ = get_variant(reading, mn2_info[15], mn2_info[16], mn2_info[17], mn2_info[5], [phy_year, phy_month, phy_day], [mn2_info[0], mn2_info[1], mn2_info[2], mn2_info[3]]) if not phy_variant.startswith('PHY') : phy_variant = 'Unknown' - + if phy_variant != 'Unknown' : phy_platform = phy_variant[-3:] phy_sku = 'G' if phy_variant.startswith('PHYDG') else phy_variant[3] # Set "G" for GSC/GFX to match regular PHY SKU naming - + phy_mn2_signed = 'Pre-Production' if mn2_info[4] == 'Debug' else 'Production' phy_mn2_signed_db = 'PRD' if phy_mn2_signed == 'Production' else 'PRE' - + # Fix Release of PRE firmware which are wrongly reported as PRD phy_mn2_signed, phy_mn2_signed_db = release_fix(phy_mn2_signed, phy_mn2_signed_db, mn2_info[5]) - + phy_fw_ver = '%d.%d.%d.%0.4d' % (mn2_info[0], mn2_info[1], mn2_info[2], mn2_info[3]) phy_meu_ver = '%d.%d.%d.%0.4d' % (mn2_info[10], mn2_info[11], mn2_info[12], mn2_info[13]) if phy_variant.startswith('PHYDG') : phy_name_db = '%s_%s_%s_%s_%s_%s' % (phy_platform[:3], phy_sku, phy_fw_ver, mn2_info[7], phy_mn2_signed_db, mn2_info[6]) else : phy_name_db = '%s_%s_%s_%s_%s' % (phy_platform[:3], phy_sku, phy_fw_ver, phy_mn2_signed_db, mn2_info[6]) - + # Search DB for PHY firmware if phy_platform.startswith(('TGP','CMP','LKF','ICP','DG1')): for line in mea_db_lines : @@ -9370,7 +9376,7 @@ def phy_anl(mn2_info) : note_new_fw('PHY %s %s' % (phy_platform, phy_sku)) else: phy_unsupported = True - + # Detect PHY RSA Public Key Recognition for line in mea_db_lines : if mn2_info[5] in line : @@ -9378,7 +9384,7 @@ def phy_anl(mn2_info) : else : err_msg = [col_r + 'Error: Unknown PHY %d.%d RSA Public Key!' % (mn2_info[0], mn2_info[1]) + col_e, True] if err_msg not in err_stor : err_stor.append(err_msg) # Do not store message twice at bare/non-stitched PHY firmware - + return phy_fw_ver, phy_sku, phy_mn2_signed, phy_mn2_signed_db, phy_platform, mn2_info[7], mn2_info[8], mn2_info[9], \ phy_meu_ver, mn2_info[3], phy_name_db, phy_unsupported @@ -9386,13 +9392,13 @@ def phy_anl(mn2_info) : def phy_parse(phy_all_init, phy_all_anl) : for phy in phy_all_init : phy_vcn,phy_mn2_ver,phy_ext15_info,phy_size = phy - + phy_fw_ver,phy_sku,phy_mn2_signed,phy_mn2_signed_db,phy_platform,phy_date,phy_svn,phy_pvbit,phy_meu_ver, \ phy_fw_rel,phy_name_db, phy_unsupported = phy_anl(phy_mn2_ver) - + phy_all_anl.append([phy_fw_ver,phy_sku,phy_mn2_signed,phy_mn2_signed_db,phy_platform,phy_date,phy_svn,phy_pvbit,phy_meu_ver, phy_fw_rel,phy_vcn,phy_mn2_ver,phy_ext15_info,phy_size,phy_name_db,phy_unsupported]) - + return phy_all_anl # CSE Huffman Dictionary Loader by "IllegalArgument" (https://github.com/IllegalArgument) @@ -9403,61 +9409,61 @@ def cse_huffman_dictionary_load(cse_variant, cse_major, cse_minor, verbosity) : HUFFMAN_UNKNOWNS = {} mapping_types = {'code' : 0x20, 'data' : 0x60} huffman_dict = os.path.join(mea_dir, 'Huffman.dat') - + # Message Verbosity: All | Error | None - + # Check if a Huffman dictionary needs to be loaded and which version is required if cse_variant.startswith(('CSTXE','PMC','PCHC','PHY','OROM')) or (cse_variant,cse_major) == ('CSSPS',1) : return HUFFMAN_SHAPE, HUFFMAN_SYMBOLS, HUFFMAN_UNKNOWNS dict_version = 11 if (cse_variant,cse_major) in [('CSME',11),('CSSPS',4)] or (cse_variant,cse_major,cse_minor) == ('CSME',14,5) else 12 - + # Check if supported Huffman dictionary file exists if not os.path.isfile(huffman_dict) : if verbosity in ['all','error'] : if param.cse_pause : input_col(col_r + '\nHuffman dictionary file is missing!' + col_e) else : print(col_r + '\nHuffman dictionary file is missing!' + col_e) - + return HUFFMAN_SHAPE, HUFFMAN_SYMBOLS, HUFFMAN_UNKNOWNS - + with open(huffman_dict, 'r', encoding='utf-8') as dict_file : dict_json = json.load(dict_file) - + dict_mappings = dict_json[str(dict_version)] mapping_codeword_ranges = {} - + for mapping_type_string, mapping in dict_mappings.items() : mapping_type = mapping_types[mapping_type_string] grouped_codeword_strings = itertools.groupby(sorted(list(mapping.keys()), key=len), key=len) # noinspection PyTypeChecker grouped_codewords = {codeword_len : [int(codeword, 2) for codeword in codewords] for codeword_len, codewords in grouped_codeword_strings} mapping_codeword_ranges[mapping_type] = {codeword_len : (min(codewords), max(codewords)) for codeword_len, codewords in grouped_codewords.items()} - + if len({frozenset(x.items()) for x in mapping_codeword_ranges.values()}) > 1 and verbosity in ['all','error'] : if param.cse_pause : input_col(col_r + '\n Mismatched mappings in the same dictionary' + col_e) else : print(col_r + '\n Mismatched mappings in the same dictionary' + col_e) - + codeword_ranges = list(mapping_codeword_ranges.values())[0] - + for i, j in zip(list(codeword_ranges.keys())[:-1], list(codeword_ranges.keys())[1:]) : if 2 * codeword_ranges[i][0] - 1 != codeword_ranges[j][1] and verbosity in ['all','error'] : if param.cse_pause : input_col(col_r + '\n Discontinuity between codeword lengths {0} and {1}'.format(i, j) + col_e) else : print(col_r + '\n Discontinuity between codeword lengths {0} and {1}'.format(i, j) + col_e) - + HUFFMAN_SHAPE = [(codeword_len, codeword_min << (32 - codeword_len), codeword_max) for codeword_len, (codeword_min, codeword_max) in codeword_ranges.items()] - + for mapping_type_string, mapping in dict_mappings.items() : mapping_type = mapping_types[mapping_type_string] - + HUFFMAN_SYMBOLS[mapping_type] = {} HUFFMAN_UNKNOWNS[mapping_type] = {} - + for codeword_len, (codeword_min, codeword_max) in codeword_ranges.items() : HUFFMAN_UNKNOWNS[mapping_type][codeword_len] = set() HUFFMAN_SYMBOLS[mapping_type][codeword_len] = [] - + for codeword in range(codeword_max, codeword_min - 1, -1) : codeword_binary = format(codeword, '0' + str(codeword_len) + 'b') symbol = mapping[codeword_binary].strip() - + if symbol == '' : HUFFMAN_UNKNOWNS[mapping_type][codeword_len].add(codeword) HUFFMAN_SYMBOLS[mapping_type][codeword_len].append([0x7F]) @@ -9466,62 +9472,62 @@ def cse_huffman_dictionary_load(cse_variant, cse_major, cse_minor, verbosity) : HUFFMAN_SYMBOLS[mapping_type][codeword_len].append(list(itertools.repeat(0x7F, int(len(symbol) / 2)))) else : HUFFMAN_SYMBOLS[mapping_type][codeword_len].append(list(bytes.fromhex(symbol))) - + return HUFFMAN_SHAPE, HUFFMAN_SYMBOLS, HUFFMAN_UNKNOWNS - + # CSE Huffman Decompressor by "IllegalArgument" (https://github.com/IllegalArgument) def cse_huffman_decompress(module_contents, compressed_size, decompressed_size, HUFFMAN_SHAPE, HUFFMAN_SYMBOLS, HUFFMAN_UNKNOWNS, verbosity) : CHUNK_SIZE = 0x1000 huff_error = False decompressed_array = [] - + # Message Verbosity: All | Error | None - + if not HUFFMAN_SHAPE : return module_contents, huff_error # Failed to load required Huffman dictionary - + chunk_count = int(decompressed_size / CHUNK_SIZE) header_size = chunk_count * 0x4 - + module_buffer = bytearray(module_contents) header_buffer = module_buffer[0:header_size] compressed_buffer = module_buffer[header_size:compressed_size] - + header_entries = struct.unpack('<{:d}I'.format(chunk_count), header_buffer) start_offsets, flags = zip(*[(x & 0x1FFFFFF, (x >> 25) & 0x7F) for x in header_entries]) end_offsets = itertools.chain(start_offsets[1:], [compressed_size - header_size]) - + for index, dictionary_type, compressed_position, compressed_limit in zip(range(chunk_count), flags, start_offsets, end_offsets) : if verbosity == 'all' : print(col_r + '\n ==Processing chunk 0x{:X} at compressed offset 0x{:X} with dictionary 0x{:X}=='.format(index, compressed_position, dictionary_type) + col_e) - + dictionary = HUFFMAN_SYMBOLS[dictionary_type] unknowns = HUFFMAN_UNKNOWNS[dictionary_type] - + decompressed_position, decompressed_limit = index * CHUNK_SIZE, (index + 1) * CHUNK_SIZE - + bit_buffer = 0 available_bits = 0 - + while decompressed_position < decompressed_limit : while available_bits <= 24 and compressed_position < compressed_limit : bit_buffer = bit_buffer | compressed_buffer[compressed_position] << (24 - available_bits) compressed_position += 1 available_bits += 8 - + codeword_length, base_codeword = 0, 0 for length, shape, base in HUFFMAN_SHAPE : if bit_buffer >= shape : codeword_length, base_codeword = length, base break - + if available_bits >= codeword_length : codeword = bit_buffer >> (32 - codeword_length) bit_buffer = (bit_buffer << codeword_length) & 0xFFFFFFFF available_bits = available_bits - codeword_length - + symbol = dictionary[codeword_length][base_codeword - codeword] symbol_length = len(symbol) - + if decompressed_limit - decompressed_position >= symbol_length : if codeword in unknowns[codeword_length] and verbosity in ['all','error'] : print(col_r + '\n Unknown codeword {: <15s} (dictionary 0x{:X}, codeword length {: >2d}, codeword {: >5s}, symbol length {:d}) at decompressed offset 0x{:X}'.format( @@ -9544,9 +9550,9 @@ def cse_huffman_decompress(module_contents, compressed_size, decompressed_size, filler = itertools.repeat(0x7F, decompressed_limit - decompressed_position) decompressed_array.extend(filler) decompressed_position = decompressed_limit - + return bytearray(decompressed_array), huff_error - + # Detect CSE Partition Instance Identifier def cse_part_inid(buffer, cpd_offset, ext_dictionary) : cpd_hdr_struct, cpd_hdr_size = get_cpd(buffer, cpd_offset) @@ -9556,15 +9562,15 @@ def cse_part_inid(buffer, cpd_offset, ext_dictionary) : in_id_stop = 0 cse_part_size = 0 cse_part_name = '' - + if cpd_hdr.Tag == b'$CPD' : # Sanity check mn2_start = cpd_offset + cpd_hdr_size + cpd_entry_num_fix(buffer, cpd_offset, cpd_hdr.NumModules, cpd_hdr_size) * 0x18 - + mn2_hdr = get_struct(buffer, mn2_start, get_manifest(buffer, mn2_start)) - + if mn2_hdr.Tag == b'$MN2' : # Sanity check mn2_size = mn2_hdr.HeaderLength * 4 - + # Detected $CPD + $MN2, search for Instance ID at CSE_Ext_03 or CSE_Ext_16 while int.from_bytes(buffer[mn2_start + mn2_size + in_id_step:mn2_start + mn2_size + in_id_step + 0x4], 'little') not in [0x3,0x16] : in_id_stop += 1 @@ -9577,14 +9583,14 @@ def cse_part_inid(buffer, cpd_offset, ext_dictionary) : cse_in_id = cse_ext_hdr.InstanceID # Partition Instance Identifier cse_part_name = cse_ext_hdr.PartitionName # Partition Name (for uncharted $FPT code, no need for almost duplicate function) cse_part_size = cse_ext_hdr.PartitionSize # Partition Size (for uncharted $FPT code, no need for almost duplicate function) - + return cse_in_id, cse_part_name, cse_part_size - + # Get correct $CPD Entry Counter for end offset detection def cpd_entry_num_fix(buffer, cpd_offset, cpd_entry_count, cpd_hdr_size) : cpd_entry_empty = 0 cpd_entry_end = cpd_offset + cpd_hdr_size + cpd_entry_count * 0x18 - + # Some $CPD may have X entries + empty Y. Try to adjust counter a maximum of 5 times (GREAT WORK INTEL/OEMs...) while int.from_bytes(buffer[cpd_entry_end:cpd_entry_end + 0x18], 'little') == 0 : cpd_entry_end += 0x18 @@ -9592,36 +9598,36 @@ def cpd_entry_num_fix(buffer, cpd_offset, cpd_entry_count, cpd_hdr_size) : if cpd_entry_empty > 5 : err_stor.append([col_r + 'Error: Failed to fix $CPD entry counter at 0x%X!' % cpd_offset + col_e, True]) break - + return cpd_entry_count + cpd_entry_empty - + # Calculate $CPD Partition size via its Entries def cpd_size_calc(buffer, cpd_offset, align_size) : cpd_fw_end = 0 cpd_offset_last = 0 - + cpd_hdr_struct, cpd_hdr_size = get_cpd(buffer, cpd_offset) cpd_hdr = get_struct(buffer, cpd_offset, cpd_hdr_struct) cpd_num = cpd_entry_num_fix(buffer, cpd_offset, cpd_hdr.NumModules, cpd_hdr_size) - + for entry in range(cpd_num) : # Check all $CPD Entry Sizes (Manifest, Metadata, Modules) cpd_entry_hdr = get_struct(buffer, cpd_offset + cpd_hdr_size + entry * 0x18, CPD_Entry) cpd_entry_offset,_,_ = cpd_entry_hdr.get_flags() - + # Store last entry (max $CPD offset) if cpd_entry_offset > cpd_offset_last : cpd_offset_last = cpd_entry_offset cpd_fw_end = cpd_entry_offset + cpd_entry_hdr.Size - + cpd_align = (cpd_fw_end - cpd_offset) % align_size cpd_fw_end = cpd_fw_end + align_size - cpd_align - + return cpd_fw_end - + # Validate $CPD Checksum def cpd_chk(cpd_data, variant, major) : cpd_hdr_struct, _ = get_cpd(cpd_data, 0) - + if cpd_hdr_struct.__name__ == 'CPD_Header_R1' : cpd_chk_file = cpd_data[0xB] cpd_sum = sum(cpd_data) - cpd_chk_file @@ -9632,10 +9638,10 @@ def cpd_chk(cpd_data, variant, major) : else : cpd_chk_file = int.from_bytes(cpd_data[0x10:0x14], 'little') cpd_chk_calc = crccheck.crc.Crc32.calc(cpd_data[:0x10] + b'\x00' * 4 + cpd_data[0x14:]) - + # Store $CPD Checksum Values to check if they exist in the known bad CSE Hashes/Checksums list cpd_chk_rslt = ['$CPD_%s_%d_0x%0.8X' % (variant,major,cpd_chk_file),'$CPD_%s_%d_0x%0.8X' % (variant,major,cpd_chk_calc)] - + return cpd_chk_file == cpd_chk_calc, cpd_chk_file, cpd_chk_calc, cpd_chk_rslt # Check CSE/GSC IUP firmware size @@ -9644,63 +9650,63 @@ def chk_iup_size(eng_size_text, file_end, eng_fw_end, variant_p, platform) : eng_size_text = [col_m + 'Warning: %s %s Firmware size exceeds File, possible data loss!' % (variant_p, platform) + col_e, True] elif eng_fw_end < file_end : padd_size_iup = file_end - eng_fw_end - + if reading[eng_fw_end:file_end] == padd_size_iup * b'\xFF' : eng_size_text = [col_y + 'Note: File has harmless unneeded %s %s Firmware end padding!' % (variant_p, platform) + col_e, False] # warn_stor if param.check : # Remove unneeded padding, when applicable (Debug/Research) with open('__RPADD__' + os.path.basename(file_in), 'wb') as o : o.write(reading[:-padd_size_iup]) else : eng_size_text = [col_m + 'Warning: File size exceeds %s %s Firmware, data in padding!' % (variant_p, platform) + col_e, True] - + return eng_size_text # Get Engine/Graphics/Independent Manifest Structure def get_manifest(buffer, offset) : man_ver = int.from_bytes(buffer[offset + 0x8:offset + 0xC], 'little') # $MAN/$MN2 Version Tag num_info = int.from_bytes(buffer[offset + 0x20:offset + 0x24], 'little') # $MAN/$MN2 NumModules/BuildTag - + if man_ver == 0x10000 and (0x0 < num_info < 0x50) : return MN2_Manifest_R0 if man_ver == 0x10000 : return MN2_Manifest_R1 if man_ver == 0x21000 : return MN2_Manifest_R2 - + return MN2_Manifest_R2 - + # Get Flash Partition Table Structure def get_fpt(buffer, offset) : fpt_ver = buffer[offset + 0x8] # $FPT Version Tag fpt_crc = buffer[offset + 0x16:offset + 0x18] # $FPT v2.1 2nd word of CRC-32 for FIT Bug (v2.1 with v2.0 Version Tag) - + if fpt_ver == 0x21 : return FPT_Header_21, fpt_ver if fpt_ver == 0x20 and fpt_crc not in [b'\x00\x00',b'\xFF\xFF'] : return FPT_Header_21, 0x21 if fpt_ver in (0x10,0x20) : return FPT_Header, fpt_ver - + return FPT_Header_21, fpt_ver - + # Get Code Partition Directory Structure def get_cpd(buffer, offset) : cpd_ver = buffer[offset + 0x8] # $CPD Version Tag - + if cpd_ver == 1 : return CPD_Header_R1, ctypes.sizeof(CPD_Header_R1) if cpd_ver == 2 : return CPD_Header_R2, ctypes.sizeof(CPD_Header_R2) - + return CPD_Header_R2, ctypes.sizeof(CPD_Header_R2) - + # Get Code Partition Directory Structure def get_bpdt(buffer, offset) : bpdt_ver = buffer[offset + 0x6] # BPDT Version Tag - + if bpdt_ver == 1 : return BPDT_Header_1 if bpdt_ver == 2 : return BPDT_Header_2 - + return BPDT_Header_2 - + # Get RBEP > rbe and/or FTPR > pm Module "Metadata" def get_rbe_pm_met(rbe_pm_data_d, rbe_pm_met_hashes) : rbe_pm_patt_256_1 = re.compile(br'\x86\x80.{70}\x86\x80.{70}\x86\x80', re.DOTALL).search(rbe_pm_data_d) # Find SHA-256 "Metadata" pattern 1 rbe_pm_patt_256_2 = re.compile(br'\x86\x80.{46}\x86\x80.{46}\x86\x80', re.DOTALL).search(rbe_pm_data_d) # Find SHA-256 "Metadata" pattern 2 rbe_pm_patt_384_1 = re.compile(br'\x86\x80.{86}\x86\x80.{86}\x86\x80', re.DOTALL).search(rbe_pm_data_d) # Find SHA-384 "Metadata" pattern 1 rbe_pm_patt_384_2 = re.compile(br'\x86\x80.{62}\x86\x80.{62}\x86\x80', re.DOTALL).search(rbe_pm_data_d) # Find SHA-384 "Metadata" pattern 2 - + if rbe_pm_patt_256_1 : rbe_pm_patt_start = rbe_pm_patt_256_1.start() rbe_pm_struct_name = RBE_PM_Metadata @@ -9719,48 +9725,48 @@ def get_rbe_pm_met(rbe_pm_data_d, rbe_pm_met_hashes) : rbe_pm_struct_size = ctypes.sizeof(RBE_PM_Metadata_R4) else : return rbe_pm_met_hashes - + rbe_pm_met_start = rbe_pm_patt_start - 0x6 # "Metadata" entry starts 0x6 before VEN_ID 8086 rbe_pm_met_end = rbe_pm_met_start # Initialize "Metadata" entries end while rbe_pm_data_d[rbe_pm_met_end + 0x6:rbe_pm_met_end + 0x8] == b'\x86\x80' : rbe_pm_met_end += rbe_pm_struct_size # Find end of "Metadata" entries rbe_pm_met_data = bytes(rbe_pm_data_d[rbe_pm_met_start:rbe_pm_met_end]) # Store "Metadata" entries rbe_pm_met_count = divmod(len(rbe_pm_met_data), rbe_pm_struct_size)[0] # Count "Metadata" entries - + for i in range(rbe_pm_met_count) : rbe_pm_met = get_struct(rbe_pm_met_data, i * rbe_pm_struct_size, rbe_pm_struct_name) # Parse "Metadata" entries rbe_pm_met_hash_len = len(rbe_pm_met.Hash) * 4 # Get "Metadata" entry Hash Length rbe_pm_met_hash = '%0.*X' % (rbe_pm_met_hash_len * 2, int.from_bytes(rbe_pm_met.Hash, 'little')) # Get "Metadata" entry Hash rbe_pm_met_hashes.append(rbe_pm_met_hash) # Store each "Metadata" entry Hash for Modules w/o Metadata Hash validation - + return rbe_pm_met_hashes # https://github.com/skochinsky/me-tools/blob/master/me_unpack.py by Igor Skochinsky def get_struct(input_stream, start_offset, class_name, param_list = None) : if param_list is None : param_list = [] - + structure = class_name(*param_list) # Unpack parameter list struct_len = ctypes.sizeof(structure) struct_data = input_stream[start_offset:start_offset + struct_len] fit_len = min(len(struct_data), struct_len) - + if (start_offset >= file_end) or (fit_len < struct_len) : err_stor.append([col_r + 'Error: Offset 0x%X out of bounds at %s, possibly incomplete image!' % (start_offset, class_name.__name__) + col_e, True]) - + for error in err_stor : print('\n' + error[0]) - + copy_on_msg(err_stor + warn_stor + note_stor) # Close input and copy it in case of messages - + mea_exit(1) - + ctypes.memmove(ctypes.addressof(structure), struct_data, fit_len) - + return structure - + # https://stackoverflow.com/a/34301571 by Sam P # noinspection PyProtectedMember def struct_json(structure) : result = {} - + def get_value(value) : if (type(value) not in [int, float, bool, str]) and not bool(value) : value = None # Null Pointer (not primitive type, is False) @@ -9770,21 +9776,21 @@ def get_value(value) : value = value.decode('utf-8') # Byte elif hasattr(value, '_fields_') : value = struct_json(value) # Probably nested struct - + return value - + def get_array(array) : ar = [] for value in array : value = get_value(value) ar.append(value) - + return ar - + for field in structure._fields_ : value = get_value(getattr(structure, field[0])) result[field[0]] = value - + return json.dumps(result, indent=4) # Initialize PLTable @@ -9797,39 +9803,39 @@ def ext_table(row_col_names, header, padd) : pt.right_padding_width = padd pt.hrules = pltable.ALL pt.vrules = pltable.ALL - + return pt - + # Convert PLTable Object to HTML String def pt_html(pt_obj) : return ansi_escape.sub('', str(pt_obj.get_html_string(format=True, attributes={}))) - + # Convert PLTable Object to JSON Dictionary def pt_json(pt_obj) : # When PLTable Object Header is hidden in MEA, we can assume it is a "Field: Value" table return pt_obj.get_json_dict(re_pattern=ansi_escape, is_field_value=not pt_obj.header) - + # Detect DB Revision def mea_hdr_init() : mea_db_rev = 'Unknown' mea_db_rev_p = col_r + mea_db_rev + col_e - + for line in mea_db_lines : if 'Revision' in line : mea_db_list = line.split() if len(mea_db_list) >= 3 : mea_db_rev = mea_db_list[2] mea_db_rev_p = col_y + mea_db_rev + col_e - + break # Revision line found, skip rest of DB - + return mea_db_rev, mea_db_rev_p # Print MEA Header def mea_hdr(mea_db_rev_p) : hdr_pt = ext_table([], False, 1) hdr_pt.add_row([col_y + ' %s' % title + col_e + ' %s ' % mea_db_rev_p]) - + print(hdr_pt) # https://stackoverflow.com/a/22881871 by jfs @@ -9851,7 +9857,7 @@ def show_exception_and_exit(exc_type, exc_value, tb) : print(col_r + '\nError: %s crashed, please report the following:\n' % title) traceback.print_exception(exc_type, exc_value, tb) print(col_e) - + mea_exit(1) # Execute final actions @@ -9861,13 +9867,13 @@ def mea_exit(code) : if not thread_update.is_alive() and thread_update.result : print(thread_update.result) except : pass - + colorama.deinit() # Stop Colorama - + if not param.skip_pause : input('\nPress enter to exit') - + sys.exit(code) - + # Input Colorama Workaround (Windows, Python 3.5+) # https://github.com/tartley/colorama/issues/103#issuecomment-629816451 def input_col(message) : @@ -9877,15 +9883,15 @@ def input_col(message) : # Calculate MD5 hash of data def md5(data) : return hashlib.md5(data).hexdigest().upper() - + # Calculate SHA-1 hash of data def sha_1(data) : return hashlib.sha1(data).hexdigest().upper() - + # Calculate SHA-256 hash of data def sha_256(data) : return hashlib.sha256(data).hexdigest().upper() - + # Calculate SHA-384 hash of data def sha_384(data) : return hashlib.sha384(data).hexdigest().upper() @@ -9896,49 +9902,49 @@ def get_hash(data, hash_size) : if hash_size == 0x14 : return sha_1(data) if hash_size == 0x20 : return sha_256(data) if hash_size == 0x30 : return sha_384(data) - + return sha_384(data) - + # Validate CPU Microcode Checksum def mc_chk32(data) : chk32 = 0 - + for idx in range(0, len(data), 4) : # Move 4 bytes at a time chkbt = int.from_bytes(data[idx:idx + 4], 'little') # Convert to int, MSB at the end (LE) chk32 += chkbt - + return -chk32 & 0xFFFFFFFF # Return 0 - + # Copy input file if there are worthy Notes, Warnings or Errors # Must be called at the end of analysis to gather any generated messages def copy_on_msg(msg_all) : if param.copy_dis or is_unsupported: return - + copy = False - + # Detect if any copy-worthy generated message exists for message in msg_all : if message[1] : copy = True - + if param.bypass and (err_stor or warn_stor or note_stor) : copy = True # Copy on any message (Debug/Research) - + # At least one message needs a file copy if copy : file_name = os.path.basename(file_in) check_dir = os.path.join(out_dir, '__CHECK__', '') check_name = os.path.join(check_dir, file_name) - + if not os.path.isdir(check_dir) : os.mkdir(check_dir) - + # Check if same file already exists if os.path.isfile(check_name) : with open(file_in, 'rb') as input_file : input_sha1 = sha_1(input_file.read()) with open(check_name, 'rb') as same_file : same_sha1 = sha_1(same_file.read()) if input_sha1 == same_sha1 : return - + check_name += '_%d' % cur_count - + shutil.copyfile(file_in, check_name) # Store/Show new firmware Note @@ -9955,13 +9961,13 @@ def note_new_fw(variant_p) : def get_db_json_obj(obj_name) : obj_bgn = mea_db_read.find(obj_name + '*BGN') obj_end = mea_db_read.find(obj_name + '*END') - + if obj_bgn == -1 or obj_end == -1 or obj_bgn >= obj_end : return None - + obj_data = mea_db_read[obj_bgn + len(obj_name + '*BGN'):obj_end] - + obj_data = ''.join([line.split(' # ')[0] for line in obj_data.split('\n')]) - + return json.loads(obj_data) # Detect Intel Flash Descriptor (FD) @@ -9969,15 +9975,15 @@ def fd_anl_init(reading, file_end, start_man_match, end_man_match) : fd_match = list(fd_pat.finditer(reading)) # Flash Descriptor Pattern Match/Iteration ranges fd_count = len(fd_match) # Flash Descriptor Pattern Count reading_msg = '' # Input buffer new Flash Descriptor range message - + if not fd_match : return False, reading, file_end, start_man_match, end_man_match, 0, 0, 0, 0, False, False, reading_msg - + # Detected Flash Descriptor, use first but notify if more exist fd_start = fd_match[0].start() fd_end = fd_match[0].end() - + fd_flmap0_fcba = reading[fd_start + 0x4] * 0x10 # Component Base Address from FD start (ICH8-ICH10 = 1, IBX = 2, CPT+ = 3) - + # I/O Controller Hub (ICH) if fd_flmap0_fcba == 0x10 : fd_is_ich = True @@ -9988,10 +9994,10 @@ def fd_anl_init(reading, file_end, start_man_match, end_man_match) : fd_is_ich = False start_substruct = 0x10 # At PCH, Flash Descriptor starts at 0x10 end_substruct = 0xBC # 0xBC for [0xAC] + 0xFF * 16 sanity check - + start_fd_match = fd_start - start_substruct # Flash Descriptor pattern Start Offset end_fd_match = fd_end - end_substruct # Flash Descriptor pattern End Offset - + # Calculate Flash Descriptor Flash Component Total Size fd_flmap0_nc = ((int.from_bytes(reading[end_fd_match:end_fd_match + 0x4], 'little') >> 8) & 3) + 1 # Component Count (00 = 1, 01 = 2) fd_flmap1_isl = reading[end_fd_match + 0x7] # PCH/ICH Strap Length (ME 2-8 & TXE 0-2 & SPS 1-2 <= 0x12, ME 9+ & TXE 3+ & SPS 3+ >= 0x13) @@ -10000,7 +10006,7 @@ def fd_anl_init(reading, file_end, start_man_match, end_man_match) : fd_comp_2_bitwise = 0x4 if fd_flmap1_isl >= 0x13 else 0x3 # Component 2 Density Bits (ME 2-8 & TXE 0-2 & SPS 1-2 = 3, ME 9+ & TXE 3+ & SPS 3+ = 4) fd_comp_all_size = comp_dict[fd_comp_den & fd_comp_1_bitwise] # Component 1 Density (FCBA > C0DEN) if fd_flmap0_nc == 2 : fd_comp_all_size += comp_dict[fd_comp_den >> fd_comp_2_bitwise] # Component 2 Density (FCBA > C1DEN) - + # Update input file RAM buffer (reading) based on the actual Flash Descriptor Flash Component Total Size # Do not update reading if the initially detected Manifest pattern is outside the FD Component Total Data # Do not update reading if the input file starts with Download & Execute (DnX) $CPD RCIP Partition @@ -10013,11 +10019,11 @@ def fd_anl_init(reading, file_end, start_man_match, end_man_match) : start_man_match -= start_fd_match # Update Manifest Pattern Start Offset (before FD) end_man_match -= start_fd_match # Update Manifest Pattern End Offset (before FD) start_fd_match,end_fd_match = (0x0,0x4) if fd_is_ich else (0x0,0x14) # Update FD Pattern Start & End Offsets (after Manifest) - + # Do not notify for OEM Backup Flash Descriptors within the chosen/first Flash Descriptor for match in fd_match[1:] : if fd_start < match.start() <= fd_start + 0x1000 : fd_count -= 1 - + # Check if the Flash Descriptor Flash Component Total Size fits within the input file fd_input_size = len(reading[start_fd_match:start_fd_match + fd_comp_all_size]) if fd_input_size != fd_comp_all_size and not file_in.endswith('.scap') : @@ -10025,15 +10031,15 @@ def fd_anl_init(reading, file_end, start_man_match, end_man_match) : err_stor.append([col_r + 'Error: Firmware is incomplete/corrupted, expected 0x%X not 0x%X!' % (fd_comp_all_size, fd_input_size) + col_e, False]) else : fd_is_cut = False - + return True, reading, file_end, start_man_match, end_man_match, start_fd_match, end_fd_match, fd_count, fd_comp_all_size, fd_is_ich, fd_is_cut, reading_msg # Analyze Intel Flash Descriptor (FD) Regions def fd_anl_rgn(start_fd_match, end_fd_match, fd_is_ich) : fd_reg_exist = [] # BIOS/IAFW + Engine/Graphics - + fd_rgn_base = end_fd_match + 0x3C if fd_is_ich else end_fd_match + 0x2C - + bios_fd_base = int.from_bytes(reading[fd_rgn_base + 0x4:fd_rgn_base + 0x6], 'little') bios_fd_limit = int.from_bytes(reading[fd_rgn_base + 0x6:fd_rgn_base + 0x8], 'little') me_fd_base = int.from_bytes(reading[fd_rgn_base + 0x8:fd_rgn_base + 0xA], 'little') @@ -10042,7 +10048,7 @@ def fd_anl_rgn(start_fd_match, end_fd_match, fd_is_ich) : pdr_fd_limit = int.from_bytes(reading[fd_rgn_base + 0x12:fd_rgn_base + 0x14], 'little') devexp_fd_base = int.from_bytes(reading[fd_rgn_base + 0x14:fd_rgn_base + 0x16], 'little') devexp_fd_limit = int.from_bytes(reading[fd_rgn_base + 0x16:fd_rgn_base + 0x18], 'little') - + if bios_fd_limit != 0 : bios_fd_start = bios_fd_base * 0x1000 + start_fd_match # fd_match required in case FD is not at the start of image bios_fd_size = (bios_fd_limit + 1 - bios_fd_base) * 0x1000 # The +1 is required to include last Region byte @@ -10050,7 +10056,7 @@ def fd_anl_rgn(start_fd_match, end_fd_match, fd_is_ich) : fd_reg_exist.extend((bios_fd_exist,bios_fd_start,bios_fd_size)) # BIOS/IAFW Region exists else : fd_reg_exist.extend((False,0,0)) # BIOS/IAFW Region missing - + if me_fd_limit != 0 : me_fd_start = me_fd_base * 0x1000 + start_fd_match me_fd_size = (me_fd_limit + 1 - me_fd_base) * 0x1000 @@ -10058,7 +10064,7 @@ def fd_anl_rgn(start_fd_match, end_fd_match, fd_is_ich) : fd_reg_exist.extend((me_fd_exist,me_fd_start,me_fd_size)) # Engine/Graphics Region exists else : fd_reg_exist.extend((False,0,0)) # Engine/Graphics Region missing - + if pdr_fd_limit != 0 : pdr_fd_start = pdr_fd_base * 0x1000 + start_fd_match pdr_fd_size = (pdr_fd_limit + 1 - pdr_fd_base) * 0x1000 @@ -10066,7 +10072,7 @@ def fd_anl_rgn(start_fd_match, end_fd_match, fd_is_ich) : fd_reg_exist.extend((pdr_fd_exist,pdr_fd_start,pdr_fd_size)) # Platform Data Region exists else : fd_reg_exist.extend((False,0,0)) # Engine/Graphics Region missing - + if devexp_fd_limit != 0 : devexp_fd_start = devexp_fd_base * 0x1000 + start_fd_match devexp_fd_size = (devexp_fd_limit + 1 - devexp_fd_base) * 0x1000 @@ -10074,9 +10080,9 @@ def fd_anl_rgn(start_fd_match, end_fd_match, fd_is_ich) : fd_reg_exist.extend((devexp_fd_exist,devexp_fd_start,devexp_fd_size)) # Device Expansion Region exists else : fd_reg_exist.extend((False,0,0)) # Device Expansion Region missing - + return fd_reg_exist - + # Format firmware version based on Variant def get_fw_ver(variant, major, minor, hotfix, build) : if variant in ['SPS','CSSPS'] : @@ -10093,51 +10099,51 @@ def get_fw_ver(variant, major, minor, hotfix, build) : version = '%s.%s.%s.%s' % (major, minor, hotfix, '{0:04d}'.format(build)) else : version = '%s.%s.%s.%s' % (major, minor, hotfix, build) - + return version - + # Check if Fixed Offset Variables (FOVD/NVKR) Partition is dirty -def fovd_clean(fovdtype) : +def fovd_clean(fovdtype) : for part in fpt_part_all : if (fovdtype,part[0]) in [('new','FOVD'),('old','NVKR')] : if part[6] : return True # Empty = Clean - + if fovdtype == 'new' : return False # Non-Empty = Dirty - + if fovdtype == 'old' : nvkr_size = int.from_bytes(reading[part[1] + 0x19:part[1] + 0x1C], 'little') nvkr_data = reading[part[1] + 0x1C:part[1] + 0x1C + nvkr_size] - + return bool(nvkr_data == b'\xFF' * nvkr_size) - + break # FOVD/NVKR Partition found, skip the rest - + return True - + # Calculate Hash Hex Digest of Message def calc_hash_hex(message, hash_func) : msg_hash = hash_func() msg_hash.update(message) - + return msg_hash.hexdigest() - + # Calculate Hash Digest of Message def calc_hash(message, hash_func) : msg_hash = hash_func() msg_hash.update(message) - + return msg_hash.digest() # SSA-PSS Mask Generation Function def pss_mgf(seed, mask_len, hash_func) : mask = b'' - + hash_len = hash_func().digest_size if mask_len > (hash_len << 32) : return '' # Mask length is invalid - + for i in range(-(-mask_len // hash_len)) : # math.ceil(x/y) = -(-x//y) mask += calc_hash(seed + i.to_bytes(4, 'big'), hash_func) - + return mask # Apply SSA-PSS Mask to DB @@ -10147,39 +10153,39 @@ def unmask_DB(masked_DB, mask) : # Get SSA-PSS Hash & Mask DB def parseSign(em_sign, hash_func) : TF = 0xBC - + digest_size = hash_func().digest_size sign = bytes.fromhex(em_sign) sig_hash = sign[-digest_size - 1:-1] if sign[-1] != TF : return '', None # TF is invalid masked_DB = sign[0:-digest_size - 1] - + return sig_hash, masked_DB # Get SSA-PSS Salt from DB def get_salt(unmasked_DB, mod_size) : PADDING_BYTE = b'\x00' SEPARATOR = b'\x01' - + z_bits = 8 - (mod_size - 1) % 8 z_byte = unmasked_DB[0] for i in range(z_bits) : z_byte &= ~(0x80 >> i) - + index = unmasked_DB.find(SEPARATOR) if (index == -1) or (z_byte != 0) or (unmasked_DB[1:index] != PADDING_BYTE * (index-1)) : return '' # Invalid padding - + return unmasked_DB[index + 1:] # Final SSA-PSS Signature validation def pss_final_validate(message, salt_unmask, hash_func) : PADDING_BYTE = b'\x00' SALT_PADDING_COUNT = 8 - + # Calculate hash of the message message_hash = calc_hash(message, hash_func) M_salt = PADDING_BYTE * SALT_PADDING_COUNT + message_hash + salt_unmask - + return calc_hash(M_salt, hash_func) # Verify SSA-PSS Signature @@ -10187,20 +10193,20 @@ def pss_verify(em_sign, message, sign_len, hash_func) : # Extract hash and Mask DB sig_hash, masked_DB = parseSign(em_sign, hash_func) if sig_hash == '' : return '', None - + # Calculate a mask mask = pss_mgf(sig_hash, len(masked_DB), hash_func) if mask == '' : return sig_hash, '' - + # Apply mask to DB unmasked_DB = unmask_DB(masked_DB, mask) - + # Extract salt from DB salt_unmask = get_salt(unmasked_DB, sign_len) if salt_unmask == '' : return sig_hash, '' - + return sig_hash, pss_final_validate(message, salt_unmask, hash_func) - + # Validate Manifest RSA Signature def rsa_sig_val(man_hdr_struct, buffer, check_start) : man_tag = man_hdr_struct.Tag.decode('utf-8') @@ -10212,12 +10218,12 @@ def rsa_sig_val(man_hdr_struct, buffer, check_start) : man_sign = int.from_bytes(man_hdr_struct.RSASignature, 'little') hash_data = buffer[check_start:check_start + 0x80] # First 0x80 before RSA block hash_data += buffer[check_start + man_hdr_size:check_start + man_size] # Manifest protected data - + # return [RSA Sig isValid, RSA Sig Decr Hash, RSA Sig Data Hash, RSA Validation isCrashed, $MN2 Offset, $MN2 Struct Object] - + try : dec_sign = '%0.*X' % (man_key_size * 2, pow(man_sign, man_pexp, man_pkey)) # Decrypted Signature - + if (man_tag,man_key_size) == ('$MAN',0x100) : # SHA-1 rsa_hash = calc_hash_hex(hash_data, hashlib.sha1).upper() dec_hash = dec_sign[-40:] # 160-bit @@ -10230,32 +10236,32 @@ def rsa_sig_val(man_hdr_struct, buffer, check_start) : else : rsa_hash, dec_hash = pss_verify(dec_sign, hash_data, 0x180, hashlib.sha384) rsa_hash, dec_hash = rsa_hash.hex().upper(), dec_hash.hex().upper() - + return [dec_hash == rsa_hash, dec_hash, rsa_hash, False, check_start, man_hdr_struct] # RSA block validation check OK except : if (man_pexp,man_pkey,man_sign) == (0,0,0) : return [True, 0, 0, False, check_start, man_hdr_struct] # Valid/Empty RSA block, no validation crash - + return [False, 0, 0, True, check_start, man_hdr_struct] # RSA block validation check crashed, debugging required - + # Fix early PRE firmware which are wrongly reported as PRD def release_fix(release, rel_db, rsa_key_hash) : if release == 'Production' and rsa_key_hash in rsa_pre_keys : release = 'Pre-Production' rel_db = 'PRE' - + return release, rel_db - + # Search DB for manual CSE SKU values def get_cse_db(variant) : db_sku_chk = 'NaN' sku = 'NaN' sku_stp = 'Unknown' sku_pdm = 'UPDM' - + for line in mea_db_lines : if rsa_sig_hash in line : line_parts = line.strip().split('_') - + if variant == 'CSME' : db_sku_chk = line_parts[2] # Store the SKU from DB for latter use sku = sku_init + " " + line_parts[2] # Cell 2 is SKU @@ -10265,7 +10271,7 @@ def get_cse_db(variant) : if line_parts[1] not in ('X','XX') : sku_stp = line_parts[1] # Cell 1 is PCH/SoC Stepping elif variant == 'CSSPS' : if line_parts[-1] == 'EXTR' and line_parts[3] not in ('X','XX') : sku_stp = line_parts[3] # Cell 3 is PCH/SoC Stepping - + break # Break loop at 1st rsa_sig_hash match return db_sku_chk, sku, sku_stp, sku_pdm @@ -10273,7 +10279,7 @@ def get_cse_db(variant) : # Get CSME 12+ Final SKU, SKU Platform, SKU Stepping def get_csme12_sku(sku_init, fw_0C_sku0, fw_0C_sku2, sku, sku_result, sku_stp, db_sku_chk, pos_sku_tbl, pch_init_final) : if (variant,major) == ('CSME',11) : return sku, sku_result, sku_stp # CSME 11 has its own SKU Platform retrieval methodology - + if sku != 'NaN' : sku_result = db_sku_chk # SKU Platform retrieved from DB (Override) elif pos_sku_tbl != 'Unknown' : @@ -10286,23 +10292,23 @@ def get_csme12_sku(sku_init, fw_0C_sku0, fw_0C_sku2, sku, sku_result, sku_stp, d if 'LP' in fw_0C_sku0 : sku_result = 'LP' elif 'H' in fw_0C_sku0 : sku_result = 'H' else : sku_result = 'Unknown' - + if (variant,major,minor,sku_result) == ('CSME',14,5,'H') : sku_result = 'V' # Adjust CSME 14.5 SKU Platform from H to V elif (variant,major,sku_init,sku_result) == ('CSME',13,'Slim','LP') : sku_result = 'N' # Adjust CSME 13 SLM SKU Platform from LP to N - + sku = '%s %s' % (sku_init, sku_result) # Adjust final SKU to add Platform - + if sku_stp == 'Unknown' and pch_init_final : sku_stp = pch_init_final[-1][1] # Set Chipset Stepping, if not found at DB - + return sku, sku_result, sku_stp # Get CSE DB SKU def sku_db_cse(sku_type, sku_plat, sku_stp, sku_db, stp_only, skip_csme11) : if (variant,major,skip_csme11) == ('CSME',11,True) : return sku_db - + if sku_stp == 'Unknown' : sku_db = '%s%sX' % (sku_type if stp_only else sku_type + '_', sku_plat if stp_only else sku_plat + '_') else : sku_db = '%s%s' % (sku_type if stp_only else sku_type + '_', sku_plat if stp_only else sku_plat + '_') + sku_stp - + return sku_db # Detect Variant/Family @@ -10313,27 +10319,27 @@ def get_variant(buffer, mn2_struct, mn2_match_start, mn2_match_end, mn2_rsa_hash var_rsa_db = True year,month,_ = mn2_date major,minor,hotfix,build = mn2_ver - + # Detect Variant by unique DB RSA Public Key for line in mea_db_lines : if mn2_rsa_hash in line : line_parts = line.strip().split('_') variant = line_parts[1] # Store the Variant - + break # Break loop at 1st match - + if mn2_rsa_hash == '86C0E5EF0CFEFF6D810D68D83D8C6ECB68306A644C03C0446B646A3971D37894' and 6 <= major <= 10 : variant = 'ME' elif mn2_rsa_hash == '86C0E5EF0CFEFF6D810D68D83D8C6ECB68306A644C03C0446B646A3971D37894' and 0 <= major <= 2 : variant = 'TXE' - + if variant.startswith(('Unknown','TBD')) : if variant == 'Unknown' : var_rsa_db = False # TBD are multi-platform RSA Public Keys - + # Get CSE $CPD Module Names only for targeted variant detection via special ext_anl _Stage1 mode cpd_mod_names,_ = ext_anl(buffer, '$MN2_Stage1', mn2_match_start, file_end, ['CSME',major,minor,hotfix,build,year,month,variant_p], None, [[],''], [[],-1,-1,-1]) - + if cpd_mod_names : is_meu = hasattr(mn2_struct, 'MEU_Minor') # Check if $MN2 has MEU fields - + for mod in cpd_mod_names : if mod == 'fwupdate' : variant = 'CSME' # CSME elif mod in ['bup_rcv', 'sku_mgr', 'manuf'] : variant = 'CSSPS' # REC, OPR, IGN @@ -10377,17 +10383,17 @@ def get_variant(buffer, mn2_struct, mn2_match_start, mn2_match_end, mn2_rsa_hash elif mod == 'VBT' and major == 19 : variant = 'OROMDG1' # DG1 elif mod == 'VBT' and major == 20 : variant = 'OROMDG2' # DG2 elif mod == 'VBT' : variant = 'OROM' # Unknown - + if variant.startswith(('Unknown','TBD')) and (major,minor) in [(4,0),(3,0)] : variant = 'CSTXE' # CSTXE - + elif buffer[mn2_match_end + 0x270 + 0x80:mn2_match_end + 0x270 + 0x84] == b'$MME' : # $MME: ME2-5/SPS1 = 0x50, ME6-10/SPS2-3 = 0x60, TXE1-2 = 0x80 variant = 'TXE' - + elif re.compile(br'\$SKU\x03\x00\x00\x00\x2F\xE4\x01\x00').search(buffer) or \ re.compile(br'\$SKU\x03\x00\x00\x00\x08\x00\x00\x00').search(buffer) : variant = 'SPS' - + # Create Variant display-friendly text if variant == 'CSME': variant_p = 'CSE ME' @@ -10422,13 +10428,13 @@ def get_variant(buffer, mn2_struct, mn2_match_start, mn2_match_end, mn2_rsa_hash elif variant == 'SPS': variant_p = variant variant_p_fw = 'Server Platform Services' - + return variant, variant_p, variant_p_fw, var_rsa_db # Check online for MEA & DB Updates def mea_upd_check(db_path) : result = None - + try : with urllib.request.urlopen('https://raw.githubusercontent.com/platomav/MEAnalyzer/master/MEA.py') as gpy : git_py = gpy.read(0x100) git_py_utf = git_py.decode('utf-8','ignore') @@ -10441,20 +10447,20 @@ def mea_upd_check(db_path) : py_print = '(v%s --> v%s)' % (cur_py_ver, git_py_ver) py_is_upd = bool(int(cur_py_tup[0]) > int(git_py_tup[0]) or (int(cur_py_tup[0]) == int(git_py_tup[0]) and (int(cur_py_tup[1]) > int(git_py_tup[1]) or (int(cur_py_tup[1]) == int(git_py_tup[1]) and int(cur_py_tup[2]) >= int(git_py_tup[2]))))) - + with urllib.request.urlopen('https://raw.githubusercontent.com/platomav/MEAnalyzer/master/MEA.dat') as gdb : git_db = gdb.readlines(0x80) git_db_ver = git_db[1].decode('utf-8','ignore')[14:].split('_')[0].split(' ')[0] with open(db_path, 'r', encoding='utf-8') as db : cur_db_ver = db.readlines()[1][14:].split('_')[0].split(' ')[0] db_print = '(r%s --> r%s)' % (cur_db_ver, git_db_ver) db_is_upd = cur_db_ver >= git_db_ver - + git_link = '\n Download the latest from https://github.com/platomav/MEAnalyzer/' if not py_is_upd and not db_is_upd : result = col_m + '\nWarning: Outdated ME Analyzer %s & Database %s!' % (py_print,db_print) + git_link + col_e elif not py_is_upd : result = col_m + '\nWarning: Outdated ME Analyzer %s!' % py_print + git_link + col_e elif not db_is_upd : result = col_m + '\nWarning: Outdated Database %s!' % db_print + git_link + col_e except : result = None - + return result # Scan all files of a given directory @@ -10463,9 +10469,9 @@ def mass_scan(f_path) : for root, _, files in os.walk(f_path): for name in files : mass_files.append(os.path.join(root, name)) - + input('\nFound %s file(s)\n\nPress enter to start' % len(mass_files)) - + return mass_files # Colorama ANSI Color/Font Escape Character Sequences Regex @@ -10681,7 +10687,7 @@ def mass_scan(f_path) : 'CSE_Ext_22_Mod' : CSE_Ext_22_Mod, 'CSE_Ext_23_Mod' : CSE_Ext_23_Mod, } - + # CSE Key Manifest Hash Usages key_dict = { 0 : 'CSE BUP', # Fault Tolerant Partition (FTPR) @@ -10742,7 +10748,7 @@ def mass_scan(f_path) : 197 : 'SoC SoCC', # Configuration 198 : 'SoC SPHY', } - + # IFWI BPDT Entry Types ($CPD Partition Names) bpdt_dict = { 0 : 'SMIP', # OEM-SMIP Partition @@ -10788,7 +10794,7 @@ def mass_scan(f_path) : 44 : 'TCCP', # USB Type C Controller Partition (a.k.a. TPCC) 45 : 'PSEP', # Programmable Services Engine Partition } - + # CSE Extension 12 SKU Capabilities (ConfigRuleSettings) skuc_dict = { 0 : 'MNG_FULL', # Full Manageability @@ -10821,7 +10827,7 @@ def mass_scan(f_path) : 30 : 'MDNSPROXY', # Multicast DNS Proxy 31 : 'NFC', # Near Field Communication } - + # CSE PCH Platforms pch_dict = { 0x0 : 'LBG-H', # Lewisburg H @@ -10854,7 +10860,7 @@ def mass_scan(f_path) : 7 : 'OEM Configuration', 9 : 'Manifest Backup', } - + # FD Component Sizes comp_dict = { 0 : 0x80000, # 512 KB @@ -10938,7 +10944,7 @@ def mass_scan(f_path) : mea_hdr(mea_db_rev_p) print("\nWelcome to Intel Engine & Graphics Firmware Analysis Tool\n") - + if arg_num == 2 : print("Press Enter to skip or input -? to list options\n") print("\nFile: " + col_g + "%s" % os.path.basename(sys.argv[1]) + col_e) @@ -10950,29 +10956,29 @@ def mass_scan(f_path) : print("\nFile: " + col_m + "None" + col_e) input_var = input('\nOption(s): ') - + # Anything quoted ("") is taken as one (file paths etc) input_var = re.split(''' (?=(?:[^'"]|'[^']*'|"[^"]*")*$)''', input_var.strip()) - + # Get MEA Parameters based on given Options param = MEA_Param(input_var) - + # Non valid parameters are treated as files if input_var[0] != "" : for i in input_var: if i not in param.val : sys.argv.append(i.strip('"')) - + # Re-enumerate parameter input arg_num = len(sys.argv) - + os.system(cl_wipe) - + mea_hdr(mea_db_rev_p) - + else : mea_hdr(mea_db_rev_p) - + if (arg_num < 2 and not param.help_scr and not param.mass_scan) or param.help_scr : mea_help() @@ -10981,7 +10987,7 @@ def mass_scan(f_path) : source = mass_scan(in_path) else : source = sys.argv[1:] # Skip script/executable - + # Initialize file input file_in = '' cur_count = 0 @@ -11019,7 +11025,7 @@ def mass_scan(f_path) : pr_man_cpd_pats = {part: re.compile(cpd_pat.pattern + b'.' + part.encode(), re.DOTALL) for part in pr_cpd_parts} for file_in in source : - + # Variable Initialization nvm_db = '' fw_type = '' @@ -11238,63 +11244,63 @@ def mass_scan(f_path) : p_offset_min = 0xFFFFFFFF cse_lt_entry_min = 0xFFFFFFFF cur_count += 1 - + if not os.path.isfile(file_in) : if any(p in file_in for p in param.val) : continue # Next input file - + print(col_r + '\nError: File %s was not found!' % file_in + col_e) - + if not param.mass_scan : mea_exit(1) else : continue - + # Store input file buffer to RAM, will change if Flash Descriptor is detected with open(file_in, 'rb') as in_file : reading = in_file.read() file_end = len(reading) # Store the input file buffer Size/EOF reading_16 = reading[:0x10] # Store the first 16 input file buffer bytes - + # Detect & Skip AMI BIOS Guard (PFAT) protected images if reading[0x8:0x10] == b'_AMIPFAT' : msg_pt = ext_table([], False, 1) msg_pt.add_row([col_c + '%s (%d/%d)' % (os.path.basename(file_in)[:45], cur_count, in_count) + col_e]) - + print('\n%s\n\nDetected' % msg_pt + col_y + ' AMI BIOS Guard (PFAT) ' + col_e + 'protected image, prior extraction' ' required!\n\nUse "AMI BIOS Guard Extractor" from https://github.com/platomav/BIOSUtilities') - + copy_on_msg(['PFAT']) # Close input and copy it in case of messages - + continue # Next input file # Detect & Skip Intel (CS)SPS Capsule multi images if reading[:0x10] == b'\x34\x59\xEF\x99\x22\x78\xC4\x49\x83\xA4\x50\xC1\xAF\xBC\xBE\x00' : msg_pt = ext_table([], False, 1) msg_pt.add_row([col_c + '%s (%d/%d)' % (os.path.basename(file_in)[:45], cur_count, in_count) + col_e]) - + print('\n%s\n\nDetected' % msg_pt + col_y + ' Intel (CS)SPS Capsule ' + col_e + 'multi image, format is not supported!') - + continue # Next input file - + # Parse VFS & EFS File Table Blobs if param.mfs_ftbl : ftbl = get_struct(reading, 0, FTBL_Header) - + if ftbl.Signature == b'FTBL' : for i in range(ftbl.TableCount) : tbl = get_struct(reading, 0x10 + i * 0x10, FTBL_Table) - + tbl_data = reading[tbl.Offset:tbl.Offset + tbl.Size] - + ftbl_pt = ext_table(['Path','File ID','Integrity','Encryption','Anti-Replay','Access Unknown','User ID','Group ID','VFS ID','Unknown'], True, 1) ftbl_pt.title = 'FTBL Table ' + '%0.2X' % tbl.Dictionary - + for j in range(tbl.EntryCount) : entry_data = tbl_data[j * 0x44:j * 0x44 + 0x44] - + entry = get_struct(entry_data, 0, FTBL_Entry) - + # Remember to also adjust FTBL_Entry, mfs_home13_anl & efs_anl - + f1,f2,f3,f4,f5 = entry.get_flags() # Integrity, Encryption, Anti-Replay, Access Unknown, Unknown - + path = entry.Path.decode('utf-8').strip() # Local Path (strip extra spaces, e.g. INTC_defpdt) file_id = '0x%0.8X' % entry.FileID # File ID access_int = ['No','Yes'][f1] # Access > Integrity @@ -11305,56 +11311,56 @@ def mass_scan(f_path) : user_id = '0x%0.4X' % entry.UserID # User ID vfs_id = '%0.4d' % entry.VFSID # VFS ID (Low Level File) unknown = '{0:064b}b'.format(f5) # Unknown - + # Create File Table Entries Dictionary ftbl_entry_dict['%0.8X' % entry.FileID] = '%s,%d,%d,%d,%d,%d,%d,%d,%d' % (path,f1,f2,f3,f4,entry.GroudID,entry.UserID,entry.VFSID,f5) - + # Create File Table Entries Info ftbl_pt.add_row([path,file_id,access_int,access_enc,access_arp,access_unk,user_id,group_id,vfs_id,unknown]) - + ftbl_blob_dict['%0.2X' % tbl.Dictionary] = {} ftbl_blob_dict['%0.2X' % tbl.Dictionary]['FTBL'] = ftbl_entry_dict # Create File Table Blob Dictionary - + with open('%s_FTBL_%0.2X.txt' % (os.path.basename(file_in), tbl.Dictionary), 'w', encoding='utf-8') as to: to.write(str(ftbl_pt)) - + if param.write_html: with open('%s_FTBL_%0.2X.html' % (os.path.basename(file_in), tbl.Dictionary), 'w', encoding='utf-8') as ho: ho.write(pt_html(ftbl_pt)) - + if param.write_json: with open('%s_FTBL_%0.2X.json' % (os.path.basename(file_in), tbl.Dictionary), 'w', encoding='utf-8') as jo: json.dump(pt_json(ftbl_pt), jo, indent=4) - + if reading[ftbl.HeaderSize:ftbl.HeaderSize + 0x4] == b'EFST' : efst = get_struct(reading, ftbl.HeaderSize, EFST_Header) - + e_pt = ext_table(['Dictionary','Offset','File Count','Size','Unknown 0','Data Pages Committed', 'Data Pages Reserved','Max Files','Unknown 1','Table Revision'], True, 1) e_pt.title = 'EFST Tables' - + for i in range(efst.TableCount) : tbl = get_struct(reading, ftbl.HeaderSize + 0x10 + i * 0x28, EFST_Table) - + e_pt.add_row(['0x%0.2X' % tbl.Dictionary,'0x%X' % tbl.Offset,tbl.EntryCount,'0x%X' % tbl.Size,'0x%X' % tbl.Unknown0, tbl.DataPagesCom,tbl.DataPagesRes,tbl.MaxEntries,'0x%X' % tbl.Unknown1,tbl.Revision]) - + with open('%s_EFST.txt' % os.path.basename(file_in), 'w', encoding='utf-8') as o : o.write(str(e_pt)) - + tbl_data = reading[tbl.Offset:tbl.Offset + tbl.Size] - + efst_pt = ext_table(['VFS ID','Name','Page','Offset','Size','Reserved'], True, 1) efst_pt.title = 'EFST Table ' + '%0.2X' % tbl.Dictionary - + file_info = [] efst_entry_dict = {} for j in range(tbl.EntryCount) : entry_data = tbl_data[j * 0x3C:j * 0x3C + 0x3C] - + entry = get_struct(entry_data, 0, EFST_Entry) - + # Remember to also adjust EFST_Entry - + file_id = entry.FileID # File Count file_name = entry.FileName.decode('utf-8').strip() # File Name (strip extra spaces) if file_name.startswith('EFS_FILE_') : file_name = file_name[9:] @@ -11363,12 +11369,12 @@ def mass_scan(f_path) : file_length = entry.FileSize # File Size reserved = entry.Reserved # Reserved file_info.append((file_page,file_offset,file_length,file_id,reserved,file_name)) - + # Create EFS Table Entries Info efst_pt.add_row(['%0.4d' % file_id,file_name,file_page,'0x%X' % file_offset,'0x%X' % file_length,'0x%X' % reserved]) - + file_info.sort() # Sort EFS Entries/Files based on File Page & File Offset - + # Determine actual EFS Data Area Buffer Offset for each Entry/File. # EFS Data Area Buffer consists of all Data Pages w/o Header & Footer. # EFS Files within the Data Area Buffer are sequential so we can use @@ -11377,38 +11383,38 @@ def mass_scan(f_path) : for info_idx in range(len(file_info) - 1) : # Last File Size is not needed last_offset = dict_offset[info_idx] # Get previous File Offset dict_offset.append(last_offset + 0x4 + file_info[info_idx][2]) # Calculate current File Offset - + # Create EFS Table Entries/Files Dictionary for info_idx in range(len(file_info)) : efst_entry_dict['%0.8X' % dict_offset[info_idx]] = '%d,%d,%d,%d,%d,%s' % file_info[info_idx] - + # Create EFS Table Blob Dictionary if 'EFST' not in ftbl_blob_dict['%0.2X' % tbl.Dictionary] : ftbl_blob_dict['%0.2X' % tbl.Dictionary]['EFST'] = {} ftbl_blob_dict['%0.2X' % tbl.Dictionary]['EFST']['%0.2X' % tbl.Revision] = efst_entry_dict - + with open('%s_EFST_%0.2X.txt' % (os.path.basename(file_in), tbl.Dictionary), 'w', encoding='utf-8') as to: to.write(str(efst_pt)) - + if param.write_html: with open('%s_EFST_%0.2X.html' % (os.path.basename(file_in), tbl.Dictionary), 'w', encoding='utf-8') as ho: ho.write(pt_html(efst_pt)) - + if param.write_json: with open('%s_EFST_%0.2X.json' % (os.path.basename(file_in), tbl.Dictionary), 'w', encoding='utf-8') as jo: json.dump(pt_json(efst_pt), jo, indent=4) - + with open('%s_FileTable.dat' % os.path.basename(file_in), 'w', encoding='utf-8') as jo: json.dump(ftbl_blob_dict, jo, sort_keys=True, indent=4) - + continue # Next input file - + # Detect Intel Engine/Graphics/Independent firmware for man_range in list(man_pat.finditer(reading)) : start_man_match = man_range.start() + 0xB # 8680.{9} sanity check before .$MN2 or .$MAN end_man_match = man_range.end() - + reading_4K = reading[max(0x0, end_man_match - 0x4020):max(0x0, end_man_match - 0x20)] # Based on MN2_Manifest Max Size - + pr_man_01 = reading[end_man_match + 0x264:end_man_match + 0x266] # FT,OP (ME 6 - 10 Part 1, TXE 0 - 2 Part 1, SPS 2 - 3 Part 1) pr_man_02 = reading[end_man_match + 0x266:end_man_match + 0x268] # PR,xx (ME 6 - 10 Part 2, TXE 0 - 2 Part 2) pr_man_03 = reading[end_man_match + 0x28C:end_man_match + 0x293] # BRINGUP (ME 2 - 5) @@ -11418,11 +11424,11 @@ def mass_scan(f_path) : pr_man_08 = pr_man_08_pat.search(reading_4K) # FTPR.man (CSME 15.0.35 +) pr_man_09 = pr_man_09_pat.search(reading_4K) # OROM.man (GSC) pr_man_10 = pr_man_10_pat.search(reading_4K) # grtos.met (Ignore CSSPS IE) - + pr_man_cpd = {name: pr_man_cpd_pats[name].search(reading_16) for name in pr_cpd_parts} if param.bypass : break # Force MEA to accept any $MAN/$MN2 (Debug/Research) - + # Break if a valid Recovery Manifest is found if pr_man_01 + pr_man_02 == b'FTPR' \ or pr_man_01 + pr_man_05 + pr_man_06 == b'OP$MMEBUP\x00\x00\x00\x00' \ @@ -11434,33 +11440,33 @@ def mass_scan(f_path) : or any(pr_man_cpd.values()): if pr_man_09 : is_orom_img = True # GSC Option ROM Image (OROM) break - + else : # Recovery Manifest not found (for > finish) msg_pt = ext_table([], False, 1) msg_pt.add_row([col_c + '%s (%d/%d)' % (os.path.basename(file_in)[:45], cur_count, in_count) + col_e]) print('\n%s\n\nFile does not contain Intel Engine/Graphics/Independent Firmware' % msg_pt) - + continue # Next input file # Engine/Graphics/Independent firmware found (for > break), Manifest analysis - + # Detect Intel Flash Descriptor (FD) fd_exist,reading,file_end,start_man_match,end_man_match,start_fd_match,end_fd_match,fd_count,fd_comp_all_size,fd_is_ich,fd_is_cut,reading_msg = \ fd_anl_init(reading,file_end,start_man_match,end_man_match) - + # Store Initial Manifest Offset for CSSPS EXTR RSA Signatures Hash init_man_match = [start_man_match,end_man_match] - + # Analyze Intel Flash Descriptor Regions if fd_exist : fd_bios_rgn_exist,bios_fd_start,bios_fd_size,fd_me_rgn_exist,me_fd_start,me_fd_size,fd_pdr_rgn_exist,pdr_fd_start,pdr_fd_size, \ fd_devexp_rgn_exist,devexp_fd_start,devexp_fd_size = fd_anl_rgn(start_fd_match,end_fd_match,fd_is_ich) - + fd_data_all = reading[start_fd_match:start_fd_match + 0x1000] # Flash Descriptor Data fd_data_mn2 = fd_data_all[0x800:0xC00] # Flash Descriptor Manifest Data fd_is_rsa = bool(man_pat.search(fd_data_mn2[:0x20])) # Check if FD Manifest exists - + if fd_is_rsa : # Flash Descriptor is Hash protected ext_print,mn2_signs,fd_info = ext_anl(fd_data_mn2, '$MN2', 0x1B, file_end, ['CSME',0,0,0,0,0,0,'CSE ME'], 'FDV', [[],''], [[],-1,-1,-1]) fd_rsa_valid = mn2_signs[0] # FDV RSA Signature validity @@ -11469,24 +11475,24 @@ def mass_scan(f_path) : fd_hash_excl = fd_info[1] # FDV Extension 23 Exclusion Ranges fd_hash_data = bytearray(fd_data_all) # Flash Descriptor Hash Data ext_print[1][0].title = col_y + 'Flash Descriptor Manifest' + col_e # Adjust Manifest PLTable object's title - + # Check Flash Descriptor RSA Signature status if fd_rsa_crash : err_stor.append([col_r + 'Error: Could not validate Flash Descriptor RSA Signature!' + col_e, True]) elif not fd_rsa_valid : err_stor.append([col_r + 'Error: Invalid Flash Descriptor RSA Signature!' + col_e, True]) - + # Padd Excluded Ranges from Flash Descriptor Hash Data for excl_range in fd_hash_excl : range_size = excl_range[1] - excl_range[0] if range_size : fd_hash_data[excl_range[0]:excl_range[1]] = b'\xFF' * range_size - + fd_hash_mea = get_hash(fd_hash_data, len(fd_hash_int) // 2) # Calculate Flash Descriptor Hash value - + # Check Flash Descriptor Hash status if fd_hash_int != fd_hash_mea : err_stor.append([col_r + 'Error: Invalid Flash Descriptor Hash!' + col_e, True]) - + # Store Flash Descriptor RSA Signature & Hash status and Manifest/Extension info fdv_status = [fd_is_rsa, fd_rsa_valid, fd_rsa_crash, fd_hash_int == fd_hash_mea, ext_print] - + # Detect CSE Layout Table, it unfortunately lacks a unique identifier (GREAT WORK INTEL...) if fd_me_rgn_exist : cse_lt_off = me_fd_start # If Flash Descriptor exists, use Engine/Graphics region offset (robust) @@ -11496,7 +11502,7 @@ def mass_scan(f_path) : if cse_lt_pos_16 != -1 : cse_lt_off = cse_lt_pos_16 - 0x28 elif cse_lt_pos_17 : cse_lt_off = cse_lt_pos_17.start() - 0x10 else : cse_lt_off = 0x0 # Assume 0x0 on cse_lt_pos_16/cse_lt_pos_17 miss (risky) - + cse_lt_size = 0x1000 # CSE LT Size is usually 0x1000 (4KB) cse_lt_bp = [b'\xAA\x55\x00\x00',b'\xAA\x55\xAA\x00'] # IFWI BPDT Signatures cse_lt_16 = get_struct(reading, cse_lt_off, CSE_Layout_Table_16) # IFWI 1.6 Structure @@ -11507,7 +11513,7 @@ def mass_scan(f_path) : cse_lt_17_fpt_sig = reading[cse_lt_off + cse_lt_17.DataOffset:cse_lt_off + cse_lt_17.DataOffset + 0x4] # IFWI 1.7 FPT Signature cse_lt_16_bp1_sig = reading[cse_lt_off + cse_lt_16.BP1Offset:cse_lt_off + cse_lt_16.BP1Offset + 0x4] # IFWI 1.6 BP1 Signature cse_lt_17_bp1_sig = reading[cse_lt_off + cse_lt_17.BP1Offset:cse_lt_off + cse_lt_17.BP1Offset + 0x4] # IFWI 1.7 BP1 Signature - + # If $FPT exists, verify CSE LT via Data, BP1 & Padding. Otherwise, only via BP1 & Padding (risky) if reading[cse_lt_off:cse_lt_off + 0x4] in cse_lt_bp : pass # Skip any CSE LT Structure "matches" which are actually IFWI 2.0 BPx (BPDT) @@ -11523,15 +11529,15 @@ def mass_scan(f_path) : cse_lt_struct = cse_lt_16 # CSE LT IFWI 1.6 without Data elif cse_lt_17_bp1_sig in cse_lt_bp and cse_lt_17_hdr_pad == len(cse_lt_17_hdr_pad) * b'\xFF' : cse_lt_struct = cse_lt_17 # CSE LT IFWI 1.7 without Data - + # Analyze CSE Layout Table if cse_lt_struct : NA = [0,0xFFFFFFFF] - + cse_lt_hdr_info = [['Data',cse_lt_struct.DataOffset,cse_lt_struct.DataSize],['Boot 1',cse_lt_struct.BP1Offset,cse_lt_struct.BP1Size], ['Boot 2',cse_lt_struct.BP2Offset,cse_lt_struct.BP2Size],['Boot 3',cse_lt_struct.BP3Offset,cse_lt_struct.BP3Size], ['Boot 4',cse_lt_struct.BP4Offset,cse_lt_struct.BP4Size],['Boot 5',cse_lt_struct.BP5Offset,cse_lt_struct.BP5Size]] - + # Perform IFWI 1.7 specific CSE LT actions if cse_lt_struct == cse_lt_17 : # Validate IFWI 1.7 CSE LT CRC-32 @@ -11544,41 +11550,41 @@ def mass_scan(f_path) : if param.check : # Fix CSE LT CRC-32, when applicable (Debug/Research) with open('__FCCLT__' + os.path.basename(file_in), 'wb') as o : o.write(reading[:cse_lt_off + 0x14] + struct.pack('= 0x48 : cse_lt_hdr_info.append(['ELog',cse_lt_struct.ELogOffset,cse_lt_struct.ELogSize]) - + # Get IFWI 1.7 CSE LT Flags Info (CSE Redundancy, Reserved) cse_lt_flags_red, _ = cse_lt_struct.get_flags() - + # Calculate CSE LT Data Partition Total Size (w/o Boot, Temp & ELog) cse_lt_dp_size = cse_lt_struct.DataSize - + # Calculate CSE LT Boot, Temp & ELog Partitions Total Size (w/o Data) cse_lt_bp_size = sum(info[2] for info in cse_lt_hdr_info[1:]) - + # Store CSE LT partition details for entry in cse_lt_hdr_info : cse_lt_entry_name = entry[0] cse_lt_entry_off = entry[1] cse_lt_entry_size = entry[2] - + cse_lt_entry_spi = cse_lt_off + cse_lt_entry_off cse_lt_entry_end = cse_lt_entry_spi + cse_lt_entry_size cse_lt_entry_data = reading[cse_lt_entry_spi:cse_lt_entry_end] cse_lt_entry_empty = bool(cse_lt_entry_off in NA or cse_lt_entry_size in NA or cse_lt_entry_data in [b'\x00' * cse_lt_entry_size,b'\xFF' * cse_lt_entry_size]) if not cse_lt_entry_empty : cse_lt_entry_min = min(cse_lt_entry_off, cse_lt_entry_min) - + cse_lt_part_all.append([cse_lt_entry_name,cse_lt_entry_spi,cse_lt_entry_size,cse_lt_entry_end,cse_lt_entry_empty]) - + if cse_lt_entry_min > cse_lt_size : cse_lt_size = cse_lt_entry_min # Adjust CSE LT Size when 1st Partition does not start at 0x1000 - + pt_dcselt = ext_table([col_y + 'Name' + col_e, col_y + 'Start' + col_e, col_y + 'Size' + col_e, col_y + 'End' + col_e, col_y + 'Empty' + col_e], True, 1) pt_dcselt.title = col_y + 'CSE Region Layout Table' + col_e - + # Detect CSE LT partition overlaps for part in cse_lt_part_all : pt_dcselt.add_row([part[0],'0x%0.6X' % part[1],'0x%0.6X' % part[2],'0x%0.6X' % part[3],part[4]]) # For -dfpt @@ -11590,19 +11596,19 @@ def mass_scan(f_path) : and not ((part[0],all_part[0],reading[all_part[1]:all_part[1] + 0x4]) == ('Data','Boot 4',b'$FPT') and all_part[1] >= part[1] and all_part[3] <= part[3]) : err_stor.append([col_r + 'Error: CSE LT partition %s (0x%0.6X - 0x%0.6X) overlaps with %s (0x%0.6X - 0x%0.6X)' % \ (part[0],part[1],part[2],all_part[0],all_part[1],all_part[2]) + col_e, True]) - + # Show CSE LT partition info on demand (-dfpt) if param.fpt_disp : print('%s\n' % pt_dcselt) - + # Detect ROMB code within CSE LT (IFWI 1.6 & 1.7, must be after cse_lt_size adjustment) if b'Non-Intel Root Key' in reading[cse_lt_off:cse_lt_off + cse_lt_size] : note_stor.append([col_y + 'Note: CSE LT seems to include ROM-Bypass code!' + col_e, True]) - + # Detect all $FPT and/or BPDT starting offsets (both allowed/needed) if fd_me_rgn_exist : # $FPT detection based on FD with Engine/Graphics region (limits false positives from IE or CSTXE Engine/ROMB & DevExp1/Init) fpt_matches_init = list(fpt_pat.finditer(reading[me_fd_start:me_fd_start + me_fd_size])) - + # Detect ROMB code within FD > Engine/Graphics (IFWI 2.0, FD > DevExp & No CSE LT & CSE/UEFI in FD > BIOS & ROMB in FD > Engine) if fd_devexp_rgn_exist and not cse_lt_struct and fd_bios_rgn_exist and bpdt_pat.search(reading[bios_fd_start:bios_fd_start + 0x100]) \ and b'Non-Intel Root Key' in reading[me_fd_start:me_fd_start + me_fd_size] : @@ -11610,11 +11616,11 @@ def mass_scan(f_path) : else : # FD with Engine/Graphics region not found or multiple FD detected, scan entire file (could lead to false positives) fpt_matches_init = list(fpt_pat.finditer(reading)) - + # No Variant known yet but, if possible, get CSE Stage 1 Info for false positive removal via special ext_anl _Stage1 mode man_mod_names,fptemp_info = ext_anl(reading, '$MN2_Stage1', start_man_match, file_end, ['CSME',0,0,0,0,0,0,'CSE ME'], None, [[],''], [[],-1,-1,-1]) fptemp_exists = bool(man_mod_names and man_mod_names[0] == 'FTPR.man' and fptemp_info[0]) # Detect if CSE FTPR > fptemp module exists - + # Adjust $FPT matches, ignore known false positives for fpt_match in fpt_matches_init : fpt_match_start = me_fd_start + fpt_match.start() if fd_me_rgn_exist else fpt_match.start() @@ -11624,37 +11630,37 @@ def mass_scan(f_path) : elif cse_lt_struct and not cse_lt_part_all[5][4] and fpt_match_start == cse_lt_part_all[5][1] : pass # CSE Default Data > $FPT elif cse_lt_struct and not cse_lt_part_all[5][4] and fpt_match_start == cse_lt_part_all[5][1] + 0x1000 : pass # CSE Default Data Redundancy > $FPT else : fpt_matches.append(fpt_match) - + # Detect $FPT Firmware Starting Offset if len(fpt_matches) : rgn_exist = True # Set $FPT detection boolean - + fpt_count = len(fpt_matches) # Count $FPT matches - + # Set $FPT Start & End when no CSE LT Data was found if fpt_pat_end == 0 : (fpt_pat_bgn, fpt_pat_end) = fpt_matches[0].span() # Select the 1st $FPT match by default - + # Adjust $FPT offset if FD with Engine/Graphics region exists if fd_me_rgn_exist : fpt_pat_bgn += me_fd_start fpt_pat_end += me_fd_start - + # Analyze $FPT header pt_dfpt = ext_table([col_y + 'Name' + col_e, col_y + 'Owner' + col_e, col_y + 'Start' + col_e, col_y + 'Size' + col_e, col_y + 'End' + col_e, col_y + 'Type' + col_e, col_y + 'ID' + col_e, col_y + 'Valid' + col_e, col_y + 'Empty' + col_e], True, 1) pt_dfpt.title = col_y + 'Flash Partition Table' + col_e - + fpt_get = get_fpt(reading, fpt_pat_bgn) fpt_hdr = get_struct(reading, fpt_pat_bgn, fpt_get[0]) - + fpt_part_num = fpt_hdr.NumPartitions fpt_version = fpt_get[1] fpt_length = fpt_hdr.HeaderLength fpt_chk_int = fpt_hdr.HeaderChecksum - + fpt_start = fpt_pat_bgn if fpt_pat_bgn == 0 else fpt_pat_bgn - 0x10 - + if (cse_lt_struct or (fd_devexp_rgn_exist and reading[devexp_fd_start:devexp_fd_start + 0x4] == b'$FPT')) \ and fpt_version in [0x20,0x21] and fpt_length == 0x20 : fpt_start = fpt_pat_bgn @@ -11663,17 +11669,17 @@ def mass_scan(f_path) : fpt_start = fpt_pat_bgn elif fpt_version == 0x10 and fpt_length == 0x20 : fpt_start = fpt_pat_bgn - + fpt_step = fpt_pat_bgn + 0x20 # 0x20 $FPT entry size - + for i in range(0, fpt_part_num): cse_in_id = 0 cse_in_id_str = '0000' - + fpt_entry = get_struct(reading, fpt_step, FPT_Entry) - + p_type,_,_,_,_,_,p_valid = fpt_entry.get_flags() - + p_name = fpt_entry.Name p_owner = fpt_entry.Owner p_offset = fpt_entry.Offset @@ -11684,19 +11690,19 @@ def mass_scan(f_path) : p_type_print = p_type_dict[p_type] if p_type in p_type_dict else 'Unknown' is_cpd = reading[p_offset_spi:p_offset_spi + 0x4] == b'$CPD' cpd_name = reading[p_offset_spi + 0xC:p_offset_spi + 0x10].strip(b'\x00') - + if p_name in [b'\xFF\xFF\xFF\xFF', b''] : p_name = '' # If appears, wrong NumPartitions elif p_name == b'\xE0\x15' : p_name = 'E0150020' # ME8 (E0150020) elif is_cpd and p_name != b'FTUP' : p_name = cpd_name.decode('utf-8','ignore') else : p_name = p_name.decode('utf-8','ignore') - + p_empty = bool(p_offset in (0xFFFFFFFF, 0) or p_size == 0 or p_size != 0xFFFFFFFF and reading[p_offset_spi:p_offset_spi + p_size] in (b'', p_size * b'\xFF')) - + if not p_empty and p_offset_spi < file_end : # Get CSE Partition Instance ID cse_in_id,_,_ = cse_part_inid(reading, p_offset_spi, ext_dict) cse_in_id_str = '%0.4X' % cse_in_id - + # Get ME LOCL/WCOD Partition Instance ID mn2_hdr = get_struct(reading, p_offset_spi, get_manifest(reading, p_offset_spi)) if mn2_hdr.Tag in [b'$MN2',b'$MAN'] : # Sanity check @@ -11705,84 +11711,84 @@ def mass_scan(f_path) : if mod_name in ['LOCL','WCOD'] : cse_in_id = reading[p_offset_spi + mn2_len + 0x15:p_offset_spi + mn2_len + 0x15 + 0xB].strip(b'\x00').decode('utf-8') cse_in_id_str = cse_in_id - + fpt_part_all.append([p_name, p_offset_spi, p_offset_spi + p_size, cse_in_id, p_type_print, p_valid_print, p_empty]) - + # Store $FPT Partition info for -dfpt if param.fpt_disp : if p_owner in [b'\xFF\xFF\xFF\xFF', b''] : p_owner = '' # Missing else : p_owner = p_owner.decode('utf-8','ignore') - + if p_offset in [0xFFFFFFFF, 0] : p_offset_print = '' else : p_offset_print = '0x%0.6X' % p_offset_spi - + if p_size in [0xFFFFFFFF, 0] : p_size_print = '' else : p_size_print = '0x%0.6X' % p_size - + if p_offset_print == '' or p_size_print == '' : p_end_print = '' else : p_end_print = '0x%0.6X' % (p_offset_spi + p_size) - + pt_dfpt.add_row([p_name,p_owner,p_offset_print,p_size_print,p_end_print,p_type_print,cse_in_id_str,p_valid_print,p_empty]) - + p_store_all.append([p_name, p_offset_spi, p_size]) # For $FPT Recovery/Operational adjustment - + # Detect if firmware has ROM-Bypass (ROMB) partition if p_name == 'ROMB' and not p_empty : fpt_romb_found = True - + # Detect if firmware has (CS)SPS Operational (OPRx/COD1) partition if p_name.startswith(('OPR','COD1')) and not p_empty : sps_opr_found = True - + # Detect if firmware has ROM Boot Extensions (RBEP) partition if p_name == 'RBEP' and not p_empty : rbep_found = True rbep_start = p_offset_spi - + # Detect if firmware has CSE MFS File System Partition if p_name in ('MFS','AFSP') and not p_empty : mfs_found = True mfs_start = p_offset_spi mfs_size = p_size mfs_is_afs = p_name == 'AFSP' - + # Detect if firmware has CSE MFSB File System Backup Partition if p_name == 'MFSB' and not p_empty : mfsb_found = True mfsb_start = p_offset_spi mfsb_size = p_size - + # Detect if firmware has CSE EFS File System Partition if p_name == 'EFS' and not p_empty : efs_found = True efs_start = p_offset_spi efs_size = p_size - + # Detect if firmware has FITC File System Configuration Partition if p_name == 'FITC' : if not p_empty : fitc_found = True fitc_size = p_size - + # Detect if firmware has CDMD Partition if p_name == 'CDMD' : if not p_empty : cdmd_found = True cdmd_size = p_size - + # Detect if firmware has valid OEM Unlock/Security Token (UTOK/STKN) if p_name in ['UTOK','STKN'] and not p_empty and p_offset_spi < file_end and reading[p_offset_spi:p_offset_spi + 0x10] != b'\xFF' * 0x10 : utok_found = True - + # Detect if CSE firmware has valid and non-placeholder (VEN_ID BCCB) OEM Key Manager Partition (OEMP) if p_name == 'OEMP' and not p_empty and p_offset_spi < file_end and reading[p_offset_spi:p_offset_spi + 0x10] != b'\xFF' * 0x10 \ and not bccb_pat.search(reading[p_offset_spi:p_offset_spi + 0x50]) : oemp_found = True - + if 0 < p_offset_spi < p_max_size and 0 < p_size < p_max_size : eng_fw_end = p_offset_spi + p_size else : eng_fw_end = p_max_size - + # Store last partition (max offset) if p_offset_last < p_offset_spi < p_max_size: p_offset_last = p_offset_spi p_end_last = eng_fw_end - + fpt_step += 0x20 # Next $FPT entry - + # Adjust Manifest to Recovery (ME/TXE) or Operational (SPS) partition based on $FPT if fpt_count <= 2 : # This does not work with Intel Engine Capsule images because they have multiple $FPT and Engine CODE @@ -11797,12 +11803,12 @@ def mass_scan(f_path) : # Only if partition exists at file (counter-example: sole $FPT etc) if p_rec_fix[1] + p_rec_fix[2] <= file_end : rec_man_match = man_pat.search(reading[p_rec_fix[1]:p_rec_fix[1] + p_rec_fix[2]]) - + if rec_man_match : # Store the old/initial SPS Manifest (i.e. Recovery instead of Operational) for RSA Signature validation old_mn2_off = start_man_match - 0x1B old_mn2_hdr = get_struct(reading, old_mn2_off, get_manifest(reading, old_mn2_off)) - + # Adjust new/correct Manifest match range (start, end) (start_man_match, end_man_match) = rec_man_match.span() start_man_match += p_rec_fix[1] + 0xB # Add Recovery/Operational offset and 8680.{9} sanity check before .$MN2 or .$MAN @@ -11811,7 +11817,7 @@ def mass_scan(f_path) : # More than two $FPT detected, probably Intel Engine Capsule image mfs_found = False mfsb_found = False - + # Check CSE Redundancy (Boot & Data) if cse_lt_struct and cse_lt_flags_red : cse_red_info[0] = True # CSE Redundancy is enabled @@ -11819,18 +11825,18 @@ def mass_scan(f_path) : boot1_part_data = reading[cse_lt_part_all[1][1]:cse_lt_part_all[1][3]] boot2_part_data = reading[cse_lt_part_all[2][1]:cse_lt_part_all[2][3]] data_fpt_size = p_offset_min - 0x1000 if p_offset_min > 0x1000 else p_offset_min - + # Boot Partition 2 must be a copy of Boot Partition 1 if boot1_part_data != boot2_part_data : cse_red_info[1] = False # Boot Partition CSE Redundancy check failed err_stor.append([col_r + 'Error: CSE Redundancy check failed, Boot 1 != Boot 2!' + col_e, True]) - + # Data Partition, when present, must have a copy of its $FPT at 0x1000 # The backup $FPT may not be 0x1000, compare data up until 1st partition if not cse_lt_part_all[0][4] and data_part_data[:data_fpt_size] != data_part_data[0x1000:0x1000 + data_fpt_size] : cse_red_info[2] = False # Data Partition $FPT Redundancy check failed err_stor.append([col_r + 'Error: CSE Redundancy check failed, Data $FPT != Data $FPT Backup!' + col_e, True]) - + # Scan for IFWI/BPDT Ranges if cse_lt_struct : # Search Boot Partitions only when CSE LT exists (fast & robust) @@ -11845,277 +11851,277 @@ def mass_scan(f_path) : if mfs_found and mfs_start <= match.start() < mfs_start + mfs_size : continue # Skip BPDT within MFS (e.g. 008 > fwupdate> fwubpdtinfo) if mfsb_found and mfsb_start <= match.start() < mfsb_start + mfsb_size : continue # Skip BPDT within MFSB (e.g. 008 > fwupdate> fwubpdtinfo) bpdt_matches.append(match.span()) # Store all BPDT ranges, already relative to 0x0 - + # Parse IFWI/BPDT Ranges for ifwi_bpdt in range(len(bpdt_matches)): - + ifwi_exist = True # Set IFWI/BPDT detection boolean - + bpdt_pat_bgn = bpdt_matches[ifwi_bpdt][0] # Get BPDT Start Offset via bpdt_matches index - + if bpdt_pat_bgn in s_bpdt_all : continue # Skip already parsed S-BPDT (Type 5) - + bpdt_hdr = get_struct(reading, bpdt_pat_bgn, get_bpdt(reading, bpdt_pat_bgn)) - + # Store Primary BPDT info to show at CSE unpacking if param.cse_unpack : bpdt_hdr_all.append(bpdt_hdr.hdr_print()) bpdt_data_all.append(reading[bpdt_pat_bgn:bpdt_pat_bgn + 0x200]) # Min size 0x200 (no size at Header, min is enough though) - + # Analyze BPDT header bpdt_step = bpdt_pat_bgn + 0x18 # 0x18 BPDT Header size bpdt_part_num = bpdt_hdr.DescCount - + pt_dbpdt = ext_table([col_y + 'Name' + col_e, col_y + 'Type' + col_e, col_y + 'Partition' + col_e, col_y + 'Start' + col_e, col_y + 'Size' + col_e, col_y + 'End' + col_e, col_y + 'ID' + col_e, col_y + 'Empty' + col_e], True, 1) pt_dbpdt.title = col_y + 'Boot Partition Descriptor Table' + col_e - + for i in range(0, bpdt_part_num): cse_in_id = 0 - + bpdt_entry = get_struct(reading, bpdt_step, BPDT_Entry) - + p_type = bpdt_entry.Type p_offset = bpdt_entry.Offset p_offset_spi = bpdt_pat_bgn + p_offset p_size = bpdt_entry.Size is_cpd = reading[p_offset_spi:p_offset_spi + 0x4] == b'$CPD' cpd_name = reading[p_offset_spi + 0xC:p_offset_spi + 0x10].strip(b'\x00').decode('utf-8','ignore') - + p_empty = bool(p_offset in (0xFFFFFFFF, 0) or p_size in (0xFFFFFFFF, 0) or reading[p_offset_spi:p_offset_spi + p_size] in (b'', p_size * b'\xFF')) - + if is_cpd : p_name = cpd_name elif p_type in bpdt_dict : p_name = bpdt_dict[p_type] else : p_name = 'Unknown' - + if not p_empty and p_offset_spi < file_end : # Get CSE Partition Instance ID cse_in_id,_,_ = cse_part_inid(reading, p_offset_spi, ext_dict) - + # Store BPDT Partition info for -dfpt if param.fpt_disp : if p_offset in [0xFFFFFFFF, 0] : p_offset_print = '' else : p_offset_print = '0x%0.6X' % p_offset_spi - + if p_size in [0xFFFFFFFF, 0] : p_size_print = '' else : p_size_print = '0x%0.6X' % p_size - + if p_offset_print == '' or p_size_print == '' : p_end_print = '' else : p_end_print = '0x%0.6X' % (p_offset_spi + p_size) - + pt_dbpdt.add_row([p_name,'%0.2d' % p_type,'Primary',p_offset_print,p_size_print,p_end_print,'%0.4X' % cse_in_id,p_empty]) - + # Detect if IFWI Primary includes ROM Boot Extensions (RBEP) partition if p_name == 'RBEP' and not p_empty : rbep_found = True rbep_start = p_offset_spi - + # Detect if IFWI Primary includes Power Management Controller (PMCP/PCOD) partition if p_name in ('PMCP','PCOD') and not p_empty : pmcp_fwu_found = False pmcp_size = p_size - + _,_,_,pmc_vcn,_,_,_,_,_,_,_,_,pmc_mn2_ver,_,pmc_ext15_info,_,_,_,_ = \ ext_anl(reading, '$CPD', p_offset_spi, file_end, ['PMC',-1,-1,-1,-1,-1,-1,'PMC'], None, [[],''], [[],-1,-1,-1]) - + pmc_all_init.append([pmc_vcn,pmc_mn2_ver,pmc_ext15_info,pmcp_size]) - + # Detect if IFWI Primary includes Platform Controller Hub Configuration (PCHC) partition if p_name == 'PCHC' and not p_empty : pchc_fwu_found = False pchc_size = p_size - + _,_,_,pchc_vcn,_,_,_,_,_,_,_,_,pchc_mn2_ver,_,pchc_ext15_info,_,_,_,_ = \ ext_anl(reading, '$CPD', p_offset_spi, file_end, ['PCHC',-1,-1,-1,-1,-1,-1,'PCHC'], None, [[],''], [[],-1,-1,-1]) - + pchc_all_init.append([pchc_vcn,pchc_mn2_ver,pchc_ext15_info,pchc_size]) - + # Detect if IFWI Primary includes USB Type C Physical (PHY) partition if p_name in ('PPHY','NPHY','SPHY','PHYP') and not p_empty : phy_fwu_found = False phy_size = p_size - + _,_,_,phy_vcn,_,_,_,_,_,_,_,_,phy_mn2_ver,_,phy_ext15_info,_,_,_,_ = \ ext_anl(reading, '$CPD', p_offset_spi, file_end, ['PHY',-1,-1,-1,-1,-1,-1,'PHY'], None, [[],''], [[],-1,-1,-1]) - + phy_all_init.append([phy_vcn,phy_mn2_ver,phy_ext15_info,phy_size]) - + # Detect if IFWI Primary includes CSE MFS File System partition (Not POR, just in case) if p_name in ('MFS','AFSP') and not p_empty : mfs_found = True mfs_start = p_offset_spi mfs_size = p_size mfs_is_afs = p_name == 'AFSP' - + # Detect if IFWI Primary includes CSE File System Backup partition (Not POR, just in case) if p_name == 'MFSB' and not p_empty : mfsb_found = True mfsb_start = p_offset_spi mfsb_size = p_size - + # Detect if IFWI Primary includes CSE EFS File System partition (Not POR, just in case) if p_name == 'EFS' and not p_empty : efs_found = True efs_start = p_offset_spi efs_size = p_size - + # Detect if IFWI Primary includes FITC File System Configuration partition (Not POR, just in case) if p_name == 'FITC' : if not p_empty : fitc_found = True fitc_size = p_size - + # Detect if IFWI Primary includes CDMD partition (Not POR, just in case) if p_name == 'CDMD' : if not p_empty : cdmd_found = True cdmd_size = p_size - + if p_type == 5 and not p_empty and p_offset_spi < file_end and reading[p_offset_spi:p_offset_spi + 0x2] == b'\xAA\x55' : # Secondary BPDT (S-BPDT) s_bpdt_hdr = get_struct(reading, p_offset_spi, get_bpdt(reading, p_offset_spi)) - + # Store Secondary BPDT info to show at CSE unpacking if param.cse_unpack : bpdt_hdr_all.append(s_bpdt_hdr.hdr_print()) bpdt_data_all.append(reading[bpdt_pat_bgn:bpdt_pat_bgn + 0x200]) # Min size 0x200 (no size at Header, min is enough though) - + s_bpdt_all.append(p_offset_spi) # Store parsed S-BPDT offset to skip at IFWI/BPDT Starting Offsets - + s_bpdt_step = p_offset_spi + 0x18 # 0x18 S-BPDT Header size s_bpdt_part_num = s_bpdt_hdr.DescCount - + for j in range(0, s_bpdt_part_num): cse_in_id = 0 - + s_bpdt_entry = get_struct(reading, s_bpdt_step, BPDT_Entry) - + s_p_type = s_bpdt_entry.Type s_p_offset = s_bpdt_entry.Offset s_p_offset_spi = bpdt_pat_bgn + s_p_offset s_p_size = s_bpdt_entry.Size s_is_cpd = reading[s_p_offset_spi:s_p_offset_spi + 0x4] == b'$CPD' s_cpd_name = reading[s_p_offset_spi + 0xC:s_p_offset_spi + 0x10].strip(b'\x00').decode('utf-8','ignore') - + s_p_empty = bool(s_p_offset in (0xFFFFFFFF, 0) or s_p_size in (0xFFFFFFFF, 0) or reading[s_p_offset_spi:s_p_offset_spi + s_p_size] in (b'', s_p_size * b'\xFF')) - + if s_is_cpd : s_p_name = s_cpd_name elif s_p_type in bpdt_dict : s_p_name = bpdt_dict[s_p_type] else : s_p_name = 'Unknown' - + if not s_p_empty and s_p_offset_spi < file_end : cse_in_id,_,_ = cse_part_inid(reading, s_p_offset_spi, ext_dict) - + # Store BPDT Partition info for -dfpt if param.fpt_disp : if s_p_offset in [0xFFFFFFFF, 0] : s_p_offset_print = '' else : s_p_offset_print = '0x%0.6X' % s_p_offset_spi - + if s_p_size in [0xFFFFFFFF, 0] : s_p_size_print = '' else : s_p_size_print = '0x%0.6X' % s_p_size - + if s_p_offset_print == '' or s_p_size_print == '' : s_p_end_print = '' else : s_p_end_print = '0x%0.6X' % (s_p_offset_spi + s_p_size) - + pt_dbpdt.add_row([s_p_name,'%0.2d' % s_p_type,'Secondary',s_p_offset_print,s_p_size_print,s_p_end_print,'%0.4X' % cse_in_id,s_p_empty]) - + # Detect if IFWI Secondary includes ROM Boot Extensions (RBEP) partition if s_p_name == 'RBEP' and not s_p_empty : rbep_found = True rbep_start = s_p_offset_spi - + # Detect if IFWI Secondary includes Power Management Controller (PMCP/PCOD) partition if s_p_name in ('PMCP','PCOD') and not s_p_empty : pmcp_fwu_found = False pmcp_size = s_p_size - + _,_,_,pmc_vcn,_,_,_,_,_,_,_,_,pmc_mn2_ver,_,pmc_ext15_info,_,_,_,_ = \ ext_anl(reading, '$CPD', s_p_offset_spi, file_end, ['PMC',-1,-1,-1,-1,-1,-1,'PMC'], None, [[],''], [[],-1,-1,-1]) - + pmc_all_init.append([pmc_vcn,pmc_mn2_ver,pmc_ext15_info,pmcp_size]) - + # Detect if IFWI Secondary includes Platform Controller Hub Configuration (PCHC) partition if s_p_name == 'PCHC' and not s_p_empty : pchc_fwu_found = False pchc_size = s_p_size - + _,_,_,pchc_vcn,_,_,_,_,_,_,_,_,pchc_mn2_ver,_,pchc_ext15_info,_,_,_,_ = \ ext_anl(reading, '$CPD', s_p_offset_spi, file_end, ['PCHC',-1,-1,-1,-1,-1,-1,'PCHC'], None, [[],''], [[],-1,-1,-1]) - + pchc_all_init.append([pchc_vcn,pchc_mn2_ver,pchc_ext15_info,pchc_size]) - + # Detect if IFWI Secondary includes USB Type C Physical firmware (PHY) partition if s_p_name in ('PPHY','NPHY','SPHY','PHYP') and not s_p_empty : phy_fwu_found = False phy_size = s_p_size - + _,_,_,phy_vcn,_,_,_,_,_,_,_,_,phy_mn2_ver,_,phy_ext15_info,_,_,_,_ = \ ext_anl(reading, '$CPD', s_p_offset_spi, file_end, ['PHY',-1,-1,-1,-1,-1,-1,'PHY'], None, [[],''], [[],-1,-1,-1]) - + phy_all_init.append([phy_vcn,phy_mn2_ver,phy_ext15_info,phy_size]) - + # Detect if IFWI Secondary includes CSE MFS File System partition (Not POR, just in case) if s_p_name in ('MFS','AFSP') and not s_p_empty : mfs_found = True mfs_start = s_p_offset_spi mfs_size = s_p_size mfs_is_afs = s_p_name == 'AFSP' - + # Detect if IFWI Secondary includes CSE File System Backup partition (Not POR, just in case) if s_p_name == 'MFSB' and not s_p_empty : mfsb_found = True mfsb_start = s_p_offset_spi mfsb_size = s_p_size - + # Detect if IFWI Secondary includes CSE EFS File System partition (Not POR, just in case) if s_p_name == 'EFS' and not s_p_empty : efs_found = True efs_start = s_p_offset_spi efs_size = s_p_size - + # Detect if IFWI Secondary includes FITC File System Configuration partition (Not POR, just in case) if s_p_name == 'FITC' : if not s_p_empty : fitc_found = True fitc_size = s_p_size - + # Detect if IFWI Secondary includes CDMD partition (Not POR, just in case) if s_p_name == 'CDMD' : if not s_p_empty : cdmd_found = True cdmd_size = s_p_size - + # Store all Secondary BPDT entries for extraction bpdt_part_all.append([s_p_name,s_p_offset_spi,s_p_offset_spi + s_p_size,s_p_type,s_p_empty,'Secondary',cse_in_id]) - + s_bpdt_step += 0xC # 0xC BPDT Entry size - + # Store all Primary BPDT entries for extraction bpdt_part_all.append([p_name,p_offset_spi,p_offset_spi + p_size,p_type,p_empty,'Primary',cse_in_id]) - + bpdt_step += 0xC # 0xC BPDT Entry size - + # Show BPDT Partition info on demand (-dfpt) if param.fpt_disp : print('%s\n' % pt_dbpdt) - + # Perform actions on total stored BPDT entries for part in bpdt_part_all : # Detect if IFWI includes CSSPS Operational (OPRx) partition if part[3] == 2 and not part[4] and reading[part[1] + 0xC:part[1] + 0xF] == b'OPR' : sps_opr_found = True - + # Adjust Manifest to Recovery (CSME/CSTXE) or Operational (CSSPS) partition based on BPDT if part[3] == 2 and not part[4] and part[1] < file_end : # Type = CSE_BUP, non-Empty, Start < EOF # Only if partition exists at file (counter-example: sole IFWI etc) if part[1] + (part[2] - part[1]) <= file_end : rec_man_match = man_pat.search(reading[part[1]:part[1] + (part[2] - part[1])]) - + if rec_man_match and part[0] not in ['MFTP'] : (start_man_match, end_man_match) = rec_man_match.span() start_man_match += part[1] + 0xB # Add CSE_BUP offset and 8680.{9} sanity check before .$MN2 end_man_match += part[1] - + # Detect if CSE firmware has valid OEM Unlock/Security Token (UTOK/STKN) if part[0] in ['UTOK','STKN'] and not part[4] and part[1] < file_end and reading[part[1]:part[1] + 0x10] != b'\xFF' * 0x10 : utok_found = True - + # Detect if CSE firmware has valid and non-placeholder (VEN_ID BCCB) OEM Key Manager Partition (OEMP) if part[0] == 'OEMP' and not part[4] and part[1] < file_end and reading[part[1]:part[1] + 0x10] != b'\xFF' * 0x10 \ and not bccb_pat.search(reading[part[1]:part[1] + 0x50]) : oemp_found = True - + # Detect BPDT partition overlaps for all_part in bpdt_part_all : # Partition A starts before B but ends after B start @@ -12125,20 +12131,20 @@ def mass_scan(f_path) : and part[0] not in ['S-BPDT','DLMP'] and all_part[0] not in ['S-BPDT','DLMP'] and (part[1] < all_part[1] < part[2]) : err_stor.append([col_r + 'Error: BPDT partition %s (0x%0.6X - 0x%0.6X) overlaps with %s (0x%0.6X - 0x%0.6X)' % \ (part[0],part[1],part[2],all_part[0],all_part[1],all_part[2]) + col_e, True]) - + # Ignore Flash Descriptor OEM backup at BPDT > OBBP > NvCommon (HP) if part[0] == 'OBBP' and not part[4] and fd_pat.search(reading[part[1]:part[2]]) : fd_count -= 1 - + # Parse OROM/PCIR Images, only if GSC OROM firmware is detected orom_match = list(orom_pat.finditer(reading)) if is_orom_img else [] # OROM/PCIR detection for match in orom_match : orom_pat_bgn = match.start() # Get OROM start offset - + orom_struct_size = ctypes.sizeof(GSC_OROM_Header) # Get OROM Header Structure Size orom_hdr_data = reading[orom_pat_bgn:orom_pat_bgn + orom_struct_size] # Store OROM Header Structure Contents orom_hdr = get_struct(orom_hdr_data, 0, GSC_OROM_Header) # Get OROM Header Structure orom_hdr_p = orom_hdr.gsc_print() # Get OROM Header Structure Info - + pcir_off = orom_pat_bgn + orom_hdr.PCIDataHdrOff # Get PCIR offset via OROM Header pcir_struct_size = ctypes.sizeof(GSC_OROM_PCI_Data) # Get MEA OROM PCI Data Structure Size pcir_hdr_size = int.from_bytes(reading[pcir_off + 0xA:pcir_off + 0xC], 'little') # Get Image OROM PCI Data Size @@ -12146,25 +12152,25 @@ def mass_scan(f_path) : if pcir_hdr_size < pcir_struct_size : pcir_hdr_data += b'\x00' * (pcir_struct_size - pcir_hdr_size) # Adjust shorter PCIR pcir_hdr = get_struct(pcir_hdr_data, 0, GSC_OROM_PCI_Data) # Get OROM PCI Data Structure pcir_hdr_p = pcir_hdr.gsc_print() # Get OROM PCI Data Structure Info - + orom_pci_size += orom_hdr.ImageSize * 512 # Calculate OROM IUP Size by appending all OROM/PCIR Image Sizes - + # Calculate OROM IUP Size by appending all $CPD Partition and OROM/PCIR Structure Sizes data_off = max(orom_hdr.PCIDataHdrOff + pcir_hdr.PCIDataHdrLen, orom_hdr.EFIImageOffset, orom_hdr.OROMPayloadOff) # Data Offset if reading[orom_pat_bgn + data_off:orom_pat_bgn + data_off + 0x4] == b'$CPD' : # Check if Data is actually an OROM $CPD Partition orom_cpd_size += orom_pat_bgn - orom_match[0].start() - orom_cpd_size # Append Size between current & previous OROM $CPD Partitions orom_cpd_size += data_off # Append Size between current OROM/PCIR Headers & Data Payload (OROM $CPD Partition) orom_cpd_size += cpd_size_calc(reading[data_off + orom_pat_bgn:], 0, 0x1000) # Append Size of current OROM $CPD Partition - + # Show OROM/PCIR Image info on demand (-dfpt) if param.fpt_disp : print('%s\n%s\n' % (orom_hdr_p, pcir_hdr_p)) - + # Store OROM/PCIR Image info to show at OROM unpacking if param.cse_unpack : orom_hdr_all.extend([orom_hdr_p, pcir_hdr_p]) - + # Scan $MAN/$MN2 Manifest, for basic info only mn2_ftpr_hdr = get_struct(reading, start_man_match - 0x1B, get_manifest(reading, start_man_match - 0x1B)) - + major = mn2_ftpr_hdr.Major minor = mn2_ftpr_hdr.Minor hotfix = mn2_ftpr_hdr.Hotfix @@ -12175,7 +12181,7 @@ def mass_scan(f_path) : month = mn2_ftpr_hdr.Month year = mn2_ftpr_hdr.Year date = '%0.4X-%0.2X-%0.2X' % (year, month, day) - + # Get & Hash the Manifest RSA Public Key and Signature rsa_block_off = end_man_match + 0x60 # RSA Block Offset rsa_key_len = mn2_ftpr_hdr.PublicKeySize * 4 # RSA Key/Signature Length @@ -12184,11 +12190,11 @@ def mass_scan(f_path) : rsa_key_hash = get_hash(rsa_key, 0x20) # SHA-256 of RSA Public Key rsa_sig = reading[rsa_block_off + rsa_key_len + rsa_exp_len:rsa_block_off + rsa_key_len * 2 + rsa_exp_len] # RSA Signature rsa_sig_hash = get_hash(rsa_sig, 0x20) # SHA-256 of RSA Signature - + # Detect Variant/Family variant, variant_p, variant_p_fw, var_rsa_db = get_variant(reading, mn2_ftpr_hdr, start_man_match, end_man_match, rsa_key_hash, [year, month, day], [major, minor, hotfix, build]) - + # Get the Proper + Initial Manifest Info for (CS)SPS EXTR (FTPR + OPR1) if variant in ('SPS','CSSPS') and sps_opr_found : # Hash the merger of the FTPR & OPR RSA Signatures at EXTR @@ -12196,94 +12202,94 @@ def mass_scan(f_path) : rsa_sig_i = reading[rsa_block_off_i + rsa_key_len + rsa_exp_len:rsa_block_off_i + rsa_key_len * 2 + rsa_exp_len] # Initial (FTPR) RSA Signature rsa_sig_i_hash = get_hash(rsa_sig_i, 0x20) # SHA-256 of Initial (FTPR) RSA Signature rsa_sig_s = rsa_sig_i + rsa_sig if rsa_sig != rsa_sig_i else rsa_sig # Proper (OPR1) + Initial (FTPR) RSA Signatures - + # Ignore the EXTR at FTPR & OPR Firmware Version mismatch (must be before new/merged EXTR rsa_sig_hash) sps_ver_rec = reading[init_man_match[1] + 0x4:init_man_match[1] + 0xC] # Initial/Recovery (FTPR) Firmware Version sps_ver_opr = reading[end_man_match + 0x4:end_man_match + 0xC] # Proper/Operational (OPR1) Firmware Version if (sps_ver_rec != sps_ver_opr) and rsa_sig_i_hash in mea_db_read and rsa_sig_hash in mea_db_read : sps_extr_ignore = True - + rsa_sig_hash = get_hash(rsa_sig_s, 0x20) # SHA-256 of Proper (OPR1) + Initial (FTPR) RSA Signatures - + # Detect & Scan $MAN/$MN2 Manifest via Variant, for accurate info mn2_ftpr_hdr = get_struct(reading, start_man_match - 0x1B, get_manifest(reading, start_man_match - 0x1B)) - + # Detect $MN2 Manifest Manifest Extension Utility version, if applicable if hasattr(mn2_ftpr_hdr, 'MEU_Major') and mn2_ftpr_hdr.MEU_Major not in (0,0xFFFF) : # noinspection PyStringFormat mn2_meu_ver = '%d.%d.%d.%0.4d' % (mn2_ftpr_hdr.MEU_Major,mn2_ftpr_hdr.MEU_Minor,mn2_ftpr_hdr.MEU_Hotfix,mn2_ftpr_hdr.MEU_Build) - + # Detect RSA Public Key Recognition if not var_rsa_db : err_stor.append([col_r + 'Error: Unknown %s %d.%d RSA Public Key!' % (variant, major, minor) + col_e, True]) - + # Detect (CS)SPS Old/Initial RSA Signature Validity if variant.endswith('SPS') and old_mn2_hdr : old_man_valid = rsa_sig_val(old_mn2_hdr, reading, old_mn2_off) if not old_man_valid[0] : rsa_check = bool([old_man_valid[1],old_man_valid[2]] not in cse_known_bad_hashes) # Ignore known bad RSA Signatures err_stor.append([col_r + 'Error: Invalid %s %d.%d RSA Signature (Recovery)!' % (variant, major, minor) + col_e, rsa_check]) - + # Detect RSA Signature Validity man_valid = rsa_sig_val(mn2_ftpr_hdr, reading, start_man_match - 0x1B) if not man_valid[0] : rsa_check = bool([man_valid[1],man_valid[2]] not in cse_known_bad_hashes) # Ignore known bad RSA Signatures err_stor.append([col_r + 'Error: Invalid %s %d.%d RSA Signature!' % (variant, major, minor) + col_e, rsa_check]) - + if rgn_exist : - + # Multiple Backup $FPT header bypass at SPS1/SPS4 (DFLT/FPTB) if variant == 'CSSPS' or (variant,major) == ('SPS',1) and fpt_count % 2 == 0 : fpt_count /= 2 - + # Last/Uncharted partition scanning inspired by Lordkag's UEFIStrip # ME2-ME6 don't have size for last partition, scan its submodules if p_end_last == p_max_size : mn2_hdr = get_struct(reading, p_offset_last, get_manifest(reading, p_offset_last)) mod_start = p_offset_last + mn2_hdr.HeaderLength * 4 + 0xC - + # ME 6 if mn2_hdr.Tag == b'$MN2' : for _ in range(mn2_hdr.NumModules) : mme_mod = get_struct(reading, mod_start, MME_Header_New) - + if mme_mod.Tag != b'$MME' : break # Sanity check - + mod_size = mme_mod.SizeComp if mme_mod.SizeComp else mme_mod.SizeUncomp - + mod_end = p_offset_last + mme_mod.Offset_MN2 + mod_size - + if mod_end > mod_end_max : mod_end_max = mod_end # In case modules are not offset sorted - + mod_start += 0x60 - + # ME 2-5 elif mn2_hdr.Tag == b'$MAN' : mod_size_all = 0 - + for _ in range(mn2_hdr.NumModules) : mme_mod = get_struct(reading, mod_start, MME_Header_Old) - + if mme_mod.Tag != b'$MME' : break # Sanity check - + mod_size_all += mme_mod.Size - + mod_start += 0x50 - + mod_end_max = mod_start + 0x50 + 0xC + mod_size_all # Last $MME + $MME size + $SKU size + all $MOD sizes - + # For Engine/Graphics alignment & size, remove fpt_start (included in mod_end_max < mod_end < p_offset_last) eng_fw_align -= (mod_end_max - fpt_start) % 0x1000 # 4K alignment Size of entire Engine/Graphics firmware - + if eng_fw_align != 0x1000 : eng_fw_end = mod_end_max + eng_fw_align - fpt_start - + file_has_align = min(eng_fw_align, file_end - mod_end_max) # Check whatever alignment padding is actually available in file file_bad_align = reading[mod_end_max:mod_end_max + file_has_align] not in [b'', b'\xFF' * file_has_align] # Data in alignment padding bool if fpt_start == 0 and file_has_align > 0 and file_bad_align : warn_stor.append([col_m + 'Warning: File has data in firmware 4K alignment padding!' + col_e, param.check]) - + check_fw_align = 0x0 if mod_end_max > file_end else eng_fw_align - file_has_align else : eng_fw_end = mod_end_max - fpt_start - + # Last $FPT entry has size, scan for uncharted partitions else : # Due to 4K $FPT Partition alignment, Uncharted can start after 0x0 to 0x1000 bytes @@ -12291,14 +12297,14 @@ def mass_scan(f_path) : p_end_last_back = p_end_last # Store $FPT-based p_end_last offset for CSME 12+ FWUpdate Support detection uncharted_match = cpd_pat.search(reading[p_end_last:p_end_last + 0x200B]) # Should be within the next 4-8K bytes if uncharted_match : p_end_last += uncharted_match.start() # Adjust p_end_last to actual Uncharted start - + # ME8-10 WCOD/LOCL but works for ME7, TXE1-2, SPS2-3 even though these end at last $FPT entry while reading[p_end_last + 0x1C:p_end_last + 0x20] == b'$MN2' : mod_in_id = '0000' - + mn2_hdr = get_struct(reading, p_end_last, get_manifest(reading, p_end_last)) man_ven = '%X' % mn2_hdr.VEN_ID - + if man_ven == '8086' : # Sanity check man_num = mn2_hdr.NumModules man_len = mn2_hdr.HeaderLength * 4 @@ -12308,93 +12314,93 @@ def mass_scan(f_path) : if variant == 'TXE' : mme_size = 0x80 else : mme_size = 0x60 # ME & SPS mcp_start = mod_start + man_num * mme_size + mme_size # (each $MME = mme_size, mme_size padding after last $MME) - + mcp_mod = get_struct(reading, mcp_start, MCP_Header) # $MCP holds total partition size - + if mcp_mod.Tag == b'$MCP' : # Sanity check fpt_part_all.append([mod_name,p_end_last,p_end_last + mcp_mod.Offset_Code_MN2 + mcp_mod.CodeSize,mod_in_id,'Code',True,False]) - + # Store $FPT Partition info for -dfpt if param.fpt_disp : # No Owner, Type Code, Valid, Not Empty pt_dfpt.add_row([mod_name,'','0x%0.6X' % p_end_last,'0x%0.6X' % mcp_mod.CodeSize, '0x%0.6X' % (p_end_last + mcp_mod.Offset_Code_MN2 + mcp_mod.CodeSize),'Code',mod_in_id,True,False]) - + p_end_last += mcp_mod.Offset_Code_MN2 + mcp_mod.CodeSize else : break # main "while" loop else : break # main "while" loop - + # SPS1, should not be run but works even though it ends at last $FPT entry while reading[p_end_last + 0x1C:p_end_last + 0x20] == b'$MAN' : - + mn2_hdr = get_struct(reading, p_end_last, get_manifest(reading, p_end_last)) man_ven = '%X' % mn2_hdr.VEN_ID - + if man_ven == '8086': # Sanity check man_num = mn2_hdr.NumModules man_len = mn2_hdr.HeaderLength * 4 mod_start = p_end_last + man_len + 0xC mod_size_all = 0 - + for _ in range(0, man_num) : mme_mod = get_struct(reading, mod_start, MME_Header_Old) mme_tag = mme_mod.Tag - + if mme_tag == b'$MME': # Sanity check mod_size_all += mme_mod.Size # Append all $MOD ($MME Code) sizes p_end_last = mod_start + 0x50 + 0xC + mod_size_all # Last $MME + $MME size + $SKU + all $MOD sizes - + mod_start += 0x50 else : p_end_last += 10 # to break main "while" loop break # nested "for" loop else : break # main "while" loop - + # CSE WCOD/LOCL/DNXP while reading[p_end_last:p_end_last + 0x4] == b'$CPD' : cpd_hdr_struct, cpd_hdr_size = get_cpd(reading, p_end_last) cpd_hdr = get_struct(reading, p_end_last, cpd_hdr_struct) cpd_num = cpd_entry_num_fix(reading, p_end_last, cpd_hdr.NumModules, cpd_hdr_size) cpd_tag = cpd_hdr.PartitionName.strip(b'\x00').decode('utf-8','ignore') - + # Calculate partition size by the CSE Extension 03 or 16 (CSE_Ext_03 or CSE_Ext_16) # PartitionSize of CSE_Ext_03/16 is always 0x0A at CSTXE so check $CPD entries instead cse_in_id,_,cse_ext_part_size = cse_part_inid(reading, p_end_last, ext_dict) - + # Last charted $FPT region size can be larger than CSE_Ext_03/16.PartitionSize because of 4K pre-alignment by Intel # Calculate partition size by the $CPD entries (Needed for CSTXE, 2nd check for CSME/CSSPS) cpd_offset_last = 0 # Reset Last Module Offset at each $CPD for entry in range(cpd_num) : # Check all $CPD Entry Sizes (Manifest, Metadata, Modules) cpd_entry_hdr = get_struct(reading, p_end_last + cpd_hdr_size + entry * 0x18, CPD_Entry) cpd_entry_offset,_,_ = cpd_entry_hdr.get_flags() - + # Store last entry (max $CPD offset) if cpd_entry_offset > cpd_offset_last : cpd_offset_last = cpd_entry_offset cpd_end_last = cpd_entry_offset + cpd_entry_hdr.Size - + fpt_off_start = p_end_last # Store starting offset of current $FPT Partition for fpt_part_all - + # Take the largest partition size from the two checks # Add previous $CPD start for next size calculation p_end_last += max(cse_ext_part_size,cpd_end_last) - + # Store all $FPT Partitions, uncharted (Type Code, Valid, Not Empty) fpt_part_all.append([cpd_tag,fpt_off_start,p_end_last,cse_in_id,'Code',True,False]) - + # Store $FPT Partition info for -dfpt if param.fpt_disp : pt_dfpt.add_row([cpd_tag,'','0x%0.6X' % fpt_off_start,'0x%0.6X' % (p_end_last - fpt_off_start), '0x%0.6X' % p_end_last,'Code','%0.4X' % cse_in_id,True,False]) - + # Show $FPT Partition info on demand (-dfpt) if param.fpt_disp : print('%s\n' % pt_dfpt) - + # Detect if uncharted $FPT partitions (IUPs) exist if len(fpt_part_all) > fpt_part_num : fwu_iup_exist = True - + # Detect $FPT partition overlaps for part in fpt_part_all : for all_part in fpt_part_all : @@ -12406,39 +12412,39 @@ def mass_scan(f_path) : and part[0] not in ['FTUP','DLMP'] and all_part[0] not in ['FTUP','DLMP'] and (part[1] < all_part[1] < part[2]) : err_stor.append([col_r + 'Error: $FPT partition %s (0x%0.6X - 0x%0.6X) overlaps with %s (0x%0.6X - 0x%0.6X)' % \ (part[0],part[1],part[2],all_part[0],all_part[1],all_part[2]) + col_e, True]) - + # Detect CSE IUP required by FWUpdate at $FPT Charted & Uncharted to avoid FIT bug for part in fpt_part_all : # Detect if firmware has Power Management Controller (PMCP/PCOD) partition if part[0] in ('PMCP','PCOD') and not part[6] : pmcp_fwu_found = True pmcp_size = part[2] - part[1] - + _,_,_,pmc_vcn,_,_,_,_,_,_,_,_,pmc_mn2_ver,_,pmc_ext15_info,_,_,_,_ = \ ext_anl(reading, '$CPD', part[1], file_end, ['PMC',-1,-1,-1,-1,-1,-1,'PMC'], None, [[],''], [[],-1,-1,-1]) - + pmc_all_init.append([pmc_vcn,pmc_mn2_ver,pmc_ext15_info,pmcp_size]) - + # Detect if firmware has Platform Controller Hub Configuration (PCHC) partition if part[0] == 'PCHC' and not part[6] : pchc_fwu_found = True pchc_size = part[2] - part[1] - + _,_,_,pchc_vcn,_,_,_,_,_,_,_,_,pchc_mn2_ver,_,pchc_ext15_info,_,_,_,_ = \ ext_anl(reading, '$CPD', part[1], file_end, ['PCHC',-1,-1,-1,-1,-1,-1,'PCHC'], None, [[],''], [[],-1,-1,-1]) - + pchc_all_init.append([pchc_vcn,pchc_mn2_ver,pchc_ext15_info,pchc_size]) - + # Detect if firmware has USB Type C Physical (PHY) partition if part[0] in ('PPHY','NPHY','SPHY','PHYP') and not part[6] : phy_fwu_found = True phy_size = part[2] - part[1] - + _,_,_,phy_vcn,_,_,_,_,_,_,_,_,phy_mn2_ver,_,phy_ext15_info,_,_,_,_ = \ ext_anl(reading, '$CPD', part[1], file_end, ['PHY',-1,-1,-1,-1,-1,-1,'PHY'], None, [[],''], [[],-1,-1,-1]) - + phy_all_init.append([phy_vcn,phy_mn2_ver,phy_ext15_info,phy_size]) - + # Calculate Firmware Size based on $FPT and/or IFWI LT if (cse_lt_struct and not rgn_exist) or (rgn_exist and p_end_last != p_max_size) : if cse_lt_struct : @@ -12447,19 +12453,19 @@ def mass_scan(f_path) : for entry_o in cse_lt_hdr_info : if entry_c[0] != entry_o[0] and entry_c[1] >= entry_o[1] and entry_c[1] + entry_c[2] <= entry_o[1] + entry_o[2] : cse_lt_dup_size += entry_c[2] # If entry_c different than entry_o and entry_c within entry_o, duplicated/nested - + # CSME 12+ consists of Layout Table (0x1000) + Data (MEA or LT size) + Boot/Temp/ELog (LT size) - Duplicates/Nested (LT size) p_end_last = cse_lt_size + max(p_end_last,cse_lt_dp_size) + cse_lt_bp_size - cse_lt_dup_size - + # For Engine/Graphics alignment & size, remove fpt_start (included in p_end_last < eng_fw_end < p_offset_spi) eng_fw_align -= (p_end_last - fpt_start) % 0x1000 # 4K alignment Size of entire Engine/Graphics firmware - + if eng_fw_align != 0x1000 : file_has_align = min(eng_fw_align, file_end - p_end_last) # Check whatever alignment padding is actually available in file file_bad_align = reading[p_end_last:p_end_last + file_has_align] not in [b'', b'\xFF' * file_has_align] # Data in alignment padding bool if (fpt_start == 0 or (cse_lt_struct and cse_lt_off == 0)) and file_has_align > 0 and file_bad_align : warn_stor.append([col_m + 'Warning: File has data in Firmware 4K alignment padding!' + col_e, param.check]) - + # Ignore 4K alignment at CSME 16+ for FWUpdate image creation via MFIT (seriously Intel ?) if variant == 'CSME' and major >= 16 : eng_fw_end = p_end_last - fpt_start @@ -12468,7 +12474,7 @@ def mass_scan(f_path) : check_fw_align = 0x0 if p_end_last > file_end else eng_fw_align - file_has_align else : eng_fw_end = p_end_last - fpt_start - + # Detect Firmware Data inconsistency (eng_fw_end dependent) if rgn_exist or cse_lt_struct : # SPI image with FD @@ -12483,18 +12489,18 @@ def mass_scan(f_path) : padd_start_fd = (cse_lt_off if cse_lt_struct else fpt_start) + eng_fw_end padd_end_fd = (cse_lt_off if cse_lt_struct else fpt_start) + eng_fw_end + padd_size_fd padd_data_fd = reading[padd_start_fd:padd_end_fd] - + if padd_data_fd != padd_size_fd * b'\xFF' : # Detect CSSPS 4, sometimes uncharted/empty, $BIS partition sps4_bis_match = b'$BIS\x00' in padd_data_fd if (variant,major) == ('CSSPS',4) else None - + if sps4_bis_match is not None : eng_size_text = ['', False] else : eng_size_text = [col_m + 'Warning: Data in Engine/Graphics region padding, possible data corruption!' + col_e, True] - + # Bare Engine/Graphics Region elif fpt_start == 0 or (cse_lt_struct and cse_lt_off == 0) : padd_size_file = file_end - eng_fw_end - + if eng_fw_end > file_end : if eng_fw_end <= file_end + check_fw_align : # Firmware ends at last $FPT entry but is not 4K aligned, can/must be ignored (CSME 12-15/16+) @@ -12505,7 +12511,7 @@ def mass_scan(f_path) : eng_size_text = [col_m + 'Warning: Firmware size exceeds File, possible data loss!' + col_e, True] elif eng_fw_end < file_end : padd_data_file = reading[eng_fw_end:eng_fw_end + padd_size_file] - + if padd_data_file == padd_size_file * b'\xFF' : # Extra padding is clear eng_size_text = [col_y + 'Note: File has harmless unneeded Firmware end padding!' + col_e, False] # warn_stor @@ -12514,11 +12520,11 @@ def mass_scan(f_path) : else : # Detect CSSPS 4, sometimes uncharted/empty, $BIS partition sps4_bis_match = b'$BIS\x00' in padd_data_file if (variant,major) == ('CSSPS',4) else None - + # Extra padding has data if sps4_bis_match is not None : eng_size_text = ['', False] else : eng_size_text = [col_m + 'Warning: File size exceeds Firmware, data in padding!' + col_e, True] - + # Firmware Type detection (Stock, Extracted, Update) if ifwi_exist : # IFWI fitc_ver_found = True @@ -12543,20 +12549,20 @@ def mass_scan(f_path) : elif (variant == 'ME' and major >= 8) or variant in ['CSME','CSTXE','CSSPS','TXE','GSC'] : fpt_upd_only = [] # Initialize $FPT w/ Update partitions only list _ = [fpt_upd_only.append(part[0]) for part in fpt_part_all if not part[6]] - + # Check 1, $FPT Update image if sorted(fpt_upd_only) == ['FTPR','FTUP','NFTP'] : fw_type = 'Update' # $FPT exists but includes FTPR & FTUP/NFTP only, Update # Check 2, FITC Version elif fpt_hdr.FitBuild in [0x0,0xFFFF] : # 0000/FFFF --> clean (CS)ME/(CS)TXE fw_type = 'Stock' - + # Check 3, FOVD partition if not fovd_clean('new') : fw_type = 'Extracted' - + # Check 4, CSTXE FIT placeholder $FPT Header entries if reading[fpt_start:fpt_start + 0x10] + reading[fpt_start + 0x1C:fpt_start + 0x30] == b'\xFF' * 0x24 : fw_type = 'Extracted' - + # Check 5, CSME 13+ FWUpdate EXTR has placeholder $FPT ROM-Bypass Vectors 0-3 (0xFF instead of 0x00 padding) # If not enough (should be OK), MEA could further check if FTUP is empty and/or if PMCP/PCOD, PCHC & PHY exist or not if variant == 'CSME' and major >= 13 and reading[fpt_start:fpt_start + 0x10] == b'\xFF' * 0x10 : fw_type = 'Extracted' @@ -12570,13 +12576,13 @@ def mass_scan(f_path) : fitc_build = fpt_hdr.FitBuild else : fw_type = 'Update' # No Region detected, Update - + # Verify $FPT Checksums (must be after Firmware Type detection) if rgn_exist : fpt_chk_bgn = fpt_pat_bgn if fpt_length <= 0x20 else fpt_start fpt_ver_byte = struct.pack('= 12) or (variant == 'CSTXE' and major >= 3) or (variant == 'CSSPS' and major >= 5)) : fpt_chk_fail = False - + # Warn when $FPT Checksum is wrong if fpt_chk_fail : warn_stor.append([col_m + 'Warning: Wrong $FPT Checksum %s, expected %s!' % (fpt_chk_int,fpt_chk_mea) + col_e, True]) - + # Check SPS 3 extra $FPT Checksum-16 (from Lordkag's UEFIStrip) if variant == 'SPS' and major == 3 : sps3_chk_start = fpt_start + 0x30 @@ -12613,15 +12619,15 @@ def mass_scan(f_path) : sps3_chk16_calc = '0x%0.4X' % (~sps3_chk16_sum & 0xFFFF) if sps3_chk16_calc != sps3_chk16_file: warn_stor.append([col_m + 'Warning: Wrong $FPT SPS3 Checksum %s, expected %s!' % (sps3_chk16_file,sps3_chk16_calc) + col_e, True]) - + # Check for Fujitsu UMEM ME Region (RGN/$FPT or UPD/$MN2) if (fd_me_rgn_exist and reading[me_fd_start:me_fd_start + 0x4] == b'\x55\x4D\xC9\x4D') or (reading[:0x4] == b'\x55\x4D\xC9\x4D') : warn_stor.append([col_m + 'Warning: Fujitsu Intel Engine/Graphics firmware detected!' + col_e, False]) - + # Detect Firmware Release (Production, Pre-Production, ROM-Bypass) mn2_flags_pvbit,_,_,_,mn2_flags_debug = mn2_ftpr_hdr.get_flags() rel_signed = ['Production', 'Debug'][mn2_flags_debug] - + if fpt_romb_found : release = 'ROM-Bypass' rel_db = 'BYP' @@ -12631,24 +12637,24 @@ def mass_scan(f_path) : else : release = 'Pre-Production' rel_db = 'PRE' - + # Fix Release of PRE firmware which are wrongly reported as PRD release, rel_db = release_fix(release, rel_db, rsa_key_hash) - + # Detect PV/PC bit (0 or 1) if (variant == 'ME' and major >= 8) or variant == 'TXE' : pvbit_match = (re.compile(br'\$DAT.{20}IFRP', re.DOTALL)).search(reading[start_man_match:]) # $DAT + [0x14] + IFRP detection if pvbit_match : pvbit = reading[start_man_match + pvbit_match.start() + 0x10] elif variant in ['CSME','CSTXE','CSSPS','GSC'] or variant.startswith(('PMC','PCHC','PHY','OROM')) : pvbit = mn2_flags_pvbit - + if variant == 'ME' : # Management Engine - + # Detect SKU Attributes sku_match = re.compile(br'\$SKU[\x03-\x04]\x00\x00\x00').search(reading[start_man_match:]) # $SKU detection if sku_match is not None : start_sku_match = sku_match.start() + start_man_match - + if 2 <= major <= 6 : # https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/WordDocuments/instanceidandversionstringformats.htm # https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/WordDocuments/vproverificationtableparameterdefinitions.htm @@ -12656,10 +12662,10 @@ def mass_scan(f_path) : elif 7 <= major <= 10 : sku_attrib = get_struct(reading, start_sku_match, SKU_Attributes) _,sku_slim,_,_,_,_,_,_,_,is_patsburg,sku_type,sku_size,_ = sku_attrib.get_flags() - + if major == 2 : # ICH8 2.0 - 2.2 or ICH8M 2.5 - 2.6 sku_byte = {0: 'AMT + ASF + QST', 1: 'ASF + QST', 2: 'QST'} - + if sku_me == 0x00000000 : # AMT + ASF + QST sku = 'AMT' sku_db = 'AMT' @@ -12669,7 +12675,7 @@ def mass_scan(f_path) : else : sku = col_r + 'Unknown' + col_e err_stor.append([col_r + 'Error: Unknown %s %d.%d SKU!' % (variant, major, minor) + col_e, True]) - + # ME2-Only Fix 1 : The usual method to detect EXTR vs RGN does not work for ME2 if fw_type_fix : if sku == 'QST' or (sku == 'AMT' and minor >= 5) : @@ -12705,10 +12711,10 @@ def mass_scan(f_path) : netip_end = fpt_start + end_netip_match + netip_size + 0x3 # (+ 0x4 - 0x1) me2_type_fix = int.from_bytes(reading[netip_start:netip_end], 'big') me2_type_exp = int.from_bytes(b'\x00' * (netip_size - 0x1), 'big') - + if me2_type_fix != me2_type_exp : fw_type = 'Extracted' else : fw_type = 'Stock' - + # ME2-Only Fix 2 : Identify ICH Revision B0 firmware SKUs me2_sku_fix = ['FF4DAEACF679A7A82269C1C722669D473F7D76AD3DFDE12B082A0860E212CD93', '345F39266670F432FCFF3B6DA899C7B7E0137ED3A8A6ABAD4B44FB403E9BB3BB', @@ -12716,25 +12722,25 @@ def mass_scan(f_path) : if rsa_sig_hash in me2_sku_fix : sku = 'AMT B0' sku_db = 'AMT_B0' - + # ME2-Only Fix 3 : Detect ROMB RGN/EXTR image correctly (at $FPT v1 ROMB was before $FPT) if rgn_exist and release == 'Pre-Production' : byp_pat = re.compile(br'\$VER\x02\x00\x00\x00') # $VER2... detection (ROM-Bypass) byp_match = byp_pat.search(reading) - + if byp_match : release = 'ROM-Bypass' rel_db = 'BYP' byp_size = fpt_start - (byp_match.start() - 0x80) eng_fw_end += byp_size eng_size_text = ['', False] - + if minor >= 5 : platform = 'ICH8M' else : platform = 'ICH8' - + elif major == 3 : # ICH9 or ICH9DO sku_bits = {1: 'IDT', 2: 'TPM', 3: 'AMT Lite', 4: 'AMT', 5: 'ASF', 6: 'QST'} - + if sku_me in [0x0E000000,0x00000000] : # AMT + ASF + QST (00000000 for Pre-Alpha ROMB) sku = 'AMT' # Q35 only sku_db = 'AMT' @@ -12760,11 +12766,11 @@ def mass_scan(f_path) : effs_start = int.from_bytes(reading[end_effs_match:end_effs_match + 0x4], 'little') effs_size = int.from_bytes(reading[end_effs_match + 0x4:end_effs_match + 0x8], 'little') effs_data = reading[fpt_start + effs_start:fpt_start + effs_start + effs_size] - + me3_type_fix1 = (re.compile(br'ME_CFG_DEF\x04NVKR')).findall(effs_data) # ME_CFG_DEF.NVKR detection (RGN have <= 2) me3_type_fix2 = (re.compile(br'MaxUsedKerMem\x04NVKR\x7Fx\x01')).search(effs_data) # MaxUsedKerMem.NVKR.x. detection me3_type_fix3 = int.from_bytes(reading[fpt_start + effs_start + effs_size - 0x20:fpt_start + effs_start + effs_size - 0x10], 'big') - + if me3_type_fix2 is not None : (start_me3f2_match, end_me3f2_match) = me3_type_fix2.span() me3_type_fix2a = int.from_bytes(reading[fpt_start + effs_start + end_me3f2_match - 0x30:fpt_start + effs_start + end_me3f2_match - 0x20], 'big') @@ -12772,7 +12778,7 @@ def mass_scan(f_path) : if len(me3_type_fix1) > 2 or me3_type_fix3 != 0x10 * 0xFF or me3_type_fix2a != 0x10 * 0xFF or me3_type_fix2b != 0x10 * 0xFF : fw_type = 'Extracted' else : fw_type = 'Stock' - + # ME3-Only Fix 2 : Detect AMT ROMB UPD image correctly (very vague, may not always work) if fw_type == 'Update' and release == 'Pre-Production' : # Debug Flag detected at $MAN but PRE vs BYP is needed for UPD (not RGN) # It seems that ROMB UPD is smaller than equivalent PRE UPD @@ -12784,24 +12790,24 @@ def mass_scan(f_path) : if (sku == 'AMT' and 0x100000 < file_end < 0x185000) or (sku == 'ASF' and 0x40000 < file_end < 0xAF000) or (sku == 'QST' and file_end < 0x2B000) : release = 'ROM-Bypass' rel_db = 'BYP' - + # ME3-Only Fix 3 : Detect Pre-Alpha ($FPT v1) ROMB RGN/EXTR image correctly if rgn_exist and fpt_version == 16 and release == 'Pre-Production' : byp_pat = re.compile(br'\$VER\x03\x00\x00\x00') # $VER3... detection (ROM-Bypass) byp_match = byp_pat.search(reading) - + if byp_match : release = 'ROM-Bypass' rel_db = 'BYP' byp_size = fpt_start - (byp_match.start() - 0x80) eng_fw_end += byp_size eng_size_text = ['', False] - + platform = 'ICH9' - + elif major == 4 : # ICH9M or ICH9M-E (AMT or TPM+AMT): 4.0 - 4.2 , xx00xx --> 4.0 , xx20xx --> 4.1 or 4.2 sku_bits = {0: 'Reserved', 1: 'IDT', 2: 'TPM', 3: 'AMT Lite', 4: 'AMT', 5: 'ASF', 6: 'QST', 7: 'Reserved'} - + if sku_me in [0xAC200000,0xAC000000,0x04000000] : # 040000 for Pre-Alpha ROMB sku = 'AMT + TPM' # CA_ICH9_REL_ALL_SKUs_ (TPM + AMT) sku_db = 'ALL' @@ -12814,7 +12820,7 @@ def mass_scan(f_path) : else : sku = col_r + 'Unknown' + col_e err_stor.append([col_r + 'Error: Unknown %s %d.%d SKU!' % (variant, major, minor) + col_e, True]) - + # ME4-Only Fix 1 : Detect ROMB UPD image correctly if fw_type == "Update" : byp_pat = re.compile(br'ROMB') # ROMB detection (ROM-Bypass) @@ -12822,7 +12828,7 @@ def mass_scan(f_path) : if byp_match : release = 'ROM-Bypass' rel_db = 'BYP' - + # ME4-Only Fix 2 : Detect SKUs correctly, only for Pre-Alpha firmware if minor == 0 and hotfix == 0 : if fw_type == 'Update' : @@ -12831,7 +12837,7 @@ def mass_scan(f_path) : else : tpm_tag = (re.compile(br'NVTPTPID')).search(reading) # NVTPTPID partition found at ALL or TPM amt_tag = (re.compile(br'NVCMAMTC')).search(reading) # NVCMAMTC partition found at ALL or AMT - + if tpm_tag is not None and amt_tag is not None : sku = 'AMT + TPM' # CA_ICH9_REL_ALL_SKUs_ sku_db = 'ALL' @@ -12841,7 +12847,7 @@ def mass_scan(f_path) : else : sku = 'AMT' # CA_ICH9_REL_IAMT_ sku_db = 'AMT' - + # ME4-Only Fix 3 : The usual method to detect EXTR vs RGN does not work for ME4, KRND. not enough if fw_type_fix : effs_match = (re.compile(br'EFFSOSID')).search(reading) # EFFSOSID detection @@ -12850,19 +12856,19 @@ def mass_scan(f_path) : effs_start = int.from_bytes(reading[end_effs_match:end_effs_match + 0x4], 'little') effs_size = int.from_bytes(reading[end_effs_match + 0x4:end_effs_match + 0x8], 'little') effs_data = reading[fpt_start + effs_start:fpt_start + effs_start + effs_size] - + me4_type_fix1 = (re.compile(br'ME_CFG_DEF')).findall(effs_data) # ME_CFG_DEF detection (RGN have 2-4) me4_type_fix2 = (re.compile(br'GPIO10Owner')).search(effs_data) # GPIO10Owner detection me4_type_fix3 = (re.compile(br'AppRule\.03\.000000')).search(effs_data) # AppRule.03.000000 detection - + if len(me4_type_fix1) > 5 or me4_type_fix2 is not None or me4_type_fix3 is not None : fw_type = "Extracted" else : fw_type = 'Stock' - + platform = 'ICH9M' - + elif major == 5 : # ICH10D or ICH10DO sku_bits = {3: 'Standard Manageability', 4: 'AMT', 5: 'ASF', 6: 'QST', 8: 'Level III Manageability Upgrade', 9: 'Corporate', 10: 'Anti-Theft', 15: 'Remote PC Assist'} - + if sku_me == 0x3E080000 : # EL_ICH10_SKU1 sku = 'Digital Office' # AMT sku_db = 'DO' @@ -12875,7 +12881,7 @@ def mass_scan(f_path) : else : sku = col_r + 'Unknown' + col_e err_stor.append([col_r + 'Error: Unknown %s %d.%d SKU!' % (variant, major, minor) + col_e, True]) - + # ME5-Only Fix : Detect ROMB UPD image correctly if fw_type == 'Update' : byp_pat = re.compile(br'ROMB') # ROMB detection (ROM-Bypass) @@ -12883,14 +12889,14 @@ def mass_scan(f_path) : if byp_match : release = 'ROM-Bypass' rel_db = 'BYP' - + platform = 'ICH10' - + elif major == 6 : platform = 'IBX' - + sku_bits = {3: 'Standard Manageability', 4: 'AMT', 6: 'QST', 8: 'Local Wakeup Timer', 9: 'KVM', 10: 'Anti-Theft', 15: 'Remote PC Assist'} - + if sku_me == 0x00000000 : # Ignition (128KB, 2MB) if hotfix == 50 : # 89xx (Cave/Coleto Creek) ign_pch = 'CCK' @@ -12912,19 +12918,19 @@ def mass_scan(f_path) : else : sku = col_r + 'Unknown' + col_e err_stor.append([col_r + 'Error: Unknown %s %d.%d SKU!' % (variant, major, minor) + col_e, True]) - + # ME6-Only Fix 1 : ME6 Ignition does not work with KRND if 'Ignition' in sku and rgn_exist : ign_pat = (re.compile(br'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x6D\x3C\x75\x6D')).findall(reading) # Clean $MINIFAD checksum if len(ign_pat) < 2 : fw_type = "Extracted" # 2 before NFTP & IGRT else : fw_type = "Stock" - + # ME6-Only Fix 2 : Ignore errors at ROMB (Region present, FTPR tag & size missing) if release == 'ROM-Bypass' : eng_size_text = ['', False] - + elif major == 7 : sku_bits = {3: 'Standard Manageability', 4: 'AMT', 8: 'Local Wakeup Timer', 9: 'KVM', 10: 'Anti-Theft', 15: 'Remote PC Assist'} - + if sku_slim == 1 : sku = 'Slim' sku_db = 'SLM' @@ -12934,17 +12940,17 @@ def mass_scan(f_path) : elif sku_size * 0.5 == 5 or (build,hotfix,minor,sku_size) == (1041,0,0,1) : sku = '5MB' sku_db = '5MB' - + # ME7-Only Fix: ROMB UPD detection if fw_type == 'Update' : me7_mn2_hdr_len = mn2_ftpr_hdr.HeaderLength * 4 me7_mn2_mod_len = (mn2_ftpr_hdr.NumModules + 1) * 0x60 me7_mcp = get_struct(reading, start_man_match - 0x1B + me7_mn2_hdr_len + 0xC + me7_mn2_mod_len, MCP_Header) # Goto $MCP - + if me7_mcp.CodeSize in [374928,419984] : # 1.5/5MB ROMB Code Sizes release = 'ROM-Bypass' rel_db = 'BYP' - + # ME7 Blacklist Table Detection me7_blist_1_minor = int.from_bytes(reading[start_man_match + 0x6DF:start_man_match + 0x6E1], 'little') me7_blist_1_hotfix = int.from_bytes(reading[start_man_match + 0x6E1:start_man_match + 0x6E3], 'little') @@ -12954,27 +12960,27 @@ def mass_scan(f_path) : me7_blist_2_hotfix = int.from_bytes(reading[start_man_match + 0x6ED:start_man_match + 0x6EF], 'little') me7_blist_2_build = int.from_bytes(reading[start_man_match + 0x6EF:start_man_match + 0x6F1], 'little') if me7_blist_2_build != 0 : me7_blist_2 = '<= 7.%d.%d.%d' % (me7_blist_2_minor, me7_blist_2_hotfix, me7_blist_2_build) - + platform = ['CPT','CPT/PBG'][is_patsburg] - + elif major == 8 : sku_bits = {3: 'Standard Manageability', 4: 'AMT', 8: 'Local Wakeup Timer', 9: 'KVM', 10: 'Anti-Theft', 15: 'Remote PC Assist', 23: 'Small Business'} - + if sku_size * 0.5 == 1.5 : sku = '1.5MB' sku_db = '1.5MB' elif sku_size * 0.5 == 5 : sku = '5MB' sku_db = '5MB' - + # ME8-Only Fix: SVN location svn = mn2_ftpr_hdr.SVN_8 - + platform = 'CPT/PBG/PPT' - + elif major == 9 : sku_bits = {3: 'Standard Manageability', 4: 'AMT', 8: 'Local Wakeup Timer', 9: 'KVM', 10: 'Anti-Theft', 15: 'Remote PC Assist', 23: 'Small Business'} - + if sku_type == 0 : sku = '5MB' sku_db = '5MB' @@ -12984,16 +12990,16 @@ def mass_scan(f_path) : elif sku_type == 2 : sku = 'Slim' sku_db = 'SLM' - + if minor == 0 : platform = 'LPT' elif minor == 1 : platform = 'LPT/WPT' elif minor in [5,6] : platform = 'LPT-LP' - + # 9.6 --> Intel Harris Beach Ultrabook, HSW developer preview (https://bugs.freedesktop.org/show_bug.cgi?id=90002) - + elif major == 10 : sku_bits = {3: 'Standard Manageability', 4: 'AMT', 8: 'Local Wakeup Timer', 9: 'KVM', 10: 'Anti-Theft', 15: 'Remote PC Assist', 23: 'Small Business'} - + if sku_type == 0 : sku = '5MB' sku_db = '5MB' @@ -13003,39 +13009,39 @@ def mass_scan(f_path) : elif sku_type == 2 : sku = 'Slim' sku_db = 'SLM' - + if minor == 0 : platform = 'WPT-LP' - + else : is_unsupported = True - + elif variant == 'CSME' : # Converged Security Management Engine - + # Firmware Unpacking for all CSME if param.cse_unpack : cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start if rgn_exist else -1, fpt_chk_fail, cse_lt_chk_fail, cse_red_info, fdv_status, reading_msg, orom_hdr_all) continue # Next input file - + # Get CSE MFS File System Attributes & Configuration State (invokes mfs_anl, must be before ext_anl) mfs_state,mfs_parsed_idx,intel_cfg_hash_mfs,mfs_info,pch_init_final,vol_ftbl_id,config_rec_size,vol_ftbl_pl \ = get_mfs_anl(mfs_state,mfs_parsed_idx,intel_cfg_hash_mfs,mfs_info,pch_init_final) - + # Get CSE EFS File System Attributes & Configuration State (must be after mfs_anl) if efs_found : efs_init = efs_anl(out_dir, efs_start, efs_start + efs_size, vol_ftbl_id, vol_ftbl_pl) - + # Get CSE Firmware Attributes (must be after mfs_anl) cpd_offset,cpd_mod_attr,cpd_ext_attr,vcn,ext12_info,ext_print,ext_pname,ext50_info,ext_phval,ext_dnx_val,oem_config,oem_signed,cpd_mn2_info, \ ext_iunit_val,ext15_info,pch_init_final,gmf_blob_info,fwi_iup_hashes,gsc_info \ = ext_anl(reading, '$MN2', start_man_match, file_end, [variant,major,minor,hotfix,build,year,month,variant_p], None, [mfs_parsed_idx,intel_cfg_hash_mfs], [pch_init_final,config_rec_size,vol_ftbl_id,vol_ftbl_pl]) - + # MFS missing, determine state via FTPR > fitc.cfg, FITC, MFSB or EFS (must be after mfs_anl, efs_anl & ext_anl) if mfs_state != 'Initialized' and efs_init : mfs_state = 'Initialized' elif mfs_state == 'Unconfigured' and (oem_config or fitc_found or mfsb_found or cdmd_found) : mfs_state = 'Configured' - + fw_0C_sku0,fw_0C_sku1,fw_0C_lbg,fw_0C_sku2 = ext12_info # Get SKU Capabilities, SKU Type, HEDT Support, SKU Platform - + # Set SKU Type via Extension 12 or 15 or 35 (CSME logic) # When CSE_Ext_0F_R2 was introduced, Firmware SKU field was reserved to the meaningless value 1. After some time, Firmware SKU # was adjusted with actual values 0+ and 1 now means Corporate (COR). To avoid confusion when comparing against the SKU value @@ -13051,53 +13057,53 @@ def mass_scan(f_path) : else : sku_init = 'Unknown' sku_init_db = 'UNK' - + # Detect SKU Platform via MFS Intel PCH Initialization Table if pch_init_final and '-LP' in pch_init_final[-1][0] : pos_sku_tbl = 'LP' elif pch_init_final and '-H' in pch_init_final[-1][0] : pos_sku_tbl = 'H' elif pch_init_final and '-N' in pch_init_final[-1][0] : pos_sku_tbl = 'N' elif pch_init_final and '-V' in pch_init_final[-1][0] : pos_sku_tbl = 'V' - + db_sku_chk,sku,sku_stp,sku_pdm = get_cse_db(variant) # Get CSE SKU info from DB - + # Get CSME 12+ Final SKU, SKU Platform, SKU Stepping sku,sku_result,sku_stp = get_csme12_sku(sku_init, fw_0C_sku0, fw_0C_sku2, sku, sku_result, sku_stp, db_sku_chk, pos_sku_tbl, pch_init_final) - + # Parse all detected stitched PMC firmware pmc_all_anl = pmc_parse(pmc_all_init, pmc_all_anl) - + # Parse all detected stitched PCHC firmware pchc_all_anl = pchc_parse(pchc_all_init, pchc_all_anl) - + # Parse all detected stitched PHY firmware phy_all_anl = phy_parse(phy_all_init, phy_all_anl) - + if major == 11 : - + # Set SKU Platform via Extension 12 Attributes if minor > 0 or (minor == 0 and (hotfix > 0 or (hotfix == 0 and build >= 1205 and build != 7101))) : if fw_0C_sku2 == 0 : pos_sku_ext = 'H' # Halo elif fw_0C_sku2 == 1 : pos_sku_ext = 'LP' # Low Power else : pos_sku_ext = 'Invalid' # Only for CSME >= 11.0.0.1205 - + # SKU not in Extension 12 and not in DB, scan decompressed Huffman module FTPR > kernel if pos_sku_ext == 'Invalid' and sku == 'NaN' : for mod in cpd_mod_attr : if mod[0] == 'kernel' : huff_shape, huff_sym, huff_unk = cse_huffman_dictionary_load(variant, major, minor, 'error') ker_decomp, huff_error = cse_huffman_decompress(reading[mod[3]:mod[3] + mod[4]], mod[4], mod[5], huff_shape, huff_sym, huff_unk, 'none') - + # 0F22D88D65F85B5E5DC355B8 (56AA|36AA for H, 60A0|004D|9C64 for LP) sku_pat = re.compile(br'\x0F\x22\xD8\x8D\x65\xF8\x5B\x5E\x5D\xC3\x55\xB8').search(ker_decomp) - + if sku_pat : sku_bytes = int.from_bytes(ker_decomp[sku_pat.end():sku_pat.end() + 0x1] + ker_decomp[sku_pat.end() + 0x17:sku_pat.end() + 0x18], 'big') if sku_bytes in (0x56AA,0x36AA) : pos_sku_ker = 'H' # 0x36AA for 11.0.0.1126 elif sku_bytes in (0x60A0,0x004D,0x9C64) : pos_sku_ker = 'LP' # 0x004D,0x9C64 for 11.0.0.1100,11.0.0.1109 - + break # Skip rest of FTPR modules - + if pos_sku_ext in ['Unknown','Invalid'] : # SKU not retrieved from Extension 12 if pos_sku_ker == 'Invalid' : # SKU not retrieved from Kernel if sku == 'NaN' : # SKU not retrieved from manual MEA DB entry @@ -13109,23 +13115,23 @@ def mass_scan(f_path) : sku = sku_init + ' ' + pos_sku_ker # SKU retrieved from Kernel else : sku = sku_init + ' ' + pos_sku_ext # SKU retrieved from Extension 12 - + # Store final SKU result (CSME 11 only) if ' LP' in sku : sku_result = 'LP' elif ' H' in sku : sku_result = 'H' - + # Set PCH/SoC Stepping, if not found at DB if sku_stp == 'Unknown' and pch_init_final : sku_stp = pch_init_final[-1][1] - + # Adjust PCH Platform via Minor version if minor == 0 and not pch_init_final : platform = 'SPT' # Sunrise Point elif minor in [5,6,7,8] and not pch_init_final : platform = 'SPT/KBP' # Sunrise Point, Union Point elif minor in [10,11,12] and not pch_init_final : platform = 'BSF/GCF' # Basin Falls, Glacier Falls elif minor in [20,21,22] and not pch_init_final : platform = 'LBG' # Lewisburg - + # Get CSME 11 DB SKU (must be before sku_pdm) sku_db = sku_db_cse(sku_init_db, sku_result, sku_stp, sku_db, False, False) - + # Power Down Mitigation (PDM) is a SPT-LP C erratum, first fixed at ~11.0.0.1183 # Hardcoded in FTPR > BUP, Huffman decompression required to detect NPDM or YPDM # Hardfixed at KBP-LP A but 11.5-8 have PDM firmware for SPT-LP C with KBL(R) @@ -13136,73 +13142,73 @@ def mass_scan(f_path) : if mod[0] == 'bup' : huff_shape, huff_sym, huff_unk = cse_huffman_dictionary_load(variant, major, minor, 'error') bup_decomp, huff_error = cse_huffman_decompress(reading[mod[3]:mod[3] + mod[4]], mod[4], mod[5], huff_shape, huff_sym, huff_unk, 'none') - + if bup_decomp != b'' : # 55B00189E55DC3 pdm_pat = re.compile(br'\x55\xB0\x01\x89\xE5\x5D\xC3').search(bup_decomp) - + if pdm_pat : sku_pdm = 'YPDM' else : sku_pdm = 'NPDM' - + break # Skip rest of FTPR modules - + if sku_pdm == 'YPDM' : pdm_status = 'Yes' elif sku_pdm == 'NPDM' : pdm_status = 'No' elif sku_pdm == 'UPDM1' : pdm_status = 'Unknown 1' elif sku_pdm == 'UPDM2' : pdm_status = 'Unknown 2' else : pdm_status = 'Unknown' - + sku_db += '_%s' % sku_pdm # Must be after sku_db_cse - + elif major == 12 : - + if minor == 0 and not pch_init_final : platform = 'CNP' # Cannon Point - + elif major == 13 : - + if minor == 0 and not pch_init_final : platform = 'ICP' # Ice Point elif minor == 30 and not pch_init_final : platform = 'LKF' # Lakefield elif minor == 50 and not pch_init_final : platform = 'JSP' # Jasper Point - + elif major == 14 : - + if minor in [0,1] and not pch_init_final : platform = 'CMP-H/LP' # Comet Point H/LP elif minor == 5 and not pch_init_final : platform = 'CMP-V' # Comet Point V - + elif major == 15 : - + if minor == 0 and not pch_init_final : platform = 'TGP' # Tiger Point elif minor == 40 and not pch_init_final : platform = 'MCC' # Mule Creek Canyon (Elkhart Lake) - + if minor not in [0, 40]: is_unsupported = True - + elif major == 16 : - + if minor == 0 and not pch_init_final : platform = 'ADP' # Alder Point elif minor == 1 and not pch_init_final : platform = 'ADP/RPP' # Raptor Point - + is_unsupported = True - + else : - + is_unsupported = True - + # Get CSME 12+ DB SKU sku_db = sku_db_cse(sku_init_db, sku_result, sku_stp, sku_db, False, True) - + elif variant == 'TXE' : # Trusted Execution Engine - + # Detect SKU Attributes sku_match = re.compile(br'\$SKU[\x03-\x04]\x00\x00\x00').search(reading[start_man_match:]) # $SKU detection if sku_match is not None : start_sku_match = sku_match.start() + start_man_match - + sku_attrib = get_struct(reading, start_sku_match, SKU_Attributes) _,_,_,_,_,_,_,_,_,_,_,sku_size,_ = sku_attrib.get_flags() - + if major in [0,1] : - + if sku_size * 0.5 == 1.0 : sku = '1MB N/W' sku_db = '1MB_NW' @@ -13217,111 +13223,111 @@ def mass_scan(f_path) : platform = 'BYT' else : sku = col_r + 'Unknown' + col_e - + if rsa_key_hash == '6B8B10107E20DFD45F6C521100B950B78969B4AC9245D90DE3833E0A082DF374' : sku += ' M/D' sku_db += '_MD' elif rsa_key_hash == '613421A156443F1C038DDE342FF6564513A1818E8CC23B0E1D7D7FB0612E04AC' : sku += ' I/T' sku_db += '_IT' - + elif major == 2 : if sku_size * 0.5 == 1.5 : sku = '1.375MB' sku_db = '1.375MB' else : sku = col_r + 'Unknown' + col_e - + platform = 'BSW/CHT' - + else : is_unsupported = True - + elif variant == 'CSTXE' : # Converged Security Trusted Execution Engine - + # Firmware Unpacking for all CSTXE if param.cse_unpack : cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start if rgn_exist else -1, fpt_chk_fail, cse_lt_chk_fail, cse_red_info, fdv_status, reading_msg, orom_hdr_all) continue # Next input file - + # Get CSE MFS File System Attributes & Configuration State (invokes mfs_anl, must be before ext_anl) mfs_state,mfs_parsed_idx,intel_cfg_hash_mfs,mfs_info,pch_init_final,vol_ftbl_id,config_rec_size,vol_ftbl_pl \ = get_mfs_anl(mfs_state,mfs_parsed_idx,intel_cfg_hash_mfs,mfs_info,pch_init_final) - + # Detect CSE Firmware Attributes (must be after mfs_anl) cpd_offset,cpd_mod_attr,cpd_ext_attr,vcn,ext12_info,ext_print,ext_pname,ext50_info,ext_phval,ext_dnx_val,oem_config,oem_signed,cpd_mn2_info, \ ext_iunit_val,ext15_info,pch_init_final,gmf_blob_info,fwi_iup_hashes,gsc_info \ = ext_anl(reading, '$MN2', start_man_match, file_end, [variant,major,minor,hotfix,build,year,month,variant_p], None, [mfs_parsed_idx,intel_cfg_hash_mfs], [pch_init_final,config_rec_size,vol_ftbl_id,vol_ftbl_pl]) - + # MFS missing, determine state via FTPR > fitc.cfg, FITC, MFSB or EFS (must be after mfs_anl, efs_anl & ext_anl) if mfs_state != 'Initialized' and efs_init : mfs_state = 'Initialized' elif mfs_state == 'Unconfigured' and (oem_config or fitc_found or mfsb_found or cdmd_found) : mfs_state = 'Configured' - + fw_0C_sku0,fw_0C_sku1,fw_0C_lbg,fw_0C_sku2 = ext12_info # Get SKU Capabilities, SKU Type, HEDT Support, SKU Platform - + db_sku_chk,sku,sku_stp,sku_pdm = get_cse_db(variant) # Get CSE SKU info from DB - + if major == 3 : - + if minor in [0,1] : - + # Adjust SoC Stepping if not from DB if sku_stp == 'Unknown' : if release == 'Production' : sku_stp = 'B' # PRD else : sku_stp = 'A' # PRE, BYP - + platform = 'APL' # Apollo Lake - + elif minor == 2 : - + # Adjust SoC Stepping if not from DB if sku_stp == 'Unknown' : if release == 'Production' : sku_stp = 'C' # PRD (Joule_C0-X64-Release) else : sku_stp = 'A' # PRE, BYP - + platform = 'BXT' # Broxton (Joule) - + elif major == 4 : - + if minor == 0 : - + # Adjust SoC Stepping if not from DB if sku_stp == 'Unknown' : if release == 'Production' : sku_stp = 'B' # PRD else : sku_stp = 'A' # PRE, BYP - + platform = 'GLK' - + else : - + is_unsupported = True - + # Parse all detected stitched PMC firmware (must be at the end due to SKU Stepping adjustments) pmc_all_anl = pmc_parse(pmc_all_init, pmc_all_anl) - + # Get DB SKU (must be at the end due to superseded minor versions) sku_db = sku_db_cse('', '', sku_stp, sku_db, True, True) - + elif variant == 'SPS' : # Server Platform Services - + if major == 1 : if not rgn_exist : sps1_rec_match = re.compile(br'EpsRecovery').search(reading[start_man_match:]) # EpsRecovery detection if sps1_rec_match : fw_type = 'Recovery' else : fw_type = 'Operational' - + elif major in [2,3] : sps_platform = {'GR':'Grantley', 'GP':'Grantley-EP', 'GV':'Grangeville', 'DE':'Denlow', 'BR':'Bromolow', 'RO':'Romley', 'BK':'Brickland'} sps_type = (reading[end_man_match + 0x264:end_man_match + 0x266]).decode('utf-8') # FT (Recovery) or OP (Operational) - + if sps_type == 'OP' : if not rgn_exist : fw_type = 'Operational' sku = (reading[end_man_match + 0x266:end_man_match + 0x268]).decode('utf-8') # OPxx (example: OPGR --> Operational Grantley) sku_db = sku platform = sps_platform[sku] if sku in sps_platform else 'Unknown ' + sku - + elif sps_type == 'FT' : if not rgn_exist : fw_type = 'Recovery' rec_sku_match = re.compile(br'R2OP.{6}OP', re.DOTALL).search(reading[start_man_match:start_man_match + 0x2000]) # R2OP.{6}OP detection @@ -13331,38 +13337,38 @@ def mass_scan(f_path) : platform = sps_platform[sku] if sku in sps_platform else 'Unknown ' + sku else : is_unsupported = True - + elif variant == 'CSSPS' : # Converged Security Server Platform Services - + # Firmware Unpacking for all CSSPS if param.cse_unpack : cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start if rgn_exist else -1, fpt_chk_fail, cse_lt_chk_fail, cse_red_info, fdv_status, reading_msg, orom_hdr_all) continue # Next input file - + # Get CSE MFS File System Attributes & Configuration State (invokes mfs_anl, must be before ext_anl) mfs_state,mfs_parsed_idx,intel_cfg_hash_mfs,mfs_info,pch_init_final,vol_ftbl_id,config_rec_size,vol_ftbl_pl = get_mfs_anl(mfs_state,mfs_parsed_idx,intel_cfg_hash_mfs,mfs_info,pch_init_final) - + # Get CSE EFS File System Attributes & Configuration State (must be after mfs_anl) if efs_found : efs_init = efs_anl(out_dir, efs_start, efs_start + efs_size, vol_ftbl_id, vol_ftbl_pl) - + # Detect CSE Firmware Attributes (must be after mfs_anl) cpd_offset,cpd_mod_attr,cpd_ext_attr,vcn,ext12_info,ext_print,ext_pname,ext50_info,ext_phval,ext_dnx_val,oem_config,oem_signed,cpd_mn2_info, \ ext_iunit_val,ext15_info,pch_init_final,gmf_blob_info,fwi_iup_hashes,gsc_info \ = ext_anl(reading, '$MN2', start_man_match, file_end, [variant,major,minor,hotfix,build,year,month,variant_p], None, [mfs_parsed_idx,intel_cfg_hash_mfs], [pch_init_final,config_rec_size,vol_ftbl_id,vol_ftbl_pl]) - + # MFS missing, determine state via FTPR > fitc.cfg, FITC, MFSB or EFS (must be after mfs_anl, efs_anl & ext_anl) if mfs_state != 'Initialized' and efs_init : mfs_state = 'Initialized' elif mfs_state == 'Unconfigured' and (oem_config or fitc_found or mfsb_found or cdmd_found) : mfs_state = 'Configured' - + fw_0C_sku0,fw_0C_sku1,fw_0C_lbg,fw_0C_sku2 = ext12_info # Get SKU Capabilities, SKU Type, HEDT Support, SKU Platform - + db_sku_chk,sku,sku_stp,sku_pdm = get_cse_db(variant) # Get CSE SKU info from DB - + # Set PCH/SoC Stepping, if not found at DB if sku_stp == 'Unknown' and pch_init_final : sku_stp = pch_init_final[-1][1] - + # Set Recovery or Operational Region Type if not rgn_exist : # Intel releases OPR as partition ($CPD) but REC as region ($FPT) @@ -13370,7 +13376,7 @@ def mass_scan(f_path) : elif ext_pname == 'OPR' : fw_type = 'Operational' # Intel POR for OPR elif not ifwi_exist and not sps_opr_found : fw_type = 'Recovery' # Intel POR for REC ($FPT + FTPR) - + # Set SKU Type via Extension 12 or 15 (CSSPS logic) if not fw_0C_sku0 and ext15_info[0] == 0 : # CSE_Ext_0C > FWSKUCaps and CSE_Ext_0F_R2 > ARBSVN cannot be empty/0 sku = 'Unknown' @@ -13384,191 +13390,191 @@ def mass_scan(f_path) : else : # SKU in non-empty CSE_Ext_0F_R2, fallback if CSE_Ext_0C not used and not IGN sku = ext15_info[2][0] sku_init_db = ext15_info[2][1] - + sku_plat = ext50_info[1] sku_db = '%s_%s' % (sku_plat, sku_init_db) if sku_stp != 'Unknown' : sku_db += '_%s' % sku_stp - + if sku_plat in cssps_platform : platform = cssps_platform[sku_plat] # Chipset Platform via SKU Platform elif pch_init_final : platform = pch_init_final[0][0] # Chipset Platform via MFS Intel PCH Initialization Table else : platform = 'Unknown' # Chipset Platform is Unknown - + # Parse all detected stitched PMC firmware pmc_all_anl = pmc_parse(pmc_all_init, pmc_all_anl) - + # Parse all detected stitched PCHC firmware pchc_all_anl = pchc_parse(pchc_all_init, pchc_all_anl) - + # Parse all detected stitched PHY firmware phy_all_anl = phy_parse(phy_all_init, phy_all_anl) - + if major in [4,1] : - + if platform == 'Unknown' : platform = 'SPT-H' # Sunrise Point - + if sku_plat not in ['XX','BA','HA','PU'] : is_unsupported = True - + elif major == 5 : - + if platform == 'Unknown' : platform = 'CNP-H' # Cannon Point - + if sku_plat not in ['XX','ME'] : is_unsupported = True - + else : - + is_unsupported = True - + elif variant == 'GSC' : # Graphics System Controller - + # Firmware Unpacking for all GSC if param.cse_unpack : cse_unpack(variant, fpt_part_all, bpdt_part_all, file_end, fpt_start if rgn_exist else -1, fpt_chk_fail, cse_lt_chk_fail, cse_red_info, fdv_status, reading_msg, orom_hdr_all) continue # Next input file - + # Get GSC MFS File System Attributes & Configuration State (invokes mfs_anl, must be before ext_anl) mfs_state,mfs_parsed_idx,intel_cfg_hash_mfs,mfs_info,pch_init_final,vol_ftbl_id,config_rec_size,vol_ftbl_pl \ = get_mfs_anl(mfs_state,mfs_parsed_idx,intel_cfg_hash_mfs,mfs_info,pch_init_final) - + # Get GSC EFS File System Attributes & Configuration State (must be after mfs_anl) if efs_found : efs_init = efs_anl(out_dir, efs_start, efs_start + efs_size, vol_ftbl_id, vol_ftbl_pl) - + # Get GSC Firmware Attributes (must be after mfs_anl) cpd_offset,cpd_mod_attr,cpd_ext_attr,vcn,ext12_info,ext_print,ext_pname,ext50_info,ext_phval,ext_dnx_val,oem_config,oem_signed,cpd_mn2_info, \ ext_iunit_val,ext15_info,pch_init_final,gmf_blob_info,fwi_iup_hashes,_ \ = ext_anl(reading, '$MN2', start_man_match, file_end, [variant,major,minor,hotfix,build,year,month,variant_p], None, [mfs_parsed_idx,intel_cfg_hash_mfs], [pch_init_final,config_rec_size,vol_ftbl_id,vol_ftbl_pl]) - + if rbep_found : _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,gsc_info \ = ext_anl(reading, '$CPD', rbep_start, file_end, [variant,major,minor,hotfix,build,year,month,variant_p], None, [mfs_parsed_idx,intel_cfg_hash_mfs], [pch_init_final,config_rec_size,vol_ftbl_id,vol_ftbl_pl]) - + # MFS missing, determine state via FTPR > fitc.cfg, FITC, MFSB or EFS (must be after mfs_anl, efs_anl & ext_anl) if mfs_state != 'Initialized' and efs_init : mfs_state = 'Initialized' elif mfs_state == 'Unconfigured' and (oem_config or fitc_found or mfsb_found or cdmd_found) : mfs_state = 'Configured' - + # Get GSC Project Info if gsc_info : sku = '%s SOC%d %0.4d' % (gsc_info[0], gsc_info[1], gsc_info[2]) sku_stp = 'A' if gsc_info[0] == 'DG01' else gsc_stp_dict[gsc_info[2] // 1000] sku_db = '%s_SOC%s_%s_%0.4d' % (gsc_info[0], gsc_info[1], sku_stp, gsc_info[2]) - + # Parse all detected stitched PMC firmware pmc_all_anl = pmc_parse(pmc_all_init, pmc_all_anl) - + # Parse all detected stitched PHY firmware phy_all_anl = phy_parse(phy_all_init, phy_all_anl) - + # TODO: GSC information should be taken by RBEP, not FTPR - + if major == 100 : - + if minor == 0 and not gsc_info and not pch_init_final : sku,sku_db,platform = ['DG01'] * 3 # Dedicated Xe Graphics 1 - + elif major == 101 : - + if minor == 0 and not gsc_info and not pch_init_final : sku,sku_db,platform = ['DG02'] * 3 # Dedicated Xe Graphics 2 - + is_unsupported = True - + else : - + is_unsupported = True - + elif variant.startswith('OROM') : # Graphics System Controller Option ROM - + # Firmware Unpacking for all OROM if param.cse_unpack : cse_unpack('OROM', fpt_part_all, bpdt_part_all, file_end, fpt_start if rgn_exist else -1, fpt_chk_fail, cse_lt_chk_fail, cse_red_info, fdv_status, reading_msg, orom_hdr_all) continue # Next input file - + # Detect IUP Firmware Attributes (must be after mfs_anl) cpd_offset,cpd_mod_attr,cpd_ext_attr,vcn,ext12_info,ext_print,ext_pname,ext50_info,ext_phval,ext_dnx_val,oem_config,oem_signed,cpd_mn2_info, \ ext_iunit_val,ext15_info,pch_init_final,gmf_blob_info,fwi_iup_hashes,gsc_info \ = ext_anl(reading, '$MN2', start_man_match, file_end, ['OROM',-1,-1,-1,-1,-1,-1,'OROM'], None, [[],''], [[],-1,-1,-1]) - + platform = variant[4:] if len(variant) > 4 else 'Unknown' - + orom_all_size = max(orom_pci_size, orom_cpd_size) # Get total OROM IUP Size ($CPD Size may exceed PCIR) eng_fw_end = orom_all_size + 0x1000 - (orom_all_size % 0x1000) # Calculate 4K aligned OROM IUP Size - + if orom_match and orom_match[0].start() == 0x0 : # OROM 1st Image POR Start Offset is 0x0 eng_size_text = chk_iup_size(eng_size_text, file_end, eng_fw_end, variant_p, platform) # Check OROM IUP Size - + if not platform.startswith('DG1'): is_unsupported = True - + elif variant.startswith('PMC') : # Power Management Controller - + # Firmware Unpacking for all PMC if param.cse_unpack : cse_unpack('PMC', fpt_part_all, bpdt_part_all, file_end, fpt_start if rgn_exist else -1, fpt_chk_fail, cse_lt_chk_fail, cse_red_info, fdv_status, reading_msg, orom_hdr_all) continue # Next input file - + # Detect IUP Firmware Attributes cpd_offset,cpd_mod_attr,cpd_ext_attr,vcn,ext12_info,ext_print,ext_pname,ext50_info,ext_phval,ext_dnx_val,oem_config,oem_signed,cpd_mn2_info, \ ext_iunit_val,ext15_info,pch_init_final,gmf_blob_info,fwi_iup_hashes,gsc_info \ = ext_anl(reading, '$CPD', 0, file_end, ['PMC',-1,-1,-1,-1,-1,-1,'PMC'], None, [[],''], [[],-1,-1,-1]) - + pmc_fw_ver,pmc_pch_gen,sku,sku_stp,pmc_fw_rel,release,rel_db,platform,date,svn,pvbit, \ mn2_meu_ver,pmc_name_db,is_unsupported = pmc_anl(cpd_mn2_info) - + if sku_stp != 'Unknown' : sku_stp = sku_stp[0] sku_db = '%s_%s' % (sku, sku_stp) - + eng_fw_end = cpd_size_calc(reading, 0, 0x1000) # Get PMC firmware size - + eng_size_text = chk_iup_size(eng_size_text, file_end, eng_fw_end, variant_p, platform) # Check PMC firmware size - + elif variant.startswith('PCHC') : # Platform Controller Hub Configuration - + # Firmware Unpacking for all PCHC if param.cse_unpack : cse_unpack('PCHC', fpt_part_all, bpdt_part_all, file_end, fpt_start if rgn_exist else -1, fpt_chk_fail, cse_lt_chk_fail, cse_red_info, fdv_status, reading_msg, orom_hdr_all) continue # Next input file - + # Detect IUP Firmware Attributes cpd_offset,cpd_mod_attr,cpd_ext_attr,vcn,ext12_info,ext_print,ext_pname,ext50_info,ext_phval,ext_dnx_val,oem_config,oem_signed,cpd_mn2_info, \ ext_iunit_val,ext15_info,pch_init_final,gmf_blob_info,fwi_iup_hashes,gsc_info \ = ext_anl(reading, '$CPD', 0, file_end, ['PCHC',-1,-1,-1,-1,-1,-1,'PCHC'], None, [[],''], [[],-1,-1,-1]) - + pchc_fw_ver,pchc_fw_major,pchc_fw_minor,pchc_fw_rel,release,rel_db,platform,date,svn,pvbit, \ mn2_meu_ver,pchc_name_db,is_unsupported = pchc_anl(cpd_mn2_info) - + eng_fw_end = cpd_size_calc(reading, 0, 0x1000) # Get PCHC firmware size - + eng_size_text = chk_iup_size(eng_size_text, file_end, eng_fw_end, variant_p, platform) # Check PCHC firmware size - + elif variant.startswith('PHY') : # USB Type C Physical - + # Firmware Unpacking for all PHY if param.cse_unpack : cse_unpack('PHY', fpt_part_all, bpdt_part_all, file_end, fpt_start if rgn_exist else -1, fpt_chk_fail, cse_lt_chk_fail, cse_red_info, fdv_status, reading_msg, orom_hdr_all) continue # Next input file - + # Detect IUP Firmware Attributes cpd_offset,cpd_mod_attr,cpd_ext_attr,vcn,ext12_info,ext_print,ext_pname,ext50_info,ext_phval,ext_dnx_val,oem_config,oem_signed,cpd_mn2_info, \ ext_iunit_val,ext15_info,pch_init_final,gmf_blob_info,fwi_iup_hashes,gsc_info \ = ext_anl(reading, '$CPD', 0, file_end, ['PHY',-1,-1,-1,-1,-1,-1,'PHY'], None, [[],''], [[],-1,-1,-1]) - + phy_fw_ver,sku,release,rel_db,platform,date,svn,pvbit,mn2_meu_ver,phy_fw_rel,phy_name_db,is_unsupported = phy_anl(cpd_mn2_info) - + eng_fw_end = cpd_size_calc(reading, 0, 0x1000) # Get PHY firmware size - + eng_size_text = chk_iup_size(eng_size_text, file_end, eng_fw_end, variant_p, platform) # Check PHY firmware size - + else: - + is_unsupported = True - + # Create Firmware Type DB entry if variant.startswith(('PMC','PCHC','PHY','OROM')) : fw_type = 'Independent' - + if fw_type == 'Extracted' : type_db = 'EXTR' elif fw_type == 'Stock' : type_db = 'RGN' elif fw_type == 'Update' : type_db = 'UPD' @@ -13579,7 +13585,7 @@ def mass_scan(f_path) : elif fw_type == 'Independent' and variant.startswith('PCHC') : type_db = 'PCHC' elif fw_type == 'Independent' and variant.startswith('OROM') : type_db = 'OROM' elif fw_type == 'Unknown' : type_db = 'UNK' - + # Check for CSME 12+ FWUpdate Support/Compatibility fwu_iup_check = (variant,major >= 12,type_db,sku_db[:3]) == ('CSME',True,'EXTR','COR') if fwu_iup_check and (uncharted_match or not fwu_iup_exist) : fwu_iup_result = 'Impossible' @@ -13593,13 +13599,13 @@ def mass_scan(f_path) : fwu_iup_result = ['No','Yes'][int(pmcp_fwu_found and pchc_fwu_found and phy_fwu_found)] else : fwu_iup_result = ['No','Yes'][int(pmcp_fwu_found and pchc_fwu_found and phy_fwu_found)] - + # Check for CSE Extension 15 R2 NVM Compatibility if ext15_info[3] not in ['', 'Undefined'] : nvm_db = '_%s' % ext15_info[3] - + # Format Firmware Version Tag based on Variant fw_ver = get_fw_ver(variant, major, minor, hotfix, build) - + # Create Firmware File Name if variant.endswith('SPS') and sku == 'NaN' : # (CS)SPS w/o SKU name_fw = '%s_%s_%s' % (fw_ver, rel_db, type_db) @@ -13621,21 +13627,21 @@ def mass_scan(f_path) : name_fw = '%s_%s_%s' % (platform, fw_ver, rel_db) else : # (CS)ME, (CS)TXE, (CS)SPS, GSC name_fw = '%s_%s%s_%s_%s' % (fw_ver, sku_db, nvm_db, rel_db, type_db) - + # CSME 12+ Corporate Extracted must include IUP existence state as well for FWUpdate tool compatibility & usage if fwu_iup_check : name_fw += ['-N','-Y'][int(fwu_iup_exist)] - + # Create Firmware Database Name/Entry name_db = name_fw + '_' + rsa_sig_hash - + # Append part of RSA Signature Hash to avoid duplicate Names name_fw += '_%s' % rsa_sig_hash[:8] - + # Store Firmware DB entry to file if param.db_print_new : with open(os.path.join(out_dir, 'MEA_PDB.txt'), 'a', encoding = 'utf-8') as db_file : db_file.write(name_db + '\n') continue # Next input file - + # Search Database for Firmware if not is_unsupported and not variant.startswith(('PMC','PCHC','PHY')) : # Not PMC, PCHC and PHY for line in mea_db_lines : @@ -13652,113 +13658,113 @@ def mass_scan(f_path) : fw_in_db_found = True # REC w/o $FPT are not POR for CSSPS, notify only if REC w/ $FPT does not exist else : can_search_db = False # Do not search DB for Unsupported and IUP - + if can_search_db and not rgn_over_extr_found and not fw_in_db_found and not sps_extr_ignore : note_new_fw(variant_p) - + # Rename input file based on the DB structured name if param.give_db_name : old_file_name = file_in new_file_name = os.path.join(os.path.dirname(file_in), name_fw + '.bin') - + if not os.path.isfile(new_file_name) : os.replace(old_file_name, new_file_name) elif os.path.basename(file_in) == name_fw + '.bin' : pass else : print(col_r + 'Error: A file with the same name already exists!' + col_e) - + continue # Next input file - + # Print Firmware Info msg_pt = ext_table(['Field', 'Value'], False, 1) msg_pt.title = col_c + '%s (%d/%d)' % (os.path.basename(file_in)[:45], cur_count, in_count) + col_e - + msg_pt.add_row(['Family', variant_p]) msg_pt.add_row(['Version', fw_ver]) msg_pt.add_row(['Release', release + ', Engineering' if build >= 7000 else release]) msg_pt.add_row(['Type', fw_type]) - + if (variant == 'CSTXE' and 'Unknown' not in sku) or (variant,sku) == ('SPS','NaN') \ or variant.startswith(('PMCAPL','PMCBXT','PMCGLK','PCHC','PMCDG','OROM')) : pass else : msg_pt.add_row(['SKU', sku]) - + if variant.startswith(('CS','PMC','GSC')) and not variant.startswith('PMCDG') : if pch_init_final : msg_pt.add_row(['Chipset', pch_init_final[-1][0]]) elif sku_stp == 'Unknown' : msg_pt.add_row(['Chipset', 'Unknown']) else : msg_pt.add_row(['Chipset Stepping', ', '.join(map(str, list(sku_stp)))]) - + if nvm_db : msg_pt.add_row(['NVM Compatibility', ext15_info[3]]) - + if (variant == 'ME' and major >= 8) or variant.startswith(('TXE', 'CS', 'GSC', 'PMC', 'PCHC', 'PHY', 'OROM')): msg_pt.add_row(['TCB Security Version Number', svn]) - + if (variant == 'CSME' and major >= 12) or variant.startswith(('CSTXE', 'CSSPS', 'GSC', 'PMC', 'PCHC', 'PHY', 'OROM')): msg_pt.add_row(['ARB Security Version Number', ext15_info[0]]) - + if (variant == 'ME' and major >= 8) or variant.startswith(('TXE', 'CS', 'GSC', 'PMC', 'PCHC', 'PHY', 'OROM')): msg_pt.add_row(['Version Control Number', vcn]) - + if pvbit is not None : msg_pt.add_row(['Production Ready', ['No','Yes'][pvbit]]) # Always check against None - + if [variant,major] == ['CSME',11] : if pdm_status != 'NaN' : msg_pt.add_row(['Power Down Mitigation', pdm_status]) msg_pt.add_row(['Workstation Support', ['No','Yes'][fw_0C_lbg]]) - + if variant == 'ME' and major == 7 : msg_pt.add_row(['Patsburg Support', ['No','Yes'][is_patsburg]]) - + if variant in ('CSME','CSTXE','CSSPS','TXE','GSC') : msg_pt.add_row(['OEM Configuration', ['No','Yes'][int(oem_signed or oemp_found or utok_found)]]) - + if variant == 'CSME' and major >= 12 : msg_pt.add_row(['FWUpdate Support', fwu_iup_result]) - + msg_pt.add_row(['Date', date]) if variant in ('CSME','CSTXE','CSSPS','GSC') : msg_pt.add_row(['File System State', mfs_state]) - + if rgn_exist or cse_lt_struct or variant.startswith(('PMC','PCHC','PHY','OROM')) : if (variant,major,release) == ('ME',6,'ROM-Bypass') : msg_pt.add_row(['Size', 'Unknown']) elif (variant,fd_devexp_rgn_exist) == ('CSTXE',True) : pass else : msg_pt.add_row(['Size', '0x%X' % eng_fw_end]) - + if fitc_ver_found : msg_pt.add_row(['Flash Image Tool', get_fw_ver(variant,fitc_major,fitc_minor,fitc_hotfix,fitc_build)]) - + if mn2_meu_ver != '0.0.0.0000' : msg_pt.add_row(['Manifest Extension Utility', mn2_meu_ver]) - + if (variant,major) == ('ME',7) : msg_pt.add_row(['Downgrade Blacklist 7.0', me7_blist_1]) msg_pt.add_row(['Downgrade Blacklist 7.1', me7_blist_2]) - + if platform != 'NaN' : msg_pt.add_row(['Chipset Support', platform]) - + print('\n%s' % msg_pt) - + msg_pt.title = variant_p_fw msg_pt.add_row(['MEA Database Name', name_db.rsplit('_', 1)[0]]) msg_pt.add_row(['MEA Support Status', ['Yes','No'][is_unsupported]]) msg_pt.add_row(['RSA Signature Hash', rsa_sig_hash]) - + if param.check : # Debug/Research if mfs_state != 'Unconfigured' or oem_signed or oemp_found or utok_found or fitc_size or cdmd_size : input('\nCFG_PRESENT!\n') if pmc_all_init or pchc_all_init or phy_all_init : input('\nIUP_PRESENT!\n') - + if param.write_html: with open(os.path.join(out_dir, f'{os.path.basename(file_in)}.html'), 'w', encoding='utf-8') as ho: ho.write('\n
\n%s' % pt_html(msg_pt)) - + if param.write_json: mea_json[file_in] = {**mea_json[file_in], **pt_json(msg_pt)} - + for pmc_idx, pmc_val in enumerate(pmc_all_anl, 1): msg_pmc_pt = ext_table(['Field', 'Value'], False, 1) msg_pmc_pt.title = 'Power Management Controller' - + pmc_fw_ver,pmc_pch_gen,pmc_pch_sku,pmc_pch_rev,pmc_fw_rel,pmc_mn2_signed,pmc_mn2_signed_db, \ pmc_platform,pmc_date,pmc_svn,pmc_pvbit,pmc_meu_ver,pmc_vcn,pmc_mn2_ver,pmc_ext15_info, \ pmc_size,pmc_name_db,pmc_unsupported = pmc_val - + is_unsupported |= pmc_unsupported - + msg_pmc_pt.add_row(['Family', 'PMC']) msg_pmc_pt.add_row(['Version', pmc_fw_ver]) msg_pmc_pt.add_row(['Release', pmc_mn2_signed + ', Engineering' if pmc_fw_rel >= 7000 else pmc_mn2_signed]) @@ -13774,33 +13780,33 @@ def mass_scan(f_path) : msg_pmc_pt.add_row(['Size', '0x%X' % pmc_size]) if pmc_meu_ver != '0.0.0.0000' : msg_pmc_pt.add_row(['Manifest Extension Utility', pmc_meu_ver]) msg_pmc_pt.add_row(['Chipset Support', pmc_platform]) - + print(msg_pmc_pt) - + msg_pmc_pt.add_row(['MEA Database Name', pmc_name_db.rsplit('_', 1)[0]]) msg_pmc_pt.add_row(['MEA Support Status', ['Yes','No'][pmc_unsupported]]) msg_pmc_pt.add_row(['RSA Signature Hash', pmc_mn2_ver[6]]) - + if param.write_html: with open(os.path.join(out_dir, f'{os.path.basename(file_in)}.html'), 'a', encoding='utf-8') as ho: ho.write('\n
\n%s' % pt_html(msg_pmc_pt)) - + if param.write_json: if msg_pmc_pt.title not in mea_json[file_in]: mea_json[file_in][msg_pmc_pt.title] = [] - + mea_json[file_in][msg_pmc_pt.title].extend(list(pt_json(msg_pmc_pt).values())[0]) - + for pchc_idx, pchc_val in enumerate(pchc_all_anl, 1): msg_pchc_pt = ext_table(['Field', 'Value'], False, 1) msg_pchc_pt.title = 'Platform Controller Hub Configuration' - + pchc_fw_ver,pchc_fw_major,pchc_fw_minor,pchc_fw_rel,pchc_mn2_signed,pchc_mn2_signed_db, \ pchc_platform,pchc_date,pchc_svn,pchc_pvbit,pchc_meu_ver,pchc_vcn,pchc_mn2_ver,pchc_ext15_info, \ pchc_size,pchc_name_db,pchc_unsupported = pchc_val - + is_unsupported |= pchc_unsupported - + msg_pchc_pt.add_row(['Family', 'PCHC']) msg_pchc_pt.add_row(['Version', pchc_fw_ver]) msg_pchc_pt.add_row(['Release', pchc_mn2_signed + ', Engineering' if pchc_fw_rel >= 7000 else pchc_mn2_signed]) @@ -13813,32 +13819,32 @@ def mass_scan(f_path) : msg_pchc_pt.add_row(['Size', '0x%X' % pchc_size]) if pchc_meu_ver != '0.0.0.0000' : msg_pchc_pt.add_row(['Manifest Extension Utility', pchc_meu_ver]) msg_pchc_pt.add_row(['Chipset Support', pchc_platform]) - + print(msg_pchc_pt) - + msg_pchc_pt.add_row(['MEA Database Name', pchc_name_db.rsplit('_', 1)[0]]) msg_pchc_pt.add_row(['MEA Support Status', ['Yes','No'][pchc_unsupported]]) msg_pchc_pt.add_row(['RSA Signature Hash', pchc_mn2_ver[6]]) - + if param.write_html: with open(os.path.join(out_dir, f'{os.path.basename(file_in)}.html'), 'a', encoding='utf-8') as ho: ho.write('\n
\n%s' % pt_html(msg_pchc_pt)) - + if param.write_json: if msg_pchc_pt.title not in mea_json[file_in]: mea_json[file_in][msg_pchc_pt.title] = [] - + mea_json[file_in][msg_pchc_pt.title].extend(list(pt_json(msg_pchc_pt).values())[0]) - + for phy_idx, phy_val in enumerate(phy_all_anl, 1): msg_phy_pt = ext_table(['Field', 'Value'], False, 1) msg_phy_pt.title = 'USB Type C Physical' - + phy_fw_ver,phy_sku,phy_mn2_signed,phy_mn2_signed_db,phy_platform,phy_date,phy_svn,phy_pvbit,phy_meu_ver, \ phy_fw_rel,phy_vcn,phy_mn2_ver,phy_ext15_info,phy_size,phy_name_db,phy_unsupported = phy_val - + is_unsupported |= phy_unsupported - + msg_phy_pt.add_row(['Family', 'PHY']) msg_phy_pt.add_row(['Version', phy_fw_ver]) msg_phy_pt.add_row(['Release', phy_mn2_signed + ', Engineering' if phy_fw_rel >= 7000 else phy_mn2_signed]) @@ -13852,65 +13858,65 @@ def mass_scan(f_path) : msg_phy_pt.add_row(['Size', '0x%X' % phy_size]) if phy_meu_ver != '0.0.0.0000' : msg_phy_pt.add_row(['Manifest Extension Utility', phy_meu_ver]) msg_phy_pt.add_row(['Chipset Support', phy_platform]) - + print(msg_phy_pt) - + msg_phy_pt.add_row(['MEA Database Name', phy_name_db.rsplit('_', 1)[0]]) msg_phy_pt.add_row(['MEA Support Status', ['Yes','No'][phy_unsupported]]) msg_phy_pt.add_row(['RSA Signature Hash', phy_mn2_ver[6]]) - + if param.write_html: with open(os.path.join(out_dir, f'{os.path.basename(file_in)}.html'), 'a', encoding='utf-8') as ho: ho.write('\n
\n%s' % pt_html(msg_phy_pt)) - + if param.write_json: if msg_phy_pt.title not in mea_json[file_in]: mea_json[file_in][msg_phy_pt.title] = [] - + mea_json[file_in][msg_phy_pt.title].extend(list(pt_json(msg_phy_pt).values())[0]) - + # Print Messages which must be at the end of analysis if is_unsupported : err_stor.append([col_r + 'Error: Unsupported Intel Engine, Graphics and/or Independent firmware!' + col_e, True]) - + if eng_size_text != ['', False] : warn_stor.append(['%s' % eng_size_text[0], eng_size_text[1]]) - + if fwu_iup_result == 'Impossible' and uncharted_match : fwu_iup_msg = (uncharted_match.start(),p_end_last_back,p_end_last_back + uncharted_match.start()) warn_stor.append([col_m + 'Warning: Remove 0x%X padding from 0x%X - 0x%X for FWUpdate Support!' % fwu_iup_msg + col_e, False]) - + if variant == 'CSME' and major >= 16 and fwu_iup_result == 'Impossible' and file_has_align : warn_stor.append([col_m + 'Warning: Remove 0x%X padding from image end for FWUpdate Support!' % file_has_align + col_e, False]) - + if fpt_count > 1 : note_stor.append([col_y + 'Note: Multiple (%d) Intel Flash Partition Tables detected!' % fpt_count + col_e, False]) - + if fd_count > 1 : note_stor.append([col_y + 'Note: Multiple (%d) Intel Flash Descriptors detected!' % fd_count + col_e, False]) - + msg_all = err_stor + warn_stor + note_stor - + for msg_idx in range(len(msg_all)) : msg_tuple = tuple(msg_all[msg_idx]) - + if msg_tuple not in msg_set: msg_set.add(msg_tuple) - + print('\n' + msg_all[msg_idx][0]) - + if param.write_html: with open(os.path.join(out_dir, f'{os.path.basename(file_in)}.html'), 'a', encoding='utf-8') as ho: ho.write('\n

%s

' % ansi_escape.sub('', str(msg_all[msg_idx][0]))) - + if param.write_json: msg_entries.append(ansi_escape.sub('', str(msg_all[msg_idx][0]))) - + if param.write_json: mea_json[file_in] = {**mea_json[file_in], **{'Messages': msg_entries}} - + with open(os.path.join(out_dir, f'{os.path.basename(file_in)}.json'), 'w', encoding='utf-8') as jo: json.dump(mea_json, jo, indent=4) - + # Close input and copy it in case of messages copy_on_msg(msg_all) - + # Show MEA help screen only once if param.help_scr : mea_exit(0)