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
|
@ -1,3 +1,12 @@
|
|||
24.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
|
||||
|
||||
24.10.18
|
||||
|
||||
Removed all Python built-in library keyword arguments (#55)
|
||||
|
|
143
README.md
143
README.md
|
@ -8,10 +8,10 @@ Various BIOS/UEFI-related utilities which aid in modding and/or research
|
|||
|
||||
### Main
|
||||
|
||||
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:
|
||||
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 "Package" method. If needed, a few options can be set, by using the command line:
|
||||
|
||||
``` bash
|
||||
usage: [-h] [-e] [-o OUTPUT_DIR] paths [paths ...]
|
||||
usage: main.py [-h] [-e] [-o OUTPUT_DIR] [paths ...]
|
||||
|
||||
positional arguments:
|
||||
paths
|
||||
|
@ -26,29 +26,9 @@ options:
|
|||
python ./main.py "/path/to/input/file.bin" --output-dir "/path/to/file extractions"
|
||||
```
|
||||
|
||||
### BIOSUtility
|
||||
If no arguments/options are provided, the "main" script requests the input and output paths from the user. If no output path is provided, the utility will use the parent directory of the first input file or fallback to the runtime execution directory.
|
||||
|
||||
Each utility is derived from a base template: BIOSUtility. The base BIOSUtility offers the following options, applicable to all utilities:
|
||||
|
||||
``` bash
|
||||
usage: [-h] [-e] [-o OUTPUT_DIR] [paths ...]
|
||||
|
||||
positional arguments:
|
||||
paths
|
||||
|
||||
options:
|
||||
-h, --help show help and exit
|
||||
-e, --auto-exit skip user action prompts
|
||||
-o OUTPUT_DIR, --output-dir OUTPUT_DIR output extraction directory
|
||||
```
|
||||
|
||||
``` bash
|
||||
python -m biosutilities.ami_pfat_extract -e "/path/to/input/file1.bin" "/path/to/input/file2.bin" "/path/to/input/folder/with/files/" -o "/path/to/output_directory"
|
||||
```
|
||||
|
||||
If no arguments are provided, the BIOSUtility.run_utility() method gets executed, which will request the input and output paths from the user. If no output path is provided, the utility will use the parent directory of the first input file or fallback to the runtime execution directory.
|
||||
|
||||
``` bash
|
||||
``` python
|
||||
Enter input file or directory path: "C:\P5405CSA.303"
|
||||
|
||||
Enter output directory path: "C:\P5405CSA.303_output"
|
||||
|
@ -56,7 +36,7 @@ Enter output directory path: "C:\P5405CSA.303_output"
|
|||
|
||||
### Package
|
||||
|
||||
All utilities form the "biosutilities" python package, which can be installed from PyPi:
|
||||
Each utility is derived from a base "BIOSUtility" template and all utilities form the "biosutilities" python package, which can be installed from PyPi:
|
||||
|
||||
``` bash
|
||||
python -m pip install --upgrade biosutilities[pefile,lznt1]
|
||||
|
@ -67,72 +47,67 @@ Installing the python package is the recommended way to call one or more utiliti
|
|||
``` python
|
||||
from biosutilities.ami_pfat_extract import AmiPfatExtract
|
||||
|
||||
ami_pfat_extractor = AmiPfatExtract()
|
||||
ami_pfat_extractor = AmiPfatExtract(input_object='/path/to/input/file.bin', extract_path='/path/to/output/folder/')
|
||||
|
||||
ami_pfat_extractor.check_format(input_object='/path/to/input/file.bin')
|
||||
ami_pfat_extractor.parse_format(input_object='/path/to/input/file.bin', extract_path='/path/to/output/folder/')
|
||||
is_supported = ami_pfat_extractor.check_format()
|
||||
is_extracted = ami_pfat_extractor.parse_format()
|
||||
```
|
||||
|
||||
``` python
|
||||
from biosutilities.dell_pfs_extract import DellPfsExtract
|
||||
|
||||
dell_pfs_extractor = DellPfsExtract()
|
||||
with open('/path/to/input/file.bin', 'rb') as pfs_file:
|
||||
pfs_data = pfs_file.read()
|
||||
|
||||
with open(file='/path/to/input/file.bin', mode='rb') as pfs_file:
|
||||
pfs_buffer = pfs_file.read()
|
||||
dell_pfs_extractor = DellPfsExtract(input_object=pfs_data, extract_path='/path/to/output/directory/', padding=8)
|
||||
|
||||
dell_pfs_extractor.check_format(input_object=pfs_buffer)
|
||||
dell_pfs_extractor.parse_format(input_object=pfs_buffer, extract_path='/path/to/output/directory/', padding=8)
|
||||
is_supported = dell_pfs_extractor.check_format()
|
||||
is_extracted = dell_pfs_extractor.parse_format()
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
|
||||
Each BIOSUtility expects the following required and optional arguments to check and/or parse a given file format:
|
||||
|
||||
##### input_object (required)
|
||||
|
||||
``` python
|
||||
from biosutilities.phoenix_tdk_extract import PhoenixTdkExtract
|
||||
|
||||
phoenix_tdk_extractor = PhoenixTdkExtract(arguments=['-e', '/path/to/input/file.bin', '-o', '/path/to/output/folder/'])
|
||||
|
||||
phoenix_tdk_extractor.run_utility(padding=4)
|
||||
input_object: str | bytes | bytearray = b''
|
||||
```
|
||||
|
||||
##### extract_path (required)
|
||||
|
||||
``` python
|
||||
from biosutilities.apple_efi_pbzx import AppleEfiPbzxExtract
|
||||
|
||||
apple_efi_pbzx_extractor = AppleEfiPbzxExtract()
|
||||
|
||||
apple_efi_pbzx_extractor.show_version(is_boxed=False, padding=12)
|
||||
extract_path: str = ''
|
||||
```
|
||||
|
||||
It also allows to use directly the four public methods which are inherited by every utility from the base BIOSUtility class.
|
||||
|
||||
#### run_utility
|
||||
|
||||
Run utility after checking for supported format
|
||||
##### padding (optional)
|
||||
|
||||
``` python
|
||||
run_utility(padding: int = 0) -> bool
|
||||
padding: int = 0
|
||||
```
|
||||
|
||||
#### check_format
|
||||
If the required arguments are not provided, it is still possible to use the BIOSUtility-inherited instance to access
|
||||
auxiliary public methods and class constants. However, checking and/or parsing of file formats will not yield results.
|
||||
|
||||
#### Methods
|
||||
|
||||
Once the BIOSUtility-inherited object is initialized with arguments, its two public methods can be called:
|
||||
|
||||
##### check_format
|
||||
|
||||
Check if input object is of specific supported format
|
||||
|
||||
``` python
|
||||
check_format(input_object: str | bytes | bytearray) -> bool
|
||||
is_supported: bool = check_format()
|
||||
```
|
||||
|
||||
#### parse_format
|
||||
##### parse_format
|
||||
|
||||
Process input object as a specific supported format
|
||||
|
||||
``` python
|
||||
parse_format(input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool
|
||||
```
|
||||
|
||||
#### show_version
|
||||
|
||||
Show title and version of utility
|
||||
|
||||
``` python
|
||||
show_version(is_boxed: bool = True, padding: int = 0) -> None
|
||||
is_extracted: bool = parse_format()
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
|
@ -208,7 +183,7 @@ Parses AMI BIOS Guard (a.k.a. PFAT, Platform Firmware Armoring Technology) image
|
|||
|
||||
Note that the AMI PFAT structure may not have an explicit component order. AMI's BIOS Guard Firmware Update Tool (AFUBGT) updates components based on the user/OEM provided Parameters and Options or Index Information table, when applicable. Thus, merging all the components together does not usually yield a proper SPI/BIOS/UEFI image. The utility does generate such a merged file with the name "00 -- \<filename\>\_ALL.bin" but it is up to the end user to determine its usefulness. Additionally, any custom OEM data, after the AMI PFAT structure, is stored in the last file with the name "\<n+1\> -- \_OOB.bin" and it is once again up to the end user to determine its usefulness. In cases where the trailing custom OEM data includes a nested AMI PFAT structure, the utility will process and extract it automatically as well.
|
||||
|
||||
#### Usage
|
||||
#### Arguments
|
||||
|
||||
No additional optional arguments are provided for this utility.
|
||||
|
||||
|
@ -224,11 +199,11 @@ Optionally, to decompile the AMI PFAT \> Intel BIOS Guard Scripts, you must have
|
|||
|
||||
Parses AMI UCP (Utility Configuration Program) Update executables, extracts their firmware components (e.g. SPI/BIOS/UEFI, EC, ME etc) and shows all relevant info. It supports all AMI UCP revisions and formats, including those with nested AMI PFAT, AMI UCP or Insyde iFlash/iFdPacker structures. The output comprises only final firmware components and utilities which are directly usable by end users.
|
||||
|
||||
#### Usage
|
||||
#### Arguments
|
||||
|
||||
Additional optional arguments are provided for this utility:
|
||||
|
||||
* -c or --checksum : verify AMI UCP Checksums (slow)
|
||||
* checksum -> bool : verify AMI UCP Checksums (slow)
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
|
@ -249,7 +224,7 @@ Note: On Linux and macOS, you'll need to compile TianoCompress from sources as n
|
|||
|
||||
Parses Apple IM4P multi-EFI files and splits all detected EFI firmware into separate Intel SPI/BIOS images. The output comprises only final firmware components and utilities which are directly usable by end users.
|
||||
|
||||
#### Usage
|
||||
#### Arguments
|
||||
|
||||
No additional optional arguments are provided for this utility.
|
||||
|
||||
|
@ -263,17 +238,17 @@ To run the utility, you do not need any prerequisites.
|
|||
|
||||
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
|
||||
#### Arguments
|
||||
|
||||
Additional optional arguments are provided for this utility:
|
||||
|
||||
* -q or --silent : suppress structure display
|
||||
* silent -> bool : suppress structure display
|
||||
|
||||
The utility exposes certain public class attributes, once parse_format() method has been successfully executed:
|
||||
|
||||
* _efi_file_name_ -> str : Suggested image filename, based on Intel "IBIOSI" information
|
||||
* _intel_bios_info_ -> dict[str, str] : Information contained at Intel "IBIOSI" structure
|
||||
* _apple_rom_version_ -> defaultdict[str, set] : Information contained at "Apple ROM Version" structure
|
||||
* efi_file_name -> str : Suggested image filename, based on Intel "IBIOSI" information
|
||||
* intel_bios_info -> dict[str, str] : Information contained at Intel "IBIOSI" structure
|
||||
* apple_rom_version -> defaultdict[str, set] : Information contained at "Apple ROM Version" structure
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
|
@ -288,7 +263,7 @@ To run the utility, you must have the following 3rd party tools at PATH or "exte
|
|||
|
||||
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
|
||||
#### Arguments
|
||||
|
||||
No additional optional arguments are provided for this utility.
|
||||
|
||||
|
@ -304,7 +279,7 @@ To run the utility, you must have the following 3rd party tools at PATH or "exte
|
|||
|
||||
Parses Apple EFI PBZX images, re-assembles their CPIO payload and extracts its firmware components (e.g. IM4P, EFI, Utilities, Scripts etc). It supports CPIO re-assembly from both Raw and XZ compressed PBZX Chunks. The output comprises only final firmware components and utilities which are directly usable by end users.
|
||||
|
||||
#### Usage
|
||||
#### Arguments
|
||||
|
||||
No additional optional arguments are provided for this utility.
|
||||
|
||||
|
@ -320,7 +295,7 @@ To run the utility, you must have the following 3rd party tools at PATH or "exte
|
|||
|
||||
Parses Award BIOS images and extracts their modules (e.g. RAID, MEMINIT, \_EN_CODE, awardext etc). It supports all Award BIOS image revisions and formats, including those which contain LZH compressed files. The output comprises only final firmware components which are directly usable by end users.
|
||||
|
||||
#### Usage
|
||||
#### Arguments
|
||||
|
||||
No additional optional arguments are provided for this utility.
|
||||
|
||||
|
@ -336,12 +311,12 @@ To run the utility, you must have the following 3rd party tool at PATH or "exter
|
|||
|
||||
Parses Dell PFS Update images and extracts their Firmware (e.g. SPI, BIOS/UEFI, EC, ME etc) and Utilities (e.g. Flasher etc) component sections. It supports all Dell PFS revisions and formats, including those which are originally LZMA compressed in ThinOS packages (PKG), ZLIB compressed or Intel BIOS Guard (PFAT) protected. The output comprises only final firmware components which are directly usable by end users.
|
||||
|
||||
#### Usage
|
||||
#### Arguments
|
||||
|
||||
Additional optional arguments are provided for this utility:
|
||||
|
||||
* -a or --advanced : extract signatures and metadata
|
||||
* -s or --structure : show PFS structure information
|
||||
* advanced -> bool : extract signatures and metadata
|
||||
* structure -> bool : show PFS structure information
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
|
@ -355,7 +330,7 @@ Optionally, to decompile the Intel BIOS Guard (PFAT) Scripts, you must have the
|
|||
|
||||
Parses Fujitsu SFX BIOS images and extracts their obfuscated Microsoft CAB archived firmware (e.g. SPI, BIOS/UEFI, EC, ME etc) and utilities (e.g. WinPhlash, PHLASH.INI etc) components. The output comprises only final firmware components which are directly usable by end users.
|
||||
|
||||
#### Usage
|
||||
#### Arguments
|
||||
|
||||
No additional optional arguments are provided for this utility.
|
||||
|
||||
|
@ -371,7 +346,7 @@ To run the utility, you must have the following 3rd party tool at PATH or "exter
|
|||
|
||||
Parses Fujitsu UPC BIOS images and extracts their EFI compressed SPI/BIOS/UEFI firmware component. The output comprises only a final firmware component which is directly usable by end users.
|
||||
|
||||
#### Usage
|
||||
#### Arguments
|
||||
|
||||
No additional optional arguments are provided for this utility.
|
||||
|
||||
|
@ -389,7 +364,7 @@ Note: On Linux and macOS, you'll need to compile TianoCompress from sources as n
|
|||
|
||||
Parses Insyde iFlash/iFdPacker Update images and extracts their firmware (e.g. SPI, BIOS/UEFI, EC, ME etc) and utilities (e.g. InsydeFlash, H2OFFT, FlsHook, iscflash, platform.ini etc) components. It supports all Insyde iFlash/iFdPacker revisions and formats, including those which are 7-Zip SFX 7z compressed in raw, obfuscated or password-protected form. The output comprises only final firmware components which are directly usable by end users.
|
||||
|
||||
#### Usage
|
||||
#### Arguments
|
||||
|
||||
No additional optional arguments are provided for this utility.
|
||||
|
||||
|
@ -403,7 +378,7 @@ To run the utility, you do not need any prerequisites.
|
|||
|
||||
Parses Panasonic BIOS Package executables and extracts their firmware (e.g. SPI, BIOS/UEFI, EC etc) and utilities (e.g. winprom, configuration etc) components. It supports all Panasonic BIOS Package revisions and formats, including those which contain LZNT1 compressed files and/or AMI PFAT payloads. The output comprises only final firmware components which are directly usable by end users.
|
||||
|
||||
#### Usage
|
||||
#### Arguments
|
||||
|
||||
No additional optional arguments are provided for this utility.
|
||||
|
||||
|
@ -424,7 +399,7 @@ Moreover, you must have the following 3rd party tool at PATH or "external":
|
|||
|
||||
Parses Phoenix Tools Development Kit (TDK) Packer executables and extracts their firmware (e.g. SPI, BIOS/UEFI, EC etc) and utilities (e.g. WinFlash etc) components. It supports all Phoenix TDK Packer revisions and formats, including those which contain LZMA compressed files. The output comprises only final firmware components which are directly usable by end users.
|
||||
|
||||
#### Usage
|
||||
#### Arguments
|
||||
|
||||
No additional optional arguments are provided for this utility.
|
||||
|
||||
|
@ -440,7 +415,7 @@ To run the utility, you must have the following 3rd party Python module installe
|
|||
|
||||
Parses Portwell UEFI Unpacker EFI executables (usually named "Update.efi") and extracts their firmware (e.g. SPI, BIOS/UEFI, EC etc) and utilities (e.g. Flasher etc) components. It supports all known Portwell UEFI Unpacker revisions (v1.1, v1.2, v2.0) and formats (used, empty, null), including those which contain EFI compressed files. The output comprises only final firmware components and utilities which are directly usable by end users.
|
||||
|
||||
#### Usage
|
||||
#### Arguments
|
||||
|
||||
No additional optional arguments are provided for this utility.
|
||||
|
||||
|
@ -462,7 +437,7 @@ Note: On Linux and macOS, you'll need to compile TianoCompress from sources as n
|
|||
|
||||
Parses Toshiba BIOS COM images and extracts their raw or compressed SPI/BIOS/UEFI firmware component. This utility is effectively a python wrapper around [ToshibaComExtractor by LongSoft](https://github.com/LongSoft/ToshibaComExtractor). The output comprises only a final firmware component which is directly usable by end users.
|
||||
|
||||
#### Usage
|
||||
#### Arguments
|
||||
|
||||
No additional optional arguments are provided for this utility.
|
||||
|
||||
|
@ -480,7 +455,7 @@ Note: On Linux, you'll need to compile comextract from sources as no pre-built b
|
|||
|
||||
Parses VAIO Packaging Manager executables and extracts their firmware (e.g. SPI, BIOS/UEFI, EC, ME etc), utilities (e.g. WBFLASH etc) and driver (audio, video etc) components. If direct extraction fails, it attempts to unlock the executable in order to run at all non-VAIO systems and allow the user to choose the extraction location. It supports all VAIO Packaging Manager revisions and formats, including those which contain obfuscated Microsoft CAB archives or obfuscated unlock values. The output comprises only final firmware components which are directly usable by end users.
|
||||
|
||||
#### Usage
|
||||
#### Arguments
|
||||
|
||||
No additional optional arguments are provided for this utility.
|
||||
|
||||
|
|
|
@ -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):
|
||||
insyde_ifd_extract: InsydeIfdExtract = InsydeIfdExtract(
|
||||
input_object=uaf_fname, extract_path=extract_folder(ins_dir), padding=padding + 4)
|
||||
|
||||
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
|
||||
|
||||
ami_pfat_extract: AmiPfatExtract = AmiPfatExtract()
|
||||
|
||||
# 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)
|
||||
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():
|
||||
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()
|
||||
|
||||
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, suffix='_pbzx_zip')
|
||||
|
||||
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)
|
||||
pbzx_module: AppleEfiPbzxExtract = AppleEfiPbzxExtract(
|
||||
input_object=input_path, extract_path=pbzx_path, padding=padding + 4)
|
||||
|
||||
if pbzx_module.check_format():
|
||||
printer(message=f'Extracting PBZX 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)
|
||||
|
|
|
@ -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()
|
||||
|
||||
# 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)
|
||||
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():
|
||||
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()
|
||||
|
|
164
main.py
164
main.py
|
@ -5,8 +5,12 @@
|
|||
Copyright (C) 2018-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from argparse import ArgumentParser, Namespace
|
||||
from pathlib import Path
|
||||
from typing import Any, Final
|
||||
|
||||
from biosutilities import __version__
|
||||
|
||||
from biosutilities.ami_pfat_extract import AmiPfatExtract
|
||||
from biosutilities.ami_ucp_extract import AmiUcpExtract
|
||||
|
@ -25,39 +29,149 @@ from biosutilities.portwell_efi_extract import PortwellEfiExtract
|
|||
from biosutilities.toshiba_com_extract import ToshibaComExtract
|
||||
from biosutilities.vaio_package_extract import VaioPackageExtract
|
||||
|
||||
from biosutilities.common.paths import (delete_dirs, extract_folder, is_access, is_dir, is_empty_dir, is_file,
|
||||
path_files, path_name, path_parent, real_path, runtime_root)
|
||||
from biosutilities.common.system import python_version, printer, system_platform
|
||||
from biosutilities.common.texts import remove_quotes, to_boxed, to_ordinal
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
class BIOSUtilities:
|
||||
""" Main BIOSUtilities class """
|
||||
|
||||
MAX_FAT32_ITEMS: Final[int] = 65535
|
||||
|
||||
MIN_PYTHON_VER: Final[tuple[int, int]] = (3, 10)
|
||||
|
||||
def __init__(self) -> None:
|
||||
main_argparser: ArgumentParser = ArgumentParser(allow_abbrev=False)
|
||||
|
||||
main_argparser.add_argument('paths', nargs='+')
|
||||
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()
|
||||
self.main_arguments: Namespace = main_argparser.parse_args()
|
||||
|
||||
if main_arguments.output_dir:
|
||||
output_folder: Path = Path(main_arguments.output_dir)
|
||||
self._input_files: list[str] = []
|
||||
|
||||
self._output_path: str = ''
|
||||
|
||||
def _setup_input_files(self, padding: int = 0) -> None:
|
||||
self._input_files = []
|
||||
|
||||
input_paths: list[str] = self.main_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.main_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:
|
||||
output_folder = Path(main_arguments.paths[0]).parent
|
||||
self._output_path = runtime_root()
|
||||
|
||||
util_arguments: list[str] = [*main_arguments.paths, '-e', '-o', str(output_folder.absolute())]
|
||||
def _check_sys_py(self) -> None:
|
||||
""" Check Python Version """
|
||||
|
||||
AmiUcpExtract(arguments=util_arguments).run_utility()
|
||||
AmiPfatExtract(arguments=util_arguments).run_utility()
|
||||
InsydeIfdExtract(arguments=util_arguments).run_utility()
|
||||
DellPfsExtract(arguments=util_arguments).run_utility()
|
||||
PhoenixTdkExtract(arguments=util_arguments).run_utility()
|
||||
PanasonicBiosExtract(arguments=util_arguments).run_utility()
|
||||
VaioPackageExtract(arguments=util_arguments).run_utility()
|
||||
PortwellEfiExtract(arguments=util_arguments).run_utility()
|
||||
ToshibaComExtract(arguments=util_arguments).run_utility()
|
||||
FujitsuSfxExtract(arguments=util_arguments).run_utility()
|
||||
FujitsuUpcExtract(arguments=util_arguments).run_utility()
|
||||
AwardBiosExtract(arguments=util_arguments).run_utility()
|
||||
AppleEfiPkgExtract(arguments=util_arguments).run_utility()
|
||||
AppleEfiPbzxExtract(arguments=util_arguments).run_utility()
|
||||
AppleEfiIm4pSplit(arguments=util_arguments).run_utility()
|
||||
AppleEfiIdentify(arguments=util_arguments).run_utility()
|
||||
sys_py: tuple = python_version()
|
||||
|
||||
if not main_arguments.auto_exit:
|
||||
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}')
|
||||
|
||||
def run_main(self, padding: int = 0) -> bool:
|
||||
""" Run main """
|
||||
|
||||
self._check_sys_py()
|
||||
|
||||
self._check_sys_os()
|
||||
|
||||
self._setup_input_files(padding=padding)
|
||||
|
||||
self._setup_output_dir(padding=padding)
|
||||
|
||||
exit_code: int = len(self._input_files)
|
||||
|
||||
utilities_classes: list[Any] = [
|
||||
AmiUcpExtract, AmiPfatExtract, InsydeIfdExtract, DellPfsExtract, PhoenixTdkExtract, PanasonicBiosExtract,
|
||||
VaioPackageExtract, PortwellEfiExtract, ToshibaComExtract, FujitsuSfxExtract, FujitsuUpcExtract,
|
||||
AwardBiosExtract, AppleEfiPkgExtract, AppleEfiPbzxExtract, AppleEfiIm4pSplit, AppleEfiIdentify
|
||||
]
|
||||
|
||||
for input_file in self._input_files:
|
||||
input_name: str = path_name(in_path=input_file, limit=True)
|
||||
|
||||
printer(message=f'{input_name}\n', padding=padding)
|
||||
|
||||
for utility_class in utilities_classes:
|
||||
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
|
||||
|
||||
utility: Any = utility_class(input_object=input_file, extract_path=extract_path, padding=padding + 8)
|
||||
|
||||
if not utility.check_format():
|
||||
continue
|
||||
|
||||
printer(message=to_boxed(in_text=f'{utility.TITLE} v{__version__}'),
|
||||
new_line=False, padding=padding + 4)
|
||||
|
||||
is_parsed_format: bool = utility.parse_format()
|
||||
|
||||
is_empty_output: bool = is_empty_dir(in_path=extract_path)
|
||||
|
||||
if is_empty_output:
|
||||
delete_dirs(in_path=extract_path)
|
||||
|
||||
if is_parsed_format and not is_empty_output:
|
||||
exit_code -= 1
|
||||
|
||||
break
|
||||
|
||||
printer(message=None, new_line=False)
|
||||
|
||||
if not self.main_arguments.auto_exit:
|
||||
input('Press any key to exit...')
|
||||
|
||||
return exit_code == 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIOSUtilities().run_main()
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
mypy==1.12.0
|
||||
mypy==1.13.0
|
||||
pylint==3.3.1
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue