BIOSUtilities v24.10.06

24.10.06

Changed BIOSUtility.parse_format() to return a boolean
Changed 7-Zip and EFI decompressors to return booleans
Apple EFI Package Extractor support for InstallAssistant
Apple EFI Image Identifier support for Apple ROM Version
Added Apple EFI Image Identifier class instance attributes
Improved flow of non-PATH external executable dependencies
Fixed crash when attempting to clear read-only attribute
Fixed incompatibility with Python versions prior to 3.12
Performance improvements when initializing BIOSUtilities
Improved argument naming and definitions of "main" script
Improved the README with new "main" and Apple EFI changes
This commit is contained in:
Plato Mavropoulos 2024-10-07 01:24:12 +03:00
parent 4175af9eb1
commit eda154b0f2
26 changed files with 346 additions and 223 deletions

View file

@ -1,3 +1,17 @@
24.10.06
Changed BIOSUtility.parse_format() to return a boolean
Changed 7-Zip and EFI decompressors to return booleans
Apple EFI Package Extractor support for InstallAssistant
Apple EFI Image Identifier support for Apple ROM Version
Added Apple EFI Image Identifier class instance attributes
Improved flow of non-PATH external executable dependencies
Fixed crash when attempting to clear read-only attribute
Fixed incompatibility with Python versions prior to 3.12
Performance improvements when initializing BIOSUtilities
Improved argument naming and definitions of "main" script
Improved the README with new "main" and Apple EFI changes
24.10.03
Added external executable dependencies non-PATH flow (#51)

View file

@ -11,19 +11,19 @@ Various BIOS/UEFI-related utilities which aid in modding and/or research
The "main" script provides a simple way to check and parse each of the user provided files against all utilities, in succession. It is ideal for quick drag & drop operations but lacks the finer control of the BIOSUtility method. If needed, a few options can be set, by using the command line:
``` bash
usage: main.py [-h] [--output-folder OUTPUT_FOLDER] [--pause-exit] input_files [input_files ...]
usage: [-h] [-e] [-o OUTPUT_DIR] paths [paths ...]
positional arguments:
input_files
paths
options:
-h, --help show help message and exit
--output-folder OUTPUT_FOLDER extraction folder
--pause-exit pause on exit
-h, --help show help and exit
-e, --auto-exit do not pause on exit
-o OUTPUT_DIR, --output-dir OUTPUT_DIR extraction directory
```
``` bash
python ./main.py "/path/to/input/file.bin" --output-folder "/path/to/file extractions"
python ./main.py "/path/to/input/file.bin" --output-dir "/path/to/file extractions"
```
### BIOSUtility
@ -37,7 +37,7 @@ positional arguments:
paths
options:
-h, --help show help message and exit
-h, --help show help and exit
-e, --auto-exit skip user action prompts
-o OUTPUT_DIR, --output-dir OUTPUT_DIR output extraction directory
```
@ -108,7 +108,7 @@ It also allows to use directly the four public methods which are inherited by ev
Run utility after checking for supported format
``` python
run_utility(padding: int = 0) -> int
run_utility(padding: int = 0) -> bool
```
#### check_format
@ -124,7 +124,7 @@ check_format(input_object: str | bytes | bytearray) -> bool
Process input object as a specific supported format
``` python
parse_format(input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int | None
parse_format(input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool
```
#### show_version
@ -148,7 +148,7 @@ python -m pip install --upgrade -r requirements.txt
```
``` bash
python -m pip install --upgrade pefile
python -m pip install --upgrade pefile dissect.util
```
### External Executables / Scripts
@ -269,7 +269,7 @@ To run the utility, you do not need any prerequisites.
#### **Description**
Parses Apple EFI images and identifies them based on Intel's official $IBIOSI$ tag, which contains info such as Model, Version, Build, Date and Time. Optionally, the utility can rename the input Apple EFI image based on the retrieved $IBIOSI$ tag info, while also making sure to differentiate any EFI images with the same $IBIOSI$ tag (e.g. Production, Pre-Production) by appending a checksum of their data.
Parses Apple EFI images and identifies them based on Intel's official "IBIOSI" tag, which contains info such as Model, Version, Build, Date and Time. Additionally, the utility can provide both "IBIOSI" and "Apple ROM Version" structure info, when available, as well as a suggested EFI image filename, while also making sure to differentiate any EFI images with the same "IBIOSI" tag (e.g. Production, Pre-Production) by appending a checksum of their data.
#### **Usage**
@ -290,7 +290,7 @@ To run the utility, you must have the following 3rd party tools at PATH or "exte
#### **Description**
Parses Apple EFI PKG firmware packages (i.e. FirmwareUpdate.pkg, BridgeOSUpdateCustomer.pkg), extracts their EFI images, splits those in IM4P format and identifies/renames the final Intel SPI/BIOS images accordingly. The output comprises only final firmware components which are directly usable by end users.
Parses Apple EFI PKG firmware packages (e.g. FirmwareUpdate.pkg, BridgeOSUpdateCustomer.pkg, InstallAssistant.pkg, iMacEFIUpdate.pkg, iMacFirmwareUpdate.tar), extracts their EFI images, splits those in IM4P format and identifies/renames the final Intel SPI/BIOS images accordingly. The output comprises only final firmware components which are directly usable by end users.
#### **Usage**

View file

@ -5,4 +5,4 @@
Copyright (C) 2018-2024 Plato Mavropoulos
"""
__version__ = '24.10.03'
__version__ = '24.10.06'

View file

@ -12,7 +12,7 @@ import os
import re
import struct
from typing import Any, Type
from typing import Any, Final, Type
from biosutilities.common.externals import big_script_tool
from biosutilities.common.paths import extract_suffix, extract_folder, make_dirs, path_name, safe_name
@ -71,6 +71,7 @@ class IntelBiosGuardHeader(ctypes.LittleEndianStructure):
id_text: str = re.sub(r'[\n\t\r\x00 ]', '', id_byte.decode(encoding='utf-8', errors='ignore'))
id_hexs: str = f'{int.from_bytes(bytes=id_byte, byteorder="big"):0{0x10 * 2}X}'
id_guid: str = f'{{{id_hexs[:8]}-{id_hexs[8:12]}-{id_hexs[12:16]}-{id_hexs[16:20]}-{id_hexs[20:]}}}'
return f'{id_text} {id_guid}'
@ -205,12 +206,12 @@ class AmiPfatExtract(BIOSUtility):
TITLE: str = 'AMI BIOS Guard Extractor'
PFAT_AMI_HDR_LEN: int = ctypes.sizeof(AmiBiosGuardHeader)
PFAT_INT_HDR_LEN: int = ctypes.sizeof(IntelBiosGuardHeader)
PFAT_INT_SIG_HDR_LEN: int = ctypes.sizeof(IntelBiosGuardSignatureHeader)
PFAT_INT_SIG_R2K_LEN: int = ctypes.sizeof(IntelBiosGuardSignatureRsa2k)
PFAT_INT_SIG_R3K_LEN: int = ctypes.sizeof(IntelBiosGuardSignatureRsa3k)
PFAT_INT_SIG_MAX_LEN: int = PFAT_INT_SIG_HDR_LEN + PFAT_INT_SIG_R3K_LEN
PFAT_AMI_HDR_LEN: Final[int] = ctypes.sizeof(AmiBiosGuardHeader)
PFAT_INT_HDR_LEN: Final[int] = ctypes.sizeof(IntelBiosGuardHeader)
PFAT_INT_SIG_HDR_LEN: Final[int] = ctypes.sizeof(IntelBiosGuardSignatureHeader)
PFAT_INT_SIG_R2K_LEN: Final[int] = ctypes.sizeof(IntelBiosGuardSignatureRsa2k)
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:
""" Check if input is AMI BIOS Guard """
@ -219,7 +220,7 @@ class AmiPfatExtract(BIOSUtility):
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) -> int:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Process and store AMI BIOS Guard output file """
input_buffer: bytes = file_to_bytes(in_object=input_object)
@ -317,7 +318,7 @@ class AmiPfatExtract(BIOSUtility):
with open(file=in_all_path, mode='wb') as out_all:
out_all.write(in_all_data + pfat_oob_data)
return 0
return True
def parse_bg_sign(self, input_data: bytes, sign_offset: int, sign_length: int = 0,
print_info: bool = False, padding: int = 0) -> int:

View file

@ -13,12 +13,12 @@ import os
import re
import struct
from typing import Any
from typing import Any, Final
from biosutilities.common.checksums import checksum_16
from biosutilities.common.compression import efi_decompress, is_efi_compressed
from biosutilities.common.paths import agnostic_path, extract_folder, make_dirs, safe_name, safe_path
from biosutilities.common.patterns import PAT_AMI_UCP, PAT_INTEL_ENG
from biosutilities.common.patterns import PAT_AMI_UCP, PAT_INTEL_ENGINE
from biosutilities.common.structs import CHAR, ctypes_struct, UINT8, UINT16, UINT32
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
@ -101,11 +101,12 @@ class UiiHeader(ctypes.LittleEndianStructure):
# 0x10
]
SBI: dict[int, str] = {1: 'ALL', 2: 'AMIBIOS8', 3: 'UEFI', 4: 'AMIBIOS8/UEFI'}
SOS: dict[int, str] = {1: 'DOS', 2: 'EFI', 3: 'Windows', 4: 'Linux', 5: 'FreeBSD', 6: 'MacOS', 128: 'Multi-OS'}
DBW: dict[int, str] = {1: '16b', 2: '16/32b', 3: '32b', 4: '64b'}
PTP: dict[int, str] = {1: 'Executable', 2: 'Library', 3: 'Driver'}
PMD: dict[int, str] = {1: 'API', 2: 'Console', 3: 'GUI', 4: 'Console/GUI'}
SBI: Final[dict[int, str]] = {1: 'ALL', 2: 'AMIBIOS8', 3: 'UEFI', 4: 'AMIBIOS8/UEFI'}
SOS: Final[dict[int, str]] = {1: 'DOS', 2: 'EFI', 3: 'Windows', 4: 'Linux', 5: 'FreeBSD',
6: 'MacOS', 128: 'Multi-Platform'}
DBW: Final[dict[int, str]] = {1: '16b', 2: '16/32b', 3: '32b', 4: '64b'}
PTP: Final[dict[int, str]] = {1: 'Executable', 2: 'Library', 3: 'Driver'}
PMD: Final[dict[int, str]] = {1: 'API', 2: 'Console', 3: 'GUI', 4: 'Console/GUI'}
def struct_print(self, description: str, padding: int = 0) -> None:
""" Display structure information """
@ -160,8 +161,8 @@ class DisModule(ctypes.LittleEndianStructure):
# 0x122
]
ENDIS: dict[int, str] = {0: 'Disabled', 1: 'Enabled'}
SHOWN: dict[int, str] = {0: 'Hidden', 1: 'Shown', 2: 'Shown Only'}
ENDIS: Final[dict[int, str]] = {0: 'Disabled', 1: 'Enabled'}
SHOWN: Final[dict[int, str]] = {0: 'Hidden', 1: 'Shown', 2: 'Shown Only'}
def struct_print(self, padding: int = 0) -> None:
""" Display structure information """
@ -187,14 +188,14 @@ class AmiUcpExtract(BIOSUtility):
]
# Get common ctypes Structure Sizes
UAF_HDR_LEN: int = ctypes.sizeof(UafHeader)
UAF_MOD_LEN: int = ctypes.sizeof(UafModule)
DIS_HDR_LEN: int = ctypes.sizeof(DisHeader)
DIS_MOD_LEN: int = ctypes.sizeof(DisModule)
UII_HDR_LEN: int = ctypes.sizeof(UiiHeader)
UAF_HDR_LEN: Final[int] = ctypes.sizeof(UafHeader)
UAF_MOD_LEN: Final[int] = ctypes.sizeof(UafModule)
DIS_HDR_LEN: Final[int] = ctypes.sizeof(DisHeader)
DIS_MOD_LEN: Final[int] = ctypes.sizeof(DisModule)
UII_HDR_LEN: Final[int] = ctypes.sizeof(UiiHeader)
# AMI UCP Tag Dictionary
UAF_TAG_DICT: dict[str, list[str]] = {
UAF_TAG_DICT: Final[dict[str, list[str]]] = {
'@3FI': ['HpBiosUpdate32.efi', 'HpBiosUpdate32.efi', ''],
'@3S2': ['HpBiosUpdate32.s12', 'HpBiosUpdate32.s12', ''],
'@3S4': ['HpBiosUpdate32.s14', 'HpBiosUpdate32.s14', ''],
@ -259,7 +260,7 @@ class AmiUcpExtract(BIOSUtility):
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) -> None:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Parse & Extract AMI UCP structures """
input_buffer: bytes = file_to_bytes(in_object=input_object)
@ -301,6 +302,8 @@ class AmiUcpExtract(BIOSUtility):
nal_dict = self._uaf_extract(buffer=ucp_buffer, extract_path=extract_path, mod_info=mod_info,
nal_dict=nal_dict, padding=padding + 8)
return True
@staticmethod
def _chk16_validate(data: bytes | bytearray, tag: str, padding: int = 0) -> None:
""" Validate UCP Module Checksum-16 """
@ -482,7 +485,7 @@ class AmiUcpExtract(BIOSUtility):
# Decompressed @UAF|@HPU Module file path
dec_fname: str = uaf_fname.replace('.temp', uaf_fext)
if efi_decompress(in_path=uaf_fname, out_path=dec_fname, padding=padding + 4) == 0:
if efi_decompress(in_path=uaf_fname, out_path=dec_fname, padding=padding + 4):
with open(file=dec_fname, mode='rb') as dec:
uaf_data_raw = dec.read() # Read back the @UAF|@HPU Module decompressed Raw data
@ -558,26 +561,30 @@ 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 InsydeIfdExtract().check_format(input_object=uaf_fname):
if uaf_tag == '@INS' and insyde_ifd_extract.check_format(input_object=uaf_fname):
# Generate extraction directory
ins_dir: str = os.path.join(extract_path, safe_name(in_name=f'{uaf_tag}_nested-IFD'))
if InsydeIfdExtract().parse_format(input_object=uaf_fname, extract_path=extract_folder(ins_dir),
padding=padding + 4) == 0:
if insyde_ifd_extract.parse_format(input_object=uaf_fname, extract_path=extract_folder(ins_dir),
padding=padding + 4):
os.remove(path=uaf_fname) # Delete raw nested Insyde IFD image after successful extraction
ami_pfat_extract: AmiPfatExtract = AmiPfatExtract()
# Detect & Unpack AMI BIOS Guard (PFAT) BIOS image
if AmiPfatExtract().check_format(input_object=uaf_data_raw):
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))
AmiPfatExtract().parse_format(input_object=uaf_data_raw, extract_path=extract_folder(pfat_dir),
ami_pfat_extract.parse_format(input_object=uaf_data_raw, extract_path=extract_folder(pfat_dir),
padding=padding + 4)
os.remove(path=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(string=uaf_data_raw):
if uaf_tag.startswith('@ME') and PAT_INTEL_ENGINE.search(string=uaf_data_raw):
printer(message='Intel Management Engine (ME) Firmware:\n', padding=padding + 4)
printer(message='Use "ME Analyzer" from https://github.com/platomav/MEAnalyzer',
padding=padding + 8, new_line=False)

View file

@ -14,12 +14,13 @@ import struct
import subprocess
import zlib
from collections import defaultdict
from re import Match
from typing import Any, Final
from biosutilities.common.externals import uefiextract_path, uefifind_path
from biosutilities.common.paths import delete_dirs, delete_file, path_suffixes, runtime_root
from biosutilities.common.patterns import PAT_APPLE_EFI
from biosutilities.common.patterns import PAT_INTEL_IBIOSI, PAT_APPLE_ROM_VER
from biosutilities.common.structs import CHAR, ctypes_struct, UINT8
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
@ -58,8 +59,10 @@ class IntelBiosId(ctypes.LittleEndianStructure):
def _decode(field: bytes) -> str:
return struct.pack('B' * len(field), *field).decode(encoding='utf-16', errors='ignore').strip('\x00 ')
def get_bios_id(self) -> tuple:
""" Create Apple EFI BIOS ID """
def get_bios_id(self) -> dict[str, str]:
""" Get Apple/Intel EFI BIOS ID """
intel_sig: str = self.Signature.decode(encoding='utf-8')
board_id: str = self._decode(field=self.BoardID)
board_ext: str = self._decode(field=self.BoardExt)
@ -72,26 +75,40 @@ class IntelBiosId(ctypes.LittleEndianStructure):
build_hour: str = self._decode(field=self.Hour)
build_minute: str = self._decode(field=self.Minute)
build_date: str = f'20{build_year}-{build_month}-{build_day}'
build_time: str = f'{build_hour}-{build_minute}'
efi_name_id: str = (f'{board_id}_{board_ext}_{version_major}_{build_type}{version_minor}'
f'_20{build_year}-{build_month}-{build_day}_{build_hour}-{build_minute}')
return board_id, board_ext, version_major, build_type, version_minor, build_date, build_time
return {
'intel_sig': intel_sig,
'board_id': board_id,
'board_ext': board_ext,
'version_major': version_major,
'version_minor': version_minor,
'build_type': build_type,
'build_year': build_year,
'build_month': build_month,
'build_day': build_day,
'build_hour': build_hour,
'build_minute': build_minute,
'efi_name_id': efi_name_id
}
def struct_print(self, padding: int = 0) -> None:
""" Display structure information """
board_id, board_ext, version_major, build_type, version_minor, build_date, build_time = self.get_bios_id()
ibiosi: dict[str, str] = self.get_bios_id()
intel_id: str = self.Signature.decode(encoding='utf-8')
ibiosi_date: str = f'20{ibiosi["build_year"]}-{ibiosi["build_month"]}-{ibiosi["build_day"]}'
ibiosi_time: str = f'{ibiosi["build_hour"]}:{ibiosi["build_minute"]}'
printer(message=['Intel Signature:', intel_id], padding=padding, new_line=False)
printer(message=['Board Identity: ', board_id], padding=padding, new_line=False)
printer(message=['Apple Identity: ', board_ext], padding=padding, new_line=False)
printer(message=['Major Version: ', version_major], padding=padding, new_line=False)
printer(message=['Minor Version: ', version_minor], padding=padding, new_line=False)
printer(message=['Build Type: ', build_type], padding=padding, new_line=False)
printer(message=['Build Date: ', build_date], padding=padding, new_line=False)
printer(message=['Build Time: ', build_time.replace('-', ':')], padding=padding, new_line=False)
printer(message=['Intel Signature:', ibiosi['intel_sig']], padding=padding, new_line=False)
printer(message=['Board Identity: ', ibiosi['board_id']], padding=padding, new_line=False)
printer(message=['Apple Identity: ', ibiosi['board_ext']], padding=padding, new_line=False)
printer(message=['Major Version: ', ibiosi['version_major']], padding=padding, new_line=False)
printer(message=['Minor Version: ', ibiosi['version_minor']], padding=padding, new_line=False)
printer(message=['Build Type: ', ibiosi['build_type']], padding=padding, new_line=False)
printer(message=['Build Date: ', ibiosi_date], padding=padding, new_line=False)
printer(message=['Build Time: ', ibiosi_time], padding=padding, new_line=False)
class AppleEfiIdentify(BIOSUtility):
@ -104,14 +121,16 @@ class AppleEfiIdentify(BIOSUtility):
def __init__(self, arguments: list[str] | None = None) -> None:
super().__init__(arguments=arguments)
self.efi_name_id: str = ''
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:
""" Check if input is Apple EFI image """
input_buffer: bytes = file_to_bytes(in_object=input_object)
if PAT_APPLE_EFI.search(string=input_buffer):
if PAT_INTEL_IBIOSI.search(string=input_buffer):
return True
if isinstance(input_object, str) and os.path.isfile(path=input_object):
@ -135,7 +154,7 @@ class AppleEfiIdentify(BIOSUtility):
if input_path != input_object:
delete_file(in_path=input_path)
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Parse & Identify (or Rename) Apple EFI image """
input_buffer: bytes = file_to_bytes(in_object=input_object)
@ -148,7 +167,7 @@ class AppleEfiIdentify(BIOSUtility):
with open(file=input_path, mode='wb') as parse_out:
parse_out.write(input_buffer)
bios_id_match: Match[bytes] | None = PAT_APPLE_EFI.search(string=input_buffer)
bios_id_match: Match[bytes] | None = PAT_INTEL_IBIOSI.search(string=input_buffer)
if bios_id_match:
bios_id_res: str = f'0x{bios_id_match.start():X}'
@ -171,7 +190,7 @@ class AppleEfiIdentify(BIOSUtility):
body_buffer: bytes = raw_body.read()
# Detect decompressed $IBIOSI$ pattern
bios_id_match = PAT_APPLE_EFI.search(string=body_buffer)
bios_id_match = PAT_INTEL_IBIOSI.search(string=body_buffer)
if not bios_id_match:
raise RuntimeError('Failed to detect decompressed $IBIOSI$ pattern!')
@ -183,25 +202,56 @@ class AppleEfiIdentify(BIOSUtility):
except Exception as error: # pylint: disable=broad-except
printer(message=f'Error: Failed to parse compressed $IBIOSI$ pattern: {error}!', padding=padding)
return 1
return False
printer(message=f'Detected $IBIOSI$ at {bios_id_res}\n', padding=padding)
printer(message=f'Detected Intel BIOS Info at {bios_id_res}\n', padding=padding)
bios_id_hdr.struct_print(padding=padding + 4)
input_suffix: str = path_suffixes(input_path)[-1]
self.intel_bios_info = bios_id_hdr.get_bios_id()
input_adler32: int = zlib.adler32(input_buffer)
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]}')
fw_id, fw_ext, fw_major, fw_type, fw_minor, fw_date, fw_time = bios_id_hdr.get_bios_id()
self.efi_name_id = (f'{fw_id}_{fw_ext}_{fw_major}_{fw_type}{fw_minor}_{fw_date}_{fw_time}_'
f'{input_adler32:08X}{input_suffix}')
_ = self._apple_rom_version(input_buffer=input_buffer, padding=padding)
if input_path != input_object:
delete_file(in_path=input_path)
return 0
return True
def _apple_rom_version(self, input_buffer: bytes | bytearray, padding: int = 0) -> bool:
rom_version_match: Match[bytes] | None = PAT_APPLE_ROM_VER.search(string=input_buffer)
if rom_version_match:
rom_version_match_off: int = rom_version_match.start()
rom_version_header_len: int = input_buffer[rom_version_match_off:].find(b'\n')
if rom_version_header_len != -1:
rom_version_data_bgn: int = rom_version_match_off + rom_version_header_len
rom_version_data_len: int = input_buffer[rom_version_data_bgn:].find(b'\x00')
if rom_version_data_len != -1:
rom_version_data_end: int = rom_version_data_bgn + rom_version_data_len
rom_version_data: bytes = input_buffer[rom_version_data_bgn:rom_version_data_end]
rom_version_text: str = rom_version_data.decode('utf-8').strip('\n')
for rom_version_line in [line.strip() for line in rom_version_text.split('\n')]:
rom_version_parts: list[str] = rom_version_line.split(sep=':', maxsplit=1)
self.apple_rom_version[rom_version_parts[0].strip()].add(rom_version_parts[1].strip())
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)
return True
return False
if __name__ == '__main__':

View file

@ -13,7 +13,7 @@ from re import Match
from typing import Final
from biosutilities.common.paths import make_dirs, path_stem
from biosutilities.common.patterns import PAT_APPLE_IM4P, PAT_INTEL_IFD
from biosutilities.common.patterns import PAT_APPLE_IM4P, PAT_INTEL_FD
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
@ -32,15 +32,15 @@ class AppleEfiIm4pSplit(BIOSUtility):
input_buffer: bytes = file_to_bytes(in_object=input_object)
if PAT_APPLE_IM4P.search(string=input_buffer) and PAT_INTEL_IFD.search(string=input_buffer):
if PAT_APPLE_IM4P.search(string=input_buffer) and PAT_INTEL_FD.search(string=input_buffer):
return True
return False
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Parse & Split Apple EFI IM4P image """
exit_codes: list[int] = []
parse_success: bool = True
input_buffer: bytes = file_to_bytes(in_object=input_object)
@ -50,7 +50,7 @@ class AppleEfiIm4pSplit(BIOSUtility):
im4p_match: Match[bytes] | None = PAT_APPLE_IM4P.search(string=input_buffer)
if not im4p_match:
return 1
return False
# After IM4P mefi (0x15), multi EFI payloads have _MEFIBIN (0x100) but is difficult to RE w/o varying samples.
# However, _MEFIBIN is not required for splitting SPI images due to Intel Flash Descriptor Components Density.
@ -75,7 +75,7 @@ class AppleEfiIm4pSplit(BIOSUtility):
input_buffer = input_buffer[efi_data_bgn:efi_data_bgn + efi_data_len]
# Parse Intel Flash Descriptor pattern matches
for ifd in PAT_INTEL_IFD.finditer(string=input_buffer):
for ifd in PAT_INTEL_FD.finditer(string=input_buffer):
# Component Base Address from FD start (ICH8-ICH10 = 1, IBX = 2, CPT+ = 3)
ifd_flmap0_fcba: int = input_buffer[ifd.start() + 0x4] * 0x10
@ -148,9 +148,9 @@ class AppleEfiIm4pSplit(BIOSUtility):
printer(message=f'Error: Bad image size 0x{output_size:07X}, expected 0x{ifd_comp_all_size:07X}!',
padding=padding + 4)
exit_codes.append(1)
parse_success = False
return sum(exit_codes)
return parse_success
if __name__ == '__main__':

View file

@ -56,9 +56,9 @@ class AppleEfiPbzxExtract(BIOSUtility):
input_buffer: bytes = file_to_bytes(in_object=input_object)
return bool(PAT_APPLE_PBZX.search(string=input_buffer[:0x4]))
return bool(PAT_APPLE_PBZX.search(string=input_buffer, endpos=4))
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Parse & Extract Apple PBZX image """
input_buffer: bytes = file_to_bytes(in_object=input_object)
@ -107,7 +107,7 @@ class AppleEfiPbzxExtract(BIOSUtility):
if cpio_len != len(cpio_bin):
printer(message='Error: Unexpected CPIO archive size!', padding=padding)
return 1
return False
cpio_name: str = path_stem(in_path=input_object) if isinstance(input_object, str) else 'Payload'
@ -119,14 +119,14 @@ class AppleEfiPbzxExtract(BIOSUtility):
# 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'], check=True) == 0:
padding=padding, args=['-tCPIO']):
os.remove(path=cpio_path) # Successful extraction, delete PBZX > CPIO archive
else:
return 3
return False
else:
return 2
return False
return 0
return True
if __name__ == '__main__':

View file

@ -10,9 +10,9 @@ Copyright (C) 2019-2024 Plato Mavropoulos
import os
from biosutilities.common.compression import is_szip_supported, szip_decompress
from biosutilities.common.paths import (copy_file, delete_dirs, extract_folder, path_files,
make_dirs, path_name, path_parent, runtime_root)
from biosutilities.common.patterns import PAT_APPLE_PKG_TAR, PAT_APPLE_PKG_XAR
from biosutilities.common.paths import (copy_file, delete_dirs, extract_folder, make_dirs, path_files,
path_name, path_parent, path_suffixes, runtime_root)
from biosutilities.common.patterns import PAT_APPLE_PKG_DMG, PAT_APPLE_PKG_TAR, PAT_APPLE_PKG_XAR
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
@ -42,19 +42,22 @@ class AppleEfiPkgExtract(BIOSUtility):
with open(file=input_path, mode='wb') as input_path_object:
input_path_object.write(input_buffer)
if is_szip_supported(in_path=input_path, args=['-tXAR']):
if is_szip_supported(in_path=input_path, args=['-tXAR:s0']):
if bool(PAT_APPLE_PKG_XAR.search(string=input_buffer, endpos=4)):
is_apple_efi_pkg = True
elif is_szip_supported(in_path=input_path, args=['-tTAR']):
elif is_szip_supported(in_path=input_path, args=['-tTAR:s0']):
if bool(PAT_APPLE_PKG_TAR.search(string=input_buffer)):
is_apple_efi_pkg = True
elif is_szip_supported(in_path=input_path, args=['-tDMG:s0']):
if bool(PAT_APPLE_PKG_DMG.search(string=input_buffer, endpos=200)):
is_apple_efi_pkg = True
if input_path != input_object:
os.remove(path=input_path)
return is_apple_efi_pkg
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Parse & Extract Apple EFI PKG packages """
if isinstance(input_object, str) and os.path.isfile(path=input_object):
@ -69,13 +72,15 @@ class AppleEfiPkgExtract(BIOSUtility):
working_dir: str = os.path.join(extract_path, 'temp')
for pkg_type in ('XAR', 'TAR'):
make_dirs(in_path=working_dir, delete=True)
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,
args=[f'-t{pkg_type}'], check=True) == 0:
args=None if pkg_type == 'DMG' else [f'-t{pkg_type}']):
break
else:
return 1
return False
if input_path != input_object:
os.remove(path=input_path)
@ -83,22 +88,42 @@ class AppleEfiPkgExtract(BIOSUtility):
for work_file in path_files(in_path=working_dir):
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._tar_gzip(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)
delete_dirs(in_path=working_dir)
return 0
return True
def _tar_gzip(self, input_path: str, extract_path: str, padding: int = 0) -> None:
""" TAR > GZIP """
def _xar_gzip(self, input_path: str, extract_path: str, padding: int = 0) -> None:
""" XAR/TAR > GZIP """
if is_szip_supported(in_path=input_path, padding=padding, args=['-tTAR']):
tar_path: str = extract_folder(in_path=input_path)
for pkg_type in ('XAR', 'TAR'):
if is_szip_supported(in_path=input_path, padding=padding, args=[f'-t{pkg_type}']):
pkg_path: str = extract_folder(in_path=input_path, suffix=f'_{pkg_type.lower()}_gzip')
if szip_decompress(in_path=input_path, out_path=tar_path, in_name='TAR', padding=padding,
args=['-tTAR'], check=False) == 0:
for tar_file in path_files(in_path=tar_path):
self._gzip_cpio(input_path=tar_file, extract_path=extract_path, padding=padding + 4)
if szip_decompress(in_path=input_path, out_path=pkg_path, in_name=pkg_type,
padding=padding, args=[f'-t{pkg_type}']):
for pkg_file in path_files(in_path=pkg_path):
self._gzip_cpio(input_path=pkg_file, extract_path=extract_path, padding=padding + 4)
break
def _dmg_zip(self, input_path: str, extract_path: str, padding: int = 0) -> None:
""" DMG > ZIP """
if is_szip_supported(in_path=input_path, padding=padding, args=['-tDMG']):
dmg_path: str = extract_folder(in_path=input_path, suffix='_dmg_zip')
if szip_decompress(in_path=input_path, out_path=dmg_path, in_name='DMG', padding=padding, args=None):
for dmg_file in path_files(in_path=dmg_path):
if is_szip_supported(in_path=dmg_file, padding=padding + 4, args=['-tZIP']):
zip_path: str = extract_folder(in_path=dmg_file)
if szip_decompress(in_path=dmg_file, out_path=zip_path, in_name='ZIP',
padding=padding + 4, args=['-tZIP']):
for zip_file in path_files(in_path=zip_path):
self._im4p_id(input_path=zip_file, output_path=extract_path, padding=padding + 8)
def _pbzx_zip(self, input_path: str, extract_path: str, padding: int = 0) -> None:
""" PBZX > ZIP """
@ -108,9 +133,9 @@ class AppleEfiPkgExtract(BIOSUtility):
if pbzx_module.check_format(input_object=input_path):
printer(message=f'Extracting PBZX via {pbzx_module.title}', padding=padding)
pbzx_path: str = extract_folder(in_path=input_path)
pbzx_path: str = extract_folder(in_path=input_path, suffix='_pbzx_zip')
if pbzx_module.parse_format(input_object=input_path, extract_path=pbzx_path, padding=padding + 4) == 0:
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)
for pbzx_file in path_files(in_path=pbzx_path):
@ -118,7 +143,7 @@ class AppleEfiPkgExtract(BIOSUtility):
zip_path: str = extract_folder(in_path=pbzx_file)
if szip_decompress(in_path=pbzx_file, out_path=zip_path, in_name='ZIP',
padding=padding + 4, args=['-tZIP'], check=False) == 0:
padding=padding + 4, args=['-tZIP']):
for zip_file in path_files(in_path=zip_path):
self._im4p_id(input_path=zip_file, output_path=extract_path, padding=padding + 8)
@ -126,16 +151,16 @@ class AppleEfiPkgExtract(BIOSUtility):
""" GZIP > CPIO """
if is_szip_supported(in_path=input_path, padding=padding, args=['-tGZIP']):
gzip_path: str = extract_folder(in_path=input_path)
gzip_path: str = extract_folder(in_path=input_path, suffix='_gzip_cpio')
if szip_decompress(in_path=input_path, out_path=gzip_path, in_name='GZIP', padding=padding,
args=['-tGZIP'], check=True) == 0:
if szip_decompress(in_path=input_path, out_path=gzip_path, in_name='GZIP',
padding=padding, args=['-tGZIP']):
for gzip_file in path_files(in_path=gzip_path):
if is_szip_supported(in_path=gzip_file, padding=padding + 4, args=['-tCPIO']):
cpio_path: str = extract_folder(in_path=gzip_file)
if szip_decompress(in_path=gzip_file, out_path=cpio_path, in_name='CPIO',
padding=padding + 4, args=['-tCPIO'], check=False) == 0:
padding=padding + 4, args=['-tCPIO']):
for cpio_file in path_files(in_path=cpio_path):
self._im4p_id(input_path=cpio_file, output_path=extract_path, padding=padding + 8)
@ -143,6 +168,9 @@ class AppleEfiPkgExtract(BIOSUtility):
def _im4p_id(input_path: str, output_path: str, padding: int = 0) -> None:
""" Split IM4P (if applicable), identify and rename EFI """
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):
return None
@ -169,11 +197,9 @@ class AppleEfiPkgExtract(BIOSUtility):
if efi_id_module.check_format(input_object=efi_source):
printer(message=f'Identifying EFI via {efi_id_module.title}', padding=padding + 4)
efi_id_exit: int = efi_id_module.parse_format(
input_object=efi_source, extract_path=extract_folder(in_path=efi_source), padding=padding + 8)
if efi_id_exit == 0:
efi_dest: str = os.path.join(path_parent(in_path=efi_source), efi_id_module.efi_name_id)
if efi_id_module.parse_format(input_object=efi_source, extract_path=extract_folder(in_path=efi_source),
padding=padding + 8):
efi_dest: str = os.path.join(path_parent(in_path=efi_source), efi_id_module.efi_file_name)
os.rename(src=efi_source, dst=efi_dest)

View file

@ -30,7 +30,7 @@ class AwardBiosExtract(BIOSUtility):
return bool(PAT_AWARD_LZH.search(string=in_buffer))
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> None:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Parse & Extract Award BIOS image """
input_buffer: bytes = file_to_bytes(in_object=input_object)
@ -74,7 +74,7 @@ class AwardBiosExtract(BIOSUtility):
# 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, check=False)
padding=padding + 4)
# Manually check if 7-Zip extracted LZH due to its CRC check issue
if os.path.isfile(path=mod_path):
@ -88,6 +88,8 @@ class AwardBiosExtract(BIOSUtility):
self.parse_format(input_object=mod_path, extract_path=extract_folder(mod_path),
padding=padding + 8)
return True
if __name__ == '__main__':
AwardBiosExtract().run_utility()

