mirror of
https://github.com/platomav/BIOSUtilities.git
synced 2025-05-09 13:52:00 -04:00
BIOSUtilities v24.10.23
New "package" flow, arguments now provided during utility call (README) New "main" flow, using old "run_utility" method of BIOSUtility (README) Removed "run_utility" and "show_version" methods from base BIOSUtility Removed argparse argument parsing logic from base BIOSUtility class Removed notion of "pause" (i.e. user action) from BIOSUtility logic Adjusted the README with usage info for "main" and "package" flows
This commit is contained in:
parent
35455f735c
commit
d8e23f9ef3
24 changed files with 561 additions and 652 deletions
|
@ -5,4 +5,4 @@
|
|||
Copyright (C) 2018-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
__version__ = '24.10.18'
|
||||
__version__ = '24.10.23'
|
||||
|
|
|
@ -213,17 +213,17 @@ class AmiPfatExtract(BIOSUtility):
|
|||
PFAT_INT_SIG_R3K_LEN: Final[int] = ctypes.sizeof(IntelBiosGuardSignatureRsa3k)
|
||||
PFAT_INT_SIG_MAX_LEN: Final[int] = PFAT_INT_SIG_HDR_LEN + PFAT_INT_SIG_R3K_LEN
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input is AMI BIOS Guard """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
return bool(self._get_ami_pfat(input_object=input_buffer))
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Process and store AMI BIOS Guard output file """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
pfat_buffer: bytes = self._get_ami_pfat(input_object=input_buffer)
|
||||
|
||||
|
@ -233,19 +233,19 @@ class AmiPfatExtract(BIOSUtility):
|
|||
|
||||
bg_sign_len: int = 0
|
||||
|
||||
extract_name: str = path_name(in_path=extract_path).removesuffix(extract_suffix())
|
||||
extract_name: str = path_name(in_path=self.extract_path).removesuffix(extract_suffix())
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
make_dirs(in_path=self.extract_path, delete=True)
|
||||
|
||||
block_all, block_off, file_count = self._parse_pfat_hdr(buffer=pfat_buffer, padding=padding)
|
||||
block_all, block_off, file_count = self._parse_pfat_hdr(buffer=pfat_buffer, padding=self.padding)
|
||||
|
||||
for block in block_all:
|
||||
file_desc, file_name, _, _, _, file_index, block_index, block_count = block
|
||||
|
||||
if block_index == 0:
|
||||
printer(message=file_desc, padding=padding + 4)
|
||||
printer(message=file_desc, padding=self.padding + 4)
|
||||
|
||||
file_path = os.path.join(extract_path, self._get_file_name(index=file_index + 1, name=file_name))
|
||||
file_path = os.path.join(self.extract_path, self._get_file_name(index=file_index + 1, name=file_name))
|
||||
|
||||
all_blocks_dict[file_index] = b''
|
||||
|
||||
|
@ -253,9 +253,9 @@ class AmiPfatExtract(BIOSUtility):
|
|||
|
||||
bg_hdr: Any = ctypes_struct(buffer=pfat_buffer, start_offset=block_off, class_object=IntelBiosGuardHeader)
|
||||
|
||||
printer(message=f'Intel BIOS Guard {block_status} Header:\n', padding=padding + 8)
|
||||
printer(message=f'Intel BIOS Guard {block_status} Header:\n', padding=self.padding + 8)
|
||||
|
||||
bg_hdr.struct_print(padding=padding + 12)
|
||||
bg_hdr.struct_print(padding=self.padding + 12)
|
||||
|
||||
bg_script_bgn: int = block_off + self.PFAT_INT_HDR_LEN
|
||||
bg_script_end: int = bg_script_bgn + bg_hdr.ScriptSize
|
||||
|
@ -270,7 +270,7 @@ class AmiPfatExtract(BIOSUtility):
|
|||
is_sfam, _, _, _, _ = bg_hdr.get_flags() # SFAM, ProtectEC, GFXMitDis, FTU, Reserved
|
||||
|
||||
if is_sfam:
|
||||
printer(message=f'Intel BIOS Guard {block_status} Signature:\n', padding=padding + 8)
|
||||
printer(message=f'Intel BIOS Guard {block_status} Signature:\n', padding=self.padding + 8)
|
||||
|
||||
# Manual BIOS Guard Signature length detection from Header pattern (e.g. Panasonic)
|
||||
if bg_sign_len == 0:
|
||||
|
@ -280,11 +280,11 @@ class AmiPfatExtract(BIOSUtility):
|
|||
|
||||
# Adjust next block to start after current block Data + Signature
|
||||
block_off += self.parse_bg_sign(input_data=pfat_buffer, sign_offset=bg_data_end,
|
||||
sign_length=bg_sign_len, print_info=True, padding=padding + 12)
|
||||
sign_length=bg_sign_len, print_info=True, padding=self.padding + 12)
|
||||
|
||||
printer(message=f'Intel BIOS Guard {block_status} Script:\n', padding=padding + 8)
|
||||
printer(message=f'Intel BIOS Guard {block_status} Script:\n', padding=self.padding + 8)
|
||||
|
||||
_ = self.parse_bg_script(script_data=pfat_buffer[bg_script_bgn:bg_script_end], padding=padding + 12)
|
||||
_ = self.parse_bg_script(script_data=pfat_buffer[bg_script_bgn:bg_script_end], padding=self.padding + 12)
|
||||
|
||||
with open(file_path, 'ab') as out_dat:
|
||||
out_dat.write(bg_data_bin)
|
||||
|
@ -292,27 +292,33 @@ class AmiPfatExtract(BIOSUtility):
|
|||
all_blocks_dict[file_index] += bg_data_bin
|
||||
|
||||
if block_index + 1 == block_count:
|
||||
if self.check_format(input_object=all_blocks_dict[file_index]):
|
||||
self.parse_format(input_object=all_blocks_dict[file_index],
|
||||
extract_path=extract_folder(file_path), padding=padding + 8)
|
||||
ami_pfat_extract: AmiPfatExtract = AmiPfatExtract(
|
||||
input_object=all_blocks_dict[file_index], extract_path=extract_folder(file_path),
|
||||
padding=self.padding + 8)
|
||||
|
||||
if ami_pfat_extract.check_format():
|
||||
ami_pfat_extract.parse_format()
|
||||
|
||||
pfat_oob_data: bytes = pfat_buffer[block_off:] # Store out-of-bounds data after the end of PFAT files
|
||||
|
||||
pfat_oob_name: str = self._get_file_name(index=file_count + 1, name=f'{extract_name}_OOB.bin')
|
||||
|
||||
pfat_oob_path: str = os.path.join(extract_path, pfat_oob_name)
|
||||
pfat_oob_path: str = os.path.join(self.extract_path, pfat_oob_name)
|
||||
|
||||
with open(pfat_oob_path, 'wb') as out_oob:
|
||||
out_oob.write(pfat_oob_data)
|
||||
|
||||
if self.check_format(input_object=pfat_oob_data):
|
||||
self.parse_format(input_object=pfat_oob_data, extract_path=extract_folder(pfat_oob_path), padding=padding)
|
||||
ami_pfat_extract = AmiPfatExtract(
|
||||
input_object=pfat_oob_data, extract_path=extract_folder(pfat_oob_path), padding=self.padding)
|
||||
|
||||
if ami_pfat_extract.check_format():
|
||||
ami_pfat_extract.parse_format()
|
||||
|
||||
in_all_data: bytes = b''.join([block[1] for block in sorted(all_blocks_dict.items())])
|
||||
|
||||
in_all_name: str = self._get_file_name(index=0, name=f'{extract_name}_ALL.bin')
|
||||
|
||||
in_all_path: str = os.path.join(extract_path, in_all_name)
|
||||
in_all_path: str = os.path.join(self.extract_path, in_all_name)
|
||||
|
||||
with open(in_all_path, 'wb') as out_all:
|
||||
out_all.write(in_all_data + pfat_oob_data)
|
||||
|
@ -471,7 +477,3 @@ class AmiPfatExtract(BIOSUtility):
|
|||
printer(message=block[0], padding=padding + 8, new_line=False)
|
||||
|
||||
return block_all, hdr_size, files_count
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
AmiPfatExtract().run_utility()
|
||||
|
|
|
@ -183,10 +183,6 @@ class AmiUcpExtract(BIOSUtility):
|
|||
|
||||
TITLE: str = 'AMI UCP Update Extractor'
|
||||
|
||||
ARGUMENTS: list[tuple[list[str], dict[str, str]]] = [
|
||||
(['-c', '--checksum'], {'help': 'verify AMI UCP Checksums (slow)', 'action': 'store_true'})
|
||||
]
|
||||
|
||||
# Get common ctypes Structure Sizes
|
||||
UAF_HDR_LEN: Final[int] = ctypes.sizeof(UafHeader)
|
||||
UAF_MOD_LEN: Final[int] = ctypes.sizeof(UafModule)
|
||||
|
@ -254,23 +250,29 @@ class AmiUcpExtract(BIOSUtility):
|
|||
'@W64': ['amifldrv64.sys', 'amifldrv64.sys', '']
|
||||
}
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def __init__(self, input_object: str | bytes | bytearray = b'', extract_path: str = '', padding: int = 0,
|
||||
checksum: bool = False) -> None:
|
||||
super().__init__(input_object=input_object, extract_path=extract_path, padding=padding)
|
||||
|
||||
self.checksum: bool = checksum
|
||||
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input is AMI UCP image """
|
||||
|
||||
buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
return bool(self._get_ami_ucp(input_object=buffer)[0])
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Parse & Extract AMI UCP structures """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
nal_dict: dict[str, tuple[str, str]] = {} # Initialize @NAL Dictionary per UCP
|
||||
|
||||
printer(message='Utility Configuration Program', padding=padding)
|
||||
printer(message='Utility Configuration Program', padding=self.padding)
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
make_dirs(in_path=self.extract_path, delete=True)
|
||||
|
||||
# Get best AMI UCP Pattern match based on @UAF|@HPU Size
|
||||
ucp_buffer, ucp_tag = self._get_ami_ucp(input_object=input_buffer)
|
||||
|
@ -278,9 +280,9 @@ class AmiUcpExtract(BIOSUtility):
|
|||
# Parse @UAF|@HPU Header Structure
|
||||
uaf_hdr: Any = ctypes_struct(buffer=ucp_buffer, start_offset=0, class_object=UafHeader)
|
||||
|
||||
printer(message=f'Utility Auxiliary File > {ucp_tag}:\n', padding=padding + 4)
|
||||
printer(message=f'Utility Auxiliary File > {ucp_tag}:\n', padding=self.padding + 4)
|
||||
|
||||
uaf_hdr.struct_print(padding=padding + 8)
|
||||
uaf_hdr.struct_print(padding=self.padding + 8)
|
||||
|
||||
fake = struct.pack('<II', len(ucp_buffer), len(ucp_buffer)) # Generate UafModule Structure
|
||||
|
||||
|
@ -292,16 +294,16 @@ class AmiUcpExtract(BIOSUtility):
|
|||
uaf_desc = self.UAF_TAG_DICT[ucp_tag][1] # Get @UAF|@HPU Module Description
|
||||
|
||||
# Print @UAF|@HPU Module EFI Info
|
||||
uaf_mod.struct_print(filename=uaf_name, description=uaf_desc, padding=padding + 8)
|
||||
uaf_mod.struct_print(filename=uaf_name, description=uaf_desc, padding=self.padding + 8)
|
||||
|
||||
if self.arguments.checksum:
|
||||
self._chk16_validate(data=ucp_buffer, tag=ucp_tag, padding=padding + 8)
|
||||
if self.checksum:
|
||||
self._chk16_validate(data=ucp_buffer, tag=ucp_tag, padding=self.padding + 8)
|
||||
|
||||
uaf_all = self._get_uaf_mod(buffer=ucp_buffer, uaf_off=self.UAF_HDR_LEN)
|
||||
|
||||
for mod_info in uaf_all:
|
||||
nal_dict = self._uaf_extract(buffer=ucp_buffer, extract_path=extract_path, mod_info=mod_info,
|
||||
nal_dict=nal_dict, padding=padding + 8)
|
||||
nal_dict = self._uaf_extract(buffer=ucp_buffer, extract_path=self.extract_path, mod_info=mod_info,
|
||||
nal_dict=nal_dict, padding=self.padding + 8)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -421,7 +423,7 @@ class AmiUcpExtract(BIOSUtility):
|
|||
not uaf_tag.startswith(('@ROM', '@R0', '@S0', '@DR', '@DS')):
|
||||
|
||||
printer(message=f'Note: Detected new AMI UCP Module {uaf_tag} ({nal_dict[uaf_tag][1]}) in @NAL!',
|
||||
padding=padding + 4, pause=not self.arguments.auto_exit)
|
||||
padding=padding + 4)
|
||||
|
||||
# Generate @UAF|@HPU Module File name, depending on whether decompression will be required
|
||||
uaf_sname: str = safe_name(in_name=uaf_name + ('.temp' if is_comp else uaf_fext))
|
||||
|
@ -435,7 +437,7 @@ class AmiUcpExtract(BIOSUtility):
|
|||
else:
|
||||
uaf_fname = safe_path(base_path=extract_path, user_paths=uaf_sname)
|
||||
|
||||
if self.arguments.checksum:
|
||||
if self.checksum:
|
||||
self._chk16_validate(data=uaf_data_all, tag=uaf_tag, padding=padding + 4)
|
||||
|
||||
# Parse Utility Identification Information @UAF|@HPU Module (@UII)
|
||||
|
@ -453,7 +455,7 @@ class AmiUcpExtract(BIOSUtility):
|
|||
|
||||
info_hdr.struct_print(description=info_desc, padding=padding + 8) # Print @UII Module Info
|
||||
|
||||
if self.arguments.checksum:
|
||||
if self.checksum:
|
||||
self._chk16_validate(data=uaf_data_raw, tag='@UII > Info', padding=padding + 8)
|
||||
|
||||
# Store/Save @UII Module Info in file
|
||||
|
@ -562,25 +564,25 @@ class AmiUcpExtract(BIOSUtility):
|
|||
# Assign a file path & name to each Tag
|
||||
nal_dict[info_tag] = (info_path, info_name)
|
||||
|
||||
insyde_ifd_extract: InsydeIfdExtract = InsydeIfdExtract()
|
||||
|
||||
# Parse Insyde BIOS @UAF|@HPU Module (@INS)
|
||||
if uaf_tag == '@INS' and insyde_ifd_extract.check_format(input_object=uaf_fname):
|
||||
# Generate extraction directory
|
||||
if uaf_tag == '@INS':
|
||||
ins_dir: str = os.path.join(extract_path, safe_name(in_name=f'{uaf_tag}_nested-IFD'))
|
||||
|
||||
if insyde_ifd_extract.parse_format(input_object=uaf_fname, extract_path=extract_folder(ins_dir),
|
||||
padding=padding + 4):
|
||||
os.remove(uaf_fname) # Delete raw nested Insyde IFD image after successful extraction
|
||||
insyde_ifd_extract: InsydeIfdExtract = InsydeIfdExtract(
|
||||
input_object=uaf_fname, extract_path=extract_folder(ins_dir), padding=padding + 4)
|
||||
|
||||
ami_pfat_extract: AmiPfatExtract = AmiPfatExtract()
|
||||
if insyde_ifd_extract.check_format():
|
||||
if insyde_ifd_extract.parse_format():
|
||||
os.remove(uaf_fname) # Delete raw nested Insyde IFD image after successful extraction
|
||||
|
||||
pfat_dir: str = os.path.join(extract_path, safe_name(in_name=uaf_name))
|
||||
|
||||
ami_pfat_extract: AmiPfatExtract = AmiPfatExtract(
|
||||
input_object=uaf_data_raw, extract_path=extract_folder(pfat_dir), padding=padding + 4)
|
||||
|
||||
# Detect & Unpack AMI BIOS Guard (PFAT) BIOS image
|
||||
if ami_pfat_extract.check_format(input_object=uaf_data_raw):
|
||||
pfat_dir: str = os.path.join(extract_path, safe_name(in_name=uaf_name))
|
||||
|
||||
ami_pfat_extract.parse_format(input_object=uaf_data_raw, extract_path=extract_folder(pfat_dir),
|
||||
padding=padding + 4)
|
||||
if ami_pfat_extract.check_format():
|
||||
ami_pfat_extract.parse_format()
|
||||
|
||||
os.remove(uaf_fname) # Delete raw PFAT BIOS image after successful extraction
|
||||
|
||||
|
@ -590,18 +592,15 @@ class AmiUcpExtract(BIOSUtility):
|
|||
printer(message='Use "ME Analyzer" from https://github.com/platomav/MEAnalyzer',
|
||||
padding=padding + 8, new_line=False)
|
||||
|
||||
# Parse Nested AMI UCP image
|
||||
if self.check_format(input_object=uaf_data_raw):
|
||||
# Generate extraction directory
|
||||
uaf_dir: str = os.path.join(extract_path, safe_name(in_name=f'{uaf_tag}_nested-UCP'))
|
||||
uaf_dir: str = extract_folder(os.path.join(extract_path, safe_name(in_name=f'{uaf_tag}_nested-UCP')))
|
||||
|
||||
self.parse_format(input_object=uaf_data_raw, extract_path=extract_folder(uaf_dir),
|
||||
padding=padding + 4) # Call recursively
|
||||
ami_ucp_extract: AmiUcpExtract = AmiUcpExtract(
|
||||
input_object=uaf_data_raw, extract_path=uaf_dir, padding=padding + 4, checksum=self.checksum)
|
||||
|
||||
# Parse Nested AMI UCP image
|
||||
if ami_ucp_extract.check_format():
|
||||
ami_ucp_extract.parse_format()
|
||||
|
||||
os.remove(uaf_fname) # Delete raw nested AMI UCP image after successful extraction
|
||||
|
||||
return nal_dict
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
AmiUcpExtract().run_utility()
|
||||
|
|
|
@ -116,31 +116,31 @@ class AppleEfiIdentify(BIOSUtility):
|
|||
|
||||
TITLE: str = 'Apple EFI Image Identifier'
|
||||
|
||||
ARGUMENTS: list[tuple[list[str], dict[str, str]]] = [
|
||||
(['-q', '--silent'], {'help': 'suppress structure display', 'action': 'store_true'})
|
||||
]
|
||||
|
||||
PAT_UEFIFIND: Final[str] = f'244942494F534924{"." * 32}2E00{"." * 12}2E00{"." * 16}2E00{"." * 12}2E00{"." * 40}00'
|
||||
|
||||
def __init__(self, arguments: list[str] | None = None) -> None:
|
||||
super().__init__(arguments=arguments)
|
||||
def __init__(self, input_object: str | bytes | bytearray = b'', extract_path: str = '', padding: int = 0,
|
||||
silent: bool = False) -> None:
|
||||
super().__init__(input_object=input_object, extract_path=extract_path, padding=padding)
|
||||
|
||||
self.silent: bool = silent
|
||||
|
||||
self.efi_file_name: str = ''
|
||||
self.intel_bios_info: dict[str, str] = {}
|
||||
self.apple_rom_version: defaultdict[str, set] = defaultdict(set)
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input is Apple EFI image """
|
||||
|
||||
if isinstance(input_object, str) and is_file(in_path=input_object) and is_access(in_path=input_object):
|
||||
if path_suffixes(in_path=input_object)[-1].lower() not in ('.fd', '.scap', '.im4p'):
|
||||
if isinstance(self.input_object, str) and is_file(in_path=self.input_object) and is_access(
|
||||
in_path=self.input_object):
|
||||
if path_suffixes(in_path=self.input_object)[-1].lower() not in ('.fd', '.scap', '.im4p'):
|
||||
return False
|
||||
|
||||
input_path: str = input_object
|
||||
input_path: str = self.input_object
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_path)
|
||||
elif isinstance(input_object, (bytes, bytearray)):
|
||||
elif isinstance(self.input_object, (bytes, bytearray)):
|
||||
input_path = os.path.join(runtime_root(), 'APPLE_EFI_ID_INPUT_BUFFER_CHECK.tmp')
|
||||
input_buffer = input_object
|
||||
input_buffer = self.input_object
|
||||
|
||||
with open(input_path, 'wb') as check_out:
|
||||
check_out.write(input_buffer)
|
||||
|
@ -160,16 +160,16 @@ class AppleEfiIdentify(BIOSUtility):
|
|||
|
||||
return False
|
||||
finally:
|
||||
if input_path != input_object:
|
||||
if input_path != self.input_object:
|
||||
delete_file(in_path=input_path)
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Parse & Identify (or Rename) Apple EFI image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
if isinstance(input_object, str) and is_file(in_path=input_object):
|
||||
input_path: str = input_object
|
||||
if isinstance(self.input_object, str) and is_file(in_path=self.input_object):
|
||||
input_path: str = self.input_object
|
||||
else:
|
||||
input_path = os.path.join(runtime_root(), 'APPLE_EFI_ID_INPUT_BUFFER_PARSE.bin')
|
||||
|
||||
|
@ -190,12 +190,12 @@ class AppleEfiIdentify(BIOSUtility):
|
|||
self.PAT_UEFIFIND], text=True)[:36]
|
||||
|
||||
# UEFIExtract must create its output folder itself
|
||||
delete_dirs(in_path=extract_path)
|
||||
delete_dirs(in_path=self.extract_path)
|
||||
|
||||
_ = subprocess.run([uefiextract_path(), input_path, bios_id_res, '-o', extract_path, '-m', 'body'],
|
||||
_ = subprocess.run([uefiextract_path(), input_path, bios_id_res, '-o', self.extract_path, '-m', 'body'],
|
||||
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
with open(os.path.join(extract_path, 'body.bin'), 'rb') as raw_body:
|
||||
with open(os.path.join(self.extract_path, 'body.bin'), 'rb') as raw_body:
|
||||
body_buffer: bytes = raw_body.read()
|
||||
|
||||
# Detect decompressed $IBIOSI$ pattern
|
||||
|
@ -207,25 +207,25 @@ class AppleEfiIdentify(BIOSUtility):
|
|||
bios_id_hdr = ctypes_struct(buffer=body_buffer, start_offset=bios_id_match.start(),
|
||||
class_object=IntelBiosId)
|
||||
|
||||
delete_dirs(in_path=extract_path) # Successful UEFIExtract extraction, remove its output folder
|
||||
delete_dirs(in_path=self.extract_path) # Successful UEFIExtract extraction, remove its output folder
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
printer(message=f'Error: Failed to parse compressed $IBIOSI$ pattern: {error}!', padding=padding)
|
||||
printer(message=f'Error: Failed to parse compressed $IBIOSI$ pattern: {error}!', padding=self.padding)
|
||||
|
||||
return False
|
||||
|
||||
if not self.arguments.silent:
|
||||
printer(message=f'Detected Intel BIOS Info at {bios_id_res}\n', padding=padding)
|
||||
if not self.silent:
|
||||
printer(message=f'Detected Intel BIOS Info at {bios_id_res}\n', padding=self.padding)
|
||||
|
||||
bios_id_hdr.struct_print(padding=padding + 4)
|
||||
bios_id_hdr.struct_print(padding=self.padding + 4)
|
||||
|
||||
self.intel_bios_info = bios_id_hdr.get_bios_id()
|
||||
|
||||
self.efi_file_name = (f'{self.intel_bios_info["efi_name_id"]}_{zlib.adler32(input_buffer):08X}'
|
||||
f'{path_suffixes(in_path=input_path)[-1]}')
|
||||
|
||||
_ = self._apple_rom_version(input_buffer=input_buffer, padding=padding)
|
||||
_ = self._apple_rom_version(input_buffer=input_buffer, padding=self.padding)
|
||||
|
||||
if input_path != input_object:
|
||||
if input_path != self.input_object:
|
||||
delete_file(in_path=input_path)
|
||||
|
||||
return True
|
||||
|
@ -255,7 +255,7 @@ class AppleEfiIdentify(BIOSUtility):
|
|||
|
||||
self.apple_rom_version[rom_version_parts[0].strip()].add(rom_version_parts[1].strip())
|
||||
|
||||
if not self.arguments.silent:
|
||||
if not self.silent:
|
||||
printer(message=f'Detected Apple ROM Version at 0x{rom_version_match_off:X}', padding=padding)
|
||||
|
||||
printer(message=rom_version_text, strip=True, padding=padding + 4)
|
||||
|
@ -263,7 +263,3 @@ class AppleEfiIdentify(BIOSUtility):
|
|||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
AppleEfiIdentify().run_utility()
|
||||
|
|
|
@ -27,27 +27,27 @@ class AppleEfiIm4pSplit(BIOSUtility):
|
|||
# Intel Flash Descriptor Component Sizes (2MB, 4MB, 8MB, 16MB and 32MB)
|
||||
IFD_COMP_LEN: Final[dict[int, int]] = {2: 0x200000, 3: 0x400000, 4: 0x800000, 5: 0x1000000, 6: 0x2000000}
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input is Apple EFI IM4P image """
|
||||
|
||||
if isinstance(input_object, str) and not input_object.lower().endswith('.im4p'):
|
||||
if isinstance(self.input_object, str) and not self.input_object.lower().endswith('.im4p'):
|
||||
return False
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
if PAT_APPLE_IM4P.search(input_buffer) and PAT_INTEL_FD.search(input_buffer):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Parse & Split Apple EFI IM4P image """
|
||||
|
||||
parse_success: bool = True
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
make_dirs(in_path=self.extract_path, delete=True)
|
||||
|
||||
# Detect IM4P EFI pattern
|
||||
im4p_match: Match[bytes] | None = PAT_APPLE_IM4P.search(input_buffer)
|
||||
|
@ -138,23 +138,19 @@ class AppleEfiIm4pSplit(BIOSUtility):
|
|||
|
||||
output_size: int = len(output_data)
|
||||
|
||||
output_name: str = path_stem(in_path=input_object) if isinstance(input_object, str) else 'Part'
|
||||
output_name: str = path_stem(in_path=self.input_object) if isinstance(self.input_object, str) else 'Part'
|
||||
|
||||
output_path: str = os.path.join(extract_path, f'{output_name}_[{ifd_data_txt}].fd')
|
||||
output_path: str = os.path.join(self.extract_path, f'{output_name}_[{ifd_data_txt}].fd')
|
||||
|
||||
with open(output_path, 'wb') as output_image:
|
||||
output_image.write(output_data)
|
||||
|
||||
printer(message=f'Split Apple EFI image at {ifd_data_txt}!', padding=padding)
|
||||
printer(message=f'Split Apple EFI image at {ifd_data_txt}!', padding=self.padding)
|
||||
|
||||
if output_size != ifd_comp_all_size:
|
||||
printer(message=f'Error: Bad image size 0x{output_size:07X}, expected 0x{ifd_comp_all_size:07X}!',
|
||||
padding=padding + 4)
|
||||
padding=self.padding + 4)
|
||||
|
||||
parse_success = False
|
||||
|
||||
return parse_success
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
AppleEfiIm4pSplit().run_utility()
|
||||
|
|
|
@ -51,19 +51,19 @@ class AppleEfiPbzxExtract(BIOSUtility):
|
|||
|
||||
PBZX_CHUNK_HDR_LEN: Final[int] = ctypes.sizeof(PbzxChunk)
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input is Apple PBZX image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
return bool(PAT_APPLE_PBZX.search(input_buffer, 0, 4))
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Parse & Extract Apple PBZX image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
make_dirs(in_path=self.extract_path, delete=True)
|
||||
|
||||
cpio_bin: bytes = b'' # Initialize PBZX > CPIO Buffer
|
||||
|
||||
|
@ -74,9 +74,9 @@ class AppleEfiPbzxExtract(BIOSUtility):
|
|||
while chunk_off < len(input_buffer):
|
||||
chunk_hdr: Any = ctypes_struct(buffer=input_buffer, start_offset=chunk_off, class_object=PbzxChunk)
|
||||
|
||||
printer(message=f'PBZX Chunk at 0x{chunk_off:08X}\n', padding=padding)
|
||||
printer(message=f'PBZX Chunk at 0x{chunk_off:08X}\n', padding=self.padding)
|
||||
|
||||
chunk_hdr.struct_print(padding=padding + 4)
|
||||
chunk_hdr.struct_print(padding=self.padding + 4)
|
||||
|
||||
# PBZX Chunk data starts after its Header
|
||||
comp_bgn: int = chunk_off + self.PBZX_CHUNK_HDR_LEN
|
||||
|
@ -90,7 +90,7 @@ class AppleEfiPbzxExtract(BIOSUtility):
|
|||
# Attempt XZ decompression, if applicable to Chunk data
|
||||
cpio_bin += lzma.LZMADecompressor().decompress(comp_bin)
|
||||
|
||||
printer(message='Successful LZMA decompression!', padding=padding + 8)
|
||||
printer(message='Successful LZMA decompression!', padding=self.padding + 8)
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
logging.debug('Error: Failed to LZMA decompress PBZX Chunk 0x%X: %s', chunk_off, error)
|
||||
|
||||
|
@ -105,21 +105,21 @@ class AppleEfiPbzxExtract(BIOSUtility):
|
|||
|
||||
# Check that CPIO size is valid based on all Chunks > Initial Size
|
||||
if cpio_len != len(cpio_bin):
|
||||
printer(message='Error: Unexpected CPIO archive size!', padding=padding)
|
||||
printer(message='Error: Unexpected CPIO archive size!', padding=self.padding)
|
||||
|
||||
return False
|
||||
|
||||
cpio_name: str = path_stem(in_path=input_object) if isinstance(input_object, str) else 'Payload'
|
||||
cpio_name: str = path_stem(in_path=self.input_object) if isinstance(self.input_object, str) else 'Payload'
|
||||
|
||||
cpio_path: str = os.path.join(extract_path, f'{cpio_name}.cpio')
|
||||
cpio_path: str = os.path.join(self.extract_path, f'{cpio_name}.cpio')
|
||||
|
||||
with open(cpio_path, 'wb') as cpio_object:
|
||||
cpio_object.write(cpio_bin)
|
||||
|
||||
# Decompress PBZX > CPIO archive with 7-Zip
|
||||
if is_szip_supported(in_path=cpio_path, padding=padding, args=['-tCPIO'], silent=False):
|
||||
if szip_decompress(in_path=cpio_path, out_path=extract_path, in_name='CPIO',
|
||||
padding=padding, args=['-tCPIO']):
|
||||
if is_szip_supported(in_path=cpio_path, padding=self.padding, args=['-tCPIO'], silent=False):
|
||||
if szip_decompress(in_path=cpio_path, out_path=self.extract_path, in_name='CPIO',
|
||||
padding=self.padding, args=['-tCPIO']):
|
||||
os.remove(cpio_path) # Successful extraction, delete PBZX > CPIO archive
|
||||
else:
|
||||
return False
|
||||
|
@ -127,7 +127,3 @@ class AppleEfiPbzxExtract(BIOSUtility):
|
|||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
AppleEfiPbzxExtract().run_utility()
|
||||
|
|
|
@ -26,15 +26,15 @@ class AppleEfiPkgExtract(BIOSUtility):
|
|||
|
||||
TITLE: str = 'Apple EFI Package Extractor'
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input is Apple EFI PKG package """
|
||||
|
||||
is_apple_efi_pkg: bool = False
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
if isinstance(input_object, str) and is_file(in_path=input_object):
|
||||
input_path: str = input_object
|
||||
if isinstance(self.input_object, str) and is_file(in_path=self.input_object):
|
||||
input_path: str = self.input_object
|
||||
else:
|
||||
input_path = os.path.join(runtime_root(), 'APPLE_EFI_PKG_INPUT_BUFFER_CHECK.bin')
|
||||
|
||||
|
@ -47,45 +47,45 @@ class AppleEfiPkgExtract(BIOSUtility):
|
|||
|
||||
break
|
||||
|
||||
if input_path != input_object:
|
||||
if input_path != self.input_object:
|
||||
os.remove(input_path)
|
||||
|
||||
return is_apple_efi_pkg
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Parse & Extract Apple EFI PKG packages """
|
||||
|
||||
if isinstance(input_object, str) and is_file(in_path=input_object):
|
||||
input_path: str = input_object
|
||||
if isinstance(self.input_object, str) and is_file(in_path=self.input_object):
|
||||
input_path: str = self.input_object
|
||||
else:
|
||||
input_path = os.path.join(runtime_root(), 'APPLE_EFI_PKG_INPUT_BUFFER_PARSE.bin')
|
||||
|
||||
with open(input_path, 'wb') as input_path_object:
|
||||
input_path_object.write(file_to_bytes(in_object=input_object))
|
||||
input_path_object.write(file_to_bytes(in_object=self.input_object))
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
make_dirs(in_path=self.extract_path, delete=True)
|
||||
|
||||
working_dir: str = os.path.join(extract_path, 'temp')
|
||||
working_dir: str = os.path.join(self.extract_path, 'temp')
|
||||
|
||||
make_dirs(in_path=working_dir)
|
||||
|
||||
for pkg_type in ('XAR', 'TAR', 'DMG'):
|
||||
if is_szip_supported(in_path=input_path, padding=padding, args=[f'-t{pkg_type}']):
|
||||
if szip_decompress(in_path=input_path, out_path=working_dir, in_name=pkg_type, padding=padding,
|
||||
if is_szip_supported(in_path=input_path, padding=self.padding, args=[f'-t{pkg_type}']):
|
||||
if szip_decompress(in_path=input_path, out_path=working_dir, in_name=pkg_type, padding=self.padding,
|
||||
args=None if pkg_type == 'DMG' else [f'-t{pkg_type}']):
|
||||
break
|
||||
else:
|
||||
return False
|
||||
|
||||
if input_path != input_object:
|
||||
if input_path != self.input_object:
|
||||
os.remove(input_path)
|
||||
|
||||
for work_file in path_files(in_path=working_dir):
|
||||
if is_file(in_path=work_file) and is_access(in_path=work_file):
|
||||
self._pbzx_zip(input_path=work_file, extract_path=extract_path, padding=padding + 4)
|
||||
self._gzip_cpio(input_path=work_file, extract_path=extract_path, padding=padding + 4)
|
||||
self._dmg_zip(input_path=work_file, extract_path=extract_path, padding=padding + 4)
|
||||
self._xar_gzip(input_path=work_file, extract_path=extract_path, padding=padding + 4)
|
||||
self._pbzx_zip(input_path=work_file, extract_path=self.extract_path, padding=self.padding + 4)
|
||||
self._gzip_cpio(input_path=work_file, extract_path=self.extract_path, padding=self.padding + 4)
|
||||
self._dmg_zip(input_path=work_file, extract_path=self.extract_path, padding=self.padding + 4)
|
||||
self._xar_gzip(input_path=work_file, extract_path=self.extract_path, padding=self.padding + 4)
|
||||
|
||||
delete_dirs(in_path=working_dir)
|
||||
|
||||
|
@ -126,15 +126,16 @@ class AppleEfiPkgExtract(BIOSUtility):
|
|||
def _pbzx_zip(self, input_path: str, extract_path: str, padding: int = 0) -> None:
|
||||
""" PBZX > ZIP """
|
||||
|
||||
pbzx_module: AppleEfiPbzxExtract = AppleEfiPbzxExtract()
|
||||
pbzx_path: str = extract_folder(in_path=input_path, suffix='_pbzx_zip')
|
||||
|
||||
if pbzx_module.check_format(input_object=input_path):
|
||||
printer(message=f'Extracting PBZX via {pbzx_module.title}', padding=padding)
|
||||
pbzx_module: AppleEfiPbzxExtract = AppleEfiPbzxExtract(
|
||||
input_object=input_path, extract_path=pbzx_path, padding=padding + 4)
|
||||
|
||||
pbzx_path: str = extract_folder(in_path=input_path, suffix='_pbzx_zip')
|
||||
if pbzx_module.check_format():
|
||||
printer(message=f'Extracting PBZX via {pbzx_module.TITLE}', padding=padding)
|
||||
|
||||
if pbzx_module.parse_format(input_object=input_path, extract_path=pbzx_path, padding=padding + 4):
|
||||
printer(message=f'Successful PBZX extraction via {pbzx_module.title}!', padding=padding)
|
||||
if pbzx_module.parse_format():
|
||||
printer(message=f'Successful PBZX extraction via {pbzx_module.TITLE}!', padding=padding)
|
||||
|
||||
for pbzx_file in path_files(in_path=pbzx_path):
|
||||
if is_file(in_path=pbzx_file) and is_access(in_path=pbzx_file):
|
||||
|
@ -174,7 +175,7 @@ class AppleEfiPkgExtract(BIOSUtility):
|
|||
if path_suffixes(in_path=input_path)[-1].lower() not in ('.fd', '.scap', '.im4p'):
|
||||
return None
|
||||
|
||||
if not AppleEfiIdentify().check_format(input_object=input_path):
|
||||
if not AppleEfiIdentify(input_object=input_path).check_format():
|
||||
return None
|
||||
|
||||
input_name: str = path_name(in_path=input_path)
|
||||
|
@ -183,12 +184,13 @@ class AppleEfiPkgExtract(BIOSUtility):
|
|||
|
||||
working_dir: str = extract_folder(in_path=input_path)
|
||||
|
||||
im4p_module: AppleEfiIm4pSplit = AppleEfiIm4pSplit()
|
||||
im4p_module: AppleEfiIm4pSplit = AppleEfiIm4pSplit(
|
||||
input_object=input_path, extract_path=working_dir, padding=padding + 8)
|
||||
|
||||
if im4p_module.check_format(input_object=input_path):
|
||||
printer(message=f'Splitting IM4P via {im4p_module.title}', padding=padding + 4)
|
||||
if im4p_module.check_format():
|
||||
printer(message=f'Splitting IM4P via {im4p_module.TITLE}', padding=padding + 4)
|
||||
|
||||
im4p_module.parse_format(input_object=input_path, extract_path=working_dir, padding=padding + 8)
|
||||
im4p_module.parse_format()
|
||||
else:
|
||||
make_dirs(in_path=working_dir, delete=True)
|
||||
|
||||
|
@ -196,13 +198,13 @@ class AppleEfiPkgExtract(BIOSUtility):
|
|||
|
||||
for efi_source in path_files(in_path=working_dir):
|
||||
if is_file(in_path=efi_source) and is_access(in_path=efi_source):
|
||||
efi_id_module: AppleEfiIdentify = AppleEfiIdentify()
|
||||
efi_id_module: AppleEfiIdentify = AppleEfiIdentify(
|
||||
input_object=efi_source, extract_path=extract_folder(in_path=efi_source), padding=padding + 8)
|
||||
|
||||
if efi_id_module.check_format(input_object=efi_source):
|
||||
printer(message=f'Identifying EFI via {efi_id_module.title}', padding=padding + 4)
|
||||
if efi_id_module.check_format():
|
||||
printer(message=f'Identifying EFI via {efi_id_module.TITLE}', padding=padding + 4)
|
||||
|
||||
if efi_id_module.parse_format(input_object=efi_source, extract_path=extract_folder(
|
||||
in_path=efi_source), padding=padding + 8):
|
||||
if efi_id_module.parse_format():
|
||||
efi_dest: str = os.path.join(path_parent(in_path=efi_source), efi_id_module.efi_file_name)
|
||||
|
||||
os.replace(efi_source, efi_dest)
|
||||
|
@ -214,7 +216,3 @@ class AppleEfiPkgExtract(BIOSUtility):
|
|||
delete_dirs(in_path=working_dir)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
AppleEfiPkgExtract().run_utility()
|
||||
|
|
|
@ -22,19 +22,19 @@ class AwardBiosExtract(BIOSUtility):
|
|||
|
||||
TITLE: str = 'Award BIOS Module Extractor'
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input is Award BIOS image """
|
||||
|
||||
in_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
in_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
return bool(PAT_AWARD_LZH.search(in_buffer))
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Parse & Extract Award BIOS image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
make_dirs(in_path=self.extract_path, delete=True)
|
||||
|
||||
for lzh_match in PAT_AWARD_LZH.finditer(input_buffer):
|
||||
lzh_type: str = lzh_match.group(0).decode('utf-8')
|
||||
|
@ -52,7 +52,7 @@ class AwardBiosExtract(BIOSUtility):
|
|||
|
||||
if len(mod_bin) != 0x2 + hdr_len + mod_len:
|
||||
printer(message=f'Error: Skipped incomplete LZH stream at 0x{mod_bgn:X}!',
|
||||
padding=padding, new_line=True)
|
||||
padding=self.padding, new_line=True)
|
||||
|
||||
continue
|
||||
|
||||
|
@ -61,9 +61,9 @@ class AwardBiosExtract(BIOSUtility):
|
|||
else:
|
||||
tag_txt = f'{mod_bgn:X}_{mod_end:X}'
|
||||
|
||||
printer(message=f'{lzh_text} > {tag_txt} [0x{mod_bgn:06X}-0x{mod_end:06X}]', padding=padding)
|
||||
printer(message=f'{lzh_text} > {tag_txt} [0x{mod_bgn:06X}-0x{mod_end:06X}]', padding=self.padding)
|
||||
|
||||
mod_path: str = os.path.join(extract_path, tag_txt)
|
||||
mod_path: str = os.path.join(self.extract_path, tag_txt)
|
||||
|
||||
lzh_path: str = f'{mod_path}.lzh'
|
||||
|
||||
|
@ -71,8 +71,8 @@ class AwardBiosExtract(BIOSUtility):
|
|||
lzh_file.write(mod_bin) # Store LZH archive
|
||||
|
||||
# 7-Zip returns critical exit code (i.e. 2) if LZH CRC is wrong, do not check result
|
||||
szip_decompress(in_path=lzh_path, out_path=extract_path, in_name=lzh_text,
|
||||
padding=padding + 4)
|
||||
szip_decompress(in_path=lzh_path, out_path=self.extract_path, in_name=lzh_text,
|
||||
padding=self.padding + 4)
|
||||
|
||||
# Manually check if 7-Zip extracted LZH due to its CRC check issue
|
||||
if is_file(in_path=mod_path):
|
||||
|
@ -80,14 +80,11 @@ class AwardBiosExtract(BIOSUtility):
|
|||
|
||||
os.remove(lzh_path) # Successful extraction, delete LZH archive
|
||||
|
||||
award_bios_extract: AwardBiosExtract = AwardBiosExtract(
|
||||
input_object=mod_path, extract_path=extract_folder(mod_path), padding=self.padding + 8)
|
||||
|
||||
# Extract any nested LZH archives
|
||||
if self.check_format(input_object=mod_path):
|
||||
# Recursively extract nested Award BIOS modules
|
||||
self.parse_format(input_object=mod_path, extract_path=extract_folder(mod_path),
|
||||
padding=padding + 8)
|
||||
if award_bios_extract.check_format():
|
||||
award_bios_extract.parse_format()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
AwardBiosExtract().run_utility()
|
||||
|
|
|
@ -30,7 +30,7 @@ def python_version() -> tuple:
|
|||
|
||||
|
||||
def printer(message: str | list | tuple | None = None, padding: int = 0, new_line: bool = True,
|
||||
pause: bool = False, sep_char: str = ' ', strip: bool = False) -> None:
|
||||
sep_char: str = ' ', strip: bool = False) -> None:
|
||||
""" Show message(s), controlling padding, newline, stripping, pausing & separating """
|
||||
|
||||
message_string: str = to_string(in_object='' if message is None else message, sep_char=sep_char)
|
||||
|
@ -44,7 +44,4 @@ def printer(message: str | list | tuple | None = None, padding: int = 0, new_lin
|
|||
|
||||
message_output += f'{line_new}{" " * padding}{line_text}'
|
||||
|
||||
if pause:
|
||||
input(message_output)
|
||||
else:
|
||||
print(message_output)
|
||||
print(message_output)
|
||||
|
|
|
@ -5,161 +5,23 @@
|
|||
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from argparse import ArgumentParser, Namespace
|
||||
from typing import Final
|
||||
|
||||
from biosutilities import __version__
|
||||
from biosutilities.common.paths import (delete_dirs, extract_folder, is_access, is_dir, is_file, is_empty_dir,
|
||||
path_files, path_name, path_parent, real_path, runtime_root)
|
||||
from biosutilities.common.system import system_platform, python_version, printer
|
||||
from biosutilities.common.texts import remove_quotes, to_boxed, to_ordinal
|
||||
|
||||
|
||||
class BIOSUtility:
|
||||
""" Base utility class for BIOSUtilities """
|
||||
|
||||
TITLE: str = 'BIOS Utility'
|
||||
|
||||
ARGUMENTS: list[tuple[list[str], dict[str, str]]] = []
|
||||
def __init__(self, input_object: str | bytes | bytearray = b'', extract_path: str = '', padding: int = 0) -> None:
|
||||
self.input_object: str | bytes | bytearray = input_object
|
||||
self.extract_path: str = extract_path
|
||||
self.padding: int = padding
|
||||
|
||||
MAX_FAT32_ITEMS: Final[int] = 65535
|
||||
|
||||
MIN_PYTHON_VER: Final[tuple[int, int]] = (3, 10)
|
||||
|
||||
def __init__(self, arguments: list[str] | None = None) -> None:
|
||||
self.title: str = f'{self.TITLE.strip()} v{__version__}'
|
||||
|
||||
argparser: ArgumentParser = ArgumentParser(allow_abbrev=False)
|
||||
|
||||
argparser.add_argument('paths', nargs='*')
|
||||
argparser.add_argument('-e', '--auto-exit', help='skip user action prompts', action='store_true')
|
||||
argparser.add_argument('-o', '--output-dir', help='output extraction directory')
|
||||
|
||||
for argument in self.ARGUMENTS:
|
||||
argparser.add_argument(*argument[0], **argument[1]) # type: ignore
|
||||
|
||||
sys_argv: list[str] = arguments if isinstance(arguments, list) and arguments else sys.argv[1:]
|
||||
|
||||
self.arguments: Namespace = argparser.parse_known_args(sys_argv)[0]
|
||||
|
||||
self._input_files: list[str] = []
|
||||
|
||||
self._output_path: str = ''
|
||||
|
||||
def run_utility(self, padding: int = 0) -> bool:
|
||||
""" Run utility after checking for supported format """
|
||||
|
||||
self._check_sys_py()
|
||||
|
||||
self._check_sys_os()
|
||||
|
||||
self.show_version(padding=padding)
|
||||
|
||||
self._setup_input_files(padding=padding)
|
||||
|
||||
self._setup_output_dir(padding=padding)
|
||||
|
||||
exit_code: int = len(self._input_files)
|
||||
|
||||
for input_file in self._input_files:
|
||||
input_name: str = path_name(in_path=input_file, limit=True)
|
||||
|
||||
printer(message=input_name, padding=padding + 4)
|
||||
|
||||
if not self.check_format(input_object=input_file):
|
||||
printer(message='Error: This is not a supported format!', padding=padding + 8)
|
||||
|
||||
continue
|
||||
|
||||
extract_path: str = os.path.join(self._output_path, extract_folder(in_path=input_name))
|
||||
|
||||
if is_dir(in_path=extract_path):
|
||||
for suffix in range(2, self.MAX_FAT32_ITEMS):
|
||||
renamed_path: str = f'{os.path.normpath(extract_path)}_{to_ordinal(in_number=suffix)}'
|
||||
|
||||
if not is_dir(in_path=renamed_path):
|
||||
extract_path = renamed_path
|
||||
|
||||
break
|
||||
|
||||
if self.parse_format(input_object=input_file, extract_path=extract_path, padding=padding + 8):
|
||||
exit_code -= 1
|
||||
|
||||
if is_empty_dir(in_path=extract_path):
|
||||
delete_dirs(in_path=extract_path)
|
||||
|
||||
printer(message='Done!\n' if not self.arguments.auto_exit else None, pause=not self.arguments.auto_exit)
|
||||
|
||||
return exit_code == 0
|
||||
|
||||
def show_version(self, is_boxed: bool = True, padding: int = 0) -> None:
|
||||
""" Show title and version of utility """
|
||||
|
||||
printer(message=to_boxed(in_text=self.title) if is_boxed else self.title, new_line=False, padding=padding)
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Process input object as a specific supported format """
|
||||
|
||||
raise NotImplementedError(f'Method "parse_format" not implemented at {__name__}')
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input object is of specific supported format """
|
||||
|
||||
raise NotImplementedError(f'Method "check_format" not implemented at {__name__}')
|
||||
|
||||
def _setup_input_files(self, padding: int = 0) -> None:
|
||||
self._input_files = []
|
||||
def parse_format(self) -> bool:
|
||||
""" Process input object as a specific supported format """
|
||||
|
||||
input_paths: list[str] = self.arguments.paths
|
||||
|
||||
if not input_paths:
|
||||
input_paths = [remove_quotes(in_text=input(f'\n{" " * padding}Enter input file or directory path: '))]
|
||||
|
||||
for input_path in [input_path for input_path in input_paths if input_path]:
|
||||
input_path_real: str = real_path(in_path=input_path)
|
||||
|
||||
if is_dir(in_path=input_path_real):
|
||||
for input_file in path_files(in_path=input_path_real):
|
||||
if is_file(in_path=input_file) and is_access(in_path=input_file):
|
||||
self._input_files.append(input_file)
|
||||
elif is_file(in_path=input_path_real) and is_access(in_path=input_path_real):
|
||||
self._input_files.append(input_path_real)
|
||||
|
||||
def _setup_output_dir(self, padding: int = 0) -> None:
|
||||
self._output_path = ''
|
||||
|
||||
output_path: str = self.arguments.output_dir
|
||||
|
||||
if not output_path:
|
||||
output_path = remove_quotes(in_text=input(f'\n{" " * padding}Enter output directory path: '))
|
||||
|
||||
if not output_path and self._input_files:
|
||||
output_path = str(path_parent(in_path=self._input_files[0]))
|
||||
|
||||
if output_path and is_dir(in_path=output_path) and is_access(in_path=output_path):
|
||||
self._output_path = output_path
|
||||
else:
|
||||
self._output_path = runtime_root()
|
||||
|
||||
def _check_sys_py(self) -> None:
|
||||
""" Check Python Version """
|
||||
|
||||
sys_py: tuple = python_version()
|
||||
|
||||
if sys_py < self.MIN_PYTHON_VER:
|
||||
min_py_str: str = '.'.join(map(str, self.MIN_PYTHON_VER))
|
||||
sys_py_str: str = '.'.join(map(str, sys_py[:2]))
|
||||
|
||||
raise RuntimeError(f'Python >= {min_py_str} required, not {sys_py_str}')
|
||||
|
||||
@staticmethod
|
||||
def _check_sys_os() -> None:
|
||||
""" Check OS Platform """
|
||||
|
||||
os_tag, is_win, is_lnx = system_platform()
|
||||
|
||||
if not (is_win or is_lnx):
|
||||
raise OSError(f'Unsupported operating system: {os_tag}')
|
||||
raise NotImplementedError(f'Method "parse_format" not implemented at {__name__}')
|
||||
|
|
|
@ -37,13 +37,11 @@ def to_ordinal(in_number: int) -> str:
|
|||
def file_to_bytes(in_object: str | bytes | bytearray) -> bytes:
|
||||
""" Get bytes from given buffer or file path """
|
||||
|
||||
if not isinstance(in_object, (bytes, bytearray)):
|
||||
with open(to_string(in_object=in_object), 'rb') as object_data:
|
||||
object_bytes: bytes = object_data.read()
|
||||
else:
|
||||
object_bytes = in_object
|
||||
if isinstance(in_object, str):
|
||||
with open(in_object, 'rb') as object_fp:
|
||||
return object_fp.read()
|
||||
|
||||
return object_bytes
|
||||
return bytes(in_object)
|
||||
|
||||
|
||||
def bytes_to_hex(in_buffer: bytes, order: str, data_len: int, slice_len: int | None = None) -> str:
|
||||
|
|
|
@ -226,11 +226,6 @@ class DellPfsExtract(BIOSUtility):
|
|||
|
||||
TITLE: str = 'Dell PFS Update Extractor'
|
||||
|
||||
ARGUMENTS: list[tuple[list[str], dict[str, str]]] = [
|
||||
(['-a', '--advanced'], {'help': 'extract signatures and metadata', 'action': 'store_true'}),
|
||||
(['-s', '--structure'], {'help': 'show PFS structure information', 'action': 'store_true'})
|
||||
]
|
||||
|
||||
PFS_HEAD_LEN: Final[int] = ctypes.sizeof(DellPfsHeader)
|
||||
PFS_FOOT_LEN: Final[int] = ctypes.sizeof(DellPfsFooter)
|
||||
PFS_INFO_LEN: Final[int] = ctypes.sizeof(DellPfsInfo)
|
||||
|
@ -239,10 +234,17 @@ class DellPfsExtract(BIOSUtility):
|
|||
PFS_PFAT_LEN: Final[int] = ctypes.sizeof(DellPfsPfatMetadata)
|
||||
PFAT_HDR_LEN: Final[int] = ctypes.sizeof(IntelBiosGuardHeader)
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def __init__(self, input_object: str | bytes | bytearray = b'', extract_path: str = '', padding: int = 0,
|
||||
advanced: bool = False, structure: bool = False) -> None:
|
||||
super().__init__(input_object=input_object, extract_path=extract_path, padding=padding)
|
||||
|
||||
self.advanced: bool = advanced
|
||||
self.structure: bool = structure
|
||||
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input is Dell PFS/PKG image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
if self._is_pfs_pkg(input_object=input_buffer):
|
||||
return True
|
||||
|
@ -252,33 +254,34 @@ class DellPfsExtract(BIOSUtility):
|
|||
|
||||
return False
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Parse & Extract Dell PFS Update image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
make_dirs(in_path=self.extract_path, delete=True)
|
||||
|
||||
is_dell_pkg: bool = self._is_pfs_pkg(input_object=input_buffer)
|
||||
|
||||
if is_dell_pkg:
|
||||
pfs_results: dict[str, bytes] = self._thinos_pkg_extract(
|
||||
input_object=input_buffer, extract_path=extract_path)
|
||||
input_object=input_buffer, extract_path=self.extract_path)
|
||||
else:
|
||||
pfs_results = {path_stem(in_path=input_object) if isinstance(input_object, str) and is_file(
|
||||
in_path=input_object) else 'Image': input_buffer}
|
||||
pfs_results = {path_stem(in_path=self.input_object) if isinstance(self.input_object, str) and is_file(
|
||||
in_path=self.input_object) else 'Image': input_buffer}
|
||||
|
||||
# Parse each Dell PFS image contained in the input file
|
||||
for pfs_index, (pfs_name, pfs_buffer) in enumerate(pfs_results.items(), start=1):
|
||||
# At ThinOS PKG packages, multiple PFS images may be included in separate model-named folders
|
||||
pfs_path: str = os.path.join(extract_path, f'{pfs_index} {pfs_name}') if is_dell_pkg else extract_path
|
||||
pfs_path: str = os.path.join(
|
||||
self.extract_path, f'{pfs_index} {pfs_name}') if is_dell_pkg else self.extract_path
|
||||
|
||||
# Parse each PFS ZLIB section
|
||||
for zlib_offset in self._get_section_offsets(buffer=pfs_buffer):
|
||||
# Call the PFS ZLIB section parser function
|
||||
self._pfs_section_parse(zlib_data=pfs_buffer, zlib_start=zlib_offset, extract_path=pfs_path,
|
||||
pfs_name=pfs_name, pfs_index=pfs_index, pfs_count=1, is_rec=False,
|
||||
padding=padding)
|
||||
padding=self.padding)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -496,7 +499,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
""" Parse & Extract Dell PFS Volume """
|
||||
|
||||
# Show PFS Volume indicator
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message='PFS Volume:', padding=padding)
|
||||
|
||||
# Get PFS Header Structure values
|
||||
|
@ -509,7 +512,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
return # Critical error, abort
|
||||
|
||||
# Show PFS Header Structure info
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message='PFS Header:\n', padding=padding + 4)
|
||||
|
||||
pfs_hdr.struct_print(padding=padding + 8)
|
||||
|
@ -576,7 +579,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
class_object=DellPfsInfo)
|
||||
|
||||
# Show PFS Information Header Structure info
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message='PFS Filename Information Header:\n', padding=padding + 4)
|
||||
|
||||
filename_info_hdr.struct_print(padding=padding + 8)
|
||||
|
@ -610,7 +613,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
entry_name: str = safe_name(in_name=name_data.decode('utf-16').strip())
|
||||
|
||||
# Show PFS FileName Structure info
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message='PFS FileName Entry:\n', padding=padding + 8)
|
||||
|
||||
entry_info_mod.struct_print(name=entry_name, padding=padding + 12)
|
||||
|
@ -639,7 +642,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
entry_info: Any = ctypes_struct(buffer=entry_metadata, start_offset=0, class_object=DellPfsMetadata)
|
||||
|
||||
# Show Nested PFS Metadata Structure info
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message='PFS Metadata Information:\n', padding=padding + 4)
|
||||
|
||||
entry_info.struct_print(padding=padding + 8)
|
||||
|
@ -667,7 +670,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
class_object=DellPfsInfo)
|
||||
|
||||
# Show PFS Information Header Structure info
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message='PFS Signature Information Header:\n', padding=padding + 4)
|
||||
|
||||
signature_info_hdr.struct_print(padding=padding + 8)
|
||||
|
@ -688,7 +691,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
class_object=pfs_entry_struct)
|
||||
|
||||
# Show PFS Information Header Structure info
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message='PFS Information Entry:\n', padding=padding + 8)
|
||||
|
||||
entry_hdr.struct_print(padding=padding + 12)
|
||||
|
@ -703,7 +706,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
|
||||
sign_data_txt: str = f'{int.from_bytes(sign_data_raw, byteorder="little"):0{sign_size * 2}X}'
|
||||
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message='Signature Information:\n', padding=padding + 8)
|
||||
|
||||
printer(message=f'Signature Size: 0x{sign_size:X}', padding=padding + 12, new_line=False)
|
||||
|
@ -757,11 +760,11 @@ class DellPfsExtract(BIOSUtility):
|
|||
class_object=DellPfsHeader)
|
||||
|
||||
# Show PFS Volume indicator
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message='PFS Volume:', padding=padding + 4)
|
||||
|
||||
# Show sub-PFS Header Structure Info
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message='PFS Header:\n', padding=padding + 8)
|
||||
|
||||
pfat_pfs_header.struct_print(padding=padding + 12)
|
||||
|
@ -824,12 +827,12 @@ class DellPfsExtract(BIOSUtility):
|
|||
elif file_type == 'NAME_INFO':
|
||||
file_name = 'Filename Information'
|
||||
|
||||
if not self.arguments.advanced:
|
||||
if not self.advanced:
|
||||
continue # Don't store Filename Information in non-advanced user mode
|
||||
elif file_type == 'SIG_INFO':
|
||||
file_name = 'Signature Information'
|
||||
|
||||
if not self.arguments.advanced:
|
||||
if not self.advanced:
|
||||
continue # Don't store Signature Information in non-advanced user mode
|
||||
else:
|
||||
file_name = ''
|
||||
|
@ -863,13 +866,13 @@ class DellPfsExtract(BIOSUtility):
|
|||
if file_data and not is_zlib:
|
||||
write_files.append([file_data, 'data']) # PFS Entry Data Payload
|
||||
|
||||
if file_data_sig and self.arguments.advanced:
|
||||
if file_data_sig and self.advanced:
|
||||
write_files.append([file_data_sig, 'sign_data']) # PFS Entry Data Signature
|
||||
|
||||
if file_meta and (is_zlib or self.arguments.advanced):
|
||||
if file_meta and (is_zlib or self.advanced):
|
||||
write_files.append([file_meta, 'meta']) # PFS Entry Metadata Payload
|
||||
|
||||
if file_meta_sig and self.arguments.advanced:
|
||||
if file_meta_sig and self.advanced:
|
||||
write_files.append([file_meta_sig, 'sign_meta']) # PFS Entry Metadata Signature
|
||||
|
||||
# Write/Extract PFS Entry files
|
||||
|
@ -896,7 +899,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
pfs_entry: Any = ctypes_struct(buffer=entry_buffer, start_offset=entry_start, class_object=entry_struct)
|
||||
|
||||
# Show PFS Entry Structure info
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message='PFS Entry:\n', padding=padding + 4)
|
||||
|
||||
pfs_entry.struct_print(padding=padding + 8)
|
||||
|
@ -981,7 +984,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
pfat_entry_idx_ord: str = to_ordinal(in_number=pfat_entry_index)
|
||||
|
||||
# Show sub-PFS PFAT Header Structure info
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message=f'PFAT Block {pfat_entry_idx_ord} - Header:\n', padding=padding + 12)
|
||||
|
||||
pfat_hdr.struct_print(padding=padding + 16)
|
||||
|
@ -1012,7 +1015,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
|
||||
# Parse sub-PFS PFAT Signature, if applicable (only when PFAT Header > SFAM flag is set)
|
||||
if is_sfam:
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message=f'PFAT Block {pfat_entry_idx_ord} - Signature:\n', padding=padding + 12)
|
||||
|
||||
# Get sub-PFS PFAT Signature Size from Header pattern (not necessary for Dell PFS)
|
||||
|
@ -1027,7 +1030,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
# Get sub-PFS PFAT Signature Structure values
|
||||
pfat_sign_len: int = ami_pfat_extract.parse_bg_sign(
|
||||
input_data=pfat_payload, sign_offset=pfat_payload_end, sign_length=_pfat_sign_len,
|
||||
print_info=self.arguments.structure, padding=padding + 16)
|
||||
print_info=self.structure, padding=padding + 16)
|
||||
|
||||
if not len(pfat_payload[pfat_payload_end:pfat_payload_end + pfat_sign_len]
|
||||
) == pfat_sign_len == _pfat_sign_len:
|
||||
|
@ -1035,7 +1038,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
f'Size mismatch!', padding=padding + 12)
|
||||
|
||||
# Show PFAT Script via BIOS Guard Script Tool
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message=f'PFAT Block {pfat_entry_idx_ord} - Script:\n', padding=padding + 12)
|
||||
|
||||
_ = ami_pfat_extract.parse_bg_script(script_data=pfat_script_data, padding=padding + 16)
|
||||
|
@ -1066,7 +1069,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
pfat_entry_adr = pfat_met.Address
|
||||
|
||||
# Show sub-PFS PFAT Metadata Structure info
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message=f'PFAT Block {pfat_entry_idx_ord} - Metadata:\n', padding=padding + 12)
|
||||
|
||||
pfat_met.struct_print(padding=padding + 16)
|
||||
|
@ -1134,7 +1137,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
block_data_gap: int = block_start - block_start_exp
|
||||
|
||||
if block_data_gap > 0:
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message=f'Warning: Filled sub-PFS PFAT {block_index} data gap 0x{block_data_gap:X} '
|
||||
f'[0x{block_start_exp:X}-0x{block_start:X}]!', padding=padding + 8)
|
||||
|
||||
|
@ -1206,7 +1209,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
# Validate that a PFS Footer was parsed
|
||||
if pfs_ftr.Tag == b'PFS.FTR.':
|
||||
# Show PFS Footer Structure info
|
||||
if self.arguments.structure:
|
||||
if self.structure:
|
||||
printer(message='PFS Footer:\n', padding=padding + 4)
|
||||
|
||||
pfs_ftr.struct_print(padding=padding + 8)
|
||||
|
@ -1240,7 +1243,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
return # Skip further processing for Signatures
|
||||
|
||||
# Store Data/Metadata Payload (simpler Data/Metadata Extension for non-advanced users)
|
||||
bin_ext: str = f'.{bin_name}.bin' if self.arguments.advanced else '.bin'
|
||||
bin_ext: str = f'.{bin_name}.bin' if self.advanced else '.bin'
|
||||
|
||||
# Some Data may be Text or XML files with useful information for non-advanced users
|
||||
final_data, file_ext = self._bin_is_text(buffer=bin_buff, file_type=bin_type,
|
||||
|
@ -1265,7 +1268,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
|
||||
extension: str = '.bin'
|
||||
|
||||
if self.arguments.advanced:
|
||||
if self.advanced:
|
||||
return buffer, extension
|
||||
|
||||
buffer_text: str = ''
|
||||
|
@ -1293,7 +1296,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
|
||||
# Show Model/PCR XML Information, if applicable
|
||||
# Metadata is shown at initial DellPfsMetadata analysis
|
||||
if self.arguments.structure and buffer_text and not is_metadata:
|
||||
if self.structure and buffer_text and not is_metadata:
|
||||
metadata_info_type: str = {".txt": "Model", ".xml": "PCR XML"}[extension]
|
||||
|
||||
printer(message=f'PFS {metadata_info_type} Information:\n', padding=padding + 8)
|
||||
|
@ -1302,7 +1305,3 @@ class DellPfsExtract(BIOSUtility):
|
|||
printer(message=line.strip('\r'), padding=padding + 12, new_line=False)
|
||||
|
||||
return buffer_text or buffer, extension
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
DellPfsExtract().run_utility()
|
||||
|
|
|
@ -23,17 +23,17 @@ class FujitsuSfxExtract(BIOSUtility):
|
|||
|
||||
TITLE: str = 'Fujitsu SFX BIOS Extractor'
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input is Fujitsu SFX image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
return bool(PAT_FUJITSU_SFX.search(input_buffer))
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Parse & Extract Fujitsu SFX image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
# Microsoft CAB Header XOR 0xFF
|
||||
match_cab: re.Match[bytes] | None = PAT_FUJITSU_SFX.search(input_buffer)
|
||||
|
@ -41,7 +41,7 @@ class FujitsuSfxExtract(BIOSUtility):
|
|||
if not match_cab:
|
||||
return False
|
||||
|
||||
printer(message='Detected obfuscated CAB archive!', padding=padding)
|
||||
printer(message='Detected obfuscated CAB archive!', padding=self.padding)
|
||||
|
||||
# Microsoft CAB Header XOR 0xFF starts after "FjSfxBinay" signature
|
||||
cab_start: int = match_cab.start() + 0xA
|
||||
|
@ -55,7 +55,7 @@ class FujitsuSfxExtract(BIOSUtility):
|
|||
# Perform XOR 0xFF and get actual CAB size
|
||||
cab_size ^= xor_size
|
||||
|
||||
printer(message='Removing obfuscation...', padding=padding + 4)
|
||||
printer(message='Removing obfuscation...', padding=self.padding + 4)
|
||||
|
||||
# Get BE XOR-ed CAB data
|
||||
cab_data: int = int.from_bytes(input_buffer[cab_start:cab_start + cab_size], byteorder='big')
|
||||
|
@ -66,19 +66,19 @@ class FujitsuSfxExtract(BIOSUtility):
|
|||
# Perform XOR 0xFF and get actual CAB data
|
||||
raw_data: bytes = (cab_data ^ xor_data).to_bytes(cab_size, 'big')
|
||||
|
||||
printer(message='Extracting archive...', padding=padding + 4)
|
||||
printer(message='Extracting archive...', padding=self.padding + 4)
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
make_dirs(in_path=self.extract_path, delete=True)
|
||||
|
||||
cab_path: str = os.path.join(extract_path, 'FjSfxBinay.cab')
|
||||
cab_path: str = os.path.join(self.extract_path, 'FjSfxBinay.cab')
|
||||
|
||||
# Create temporary CAB archive
|
||||
with open(cab_path, 'wb') as cab_file_object:
|
||||
cab_file_object.write(raw_data)
|
||||
|
||||
if is_szip_supported(in_path=cab_path, padding=padding + 8, silent=False):
|
||||
if szip_decompress(in_path=cab_path, out_path=extract_path, in_name='FjSfxBinay CAB',
|
||||
padding=padding + 8, check=True):
|
||||
if is_szip_supported(in_path=cab_path, padding=self.padding + 8, silent=False):
|
||||
if szip_decompress(in_path=cab_path, out_path=self.extract_path, in_name='FjSfxBinay CAB',
|
||||
padding=self.padding + 8, check=True):
|
||||
os.remove(cab_path)
|
||||
else:
|
||||
return False
|
||||
|
@ -86,7 +86,3 @@ class FujitsuSfxExtract(BIOSUtility):
|
|||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
FujitsuSfxExtract().run_utility()
|
||||
|
|
|
@ -20,49 +20,45 @@ class FujitsuUpcExtract(BIOSUtility):
|
|||
|
||||
TITLE: str = 'Fujitsu UPC BIOS Extractor'
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input is Fujitsu UPC image """
|
||||
is_upc: bool = False
|
||||
|
||||
if isinstance(input_object, str) and is_file(in_path=input_object):
|
||||
is_upc = path_suffixes(input_object)[-1].upper() == '.UPC'
|
||||
elif isinstance(input_object, (bytes, bytearray)):
|
||||
if isinstance(self.input_object, str) and is_file(in_path=self.input_object):
|
||||
is_upc = path_suffixes(self.input_object)[-1].upper() == '.UPC'
|
||||
elif isinstance(self.input_object, (bytes, bytearray)):
|
||||
is_upc = True
|
||||
|
||||
if is_upc:
|
||||
is_upc = is_efi_compressed(data=file_to_bytes(in_object=input_object))
|
||||
is_upc = is_efi_compressed(data=file_to_bytes(in_object=self.input_object))
|
||||
|
||||
return is_upc
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Parse & Extract Fujitsu UPC image """
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
make_dirs(in_path=self.extract_path, delete=True)
|
||||
|
||||
if isinstance(input_object, str) and is_file(in_path=input_object):
|
||||
input_name: str = path_name(in_path=input_object)
|
||||
if isinstance(self.input_object, str) and is_file(in_path=self.input_object):
|
||||
input_name: str = path_name(in_path=self.input_object)
|
||||
|
||||
input_path: str = input_object
|
||||
input_path: str = self.input_object
|
||||
|
||||
if input_name.upper().endswith('.UPC'):
|
||||
input_name = input_name[:-4]
|
||||
else:
|
||||
input_name = 'Fujitsu_UPC_Image'
|
||||
|
||||
input_path = os.path.join(extract_path, f'{input_name}.UPC')
|
||||
input_path = os.path.join(self.extract_path, f'{input_name}.UPC')
|
||||
|
||||
with open(input_path, 'wb') as input_path_object:
|
||||
input_path_object.write(file_to_bytes(in_object=input_object))
|
||||
input_path_object.write(file_to_bytes(in_object=self.input_object))
|
||||
|
||||
output_path: str = os.path.join(extract_path, f'{input_name}.bin')
|
||||
output_path: str = os.path.join(self.extract_path, f'{input_name}.bin')
|
||||
|
||||
efi_status: bool = efi_decompress(in_path=input_path, out_path=output_path, padding=padding)
|
||||
efi_status: bool = efi_decompress(in_path=input_path, out_path=output_path, padding=self.padding)
|
||||
|
||||
if input_path != input_object:
|
||||
if input_path != self.input_object:
|
||||
os.remove(input_path)
|
||||
|
||||
return efi_status
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
FujitsuUpcExtract().run_utility()
|
||||
|
|
|
@ -80,10 +80,10 @@ class InsydeIfdExtract(BIOSUtility):
|
|||
# Get common ctypes Structure Sizes
|
||||
INS_IFL_LEN: Final[int] = ctypes.sizeof(IflashHeader)
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input is Insyde iFlash/iFdPacker Update image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
if bool(self._insyde_iflash_detect(input_buffer=input_buffer)):
|
||||
return True
|
||||
|
@ -93,18 +93,18 @@ class InsydeIfdExtract(BIOSUtility):
|
|||
|
||||
return False
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Parse & Extract Insyde iFlash/iFdPacker Update images """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
iflash_code: int = self._insyde_iflash_extract(input_buffer=input_buffer, extract_path=extract_path,
|
||||
padding=padding)
|
||||
iflash_code: int = self._insyde_iflash_extract(input_buffer=input_buffer, extract_path=self.extract_path,
|
||||
padding=self.padding)
|
||||
|
||||
ifdpack_path: str = os.path.join(extract_path, 'Insyde iFdPacker SFX')
|
||||
ifdpack_path: str = os.path.join(self.extract_path, 'Insyde iFdPacker SFX')
|
||||
|
||||
ifdpack_code: int = self._insyde_packer_extract(input_buffer=input_buffer, extract_path=ifdpack_path,
|
||||
padding=padding)
|
||||
padding=self.padding)
|
||||
|
||||
return (iflash_code and ifdpack_code) == 0
|
||||
|
||||
|
@ -168,8 +168,7 @@ class InsydeIfdExtract(BIOSUtility):
|
|||
ifl_hdr.struct_print(padding=padding + 8)
|
||||
|
||||
if img_val == [img_tag, img_ext]:
|
||||
printer(message=f'Note: Detected new Insyde iFlash tag {img_tag}!',
|
||||
padding=padding + 12, pause=not self.arguments.auto_exit)
|
||||
printer(message=f'Note: Detected new Insyde iFlash tag {img_tag}!', padding=padding + 12)
|
||||
|
||||
out_name: str = f'{img_name}.{img_ext}'
|
||||
|
||||
|
@ -231,16 +230,14 @@ class InsydeIfdExtract(BIOSUtility):
|
|||
|
||||
for sfx_file in path_files(in_path=extract_path):
|
||||
if is_file(in_path=sfx_file) and is_access(in_path=sfx_file):
|
||||
if self.check_format(input_object=sfx_file):
|
||||
insyde_ifd_extract: InsydeIfdExtract = InsydeIfdExtract(
|
||||
input_object=sfx_file, extract_path=extract_folder(sfx_file), padding=padding + 16)
|
||||
|
||||
if insyde_ifd_extract.check_format():
|
||||
printer(message=path_name(in_path=sfx_file), padding=padding + 12)
|
||||
|
||||
ifd_status: int = self.parse_format(input_object=sfx_file, extract_path=extract_folder(sfx_file),
|
||||
padding=padding + 16)
|
||||
ifd_status: int = insyde_ifd_extract.parse_format()
|
||||
|
||||
exit_codes.append(0 if ifd_status else 1)
|
||||
|
||||
return sum(exit_codes)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
InsydeIfdExtract().run_utility()
|
||||
|
|
|
@ -39,10 +39,10 @@ class PanasonicBiosExtract(BIOSUtility):
|
|||
|
||||
PAN_PE_DESC_UPD: Final[str] = 'BIOS UPDATE'
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input is Panasonic BIOS Package PE """
|
||||
|
||||
pe_file: pefile.PE | None = ms_pe(in_file=input_object, silent=True)
|
||||
pe_file: pefile.PE | None = ms_pe(in_file=self.input_object, silent=True)
|
||||
|
||||
if not pe_file:
|
||||
return False
|
||||
|
@ -53,26 +53,26 @@ class PanasonicBiosExtract(BIOSUtility):
|
|||
|
||||
return True
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Parse & Extract Panasonic BIOS Package PE """
|
||||
|
||||
upd_pe_file: pefile.PE = ms_pe(in_file=input_object, padding=padding) # type: ignore
|
||||
upd_pe_file: pefile.PE = ms_pe(in_file=self.input_object, padding=self.padding) # type: ignore
|
||||
|
||||
upd_pe_name: str = self._panasonic_pkg_name(input_object=input_object)
|
||||
upd_pe_name: str = self._panasonic_pkg_name(input_object=self.input_object)
|
||||
|
||||
printer(message=f'Panasonic BIOS Package > PE ({upd_pe_name})\n'.replace(' ()', ''), padding=padding)
|
||||
printer(message=f'Panasonic BIOS Package > PE ({upd_pe_name})\n'.replace(' ()', ''), padding=self.padding)
|
||||
|
||||
ms_pe_info_show(pe_file=upd_pe_file, padding=padding + 4)
|
||||
ms_pe_info_show(pe_file=upd_pe_file, padding=self.padding + 4)
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
make_dirs(in_path=self.extract_path, delete=True)
|
||||
|
||||
upd_pe_path: str = self._panasonic_cab_extract(input_object=input_object,
|
||||
extract_path=extract_path, padding=padding + 8)
|
||||
upd_pe_path: str = self._panasonic_cab_extract(input_object=self.input_object,
|
||||
extract_path=self.extract_path, padding=self.padding + 8)
|
||||
|
||||
upd_padding: int = padding
|
||||
upd_padding: int = self.padding
|
||||
|
||||
if upd_pe_path:
|
||||
upd_padding = padding + 16
|
||||
upd_padding = self.padding + 16
|
||||
|
||||
upd_pe_name = self._panasonic_pkg_name(input_object=upd_pe_path)
|
||||
|
||||
|
@ -84,11 +84,11 @@ class PanasonicBiosExtract(BIOSUtility):
|
|||
|
||||
os.remove(upd_pe_path)
|
||||
|
||||
is_upd_extracted: bool = self._panasonic_res_extract(pe_file=upd_pe_file, extract_path=extract_path,
|
||||
is_upd_extracted: bool = self._panasonic_res_extract(pe_file=upd_pe_file, extract_path=self.extract_path,
|
||||
pe_name=upd_pe_name, padding=upd_padding + 8)
|
||||
|
||||
if not is_upd_extracted:
|
||||
is_upd_extracted = self._panasonic_img_extract(pe_file=upd_pe_file, extract_path=extract_path,
|
||||
is_upd_extracted = self._panasonic_img_extract(pe_file=upd_pe_file, extract_path=self.extract_path,
|
||||
pe_name=upd_pe_name, padding=upd_padding + 8)
|
||||
|
||||
return is_upd_extracted
|
||||
|
@ -184,14 +184,14 @@ class PanasonicBiosExtract(BIOSUtility):
|
|||
|
||||
printer(message='Successful PE Resource extraction!', padding=padding + 4)
|
||||
|
||||
ami_pfat_extract: AmiPfatExtract = AmiPfatExtract()
|
||||
pfat_dir: str = os.path.join(extract_path, res_tag)
|
||||
|
||||
ami_pfat_extract: AmiPfatExtract = AmiPfatExtract(
|
||||
input_object=res_raw, extract_path=pfat_dir, padding=padding + 8)
|
||||
|
||||
# Detect & Unpack AMI BIOS Guard (PFAT) BIOS image
|
||||
if ami_pfat_extract.check_format(input_object=res_raw):
|
||||
pfat_dir: str = os.path.join(extract_path, res_tag)
|
||||
|
||||
ami_pfat_extract.parse_format(input_object=res_raw, extract_path=pfat_dir,
|
||||
padding=padding + 8)
|
||||
if ami_pfat_extract.check_format():
|
||||
ami_pfat_extract.parse_format()
|
||||
else:
|
||||
if is_ms_pe(in_file=res_raw):
|
||||
res_ext: str = 'exe'
|
||||
|
@ -242,7 +242,3 @@ class PanasonicBiosExtract(BIOSUtility):
|
|||
printer(message='Successful PE Data extraction!', padding=padding + 4)
|
||||
|
||||
return bool(img_bin)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
PanasonicBiosExtract().run_utility()
|
||||
|
|
|
@ -103,23 +103,23 @@ class PhoenixTdkExtract(BIOSUtility):
|
|||
|
||||
TDK_DUMMY_LEN: Final[int] = 0x200
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input contains valid Phoenix TDK image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
return bool(self._get_phoenix_tdk(in_buffer=input_buffer)[1] is not None)
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Parse & Extract Phoenix Tools Development Kit (TDK) Packer """
|
||||
|
||||
exit_code: int = 0
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
make_dirs(in_path=self.extract_path, delete=True)
|
||||
|
||||
printer(message='Phoenix Tools Development Kit Packer', padding=padding)
|
||||
printer(message='Phoenix Tools Development Kit Packer', padding=self.padding)
|
||||
|
||||
base_off, pack_off = self._get_phoenix_tdk(in_buffer=input_buffer)
|
||||
|
||||
|
@ -127,13 +127,13 @@ class PhoenixTdkExtract(BIOSUtility):
|
|||
tdk_hdr: Any = ctypes_struct(buffer=input_buffer, start_offset=pack_off, class_object=PhoenixTdkHeader)
|
||||
|
||||
# Print TDK Header structure info
|
||||
printer(message='Phoenix TDK Header:\n', padding=padding + 4)
|
||||
printer(message='Phoenix TDK Header:\n', padding=self.padding + 4)
|
||||
|
||||
tdk_hdr.struct_print(padding=padding + 8)
|
||||
tdk_hdr.struct_print(padding=self.padding + 8)
|
||||
|
||||
# Check if reported TDK Header Size matches manual TDK Entry Count calculation
|
||||
if tdk_hdr.Size != self.TDK_HDR_LEN + self.TDK_DUMMY_LEN + tdk_hdr.Count * self.TDK_MOD_LEN:
|
||||
printer(message='Error: Phoenix TDK Header Size & Entry Count mismatch!\n', padding=padding + 8)
|
||||
printer(message='Error: Phoenix TDK Header Size & Entry Count mismatch!\n', padding=self.padding + 8)
|
||||
|
||||
exit_code = 1
|
||||
|
||||
|
@ -147,16 +147,16 @@ class PhoenixTdkExtract(BIOSUtility):
|
|||
class_object=PhoenixTdkEntry, param_list=[base_off])
|
||||
|
||||
# Print TDK Entry structure info
|
||||
printer(message=f'Phoenix TDK Entry ({entry_index + 1}/{tdk_hdr.Count}):\n', padding=padding + 8)
|
||||
printer(message=f'Phoenix TDK Entry ({entry_index + 1}/{tdk_hdr.Count}):\n', padding=self.padding + 8)
|
||||
|
||||
tdk_mod.struct_print(padding=padding + 12)
|
||||
tdk_mod.struct_print(padding=self.padding + 12)
|
||||
|
||||
# Get TDK Entry raw data Offset (TDK Base + Entry Offset)
|
||||
mod_off: int = tdk_mod.get_offset()
|
||||
|
||||
# Check if TDK Entry raw data Offset is valid
|
||||
if mod_off >= len(input_buffer):
|
||||
printer(message='Error: Phoenix TDK Entry > Offset is out of bounds!\n', padding=padding + 12)
|
||||
printer(message='Error: Phoenix TDK Entry > Offset is out of bounds!\n', padding=self.padding + 12)
|
||||
|
||||
exit_code = 2
|
||||
|
||||
|
@ -165,13 +165,13 @@ class PhoenixTdkExtract(BIOSUtility):
|
|||
|
||||
# Check if TDK Entry raw data is complete
|
||||
if len(mod_data) != tdk_mod.Size:
|
||||
printer(message='Error: Phoenix TDK Entry > Data is truncated!\n', padding=padding + 12)
|
||||
printer(message='Error: Phoenix TDK Entry > Data is truncated!\n', padding=self.padding + 12)
|
||||
|
||||
exit_code = 3
|
||||
|
||||
# Check if TDK Entry Reserved is present
|
||||
if tdk_mod.Reserved:
|
||||
printer(message='Error: Phoenix TDK Entry > Reserved is not empty!\n', padding=padding + 12)
|
||||
printer(message='Error: Phoenix TDK Entry > Reserved is not empty!\n', padding=self.padding + 12)
|
||||
|
||||
exit_code = 4
|
||||
|
||||
|
@ -181,7 +181,7 @@ class PhoenixTdkExtract(BIOSUtility):
|
|||
mod_data = lzma.LZMADecompressor().decompress(data=mod_data)
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
printer(message=f'Error: Phoenix TDK Entry > LZMA decompression failed: {error}!\n',
|
||||
padding=padding + 12)
|
||||
padding=self.padding + 12)
|
||||
|
||||
exit_code = 5
|
||||
|
||||
|
@ -189,7 +189,7 @@ class PhoenixTdkExtract(BIOSUtility):
|
|||
mod_name: str = tdk_mod.get_name() or f'Unknown_{entry_index + 1:02d}.bin'
|
||||
|
||||
# Generate TDK Entry file data output path
|
||||
mod_file: str = os.path.join(extract_path, safe_name(mod_name))
|
||||
mod_file: str = os.path.join(self.extract_path, safe_name(mod_name))
|
||||
|
||||
# Account for potential duplicate file names
|
||||
if is_file(in_path=mod_file):
|
||||
|
@ -280,7 +280,3 @@ class PhoenixTdkExtract(BIOSUtility):
|
|||
tdk_base_off: int | None = self._get_tdk_base(in_buffer, tdk_pack_off)
|
||||
|
||||
return tdk_base_off, tdk_pack_off
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
PhoenixTdkExtract().run_utility()
|
||||
|
|
|
@ -37,10 +37,10 @@ class PortwellEfiExtract(BIOSUtility):
|
|||
4: 'SaveDmiData.efi'
|
||||
}
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input is Portwell EFI executable """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
try:
|
||||
pe_buffer: bytes = self._get_portwell_pe(in_buffer=input_buffer)[1]
|
||||
|
@ -57,21 +57,21 @@ class PortwellEfiExtract(BIOSUtility):
|
|||
|
||||
return False
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Parse & Extract Portwell UEFI Unpacker """
|
||||
|
||||
# Initialize EFI Payload file chunks
|
||||
efi_files: list[bytes] = []
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
make_dirs(in_path=self.extract_path, delete=True)
|
||||
|
||||
pe_file, pe_data = self._get_portwell_pe(in_buffer=input_buffer)
|
||||
|
||||
efi_title: str = self._get_unpacker_tag(input_buffer=input_buffer, pe_file=pe_file)
|
||||
|
||||
printer(message=efi_title, padding=padding)
|
||||
printer(message=efi_title, padding=self.padding)
|
||||
|
||||
# Split EFI Payload into <UU> file chunks
|
||||
efi_list: list[Match[bytes]] = list(PAT_PORTWELL_EFI.finditer(pe_data))
|
||||
|
@ -82,7 +82,7 @@ class PortwellEfiExtract(BIOSUtility):
|
|||
|
||||
efi_files.append(pe_data[efi_bgn:efi_end])
|
||||
|
||||
self._parse_efi_files(extract_path=extract_path, efi_files=efi_files, padding=padding)
|
||||
self._parse_efi_files(extract_path=self.extract_path, efi_files=efi_files, padding=self.padding)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -145,8 +145,7 @@ class PortwellEfiExtract(BIOSUtility):
|
|||
printer(message=f'[{file_index}] {file_name}', padding=padding + 4)
|
||||
|
||||
if file_name.startswith('Unknown_'):
|
||||
printer(message=f'Note: Detected new Portwell EFI file ID {file_index}!',
|
||||
padding=padding + 8, pause=not self.arguments.auto_exit)
|
||||
printer(message=f'Note: Detected new Portwell EFI file ID {file_index}!', padding=padding + 8)
|
||||
|
||||
# Store EFI file output path
|
||||
file_path: str = os.path.join(extract_path, safe_name(in_name=file_name))
|
||||
|
@ -166,7 +165,3 @@ class PortwellEfiExtract(BIOSUtility):
|
|||
# Successful decompression, delete compressed file
|
||||
if efi_decompress(in_path=comp_fname, out_path=file_path, padding=padding + 8):
|
||||
os.remove(comp_fname)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
PortwellEfiExtract().run_utility()
|
||||
|
|
|
@ -23,29 +23,29 @@ class ToshibaComExtract(BIOSUtility):
|
|||
|
||||
TITLE: str = 'Toshiba BIOS COM Extractor'
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input is Toshiba BIOS COM image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
return bool(PAT_TOSHIBA_COM.search(input_buffer, 0, 0x100))
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Parse & Extract Toshiba BIOS COM image """
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
make_dirs(in_path=self.extract_path, delete=True)
|
||||
|
||||
if isinstance(input_object, str) and is_file(in_path=input_object):
|
||||
input_path: str = input_object
|
||||
if isinstance(self.input_object, str) and is_file(in_path=self.input_object):
|
||||
input_path: str = self.input_object
|
||||
else:
|
||||
input_path = os.path.join(extract_path, 'toshiba_bios.com')
|
||||
input_path = os.path.join(self.extract_path, 'toshiba_bios.com')
|
||||
|
||||
with open(input_path, 'wb') as input_buffer:
|
||||
input_buffer.write(file_to_bytes(in_object=input_object))
|
||||
input_buffer.write(file_to_bytes(in_object=self.input_object))
|
||||
|
||||
output_name: str = f'{safe_name(in_name=path_stem(in_path=input_path))}_extracted.bin'
|
||||
|
||||
output_path: str = os.path.join(extract_path, output_name)
|
||||
output_path: str = os.path.join(self.extract_path, output_name)
|
||||
|
||||
try:
|
||||
subprocess.run([comextract_path(), input_path, output_path], check=True, stdout=subprocess.DEVNULL)
|
||||
|
@ -53,17 +53,14 @@ class ToshibaComExtract(BIOSUtility):
|
|||
if not is_file(in_path=output_path):
|
||||
raise FileNotFoundError('EXTRACTED_FILE_MISSING')
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
printer(message=f'Error: ToshibaComExtractor could not extract {input_path}: {error}!', padding=padding)
|
||||
printer(message=f'Error: ToshibaComExtractor could not extract {input_path}: {error}!',
|
||||
padding=self.padding)
|
||||
|
||||
return False
|
||||
|
||||
if input_path != input_object:
|
||||
if input_path != self.input_object:
|
||||
os.remove(input_path)
|
||||
|
||||
printer(message='Successful extraction via ToshibaComExtractor!', padding=padding)
|
||||
printer(message='Successful extraction via ToshibaComExtractor!', padding=self.padding)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ToshibaComExtract().run_utility()
|
||||
|
|
|
@ -24,28 +24,30 @@ class VaioPackageExtract(BIOSUtility):
|
|||
|
||||
TITLE: str = 'VAIO Packaging Manager Extractor'
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
def check_format(self) -> bool:
|
||||
""" Check if input is VAIO Packaging Manager """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
|
||||
|
||||
return bool(PAT_VAIO_CFG.search(input_buffer))
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
def parse_format(self) -> bool:
|
||||
""" Parse & Extract or Unlock VAIO Packaging Manager """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(input_object)
|
||||
input_buffer: bytes = file_to_bytes(self.input_object)
|
||||
|
||||
input_name: str = os.path.basename(input_object) if isinstance(input_buffer, str) else 'VAIO_Package'
|
||||
input_name: str = os.path.basename(self.input_object) if isinstance(input_buffer, str) else 'VAIO_Package'
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
make_dirs(in_path=self.extract_path, delete=True)
|
||||
|
||||
if self._vaio_cabinet(name=input_name, buffer=input_buffer, extract_path=extract_path, padding=padding) == 0:
|
||||
printer(message='Successfully Extracted!', padding=padding)
|
||||
elif self._vaio_unlock(name=input_name, buffer=input_buffer, extract_path=extract_path, padding=padding) == 0:
|
||||
printer(message='Successfully Unlocked!', padding=padding)
|
||||
if self._vaio_cabinet(name=input_name, buffer=input_buffer, extract_path=self.extract_path,
|
||||
padding=self.padding) == 0:
|
||||
printer(message='Successfully Extracted!', padding=self.padding)
|
||||
elif self._vaio_unlock(name=input_name, buffer=input_buffer, extract_path=self.extract_path,
|
||||
padding=self.padding) == 0:
|
||||
printer(message='Successfully Unlocked!', padding=self.padding)
|
||||
else:
|
||||
printer(message='Error: Failed to Extract or Unlock executable!', padding=padding)
|
||||
printer(message='Error: Failed to Extract or Unlock executable!', padding=self.padding)
|
||||
|
||||
return False
|
||||
|
||||
|
@ -174,7 +176,3 @@ class VaioPackageExtract(BIOSUtility):
|
|||
unl_file.write(input_buffer)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
VaioPackageExtract().run_utility()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue