diff --git a/AMI_PFAT_Extract.py b/AMI_PFAT_Extract.py index 34de9c2..e6dd03c 100644 --- a/AMI_PFAT_Extract.py +++ b/AMI_PFAT_Extract.py @@ -7,7 +7,7 @@ AMI BIOS Guard Extractor Copyright (C) 2018-2022 Plato Mavropoulos """ -TITLE = 'AMI BIOS Guard Extractor v4.0_a9' +TITLE = 'AMI BIOS Guard Extractor v4.0_a10' import os import re @@ -36,10 +36,10 @@ class AmiBiosGuardHeader(ctypes.LittleEndianStructure): ] def struct_print(self, p): - printer(['Size :', '0x%X' % self.Size], p, False) - printer(['Checksum:', '0x%0.4X' % self.Checksum], p, False) + printer(['Size :', f'0x{self.Size:X}'], p, False) + printer(['Checksum:', f'0x{self.Checksum:04X}'], p, False) printer(['Tag :', self.Tag.decode('utf-8')], p, False) - printer(['Flags :', '0x%0.2X' % self.Flags], p, False) + printer(['Flags :', f'0x{self.Flags:02X}'], p, False) class IntelBiosGuardHeader(ctypes.LittleEndianStructure): _pack_ = 1 @@ -63,10 +63,10 @@ class IntelBiosGuardHeader(ctypes.LittleEndianStructure): id_text = re.sub(r'[\n\t\r\x00 ]', '', id_byte.decode('utf-8','ignore')) - id_hexs = '%0.*X' % (0x10 * 2, int.from_bytes(id_byte, 'big')) - id_guid = '{%s-%s-%s-%s-%s}' % (id_hexs[:8], id_hexs[8:12], id_hexs[12:16], id_hexs[16:20], id_hexs[20:]) + id_hexs = f'{int.from_bytes(id_byte, "big"):0{0x10 * 2}X}' + id_guid = f'{{{id_hexs[:8]}-{id_hexs[8:12]}-{id_hexs[12:16]}-{id_hexs[16:20]}-{id_hexs[20:]}}}' - return '%s %s' % (id_text, id_guid) + return f'{id_text} {id_guid}' def get_flags(self): attr = IntelBiosGuardHeaderGetAttributes() @@ -78,19 +78,19 @@ class IntelBiosGuardHeader(ctypes.LittleEndianStructure): no_yes = ['No','Yes'] f1,f2,f3,f4,f5 = self.get_flags() - printer(['BIOS Guard Version :', '%d.%d' % (self.BGVerMajor, self.BGVerMinor)], p, False) + printer(['BIOS Guard Version :', f'{self.BGVerMajor}.{self.BGVerMinor}'], p, False) printer(['Platform Identity :', self.get_platform_id()], p, False) printer(['Signed Flash Address Map :', no_yes[f1]], p, False) printer(['Protected EC OpCodes :', no_yes[f2]], p, False) printer(['Graphics Security Disable :', no_yes[f3]], p, False) printer(['Fault Tolerant Update :', no_yes[f4]], p, False) - printer(['Attributes Reserved :', '0x%X' % f5], p, False) - printer(['Script Version :', '%d.%d' % (self.ScriptVerMajor, self.ScriptVerMinor)], p, False) - printer(['Script Size :', '0x%X' % self.ScriptSize], p, False) - printer(['Data Size :', '0x%X' % self.DataSize], p, False) - printer(['BIOS Security Version Number:', '0x%X' % self.BIOSSVN], p, False) - printer(['EC Security Version Number :', '0x%X' % self.ECSVN], p, False) - printer(['Vendor Information :', '0x%X' % self.VendorInfo], p, False) + printer(['Attributes Reserved :', f'0x{f5:X}'], p, False) + printer(['Script Version :', f'{self.ScriptVerMajor}.{self.ScriptVerMinor}'], p, False) + printer(['Script Size :', f'0x{self.ScriptSize:X}'], p, False) + printer(['Data Size :', f'0x{self.DataSize:X}'], p, False) + printer(['BIOS Security Version Number:', f'0x{self.BIOSSVN:X}'], p, False) + printer(['EC Security Version Number :', f'0x{self.ECSVN:X}'], p, False) + printer(['Vendor Information :', f'0x{self.VendorInfo:X}'], p, False) class IntelBiosGuardHeaderAttributes(ctypes.LittleEndianStructure): _fields_ = [ @@ -119,14 +119,14 @@ class IntelBiosGuardSignature2k(ctypes.LittleEndianStructure): ] def struct_print(self, p): - Modulus = '%0.*X' % (0x100 * 2, int.from_bytes(self.Modulus, 'little')) - Signature = '%0.*X' % (0x100 * 2, int.from_bytes(self.Signature, 'little')) + Modulus = f'{int.from_bytes(self.Modulus, "little"):0{0x100 * 2}X}' + Signature = f'{int.from_bytes(self.Signature, "little"):0{0x100 * 2}X}' - printer(['Unknown 0:', '0x%X' % self.Unknown0], p, False) - printer(['Unknown 1:', '0x%X' % self.Unknown1], p, False) - printer(['Modulus :', '%s [...]' % Modulus[:32]], p, False) - printer(['Exponent :', '0x%X' % self.Exponent], p, False) - printer(['Signature:', '%s [...]' % Signature[:32]], p, False) + printer(['Unknown 0:', f'0x{self.Unknown0:X}'], p, False) + printer(['Unknown 1:', f'0x{self.Unknown1:X}'], p, False) + printer(['Modulus :', f'{Modulus[:32]} [...]'], p, False) + printer(['Exponent :', f'0x{self.Exponent:X}'], p, False) + printer(['Signature:', f'{Signature[:32]} [...]'], p, False) def is_ami_pfat(in_file): input_buffer = file_to_bytes(in_file) @@ -141,7 +141,7 @@ def get_ami_pfat(input_buffer): return match, buffer def get_file_name(index, name): - return safe_name('%0.2d -- %s' % (index, name)) + return safe_name(f'{index:02d} -- {name}') def parse_bg_script(script_data, padding): is_opcode_div = len(script_data) % 8 == 0 @@ -212,7 +212,7 @@ def parse_pfat_hdr(buffer, padding): order = get_ordinal((bgt_indexes[index] if bgt_indexes else index) + 1) - desc = '%s (Index: %0.2d, Flash: %s, Parameter: %s, Flags: 0x%X, Blocks: %d)' % (name, index + 1, order, param, flags, count) + desc = f'{name} (Index: {index + 1:02d}, Flash: {order}, Parameter: {param}, Flags: 0x{flags:X}, Blocks: {count})' block_all += [(desc, name, order, param, flags, index, i, count) for i in range(count)] @@ -226,7 +226,7 @@ def parse_pfat_file(buffer, output_path, padding): extract_name = os.path.basename(output_path) - extract_path = os.path.join(output_path + '_extracted') + extract_path = os.path.join(f'{output_path}_extracted') make_dirs(extract_path, delete=True) @@ -242,11 +242,11 @@ def parse_pfat_file(buffer, output_path, padding): all_blocks_dict[file_index] = b'' - block_status = '%d/%d' % (block_index + 1, block_count) + block_status = f'{block_index + 1}/{block_count}' bg_hdr = get_struct(buffer, block_off, IntelBiosGuardHeader) - printer('Intel BIOS Guard %s Header:\n' % block_status, padding + 8) + printer(f'Intel BIOS Guard {block_status} Header:\n', padding + 8) bg_hdr.struct_print(padding + 12) @@ -270,13 +270,13 @@ def parse_pfat_file(buffer, output_path, padding): if len(bg_sig_bin) == PFAT_BLK_S2K_LEN: bg_sig = get_struct(bg_sig_bin, 0x0, IntelBiosGuardSignature2k) - printer('Intel BIOS Guard %s Signature:\n' % block_status, padding + 8) + printer(f'Intel BIOS Guard {block_status} Signature:\n', padding + 8) bg_sig.struct_print(padding + 12) block_off = bg_sig_end # Adjust next block to start at data + signature end - printer('Intel BIOS Guard %s Script:\n' % block_status, padding + 8) + printer(f'Intel BIOS Guard {block_status} Script:\n', padding + 8) _ = parse_bg_script(bg_script_bin, padding + 12) @@ -286,7 +286,9 @@ def parse_pfat_file(buffer, output_path, padding): pfat_oob_data = buffer[block_off:] # Store out-of-bounds data after the end of PFAT files - pfat_oob_path = os.path.join(extract_path, get_file_name(file_count + 1, extract_name + '_OOB.bin')) + pfat_oob_name = get_file_name(file_count + 1, f'{extract_name}_OOB.bin') + + pfat_oob_path = os.path.join(extract_path, pfat_oob_name) with open(pfat_oob_path, 'wb') as out_oob: out_oob.write(pfat_oob_data) @@ -296,7 +298,9 @@ def parse_pfat_file(buffer, output_path, padding): in_all_data = b''.join([block[1] for block in sorted(all_blocks_dict.items())]) - in_all_path = os.path.join(extract_path, get_file_name(0, extract_name + '_ALL.bin')) + in_all_name = get_file_name(0, f'{extract_name}_ALL.bin') + + in_all_path = os.path.join(extract_path, in_all_name) with open(in_all_path, 'wb') as out_all: out_all.write(in_all_data + pfat_oob_data) diff --git a/AMI_UCP_Extract.py b/AMI_UCP_Extract.py index 769048b..f4a48d0 100644 --- a/AMI_UCP_Extract.py +++ b/AMI_UCP_Extract.py @@ -3,11 +3,11 @@ """ AMI UCP Extract -AMI UCP BIOS Extractor +AMI UCP Update Extractor Copyright (C) 2021-2022 Plato Mavropoulos """ -TITLE = 'AMI UCP BIOS Extractor v2.0_a12' +TITLE = 'AMI UCP Update Extractor v2.0_a13' import os import re @@ -45,20 +45,20 @@ class UafHeader(ctypes.LittleEndianStructure): def _get_reserved(self): res_bytes = bytes(self.Reserved) + res_hex = f'0x{int.from_bytes(res_bytes, "big"):0{0x4 * 2}X}' + res_str = re.sub(r'[\n\t\r\x00 ]', '', res_bytes.decode('utf-8','ignore')) - res_hex = '0x%0.*X' % (0x4 * 2, int.from_bytes(res_bytes, 'big')) + res_txt = f' ({res_str})' if len(res_str) else '' - res_out = res_hex + (' (%s)' % res_str if len(res_str) else '') - - return res_out + return f'{res_hex}{res_txt}' def struct_print(self, p): printer(['Tag :', self.ModuleTag.decode('utf-8')], p, False) - printer(['Size :', '0x%X' % self.ModuleSize], p, False) - printer(['Checksum :', '0x%0.4X' % self.Checksum], p, False) - printer(['Unknown 0 :', '0x%0.2X' % self.Unknown0], p, False) - printer(['Unknown 1 :', '0x%0.2X' % self.Unknown1], p, False) + printer(['Size :', f'0x{self.ModuleSize:X}'], p, False) + printer(['Checksum :', f'0x{self.Checksum:04X}'], p, False) + printer(['Unknown 0 :', f'0x{self.Unknown0:02X}'], p, False) + printer(['Unknown 1 :', f'0x{self.Unknown1:02X}'], p, False) printer(['Reserved :', self._get_reserved()], p, False) class UafModule(ctypes.LittleEndianStructure): @@ -70,8 +70,8 @@ class UafModule(ctypes.LittleEndianStructure): ] def struct_print(self, p, filename, description): - printer(['Compress Size:', '0x%X' % self.CompressSize], p, False) - printer(['Original Size:', '0x%X' % self.OriginalSize], p, False) + printer(['Compress Size:', f'0x{self.CompressSize:X}'], p, False) + printer(['Original Size:', f'0x{self.OriginalSize:X}'], p, False) printer(['Filename :', filename], p, False) printer(['Description :', description], p, False) @@ -98,22 +98,22 @@ class UiiHeader(ctypes.LittleEndianStructure): PMD = {1: 'API', 2: 'Console', 3: 'GUI', 4: 'Console/GUI'} def struct_print(self, p, description): - SupportBIOS = self.SBI.get(self.SupportBIOS, 'Unknown (%d)' % self.SupportBIOS) - SupportOS = self.SOS.get(self.SupportOS, 'Unknown (%d)' % self.SupportOS) - DataBusWidth = self.DBW.get(self.DataBusWidth, 'Unknown (%d)' % self.DataBusWidth) - ProgramType = self.PTP.get(self.ProgramType, 'Unknown (%d)' % self.ProgramType) - ProgramMode = self.PMD.get(self.ProgramMode, 'Unknown (%d)' % self.ProgramMode) + SupportBIOS = self.SBI.get(self.SupportBIOS, f'Unknown ({self.SupportBIOS})') + SupportOS = self.SOS.get(self.SupportOS, f'Unknown ({self.SupportOS})') + DataBusWidth = self.DBW.get(self.DataBusWidth, f'Unknown ({self.DataBusWidth})') + ProgramType = self.PTP.get(self.ProgramType, f'Unknown ({self.ProgramType})') + ProgramMode = self.PMD.get(self.ProgramMode, f'Unknown ({self.ProgramMode})') - printer(['UII Size :', '0x%X' % self.UIISize], p, False) - printer(['Checksum :', '0x%0.4X' % self.Checksum], p, False) - printer(['Tool Version :', '0x%0.8X' % self.UtilityVersion], p, False) - printer(['Info Size :', '0x%X' % self.InfoSize], p, False) + printer(['UII Size :', f'0x{self.UIISize:X}'], p, False) + printer(['Checksum :', f'0x{self.Checksum:04X}'], p, False) + printer(['Tool Version :', f'0x{self.UtilityVersion:08X}'], p, False) + printer(['Info Size :', f'0x{self.InfoSize:X}'], p, False) printer(['Supported BIOS:', SupportBIOS], p, False) printer(['Supported OS :', SupportOS], p, False) printer(['Data Bus Width:', DataBusWidth], p, False) printer(['Program Type :', ProgramType], p, False) printer(['Program Mode :', ProgramMode], p, False) - printer(['SourceSafe Tag:', '%0.2d' % self.SourceSafeRel], p, False) + printer(['SourceSafe Tag:', f'{self.SourceSafeRel:02d}'], p, False) printer(['Description :', description], p, False) class DisHeader(ctypes.LittleEndianStructure): @@ -126,7 +126,7 @@ class DisHeader(ctypes.LittleEndianStructure): ] def struct_print(self, p): - printer(['Password Size:', '0x%X' % self.PasswordSize], p, False) + printer(['Password Size:', f'0x{self.PasswordSize:X}'], p, False) printer(['Entry Count :', self.EntryCount], p, False) printer(['Password :', self.Password.decode('utf-8')], p, False) @@ -144,8 +144,8 @@ class DisModule(ctypes.LittleEndianStructure): SHOWN = {0: 'Hidden', 1: 'Shown', 2: 'Shown Only'} def struct_print(self, p): - EnabledDisabled = self.ENDIS.get(self.EnabledDisabled, 'Unknown (%d)' % self.EnabledDisabled) - ShownHidden = self.SHOWN.get(self.ShownHidden, 'Unknown (%d)' % self.ShownHidden) + EnabledDisabled = self.ENDIS.get(self.EnabledDisabled, f'Unknown ({self.EnabledDisabled})') + ShownHidden = self.SHOWN.get(self.ShownHidden, f'Unknown ({self.ShownHidden})') printer(['State :', EnabledDisabled], p, False) printer(['Display :', ShownHidden], p, False) @@ -155,9 +155,9 @@ class DisModule(ctypes.LittleEndianStructure): # Validate UCP Module Checksum-16 def chk16_validate(data, tag, padd=0): if get_chk_16(data) != 0: - printer('Error: Invalid UCP Module %s Checksum!' % tag, padd, pause=True) + printer(f'Error: Invalid UCP Module {tag} Checksum!', padd, pause=True) else: - printer('Checksum of UCP Module %s is valid!' % tag, padd) + printer(f'Checksum of UCP Module {tag} is valid!', padd) # Check if input is AMI UCP image def is_ami_ucp(in_file): @@ -213,13 +213,13 @@ def ucp_extract(buffer, out_path, ucp_tag='@UAF', padding=0, is_checksum=False, printer('Utility Configuration Program', padding) - extract_path = os.path.join(out_path + '_extracted') + extract_path = os.path.join(f'{out_path}_extracted') make_dirs(extract_path, delete=True) uaf_hdr = get_struct(buffer, 0, UafHeader) # Parse @UAF|@HPU Header Structure - printer('Utility Auxiliary File > %s:\n' % ucp_tag, padding + 4) + printer(f'Utility Auxiliary File > {ucp_tag}:\n', padding + 4) uaf_hdr.struct_print(padding + 8) @@ -251,7 +251,7 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, is_checksum=False, is uaf_data_raw = uaf_data_mod[UAF_MOD_LEN:] # @UAF|@HPU Module Raw Data - printer('Utility Auxiliary File > %s:\n' % uaf_tag, padding) + printer(f'Utility Auxiliary File > {uaf_tag}:\n', padding) uaf_hdr.struct_print(padding + 4) # Print @UAF|@HPU Module Info @@ -262,12 +262,12 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, is_checksum=False, is if uaf_tag in nal_dict: uaf_name = nal_dict[uaf_tag][1] # Always prefer @NAL naming first elif uaf_tag in UAF_TAG_DICT: uaf_name = UAF_TAG_DICT[uaf_tag][0] # Otherwise use built-in naming elif uaf_tag == '@ROM': uaf_name = 'BIOS.bin' # BIOS/PFAT Firmware (w/o Signature) - elif uaf_tag.startswith('@R0'): uaf_name = 'BIOS_0%s.bin' % uaf_tag[3:] # BIOS/PFAT Firmware - elif uaf_tag.startswith('@S0'): uaf_name = 'BIOS_0%s.sig' % uaf_tag[3:] # BIOS/PFAT Signature - elif uaf_tag.startswith('@DR'): uaf_name = 'DROM_0%s.bin' % uaf_tag[3:] # Thunderbolt Retimer Firmware - elif uaf_tag.startswith('@DS'): uaf_name = 'DROM_0%s.sig' % uaf_tag[3:] # Thunderbolt Retimer Signature - elif uaf_tag.startswith('@EC'): uaf_name = 'EC_0%s.bin' % uaf_tag[3:] # Embedded Controller Firmware - elif uaf_tag.startswith('@ME'): uaf_name = 'ME_0%s.bin' % uaf_tag[3:] # Management Engine Firmware + elif uaf_tag.startswith('@R0'): uaf_name = f'BIOS_0{uaf_tag[3:]}.bin' # BIOS/PFAT Firmware + elif uaf_tag.startswith('@S0'): uaf_name = f'BIOS_0{uaf_tag[3:]}.sig' # BIOS/PFAT Signature + elif uaf_tag.startswith('@DR'): uaf_name = f'DROM_0{uaf_tag[3:]}.bin' # Thunderbolt Retimer Firmware + elif uaf_tag.startswith('@DS'): uaf_name = f'DROM_0{uaf_tag[3:]}.sig' # Thunderbolt Retimer Signature + elif uaf_tag.startswith('@EC'): uaf_name = f'EC_0{uaf_tag[3:]}.bin' # Embedded Controller Firmware + elif uaf_tag.startswith('@ME'): uaf_name = f'ME_0{uaf_tag[3:]}.bin' # Management Engine Firmware else: uaf_name = uaf_tag # Could not name the @UAF|@HPU Module, use Tag instead uaf_fext = '' if uaf_name != uaf_tag else '.bin' @@ -278,7 +278,7 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, is_checksum=False, is # Check if unknown @UAF|@HPU Module Tag is present in @NAL but not in built-in dictionary if uaf_tag in nal_dict and uaf_tag not in UAF_TAG_DICT and not uaf_tag.startswith(('@ROM','@R0','@S0','@DR','@DS')): - printer('Note: Detected new AMI UCP Module %s (%s) in @NAL!' % (uaf_tag, nal_dict[uaf_tag][1]), padding + 4, pause=True) + printer(f'Note: Detected new AMI UCP Module {uaf_tag} ({nal_dict[uaf_tag][1]}) in @NAL!', padding + 4, pause=True) # Generate @UAF|@HPU Module File name, depending on whether decompression will be required uaf_sname = safe_name(uaf_name + ('.temp' if is_comp else uaf_fext)) @@ -339,7 +339,7 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, is_checksum=False, is # Process and Print known text only @UAF|@HPU Modules (after EFI/Tiano Decompression) if uaf_tag in UAF_TAG_DICT and UAF_TAG_DICT[uaf_tag][2] == 'Text': - printer(UAF_TAG_DICT[uaf_tag][1] + ':', padding + 4) + printer(f'{UAF_TAG_DICT[uaf_tag][1]}:', padding + 4) printer(uaf_data_raw.decode('utf-8','ignore'), padding + 8) # Parse Default Command Status @UAF|@HPU Module (@DIS) @@ -361,7 +361,7 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, is_checksum=False, is for mod_idx in range(dis_hdr.EntryCount): dis_mod = get_struct(dis_data, mod_idx * DIS_MOD_LEN, DisModule) # Parse @DIS Module Raw Entry Structure - printer('Default Command Status Entry %0.2d/%0.2d:\n' % (mod_idx + 1, dis_hdr.EntryCount), padding + 8) + printer(f'Default Command Status Entry {mod_idx + 1:02d}/{dis_hdr.EntryCount:02d}:\n', padding + 8) dis_mod.struct_print(padding + 12) # Print @DIS Module Raw Entry Info @@ -383,7 +383,7 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, is_checksum=False, is for info in nal_info: info_tag,info_value = info.split(':',1) - printer(info_tag + ' : ' + info_value, padding + 8, False) # Print @NAL Module Tag-Path Info + printer(f'{info_tag} : {info_value}', padding + 8, False) # Print @NAL Module Tag-Path Info info_part = agnostic_path(info_value).parts # Split OS agnostic path in parts info_path = to_string(info_part[1:-1], os.sep) # Get path without drive/root or file @@ -393,7 +393,7 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, is_checksum=False, is # Parse Insyde BIOS @UAF|@HPU Module (@INS) if uaf_tag == '@INS' and is_7z_supported(uaf_fname, padding + 4, static=is_static): - ins_dir = os.path.join(extract_path, safe_name(uaf_tag + '_nested-SFX')) # Generate extraction directory + ins_dir = os.path.join(extract_path, safe_name(f'{uaf_tag}_nested-SFX')) # Generate extraction directory printer('Insyde BIOS 7z SFX Archive:', padding + 4) @@ -420,7 +420,7 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, is_checksum=False, is # Parse Nested AMI UCP Structure if nested_uaf_off: - uaf_dir = os.path.join(extract_path, safe_name(uaf_tag + '_nested-UCP')) # Generate extraction directory + uaf_dir = os.path.join(extract_path, safe_name(f'{uaf_tag}_nested-UCP')) # Generate extraction directory ucp_extract(nested_uaf_bin, uaf_dir, nested_uaf_tag, padding + 4, is_checksum, is_static) # Call recursively @@ -514,7 +514,7 @@ if __name__ == '__main__': main_uaf_off,main_uaf_bin,main_uaf_tag = get_ami_ucp(input_buffer) if not main_uaf_off: - printer('Error: This is not an AMI UCP BIOS executable!', padding) + printer('Error: This is not an AMI UCP Update executable!', padding) continue # Next input file diff --git a/Dell_PFS_Extract.py b/Dell_PFS_Extract.py index fb9af1a..d322423 100644 --- a/Dell_PFS_Extract.py +++ b/Dell_PFS_Extract.py @@ -7,7 +7,7 @@ Dell PFS Update Extractor Copyright (C) 2018-2022 Plato Mavropoulos """ -TITLE = 'Dell PFS Update Extractor v6.0_a9' +TITLE = 'Dell PFS Update Extractor v6.0_a10' import os import io @@ -42,7 +42,7 @@ class DellPfsHeader(ctypes.LittleEndianStructure): def struct_print(self, p): printer(['Header Tag :', self.Tag.decode('utf-8')], p, False) printer(['Header Version:', self.HeaderVersion], p, False) - printer(['Payload Size :', '0x%X' % self.PayloadSize], p, False) + printer(['Payload Size :', f'0x{self.PayloadSize:X}'], p, False) # Dell PFS Footer Structure class DellPfsFooter(ctypes.LittleEndianStructure): @@ -55,8 +55,8 @@ class DellPfsFooter(ctypes.LittleEndianStructure): ] def struct_print(self, p): - printer(['Payload Size :', '0x%X' % self.PayloadSize], p, False) - printer(['Payload Checksum:', '0x%0.8X' % self.Checksum], p, False) + printer(['Payload Size :', f'0x{self.PayloadSize:X}'], p, False) + printer(['Payload Checksum:', f'0x{self.Checksum:08X}'], p, False) printer(['Footer Tag :', self.Tag.decode('utf-8')], p, False) # Dell PFS Entry Base Structure @@ -76,19 +76,19 @@ class DellPfsEntryBase(ctypes.LittleEndianStructure): ] def struct_print(self, p): - GUID = '%0.*X' % (0x10 * 2, int.from_bytes(self.GUID, 'little')) - Unknown = '%0.*X' % (len(self.Unknown) * 8, int.from_bytes(self.Unknown, 'little')) + GUID = f'{int.from_bytes(self.GUID, "little"):0{0x10 * 2}X}' + Unknown = f'{int.from_bytes(self.Unknown, "little"):0{len(self.Unknown) * 8}X}' Version = get_entry_ver(self.Version, self.VersionType) printer(['Entry GUID :', GUID], p, False) printer(['Entry Version :', self.HeaderVersion], p, False) printer(['Payload Version :', Version], p, False) - printer(['Reserved :', '0x%X' % self.Reserved], p, False) - printer(['Payload Data Size :', '0x%X' % self.DataSize], p, False) - printer(['Payload Signature Size :', '0x%X' % self.DataSigSize], p, False) - printer(['Metadata Data Size :', '0x%X' % self.DataMetSize], p, False) - printer(['Metadata Signature Size:', '0x%X' % self.DataMetSigSize], p, False) - printer(['Unknown :', '0x%s' % Unknown], p, False) + printer(['Reserved :', f'0x{self.Reserved:X}'], p, False) + printer(['Payload Data Size :', f'0x{self.DataSize:X}'], p, False) + printer(['Payload Signature Size :', f'0x{self.DataSigSize:X}'], p, False) + printer(['Metadata Data Size :', f'0x{self.DataMetSize:X}'], p, False) + printer(['Metadata Signature Size:', f'0x{self.DataMetSigSize:X}'], p, False) + printer(['Unknown :', f'0x{Unknown}'], p, False) # Dell PFS Entry Revision 1 Structure class DellPfsEntryR1(DellPfsEntryBase): @@ -116,7 +116,7 @@ class DellPfsInfo(ctypes.LittleEndianStructure): ] def struct_print(self, p): - GUID = '%0.*X' % (0x10 * 2, int.from_bytes(self.GUID, 'little')) + GUID = f'{int.from_bytes(self.GUID, "little"):0{0x10 * 2}X}' printer(['Info Version:', self.HeaderVersion], p, False) printer(['Entry GUID :', GUID], p, False) @@ -178,13 +178,13 @@ class DellPfsPfatMetadata(ctypes.LittleEndianStructure): ] def struct_print(self, p): - printer(['Offset Top :', '0x%X' % self.OffsetTop], p, False) - printer(['Unknown 0 :', '0x%X' % self.Unknown0], p, False) - printer(['Offset Base:', '0x%X' % self.OffsetBase], p, False) - printer(['Block Size :', '0x%X' % self.BlockSize], p, False) - printer(['Unknown 1 :', '0x%X' % self.Unknown1], p, False) - printer(['Unknown 2 :', '0x%X' % self.Unknown2], p, False) - printer(['Unknown 3 :', '0x%X' % self.Unknown3], p, False) + printer(['Offset Top :', f'0x{self.OffsetTop:X}'], p, False) + printer(['Unknown 0 :', f'0x{self.Unknown0:X}'], p, False) + printer(['Offset Base:', f'0x{self.OffsetBase:X}'], p, False) + printer(['Block Size :', f'0x{self.BlockSize:X}'], p, False) + printer(['Unknown 1 :', f'0x{self.Unknown1:X}'], p, False) + printer(['Unknown 2 :', f'0x{self.Unknown2:X}'], p, False) + printer(['Unknown 3 :', f'0x{self.Unknown3:X}'], p, False) # The Dell ThinOS PKG update images usually contain multiple sections. # Each section starts with a 0x30 header, which begins with pattern 72135500. @@ -239,10 +239,10 @@ def pfs_section_parse(zlib_data, zlib_start, output_path, pfs_name, pfs_index, p 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 = {0xAA:'Firmware', 0xBB:'Utilities'}.get(section_type, 'Unknown (%0.2X)' % section_type) + section_name = {0xAA:'Firmware', 0xBB:'Utilities'}.get(section_type, f'Unknown ({section_type:02X})') # Show extraction complete message for each main PFS ZLIB Section - printer('Extracting Dell PFS %d >%s > %s' % (pfs_index, pfs_name, section_name), padding) + printer(f'Extracting Dell PFS {pfs_index} >{pfs_name} > {section_name}', padding) # Set PFS ZLIB Section extraction sub-directory path section_path = os.path.join(output_path, safe_name(section_name)) @@ -390,11 +390,11 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i # Validate that a known PFS Information Header Version was encountered if entry_info_hdr.HeaderVersion != 1: - printer('Error: Unknown PFS Information Header Version %d!' % entry_info_hdr.HeaderVersion, pfs_padd + 8) + printer(f'Error: Unknown PFS Information Header Version {entry_info_hdr.HeaderVersion}!', pfs_padd + 8) 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')) + entry_guid = f'{int.from_bytes(entry_info_hdr.GUID, "little"):0{0x10 * 2}X}' # Get PFS FileName Structure values entry_info_mod = get_struct(filename_info, info_start + PFS_INFO_LEN, DellPfsName) @@ -466,7 +466,7 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i # Validate that a known PFS Information Header Version was encountered if entry_info_hdr.HeaderVersion != 1: - printer('Error: Unknown PFS Information Header Version %d!' % entry_info_hdr.HeaderVersion, pfs_padd + 8) + printer(f'Error: Unknown PFS Information Header Version {entry_info_hdr.HeaderVersion}!', pfs_padd + 8) break # Skip PFS Signature Entries/Descriptors in case of unknown Header Version # PFS Signature Entries/Descriptors have DellPfsInfo + DellPfsEntryR* + Sign Size [0x2] + Sign Data [Sig Size] @@ -484,12 +484,12 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i sign_info_start = sign_start + PFS_INFO_LEN + 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')) + sign_data_txt = f'{int.from_bytes(sign_data_raw, "little"):0{sign_size * 2}X}' if is_structure: printer('Signature Information:\n', pfs_padd + 8) - printer('Signature Size: 0x%X' % sign_size, pfs_padd + 12, False) - printer('Signature Data: %s [...]' % sign_data_txt[:32], pfs_padd + 12, False) + printer(f'Signature Size: 0x{sign_size:X}', pfs_padd + 12, False) + printer(f'Signature Data: {sign_data_txt[:32]} [...]', pfs_padd + 12, False) # The next PFS Signature Entry/Descriptor starts after the previous Signature Data sign_start += (PFS_INFO_LEN + pfs_entry_size + 0x2 + sign_size) @@ -533,7 +533,7 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i # 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' + sub_pfs_name = f' {info_all[pfs_count - 2][1]} v{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) + safe_name(sub_pfs_name)) @@ -597,7 +597,7 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i # Write/Extract PFS Entry files for file in write_files: - full_name = '%d%s -- %d %s v%s' % (pfs_index, pfs_name, file_index, file_name, file_version) # Full PFS Entry Name + full_name = f'{pfs_index}{pfs_name} -- {file_index} {file_name} v{file_version}' # Full PFS Entry Name pfs_file_write(file[0], file[1], file_type, full_name, output_path, pfs_padd, is_structure, is_advanced) # Get PFS Footer Data after PFS Header Payload @@ -621,13 +621,13 @@ def parse_pfs_entry(entry_buffer, entry_start, entry_size, entry_struct, text, p # Validate that the PFS Entry Reserved field is empty if pfs_entry.Reserved != 0: - printer('Error: Detected non-empty %s Reserved field!' % text, padding + 8) + printer(f'Error: Detected non-empty {text} Reserved field!', padding + 8) # Get PFS Entry Version string via "Version" and "VersionType" fields entry_version = get_entry_ver(pfs_entry.Version, pfs_entry.VersionType) # Get PFS Entry GUID in Big Endian format - entry_guid = '%0.*X' % (0x10 * 2, int.from_bytes(pfs_entry.GUID, 'little')) + entry_guid = f'{int.from_bytes(pfs_entry.GUID, "little"):0{0x10 * 2}X}' # PFS Entry Data starts after the PFS Entry Structure entry_data_start = entry_start + entry_size @@ -694,7 +694,7 @@ def parse_pfat_pfs(entry_hdr, entry_data, padding, is_structure=True): # Show sub-PFS PFAT Header Structure info if is_structure: - printer('PFAT Block %d Header:\n' % pfat_entry_index, padding + 12) + printer(f'PFAT Block {pfat_entry_index} Header:\n', padding + 12) pfat_hdr.struct_print(padding + 16) pfat_script_start = pfat_hdr_off + PFAT_HDR_LEN # PFAT Block Script Start @@ -719,12 +719,12 @@ def parse_pfat_pfs(entry_hdr, entry_data, padding, is_structure=True): # Show sub-PFS PFAT Signature Structure info if is_structure: - printer('PFAT Block %d Signature:\n' % pfat_entry_index, padding + 12) + printer(f'PFAT Block {pfat_entry_index} Signature:\n', padding + 12) pfat_sig.struct_print(padding + 16) # Show PFAT Script via BIOS Guard Script Tool if is_structure: - printer('PFAT Block %d Script:\n' % pfat_entry_index, padding + 12) + printer(f'PFAT Block {pfat_entry_index} Script:\n', padding + 12) # https://github.com/allowitsme/big-tool by Dmitry Frolov _ = parse_bg_script(pfat_script_data, padding + 16) @@ -741,7 +741,7 @@ def parse_pfat_pfs(entry_hdr, entry_data, padding, is_structure=True): # Show sub-PFS PFAT Metadata Structure info if is_structure: - printer('PFAT Block %d Metadata:\n' % pfat_entry_index, padding + 12) + printer(f'PFAT Block {pfat_entry_index} Metadata:\n', padding + 12) pfat_met.struct_print(padding + 16) # Another way to get each PFAT Entry payload's Order is from its Metadata at 0x8-0xC, if applicable @@ -801,10 +801,10 @@ def get_entry_ver(version_fields, version_types): for index,field in enumerate(version_fields): eol = '' if index == len(version_fields) - 1 else '.' - if version_types[index] == 65: version += '%X%s' % (field, eol) # 0x41 = ASCII - elif version_types[index] == 78: version += '%d%s' % (field, eol) # 0x4E = Number + if version_types[index] == 65: version += f'{field:X}{eol}' # 0x41 = ASCII + elif version_types[index] == 78: version += f'{field:d}{eol}' # 0x4E = Number elif version_types[index] in (0, 32): version = version.strip('.') # 0x00 or 0x20 = Unused - else: version += '%X%s' % (field, eol) # Unknown + else: version += f'{field:X}{eol}' # Unknown return version @@ -812,7 +812,7 @@ def get_entry_ver(version_fields, version_types): def chk_hdr_ver(version, text, padding): if version in (1,2): return - printer('Error: Unknown %s Header Version %d!' % (text, version), padding) + printer(f'Error: Unknown {text} Header Version {version}!', padding) # Analyze Dell PFS Footer Structure def chk_pfs_ftr(footer_buffer, data_buffer, data_size, text, padding, is_structure=True): @@ -826,24 +826,24 @@ def chk_pfs_ftr(footer_buffer, data_buffer, data_size, text, padding, is_structu printer('PFS Footer:\n', padding + 4) pfs_ftr.struct_print(padding + 8) else: - printer('Error: %s Footer could not be found!' % text, padding + 4) + printer(f'Error: {text} Footer could not be found!', padding + 4) # Validate that PFS Header Payload Size matches the one at PFS Footer if data_size != pfs_ftr.PayloadSize: - printer('Error: %s Header & Footer Payload Size mismatch!' % text, padding + 4) + printer(f'Error: {text} Header & Footer Payload Size mismatch!', padding + 4) # 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: - printer('Error: Invalid %s Footer Payload Checksum!' % text, padding + 4) + printer(f'Error: Invalid {text} Footer Payload Checksum!', padding + 4) # Write/Extract Dell PFS Entry Files (Data, Metadata, Signature) def pfs_file_write(bin_buff, bin_name, bin_type, full_name, out_path, padding, is_structure=True, is_advanced=True): # Store Data/Metadata Signature (advanced users only) if bin_name.startswith('sign'): - final_name = '%s.%s.sig' % (safe_name(full_name), bin_name.split('_')[1]) + final_name = f'{safe_name(full_name)}.{bin_name.split("_")[1]}.sig' final_path = os.path.join(out_path, final_name) with open(final_path, 'wb') as pfs_out: pfs_out.write(bin_buff) # Write final Data/Metadata Signature @@ -851,12 +851,12 @@ def pfs_file_write(bin_buff, bin_name, bin_type, full_name, out_path, padding, i 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 + bin_ext = f'.{bin_name}.bin' 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', padding, is_structure, is_advanced) - final_name = '%s%s' % (safe_name(full_name), bin_ext[:-4] + file_ext if is_text else bin_ext) + final_name = f'{safe_name(full_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 @@ -893,7 +893,7 @@ def bin_is_text(buffer, file_type, is_metadata, pfs_padd, is_structure=True, is_ # Show Model/PCR XML Information, if applicable if is_structure and is_text and not is_metadata: # Metadata is shown at initial DellPfsMetadata analysis - printer('PFS %s Information:\n' % {'.txt': 'Model', '.xml': 'PCR XML'}[extension], pfs_padd + 8) + printer(f'PFS { {".txt": "Model", ".xml": "PCR XML"}[extension] } Information:\n', pfs_padd + 8) _ = [printer(line.strip('\r'), pfs_padd + 12, False) for line in buffer.split('\n') if line] # Only for non-advanced users due to signature (.sig) invalidation @@ -957,7 +957,7 @@ if __name__ == '__main__': continue # Next input file - extract_path = os.path.join(output_path, input_name + '_extracted') + extract_path = os.path.join(output_path, f'{input_name}_extracted') extract_name = ' ' + os.path.splitext(input_name)[0] diff --git a/Phoenix_TDK_Extract.py b/Phoenix_TDK_Extract.py new file mode 100644 index 0000000..85ddcff --- /dev/null +++ b/Phoenix_TDK_Extract.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 +#coding=utf-8 + +""" +Phoenix TDK Extract +Phoenix TDK Packer Extractor +Copyright (C) 2021-2022 Plato Mavropoulos +""" + +TITLE = 'Phoenix TDK Packer Extractor v2.0_a4' + +import os +import sys +import lzma +import ctypes + +# Stop __pycache__ generation +sys.dont_write_bytecode = True + +from common.path_ops import safe_name, make_dirs +from common.patterns import PAT_PHOENIX_TDK +from common.struct_ops import get_struct, char, uint32_t +from common.system import script_init, argparse_init, printer +from common.text_ops import file_to_bytes + +class PhoenixTdkHeader(ctypes.LittleEndianStructure): + _pack_ = 1 + _fields_ = [ + ('Tag', char*8), # 0x00 + ('Size', uint32_t), # 0x08 + ('Count', uint32_t), # 0x0C + # 0x10 + ] + + def _get_tag(self): + return self.Tag.decode('utf-8','ignore').strip() + + def struct_print(self, p): + printer(['Tag :', self._get_tag()], p, False) + printer(['Size :', f'0x{self.Size:X}'], p, False) + printer(['Entries:', self.Count], p, False) + +class PhoenixTdkEntry(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 + ] + + COMP = {0: 'None', 1: 'LZMA'} + + def get_name(self): + return self.Name.decode('utf-8','replace').strip() + + def get_compression(self): + return self.COMP.get(self.Compressed, f'Unknown ({self.Compressed})') + + def struct_print(self, p): + printer(['Name :', self.get_name()], p, False) + printer(['Offset :', f'0x{self.Offset:X}'], p, False) + printer(['Size :', f'0x{self.Size:X}'], p, False) + printer(['Compression:', self.get_compression()], p, False) + printer(['Reserved :', f'0x{self.Reserved:X}'], p, False) + +# Scan input buffer for Phoenix TDK pattern +def get_phoenix_tdk(in_buffer): + return PAT_PHOENIX_TDK.search(in_buffer) + +# Check if input is Phoenix TDK image +def is_phoenix_tdk(in_file): + buffer = file_to_bytes(in_file) + + return bool(get_phoenix_tdk(buffer)) + +# Parse & Extract Phoenix Tools Development Kit (TDK) Packer +def phoenix_tdk_extract(input_buffer, output_path, padding=0): + exit_code = 0 + + extract_path = os.path.join(f'{output_path}_extracted') + + make_dirs(extract_path, delete=True) + + printer('Phoenix Tools Development Kit Packer', padding) + + # Search for Phoenix TDK Package pattern + tdk_match = get_phoenix_tdk(input_buffer) + + # Parse TDK Header structure + tdk_hdr = get_struct(input_buffer, tdk_match.start(), PhoenixTdkHeader) + + # Print TDK Header structure info + printer('Phoenix TDK Header:\n', padding + 4) + tdk_hdr.struct_print(padding + 8) + + # Check if reported TDK Header Size matches manual TDK Entry Count calculation + if tdk_hdr.Size != TDK_HDR_LEN + TDK_DUMMY_LEN + tdk_hdr.Count * TDK_MOD_LEN: + printer('Error: Phoenix TDK Header Size & Entry Count mismatch!\n', padding + 8, pause=True) + exit_code = 1 + + # Store TDK Entries offset after the dummy/placeholder data + entries_off = tdk_match.start() + TDK_HDR_LEN + TDK_DUMMY_LEN + + # Parse and extract each TDK Header Entry + for entry_index in range(tdk_hdr.Count): + # Parse TDK Entry structure + tdk_mod = get_struct(input_buffer, entries_off + entry_index * TDK_MOD_LEN, PhoenixTdkEntry) + + # Print TDK Entry structure info + printer(f'Phoenix TDK Entry ({entry_index + 1}/{tdk_hdr.Count}):\n', padding + 8) + tdk_mod.struct_print(padding + 12) + + # Store TDK Entry raw data (relative to 0x0, not TDK Header) + mod_data = input_buffer[tdk_mod.Offset:tdk_mod.Offset + tdk_mod.Size] + + # Check if TDK Entry raw data is complete + if len(mod_data) != tdk_mod.Size: + printer('Error: Phoenix TDK Entry > Data is truncated!\n', padding + 12, pause=True) + exit_code = 2 + + # Check if TDK Entry Reserved is present + if tdk_mod.Reserved: + printer('Error: Phoenix TDK Entry > Reserved is not empty!\n', padding + 12, pause=True) + exit_code = 3 + + # Decompress TDK Entry raw data, when applicable (i.e. LZMA) + if tdk_mod.get_compression() == 'LZMA': + mod_data = lzma.LZMADecompressor().decompress(mod_data) + + # Generate TDK Entry file name, avoid crash if Entry data is bad + mod_name = tdk_mod.get_name() or f'Unknown_{entry_index + 1:02d}.bin' + + # Generate TDK Entry file data output path + mod_file = os.path.join(extract_path, safe_name(mod_name)) + + # Account for potential duplicate file names + if os.path.isfile(mod_file): mod_file += f'_{entry_index + 1:02d}' + + # Save TDK Entry data to output file + with open(mod_file, 'wb') as out_file: out_file.write(mod_data) + + return exit_code + +# Get ctypes Structure Sizes +TDK_HDR_LEN = ctypes.sizeof(PhoenixTdkHeader) +TDK_MOD_LEN = ctypes.sizeof(PhoenixTdkEntry) + +# Set dummy/placeholder TDK Entries Size +TDK_DUMMY_LEN = 0x200 # Top 2, Names only + +if __name__ == '__main__': + # Set argparse Arguments + argparser = argparse_init() + arguments = argparser.parse_args() + + # Initialize script (must be after argparse) + exit_code,input_files,output_path,padding = script_init(TITLE, arguments, 4) + + for input_file in input_files: + input_name = os.path.basename(input_file) + + printer(['***', input_name], padding - 4) + + with open(input_file, 'rb') as in_file: input_buffer = in_file.read() + + # Check if Phoenix TDK Packer pattern was found on executable + if not is_phoenix_tdk(input_buffer): + printer('Error: This is not a Phoenix TDK Packer executable!', padding) + + continue # Next input file + + extract_path = os.path.join(output_path, input_name) + + if phoenix_tdk_extract(input_buffer, extract_path, padding) == 0: + exit_code -= 1 + + printer('Done!', pause=True) + + sys.exit(exit_code) diff --git a/Portwell_EFI_Extract.py b/Portwell_EFI_Extract.py index 3546065..c241347 100644 --- a/Portwell_EFI_Extract.py +++ b/Portwell_EFI_Extract.py @@ -3,11 +3,11 @@ """ Portwell EFI Extract -Portwell EFI BIOS Extractor +Portwell EFI Update Extractor Copyright (C) 2021-2022 Plato Mavropoulos """ -TITLE = 'Portwell EFI BIOS Extractor v2.0_a4' +TITLE = 'Portwell EFI Update Extractor v2.0_a5' import os import sys @@ -54,8 +54,8 @@ def get_portwell_pe(in_buffer): return pe_file, pe_data # Parse & Extract Portwell UEFI Unpacker -def portwell_efi_extract(input_buffer, out_path, padding=0): - extract_path = os.path.join(out_path + '_extracted') +def portwell_efi_extract(input_buffer, output_path, padding=0): + extract_path = os.path.join(f'{output_path}_extracted') make_dirs(extract_path, delete=True) diff --git a/README.md b/README.md index df5392a..6bb9277 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,10 @@ BIOS Utilities Donation via Paypal or Debit/Credit Card * [**Dell PFS Update Extractor**](#dell-pfs-update-extractor) -* [**AMI UCP BIOS Extractor**](#ami-ucp-bios-extractor) +* [**AMI UCP Update Extractor**](#ami-ucp-update-extractor) * [**AMI BIOS Guard Extractor**](#ami-bios-guard-extractor) -* [**Portwell EFI BIOS Extractor**](#portwell-efi-bios-extractor) +* [**Phoenix TDK Packer Extractor**](#phoenix-tdk-packer-extractor) +* [**Portwell EFI Update Extractor**](#portwell-efi-update-extractor) ## **Dell PFS Update Extractor** @@ -71,17 +72,17 @@ Some Anti-Virus software may claim that the built/frozen/compiled executable con ![]() -## **AMI UCP BIOS Extractor** +## **AMI UCP Update Extractor** ![]() #### **Description** -Parses AMI UCP (Utility Configuration Program) BIOS executables, extracts their firmware components (e.g. SPI/BIOS/UEFI, EC, ME etc) and shows all relevant info. It supports all AMI UCP revisions and formats, including those with nested AMI PFAT, AMI UCP or Insyde SFX structures. The output comprises only final firmware components and utilities which are directly usable by end users. +Parses AMI UCP (Utility Configuration Program) Update executables, extracts their firmware components (e.g. SPI/BIOS/UEFI, EC, ME etc) and shows all relevant info. It supports all AMI UCP revisions and formats, including those with nested AMI PFAT, AMI UCP or Insyde SFX structures. The output comprises only final firmware components and utilities which are directly usable by end users. #### **Usage** -You can either Drag & Drop or manually enter AMI UCP BIOS executable file(s). Optional arguments: +You can either Drag & Drop or manually enter AMI UCP Update executable file(s). Optional arguments: * -h or --help : show help message and exit * -v or --version : show utility name and version @@ -199,7 +200,60 @@ Some Anti-Virus software may claim that the built/frozen/compiled executable con ![]() -## **Portwell EFI BIOS Extractor** +## **Phoenix TDK Packer Extractor** + +![]() + +#### **Description** + +Parses Phoenix Tools Development Kit (TDK) Packer executables and extracts their firmware (e.g. SPI, BIOS/UEFI, EC etc) and utilities (e.g. WinFlash etc) components. It supports all Phoenix TDK Packer revisions and formats, including those which contain LZMA compressed files. The output comprises only final firmware components which are directly usable by end users. + +#### **Usage** + +You can either Drag & Drop or manually enter Phoenix Tools Development Kit (TDK) Packer executable file(s). Optional arguments: + +* -h or --help : show help message and exit +* -v or --version : show utility name and version +* -i or --input-dir : extract from given input directory +* -o or --output-dir : extract in given output directory +* -e or --auto-exit : skip press enter to exit prompts +* --static : use static-built external dependencies + +#### **Compatibility** + +Should work at all Windows, Linux or macOS operating systems which have Python 3.8 support. + +#### **Prerequisites** + +No prerequisites needed to run the utility. + +#### **Build/Freeze/Compile with PyInstaller** + +PyInstaller can build/freeze/compile the utility at all three supported platforms, it is simple to run and gets updated often. + +1. Make sure Python 3.8.0 or newer is installed: + +> python --version + +2. Use pip to install PyInstaller: + +> pip3 install pyinstaller + +3. Build/Freeze/Compile: + +> pyinstaller --noupx --onefile \\/Phoenix_TDK_Extract.py + +At dist folder you should find the final utility executable + +#### **Anti-Virus False Positives** + +Some Anti-Virus software may claim that the built/frozen/compiled executable contains viruses. Any such detections are false positives, usually of PyInstaller. You can switch to a better Anti-Virus software, report the false positive to their support, add the executable to the exclusions, build/freeze/compile yourself or use the Python script directly. + +#### **Pictures** + +![]() + +## **Portwell EFI Update Extractor** ![]() diff --git a/common/a7z_comp.py b/common/a7z_comp.py index 99de48a..5293d68 100644 --- a/common/a7z_comp.py +++ b/common/a7z_comp.py @@ -23,7 +23,7 @@ def is_7z_supported(in_path, padding=0, static=False): try: subprocess.run([get_7z_path(static), 't', in_path, '-bso0', '-bse0', '-bsp0'], check=True) except: - printer('Error: 7-Zip could not check support for file %s!' % in_path, padding) + printer(f'Error: 7-Zip could not check support for file {in_path}!', padding) return False @@ -38,10 +38,10 @@ def a7z_decompress(in_path, out_path, in_name, padding=0, static=False): if not os.path.isdir(out_path): raise Exception('EXTRACT_DIR_MISSING') except: - printer('Error: 7-Zip could not extract %s file %s!' % (in_name, in_path), padding) + printer(f'Error: 7-Zip could not extract {in_name} file {in_path}!', padding) return 1 - printer('Succesfull %s decompression via 7-Zip!' % in_name, padding) + printer(f'Succesfull {in_name} decompression via 7-Zip!', padding) return 0 diff --git a/common/efi_comp.py b/common/efi_comp.py index e2c283f..da6b57f 100644 --- a/common/efi_comp.py +++ b/common/efi_comp.py @@ -43,7 +43,7 @@ def efi_decompress(in_path, out_path, padding=0, comp_type='--uefi'): if os.path.getsize(out_path) != size_orig: raise Exception('EFI_DECOMPRESS_ERROR') except: - printer('Error: TianoCompress could not extract file %s!' % in_path, padding) + printer(f'Error: TianoCompress could not extract file {in_path}!', padding) return 1 diff --git a/common/patterns.py b/common/patterns.py index d66f268..326cdcb 100644 --- a/common/patterns.py +++ b/common/patterns.py @@ -13,3 +13,4 @@ PAT_DELL_FTR = re.compile(br'\xEE\xAA\xEE\x8F\x49\x1B\xE8\xAE\x14\x37\x90') PAT_DELL_HDR = re.compile(br'\xEE\xAA\x76\x1B\xEC\xBB\x20\xF1\xE6\x51.\x78\x9C', re.DOTALL) PAT_DELL_PKG = re.compile(br'\x72\x13\x55\x00.{45}7zXZ', re.DOTALL) PAT_INTEL_ENG = re.compile(br'\x04\x00{3}[\xA1\xE1]\x00{3}.{8}\x86\x80.{9}\x00\$((MN2)|(MAN))', re.DOTALL) +PAT_PHOENIX_TDK = re.compile(br'\$PACK\x00{3}..\x00{2}.\x00{3}', re.DOTALL) diff --git a/common/system.py b/common/system.py index b08ae1f..621755c 100644 --- a/common/system.py +++ b/common/system.py @@ -35,7 +35,7 @@ def check_sys_py(): sys_py = get_py_ver() if sys_py < (3,8): - sys.stdout.write('\nError: Python >= 3.8 required, not %d.%d!' % (sys_py[0], sys_py[1])) + sys.stdout.write(f'\nError: Python >= 3.8 required, not {sys_py[0]}.{sys_py[1]}!') if not is_auto_exit(): # noinspection PyUnresolvedReferences @@ -48,7 +48,7 @@ def check_sys_os(): os_tag,os_win,os_sup = get_os_ver() if not os_sup: - printer('Error: Unsupported platform "%s"!' % os_tag) + printer(f'Error: Unsupported platform "{os_tag}"!') if not is_auto_exit(): input('\nPress enter to exit')