View file

@ -41,7 +41,7 @@ def is_szip_supported(in_path: str, padding: int = 0, args: list | None = None,
def szip_decompress(in_path: str, out_path: str, in_name: str | None, padding: int = 0, args: list | None = None,
check: bool = False, silent: bool = False) -> int:
check: bool = False, silent: bool = False) -> bool:
""" Archive decompression via 7-Zip """
if not in_name:
@ -64,12 +64,12 @@ def szip_decompress(in_path: str, out_path: str, in_name: str | None, padding: i
if not silent:
printer(message=f'Error: 7-Zip could not extract {in_name} file {in_path}: {error}!', padding=padding)
return 1
return False
if not silent:
printer(message=f'Successful {in_name} decompression via 7-Zip!', padding=padding)
return 0
return True
def efi_compress_sizes(data: bytes | bytearray) -> tuple[int, int]:
@ -98,7 +98,7 @@ def is_efi_compressed(data: bytes | bytearray, strict: bool = True) -> bool:
def efi_decompress(in_path: str, out_path: str, padding: int = 0, silent: bool = False,
comp_type: str = '--uefi') -> int:
comp_type: str = '--uefi') -> bool:
""" EFI/Tiano Decompression via TianoCompress """
try:
@ -114,9 +114,9 @@ def efi_decompress(in_path: str, out_path: str, padding: int = 0, silent: bool =
if not silent:
printer(message=f'Error: TianoCompress could not extract file {in_path}: {error}!', padding=padding)
return 1
return False
if not silent:
printer(message='Successful EFI decompression via TianoCompress!', padding=padding)
return 0
return True

View file

@ -20,7 +20,7 @@ from biosutilities.common.paths import project_root
from biosutilities.common.texts import to_string
def get_external_path(cmd: str | list | tuple, raise_on_error: bool = True) -> str | None:
def get_external_path(cmd: str | list | tuple) -> str:
""" Get external dependency path (PATH environment variable or "external" directory) """
external_root: str = os.path.join(project_root(), 'external')
@ -33,18 +33,15 @@ def get_external_path(cmd: str | list | tuple, raise_on_error: bool = True) -> s
if command_path and os.path.isfile(path=command_path):
return command_path
if raise_on_error:
raise OSError(f'{to_string(in_object=cmd, sep_char=", ")} could not be found!')
return None
def big_script_tool() -> Type | None:
""" Get Intel BIOS Guard Script Tool class """
bgst: str | None = get_external_path(cmd='big_script_tool', raise_on_error=False)
try:
bgst: str = get_external_path(cmd='big_script_tool')
if bgst is not None:
bgst_spec: ModuleSpec | None = spec_from_file_location(
name='big_script_tool', location=re.sub(r'\.PY$', '.py', bgst))
@ -57,35 +54,37 @@ def big_script_tool() -> Type | None:
bgst_spec.loader.exec_module(module=bgst_module)
return getattr(bgst_module, 'BigScript')
except OSError:
pass
return None
def comextract_path() -> str | None:
def comextract_path() -> str:
""" Get ToshibaComExtractor path """
return get_external_path(cmd='comextract')
def szip_path() -> str | None:
def szip_path() -> str:
""" Get 7-Zip path """
return get_external_path(cmd=['7zzs', '7zz', '7z'])
def tiano_path() -> str | None:
def tiano_path() -> str:
""" Get TianoCompress path """
return get_external_path(cmd='TianoCompress')
def uefifind_path() -> str | None:
def uefifind_path() -> str:
""" Get UEFIFind path """
return get_external_path(cmd='UEFIFind')
def uefiextract_path() -> str | None:
def uefiextract_path() -> str:
""" Get UEFIExtract path """
return get_external_path(cmd='UEFIExtract')

View file

@ -132,7 +132,7 @@ def delete_dirs(in_path: str) -> None:
""" Delete folder(s), if present """
if Path(in_path).is_dir():
shutil.rmtree(path=in_path, onexc=clear_readonly_callback)
shutil.rmtree(path=in_path, onerror=clear_readonly_callback) # pylint: disable=deprecated-argument
def delete_file(in_path: str) -> None:
@ -164,7 +164,7 @@ def clear_readonly_callback(in_func: Callable, in_path: str, _) -> None:
clear_readonly(in_path=in_path)
in_func(in_path=in_path)
in_func(path=in_path)
def path_files(in_path: str, follow_links: bool = False) -> list[str]:

View file

@ -19,9 +19,8 @@ PAT_AMI_UCP: Final[re.Pattern[bytes]] = re.compile(
flags=re.DOTALL
)
PAT_APPLE_EFI: Final[re.Pattern[bytes]] = re.compile(
pattern=br'\$IBIOSI\$.{16}\x2E\x00.{6}\x2E\x00.{8}\x2E\x00.{6}\x2E\x00.{20}\x00{2}',
flags=re.DOTALL
PAT_APPLE_ROM_VER: Final[re.Pattern[bytes]] = re.compile(
pattern=br'Apple ROM Version\x0A\x20{2}'
)
PAT_APPLE_IM4P: Final[re.Pattern[bytes]] = re.compile(
@ -32,14 +31,18 @@ PAT_APPLE_PBZX: Final[re.Pattern[bytes]] = re.compile(
pattern=br'pbzx'
)
PAT_APPLE_PKG_XAR: Final[re.Pattern[bytes]] = re.compile(
pattern=br'xar!'
PAT_APPLE_PKG_DMG: Final[re.Pattern[bytes]] = re.compile(
pattern=br'EFI PART'
)
PAT_APPLE_PKG_TAR: Final[re.Pattern[bytes]] = re.compile(
pattern=br'<key>IFPkgDescriptionDescription</key>'
)
PAT_APPLE_PKG_XAR: Final[re.Pattern[bytes]] = re.compile(
pattern=br'xar!'
)
PAT_AWARD_LZH: Final[re.Pattern[bytes]] = re.compile(
pattern=br'-lh[04567]-'
)
@ -71,16 +74,21 @@ PAT_INSYDE_SFX: Final[re.Pattern[bytes]] = re.compile(
pattern=br'\x0D\x0A;!@InstallEnd@!\x0D\x0A(7z\xBC\xAF\x27|\x6E\xF4\x79\x5F\x4E)'
)
PAT_INTEL_ENG: Final[re.Pattern[bytes]] = re.compile(
PAT_INTEL_ENGINE: Final[re.Pattern[bytes]] = re.compile(
pattern=br'\x04\x00{3}[\xA1\xE1]\x00{3}.{8}\x86\x80.{9}\x00\$((MN2)|(MAN))',
flags=re.DOTALL
)
PAT_INTEL_IFD: Final[re.Pattern[bytes]] = re.compile(
PAT_INTEL_FD: Final[re.Pattern[bytes]] = re.compile(
pattern=br'\x5A\xA5\xF0\x0F.{172}\xFF{16}',
flags=re.DOTALL
)
PAT_INTEL_IBIOSI: Final[re.Pattern[bytes]] = re.compile(
pattern=br'\$IBIOSI\$.{16}\x2E\x00.{6}\x2E\x00.{8}\x2E\x00.{6}\x2E\x00.{20}\x00{2}',
flags=re.DOTALL
)
PAT_MICROSOFT_CAB: Final[re.Pattern[bytes]] = re.compile(
pattern=br'MSCF\x00{4}'
)

View file

@ -30,17 +30,19 @@ 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 = ' ') -> None:
""" Show message(s), controlling padding, newline, pausing & separator """
pause: bool = False, 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)
message_output: str = '\n' if new_line else ''
for line_index, line_text in enumerate(iterable=message_string.split('\n')):
line_newline: str = '' if line_index == 0 else '\n'
for message_line_index, message_line_text in enumerate(iterable=message_string.split('\n')):
line_new: str = '' if message_line_index == 0 else '\n'
message_output += f'{line_newline}{" " * padding}{line_text}'
line_text: str = message_line_text.strip() if strip else message_line_text
message_output += f'{line_new}{" " * padding}{line_text}'
if pause:
input(message_output)

View file

@ -30,10 +30,6 @@ class BIOSUtility:
MIN_PYTHON_VER: Final[tuple[int, int]] = (3, 10)
def __init__(self, arguments: list[str] | None = None) -> None:
self._check_sys_py()
self._check_sys_os()
self.title: str = f'{self.TITLE.strip()} v{__version__}'
argparser: ArgumentParser = ArgumentParser(allow_abbrev=False)
@ -53,9 +49,13 @@ class BIOSUtility:
self._output_path: str = ''
def run_utility(self, padding: int = 0) -> int:
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)
@ -85,8 +85,7 @@ class BIOSUtility:
break
if self.parse_format(input_object=input_file, extract_path=extract_path,
padding=padding + 8) in [0, None]:
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):
@ -94,14 +93,14 @@ class BIOSUtility:
printer(message='Done!\n' if not self.arguments.auto_exit else None, pause=not self.arguments.auto_exit)
return exit_code
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) -> int | None:
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__}')
@ -112,6 +111,8 @@ class BIOSUtility:
raise NotImplementedError(f'Method "check_format" not implemented at {__name__}')
def _setup_input_files(self, padding: int = 0) -> None:
self._input_files = []
input_paths: list[str] = self.arguments.paths
if not input_paths:
@ -126,6 +127,8 @@ class BIOSUtility:
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:

