Added Phoenix TDK Packer Extractor v2.0_a4

f-strings all the things!
This commit is contained in:
platomav 2022-05-22 00:24:20 +03:00
parent b4a93513f7
commit 7bb0c5f9a9
10 changed files with 379 additions and 138 deletions

View file

@ -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)

View file

@ -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

View file

@ -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]

182
Phoenix_TDK_Extract.py Normal file
View file

@ -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)

View file

@ -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)

View file

@ -6,9 +6,10 @@
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DJDZD3PRGCSCL"><img border="0" title="BIOS Utilities Donation via Paypal or Debit/Credit Card" alt="BIOS Utilities Donation via Paypal or Debit/Credit Card" src="https://user-images.githubusercontent.com/11527726/109392268-e0f68280-7923-11eb-83d8-0a63f0d20783.png"></a>
* [**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 \<path-to-project\>\/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**
![]()

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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')