#!/usr/bin/env python3 #coding=utf-8 """ ME Analyzer Intel Engine & Graphics Firmware Analysis Tool Copyright (C) 2014-2026 Plato Mavropoulos """ title = 'ME Analyzer v1.311.0' import sys # Detect Python version sys_py = sys.version_info if sys_py < (3,7) : sys.stdout.write('%s\n\nError: Python >= 3.7 required, not %d.%d!\n' % (title, sys_py[0], sys_py[1])) if '-exit' not in sys.argv : (raw_input if sys_py[0] <= 2 else input)('\nPress enter to exit') # pylint: disable=E0602 sys.exit(1) # Detect OS platform sys_os = sys.platform if sys_os == 'win32' : cl_wipe = 'cls' sys.stdout.reconfigure(encoding='utf-8') # Fix Windows Unicode console redirection elif sys_os.startswith('linux') or sys_os == 'darwin' or sys_os.find('bsd') != -1 : cl_wipe = 'clear' else : print('%s\n\nError: Unsupported platform "%s"!\n' % (title, sys_os)) if '-exit' not in sys.argv : input('Press enter to exit') sys.exit(1) import os import re import lzma import json import struct import ctypes import shutil import hashlib import inspect import threading import itertools import traceback import subprocess import urllib.request import importlib.util # Check code dependency installation for depend in ['colorama','crccheck','pltable'] : if not importlib.util.find_spec(depend) : print('%s\n\nError: Dependency "%s" is missing!\n Install via "pip3 install %s"\n' % (title, depend, depend)) if '-exit' not in sys.argv : input('Press enter to exit') sys.exit(1) import pltable import colorama import crccheck # Initialize and setup Colorama colorama.init() col_r = colorama.Fore.RED + colorama.Style.BRIGHT col_c = colorama.Fore.CYAN + colorama.Style.BRIGHT col_b = colorama.Fore.BLUE + colorama.Style.BRIGHT col_g = colorama.Fore.GREEN + colorama.Style.BRIGHT col_y = colorama.Fore.YELLOW + colorama.Style.BRIGHT col_m = colorama.Fore.MAGENTA + colorama.Style.BRIGHT col_e = colorama.Fore.RESET + colorama.Style.RESET_ALL # Set ctypes Structure types char = ctypes.c_char uint8_t = ctypes.c_ubyte uint16_t = ctypes.c_ushort uint32_t = ctypes.c_uint uint64_t = ctypes.c_uint64 # Print MEA Help screen def mea_help() : print( '\nUsage: MEA [FilePath] {Options}\n\n{Options}\n\n' '-? : Displays help & usage screen\n' '-skip : Skips welcome & options screen\n' '-exit : Skips Press enter to exit prompt\n' '-mass : Scans all files of a given directory\n' '-pdb : Writes unique input file DB name to file\n' '-dbn : Renames input file based on unique DB name\n' '-duc : Disables automatic check for MEA & DB updates\n' '-dcm : Disables automatic input file copy on messages\n' '-out : Defines output directory for all MEA operations\n' '-dfpt : Shows FPT, BPDT, OROM & CSE/GSC Layout Table info\n' '-unp86 : Unpacks all supported CSE, GSC and/or IUP firmware\n' '-bug86 : Enables pause on error during CSE/GSC/IUP unpacking\n' '-ver86 : Enables verbose output during CSE/GSC/IUP unpacking\n' '-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-2026 Plato Mavropoulos' + col_e) if getattr(sys, 'frozen', False) : print(col_c + '\nRunning in frozen state!' + col_e) mea_exit(0) # Process MEA Parameters 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 self.cse_verbose = False self.cse_pause = False self.fpt_disp = False self.db_print_new = False self.give_db_name = False self.mass_scan = False self.skip_pause = False self.write_html = False self.write_json = False self.mfs_ftbl = False self.mfs_rcfg = False self.check = False self.bypass = False 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 if '-ver86' in source : self.cse_verbose = True if '-bug86' in source : self.cse_pause = True if '-pdb' in source : self.db_print_new = True if '-dbn' in source : self.give_db_name = True if '-mass' in source : self.mass_scan = True if '-dfpt' in source : self.fpt_disp = True if '-exit' in source : self.skip_pause = True if '-html' in source : self.write_html = True if '-json' in source : self.write_json = True if '-ftbl' in source : self.mfs_ftbl = True # Hidden if '-rcfg' in source : self.mfs_rcfg = True # Hidden if '-chk' in source : self.check = True # Hidden 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 # https://stackoverflow.com/a/65447493 by Shail-Shouryya class Thread_With_Result(threading.Thread) : def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None) : self.result = None if kwargs is None : kwargs = {} 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 _fields_ = [ ('ROMB_Instr_0', uint32_t), # 0x00 ('ROMB_Instr_1', uint32_t), # 0x04 ('ROMB_Instr_2', uint32_t), # 0x08 ('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) _pack_ = 1 _fields_ = [ ('Tag', char*4), # 0x00 ('NumPartitions', uint32_t), # 0x04 ('HeaderVersion', uint8_t), # 0x08 ('EntryVersion', uint8_t), # 0x09 ('HeaderLength', uint8_t), # 0x0A ('HeaderChecksum', uint8_t), # 0x0B ('FlashCycleLife', uint16_t), # 0x0C TicksToAdd at CSE ('FlashCycleLimit', uint16_t), # 0x0E TokensToAdd at CSE ('UMASize', uint32_t), # 0x10 Reserved at CSE ('Flags', uint32_t), # 0x14 FlashLayout at CSE (FLASH_LAYOUT_TYPES) ('FitMajor', uint16_t), # 0x18 ('FitMinor', uint16_t), # 0x1A ('FitHotfix', uint16_t), # 0x1C ('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]) pt.add_row(['Header Version', '0x%X' % self.HeaderVersion]) pt.add_row(['Entry Version', '0x%X' % self.EntryVersion]) pt.add_row(['Header Size', '0x%X' % self.HeaderLength]) pt.add_row(['Header Checksum', '0x%X' % self.HeaderChecksum]) pt.add_row(['Ticks To Add', '0x%X' % self.FlashCycleLife]) pt.add_row(['Tokens To Add', '0x%X' % self.FlashCycleLimit]) 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_ = [ ('Tag', char*4), # 0x00 ('NumPartitions', uint32_t), # 0x04 ('HeaderVersion', uint8_t), # 0x08 21 ('EntryVersion', uint8_t), # 0x09 ('HeaderLength', uint8_t), # 0x0A ('Flags', uint8_t), # 0x0B 0 $FPT Redundancy, 1-7 Reserved ('TicksToAdd', uint16_t), # 0x0C ('TokensToAdd', uint16_t), # 0x0E ('SPSFlags', uint32_t), # 0x10 Unknown/Unused ('HeaderChecksum', uint32_t), # 0x14 CRC-32 (Header + Entries, Checksum = 0) ('FitMajor', uint16_t), # 0x18 ('FitMinor', uint16_t), # 0x1A ('FitHotfix', uint16_t), # 0x1C ('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]) pt.add_row(['Header Version', '0x%X' % self.HeaderVersion]) pt.add_row(['Entry Version', '0x%X' % self.EntryVersion]) pt.add_row(['Header Size', '0x%X' % self.HeaderLength]) pt.add_row(['FPT Redundancy', ['No','Yes'][f1]]) pt.add_row(['Flags Reserved', '0x%X' % f2]) pt.add_row(['Ticks To Add', '0x%X' % self.TicksToAdd]) pt.add_row(['Tokens To Add', '0x%X' % self.TokensToAdd]) 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): _fields_ = [ ('Redundancy', uint8_t, 1), ('Reserved', uint8_t, 7), ] class FPT_Header_21_GetFlags(ctypes.Union): _fields_ = [ ('b', FPT_Header_21_Flags), ('asbytes', uint8_t) ] class FPT_Entry(ctypes.LittleEndianStructure) : # (FPT_ENTRY) _pack_ = 1 _fields_ = [ ('Name', char*4), # 0x00 ('Owner', char*4), # 0x04 Reserved at CSE ('Offset', uint32_t), # 0x08 ('Size', uint32_t), # 0x0C ('StartTokens', uint32_t), # 0x10 Reserved at CSE ('MaxTokens', uint32_t), # 0x14 Reserved at CSE ('ScratchSectors', uint32_t), # 0x18 Reserved at CSE ('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')]) pt.add_row(['Offset', '0x%X' % self.Offset]) pt.add_row(['Reserved 1', '0x%X' % self.StartTokens]) pt.add_row(['Reserved 2', '0x%X' % self.MaxTokens]) pt.add_row(['Reserved 3', '0x%X' % self.ScratchSectors]) pt.add_row(['Type', ['Code','Data'][f1]]) pt.add_row(['Copy To DRAM Cache', ['No','Yes'][f2]]) pt.add_row(['Reserved 4', '0x%X' % f3]) pt.add_row(['Built With Length 1', '0x%X' % f4]) 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 class FPT_Entry_Flags(ctypes.LittleEndianStructure): _fields_ = [ ('Type', uint32_t, 7), # (PARTITION_TYPES) ('CopyToDramCache', uint32_t, 1), # Partition should be copied to persistent DRAM cache ('Reserved0', uint32_t, 7), ('BuiltWithLength1', uint32_t, 1), # Indication to Flash Building Tool ('BuiltWithLength2', uint32_t, 1), # Indication to Flash Building Tool ('Reserved1', uint32_t, 7), ('EntryValid', uint32_t, 8) ] class FPT_Entry_GetFlags(ctypes.Union): _fields_ = [ ('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_ = [ ('Project', char*4), # 0x00 (gsc_fwu_external_version) ('Hotfix', uint16_t), # 0x04 ('Build', uint16_t), # 0x06 ('GSCMajor', uint16_t), # 0x08 ('GSCMinor', uint16_t), # 0x0A ('GSCHotfix', uint16_t), # 0x0C ('GSCBuild', uint16_t), # 0x0E ('Flags', uint16_t), # 0x10 Unknown ('FWType', uint8_t), # 0x12 ('FWSKU', uint8_t), # 0x13 ('ARBSVN', uint32_t), # 0x14 ('TCBSVN', uint32_t), # 0x18 ('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]) pt.add_row(['Project Build', self.Build]) pt.add_row(['Version', 'N/A' if self.GSCMajor in [0,0xFFFF] else gsc_ver]) pt.add_row(['Flags', '0x%X' % self.Flags]) pt.add_row(['Type', ext15_fw_type[f1] if f1 in ext15_fw_type else 'Unknown (%d)' % f1]) pt.add_row(['Type Reserved', '0x%X' % f2]) pt.add_row(['SKU', ext15_fw_sku[f3][0] if f3 in ext15_fw_sku else 'Unknown (%d)' % f3]) pt.add_row(['SKU Reserved', '0x%X' % f4]) 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_ = [ ('Name', char*4), # 0x00 ('Flags', uint16_t), # 0x04 Unknown ('Reserved', uint16_t), # 0x06 ('SVN', uint32_t), # 0x08 ('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_ = [ ('Signature', uint16_t), # 0x00 AA55 ('ImageSize', uint16_t), # 0x02 Multiple of 512, includes Headers ('InitFuncEntrPnt', uint32_t), # 0x04 ('SubSystem', uint16_t), # 0x08 ('MachineType', uint16_t), # 0x0A ('CompressionType', uint16_t), # 0x0C ('Reserved', uint64_t), # 0x0E ('EFIImageOffset', uint16_t), # 0x16 ('PCIDataHdrOff', uint16_t), # 0x18 ('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)]) pt.add_row(['Init Function Entry Point', '0x%X' % self.InitFuncEntrPnt]) pt.add_row(['Sub System', '0x%X' % self.SubSystem]) pt.add_row(['Machine Type', '0x%X' % self.MachineType]) pt.add_row(['Compression Type', '0x%X' % self.CompressionType]) pt.add_row(['Reserved', '0x%X' % self.Reserved]) 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_ = [ ('Signature', char*4), # 0x00 PCIR ('VEN_ID', uint16_t), # 0x04 ('DEV_ID', uint16_t), # 0x06 ('DevListPointer', uint16_t), # 0x08 ('PCIDataHdrLen', uint16_t), # 0x0A ('PCIDataHdrRev', uint8_t), # 0x0C ('ClassCode', uint8_t*3), # 0x0D ('ImageSize', uint16_t), # 0x10 Multiple of 512, includes Headers ('RevisionLevel', uint16_t), # 0x12 ('CodeType', uint8_t), # 0x14 ('LastImageMark', uint8_t), # 0x15 ('MaxRuntimeImgLen',uint16_t), # 0x16 ('ConfUtlCodHdrPtr',uint16_t), # 0x18 Config Utility Code Header Pointer ('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]) pt.add_row(['Device ID', '%0.4X' % self.DEV_ID]) pt.add_row(['Device List Pointer', '0x%X' % self.DevListPointer]) pt.add_row(['Header Size', '0x%X' % self.PCIDataHdrLen]) pt.add_row(['Header Revision', self.PCIDataHdrRev]) pt.add_row(['Class Code', '0x%X' % int.from_bytes(self.ClassCode, 'little')]) pt.add_row(['Image Size', '0x%X' % (self.ImageSize * 512)]) pt.add_row(['Revision Level', '0x%X' % self.RevisionLevel]) pt.add_row(['Code Type', pcir_code_types[self.CodeType] if self.CodeType in pcir_code_types else 'Unknown (0x%X)' % self.CodeType]) pt.add_row(['Last Image', ['No','Yes'][self.LastImageMark >> 7]]) 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) _pack_ = 1 _fields_ = [ ('ROMBInstr0', uint32_t), # 0x00 ROM-Bypass Vector 0 ('ROMBInstr1', uint32_t), # 0x04 ('ROMBInstr2', uint32_t), # 0x08 ('ROMBInstr3', uint32_t), # 0x0C ('DataOffset', uint32_t), # 0x10 Data Partition Base Address ('DataSize', uint32_t), # 0x14 Data Partition Size ('BP1Offset', uint32_t), # 0x18 Boot Partition 1 Base Address ('BP1Size', uint32_t), # 0x1C Boot Partition 1 Size ('BP2Offset', uint32_t), # 0x20 ('BP2Size', uint32_t), # 0x24 ('BP3Offset', uint32_t), # 0x28 ('BP3Size', uint32_t), # 0x2C ('BP4Offset', uint32_t), # 0x30 ('BP4Size', uint32_t), # 0x34 ('BP5Offset', uint32_t), # 0x38 ('BP5Size', uint32_t), # 0x3C ('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]) pt.add_row(['ROMB Instruction 2', 'N/A' if self.ROMBInstr2 in NA else '0x%0.8X' % self.ROMBInstr2]) pt.add_row(['ROMB Instruction 3', 'N/A' if self.ROMBInstr3 in NA else '0x%0.8X' % self.ROMBInstr3]) pt.add_row(['Data Partition Offset', '0x%X' % self.DataOffset]) pt.add_row(['Data Partition Size', '0x%X' % self.DataSize]) pt.add_row(['Boot Partition 1 Offset', '0x%X' % self.BP1Offset]) pt.add_row(['Boot Partition 1 Size', '0x%X' % self.BP1Size]) pt.add_row(['Boot Partition 2 Offset', '0x%X' % self.BP2Offset]) pt.add_row(['Boot Partition 2 Size', '0x%X' % self.BP2Size]) pt.add_row(['Boot Partition 3 Offset', '0x%X' % self.BP3Offset]) pt.add_row(['Boot Partition 3 Size', '0x%X' % self.BP3Size]) pt.add_row(['Boot Partition 4 Offset', '0x%X' % self.BP4Offset]) pt.add_row(['Boot Partition 4 Size', '0x%X' % self.BP4Size]) 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) _pack_ = 1 _fields_ = [ ('ROMBInstr0', uint32_t), # 0x00 ROM-Bypass Vector 0 ('ROMBInstr1', uint32_t), # 0x04 ('ROMBInstr2', uint32_t), # 0x08 ('ROMBInstr3', uint32_t), # 0x0C ('Size', uint16_t), # 0x10 0x40 w/o ELOG, 0x48 w/ ELOG ('Flags', uint8_t), # 0x12 0 CSE Redundancy, 1-7 Reserved ('Reserved', uint8_t), # 0x13 ('Checksum', uint32_t), # 0x14 CRC-32 of CSE LT pointers w/o ROMB (DataOffset - TempPagesSize, Checksum = 0) ('DataOffset', uint32_t), # 0x18 Data Partition Base Address ('DataSize', uint32_t), # 0x1C Data Partition Size ('BP1Offset', uint32_t), # 0x20 Boot Partition 1 Base Address ('BP1Size', uint32_t), # 0x24 Boot Partition 1 Size ('BP2Offset', uint32_t), # 0x28 ('BP2Size', uint32_t), # 0x2C ('BP3Offset', uint32_t), # 0x30 ('BP3Size', uint32_t), # 0x34 ('BP4Offset', uint32_t), # 0x38 ('BP4Size', uint32_t), # 0x3C ('BP5Offset', uint32_t), # 0x40 ('BP5Size', uint32_t), # 0x44 ('TempPagesOffset', uint32_t), # 0x48 Temporary Pages for DRAM cache, 0 for NVM ('TempPagesSize', uint32_t), # 0x4C ('ELogOffset', uint32_t), # 0x50 External Fatal Error Log ('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]) pt.add_row(['ROMB Instruction 2', 'N/A' if self.ROMBInstr2 in NA else '0x%0.8X' % self.ROMBInstr2]) pt.add_row(['ROMB Instruction 3', 'N/A' if self.ROMBInstr3 in NA else '0x%0.8X' % self.ROMBInstr3]) pt.add_row(['Size', '0x%X' % self.Size]) pt.add_row(['CSE Redundancy', ['No','Yes'][f1]]) pt.add_row(['Flags Reserved', '0x%X' % f2]) pt.add_row(['Reserved', '0x%X' % self.Reserved]) pt.add_row(['Checksum', '0x%X' % self.Checksum]) pt.add_row(['Data Partition Offset', '0x%X' % self.DataOffset]) pt.add_row(['Data Partition Size', '0x%X' % self.DataSize]) pt.add_row(['Boot Partition 1 Offset', '0x%X' % self.BP1Offset]) pt.add_row(['Boot Partition 1 Size', '0x%X' % self.BP1Size]) pt.add_row(['Boot Partition 2 Offset', '0x%X' % self.BP2Offset]) pt.add_row(['Boot Partition 2 Size', '0x%X' % self.BP2Size]) pt.add_row(['Boot Partition 3 Offset', '0x%X' % self.BP3Offset]) pt.add_row(['Boot Partition 3 Size', '0x%X' % self.BP3Size]) pt.add_row(['Boot Partition 4 Offset', '0x%X' % self.BP4Offset]) pt.add_row(['Boot Partition 4 Size', '0x%X' % self.BP4Size]) 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(['Temporary Pages Offset', '0x%X' % self.TempPagesOffset]) 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), ('Reserved', uint8_t, 7), ] class CSE_Layout_Table_17_GetFlags(ctypes.Union): _fields_ = [ ('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_ = [ ('Signature', uint32_t), # 0x00 AA550000 Boot/Green, AA55AA00 Recovery/Yellow ('DescCount', uint16_t), # 0x04 Minimum 6 Entries ('BPDTVersion', uint16_t), # 0x06 1 IFWI 1.6 & 2.0, 2 IFWI 1.7 ('Reserved', uint16_t), # 0x08 ('Checksum', uint16_t), # 0x0A ('IFWIVersion', uint32_t), # 0x0C Unique mark from build server ('FitMajor', uint16_t), # 0x10 ('FitMinor', uint16_t), # 0x12 ('FitHotfix', uint16_t), # 0x14 ('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]) pt.add_row(['BPDT Version', bpdt_ver[self.BPDTVersion] if self.BPDTVersion in bpdt_ver else 'Unknown']) pt.add_row(['Reserved', '0x%X' % self.Reserved]) 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_ = [ ('Signature', uint32_t), # 0x00 AA550000 Boot/Green, AA55AA00 Recovery/Yellow ('DescCount', uint16_t), # 0x04 Minimum 6 Entries ('BPDTVersion', uint8_t), # 0x06 1 IFWI 1.6 & 2.0, 2 IFWI 1.7 ('BPDTConfig', uint8_t), # 0x07 0 BPDT Redundancy Support, 1-7 Reserved ('Checksum', uint32_t), # 0x08 CRC32 of entire BPDT (Header + Entries) without Signature ('IFWIVersion', uint32_t), # 0x0C Unique mark from build server ('FitMajor', uint16_t), # 0x10 ('FitMinor', uint16_t), # 0x12 ('FitHotfix', uint16_t), # 0x14 ('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]) pt.add_row(['BPDT Version', bpdt_ver[self.BPDTVersion] if self.BPDTVersion in bpdt_ver else 'Unknown']) pt.add_row(['BPDT Redundancy', ['No','Yes'][f1]]) pt.add_row(['BPDT Config Reserved', '0x%X' % f2]) 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), ('Reserved', uint8_t, 7), ] class BPDT_Header_2_GetFlags(ctypes.Union): _fields_ = [ ('b', BPDT_Header_2_Flags), ('asbytes', uint8_t) ] class BPDT_Entry(ctypes.LittleEndianStructure) : # (BpdtEntry) _pack_ = 1 _fields_ = [ ("Type", uint16_t), # 0x00 dword at non-APL IFWI 1.6, 1.7 & 2.0 ("Flags", uint16_t), # 0x02 only at APL IFWI 2.0 ("Offset", uint32_t), # 0x04 ("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]]) pt.add_row(['Split Sub-Partition 2nd Part', fvalue[f2]]) pt.add_row(['Code Sub-Partition', fvalue[f3]]) pt.add_row(['UMA Cacheable', fvalue[f4]]) 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) ('SplitSubPartitionSecondPart', uint16_t, 1), # 1st part in other LBP, 2nd part in this LBP (for up to 1 non-critical Sub-Partition) ('CodeSubPartition', uint16_t, 1), # Sub-Partition Contains Directory Structure ('UMACachable', uint16_t, 1), # Copied to DRAM cache if required for a reset flow (for all Sub-Partition except BIOS-based) ('Reserved', uint16_t, 12) ] class BPDT_Entry_GetFlags(ctypes.Union): _fields_ = [ ('b', BPDT_Entry_Flags), ('asbytes', uint16_t) ] class MN2_Manifest_R0(ctypes.LittleEndianStructure) : # Manifest $MAN/$MN2 Pre-CSE R0 (MANIFEST_HEADER) _pack_ = 1 _fields_ = [ ("HeaderType", uint32_t), # 0x00 ("HeaderLength", uint32_t), # 0x04 dwords ("HeaderVersion", uint32_t), # 0x08 0x10000 ("Flags", uint32_t), # 0x0C ("VEN_ID", uint32_t), # 0x10 0x8086 ("Day", uint8_t), # 0x14 ("Month", uint8_t), # 0x15 ("Year", uint16_t), # 0x16 ("Size", uint32_t), # 0x18 dwords (0x2000 max) ("Tag", char*4), # 0x1C ("NumModules", uint32_t), # 0x20 ("Major", uint16_t), # 0x24 ("Minor", uint16_t), # 0x26 ("Hotfix", uint16_t), # 0x28 ("Build", uint16_t), # 0x2A ("SVN", uint32_t), # 0x2C ME9+ (LSByte derives keys) ("SVN_8", uint32_t), # 0x30 ME8 ("VCN", uint32_t), # 0x34 ME8-10 ("Reserved", uint32_t*16), # 0x38 ("PublicKeySize", uint32_t), # 0x78 dwords ("ExponentSize", uint32_t), # 0x7C dwords ("RSAPublicKey", uint32_t*64), # 0x80 ("RSAExponent", uint32_t), # 0x180 ("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_ = [ ('HeaderType', uint16_t), # 0x00 ('HeaderSubType', uint16_t), # 0x02 ('HeaderLength', uint32_t), # 0x04 dwords ('HeaderVersion', uint32_t), # 0x08 0x10000 ('Flags', uint32_t), # 0x0C ('VEN_ID', uint32_t), # 0x10 0x8086 ('Day', uint8_t), # 0x14 ('Month', uint8_t), # 0x15 ('Year', uint16_t), # 0x16 ('Size', uint32_t), # 0x18 dwords (0x2000 max) ('Tag', char*4), # 0x1C ('BuildTag', uint32_t), # 0x20 Internal Info of FTPR > kernel or IGMF ('Major', uint16_t), # 0x24 ('Minor', uint16_t), # 0x26 ('Hotfix', uint16_t), # 0x28 ('Build', uint16_t), # 0x2A ('SVN', uint32_t), # 0x2C LS Byte derives keys ('MEU_Major', uint16_t), # 0x30 ('MEU_Minor', uint16_t), # 0x32 ('MEU_Hotfix', uint16_t), # 0x34 ('MEU_Build', uint16_t), # 0x36 ('MEU_Man_Ver', uint16_t), # 0x38 ('MEU_Man_Res', uint16_t), # 0x3A ('GeneralData', uint32_t), # 0x3C General Data of RBE ('IPSpecific0', uint32_t), # 0x40 IP Specific Data ('IPSpecific1', uint32_t), # 0x44 ('IPSpecific2', uint32_t), # 0x48 ('IPSpecific3', uint32_t), # 0x4C ('IPSpecific4', uint32_t), # 0x50 ('IPSpecific5', uint32_t), # 0x54 ('Reserved', uint32_t*8), # 0x58 ('PublicKeySize', uint32_t), # 0x78 dwords ('ExponentSize', uint32_t), # 0x7C dwords ('RSAPublicKey', uint32_t*64), # 0x80 ('RSAExponent', uint32_t), # 0x180 ('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]) pt.add_row(['Header Size', '0x%X' % (self.HeaderLength * 4)]) pt.add_row(['Header Version', '0x%X' % self.HeaderVersion]) pt.add_row(['Production Ready', fvalue[f1]]) pt.add_row(['Flags Reserved', '0x%X' % f2]) pt.add_row(['PID Bound', fvalue[f3]]) pt.add_row(['Intel Owned', fvalue[f4]]) pt.add_row(['Debug Signed', fvalue[f5]]) pt.add_row(['Vendor ID', '0x%X' % self.VEN_ID]) pt.add_row(['Date', '%0.4X-%0.2X-%0.2X' % (self.Year,self.Month,self.Day)]) pt.add_row(['Manifest Size', '0x%X' % (self.Size * 4)]) pt.add_row(['Manifest Tag', '%s' % self.Tag.decode('utf-8')]) pt.add_row(['Unique Build Tag', '0x%X' % self.BuildTag]) pt.add_row(['Version', 'N/A' if self.Major in [0,0xFFFF] else version]) pt.add_row(['TCB Security Version Number', '%d' % self.SVN]) pt.add_row(['MEU Version', 'N/A' if self.MEU_Major in [0,0xFFFF] else meu_version]) pt.add_row(['MEU Manifest Version', '%d' % self.MEU_Man_Ver]) pt.add_row(['MEU Manifest Reserved', '0x%X' % self.MEU_Man_Res]) pt.add_row(['General Data', '0x%0.8X' % self.GeneralData]) pt.add_row(['IP Specific Data 0', '0x%0.8X' % self.IPSpecific0]) pt.add_row(['IP Specific Data 1', '0x%0.8X' % self.IPSpecific1]) pt.add_row(['IP Specific Data 2', '0x%0.8X' % self.IPSpecific2]) pt.add_row(['IP Specific Data 3', '0x%0.8X' % self.IPSpecific3]) pt.add_row(['IP Specific Data 4', '0x%0.8X' % self.IPSpecific4]) pt.add_row(['IP Specific Data 5', '0x%0.8X' % self.IPSpecific5]) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) pt.add_row(['RSA Public Key Size', '0x%X' % PublicKeySize]) pt.add_row(['RSA Exponent Size', '0x%X' % (self.ExponentSize * 4)]) 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) _pack_ = 1 _fields_ = [ ('HeaderType', uint16_t), # 0x00 ('HeaderSubType', uint16_t), # 0x02 ('HeaderLength', uint32_t), # 0x04 dwords ('HeaderVersion', uint32_t), # 0x08 0x21000 ('Flags', uint32_t), # 0x0C ('VEN_ID', uint32_t), # 0x10 0x8086 ('Day', uint8_t), # 0x14 ('Month', uint8_t), # 0x15 ('Year', uint16_t), # 0x16 ('Size', uint32_t), # 0x18 dwords (max 2K, 4K for MTP+) ('Tag', char*4), # 0x1C ('BuildTag', uint32_t), # 0x20 Internal Info of FTPR > kernel or IGMF ('Major', uint16_t), # 0x24 ('Minor', uint16_t), # 0x26 ('Hotfix', uint16_t), # 0x28 ('Build', uint16_t), # 0x2A ('SVN', uint32_t), # 0x2C LS Byte derives keys ('MEU_Major', uint16_t), # 0x30 ('MEU_Minor', uint16_t), # 0x32 ('MEU_Hotfix', uint16_t), # 0x34 ('MEU_Build', uint16_t), # 0x36 ('MEU_Man_Ver', uint16_t), # 0x38 ('MEU_Man_Res', uint16_t), # 0x3A ('GeneralData', uint32_t), # 0x3C General Data of RBE ('IPSpecific0', uint32_t), # 0x40 IP Specific Data ('IPSpecific1', uint32_t), # 0x44 ('IPSpecific2', uint32_t), # 0x48 ('IPSpecific3', uint32_t), # 0x4C ('IPSpecific4', uint32_t), # 0x50 ('IPSpecific5', uint32_t), # 0x54 ('Reserved', uint32_t*8), # 0x58 ('PublicKeySize', uint32_t), # 0x78 dwords ('ExponentSize', uint32_t), # 0x7C dwords ('RSAPublicKey', uint32_t*96), # 0x80 ('RSAExponent', uint32_t), # 0x200 ('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]) pt.add_row(['Header Size', '0x%X' % (self.HeaderLength * 4)]) pt.add_row(['Header Version', '0x%X' % self.HeaderVersion]) pt.add_row(['Production Ready', fvalue[f1]]) pt.add_row(['Flags Reserved', '0x%X' % f2]) pt.add_row(['PID Bound', fvalue[f3]]) pt.add_row(['Intel Owned', fvalue[f4]]) pt.add_row(['Debug Signed', fvalue[f5]]) pt.add_row(['Vendor ID', '0x%X' % self.VEN_ID]) pt.add_row(['Date', '%0.4X-%0.2X-%0.2X' % (self.Year,self.Month,self.Day)]) pt.add_row(['Manifest Size', '0x%X' % (self.Size * 4)]) pt.add_row(['Manifest Tag', '%s' % self.Tag.decode('utf-8')]) pt.add_row(['Unique Build Tag', '0x%X' % self.BuildTag]) pt.add_row(['Version', 'N/A' if self.Major in [0,0xFFFF] else version]) pt.add_row(['TCB Security Version Number', '%d' % self.SVN]) pt.add_row(['MEU Version', 'N/A' if self.MEU_Major in [0,0xFFFF] else meu_version]) pt.add_row(['MEU Manifest Version', '%d' % self.MEU_Man_Ver]) pt.add_row(['MEU Manifest Reserved', '0x%X' % self.MEU_Man_Res]) pt.add_row(['General Data', '0x%0.8X' % self.GeneralData]) pt.add_row(['IP Specific Data 0', '0x%0.8X' % self.IPSpecific0]) pt.add_row(['IP Specific Data 1', '0x%0.8X' % self.IPSpecific1]) pt.add_row(['IP Specific Data 2', '0x%0.8X' % self.IPSpecific2]) pt.add_row(['IP Specific Data 3', '0x%0.8X' % self.IPSpecific3]) pt.add_row(['IP Specific Data 4', '0x%0.8X' % self.IPSpecific4]) pt.add_row(['IP Specific Data 5', '0x%0.8X' % self.IPSpecific5]) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) pt.add_row(['RSA Public Key Size', '0x%X' % PublicKeySize]) pt.add_row(['RSA Exponent Size', '0x%X' % (self.ExponentSize * 4)]) 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 ('Reserved', uint32_t, 28), ('PIDBound', uint32_t, 1), # CSE ('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_ = [ ('Tag', char*4), # 0x00 ('Size', uint32_t), # 0x04 dwords ('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)]) pt.add_row(['Value 1', '0x%X' % f1]) pt.add_row(['Value 2', f2]) pt.add_row(['Value 3', f3]) pt.add_row(['Value 4', f4]) pt.add_row(['Value 5', f5]) pt.add_row(['Value 6', f6]) pt.add_row(['Value 7', f7]) pt.add_row(['Value 8', f8]) pt.add_row(['Value 9', f9]) pt.add_row(['Patsburg', ['No','Yes'][f10]]) 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 class SKU_Attributes_Flags(ctypes.BigEndianStructure): _fields_ = [ ('Value1', uint64_t, 24), ('Value2', uint64_t, 1), # Slim (ME 7) ('Value3', uint64_t, 1), ('Value4', uint64_t, 1), ('Value5', uint64_t, 1), ('Value6', uint64_t, 1), ('Value7', uint64_t, 1), ('Value8', uint64_t, 1), ('Value9', uint64_t, 1), ('Patsburg', uint64_t, 1), # 0 No, 1 Yes (ME 7-8) ('SKUType', uint64_t, 3), # 0 Corporate, 1 Consumer, 2 Slim (ME 9-10) ('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), ('asbytes', uint64_t) ] class MME_Header_Old(ctypes.LittleEndianStructure) : _pack_ = 1 _fields_ = [ ("Tag", char*4), # 0x00 ("Guid", uint8_t*16), # 0x04 ("MajorVersion", uint16_t), # 0x14 ("MinorVersion", uint16_t), # 0x16 ("HotfixVersion", uint16_t), # 0x18 ("BuildVersion", uint16_t), # 0x1A ("Name", char*16), # 0x1C ("Hash", uint8_t*20), # 0x2C ("Size", uint32_t), # 0x40 ("Flags", uint32_t), # 0x44 ("Unk48_4C", uint32_t), # 0x48 ("Unk4C_50", uint32_t), # 0x4C # 0x50 ] class MME_Header_New(ctypes.LittleEndianStructure) : _pack_ = 1 _fields_ = [ ("Tag", char*4), # 0x00 ("Name", char*16), # 0x04 ("Hash", uint8_t*32), # 0x14 ("ModBase", uint32_t), # 0x34 ("Offset_MN2", uint32_t), # 0x38 from $MN2 ("SizeUncomp", uint32_t), # 0x3C ("SizeComp", uint32_t), # 0x40 ("MemorySize", uint32_t), # 0x44 ("PreUmaSize", uint32_t), # 0x48 ("EntryPoint", uint32_t), # 0x4C ("Flags", uint32_t), # 0x50 ("Unk54", uint32_t), # 0x54 ("Unk58", uint32_t), # 0x58 ("Unk5C", uint32_t), # 0x5C # 0x60 ] class MCP_Header(ctypes.LittleEndianStructure) : # Multi Chip Package _pack_ = 1 _fields_ = [ ("Tag", char*4), # 0x00 ("HeaderSize", uint32_t), # 0x04 dwords ("CodeSize", uint32_t), # 0x08 ("Offset_Code_MN2", uint32_t), # 0x0C Code start from $MN2 ("Offset_Part_FPT", uint32_t), # 0x10 Partition start from $FPT ("Hash", uint8_t*32), # 0x14 ("Unknown34_38", uint32_t), # 0x34 ("Unknown38_3C", uint32_t), # 0x38 ME8-10 ("Unknown3C_40", uint32_t), # 0x3C ME8-10 ("Unknown40_44", uint32_t), # 0x40 ME8-10 # 0x38 ME7, 0x44 ME8-10 ] class CPD_Header_R1(ctypes.LittleEndianStructure) : # Code Partition Directory R1 (CPD_HEADER) _pack_ = 1 _fields_ = [ ('Tag', char*4), # 0x00 ('NumModules', uint32_t), # 0x04 ('HeaderVersion', uint8_t), # 0x08 1 ('EntryVersion', uint8_t), # 0x09 ('HeaderLength', uint8_t), # 0x0A ('Checksum', uint8_t), # 0x0B Checksum-8 of Header + Entries with Checksum field = 0 ('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]) pt.add_row(['Header Version', '%d' % self.HeaderVersion]) pt.add_row(['Entry Version', '%d' % self.EntryVersion]) 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) _pack_ = 1 _fields_ = [ ('Tag', char*4), # 0x00 ('NumModules', uint32_t), # 0x04 ('HeaderVersion', uint8_t), # 0x08 2 ('EntryVersion', uint8_t), # 0x09 ('HeaderLength', uint8_t), # 0x0A ('Reserved', uint8_t), # 0x0B ('PartitionName', char*4), # 0x0C ('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]) pt.add_row(['Header Version', '%d' % self.HeaderVersion]) pt.add_row(['Entry Version', '%d' % self.EntryVersion]) pt.add_row(['Header Size', '0x%X' % self.HeaderLength]) 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_ = [ ("Name", char*12), # 0x00 ("OffsetAttrib", uint32_t), # 0x0C 00:24 $CPD Offset, 25 Huffman Yes/No, 26:31 Reserved ("Size", uint32_t), # 0x10 Uncompressed for LZMA/Huffman, Compressed at CSE_Ext_0A instead ("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]) pt.add_row(['Huffman Compression', ['No','Yes'][f2]]) 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): _fields_ = [ ('OffsetCPD', uint32_t, 25), ('IsHuffman', uint32_t, 1), ('Reserved', uint32_t, 6) ] class CPD_Entry_GetOffsetAttrib(ctypes.Union): _fields_ = [ ('b', CPD_Entry_OffsetAttrib), ('asbytes', uint32_t) ] class MFS_Page_Header(ctypes.LittleEndianStructure) : # MFS Page Header _pack_ = 1 _fields_ = [ ('Signature', uint32_t), # 0x00 ('PageNumber', uint32_t), # 0x04 ('EraseCount', uint32_t), # 0x08 ('NextErasePage', uint16_t), # 0x0C ('FirstChunkIndex', uint16_t), # 0x0E ('CRC8', uint8_t), # 0x10 ('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]) pt.add_row(['Erase Count', '%d' % self.EraseCount]) pt.add_row(['Next Erase Page Index', '%d' % self.NextErasePage]) 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_ = [ ('Signature', uint32_t), # 0x00 ('FTBLDictionary', uint8_t), # 0x04 0A = CON, 0B = COR, 0C = SLM etc ('FTBLPlatform', uint8_t), # 0x05 01 = ICP, 02 = CMP, 03 = LKF, 04 = TGP, 05 = CMP-V etc ('FTBLReserved', uint16_t), # 0x06 ('VolumeSize', uint32_t), # 0x08 System + Data ('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 : pt.add_row(['FTBL Dictionary', '0x%0.2X' % self.FTBLDictionary]) pt.add_row(['FTBL Platform', '0x%0.2X (%s)' % (self.FTBLPlatform, plat_name)]) pt.add_row(['FTBL Reserved', '0x%X' % self.FTBLReserved]) else : 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_ = [ ('FileName', char*12), # 0x00 ('Reserved', uint16_t), # 0x0C ('AccessMode', uint16_t), # 0x0E ('DeployOptions', uint16_t), # 0x10 ('FileSize', uint16_t), # 0x12 ('OwnerUserID', uint16_t), # 0x14 ('OwnerGroupID', uint16_t), # 0x16 ('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]]) pt.add_row(['Size', '0x%X' % self.FileSize]) #pt.add_row(['Offset', '0x%X' % self.FileOffset]) pt.add_row(['Access Rights', ''.join(map(str, self.get_rights(f1)))]) pt.add_row(['Owner User ID', '%0.4X' % self.OwnerUserID]) pt.add_row(['Owner Group ID', '%0.4X' % self.OwnerGroupID]) pt.add_row(['OEM Configurable', fvalue[f7]]) pt.add_row(['MCA Configurable', fvalue[f8]]) pt.add_row(['Integrity Protection', fvalue[f2]]) pt.add_row(['Encryption Protection', fvalue[f3]]) pt.add_row(['Anti-Replay Protection', fvalue[f4]]) 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), ('Integrity', uint16_t, 1), # HMAC ('Encryption', uint16_t, 1), ('AntiReplay', uint16_t, 1), ('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_ = [ ('FileID', uint32_t), # 0x00 ('FileOffset', uint32_t), # 0x04 ('FileSize', uint16_t), # 0x08 ('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_ = [ ('FileInfo', uint32_t), # 0x00 ('AccessMode', uint16_t), # 0x04 ('OwnerUserID', uint16_t), # 0x06 ('OwnerGroupID', uint16_t), # 0x08 ('UnknownSalt', uint16_t), # 0x0A ('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')]) pt.add_row(['Type', ['File','Folder'][f10]]) pt.add_row(['Keys', ['Intel','Other'][f9]]) pt.add_row(['File System', mfs_type[f3]]) pt.add_row(['Access Rights', ''.join(map(str, self.get_rights(f4)))]) pt.add_row(['Owner User ID', '%0.4X' % self.OwnerUserID]) pt.add_row(['Owner Group ID', '%0.4X' % self.OwnerGroupID]) pt.add_row(['Integrity Protection', fvalue[f5]]) pt.add_row(['Encryption Protection', fvalue[f6]]) pt.add_row(['Anti-Replay Protection', fvalue[f7]]) pt.add_row(['Access Mode Unknown 0', '{0:01b}b'.format(f8)]) 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_ = [ ('FileInfo', uint32_t), # 0x00 ('AccessMode', uint16_t), # 0x04 ('OwnerUserID', uint16_t), # 0x06 ('OwnerGroupID', uint16_t), # 0x08 ('UnknownSalt', uint16_t*3), # 0x0A ('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')]) pt.add_row(['Type', ['File','Folder'][f10]]) pt.add_row(['Keys', ['Intel','Other'][f9]]) pt.add_row(['File System', mfs_type[f3]]) pt.add_row(['Access Rights', ''.join(map(str, self.get_rights(f4)))]) pt.add_row(['Owner User ID', '%0.4X' % self.OwnerUserID]) pt.add_row(['Owner Group ID', '%0.4X' % self.OwnerGroupID]) pt.add_row(['Integrity Protection', fvalue[f5]]) pt.add_row(['Encryption Protection', fvalue[f6]]) pt.add_row(['Anti-Replay Protection', fvalue[f7]]) pt.add_row(['Access Mode Unknown 0', '{0:01b}b'.format(f8)]) 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 class MFS_Home_Record_FileInfo(ctypes.LittleEndianStructure): _fields_ = [ ('FileIndex', uint32_t, 12), # MFS Low Level File Index ('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), ('Integrity', uint16_t, 1), # HMAC ('Encryption', uint16_t, 1), ('AntiReplay', uint16_t, 1), ('Unknown0', uint16_t, 1), ('KeyType', uint16_t, 1), # 0 Intel, 1 Other ('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), ('asbytes', uint16_t) ] class MFS_Integrity_Table_0x34(ctypes.LittleEndianStructure) : # MFS Integrity Table 0x34 _pack_ = 1 _fields_ = [ ('HMACSHA256', uint32_t*8), # 0x00 HMAC SHA-256 ('Flags', uint32_t), # 0x20 ('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(' Low Level File 6 or CSTXE FTPR > intl.cfg ("PageableUMASize", uint32_t), # 0x30 ("Reserved0", uint64_t), # 0x34 ("Reserved1", uint32_t), # 0x3C # 0x40 ] # The MFS Intel Configuration (Low Level File 6) Hash is only checked at first boot, before the MFS is Initialized. # After the MFS Home Directory (Low Level Files 8+) is generated, MFS Intel Configuration is no longer used or checked. # The initial MFS Intel Configuration remains the same even after FWUpdate is executed so the FTPR Manifest Hash is wrong. # Thus, the MFS Intel Configuration Hash must only be checked at non-Initialized MFS before any possible FWUpdate operations. def ext_print(self) : IMGDefaultHash = '%0.*X' % (0x20 * 2, int.from_bytes(self.IMGDefaultHash, 'little')) pt = ext_table(['Field', 'Value'], False, 1) pt.title = col_y + 'Extension 0, System Information' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) pt.add_row(['Minimum UMA Size', '0x%X' % self.MinUMASize]) pt.add_row(['Chipset Version', '0x%X' % self.ChipsetVersion]) pt.add_row(['Intel Config Hash', IMGDefaultHash]) pt.add_row(['Pageable UMA Size', '0x%X' % self.PageableUMASize]) pt.add_row(['Reserved 0', '0x%X' % self.Reserved0]) pt.add_row(['Reserved 1', '0x%X' % self.Reserved1]) return pt class CSE_Ext_00_R2(ctypes.LittleEndianStructure) : # R2 - System Information (SYSTEM_INFO_EXTENSION) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("MinUMASize", uint32_t), # 0x08 ("ChipsetVersion", uint32_t), # 0x0C ("IMGDefaultHash", uint32_t*12), # 0x10 SHA-384, CSME/SPS MFS > Low Level File 6 or CSTXE FTPR > intl.cfg ("PageableUMASize", uint32_t), # 0x40 ("Reserved0", uint64_t), # 0x44 ("Reserved1", uint32_t), # 0x4C # 0x50 ] # The MFS Intel Configuration (Low Level File 6) Hash is only checked at first boot, before the MFS is Initialized. # After the MFS Home Directory (Low Level Files 8+) is generated, MFS Intel Configuration is no longer used or checked. # The initial MFS Intel Configuration remains the same even after FWUpdate is executed so the FTPR Manifest Hash is wrong. # Thus, the MFS Intel Configuration Hash must only be checked at non-Initialized MFS before any possible FWUpdate operations. def ext_print(self) : IMGDefaultHash = '%0.*X' % (0x30 * 2, int.from_bytes(self.IMGDefaultHash, 'little')) pt = ext_table(['Field', 'Value'], False, 1) pt.title = col_y + 'Extension 0, System Information' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) pt.add_row(['Minimum UMA Size', '0x%X' % self.MinUMASize]) pt.add_row(['Chipset Version', '0x%X' % self.ChipsetVersion]) pt.add_row(['Intel Config Hash', IMGDefaultHash]) pt.add_row(['Pageable UMA Size', '0x%X' % self.PageableUMASize]) pt.add_row(['Reserved 0', '0x%X' % self.Reserved0]) pt.add_row(['Reserved 1', '0x%X' % self.Reserved1]) return pt class CSE_Ext_00_Mod(ctypes.LittleEndianStructure) : # R1 - (INDEPENDENT_PARTITION_ENTRY) _pack_ = 1 _fields_ = [ ("Name", char*4), # 0x00 ("Version", uint32_t), # 0x04 ("UserID", uint16_t), # 0x08 ("GroupID", uint16_t), # 0x0A (Guess, not in XML) # 0x0C ] def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) pt.title = col_y + 'Extension 0, Independent Partition' + col_e pt.add_row(['Name', self.Name.decode('utf-8')]) pt.add_row(['Version', '0x%X' % self.Version]) pt.add_row(['User ID', '0x%0.4X' % self.UserID]) pt.add_row(['Group ID', '0x%0.4X' % self.GroupID]) return pt class CSE_Ext_00_Mod_R2(ctypes.LittleEndianStructure) : # R2 - (INDEPENDENT_PARTITION_ENTRY) _pack_ = 1 _fields_ = [ ("Name", char*4), # 0x00 ("Version", uint32_t), # 0x04 ("UserID", uint16_t), # 0x08 ("Reserved0", uint16_t), # 0x0A ("Reserved1", uint16_t), # 0x0C ("Reserved2", uint16_t), # 0x0E # 0x10 ] def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) pt.title = col_y + 'Extension 0, Independent Partition' + col_e pt.add_row(['Name', self.Name.decode('utf-8')]) pt.add_row(['Version', '0x%X' % self.Version]) pt.add_row(['User ID', '0x%0.4X' % self.UserID]) 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 class CSE_Ext_01(ctypes.LittleEndianStructure) : # R1 - Initialization Script (InitScript) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("Reserved", uint32_t), # 0x08 ("ModuleCount", uint32_t), # 0x0C # 0x10 ] def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) pt.title = col_y + 'Extension 1, Initialization Script' + 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]) pt.add_row(['Module Count', '%d' % self.ModuleCount]) return pt class CSE_Ext_01_Mod(ctypes.LittleEndianStructure) : # R1 - (InitScriptEntry) _pack_ = 1 _fields_ = [ ("PartitionName", char*4), # 0x00 ("ModuleName", char*12), # 0x0C ("InitFlowFlags", uint32_t), # 0x10 ("BootTypeFlags", uint32_t), # 0x14 # 0x18 ] def ext_print(self) : fvalue = ['No','Yes'] f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16 = self.get_flags() pt = ext_table(['Field', 'Value'], False, 1) pt.title = col_y + 'Extension 1, Entry' + col_e pt.add_row(['Partition Name', self.PartitionName.decode('utf-8')]) pt.add_row(['Module Name', self.ModuleName.decode('utf-8')]) pt.add_row(['IBL', fvalue[f1]]) pt.add_row(['Removable', fvalue[f2]]) pt.add_row(['Init Immediately', fvalue[f3]]) pt.add_row(['Restart Policy', ['Not Allowed','Immediately','On Next Boot'][f4]]) pt.add_row(['CM0 with UMA', fvalue[f5]]) pt.add_row(['CM0 without UMA', fvalue[f6]]) pt.add_row(['CM3', fvalue[f7]]) pt.add_row(['Init Flow Reserved', '0x%X' % f8]) pt.add_row(['Normal', fvalue[f9]]) pt.add_row(['HAP', fvalue[f10]]) pt.add_row(['HMRFPO', fvalue[f11]]) pt.add_row(['Temp Disable', fvalue[f12]]) pt.add_row(['Recovery', fvalue[f13]]) pt.add_row(['Safe Mode', fvalue[f14]]) pt.add_row(['FWUpdate', fvalue[f15]]) pt.add_row(['Boot Type Reserved', '0x%X' % f16]) return pt def get_flags(self) : i_flags = CSE_Ext_01_GetInitFlowFlags() b_flags = CSE_Ext_01_GetBootTypeFlags() i_flags.asbytes = self.InitFlowFlags b_flags.asbytes = self.BootTypeFlags return i_flags.b.IBL, i_flags.b.Removable, i_flags.b.InitImmediately, i_flags.b.RestartPolicy, i_flags.b.CM0_UMA,\ i_flags.b.CM0_NO_UMA, i_flags.b.CM3, i_flags.b.Reserved, b_flags.b.Normal, b_flags.b.HAP, b_flags.b.HMRFPO,\ b_flags.b.TempDisable, b_flags.b.Recovery, b_flags.b.SafeMode, b_flags.b.FWUpdate, b_flags.b.Reserved class CSE_Ext_01_Mod_R2(ctypes.LittleEndianStructure) : # R2 - (InitScriptEntry) _pack_ = 1 _fields_ = [ ("PartitionName", char*4), # 0x00 ("ModuleName", char*12), # 0x0C ("InitFlowFlags", uint32_t), # 0x10 ("BootTypeFlags", uint32_t), # 0x14 ("UnknownFlags", uint32_t), # 0x18 (Unknown) # 0x1C ] def ext_print(self) : fvalue = ['No','Yes'] f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16 = self.get_flags() pt = ext_table(['Field', 'Value'], False, 1) pt.title = col_y + 'Extension 1, Entry' + col_e pt.add_row(['Partition Name', self.PartitionName.decode('utf-8')]) pt.add_row(['Module Name', self.ModuleName.decode('utf-8')]) pt.add_row(['IBL', fvalue[f1]]) pt.add_row(['Removable', fvalue[f2]]) pt.add_row(['Init Immediately', fvalue[f3]]) pt.add_row(['Restart Policy', ['Not Allowed','Immediately','On Next Boot'][f4]]) pt.add_row(['CM0 with UMA', fvalue[f5]]) pt.add_row(['CM0 without UMA', fvalue[f6]]) pt.add_row(['CM3', fvalue[f7]]) pt.add_row(['Init Flow Reserved', '{0:025b}b'.format(f8)]) pt.add_row(['Normal', fvalue[f9]]) pt.add_row(['HAP', fvalue[f10]]) pt.add_row(['HMRFPO', fvalue[f11]]) pt.add_row(['Temp Disable', fvalue[f12]]) pt.add_row(['Recovery', fvalue[f13]]) pt.add_row(['Safe Mode', fvalue[f14]]) pt.add_row(['FWUpdate', fvalue[f15]]) pt.add_row(['Boot Type Reserved', '{0:025b}b'.format(f16)]) pt.add_row(['Unknown Flags', '{0:032b}b'.format(self.UnknownFlags)]) return pt def get_flags(self) : i_flags = CSE_Ext_01_GetInitFlowFlags() b_flags = CSE_Ext_01_GetBootTypeFlags() i_flags.asbytes = self.InitFlowFlags b_flags.asbytes = self.BootTypeFlags return i_flags.b.IBL, i_flags.b.Removable, i_flags.b.InitImmediately, i_flags.b.RestartPolicy, i_flags.b.CM0_UMA,\ i_flags.b.CM0_NO_UMA, i_flags.b.CM3, i_flags.b.Reserved, b_flags.b.Normal, b_flags.b.HAP, b_flags.b.HMRFPO,\ b_flags.b.TempDisable, b_flags.b.Recovery, b_flags.b.SafeMode, b_flags.b.FWUpdate, b_flags.b.Reserved class CSE_Ext_01_InitFlowFlags(ctypes.LittleEndianStructure): _fields_ = [ ('IBL', uint32_t, 1), ('Removable', uint32_t, 1), ('InitImmediately', uint32_t, 1), ('RestartPolicy', uint32_t, 1), # (InitScriptRestartPolicy) ('CM0_UMA', uint32_t, 1), ('CM0_NO_UMA', uint32_t, 1), ('CM3', uint32_t, 1), ('Reserved', uint32_t, 25) ] class CSE_Ext_01_GetInitFlowFlags(ctypes.Union): _fields_ = [ ('b', CSE_Ext_01_InitFlowFlags), ('asbytes', uint32_t) ] class CSE_Ext_01_BootTypeFlags(ctypes.LittleEndianStructure): _fields_ = [ ('Normal', uint32_t, 1), ('HAP', uint32_t, 1), ('HMRFPO', uint32_t, 1), ('TempDisable', uint32_t, 1), ('Recovery', uint32_t, 1), ('SafeMode', uint32_t, 1), ('FWUpdate', uint32_t, 1), ('Reserved', uint32_t, 25) ] class CSE_Ext_01_GetBootTypeFlags(ctypes.Union): _fields_ = [ ('b', CSE_Ext_01_BootTypeFlags), ('asbytes', uint32_t) ] class CSE_Ext_02(ctypes.LittleEndianStructure) : # R1 - Feature Permissions (FEATURE_PERMISSIONS_EXTENSION) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("ModuleCount", uint32_t), # 0x08 # 0x0C ] def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) pt.title = col_y + 'Extension 2, Feature Permissions' + col_e pt.add_row(['Tag', '0x%0.2X' % self.Tag]) pt.add_row(['Size', '0x%X' % self.Size]) pt.add_row(['Feature Count', '%d' % self.ModuleCount]) return pt class CSE_Ext_02_Mod(ctypes.LittleEndianStructure) : # R1 - (FEATURE_PERMISION_ENTRY) _pack_ = 1 _fields_ = [ ("UserID", uint16_t), # 0x00 ("Reserved", uint16_t), # 0x02 # 0x04 ] def ext_print(self) : pt = ext_table(['Field', 'Value'], False, 1) pt.title = col_y + 'Extension 2, Entry' + col_e pt.add_row(['User ID', '0x%0.4X' % self.UserID]) pt.add_row(['Reserved', '0x%X' % self.Reserved]) return pt class CSE_Ext_03(ctypes.LittleEndianStructure) : # R1 - Partition Information (MANIFEST_PARTITION_INFO_EXT) _pack_ = 1 _fields_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('PartitionName', char*4), # 0x08 ('PartitionSize', uint32_t), # 0x0C Complete original/RGN size before any process have been removed by the OEM or firmware update process ('Hash', uint32_t*8), # 0x10 SHA-256, Complete original/RGN partition covering everything except for the Manifest ($CPD - $MN2 + Data) ('VCN', uint32_t), # 0x30 Version Control Number ('PartitionVerMin', uint16_t), # 0x34 ('PartitionVerMaj', uint16_t), # 0x36 ('DataFormatMinor', uint16_t), # 0x38 ('DataFormatMajor', uint16_t), # 0x3A ('InstanceID', uint32_t), # 0x3C ('Flags', uint32_t), # 0x40 Used at CSE_Ext_16 as well, remember to change both! ('Reserved', uint32_t*4), # 0x44 ('Unknown', uint32_t), # 0x54 Unknown (>= 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]) pt.add_row(['Partition Name', self.PartitionName.decode('utf-8')]) pt.add_row(['Partition Size', '0x%X' % self.PartitionSize]) pt.add_row(['Partition Hash', Hash]) pt.add_row(['Version Control Number', '%d' % self.VCN]) pt.add_row(['Partition Version', '%X.%X' % (self.PartitionVerMaj, self.PartitionVerMin)]) pt.add_row(['Data Format Version', '%d.%d' % (self.DataFormatMajor, self.DataFormatMinor)]) pt.add_row(['Instance ID', '0x%0.8X' % self.InstanceID]) pt.add_row(['Support Multiple Instances', fvalue[f1]]) pt.add_row(['Support API Version Based Update', fvalue[f2]]) pt.add_row(['Action On Update', '0x%X' % f3]) pt.add_row(['Obey Full Update Rules', fvalue[f4]]) pt.add_row(['IFR Enable Only', fvalue[f5]]) pt.add_row(['Allow Cross Point Update', fvalue[f6]]) pt.add_row(['Allow Cross Hotfix Update', fvalue[f7]]) pt.add_row(['Partial Update Only', fvalue[f8]]) pt.add_row(['Not Measured', fvalue[f9]]) 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_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('PartitionName', char*4), # 0x08 ('PartitionSize', uint32_t), # 0x0C Complete original/RGN size before any process have been removed by the OEM or firmware update process ('Hash', uint32_t*12), # 0x10 SHA-384, Complete original/RGN partition covering everything except for the Manifest ($CPD - $MN2 + Data) ('VCN', uint32_t), # 0x40 Version Control Number ('PartitionVerMin', uint16_t), # 0x44 ('PartitionVerMaj', uint16_t), # 0x46 ('DataFormatMinor', uint16_t), # 0x48 ('DataFormatMajor', uint16_t), # 0x4A ('InstanceID', uint32_t), # 0x4C ('Flags', uint32_t), # 0x50 Used at CSE_Ext_16 as well, remember to change both! ('Reserved', uint32_t*4), # 0x54 ('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]) pt.add_row(['Partition Name', self.PartitionName.decode('utf-8')]) pt.add_row(['Partition Size', '0x%X' % self.PartitionSize]) pt.add_row(['Partition Hash', Hash]) pt.add_row(['Version Control Number', '%d' % self.VCN]) pt.add_row(['Partition Version', '%X.%X' % (self.PartitionVerMaj, self.PartitionVerMin)]) pt.add_row(['Data Format Version', '%d.%d' % (self.DataFormatMajor, self.DataFormatMinor)]) pt.add_row(['Instance ID', '0x%0.8X' % self.InstanceID]) pt.add_row(['Support Multiple Instances', fvalue[f1]]) pt.add_row(['Support API Version Based Update', fvalue[f2]]) pt.add_row(['Action On Update', '0x%X' % f3]) pt.add_row(['Obey Full Update Rules', fvalue[f4]]) pt.add_row(['IFR Enable Only', fvalue[f5]]) pt.add_row(['Allow Cross Point Update', fvalue[f6]]) pt.add_row(['Allow Cross Hotfix Update', fvalue[f7]]) pt.add_row(['Partial Update Only', fvalue[f8]]) pt.add_row(['Not Measured', fvalue[f9]]) 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 ('SupportApiVersionBasedUpdate', uint32_t, 1), ('ActionOnUpdate', uint32_t, 2), ('ObeyFullUpdateRules', uint32_t, 1), ('IfrEnableOnly', uint32_t, 1), ('AllowCrossPointUpdate', uint32_t, 1), ('AllowCrossHotfixUpdate', uint32_t, 1), ('PartialUpdateOnly', uint32_t, 1), ('NotMeasured', uint32_t, 1), ('Reserved', uint32_t, 22) ] class CSE_Ext_03_GetFlags(ctypes.Union): _fields_ = [ ('b', CSE_Ext_03_Flags), ('asbytes', uint32_t) ] class CSE_Ext_03_Mod(ctypes.LittleEndianStructure) : # R1 - Module Information (MANIFEST_MODULE_INFO_EXT) _pack_ = 1 _fields_ = [ ("Name", char*12), # 0x00 ("Type", uint8_t), # 0x0C 0 Process, 1 Shared Library, 2 Data, 3 Independent (MODULE_TYPES) ("Compression", uint8_t), # 0x0D 0 None, 1 Huffman, 2 LZMA ("Reserved", uint16_t), # 0x0E FFFF ("MetadataSize", uint32_t), # 0x10 ("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]) pt.add_row(['Compression', ['None','Huffman','LZMA'][self.Compression]]) 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) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("ContextSize", uint32_t), # 0x08 ("TotAlocVirtSpc", uint32_t), # 0x0C ("CodeBaseAddress", uint32_t), # 0x10 ("TLSSize", uint32_t), # 0x14 ("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]) pt.add_row(['Context Size', '0x%X' % self.ContextSize]) pt.add_row(['Total Allocated Virtual Space', '0x%X' % self.TotAlocVirtSpc]) 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) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("Flags", uint32_t), # 0x08 ("MainThreadID", uint32_t), # 0x0C ("CodeBaseAddress", uint32_t), # 0x10 ("CodeSizeUncomp", uint32_t), # 0x14 ("CM0HeapSize", uint32_t), # 0x18 ("BSSSize", uint32_t), # 0x1C ("DefaultHeapSize", uint32_t), # 0x20 ("MainThreadEntry", uint32_t), # 0x24 ("AllowedSysCalls", uint32_t*3), # 0x28 ("UserID", uint16_t), # 0x34 ("Reserved0", uint32_t), # 0x36 ("Reserved1", uint16_t), # 0x3A ("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]) pt.add_row(['Fault Tolerant', f1value[f1]]) pt.add_row(['Permanent Process', fvalue[f2]]) pt.add_row(['Single Instance', fvalue[f3]]) pt.add_row(['Trusted SendReceive Sender', fvalue[f4]]) pt.add_row(['Trusted Notify Sender', fvalue[f5]]) pt.add_row(['Public SendReceive Receiver', fvalue[f6]]) pt.add_row(['Public Notify Receiver', fvalue[f7]]) pt.add_row(['Reserved', '0x%X' % f8]) pt.add_row(['Main Thread ID', '0x%0.8X' % self.MainThreadID]) pt.add_row(['Code Base Address', '0x%X' % self.CodeBaseAddress]) pt.add_row(['Code Size Uncompressed', '0x%X' % self.CodeSizeUncomp]) pt.add_row(['CM0 Heap Size', '0x%X' % self.CM0HeapSize]) pt.add_row(['BSS Size', '0x%X' % self.BSSSize]) pt.add_row(['Default Heap Size', '0x%X' % self.DefaultHeapSize]) pt.add_row(['Main Thread Entry', '0x%X' % self.MainThreadEntry]) pt.add_row(['Allowed System Calls', AllowedSysCalls]) pt.add_row(['User ID', '0x%0.4X' % self.UserID]) 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 class CSE_Ext_05_Mod(ctypes.LittleEndianStructure) : # R1 - Group ID (PROCESS_GROUP_ID) _pack_ = 1 _fields_ = [ ('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): _fields_ = [ ('FaultTolerant', uint32_t, 1), # (EXCEPTION_HANDLE_TYPES) ('PermanentProcess', uint32_t, 1), ('SingleInstance', uint32_t, 1), ('TrustedSendReceiveSender', uint32_t, 1), ('TrustedNotifySender', uint32_t, 1), ('PublicSendReceiveReceiver', uint32_t, 1), ('PublicNotifyReceiver', uint32_t, 1), ('Reserved', uint32_t, 25) ] class CSE_Ext_05_GetFlags(ctypes.Union): _fields_ = [ ('b', CSE_Ext_05_Flags), ('asbytes', uint32_t) ] class CSE_Ext_06(ctypes.LittleEndianStructure) : # R1 - Thread Attributes (Threads) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("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) _pack_ = 1 _fields_ = [ ("StackSize", uint32_t), # 0x00 ("Flags", uint32_t), # 0x04 ("SchedulPolicy", uint32_t), # 0x08 ("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]]) pt.add_row(['Flags Reserved', '0x%X' % f2]) pt.add_row(['Scheduling Policy Fixed Priority', fvalue[f3]]) 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), ('FlagsReserved', uint32_t, 31) ] class CSE_Ext_06_GetFlags(ctypes.Union): _fields_ = [ ('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), ('asbytes', uint32_t) ] class CSE_Ext_07(ctypes.LittleEndianStructure) : # R1 - Device Types (DeviceIds) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("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) _pack_ = 1 _fields_ = [ ("DeviceID", uint32_t), # 0x00 ("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) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("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) _pack_ = 1 _fields_ = [ ("BaseAddress", uint32_t), # 0x00 ("SizeLimit", uint32_t), # 0x04 ("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) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("MajorNumber", uint16_t), # 0x08 ("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) _pack_ = 1 _fields_ = [ ("Name", char*12), # 0x00 ("AccessMode", uint16_t), # 0x0C ("UserID", uint16_t), # 0x0E ("GroupID", uint16_t), # 0x10 ("MinorNumber", uint8_t), # 0x12 ("Reserved0", uint8_t), # 0x13 ("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]) pt.add_row(['User ID', '0x%X' % self.UserID]) pt.add_row(['Group ID', '0x%X' % self.GroupID]) 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) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("Compression", uint8_t), # 0x08 0 None, 1 Huffman, 2 LZMA ("Encryption", uint8_t), # 0x09 0 None, 1 AES-CBC (?) ("Reserved0", uint8_t), # 0x0A ("Reserved1", uint8_t), # 0x0B ("SizeUncomp", uint32_t), # 0x0C ("SizeComp", uint32_t), # 0x10 LZMA & Huffman w/o EOM alignment ("DEV_ID", uint16_t), # 0x14 ("VEN_ID", uint16_t), # 0x16 0x8086 ("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]) pt.add_row(['Compression', ['None','Huffman','LZMA'][self.Compression]]) pt.add_row(['Encryption', ['None','AES-CBC'][self.Encryption]]) pt.add_row(['Reserved 0', '0x%X' % self.Reserved0]) pt.add_row(['Reserved 1', '0x%X' % self.Reserved1]) pt.add_row(['Size Uncompressed', '0x%X' % self.SizeUncomp]) pt.add_row(['Size Compressed', '0x%X' % self.SizeComp]) 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_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('Compression', uint8_t), # 0x08 0 None, 1 Huffman, 2 LZMA ('Encryption', uint8_t), # 0x09 0 None, 1 AES-ECB (?), 2 AES-CTR (?) ('Reserved0', uint8_t), # 0x0A ('Reserved1', uint8_t), # 0x0B ('SizeUncomp', uint32_t), # 0x0C ('SizeComp', uint32_t), # 0x10 LZMA & Huffman w/o EOM alignment ('DEV_ID', uint16_t), # 0x14 ('VEN_ID', uint16_t), # 0x16 0x8086 ('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]) pt.add_row(['Compression', ['None','Huffman','LZMA'][self.Compression]]) pt.add_row(['Encryption', ['None','AES-ECB','AES-CTR'][self.Encryption]]) pt.add_row(['Reserved 0', '0x%X' % self.Reserved0]) pt.add_row(['Reserved 1', '0x%X' % self.Reserved1]) pt.add_row(['Size Uncompressed', '0x%X' % self.SizeUncomp]) pt.add_row(['Size Compressed', '0x%X' % self.SizeComp]) 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) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("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) _pack_ = 1 _fields_ = [ ("RangeBase", uint32_t), # 0x00 ("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) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("FWSKUCaps", uint32_t), # 0x08 (ConfigRuleSettings) ("FWSKUCapsRes", uint32_t*7), # 0x0C ("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 self.major = major self.minor = minor self.hotfix = hotfix self.build = build 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] sku_field = 'SKU Platform' 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]) pt.add_row(['SKU Capabilities', sku_capabilities_pt]) pt.add_row(['SKU Capabilities Reserved', 'FF * 28' if FWSKUCapsRes == 'FF' * 28 else FWSKUCapsRes]) pt.add_row(['CSE Size', '0x%X' % f1]) pt.add_row(['SKU Type', ext12_fw_sku[f2][0] if f2 in ext12_fw_sku else 'Unknown (%d)' % f2]) pt.add_row(['Workstation', fvalue[f3]]) pt.add_row(['M3', fvalue[f4]]) pt.add_row(['M0', fvalue[f5]]) 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 ('SKUType', uint64_t, 3), # 0 COR, 1 CON, 2 SLM, 3 SVR ('Workstation', uint64_t, 1), # 0 11.0-12, 1 11.20-22 ('M3', uint64_t, 1), # 0 CON & SLM, 1 COR ('M0', uint64_t, 1), # 1 CON & SLM & COR ('SKUPlatform', uint64_t, 2), # 0 H/LP <= 11.0.0.1202, 0 H & 1 LP >= 11.0.0.1205 (CSME 11 only) ('SiClass', uint64_t, 4), # 2 CON & SLM, 4 COR (all, H, M, L) ('Reserved', uint64_t, 50) # 0 ] class CSE_Ext_0C_GetFWSKUAttrib(ctypes.Union): _fields_ = [ ('b', CSE_Ext_0C_FWSKUAttrib), ('asbytes', uint64_t) ] class CSE_Ext_0D(ctypes.LittleEndianStructure) : # R1 - User Information (USER_INFO_EXTENSION) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("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) _pack_ = 1 _fields_ = [ ("UserID", uint16_t), # 0x00 ("Reserved", uint16_t), # 0x02 ("NVStorageQuota", uint32_t), # 0x04 ("RAMStorageQuota", uint32_t), # 0x08 ("WOPQuota", uint32_t), # 0x0C (Wear-out Prevention) ("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]) 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]) 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) _pack_ = 1 _fields_ = [ ("UserID", uint16_t), # 0x00 ("Reserved", uint16_t), # 0x02 ("NVStorageQuota", uint32_t), # 0x04 ("RAMStorageQuota", uint32_t), # 0x08 ("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) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("KeyType", uint32_t), # 0x08 1 RoT, 2 OEM (KeyManifestTypeValues) ("KeySVN", uint32_t), # 0x0C ("OEMID", uint16_t), # 0x10 ("KeyID", uint8_t), # 0x12 Matched against Field Programmable Fuses (FPF) ("Reserved0", uint8_t), # 0x13 ("Reserved1", uint32_t*4), # 0x14 # 0x24 ] 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]) pt.add_row(['Key Type', ['Reserved','RoT','OEM'][self.KeyType]]) pt.add_row(['Key SVN', '%d' % self.KeySVN]) pt.add_row(['OEM ID', '0x%0.4X' % self.OEMID]) 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) _pack_ = 1 _fields_ = [ ("UsageBitmap", uint8_t*16), # 0x00 (KeyManifestHashUsages, OemKeyManifestHashUsages) ("Reserved0", uint32_t*4), # 0x10 ("Flags", uint8_t), # 0x20 ("HashAlgorithm", uint8_t), # 0x21 ("HashSize", uint16_t), # 0x22 ("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')]) pt.add_row(['IPI Policy', ['OEM or Intel','Intel Only'][f1]]) pt.add_row(['Flags Reserved', '0x%X' % f2]) 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_ = [ ("UsageBitmap", uint8_t*16), # 0x00 (KeyManifestHashUsages, OemKeyManifestHashUsages) ("Reserved0", uint32_t*4), # 0x10 ("Flags", uint8_t), # 0x20 ("HashAlgorithm", uint8_t), # 0x21 ("HashSize", uint16_t), # 0x22 ("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')]) pt.add_row(['IPI Policy', ['OEM or Intel','Intel Only'][f1]]) pt.add_row(['Flags Reserved', '0x%X' % f2]) 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 ('Reserved', uint8_t, 7) ] class CSE_Ext_0E_GetFlags(ctypes.Union): _fields_ = [ ('b', CSE_Ext_0E_Flags), ('asbytes', uint8_t) ] class CSE_Ext_0F(ctypes.LittleEndianStructure) : # R1 - Signed Package Information (SIGNED_PACKAGE_INFO_EXT) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("PartitionName", char*4), # 0x08 ("VCN", uint32_t), # 0x0C Version Control Number ("UsageBitmap", uint8_t*16), # 0x10 (KeyManifestHashUsages, OemKeyManifestHashUsages) ("ARBSVN", uint32_t), # 0x20 FPF Anti-Rollback (ARB) Security Version Number ("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]) pt.add_row(['Partition Name', self.PartitionName.decode('utf-8')]) pt.add_row(['Version Control Number', '%d' % self.VCN]) 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_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('PartitionName', char*4), # 0x08 ('VCN', uint32_t), # 0x0C Version Control Number ('UsageBitmap', uint8_t*16), # 0x10 (KeyManifestHashUsages, OemKeyManifestHashUsages) ('ARBSVN', uint32_t), # 0x20 FPF Anti-Rollback (ARB) Security Version Number ('FWType', uint8_t), # 0x24 Bits 0-2 FW Type, 3-7 Reserved (FwTypeValues) ('FWSKU', uint8_t), # 0x25 Bits 0-2 FW SKU, 3-7 Reserved (FwSkuIdValues) ('NVMCompatibility',uint32_t), # 0x26 Bits 0-1 NVM (00 Undefined, 01 UFS, 10 SPI, 11 Reserved), 2-31 Reserved ('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]) pt.add_row(['Partition Name', self.PartitionName.decode('utf-8')]) pt.add_row(['Version Control Number', '%d' % self.VCN]) pt.add_row(['Hash Usages', ', '.join(map(str, hash_usages))]) pt.add_row(['ARB Security Version Number', '%d' % self.ARBSVN]) pt.add_row(['Firmware Type', ext15_fw_type[f1] if f1 in ext15_fw_type else 'Unknown (%d)' % f1]) pt.add_row(['Firmware Type Reserved', '0x%X' % f2]) pt.add_row(['Firmware SKU', ext15_fw_sku[f3][0] if f3 in ext15_fw_sku else 'Unknown (%d)' % f3]) pt.add_row(['Firmware SKU Reserved', '0x%X' % f4]) 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 fw_sub_type = CSE_Ext_0F_R2_GetFWSKU() 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 class CSE_Ext_0F_R2_FWType(ctypes.LittleEndianStructure): _fields_ = [ ('FWType', uint8_t, 3), ('Reserved', uint8_t, 5) ] class CSE_Ext_0F_R2_GetFWType(ctypes.Union): _fields_ = [ ('b', CSE_Ext_0F_R2_FWType), ('asbytes', uint8_t) ] class CSE_Ext_0F_R2_FWSKU(ctypes.LittleEndianStructure): _fields_ = [ ('FWSKU', uint8_t, 3), ('Reserved', uint8_t, 5) ] class CSE_Ext_0F_R2_GetFWSKU(ctypes.Union): _fields_ = [ ('b', CSE_Ext_0F_R2_FWSKU), ('asbytes', uint8_t) ] class CSE_Ext_0F_R2_NVMCompatibility(ctypes.LittleEndianStructure): _fields_ = [ ('NVMCompatibility', uint32_t, 2), ('Reserved', uint32_t, 30) ] class CSE_Ext_0F_R2_GetNVMCompatibility(ctypes.Union): _fields_ = [ ('b', CSE_Ext_0F_R2_NVMCompatibility), ('asbytes', uint32_t) ] class CSE_Ext_0F_Mod(ctypes.LittleEndianStructure) : # R1 - (SIGNED_PACKAGE_INFO_EXT_ENTRY) _pack_ = 1 _fields_ = [ ("Name", char*12), # 0x00 ("Type", uint8_t), # 0x0C 0 Process, 1 Shared Library, 2 Data, 3 Independent (MODULE_TYPES) ("HashAlgorithm", uint8_t), # 0x0D 0 None, 1 SHA-1, 2 SHA-256 ("HashSize", uint16_t), # 0x0E ("MetadataSize", uint32_t), # 0x10 ("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]) pt.add_row(['Hash Algorithm', HashAlgorithm]) 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_ = [ ('Name', char*12), # 0x00 ('Type', uint8_t), # 0x0C 0 Process, 1 Shared Library, 2 Data, 3 Independent (MODULE_TYPES) ('HashAlgorithm', uint8_t), # 0x0D 0 None, 1 SHA-1, 2 SHA-256, 3 SHA-384 ('HashSize', uint16_t), # 0x0E ('MetadataSize', uint32_t), # 0x10 ('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]) pt.add_row(['Hash Algorithm', HashAlgorithm]) 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_ = [ ('Name', char*12), # 0x00 ('Type', uint8_t), # 0x0C 0 Process, 1 Shared Library, 2 Data, 3 Independent (MODULE_TYPES) ('ModuleSVN', uint8_t), # 0x0D ('HashSize', uint16_t), # 0x0E ('MetadataSize', uint32_t), # 0x10 ('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]) pt.add_row(['Module SVN', self.ModuleSVN]) 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) _pack_ = 1 _fields_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('Type', uint32_t), # 0x08 1 = BootLoader (IUNP), 2 = Firmware (IUNM) ('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_ = [ ('Chunk', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('Day', uint8_t), # 0x08 ('Month', uint8_t), # 0x09 ('Year', uint16_t), # 0x0A ('Hash', uint32_t*8), # 0x0C SHA-256 Big Endian ('Unknown0', uint32_t), # 0x2C ('Unknown1', uint32_t), # 0x30 Base Address ? (start of Chunk) ('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]) pt.add_row(['Date', Date]) pt.add_row(['Hash', Hash]) 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_ = [ ('Chunk', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('Day', uint8_t), # 0x08 ('Month', uint8_t), # 0x09 ('Year', uint16_t), # 0x0A ('Hash', uint32_t*12), # 0x0C SHA-384 Big Endian ('Unknown0', uint32_t), # 0x3C ('Unknown1', uint32_t), # 0x40 Base Address ? (start of Chunk) ('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]) pt.add_row(['Date', Date]) pt.add_row(['Hash', Hash]) 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) _pack_ = 1 _fields_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('IMRType', uint32_t), # 0x08 ('Reserved', uint32_t*6), # 0x0C ('Version', uint32_t), # 0x24 ('Hash', uint32_t*8), # 0x28 SHA-256 Big Endian ('OffsetBase', uint32_t), # 0x48 ('OffsetLimit', uint32_t), # 0x4C OffsetLimit - OffsetBase = Actual ($CPD) Size ('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]) pt.add_row(['IMR Type', '0x%X' % self.IMRType]) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) pt.add_row(['Version', '0x%X' % self.Version]) pt.add_row(['Hash', Hash]) 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_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('IMRType', uint32_t), # 0x08 ('Reserved', uint32_t*6), # 0x0C ('Version', uint32_t), # 0x24 ('Hash', uint32_t*12), # 0x28 SHA-384 Big Endian ('OffsetBase', uint32_t), # 0x58 ('OffsetLimit', uint32_t), # 0x5C OffsetLimit - OffsetBase = Actual ($CPD) Size ('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]) pt.add_row(['IMR Type', '0x%X' % self.IMRType]) pt.add_row(['Reserved', '0x%X' % int.from_bytes(self.Reserved, 'little')]) pt.add_row(['Version', '0x%X' % self.Version]) pt.add_row(['Hash', Hash]) 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_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("ModuleCount", uint32_t), # 0x08 Range Count ("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) _pack_ = 1 _fields_ = [ ('Unknown0', uint32_t), # 0x00 ('Unknown1', uint32_t), # 0x04 ('Unknown2', uint32_t), # 0x08 ('Unknown3', uint32_t), # 0x0C ('Unknown4', uint64_t), # 0x10 ('Unknown5', uint32_t), # 0x18 ('Unknown6', uint32_t), # 0x1C ('Unknown7', uint64_t), # 0x20 ('Unknown8', uint32_t), # 0x28 ('Unknown9', uint32_t), # 0x2C ('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]) pt.add_row(['Unknown 2', '0x%X' % self.Unknown2]) pt.add_row(['Unknown 3', '0x%X' % self.Unknown3]) pt.add_row(['Unknown 4', '0x%X' % self.Unknown4]) pt.add_row(['Unknown 5', '0x%X' % self.Unknown5]) pt.add_row(['Unknown 6', '0x%X' % self.Unknown6]) pt.add_row(['Unknown 7', '0x%X' % self.Unknown7]) 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) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("IBBNEMSize", uint32_t), # 0x08 in 4K pages (NEM: No Evict Mode or CAR: Cache as RAM) ("IBBLHashAlg", uint32_t), # 0x0C 0 None, 1 SHA-1, 2 SHA-256 ("IBBLHashSize", uint32_t), # 0x10 ("IBBLHash", uint32_t*8), # 0x14 Big Endian ("IBBHashAlg", uint32_t), # 0x34 0 None, 1 SHA-1, 2 SHA-256 ("IBBHashSize", uint32_t), # 0x38 ("IBBHash", uint32_t*8), # 0x3C Big Endian ("OBBHashAlg", uint32_t), # 0x5C 0 None, 1 SHA-1, 2 SHA-256 ("OBBHashSize", uint32_t), # 0x60 ("OBBHash", uint32_t*8), # 0x64 Big Endian ("IBBFlags", uint32_t), # 0x84 Unknown/Unused ("IBBMCHBar", uint64_t), # 0x88 ("IBBVTDBar", uint64_t), # 0x90 ("PMRLBase", uint32_t), # 0x98 ("PMRLLimit", uint32_t), # 0x9C ("PMRHBase", uint32_t), # 0xA0 ("PMRHLimit", uint32_t), # 0xA4 ("IBBEntryPoint", uint32_t), # 0xA8 ("IBBSegmentCount", uint32_t), # 0xAC ("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')) OBBHash = '%0.*X' % (0x20 * 2, int.from_bytes(self.OBBHash, 'big')) 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]) pt.add_row(['No Evict Mode Size', '0x%X' % (self.IBBNEMSize * 4096)]) pt.add_row(['IBBL Hash Algorithm', IBBLHashAlg]) pt.add_row(['IBBL Hash Size', '0x%X' % self.IBBLHashSize]) pt.add_row(['IBBL Hash', IBBLHash]) pt.add_row(['IBB Hash Algorithm', IBBHashAlg]) pt.add_row(['IBB Hash Size', '0x%X' % self.IBBHashSize]) pt.add_row(['IBB Hash', IBBHash]) pt.add_row(['OBB Hash Algorithm', OBBHashAlg]) pt.add_row(['OBB Hash Size', '0x%X' % self.OBBHashSize]) pt.add_row(['OBB Hash', OBBHash]) pt.add_row(['IBB Flags', '0x%X' % self.IBBFlags]) pt.add_row(['IBB MCH Bar', '0x%X' % self.IBBMCHBar]) pt.add_row(['IBB VTD Bar', '0x%X' % self.IBBVTDBar]) pt.add_row(['PMRL Base', '0x%X' % self.PMRLBase]) pt.add_row(['PMRL Limit', '0x%X' % self.PMRLLimit]) pt.add_row(['PMRH Base', '0x%X' % self.PMRHBase]) pt.add_row(['PMRH Limit', '0x%X' % self.PMRHLimit]) 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_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("IBBNEMSize", uint32_t), # 0x08 in 4K pages (NEM: No Evict Mode or CAR: Cache as RAM) ("IBBLHashAlg", uint32_t), # 0x0C 0 None, 1 SHA-1, 2 SHA-256, 3 SHA-384 ("IBBLHashSize", uint32_t), # 0x10 ("IBBLHash", uint32_t*12), # 0x14 Big Endian ("IBBHashAlg", uint32_t), # 0x44 0 None, 1 SHA-1, 2 SHA-256, 3 SHA-384 ("IBBHashSize", uint32_t), # 0x48 ("IBBHash", uint32_t*12), # 0x4C Big Endian ("OBBHashAlg", uint32_t), # 0x7C 0 None, 1 SHA-1, 2 SHA-256, 3 SHA-384 ("OBBHashSize", uint32_t), # 0x80 ("OBBHash", uint32_t*12), # 0x84 Big Endian ("IBBFlags", uint32_t), # 0xB4 Unknown/Unused ("IBBMCHBar", uint64_t), # 0xB8 ("IBBVTDBar", uint64_t), # 0xC0 ("PMRLBase", uint32_t), # 0xC8 ("PMRLLimit", uint32_t), # 0xCC ("PMRHBase", uint32_t), # 0xD0 ("PMRHLimit", uint32_t), # 0xD4 ("IBBEntryPoint", uint32_t), # 0xD8 ("IBBSegmentCount", uint32_t), # 0xDC ("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')) OBBHash = '%0.*X' % (0x30 * 2, int.from_bytes(self.OBBHash, 'big')) 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]) pt.add_row(['No Evict Mode Size', '0x%X' % (self.IBBNEMSize * 4096)]) pt.add_row(['IBBL Hash Algorithm', IBBLHashAlg]) pt.add_row(['IBBL Hash Size', '0x%X' % self.IBBLHashSize]) pt.add_row(['IBBL Hash', IBBLHash]) pt.add_row(['IBB Hash Algorithm', IBBHashAlg]) pt.add_row(['IBB Hash Size', '0x%X' % self.IBBHashSize]) pt.add_row(['IBB Hash', IBBHash]) pt.add_row(['OBB Hash Algorithm', OBBHashAlg]) pt.add_row(['OBB Hash Size', '0x%X' % self.OBBHashSize]) pt.add_row(['OBB Hash', OBBHash]) pt.add_row(['IBB Flags', '0x%X' % self.IBBFlags]) pt.add_row(['IBB MCH Bar', '0x%X' % self.IBBMCHBar]) pt.add_row(['IBB VTD Bar', '0x%X' % self.IBBVTDBar]) pt.add_row(['PMRL Base', '0x%X' % self.PMRLBase]) pt.add_row(['PMRL Limit', '0x%X' % self.PMRLLimit]) pt.add_row(['PMRH Base', '0x%X' % self.PMRHBase]) pt.add_row(['PMRH Limit', '0x%X' % self.PMRHLimit]) 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) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("Minor", uint8_t), # 0x08 ("Major", uint8_t), # 0x09 ("Reserved0", uint8_t), # 0x0A ("Reserved1", uint8_t), # 0x0B ("OEMID", uint16_t), # 0x0C ("PlatformID", uint16_t), # 0x0E ("MachineID", uint32_t*4), # 0x10 ("SaltID", uint32_t), # 0x20 ("PublicKey", uint32_t*64), # 0x24 ("PublicExponent", uint32_t), # 0x88 ("IFWIRegionCount", uint32_t), # 0x8C Number of eMMC/UFS components (LBPs) ("Flags", uint32_t), # 0x90 Unknown/Unused ("Reserved2", uint32_t), # 0x94 ("Reserved3", uint32_t), # 0x98 ("Reserved4", uint32_t), # 0x9C ("Reserved5", uint32_t), # 0xA0 ("ChunkSize", uint32_t), # 0xA4 0x10000 (64KB) ("ChunkCount", uint32_t), # 0xA8 # 0xAC ] 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]) pt.add_row(['Minor', '%d' % self.Minor]) pt.add_row(['Major', '%d' % self.Major]) pt.add_row(['Reserved 0', '0x%X' % self.Reserved0]) pt.add_row(['Reserved 1', '0x%X' % self.Reserved1]) pt.add_row(['OEM ID', '0x%0.4X' % self.OEMID]) pt.add_row(['Platform ID', '0x%0.4X' % self.PlatformID]) pt.add_row(['Machine ID', '0x%X' % int.from_bytes(self.MachineID, 'little')]) pt.add_row(['Salt ID', '0x%0.8X' % self.SaltID]) pt.add_row(['Public Key', '%s [...]' % PublicKey[:8]]) pt.add_row(['Public Exponent', '0x%X' % self.PublicExponent]) pt.add_row(['IFWI Region Count', '%d' % self.IFWIRegionCount]) pt.add_row(['Flags', '0x%X' % self.Flags]) pt.add_row(['Reserved 2', '0x%X' % self.Reserved2]) pt.add_row(['Reserved 3', '0x%X' % self.Reserved3]) pt.add_row(['Reserved 4', '0x%X' % self.Reserved4]) 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) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("Minor", uint8_t), # 0x08 ("Major", uint8_t), # 0x09 ("Reserved0", uint8_t), # 0x0A ("Reserved1", uint8_t), # 0x0B ("OEMID", uint16_t), # 0x0C ("PlatformID", uint16_t), # 0x0E ("MachineID", uint32_t*4), # 0x10 ("SaltID", uint32_t), # 0x20 ("PublicKey", uint32_t*64), # 0x24 ("PublicExponent", uint32_t), # 0x124 ("IFWIRegionCount", uint32_t), # 0x128 Number of eMMC/UFS components (LBPs) ("Flags", uint32_t), # 0x12C Unknown/Unused ("Reserved2", uint8_t), # 0x12D ("Reserved3", uint8_t), # 0x12E ("Reserved4", uint8_t), # 0x12F ("Reserved5", uint8_t), # 0x130 ("HashArrHdrMajor", uint8_t), # 0x131 ("HashArrHdrMinor", uint8_t), # 0x132 ("HashArrHdrCount", uint16_t), # 0x133 ("Reserved6", uint8_t), # 0x135 ("HashArrHashAlg", uint8_t), # 0x136 0 None, 1 SHA-1, 2 SHA-256 ("HashArrHashSize", uint16_t), # 0x137 ("ChunkHashAlg", uint8_t), # 0x139 0 None, 1 SHA-1, 2 SHA-256 ("Reserved7", uint8_t), # 0x13A ("Reserved8", uint8_t), # 0x13B ("Reserved9", uint8_t), # 0x13C ("ChunkHashSize", uint16_t), # 0x13D ("Reserved10", uint8_t), # 0x13F ("Reserved11", uint8_t), # 0x140 ("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]) pt.add_row(['Minor', '%d' % self.Minor]) pt.add_row(['Major', '%d' % self.Major]) pt.add_row(['Reserved 0', '0x%X' % self.Reserved0]) pt.add_row(['Reserved 1', '0x%X' % self.Reserved1]) pt.add_row(['OEM ID', '0x%0.4X' % self.OEMID]) pt.add_row(['Platform ID', '0x%0.4X' % self.PlatformID]) pt.add_row(['Machine ID', '0x%X' % int.from_bytes(self.MachineID, 'little')]) pt.add_row(['Salt ID', '0x%0.8X' % self.SaltID]) pt.add_row(['Public Key', '%s [...]' % PublicKey[:8]]) pt.add_row(['Public Exponent', '0x%X' % self.PublicExponent]) pt.add_row(['IFWI Region Count', '%d' % self.IFWIRegionCount]) pt.add_row(['Flags', '0x%X' % self.Flags]) pt.add_row(['Reserved 2', '0x%X' % self.Reserved2]) pt.add_row(['Reserved 3', '0x%X' % self.Reserved3]) pt.add_row(['Reserved 4', '0x%X' % self.Reserved4]) pt.add_row(['Reserved 5', '0x%X' % self.Reserved5]) pt.add_row(['Hashes Array Header Major', '%d' % self.HashArrHdrMajor]) pt.add_row(['Hashes Array Header Minor', '%d' % self.HashArrHdrMinor]) pt.add_row(['Hashes Array Header Count', '%d' % self.HashArrHdrCount]) pt.add_row(['Reserved 6', '0x%X' % self.Reserved6]) pt.add_row(['Hashes Array Hash Algorithm', HashArrHashAlg]) pt.add_row(['Hashes Array Hash Size', '0x%X' % self.HashArrHashSize]) pt.add_row(['IFWI Chunk Hash Algorithm', ChunkHashAlg]) pt.add_row(['Reserved 7', '0x%X' % self.Reserved7]) pt.add_row(['Reserved 8', '0x%X' % self.Reserved8]) pt.add_row(['Reserved 9', '0x%X' % self.Reserved9]) pt.add_row(['IFWI Chunk Hash Size', '0x%X' % self.ChunkHashSize]) 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_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('Minor', uint8_t), # 0x08 ('Major', uint8_t), # 0x09 ('Reserved0', uint8_t), # 0x0A ('Reserved1', uint8_t), # 0x0B ('OEMID', uint16_t), # 0x0C ('PlatformID', uint16_t), # 0x0E ('MachineID', uint32_t*4), # 0x10 ('SaltID', uint32_t), # 0x20 ('PublicKey', uint32_t*96), # 0x24 ('PublicExponent', uint32_t), # 0x1A4 ('IFWIRegionCount', uint32_t), # 0x1A8 Number of eMMC/UFS components (LBPs) ('Flags', uint32_t), # 0x1AC Unknown/Unused ('Reserved2', uint8_t), # 0x1AD ('Reserved3', uint8_t), # 0x1AE ('Reserved4', uint8_t), # 0x1AF ('Reserved5', uint8_t), # 0x1B0 ('HashArrHdrMajor', uint8_t), # 0x1B1 ('HashArrHdrMinor', uint8_t), # 0x1B2 ('HashArrHdrCount', uint16_t), # 0x1B3 ('Reserved6', uint8_t), # 0x1B5 ('HashArrHashAlg', uint8_t), # 0x1B6 0 None, 1 SHA-1, 2 SHA-256, 3 SHA-384 ('HashArrHashSize', uint16_t), # 0x1B7 ('ChunkHashAlg', uint8_t), # 0x1B9 0 None, 1 SHA-1, 2 SHA-256, 3 SHA-384 ('Reserved7', uint8_t), # 0x1BA ('Reserved8', uint8_t), # 0x1BB ('Reserved9', uint8_t), # 0x1BC ('ChunkHashSize', uint16_t), # 0x1BD ('Reserved10', uint8_t), # 0x1BF ('Reserved11', uint8_t), # 0x1C0 ('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]) pt.add_row(['Minor', '%d' % self.Minor]) pt.add_row(['Major', '%d' % self.Major]) pt.add_row(['Reserved 0', '0x%X' % self.Reserved0]) pt.add_row(['Reserved 1', '0x%X' % self.Reserved1]) pt.add_row(['OEM ID', '0x%0.4X' % self.OEMID]) pt.add_row(['Platform ID', '0x%0.4X' % self.PlatformID]) pt.add_row(['Machine ID', '0x%X' % int.from_bytes(self.MachineID, 'little')]) pt.add_row(['Salt ID', '0x%0.8X' % self.SaltID]) pt.add_row(['Public Key', '%s [...]' % PublicKey[:8]]) pt.add_row(['Public Exponent', '0x%X' % self.PublicExponent]) pt.add_row(['IFWI Region Count', '%d' % self.IFWIRegionCount]) pt.add_row(['Flags', '0x%X' % self.Flags]) pt.add_row(['Reserved 2', '0x%X' % self.Reserved2]) pt.add_row(['Reserved 3', '0x%X' % self.Reserved3]) pt.add_row(['Reserved 4', '0x%X' % self.Reserved4]) pt.add_row(['Reserved 5', '0x%X' % self.Reserved5]) pt.add_row(['Hashes Array Header Major', '%d' % self.HashArrHdrMajor]) pt.add_row(['Hashes Array Header Minor', '%d' % self.HashArrHdrMinor]) pt.add_row(['Hashes Array Header Count', '%d' % self.HashArrHdrCount]) pt.add_row(['Reserved 6', '0x%X' % self.Reserved6]) pt.add_row(['Hashes Array Hash Algorithm', HashArrHashAlg]) pt.add_row(['Hashes Array Hash Size', '0x%X' % self.HashArrHashSize]) pt.add_row(['IFWI Chunk Hash Algorithm', ChunkHashAlg]) pt.add_row(['Reserved 7', '0x%X' % self.Reserved7]) pt.add_row(['Reserved 8', '0x%X' % self.Reserved8]) pt.add_row(['Reserved 9', '0x%X' % self.Reserved9]) pt.add_row(['IFWI Chunk Hash Size', '0x%X' % self.ChunkHashSize]) 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) _pack_ = 1 _fields_ = [ ("HashArrSize", uint32_t), # 0x0 dwords ("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_ = [ ("HashArrSize", uint32_t), # 0x0 dwords ("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_ = [ ('Unknown', uint32_t), # 0x00 # 0 LBP 1, 1 LBP2, 4 SPI (?) ('RegionOffset', uint32_t), # 0x04 # Start offset from rcipifwi file base ('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) _pack_ = 1 _fields_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("ExtVersion", uint32_t), # 0x08 ("PayloadVersion", uint32_t), # 0x0C ("PartIDCount", uint32_t), # 0x10 ("TokenType", uint32_t), # 0x14 (TokenIdValues, tokens_list_broxton) ("Flags", uint32_t), # 0x18 ("ExpirationSec", uint32_t), # 0x1C From Time Base ("ManufLot", uint32_t), # 0x20 ("Reserved", uint32_t*4), # 0x24 # 0x34 ] def ext_print(self) : fvalue = ['No','Yes'] frvalue = ['Yes','No'] token_ids = { 1: 'Intel Unlock', 2: 'IDLM Unlock', # Intel Debug Launch Module 3: 'OEM Unlock', 4: 'PAVP Unlock', 5: 'Visa Override', 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]) pt.add_row(['Extension Version', '%d' % self.ExtVersion]) pt.add_row(['Payload Version', '%d' % self.PayloadVersion]) pt.add_row(['Part ID Count', '%d' % self.PartIDCount]) pt.add_row(['Token Type', token_ids[self.TokenType] if self.TokenType in token_ids else 'Unknown']) pt.add_row(['Single Boot', fvalue[f1]]) pt.add_row(['Part Restricted', frvalue[f2]]) pt.add_row(['Anti-Replay', frvalue[f3]]) pt.add_row(['Time Limited', frvalue[f4]]) pt.add_row(['Manufacturing Lot Restrict', fvalue[f5]]) pt.add_row(['Manufacturing Part ID', fvalue[f6]]) pt.add_row(['Flags Reserved', '0x%X' % f7]) 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), ('PartRestricted', uint32_t, 1), ('AntiReplay', uint32_t, 1), ('TimeLimited', uint32_t, 1), ('ManufacturingLotRestrict', uint32_t, 1), ('ManufacturingPartID', uint32_t, 1), ('Reserved', uint32_t, 26) ] class CSE_Ext_15_GetFlags(ctypes.Union): _fields_ = [ ('b', CSE_Ext_15_Flags), ('asbytes', uint32_t) ] class CSE_Ext_15_PartID(ctypes.LittleEndianStructure) : # After CSE_Ext_15 (SECURE_TOKEN_PARTID) _pack_ = 1 _fields_ = [ ("PartID", uint32_t*3), # 0x00 ("Nonce", uint32_t), # 0x0C ("TimeBase", uint32_t), # 0x10 # 0x14 ] 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) _pack_ = 1 _fields_ = [ ("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) _pack_ = 1 _fields_ = [ ("ID", uint32_t), # 0x00 (KnobIdValues) ("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 self.major = major self.minor = minor self.hotfix = hotfix self.build = build self.year = year self.month = month self.variant_p = variant_p def ext_print(self) : knob_ids = { 0x80860001 : ['Intel Unlock', ['None', 'Intel/RED', 'DAM']], 0x80860002 : ['OEM Unlock', ['None', 'OEM/ORANGE', 'DAM']], 0x80860003 : ['PAVP Unlock', ['Disabled', 'Enabled']], 0x80860010 : ['Allow Visa Override', ['Disabled', 'Enabled']], 0x80860011 : ['Enable DCI', ['No', 'Yes']], 0x80860020 : ['ISH GDB Support', ['Disabled', 'Enabled']], 0x80860030 : ['Boot Guard & CPU Run Control', ['Nothing', 'Disabled', 'No Enforcement', 'No Timeouts', 'No Enforcement & Timeouts']] \ if self.variant == 'CSME' and self.major >= 12 else ['BIOS Secure Boot', ['Enforced', 'Allow RnD Keys & Policies', 'Disabled']], 0x80860031 : ['Audio FW Authentication', ['Enforced', 'Allow RnD Keys', 'Disabled']], 0x80860032 : ['ISH FW Authentication', ['Enforced', 'Allow RnD Keys', 'Disabled']], 0x80860033 : ['IUNIT FW Authentication', ['Enforced', 'Allow RnD Keys', 'Disabled']], 0x80860040 : ['Anti-Rollback', ['Enabled', 'Disabled']], # (BtGuardArbOemKeyManifest) 0x80860050 : ['PSF and System Agent Debug', ['PSF & System Agent Disabled', 'System Agent Enabled', 'PSF Enabled', 'PSF & System Agent Enabled']], # (KnobIdValues) 0x80860051 : ['OEM BIOS Payload', ['Disabled', 'Enabled']], # (KnobIdValues, lkf_knobs_values) 0x80860052 : ['Intel BIOS Payload', ['Disabled', 'Enabled']], # (lkf_knobs_values) 0x80860060 : ['Debug/CDF Unlock', ['Disabled', 'Enabled']], # (bxt_knobs_values) 0x80860070 : ['Cancel OEM Signing', {0: 'Do Nothing', 0xFFFFFFFF: 'Cancel'}], 0x80860075 : ['CSE Tracing', {1: 'Enabled', 4: 'Disabled'}], # (lkf_knobs_values) 0x80860080 : ['Debug Interface (USB2.DBC)', ['Disabled', 'Enabled']], # (bxt_knobs_values) 0x80860090 : ['Authorized Debug (JTAG)', {1: 'Temporary re-enable TAP network', 16: 'Temporary re-enable BSCAN', 17: 'Temporary re-enable TAP network and BSCAN', 256: 'Permanently re-enable TAP network and BSCAN'}], # (bxt_knobs_values) 0x80860101 : ['DnX Capabilities', ['Get NVM Properties', 'NVM Configuration', 'Clear Platform Configuration', 'Write NVM Content', 'Read NVM Content']] \ if (self.variant == 'CSME' and self.major >= 15 or self.variant == 'CSTXE' and self.major >= 5 or self.variant == 'CSSPS' and self.major >= 6) else \ ['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) _pack_ = 1 _fields_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('PartitionName', char*4), # 0x08 ('PartitionSize', uint32_t), # 0x0C Complete original/RGN size before any process have been removed by the OEM or firmware update process ('PartitionVerMin', uint16_t), # 0x10 ('PartitionVerMaj', uint16_t), # 0x12 ('DataFormatMinor', uint16_t), # 0x14 ('DataFormatMajor', uint16_t), # 0x16 ('InstanceID', uint32_t), # 0x18 ('Flags', uint32_t), # 0x1C Used at CSE_Ext_03 as well, remember to change both! ('HashAlgorithm', uint8_t), # 0x20 0 None, 1 SHA-1, 2 SHA-256 ('HashSize', uint8_t*3), # 0x21 ('Hash', uint32_t*8), # 0x24 Complete original/RGN partition covering everything except for the Manifest ($CPD - $MN2 + Data) ('FlagsPrivate', uint32_t), # 0x44 ('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]) pt.add_row(['Partition Name', self.PartitionName.decode('utf-8')]) pt.add_row(['Partition Size', '0x%X' % self.PartitionSize]) pt.add_row(['Partition Version', '%X.%X' % (self.PartitionVerMaj, self.PartitionVerMin)]) pt.add_row(['Data Format Version', '%d.%d' % (self.DataFormatMajor, self.DataFormatMinor)]) pt.add_row(['Instance ID', '0x%0.8X' % self.InstanceID]) pt.add_row(['Support Multiple Instances', fvalue[f1]]) pt.add_row(['Support API Version Based Update', fvalue[f2]]) pt.add_row(['Action On Update', '0x%X' % f3]) pt.add_row(['Obey Full Update Rules', fvalue[f4]]) pt.add_row(['IFR Enable Only', fvalue[f5]]) pt.add_row(['Allow Cross Point Update', fvalue[f6]]) pt.add_row(['Allow Cross Hotfix Update', fvalue[f7]]) pt.add_row(['Partial Update Only', fvalue[f8]]) pt.add_row(['Not Measured', fvalue[f9]]) pt.add_row(['Flags Reserved', '0x%X' % f10]) pt.add_row(['Hash Type', HashAlgorithm]) pt.add_row(['Hash Size', '0x%X' % int.from_bytes(self.HashSize, 'little')]) pt.add_row(['Partition Hash', Hash]) 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_R2(ctypes.LittleEndianStructure) : # R2 - IFWI Partition Information (IFWI_PARTITION_MANIFEST_EXTENSION) _pack_ = 1 _fields_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('PartitionName', char*4), # 0x08 ('PartitionSize', uint32_t), # 0x0C Complete original/RGN size before any process have been removed by the OEM or firmware update process ('PartitionVerMin', uint16_t), # 0x10 ('PartitionVerMaj', uint16_t), # 0x12 ('DataFormatMinor', uint16_t), # 0x14 ('DataFormatMajor', uint16_t), # 0x16 ('InstanceID', uint32_t), # 0x18 ('Flags', uint32_t), # 0x1C ('HashAlgorithm', uint8_t), # 0x20 0 None, 1 SHA-1, 2 SHA-256, 3 SHA-384 ('HashSize', uint8_t*3), # 0x21 ('Hash', uint32_t*12), # 0x24 Complete original/RGN partition covering everything except for the Manifest ($CPD - $MN2 + Data) ('FlagsPrivate', uint32_t), # 0x54 ('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]) pt.add_row(['Partition Name', self.PartitionName.decode('utf-8')]) pt.add_row(['Partition Size', '0x%X' % self.PartitionSize]) pt.add_row(['Partition Version', '%X.%X' % (self.PartitionVerMaj, self.PartitionVerMin)]) pt.add_row(['Data Format Version', '%d.%d' % (self.DataFormatMajor, self.DataFormatMinor)]) pt.add_row(['Instance ID', '0x%0.8X' % self.InstanceID]) pt.add_row(['Support Multiple Instances', fvalue[f1]]) pt.add_row(['Support API Version Based Update', fvalue[f2]]) pt.add_row(['Action On Update', '0x%X' % f3]) pt.add_row(['Obey Full Update Rules', fvalue[f4]]) pt.add_row(['IFR Enable Only', fvalue[f5]]) pt.add_row(['Allow Cross Point Update', fvalue[f6]]) pt.add_row(['Allow Cross Hotfix Update', fvalue[f7]]) pt.add_row(['Partial Update Only', fvalue[f8]]) pt.add_row(['Not Measured', fvalue[f9]]) pt.add_row(['Flags Reserved', '0x%X' % f10]) pt.add_row(['Hash Algorithm', HashAlgorithm]) pt.add_row(['Hash Size', '0x%X' % int.from_bytes(self.HashSize, 'little')]) pt.add_row(['Partition Hash', Hash]) 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 ('SupportApiVersionBasedUpdate', uint32_t, 1), ('ActionOnUpdate', uint32_t, 2), ('ObeyFullUpdateRules', uint32_t, 1), ('IfrEnableOnly', uint32_t, 1), ('AllowCrossPointUpdate', uint32_t, 1), ('AllowCrossHotfixUpdate', uint32_t, 1), ('PartialUpdateOnly', uint32_t, 1), ('NotMeasured', uint32_t, 1), ('Reserved', uint32_t, 22) ] class CSE_Ext_16_GetFlags(ctypes.Union): _fields_ = [ ('b', CSE_Ext_16_Flags), ('asbytes', uint32_t) ] class CSE_Ext_16_FlagsPrivate(ctypes.LittleEndianStructure): _fields_ = [ ('IgnoreFwuDisablePolicy', uint32_t, 1), ('Reserved', uint32_t, 31) ] class CSE_Ext_16_GetFlagsPrivate(ctypes.Union): _fields_ = [ ('b', CSE_Ext_16_FlagsPrivate), ('asbytes', uint32_t) ] class CSE_Ext_17(ctypes.LittleEndianStructure) : # R1 - Flash Descriptor Hash (not in XML, Reverse Engineered) _pack_ = 1 _fields_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('Range1Start', uint16_t), # 0x08 Exclusion Range 1 (Intel, Manifest) ('Range1End', uint16_t), # 0x0A ('Range2Start', uint16_t), # 0x0C Exclusion Range 2 (OEM, Master Access) ('Range2End', uint16_t), # 0x0E ('Range3Start', uint16_t), # 0x10 Exclusion Range 3 (OEM, Custom) ('Range3End', uint16_t), # 0x12 ('Range4Start', uint16_t), # 0x14 Exclusion Range 4 (OEM, Custom) ('Range4End', uint16_t), # 0x16 ('Range5Start', uint16_t), # 0x18 Exclusion Range 5 (OEM, Custom) ('Range5End', uint16_t), # 0x1A ('Range6Start', uint16_t), # 0x1C Exclusion Range 6 (OEM, Custom) ('Range6End', uint16_t), # 0x1E ('Range7Start', uint16_t), # 0x20 Exclusion Range 7 (OEM, Custom) ('Range7End', uint16_t), # 0x22 ('Range8Start', uint16_t), # 0x24 Exclusion Range 8 (OEM, Custom) ('Range8End', uint16_t), # 0x26 ('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]) pt.add_row(['Range 1', '0x%X - 0x%X' % (self.Range1Start, self.Range1End)]) pt.add_row(['Range 2', '0x%X - 0x%X' % (self.Range2Start, self.Range2End)]) pt.add_row(['Range 3', '0x%X - 0x%X' % (self.Range3Start, self.Range3End)]) pt.add_row(['Range 4', '0x%X - 0x%X' % (self.Range4Start, self.Range4End)]) pt.add_row(['Range 5', '0x%X - 0x%X' % (self.Range5Start, self.Range5End)]) pt.add_row(['Range 6', '0x%X - 0x%X' % (self.Range6Start, self.Range6End)]) 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_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('Range1Start', uint16_t), # 0x08 Exclusion Range 1 (Intel, Manifest) ('Range1End', uint16_t), # 0x0A ('Range2Start', uint16_t), # 0x0C Exclusion Range 2 (OEM, Master Access) ('Range2End', uint16_t), # 0x0E ('Range3Start', uint16_t), # 0x10 Exclusion Range 3 (OEM, Custom) ('Range3End', uint16_t), # 0x12 ('Range4Start', uint16_t), # 0x14 Exclusion Range 4 (OEM, Custom) ('Range4End', uint16_t), # 0x16 ('Range5Start', uint16_t), # 0x18 Exclusion Range 5 (OEM, Custom) ('Range5End', uint16_t), # 0x1A ('Range6Start', uint16_t), # 0x1C Exclusion Range 6 (OEM, Custom) ('Range6End', uint16_t), # 0x1E ('Range7Start', uint16_t), # 0x20 Exclusion Range 7 (OEM, Custom) ('Range7End', uint16_t), # 0x22 ('Range8Start', uint16_t), # 0x24 Exclusion Range 8 (OEM, Custom) ('Range8End', uint16_t), # 0x26 ('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]) pt.add_row(['Range 1', '0x%X - 0x%X' % (self.Range1Start, self.Range1End)]) pt.add_row(['Range 2', '0x%X - 0x%X' % (self.Range2Start, self.Range2End)]) pt.add_row(['Range 3', '0x%X - 0x%X' % (self.Range3Start, self.Range3End)]) pt.add_row(['Range 4', '0x%X - 0x%X' % (self.Range4Start, self.Range4End)]) pt.add_row(['Range 5', '0x%X - 0x%X' % (self.Range5Start, self.Range5End)]) pt.add_row(['Range 6', '0x%X - 0x%X' % (self.Range6Start, self.Range6End)]) 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_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('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_ = [ ('Tag', uint32_t), # 0x00 ('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_ = [ ('HashType', uint32_t), # 0x00 ('HashAlgorithm', uint32_t), # 0x04 0 SHA-1, 1 SHA-256, 2 MD5 ('HashSize', uint32_t), # 0x08 ('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_ = [ ('HashType', uint32_t), # 0x00 ('HashAlgorithm', uint32_t), # 0x04 0 None, 1 SHA-1, 2 SHA-256, 3 SHA-384 ('HashSize', uint32_t), # 0x08 ('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_ = [ ('Name', char*4), # 0x00 ('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) _pack_ = 1 _fields_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('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_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('Project', char*4), # 0x08 ('Hotfix', uint16_t), # 0x0C Hotfix at DG1, SKU at DG2+ ('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_ = [ ('HashType', uint32_t), # 0x00 ('HashAlgorithm', uint32_t), # 0x04 0 SHA-1, 1 SHA-256, 2 MD5 ('HashSize', uint32_t), # 0x08 ('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_ = [ ('HashType', uint32_t), # 0x00 ('HashAlgorithm', uint32_t), # 0x04 0 None, 1 SHA-1, 2 SHA-256, 3 SHA-384 ('HashSize', uint32_t), # 0x08 ('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_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('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_ = [ ('Tag', uint32_t), # 0x00 ('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_ = [ ('HashType', uint32_t), # 0x00 ('HashAlgorithm', uint32_t), # 0x04 0 SHA-1, 1 SHA-256, 2 MD5 ('HashSize', uint32_t), # 0x08 ('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_ = [ ('HashType', uint32_t), # 0x00 ('HashAlgorithm', uint32_t), # 0x04 0 None, 1 SHA-1, 2 SHA-256, 3 SHA-384 ('HashSize', uint32_t), # 0x08 ('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_ = [ ('Name', char*4), # 0x00 ('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_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('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) _pack_ = 1 _fields_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 # 0x08 ] # TODO: Should be followed by a MN2_Manifest_R2, sample required 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_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('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_ = [ ('Tag', uint32_t), # 0x00 ('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) _pack_ = 1 _fields_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('KeyType', uint32_t), # 0x08 1 RoT, 2 OEM (KeyManifestTypeValues) ('OEMID', uint16_t), # 0x0C ('KeyID', uint8_t), # 0x0E Matched against Field Programmable Fuses (FPF) ('Reserved', uint8_t*12), # 0x0F ('ModuleCount', uint8_t), # 0x1B Keys Number # 0x1C ] 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]) pt.add_row(['Key Type', ['Reserved','RoT','OEM'][self.KeyType]]) pt.add_row(['OEM ID', '0x%0.4X' % self.OEMID]) 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) _pack_ = 1 _fields_ = [ ('Usage', uint16_t), # 0x00 (KeyManifestHashUsages, OemKeyManifestHashUsages) ('Flags', uint8_t), # 0x02 ('HashAlgorithm', uint8_t), # 0x03 ('MinimalSVN', uint8_t), # 0x04 ('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]]) pt.add_row(['Flags Reserved', '0x%X' % f2]) 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) _pack_ = 1 _fields_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('PartitionName', char*4), # 0x08 ('VCN', uint32_t), # 0x0C Version Control Number ('ARBSVN', uint32_t), # 0x10 FPF Anti-Rollback (ARB) Security Version Number ('Usage', uint16_t), # 0x14 (KeyManifestHashUsages, OemKeyManifestHashUsages) ('FWType', uint8_t), # 0x16 Bits 0-2 FW Type, 3-7 Reserved (FwTypeValues) ('FWSKU', uint8_t), # 0x17 Bits 0-2 FW SKU, 3-7 Reserved (FwSkuIdValues) ('ModuleCount', uint8_t), # 0x18 ('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]) pt.add_row(['Partition Name', self.PartitionName.decode('utf-8')]) pt.add_row(['Version Control Number', self.VCN]) pt.add_row(['ARB Security Version Number', self.ARBSVN]) pt.add_row(['Hash Usage', key_dict[self.Usage] if self.Usage in key_dict else 'Unknown (%d)' % self.Usage]) pt.add_row(['Firmware Type', ext15_fw_type[f1] if f1 in ext15_fw_type else 'Unknown (%d)' % f1]) pt.add_row(['Firmware Type Reserved', '0x%X' % f2]) pt.add_row(['Firmware SKU', ext15_fw_sku[f3][0] if f3 in ext15_fw_sku else 'Unknown (%d)' % f3]) 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) _pack_ = 1 _fields_ = [ ('Name', char*12), # 0x00 ('Type', uint8_t), # 0x0C 0 Process, 1 Shared Library, 2 Data, 3 Independent (MODULE_TYPES) ('HashAlgorithm', uint8_t), # 0x0D 0 None, 1 SHA-1, 2 SHA-256, 3 SHA-384 ('HashSize', uint16_t), # 0x0E ('MetadataSize', uint32_t), # 0x10 ('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]) pt.add_row(['Hash Algorithm', HashAlgorithm]) 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_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('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_ = [ ("Tag", uint32_t), # 0x00 ("Size", uint32_t), # 0x04 ("Type", char*2), # 0x08 RC Recovery, OP Operational ("Platform", char*2), # 0x08 GE Greenlow, PU Purley, HA Harrisonville, PE Purley EPO, BA Bakerville ("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) _pack_ = 1 _fields_ = [ ('Tag', uint32_t), # 0x00 ('Size', uint32_t), # 0x04 ('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) _pack_ = 1 _fields_ = [ ('Tag', char*8), # 0x00 FOOT (544F4F46) ('TotalSize', uint32_t), # 0x08 ('HeaderSize', uint32_t), # 0x0C dwords (w/o Tag) ('ModulusSize', uint32_t), # 0x10 dwords ('ExponentSize', uint32_t), # 0x14 dwords ('RSAModulus', uint32_t*96), # 0x18 3072-bits ('RSAExponent', uint32_t), # 0x198 ('RSASignature', uint32_t*96), # 0x19C 3072-bits # 0x31C ] 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]) pt.add_row(['Header Size', '0x%X' % (self.HeaderSize * 4)]) pt.add_row(['Modulus Size', '0x%X' % ModulusSize]) pt.add_row(['Exponent Size', '0x%X' % (self.ExponentSize * 4)]) 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" _pack_ = 1 _fields_ = [ ('Unknown0', uint32_t), # 0x00 ('DEV_ID', uint16_t), # 0x04 ('VEN_ID', uint16_t), # 0x06 8086 ('SizeUncomp', uint32_t), # 0x08 ('SizeComp', uint32_t), # 0x0C ('BSSSize', uint32_t), # 0x10 ('CodeSizeUncomp', uint32_t), # 0x14 ('CodeBaseAddress', uint32_t), # 0x18 ('MainThreadEntry', uint32_t), # 0x1C ('Unknown1', uint32_t), # 0x20 ('Unknown2', uint32_t), # 0x24 ('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]) pt.add_row(['Vendor ID', '0x%X' % self.VEN_ID]) pt.add_row(['Size Uncompressed', '0x%X' % self.SizeUncomp]) pt.add_row(['Size Compressed', '0x%X' % self.SizeComp]) pt.add_row(['BSS Size', '0x%X' % self.BSSSize]) pt.add_row(['Code Size Uncompressed', '0x%X' % self.CodeSizeUncomp]) pt.add_row(['Code Base Address', '0x%X' % self.CodeBaseAddress]) pt.add_row(['Main Thread Entry', '0x%X' % self.MainThreadEntry]) 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_ = [ ('Unknown0', uint32_t), # 0x00 ('DEV_ID', uint16_t), # 0x04 ('VEN_ID', uint16_t), # 0x06 8086 ('SizeUncomp', uint32_t), # 0x08 ('SizeComp', uint32_t), # 0x0C ('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]) pt.add_row(['Vendor ID', '0x%X' % self.VEN_ID]) 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_ = [ ('Unknown0', uint32_t), # 0x00 ('DEV_ID', uint16_t), # 0x04 ('VEN_ID', uint16_t), # 0x06 8086 ('SizeUncomp', uint32_t), # 0x08 ('SizeComp', uint32_t), # 0x0C ('BSSSize', uint32_t), # 0x10 ('CodeSizeUncomp', uint32_t), # 0x14 ('CodeBaseAddress', uint32_t), # 0x18 ('MainThreadEntry', uint32_t), # 0x1C ('Unknown1', uint32_t), # 0x20 ('Unknown2', uint32_t), # 0x24 ('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]) pt.add_row(['Vendor ID', '0x%X' % self.VEN_ID]) pt.add_row(['Size Uncompressed', '0x%X' % self.SizeUncomp]) pt.add_row(['Size Compressed', '0x%X' % self.SizeComp]) pt.add_row(['BSS Size', '0x%X' % self.BSSSize]) pt.add_row(['Code Size Uncompressed', '0x%X' % self.CodeSizeUncomp]) pt.add_row(['Code Base Address', '0x%X' % self.CodeBaseAddress]) pt.add_row(['Main Thread Entry', '0x%X' % self.MainThreadEntry]) 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" _pack_ = 1 _fields_ = [ ('Unknown0', uint32_t), # 0x00 ('DEV_ID', uint16_t), # 0x04 ('VEN_ID', uint16_t), # 0x06 8086 ('SizeUncomp', uint32_t), # 0x08 ('SizeComp', uint32_t), # 0x0C ('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]) pt.add_row(['Vendor ID', '0x%X' % self.VEN_ID]) 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) : rbe_pm_data_d = b'' vol_ftbl_id = -0x1 vol_ftbl_pl = -0x1 mfs_parsed_idx = None fpt_hdr_0_print = None intel_cfg_hash_mfs = None pch_init_final = [] rbe_man_hashes = [] rbe_pm_met_valid = [] rbe_pm_met_hashes = [] cse_unpack_jsons = {} len_fpt_part_all = len(fpt_part_all) len_bpdt_part_all = len(bpdt_part_all) 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 fdv_rsa_crash = fdv_status[2] # RSA Signature crashed 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) elif fdv_rsa_crash : if param.cse_pause : input_col(col_m + '\nFlash Descriptor RSA Signature is UNKNOWN!\n' + col_e) # Debug else : print(col_m + '\nFlash Descriptor RSA Signature is UNKNOWN!\n' + col_e) else : if param.cse_pause : 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) else : if param.cse_pause : 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 : if param.cse_pause : 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] : if param.cse_pause : 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' : fpt_romb_exist = False fpt_hdr_1 = get_struct(reading, fpt_start, get_fpt(reading, fpt_start)[0]) 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 : if param.cse_pause : 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] : if param.cse_pause : 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] part_start = part[1] part_end = part[2] 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 else : print(col_m + '\n RSA Signature of partition %s is UNKNOWN!' % part_name + col_e) elif mn2_signs[0] : print(col_g + '\n RSA Signature of partition %s is VALID' % part_name + col_e) else : if param.cse_pause and [mn2_signs[1],mn2_signs[2]] not in cse_known_bad_hashes : input_col(col_r + '\n RSA Signature of partition %s is INVALID!' % part_name + col_e) # Debug elif [mn2_signs[1],mn2_signs[2]] in cse_known_bad_hashes : 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) : if param.cse_verbose : print() # Print Manifest/Metadata/Key Extension Info 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] part_end = part[2] 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 else : print(col_m + '\n RSA Signature of partition %s is UNKNOWN!' % part_name + col_e) elif mn2_signs[0] : print(col_g + '\n RSA Signature of partition %s is VALID' % part_name + col_e) else : if param.cse_pause and [mn2_signs[1],mn2_signs[2]] not in cse_known_bad_hashes : input_col(col_r + '\n RSA Signature of partition %s is INVALID!' % part_name + col_e) # Debug elif [mn2_signs[1],mn2_signs[2]] in cse_known_bad_hashes : 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) : if param.cse_verbose : print() # Print Manifest/Metadata/Key Extension Info 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 # 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() orom_fname = os.path.join(out_dir, fw_name, 'OROM-PCIR Images [%d].txt' % (len_orom_hdr_all // 2)) 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: json.dump(cse_unpack_json_lists, jo, indent=4) # Analyze CSE Extensions # noinspection PyUnusedLocal def ext_anl(buffer, input_type, input_offset, file_end, ftpr_var_ver, single_man_name, mfs_idx_cfg, pch_init_input) : vcn = -1 in_id = 0 cpd_num = 0 mn2_size = -1 ext_psize = -1 mea_phash = -1 cpd_offset = -1 mn2_offset = -1 dnx_version = -1 dnx_rcip_off = -1 dnx_rcip_len = -1 cpd_hdr_size = -1 end_man_match = -1 start_man_match = -1 mn2_rsa_key_len = -1 dnx_hash_arr_off = -1 iunit_chunk_start = -1 hash_arr_valid_count = 0 chunk_hash_valid_count = 0 cpd_hdr = None mn2_hdr = None utfl_hdr = None msg_shown = False cpd_chk_ok = False oem_config = False oem_signed = False intel_cfg_ftpr = False cpd_name = '' ext_pname = '' fd_info = [] gsc_info = [] ibbp_all = [] ibbp_del = [] ext_print = [] cpd_ext_hash = [] cpd_mod_attr = [] cpd_ext_attr = [] cpd_mn2_info = [] cpd_mod_names = [] cpd_ext_names = [] mn2_hdr_print = [] gmf_blob_info = [] fwi_iup_hashes = [] cpd_wo_met_info = [] cpd_wo_met_back = [] iunit_chunk_valid = [] intel_cfg_hash_ftpr = [] ext50_info = ['UNK', 'XX'] ext15_info = [0, '', ('',''), ''] fptemp_info = [False, -1, -1] cpd_chk_info = [True,['','']] ibbp_bpm = ['IBBL', 'IBB', 'OBB'] ext12_info = [[], ('',''), 0, 0] # SKU Capabilities, SKU Type, LBG Support, SKU Platform ext_dnx_val = [-1, False, False] # [DnXVer, AllHashArrValid, AllChunkValid] ext_iunit_val = [False] # [AllChunkValid] ext_phval = [False, False, 0, 0] mn2_sigs = [False, -1, -1, True, -1, None] variant,major,minor,hotfix,build,year,month,variant_p = ftpr_var_ver # Remember to also adjust any ftpr_var_ver Classes/Constructors anl_major,anl_minor,anl_hotfix,anl_build = ftpr_var_ver[1:5] # pylint: disable=W0612 anl_meu_major,anl_meu_minor,anl_meu_hotfix,anl_meu_build = ftpr_var_ver[1:5] # pylint: disable=W0612 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 mn2_rsa_key_end = mn2_rsa_key_start + mn2_rsa_key_len # RSA Public Key End mn2_rsa_key_data = buffer[mn2_rsa_key_start:mn2_rsa_key_end] # RSA Public Key Data mn2_rsa_key_hash = get_hash(mn2_rsa_key_data, 0x20) # SHA-256 of RSA Public Key Data mn2_rsa_sig_start = mn2_rsa_key_end + mn2_rsa_exp_len # RSA Signature Start mn2_rsa_sig_end = mn2_rsa_sig_start + mn2_rsa_key_len # RSA Signature End mn2_rsa_sig_data = buffer[mn2_rsa_sig_start:mn2_rsa_sig_end] # RSA Signature Data 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, mn2_hdr.MEU_Hotfix, mn2_hdr.MEU_Build, mn2_wo_rsa_hashes, mn2_hdr, start_man_match, end_man_match] else : 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) cpd_entry_name = cpd_entry_hdr.Name.decode('utf-8') cpd_mod_names.append(cpd_entry_name) # Store each $CPD Module name cpd_entry_size = cpd_entry_hdr.Size # Uncompressed only 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 : try : intel_cfg_folder = os.path.join(out_dir, 'intl.cfg_placeholder', '') # Not used here, placeholder value for mfs_cfg_anl to work pch_init_info = mfs_cfg_anl(6, intel_cfg_data, intel_cfg_folder, intel_cfg_folder, config_rec_size, [], vol_ftbl_id, vol_ftbl_pl) # Parse MFS Configuration Records 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) : # Variable Initialization based on Single Manifest existence 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 else : cpd_offset = 0 cpd_name = single_man_name cpd_entry_offset = 0 cpd_entry_size = mn2_size cpd_entry_name = bytes(single_man_name, 'utf-8') 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] elif (variant,major) in [('CSME',15),('CSME',16),('CSSPS',6)] or (variant_p,anl_major) in [('PMC',150),('PMC',160),('PCHC',15),('PCHC',16)] or mn2_rsa_key_len == 0x180 : if ext_tag in ext_tag_rev_hdr_csme15 : hdr_rev_tag = ext_tag_rev_hdr_csme15[ext_tag] if ext_tag in ext_tag_rev_mod_csme15 : mod_rev_tag = ext_tag_rev_mod_csme15[ext_tag] elif (variant,major) == ('CSME',12) and not ((minor,hotfix) == (0,0) and build >= 7000 and year < 0x2018 and month < 0x8) or (variant,major) in [('CSME',13),('CSME',14)] : if ext_tag in ext_tag_rev_hdr_csme12 : hdr_rev_tag = ext_tag_rev_hdr_csme12[ext_tag] if ext_tag in ext_tag_rev_mod_csme12 : mod_rev_tag = ext_tag_rev_mod_csme12[ext_tag] elif (variant,major,minor,hotfix) == ('CSSPS',5,0,3) : if ext_tag in ext_tag_rev_hdr_cssps503 : hdr_rev_tag = ext_tag_rev_hdr_cssps503[ext_tag] if ext_tag in ext_tag_rev_mod_cssps503 : mod_rev_tag = ext_tag_rev_mod_cssps503[ext_tag] elif (variant,major) == ('CSSPS',5) or (variant,major,minor) == ('CSSPS',4,4) : if ext_tag in ext_tag_rev_hdr_cssps5 : hdr_rev_tag = ext_tag_rev_hdr_cssps5[ext_tag] 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 : ext_hdr = get_struct(buffer, cpd_ext_offset, ext_struct_name) # Get Extension Structure for non ext_hdr_extra Extensions 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 ext_phash_len = len(ext_hdr.Hash) * 4 # Partition Hash Length ext_phash = '%0.*X' % (ext_phash_len * 2, int.from_bytes(ext_hdr.Hash, 'little')) # Partition Hash vcn = ext_hdr.VCN # Version Control Number 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,ext_psize) == ('CSME',11,0x88000) and minor in [8,9] : (ext_phash, mea_phash) = ('IGNORE', 'IGNORE') # CSME 11.8-9 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' # Some Modules may have multiple 03/0F/16/23 and/or MetadataHash mismatch and/or double .met extension (GREAT WORK INTEL/OEMs...) 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) mod_comp_size = ext_hdr.SizeComp # Metadata's Module Compressed Size ($CPD Entry's Module Size is always Uncompressed) 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' # Some Modules may have multiple 03/0F/16/23 and/or MetadataHash mismatch and/or double .met extension (GREAT WORK INTEL/OEMs...) 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 iunit_chunk_size = chunk_hdr.Size # iUnit Module Chunk Size if chunk == 0 : iunit_chunk_start = CSE_Ext_10_iUnit_offset + chunk_hdr.Unknown1 # First Chunk starts from a Base Address ? iunit_chunk_hash_ext_len = len(chunk_hdr.Hash) * 4 # iUnit Module Chunk Intel Hash (BE) Length iunit_chunk_hash_ext = '%0.*X' % (iunit_chunk_hash_ext_len * 2, int.from_bytes(chunk_hdr.Hash, 'big')) # iUnit Module Chunk Intel Hash (BE) 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) ibb_hash = '%0.*X' % (hash_len * 2, int.from_bytes(ext_hdr.IBBHash, 'big')) # IBB Hash (BE) obb_hash = '%0.*X' % (hash_len * 2, int.from_bytes(ext_hdr.OBBHash, 'big')) # OBB Hash (BE) 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 rcip_chunk_count_ext = ext_hdr.ChunkCount # RCIP IFWI Chunk Count from Extension 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 hash_arr_prev_part_size = 0 # Step to loop through Hashes Array file sections hash_arr_hdr_count = ext_hdr.HashArrHdrCount # Hashes Array Header Count chunk_hash_size = ext_hdr.ChunkHashSize # Hashes Array Chunk Hash Size rcip_chunk_size = ext_hdr.ChunkSize # RCIP IFWI Chunk Size 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 hash_arr_part_hdr = get_struct(buffer, cpd_mod_offset + hash_arr_hdr_step, hash_arr_part_struct) 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) # Parse each IFWI Region Map 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) if in_id == 0 : in_id = ext_hdr.InstanceID # LOCL/WCOD identifier (prefer CSE_Ext_03) 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 : if (variant,major) in [('CSSPS',5),('CSSPS',6)] : (ext_phash, mea_phash) = ('IGNORE', 'IGNORE') # CSSPS 5-6 Partition Hash is always wrong, ignore 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') : iom_names = {'SAMF':'samf', 'IOMP':'iom'} tbt_names = {'TBTP':'tbt'} mg_names = {'PPHY':'pphy', 'NPHY':'nphy', 'SPHY':'sphy', 'DPHY':'dphy', 'MGPP':'mg'} tcss_types = { 1:iom_names[cpd_name] if cpd_name in iom_names else 'iom', 2:mg_names[cpd_name] if cpd_name in mg_names else 'mg', 3:tbt_names[cpd_name] if cpd_name in tbt_names else 'tbt', 4:(iom_names[cpd_name] if cpd_name in iom_names else 'iom') + '.cd', 5:(tbt_names[cpd_name] if cpd_name in tbt_names else 'tbt') + '.cd', 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' # Some Modules may have multiple 03/0F/16/23 and/or MetadataHash mismatch and/or double .met extension (GREAT WORK INTEL/OEMs...) 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 # Detect last 0x20 of UTOK/STKN for Unlock Token Flags Structure 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()) 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 # RBEP > rbe and FTPR > pm Modules contain the Compressed Size, Uncompressed Size & Hash but without Names, only hardcoded DEV_IDs # With only Huffman Yes/No bit at $CPD, we can no longer discern between Uncompressed, LZMA Compressed and Encrypted Modules # This adjustment should only be required for Huffman Modules without Metadata but MEA calculates everything just in case for i in range(len(cpd_wo_met_info)) : # All $CPD entries should be ordered by Offset in ascending order for the calculation if cpd_wo_met_info[i][6] == 1 : # Check if entry has valid Starting Offset & Size (not empty) 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 cpd_wo_met_info[i][2] = cpd_offset + ext_psize - cpd_wo_met_info[i][1] # Size is Partition End - Current Start else : # For the last entry, if CSE Extension 0x3/0x16 is missing, find its size manually via EOF 0xFF padding 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)) : if cpd_mod_attr[mod][0] == cpd_entry_name.decode('utf-8') : 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 # by its unique Name, Offset & Instance ID. If $FPT Entry size is smaller than Extension size, error is shown. # The check is skipped when Extension size is not found so no problem with OEM/FIT firmware configuration. # The check is skipped when IDLM partition (DLMP) is parsed because its $FPT size is wrong by Intel design. if not msg_shown and ext_psize != -1 and part[0] == cpd_name and part[0] != 'DLMP' \ 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 # by its unique Name, Offset & Instance ID. If BPDT Entry size is smaller than Extension size, error is shown. # The check is skipped when Extension size is not found so no problem with OEM/FIT firmware configuration. # The check is skipped when IDLM partition (DLMP) is parsed because its BPDT size is wrong by Intel design. if not msg_shown and ext_psize != -1 and part[0] == cpd_name and part[0] != 'DLMP' \ 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) : mod_comp_type = cpd_wo_met_info[i][3] # As taken from $CPD Huffman Yes/No bit 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 def mod_anl(cpd_offset, cpd_mod_attr, cpd_ext_attr, 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) : # noinspection PyUnusedLocal mea_hash_c = 0 mea_hash_u = 0 mod_hash_u_ok = False mod_anl_jsons = {} 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 : if param.cse_pause and cpd_chk_rslt not in cse_known_bad_hashes : input_col(col_r + '\n$CPD Checksum of partition "%s" is INVALID\n' % cpd_pname + col_e) # Debug elif param.cse_pause and cpd_chk_rslt in cse_known_bad_hashes : 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 mod_comp = mod[1] # Compression mod_encr = mod[2] # Encryption mod_start = mod[3] # Starting Offset mod_size_comp = mod[4] # Compressed Size mod_size_uncomp = mod[5] # Uncompressed Size mod_empty = mod[6] # Empty/Missing mod_hash = mod[7] # Hash (LZMA --> Compressed + zeros, Huffman --> Uncompressed) mod_end = mod_start + mod_size_comp # Ending Offset mn2_valid = mod[10][0] # Check if RSA Signature is valid (rsa_hash == dec_hash) mn2_sig_dec = mod[10][1] # RSA Signature Decrypted Hash 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 : if param.cse_pause : 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 else : print(col_m + '\n RSA Signature of partition "%s" is UNKNOWN' % cpd_pname + col_e) elif mn2_valid : print(col_g + '\n RSA Signature of partition "%s" is VALID' % cpd_pname + col_e) else : if param.cse_pause and [mn2_sig_dec,mn2_sig_sha] not in cse_known_bad_hashes : input_col(col_r + '\n RSA Signature of partition "%s" is INVALID' % cpd_pname + col_e) # Debug elif [mn2_sig_dec,mn2_sig_sha] in cse_known_bad_hashes : 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 print(col_g + '\n Hash of partition "%s" is VALID' % cpd_pname + col_e) elif ext_phval[0] : # Hash exists but is Invalid (CSME 11.8-9 SLM, CSSPS 5-6 & PHYP EBG Hashes are always wrong) if (variant,major,ext12_info[1][1]) == ('CSME',11,'SLM') and minor in [8,9] : print(col_r + '\n Hash of partition "%s" is INVALID (CSME 11.8 Slim Ignore)' % cpd_pname + col_e) elif (variant,major) in [('CSSPS',1),('CSSPS',5),('CSSPS',6)] or (variant,major,minor) in [('CSSPS',4,4),('CSSPS',4,2)] : print(col_r + '\n Hash of partition "%s" is INVALID (%s %d.%d Ignore)' % (cpd_pname,variant,major,minor) + col_e) elif (variant,cpd_mn2_info[0],cpd_mn2_info[10]) == ('PHYPEBG',12,0) : print(col_r + '\n Hash of partition "%s" is INVALID (%s %d Ignore)' % (cpd_pname,variant,cpd_mn2_info[0]) + col_e) elif param.cse_pause and [ext_phval[2],ext_phval[3]] not in cse_known_bad_hashes : input_col(col_r + '\n Hash of partition "%s" is INVALID' % cpd_pname + col_e) # Debug elif param.cse_pause and [ext_phval[2],ext_phval[3]] in cse_known_bad_hashes : 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] : gmf_cert_path = os.path.join(folder_name, 'GMF_Certificate.crt') with open(gmf_cert_path, 'wb') as gmf_cert : gmf_cert.write(gmf_blob_info[3][0]) 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 : if param.cse_pause and [mod_hash,mea_hash] not in cse_known_bad_hashes : input_col(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) # Debug elif param.cse_pause and [mod_hash,mea_hash] in cse_known_bad_hashes : 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 else : print(col_m + '\n RSA Signature of %s %s "%s" is UNKNOWN!' % (comp[mod_comp], mod_type, mod_name)) elif mn2_valid : print(col_g + '\n RSA Signature of %s %s "%s" is VALID' % (comp[mod_comp], mod_type, mod_name) + col_e) else : if param.cse_pause : 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 if param.cse_unpack : print(col_g + '\n Analyzing MFS Low Level File %d (%s) ...' % (mfs_file_no, mfs_dict[mfs_file_no]) + col_e) rec_folder = os.path.join(folder_name, mfs_dict[mfs_file_no], '') try : pch_init_info = mfs_cfg_anl(mfs_file_no, mod_data, rec_folder, rec_folder, config_rec_size, [], vol_ftbl_id, vol_ftbl_pl) # Parse MFS Configuration Records # noinspection PyUnusedLocal _ = pch_init_anl(pch_init_info) # Parse MFS Initialization Tables and store their Platforms/Steppings except : if param.cse_pause : 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 : if param.cse_pause and [mod_hash,mea_hash] not in cse_known_bad_hashes : input_col(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) # Debug elif param.cse_pause and [mod_hash,mea_hash] in cse_known_bad_hashes : 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 : print(col_g + '\n Checksum of %s %s "%s" is VALID' % (comp[mod_comp], mod_type, mod_name) + col_e) else : if param.cse_pause : 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) : print(col_g + '\n Hash of %s %s "%s" is VALID' % (comp[mod_comp], mod_type, mod_name) + col_e) elif mod_name == 'version' : print(col_m + '\n Hash of %s %s "%s" is UNKNOWN' % (comp[mod_comp], mod_type, mod_name) + col_e) elif param.cse_pause : input_col(col_r + '\n Hash of %s %s "%s" is INVALID' % (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) elif cpd_pname in ('IUNP','IUNM') : if (mod_name,ext_iunit_val[0]) == ('iunit',True) : print(col_g + '\n Hash of %s %s "%s" is VALID' % (comp[mod_comp], mod_type, mod_name) + col_e) elif param.cse_pause : input_col(col_r + '\n Hash of %s %s "%s" is INVALID' % (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) 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 else : if param.cse_pause and [mod_hash,mea_hash] not in cse_known_bad_hashes : input_col(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) # Debug elif param.cse_pause and [mod_hash,mea_hash] in cse_known_bad_hashes : 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) 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 : if param.cse_pause and [mod_hash,mea_hash] not in cse_known_bad_hashes : input_col(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) # Debug elif param.cse_pause and [mod_hash,mea_hash] in cse_known_bad_hashes : 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 else : if param.cse_pause and [mod_hash,mea_hash] not in cse_known_bad_hashes : input_col(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) # Debug elif param.cse_pause and [mod_hash,mea_hash] in cse_known_bad_hashes : 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 with open(mod_fname, 'wb') as mod_file: mod_file.write(mod_data_d) # Decompression complete, valid data else : if param.cse_pause and [mod_hash,mea_hash] not in cse_known_bad_hashes : input_col(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) # Debug elif param.cse_pause and [mod_hash,mea_hash] in cse_known_bad_hashes : 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) print(' MEA: %s' % mea_hash_c) elif mod_hash_u_ok : print('\n MOD: %s' % mod_hash) print(' MEA: %s' % mea_hash_u) else : 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 else : if param.cse_pause and [mod_hash,mea_hash_c] not in cse_known_bad_hashes : input_col(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) # Debug elif param.cse_pause and [mod_hash,mea_hash_c] in cse_known_bad_hashes : 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 : print(' MEA: %s' % mea_hash_c) elif mod_hash_u_ok : print(' MEA: %s' % mea_hash_u) 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 with open(mod_fname, 'wb') as mod_file: mod_file.write(mod_data_d) # Decompression complete, valid data elif mod_hash_u_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_u) # Store valid RBEP > rbe or FTPR > pm Hash to single out leftovers with open(mod_fname, 'wb') as mod_file: mod_file.write(mod_data_d) # Decompression complete, valid data else : if param.cse_pause and [mod_hash,mea_hash_c] not in cse_known_bad_hashes : input_col(col_r + '\n Hash of %s %s "%s" is INVALID' % (comp[mod_comp], mod_type, mod_name) + col_e) # Debug elif param.cse_pause and [mod_hash,mea_hash_c] in cse_known_bad_hashes : 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 : for index in range(0, ext_print_len, 2) : # Only Name (index), skip Info (index + 1) if str(ext_print[index]).startswith(mod_name) : if param.cse_verbose : print() # Print Manifest/Metadata/Key Extension Info 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 ftbl_id_msg = col_m + '%sWarning: Could not find File System Dictionary 0x%0.2X (%s) > 0x%0.2X, assuming 0x0A!' % ( 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 ftbl_pl_msg = col_m + '%sWarning: Could not find File System Platform 0x%0.2X (%s), assuming 0x01 (ICP)!' % ( 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 def get_sec_hdr_size(variant,major,minor,hotfix,vol_ftbl_pl) : if (variant,major,minor) == ('CSME',14,5) : return 0x34 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 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 # noinspection PyUnusedLocal 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 def get_mfs_anl(mfs_state, mfs_parsed_idx, intel_cfg_hash_mfs, mfs_info, pch_init_final) : 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 # noinspection PyUnusedLocal def mfs_anl(mfs_folder, mfs_start, mfs_end, variant, vol_ftbl_id, vol_ftbl_pl, mfs_is_afs) : mfs_buffer_init = reading[mfs_start:mfs_end] # MFS Initial Buffer 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 vfs_starts_at_0 = get_vfs_start_0(variant,major,minor,hotfix) # Get CSE File System Files start at 0 or not page_size = 0x2000 # MFS Page Length chunk_all_size = 0x42 # MFS Chunk Payload + CRC Length chunk_raw_size = chunk_all_size - 2 # MFS Chunk Payload index_size_sys = 0x2 # MFS System Page Index Entry Length index_size_dat = 0x1 # MFS Data Page Index Entry Length page_hdr_size = 0x12 # MFS Page Header Structure Size vol_hdr_size = 0xE # MFS Volume Header Structure Size chunks_count_sys = 0xFFFF # MFS Actual System Chunks Count mfs_info = [] # MFS Initial Info Printing mfs_files = [] # MFS Low Level Files Numbers & Contents mfs_tmp_page = [] # MFS Temporary Pages Message Storage mfs_page_init = [] # MFS Total Unsorted Pages Contents pch_init_info = [] # Store PCH Initialization Table Info pch_init_final = [] # Store PCH Initialization Table Final Info mfs_parsed_idx = [] # Store all parsed MFS Low Level Files sys_page_sorted = [] # MFS Total Sorted System Pages Contents dat_page_sorted = [] # MFS Total Sorted Data Pages Contents all_chunks_dict = {} # MFS Total Chunk Index & Data Dictionary intel_cfg_hash_mfs = None # Store MFS Low Level File 6 Hash 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 padding = int.from_bytes(mfsb_buffer[pattern.end():pattern.end() + 0x4], 'big') # The 4 bytes after 0x01030204 are Padding (0xFF) Size in BE mfs_buffer_init += (mfsb_buffer[data_start:pattern.start()] + b'\xFF' * padding) # Append Chunk Data to Actual MFS Buffer 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 else : print(col_m + '\n RSA Signature of %s is UNKNOWN!' % mfs_dict[entry_file] + col_e) elif mn2_signs[0] : print(col_g + '\n RSA Signature of %s is VALID' % mfs_dict[entry_file] + col_e) else : if param.cse_pause : 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 sys_count = page_count // 12 # MFS System Pages Count 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 page_hdr = get_struct(mfs_buffer_init, page_start, MFS_Page_Header) # Page Header Structure 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 page_hdr_data = mfs_page[:page_hdr_size] # Page Header Data page_tag = page_hdr.Signature # Page Signature Tag page_number = page_hdr.PageNumber # Page Number starting from 1 page_erase_count = page_hdr.EraseCount # Counter of Page Erases page_erase_next = page_hdr.NextErasePage # Page Number to be Erased Next page_chunk_first = page_hdr.FirstChunkIndex # Index number of Data Pages' 1st Chunk from total MFS Chunks (MFS start) 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) else : 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 index_size = chunk_count * index_size_sys + index_size_sys # System Page Total Chunk Indexes size is Chunk Count * Index Byte Length + Index Byte Length 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 for i in range(len(index_values_obf)) : # Obfuscated Index Bit 0 = 0 (0x8000) for Next Usable Entry, Obfuscated Index Bit 1 = 0 (0x4000) for Used Entry 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 for i in range(chunk_used_count) : chunk_index = chunk_indexes[i] # Index of used System Page Chunk from total MFS Chunks (MFS start) 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!' % (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 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 index_size = chunk_count * index_size_dat # Data Page Total Chunk Indexes size is Chunk Count * Index Byte Length 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 for i in range(len(index_values)) : if index_values[i] == 0 : # Used Data Page Chunk Index = 0x00, Unused = 0xFF chunk_used_count += 1 # Add Used Data Page Chunk to Total Used Count chunk_index = page_chunk_first + i # Index of used Data Page Chunk from total MFS Chunks (MFS start) chunk_all = mfs_page[chunk_start + chunk_all_size * i:chunk_start + chunk_all_size * i + chunk_all_size] # Data Page Chunk with CRC-16 (0x42) chunk_raw = chunk_all[:-2] # Data Page Chunk without CRC-16 (0x40) 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 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 for page_msg in mfs_tmp_page : # Print MFS Page Records Messages after Log 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, []) return mfs_parsed_idx, intel_cfg_hash_mfs, mfs_info, pch_init_final, vol_ftbl_id, config_rec_size, vol_ftbl_pl # The final System Area Buffer must not be empty vol_hdr = get_struct(all_chunks_dict[0], 0, MFS_Volume_Header) # System Volume is at the LAST Index 0 Chunk (the dictionary does that automatically) vol_sig = vol_hdr.Signature # Volume Signature (0x724F6201) if vol_sig != 0x724F6201 : _ = mfs_anl_msg(col_r + 'Error: MFS Volume Signature 0x%0.8X is invalid!' % vol_sig + col_e, 'error', True, False, False, []) return mfs_parsed_idx, intel_cfg_hash_mfs, mfs_info, pch_init_final, vol_ftbl_id, config_rec_size, vol_ftbl_pl # The MFS Volume Signature must be valid if param.cse_unpack : print('\n%s' % vol_hdr.mfs_print()) # Print System Volume Structure Info during CSE Unpacking mfs_info.append(vol_hdr.mfs_print()) # Store System Volume Structure Info during CSE Unpacking vol_ftbl_id = vol_hdr.FTBLDictionary # FTBL/EFST Dictionary vol_ftbl_pl = vol_hdr.FTBLPlatform # FTBL/EFST Platform vol_ftbl_rs = vol_hdr.FTBLReserved # FTBL/EFST Reserved vol_has_ftbl = not (vol_ftbl_id,vol_ftbl_pl,vol_ftbl_rs) == (1,0,0) # Detect if MFS uses FTBL/EFST vol_file_rec = vol_hdr.FileRecordCount # Number of File Records in Volume vol_total_size = vol_hdr.VolumeSize # Size of MFS System & Data Volume via Volume 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 fat_values = struct.unpack_from('<%dH' % fat_count, all_mfs_sys, vol_hdr_size) # MFS FAT Values are 2 bytes each for index in range(vol_file_rec) : # Parse all MFS Volume (Low Level File) FAT Values if fat_values[index] in (0x0000,0xFFFE,0xFFFF) : # 0x0000 = Unused, 0xFFFE = Erased, 0xFFFF = Used but Empty mfs_files.append([index, None]) # Store MFS Low Level File Index & Contents 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) mfs_parsed_idx.append(mfs_file[0]) # Set MFS Low Level File as Parsed file_name = '%0.3d %s' % (mfs_file[0], mfs_dict[mfs_file[0]]) file_folder = os.path.join(mfs_folder, file_name, '') file_path = os.path.join(file_folder, 'Contents.bin') # MFS Low Level File Path file_path_raw = f'{os.path.join(mfs_folder, file_name)}.bin' mfs_write(file_folder, file_path, mfs_file[1]) # Store MFS Low Level File mfs_write(mfs_folder, file_path_raw, mfs_file[1]) # 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))) : mfs_file_name = 'Unknown' if mfs_is_afs and mfs_file[0] in (6,7) else mfs_dict[mfs_file[0]] if param.cse_unpack : print(col_g + '\n Analyzing MFS Low Level File %d (%s) ...' % (mfs_file[0], mfs_file_name) + col_e) mfs_parsed_idx.append(mfs_file[0]) # Set MFS Low Level File as Parsed file_name = '%0.3d %s' % (mfs_file[0], mfs_file_name) file_folder = os.path.join(mfs_folder, file_name, '') file_data_path = os.path.join(file_folder, 'Contents.bin') # MFS Low Level File Contents Path file_raw_path = f'{os.path.join(mfs_folder, file_name)}.bin' # 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) : file_data = mfs_file[1][:] # MFS Low Level File Contents else : file_data = mfs_file[1][:-sec_hdr_size] # MFS Low Level File Contents without Integrity file_sec = mfs_file[1][-sec_hdr_size:] # MFS Low Level File Integrity without Contents file_sec_hdr = get_struct(file_sec, 0, sec_hdr_struct[sec_hdr_size]) # MFS Low Level File Integrity Structure if param.cse_verbose : file_sec_ptv = file_sec_hdr.mfs_print() # MFS Low Level File Integrity Structure Info file_sec_ptv.title = 'MFS %0.3d %s Integrity' % (mfs_file[0], mfs_file_name) # Adjust Integrity Structure Verbose Info Title print('\n%s' % file_sec_ptv) # Print Integrity Structure Info during Verbose CSE Unpacking 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 mfs_write(mfs_folder, file_raw_path, mfs_file[1]) # 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 # MFSTool by Peter Bosch (https://github.com/peterbjornx/meimagetool) # MFS Templates AFS_region_256K|400K|1272K.bin by Flash Image Tool v11 if param.mfs_rcfg and mfs_file[0] == 6 : 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 # with adjusted Page First Chunk & CRC-8 at each Data Page. To determine the Data Page First Chunk increase # for each page, calculate Total Volume Size Difference / Raw Chunk Size. For example, to create template # 1272K_2048_0x11E100 from 1272K_1024_0x11D900, add (0x11E100 - 0x11D900) / 0x40 = 0x20 to each Data Page # First Chunk. After adjusting all Data Page First Chunk & CRC-8, the Volume Header Info must be updated # as well by copying only the MFS Volume FTBL fields from old to new MFS and then recalculating CRC-16. # 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) : chunk_start = int.from_bytes(mfs_tmpl_new[page_offset + 0xE:page_offset + 0x10], 'little') # Get Page First Chunk value if chunk_start != 0 : # Adjust Data Pages only (First Chunk != 0), not System Pages 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 rec_path = os.path.relpath(file_path, start=init_folder) if file_index >= 8 else mfs_type[fs_id] # Set actual Record Path for printing mfs_write(os.path.normpath(os.path.join(root_folder)), file_path, file_data) # Store MFS Home Directory Record/File Contents 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 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 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']) else : 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 == 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 ftbl_pt.add_row(['Path', ftbl_path]) ftbl_pt.add_row(['File ID', '0x%s' % ftbl_file_id]) ftbl_pt.add_row(['Integrity', fvalue[ftbl_acc_int]]) ftbl_pt.add_row(['Encryption', fvalue[ftbl_acc_enc]]) ftbl_pt.add_row(['Anti-Replay', fvalue[ftbl_acc_arp]]) ftbl_pt.add_row(['Access Unknown', '{0:013b}b'.format(ftbl_acc_unk)]) ftbl_pt.add_row(['Group ID', '0x%0.4X' % ftbl_group_id]) 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 log_encr = ftbl_acc_enc log_arpl = ftbl_acc_arp sec_hdr = None 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. # The workaround below is stupid AF but should work until the proper indicator can be found. if sec_hdr.ARCounter > 0xFFFF : # The AR Counter should have small values, otherwise adjust sec_extra_size = 0x10 file_data = mfs_file_data[:-(sec_hdr_size + sec_extra_size)] if mfs_file_data else b'' 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')) sec_ar_random = '0x%0.8X' % sec_hdr.ARRandom if sec_ar else '' 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 '' sec_ar_random = '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, col_y + 'AntiReplay' + col_e, col_y + 'Rights' + col_e, col_y + 'User ID' + col_e, col_y + 'Group ID' + col_e, col_y + 'FIT' + col_e, 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 rec_res = '0x%0.4X' % rec_hdr.Reserved # Reserved rec_offset = rec_hdr.FileOffset # File Offset relative to MFS Low Level File start 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 rec_hdr_pt.add_row(['Path', local_mfs_path]) # Add Local MFS Folder Path to MFS Configuration Record Structure Info if rec_name not in ('.','..') : mfs_txt(rec_hdr_pt, rec_folder, rec_folder, 'w', False) # Store/Print MFS Configuration Record Info, skip folder markers else : # Set & Store currently working File (Name & Contents) rec_file = os.path.join(rec_folder, rec_name) # Add File name to currently working Folder path rec_data = buffer[rec_offset:rec_offset + rec_size] # Get File Contents from MFS Low Level File mfs_write(rec_folder, rec_file, rec_data) # Store File to currently working Folder 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 rec_file = os.path.normpath(rec_folder + rec_path) # Set generic/unknown File actual path when warnings occur rec_parent = os.path.normpath(os.path.join(rec_folder, 'Unknown')) # Set generic/unknown parent Folder actual path when warnings occur elif ftbl_rec_id not in ftbl_dict[ftbl_plat_id][ftbl_dict_id]['FTBL'] : if ftbl_dict : _ = mfs_anl_msg(col_m + 'Warning: File Table %s > %s does not contain ID %0.8X!' % (ftbl_plat_id,ftbl_dict_id,rec_id) + col_e, '', False, 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 rec_file = os.path.normpath(rec_folder + rec_path) # Set generic/unknown File actual path when warnings occur rec_parent = os.path.normpath(os.path.join(rec_folder, 'Unknown')) # Set generic/unknown parent Folder actual path when warnings occur else : 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 : if param.cse_pause : 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 : if param.cse_pause : 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 _ = mfs_cfg_anl(7, fitc_cfg_data, rec_folder, rec_folder, config_rec_size, [], vol_ftbl_id, vol_ftbl_pl) # Parse MFS Configuration Records except : if param.cse_pause : 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 meta_size = 0x4 crc32_len = 0x4 crc32_iv = 0x0 idx_padd_len = 0x8 sys_page_all = [] dat_page_all = [] emp_page_all = b'' efs_data_all = b'' 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' elif page_hdr.Unknown0 != 0xFFFF : dat_page_all.append(page_data) # Data Page page_type = 'Data' 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, col_y + 'Size' + col_e, col_y + 'Metadata Unknown' + col_e, col_y + 'Reserved' + col_e, col_y + 'Integrity' + col_e, col_y + 'Encryption' + col_e, col_y + 'SVN' + col_e, col_y + 'Anti-Replay' + col_e, col_y + 'AR Index' + col_e, col_y + 'AR Random' + col_e, col_y + 'AR Counter' + col_e, col_y + 'User ID' + col_e, col_y + 'Group ID' + col_e, col_y + 'FTBL Unknown 1' + col_e, col_y + 'FTBL Unknown 2' + col_e, col_y + 'HMAC MD5' + col_e, col_y + 'AES-GCM Nonce' + col_e, col_y + 'Integrity Unknown 1' + col_e, col_y + 'Integrity Unknown 2' + col_e], True, 1) 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) ftbl_pt.title = col_y + 'File Table Entry' + col_e ftbl_pt.add_row(['EFS Name', file_name]) # Extra for EFS only ftbl_pt.add_row(['VFS Path', ftbl_path]) ftbl_pt.add_row(['File ID', '0x%s' % ftbl_file_id]) ftbl_pt.add_row(['Integrity', fvalue[ftbl_acc_int]]) ftbl_pt.add_row(['Encryption', fvalue[ftbl_acc_enc]]) ftbl_pt.add_row(['Anti-Replay', fvalue[ftbl_acc_arp]]) ftbl_pt.add_row(['Access Unknown', '{0:013b}b'.format(ftbl_acc_unk)]) ftbl_pt.add_row(['Group ID', '0x%0.4X' % ftbl_group_id]) 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. # The workaround below is stupid AF but should work until the proper indicator can be found. if sec_hdr.ARCounter > 0xFFFF : # The AR Counter should have small values, otherwise adjust sec_extra_size = 0x10 file_data = file_data_all[:-(sec_hdr_size + sec_extra_size)] if file_data_all else b'' 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')) sec_aes_nonce = '%0.*X' % (0xC * 2, int.from_bytes(sec_hdr.AESGCMNonce, 'little')) sec_ar_random = '0x%0.8X' % sec_hdr.ARRandom if sec_ar else '' 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 : # Check if any uncharted EFS data remain after EFST analysis at Empty/Non-Initialized EFS only w/o Scratch data if param.cse_unpack and efs_data_rest != b'\xFF' * len(efs_data_rest) : efs_anl_msg(col_m + 'Warning: Detected additional EFS Data Buffer contents, wrong EFS Table!' + col_e, warn_stor, False) else : efs_anl_msg(col_r + 'Error: Could not find File System Platform 0x%s (%s) > Dictionary 0x%s > EFST > Revision 0x%s!' % ( ftbl_plat_id, plat_name, ftbl_dict_id, efst_dict_rev) + col_e, err_stor, False) 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 pch_init_rev = rec_data[6] # Chipset Initialization Table Revision else : pch_init_plt = pch_dict[rec_data[3] >> 4] if rec_data[3] >> 4 in pch_dict else 'Unknown' # Actual Chipset SKU Platform (SPT-H, CNP-LP etc) 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) \ or (mn2_ftpr_hdr.Year == 0x2015 and mn2_ftpr_hdr.Month == 0x05 and mn2_ftpr_hdr.Day >= 0x19) : # Absolute for CSME >=~ 11.0.0.1140 @ 2015-05-19 (0 = A, 1 = B, 2 = C, 3 = D etc) pch_true_stp = pch_stp_val[pch_init_stp] 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) or (mn2_ftpr_hdr.Year == 0x2018 and mn2_ftpr_hdr.Month == 0x01 and mn2_ftpr_hdr.Day >= 0x25)) : # Bitfield for CSME >=~ 12.0.0.1058 @ 2018-01-25 (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 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 : # Absolute for CSME 13 >=~ 13.0.0.1061 (0 = A, 1 = B, 2 = C, 3 = D etc) pch_true_stp = pch_stp_val[pch_init_stp] else : # 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 for i in range(256): 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' pmc_platform = 'Unknown' pch_sku_val = {0:'SoC', 1:'LP', 2:'H', 3:'N', 4:'M'} 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]) pmc_name_db = '%s_%s_%s_%s_%s_%s' % (pmc_platform[:3], pmc_fw_ver, pmc_pch_rev_p, mn2_info[7], pmc_mn2_signed_db, mn2_info[6]) elif pmc_platform.startswith(('CNP','WTL','IDV')) and mn2_info[0] not in [300,30] : pmc_fw_ver = '%0.2d.%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]) pmc_name_db = '%s_%s_%s_%s_%s_%s_%s' % (pmc_platform[:3], pmc_fw_ver, pmc_pch_sku, pmc_pch_rev_p, mn2_info[7], pmc_mn2_signed_db, mn2_info[6]) elif pmc_platform.startswith('DG') : 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]) pmc_name_db = '%s_%s_%s_%s_%s' % (pmc_platform[:4], pmc_fw_ver, mn2_info[7], pmc_mn2_signed_db, mn2_info[6]) else : 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 : if pmc_name_db in line : break # Break loop at 1st name match else : 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 : break # Break loop at 1st hash match 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 # Parse CSE PMC firmware after analysis 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 : if pchc_name_db in line : break # Break loop at 1st name match else : 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 : break # Break loop at 1st hash match 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 # Parse CSE PCHC firmware after analysis 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 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 : if phy_name_db in line : break # Break loop at 1st name match else : 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 : break # Break loop at 1st hash match 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 # Parse CSE PHY firmware after analysis 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) # Dictionaries by "IllegalArgument", Dmitry Sklyarov, Mark Ermolov, Maxim Goryachy & me def cse_huffman_dictionary_load(cse_variant, cse_major, cse_minor, verbosity) : HUFFMAN_SHAPE = [] HUFFMAN_SYMBOLS = {} 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]) elif re.match(r'^(\?\?)+$', symbol) : HUFFMAN_UNKNOWNS[mapping_type][codeword_len].add(codeword) 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( ('{:0>' + str(codeword_length) + 'b}').format(codeword), dictionary_type, codeword_length, "0x{:X}".format(codeword), symbol_length, decompressed_position) + col_e) huff_error = True decompressed_array.extend(symbol) decompressed_position += symbol_length else : if verbosity in ['all','error'] : print(col_r + '\n Skipping overflowing codeword {: <15s} (dictionary 0x{:X}, codeword length {: >2d}, codeword {: >5s}, symbol length {:d}) at decompressed offset 0x{:X}'.format( ('{:0>' + str(codeword_length) + 'b}').format(codeword), dictionary_type, codeword_length, '0x{:X}'.format(codeword), symbol_length, decompressed_position) + col_e) huff_error = True filler = itertools.repeat(0x7F, decompressed_limit - decompressed_position) decompressed_array.extend(filler) decompressed_position = decompressed_limit else : if verbosity in ['all','error'] : print(col_r + '\n Reached end of compressed stream early at decompressed offset 0x{:X}'.format(decompressed_position) + col_e) huff_error = True 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) cpd_hdr = get_struct(buffer, cpd_offset, cpd_hdr_struct) cse_in_id = 0 in_id_step = 0 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 if in_id_stop > 10 : break in_id_step += int.from_bytes(buffer[mn2_start + mn2_size + in_id_step + 0x4:mn2_start + mn2_size + in_id_step + 0x8], 'little') else : in_id_ext = 'CSE_Ext_%0.2X' % int.from_bytes(buffer[mn2_start + mn2_size + in_id_step:mn2_start + mn2_size + in_id_step + 0x4], 'little') if in_id_ext in ext_dictionary : cse_ext_hdr = get_struct(buffer, mn2_start + mn2_size + in_id_step, ext_dictionary[in_id_ext]) 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 cpd_entry_empty += 1 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 cpd_chk_calc = (0x100 - cpd_sum & 0xFF) & 0xFF elif cpd_hdr_struct.__name__ == 'CPD_Header_R2' : 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:]) 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 def chk_iup_size(eng_size_text, file_end, eng_fw_end, variant_p, platform) : if eng_fw_end > file_end : 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 rbe_pm_struct_size = ctypes.sizeof(RBE_PM_Metadata) elif rbe_pm_patt_256_2 : rbe_pm_patt_start = rbe_pm_patt_256_2.start() rbe_pm_struct_name = RBE_PM_Metadata_R2 rbe_pm_struct_size = ctypes.sizeof(RBE_PM_Metadata_R2) elif rbe_pm_patt_384_1 : rbe_pm_patt_start = rbe_pm_patt_384_1.start() rbe_pm_struct_name = RBE_PM_Metadata_R3 rbe_pm_struct_size = ctypes.sizeof(RBE_PM_Metadata_R3) elif rbe_pm_patt_384_2 : rbe_pm_patt_start = rbe_pm_patt_384_2.start() rbe_pm_struct_name = RBE_PM_Metadata_R4 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) elif hasattr(value, '_length_') and hasattr(value, '_type_') : value = get_array(value) # Probably an Array elif isinstance(value, (bytes, bytearray)) : 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 def ext_table(row_col_names, header, padd) : pt = pltable.PrettyTable(row_col_names) pt.set_style(pltable.UNICODE_LINES) pt.xhtml = True pt.header = header # Boolean pt.left_padding_width = 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 def get_script_dir(follow_symlinks=True) : if getattr(sys, 'frozen', False) : path = os.path.abspath(sys.executable) else : path = inspect.getabsfile(get_script_dir) if follow_symlinks : path = os.path.realpath(path) return os.path.dirname(path) # https://stackoverflow.com/a/781074 by Torsten Marek def show_exception_and_exit(exc_type, exc_value, tb) : if exc_type is KeyboardInterrupt : print('\n') else : 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 def mea_exit(code) : try : # Before exiting, print output of MEA & DB update check Thread, if completed/dead 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) : print(message, end = '') input() # 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() # Get Hash of data, digest size based def get_hash(data, hash_size) : if hash_size == 0x10 : return md5(data) 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 def note_new_fw(variant_p) : msg_1 = col_g + 'Note: This ' + col_e msg_2 = col_y + variant_p + col_e msg_3 = col_g + ' firmware is not in the database!' + col_e msg_4 = col_g + ' You can help this\n project by sharing it at ' + col_e msg_5 = col_c + 'https://win-raid.com' + col_e msg_6 = col_g + ' forum. Thank you!' + col_e note_stor.append([msg_1 + msg_2 + msg_3 + msg_4 + msg_5 + msg_6, True]) # Get JSON Structure from DB 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) 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 start_substruct = 0x0 # At ICH, Flash Descriptor starts at 0x0 end_substruct = 0xBC # 0xBC for [0xAC] + 0xFF * 16 sanity check # Platform Controller Hub (PCH) else : 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) fd_comp_den = reading[start_fd_match + fd_flmap0_fcba] # Component Density Byte (ME 2-8 & TXE 0-2 & SPS 1-2 = 0:5, ME 9+ & TXE 3+ & SPS 3+ = 0:7) fd_comp_1_bitwise = 0xF if fd_flmap1_isl >= 0x13 else 0x7 # Component 1 Density Bits (ME 2-8 & TXE 0-2 & SPS 1-2 = 3, ME 9+ & TXE 3+ & SPS 3+ = 4) 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 if start_man_match and (start_fd_match < start_man_match < start_fd_match + fd_comp_all_size) and file_end != fd_comp_all_size \ and not (reading[0x0:0x4] == b'$CPD' and reading[0xC:0x10] == b'RCIP') : reading = reading[start_fd_match:start_fd_match + fd_comp_all_size] # Update input file buffer in RAM reading_msg = col_y + 'Note: Adjusted buffer to Flash Descriptor 0x%X - 0x%X!' % (start_fd_match,start_fd_match + fd_comp_all_size) + col_e note_stor.append([reading_msg, False]) # Inform user of the new input buffer FD range file_end = fd_comp_all_size # Update input file RAM buffer length, same as FD Flash Component Total Size 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') : fd_is_cut = True 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') me_fd_limit = int.from_bytes(reading[fd_rgn_base + 0xA:fd_rgn_base + 0x0C], 'little') pdr_fd_base = int.from_bytes(reading[fd_rgn_base + 0x10:fd_rgn_base + 0x12], 'little') 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 bios_fd_exist = bios_fd_start < file_end # Basic check that Region Start Offset is valid 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 me_fd_exist = me_fd_start < file_end 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 pdr_fd_exist = pdr_fd_start < file_end 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 devexp_fd_exist = devexp_fd_start < file_end 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'] : version = '%s.%s.%s.%s' % ('{0:02d}'.format(major), '{0:02d}'.format(minor), '{0:02d}'.format(hotfix), '{0:03d}'.format(build)) # xx.xx.xx.xxx elif variant.startswith(('PMCAPL','PMCBXT','PMCGLK','PMCDG')) : version = '%s.%s.%s.%s' % (major, minor, hotfix, build) elif variant.startswith(('PMCCNP','PMCWTL','PMCIDV')) and (major < 30 or major == 3232) : version = '%s.%s.%s.%s' % ('{0:02d}'.format(major), minor, hotfix, build) elif variant.startswith('PMC') : version = '%s.%s.%s.%s' % (major, minor, '{0:02d}'.format(hotfix), build) elif variant.startswith('PCHC') : version = '%s.%s.%s.%s' % (major, minor, hotfix, '{0:04d}'.format(build)) elif variant.startswith('PHY') : 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) : 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 def unmask_DB(masked_DB, mask) : return bytes([a ^ b for (a,b) in zip(masked_DB, mask[:len(masked_DB)])]) # 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 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') man_size = man_hdr_struct.Size * 4 man_hdr_size = man_hdr_struct.HeaderLength * 4 man_key_size = man_hdr_struct.PublicKeySize * 4 man_pexp = man_hdr_struct.RSAExponent man_pkey = int.from_bytes(man_hdr_struct.RSAPublicKey, 'little') 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 elif (man_tag,man_key_size) == ('$MN2',0x100) : # SHA-256 rsa_hash = calc_hash_hex(hash_data, hashlib.sha256).upper() dec_hash = dec_sign[-64:] # 256-bit elif (man_tag,man_key_size) == ('$MN2',0x180) : # SHA-384 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() 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 if line_parts[3] not in ('X','XX') : sku_stp = line_parts[3] # Cell 3 is PCH/SoC Stepping if 'YPDM' in line_parts[4] or 'NPDM' in line_parts[4] or 'UPDM' in line_parts[4] : sku_pdm = line_parts[4] # Cell 4 is PDM elif variant == 'CSTXE' : 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 # 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' : sku_result = pos_sku_tbl # SKU Platform retrieved from MFS (Best) elif (variant,major,minor,hotfix) == ('CSME',12,0,0) and build >= 7000 and year < 0x2018 and month < 0x8 : # CSME 12 Alpha only fw_0C_dict = {0: 'H', 1: 'LP'} # 00 = H, 01 = LP, 10 = N/A, 11 = N/A sku_result = fw_0C_dict[fw_0C_sku2] if fw_0C_sku2 in fw_0C_dict else 'Unknown' # SKU Platform retrieved from Extension 12 (Best) else : # SKU Platform retrieved from SKU Capabilities (2nd Best) 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 def get_variant(buffer, mn2_struct, mn2_match_start, mn2_match_end, mn2_rsa_hash, mn2_date, mn2_ver) : variant = 'Unknown' variant_p = 'Unknown' variant_p_fw = 'Unknown' 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 elif mod.startswith('dkl') and major == 10 and is_meu and mn2_struct.MEU_Major == 13 : variant = 'PHYSLKF' # SPHY (LKF) elif mod.startswith('dkl') and major in (11,0) and is_meu and mn2_struct.MEU_Major == 100 : variant = 'PHYDG1' # PHYP (DG1) elif mod.startswith('PCIE') and major in (11,0) and is_meu and mn2_struct.MEU_Major == 101 : variant = 'PHYDG2' # PHYP (DG2) elif mod == 'gen4_i' and major in [13,14] and is_meu and mn2_struct.MEU_Major == 16 : variant = 'PHYNADP' # NPHY (ADP) elif mod == 'SNPMULTI' and major == 13 and is_meu and mn2_struct.MEU_Major == 16 : variant = 'PHYSADP' # SPHY (ADP) elif mod == 'nphy' and major in (9,7) and is_meu and mn2_struct.MEU_Major == 13 : variant = 'PHYNICP' # NPHY (ICP) elif mod == 'nphy' and major in (16,15,11) and is_meu and mn2_struct.MEU_Major == 15 : variant = 'PHYNTGP' # NPHY (TGP) elif mod == 'pphy' and major in (12,14,11) and is_meu and mn2_struct.MEU_Major == 15 : variant = 'PHYPTGP' # PPHY (TGP) elif mod == 'pphy' and major == 12 and is_meu and mn2_struct.MEU_Major == 0 : variant = 'PHYPEBG' # PPHY (EBG) elif mod == 'pphy' and major in (12,0) : variant = 'PHYPCMP' # PPHY (CMP) elif mod == 'IntelRec' and major == 16 : variant = 'PCHCADP' # ADP elif mod == 'IntelRec' and major == 15 and is_meu and mn2_struct.MEU_Minor == 40 : variant = 'PCHCMCC' # MCC elif mod == 'IntelRec' and major == 15 and is_meu and mn2_struct.MEU_Minor == 0 : variant = 'PCHCTGP' # TGP elif mod == 'IntelRec' and (major,minor) == (14,5) : variant = 'PCHCCMPV' # CMP-V elif mod == 'IntelRec' and (major,minor) == (14,0) : variant = 'PCHCCMP' # CMP elif mod == 'IntelRec' and (major,minor) == (13,30) : variant = 'PCHCLKF' # LKF elif mod == 'IntelRec' and (major,minor) == (13,5) : variant = 'PCHCJSP' # JSP elif mod == 'IntelRec' and (major,minor) == (13,0) : variant = 'PCHCICP' # ICP elif mod == 'PMCC000' and (major in (300,30,3232) or (major < 30 and year <= 0x2017)) : variant = 'PMCCNP' # 0 CNP elif mod == 'PMCC000' and major == 133 : variant = 'PMCLKF' # 0 LKF elif mod == 'PMCC000' and major in (135,130) and is_meu and mn2_struct.MEU_Minor == 50 : variant = 'PMCJSP' # JSP elif mod == 'PMCC000' and major in (400,130) : variant = 'PMCICP' # 0 ICP elif mod == 'PMCC000' and major == 140 and is_meu and mn2_struct.MEU_Minor == 5 : variant = 'PMCCMPV' # 0 CMP-V elif mod == 'PMCC000' and major == 140 : variant = 'PMCCMP' # 0 CMP (After CMP-V) elif mod == 'PMCC000' and major == 150 : variant = 'PMCTGP' # 0 TGP elif mod == 'PMCC000' and major == 154 : variant = 'PMCMCC' # 0 MCC elif mod == 'PMCC000' and major == 160 : variant = 'PMCADP' # 0 ADP elif mod == 'PMCC000' and major == 1 and not is_meu : variant = 'PMCWTL' # 0 WTL elif mod == 'PMCC000' and major == 14 : variant = 'PMCIDV' # 0 IDV elif mod == 'PMCC002' : variant = 'PMCAPLA' # 2 APL A elif mod == 'PMCC003' : variant = 'PMCAPLB' # 3 APL B elif mod == 'PMCC004' : variant = 'PMCGLKA' # 4 GLK A elif mod == 'PMCC005' : variant = 'PMCBXTC' # 5 BXT C (Joule) elif mod == 'PMCC006' : variant = 'PMCGLKB' # 6 GLK B elif mod in ['gfx_srv','chassis'] : variant = 'GSC' # GSC elif mod.startswith('PCOD') and is_meu and mn2_struct.MEU_Major == 100 : variant = 'PMCDG1' # DG1 elif mod.startswith('PCOD') and is_meu and (major in (4,2) or mn2_struct.MEU_Major == 101) : variant = 'PMCDG2' # DG2 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' variant_p_fw = 'Management Engine' elif variant == 'CSTXE': variant_p = 'CSE TXE' variant_p_fw = 'Trusted Execution Engine' elif variant == 'CSSPS': variant_p = 'CSE SPS' variant_p_fw = 'Server Platform Services' elif variant == 'GSC': variant_p = 'GSC' variant_p_fw = 'Graphics System Controller' elif variant.startswith('PHY'): variant_p = 'PHY' variant_p_fw = 'USB Type C Physical' elif variant.startswith('PMC'): variant_p = 'PMC' variant_p_fw = 'Power Management Controller' elif variant.startswith('PCHC'): variant_p = 'PCHC' variant_p_fw = 'Platform Controller Hub' elif variant.startswith('OROM'): variant_p = 'OROM' variant_p_fw = 'Option ROM' elif variant == 'ME': variant_p = variant variant_p_fw = 'Management Engine' elif variant == 'TXE': variant_p = variant variant_p_fw = 'Trusted Execution Engine' 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') git_py_idx = git_py_utf.find('title = \'ME Analyzer v') if git_py_idx == -1 : raise Exception('BAD_PY_FORMAT') git_py_ver = git_py_utf[git_py_idx:][22:].split('\'')[0].split('_')[0] cur_py_ver = title[13:].split('_')[0] git_py_tup = git_py_ver.split('.')[:3] cur_py_tup = cur_py_ver.split('.')[:3] 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 def mass_scan(f_path) : mass_files = [] 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 ansi_escape = re.compile(r'\x1b[^m]*m') # CSE Extensions 0x00-0x1B, 0x1E-0x1F, 0x22, 0x23, 0x32, 0x544F4F46 ext_tag_all = list(range(0x1C)) + list(range(0x1E,0x20)) + [0x22,0x23,0x25,0x32,0x37,0x544F4F46] # CSME 12-14 Revised Extensions ext_tag_rev_hdr_csme12 = {0xF:'_R2', 0x14:'_R2'} # CSME 12-14 Revised Extension Modules ext_tag_rev_mod_csme12 = {0x1:'_R2', 0xD:'_R2'} # CSME 15 Revised Extensions ext_tag_rev_hdr_csme15 = {0x0:'_R2', 0x3:'_R2', 0xA:'_R2', 0xF:'_R2', 0x11:'_R2', 0x13:'_R2', 0x14:'_R3', 0x16:'_R2', 0x17:'_R2', 0x1B:'_R2'} # CSME 15 Revised Extension Modules ext_tag_rev_mod_csme15 = {0x1:'_R2', 0xD:'_R2', 0xE:'_R2', 0xF:'_R3', 0x10:'_R2', 0x18:'_R2', 0x19:'_R2', 0x1A:'_R2'} # GSC/OROM 100 Revised Extensions ext_tag_rev_hdr_gsc100 = {0x0:'_R2', 0x3:'_R2', 0xA:'_R2', 0xF:'_R2', 0x11:'_R2', 0x13:'_R2', 0x14:'_R3', 0x16:'_R2', 0x18:'_R2', 0x19:'_R2', 0x1A:'_R2'} # GSC/OROM 100 Revised Extension Modules ext_tag_rev_mod_gsc100 = {0x1:'_R2', 0x7:'_R2', 0xD:'_R2', 0xE:'_R2', 0xF:'_R2', 0x10:'_R2', 0x18:'_R3', 0x1A:'_R3'} # CSSPS 5 Revised Extensions ext_tag_rev_hdr_cssps5 = {0xF:'_R2'} # CSSPS 5 Revised Extension Modules ext_tag_rev_mod_cssps5 = {0x1:'_R2', 0x0:'_R2'} # CSSPS 5.0.0-3 Revised Extensions ext_tag_rev_hdr_cssps503 = {0xF:'_R2'} # CSSPS 5.0.0-3 Revised Extension Modules ext_tag_rev_mod_cssps503 = {0x0:'_R2'} # CSE Extensions without Modules (exclude 0x1E, 0x1F, 0x544F4F46) ext_tag_mod_none = [(0x4,''), (0xA,''), (0xA,'_R2'), (0xC,''), (0x11,''), (0x11,'_R2'), (0x13,''), (0x13,'_R2'), (0x16,''), (0x16,'_R2'), (0x17,''), (0x17,'_R2'), (0x1B,''), (0x1B,'_R2'), (0x32,'')] # CSE Extensions with Module Count ext_tag_mod_count = [(0x1,''), (0x2,''), (0x12,''), (0x22,''), (0x23,'')] # CSE SPS SKU Type ID cssps_type_fw = {'RC':'Recovery', 'OP':'Operational'} # CSE File System ID mfs_type = {0:'root', 1:'home', 2:'bin', 3:'susram', 4:'fpf', 5:'dev', 6:'umafs'} # CSE Extension 0x0F NVM Compatibility ext15_nvm_type = {0:'Undefined', 1:'UFS', 2:'SPI'} # CSE Extension 0x0F Firmware Type ext15_fw_type = {0:'Default', 1:'SPS', 2:'SPS EPO', 3:'Client', 4:'GFX'} # GSC SKU Build Major to Stepping Dictionary gsc_stp_dict = {0:'A', 1:'A', 2:'B', 3:'C', 4:'D', 5:'E', 6:'F', 7:'G', 8:'H', 9:'I'} # CSE File System Home Directory Record Structures home_rec_struct = {0x18:MFS_Home_Record_0x18, 0x1C:MFS_Home_Record_0x1C} # CSE File System Configuration Record Structures config_rec_struct = {0x1C:MFS_Config_Record_0x1C, 0xC:MFS_Config_Record_0xC} # CSE File System Home Directory Integrity Structures sec_hdr_struct = {0x28:MFS_Integrity_Table_0x28, 0x34:MFS_Integrity_Table_0x34} # Flash Partition Table Partition Types p_type_dict = {0:'Code', 1:'Data', 2:'GLUT', 3:'Generic', 4:'EFFS', 5:'ROM'} # Flash Partition Table v2.0 Flags (Sector Types) sector_types = {0:'4K', 2:'8K', 4:'64K', 8:'64K-8K Mixed'} # GSC Option ROM PCI Register Code Types pcir_code_types = {0x0: 'BIOS', 0x1: 'OpenBIOS', 0x2: 'PA-RISC', 0x3: 'UEFI', 0xF0: 'Data', 0xF1: 'Code', 0xFF: 'Reserved'} # CSE Module Types cse_mod_type = {0:'Process', 1:'Shared Library', 2:'Data', 3:'Independent'} # CSE/GSC Hash Algorithms cse_hash_alg = {0:'None', 1:'SHA-1', 2:'SHA-256', 3:'SHA-384'} # CSE USB Type C Hash Algorithms tcs_hash_alg = {0:'SHA-1', 1:'SHA-256', 2:'MD5'} # CSE Extension 0x0C Firmware SKU ext12_fw_sku = { 0 : ('Corporate','COR'), 1 : ('Consumer','CON'), 2 : ('Slim','SLM'), 3 : ('Server','SVR'), 5 : ('Chrome','CHR'), } # CSE Extension 0x0F Firmware SKU ext15_fw_sku = { 0 : ('Undefined','NA'), 1 : ('Corporate','COR'), 2 : ('Consumer','CON'), 3 : ('Slim','SLM'), 4 : ('Lite','LIT'), 5 : ('Server','SVR'), 6 : ('Atom','ATM'), # Guess/Placeholder (JSP) 255 : ('All','ALL'), } # CSE SPS SKU Platform ID cssps_platform = { 'GE' : 'Greenlow', 'PU' : 'Purley', 'HA' : 'Harrisonville', 'PE' : 'Purley EPO', 'BA' : 'Bakerville', 'ME' : 'Mehlow', 'WH' : 'Whitley', 'ID' : 'Idaville', 'TA' : 'Tatlow', } # CSE Extension Structures ext_dict = { 'CSE_Ext_00' : CSE_Ext_00, 'CSE_Ext_00_R2' : CSE_Ext_00_R2, 'CSE_Ext_01' : CSE_Ext_01, 'CSE_Ext_02' : CSE_Ext_02, 'CSE_Ext_03' : CSE_Ext_03, 'CSE_Ext_03_R2' : CSE_Ext_03_R2, 'CSE_Ext_04' : CSE_Ext_04, 'CSE_Ext_05' : CSE_Ext_05, 'CSE_Ext_06' : CSE_Ext_06, 'CSE_Ext_07' : CSE_Ext_07, 'CSE_Ext_08' : CSE_Ext_08, 'CSE_Ext_09' : CSE_Ext_09, 'CSE_Ext_0A' : CSE_Ext_0A, 'CSE_Ext_0A_R2' : CSE_Ext_0A_R2, 'CSE_Ext_0B' : CSE_Ext_0B, 'CSE_Ext_0C' : CSE_Ext_0C, 'CSE_Ext_0D' : CSE_Ext_0D, 'CSE_Ext_0E' : CSE_Ext_0E, 'CSE_Ext_0F' : CSE_Ext_0F, 'CSE_Ext_0F_R2' : CSE_Ext_0F_R2, 'CSE_Ext_10' : CSE_Ext_10, 'CSE_Ext_11' : CSE_Ext_11, 'CSE_Ext_11_R2' : CSE_Ext_11_R2, 'CSE_Ext_12' : CSE_Ext_12, 'CSE_Ext_13' : CSE_Ext_13, 'CSE_Ext_13_R2' : CSE_Ext_13_R2, 'CSE_Ext_14' : CSE_Ext_14, 'CSE_Ext_14_R2' : CSE_Ext_14_R2, 'CSE_Ext_14_R3' : CSE_Ext_14_R3, 'CSE_Ext_15' : CSE_Ext_15, 'CSE_Ext_16' : CSE_Ext_16, 'CSE_Ext_16_R2' : CSE_Ext_16_R2, 'CSE_Ext_17' : CSE_Ext_17, 'CSE_Ext_17_R2' : CSE_Ext_17_R2, 'CSE_Ext_18' : CSE_Ext_18, 'CSE_Ext_18_R2' : CSE_Ext_18_R2, 'CSE_Ext_19' : CSE_Ext_19, 'CSE_Ext_19_R2' : CSE_Ext_19_R2, 'CSE_Ext_1A' : CSE_Ext_1A, 'CSE_Ext_1A_R2' : CSE_Ext_1A_R2, 'CSE_Ext_1B' : CSE_Ext_1B, 'CSE_Ext_1B_R2' : CSE_Ext_1B_R2, 'CSE_Ext_1E' : CSE_Ext_1E, 'CSE_Ext_1F' : CSE_Ext_1F, 'CSE_Ext_22' : CSE_Ext_22, 'CSE_Ext_23' : CSE_Ext_23, 'CSE_Ext_25' : CSE_Ext_25, 'CSE_Ext_32' : CSE_Ext_32, 'CSE_Ext_37' : CSE_Ext_37, 'CSE_Ext_544F4F46' : CSE_Ext_544F4F46, 'CSE_Ext_00_Mod' : CSE_Ext_00_Mod, 'CSE_Ext_00_Mod_R2' : CSE_Ext_00_Mod_R2, 'CSE_Ext_01_Mod' : CSE_Ext_01_Mod, 'CSE_Ext_01_Mod_R2' : CSE_Ext_01_Mod_R2, 'CSE_Ext_02_Mod' : CSE_Ext_02_Mod, 'CSE_Ext_03_Mod' : CSE_Ext_03_Mod, 'CSE_Ext_05_Mod' : CSE_Ext_05_Mod, 'CSE_Ext_06_Mod' : CSE_Ext_06_Mod, 'CSE_Ext_07_Mod' : CSE_Ext_07_Mod, 'CSE_Ext_07_Mod_R2' : CSE_Ext_07_Mod_R2, 'CSE_Ext_08_Mod' : CSE_Ext_08_Mod, 'CSE_Ext_09_Mod' : CSE_Ext_09_Mod, 'CSE_Ext_0B_Mod' : CSE_Ext_0B_Mod, 'CSE_Ext_0D_Mod' : CSE_Ext_0D_Mod, 'CSE_Ext_0D_Mod_R2' : CSE_Ext_0D_Mod_R2, 'CSE_Ext_0E_Mod' : CSE_Ext_0E_Mod, 'CSE_Ext_0E_Mod_R2' : CSE_Ext_0E_Mod_R2, 'CSE_Ext_0F_Mod' : CSE_Ext_0F_Mod, 'CSE_Ext_0F_Mod_R2' : CSE_Ext_0F_Mod_R2, 'CSE_Ext_0F_Mod_R3' : CSE_Ext_0F_Mod_R3, 'CSE_Ext_10_Mod' : CSE_Ext_10_Mod, 'CSE_Ext_10_Mod_R2' : CSE_Ext_10_Mod_R2, 'CSE_Ext_12_Mod' : CSE_Ext_12_Mod, 'CSE_Ext_14_HashArray' : CSE_Ext_14_HashArray, 'CSE_Ext_14_HashArray_R2' : CSE_Ext_14_HashArray_R2, 'CSE_Ext_14_RegionMap' : CSE_Ext_14_RegionMap, 'CSE_Ext_15_PartID' : CSE_Ext_15_PartID, 'CSE_Ext_15_Payload' : CSE_Ext_15_Payload, 'CSE_Ext_15_Payload_Knob' : CSE_Ext_15_Payload_Knob, 'CSE_Ext_18_Mod' : CSE_Ext_18_Mod, 'CSE_Ext_18_Mod_R2' : CSE_Ext_18_Mod_R2, 'CSE_Ext_18_Mod_R3' : CSE_Ext_18_Mod_R3, 'CSE_Ext_19_Mod' : CSE_Ext_19_Mod, 'CSE_Ext_19_Mod_R2' : CSE_Ext_19_Mod_R2, 'CSE_Ext_1A_Mod' : CSE_Ext_1A_Mod, 'CSE_Ext_1A_Mod_R2' : CSE_Ext_1A_Mod_R2, 'CSE_Ext_1A_Mod_R3' : CSE_Ext_1A_Mod_R3, '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) 1 : 'CSE Main', # Non-Fault Tolerant Partition (NFTP) 2 : 'PMCP', # Power Management Controller 6 : 'USB Type C IOM', # USB Type C I/O Manageability 7 : 'USB Type C PHY', # USB Type C Manageability, North 8 : 'USB Type C TBT', # USB Type C Thunderbolt 9 : 'WCOD', # Wireless Microcode 10 : 'LOCL', # AMT Localization 11 : 'Intel Unlock Token', 13 : 'USB Type C PHY', # South 14 : 'PCHC', 16 : 'Intel ISI', # Intel Safety Island 17 : 'SAMF', 18 : 'PPHY', 19 : 'GBST', 32 : 'Boot Policy', 33 : 'iUnit Boot Loader', # Imaging Unit (Camera) 34 : 'iUnit Main Firmware', 35 : 'cAVS Image 0', # Clear Audio Voice Speech 36 : 'cAVS Image 1', 37 : 'IFWI', # Integrated Firmware Image 38 : 'OS Boot Loader', 39 : 'OS Kernel', 40 : 'SMIP', # Signed Master Image Profile 41 : 'ISH Main', # Integrated Sensor Hub 42 : 'ISH BUP', 43 : 'OEM Unlock Token', 44 : 'OEM Life Cycle', 45 : 'OEM Key', 46 : 'SilentLake VMM', 47 : 'OEM Key Attestation', 48 : 'OEM DAL', # Dynamic Application Loader 49 : 'OEM DNX IFWI R1', # XML v1.0 (Download and Execute v1) 53 : 'OEM DNX IFWI R2', # XML v2.4 (Download and Execute v2) 57 : 'OEM Descriptor', 58 : 'OEM ISI', # Intel Safety Island 59 : 'PSE', # Programmable Services Engine 96 : 'HBM IO', 97 : 'OOB MSM', 98 : 'GT GPU', 99 : 'MDF IO', 100 : 'GSC PMC', 101 : 'GSC PHY', 102 : 'USB Type C Controller', 103 : 'OROM', 104 : 'HUC Production', 105 : 'HUC Debug', 106 : 'IFR', # In Field Repair 107 : 'GFX Data 0', 108 : 'GFX Data 1', 192 : 'ESE', 193 : 'DMU', 194 : 'PUnit', 195 : 'SoC ESE', 196 : 'SoC PMC', 197 : 'SoC SoCC', # Configuration 198 : 'SoC SPHY', } # IFWI BPDT Entry Types ($CPD Partition Names) bpdt_dict = { 0 : 'SMIP', # OEM-SMIP Partition 1 : 'RBEP', # ROM Boot Extensions Partition (CSE-RBE) 2 : 'FTPR', # Fault Tolerant Partition (CSE-BUP/FTPR) 3 : 'UCOD', # Microcode Partition 4 : 'IBBP', # IBB Partition 5 : 'S-BPDT', # Secondary BPDT 6 : 'OBBP', # OBB Partition 7 : 'NFTP', # Non-Fault Tolerant Partition (CSE-MAIN) 8 : 'ISHC', # ISH Partition 9 : 'DLMP', # Debug Launch Module Partition 10 : 'UEPB', # IFP Override/Bypass Partition 11 : 'UTOK', # Debug Tokens Partition 12 : 'UFS PHY', # UFS PHY Partition 13 : 'UFS GPP LUN', # UFS GPP LUN Partition 14 : 'PMCP', # PMC Partition (a.k.a. PCOD) 15 : 'IUNP', # IUnit Partition 16 : 'NVMC', # NVM Configuration 17 : 'UEP', # Unified Emulation Partition 18 : 'WCOD', # CSE-WCOD Partition 19 : 'LOCL', # CSE-LOCL Partition 20 : 'OEMP', # OEM KM Partition 21 : 'FITC', # OEM Configuration (fitc.cfg) 22 : 'PAVP', # Protected Audio Video Path 23 : 'IOMP', # USB Type C IO Manageability Partition (UIOM) 24 : 'xPHY', # USB Type C MG Partition (a.k.a. MGPP) 25 : 'TBTP', # USB Type C Thunderbolt Partition (TBT) 26 : 'PLTS', # Platform Settings 31 : 'DPHY', # USB Type C Dekel PHY 32 : 'PCHC', # PCH Configuration 33 : 'ISIF', # Intel Safety Island Firmware 34 : 'ISIC', # Intel Safety Island Configuration (N/A) 35 : 'HBMI', # HBM IO Partition 36 : 'OMSM', # OOB MSM Partition 37 : 'GTGP', # GT-GPU Partition 38 : 'MDFI', # MDF IO Partition 39 : 'PUNP', # PUnit Partition 40 : 'PHYP', # GSC PHY Partition 41 : 'SAMF', # SAM Firmware 42 : 'PPHY', # PPHY Partition 43 : 'GBST', # GBST Partition 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 1 : 'MNG_STD', # Standard Manageability 2 : 'AMT', # Active Management Technology 3 : 'MNG_LOCAL', # Local Manageability 4 : 'INT_TOUCH', # Integrated Precise Touch & Stylus 6 : 'SOFTCREEK', # Manageability Upgrade Service 7 : 'OUI', 8 : 'H', # PCH-H/V 9 : 'LP', # PCH-LP/N 10 : 'ISH', # Integrated Sensor Hub 12 : 'PAVP', # Protected Audio Video Path 14 : 'RCA', 15 : 'PRTCM', # Protected Real Time Clock 16 : 'HAP', # High Assurance Platform 17 : 'IPV6', # Internet Protocol v6 18 : 'KVM', # Keyboard, Video and Mouse 19 : 'OCH', 20 : 'DAL', # Dynamic Application Loader 21 : 'TLS', # Transport Layer Security 22 : 'CILA', # Client Initiated Local Access 23 : 'WLAN', # Wireless 24 : 'WL_DISP', # Wireless Display 25 : 'LH', 26 : 'NAP', # Microsoft Network Access Point 27 : 'ALARMCLK', # Local Wake and Update 28 : 'SECUREBOOT', # Secure Boot 29 : 'PTT', # Platform Trust Technology 30 : 'MDNSPROXY', # Multicast DNS Proxy 31 : 'NFC', # Near Field Communication } # CSE PCH Platforms pch_dict = { 0x0 : 'LBG-H', # Lewisburg H 0x3 : 'ICP-LP', # Ice Point LP 0x4 : 'ICP-N', # Ice Point N 0x5 : 'ICP-H', # Ice Point H 0x6 : 'TGP-LP', # Tiger Point LP 0x7 : 'TGP/EBG-H', # Tiger Point H, Tatlow H 0x8 : 'SPT/KBP-LP', # Sunrise Point LP, Union Point LP 0x9 : 'SPT-H', # Sunrise Point H 0xB : 'KBP/BSF/GCF-H', # Union Point H, Basin Falls H, Glacier Falls H, Comet Point V 0xC : 'CNP/CMP-LP', # Cannon Point LP, Comet Point LP 0xD : 'CNP/CMP-H', # Cannon Point H, Comet Point H 0xE : 'LKF-LP', # Lakefield LP 0xF : 'MCC-LP', # Mule Creek Canyon (Elkhart Lake) LP 0x10 : 'JSP-N', # Jasper Point N 0x11 : 'EBG-H', # Emmitsburg H 0x12 : 'ADP-LP', # Alder Point P (LP) } # CSE MFS Low Level File Names mfs_dict = { 0 : 'Unknown', 1 : 'Unknown', 2 : 'Anti-Replay', 3 : 'Anti-Replay', 4 : 'SVN Migration', 5 : 'Quota Storage', 6 : 'Intel Configuration', 7 : 'OEM Configuration', 9 : 'Manifest Backup', } # FD Component Sizes comp_dict = { 0 : 0x80000, # 512 KB 1 : 0x100000, # 1 MB 2 : 0x200000, # 2 MB 3 : 0x400000, # 4 MB 4 : 0x800000, # 8 MB 5 : 0x1000000, # 16 MB 6 : 0x2000000, # 32 MB 7 : 0x4000000, # 64 MB 8 : 0x8000000, # 128 MB 9 : 0x10000000, # 256 MB } # FTBL/EFST Platforms ftbl_efst_plat = { 0x01 : 'ICP', 0x02 : 'CMP', 0x03 : 'LKF', 0x04 : 'TGP', 0x05 : 'CMP-V', 0x08 : 'MCC', 0x0A : 'IDV', 0x0B : 'WTL', 0x10 : 'ADP', 0x11 : 'JSP', 0x13 : 'EBG', # TGP-H (Tatlow) } # Get MEA Parameters from input param = MEA_Param(sys.argv) # Get script location mea_dir = get_script_dir() # Set output location out_dir = param.out_dir or mea_dir # Make sure output location exists if not os.path.isdir(out_dir): os.makedirs(out_dir, exist_ok=True) # Enumerate parameter input arg_num = len(sys.argv) # Set dependencies paths mea_db_path = os.path.join(mea_dir, 'MEA.dat') # Initialize & Start background Thread for MEA & DB update check thread_update = Thread_With_Result(target=mea_upd_check, args=(mea_db_path,), daemon=True) if not param.upd_dis : thread_update.start() # Start as soon as possible (mea_dir, mea_db_path) # Check if MEA DB exists if os.path.isfile(mea_db_path) : with open(mea_db_path, 'r', encoding = 'utf-8') as db : mea_db_read = db.read() mea_db_lines = mea_db_read.splitlines() else : mea_hdr('') print(col_r + '\nError: MEA.dat file is missing!' + col_e) mea_exit(1) # Get Known Pre-Production RSA Public Key Hashes from DB rsa_pre_keys = get_db_json_obj('rsa_pre_keys') # Get CSE Known Bad Partition/Module Hashes from DB cse_known_bad_hashes = get_db_json_obj('cse_known_bad_hashes') # Get Database Revision mea_db_rev, mea_db_rev_p = mea_hdr_init() # Pause after any unexpected python exception sys.excepthook = show_exception_and_exit # Set console/shell window title mea_title = '%s %s' % (title, mea_db_rev) if sys_os == 'win32' : ctypes.windll.kernel32.SetConsoleTitleW(mea_title) elif sys_os.startswith('linux') or sys_os == 'darwin' : sys.stdout.write('\x1b]2;' + mea_title + '\x07') if not param.skip_intro : 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) elif arg_num > 2 : print("Press Enter to skip or input -? to list options\n") print("\nFiles: " + col_y + "Multiple" + col_e) else : print('Input a file name/path or press Enter to list options\n') 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() if param.mass_scan : in_path = input('\nEnter the full folder path : ') source = mass_scan(in_path) else : source = sys.argv[1:] # Skip script/executable # Initialize file input file_in = '' cur_count = 0 in_count = len(source) for arg in source : if arg in param.val : in_count -= 1 # Intel Engine/Graphics/Independent firmware Manifest pattern ($MN2 or $MAN, VEN_ID 0x8086) man_pat = re.compile(br'\x86\x80.{9}\x00\$((MN2)|(MAN))', re.DOTALL) # Intel Engine/Graphics/Independent placeholder Manifest pattern ($MN2, VEN_ID 0xBCCB) bccb_pat = re.compile(br'\xCB\xBC.{9}\x00\$MN2', re.DOTALL) # Intel Engine/Graphics/Independent firmware Code Partition Directory pattern ($CPD) cpd_pat = re.compile(br'\$CPD.\x00\x00\x00[\x01\x02]\x01[\x10\x14]', re.DOTALL) # Intel Engine/Graphics firmware Flash Partition Table pattern ($FPT) fpt_pat = re.compile(br'\$FPT[\x01-\x7F]\x00\x00\x00') # Intel Engine/Graphics firmware Boot Partition Descriptor Table pattern (BPDT) bpdt_pat = re.compile(br'\xAA\x55[\x00\xAA]\x00.\x00[\x01\x02][\x00\x01].{16}(.\x00.\x00.{3}\x00.{3}\x00){3}', re.DOTALL) # Intel Graphics firmware Option ROM Header & PCI Register pattern (PCIR) orom_pat = re.compile(br'\x55\xAA.{22}\x1C\x00.{2}PCIR\x86\x80.{4}[\x18\x1C]\x00', re.DOTALL) # Intel Flash Descriptor pattern (FD) fd_pat = re.compile(br'\x5A\xA5\xF0\x0F[\x01-\x10].{171}\xFF{16}', re.DOTALL) # Intel Engine/Graphics firmware Manifest probable patterns ($CPD + IUP, FTPR/OROM.man) pr_man_08_pat = re.compile(br'FTPR\.man\x00{4}.{2}\x00{2}.{2}\x00{6}.{19}\x00{5}', re.DOTALL) pr_man_09_pat = re.compile(br'OROM\.man\x00{4}.{2}\x00{2}.{2}\x00{6}.{19}\x00{5}', re.DOTALL) pr_man_10_pat = re.compile(br'grtos\.met\x00{3}.{2}\x00{2}.{2}\x00{6}.{19}\x00{5}', re.DOTALL) pr_cpd_parts = ['PMCP', 'PCOD', 'PCHC', 'SPHY', 'PPHY', 'PHYP', 'NPHY'] 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 = '' reading_msg = '' me2_type_fix = '' me2_type_exp = '' sku = 'NaN' sku_db = 'NaN' rel_db = 'NaN' type_db = 'NaN' platform = 'NaN' sku_init = 'NaN' sku_init_db = 'NaN' pdm_status = 'NaN' variant = 'Unknown' variant_p = 'Unknown' variant_p_fw = 'Unknown' sku_result = 'Unknown' sku_stp = 'Unknown' phy_sku = 'Unknown' pmc_date = 'Unknown' phy_date = 'Unknown' pchc_date = 'Unknown' me7_blist_1 = 'Empty' me7_blist_2 = 'Empty' cse_in_id_str = '0000' pos_sku_ker = 'Invalid' pos_sku_ext = 'Unknown' pos_sku_tbl = 'Unknown' pmc_pch_sku = 'Unknown' pmc_pch_rev = 'Unknown' pmc_platform = 'Unknown' phy_platform = 'Unknown' pchc_platform = 'Unknown' pmc_mn2_signed = 'Unknown' phy_mn2_signed = 'Unknown' pchc_mn2_signed = 'Unknown' fwu_iup_result = 'Unknown' mfs_state = 'Unconfigured' mn2_meu_ver = '0.0.0.0000' pmc_meu_ver = '0.0.0.0000' phy_meu_ver = '0.0.0.0000' pchc_meu_ver = '0.0.0.0000' pvbit = None pt_dfpt = None fpt_hdr = None bpdt_hdr = None byp_match = None pmc_pvbit = None phy_pvbit = None pchc_pvbit = None old_mn2_hdr = None pmc_mn2_ver = None phy_mn2_ver = None pchc_mn2_ver = None cse_lt_struct = None start_man_match = None end_man_match = None mfs_parsed_idx = None uncharted_match = None intel_cfg_hash_mfs = None var_rsa_db = True efs_init = False efs_found = False mfs_found = False mfsb_found = False mfs_is_afs = False rgn_exist = False rbep_found = False fitc_found = False cdmd_found = False ifwi_exist = False utok_found = False oemp_found = False is_orom_img = False fw_type_fix = False is_patsburg = False can_search_db = True fpt_chk_fail = False sps_extr_ignore = False sps_opr_found = False fwu_iup_exist = False fpt_romb_found = False fitc_ver_found = False pmcp_fwu_found = False pchc_fwu_found = False phy_fwu_found = False fw_in_db_found = False fd_me_rgn_exist = False cse_lt_chk_fail = False fd_pdr_rgn_exist = False fd_bios_rgn_exist = False fd_devexp_rgn_exist = False rgn_over_extr_found = False is_unsupported = False phy_all_anl = [] phy_all_init = [] pmc_all_anl = [] pmc_all_init = [] pchc_all_anl = [] pchc_all_init = [] gsc_info = [] mfs_info = [] err_stor = [] note_stor = [] warn_stor = [] s_bpdt_all = [] fpt_matches = [] p_store_all = [] fpt_part_all = [] orom_hdr_all = [] bpdt_matches = [] bpdt_hdr_all = [] bpdt_data_all = [] bpdt_part_all = [] pch_init_final = [] cse_lt_part_all = [] cse_lt_hdr_info = [] init_man_match = [0,0] eng_size_text = ['', False] cse_red_info = [False, True, True] ext15_info = [0, '', ('',''), ''] pmc_ext15_info = [0, '', ('',''), ''] phy_ext15_info = [0, '', ('',''), ''] pchc_ext15_info = [0, '', ('',''), ''] fdv_status = [False, False, False, False, []] msg_set = set() msg_entries = [] ftbl_blob_dict = {} ftbl_entry_dict = {} mea_json = {file_in: {}} vcn = -1 svn = -1 sku_me = -1 pmc_svn = -1 phy_svn = -1 pchc_svn = -1 pmc_vcn = -1 phy_vcn = -1 pchc_vcn = -1 mod_size = 0 fw_0C_lbg = 0 sku_type = -1 sku_size = -1 sku_slim = 0 fd_count = 0 fpt_count = 0 cse_in_id = 0 fpt_start = 0 efs_start = -1 mfs_start = -1 mfsb_start = -1 efs_size = 0 mfs_size = 0 mfsb_size = 0 pmcp_size = 0 pchc_size = 0 fitc_size = 0 cdmd_size = 0 phy_size = 0 oem_signed = 0 rbep_start = -1 fpt_length = -1 fpt_version = -1 pmc_fw_ver = -1 phy_fw_ver = -1 pchc_fw_ver = -1 fitc_major = -1 fitc_minor = -1 fitc_build = -1 fitc_hotfix = -1 p_end_last = 0 old_mn2_off = 0 mod_end_max = 0 cse_lt_off = -1 cse_lt_size = 0 cpd_end_last = 0 me_fd_start = -1 me_fd_size = -1 pdr_fd_size = -1 bios_fd_size = -1 bios_fd_start = -1 pmc_fw_rel = -1 phy_fw_rel = -1 pchc_fw_rel = -1 pmc_pch_gen = -1 pchc_fw_major = -1 pchc_fw_minor = -1 vol_ftbl_id = -1 vol_ftbl_pl = -1 pdr_fd_start = -1 fpt_part_num = -1 p_offset_last = 0 fpt_chk_int = 0 fpt_chk_mea = 0 fpt_pat_bgn = 0 fpt_pat_end = 0 bpdt_pat_bgn = 0 cse_lt_dp_size = 0 cse_lt_bp_size = 0 cse_lt_dup_size = 0 sps3_chk16_file = 0 sps3_chk16_calc = 0 devexp_fd_size = -1 devexp_fd_start = -1 p_end_last_back = -1 cse_lt_flags_red = 0 orom_pci_size = 0 orom_cpd_size = 0 check_fw_align = 0 file_has_align = 0 eng_fw_align = 0x1000 mod_end = 0xFFFFFFFF p_max_size = 0xFFFFFFFF eng_fw_end = 0xFFFFFFFF 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 access_enc = ['No','Yes'][f2] # Access > Encryption access_arp = ['No','Yes'][f3] # Access > Anti-Replay access_unk = '{0:013b}b'.format(f4) # Access > Unknown group_id = '0x%0.4X' % entry.GroudID # Group ID 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:] file_page = entry.FilePage # EFS Page Number (0,1,2...) file_offset = entry.FileOffset # File Offset 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 # each File Size to determine the next File Offset, starting from 0x0. dict_offset = [0] # First File starts at Data Area Buffer Offset 0x0 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) pr_man_04 = reading[end_man_match + 0x2DC:end_man_match + 0x2E7] # EpsRecovery,EpsFirmware (SPS 1) pr_man_05 = reading[end_man_match + 0x270:end_man_match + 0x277] # $MMEBUP (ME 6 BYP Part 1, SPS 2 - 3 Part 2) pr_man_06 = reading[end_man_match + 0x33C:end_man_match + 0x340] # $MMX (ME 6 BYP Part 2) 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' \ or pr_man_03 == b'BRINGUP' \ or pr_man_04 in (b'EpsRecovery', b'EpsFirmware') \ or pr_man_05 + pr_man_06 == b'$MMEBUP$MMX' \ or pr_man_08 and not pr_man_10 \ or pr_man_09 \ 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 fd_rsa_crash = mn2_signs[3] # FDV RSA Signature crashed fd_hash_int = fd_info[0] # FDV Extension 23 Hash value 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) else : cse_lt_pos_16 = reading[:0x1000].find(b'\x00' * 0x18 + b'\x22' + b'\x00' * 7 + b'\xFF' * 0xFB8) # At IFWI 1.6, try static "Checksum" field cse_lt_pos_17 = re.compile(br'\x40\x00[\x00\x01]\x00.{4}(.{3}\x00){14}', re.DOTALL).search(reading[:0x1000]) # At IFWI 1.7, try struct pattern 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 cse_lt_17 = get_struct(reading, cse_lt_off, CSE_Layout_Table_17) # IFWI 1.7 Structure cse_lt_16_hdr_pad = reading[cse_lt_off + ctypes.sizeof(CSE_Layout_Table_16):cse_lt_off + cse_lt_size] # IFWI 1.6 Header Padding cse_lt_17_hdr_pad = reading[cse_lt_off + ctypes.sizeof(CSE_Layout_Table_17):cse_lt_off + cse_lt_size] # IFWI 1.7 Header Padding cse_lt_16_fpt_sig = reading[cse_lt_off + cse_lt_16.DataOffset:cse_lt_off + cse_lt_16.DataOffset + 0x4] # IFWI 1.6 FPT Signature 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) elif cse_lt_16_fpt_sig == b'$FPT' and cse_lt_16_bp1_sig in cse_lt_bp and cse_lt_16_hdr_pad == len(cse_lt_16_hdr_pad) * b'\xFF' : cse_lt_struct = cse_lt_16 # CSE LT IFWI 1.6 with Data fpt_pat_bgn = cse_lt_off + cse_lt_16.DataOffset # Adjust $FPT Starting Offset based on CSE LT IFWI 1.6 Data fpt_pat_end = cse_lt_off + cse_lt_16.DataOffset + 0x4 # Adjust $FPT End Offset based on CSE LT IFWI 1.6 Data elif cse_lt_17_fpt_sig == b'$FPT' and 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 with Data fpt_pat_bgn = cse_lt_off + cse_lt_17.DataOffset # Adjust $FPT Starting Offset based on CSE LT IFWI 1.7 Data fpt_pat_end = cse_lt_off + cse_lt_17.DataOffset + 0x4 # Adjust $FPT End Offset based on CSE LT IFWI 1.7 Data elif cse_lt_16_bp1_sig in cse_lt_bp and cse_lt_16_hdr_pad == len(cse_lt_16_hdr_pad) * b'\xFF' : 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 cse_lt_pointers = reading[cse_lt_off + 0x10:cse_lt_off + 0x14] + b'\x00' * 4 + reading[cse_lt_off + 0x18:cse_lt_off + 0x10 + cse_lt_struct.Size] cse_lt_chk_int = cse_lt_struct.Checksum cse_lt_chk_mea = crccheck.crc.Crc32.calc(cse_lt_pointers) if cse_lt_chk_mea != cse_lt_chk_int : cse_lt_chk_fail = True warn_stor.append([col_m + 'Warning: Wrong CSE Layout Table CRC 0x%0.8X, expected 0x%0.8X!' % (cse_lt_chk_int,cse_lt_chk_mea) + col_e, True]) 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 for all_part in cse_lt_part_all : # Partition A starts before B but ends after B start # Ignore partitions which have empty offset or size # Ignore FPTB Boot 4 within $FPT Data (CSSPS 4.4) if not part[4] and not all_part[4] and not any(s in [0,0xFFFFFFFF] for s in (part[1],part[2],all_part[1],all_part[2])) and (part[1] < all_part[1] < part[2]) \ 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] : note_stor.append([col_y + 'Note: FD Engine/Graphics region seems to include ROM-Bypass code!' + col_e, True]) 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() if fptemp_exists and fptemp_info[2] > fpt_match_start >= fptemp_info[1] : pass # CSE FTPR > fptemp elif cse_lt_struct and cse_lt_off < fpt_match_start < fpt_pat_bgn : pass # CSE LT Padding > $FPT elif cse_lt_struct and cse_lt_flags_red and fpt_match_start == cse_lt_part_all[0][1] + 0x1000 : pass # CSE LT Data Redundancy > $FPT 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 elif reading[fpt_pat_bgn - 0x1000:fpt_pat_bgn - 0xFA8] == b'\x00' * 0x48 + b'\xFF' * 0x10 \ or reading[fpt_pat_bgn - 0x1000:fpt_pat_bgn - 0xFA0] == b'\x00' * 0x50 + b'\xFF' * 0x10 : 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 p_offset_min = min(p_offset,p_offset_min) p_offset_spi = fpt_start + fpt_entry.Offset p_size = fpt_entry.Size p_valid_print = not p_valid == 0xFF 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 mn2_len = mn2_hdr.HeaderLength * 4 mod_name = reading[p_offset_spi + mn2_len:p_offset_spi + mn2_len + 0x8].strip(b'\x00').decode('utf-8') 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 # regions. It cannot be removed because MEA needs to jump to COD1/OPR1 for (CS)SPS parsing. The Intel # POR is to have at most two $FPT at normal (CS)SPS images, Main ($FPT) and Backup (FPTB), so MEA skips # this adjustment for images with more than two $FPT hits. The drawback is that MEA detects FTPR instead # of COD1/OPR1 at these Intel Capsule images. A proper detection/extractor could be added in the future. for p_rec_fix in p_store_all : # For ME 2-5 & SPS 1, pick CODE if RCVY or COD1 are not present # For SPS, pick Operational (COD1/OPR1) instead of Recovery (CODE/FTPR) if p_rec_fix[0] in ['FTPR', 'RCVY', 'OPR1', 'OPR', 'COD1'] or (p_rec_fix[0] == 'CODE' and not any(p in ('RCVY', 'COD1') for p in p_store_all)) : # 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 end_man_match += p_rec_fix[1] else : # 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 data_part_data = reading[cse_lt_part_all[0][1]:cse_lt_part_all[0][3]] 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) for part in cse_lt_part_all : if part[0].startswith('Boot') and not part[4] : # Non-Empty CSE LT Boot Partition (skip Data/MFS) bpdt_match = bpdt_pat.search(reading[part[1]:part[3]]) # BPDT detection if bpdt_match : bpdt_matches.append((bpdt_match.start() + part[1], bpdt_match.end() + part[1])) # Store BPDT range, relative to 0x0 else : # Search entire image when no CSE LT exists (slower & false positive prone) bpdt_match = list(bpdt_pat.finditer(reading)) # BPDT detection for match in bpdt_match : 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 # Ignore partitions which have empty offset or size # Ignore DLMP partition which overlaps by Intel design if not part[4] and not all_part[4] and not any(s in [0,0xFFFFFFFF] for s in (part[1],part[2],all_part[1],all_part[2])) \ 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 pcir_hdr_data = reading[pcir_off:pcir_off + pcir_hdr_size] # Store OROM PCI Data Structure Contents 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 build = mn2_ftpr_hdr.Build svn = mn2_ftpr_hdr.SVN if hasattr(mn2_ftpr_hdr, 'VCN') : vcn = mn2_ftpr_hdr.VCN day = mn2_ftpr_hdr.Day 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 rsa_exp_len = mn2_ftpr_hdr.ExponentSize * 4 # RSA Exponent Length rsa_key = reading[rsa_block_off:rsa_block_off + rsa_key_len] # RSA Public Key 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 rsa_block_off_i = init_man_match[1] + 0x60 # Initial (FTPR) RSA Block Offset 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 if not fd_exist and not cse_lt_struct and reading[p_end_last:p_end_last + 0x4] != b'$CPD' : 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 mod_start = p_end_last + man_len + 0xC mod_name = reading[p_end_last + man_len:p_end_last + man_len + 0x8].strip(b'\x00').decode('utf-8') mod_in_id = reading[p_end_last + man_len + 0x15:p_end_last + man_len + 0x15 + 0xB].strip(b'\x00').decode('utf-8') 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 : # Partition A starts before B but ends after B start # Ignore partitions which have empty offset or size # Ignore FTUP combo partition (NFTP + WCOD + LOCL) # Ignore DLMP partition which overlaps by Intel design if not part[6] and not all_part[6] and not any(s in [0,0xFFFFFFFF] for s in (part[1],part[2],all_part[1],all_part[2])) \ 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 : # Check for duplicate/nested CSE LT entries for entry_c in cse_lt_hdr_info : 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 else : eng_fw_end = p_end_last + eng_fw_align - fpt_start 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 if fd_me_rgn_exist : if eng_fw_end > me_fd_size : eng_size_text = [col_m + 'Warning: Firmware size exceeds Engine/Graphics region, possible data loss!' + col_e, False] elif eng_fw_end < me_fd_size and fd_is_cut : eng_size_text = [col_m + 'Warning: Firmware size exceeds Engine/Graphics region, possible data loss!' + col_e, False] elif eng_fw_end < me_fd_size : # Extra data at Engine/Graphics FD region padding padd_size_fd = me_fd_size - eng_fw_end 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+) eng_size_text = [col_y + 'Note: File is missing optional Firmware 4K alignment padding!' + col_e, False] # warn_stor if param.check : # Add alignment padding, when missing (Debug/Research) with open('__APADD__' + os.path.basename(file_in), 'wb') as o : o.write(reading + b'\xFF' * check_fw_align) else : 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 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_file]) 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 fw_type = 'Extracted' fitc_major = bpdt_hdr.FitMajor fitc_minor = bpdt_hdr.FitMinor fitc_hotfix = bpdt_hdr.FitHotfix fitc_build = bpdt_hdr.FitBuild elif rgn_exist : # SPS 1-3 have their own firmware Types if variant == 'SPS' : fw_type = 'Extracted' # SPS is built manually so EXTR elif variant == 'ME' and (2 <= major <= 7) : # Check 1, FOVD partition if (major >= 3 and not fovd_clean('new')) or (major == 2 and not fovd_clean('old')) : fw_type = 'Extracted' else : # Check 2, EFFS/NVKR strings fitc_match = re.compile(br'KRND\x00').search(reading) # KRND. detection = FITC, 0x00 adds old ME RGN support if fitc_match is not None : if major == 4 : fw_type_fix = True # ME4-Only Fix 3 else : fw_type = 'Extracted' elif major in [2,3] : fw_type_fix = True # ME2-Only Fix 1, ME3-Only Fix 1 else : fw_type = 'Stock' 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' else : # Get FIT/FITC version used to build the image fitc_ver_found = True fw_type = 'Extracted' fitc_major = fpt_hdr.FitMajor fitc_minor = fpt_hdr.FitMinor fitc_hotfix = fpt_hdr.FitHotfix 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 sps3_chk_end = sps3_chk_start + fpt_part_num * 0x20 sps3_chk16_file = '0x%0.4X' % int.from_bytes(reading[sps3_chk_end:sps3_chk_end + 0x2], 'little') sps3_chk16_sum = sum(bytearray(reading[sps3_chk_start:sps3_chk_end])) & 0xFFFF 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' elif rel_signed == 'Production' : release = 'Production' rel_db = 'PRD' 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 sku_me = int.from_bytes(reading[start_sku_match + 8:start_sku_match + 0xC], 'big') 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' elif sku_me == 0x02000000 : # QST sku = 'QST' sku_db = 'QST' 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) : nvkr_match = (re.compile(br'NVKRKRID')).search(reading) # NVKRKRID detection if nvkr_match is not None : (start_nvkr_match, end_nvkr_match) = nvkr_match.span() nvkr_start = int.from_bytes(reading[end_nvkr_match:end_nvkr_match + 0x4], 'little') nvkr_size = int.from_bytes(reading[end_nvkr_match + 0x4:end_nvkr_match + 0x8], 'little') nvkr_data = reading[fpt_start + nvkr_start:fpt_start + nvkr_start + nvkr_size] # NVKR sections : Name[0xC] + Size[0x3] + Data[Size] prat_match = (re.compile(br'Pra Table\xFF\xFF\xFF')).search(nvkr_data) # "Pra Table" detection (2.5/2.6) maxk_match = (re.compile(br'MaxUsedKerMem\xFF\xFF\xFF')).search(nvkr_data) # "MaxUsedKerMem" detection if prat_match is not None : (start_prat_match, end_prat_match) = prat_match.span() prat_start = fpt_start + nvkr_start + end_prat_match + 0x3 prat_end = fpt_start + nvkr_start + end_prat_match + 0x13 me2_type_fix = int.from_bytes(reading[prat_start:prat_end], 'big') me2_type_exp = 0x7F45DBA3E65424458CB09A6E608812B1 elif maxk_match is not None : (start_maxk_match, end_maxk_match) = maxk_match.span() qstpat_start = fpt_start + nvkr_start + end_maxk_match + 0x68 qstpat_end = fpt_start + nvkr_start + end_maxk_match + 0x78 me2_type_fix = int.from_bytes(reading[qstpat_start:qstpat_end], 'big') me2_type_exp = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF elif sku == 'AMT' and minor < 5 : nvsh_match = (re.compile(br'NVSHOSID')).search(reading) # NVSHOSID detection if nvsh_match is not None : netip_match = (re.compile(br'net\.ip\xFF\xFF\xFF')).search(reading) # "net.ip" detection (2.0-2.2) if netip_match is not None : (start_netip_match, end_netip_match) = netip_match.span() netip_size = int.from_bytes(reading[end_netip_match + 0x0:end_netip_match + 0x3], 'little') netip_start = fpt_start + end_netip_match + 0x4 # 0x4 always 03 so after that byte for 00 search 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', '8310BA06D7B9687FC18847991F9B1D747B55EF30E5E0E5C7B48E1A13A5BEE5FA'] 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' elif sku_me == 0x06000000 : # ASF + QST sku = 'ASF' # Q33 (HP dc5800) sku_db = 'ASF' elif sku_me == 0x02000000 : # QST sku = 'QST' sku_db = 'QST' else : sku = col_r + 'Unknown' + col_e err_stor.append([col_r + 'Error: Unknown %s %d.%d SKU!' % (variant, major, minor) + col_e, True]) # ME3-Only Fix 1 : The usual method to detect EXTR vs RGN does not work for ME3 if fw_type_fix : me3_type_fix1 = [] me3_type_fix2a = 0x10 * 0xFF me3_type_fix2b = 0x10 * 0xFF me3_type_fix3 = 0x10 * 0xFF effs_match = (re.compile(br'EFFSOSID')).search(reading) # EFFSOSID detection if effs_match is not None : (start_effs_match, end_effs_match) = effs_match.span() 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') me3_type_fix2b = int.from_bytes(reading[fpt_start + effs_start + end_me3f2_match + 0x30:fpt_start + effs_start + end_me3f2_match + 0x40], 'big') 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 # min size(ASF, UPD) is 0xB0904 so 0x100000 safe min AMT ROMB # min size(AMT, UPD) is 0x190904 so 0x185000 safe max AMT ROMB # min size(QST, UPD) is 0x2B8CC so 0x40000 safe min for ASF ROMB # min size(ASF, UPD) is 0xB0904 so 0xAF000 safe max for ASF ROMB # min size(QST, UPD) is 0x2B8CC so 0x2B000 safe max for QST ROMB 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' elif sku_me in [0x8C200000,0x8C000000,0x0C000000] : # 0C000000 for Pre-Alpha ROMB sku = 'AMT' # CA_ICH9_REL_IAMT_ (AMT) sku_db = 'AMT' elif sku_me in [0xA0200000,0xA0000000] : sku = 'TPM' # CA_ICH9_REL_NOAMT_ (TPM) sku_db = 'TPM' 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) byp_match = byp_pat.search(reading) 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' : tpm_tag = (re.compile(br'\$MME.{24}TPM', re.DOTALL)).search(reading) # $MME + [0x18] + TPM amt_tag = (re.compile(br'\$MME.{24}MOFFM1_OVL', re.DOTALL)).search(reading) # $MME + [0x18] + MOFFM1_OVL 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' elif tpm_tag is not None : sku = 'TPM' # CA_ICH9_REL_NOAMT_ sku_db = 'TPM' 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 if effs_match is not None : (start_effs_match, end_effs_match) = effs_match.span() 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' elif sku_me == 0x060D0000 : # EL_ICH10_SKU4 sku = 'Base Consumer' # NoAMT sku_db = 'BC' elif sku_me == 0x06080000 : # EL_ICH10_SKU2 or EL_ICH10_SKU3 sku = 'Digital Home or Base Corporate (?)' sku_db = 'DHBC' 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) byp_match = byp_pat.search(reading) 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' platform = 'CCK' else : # P55, PM55, 34xx (Ibex Peak) ign_pch = 'IBX' sku_db = 'IGN_' + ign_pch sku = 'Ignition ' + ign_pch elif sku_me == 0x701C0000 : # Home IT (1.5MB, 4MB) sku = '1.5MB' sku_db = '1.5MB' # xxDCxx = 6.x, xxFCxx = 6.0, xxxxEE = Mobile, xxxx6E = Desktop, F7xxxx = Old Alpha/Beta Releases elif sku_me in [0x77DCEE00,0x77FCEE00,0xF7FEFE00] : # vPro (5MB, 8MB) sku = '5MB MB' sku_db = '5MB_MB' elif sku_me in [0x77DC6E00,0x77FC6E00,0xF7FE7E00] : # vPro (5MB, 8MB) sku = '5MB DT' sku_db = '5MB_DT' 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' elif sku_size * 0.5 == 1.5 : sku = '1.5MB' sku_db = '1.5MB' 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') me7_blist_1_build = int.from_bytes(reading[start_man_match + 0x6E3:start_man_match + 0x6E5], 'little') if me7_blist_1_build != 0 : me7_blist_1 = '<= 7.%d.%d.%d' % (me7_blist_1_minor, me7_blist_1_hotfix, me7_blist_1_build) me7_blist_2_minor = int.from_bytes(reading[start_man_match + 0x6EB:start_man_match + 0x6ED], 'little') 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' elif sku_type == 1 : sku = '1.5MB' sku_db = '1.5MB' 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' elif sku_type == 1 : sku = '1.5MB' sku_db = '1.5MB' 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 # from CSE_Ext_0C, MEA should ignore the placeholder "Corporate" SKU at CSE_Ext_0F_R2 and use the actual value from CSE_Ext_0C. # Generally, due to CSE_Ext_0F_R2 confusion, CSE_Ext_0C should be prefered when CSE_Ext_0F_R2 SKU is CON,COR,ALL,NA or missing. # There are plans for another (seriously ?) SKU Type extension, CSE_Ext_23. Support for CSE_Ext_23 can be added once it's used. if fw_0C_sku1[1] != 'UNK' and ext15_info[2][1] in ['','NA','ALL','CON','COR','ATM'] : sku_init = fw_0C_sku1[0] sku_init_db = fw_0C_sku1[1] elif ext15_info[2][1] not in ['','NA','ALL'] : sku_init = ext15_info[2][0] sku_init_db = ext15_info[2][1] 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 sku = col_r + 'Unknown' + col_e err_stor.append([col_r + 'Error: Unknown %s %d.%d SKU!' % (variant, major, minor) + col_e, True]) else : pass # SKU retrieved from manual MEA DB entry else : 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) if sku_result == 'LP' : # PDM not in DB, scan decompressed Huffman module FTPR > bup if sku_pdm not in ['NPDM','YPDM'] : for mod in cpd_mod_attr : 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' platform = 'CGM' elif sku_size * 0.5 in (1.5,2.0) : sku = '1.25MB' if minor == 0 else '1.375MB' sku_db = '1.25MB' if minor == 0 else '1.375MB' platform = 'BYT' elif sku_size * 0.5 in (2.5,3.0) : sku = '3MB' sku_db = '3MB' 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 if rec_sku_match : sku = (reading[start_man_match + rec_sku_match.start() + 0x8:start_man_match + rec_sku_match.start() + 0xA]).decode('utf-8') sku_db = sku 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) if ext_pname == 'FTPR' : fw_type = 'Recovery' # Non-Intel POR for REC 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' sku_init_db = 'UNK' elif fw_0C_sku0 : # SKU in CSE_Ext_0C, prefer over CSE_Ext_0F sku = fw_0C_sku1[0] sku_init_db = fw_0C_sku1[1] elif ext15_info[0] != 0 and ext15_info[1] == '' : # SKU in CSE_Ext_0F_R2 but empty sku = 'Ignition' sku_init_db = 'IGN' 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' elif fw_type == 'Operational' : type_db = 'OPR' elif fw_type == 'Recovery' : type_db = 'REC' elif fw_type == 'Independent' and variant.startswith('PMC') : type_db = 'PMC' elif fw_type == 'Independent' and variant.startswith('PHY') : type_db = 'PHY' 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' if variant == 'CSME' and major >= 16 and file_has_align > 0x0 and fpt_start == 0x0 : fwu_iup_result = 'Impossible' if fwu_iup_result != 'Impossible' : if major == 12 : fwu_iup_result = ['No','Yes'][int(pmcp_fwu_found)] elif (major,minor) in [(13,0),(13,50),(14,0),(14,5),(15,40),(16,0)] or (major,minor,sku_result) == (15,0,'LP') : fwu_iup_result = ['No','Yes'][int(pmcp_fwu_found and pchc_fwu_found)] elif (major,minor) in [(13,30),(14,1)] or (major,minor,sku_result) == (15,0,'H') : 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) elif variant.startswith(('PMCAPL','PMCBXT','PMCGLK')) : # PMC APL A/B, BXT C, GLK A/B name_fw = '%s_%s_%s_%s_%s' % (platform[:3], fw_ver, sku_stp[0], date, rel_db) elif variant.startswith(('PMCCNP','PMCWTL','PMCIDV')) and (major < 30 or major == 3232) : # PMC CNP A, WTL name_fw = '%s_%s_%s_%s_%s' % (platform[:3], fw_ver, sku_db, date, rel_db) elif variant.startswith('PMCDG') : # PMC DG name_fw = '%s_%s_%s_%s' % (platform[:4], fw_ver, date, rel_db) elif variant.startswith('PMC') : # PMC CNP A/B, ICP, LKF, CMP, TGP, ADP, DG1 name_fw = '%s_%s_%s_%s' % (platform[:3], fw_ver, sku_db, rel_db) elif variant.startswith('PCHC') : # PCHC ICP, LKF, CMP, TGP, ADP name_fw = '%s_%s_%s' % (platform[:3], fw_ver, rel_db) elif variant.startswith('PHYDG') : # PHY DG name_fw = '%s_%s_%s_%s_%s' % (platform[:3], sku, fw_ver, date, rel_db) elif variant.startswith('PHY') : # PHY S, P name_fw = '%s_%s_%s_%s' % (platform[:3], sku, fw_ver, rel_db) elif variant.startswith('OROM') : # OROM 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 : # Search the re-created file name without extension at the database if name_db in line : fw_in_db_found = True # Known firmware, nothing new if rsa_sig_hash in line and '_%s_' % rel_db in line and type_db == 'EXTR' and ('_RGN' in line or '_EXTR-Y' in line) : rgn_over_extr_found = True # Same firmware found but of preferred type (RGN > EXTR, EXTR-Y > EXTR-N), nothing new fw_in_db_found = True # For ME 6.0 IGN, (CS)ME 7+, (CS)TXE if rsa_sig_hash in line and type_db == 'UPD' and ((variant in ['ME','CSME'] and (major >= 7 or (major == 6 and 'Ignition' in sku))) or variant in ['TXE','CSTXE']) and ('_RGN' in line or '_EXTR' in line) : rgn_over_extr_found = True # Same RGN/EXTR firmware found at database, UPD disregarded if rsa_sig_hash in line and (variant,type_db,sku_stp) == ('CSSPS','REC','Unknown') : 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]) msg_pmc_pt.add_row(['Type', 'Independent']) if (variant == 'CSME' and major >= 12) or (variant == 'CSSPS' and major >= 5) or not pmc_platform.startswith(('APL','BXT','GLK','DG')) : msg_pmc_pt.add_row(['Chipset SKU', pmc_pch_sku]) if not pmc_platform.startswith('DG') : msg_pmc_pt.add_row(['Chipset Stepping', 'Unknown' if pmc_pch_rev[0] == 'U' else pmc_pch_rev[0]]) msg_pmc_pt.add_row(['TCB Security Version Number', pmc_svn]) msg_pmc_pt.add_row(['ARB Security Version Number', pmc_ext15_info[0]]) msg_pmc_pt.add_row(['Version Control Number', pmc_vcn]) if pmc_pvbit is not None : msg_pmc_pt.add_row(['Production Ready', ['No','Yes'][pmc_pvbit]]) # Always check against None msg_pmc_pt.add_row(['Date', pmc_date]) 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]) msg_pchc_pt.add_row(['Type', 'Independent']) msg_pchc_pt.add_row(['TCB Security Version Number', pchc_svn]) msg_pchc_pt.add_row(['ARB Security Version Number', pchc_ext15_info[0]]) msg_pchc_pt.add_row(['Version Control Number', pchc_vcn]) if pchc_pvbit is not None : msg_pchc_pt.add_row(['Production Ready', ['No','Yes'][pchc_pvbit]]) # Always check against None msg_pchc_pt.add_row(['Date', pchc_date]) 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]) msg_phy_pt.add_row(['Type', 'Independent']) msg_phy_pt.add_row(['SKU', phy_sku]) msg_phy_pt.add_row(['TCB Security Version Number', phy_svn]) msg_phy_pt.add_row(['ARB Security Version Number', phy_ext15_info[0]]) msg_phy_pt.add_row(['Version Control Number', phy_vcn]) if phy_pvbit is not None : msg_phy_pt.add_row(['Production Ready', ['No','Yes'][phy_pvbit]]) # Always check against None msg_phy_pt.add_row(['Date', phy_date]) 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) mea_exit(0)