View file

@ -15,7 +15,7 @@ import os
import zlib
from re import Match
from typing import Any
from typing import Any, Final
from biosutilities.common.checksums import checksum_8_xor
from biosutilities.common.compression import is_szip_supported, szip_decompress
@ -231,13 +231,13 @@ class DellPfsExtract(BIOSUtility):
(['-s', '--structure'], {'help': 'show PFS structure information', 'action': 'store_true'})
]
PFS_HEAD_LEN: int = ctypes.sizeof(DellPfsHeader)
PFS_FOOT_LEN: int = ctypes.sizeof(DellPfsFooter)
PFS_INFO_LEN: int = ctypes.sizeof(DellPfsInfo)
PFS_NAME_LEN: int = ctypes.sizeof(DellPfsName)
PFS_META_LEN: int = ctypes.sizeof(DellPfsMetadata)
PFS_PFAT_LEN: int = ctypes.sizeof(DellPfsPfatMetadata)
PFAT_HDR_LEN: int = ctypes.sizeof(IntelBiosGuardHeader)
PFS_HEAD_LEN: Final[int] = ctypes.sizeof(DellPfsHeader)
PFS_FOOT_LEN: Final[int] = ctypes.sizeof(DellPfsFooter)
PFS_INFO_LEN: Final[int] = ctypes.sizeof(DellPfsInfo)
PFS_NAME_LEN: Final[int] = ctypes.sizeof(DellPfsName)
PFS_META_LEN: Final[int] = ctypes.sizeof(DellPfsMetadata)
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:
""" Check if input is Dell PFS/PKG image """
@ -252,7 +252,7 @@ class DellPfsExtract(BIOSUtility):
return False
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> None:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Parse & Extract Dell PFS Update image """
input_buffer: bytes = file_to_bytes(in_object=input_object)
@ -280,6 +280,8 @@ class DellPfsExtract(BIOSUtility):
pfs_name=pfs_name, pfs_index=pfs_index, pfs_count=1, is_rec=False,
padding=padding)
return True
@staticmethod
def _is_pfs_pkg(input_object: str | bytes | bytearray) -> bool:
"""
@ -353,7 +355,7 @@ class DellPfsExtract(BIOSUtility):
if is_szip_supported(in_path=pkg_tar_path, padding=0, args=['-tTAR']):
if szip_decompress(in_path=pkg_tar_path, out_path=working_path, in_name='TAR', padding=0,
args=['-tTAR'], check=True, silent=True) == 0:
args=['-tTAR'], check=True, silent=True):
os.remove(path=pkg_tar_path)
else:
return pfs_results
@ -1005,6 +1007,8 @@ class DellPfsExtract(BIOSUtility):
# Get PFAT Header Flags (SFAM, ProtectEC, GFXMitDis, FTU, Reserved)
is_sfam, _, _, _, _ = pfat_hdr.get_flags()
ami_pfat_extract: AmiPfatExtract = AmiPfatExtract()
# Parse sub-PFS PFAT Signature, if applicable (only when PFAT Header > SFAM flag is set)
if is_sfam:
if self.arguments.structure:
@ -1014,13 +1018,13 @@ class DellPfsExtract(BIOSUtility):
if _pfat_sign_len == 0:
_pfat_sign_sig: bytes = pfat_hdr.get_hdr_marker()
_pfat_sign_pfs: int = pfs_entry_size + self.PFS_PFAT_LEN
_pfat_sign_max: int = _pfat_sign_pfs + AmiPfatExtract.PFAT_INT_SIG_MAX_LEN + len(_pfat_sign_sig)
_pfat_sign_max: int = _pfat_sign_pfs + ami_pfat_extract.PFAT_INT_SIG_MAX_LEN + len(_pfat_sign_sig)
_pfat_sign_lim: int = pfat_payload_end + _pfat_sign_max
_pfat_sign_off: int = pfat_payload.find(_pfat_sign_sig, pfat_payload_end, _pfat_sign_lim)
_pfat_sign_len = _pfat_sign_off - pfat_payload_end - _pfat_sign_pfs
# Get sub-PFS PFAT Signature Structure values
pfat_sign_len: int = AmiPfatExtract().parse_bg_sign(
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)
@ -1033,7 +1037,7 @@ class DellPfsExtract(BIOSUtility):
if self.arguments.structure:
printer(message=f'PFAT Block {pfat_entry_idx_ord} - Script:\n', padding=padding + 12)
_ = AmiPfatExtract().parse_bg_script(script_data=pfat_script_data, padding=padding + 16)
_ = ami_pfat_extract.parse_bg_script(script_data=pfat_script_data, padding=padding + 16)
# The payload of sub-PFS PFAT Entries is not in proper order by default
# We can get each payload's order from PFAT Script > OpCode #2 (set I0 imm)

