From 4749414f813d837d304025dae04b4007f129976b Mon Sep 17 00:00:00 2001 From: platomav Date: Wed, 6 Jul 2022 17:54:17 +0300 Subject: [PATCH] Insyde iFlash/iFdPacker Extractor v2.0_a8 Improved PFAT, UCP, PFS, TDK format check methods Cleanup/improvements to all utilities --- AMI_PFAT_Extract.py | 69 ++++++----- AMI_UCP_Extract.py | 129 ++++++++++--------- Award_BIOS_Extract.py | 7 +- Dell_PFS_Extract.py | 252 ++++++++++++++++++++++---------------- Fujitsu_UPC_Extract.py | 5 +- Insyde_IFD_Extract.py | 222 +++++++++++++++++++++++++++++++++ Insyde_iFlash_Extract.py | 148 ---------------------- Panasonic_BIOS_Extract.py | 15 ++- Phoenix_TDK_Extract.py | 28 +++-- Portwell_EFI_Extract.py | 24 ++-- README.md | 64 +++++----- Toshiba_COM_Extract.py | 8 +- VAIO_Package_Extract.py | 21 ++-- common/comp_efi.py | 12 +- common/comp_szip.py | 12 +- common/externals.py | 1 + common/path_ops.py | 6 +- common/patterns.py | 1 + common/struct_ops.py | 3 +- common/system.py | 9 +- external/requirements.txt | 2 +- 21 files changed, 603 insertions(+), 435 deletions(-) create mode 100644 Insyde_IFD_Extract.py delete mode 100644 Insyde_iFlash_Extract.py diff --git a/AMI_PFAT_Extract.py b/AMI_PFAT_Extract.py index e6dd03c..bb9fdae 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_a10' +TITLE = 'AMI BIOS Guard Extractor v4.0_a11' import os import re @@ -19,10 +19,10 @@ sys.dont_write_bytecode = True from common.externals import get_bgs_tool from common.num_ops import get_ordinal -from common.path_ops import safe_name, make_dirs +from common.path_ops import make_dirs, safe_name from common.patterns import PAT_AMI_PFAT -from common.struct_ops import get_struct, char, uint8_t, uint16_t, uint32_t -from common.system import script_init, argparse_init, printer +from common.struct_ops import char, get_struct, uint8_t, uint16_t, uint32_t +from common.system import argparse_init, printer, script_init from common.text_ops import file_to_bytes class AmiBiosGuardHeader(ctypes.LittleEndianStructure): @@ -128,22 +128,22 @@ class IntelBiosGuardSignature2k(ctypes.LittleEndianStructure): 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) +def is_ami_pfat(input_file): + input_buffer = file_to_bytes(input_file) - return bool(get_ami_pfat(input_buffer)[0]) + return bool(get_ami_pfat(input_buffer)) -def get_ami_pfat(input_buffer): +def get_ami_pfat(input_file): + input_buffer = file_to_bytes(input_file) + match = PAT_AMI_PFAT.search(input_buffer) - buffer = input_buffer[match.start() - 0x8:] if match else b'' - - return match, buffer + return input_buffer[match.start() - 0x8:] if match else b'' def get_file_name(index, name): return safe_name(f'{index:02d} -- {name}') -def parse_bg_script(script_data, padding): +def parse_bg_script(script_data, padding=0): is_opcode_div = len(script_data) % 8 == 0 if not is_opcode_div: @@ -177,7 +177,7 @@ def parse_bg_script(script_data, padding): return 0 -def parse_pfat_hdr(buffer, padding): +def parse_pfat_hdr(buffer, padding=0): block_all = [] pfat_hdr = get_struct(buffer, 0x0, AmiBiosGuardHeader) @@ -220,7 +220,11 @@ def parse_pfat_hdr(buffer, padding): return block_all, hdr_size, files_count -def parse_pfat_file(buffer, output_path, padding): +def parse_pfat_file(input_file, output_path, padding=0): + input_buffer = file_to_bytes(input_file) + + pfat_buffer = get_ami_pfat(input_buffer) + file_path = '' all_blocks_dict = {} @@ -230,7 +234,7 @@ def parse_pfat_file(buffer, output_path, padding): make_dirs(extract_path, delete=True) - block_all,block_off,file_count = parse_pfat_hdr(buffer, padding) + block_all,block_off,file_count = parse_pfat_hdr(pfat_buffer, padding) for block in block_all: file_desc,file_name,_,_,_,file_index,block_index,block_count = block @@ -244,7 +248,7 @@ def parse_pfat_file(buffer, output_path, padding): block_status = f'{block_index + 1}/{block_count}' - bg_hdr = get_struct(buffer, block_off, IntelBiosGuardHeader) + bg_hdr = get_struct(pfat_buffer, block_off, IntelBiosGuardHeader) printer(f'Intel BIOS Guard {block_status} Header:\n', padding + 8) @@ -252,11 +256,11 @@ def parse_pfat_file(buffer, output_path, padding): bg_script_bgn = block_off + PFAT_BLK_HDR_LEN bg_script_end = bg_script_bgn + bg_hdr.ScriptSize - bg_script_bin = buffer[bg_script_bgn:bg_script_end] + bg_script_bin = pfat_buffer[bg_script_bgn:bg_script_end] bg_data_bgn = bg_script_end bg_data_end = bg_data_bgn + bg_hdr.DataSize - bg_data_bin = buffer[bg_data_bgn:bg_data_end] + bg_data_bin = pfat_buffer[bg_data_bgn:bg_data_end] block_off = bg_data_end # Assume next block starts at data end @@ -265,7 +269,7 @@ def parse_pfat_file(buffer, output_path, padding): if is_sfam: bg_sig_bgn = bg_data_end bg_sig_end = bg_sig_bgn + PFAT_BLK_S2K_LEN - bg_sig_bin = buffer[bg_sig_bgn:bg_sig_end] + bg_sig_bin = pfat_buffer[bg_sig_bgn:bg_sig_end] if len(bg_sig_bin) == PFAT_BLK_S2K_LEN: bg_sig = get_struct(bg_sig_bin, 0x0, IntelBiosGuardSignature2k) @@ -280,21 +284,22 @@ def parse_pfat_file(buffer, output_path, padding): _ = parse_bg_script(bg_script_bin, padding + 12) - with open(file_path, 'ab') as out_dat: out_dat.write(bg_data_bin) + with open(file_path, 'ab') as out_dat: + out_dat.write(bg_data_bin) all_blocks_dict[file_index] += bg_data_bin - pfat_oob_data = buffer[block_off:] # Store out-of-bounds data after the end of PFAT files + pfat_oob_data = pfat_buffer[block_off:] # Store out-of-bounds data after the end of PFAT files 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) + with open(pfat_oob_path, 'wb') as out_oob: + out_oob.write(pfat_oob_data) - oob_pfat_match,pfat_oob_buffer = get_ami_pfat(pfat_oob_data) - - if oob_pfat_match: parse_pfat_file(pfat_oob_buffer, pfat_oob_path, padding) + if is_ami_pfat(pfat_oob_data): + parse_pfat_file(pfat_oob_data, pfat_oob_path, padding) in_all_data = b''.join([block[1] for block in sorted(all_blocks_dict.items())]) @@ -302,7 +307,10 @@ def parse_pfat_file(buffer, output_path, padding): 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) + with open(in_all_path, 'wb') as out_all: + out_all.write(in_all_data + pfat_oob_data) + + return 0 PFAT_AMI_HDR_LEN = ctypes.sizeof(AmiBiosGuardHeader) PFAT_BLK_HDR_LEN = ctypes.sizeof(IntelBiosGuardHeader) @@ -321,18 +329,17 @@ if __name__ == '__main__': printer(['***', input_name], padding - 4) - with open(input_file, 'rb') as in_file: input_buffer = in_file.read() + with open(input_file, 'rb') as in_file: + input_buffer = in_file.read() - pfat_match,pfat_buffer = get_ami_pfat(input_buffer) - - if not pfat_match: + if not is_ami_pfat(input_buffer): printer('Error: This is not an AMI BIOS Guard (PFAT) image!', padding) continue # Next input file extract_path = os.path.join(output_path, input_name) - parse_pfat_file(pfat_buffer, extract_path, padding) + parse_pfat_file(input_buffer, extract_path, padding) exit_code -= 1 diff --git a/AMI_UCP_Extract.py b/AMI_UCP_Extract.py index 44b1f98..0c9d03d 100644 --- a/AMI_UCP_Extract.py +++ b/AMI_UCP_Extract.py @@ -7,7 +7,7 @@ AMI UCP Update Extractor Copyright (C) 2021-2022 Plato Mavropoulos """ -TITLE = 'AMI UCP Update Extractor v2.0_a17' +TITLE = 'AMI UCP Update Extractor v2.0_a18' import os import re @@ -21,14 +21,14 @@ sys.dont_write_bytecode = True from common.checksums import get_chk_16 from common.comp_efi import efi_decompress, is_efi_compressed -from common.comp_szip import is_szip_supported, szip_decompress -from common.path_ops import agnostic_path, safe_name, safe_path, make_dirs +from common.path_ops import agnostic_path, make_dirs, safe_name, safe_path from common.patterns import PAT_AMI_UCP, PAT_INTEL_ENG -from common.struct_ops import get_struct, char, uint8_t, uint16_t, uint32_t -from common.system import script_init, argparse_init, printer +from common.struct_ops import char, get_struct, uint8_t, uint16_t, uint32_t +from common.system import argparse_init, printer, script_init from common.text_ops import file_to_bytes, to_string -from AMI_PFAT_Extract import get_ami_pfat, parse_pfat_file +from AMI_PFAT_Extract import is_ami_pfat, parse_pfat_file +from Insyde_IFD_Extract import insyde_ifd_extract, is_insyde_ifd class UafHeader(ctypes.LittleEndianStructure): _pack_ = 1 @@ -166,9 +166,10 @@ def is_ami_ucp(in_file): return bool(get_ami_ucp(buffer)[0] is not None) # Get all input file AMI UCP patterns -def get_ami_ucp(buffer): +def get_ami_ucp(in_file): + buffer = file_to_bytes(in_file) + uaf_len_max = 0x0 # Length of largest detected @UAF|@HPU - uaf_hdr_off = None # Offset of largest detected @UAF|@HPU uaf_buf_bin = None # Buffer of largest detected @UAF|@HPU uaf_buf_tag = '@UAF' # Tag of largest detected @UAF|@HPU @@ -181,7 +182,7 @@ def get_ami_ucp(buffer): uaf_buf_bin = buffer[uaf_hdr_off:uaf_hdr_off + uaf_len_max] uaf_buf_tag = uaf.group(0)[:4].decode('utf-8','ignore') - return uaf_hdr_off, uaf_buf_bin, uaf_buf_tag + return uaf_buf_bin, uaf_buf_tag # Get list of @UAF|@HPU Modules def get_uaf_mod(buffer, uaf_off=0x0): @@ -196,19 +197,23 @@ def get_uaf_mod(buffer, uaf_off=0x0): uaf_off += uaf_hdr.ModuleSize # Adjust to next @UAF|@HPU Module offset - if uaf_off >= len(buffer): break # Stop parsing at EOF + if uaf_off >= len(buffer): + break # Stop parsing at EOF # Check if @UAF|@HPU Module @NAL exists and place it first # Parsing @NAL first allows naming all @UAF|@HPU Modules for mod_idx,mod_val in enumerate(uaf_all): if mod_val[0] == '@NAL': uaf_all.insert(1, uaf_all.pop(mod_idx)) # After UII for visual purposes + break # @NAL found, skip the rest return uaf_all # Parse & Extract AMI UCP structures -def ucp_extract(buffer, out_path, ucp_tag='@UAF', padding=0, is_checksum=False): +def ucp_extract(in_file, out_path, padding=0, checksum=False): + input_buffer = file_to_bytes(in_file) + nal_dict = {} # Initialize @NAL Dictionary per UCP printer('Utility Configuration Program', padding) @@ -217,13 +222,16 @@ def ucp_extract(buffer, out_path, ucp_tag='@UAF', padding=0, is_checksum=False): make_dirs(extract_path, delete=True) - uaf_hdr = get_struct(buffer, 0, UafHeader) # Parse @UAF|@HPU Header Structure + # Get best AMI UCP Pattern match based on @UAF|@HPU Size + ucp_buffer,ucp_tag = get_ami_ucp(input_buffer) + + uaf_hdr = get_struct(ucp_buffer, 0, UafHeader) # Parse @UAF|@HPU Header Structure printer(f'Utility Auxiliary File > {ucp_tag}:\n', padding + 4) uaf_hdr.struct_print(padding + 8) - fake = struct.pack(' @UAF|@HPU Module/Section -def uaf_extract(buffer, extract_path, mod_info, padding=0, is_checksum=False, nal_dict=None): - if nal_dict is None: nal_dict = {} +def uaf_extract(buffer, extract_path, mod_info, padding=0, checksum=False, nal_dict=None): + if nal_dict is None: + nal_dict = {} uaf_tag,uaf_off,uaf_hdr = mod_info @@ -259,16 +269,26 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, is_checksum=False, na is_comp = uaf_mod.CompressSize != uaf_mod.OriginalSize # Detect @UAF|@HPU Module EFI Compression - 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 = 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 + 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 = 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' @@ -289,7 +309,8 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, is_checksum=False, na else: uaf_fname = safe_path(extract_path, uaf_sname) - if is_checksum: chk16_validate(uaf_data_all, uaf_tag, padding + 4) + if checksum: + chk16_validate(uaf_data_all, uaf_tag, padding + 4) # Parse Utility Identification Information @UAF|@HPU Module (@UII) if uaf_tag == '@UII': @@ -304,7 +325,8 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, is_checksum=False, na info_hdr.struct_print(padding + 8, info_desc) # Print @UII Module Info - if is_checksum: chk16_validate(uaf_data_raw, '@UII > Info', padding + 8) + if checksum: + chk16_validate(uaf_data_raw, '@UII > Info', padding + 8) # Store/Save @UII Module Info in file with open(uaf_fname[:-4] + '.txt', 'a', encoding='utf-8') as uii_out: @@ -324,14 +346,16 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, is_checksum=False, na # Store/Save @UAF|@HPU Module file if uaf_tag != '@UII': # Skip @UII binary, already parsed - with open(uaf_fname, 'wb') as uaf_out: uaf_out.write(uaf_data_raw) + with open(uaf_fname, 'wb') as uaf_out: + uaf_out.write(uaf_data_raw) # @UAF|@HPU Module EFI/Tiano Decompression if is_comp and is_efi_compressed(uaf_data_raw, False): dec_fname = uaf_fname.replace('.temp', uaf_fext) # Decompressed @UAF|@HPU Module file path if efi_decompress(uaf_fname, dec_fname, padding + 4) == 0: - with open(dec_fname, 'rb') as dec: uaf_data_raw = dec.read() # Read back the @UAF|@HPU Module decompressed Raw data + with open(dec_fname, 'rb') as dec: + uaf_data_raw = dec.read() # Read back the @UAF|@HPU Module decompressed Raw data os.remove(uaf_fname) # Successful decompression, delete compressed @UAF|@HPU Module file @@ -392,39 +416,32 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, is_checksum=False, na nal_dict[info_tag] = (info_path,info_name) # Assign a file path & name to each Tag # Parse Insyde BIOS @UAF|@HPU Module (@INS) - if uaf_tag == '@INS' and is_szip_supported(uaf_fname, padding + 4, check=True): - ins_dir = os.path.join(extract_path, safe_name(f'{uaf_tag}_nested-SFX')) # Generate extraction directory + if uaf_tag == '@INS' and is_insyde_ifd(uaf_fname): + ins_dir = os.path.join(extract_path, safe_name(f'{uaf_tag}_nested-IFD')) # Generate extraction directory - printer('Insyde BIOS 7z SFX Archive:', padding + 4) - - if szip_decompress(uaf_fname, ins_dir, '7z SFX', padding + 8, check=True) == 0: - os.remove(uaf_fname) # Successful extraction, delete @INS Module file/archive + if insyde_ifd_extract(uaf_fname, ins_dir, padding + 4) == 0: + os.remove(uaf_fname) # Delete raw nested Insyde IFD image after successful extraction # Detect & Unpack AMI BIOS Guard (PFAT) BIOS image - pfat_match,pfat_buffer = get_ami_pfat(uaf_data_raw) - - if pfat_match: + if is_ami_pfat(uaf_data_raw): pfat_dir = os.path.join(extract_path, safe_name(uaf_name)) - parse_pfat_file(pfat_buffer, pfat_dir, padding + 4) + parse_pfat_file(uaf_data_raw, pfat_dir, padding + 4) - os.remove(uaf_fname) # Delete PFAT Module file after extraction + os.remove(uaf_fname) # Delete raw PFAT BIOS image after successful extraction # Detect Intel Engine firmware image and show ME Analyzer advice if uaf_tag.startswith('@ME') and PAT_INTEL_ENG.search(uaf_data_raw): printer('Intel Management Engine (ME) Firmware:\n', padding + 4) printer('Use "ME Analyzer" from https://github.com/platomav/MEAnalyzer', padding + 8, False) - # Get best Nested AMI UCP Pattern match based on @UAF|@HPU Size - nested_uaf_off,nested_uaf_bin,nested_uaf_tag = get_ami_ucp(uaf_data_raw) - - # Parse Nested AMI UCP Structure - if nested_uaf_off is not None: + # Parse Nested AMI UCP image + if is_ami_ucp(uaf_data_raw): 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) # Call recursively + ucp_extract(uaf_data_raw, uaf_dir, padding + 4, checksum) # Call recursively - os.remove(uaf_fname) # Delete raw nested AMI UCP Structure after successful recursion/extraction + os.remove(uaf_fname) # Delete raw nested AMI UCP image after successful extraction return nal_dict @@ -497,7 +514,7 @@ if __name__ == '__main__': argparser.add_argument('-c', '--checksum', help='verify AMI UCP Checksums (slow)', action='store_true') arguments = argparser.parse_args() - is_checksum = arguments.checksum # Set Checksum verification optional argument + checksum = arguments.checksum # Set Checksum verification optional argument # Initialize script (must be after argparse) exit_code,input_files,output_path,padding = script_init(TITLE, arguments, 4) @@ -507,19 +524,17 @@ if __name__ == '__main__': printer(['***', input_name], padding - 4) - with open(input_file, 'rb') as in_file: input_buffer = in_file.read() + with open(input_file, 'rb') as in_file: + input_buffer = in_file.read() - # Get best AMI UCP Pattern match based on @UAF|@HPU Size - main_uaf_off,main_uaf_bin,main_uaf_tag = get_ami_ucp(input_buffer) - - if main_uaf_off is None: + if not is_ami_ucp(input_buffer): printer('Error: This is not an AMI UCP Update executable!', padding) continue # Next input file extract_path = os.path.join(output_path, input_name) - ucp_extract(main_uaf_bin, extract_path, main_uaf_tag, padding, is_checksum) + ucp_extract(input_buffer, extract_path, padding, checksum) exit_code -= 1 diff --git a/Award_BIOS_Extract.py b/Award_BIOS_Extract.py index 14189ff..f06963f 100644 --- a/Award_BIOS_Extract.py +++ b/Award_BIOS_Extract.py @@ -7,7 +7,7 @@ Award BIOS Module Extractor Copyright (C) 2018-2022 Plato Mavropoulos """ -TITLE = 'Award BIOS Module Extractor v2.0_a3' +TITLE = 'Award BIOS Module Extractor v2.0_a4' import os import sys @@ -18,7 +18,7 @@ sys.dont_write_bytecode = True from common.comp_szip import szip_decompress from common.path_ops import make_dirs, safe_name from common.patterns import PAT_AWARD_LZH -from common.system import script_init, argparse_init, printer +from common.system import argparse_init, printer, script_init from common.text_ops import file_to_bytes # Check if input is Award BIOS image @@ -84,7 +84,8 @@ if __name__ == '__main__': printer(['***', input_name], padding - 4) - with open(input_file, 'rb') as in_file: input_buffer = in_file.read() + with open(input_file, 'rb') as in_file: + input_buffer = in_file.read() if not is_award_bios(input_buffer): printer('Error: This is not an Award BIOS image!', padding) diff --git a/Dell_PFS_Extract.py b/Dell_PFS_Extract.py index d322423..18650a3 100644 --- a/Dell_PFS_Extract.py +++ b/Dell_PFS_Extract.py @@ -3,11 +3,11 @@ """ Dell PFS Extract -Dell PFS Update Extractor +Dell PFS/PKG Update Extractor Copyright (C) 2018-2022 Plato Mavropoulos """ -TITLE = 'Dell PFS Update Extractor v6.0_a10' +TITLE = 'Dell PFS/PKG Update Extractor v6.0_a11' import os import io @@ -21,10 +21,10 @@ import contextlib sys.dont_write_bytecode = True from common.checksums import get_chk_8_xor -from common.path_ops import safe_name, make_dirs -from common.patterns import PAT_DELL_HDR, PAT_DELL_FTR, PAT_DELL_PKG -from common.struct_ops import get_struct, char, uint8_t, uint16_t, uint32_t, uint64_t -from common.system import script_init, argparse_init, printer +from common.path_ops import make_dirs, path_stem, safe_name +from common.patterns import PAT_DELL_FTR, PAT_DELL_HDR, PAT_DELL_PKG +from common.struct_ops import char, get_struct, uint8_t, uint16_t, uint32_t, uint64_t +from common.system import argparse_init, printer, script_init from common.text_ops import file_to_bytes from AMI_PFAT_Extract import IntelBiosGuardHeader, IntelBiosGuardSignature2k, parse_bg_script @@ -190,7 +190,9 @@ class DellPfsPfatMetadata(ctypes.LittleEndianStructure): # Each section starts with a 0x30 header, which begins with pattern 72135500. # The section length is found at 0x10-0x14 and its (optional) MD5 hash at 0x20-0x30. # Section data can be raw or LZMA2 (7zXZ) compressed. The latter contains the PFS update image. -def is_pfs_pkg(in_buffer): +def is_pfs_pkg(in_file): + in_buffer = file_to_bytes(in_file) + return PAT_DELL_PKG.search(in_buffer) # The Dell PFS update images usually contain multiple sections. @@ -198,28 +200,57 @@ def is_pfs_pkg(in_buffer): # where ******** is the zlib stream size, ++ is the section type and -- the header Checksum XOR 8. # The "Firmware" section has type AA and its files are stored in PFS format. # The "Utility" section has type BB and its files are stored in PFS, BIN or 7z formats. -def is_pfs_hdr(in_buffer): - return list(PAT_DELL_HDR.finditer(in_buffer)) +def is_pfs_hdr(in_file): + in_buffer = file_to_bytes(in_file) + + return bool(PAT_DELL_HDR.search(in_buffer)) # Each section is followed by the footer pattern ********EEAAEE8F491BE8AE143790--, # where ******** is the zlib stream size and ++ the footer Checksum XOR 8. -def is_pfs_ftr(in_buffer): - return PAT_DELL_FTR.search(in_buffer) +def is_pfs_ftr(in_file): + in_buffer = file_to_bytes(in_file) + + return bool(PAT_DELL_FTR.search(in_buffer)) # Check if input is Dell PFS/PKG image def is_dell_pfs(in_file): in_buffer = file_to_bytes(in_file) - return bool(is_pfs_hdr(in_buffer) or is_pfs_pkg(in_buffer)) + is_pkg = is_pfs_pkg(in_buffer) + + is_hdr = is_pfs_hdr(in_buffer) + + is_ftr = is_pfs_ftr(in_buffer) + + return bool(is_pkg or is_hdr and is_ftr) + +# Extract Dell ThinOS PKG 7zXZ +def thinos_pkg_extract(in_file): + in_buffer = file_to_bytes(in_file) + + # Search input image for ThinOS PKG 7zXZ header + thinos_pkg_match = PAT_DELL_PKG.search(in_buffer) + + lzma_len_off = thinos_pkg_match.start() + 0x10 + lzma_len_int = int.from_bytes(in_buffer[lzma_len_off:lzma_len_off + 0x4], 'little') + lzma_bin_off = thinos_pkg_match.end() - 0x5 + lzma_bin_dat = in_buffer[lzma_bin_off:lzma_bin_off + lzma_len_int] + + # Check if the compressed 7zXZ stream is complete + if len(lzma_bin_dat) != lzma_len_int: + return in_buffer + + return lzma.decompress(lzma_bin_dat) # Get PFS ZLIB Section Offsets def get_section_offsets(buffer): - pfs_zlib_init = is_pfs_hdr(buffer) - - if not pfs_zlib_init: return [] # No PFS ZLIB detected - pfs_zlib_list = [] # Initialize PFS ZLIB offset list + pfs_zlib_init = list(PAT_DELL_HDR.finditer(buffer)) + + if not pfs_zlib_init: + return pfs_zlib_list # No PFS ZLIB detected + # Remove duplicate/nested PFS ZLIB offsets for zlib_c in pfs_zlib_init: is_duplicate = False # Initialize duplicate/nested PFS ZLIB offset @@ -228,14 +259,16 @@ def get_section_offsets(buffer): zlib_o_size = int.from_bytes(buffer[zlib_o.start() - 0x5:zlib_o.start() - 0x1], 'little') # If current PFS ZLIB offset is within another PFS ZLIB range (start-end), set as duplicate - if zlib_o.start() < zlib_c.start() < zlib_o.start() + zlib_o_size: is_duplicate = True + if zlib_o.start() < zlib_c.start() < zlib_o.start() + zlib_o_size: + is_duplicate = True - if not is_duplicate: pfs_zlib_list.append(zlib_c.start()) + if not is_duplicate: + pfs_zlib_list.append(zlib_c.start()) return pfs_zlib_list # Dell PFS ZLIB Section Parser -def pfs_section_parse(zlib_data, zlib_start, output_path, pfs_name, pfs_index, pfs_count, is_rec, padding, is_structure=True, is_advanced=True): +def pfs_section_parse(zlib_data, zlib_start, output_path, pfs_name, pfs_index, pfs_count, is_rec, padding, structure=True, advanced=True): 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) @@ -281,11 +314,8 @@ def pfs_section_parse(zlib_data, zlib_start, output_path, pfs_name, pfs_index, p # Store the PFS ZLIB section footer contents (16 bytes) footer_data = zlib_data[compressed_end:compressed_end + 0x10] - # Search input section for PFS ZLIB section footer - pfs_zlib_footer_match = is_pfs_ftr(footer_data) - # Check if PFS ZLIB section footer was found in the section - if not pfs_zlib_footer_match: + if not is_pfs_ftr(footer_data): printer('Error: This Dell PFS ZLIB section is corrupted!', padding) is_zlib_error = True @@ -304,18 +334,19 @@ def pfs_section_parse(zlib_data, zlib_start, output_path, pfs_name, pfs_index, p # Decompress PFS ZLIB section payload try: - if is_zlib_error: raise Exception('ZLIB_ERROR') # ZLIB errors are critical + if is_zlib_error: + raise Exception('ZLIB_ERROR') # ZLIB errors are critical section_data = zlib.decompress(compressed_data) # ZLIB decompression except: section_data = zlib_data # Fallback to raw ZLIB data upon critical error # Call the PFS Extract function on the decompressed PFS ZLIB Section - pfs_extract(section_data, pfs_index, pfs_name, pfs_count, section_path, padding, is_structure, is_advanced) + pfs_extract(section_data, pfs_index, pfs_name, pfs_count, section_path, padding, structure, advanced) # Parse & Extract Dell PFS Volume -def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, is_structure=True, is_advanced=True): +def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, structure=True, advanced=True): # Show PFS Volume indicator - if is_structure: + if structure: printer('PFS Volume:', pfs_padd) # Get PFS Header Structure values @@ -328,7 +359,7 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i return # Critical error, abort # Show PFS Header Structure info - if is_structure: + if structure: printer('PFS Header:\n', pfs_padd + 4) pfs_hdr.struct_print(pfs_padd + 8) @@ -348,7 +379,7 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i while len(pfs_payload[entry_start:entry_start + pfs_entry_size]) == pfs_entry_size: # Analyze PFS Entry Structure and get relevant info _,entry_version,entry_guid,entry_data,entry_data_sig,entry_met,entry_met_sig,next_entry = \ - parse_pfs_entry(pfs_payload, entry_start, pfs_entry_size, pfs_entry_struct, 'PFS Entry', pfs_padd, is_structure) + parse_pfs_entry(pfs_payload, entry_start, pfs_entry_size, pfs_entry_struct, 'PFS Entry', pfs_padd, structure) entry_type = 'OTHER' # Adjusted later if PFS Entry is Zlib, PFAT, PFS Info, Model Info @@ -384,7 +415,7 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i entry_info_hdr = get_struct(filename_info, info_start, DellPfsInfo) # Show PFS Information Header Structure info - if is_structure: + if structure: printer('PFS Information Header:\n', pfs_padd + 4) entry_info_hdr.struct_print(pfs_padd + 8) @@ -408,7 +439,7 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i entry_name = safe_name(name_data.decode('utf-16').strip()) # PFS Entry's FileName value # Show PFS FileName Structure info - if is_structure: + if structure: printer('PFS FileName Entry:\n', pfs_padd + 8) entry_info_mod.struct_print(pfs_padd + 12, entry_name) @@ -436,7 +467,7 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i entry_info = get_struct(entry_metadata, 0, DellPfsMetadata) # Show Nested PFS Metadata Structure info - if is_structure: + if structure: printer('PFS Metadata Information:\n', pfs_padd + 4) entry_info.struct_print(pfs_padd + 8) @@ -460,7 +491,7 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i entry_info_hdr = get_struct(signature_info, sign_start, DellPfsInfo) # Show PFS Information Header Structure info - if is_structure: + if structure: printer('PFS Information Header:\n', pfs_padd + 4) entry_info_hdr.struct_print(pfs_padd + 8) @@ -476,7 +507,7 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i entry_hdr = get_struct(signature_info, sign_start + PFS_INFO_LEN, pfs_entry_struct) # Show PFS Information Header Structure info - if is_structure: + if structure: printer('PFS Information Entry:\n', pfs_padd + 8) entry_hdr.struct_print(pfs_padd + 12) @@ -486,7 +517,7 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i sign_data_raw = signature_info[sign_info_start + 0x2:sign_info_start + 0x2 + sign_size] sign_data_txt = f'{int.from_bytes(sign_data_raw, "little"):0{sign_size * 2}X}' - if is_structure: + if structure: printer('Signature Information:\n', pfs_padd + 8) printer(f'Signature Size: 0x{sign_size:X}', pfs_padd + 12, False) printer(f'Signature Data: {sign_data_txt[:32]} [...]', pfs_padd + 12, False) @@ -500,7 +531,8 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i entry_type = entries_all[index][3] # Get PFS Entry Type # Very small PFS Entry Data cannot be of special type - if len(entry_data) < PFS_HEAD_LEN: continue + if len(entry_data) < PFS_HEAD_LEN: + continue # Check if PFS Entry contains zlib-compressed sub-PFS Volume pfs_zlib_offsets = get_section_offsets(entry_data) @@ -518,7 +550,7 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i if pfat_entry_hdr.Tag == b'PFS.HDR.' and is_pfat: entry_type = 'PFAT' # Re-set PFS Entry Type from OTHER to PFAT, to use such info afterwards - entry_data = parse_pfat_pfs(pfat_entry_hdr, entry_data, pfs_padd, is_structure) # Parse sub-PFS PFAT Volume + entry_data = parse_pfat_pfs(pfat_entry_hdr, entry_data, pfs_padd, structure) # Parse sub-PFS PFAT Volume # Parse PFS Entry which contains zlib-compressed sub-PFS Volume elif pfs_zlib_offsets: @@ -539,7 +571,7 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i sub_pfs_path = os.path.join(output_path, str(pfs_count) + safe_name(sub_pfs_name)) # Recursively call the PFS ZLIB Section Parser function for the sub-PFS Volume (pfs_index = pfs_count) - pfs_section_parse(entry_data, offset, sub_pfs_path, sub_pfs_name, pfs_count, pfs_count, True, pfs_padd + 4, is_structure, is_advanced) + pfs_section_parse(entry_data, offset, sub_pfs_path, sub_pfs_name, pfs_count, pfs_count, True, pfs_padd + 4, structure, advanced) entries_all[index][4] = entry_data # Adjust PFS Entry Data after parsing PFAT (same ZLIB raw data, not stored afterwards) entries_all[index][3] = entry_type # Adjust PFS Entry Type from OTHER to PFAT or ZLIB (ZLIB is ignored at file extraction) @@ -560,10 +592,12 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i file_name = 'Model Information' elif file_type == 'NAME_INFO': file_name = 'Filename Information' - if not is_advanced: continue # Don't store Filename Information in non-advanced user mode + if not advanced: + continue # Don't store Filename Information in non-advanced user mode elif file_type == 'SIG_INFO': file_name = 'Signature Information' - if not is_advanced: continue # Don't store Signature Information in non-advanced user mode + if not advanced: + continue # Don't store Signature Information in non-advanced user mode else: file_name = '' @@ -580,6 +614,7 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i file_version = info_version info_all[info_index][0] = 'USED' # PFS with zlib-compressed sub-PFS use the same GUID + break # Break at 1st Name match to not rename again from next zlib-compressed sub-PFS with the same GUID # For both advanced & non-advanced users, the goal is to store final/usable files only @@ -590,29 +625,36 @@ def pfs_extract(buffer, pfs_index, pfs_name, pfs_count, output_path, pfs_padd, i is_zlib = bool(file_type == 'ZLIB') # Determine if PFS Entry Data was zlib-compressed - if file_data and not is_zlib: write_files.append([file_data, 'data']) # PFS Entry Data Payload - if file_data_sig and is_advanced: write_files.append([file_data_sig, 'sign_data']) # PFS Entry Data Signature - if file_meta and (is_zlib or is_advanced): write_files.append([file_meta, 'meta']) # PFS Entry Metadata Payload - if file_meta_sig and is_advanced: write_files.append([file_meta_sig, 'sign_meta']) # PFS Entry Metadata Signature + if file_data and not is_zlib: + write_files.append([file_data, 'data']) # PFS Entry Data Payload + + if file_data_sig and advanced: + write_files.append([file_data_sig, 'sign_data']) # PFS Entry Data Signature + + if file_meta and (is_zlib or advanced): + write_files.append([file_meta, 'meta']) # PFS Entry Metadata Payload + + if file_meta_sig and advanced: + write_files.append([file_meta_sig, 'sign_meta']) # PFS Entry Metadata Signature # Write/Extract PFS Entry files for file in write_files: 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) + pfs_file_write(file[0], file[1], file_type, full_name, output_path, pfs_padd, structure, advanced) # Get PFS Footer Data after PFS Header Payload pfs_footer = buffer[PFS_HEAD_LEN + pfs_hdr.PayloadSize:PFS_HEAD_LEN + pfs_hdr.PayloadSize + PFS_FOOT_LEN] # Analyze PFS Footer Structure - chk_pfs_ftr(pfs_footer, pfs_payload, pfs_hdr.PayloadSize, 'PFS', pfs_padd, is_structure) + chk_pfs_ftr(pfs_footer, pfs_payload, pfs_hdr.PayloadSize, 'PFS', pfs_padd, structure) # Analyze Dell PFS Entry Structure -def parse_pfs_entry(entry_buffer, entry_start, entry_size, entry_struct, text, padding, is_structure=True): +def parse_pfs_entry(entry_buffer, entry_start, entry_size, entry_struct, text, padding, structure=True): # Get PFS Entry Structure values pfs_entry = get_struct(entry_buffer, entry_start, entry_struct) # Show PFS Entry Structure info - if is_structure: + if structure: printer('PFS Entry:\n', padding + 4) pfs_entry.struct_print(padding + 8) @@ -653,13 +695,13 @@ def parse_pfs_entry(entry_buffer, entry_start, entry_size, entry_struct, text, p return pfs_entry, entry_version, entry_guid, entry_data, entry_data_sig, entry_met, entry_met_sig, entry_met_sig_end # Parse Dell PFS Volume with PFAT Payload -def parse_pfat_pfs(entry_hdr, entry_data, padding, is_structure=True): +def parse_pfat_pfs(entry_hdr, entry_data, padding, structure=True): # Show PFS Volume indicator - if is_structure: + if structure: printer('PFS Volume:', padding + 4) # Show sub-PFS Header Structure Info - if is_structure: + if structure: printer('PFS Header:\n', padding + 8) entry_hdr.struct_print(padding + 12) @@ -679,11 +721,11 @@ def parse_pfat_pfs(entry_hdr, entry_data, padding, is_structure=True): _, pfs_entry_size = get_pfs_entry(pfat_payload, 0) # Get initial PFS PFAT Entry Size for loop while len(pfat_payload[pfat_entry_start:pfat_entry_start + pfs_entry_size]) == pfs_entry_size: # Get sub-PFS PFAT Entry Structure & Size info - pfat_entry_struct, pfat_entry_size = get_pfs_entry(pfat_payload, pfat_entry_start) + pfat_entry_struct,pfat_entry_size = get_pfs_entry(pfat_payload, pfat_entry_start) # Analyze sub-PFS PFAT Entry Structure and get relevant info pfat_entry,_,_,pfat_entry_data,_,pfat_entry_met,_,pfat_next_entry = parse_pfs_entry(pfat_payload, - pfat_entry_start, pfat_entry_size, pfat_entry_struct, 'sub-PFS PFAT Entry', padding + 4, is_structure) + pfat_entry_start, pfat_entry_size, pfat_entry_struct, 'sub-PFS PFAT Entry', padding + 4, structure) # Each sub-PFS PFAT Entry includes an AMI BIOS Guard (a.k.a. PFAT) block at the beginning # We need to parse the PFAT block and remove its contents from the final Payload/Raw Data @@ -693,7 +735,7 @@ def parse_pfat_pfs(entry_hdr, entry_data, padding, is_structure=True): pfat_hdr = get_struct(pfat_payload, pfat_hdr_off, IntelBiosGuardHeader) # Show sub-PFS PFAT Header Structure info - if is_structure: + if structure: printer(f'PFAT Block {pfat_entry_index} Header:\n', padding + 12) pfat_hdr.struct_print(padding + 16) @@ -718,15 +760,14 @@ def parse_pfat_pfs(entry_hdr, entry_data, padding, is_structure=True): pfat_sig = get_struct(pfat_payload, pfat_payload_end, IntelBiosGuardSignature2k) # Show sub-PFS PFAT Signature Structure info - if is_structure: + if structure: 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: + if structure: 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) # The payload of sub-PFS PFAT Entries is not in proper order by default @@ -740,7 +781,7 @@ def parse_pfat_pfs(entry_hdr, entry_data, padding, is_structure=True): pfat_met = get_struct(pfat_entry_met, 0, DellPfsPfatMetadata) # Show sub-PFS PFAT Metadata Structure info - if is_structure: + if structure: printer(f'PFAT Block {pfat_entry_index} Metadata:\n', padding + 12) pfat_met.struct_print(padding + 16) @@ -772,14 +813,15 @@ def parse_pfat_pfs(entry_hdr, entry_data, padding, is_structure=True): pfat_data_all.sort() # Sort all sub-PFS PFAT Entries payloads/data based on their Order/Offset entry_data = b'' # Initialize new sub-PFS Entry Data - for pfat_data in pfat_data_all: entry_data += pfat_data[1] # Merge all sub-PFS PFAT Entry Payload/Raw into the final sub-PFS Entry Data + for pfat_data in pfat_data_all: + entry_data += pfat_data[1] # Merge all sub-PFS PFAT Entry Payload/Raw into the final sub-PFS Entry Data # Verify that the Order/Offset of the last PFAT Entry w/ its Size matches the final sub-PFS Entry Data Size if len(entry_data) != pfat_data_all[-1][0] + len(pfat_data_all[-1][1]): printer('Error: Detected sub-PFS PFAT Entry Buffer & Last Offset Size mismatch!', padding + 8) # Analyze sub-PFS Footer Structure - chk_pfs_ftr(pfat_footer, pfat_payload, entry_hdr.PayloadSize, 'Sub-PFS', padding + 4, is_structure) + chk_pfs_ftr(pfat_footer, pfat_payload, entry_hdr.PayloadSize, 'Sub-PFS', padding + 4, structure) return entry_data @@ -787,8 +829,11 @@ def parse_pfat_pfs(entry_hdr, entry_data, padding, is_structure=True): def get_pfs_entry(buffer, offset): pfs_entry_ver = int.from_bytes(buffer[offset + 0x10:offset + 0x14], 'little') # PFS Entry Version - if pfs_entry_ver == 1: return DellPfsEntryR1, ctypes.sizeof(DellPfsEntryR1) - if pfs_entry_ver == 2: return DellPfsEntryR2, ctypes.sizeof(DellPfsEntryR2) + if pfs_entry_ver == 1: + return DellPfsEntryR1, ctypes.sizeof(DellPfsEntryR1) + + if pfs_entry_ver == 2: + return DellPfsEntryR2, ctypes.sizeof(DellPfsEntryR2) return DellPfsEntryR2, ctypes.sizeof(DellPfsEntryR2) @@ -801,28 +846,35 @@ 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 += 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 += f'{field:X}{eol}' # Unknown + 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 += f'{field:X}{eol}' # Unknown return version # Check if Dell PFS Header Version is known def chk_hdr_ver(version, text, padding): - if version in (1,2): return + if version in (1,2): + return printer(f'Error: Unknown {text} Header Version {version}!', padding) + + return # Analyze Dell PFS Footer Structure -def chk_pfs_ftr(footer_buffer, data_buffer, data_size, text, padding, is_structure=True): +def chk_pfs_ftr(footer_buffer, data_buffer, data_size, text, padding, structure=True): # Get PFS Footer Structure values pfs_ftr = get_struct(footer_buffer, 0, DellPfsFooter) # Validate that a PFS Footer was parsed if pfs_ftr.Tag == b'PFS.FTR.': # Show PFS Footer Structure info - if is_structure: + if structure: printer('PFS Footer:\n', padding + 4) pfs_ftr.struct_print(padding + 8) else: @@ -840,29 +892,31 @@ def chk_pfs_ftr(footer_buffer, data_buffer, data_size, text, padding, is_structu 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): +def pfs_file_write(bin_buff, bin_name, bin_type, full_name, out_path, padding, structure=True, advanced=True): # Store Data/Metadata Signature (advanced users only) if bin_name.startswith('sign'): 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 + with open(final_path, 'wb') as pfs_out: + pfs_out.write(bin_buff) # Write final Data/Metadata Signature return # Skip further processing for Signatures # Store Data/Metadata Payload - bin_ext = f'.{bin_name}.bin' if is_advanced else '.bin' # Simpler Data/Metadata Extension for non-advanced users + bin_ext = f'.{bin_name}.bin' if 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) + is_text,final_data,file_ext,write_mode = bin_is_text(bin_buff, bin_type, bin_name == 'meta', padding, structure, advanced) 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 + with open(final_path, write_mode) as pfs_out: + pfs_out.write(final_data) # Write final Data/Metadata Payload # Check if Dell PFS Entry file/data is Text/XML and Convert -def bin_is_text(buffer, file_type, is_metadata, pfs_padd, is_structure=True, is_advanced=True): +def bin_is_text(buffer, file_type, is_metadata, pfs_padd, structure=True, advanced=True): is_text = False write_mode = 'wb' extension = '.bin' @@ -892,12 +946,13 @@ def bin_is_text(buffer, file_type, is_metadata, pfs_padd, is_structure=True, is_ buffer = text_buffer.getvalue() # Show Model/PCR XML Information, if applicable - if is_structure and is_text and not is_metadata: # Metadata is shown at initial DellPfsMetadata analysis + if structure and is_text and not is_metadata: # Metadata is shown at initial DellPfsMetadata analysis 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 - if is_advanced: return False, buffer_in, '.bin', 'wb' + if advanced: + return False, buffer_in, '.bin', 'wb' return is_text, buffer, extension, write_mode @@ -918,8 +973,8 @@ if __name__ == '__main__': argparser.add_argument('-s', '--structure', help='show PFS structure information', action='store_true') arguments = argparser.parse_args() - is_advanced = arguments.advanced # Set Advanced user mode optional argument - is_structure = arguments.structure # Set Structure output mode optional argument + advanced = arguments.advanced # Set Advanced user mode optional argument + structure = arguments.structure # Set Structure output mode optional argument # Initialize script (must be after argparse) exit_code,input_files,output_path,padding = script_init(TITLE, arguments, 4) @@ -929,42 +984,25 @@ if __name__ == '__main__': printer(['***', input_name], padding - 4) - with open(input_file, 'rb') as in_file: input_buffer = in_file.read() - - # Search input image for ThinOS PKG 7zXZ section header - lzma_pkg_hdr_match = is_pfs_pkg(input_buffer) - - # Decompress ThinOS PKG 7zXZ section first, if present - if lzma_pkg_hdr_match: - lzma_len_off = lzma_pkg_hdr_match.start() + 0x10 - lzma_len_int = int.from_bytes(input_buffer[lzma_len_off:lzma_len_off + 0x4], 'little') - lzma_bin_off = lzma_pkg_hdr_match.end() - 0x5 - lzma_bin_dat = input_buffer[lzma_bin_off:lzma_bin_off + lzma_len_int] - - # Check if the compressed 7zXZ stream is complete, based on header - if len(lzma_bin_dat) != lzma_len_int: - printer('Error: This Dell ThinOS PKG update image is corrupted!', padding) - - continue # Next input file - - input_buffer = lzma.decompress(lzma_bin_dat) - - # Search input image for PFS ZLIB Sections - pfs_zlib_offsets = get_section_offsets(input_buffer) - - if not pfs_zlib_offsets: - printer('Error: This is not a Dell PFS update image!', padding) + with open(input_file, 'rb') as in_file: + input_buffer = in_file.read() + + if not is_dell_pfs(input_buffer): + printer('Error: This is not a Dell PFS/PKG Update image!', padding) continue # Next input file extract_path = os.path.join(output_path, f'{input_name}_extracted') - extract_name = ' ' + os.path.splitext(input_name)[0] + extract_name = ' ' + path_stem(input_file) + + if is_pfs_pkg(input_buffer): + input_buffer = thinos_pkg_extract(input_buffer) # Parse each PFS ZLIB Section - for offset in pfs_zlib_offsets: + for zlib_offset in get_section_offsets(input_buffer): # Call the PFS ZLIB Section Parser function - pfs_section_parse(input_buffer, offset, extract_path, extract_name, 1, 1, False, padding, is_structure, is_advanced) + pfs_section_parse(input_buffer, zlib_offset, extract_path, extract_name, 1, 1, False, padding, structure, advanced) exit_code -= 1 diff --git a/Fujitsu_UPC_Extract.py b/Fujitsu_UPC_Extract.py index 229e8e0..24a7ebd 100644 --- a/Fujitsu_UPC_Extract.py +++ b/Fujitsu_UPC_Extract.py @@ -7,7 +7,7 @@ Fujitsu UPC BIOS Extractor Copyright (C) 2021-2022 Plato Mavropoulos """ -TITLE = 'Fujitsu UPC BIOS Extractor v2.0_a3' +TITLE = 'Fujitsu UPC BIOS Extractor v2.0_a4' import os import sys @@ -55,7 +55,8 @@ if __name__ == '__main__': printer(['***', input_name], padding - 4) - with open(input_file, 'rb') as in_file: input_buffer = in_file.read() + with open(input_file, 'rb') as in_file: + input_buffer = in_file.read() if not is_fujitsu_upc(input_buffer): printer('Error: This is not a Fujitsu UPC BIOS image!', padding) diff --git a/Insyde_IFD_Extract.py b/Insyde_IFD_Extract.py new file mode 100644 index 0000000..0189ed9 --- /dev/null +++ b/Insyde_IFD_Extract.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python3 +#coding=utf-8 + +""" +Insyde IFD Extract +Insyde iFlash/iFdPacker Extractor +Copyright (C) 2022 Plato Mavropoulos +""" + +TITLE = 'Insyde iFlash/iFdPacker Extractor v2.0_a8' + +import os +import sys +import ctypes + +# Stop __pycache__ generation +sys.dont_write_bytecode = True + +from common.comp_szip import is_szip_supported, szip_decompress +from common.path_ops import get_path_files, make_dirs, safe_name +from common.patterns import PAT_INSYDE_IFL, PAT_INSYDE_SFX +from common.struct_ops import char, get_struct, uint32_t +from common.system import argparse_init, printer, script_init +from common.text_ops import file_to_bytes + +class IflashHeader(ctypes.LittleEndianStructure): + _pack_ = 1 + _fields_ = [ + ('Signature', char*8), # 0x00 $_IFLASH + ('ImageTag', char*8), # 0x08 + ('TotalSize', uint32_t), # 0x10 from header end + ('ImageSize', uint32_t), # 0x14 from header end + # 0x18 + ] + + def get_image_tag(self): + return self.ImageTag.decode('utf-8','ignore').strip('_') + + def struct_print(self, p): + printer(['Signature :', self.Signature.decode('utf-8')], p, False) + printer(['Image Name:', self.get_image_tag()], p, False) + printer(['Image Size:', f'0x{self.ImageSize:X}'], p, False) + printer(['Total Size:', f'0x{self.TotalSize:X}'], p, False) + +# Check if input is Insyde iFlash/iFdPacker Update image +def is_insyde_ifd(input_file): + input_buffer = file_to_bytes(input_file) + + is_ifl = bool(insyde_iflash_detect(input_buffer)) + + is_sfx = bool(PAT_INSYDE_SFX.search(input_buffer)) + + return is_ifl or is_sfx + +# Parse & Extract Insyde iFlash/iFdPacker Update images +def insyde_ifd_extract(input_file, output_path, padding=0): + input_buffer = file_to_bytes(input_file) + + extract_path = os.path.join(f'{output_path}_extracted') + + iflash_code = insyde_iflash_extract(input_buffer, extract_path, padding) + + ifdpack_path = os.path.join(extract_path, 'Insyde iFdPacker SFX') + + ifdpack_code = insyde_packer_extract(input_buffer, ifdpack_path, padding) + + return iflash_code and ifdpack_code + +# Detect Insyde iFlash Update image +def insyde_iflash_detect(input_buffer): + iflash_match_all = [] + iflash_match_nan = [0x0,0xFFFFFFFF] + + for iflash_match in PAT_INSYDE_IFL.finditer(input_buffer): + ifl_bgn = iflash_match.start() + + if len(input_buffer[ifl_bgn:]) <= IFL_HDR_LEN: + continue + + ifl_hdr = get_struct(input_buffer, ifl_bgn, IflashHeader) + + if ifl_hdr.TotalSize in iflash_match_nan \ + or ifl_hdr.ImageSize in iflash_match_nan \ + or ifl_hdr.TotalSize <= ifl_hdr.ImageSize: + continue + + iflash_match_all.append([ifl_bgn, ifl_hdr]) + + return iflash_match_all + +# Extract Insyde iFlash Update image +def insyde_iflash_extract(input_buffer, extract_path, padding=0): + insyde_iflash_all = insyde_iflash_detect(input_buffer) + + if not insyde_iflash_all: + return 1 + + printer('Detected Insyde iFlash Update image!', padding) + + make_dirs(extract_path, delete=True) + + for insyde_iflash in insyde_iflash_all: + ifl_bgn,ifl_hdr = insyde_iflash + + img_bgn = ifl_bgn + IFL_HDR_LEN + img_end = img_bgn + ifl_hdr.ImageSize + img_bin = input_buffer[img_bgn:img_end] + + img_val = [ifl_hdr.get_image_tag(), 'bin'] + img_tag,img_ext = IFL_IMG_NAMES.get(img_val[0], img_val) + + img_name = f'{img_tag} [0x{img_bgn:08X}-0x{img_end:08X}]' + + printer(f'{img_name}\n', padding + 4) + + ifl_hdr.struct_print(padding + 8) + + if img_val == [img_tag,img_ext]: + printer(f'Note: Detected new Insyde iFlash tag {img_tag}!', padding + 12, pause=True) + + out_name = f'{img_name}.{img_ext}' + + out_path = os.path.join(extract_path, safe_name(out_name)) + + with open(out_path, 'wb') as out_image: + out_image.write(img_bin) + + printer(f'Succesfull Insyde iFlash > {img_tag} extraction!', padding + 12) + + return 0 + +# Extract Insyde iFdPacker 7-Zip SFX 7z Update image +def insyde_packer_extract(input_buffer, extract_path, padding=0): + match_sfx = PAT_INSYDE_SFX.search(input_buffer) + + if not match_sfx: + return 127 + + printer('Detected Insyde iFdPacker Update image!', padding) + + make_dirs(extract_path, delete=True) + + sfx_buffer = bytearray(input_buffer[match_sfx.end() - 0x5:]) + + if sfx_buffer[:0x5] == b'\x6E\xF4\x79\x5F\x4E': + printer('Detected Insyde iFdPacker > 7-Zip SFX obfuscation!', padding + 4) + + for index,byte in enumerate(sfx_buffer): + sfx_buffer[index] = byte // 2 + (128 if byte % 2 else 0) + + printer('Removed Insyde iFdPacker > 7-Zip SFX obfuscation!', padding + 8) + + printer('Extracting Insyde iFdPacker > 7-Zip SFX archive...', padding + 4) + + sfx_path = os.path.join(extract_path, 'Insyde_iFdPacker_SFX.7z') + + with open(sfx_path, 'wb') as sfx_file: + sfx_file.write(sfx_buffer) + + if is_szip_supported(sfx_path, padding + 8, check=True): + if szip_decompress(sfx_path, extract_path, 'Insyde iFdPacker > 7-Zip SFX', padding + 8, check=True) == 0: + os.remove(sfx_path) + else: + return 125 + else: + return 126 + + exit_codes = [] + + for sfx_file in get_path_files(extract_path): + if is_insyde_ifd(sfx_file): + printer(f'{os.path.basename(sfx_file)}', padding + 12) + ifd_code = insyde_ifd_extract(sfx_file, sfx_file, padding + 16) + exit_codes.append(ifd_code) + + return sum(exit_codes) + +# Insyde iFlash Image Names +IFL_IMG_NAMES = { + 'BIOSCER' : ['Certificate', 'bin'], + 'BIOSCR2' : ['Certificate 2nd', 'bin'], + 'BIOSIMG' : ['BIOS-UEFI', 'bin'], + 'DRV_IMG' : ['isflash', 'efi'], + 'EC_IMG' : ['Embedded Controller', 'bin'], + 'INI_IMG' : ['platform', 'ini'], + 'ME_IMG' : ['Management Engine', 'bin'], + 'OEM_ID' : ['OEM Identifier', 'bin'], + } + +# Get common ctypes Structure Sizes +IFL_HDR_LEN = ctypes.sizeof(IflashHeader) + +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() + + if not is_insyde_ifd(input_buffer): + printer('Error: This is not an Insyde iFlash/iFdPacker Update image!', padding) + + continue # Next input file + + extract_path = os.path.join(output_path, input_name) + + insyde_ifd_extract(input_buffer, extract_path, padding) + + exit_code -= 1 + + printer('Done!', pause=True) + + sys.exit(exit_code) diff --git a/Insyde_iFlash_Extract.py b/Insyde_iFlash_Extract.py deleted file mode 100644 index 7489e60..0000000 --- a/Insyde_iFlash_Extract.py +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env python3 -#coding=utf-8 - -""" -Insyde iFlash Extract -Insyde iFlash Update Extractor -Copyright (C) 2022 Plato Mavropoulos -""" - -TITLE = 'Insyde iFlash Update Extractor v2.0_a2' - -import os -import sys -import ctypes - -# Stop __pycache__ generation -sys.dont_write_bytecode = True - -from common.path_ops import make_dirs, safe_name -from common.patterns import PAT_INSYDE_IFL -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 IflashHeader(ctypes.LittleEndianStructure): - _pack_ = 1 - _fields_ = [ - ('Signature', char*9), # 0x00 $_IFLASH_ - ('ImageTag', char*7), # 0x08 - ('TotalSize', uint32_t), # 0x10 from header end - ('ImageSize', uint32_t), # 0x14 from header end - # 0x18 - ] - - def struct_print(self, p): - printer(['Signature :', self.Signature.decode('utf-8','ignore')], p, False) - printer(['Image Name:', self.ImageTag.decode('utf-8','ignore')], p, False) - printer(['Image Size:', f'0x{self.ImageSize:X}'], p, False) - printer(['Total Size:', f'0x{self.TotalSize:X}'], p, False) - -# Parse & Extract Insyde iFlash Update image -def insyde_iflash_extract(input_buffer, ins_ifl_all, output_path, padding=0): - extract_path = os.path.join(f'{output_path}_extracted') - - make_dirs(extract_path, delete=True) - - for ins_ifl_val in ins_ifl_all: - ins_ifl_off,ins_ifl_hdr = ins_ifl_val - - mod_bgn = ins_ifl_off + IFL_HDR_LEN - mod_end = mod_bgn + ins_ifl_hdr.ImageSize - mod_bin = input_buffer[mod_bgn:mod_end] - - mod_val = [ins_ifl_hdr.ImageTag.decode('utf-8','ignore'), 'bin'] - mod_tag,mod_ext = IFL_MOD_NAMES.get(mod_val[0], mod_val) - - mod_name = f'{mod_tag} [0x{mod_bgn:08X}-0x{mod_end:08X}]' - - printer(f'{mod_name}\n', padding) - - ins_ifl_hdr.struct_print(padding + 4) - - if mod_val == [mod_tag,mod_ext]: - printer(f'Note: Detected new Insyde iFlash image tag {mod_tag}!', padding + 8, pause=True) - - out_name = f'{mod_name}.{mod_ext}' - - out_path = os.path.join(extract_path, safe_name(out_name)) - - with open(out_path, 'wb') as out: out.write(mod_bin) - - printer('Succesfull Insyde iFlash image extraction!', padding + 8) - -# Get Insyde iFlash Update image matches -def get_insyde_iflash(in_file): - ins_ifl_all = [] - ins_ifl_nan = [0x0,0xFFFFFFFF] - - buffer = file_to_bytes(in_file) - - for ins_ifl_match in PAT_INSYDE_IFL.finditer(buffer): - ins_ifl_off = ins_ifl_match.start() - - if len(buffer[ins_ifl_off:]) <= IFL_HDR_LEN: - continue - - ins_ifl_hdr = get_struct(buffer, ins_ifl_off, IflashHeader) - - if ins_ifl_hdr.TotalSize in ins_ifl_nan \ - or ins_ifl_hdr.ImageSize in ins_ifl_nan \ - or ins_ifl_hdr.TotalSize <= ins_ifl_hdr.ImageSize: - continue - - ins_ifl_all.append([ins_ifl_off, ins_ifl_hdr]) - - return ins_ifl_all - -# Check if input is Insyde iFlash Update image -def is_insyde_iflash(in_file): - buffer = file_to_bytes(in_file) - - return bool(get_insyde_iflash(buffer)) - -IFL_MOD_NAMES = { - 'DRV_IMG' : ['isflash', 'efi'], - 'INI_IMG' : ['platform', 'ini'], - 'BIOSIMG' : ['BIOS-UEFI', 'bin'], - 'ME_IMG_' : ['Management Engine', 'bin'], - 'EC_IMG_' : ['Embedded Controller', 'bin'], - 'OEM_ID_' : ['OEM Identifier', 'bin'], - 'BIOSCER' : ['Certificate', 'bin'], - 'BIOSCR2' : ['Certificate 2nd', 'bin'], - } - -# Get common ctypes Structure Sizes -IFL_HDR_LEN = ctypes.sizeof(IflashHeader) - -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() - - ins_ifl_all = get_insyde_iflash(input_buffer) - - if not ins_ifl_all: - printer('Error: This is not an Insyde iFlash Update image!', padding) - - continue # Next input file - - extract_path = os.path.join(output_path, input_name) - - insyde_iflash_extract(input_buffer, ins_ifl_all, extract_path, padding) - - exit_code -= 1 - - printer('Done!', pause=True) - - sys.exit(exit_code) diff --git a/Panasonic_BIOS_Extract.py b/Panasonic_BIOS_Extract.py index 3373f68..900f1a6 100644 --- a/Panasonic_BIOS_Extract.py +++ b/Panasonic_BIOS_Extract.py @@ -7,7 +7,7 @@ Panasonic BIOS Package Extractor Copyright (C) 2018-2022 Plato Mavropoulos """ -TITLE = 'Panasonic BIOS Package Extractor v2.0_a8' +TITLE = 'Panasonic BIOS Package Extractor v2.0_a9' import os import io @@ -22,10 +22,10 @@ from common.comp_szip import is_szip_supported, szip_decompress from common.path_ops import get_path_files, make_dirs, path_stem, safe_name from common.pe_ops import get_pe_file, get_pe_info, is_pe_file, show_pe_info from common.patterns import PAT_MICROSOFT_CAB -from common.system import script_init, argparse_init, printer +from common.system import argparse_init, printer, script_init from common.text_ops import file_to_bytes -from AMI_PFAT_Extract import get_ami_pfat, parse_pfat_file +from AMI_PFAT_Extract import is_ami_pfat, parse_pfat_file # Check if input is Panasonic BIOS Package PE def is_panasonic_pkg(in_file): @@ -117,12 +117,10 @@ def panasonic_res_extract(pe_name, pe_file, extract_path, padding=0): printer('Succesfull PE Resource extraction!', padding + 8) # Detect & Unpack AMI BIOS Guard (PFAT) BIOS image - pfat_match,pfat_buffer = get_ami_pfat(res_raw) - - if pfat_match: + if is_ami_pfat(res_raw): pfat_dir = os.path.join(extract_path, res_tag) - parse_pfat_file(pfat_buffer, pfat_dir, padding + 12) + parse_pfat_file(res_raw, pfat_dir, padding + 12) else: if is_pe_file(res_raw): res_ext = 'exe' @@ -219,7 +217,8 @@ if __name__ == '__main__': printer(['***', input_name], padding - 4) - with open(input_file, 'rb') as in_file: input_buffer = in_file.read() + with open(input_file, 'rb') as in_file: + input_buffer = in_file.read() # Check if Panasonic BIOS Package pattern was found on executable if not is_panasonic_pkg(input_buffer): diff --git a/Phoenix_TDK_Extract.py b/Phoenix_TDK_Extract.py index b36bc91..5d7456a 100644 --- a/Phoenix_TDK_Extract.py +++ b/Phoenix_TDK_Extract.py @@ -7,7 +7,7 @@ Phoenix TDK Packer Extractor Copyright (C) 2021-2022 Plato Mavropoulos """ -TITLE = 'Phoenix TDK Packer Extractor v2.0_a8' +TITLE = 'Phoenix TDK Packer Extractor v2.0_a9' import os import sys @@ -17,11 +17,11 @@ import ctypes # Stop __pycache__ generation sys.dont_write_bytecode = True -from common.path_ops import safe_name, make_dirs +from common.path_ops import make_dirs, safe_name from common.pe_ops import get_pe_file, get_pe_info -from common.patterns import PAT_PHOENIX_TDK, PAT_MICROSOFT_MZ, PAT_MICROSOFT_PE -from common.struct_ops import get_struct, char, uint32_t -from common.system import script_init, argparse_init, printer +from common.patterns import PAT_MICROSOFT_MZ, PAT_MICROSOFT_PE, PAT_PHOENIX_TDK +from common.struct_ops import char, get_struct, uint32_t +from common.system import argparse_init, printer, script_init from common.text_ops import file_to_bytes class PhoenixTdkHeader(ctypes.LittleEndianStructure): @@ -152,7 +152,9 @@ def is_phoenix_tdk(in_file): return bool(get_phoenix_tdk(buffer)[1] is not None) # Parse & Extract Phoenix Tools Development Kit (TDK) Packer -def phoenix_tdk_extract(input_buffer, output_path, pack_off, base_off=0, padding=0): +def phoenix_tdk_extract(input_file, output_path, padding=0): + input_buffer = file_to_bytes(input_file) + exit_code = 0 extract_path = os.path.join(f'{output_path}_extracted') @@ -161,6 +163,8 @@ def phoenix_tdk_extract(input_buffer, output_path, pack_off, base_off=0, padding printer('Phoenix Tools Development Kit Packer', padding) + base_off,pack_off = get_phoenix_tdk(input_buffer) + # Parse TDK Header structure tdk_hdr = get_struct(input_buffer, pack_off, PhoenixTdkHeader) @@ -224,7 +228,8 @@ def phoenix_tdk_extract(input_buffer, output_path, pack_off, base_off=0, padding 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) + with open(mod_file, 'wb') as out_file: + out_file.write(mod_data) return exit_code @@ -248,19 +253,18 @@ if __name__ == '__main__': printer(['***', input_name], padding - 4) - with open(input_file, 'rb') as in_file: input_buffer = in_file.read() - - tdk_base_off,tdk_pack_off = get_phoenix_tdk(input_buffer) + with open(input_file, 'rb') as in_file: + input_buffer = in_file.read() # Check if Phoenix TDK Packer pattern was found on executable - if tdk_pack_off is None: + 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, tdk_pack_off, tdk_base_off, padding) == 0: + if phoenix_tdk_extract(input_buffer, extract_path, padding) == 0: exit_code -= 1 printer('Done!', pause=True) diff --git a/Portwell_EFI_Extract.py b/Portwell_EFI_Extract.py index 1876f02..316181e 100644 --- a/Portwell_EFI_Extract.py +++ b/Portwell_EFI_Extract.py @@ -7,7 +7,7 @@ Portwell EFI Update Extractor Copyright (C) 2021-2022 Plato Mavropoulos """ -TITLE = 'Portwell EFI Update Extractor v2.0_a10' +TITLE = 'Portwell EFI Update Extractor v2.0_a11' import os import sys @@ -16,10 +16,10 @@ import sys sys.dont_write_bytecode = True from common.comp_efi import efi_decompress, is_efi_compressed -from common.path_ops import safe_name, make_dirs +from common.path_ops import make_dirs, safe_name from common.pe_ops import get_pe_file -from common.patterns import PAT_PORTWELL_EFI, PAT_MICROSOFT_MZ -from common.system import script_init, argparse_init, printer +from common.patterns import PAT_MICROSOFT_MZ, PAT_PORTWELL_EFI +from common.system import argparse_init, printer, script_init from common.text_ops import file_to_bytes FILE_NAMES = { @@ -34,10 +34,13 @@ FILE_NAMES = { def is_portwell_efi(in_file): in_buffer = file_to_bytes(in_file) - try: pe_buffer = get_portwell_pe(in_buffer)[1] - except: pe_buffer = b'' + try: + pe_buffer = get_portwell_pe(in_buffer)[1] + except: + pe_buffer = b'' is_mz = PAT_MICROSOFT_MZ.search(in_buffer[:0x2]) # EFI images start with PE Header MZ + is_uu = PAT_PORTWELL_EFI.search(pe_buffer[:0x4]) # Portwell EFI files start with return bool(is_mz and is_uu) @@ -104,7 +107,8 @@ def get_unpacker_tag(input_buffer, pe_file): # Process Portwell UEFI Unpacker payload files def parse_efi_files(extract_path, efi_files, padding): for file_index,file_data in enumerate(efi_files): - if file_data in (b'', b'NULL'): continue # Skip empty/unused files + if file_data in (b'', b'NULL'): + continue # Skip empty/unused files file_name = FILE_NAMES.get(file_index, f'Unknown_{file_index}.bin') # Assign Name to EFI file @@ -115,7 +119,8 @@ def parse_efi_files(extract_path, efi_files, padding): file_path = os.path.join(extract_path, safe_name(file_name)) # Store EFI file output path - with open(file_path, 'wb') as out_file: out_file.write(file_data) # Store EFI file data to drive + with open(file_path, 'wb') as out_file: + out_file.write(file_data) # Store EFI file data to drive # Attempt to detect EFI compression & decompress when applicable if is_efi_compressed(file_data): @@ -139,7 +144,8 @@ if __name__ == '__main__': printer(['***', input_name], padding - 4) - with open(input_file, 'rb') as in_file: input_buffer = in_file.read() + with open(input_file, 'rb') as in_file: + input_buffer = in_file.read() if not is_portwell_efi(input_buffer): printer('Error: This is not a Portwell EFI Update Package!', padding) diff --git a/README.md b/README.md index 17dbfef..360342b 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ * [**AMI BIOS Guard Extractor**](#ami-bios-guard-extractor) * [**AMI UCP Update Extractor**](#ami-ucp-update-extractor) * [**Award BIOS Module Extractor**](#award-bios-module-extractor) -* [**Dell PFS Update Extractor**](#dell-pfs-update-extractor) +* [**Dell PFS/PKG Update Extractor**](#dell-pfs-pkg-update-extractor) * [**Fujitsu UPC BIOS Extractor**](#fujitsu-upc-bios-extractor) -* [**Insyde iFlash Update Extractor**](#insyde-iflash-update-extractor) +* [**Insyde iFlash/iFdPacker Extractor**](#insyde-iflash-ifdpacker-update-extractor) * [**Panasonic BIOS Package Extractor**](#panasonic-bios-package-extractor) * [**Phoenix TDK Packer Extractor**](#phoenix-tdk-packer-extractor) * [**Portwell EFI Update Extractor**](#portwell-efi-update-extractor) @@ -37,7 +37,7 @@ You can either Drag & Drop or manually enter AMI BIOS Guard (PFAT) image file(s) #### **Compatibility** -Should work at all Windows, Linux or macOS operating systems which have Python 3.8 support. +Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support. #### **Prerequisites** @@ -49,7 +49,7 @@ Optionally, to decompile the AMI PFAT \> Intel BIOS Guard Scripts, you must have 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: +1. Make sure Python 3.10.0 or newer is installed: > python --version @@ -81,7 +81,7 @@ Some Anti-Virus software may claim that the built/frozen/compiled executable con #### **Description** -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. +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 iFlash/iFdPacker structures. The output comprises only final firmware components and utilities which are directly usable by end users. #### **Usage** @@ -96,7 +96,7 @@ You can either Drag & Drop or manually enter AMI UCP Update executable file(s). #### **Compatibility** -Should work at all Windows, Linux or macOS operating systems which have Python 3.8 support. +Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support. #### **Prerequisites** @@ -113,7 +113,7 @@ Optionally, to decompile the AMI UCP \> AMI PFAT \> Intel BIOS Guard Scripts (wh 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: +1. Make sure Python 3.10.0 or newer is installed: > python --version @@ -161,7 +161,7 @@ You can either Drag & Drop or manually enter Award BIOS image file(s). Optional #### **Compatibility** -Should work at all Windows, Linux or macOS operating systems which have Python 3.8 support. +Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support. #### **Prerequisites** @@ -173,7 +173,7 @@ To run the utility, you must have the following 3rd party tool at the "external" 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: +1. Make sure Python 3.10.0 or newer is installed: > python --version @@ -199,17 +199,17 @@ Some Anti-Virus software may claim that the built/frozen/compiled executable con ![]() -## **Dell PFS Update Extractor** +## **Dell PFS/PKG Update Extractor** ![]() #### **Description** -Parses Dell PFS Update images and extracts their Firmware (e.g. SPI, BIOS/UEFI, EC, ME etc) and Utilities (e.g. Flasher etc) component sections. It supports all Dell PFS revisions and formats, including those which are originally LZMA compressed in ThinOS packages, ZLIB compressed or Intel BIOS Guard (PFAT) protected. The output comprises only final firmware components which are directly usable by end users. +Parses Dell PFS/PKG Update images and extracts their Firmware (e.g. SPI, BIOS/UEFI, EC, ME etc) and Utilities (e.g. Flasher etc) component sections. It supports all Dell PFS/PKG revisions and formats, including those which are originally LZMA compressed in ThinOS packages, ZLIB compressed or Intel BIOS Guard (PFAT) protected. The output comprises only final firmware components which are directly usable by end users. #### **Usage** -You can either Drag & Drop or manually enter Dell PFS Update images(s). Optional arguments: +You can either Drag & Drop or manually enter Dell PFS/PKG Update images(s). Optional arguments: * -h or --help : show help message and exit * -v or --version : show utility name and version @@ -221,7 +221,7 @@ You can either Drag & Drop or manually enter Dell PFS Update images(s). Optional #### **Compatibility** -Should work at all Windows, Linux or macOS operating systems which have Python 3.8 support. +Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support. #### **Prerequisites** @@ -233,7 +233,7 @@ Optionally, to decompile the Intel BIOS Guard (PFAT) Scripts, you must have the 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: +1. Make sure Python 3.10.0 or newer is installed: > python --version @@ -279,7 +279,7 @@ You can either Drag & Drop or manually enter Fujitsu UPC BIOS image file(s). Opt #### **Compatibility** -Should work at all Windows, Linux or macOS operating systems which have Python 3.8 support. +Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support. #### **Prerequisites** @@ -291,7 +291,7 @@ To run the utility, you must have the following 3rd party tool at the "external" 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: +1. Make sure Python 3.10.0 or newer is installed: > python --version @@ -317,17 +317,17 @@ Some Anti-Virus software may claim that the built/frozen/compiled executable con ![]() -## **Insyde iFlash Update Extractor** +## **Insyde iFlash/iFdPacker Extractor** ![]() #### **Description** -Parses Insyde iFlash Update images and extracts their firmware (e.g. SPI, BIOS/UEFI, EC, ME etc) and utilities (e.g. Flasher, Configuration etc) components. The output comprises only final firmware components which are directly usable by end users. +Parses Insyde iFlash/iFdPacker Update images and extracts their firmware (e.g. SPI, BIOS/UEFI, EC, ME etc) and utilities (e.g. Flasher, Configuration etc) components. It supports all Insyde iFlash/iFdPacker revisions and formats, including those which are \7-Zip SFX 7z compressed in raw or obfuscated form. The output comprises only final firmware components which are directly usable by end users. #### **Usage** -You can either Drag & Drop or manually enter Insyde iFlash Update image file(s). Optional arguments: +You can either Drag & Drop or manually enter Insyde iFlash/iFdPacker Update image file(s). Optional arguments: * -h or --help : show help message and exit * -v or --version : show utility name and version @@ -337,7 +337,7 @@ You can either Drag & Drop or manually enter Insyde iFlash Update image file(s). #### **Compatibility** -Should work at all Windows, Linux or macOS operating systems which have Python 3.8 support. +Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support. #### **Prerequisites** @@ -347,7 +347,7 @@ To run the utility, you do not need any prerequisites. 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: +1. Make sure Python 3.10.0 or newer is installed: > python --version @@ -357,7 +357,7 @@ PyInstaller can build/freeze/compile the utility at all three supported platform 3. Build/Freeze/Compile: -> pyinstaller --noupx --onefile \\/Insyde_iFlash_Extract.py +> pyinstaller --noupx --onefile \\/Insyde_IFD_Extract.py At dist folder you should find the final utility executable @@ -389,7 +389,7 @@ You can either Drag & Drop or manually enter Panasonic BIOS Package executable f #### **Compatibility** -Should work at all Windows, Linux or macOS operating systems which have Python 3.8 support. +Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support. #### **Prerequisites** @@ -406,7 +406,7 @@ Moreover, you must have the following 3rd party tool at the "external" project d 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: +1. Make sure Python 3.10.0 or newer is installed: > python --version @@ -456,7 +456,7 @@ You can either Drag & Drop or manually enter Phoenix Tools Development Kit (TDK) #### **Compatibility** -Should work at all Windows, Linux or macOS operating systems which have Python 3.8 support. +Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support. #### **Prerequisites** @@ -468,7 +468,7 @@ To run the utility, you must have the following 3rd party Python module installe 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: +1. Make sure Python 3.10.0 or newer is installed: > python --version @@ -514,7 +514,7 @@ You can either Drag & Drop or manually enter Portwell UEFI Unpacker EFI executab #### **Compatibility** -Should work at all Windows, Linux or macOS operating systems which have Python 3.8 support. +Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support. #### **Prerequisites** @@ -532,7 +532,7 @@ Moreover, you must have the following 3rd party tool at the "external" project d 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: +1. Make sure Python 3.10.0 or newer is installed: > python --version @@ -582,7 +582,7 @@ You can either Drag & Drop or manually enter Toshiba BIOS COM image file(s). Opt #### **Compatibility** -Should work at all Windows, Linux or macOS operating systems which have Python 3.8 support. +Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support. #### **Prerequisites** @@ -594,7 +594,7 @@ To run the utility, you must have the following 3rd party tool at the "external" 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: +1. Make sure Python 3.10.0 or newer is installed: > python --version @@ -640,7 +640,7 @@ You can either Drag & Drop or manually enter VAIO Packaging Manager executable f #### **Compatibility** -Should work at all Windows, Linux or macOS operating systems which have Python 3.8 support. +Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support. #### **Prerequisites** @@ -650,7 +650,7 @@ To run the utility, you do not need any prerequisites. 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: +1. Make sure Python 3.10.0 or newer is installed: > python --version diff --git a/Toshiba_COM_Extract.py b/Toshiba_COM_Extract.py index 6af0770..4a6e1c2 100644 --- a/Toshiba_COM_Extract.py +++ b/Toshiba_COM_Extract.py @@ -7,7 +7,7 @@ Toshiba BIOS COM Extractor Copyright (C) 2018-2022 Plato Mavropoulos """ -TITLE = 'Toshiba BIOS COM Extractor v2.0_a2' +TITLE = 'Toshiba BIOS COM Extractor v2.0_a3' import os import sys @@ -49,7 +49,8 @@ def toshiba_com_extract(input_file, output_path, padding=0): try: subprocess.run([get_comextract_path(), input_file, output_file], check=True, stdout=subprocess.DEVNULL) - if not os.path.isfile(output_file): raise Exception('EXTRACT_FILE_MISSING') + if not os.path.isfile(output_file): + raise Exception('EXTRACT_FILE_MISSING') except: printer(f'Error: ToshibaComExtractor could not extract file {input_file}!', padding) @@ -72,7 +73,8 @@ if __name__ == '__main__': printer(['***', input_name], padding - 4) - with open(input_file, 'rb') as in_file: input_buffer = in_file.read() + with open(input_file, 'rb') as in_file: + input_buffer = in_file.read() if not is_toshiba_com(input_file): printer('Error: This is not a Toshiba BIOS COM image!', padding) diff --git a/VAIO_Package_Extract.py b/VAIO_Package_Extract.py index 108f4e3..c8cec9c 100644 --- a/VAIO_Package_Extract.py +++ b/VAIO_Package_Extract.py @@ -7,7 +7,7 @@ VAIO Packaging Manager Extractor Copyright (C) 2019-2022 Plato Mavropoulos """ -TITLE = 'VAIO Packaging Manager Extractor v3.0_a6' +TITLE = 'VAIO Packaging Manager Extractor v3.0_a7' import os import sys @@ -17,8 +17,8 @@ sys.dont_write_bytecode = True from common.comp_szip import is_szip_supported, szip_decompress from common.path_ops import make_dirs -from common.patterns import PAT_VAIO_CFG, PAT_VAIO_CHK, PAT_VAIO_EXT, PAT_VAIO_CAB -from common.system import script_init, argparse_init, printer +from common.patterns import PAT_VAIO_CAB, PAT_VAIO_CFG, PAT_VAIO_CHK, PAT_VAIO_EXT +from common.system import argparse_init, printer, script_init from common.text_ops import file_to_bytes # Check if input is VAIO Packaging Manager @@ -31,7 +31,8 @@ def is_vaio_pkg(in_file): def vaio_cabinet(name, buffer, extract_path, padding=0): match_cab = PAT_VAIO_CAB.search(buffer) # Microsoft CAB Header XOR 0xFF - if not match_cab: return 1 + if not match_cab: + return 1 printer('Detected obfuscated CAB archive!', padding) @@ -51,7 +52,8 @@ def vaio_cabinet(name, buffer, extract_path, padding=0): cab_path = os.path.join(extract_path, f'{name}_Temporary.cab') - with open(cab_path, 'wb') as cab_file: cab_file.write(cab_data) # Create temporary CAB archive + with open(cab_path, 'wb') as cab_file: + cab_file.write(cab_data) # Create temporary CAB archive if is_szip_supported(cab_path, padding + 8, check=True): if szip_decompress(cab_path, extract_path, 'CAB', padding + 8, check=True) == 0: @@ -67,7 +69,8 @@ def vaio_cabinet(name, buffer, extract_path, padding=0): def vaio_unlock(name, buffer, extract_path, padding=0): match_cfg = PAT_VAIO_CFG.search(buffer) - if not match_cfg: return 1 + if not match_cfg: + return 1 printer('Attempting to Unlock executable!', padding) @@ -116,7 +119,8 @@ def vaio_unlock(name, buffer, extract_path, padding=0): # Store Unlocked VAIO Packaging Manager executable if vaio_check and user_path: unlock_path = os.path.join(extract_path, f'{name}_Unlocked.exe') - with open(unlock_path, 'wb') as unl_file: unl_file.write(buffer) + with open(unlock_path, 'wb') as unl_file: + unl_file.write(buffer) return 0 @@ -149,7 +153,8 @@ if __name__ == '__main__': printer(['***', input_name], padding - 4) - with open(input_file, 'rb') as in_file: input_buffer = in_file.read() + with open(input_file, 'rb') as in_file: + input_buffer = in_file.read() # Check if VAIO Packaging Manager pattern was found on executable if not is_vaio_pkg(input_buffer): diff --git a/common/comp_efi.py b/common/comp_efi.py index da2f214..3c14b71 100644 --- a/common/comp_efi.py +++ b/common/comp_efi.py @@ -22,8 +22,10 @@ def is_efi_compressed(data, strict=True): check_diff = size_comp < size_orig - if strict: check_size = size_comp + 0x8 == len(data) - else: check_size = size_comp + 0x8 <= len(data) + if strict: + check_size = size_comp + 0x8 == len(data) + else: + check_size = size_comp + 0x8 <= len(data) return check_diff and check_size @@ -38,9 +40,11 @@ def efi_decompress(in_path, out_path, padding=0, silent=False, comp_type='--uefi try: subprocess.run([get_tiano_path(), '-d', in_path, '-o', out_path, '-q', comp_type], check=True, stdout=subprocess.DEVNULL) - with open(in_path, 'rb') as file: _,size_orig = get_compress_sizes(file.read()) + with open(in_path, 'rb') as file: + _,size_orig = get_compress_sizes(file.read()) - if os.path.getsize(out_path) != size_orig: raise Exception('EFI_DECOMPRESS_ERROR') + if os.path.getsize(out_path) != size_orig: + raise Exception('EFI_DECOMPRESS_ERROR') except: if not silent: printer(f'Error: TianoCompress could not extract file {in_path}!', padding) diff --git a/common/comp_szip.py b/common/comp_szip.py index 4ac053a..a3cb6ad 100644 --- a/common/comp_szip.py +++ b/common/comp_szip.py @@ -27,7 +27,8 @@ def is_szip_supported(in_path, padding=0, check=False, silent=False): try: szip_t = subprocess.run([get_szip_path(), 't', in_path, '-bso0', '-bse0', '-bsp0'], check=False) - if check: check_bad_exit_code(szip_t.returncode) + if check: + check_bad_exit_code(szip_t.returncode) except: if not silent: printer(f'Error: 7-Zip could not check support for file {in_path}!', padding) @@ -38,14 +39,17 @@ def is_szip_supported(in_path, padding=0, check=False, silent=False): # Archive decompression via 7-Zip def szip_decompress(in_path, out_path, in_name, padding=0, check=False, silent=False): - if not in_name: in_name = 'archive' + if not in_name: + in_name = 'archive' try: szip_x = subprocess.run([get_szip_path(), 'x', '-aou', '-bso0', '-bse0', '-bsp0', '-o' + out_path, in_path], check=False) - if check: check_bad_exit_code(szip_x.returncode) + if check: + check_bad_exit_code(szip_x.returncode) - if not os.path.isdir(out_path): raise Exception('EXTRACT_DIR_MISSING') + if not os.path.isdir(out_path): + raise Exception('EXTRACT_DIR_MISSING') except: if not silent: printer(f'Error: 7-Zip could not extract {in_name} file {in_path}!', padding) diff --git a/common/externals.py b/common/externals.py index 89d6a9e..28b7631 100644 --- a/common/externals.py +++ b/common/externals.py @@ -6,6 +6,7 @@ Copyright (C) 2022 Plato Mavropoulos """ # https://github.com/allowitsme/big-tool by Dmitry Frolov +# https://github.com/platomav/BGScriptTool by Plato Mavropoulos def get_bgs_tool(): try: from external.big_script_tool import BigScript diff --git a/common/path_ops.py b/common/path_ops.py index 5e76eef..4357108 100644 --- a/common/path_ops.py +++ b/common/path_ops.py @@ -84,7 +84,8 @@ def is_path_absolute(in_path): # Create folder(s), controlling parents, existence and prior deletion def make_dirs(in_path, parents=True, exist_ok=False, delete=False): - if delete: del_dirs(in_path) + if delete: + del_dirs(in_path) Path.mkdir(Path(in_path), parents=parents, exist_ok=exist_ok) @@ -129,7 +130,8 @@ def get_argparse_path(argparse_path): # Process input files (argparse object) def process_input_files(argparse_args, sys_argv=None): - if sys_argv is None: sys_argv = [] + if sys_argv is None: + sys_argv = [] if len(sys_argv) >= 2: # Drag & Drop or CLI diff --git a/common/patterns.py b/common/patterns.py index 797d467..9252c91 100644 --- a/common/patterns.py +++ b/common/patterns.py @@ -14,6 +14,7 @@ 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_INSYDE_IFL = re.compile(br'\$_IFLASH_') +PAT_INSYDE_SFX = re.compile(br'\x0D\x0A;!@InstallEnd@!\x0D\x0A(7z\xBC\xAF\x27|\x6E\xF4\x79\x5F\x4E)') PAT_INTEL_ENG = re.compile(br'\x04\x00{3}[\xA1\xE1]\x00{3}.{8}\x86\x80.{9}\x00\$((MN2)|(MAN))', re.DOTALL) PAT_MICROSOFT_CAB = re.compile(br'MSCF\x00{4}') PAT_MICROSOFT_MZ = re.compile(br'MZ') diff --git a/common/struct_ops.py b/common/struct_ops.py index 31c6e5b..b995ba1 100644 --- a/common/struct_ops.py +++ b/common/struct_ops.py @@ -15,7 +15,8 @@ uint64_t = ctypes.c_uint64 # https://github.com/skochinsky/me-tools/blob/master/me_unpack.py by Igor Skochinsky def get_struct(buffer, start_offset, class_name, param_list=None): - if param_list is None: param_list = [] + if param_list is None: + param_list = [] structure = class_name(*param_list) # Unpack parameter list struct_len = ctypes.sizeof(structure) diff --git a/common/system.py b/common/system.py index 5dd4ff4..fab02ef 100644 --- a/common/system.py +++ b/common/system.py @@ -56,7 +56,8 @@ def check_sys_os(): sys.exit(126) # Fix Windows Unicode console redirection - if os_win: sys.stdout.reconfigure(encoding='utf-8') + if os_win: + sys.stdout.reconfigure(encoding='utf-8') # Initialize common argparse arguments def argparse_init(): @@ -85,10 +86,12 @@ def script_init(title, arguments, padding=0): printer(title, new_line=False) # Show Utility Version on demand - if arguments.version: sys.exit(0) + if arguments.version: + sys.exit(0) # Set console/terminal window title (Windows only) - if get_os_ver()[1]: ctypes.windll.kernel32.SetConsoleTitleW(title) + if get_os_ver()[1]: + ctypes.windll.kernel32.SetConsoleTitleW(title) # Process input files and generate output path input_files,output_path = process_input_files(arguments, sys.argv) diff --git a/external/requirements.txt b/external/requirements.txt index 06fc06e..f6b760a 100644 --- a/external/requirements.txt +++ b/external/requirements.txt @@ -1,2 +1,2 @@ lznt1==0.2 -pefile==2021.9.3 +pefile==2022.5.30