From 7c00479a9e01cd6c39b31201413f3c4b93c4225e Mon Sep 17 00:00:00 2001 From: platomav Date: Fri, 1 Apr 2022 17:48:20 +0300 Subject: [PATCH] cleanup old files --- .gitattributes | 2 - AMI BIOS Guard Extractor/AMI_PFAT_Extract.py | 321 ----- AMI UCP BIOS Extractor/AMI_UCP_Extract.py | 610 -------- Apple EFI File Renamer/Apple_EFI_Rename.py | 104 -- Apple EFI IM4P Splitter/Apple_EFI_Split.py | 146 -- .../Apple_EFI_Package.py | 101 -- Apple EFI Package Grabber/Apple_EFI_Grab.dat | 4 - Apple EFI Package Grabber/Apple_EFI_Grab.py | 129 -- .../Award_BIOS_Extract.py | 65 - Dell PFS Update Extractor/Dell_PFS_Extract.py | 1245 ----------------- .../Fujitsu_SFX_Extract.py | 82 -- .../Fujitsu_UPC_Extract.py | 108 -- .../Insyde_iFlash_Extract.py | 270 ---- .../Panasonic_BIOS_Extract.py | 74 - .../Phoenix_SCT_Extract.py | 213 --- .../Portwell_EFI_Extract.py | 174 --- .../VAIO_Package_Extract.py | 126 -- 17 files changed, 3774 deletions(-) delete mode 100644 .gitattributes delete mode 100644 AMI BIOS Guard Extractor/AMI_PFAT_Extract.py delete mode 100644 AMI UCP BIOS Extractor/AMI_UCP_Extract.py delete mode 100644 Apple EFI File Renamer/Apple_EFI_Rename.py delete mode 100644 Apple EFI IM4P Splitter/Apple_EFI_Split.py delete mode 100644 Apple EFI Package Extractor/Apple_EFI_Package.py delete mode 100644 Apple EFI Package Grabber/Apple_EFI_Grab.dat delete mode 100644 Apple EFI Package Grabber/Apple_EFI_Grab.py delete mode 100644 Award BIOS Module Extractor/Award_BIOS_Extract.py delete mode 100644 Dell PFS Update Extractor/Dell_PFS_Extract.py delete mode 100644 Fujitsu SFX BIOS Extractor/Fujitsu_SFX_Extract.py delete mode 100644 Fujitsu UPC BIOS Extractor/Fujitsu_UPC_Extract.py delete mode 100644 Insyde iFlash Image Extractor/Insyde_iFlash_Extract.py delete mode 100644 Panasonic BIOS Update Extractor/Panasonic_BIOS_Extract.py delete mode 100644 Phoenix SCT BIOS Extractor/Phoenix_SCT_Extract.py delete mode 100644 Portwell EFI BIOS Extractor/Portwell_EFI_Extract.py delete mode 100644 VAIO Packaging Manager Extractor/VAIO_Package_Extract.py diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index dfe0770..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto diff --git a/AMI BIOS Guard Extractor/AMI_PFAT_Extract.py b/AMI BIOS Guard Extractor/AMI_PFAT_Extract.py deleted file mode 100644 index bd1a1f0..0000000 --- a/AMI BIOS Guard Extractor/AMI_PFAT_Extract.py +++ /dev/null @@ -1,321 +0,0 @@ -#!/usr/bin/env python3 -#coding=utf-8 - -""" -AMI PFAT Extract -AMI BIOS Guard Extractor -Copyright (C) 2018-2021 Plato Mavropoulos -""" - -print('AMI BIOS Guard Extractor v3.2') - -import sys - -# Detect Python version -sys_ver = sys.version_info -if sys_ver < (3,7) : - sys.stdout.write('\n\nError: Python >= 3.7 required, not %d.%d!\n' % (sys_ver[0], sys_ver[1])) - (raw_input if sys_ver[0] <= 2 else input)('\nPress enter to exit') # pylint: disable=E0602 - sys.exit(1) - -import os -import re -import ctypes -import shutil -import traceback - -# 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('\nError: ABGE crashed, please report the following:\n') - traceback.print_exception(exc_type, exc_value, tb) - input('\nPress enter to exit') - sys.exit(1) - -# Pause after any unexpected python exception -sys.excepthook = show_exception_and_exit - -sys.dont_write_bytecode = True - -# https://github.com/allowitsme/big-tool by Dmitry Frolov -try : - from big_script_tool import BigScript - is_bgst = True -except : - is_bgst = False - -# 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 - -class PFAT_Header(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('Size', uint32_t), # 0x00 - ('Checksum', uint32_t), # 0x04 Unknown 16-bits - ('Tag', char*8), # 0x04 _AMIPFAT - ('Flags', uint8_t), # 0x10 - # 0x11 - ] - - def pfat_print(self) : - print('\n PFAT Main Header:\n') - print(' Size : 0x%X' % self.Size) - print(' Checksum : 0x%0.4X' % self.Checksum) - print(' Tag : %s' % self.Tag.decode('utf-8')) - print(' Flags : 0x%0.2X' % self.Flags) - -class PFAT_Block_Header(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('PFATVerMajor', uint16_t), # 0x00 - ('PFATVerMinor', uint16_t), # 0x02 - ('PlatformID', uint8_t*16), # 0x04 - ('Attributes', uint32_t), # 0x14 - ('ScriptVerMajor', uint16_t), # 0x16 - ('ScriptVerMinor', uint16_t), # 0x18 - ('ScriptSize', uint32_t), # 0x1C - ('DataSize', uint32_t), # 0x20 - ('BIOSSVN', uint32_t), # 0x24 - ('ECSVN', uint32_t), # 0x28 - ('VendorInfo', uint32_t), # 0x2C - # 0x30 - ] - - def __init__(self, count, *args, **kwargs): - super().__init__(*args, **kwargs) - self.count = count - - def get_flags(self) : - attr = PFAT_Block_Header_GetAttributes() - attr.asbytes = self.Attributes - - return attr.b.SFAM, attr.b.ProtectEC, attr.b.GFXMitDis, attr.b.FTU, attr.b.Reserved - - def pfat_print(self) : - no_yes = ['No','Yes'] - f1,f2,f3,f4,f5 = self.get_flags() - - PlatformID = bytes(self.PlatformID).strip(b'\x00') - if PlatformID.isalpha() : # STRING - PlatformID = PlatformID.decode('utf-8', 'ignore') - else : # GUID - PlatformID = '%0.*X' % (0x10 * 2, int.from_bytes(self.PlatformID, 'big')) - PlatformID = '{%s-%s-%s-%s-%s}' % (PlatformID[:8], PlatformID[8:12], PlatformID[12:16], PlatformID[16:20], PlatformID[20:]) - - print('\n PFAT Block %s Header:\n' % self.count) - print(' PFAT Version : %d.%d' % (self.PFATVerMajor, self.PFATVerMinor)) - print(' Platform ID : %s' % PlatformID) - print(' Signed Flash Address Map : %s' % no_yes[f1]) - print(' Protected EC OpCodes : %s' % no_yes[f2]) - print(' Graphics Security Disable : %s' % no_yes[f3]) - print(' Fault Tolerant Update : %s' % no_yes[f4]) - print(' Attributes Reserved : 0x%X' % f5) - print(' Script Version : %d.%d' % (self.ScriptVerMajor, self.ScriptVerMinor)) - print(' Script Size : 0x%X' % self.ScriptSize) - print(' Data Size : 0x%X' % self.DataSize) - print(' BIOS SVN : 0x%X' % self.BIOSSVN) - print(' EC SVN : 0x%X' % self.ECSVN) - print(' Vendor Info : 0x%X' % self.VendorInfo) - -class PFAT_Block_Header_Attributes(ctypes.LittleEndianStructure): - _fields_ = [ - ('SFAM', uint32_t, 1), # Signed Flash Address Map - ('ProtectEC', uint32_t, 1), # Protected EC OpCodes - ('GFXMitDis', uint32_t, 1), # GFX Security Disable - ('FTU', uint32_t, 1), # Fault Tolerant Update - ('Reserved', uint32_t, 28) - ] - -class PFAT_Block_Header_GetAttributes(ctypes.Union): - _fields_ = [ - ('b', PFAT_Block_Header_Attributes), - ('asbytes', uint32_t) - ] - -class PFAT_Block_RSA(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('Unknown0', uint32_t), # 0x00 - ('Unknown1', uint32_t), # 0x04 - ('PublicKey', uint32_t*64), # 0x08 - ('Exponent', uint32_t), # 0x108 - ('Signature', uint32_t*64), # 0x10C - # 0x20C - ] - - def __init__(self, count, *args, **kwargs): - super().__init__(*args, **kwargs) - self.count = count - - def pfat_print(self) : - PublicKey = '%0.*X' % (0x100 * 2, int.from_bytes(self.PublicKey, 'little')) - Signature = '%0.*X' % (0x100 * 2, int.from_bytes(self.Signature, 'little')) - - print('\n PFAT Block %s Signature:\n' % self.count) - print(' Unknown 0 : 0x%X' % self.Unknown0) - print(' Unknown 1 : 0x%X' % self.Unknown1) - print(' Public Key : %s [...]' % PublicKey[:8]) - print(' Exponent : 0x%X' % self.Exponent) - print(' Signature : %s [...]' % Signature[:8]) - -# https://github.com/skochinsky/me-tools/blob/master/me_unpack.py by Igor Skochinsky -def get_struct(buffer, 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 = buffer[start_offset:start_offset + struct_len] - fit_len = min(len(struct_data), struct_len) - - if (start_offset >= len(buffer)) or (fit_len < struct_len) : - input('\n Error: Offset 0x%X out of bounds at %s, possibly incomplete image!' % (start_offset, class_name.__name__)) - sys.exit(1) - - ctypes.memmove(ctypes.addressof(structure), struct_data, fit_len) - - return structure - -if len(sys.argv) >= 2 : - # Drag & Drop or CLI - ami_pfat = sys.argv[1:] -else : - # Folder path - ami_pfat = [] - in_path = input('\nEnter the full folder path: ') - print('\nWorking...') - for root, _, files in os.walk(in_path): - for name in files : - ami_pfat.append(os.path.join(root, name)) - -pfat_index = 1 -input_name = '' -input_extension = '' -output_path = '' -block_hdr_size = ctypes.sizeof(PFAT_Block_Header) -block_rsa_size = ctypes.sizeof(PFAT_Block_RSA) -pfat_pat = re.compile(b'_AMIPFAT.AMI_BIOS_GUARD_FLASH_CONFIGURATIONS', re.DOTALL) - -for input_file in ami_pfat : - file_data = b'' - final_data = b'' - block_name = '' - block_count = 0 - file_index = 0 - blocks = [] - - with open(input_file, 'rb') as in_file : buffer = in_file.read() - - pfat_match = pfat_pat.search(buffer) - - if pfat_index == 1 : - input_name,input_extension = os.path.splitext(os.path.basename(input_file)) - input_dir = os.path.dirname(os.path.abspath(input_file)) - - print('\n*** %s%s' % (input_name, input_extension)) - - if not pfat_match : - print('\n Error: This is not an AMI BIOS Guard (PFAT) image!') - continue - - output_path = os.path.join(input_dir, '%s%s' % (input_name, input_extension) + '_extracted') # Set extraction directory - - if os.path.isdir(output_path) : shutil.rmtree(output_path) # Delete any existing extraction directory - - os.mkdir(output_path) # Create extraction directory - - if not pfat_match : continue - - buffer = buffer[pfat_match.start() - 0x8:] - - pfat_hdr = get_struct(buffer, 0, PFAT_Header) - - hdr_size = pfat_hdr.Size - hdr_data = buffer[0x11:hdr_size].decode('utf-8').splitlines() - - pfat_hdr.pfat_print() - print(' Title : %s' % hdr_data[0]) - - file_path = os.path.join(output_path, '%s%s -- %d' % (input_name, input_extension, pfat_index)) - - for entry in hdr_data[1:] : - entry_data = entry.split(' ') - entry_data = [s for s in entry_data if s != ''] - entry_flags = int(entry_data[0]) - entry_param = entry_data[1] - entry_blocks = int(entry_data[2]) - entry_name = entry_data[3][1:] - - for i in range(entry_blocks) : blocks.append([entry_name, entry_param, entry_flags, i + 1, entry_blocks]) - - block_count += entry_blocks - - block_start = hdr_size - for i in range(block_count) : - is_file_start = blocks[i][0] != block_name - - if is_file_start : print('\n %s (Parameter: %s, Flags: 0x%X)' % (blocks[i][0], blocks[i][1], blocks[i][2])) - - block_hdr = get_struct(buffer, block_start, PFAT_Block_Header, ['%d/%d' % (blocks[i][3], blocks[i][4])]) - block_hdr.pfat_print() - - block_script_size = block_hdr.ScriptSize - block_script_data = buffer[block_start + block_hdr_size:block_start + block_hdr_size + block_script_size] - block_data_start = block_start + block_hdr_size + block_script_size - block_data_end = block_data_start + block_hdr.DataSize - block_data = buffer[block_data_start:block_data_end] - - block_rsa = get_struct(buffer, block_data_end, PFAT_Block_RSA, ['%d/%d' % (blocks[i][3], blocks[i][4])]) - block_rsa.pfat_print() - - print('\n PFAT Block %d/%d Script:\n' % (blocks[i][3], blocks[i][4])) - is_opcode_div = len(block_script_data) % 8 == 0 - is_begin_end = block_script_data[:8] + block_script_data[-8:] == b'\x01' + b'\x00' * 7 + b'\xFF' + b'\x00' * 7 - if is_opcode_div and is_begin_end and is_bgst : - block_script_decomp = BigScript(code_bytes=block_script_data) - block_script_lines = block_script_decomp.to_string().replace('\t',' ').split('\n') - for line in block_script_lines : - spacing = ' ' * 16 if line.endswith(('begin','end',':')) else ' ' * 24 - operands = [op for op in line.split(' ') if op != ''] - print(spacing + ('{:<12s}' + '{:<11s}' * (len(operands) - 1)).format(*operands)) - elif not is_opcode_div : - print(' Error: Script not divisible by OpCode length!') - elif not is_begin_end : - print(' Error: Script lacks Begin and/or End OpCodes!') - elif not is_bgst : - print(' Error: BIOS Guard Script Tool dependency missing!') - - file_data += block_data - final_data += block_data - - if i and is_file_start and file_data : - file_index += 1 - with open('%s_%0.2d -- %s' % (file_path, file_index, block_name), 'wb') as o : o.write(file_data) - file_data = b'' - - block_name = blocks[i][0] - block_start = block_data_end + block_rsa_size - - with open('%s_%0.2d -- %s' % (file_path, file_index + 1, block_name), 'wb') as o : o.write(file_data) # Last File - - eof_data = buffer[block_start:] # Store any data after the end of PFAT - - with open('%s_00 -- AMI_PFAT_%d_DATA_ALL.bin' % (file_path, pfat_index), 'wb') as final : final.write(final_data + eof_data) - - if eof_data[:-0x100] != b'\xFF' * (len(eof_data) - 0x100) : - eof_path = '%s_%0.2d -- AMI_PFAT_%d_DATA_END.bin' % (file_path, file_index + 2, pfat_index) - with open(eof_path, 'wb') as final : final.write(eof_data) - - if pfat_pat.search(eof_data) : - pfat_index += 1 - ami_pfat_index = ami_pfat.index(input_file) + 1 - ami_pfat.insert(ami_pfat_index, eof_path) - else : - pfat_index = 1 - -input('\nDone!') \ No newline at end of file diff --git a/AMI UCP BIOS Extractor/AMI_UCP_Extract.py b/AMI UCP BIOS Extractor/AMI_UCP_Extract.py deleted file mode 100644 index 7f845b0..0000000 --- a/AMI UCP BIOS Extractor/AMI_UCP_Extract.py +++ /dev/null @@ -1,610 +0,0 @@ -#!/usr/bin/env python3 -#coding=utf-8 - -""" -AMI UCP Extract -AMI UCP BIOS Extractor -Copyright (C) 2021 Plato Mavropoulos -""" - -title = 'AMI UCP BIOS Extractor v1.2' - -print('\n' + title) # Print script title - -import sys - -# Detect Python version -sys_ver = sys.version_info -if sys_ver < (3,7) : - sys.stdout.write('\n\nError: Python >= 3.7 required, not %d.%d!\n' % (sys_ver[0], sys_ver[1])) - (raw_input if sys_ver[0] <= 2 else input)('\nPress enter to exit') # pylint: disable=E0602 - sys.exit(1) - -import os -import re -import shutil -import ctypes -import argparse -import traceback -import subprocess -import contextlib - -# Pause after any unexpected Python exception -# 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('\nError: %s crashed, please report the following:\n' % title) - traceback.print_exception(exc_type, exc_value, tb) - input('\nPress enter to exit') - - sys.exit(1) - -# Set pause-able Python exception handler -sys.excepthook = show_exception_and_exit - -# Set console/shell window title -user_os = sys.platform -if user_os == 'win32' : ctypes.windll.kernel32.SetConsoleTitleW(title) -elif user_os.startswith('linux') or user_os == 'darwin' or user_os.find('bsd') != -1 : sys.stdout.write('\x1b]2;' + title + '\x07') - -# Set argparse Arguments -ucp_parser = argparse.ArgumentParser() -ucp_parser.add_argument('executables', type=argparse.FileType('r'), nargs='*') -ucp_parser.add_argument('-p', '--path', help='parse files within given folder', type=str) -ucp_parser.add_argument('-c', '--checksum', help='verify AMI UCP Checksums (slow)', action='store_true') -ucp_params = ucp_parser.parse_args() - -verify_chk16 = bool(ucp_params.checksum) # Get Checksum16 Verification optional argument - -# Get all files within path -def get_files(path) : - inputs = [] - - for root, _, files in os.walk(path): - for name in files : - inputs.append(os.path.join(root, name)) - - return inputs - -if len(sys.argv) >= 2 : - if bool(ucp_params.path) : - ucp_exec = get_files(ucp_params.path) # CLI with --path - else : - ucp_exec = [] - for executable in ucp_params.executables : - ucp_exec.append(executable.name) # Drag & Drop -else : - in_path = input('\nEnter the full folder path: ') - ucp_exec = get_files(in_path) # Direct Run - -# 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 - -class UAF_HDR(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('ModuleTag', char*4), # 0x00 - ('ModuleSize', uint32_t), # 0x04 - ('Checksum', uint16_t), # 0x08 - ('Unknown0', uint8_t), # 0x0A - ('Unknown1', uint8_t), # 0x0A - ('Reserved', uint32_t), # 0x0C - # 0x10 - ] - - def __init__(self, padd, *args, **kwargs) : - super().__init__(*args, **kwargs) - self.p = padd - - def ucp_print(self, chk16) : - print('\n%s Utility Auxiliary File:\n' % self.p) - print('%s Module Tag : %s' % (self.p, self.ModuleTag.decode('utf-8'))) - print('%s Module Size : 0x%X' % (self.p, self.ModuleSize)) - print('%s Checksum : 0x%0.4X (%s)' % (self.p, self.Checksum, chk16)) - print('%s Unknown 0 : 0x%0.2X' % (self.p, self.Unknown0)) - print('%s Unknown 1 : 0x%0.2X' % (self.p, self.Unknown1)) - print('%s Reserved : 0x%0.8X' % (self.p, self.Reserved)) - -class UAF_MOD(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('CompressSize', uint32_t), # 0x00 - ('OriginalSize', uint32_t), # 0x04 - # 0x08 - ] - - def __init__(self, padd, *args, **kwargs) : - super().__init__(*args, **kwargs) - self.p = padd - - def ucp_print(self) : - print('%s Compress Size : 0x%X' % (self.p, self.CompressSize)) - print('%s Original Size : 0x%X' % (self.p, self.OriginalSize)) - -class UII_HDR(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('UIISize', uint16_t), # 0x00 - ('Checksum', uint16_t), # 0x02 - ('UtilityVersion', uint32_t), # 0x04 i.e. AFU (Unknown Encoding, Signed) - ('InfoSize', uint16_t), # 0x08 - ('SupportBIOS', uint8_t), # 0x0A - ('SupportOS', uint8_t), # 0x0B - ('DataBusWidth', uint8_t), # 0x0C - ('ProgramType', uint8_t), # 0x0D - ('ProgramMode', uint8_t), # 0x0E - ('SourceSafeRelease', uint8_t), # 0x0F - # 0x10 - ] - - def __init__(self, padd, *args, **kwargs) : - super().__init__(*args, **kwargs) - self.p = padd - - def ucp_print(self, chk16) : - sbios = {1: 'ALL', 2: 'AMIBIOS8', 3: 'UEFI', 4: 'AMIBIOS8/UEFI'} - sos = {1: 'DOS', 2: 'EFI', 3: 'Windows', 4: 'Linux', 5: 'FreeBSD', 6: 'MacOS', 128: 'Multi-Platform'} - dbwidth = {1: '16b', 2: '16/32b', 3: '32b', 4: '64b'} - ptype = {1: 'Executable', 2: 'Library', 3: 'Driver'} - pmode = {1: 'API', 2: 'Console', 3: 'GUI', 4: 'Console/GUI'} - - SupportBIOS = sbios[self.SupportBIOS] if self.SupportBIOS in sbios else 'Unknown (%d)' % self.SupportBIOS - SupportOS = sos[self.SupportOS] if self.SupportOS in sos else 'Unknown (%d)' % self.SupportOS - DataBusWidth = dbwidth[self.DataBusWidth] if self.DataBusWidth in dbwidth else 'Unknown (%d)' % self.DataBusWidth - ProgramType = ptype[self.ProgramType] if self.ProgramType in ptype else 'Unknown (%d)' % self.ProgramType - ProgramMode = pmode[self.ProgramMode] if self.ProgramMode in pmode else 'Unknown (%d)' % self.ProgramMode - - print('\n%s Utility Identification Information:\n' % self.p) - print('%s UII Size : 0x%X' % (self.p, self.UIISize)) - print('%s Checksum : 0x%0.4X (%s)' % (self.p, self.Checksum, chk16)) - print('%s Tool Version : 0x%0.8X (Unknown)' % (self.p, self.UtilityVersion)) - print('%s Info Size : 0x%X' % (self.p, self.InfoSize)) - print('%s Supported BIOS : %s' % (self.p, SupportBIOS)) - print('%s Supported OS : %s' % (self.p, SupportOS)) - print('%s Data Bus Width : %s' % (self.p, DataBusWidth)) - print('%s Program Type : %s' % (self.p, ProgramType)) - print('%s Program Mode : %s' % (self.p, ProgramMode)) - print('%s SourceSafe Tag : %0.2d' % (self.p, self.SourceSafeRelease)) - -class DIS_HDR(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('PasswordSize', uint16_t), # 0x00 - ('EntryCount', uint16_t), # 0x02 - ('Password', char*12), # 0x04 - # 0x10 - ] - - def __init__(self, padd, *args, **kwargs) : - super().__init__(*args, **kwargs) - self.p = padd - - def ucp_print(self) : - print('\n%s Default Command Status Header:\n' % self.p) - print('%s Password Size : 0x%X' % (self.p, self.PasswordSize)) - print('%s Entry Count : %d' % (self.p, self.EntryCount)) - print('%s Password : %s' % (self.p, self.Password.decode('utf-8'))) - -class DIS_MOD(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('EnabledDisabled', uint8_t), # 0x00 - ('ShownHidden', uint8_t), # 0x01 - ('Command', char*32), # 0x02 - ('Description', char*256), # 0x22 - # 0x122 - ] - - def __init__(self, padd, *args, **kwargs) : - super().__init__(*args, **kwargs) - self.p = padd - - def ucp_print(self) : - enabled = {0: 'Disabled', 1: 'Enabled'} - shown = {0: 'Hidden', 1: 'Shown', 2: 'Shown Only'} - - EnabledDisabled = enabled[self.EnabledDisabled] if self.EnabledDisabled in enabled else 'Unknown (%d)' % self.EnabledDisabled - ShownHidden = shown[self.ShownHidden] if self.ShownHidden in shown else 'Unknown (%d)' % self.ShownHidden - - print('\n%s Default Command Status Entry:\n' % self.p) - print('%s State : %s' % (self.p, EnabledDisabled)) - print('%s Display : %s' % (self.p, ShownHidden)) - print('%s Command : %s' % (self.p, self.Command.decode('utf-8').strip())) - print('%s Description : %s' % (self.p, self.Description.decode('utf-8').strip())) - -# Process ctypes Structure Classes -# https://github.com/skochinsky/me-tools/blob/master/me_unpack.py by Igor Skochinsky -def get_struct(buffer, 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 = buffer[start_offset:start_offset + struct_len] - fit_len = min(len(struct_data), struct_len) - - if (start_offset >= len(buffer)) or (fit_len < struct_len) : - print('\n Error: Offset 0x%X out of bounds at %s, possibly incomplete image!' % (start_offset, class_name.__name__)) - - input('\n Press enter to exit') - - sys.exit(1) - - ctypes.memmove(ctypes.addressof(structure), struct_data, fit_len) - - return structure - -# Get Checksum16 validity result -def checksum16(buffer, check) : - if not check : return 'Skipped' - - chk16 = 0 - - for idx in range(0, len(buffer), 2) : - chk16 += int.from_bytes(buffer[idx:idx + 2], 'little') - - chk16 &= 0xFFFF - - return 'Good' if chk16 == 0 else 'Bad' - -# Get all input file AMI UCP patterns -def get_matches(buffer) : - uaf_len_max = 0 # Length of largest detected @UAF - uaf_hdr_off = 0 # Offset of largest detected @UAF - - for uaf in ami_ucp_pat.finditer(buffer) : - uaf_len_cur = int.from_bytes(buffer[uaf.start() + 0x4:uaf.start() + 0x8], 'little') - - if uaf_len_cur > uaf_len_max : - uaf_len_max = uaf_len_cur - uaf_hdr_off = uaf.start() - - return uaf_hdr_off, uaf_len_max - -# Parse & Extract AMI UCP structures -def ucp_extract(buffer, out_dir, level, padd) : - nal_dict = {} # Initialize @NAL Dictionary per UCP - - uaf_hdr = get_struct(buffer, 0, UAF_HDR, [padd]) # Parse @UAF Header Structure - - uaf_chk = checksum16(buffer, verify_chk16) # Get @UAF Header Checksum16 - - # Print @UAF Header Info - uaf_hdr.ucp_print(uaf_chk) - print('%s Compress Size : 0x%X' % (padd, len(buffer))) - print('%s Original Size : 0x%X' % (padd, len(buffer))) - print('%s Module Name : %s' % (padd, tag_dict['UAF'])) - - if uaf_chk == 'Bad' : - input('\n%s Error: Invalid AMI UCP Module UAF Checksum!' % padd) - - uaf_off = uaf_hdr_len # Parsed @UAF, next Modules - uaf_all = [] # Initialize list of all UAF Modules - is_pfat = False # Initialize PFAT BIOS detection - is_dual = False # Initialize AMI/Insyde detection - - while buffer[uaf_off] == 0x40 : # ASCII of @ is 0x40 - uaf_hdr = get_struct(buffer, uaf_off, UAF_HDR, [padd]) # Parse UAF Module Structure - - uaf_tag = uaf_hdr.ModuleTag.decode('utf-8')[1:] # Get unique UAF Module Tag - - if uaf_tag == 'PFC' : is_pfat = True # Detect if UAF Module has PFAT BIOS - - if uaf_tag == 'AMI' : is_dual = True # Detect if UAF Module has dual AMI/Insyde BIOS - - uaf_all.append([uaf_tag, uaf_off, uaf_hdr]) # Store UAF Module Info - - uaf_off += uaf_hdr.ModuleSize # Adjust to next UAF Module offset - - if uaf_off >= len(buffer) : break # Stop parsing at EOF - - # Check if UAF Module NAL exists and place it first - # Parsing NAL first allows naming all UAF Modules - for i in range(len(uaf_all)) : - if uaf_all[i][0] == 'NAL' : - uaf_all.insert(1, uaf_all.pop(i)) # After UII for visual purposes - break # NAL found, skip the rest - - # Parse all UAF Modules - for uaf in uaf_all : - uaf_tag = uaf[0] # Store UAF Module Tag - uaf_off = uaf[1] # Store UAF Module Offset - uaf_hdr = uaf[2] # Store UAF Module Struct - - uaf_data_all = buffer[uaf_off:uaf_off + uaf_hdr.ModuleSize] # UAF Module Entire Data - - uaf_data_mod = uaf_data_all[uaf_hdr_len:] # UAF Module EFI Data - - uaf_data_raw = uaf_data_mod[uaf_mod_len:] # UAF Module Raw Data - - uaf_chk = checksum16(uaf_data_all, verify_chk16) # Get UAF Module Checksum16 - - uaf_hdr.ucp_print(uaf_chk) # Print UAF Module Info - - uaf_mod = get_struct(buffer, uaf_off + uaf_hdr_len, UAF_MOD, [padd]) # Parse UAF Module EFI Structure - - uaf_mod.ucp_print() # Print UAF Module EFI Info - - is_comp = uaf_mod.CompressSize != uaf_mod.OriginalSize # Detect UAF Module EFI Compression - - rom_name = 'PFAT' if is_pfat else 'BIOS' # Set UAF Module BIOS/ROM name based on PFAT state - - if uaf_tag in nal_dict : uaf_name = nal_dict[uaf_tag] # Always prefer NAL naming first - elif uaf_tag in tag_dict : uaf_name = tag_dict[uaf_tag] # Otherwise use built-in naming - elif uaf_tag == 'ROM' : uaf_name = '%s.bin' % rom_name # BIOS/PFAT Firmware - elif uaf_tag.startswith('R0') : uaf_name = '%s_0%s.bin' % (rom_name, uaf_tag[2:]) # BIOS/PFAT Firmware - elif uaf_tag.startswith('S0') : uaf_name = '%s_0%s.sig' % (rom_name, uaf_tag[2:]) # BIOS/PFAT Signature - elif uaf_tag.startswith('DR') : uaf_name = 'DROM_0%s.bin' % uaf_tag[2:] # Thunderbolt Retimer Firmware - elif uaf_tag.startswith('DS') : uaf_name = 'DROM_0%s.sig' % uaf_tag[2:] # Thunderbolt Retimer Signature - elif uaf_tag.startswith('EC') : uaf_name = 'EC_0%s.bin' % uaf_tag[2:] # Embedded Controller Firmware - elif uaf_tag.startswith('ME') : uaf_name = 'ME_0%s.bin' % uaf_tag[2:] # Management Engine Firmware - else : uaf_name = uaf_tag # Could not name the UAF Module, use Tag instead - - if uaf_name != uaf_tag : - uaf_fext = '' # File extension included in name - print('%s Module Name : %s' % (padd, uaf_name)) - elif uaf_tag in ['CMD','PFC','VER','MEC','NAL','CKV'] : - uaf_fext = '.txt' # Known Text files - print('%s Module Name : %s%s (Unknown)' % (padd, uaf_name, uaf_fext)) - else : - uaf_fext = '.bin' # Unknown files, assume binary - print('%s Module Name : %s%s (Unknown)' % (padd, uaf_name, uaf_fext)) - - # Check if unknown UAF Module Tag is present in NAL but not in built-in dictionary - if uaf_tag in nal_dict and uaf_tag not in tag_dict and not uaf_tag.startswith(('ROM','R0','S0','DR','DS')) : - input('\n%s Note: Detected new AMI UCP Module %s (%s) in NAL!' % (padd, uaf_tag, nal_dict[uaf_tag])) - - # Generate UAF Module File name, depending on whether decompression will be required - uaf_fname = os.path.join(out_dir, '%s%s' % (uaf_name, '.temp' if is_comp else uaf_fext)) - - if uaf_chk == 'Bad' : - input('\n%s Error: Invalid AMI UCP Module %s Checksum!' % (padd, uaf_tag)) - - # Parse Utility Identification Information UAF Module (UII) - if uaf_tag == 'UII' : - info_hdr = get_struct(uaf_data_raw, 0, UII_HDR, [padd]) # Parse UII Module Raw Structure - - info_chk = checksum16(uaf_data_raw, verify_chk16) # Get UII Module Checksum16 - - info_hdr.ucp_print(info_chk) # Print UII Module Info - - # Get UII Module Description text field - desc = uaf_data_raw[info_hdr.InfoSize:info_hdr.UIISize].strip(b'\x00').decode('utf-8') - - print('%s Description : %s' % (padd, desc)) # Print UII Module Description - - if info_chk == 'Bad' : - input('\n%s Error: Invalid AMI UCP Module %s > Info Checksum!' % (padd, uaf_tag)) - - # Store/Save UII Module Info in file - with open(uaf_fname[:-3] + 'txt', 'a') as uii : - with contextlib.redirect_stdout(uii) : - info_hdr.ucp_print(info_chk) # Store UII Module Info - - print('%s Description : %s' % (padd, desc)) # Store UII Module Description - - # Adjust UAF Module Raw Data for extraction - if is_comp : - # Some Compressed UAF Module EFI data lack necessary padding in the end - if uaf_mod.CompressSize > len(uaf_data_raw) : - comp_padd = b'\x00' * (uaf_mod.CompressSize - len(uaf_data_raw)) - uaf_data_raw = uaf_data_mod[:uaf_mod_len] + uaf_data_raw + comp_padd # Add missing padding for decompression - else : - uaf_data_raw = uaf_data_mod[:uaf_mod_len] + uaf_data_raw # Add the EFI/Tiano Compression info before Raw Data - else : - uaf_data_raw = uaf_data_raw[:uaf_mod.OriginalSize] # No compression, extend to end of Original UAF Module size - - # Store/Save UAF Module file - if uaf_tag != 'UII' : # Skip UII binary, already parsed - with open(uaf_fname, 'wb') as out : out.write(uaf_data_raw) - - # UAF Module EFI/Tiano Decompression - if is_comp : - try : - dec_fname = uaf_fname[:-5] + uaf_fext # Decompressed UAF Module file path - subprocess.run(['TianoCompress', '-d', uaf_fname, '-o', dec_fname, '--uefi', '-q'], check = True, stdout = subprocess.DEVNULL) - - with open(dec_fname, 'rb') as dec : uaf_data_raw = dec.read() # Read back the UAF Module decompressed Raw data - - if len(uaf_data_raw) == 0 : raise Exception('DECOMP_OUT_EMPTY') # If decompressed file is empty, something went wrong - - os.remove(uaf_fname) # Successful decompression, delete compressed UAF Module file - - uaf_fname = dec_fname # Adjust UAF Module file path to the decompressed one - except : - print('\n%s Error: Could not extract AMI UCP Module %s via TianoCompress!' % (padd, uaf_tag)) - input('%s Make sure that "TianoCompress" executable exists!' % padd) - - # Process and Print known text only UAF Modules (after EFI/Tiano Decompression) - if uaf_tag in ['CMD','PFC','VER','MEC','CKV'] : # Always referenced in tag_desc - text_data = uaf_data_raw.decode('utf-8') - print('\n%s %s:\n\n%s %s' % (padd, tag_desc[uaf_tag], padd, text_data)) - - # Parse Default Command Status UAF Module (DIS) - if len(uaf_data_raw) and uaf_tag == 'DIS' : - dis_hdr = get_struct(uaf_data_raw, 0, DIS_HDR, [padd]) # Parse DIS Module Raw Header Structure - dis_hdr.ucp_print() # Print DIS Module Raw Header Info - - # Store/Save DIS Module Header Info in file - with open(uaf_fname[:-3] + 'txt', 'a') as dis : - with contextlib.redirect_stdout(dis) : - dis_hdr.ucp_print() # Store DIS Module Header Info - - dis_data = uaf_data_raw[uaf_hdr_len:] # DIS Module Entries Data - - # Parse all DIS Module Entries - for e_idx in range(dis_hdr.EntryCount) : - dis_mod = get_struct(dis_data, e_idx * 0x122, DIS_MOD, [padd]) # Parse DIS Module Raw Entry Structure - dis_mod.ucp_print() # Print DIS Module Raw Entry Info - - # Store/Save DIS Module Entry Info in file - with open(uaf_fname[:-3] + 'txt', 'a') as dis : - with contextlib.redirect_stdout(dis) : - dis_mod.ucp_print() # Store DIS Module Entry Info - - os.remove(uaf_fname) # Delete DIS Module binary, info exported as text - - # Parse Non-AMI List (?) UAF Module (NAL) - if len(uaf_data_raw) >= 5 and (uaf_tag,uaf_data_raw[0],uaf_data_raw[4]) == ('NAL',0x40,0x3A) : - nal_info = uaf_data_raw.decode('utf-8').strip().replace('\r','').split('\n') - - print('\n%s UAF List:\n' % padd) - - # Parse all NAL Module Entries - for info in nal_info : - print('%s %s : %s' % (padd, info[1:4], info[5:])) # Print NAL Module Tag-Path Info - nal_dict[info[1:4]] = os.path.basename(info[5:]) # Assign a file name (w/o path) to each Tag - - # Parse Insyde BIOS UAF Module (INS) - if len(uaf_data_raw) >= 2 and (uaf_tag,is_dual,uaf_data_raw[:2]) == ('INS',True,b'\x4D\x5A') : - ins_dir = os.path.join(out_dir, '%s_extracted (SFX)' % uaf_tag) # Generate extraction directory - - print('\n%s Insyde BIOS 7-Zip SFX Archive:\n\n%s 7-Zip will be used for extraction' % (padd, padd)) - - # INS Module extraction - try : - subprocess.run(['7z', 'x', '-aou', '-bso0', '-bse0', '-bsp0', '-o' + ins_dir, uaf_fname], check = True, stdout = subprocess.DEVNULL) - - if not os.path.isdir(ins_dir) : raise Exception('EXTR_DIR_MISSING') # If extraction folder is missing, something went wrong - - os.remove(uaf_fname) # Successful extraction, delete archived INS Module file - except : - print('\n%s Error: Could not extract AMI UCP Module %s via 7-Zip!' % (padd, uaf_tag)) - input('%s Make sure that "7z" executable exists!' % padd) - - # Detect AMI BIOS Guard (PFAT) image and print extraction instructions/utility - if len(uaf_data_raw) >= 16 and (is_pfat,uaf_data_raw[0x8:0x10]) == (True,b'_AMIPFAT') : - print('\n%s AMI BIOS Guard (PFAT) Image:\n' % padd) - print('%s Use "AMI BIOS Guard Extractor" from https://github.com/platomav/BIOSUtilities' % padd) - - # Detect Intel Management Engine (ME) image and print parsing instructions/utility - if len(uaf_data_raw) and uaf_name.startswith('ME_0') : - print('\n%s Intel Management Engine (ME) Firmware:\n' % padd) - print('%s Use "ME Analyzer" from https://github.com/platomav/MEAnalyzer' % padd) - - # Get best Nested AMI UCP Pattern match based on @UAF Size - uaf_hdr_off,uaf_len_max = get_matches(uaf_data_raw) - - # Parse Nested AMI UCP Structure - if uaf_hdr_off : - level += 1 # Increase structure Level to control output padding - uaf_dir = os.path.join(out_dir, '%s_extracted (UCP)' % uaf_tag) # Generate extraction directory - os.mkdir(uaf_dir) # Create extraction directory - ucp_extract(uaf_data_raw[uaf_hdr_off:uaf_hdr_off + uaf_len_max], uaf_dir, level, ' ' * level) # Call recursively - os.remove(uaf_fname) # Delete raw nested AMI UCP Structure after successful recursion/extraction - -# Utility Auxiliary File (@UAF) and Utility Identification Information (@UII) -ami_ucp_pat = re.compile(br'\x40\x55\x41\x46.{12}\x40\x55\x49\x49', re.DOTALL) - -# Get common ctypes Structure Sizes -uaf_hdr_len = ctypes.sizeof(UAF_HDR) -uaf_mod_len = ctypes.sizeof(UAF_MOD) - -# User friendly Tag Descriptions -tag_desc = { - 'CMD' : 'AMI AFU Command', - 'PFC' : 'AMI BGT Command', - 'VER' : 'OEM Version', - 'CKV' : 'Check Version', - 'MEC' : 'ME FWUpdLcl Command', - } - -# AMI UCP Tag-File Dictionary -tag_dict = { - 'W32' : 'amifldrv32.sys', - 'W64' : 'amifldrv64.sys', - 'VXD' : 'amifldrv.vxd', - 'DCT' : 'DevCon32.exe', - 'DCX' : 'DevCon64.exe', - 'CMD' : 'AFU_Command.txt', - 'PFC' : 'BGT_Command.txt', - 'VER' : 'OEM_Version.txt', - 'CKV' : 'Check_Version.txt', - 'OKM' : 'OK_Message.txt', - 'CPM' : 'AC_Message.txt', - 'DIS' : 'Command_Status.bin', - 'UAF' : 'UCP_Main.bin', - 'UII' : 'UCP_Info.txt', - 'NAL' : 'UAF_List.txt', - 'MEC' : 'FWUpdLcl.txt', - 'MED' : 'FWUpdLcl_DOS.exe', - 'MET' : 'FWUpdLcl_WIN.exe', - 'AMI' : 'UCP_Nested.bin', - 'INS' : 'Insyde_Nested.bin', - 'RFI' : 'CryptRSA.efi', - 'R3I' : 'CryptRSA32.efi', - 'UFI' : 'HpBiosUpdate.efi', - 'US9' : 'HpBiosUpdate.s09', - 'US2' : 'HpBiosUpdate.s12', - 'USG' : 'HpBiosUpdate.sig', - '3FI' : 'HpBiosUpdate32.efi', - '3S9' : 'HpBiosUpdate32.s09', - '3S2' : 'HpBiosUpdate32.s12', - '3SG' : 'HpBiosUpdate32.sig', - 'MFI' : 'HpBiosMgmt.efi', - 'MS9' : 'HpBiosMgmt.s09', - 'MS2' : 'HpBiosMgmt.s12', - 'US4' : 'HpBiosUpdate.s14', - '3S4' : 'HpBiosUpdate32.s14', - 'MS4' : 'HpBiosMgmt.s14', - 'M3I' : 'HpBiosMgmt32.efi', - 'M39' : 'HpBiosMgmt32.s09', - 'M32' : 'HpBiosMgmt32.s12', - 'M34' : 'HpBiosMgmt32.s14', - 'BME' : 'BiosMgmt.efi', - 'BM9' : 'BiosMgmt.s09', - 'B12' : 'BiosMgmt.s12', - 'B14' : 'BiosMgmt.s14', - 'B3E' : 'BiosMgmt32.efi', - 'B39' : 'BiosMgmt32.s09', - 'B32' : 'BiosMgmt32.s12', - 'B34' : 'BiosMgmt32.s14', - 'DFE' : 'HpDevFwUpdate.efi', - 'DFS' : 'HpDevFwUpdate.s12', - 'ENB' : 'ENBG64.exe', - } - -# Process each input AMI UCP BIOS executable -for input_file in ucp_exec : - input_name,input_extension = os.path.splitext(os.path.basename(input_file)) - input_dir = os.path.dirname(os.path.abspath(input_file)) - - print('\n*** %s%s' % (input_name, input_extension)) - - # Check if input file exists - if not os.path.isfile(input_file) : - print('\n Error: This input file does not exist!') - continue # Next input file - - with open(input_file, 'rb') as in_file : input_data = in_file.read() - - # Get best AMI UCP Pattern match based on @UAF Size - uaf_hdr_off,uaf_len_max = get_matches(input_data) - - # Check if AMI UCP Pattern was found on executable - if not uaf_hdr_off : - print('\n Error: This is not an AMI UCP BIOS executable!') - continue # Next input file - - output_path = os.path.join(input_dir, '%s%s' % (input_name, input_extension) + '_extracted') # Set extraction directory - - if os.path.isdir(output_path) : shutil.rmtree(output_path) # Delete any existing extraction directory - - os.mkdir(output_path) # Create extraction directory - - print('\n AMI Utility Configuration Program') - - level = 0 # Set initial AMI UCP structure Level to control padding in nested ones - - ucp_extract(input_data[uaf_hdr_off:uaf_hdr_off + uaf_len_max], output_path, level, '') # Call the AMI UCP Extractor function - - print('\n Extracted AMI UCP BIOS executable!') - -input('\nDone!') - -sys.exit(0) \ No newline at end of file diff --git a/Apple EFI File Renamer/Apple_EFI_Rename.py b/Apple EFI File Renamer/Apple_EFI_Rename.py deleted file mode 100644 index ac69452..0000000 --- a/Apple EFI File Renamer/Apple_EFI_Rename.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env python3 - -""" -Apple EFI Rename -Apple EFI File Renamer -Copyright (C) 2018-2019 Plato Mavropoulos -https://github.com/tianocore/edk2/blob/master/Vlv2TbltDevicePkg/Include/Library/BiosIdLib.h -""" - -print('Apple EFI File Renamer v1.3\n') - -import os -import re -import sys -import zlib -import shutil -import subprocess - -pattern = re.compile(br'\x24\x49\x42\x49\x4F\x53\x49\x24') # Intel $IBIOSI$ - -if len(sys.argv) >= 3 and sys.argv[1] == '-skip' : - # Called via Apple_EFI_Package - apple_efi = sys.argv[2:] - skip_pause = True -elif len(sys.argv) >= 2 : - # Drag & Drop or CLI - apple_efi = sys.argv[1:] - skip_pause = False -else : - # Folder path - apple_efi = [] - skip_pause = False - in_path = input('\nEnter the full folder path: ') - print('\nWorking...\n') - for root, dirs, files in os.walk(in_path): - for name in files : - apple_efi.append(os.path.join(root, name)) - -for input_file in apple_efi : - file_path = os.path.abspath(input_file) - file_name = os.path.basename(input_file) - file_dir = os.path.dirname(file_path) - file_ext = os.path.splitext(file_path)[1] - error = False - - with open(input_file, 'rb') as in_file : buffer = in_file.read() - - is_ibiosi = pattern.search(buffer) # Detect $IBIOSI$ pattern - - if not is_ibiosi : - - # On some Apple EFI, the $IBIOSI$ pattern is within compressed modules so we need to use UEFIFind and UEFIExtract - - try : - uefifind = subprocess.check_output(['UEFIFind', file_path, 'body', 'list', '244942494F534924'], universal_newlines=True) - uefiextr = subprocess.run(['UEFIExtract', file_path, uefifind[0:36], '-o', '_$IBIOSI$_', '-m', 'body'], stdout=subprocess.DEVNULL) - - with open(os.path.join('_$IBIOSI$_', 'body.bin'), 'rb') as in_file : buffer = in_file.read() - - is_ibiosi = pattern.search(buffer) # Detect decompressed $IBIOSI$ pattern - - shutil.rmtree('_$IBIOSI$_') # Remove temporary folder - - except : - error = True - - if not error : - - bios_info = buffer[is_ibiosi.end():is_ibiosi.end() + 0x42].decode('utf-16') - - BoardID = bios_info[:7].strip() - BoardRev = bios_info[7] - OEMID = bios_info[9:12] # 88Z - MajorVer = bios_info[13:17] - BuildType = bios_info[18] # B - MinorVer = bios_info[19:21] - Year = bios_info[22:24] - Month = bios_info[24:26] - Day = bios_info[26:28] - Hour = bios_info[28:30] - Minute = bios_info[30:32] - - file_chk = zlib.adler32(buffer) # Checksum for EFI with same $IBIOSI$ but different PRD/PRE status - - new_name = '%s%s_%s_%s%s_20%s-%s-%s_%s-%s_%0.8X%s' % (BoardID, BoardRev, MajorVer, BuildType, MinorVer, Year, Month, Day, Hour, Minute, file_chk, file_ext) - - file_path_new = os.path.join(file_dir, new_name) - - if not os.path.isfile(file_path_new) : os.replace(file_path, file_path_new) # Rename input EFI with proper name - - print(new_name) - print('\nBoard Identity: %s%s' % (BoardID, BoardRev)) - print('Apple Identity: %s' % OEMID) - print('Major Version: %s' % MajorVer) - print('Minor Version: %s' % MinorVer) - print('Build Type: %s' % BuildType) - print('Build Date: 20%s-%s-%s' % (Year, Month, Day)) - print('Build Time: %s:%s\n' % (Hour, Minute)) - - else : - print('\nError: Could not find $IBIOSI$ pattern at %s!' % file_name) - print(' Make sure that "UEFIFind" and "UEFIExtract" executables exist!\n') - -if not skip_pause : input('Done!') \ No newline at end of file diff --git a/Apple EFI IM4P Splitter/Apple_EFI_Split.py b/Apple EFI IM4P Splitter/Apple_EFI_Split.py deleted file mode 100644 index cb6662a..0000000 --- a/Apple EFI IM4P Splitter/Apple_EFI_Split.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python3 - -""" -Apple EFI Split -Apple EFI IM4P Splitter -Copyright (C) 2018-2020 Plato Mavropoulos -""" - -title = 'Apple EFI IM4P Splitter v2.1' - -import os -import re -import sys - -im4p = re.compile(br'\x16\x04\x49\x4D\x34\x50\x16\x04') # Apple IM4P -ifd = re.compile(br'\x5A\xA5\xF0\x0F.{172}\xFF{16}', re.DOTALL) # Intel Flash Descriptor (Z¥π. + [0xAC] + 0xFF * 16) - -# Flash Descriptor 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 - } - -# Get input catalog file paths -if len(sys.argv) >= 3 and sys.argv[1] == '-skip' : - # Called via Apple_EFI_Package - apple_im4p = sys.argv[2:] - skip_pause = True - skip_space = ' ' - print('\n%s%s' % (skip_space, title)) # Print Title -elif len(sys.argv) >= 2 : - # Drag & Drop or CLI - apple_im4p = sys.argv[1:] - skip_pause = False - skip_space = '' - print('\n%s%s' % (skip_space, title)) # Print Title -else : - # Folder path - apple_im4p = [] - skip_pause = False - skip_space = '' - print('\n%s%s' % (skip_space, title)) # Print Title - in_path = input('\nEnter the full folder path: ') - print('\nWorking...') - for root, dirs, files in os.walk(in_path): - for name in files : - apple_im4p.append(os.path.join(root, name)) - -for input_file in apple_im4p : - file_path = os.path.abspath(input_file) - file_name = os.path.basename(input_file) - file_dir = os.path.dirname(file_path) - file_ext = os.path.splitext(file_path)[1] - - print('\n%sFile: %s' % (skip_space, file_name)) # Print File Name - - # Must be IM4P file because its size is 0x0 dependent - if file_ext not in ('.im4p','.IM4P') : - print('\n%s Error: Could not find IM4P file extension at %s!' % (skip_space, file_name)) - continue # Critical error - - with open(input_file, 'rb') as in_file : buffer = in_file.read() - - is_im4p = im4p.search(buffer) # Detect IM4P pattern - - if not is_im4p : - print('\n%s Error: Could not find IM4P pattern at %s!' % (skip_space, file_name)) - continue # Critical error - - im4p_size = int.from_bytes(buffer[2:is_im4p.start()], 'big') # Variable, from 0x2 - IM4P - im4p_type = buffer[is_im4p.end():is_im4p.end() + 0x4].decode('utf-8') # mefi - - if im4p_type != 'mefi' : - print('\n%s Error: Could not find "mefi" IM4P Type at %s!' % (skip_space, file_name)) - continue # Critical error - - # After IM4P mefi (0x15), multi EFI payloads have _MEFIBIN (0x100) which is difficult to reverse without varying samples. - # However, _MEFIBIN is not required for splitting SPI/EFI images because Intel Flash Descriptor Component Density exists. - mefi_data_start = is_im4p.start() + buffer[is_im4p.start() - 0x1] # IM4P mefi payload start offset - mefi_data_size = int.from_bytes(buffer[is_im4p.end() + 0x9:is_im4p.end() + 0xD], 'big') # IM4P mefi payload size - mefibin_exist = buffer[mefi_data_start:mefi_data_start + 0x8] == b'_MEFIBIN' # Check if mefi is followed by _MEFIBIN - efi_data_start = mefi_data_start + 0x100 if mefibin_exist else mefi_data_start # Actual multi EFI payloads start after _MEFIBIN - efi_data_size = mefi_data_size - 0x100 if mefibin_exist else mefi_data_size # Actual multi EFI payloads size without _MEFIBIN - buffer = buffer[efi_data_start:efi_data_start + efi_data_size] # Adjust input file buffer to actual multi EFI payloads data - - fd_matches = list(ifd.finditer(buffer)) # Find Intel Flash Descriptor pattern matches - fd_count = len(fd_matches) # Count found Intel Flash Descriptor pattern matches - fd_final = [] # Initialize final Intel Flash Descriptor info storage - - # Parse Intel Flash Descriptor pattern matches - for fd_idx in range(fd_count) : - fd = fd_matches[fd_idx] # Get Intel Flash Descriptor match object - - fd_flmap0_fcba = buffer[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 : - 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 : - start_substruct = 0x10 # At PCH, Flash Descriptor starts at 0x10 - end_substruct = 0xBC # 0xBC for [0xAC] + 0xFF * 16 sanity check - - fd_match_start = fd.start() - start_substruct # Actual Flash Descriptor Start Offset - fd_match_end = fd.end() - end_substruct # Actual Flash Descriptor End Offset - - # Calculate Intel Flash Descriptor Flash Component Total Size - fd_flmap0_nc = ((int.from_bytes(buffer[fd_match_end:fd_match_end + 0x4], 'little') >> 8) & 3) + 1 # Component Count (00 = 1, 01 = 2) - fd_flmap1_isl = buffer[fd_match_end + 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 = buffer[fd_match_start + 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) - - fd_final.append((fd_match_start,fd_comp_all_size)) # Store Intel Flash Descriptor final info - - # Split IM4P via the final Intel Flash Descriptor matches - for fd_idx in range(fd_count) : - fd = fd_final[fd_idx] # Get Intel Flash Descriptor final info [FD Start, FD Component(s) Size] - - # The Intel Flash Descriptor Flash Component Total Size should be enough to split the IM4P. - # However, for sanity, its Size can be compared to the Size different of Next - Current FD. - fd_diff_size = len(buffer) - fd[0] if fd_idx == fd_count - 1 else fd_final[fd_idx + 1][0] - fd[0] # Last FD ends at mefi payload end - if fd[1] != fd_diff_size : # FD Total Component Size should be equal to Next-Current FD Difference Size - print('\n%s Error: Intel FD %d/%d Component Size 0x%X != Next-Current FD Size Difference 0x%X!' - % (skip_space, fd_idx + 1, fd_count, fd[1], fd_diff_size)) - - file_data = buffer[fd[0]:fd[0] + max(fd[1], fd_diff_size)] # Split EFI image file data (use largest FD Size just in case) - - file_path_new = os.path.join(file_dir, '%s_%d.fd' % (file_name[:-5], fd_idx + 1)) # Split EFI image file path - - with open(file_path_new, 'wb') as spi_image : spi_image.write(file_data) # Store split EFI image file - - print('\n%s Split IM4P into %d EFI image(s)!' % (skip_space, fd_count)) - -if not skip_pause : input('\nDone!') \ No newline at end of file diff --git a/Apple EFI Package Extractor/Apple_EFI_Package.py b/Apple EFI Package Extractor/Apple_EFI_Package.py deleted file mode 100644 index 902a926..0000000 --- a/Apple EFI Package Extractor/Apple_EFI_Package.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python3 - -""" -Apple EFI Package -Apple EFI Package Extractor -Copyright (C) 2019 Plato Mavropoulos -""" - -print('Apple EFI Package Extractor v1.1') - -import os -import sys -import zlib -import shutil -import subprocess - -if len(sys.argv) >= 2 : - pkg = sys.argv[1:] -else : - pkg = [] - in_path = input('\nEnter the full folder path: ') - print('\nWorking...') - for root, dirs, files in os.walk(in_path): - for name in files : - pkg.append(os.path.join(root, name)) - -anytoiso_path = 'C:\\Program Files (x86)\\AnyToISO\\anytoiso.exe' - -final_path = os.path.join(os.getcwd(), 'AppleEFI') -if os.path.exists(final_path) : shutil.rmtree(final_path) - -for input_file in pkg : - file_path = os.path.abspath(input_file) - file_name = os.path.basename(input_file) - file_dir = os.path.dirname(file_path) - file_ext = os.path.splitext(file_path)[1] - - print('\nFile: %s\n' % file_name) - - with open(input_file, 'rb') as in_buff : file_adler = zlib.adler32(in_buff.read()) & 0xFFFFFFFF - - pkg_payload = os.path.join(final_path, '%s_%0.8X' % (file_name, file_adler)) - pkg_temp = os.path.join(final_path, '__TEMP_%s_%0.8X' % (file_name, file_adler)) - os.makedirs(pkg_temp) - - subprocess.run([anytoiso_path, '/extract', file_path, pkg_temp], check = True, stdout=subprocess.DEVNULL) - - if os.path.isfile(os.path.join(pkg_temp, 'Scripts')) : - scripts_init = os.path.join(pkg_temp, 'Scripts') - scripts_cpgz = os.path.join(pkg_temp, 'Scripts.cpgz') - scripts_extr = os.path.join(pkg_temp, 'Scripts', '') - efi_path = os.path.join(scripts_extr, 'Tools', 'EFIPayloads', '') - - os.replace(scripts_init, scripts_cpgz) - - subprocess.run([anytoiso_path, '/extract', scripts_cpgz, scripts_extr], check = True, stdout=subprocess.DEVNULL) - - shutil.copytree(efi_path, pkg_payload) - - elif os.path.isfile(os.path.join(pkg_temp, 'Payload')) : - payload_init = os.path.join(pkg_temp, 'Payload') - payload_pbzx = os.path.join(pkg_temp, 'Payload.pbzx') - payload_extr = os.path.join(pkg_temp, 'Payload', '') - zip_path = os.path.join(payload_extr, 'usr', 'standalone', 'firmware', 'bridgeOSCustomer.bundle', 'Contents', 'Resources', 'UpdateBundle') - efi_path = os.path.join(zip_path, 'boot', 'Firmware', 'MacEFI', '') - - os.replace(payload_init, payload_pbzx) - - subprocess.run([anytoiso_path, '/extract', payload_pbzx, payload_extr], check = True, stdout=subprocess.DEVNULL) - - subprocess.run([anytoiso_path, '/extract', zip_path + '.zip', zip_path], check = True, stdout=subprocess.DEVNULL) - - if os.path.exists(efi_path) : shutil.copytree(efi_path, pkg_payload) - - shutil.rmtree(pkg_temp) - - im4p_files = [] - for root, dirs, files in os.walk(pkg_payload): - for name in files : - if name.endswith('.im4p') : - im4p_files.append(os.path.join(root, name)) - - if im4p_files : subprocess.run(['python', 'Apple_EFI_Split.py', '-skip', *im4p_files], check = True, stdout=subprocess.DEVNULL) - for im4p in im4p_files : os.remove(im4p) - - final_files = [] - for root, dirs, files in os.walk(pkg_payload): - for name in files : - final_files.append(os.path.join(root, name)) - - if final_files : subprocess.run(['python', 'Apple_EFI_Rename.py', '-skip', *final_files], check = True, stdout=subprocess.DEVNULL) - - for root, dirs, files in os.walk(pkg_payload): - for name in files : - if not os.path.isfile(os.path.join(final_path, name)) : - shutil.copy2(os.path.join(root, name), os.path.join(final_path, name)) - - shutil.rmtree(pkg_payload) - -else : - input('\nDone!') \ No newline at end of file diff --git a/Apple EFI Package Grabber/Apple_EFI_Grab.dat b/Apple EFI Package Grabber/Apple_EFI_Grab.dat deleted file mode 100644 index 5740129..0000000 --- a/Apple EFI Package Grabber/Apple_EFI_Grab.dat +++ /dev/null @@ -1,4 +0,0 @@ -2021-01-01 00:00:00 - - - diff --git a/Apple EFI Package Grabber/Apple_EFI_Grab.py b/Apple EFI Package Grabber/Apple_EFI_Grab.py deleted file mode 100644 index c4d544b..0000000 --- a/Apple EFI Package Grabber/Apple_EFI_Grab.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env python3 -#coding=utf-8 - -""" -Apple EFI Grab -Apple EFI Package Grabber -Copyright (C) 2018-2021 Plato Mavropoulos -""" - -title = 'Apple EFI Package Grabber v2.0' - -print('\n' + title) - -import sys - -sys_ver = sys.version_info -if sys_ver < (3,7): - sys.stdout.write('\n\nError: Python >= 3.7 required, not %d.%d!\n' % (sys_ver[0], sys_ver[1])) - (raw_input if sys_ver[0] <= 2 else input)('\nPress enter to exit') # pylint: disable=E0602 - sys.exit(1) - -import traceback - -def show_exception_and_exit(exc_type, exc_value, tb): - if exc_type is KeyboardInterrupt: - print('\nNote: Keyboard Interrupt!') - else: - print('\nError: %s crashed, please report the following:\n' % title) - traceback.print_exception(exc_type, exc_value, tb) - input('\nPress enter to exit') - - sys.exit(1) - -sys.excepthook = show_exception_and_exit - -import datetime -import urllib.request -from multiprocessing.pool import ThreadPool - -def fetch_cat_info(name): - url = cat_url[:-len('others/')] + name if name in ['index.sucatalog','index-1.sucatalog'] else cat_url + name - with urllib.request.urlopen(urllib.request.Request(url, method='HEAD')) as head : mod = head.headers['last-modified'] - - return name, url, mod - -def fetch_cat_links(cat_file): - cat_links = [] - - with urllib.request.urlopen(cat_file[1]) as link: fdata = link.readlines() - - cat_lines = [l.decode('utf-8').strip('\n') for l in fdata] - - for line in cat_lines: - if ('.pkg' in line or '.tar' in line) and ('FirmwareUpd' in line or '/BridgeOSUpdateCustomer' in line or 'EFIUpd' in line) \ - and 'Bluetooth' not in line and 'DPVGA' not in line and 'Thunderbolt' not in line and 'PMG5' not in line and 'HardDrive' not in line: - down_link = line[line.find('http'):(line.find('.pkg') if '.pkg' in line else line.find('.tar')) + 4] - down_link = down_link.replace('http:','https:') - cat_links.append(down_link) - - return cat_links - -dat_db = 'Apple_EFI_Grab.dat' -cat_url = 'https://swscan.apple.com/content/catalogs/others/' -apple_cat = [] -down_links = [] -svr_date = None -thread_num = 2 - -with open(dat_db, 'r', encoding='utf-8') as dat: db_lines = dat.readlines() -db_lines = [line.strip('\n') for line in db_lines] - -db_date = datetime.datetime.strptime(db_lines[0], '%Y-%m-%d %H:%M:%S') -db_links = set([line for line in db_lines if line.startswith('https')]) -db_sucat = [line for line in db_lines if line.startswith('index')] - -print('\nGetting Catalog Listing...') - -if not db_sucat: - input('\nError: Failed to retrieve Catalogs from DB!\n\nDone!') - sys.exit(1) - -apple_mod = ThreadPool(thread_num).imap_unordered(fetch_cat_info, db_sucat) - -for name, url, mod in apple_mod: - dt = datetime.datetime.strptime(mod, '%a, %d %b %Y %H:%M:%S %Z') - if not svr_date or dt > svr_date : svr_date = dt - - apple_cat.append((name, url, dt)) - -if not svr_date: - input('\nError: Failed to retrieve Current Catalog Datetime!\n\nDone!') - sys.exit(1) - -print('\n Previous Catalog Datetime :', db_date) -print(' Current Catalog Datetime :', svr_date) - -if svr_date <= db_date: - input('\nNothing new since %s!\n\nDone!' % db_date) - sys.exit() - -print('\nGetting Catalog Links...') - -down_links = ThreadPool(thread_num).imap_unordered(fetch_cat_links, apple_cat) -down_links = [item for sublist in down_links for item in sublist] - -if not down_links: - input('\nError: Failed to retrieve Catalog Links!\n\nDone!') - sys.exit(1) - -new_links = sorted(list(dict.fromkeys([link for link in down_links if link not in db_links]))) - -if new_links: - print('\nFound %d new link(s) between %s and %s!' % (len(new_links), db_date, svr_date)) - - cur_date = datetime.datetime.utcnow().isoformat(timespec='seconds').replace('-','').replace('T','').replace(':','') # Local UTC Unix - - with open('Apple_%s.txt' % cur_date, 'w', encoding='utf-8') as lout: lout.write('\n'.join(map(str, new_links))) -else: - print('\nThere are no new links between %s and %s!' % (db_date, svr_date)) - -new_db_sucat = '\n'.join(map(str, db_sucat)) - -new_db_links = '\n'.join(map(str, sorted(list(dict.fromkeys(down_links))))) - -new_db_lines = '%s\n\n%s\n\n%s' % (svr_date, new_db_sucat, new_db_links) - -with open(dat_db, 'w', encoding='utf-8') as dbout: dbout.write(new_db_lines) - -input('\nDone!') \ No newline at end of file diff --git a/Award BIOS Module Extractor/Award_BIOS_Extract.py b/Award BIOS Module Extractor/Award_BIOS_Extract.py deleted file mode 100644 index 5a39788..0000000 --- a/Award BIOS Module Extractor/Award_BIOS_Extract.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python3 - -""" -Award BIOS Extract -Award BIOS Module Extractor -Copyright (C) 2018-2019 Plato Mavropoulos -http://www.onicos.com/staff/iz/formats/lzh.html -https://ist.uwaterloo.ca/~schepers/formats/LHA.TXT -https://sites.google.com/site/pinczakko/pinczakko-s-guide-to-award-bios-reverse-engineering -""" - -print('Award BIOS Module Extractor v1.2\n') - -import os -import re -import sys -import subprocess - -if len(sys.argv) >= 2 : - # Drag & Drop or CLI - awd_images = sys.argv[1:] -else : - # Folder path - awd_images = [] - in_path = input('\nEnter the full folder path: ') - print('\nWorking...') - for root, dirs, files in os.walk(in_path): - for name in files : - awd_images.append(os.path.join(root, name)) - -pat_lzh = re.compile(br'\x2D\x6C((\x68(([\x30-\x37])|(\x64)))|(\x7A([\x34\x73])))\x2D') # All 11 LZH Method IDs (Award probably used LH0 and LH5 only) - -# Create output folder -extr_path = os.path.join(os.getcwd(), 'AWD_Extracted') -if not os.path.exists(extr_path) : os.makedirs(extr_path) - -for in_file in awd_images : - file_path = os.path.abspath(in_file) - file_name = os.path.basename(in_file) - file_dir = os.path.dirname(file_path) - file_ext = os.path.splitext(file_path)[1] - match_lzh_list = [] - - print('\nFile: %s%s' % (file_name, file_ext)) - - with open(in_file, 'rb') as awd_img : buffer = awd_img.read() - - match_lzh_list += pat_lzh.finditer(buffer) # Detect LZH patterns - - for match_lzh in match_lzh_list : - hdr_size = buffer[match_lzh.start() - 0x2] # From LZH Tag (0x2+) - comp_size = int.from_bytes(buffer[match_lzh.end():match_lzh.end() + 0x4], 'little') # From LZH Header end - mod_data = buffer[match_lzh.start() - 0x2:match_lzh.start() + hdr_size + comp_size] - - with open('mod_temp.bin', 'wb') as lzh_img : lzh_img.write(mod_data) - - try : - decomp = subprocess.run(['7z', 'x', '-aou', '-bso0', '-bse0', '-bsp0', '-o%s' % os.path.join(extr_path, file_name), 'mod_temp.bin']) # 7-Zip - except : - print('Error: Could not decompress LZH image at %s!' % file_name) - print(' Make sure that "7z" executable exists!\n') - - os.remove('mod_temp.bin') # Remove temporary LZH image - -input('\nDone!') \ No newline at end of file diff --git a/Dell PFS Update Extractor/Dell_PFS_Extract.py b/Dell PFS Update Extractor/Dell_PFS_Extract.py deleted file mode 100644 index af1ad75..0000000 --- a/Dell PFS Update Extractor/Dell_PFS_Extract.py +++ /dev/null @@ -1,1245 +0,0 @@ -#!/usr/bin/env python3 -#coding=utf-8 - -""" -Dell PFS Extract -Dell PFS Update Extractor -Copyright (C) 2018-2021 Plato Mavropoulos -""" - -title = 'Dell PFS Update Extractor v5.1' - -import sys - -# Detect Python version -sys_py = sys.version_info - -# Check Python version -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 '--auto-exit' not in sys.argv and '-e' 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 - -# Check OS platform -if sys_os == 'win32' : - 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 : - pass # Supported/Tested -else : - print('%s\n\nError: Unsupported platform "%s"!\n' % (title, sys_os)) - - if '--auto-exit' not in sys.argv and '-e' not in sys.argv : input('Press enter to exit') - - sys.exit(1) - -# Skip __pycache__ generation -sys.dont_write_bytecode = True - -# Python imports -import os -import re -import zlib -import lzma -import shutil -import ctypes -import inspect -import pathlib -import argparse -import traceback - -# Optional imports -try : - from big_script_tool import BigScript - is_bgst = True -except : - is_bgst = False - -# 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 - -# Dell PFS Header Structure -class PFS_DELL_HDR(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('Tag', char*8), # 0x00 - ('HeaderVersion', uint32_t), # 0x08 - ('PayloadSize', uint32_t), # 0x0C - # 0x10 - ] - - def pfs_print(self, padd) : - print('\n%sPFS Header:\n' % (' ' * (padd - 4))) - print('%sHeader Tag : %s' % (' ' * padd, self.Tag.decode('utf-8'))) - print('%sHeader Version : %d' % (' ' * padd, self.HeaderVersion)) - print('%sPayload Size : 0x%X' % (' ' * padd, self.PayloadSize)) - -# Dell PFS Footer Structure -class PFS_DELL_FTR(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('PayloadSize', uint32_t), # 0x00 - ('Checksum', uint32_t), # 0x04 ~CRC32 w/ Vector 0 - ('Tag', char*8), # 0x08 - # 0x10 - ] - - def pfs_print(self, padd) : - print('\n%sPFS Footer:\n' % (' ' * (padd - 4))) - print('%sPayload Size : 0x%X' % (' ' * padd, self.PayloadSize)) - print('%sPayload Checksum : 0x%0.8X' % (' ' * padd, self.Checksum)) - print('%sFooter Tag : %s' % (' ' * padd, self.Tag.decode('utf-8'))) - -# Dell PFS Entry Revision 1 Structure -class PFS_ENTRY_R1(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('GUID', uint32_t*4), # 0x00 Little Endian - ('HeaderVersion', uint32_t), # 0x10 1 - ('VersionType', uint8_t*4), # 0x14 - ('Version', uint16_t*4), # 0x18 - ('Reserved', uint64_t), # 0x20 - ('DataSize', uint32_t), # 0x28 - ('DataSigSize', uint32_t), # 0x2C - ('DataMetSize', uint32_t), # 0x30 - ('DataMetSigSize', uint32_t), # 0x34 - ('Unknown', uint32_t*4), # 0x38 - # 0x48 - ] - - def pfs_print(self, padd) : - GUID = '%0.*X' % (0x10 * 2, int.from_bytes(self.GUID, 'little')) - Unknown = '%0.*X' % (0x10 * 2, int.from_bytes(self.Unknown, 'little')) - Version = get_entry_ver(self.Version, self.VersionType, padd - 4) - - print('\n%sPFS Entry:\n' % (' ' * (padd - 4))) - print('%sEntry GUID : %s' % (' ' * padd, GUID)) - print('%sEntry Version : %d' % (' ' * padd, self.HeaderVersion)) - print('%sPayload Version : %s' % (' ' * padd, Version)) - print('%sReserved : 0x%X' % (' ' * padd, self.Reserved)) - print('%sPayload Data Size : 0x%X' % (' ' * padd, self.DataSize)) - print('%sPayload Signature Size : 0x%X' % (' ' * padd, self.DataSigSize)) - print('%sMetadata Data Size : 0x%X' % (' ' * padd, self.DataMetSize)) - print('%sMetadata Signature Size : 0x%X' % (' ' * padd, self.DataMetSigSize)) - print('%sUnknown : %s' % (' ' * padd, Unknown)) - -# Dell PFS Entry Revision 2 Structure -class PFS_ENTRY_R2(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('GUID', uint32_t*4), # 0x00 Little Endian - ('HeaderVersion', uint32_t), # 0x10 2 - ('VersionType', uint8_t*4), # 0x14 - ('Version', uint16_t*4), # 0x18 - ('Reserved', uint64_t), # 0x20 - ('DataSize', uint32_t), # 0x28 - ('DataSigSize', uint32_t), # 0x2C - ('DataMetSize', uint32_t), # 0x30 - ('DataMetSigSize', uint32_t), # 0x34 - ('Unknown', uint32_t*8), # 0x38 - # 0x58 - ] - - def pfs_print(self, padd) : - GUID = '%0.*X' % (0x10 * 2, int.from_bytes(self.GUID, 'little')) - Unknown = '%0.*X' % (0x20 * 2, int.from_bytes(self.Unknown, 'little')) - Version = get_entry_ver(self.Version, self.VersionType, padd - 4) - - print('\n%sPFS Entry:\n' % (' ' * (padd - 4))) - print('%sEntry GUID : %s' % (' ' * padd, GUID)) - print('%sEntry Version : %d' % (' ' * padd, self.HeaderVersion)) - print('%sPayload Version : %s' % (' ' * padd, Version)) - print('%sReserved : 0x%X' % (' ' * padd, self.Reserved)) - print('%sPayload Data Size : 0x%X' % (' ' * padd, self.DataSize)) - print('%sPayload Signature Size : 0x%X' % (' ' * padd, self.DataSigSize)) - print('%sMetadata Data Size : 0x%X' % (' ' * padd, self.DataMetSize)) - print('%sMetadata Signature Size : 0x%X' % (' ' * padd, self.DataMetSigSize)) - print('%sUnknown : %s' % (' ' * padd, Unknown)) - -# Dell PFS Information Header Structure -class PFS_INFO_HDR(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('HeaderVersion', uint32_t), # 0x00 - ('GUID', uint32_t*4), # 0x04 Little Endian - # 0x14 - ] - - def pfs_print(self, padd) : - GUID = '%0.*X' % (0x10 * 2, int.from_bytes(self.GUID, 'little')) - - print('\n%sPFS Information Header:\n' % (' ' * (padd - 4))) - print('%sInfo Version : %d' % (' ' * padd, self.HeaderVersion)) - print('%sEntry GUID : %s' % (' ' * padd, GUID)) - -# Dell PFS FileName Header Structure -class PFS_NAME_HDR(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('Version', uint16_t*4), # 0x00 - ('VersionType', uint8_t*4), # 0x08 - ('CharacterCount', uint16_t), # 0x0C UTF-16 2-byte Characters - # 0x0E - ] - - def pfs_print(self, padd) : - Version = get_entry_ver(self.Version, self.VersionType, padd - 4) - - print('\n%sPFS FileName Entry:\n' % (' ' * (padd - 4))) - print('%sPayload Version : %s' % (' ' * padd, Version)) - print('%sCharacter Count : %d' % (' ' * padd, self.CharacterCount)) - -# Dell PFS Metadata Header Structure -class PFS_META_HDR(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('ModelIDs', char*501), # 0x000 - ('FileName', char*100), # 0x1F5 - ('FileVersion', char*33), # 0x259 - ('Date', char*33), # 0x27A - ('Brand', char*80), # 0x29B - ('ModelFile', char*80), # 0x2EB - ('ModelName', char*100), # 0x33B - ('ModelVersion', char*33), # 0x39F - # 0x3C0 - ] - - def pfs_print(self, padd) : - print('\n%sPFS Metadata Information:\n' % (' ' * (padd - 4))) - print('%sModel IDs : %s' % (' ' * padd, self.ModelIDs.decode('utf-8').strip(',END'))) - print('%sFile Name : %s' % (' ' * padd, self.FileName.decode('utf-8'))) - print('%sFile Version : %s' % (' ' * padd, self.FileVersion.decode('utf-8'))) - print('%sDate : %s' % (' ' * padd, self.Date.decode('utf-8'))) - print('%sBrand : %s' % (' ' * padd, self.Brand.decode('utf-8'))) - print('%sModel File : %s' % (' ' * padd, self.ModelFile.decode('utf-8'))) - print('%sModel Name : %s' % (' ' * padd, self.ModelName.decode('utf-8'))) - print('%sModel Version : %s' % (' ' * padd, self.ModelVersion.decode('utf-8'))) - - def pfs_write(self) : - return '%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s' % (self.ModelIDs.decode('utf-8').strip(',END'), self.FileName.decode('utf-8'), - self.FileVersion.decode('utf-8'), self.Date.decode('utf-8'), self.Brand.decode('utf-8'), self.ModelFile.decode('utf-8'), - self.ModelName.decode('utf-8'), self.ModelVersion.decode('utf-8')) - -# Dell PFS BIOS Guard Header Structure -class PFS_PFAT_HDR(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('PFATVerMajor', uint16_t), # 0x00 - ('PFATVerMinor', uint16_t), # 0x02 - ('PlatformID', uint8_t*16), # 0x04 - ('Attributes', uint32_t), # 0x14 - ('ScriptVerMajor', uint16_t), # 0x16 - ('ScriptVerMinor', uint16_t), # 0x18 - ('ScriptSize', uint32_t), # 0x1C - ('DataSize', uint32_t), # 0x20 - ('BIOSSVN', uint32_t), # 0x24 - ('ECSVN', uint32_t), # 0x28 - ('VendorInfo', uint32_t), # 0x2C - # 0x30 - ] - - def __init__(self, count, *args, **kwargs): - super().__init__(*args, **kwargs) - self.count = count - - def get_flags(self) : - attr = PFS_PFAT_HDR_ATTRIBUTES_GET() - attr.asbytes = self.Attributes - - return attr.b.SFAM, attr.b.ProtectEC, attr.b.GFXMitDis, attr.b.FTU, attr.b.Reserved - - def pfs_print(self, padd) : - no_yes = ['No','Yes'] - f1,f2,f3,f4,f5 = self.get_flags() - - PlatformID = bytes(self.PlatformID).strip(b'\x00') - try : # STRING - PlatformID = PlatformID.decode('utf-8') - except : # GUID - PlatformID = '%0.*X' % (0x10 * 2, int.from_bytes(self.PlatformID, 'big')) - PlatformID = '{%s-%s-%s-%s-%s}' % (PlatformID[:8], PlatformID[8:12], PlatformID[12:16], PlatformID[16:20], PlatformID[20:]) - - print('\n%sPFAT Block %d Header:\n' % (' ' * (padd - 4), self.count)) - print('%sPFAT Version : %d.%d' % (' ' * padd, self.PFATVerMajor, self.PFATVerMinor)) - print('%sPlatform ID : %s' % (' ' * padd, PlatformID)) - print('%sSigned Flash Address Map : %s' % (' ' * padd, no_yes[f1])) - print('%sProtected EC OpCodes : %s' % (' ' * padd, no_yes[f2])) - print('%sGraphics Security Disable : %s' % (' ' * padd, no_yes[f3])) - print('%sFault Tolerant Update : %s' % (' ' * padd, no_yes[f4])) - print('%sAttributes Reserved : 0x%X' % (' ' * padd, f5)) - print('%sScript Version : %d.%d' % (' ' * padd, self.ScriptVerMajor, self.ScriptVerMinor)) - print('%sScript Size : 0x%X' % (' ' * padd, self.ScriptSize)) - print('%sData Size : 0x%X' % (' ' * padd, self.DataSize)) - print('%sBIOS SVN : 0x%X' % (' ' * padd, self.BIOSSVN)) - print('%sEC SVN : 0x%X' % (' ' * padd, self.ECSVN)) - print('%sVendor Info : 0x%X' % (' ' * padd, self.VendorInfo)) - -# Dell PFS BIOS Guard Attributes Flags Structure -class PFS_PFAT_HDR_ATTRIBUTES(ctypes.LittleEndianStructure): - _fields_ = [ - ('SFAM', uint32_t, 1), # Signed Flash Address Map - ('ProtectEC', uint32_t, 1), # Protected EC OpCodes - ('GFXMitDis', uint32_t, 1), # GFX Security Disable - ('FTU', uint32_t, 1), # Fault Tolerant Update - ('Reserved', uint32_t, 28) - ] - -# Dell PFS BIOS Guard Attributes Get Structure -class PFS_PFAT_HDR_ATTRIBUTES_GET(ctypes.Union): - _fields_ = [ - ('b', PFS_PFAT_HDR_ATTRIBUTES), - ('asbytes', uint32_t) - ] - -# Dell PFS BIOS Guard Signature Structure -class PFS_PFAT_SIG(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('Unknown0', uint32_t), # 0x00 - ('Unknown1', uint32_t), # 0x04 - ('PublicKey', uint32_t*64), # 0x08 - ('Exponent', uint32_t), # 0x108 - ('Signature', uint32_t*64), # 0x10C - # 0x20C - ] - - def __init__(self, count, *args, **kwargs): - super().__init__(*args, **kwargs) - self.count = count - - def pfs_print(self, padd) : - PublicKey = '%0.*X' % (0x100 * 2, int.from_bytes(self.PublicKey, 'little')) - Signature = '%0.*X' % (0x100 * 2, int.from_bytes(self.Signature, 'little')) - - print('\n%sPFAT Block %d Signature:\n' % (' ' * (padd - 4), self.count)) - print('%sUnknown 0 : 0x%X' % (' ' * padd, self.Unknown0)) - print('%sUnknown 1 : 0x%X' % (' ' * padd, self.Unknown1)) - print('%sPublic Key : %s [...]' % (' ' * padd, PublicKey[:32])) - print('%sExponent : 0x%X' % (' ' * padd, self.Exponent)) - print('%sSignature : %s [...]' % (' ' * padd, Signature[:32])) - -# Dell PFS BIOS Guard Metadata Structure -class PFS_PFAT_MET(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('OffsetTop', uint32_t), # 0x00 - ('Unknown0', uint32_t), # 0x04 - ('OffsetBase', uint32_t), # 0x08 - ('BlockSize', uint32_t), # 0x0C - ('Unknown1', uint32_t), # 0x10 - ('Unknown2', uint32_t), # 0x14 - ('Unknown3', uint8_t), # 0x18 - # 0x19 - ] - - def __init__(self, count, *args, **kwargs): - super().__init__(*args, **kwargs) - self.count = count - - def pfs_print(self, padd) : - print('\n%sPFAT Block %d Metadata:\n' % (' ' * (padd - 4), self.count)) - print('%sOffset Top : 0x%X' % (' ' * padd, self.OffsetTop)) - print('%sUnknown 0 : 0x%X' % (' ' * padd, self.Unknown0)) - print('%sOffset Base : 0x%X' % (' ' * padd, self.OffsetBase)) - print('%sBlock Size : 0x%X' % (' ' * padd, self.BlockSize)) - print('%sUnknown 1 : 0x%X' % (' ' * padd, self.Unknown1)) - print('%sUnknown 2 : 0x%X' % (' ' * padd, self.Unknown2)) - print('%sUnknown 3 : 0x%X' % (' ' * padd, self.Unknown3)) - -# Dell PFS Update Analysis -def main(exit_code, pfs_input_images) : - # Process each input Dell PFS update image - for input_file in pfs_input_images : - input_name,input_ext = os.path.splitext(os.path.basename(input_file)) - input_dir = os.path.dirname(os.path.abspath(input_file)) - - print('\n*** %s%s' % (input_name, input_ext)) - - # Check if input file exists - if not os.path.isfile(input_file) : - print('\n Error: This input file does not exist!') - continue # Next input file - - with open(input_file, 'rb') as in_file : input_data = in_file.read() - - # Search input image for ThinOS PKG 7zXZ section header - lzma_pkg_hdr_match = lzma_pkg_header.search(input_data) - - # Decompress ThinOS PKG 7zXZ section first, if present - if lzma_pkg_hdr_match : - lzma_len_off = lzma_pkg_hdr_match.start() + 0x10 - lzma_len_int = int.from_bytes(input_data[lzma_len_off:lzma_len_off + 0x4], 'little') - lzma_bin_off = lzma_pkg_hdr_match.end() - 0x5 - lzma_bin_dat = input_data[lzma_bin_off:lzma_bin_off + lzma_len_int] - - # Check if the compressed 7zXZ stream is complete, based on header - if len(lzma_bin_dat) != lzma_len_int : - print('\n Error: This Dell ThinOS PKG update image is corrupted!') - continue # Next input file - - input_data = lzma.decompress(lzma_bin_dat) - - # Search input image for PFS ZLIB Sections - pfs_zlib_offsets = get_section_offsets(input_data) - - if not pfs_zlib_offsets : - print('\n Error: This is not a Dell PFS update image!') - continue # Next input file - - # Set user extraction path - extract_path_user = get_absolute_path(args.output_dir) - - # Set main extraction path (optional user specified path taken into account) - extract_path_main = os.path.join(extract_path_user, '%s%s' % (input_name, input_ext) + '_extracted') - - # Parse each PFS ZLIB Section - for offset in pfs_zlib_offsets : - # Call the PFS ZLIB Section Parser function - pfs_section_parse(input_data, offset, extract_path_main, ' ' + input_name, 1, 1, False, 4) - - exit_code -= 1 # Adjust exit code to reflect extraction progress - - if not bool(args.auto_exit) : input('\nDone!') - - return exit_code - -# Get PFS ZLIB Section Offsets -def get_section_offsets(buffer) : - pfs_zlib_init = list(pfs_zlib_header.finditer(buffer)) - - if not pfs_zlib_init : return [] # No PFS ZLIB detected - - pfs_zlib_list = [] # Initialize PFS ZLIB offset list - - # Remove duplicate/nested PFS ZLIB offsets - for zlib_c in pfs_zlib_init : - is_duplicate = False # Initialize duplicate/nested PFS ZLIB offset - - for zlib_o in pfs_zlib_init : - zlib_o_size = int.from_bytes(buffer[zlib_o.start() - 0x5:zlib_o.start() - 0x1], 'little') - - # If current PFS ZLIB offset is within another PFS ZLIB range (start-end), set as duplicate - if zlib_o.start() < zlib_c.start() < zlib_o.start() + zlib_o_size : is_duplicate = True - - if not is_duplicate : pfs_zlib_list.append(zlib_c.start()) - - return pfs_zlib_list - -# Dell PFS ZLIB Section Parser -def pfs_section_parse(zlib_data, zlib_start, output_path, pfs_name, pfs_index, pfs_count, is_rec, padd) : - is_zlib_error = False # Initialize PFS ZLIB-related error state - - section_type = zlib_data[zlib_start - 0x1] # Byte before PFS ZLIB Section pattern is Section Type (e.g. AA, BB) - section_name = section_dict[section_type] if section_type in section_dict else 'Unknown (%0.2X)' % section_type - - # Set PFS ZLIB Section extraction sub-directory path - section_path = os.path.join(output_path, section_name) - - # Delete existing extraction sub-directory (not in recursions) - if os.path.isdir(section_path) and not is_rec : shutil.rmtree(section_path) - - # Create extraction sub-directory - if not os.path.isdir(section_path) : os.makedirs(section_path) - - # Store the compressed zlib stream start offset - compressed_start = zlib_start + 0xB - - # Store the PFS ZLIB section header start offset - header_start = zlib_start - 0x5 - - # Store the PFS ZLIB section header contents (16 bytes) - header_data = zlib_data[header_start:compressed_start] - - # Check if the PFS ZLIB section header Checksum XOR 8 is valid - if chk_xor_8(header_data[:0xF], 0) != header_data[0xF] : - print('\n%sError: Invalid Dell PFS ZLIB section Header Checksum!' % (' ' * padd)) - is_zlib_error = True - - # Store the compressed zlib stream size from the header contents - compressed_size_hdr = int.from_bytes(header_data[:0x4], 'little') - - # Store the compressed zlib stream end offset - compressed_end = compressed_start + compressed_size_hdr - - # Store the compressed zlib stream contents - compressed_data = zlib_data[compressed_start:compressed_end] - - # Check if the compressed zlib stream is complete, based on header - if len(compressed_data) != compressed_size_hdr : - print('\n%sError: Incomplete Dell PFS ZLIB section data (Header)!' % (' ' * padd)) - is_zlib_error = True - - # Store the PFS ZLIB section footer contents (16 bytes) - footer_data = zlib_data[compressed_end:compressed_end + 0x10] - - # Search input section for PFS ZLIB section footer - pfs_zlib_footer_match = pfs_zlib_footer.search(footer_data) - - # Check if PFS ZLIB section footer was found in the section - if not pfs_zlib_footer_match : - print('\n%sError: This Dell PFS ZLIB section is corrupted!' % (' ' * padd)) - is_zlib_error = True - - # Check if the PFS ZLIB section footer Checksum XOR 8 is valid - if chk_xor_8(footer_data[:0xF], 0) != footer_data[0xF] : - print('\n%sError: Invalid Dell PFS ZLIB section Footer Checksum!' % (' ' * padd)) - is_zlib_error = True - - # Store the compressed zlib stream size from the footer contents - compressed_size_ftr = int.from_bytes(footer_data[:0x4], 'little') - - # Check if the compressed zlib stream is complete, based on footer - if compressed_size_ftr != compressed_size_hdr : - print('\n%sError: Incomplete Dell PFS ZLIB section data (Footer)!' % (' ' * padd)) - is_zlib_error = True - - # Decompress PFS ZLIB section payload - try : - assert not is_zlib_error # ZLIB errors are critical - section_data = zlib.decompress(compressed_data) # ZLIB decompression - except : - section_data = zlib_data # Fallback to raw ZLIB data upon critical error - - # Call the PFS Extract function on the decompressed PFS ZLIB Section - pfs_extract(section_data, pfs_index, pfs_name, pfs_count, section_path, padd) - - # Show extraction complete message for each main PFS ZLIB Section - print('\n%sExtracted Dell PFS %d >%s > %s section!' % (' ' * padd, pfs_index, pfs_name, section_name)) - -# Parse & Extract Dell PFS Volume -def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd) : - if is_verbose : print('\n%sPFS Volume:' % (' ' * pfs_padd)) - - # Get PFS Header Structure values - pfs_hdr = get_struct(buffer, 0, PFS_DELL_HDR, None, pfs_padd + 4) - - # Validate that a PFS Header was parsed - if pfs_hdr.Tag != b'PFS.HDR.' : - msg_print(pfs_padd + 4, 'Error: PFS Header could not be found!') - return # Critical error, abort - - # Show PFS Header Structure info - if is_verbose : pfs_hdr.pfs_print(pfs_padd + 8) - - # Validate that a known PFS Header Version was encountered - chk_hdr_ver(pfs_hdr.HeaderVersion, 'PFS', pfs_padd + 8) - - # Get PFS Payload Data - pfs_payload = buffer[dpfs_hdr_size:dpfs_hdr_size + pfs_hdr.PayloadSize] - - # Parse all PFS Payload Entries/Components - entry_index = 1 # Index number of each PFS Entry - entry_start = 0 # Increasing PFS Entry starting offset - entries_all = [] # Storage for each PFS Entry details - filename_info = [] # Buffer for FileName Information Entry Data - signature_info = [] # Buffer for Signature Information Entry Data - pfs_entry_struct, pfs_entry_size = get_pfs_entry(pfs_payload, entry_start) # Get PFS Entry Info - while len(pfs_payload[entry_start:entry_start + pfs_entry_size]) == pfs_entry_size : - # Analyze PFS Entry Structure and get relevant info - pfs_entry,entry_version,entry_guid,entry_data,entry_data_sig,entry_met,entry_met_sig,next_entry = \ - parse_pfs_entry(pfs_payload, entry_start, pfs_entry_size, pfs_entry_struct, None, 'PFS Entry', pfs_padd) - - entry_type = 'OTHER' # Adjusted later if PFS Entry is Zlib, PFAT, PFS Info, Model Info - - # Get PFS Information from the PFS Entry with GUID E0717CE3A9BB25824B9F0DC8FD041960 or B033CB16EC9B45A14055F80E4D583FD3 - if entry_guid in ['E0717CE3A9BB25824B9F0DC8FD041960','B033CB16EC9B45A14055F80E4D583FD3'] : - filename_info = entry_data - entry_type = 'NAME_INFO' - - # Get Model Information from the PFS Entry with GUID 6F1D619A22A6CB924FD4DA68233AE3FB - elif entry_guid == '6F1D619A22A6CB924FD4DA68233AE3FB' : - entry_type = 'MODEL_INFO' - - # Get Signature Information from the PFS Entry with GUID D086AFEE3ADBAEA94D5CED583C880BB7 - elif entry_guid == 'D086AFEE3ADBAEA94D5CED583C880BB7' : - signature_info = entry_data - entry_type = 'SIG_INFO' - - # Get Nested PFS from the PFS Entry with GUID 900FAE60437F3AB14055F456AC9FDA84 - elif entry_guid == '900FAE60437F3AB14055F456AC9FDA84' : - entry_type = 'NESTED_PFS' # Nested PFS are usually zlib-compressed so it might change to 'ZLIB' later - - # Store all relevant PFS Entry details - entries_all.append([entry_index, entry_guid, entry_version, entry_type, entry_data, entry_data_sig, entry_met, entry_met_sig]) - - entry_index += 1 # Increase PFS Entry Index number for user-friendly output and name duplicates - entry_start = next_entry # Next PFS Entry starts after PFS Entry Metadata Signature - - # Parse all PFS Information Entries/Descriptors - info_start = 0 # Increasing PFS Information Entry starting offset - info_all = [] # Storage for each PFS Information Entry details - while len(filename_info[info_start:info_start + info_hdr_size]) == info_hdr_size : - # Get PFS Information Header Structure info - entry_info_hdr = get_struct(filename_info, info_start, PFS_INFO_HDR, None, pfs_padd + 8) - - # Show PFS Information Header Structure info - if is_verbose : entry_info_hdr.pfs_print(pfs_padd + 8) - - # Validate that a known PFS Information Header Version was encountered - if entry_info_hdr.HeaderVersion != 1 : - msg_print(pfs_padd + 8, 'Error: Unknown PFS Information Header Version %d!' % entry_info_hdr.HeaderVersion) - break # Skip PFS Information Entries/Descriptors in case of unknown PFS Information Header Version - - # Get PFS Information Header GUID in Big Endian format to match each Info to the equivalent stored PFS Entry details - entry_guid = '%0.*X' % (0x10 * 2, int.from_bytes(entry_info_hdr.GUID, 'little')) - - # Get PFS FileName Structure values - entry_info_mod = get_struct(filename_info, info_start + info_hdr_size, PFS_NAME_HDR, None, pfs_padd + 8) - - # Show PFS FileName Structure info - if is_verbose : entry_info_mod.pfs_print(pfs_padd + 12) - - # The PFS FileName Structure is not complete by itself. The size of the last field (Entry Name) is determined from - # CharacterCount multiplied by 2 due to usage of UTF-16 2-byte Characters. Any Entry Name leading and/or trailing - # space/null characters are stripped and common Windows reserved/illegal filename characters are replaced - name_start = info_start + info_hdr_size + name_hdr_size # PFS Entry's FileName start offset - name_size = entry_info_mod.CharacterCount * 2 # PFS Entry's FileName buffer total size - name_data = filename_info[name_start:name_start + name_size] # PFS Entry's FileName buffer - entry_name = re.sub(win_char_bad, '_', name_data.decode('utf-16').strip()) # PFS Entry's FileName value - - # Show PFS FileName Name info (padding matches the one from PFS FileName Structure info) - if is_verbose : print('%sPayload Name%s: %s' % (' ' * (pfs_padd + 12), ' ' * 4, entry_name)) - - # Get PFS FileName Version string via "Version" and "VersionType" fields - # PFS FileName Version string must be preferred over PFS Entry's Version - entry_version = get_entry_ver(entry_info_mod.Version, entry_info_mod.VersionType, pfs_padd + 12) - - # Store all relevant PFS FileName details - info_all.append([entry_guid, entry_name, entry_version]) - - # The next PFS Information Header starts after the calculated FileName size - # Two space/null characters seem to always exist after each FileName value - info_start += (info_hdr_size + name_hdr_size + name_size + 0x2) - - # Parse Nested PFS Metadata when its PFS Information Entry is missing - for index in range(len(entries_all)) : - if entries_all[index][3] == 'NESTED_PFS' and not filename_info : - entry_guid = entries_all[index][1] # Nested PFS Entry GUID in Big Endian format - entry_metadata = entries_all[index][6] # Use Metadata as PFS Information Entry - - # When PFS Information Entry exists, Nested PFS Metadata contains only Model IDs - # When it's missing, the Metadata structure is large and contains equivalent info - if len(entry_metadata) >= meta_hdr_size : - # Get Nested PFS Metadata Structure values - entry_info = get_struct(entry_metadata, 0, PFS_META_HDR, None, pfs_padd + 4) - - # Show Nested PFS Metadata Structure info - if is_verbose : entry_info.pfs_print(pfs_padd + 8) - - # As Nested PFS Entry Name, we'll use the actual PFS File Name - # Replace common Windows reserved/illegal filename characters - entry_name = re.sub(win_char_bad, '_', entry_info.FileName.decode('utf-8').strip('.exe')) - - # As Nested PFS Entry Version, we'll use the actual PFS File Version - entry_version = entry_info.FileVersion.decode('utf-8') - - # Store all relevant Nested PFS Metadata/Information details - info_all.append([entry_guid, entry_name, entry_version]) - - # Re-set Nested PFS Entry Version from Metadata - entries_all[index][2] = entry_version - - # Parse all PFS Signature Entries/Descriptors - sign_start = 0 # Increasing PFS Signature Entry starting offset - while len(signature_info[sign_start:sign_start + info_hdr_size]) == info_hdr_size : - # Get PFS Information Header Structure info - entry_info_hdr = get_struct(signature_info, sign_start, PFS_INFO_HDR, None, pfs_padd + 8) - - # Show PFS Information Header Structure info - if is_verbose : entry_info_hdr.pfs_print(pfs_padd + 8) - - # Validate that a known PFS Information Header Version was encountered - if entry_info_hdr.HeaderVersion != 1 : - msg_print(pfs_padd + 8, 'Error: Unknown PFS Information Header Version %d!' % entry_info_hdr.HeaderVersion) - break # Skip PFS Signature Entries/Descriptors in case of unknown Header Version - - # PFS Signature Entries/Descriptors have PFS_INFO_HDR + PFS_ENTRY_R* + Sign Size [0x2] + Sign Data [Sig Size] - pfs_entry_struct, pfs_entry_size = get_pfs_entry(signature_info, sign_start + info_hdr_size) # Get PFS Entry Info - - # Get PFS Entry Header Structure info - entry_hdr = get_struct(signature_info, sign_start + info_hdr_size, pfs_entry_struct, None, pfs_padd + 8) - - # Show PFS Information Header Structure info - if is_verbose : entry_hdr.pfs_print(pfs_padd + 12) - - # Show PFS Signature Size & Data (after PFS_ENTRY_R*) - sign_info_start = sign_start + info_hdr_size + pfs_entry_size - sign_size = int.from_bytes(signature_info[sign_info_start:sign_info_start + 0x2], 'little') - sign_data_raw = signature_info[sign_info_start + 0x2:sign_info_start + 0x2 + sign_size] - sign_data_txt = '%0.*X' % (sign_size * 2, int.from_bytes(sign_data_raw, 'little')) - if is_verbose : - print('\n%sSignature Information:\n' % (' ' * (pfs_padd + 8))) - print('%sSignature Size : 0x%X' % (' ' * (pfs_padd + 12), sign_size)) - print('%sSignature Data : %s [...]' % (' ' * (pfs_padd + 12), sign_data_txt[:32])) - - # The next PFS Signature Entry/Descriptor starts after the previous Signature Data - sign_start += (info_hdr_size + pfs_entry_size + 0x2 + sign_size) - - # Parse each PFS Entry Data for special types (zlib or PFAT) - for index in range(len(entries_all)) : - entry_data = entries_all[index][4] # Get PFS Entry Data - entry_type = entries_all[index][3] # Get PFS Entry Type - - # Very small PFS Entry Data cannot be of special type - if len(entry_data) < dpfs_hdr_size : continue - - # Check if PFS Entry contains zlib-compressed sub-PFS Volume - pfs_zlib_offsets = get_section_offsets(entry_data) - - # Check if PFS Entry contains sub-PFS Volume with PFAT Payload - is_pfat = False # Initial PFAT state for sub-PFS Entry - _, pfat_entry_size = get_pfs_entry(entry_data, dpfs_hdr_size) # Get possible PFS PFAT Entry Size - pfat_hdr_off = dpfs_hdr_size + pfat_entry_size # Possible PFAT Header starts after PFS Header & Entry - pfat_entry_hdr = get_struct(entry_data, 0, PFS_DELL_HDR, None, pfs_padd + 8) # Possible PFS PFAT Entry - if len(entry_data) - pfat_hdr_off >= pfat_hdr_size : - pfat_hdr = get_struct(entry_data, pfat_hdr_off, PFS_PFAT_HDR, [0], pfs_padd + 8) - is_pfat = bytes(pfat_hdr.PlatformID).upper().startswith(b'DELL') - - # Parse PFS Entry which contains sub-PFS Volume with PFAT Payload - if pfat_entry_hdr.Tag == b'PFS.HDR.' and is_pfat : - entry_type = 'PFAT' # Re-set PFS Entry Type from OTHER to PFAT, to use such info afterwards - - entry_data = parse_pfat_pfs(pfat_entry_hdr, entry_data, pfs_padd) # Parse sub-PFS PFAT Volume - - # Parse PFS Entry which contains zlib-compressed sub-PFS Volume - elif pfs_zlib_offsets : - entry_type = 'ZLIB' # Re-set PFS Entry Type from OTHER to ZLIB, to use such info afterwards - pfs_count += 1 # Increase the count/index of parsed main PFS structures by one - - # Parse each sub-PFS ZLIB Section - for offset in pfs_zlib_offsets : - # Get the Name of the zlib-compressed full PFS structure via the already stored PFS Information - # The zlib-compressed full PFS structure(s) are used to contain multiple FW (CombineBiosNameX) - # When zlib-compressed full PFS structure(s) exist within the main/first full PFS structure, - # its PFS Information should contain their names (CombineBiosNameX). Since the main/first - # full PFS structure has count/index 1, the rest start at 2+ and thus, their PFS Information - # names can be retrieved in order by subtracting 2 from the main/first PFS Information values - sub_pfs_name = ' %s v%s' % (info_all[pfs_count - 2][1], info_all[pfs_count - 2][2]) if info_all else ' UNKNOWN' - - # Set the sub-PFS output path (create sub-folders for each sub-PFS and its ZLIB sections) - sub_pfs_path = os.path.join(output_path, str(pfs_count) + sub_pfs_name) - - # Recursively call the PFS ZLIB Section Parser function for the sub-PFS Volume (pfs_index = pfs_count) - pfs_section_parse(entry_data, offset, sub_pfs_path, sub_pfs_name, pfs_count, pfs_count, True, pfs_padd + 4) - - entries_all[index][4] = entry_data # Adjust PFS Entry Data after parsing PFAT (same ZLIB raw data, not stored afterwards) - entries_all[index][3] = entry_type # Adjust PFS Entry Type from OTHER to PFAT or ZLIB (ZLIB is ignored at file extraction) - - # Name & Store each PFS Entry/Component Data, Data Signature, Metadata, Metadata Signature - for entry_index in range(len(entries_all)) : - file_index = entries_all[entry_index][0] - file_guid = entries_all[entry_index][1] - file_version = entries_all[entry_index][2] - file_type = entries_all[entry_index][3] - file_data = entries_all[entry_index][4] - file_data_sig = entries_all[entry_index][5] - file_meta = entries_all[entry_index][6] - file_meta_sig = entries_all[entry_index][7] - - # Give Names to special PFS Entries, not covered by PFS Information - if file_type == 'MODEL_INFO' : - file_name = 'Model Information' - elif file_type == 'NAME_INFO' : - file_name = 'Filename Information' - if not is_advanced : continue # Don't store Filename Information in non-advanced user mode - elif file_type == 'SIG_INFO' : - file_name = 'Signature Information' - if not is_advanced : continue # Don't store Signature Information in non-advanced user mode - else : - file_name = '' - - # Most PFS Entry Names & Versions are found at PFS Information via their GUID - # Version can be found at PFS_ENTRY_R* but prefer PFS Information when possible - for info_index in range(len(info_all)) : - info_guid = info_all[info_index][0] - info_name = info_all[info_index][1] - info_version = info_all[info_index][2] - - # Give proper Name & Version info if Entry/Information GUIDs match - if info_guid == file_guid : - file_name = info_name - file_version = info_version - - info_all[info_index][0] = 'USED' # PFS with zlib-compressed sub-PFS use the same GUID - break # Break at 1st Name match to not rename again from next zlib-compressed sub-PFS with the same GUID - - # For both advanced & non-advanced users, the goal is to store final/usable files only - # so empty or intermediate files such as sub-PFS, PFS w/ PFAT or zlib-PFS are skipped - # Main/First PFS CombineBiosNameX Metadata files must be kept for accurate Model Information - # All users should check these files in order to choose the correct CombineBiosNameX modules - write_files = [] # Initialize list of output PFS Entry files to be written/extracted - - is_zlib = bool(file_type == 'ZLIB') # Determine if PFS Entry Data was zlib-compressed - - if file_data and not is_zlib : write_files.append([file_data, 'data']) # PFS Entry Data Payload - if file_data_sig and is_advanced : write_files.append([file_data_sig, 'sign_data']) # PFS Entry Data Signature - if file_meta and (is_zlib or is_advanced) : write_files.append([file_meta, 'meta']) # PFS Entry Metadata Payload - if file_meta_sig and is_advanced : write_files.append([file_meta_sig, 'sign_meta']) # PFS Entry Metadata Signature - - # Write/Extract PFS Entry files - for file in write_files : - pfs_file_write(file[0], file[1], file_type, output_path, pfs_padd, pfs_index, pfs_name, file_index, file_name, file_version, output_path) - - # Get PFS Footer Data after PFS Header Payload - pfs_footer = buffer[dpfs_hdr_size + pfs_hdr.PayloadSize:dpfs_hdr_size + pfs_hdr.PayloadSize + dpfs_ftr_size] - - # Analyze PFS Footer Structure - chk_pfs_ftr(pfs_footer, pfs_payload, pfs_hdr.PayloadSize, 'PFS', pfs_padd) - -# Analyze Dell PFS Entry Structure -def parse_pfs_entry(entry_buffer, entry_start, entry_size, entry_struct, struct_args, text, padd) : - # Get PFS Entry Structure values - pfs_entry = get_struct(entry_buffer, entry_start, entry_struct, struct_args, padd + 4) - - # Show PFS Entry Structure info - if is_verbose : pfs_entry.pfs_print(padd + 8) - - # Validate that a known PFS Entry Header Version was encountered - chk_hdr_ver(pfs_entry.HeaderVersion, text, padd + 8) - - # Validate that the PFS Entry Reserved field is empty - if pfs_entry.Reserved != 0 : - msg_print(padd + 8, 'Error: Detected non-empty %s Reserved field!' % text) - - # Get PFS Entry Version string via "Version" and "VersionType" fields - entry_version = get_entry_ver(pfs_entry.Version, pfs_entry.VersionType, padd + 8) - - # Get PFS Entry GUID in Big Endian format - entry_guid = '%0.*X' % (0x10 * 2, int.from_bytes(pfs_entry.GUID, 'little')) - - # PFS Entry Data starts after the PFS Entry Structure - entry_data_start = entry_start + entry_size - entry_data_end = entry_data_start + pfs_entry.DataSize - - # PFS Entry Data Signature starts after PFS Entry Data - entry_data_sig_start = entry_data_end - entry_data_sig_end = entry_data_sig_start + pfs_entry.DataSigSize - - # PFS Entry Metadata starts after PFS Entry Data Signature - entry_met_start = entry_data_sig_end - entry_met_end = entry_met_start + pfs_entry.DataMetSize - - # PFS Entry Metadata Signature starts after PFS Entry Metadata - entry_met_sig_start = entry_met_end - entry_met_sig_end = entry_met_sig_start + pfs_entry.DataMetSigSize - - entry_data = entry_buffer[entry_data_start:entry_data_end] # Store PFS Entry Data - entry_data_sig = entry_buffer[entry_data_sig_start:entry_data_sig_end] # Store PFS Entry Data Signature - entry_met = entry_buffer[entry_met_start:entry_met_end] # Store PFS Entry Metadata - entry_met_sig = entry_buffer[entry_met_sig_start:entry_met_sig_end] # Store PFS Entry Metadata Signature - - return pfs_entry, entry_version, entry_guid, entry_data, entry_data_sig, entry_met, entry_met_sig, entry_met_sig_end - -# Parse Dell PFS Volume with PFAT Payload -def parse_pfat_pfs(entry_hdr, entry_data, padd) : - if is_verbose : print('\n%sPFS Volume:' % (' ' * (padd + 4))) - - # Show sub-PFS Header Structure Info - if is_verbose : entry_hdr.pfs_print(padd + 12) - - # Validate that a known sub-PFS Header Version was encountered - chk_hdr_ver(entry_hdr.HeaderVersion, 'sub-PFS', padd + 12) - - # Get sub-PFS Payload Data - pfat_payload = entry_data[dpfs_hdr_size:dpfs_hdr_size + entry_hdr.PayloadSize] - - # Get sub-PFS Footer Data after sub-PFS Header Payload (must be retrieved at the initial entry_data, before PFAT parsing) - pfat_footer = entry_data[dpfs_hdr_size + entry_hdr.PayloadSize:dpfs_hdr_size + entry_hdr.PayloadSize + dpfs_ftr_size] - - # Parse all sub-PFS Payload PFAT Entries - pfat_data_all = [] # Storage for all sub-PFS PFAT Entries Order/Offset & Payload/Raw Data - pfat_entry_start = 0 # Increasing sub-PFS PFAT Entry start offset - pfat_entry_index = 0 # Increasing sub-PFS PFAT Entry count index - _, pfs_entry_size = get_pfs_entry(pfat_payload, 0) # Get initial PFS PFAT Entry Size for loop - while len(pfat_payload[pfat_entry_start:pfat_entry_start + pfs_entry_size]) == pfs_entry_size : - # Get sub-PFS PFAT Entry Structure & Size info - pfat_entry_struct, pfat_entry_size = get_pfs_entry(pfat_payload, pfat_entry_start) - - # Analyze sub-PFS PFAT Entry Structure and get relevant info - pfat_entry,pfat_entry_version,pfat_entry_guid,pfat_entry_data,pfat_entry_data_sig,pfat_entry_met,pfat_entry_met_sig,pfat_next_entry = \ - parse_pfs_entry(pfat_payload, pfat_entry_start, pfat_entry_size, pfat_entry_struct, None, 'sub-PFS PFAT Entry', padd + 4) - - # Each sub-PFS PFAT Entry includes an AMI BIOS Guard (a.k.a. PFAT) block at the beginning - # We need to parse the PFAT block and remove its contents from the final Payload/Raw Data - pfat_hdr_off = pfat_entry_start + pfat_entry_size # PFAT block starts after PFS Entry - - # Get sub-PFS PFAT Header Structure values - pfat_hdr = get_struct(pfat_payload, pfat_hdr_off, PFS_PFAT_HDR, [pfat_entry_index], padd + 12) - - # Show sub-PFS PFAT Header Structure info - if is_verbose : pfat_hdr.pfs_print(padd + 16) - - # Get PFAT Header Flags (SFAM, ProtectEC, GFXMitDis, FTU, Reserved) - pfat_flag_sig,_,_,_,_ = pfat_hdr.get_flags() - - pfat_script_start = pfat_hdr_off + pfat_hdr_size # PFAT Block Script Start - pfat_script_end = pfat_script_start + pfat_hdr.ScriptSize # PFAT Block Script End - pfat_script_data = pfat_payload[pfat_script_start:pfat_script_end] # PFAT Block Script Data - pfat_payload_start = pfat_script_end # PFAT Block Payload Start (at Script end) - pfat_payload_end = pfat_script_end + pfat_hdr.DataSize # PFAT Block Data End - pfat_payload_data = pfat_payload[pfat_payload_start:pfat_payload_end] # PFAT Block Raw Data - pfat_hdr_bgs_size = pfat_hdr_size + pfat_hdr.ScriptSize # PFAT Block Header & Script Size - - # The PFAT Script End should match the total Entry Data Size w/o PFAT block - if pfat_hdr_bgs_size != pfat_entry.DataSize - pfat_hdr.DataSize : - msg_print(padd + 16, 'Error: Detected sub-PFS PFAT Entry Header & PFAT Size mismatch!') - - # Parse sub-PFS PFAT Signature, if applicable (only when PFAT Header > SFAM flag is set) - if pfat_flag_sig and len(pfat_payload[pfat_payload_end:pfat_payload_end + pfat_sig_size]) == pfat_sig_size : - # Get sub-PFS PFAT Signature Structure values - pfat_sig = get_struct(pfat_payload, pfat_payload_end, PFS_PFAT_SIG, [pfat_entry_index], padd + 12) - - # Show sub-PFS PFAT Signature Structure info - if is_verbose : pfat_sig.pfs_print(padd + 16) - - # Show PFAT Script via BIOS Guard Script Tool - # https://github.com/allowitsme/big-tool by Dmitry Frolov - if is_verbose : - print('\n%sPFAT Block %d Script:\n' % (' ' * (padd + 12), pfat_entry_index)) - is_opcode_div = len(pfat_script_data) % 8 == 0 - is_begin_end = pfat_script_data[:8] + pfat_script_data[-8:] == b'\x01' + b'\x00' * 7 + b'\xFF' + b'\x00' * 7 - if is_opcode_div and is_begin_end and is_bgst : - pfat_script_decomp = BigScript(code_bytes=pfat_script_data) - pfat_script_lines = pfat_script_decomp.to_string().replace('\t',' ').split('\n') - for line in pfat_script_lines : - spacing = ' ' * (padd + 16) if line.endswith(('begin','end',':')) else ' ' * (padd + 24) - operands = [op for op in line.split(' ') if op != ''] - print(spacing + ('{:<12s}' + '{:<11s}' * (len(operands) - 1)).format(*operands)) - elif not is_opcode_div : - print('%sError: Script not divisible by OpCode length!' % (' ' * (padd + 16))) - elif not is_begin_end : - print('%sError: Script lacks Begin and/or End OpCodes!' % (' ' * (padd + 16))) - elif not is_bgst : - print('%sError: BIOS Guard Script Tool dependency missing!' % (' ' * (padd + 16))) - - # The payload of sub-PFS PFAT Entries is not in proper order by default - # We can get each payload's order from PFAT Script > OpCode #2 (set I0 imm) - # PFAT Script OpCode #2 > Operand #3 stores the payload Offset in final image - pfat_entry_off = int.from_bytes(pfat_script_data[0xC:0x10], 'little') - - # Parse sub-PFS PFAT Entry/Block Metadata - if len(pfat_entry_met) >= pfat_met_size : - # Get sub-PFS PFAT Metadata Structure values - pfat_met = get_struct(pfat_entry_met, 0, PFS_PFAT_MET, [pfat_entry_index], padd + 12) - - # Show sub-PFS PFAT Metadata Structure info - if is_verbose : pfat_met.pfs_print(padd + 16) - - # Another way to get each PFAT Entry payload's Order is from its Metadata at 0x8-0xC, if applicable - # Check that the PFAT Entry payload Order/Offset from PFAT Script matches the one from PFAT Metadata - if pfat_entry_off != pfat_met.OffsetBase : - msg_print(padd + 16, 'Error: Detected sub-PFS PFAT Entry Metadata & PFAT Base Offset mismatch!') - pfat_entry_off = pfat_met.OffsetBase # Prefer Offset from Metadata, in case PFAT Script differs - - # Check that the PFAT Entry payload Size from PFAT Header matches the one from PFAT Metadata - if pfat_hdr.DataSize != pfat_met.BlockSize : - msg_print(padd + 16, 'Error: Detected sub-PFS PFAT Entry Metadata & PFAT Block Size mismatch!') - - # Get sub-PFS Entry Raw Data by subtracting PFAT Header & Script from PFAT Entry Data - pfat_entry_data_raw = pfat_entry_data[pfat_hdr_bgs_size:] - - # The sub-PFS Entry Raw Data (w/o PFAT Header & Script) should match with the PFAT Block payload - if pfat_entry_data_raw != pfat_payload_data : - msg_print(padd + 16, 'Error: Detected sub-PFS PFAT Entry w/o PFAT & PFAT Block Data mismatch!') - pfat_entry_data_raw = pfat_payload_data # Prefer Data from PFAT Block, in case PFAT Entry differs - - # Store each sub-PFS PFAT Entry Order/Offset and Payload/Raw Data (w/o PFAT) - pfat_data_all.append((pfat_entry_off, pfat_entry_data_raw)) - - pfat_entry_start = pfat_next_entry # Next sub-PFS PFAT Entry starts after sub-PFS Entry Metadata Signature - - pfat_entry_index += 1 - - pfat_data_all.sort() # Sort all sub-PFS PFAT Entries payloads/data based on their Order/Offset - - entry_data = b'' # Initialize new sub-PFS Entry Data - for pfat_data in pfat_data_all : entry_data += pfat_data[1] # Merge all sub-PFS PFAT Entry Payload/Raw into the final sub-PFS Entry Data - - # Verify that the Order/Offset of the last PFAT Entry w/ its Size matches the final sub-PFS Entry Data Size - if len(entry_data) != pfat_data_all[-1][0] + len(pfat_data_all[-1][1]) : - msg_print(padd + 8, 'Error: Detected sub-PFS PFAT Entry Buffer & Last Offset Size mismatch!') - - # Analyze sub-PFS Footer Structure - chk_pfs_ftr(pfat_footer, pfat_payload, entry_hdr.PayloadSize, 'Sub-PFS', padd + 4) - - return entry_data - -# Get Dell PFS Entry Structure & Size via its Version -def get_pfs_entry(buffer, offset) : - pfs_entry_ver = int.from_bytes(buffer[offset + 0x10:offset + 0x14], 'little') # PFS Entry Version - - if pfs_entry_ver == 1 : return PFS_ENTRY_R1, ctypes.sizeof(PFS_ENTRY_R1) - if pfs_entry_ver == 2 : return PFS_ENTRY_R2, ctypes.sizeof(PFS_ENTRY_R2) - - return PFS_ENTRY_R2, ctypes.sizeof(PFS_ENTRY_R2) - -# Determine Dell PFS Entry Version string -def get_entry_ver(version_fields, version_types, msg_padd) : - version = '' # Initialize Version string - - # Each Version Type (1 byte) determines the type of each Version Value (2 bytes) - # Version Type 'N' is Number, 'A' is Text and ' ' is Empty/Unused - for idx in range(len(version_fields)) : - eol = '' if idx == len(version_fields) - 1 else '.' - - if version_types[idx] == 65 : version += '%X%s' % (version_fields[idx], eol) # 0x41 = ASCII - elif version_types[idx] == 78 : version += '%d%s' % (version_fields[idx], eol) # 0x4E = Number - elif version_types[idx] in (0, 32) : version = version.strip('.') # 0x00 or 0x20 = Unused - else : - version += '%X%s' % (version_fields[idx], eol) # Unknown - msg_print(msg_padd, 'Error: Unknown PFS Entry Version Type 0x%0.2X!' % version_types[idx]) - - return version - -# Check if Dell PFS Header Version is known -def chk_hdr_ver(version, text, padd) : - if version in (1,2) : return - - msg_print(padd, 'Error: Unknown %s Header Version %d!' % (text, version)) - -# Analyze Dell PFS Footer Structure -def chk_pfs_ftr(footer_buffer, data_buffer, data_size, text, padd) : - # Get PFS Footer Structure values - pfs_ftr = get_struct(footer_buffer, 0, PFS_DELL_FTR, None, padd + 8) - - # Validate that a PFS Footer was parsed - if pfs_ftr.Tag == b'PFS.FTR.' : - # Show PFS Footer Structure info - if is_verbose : pfs_ftr.pfs_print(padd + 8) - else : - msg_print(padd + 4, 'Error: %s Footer could not be found!' % text) - - # Validate that PFS Header Payload Size matches the one at PFS Footer - if data_size != pfs_ftr.PayloadSize : - msg_print(padd + 4, 'Error: %s Header & Footer Payload Size mismatch!' % text) - - # Calculate the PFS Payload Data CRC-32 w/ Vector 0 - pfs_ftr_crc = ~zlib.crc32(data_buffer, 0) & 0xFFFFFFFF - - # Validate PFS Payload Data Checksum via PFS Footer - if pfs_ftr.Checksum != pfs_ftr_crc : - msg_print(padd + 4, 'Error: Invalid %s Footer Payload Checksum!' % text) - -# Write/Extract Dell PFS Entry Files (Data, Metadata, Signature) -def pfs_file_write(bin_buff, bin_name, bin_type, out_path, padd, pfs_idx, pfs_name, file_idx, file_name, file_ver, output_path) : - full_name = '%d%s -- %d %s v%s' % (pfs_idx, pfs_name, file_idx, file_name, file_ver) # Full PFS Entry Name - safe_name = re.sub(win_char_bad, '_', full_name) # Replace common Windows reserved/illegal filename characters - - # Store Data/Metadata Signature (advanced users only) - if bin_name.startswith('sign') : - final_name = '%s.%s.sig' % (safe_name, bin_name.split('_')[1]) - final_path = os.path.join(output_path, final_name) - - with open(final_path, 'wb') as pfs_out : pfs_out.write(bin_buff) # Write final Data/Metadata Signature - - return # Skip further processing for Signatures - - # Store Data/Metadata Payload - bin_ext = '.%s.bin' % bin_name if is_advanced else '.bin' # Simpler Data/Metadata Extension for non-advanced users - - # Some Data may be Text or XML files with useful information for non-advanced users - is_text,final_data,file_ext,write_mode = bin_is_text(bin_buff, bin_type, bin_name == 'meta', is_advanced, is_verbose, padd) - - final_name = '%s%s' % (safe_name, bin_ext[:-4] + file_ext if is_text else bin_ext) - final_path = os.path.join(out_path, final_name) - - with open(final_path, write_mode) as pfs_out : pfs_out.write(final_data) # Write final Data/Metadata Payload - -# Check if Dell PFS Entry file/data is Text/XML and Convert -def bin_is_text(buffer, file_type, is_metadata, is_advanced, is_verbose, pfs_padd) : - is_text = False - write_mode = 'wb' - extension = '.bin' - buffer_in = buffer - - if b',END' in buffer[-0x8:] : # Text Type 1 - is_text = True - write_mode = 'w' - extension = '.txt' - buffer = buffer.decode('utf-8').split(',END')[0].replace(';','\n') - elif buffer.startswith(b'VendorName=Dell') : # Text Type 2 - is_text = True - write_mode = 'w' - extension = '.txt' - buffer = buffer.split(b'\x00')[0].decode('utf-8').replace(';','\n') - elif b'|]' - -# Initialize Dell PFS input file list -pfs_input_images = [] - -# Process input files -if len(sys.argv) >= 2 : - # Drag & Drop or CLI - if args.input_dir : - input_path_user = get_absolute_path(args.input_dir) - pfs_input_images = get_path_files(input_path_user) - else : - pfs_input_images = [image.name for image in args.images] -else : - # Script w/o parameters - input_path_user = get_absolute_path(input('\nEnter input directory path: ')) - pfs_input_images = get_path_files(input_path_user) - -# Initialize global variables -exit_code = len(pfs_input_images) # Initialize exit code with input file count -is_advanced = bool(args.advanced) # Set Advanced user mode optional argument -is_verbose = bool(args.verbose) # Set Verbose output mode optional argument - -# Initialize Dell PFS Update Extractor -if __name__ == '__main__': - sys.exit(main(exit_code, pfs_input_images)) \ No newline at end of file diff --git a/Fujitsu SFX BIOS Extractor/Fujitsu_SFX_Extract.py b/Fujitsu SFX BIOS Extractor/Fujitsu_SFX_Extract.py deleted file mode 100644 index 4cc657f..0000000 --- a/Fujitsu SFX BIOS Extractor/Fujitsu_SFX_Extract.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python3 - -""" -Fujitsu SFX Extractor -Fujitsu SFX BIOS Extractor -Copyright (C) 2019-2021 Plato Mavropoulos -""" - -print('Fujitsu SFX BIOS Extractor v2.1') - -import os -import re -import sys -import subprocess - -if len(sys.argv) >= 2 : - # Drag & Drop or CLI - fjsfx_exec = sys.argv[1:] -else : - # Folder path - fjsfx_exec = [] - in_path = input('\nEnter the full folder path: ') - print('\nWorking...') - for root, dirs, files in os.walk(in_path): - for name in files : - fjsfx_exec.append(os.path.join(root, name)) - -# "FjSfxBinay" + Microsoft CAB Header XOR 0xFF (Tag[4] + Res[4] + Size[4] + Res[4] + Offset[4] + Res[4] + Ver[2]) pattern -mscf_pattern = re.compile(br'\x46\x6A\x53\x66\x78\x42\x69\x6E\x61\x79\xB2\xAC\xBC\xB9\xFF{4}.{4}\xFF{4}.{4}\xFF{4}\xFC\xFE', re.DOTALL) - -for input_file in fjsfx_exec : - file_path = os.path.abspath(input_file) - file_dir = os.path.dirname(file_path) - file_name = os.path.basename(file_path) - - print('\nFile: ' + file_name) - - # Open Fujitsu SFX Binary Packager executable as mutable bytearray - with open(input_file, 'rb') as in_file : FjSfx = bytearray(in_file.read()) - - match_mscf = mscf_pattern.search(FjSfx) # Search for Fujitsu Microsoft CAB Header XOR 0xFF pattern - - # Check if Microsoft CAB Header XOR 0xFF pattern exists - if match_mscf : - print('\n Detected Obfuscation!') - - mscf_start = match_mscf.start() + 0xA # Microsoft CAB Header XOR 0xFF starts after "FjSfxBinay" signature - - # Determine the Microsoft CAB image Size - cab_size = int.from_bytes(FjSfx[mscf_start + 0x8:mscf_start + 0xC], 'little') # Get LE XOR-ed CAB Size - xor_size = int.from_bytes(b'\xFF' * 0x4, 'little') # Create CAB Size XOR value - cab_size = cab_size ^ xor_size # Perform XOR 0xFF and get actual CAB Size - - print('\n Removing Obfuscation...') - - # Determine the Microsoft CAB image Data - cab_data = int.from_bytes(FjSfx[mscf_start:mscf_start + cab_size], 'big') # Get BE XOR-ed CAB Data - xor_data = int.from_bytes(b'\xFF' * cab_size, 'big') # Create CAB Data XOR value - cab_data = (cab_data ^ xor_data).to_bytes(cab_size, 'big') # Perform XOR 0xFF and get actual CAB Data - - print('\n Extracting...') - - with open('fjsfx_temp.cab', 'wb') as cab_file : cab_file.write(cab_data) # Create temporary CAB image - - extr_path = os.path.join(file_dir, file_name[:-4], '') # Create CAB image extraction path - - try : - decomp = subprocess.run(['7z', 'x', '-aou', '-bso0', '-bse0', '-bsp0', '-o' + extr_path, 'fjsfx_temp.cab']) # 7-Zip - - print('\n Extracted!') - except : - print('\n Error: Could not decompress Microsoft CAB image!') - print(' Make sure that "7z" executable exists!') - - os.remove('fjsfx_temp.cab') # Remove temporary CAB image - - else : - print('\n Error: This is not a Fujitsu SFX BIOS image!') - continue # Next input file - -else : - input('\nDone!') \ No newline at end of file diff --git a/Fujitsu UPC BIOS Extractor/Fujitsu_UPC_Extract.py b/Fujitsu UPC BIOS Extractor/Fujitsu_UPC_Extract.py deleted file mode 100644 index 6bb9993..0000000 --- a/Fujitsu UPC BIOS Extractor/Fujitsu_UPC_Extract.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env python3 -#coding=utf-8 - -""" -Fujitsu UPC Extract -Fujitsu UPC BIOS Extractor -Copyright (C) 2021 Plato Mavropoulos -""" - -title = 'Fujitsu UPC BIOS Extractor v1.0' - -print('\n' + title) # Print script title - -import sys - -# Detect Python version -sys_ver = sys.version_info -if sys_ver < (3,7) : - sys.stdout.write('\n\nError: Python >= 3.7 required, not %d.%d!\n' % (sys_ver[0], sys_ver[1])) - (raw_input if sys_ver[0] <= 2 else input)('\nPress enter to exit') # pylint: disable=E0602 - sys.exit(1) - -import os -import ctypes -import argparse -import traceback -import subprocess - -# Pause after any unexpected Python exception -# 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('\nError: %s crashed, please report the following:\n' % title) - traceback.print_exception(exc_type, exc_value, tb) - input('\nPress enter to exit') - - sys.exit(1) - -# Set pause-able Python exception handler -sys.excepthook = show_exception_and_exit - -# Set console/shell window title -user_os = sys.platform -if user_os == 'win32' : ctypes.windll.kernel32.SetConsoleTitleW(title) -elif user_os.startswith('linux') or user_os == 'darwin' or user_os.find('bsd') != -1 : sys.stdout.write('\x1b]2;' + title + '\x07') - -# Set argparse Arguments -upc_parser = argparse.ArgumentParser() -upc_parser.add_argument('upc', type=argparse.FileType('r'), nargs='*') -upc_parser.add_argument('-p', '--path', help='parse files within given folder', type=str) -upc_params = upc_parser.parse_args() - -# Get all files within path -def get_files(path) : - inputs = [] - - for root, _, files in os.walk(path): - for name in files : - inputs.append(os.path.join(root, name)) - - return inputs - -if len(sys.argv) >= 2 : - if bool(upc_params.path) : - upc_exec = get_files(upc_params.path) # CLI with --path - else : - upc_exec = [] - for executable in upc_params.upc : - upc_exec.append(executable.name) # Drag & Drop -else : - in_path = input('\nEnter the full folder path: ') - upc_exec = get_files(in_path) # Direct Run - -# Process each input Fujitsu UPC BIOS image -for input_file in upc_exec : - input_name,input_extension = os.path.splitext(os.path.basename(input_file)) - - print('\n*** %s%s' % (input_name, input_extension)) - - # Check if input file exists - if not os.path.isfile(input_file) : - print('\n Error: This input file does not exist!') - continue # Next input file - - with open(input_file, 'rb') as in_file : upc_data = in_file.read() - - if input_extension.upper() != '.UPC' or int.from_bytes(upc_data[0x0:0x4], 'little') + 0x8 != len(upc_data) : - print('\n Error: This is not a Fujitsu UPC BIOS image!') - continue # Next input file - - output_file = input_file[:-4] + '.bin' # Decompressed filename - - # EFI/Tiano Decompression - try : - subprocess.run(['TianoCompress', '-d', input_file, '-o', output_file, '--uefi', '-q'], check = True, stdout = subprocess.DEVNULL) - - if os.path.getsize(output_file) != int.from_bytes(upc_data[0x4:0x8], 'little') : raise Exception('EFI_DECOMP_ERROR') - except : - print('\n Error: Could not extract input file via TianoCompress!') - input(' Make sure that "TianoCompress" executable exists!') - - print('\n Extracted Fujitsu UPC BIOS image!') - -input('\nDone!') - -sys.exit(0) \ No newline at end of file diff --git a/Insyde iFlash Image Extractor/Insyde_iFlash_Extract.py b/Insyde iFlash Image Extractor/Insyde_iFlash_Extract.py deleted file mode 100644 index d8ca64c..0000000 --- a/Insyde iFlash Image Extractor/Insyde_iFlash_Extract.py +++ /dev/null @@ -1,270 +0,0 @@ -#!/usr/bin/env python3 -#coding=utf-8 - -""" -Insyde iFlash Extract -Insyde iFlash Image Extractor -Copyright (C) 2022 Plato Mavropoulos -""" - -title = 'Insyde iFlash Image Extractor v1.0' - -import sys - -# Detect Python version -sys_py = sys.version_info - -# Check Python version -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 '--auto-exit' not in sys.argv and '-e' 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 - -# Check OS platform -if sys_os == 'win32': - 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: - pass # Supported/Tested -else: - print('%s\n\nError: Unsupported platform "%s"!\n' % (title, sys_os)) - - if '--auto-exit' not in sys.argv and '-e' not in sys.argv: input('Press enter to exit') - - sys.exit(1) - -# Python imports -import os -import re -import ctypes -import inspect -import pathlib -import argparse -import traceback - -# Set ctypes Structure types -char = ctypes.c_char -uint32_t = ctypes.c_uint - -class IflashHeader(ctypes.LittleEndianStructure): - _pack_ = 1 - _fields_ = [ - ('Signature', char*9), # 0x00 $_IFLASH_ - ('ImageTag', char*7), # 0x08 - ('TotalSize', uint32_t), # 0x10 from header end - ('ImageSize', uint32_t), # 0x14 from header end - # 0x18 - ] - - def ifl_print(self, padd): - p = ' ' * (padd - 1) - - print(p, 'Signature : %s' % self.Signature.decode('utf-8','ignore')) - print(p, 'Image Name: %s' % self.ImageTag.decode('utf-8','ignore')) - print(p, 'Total Size: 0x%X' % self.TotalSize) - print(p, 'Image Size: 0x%X' % self.ImageSize) - -class InsydeIflash: - def __init__(self, in_data, out_path, in_padd, in_verbose): - self.fw_data = in_data - self.ex_path = out_path - self.padding = in_padd - self.verbose = in_verbose - - self.hdr_len = ctypes.sizeof(IflashHeader) - - self.mod_names = { - 'DRV_IMG':['isflash','efi'], - 'INI_IMG':['platform','ini'], - 'BIOSIMG':['BIOS','bin'], - 'ME_IMG_':['ME','bin'], - 'EC_IMG_':['EC','bin'], - 'OEM_ID_':['OEM_ID','bin'], - 'BIOSCER':['Certificate','bin'], - 'BIOSCR2':['Certificate_2','bin'], - } - - def iflash_parse(self): - all_ins_ifl = pat_ins_ifl.finditer(self.fw_data) - - if not all_ins_ifl: return 1 - - if not os.path.isdir(self.ex_path): os.mkdir(self.ex_path) - - for ins_ifl in all_ins_ifl: - ifl_off = ins_ifl.start() - - ifl_hdr = get_struct(self.fw_data, ifl_off, IflashHeader) - - if self.verbose: - print('\n%sInsyde iFlash Module @ 0x%0.8X\n' % (' ' * self.padding, ifl_off)) - - ifl_hdr.ifl_print(self.padding + 4) - - mod_bgn = ifl_off + self.hdr_len - mod_end = mod_bgn + ifl_hdr.ImageSize - mod_bin = self.fw_data[mod_bgn:mod_end] - - if not mod_bin: continue # Empty/Missing Module - - mod_tag = ifl_hdr.ImageTag.decode('utf-8','ignore') - out_tag = self.mod_names[mod_tag][0] if mod_tag in self.mod_names else mod_tag - out_ext = self.mod_names[mod_tag][1] if mod_tag in self.mod_names else 'bin' - - out_name = get_safe_name('%s [0x%0.8X-0x%0.8X].%s' % (out_tag, mod_bgn, mod_end, out_ext)) - out_path = os.path.join(self.ex_path, out_name) - - with open(out_path, 'wb') as out: out.write(mod_bin) - - print('\n%sExtracted' % (' ' * (self.padding + 8 if self.verbose else self.padding)), out_name) - - return 0 - -# Process ctypes Structure Classes -# https://github.com/skochinsky/me-tools/blob/master/me_unpack.py by Igor Skochinsky -def get_struct(buffer, 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 = buffer[start_offset:start_offset + struct_len] - fit_len = min(len(struct_data), struct_len) - - ctypes.memmove(ctypes.addressof(structure), struct_data, fit_len) - - return structure - -# Get absolute file path (argparse object) -def get_absolute_path(argparse_path): - if not argparse_path: - absolute_path = get_script_dir() # Use input file directory if no user path is specified - else: - # Check if user specified path is absolute, otherwise convert it to input file relative - if pathlib.Path(argparse_path).is_absolute(): absolute_path = argparse_path - else: absolute_path = os.path.join(get_script_dir(), argparse_path) - - return absolute_path - -# Get list of files from absolute path -def get_path_files(abs_path): - file_list = [] # Initialize list of files - - # Traverse input absolute path - for root,_,files in os.walk(abs_path): - file_list = [os.path.join(root, name) for name in files] - - return file_list - -# Fix illegal/reserved Windows characters -def get_safe_name(file_name): - raw_name = repr(file_name).strip("'") - - return re.sub(r'[\\/*?:"<>|]', '_', raw_name) - -# Get python script working directory -# 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) - -# Pause after any unexpected Python exception -# 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('\nError: %s crashed, please report the following:\n' % title) - traceback.print_exception(exc_type, exc_value, tb) - if not bool(args.auto_exit): input('\nPress enter to exit') - - sys.exit(1) # Crash exceptions are critical - -# Insyde iFlash Section Signature -pat_ins_ifl = re.compile(br'\$_IFLASH_') - -if __name__ == '__main__': - # Show script title - print('\n' + title) - - # Set console/shell window title - user_os = sys.platform - if user_os == 'win32': ctypes.windll.kernel32.SetConsoleTitleW(title) - elif user_os.startswith('linux') or user_os == 'darwin' or user_os.find('bsd') != -1: sys.stdout.write('\x1b]2;' + title + '\x07') - - # Set argparse Arguments - parser = argparse.ArgumentParser() - parser.add_argument('images', type=argparse.FileType('r'), nargs='*') - parser.add_argument('-v', '--verbose', help='show iFlash structure information', action='store_true') - parser.add_argument('-e', '--auto-exit', help='skip press enter to exit prompts', action='store_true') - parser.add_argument('-o', '--output-dir', help='extract in given output directory') - parser.add_argument('-i', '--input-dir', help='extract from given input directory') - args = parser.parse_args() - - # Set pause-able Python exception handler (must be after args) - sys.excepthook = show_exception_and_exit - - # Initialize Dell PFS input file list - iflash_input_images = [] - - # Process input files - if len(sys.argv) >= 2: - # Drag & Drop or CLI - if args.input_dir: - input_path_user = get_absolute_path(args.input_dir) - iflash_input_images = get_path_files(input_path_user) - else: - iflash_input_images = [image.name for image in args.images] - - output_path_user = get_absolute_path(args.output_dir or args.input_dir) - else: - # Script w/o parameters - input_path_user = get_absolute_path(input('\nEnter input directory path: ')) - iflash_input_images = get_path_files(input_path_user) - - output_path_user = get_absolute_path(input('\nEnter output directory path: ')) - - # Initialize global variables - exit_code = len(iflash_input_images) # Initialize exit code with input file count - is_verbose = bool(args.verbose) # Set Verbose output mode optional argument - - for input_file in iflash_input_images: - input_name = os.path.basename(input_file) - input_padd = 8 - - print('\n*** %s' % input_name) - - # Check if input file exists - if not os.path.isfile(input_file): - print('\n%sError: This input file does not exist!' % (' ' * input_padd)) - continue # Next input file - - with open(input_file, 'rb') as in_file: input_data = in_file.read() - - # Search input image for Insyde iFlash Sections - is_ins_ifl = pat_ins_ifl.search(input_data) - - if not is_ins_ifl: - print('\n%sError: This is not an Insyde iFlash image!' % (' ' * input_padd)) - continue # Next input file - - # Set main extraction path (optional user specified path taken into account) - output_path = os.path.join(output_path_user, input_name + '_extracted') - - InsydeIflash(input_data, output_path, input_padd, is_verbose).iflash_parse() - - exit_code -= 1 # Adjust exit code to reflect extraction progress - - if not bool(args.auto_exit): input('\nDone!') - - sys.exit(exit_code) diff --git a/Panasonic BIOS Update Extractor/Panasonic_BIOS_Extract.py b/Panasonic BIOS Update Extractor/Panasonic_BIOS_Extract.py deleted file mode 100644 index 414bad4..0000000 --- a/Panasonic BIOS Update Extractor/Panasonic_BIOS_Extract.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python3 - -""" -Panasonic BIOS Extract -Panasonic BIOS Update Extractor -Copyright (C) 2018 Plato Mavropoulos -""" - -print('Panasonic BIOS Update Extractor v1.0') - -import os -import sys -import shutil -import pefile -import subprocess - -if len(sys.argv) >= 2 : - # Drag & Drop or CLI - panasonic = sys.argv[1:] -else : - # Folder path - panasonic = [] - in_path = input('\nEnter the full folder path: ') - print('\nWorking...') - for root, dirs, files in os.walk(in_path): - for name in files : - panasonic.append(os.path.join(root, name)) - -for input_file in panasonic : - file_path = os.path.abspath(input_file) - file_name = os.path.basename(input_file) - file_dir = os.path.dirname(file_path) - file_ext = os.path.splitext(file_path)[1] - - # Create output folder - extr_path = os.path.join(os.getcwd(), 'RCDATA') - if os.path.exists(extr_path) : shutil.rmtree(extr_path) - os.makedirs(extr_path) - - max_size = 0 - max_file = None - pe = pefile.PE(input_file) # Analyze Portable Executable (PE) - for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries : - # Parse Resource Data directories only - if entry.struct.name == 'IMAGE_RESOURCE_DIRECTORY_ENTRY' and entry.struct.Id == 10 : # RCDATA ID = 10 - for resource in entry.directory.entries : - offset = resource.directory.entries[0].data.struct.OffsetToData - size = resource.directory.entries[0].data.struct.Size - data = pe.get_data(offset, size) - file = os.path.join(extr_path, '%X_%X.bin' % (offset, size)) - with open(file, 'wb') as out_file : out_file.write(data) - - # Remember largest resource (SPI/BIOS) - if size > max_size : - max_size = size - max_file = file - - if not max_file : - print('\nError: No Panasonic BIOS Update at %s!' % file_name) - shutil.rmtree(extr_path) # Remove temporary folder - continue # Next input file - - # Call Rustam Abdullaev's unpack_lznt1 to extract the LZNT1-compressed SPI/BIOS resource at 0x8 onwards - try : - subprocess.run(['unpack_lznt1', max_file, os.path.join(file_dir, file_name[:-4] + '.bin'), '8'], check = True, stdout = subprocess.DEVNULL) - print('\nExtracted %s via unpack_lznt1' % (file_name[:-4] + '.bin')) - except : - print('\nError: Could not extract %s via unpack_lznt1!' % (file_name[:-4] + '.bin')) - print(' Make sure that "unpack_lznt1.exe" executable exists!') - - shutil.rmtree(extr_path) # Remove temporary folder - -else : - input('\nDone!') \ No newline at end of file diff --git a/Phoenix SCT BIOS Extractor/Phoenix_SCT_Extract.py b/Phoenix SCT BIOS Extractor/Phoenix_SCT_Extract.py deleted file mode 100644 index 9d873d6..0000000 --- a/Phoenix SCT BIOS Extractor/Phoenix_SCT_Extract.py +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/env python3 -#coding=utf-8 - -""" -Phoenix SCT Extract -Phoenix SCT BIOS Extractor -Copyright (C) 2021 Plato Mavropoulos -""" - -title = 'Phoenix SCT BIOS Extractor v1.0' - -print('\n' + title) # Print script title - -import sys - -# Detect Python version -sys_ver = sys.version_info -if sys_ver < (3,7) : - sys.stdout.write('\n\nError: Python >= 3.7 required, not %d.%d!\n' % (sys_ver[0], sys_ver[1])) - (raw_input if sys_ver[0] <= 2 else input)('\nPress enter to exit') # pylint: disable=E0602 - sys.exit(1) - -import os -import re -import lzma -import shutil -import ctypes -import argparse -import traceback - -# Pause after any unexpected Python exception -# 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('\nError: %s crashed, please report the following:\n' % title) - traceback.print_exception(exc_type, exc_value, tb) - input('\nPress enter to exit') - - sys.exit(1) - -# Set pause-able Python exception handler -sys.excepthook = show_exception_and_exit - -# Set console/shell window title -user_os = sys.platform -if user_os == 'win32' : ctypes.windll.kernel32.SetConsoleTitleW(title) -elif user_os.startswith('linux') or user_os == 'darwin' or user_os.find('bsd') != -1 : sys.stdout.write('\x1b]2;' + title + '\x07') - -# Set argparse Arguments -sct_parser = argparse.ArgumentParser() -sct_parser.add_argument('executables', type=argparse.FileType('r'), nargs='*') -sct_parser.add_argument('-p', '--path', help='parse files within given folder', type=str) -sct_params = sct_parser.parse_args() - -# Get all files within path -def get_files(path) : - inputs = [] - - for root, _, files in os.walk(path): - for name in files : - inputs.append(os.path.join(root, name)) - - return inputs - -if len(sys.argv) >= 2 : - if bool(sct_params.path) : - sct_exec = get_files(sct_params.path) # CLI with --path - else : - sct_exec = [] - for executable in sct_params.executables : - sct_exec.append(executable.name) # Drag & Drop -else : - in_path = input('\nEnter the full folder path: ') - sct_exec = get_files(in_path) # Direct Run - -# 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 - -class SCT_HDR(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('Tag', char*8), # 0x00 - ('Size', uint32_t), # 0x08 - ('Count', uint32_t), # 0x0C - # 0x10 - ] - - def sct_print(self) : - print('\n Phoenix SCT Header:\n') - print(' Tag : %s' % self.Tag.decode('utf-8','replace').strip()) - print(' Size : 0x%X' % self.Size) - print(' Count : %d' % self.Count) - -class SCT_MOD(ctypes.LittleEndianStructure) : - _pack_ = 1 - _fields_ = [ - ('Name', char*256), # 0x000 - ('Offset', uint32_t), # 0x100 - ('Size', uint32_t), # 0x104 - ('Compressed', uint32_t), # 0x108 - ('Reserved', uint32_t), # 0x10C - # 0x110 - ] - - def sct_print(self) : - print('\n Phoenix SCT Entry:\n') - print(' Name : %s' % self.Name.decode('utf-8','replace').strip()) - print(' Offset : 0x%X' % self.Offset) - print(' Size : 0x%X' % self.Size) - print(' Compressed : %s' % ['No','Yes'][self.Compressed]) - print(' Reserved : 0x%X' % self.Reserved) - -# Process ctypes Structure Classes -# https://github.com/skochinsky/me-tools/blob/master/me_unpack.py by Igor Skochinsky -def get_struct(buffer, 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 = buffer[start_offset:start_offset + struct_len] - fit_len = min(len(struct_data), struct_len) - - if (start_offset >= len(buffer)) or (fit_len < struct_len) : - print('\n Error: Offset 0x%X out of bounds at %s, possibly incomplete image!' % (start_offset, class_name.__name__)) - - input('\n Press enter to exit') - - sys.exit(1) - - ctypes.memmove(ctypes.addressof(structure), struct_data, fit_len) - - return structure - -# Phoenix SCT BIOS Package Pattern ($PACK + Size + Count) -sct_pat = re.compile(br'\x24\x50\x41\x43\x4B\x00{3}..\x00{2}.\x00{3}', re.DOTALL) - -# Get common ctypes Structure Sizes -sct_hdr_len = ctypes.sizeof(SCT_HDR) -sct_mod_len = ctypes.sizeof(SCT_MOD) - -# Size of dummy/placeholder SCT Entries -sct_dummy_len = 0x200 # Top 2, Names only - -# Process each input Phoenix SCT BIOS executable -for input_file in sct_exec : - input_name,input_extension = os.path.splitext(os.path.basename(input_file)) - input_dir = os.path.dirname(os.path.abspath(input_file)) - - print('\n*** %s%s' % (input_name, input_extension)) - - # Check if input file exists - if not os.path.isfile(input_file) : - print('\n Error: This input file does not exist!') - continue # Next input file - - with open(input_file, 'rb') as in_file : input_data = in_file.read() - - sct_match = sct_pat.search(input_data) # Search for Phoenix SCT BIOS Pattern - - # Check if Phoenix SCT BIOS Pattern was found on executable - if not sct_match : - print('\n Error: This is not a Phoenix SCT BIOS executable!') - continue # Next input file - - output_path = os.path.join(input_dir, '%s%s' % (input_name, input_extension) + '_extracted') # Set extraction directory - - if os.path.isdir(output_path) : shutil.rmtree(output_path) # Delete any existing extraction directory - - os.mkdir(output_path) # Create extraction directory - - print('\n Phoenix SecureCore Technology') - - sct_hdr = get_struct(input_data, sct_match.start(), SCT_HDR) # Parse SCT Header Structure - sct_hdr.sct_print() # Print SCT Header Info - - # Check if reported SCT Header Size matches manual SCT Entry Count calculation - if sct_hdr.Size != sct_hdr_len + sct_dummy_len + sct_hdr.Count * sct_mod_len : - input('\n Error: This Phoenix SCT BIOS image is corrupted!') - continue # Next input file - - # Store all SCT $PACK Data w/o initial dummy/placeholder Entries - pack_data = input_data[sct_match.end() + sct_dummy_len:sct_match.start() + sct_hdr.Size] - - # Parse each SCT Entry - for e_idx in range(sct_hdr.Count) : - mod_hdr = get_struct(pack_data, e_idx * sct_mod_len, SCT_MOD) # Parse SCT Entry Structure - mod_hdr.sct_print() # Print SCT Entry Info - - mod_data = input_data[mod_hdr.Offset:mod_hdr.Offset + mod_hdr.Size] # Store SCT Entry Raw Data - - # Check if SCT Entry Raw Data is complete - if len(mod_data) != mod_hdr.Size : - input('\n Error: This Phoenix SCT BIOS image is incomplete!') - - # Store SCT Entry LZMA Decompressed Data, when applicable - if mod_hdr.Compressed : mod_data = lzma.LZMADecompressor().decompress(mod_data) - - # Replace common Windows reserved/illegal filename characters - mod_fname = re.sub(r'[\\/*?:"<>|]', '_', mod_hdr.Name.decode('utf-8','replace').strip()) - - with open(os.path.join(output_path, mod_fname), 'wb') as out : out.write(mod_data) # Store SCT Entry Data/File - - print('\n Extracted Phoenix SCT BIOS executable!') - -input('\nDone!') - -sys.exit(0) \ No newline at end of file diff --git a/Portwell EFI BIOS Extractor/Portwell_EFI_Extract.py b/Portwell EFI BIOS Extractor/Portwell_EFI_Extract.py deleted file mode 100644 index 7c939b7..0000000 --- a/Portwell EFI BIOS Extractor/Portwell_EFI_Extract.py +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env python3 -#coding=utf-8 - -""" -Portwell EFI Extract -Portwell EFI BIOS Extractor -Copyright (C) 2021 Plato Mavropoulos -""" - -title = 'Portwell EFI BIOS Extractor v1.0' - -print('\n' + title) # Print script title - -import sys - -# Detect Python version -sys_ver = sys.version_info -if sys_ver < (3,7) : - sys.stdout.write('\n\nError: Python >= 3.7 required, not %d.%d!\n' % (sys_ver[0], sys_ver[1])) - (raw_input if sys_ver[0] <= 2 else input)('\nPress enter to exit') # pylint: disable=E0602 - sys.exit(1) - -import os -import pefile -import shutil -import ctypes -import argparse -import traceback -import subprocess - -# Pause after any unexpected Python exception -# 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('\nError: %s crashed, please report the following:\n' % title) - traceback.print_exception(exc_type, exc_value, tb) - input('\nPress enter to exit') - - sys.exit(1) - -# Set pause-able Python exception handler -sys.excepthook = show_exception_and_exit - -# Set console/shell window title -user_os = sys.platform -if user_os == 'win32' : ctypes.windll.kernel32.SetConsoleTitleW(title) -elif user_os.startswith('linux') or user_os == 'darwin' or user_os.find('bsd') != -1 : sys.stdout.write('\x1b]2;' + title + '\x07') - -# Set argparse Arguments -efi_parser = argparse.ArgumentParser() -efi_parser.add_argument('efi', type=argparse.FileType('r'), nargs='*') -efi_parser.add_argument('-p', '--path', help='parse files within given folder', type=str) -efi_params = efi_parser.parse_args() - -# Get all files within path -def get_files(path) : - inputs = [] - - for root, _, files in os.walk(path): - for name in files : - inputs.append(os.path.join(root, name)) - - return inputs - -if len(sys.argv) >= 2 : - if bool(efi_params.path) : - efi_exec = get_files(efi_params.path) # CLI with --path - else : - efi_exec = [] - for executable in efi_params.efi : - efi_exec.append(executable.name) # Drag & Drop -else : - in_path = input('\nEnter the full folder path: ') - efi_exec = get_files(in_path) # Direct Run - -# Portwell UEFI Unpacker File Names (v1.1 - v1.2) -file_names = {0 : 'Flash.efi', 1 : 'Fparts.txt', 2 : 'Update.nsh', 3 : 'Temp.bin', 4 : 'SaveDmiData.efi'} - -# Process each input Portwell EFI Update Package -for input_file in efi_exec : - input_name,input_extension = os.path.splitext(os.path.basename(input_file)) - input_dir = os.path.dirname(os.path.abspath(input_file)) - - print('\n*** %s%s' % (input_name, input_extension)) - - # Check if input file exists - if not os.path.isfile(input_file) : - print('\n Error: This input file does not exist!') - continue # Next input file - - with open(input_file, 'rb') as in_file : input_data = in_file.read() - - try : - assert input_data[0x0:0x2] == b'\x4D\x5A' # EFI images start with DOS Header MZ - - pe = pefile.PE(input_file, fast_load=True) # Analyze EFI Portable Executable (PE) - - payload_data = input_data[pe.OPTIONAL_HEADER.SizeOfImage:] # Skip EFI executable (pylint: disable=E1101) - - assert payload_data[0x0:0x4] == b'\x3C\x55\x55\x3E' # Portwell EFI files start with - except : - print('\n Error: This is not a Portwell EFI Update Package!') - continue # Next input file - - output_path = os.path.join(input_dir, '%s%s' % (input_name, input_extension) + '_extracted') # Set extraction directory - - if os.path.isdir(output_path) : shutil.rmtree(output_path) # Delete any existing extraction directory - - os.mkdir(output_path) # Create extraction directory - - pack_tag = 'UEFI Unpacker' # Initialize Portwell UEFI Unpacker tag - - # Get Portwell UEFI Unpacker tag - for s in pe.sections : - if s.Name.startswith(b'.data') : # Unpacker Tag, Version, Strings etc are found in .data PE section - # Decode any valid UTF-16 .data PE section info to a parsable text buffer - info = input_data[s.PointerToRawData:s.PointerToRawData + s.SizeOfRawData].decode('utf-16','ignore') - - # Search .data for UEFI Unpacker tag - pack_tag_off = info.find('UEFI Unpacker') - if pack_tag_off != -1 : - pack_tag_len = info[pack_tag_off:].find('=') - if pack_tag_len != -1 : - # Found full UEFI Unpacker tag, store and slightly beautify the resulting text - pack_tag = info[pack_tag_off:pack_tag_off + pack_tag_len].strip().replace(' ',' ').replace('<',' <') - - break # Found PE .data section, skip the rest - - print('\n Portwell %s' % pack_tag) # Print Portwell UEFI Unpacker tag - - efi_files = payload_data.split(b'\x3C\x55\x55\x3E')[1:] # Split EFI Payload into file chunks - - # Parse each EFI Payload File - for i in range(len(efi_files)) : - file_data = efi_files[i] # Store EFI File data - - if len(file_data) == 0 or file_data == b'NULL' : continue # Skip empty/unused files - - is_known = i in file_names # Check if EFI file is known & Store result - - file_name = file_names[i] if is_known else 'Unknown_%d.bin' % i # Assign Name to EFI file - - print('\n %s' % file_name) # Print EFI file name, indicate progress - - if not is_known : input('\n Note: Detected unknown Portwell EFI file with ID %d!' % i) # Report new EFI files - - file_path = os.path.join(output_path, file_name) # Store EFI file output path - - with open(file_path, 'wb') as o : o.write(file_data) # Store EFI file data to drive - - # Attempt to detect EFI/Tiano Compression & Decompress when applicable - if int.from_bytes(file_data[0x0:0x4], 'little') + 0x8 == len(file_data) : - try : - comp_fname = file_path + '.temp' # Store temporary compressed file name - - os.replace(file_path, comp_fname) # Rename initial/compressed file - - subprocess.run(['TianoCompress', '-d', comp_fname, '-o', file_path, '--uefi', '-q'], check = True, stdout = subprocess.DEVNULL) - - if os.path.getsize(file_path) != int.from_bytes(file_data[0x4:0x8], 'little') : raise Exception('EFI_DECOMP_ERROR') - - os.remove(comp_fname) # Successful decompression, delete initial/compressed file - - except : - print('\n Error: Could not extract file %s via TianoCompress!' % file_name) - input(' Make sure that "TianoCompress" executable exists!') - - print('\n Extracted Portwell EFI Update Package!') - -input('\nDone!') - -sys.exit(0) \ No newline at end of file diff --git a/VAIO Packaging Manager Extractor/VAIO_Package_Extract.py b/VAIO Packaging Manager Extractor/VAIO_Package_Extract.py deleted file mode 100644 index 0d7758f..0000000 --- a/VAIO Packaging Manager Extractor/VAIO_Package_Extract.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env python3 - -""" -VAIO Package Extractor -VAIO Packaging Manager Extractor -Copyright (C) 2019-2020 Plato Mavropoulos -""" - -print('VAIO Packaging Manager Extractor v2.0') - -import os -import re -import sys -import subprocess - -if len(sys.argv) >= 2 : - # Drag & Drop or CLI - vaio_exec = sys.argv[1:] -else : - # Folder path - vaio_exec = [] - in_path = input('\nEnter the full folder path: ') - print('\nWorking...') - for root, dirs, files in os.walk(in_path): - for name in files : - vaio_exec.append(os.path.join(root, name)) - -# Microsoft CAB Header XOR 0xFF (Tag[4] + Res[4] + Size[4] + Res[4] + Offset[4] + Res[4] + Ver[2]) pattern -mscf_pattern = re.compile(br'\xB2\xAC\xBC\xB9\xFF{4}.{4}\xFF{4}.{4}\xFF{4}\xFC\xFE', re.DOTALL) - -# VAIO Packaging Manager Configuration file ("[Setting]" + Windows_new_line) pattern -vaio_pattern = re.compile(br'\x5B\x53\x65\x74\x74\x69\x6E\x67\x5D\x0D\x0A') - -# VAIO Packaging Manager Configuration file entry "UseVAIOCheck" pattern -check_pattern = re.compile(br'\x0A\x55\x73\x65\x56\x41\x49\x4F\x43\x68\x65\x63\x6B\x3D') - -# VAIO Packaging Manager Configuration file entry "ExtractPathByUser" pattern -path_pattern = re.compile(br'\x0A\x45\x78\x74\x72\x61\x63\x74\x50\x61\x74\x68\x42\x79\x55\x73\x65\x72\x3D') - -for input_file in vaio_exec : - file_path = os.path.abspath(input_file) - file_dir = os.path.dirname(file_path) - file_name = os.path.basename(file_path) - - print('\nFile: ' + file_name) - - # Open Locked VAIO Packaging Manager executable as mutable bytearray - with open(input_file, 'rb') as in_file : vaio_data = bytearray(in_file.read()) - - match_mscf = mscf_pattern.search(vaio_data) # Search for Microsoft CAB Header XOR 0xFF pattern - - match_vaio = vaio_pattern.search(vaio_data) if not match_mscf else None # Search for VAIO Packaging Manager Configuration file - - # Check if Microsoft CAB Header XOR 0xFF pattern exists - if match_mscf : - print('\n Detected Obfuscation!') - - # Determine the Microsoft CAB image Size - cab_size = int.from_bytes(vaio_data[match_mscf.start() + 0x8:match_mscf.start() + 0xC], 'little') # Get LE XOR-ed CAB Size - xor_size = int.from_bytes(b'\xFF' * 0x4, 'little') # Create CAB Size XOR value - cab_size = cab_size ^ xor_size # Perform XOR 0xFF and get actual CAB Size - - print('\n Removing Obfuscation...') - - # Determine the Microsoft CAB image Data - cab_data = int.from_bytes(vaio_data[match_mscf.start():match_mscf.start() + cab_size], 'big') # Get BE XOR-ed CAB Data - xor_data = int.from_bytes(b'\xFF' * cab_size, 'big') # Create CAB Data XOR value - cab_data = (cab_data ^ xor_data).to_bytes(cab_size, 'big') # Perform XOR 0xFF and get actual CAB Data - - print('\n Extracting...') - - with open('vaio_temp.cab', 'wb') as cab_file : cab_file.write(cab_data) # Create temporary CAB image - - extr_path = os.path.join(file_dir, file_name[:-4], '') # Create CAB image extraction path - - try : - decomp = subprocess.run(['7z', 'x', '-aou', '-bso0', '-bse0', '-bsp0', '-o' + extr_path, 'vaio_temp.cab']) # 7-Zip - - print('\n Extracted!') - except : - print('\n Error: Could not decompress Microsoft CAB image!') - print(' Make sure that "7z" executable exists!') - - os.remove('vaio_temp.cab') # Remove temporary CAB image - - # Check if VAIO Packaging Manager Configuration file pattern exists - elif match_vaio : - print('\n Error: Failed to Extract, attempting to Unlock instead...') - - # Initialize VAIO Package Configuration file variables (assume overkill size of 0x500) - info_start, info_end, val_false, val_true = [match_vaio.start(), match_vaio.start() + 0x500, b'', b''] - - # Get VAIO Package Configuration file info, split at new_line and stop at payload DOS header (EOF) - vaio_info = vaio_data[info_start:info_end].split(b'\x0D\x0A\x4D\x5A')[0].replace(b'\x0D',b'').split(b'\x0A') - - # Determine VAIO Package Configuration file True & False values - for info in vaio_info : - if info.startswith(b'ExtractPathByUser=') : val_false = bytearray(b'0' if info[18:] in (b'0',b'1') else info[18:]) # Should be 0/No/False - if info.startswith(b'UseCompression=') : val_true = bytearray(b'1' if info[15:] in (b'0',b'1') else info[15:]) # Should be 1/Yes/True - else : - if val_false == val_true or not val_false or not val_true : - print('\n Error: Could not determine True/False values!') - print(' Please report this VAIO Packaging Manager!') - continue # Next input file - - # Find and replace UseVAIOCheck entry from 1/Yes/True to 0/No/False - UseVAIOCheck = check_pattern.search(vaio_data[info_start:]) - if UseVAIOCheck : vaio_data[info_start + UseVAIOCheck.end():info_start + UseVAIOCheck.end() + len(val_false)] = val_false - else : print('\n Error: Could not find UseVAIOCheck entry!') - - # Find and replace ExtractPathByUser entry from 0/No/False to 1/Yes/True - ExtractPathByUser = path_pattern.search(vaio_data[info_start:]) - if ExtractPathByUser : vaio_data[info_start + ExtractPathByUser.end():info_start + ExtractPathByUser.end() + len(val_false)] = val_true - else : print('\n Error: Could not find ExtractPathByUser entry!') - - # Store Unlocked VAIO Packaging Manager executable - if UseVAIOCheck and ExtractPathByUser : - with open(os.path.join(file_dir, file_name + '_Unlocked.exe'), 'wb') as unl_file : unl_file.write(vaio_data) - print('\n Unlocked!') - - else : - print('\n Error: No VAIO Packaging Manager found!') - continue # Next input file - -else : - input('\nDone!') \ No newline at end of file