View file

@ -30,7 +30,7 @@ class FujitsuSfxExtract(BIOSUtility):
return bool(PAT_FUJITSU_SFX.search(string=input_buffer))
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Parse & Extract Fujitsu SFX image """
input_buffer: bytes = file_to_bytes(in_object=input_object)
@ -39,7 +39,7 @@ class FujitsuSfxExtract(BIOSUtility):
match_cab: re.Match[bytes] | None = PAT_FUJITSU_SFX.search(string=input_buffer)
if not match_cab:
return 1
return False
printer(message='Detected obfuscated CAB archive!', padding=padding)
@ -78,14 +78,14 @@ class FujitsuSfxExtract(BIOSUtility):
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) == 0:
padding=padding + 8, check=True):
os.remove(path=cab_path)
else:
return 3
return False
else:
return 2
return False
return 0
return True
if __name__ == '__main__':

View file

@ -34,7 +34,7 @@ class FujitsuUpcExtract(BIOSUtility):
return is_upc
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Parse & Extract Fujitsu UPC image """
make_dirs(in_path=extract_path, delete=True)
@ -56,12 +56,12 @@ class FujitsuUpcExtract(BIOSUtility):
output_path: str = os.path.join(extract_path, f'{input_name}.bin')
efi_code: int = 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=padding)
if input_path != input_object:
os.remove(path=input_path)
return efi_code
return efi_status
if __name__ == '__main__':

View file

@ -11,7 +11,7 @@ import ctypes
import os
import re
from typing import Any
from typing import Any, Final
from biosutilities.common.compression import is_szip_supported, szip_decompress
from biosutilities.common.paths import extract_folder, path_files, make_dirs, path_name, safe_name
@ -58,10 +58,10 @@ class InsydeIfdExtract(BIOSUtility):
TITLE: str = 'Insyde iFlash/iFdPacker Extractor'
# Insyde iFdPacker known 7-Zip SFX Password
INS_SFX_PWD: str = 'Y`t~i!L@i#t$U%h^s7A*l(f)E-d=y+S_n?i'
INS_SFX_PWD: Final[str] = 'Y`t~i!L@i#t$U%h^s7A*l(f)E-d=y+S_n?i'
# Insyde iFlash known Image Names
INS_IFL_IMG: dict = {
INS_IFL_IMG: Final[dict[str, list[str]]] = {
'BIOSCER': ['Certificate', 'bin'],
'BIOSCR2': ['Certificate 2nd', 'bin'],
'BIOSIMG': ['BIOS-UEFI', 'bin'],
@ -77,7 +77,7 @@ class InsydeIfdExtract(BIOSUtility):
}
# Get common ctypes Structure Sizes
INS_IFL_LEN: int = ctypes.sizeof(IflashHeader)
INS_IFL_LEN: Final[int] = ctypes.sizeof(IflashHeader)
def check_format(self, input_object: str | bytes | bytearray) -> bool:
""" Check if input is Insyde iFlash/iFdPacker Update image """
@ -92,7 +92,7 @@ class InsydeIfdExtract(BIOSUtility):
return False
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Parse & Extract Insyde iFlash/iFdPacker Update images """
input_buffer: bytes = file_to_bytes(in_object=input_object)
@ -105,7 +105,7 @@ class InsydeIfdExtract(BIOSUtility):
ifdpack_code: int = self._insyde_packer_extract(input_buffer=input_buffer, extract_path=ifdpack_path,
padding=padding)
return iflash_code and ifdpack_code
return (iflash_code and ifdpack_code) == 0
def _insyde_iflash_detect(self, input_buffer: bytes) -> list:
""" Detect Insyde iFlash Update image """
@ -219,7 +219,7 @@ class InsydeIfdExtract(BIOSUtility):
if is_szip_supported(in_path=sfx_path, padding=padding + 8, args=[f'-p{self.INS_SFX_PWD}'], silent=False):
if szip_decompress(in_path=sfx_path, out_path=extract_path, in_name='Insyde iFdPacker > 7-Zip SFX',
padding=padding + 8, args=[f'-p{self.INS_SFX_PWD}'], check=True) == 0:
padding=padding + 8, args=[f'-p{self.INS_SFX_PWD}'], check=True):
os.remove(path=sfx_path)
else:
return 125
@ -232,10 +232,10 @@ class InsydeIfdExtract(BIOSUtility):
if self.check_format(input_object=sfx_file):
printer(message=path_name(in_path=sfx_file), padding=padding + 12)
ifd_code: int = self.parse_format(input_object=sfx_file, extract_path=extract_folder(sfx_file),
ifd_status: int = self.parse_format(input_object=sfx_file, extract_path=extract_folder(sfx_file),
padding=padding + 16)
exit_codes.append(ifd_code)
exit_codes.append(0 if ifd_status else 1)
return sum(exit_codes)

View file

@ -12,6 +12,8 @@ import logging
import os
import re
from typing import Final
import pefile
# noinspection PyPackageRequirements
@ -33,9 +35,9 @@ class PanasonicBiosExtract(BIOSUtility):
TITLE: str = 'Panasonic BIOS Package Extractor'
PAN_PE_DESC_UNP: str = 'UNPACK UTILITY'
PAN_PE_DESC_UNP: Final[str] = 'UNPACK UTILITY'
PAN_PE_DESC_UPD: str = 'BIOS UPDATE'
PAN_PE_DESC_UPD: Final[str] = 'BIOS UPDATE'
def check_format(self, input_object: str | bytes | bytearray) -> bool:
""" Check if input is Panasonic BIOS Package PE """
@ -51,7 +53,7 @@ class PanasonicBiosExtract(BIOSUtility):
return True
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Parse & Extract Panasonic BIOS Package PE """
upd_pe_file: pefile.PE = ms_pe(in_file=input_object, padding=padding) # type: ignore
@ -89,7 +91,7 @@ class PanasonicBiosExtract(BIOSUtility):
is_upd_extracted = self._panasonic_img_extract(pe_file=upd_pe_file, extract_path=extract_path,
pe_name=upd_pe_name, padding=upd_padding + 8)
return 0 if is_upd_extracted else 1
return is_upd_extracted
@staticmethod
def _panasonic_pkg_name(input_object: str | bytes | bytearray) -> str:
@ -123,7 +125,7 @@ class PanasonicBiosExtract(BIOSUtility):
printer(message=f'Panasonic BIOS Package > PE > CAB {cab_tag}', padding=padding)
if szip_decompress(in_path=cab_path, out_path=extract_path, in_name='CAB',
padding=padding + 4, check=True) == 0:
padding=padding + 4, check=True):
os.remove(path=cab_path) # Successful extraction, delete CAB archive
for extracted_file_path in path_files(in_path=extract_path):
@ -181,11 +183,13 @@ class PanasonicBiosExtract(BIOSUtility):
printer(message='Successful PE Resource extraction!', padding=padding + 4)
ami_pfat_extract: AmiPfatExtract = AmiPfatExtract()
# Detect & Unpack AMI BIOS Guard (PFAT) BIOS image
if AmiPfatExtract().check_format(input_object=res_raw):
if ami_pfat_extract.check_format(input_object=res_raw):
pfat_dir: str = os.path.join(extract_path, res_tag)
AmiPfatExtract().parse_format(input_object=res_raw, extract_path=pfat_dir,
ami_pfat_extract.parse_format(input_object=res_raw, extract_path=pfat_dir,
padding=padding + 8)
else:
if is_ms_pe(in_file=res_raw):

View file

@ -110,7 +110,7 @@ class PhoenixTdkExtract(BIOSUtility):
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) -> int:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Parse & Extract Phoenix Tools Development Kit (TDK) Packer """
exit_code: int = 0
@ -199,7 +199,7 @@ class PhoenixTdkExtract(BIOSUtility):
with open(file=mod_file, mode='wb') as out_file:
out_file.write(mod_data)
return exit_code
return exit_code == 0
@staticmethod
def _get_tdk_base(in_buffer: bytes | bytearray, pack_off: int) -> int | None:

View file

@ -11,6 +11,7 @@ import logging
import os
from re import Match
from typing import Final
from pefile import PE
@ -28,7 +29,7 @@ class PortwellEfiExtract(BIOSUtility):
TITLE: str = 'Portwell EFI Update Extractor'
FILE_NAMES: dict[int, str] = {
FILE_NAMES: Final[dict[int, str]] = {
0: 'Flash.efi',
1: 'Fparts.txt',
2: 'Update.nsh',
@ -56,7 +57,7 @@ class PortwellEfiExtract(BIOSUtility):
return False
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> None:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Parse & Extract Portwell UEFI Unpacker """
# Initialize EFI Payload file chunks
@ -83,6 +84,8 @@ class PortwellEfiExtract(BIOSUtility):
self._parse_efi_files(extract_path=extract_path, efi_files=efi_files, padding=padding)
return True
@staticmethod
def _get_portwell_pe(in_buffer: bytes) -> tuple:
""" Get PE of Portwell EFI executable """
@ -161,7 +164,7 @@ class PortwellEfiExtract(BIOSUtility):
os.replace(src=file_path, dst=comp_fname)
# Successful decompression, delete compressed file
if efi_decompress(in_path=comp_fname, out_path=file_path, padding=padding + 8) == 0:
if efi_decompress(in_path=comp_fname, out_path=file_path, padding=padding + 8):
os.remove(path=comp_fname)

View file

@ -30,7 +30,7 @@ class ToshibaComExtract(BIOSUtility):
return bool(PAT_TOSHIBA_COM.search(string=input_buffer, endpos=0x100))
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Parse & Extract Toshiba BIOS COM image """
make_dirs(in_path=extract_path, delete=True)
@ -55,14 +55,14 @@ class ToshibaComExtract(BIOSUtility):
except Exception as error: # pylint: disable=broad-except
printer(message=f'Error: ToshibaComExtractor could not extract {input_path}: {error}!', padding=padding)
return 1
return False
if input_path != input_object:
os.remove(path=input_path)
printer(message='Successful extraction via ToshibaComExtractor!', padding=padding)
return 0
return True
if __name__ == '__main__':

View file

@ -31,7 +31,7 @@ class VaioPackageExtract(BIOSUtility):
return bool(PAT_VAIO_CFG.search(string=input_buffer))
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
""" Parse & Extract or Unlock VAIO Packaging Manager """
input_buffer: bytes = file_to_bytes(input_object)
@ -47,9 +47,9 @@ class VaioPackageExtract(BIOSUtility):
else:
printer(message='Error: Failed to Extract or Unlock executable!', padding=padding)
return 1
return False
return 0
return True
@staticmethod
def _vaio_cabinet(name: str, buffer: bytes | bytearray, extract_path: str, padding: int = 0) -> int:
@ -94,7 +94,7 @@ class VaioPackageExtract(BIOSUtility):
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='VAIO CAB',
padding=padding + 8, check=True) == 0:
padding=padding + 8, check=True):
os.remove(path=cab_path)
else:
return 3

16
main.py
View file

@ -29,18 +29,18 @@ from biosutilities.vaio_package_extract import VaioPackageExtract
if __name__ == '__main__':
main_argparser: ArgumentParser = ArgumentParser(allow_abbrev=False)
main_argparser.add_argument('input_files', nargs='+')
main_argparser.add_argument('--output-folder', help='extraction folder')
main_argparser.add_argument('--pause-exit', help='pause on exit', action='store_false')
main_argparser.add_argument('paths', nargs='+')
main_argparser.add_argument('-e', '--auto-exit', help='do not pause on exit', action='store_true')
main_argparser.add_argument('-o', '--output-dir', help='extraction directory')
main_arguments: Namespace = main_argparser.parse_args()
if main_arguments.output_folder:
output_folder: Path = Path(main_arguments.output_folder)
if main_arguments.output_dir:
output_folder: Path = Path(main_arguments.output_dir)
else:
output_folder = Path(main_arguments.input_files[0]).parent
output_folder = Path(main_arguments.paths[0]).parent
util_arguments: list[str] = [*main_arguments.input_files, '-e', '-o', str(output_folder.absolute())]
util_arguments: list[str] = [*main_arguments.paths, '-e', '-o', str(output_folder.absolute())]
AmiUcpExtract(arguments=util_arguments).run_utility()
AmiPfatExtract(arguments=util_arguments).run_utility()
@ -59,5 +59,5 @@ if __name__ == '__main__':
AppleEfiIm4pSplit(arguments=util_arguments).run_utility()
AppleEfiIdentify(arguments=util_arguments).run_utility()
if main_arguments.pause_exit:
if not main_arguments.auto_exit:
input('Press any key to exit...')