mirror of
https://github.com/platomav/BIOSUtilities.git
synced 2025-05-12 22:26:13 -04:00
BIOSUtilities v24.10.06
24.10.06 Changed BIOSUtility.parse_format() to return a boolean Changed 7-Zip and EFI decompressors to return booleans Apple EFI Package Extractor support for InstallAssistant Apple EFI Image Identifier support for Apple ROM Version Added Apple EFI Image Identifier class instance attributes Improved flow of non-PATH external executable dependencies Fixed crash when attempting to clear read-only attribute Fixed incompatibility with Python versions prior to 3.12 Performance improvements when initializing BIOSUtilities Improved argument naming and definitions of "main" script Improved the README with new "main" and Apple EFI changes
This commit is contained in:
parent
4175af9eb1
commit
eda154b0f2
26 changed files with 346 additions and 223 deletions
14
CHANGELOG
14
CHANGELOG
|
@ -1,3 +1,17 @@
|
|||
24.10.06
|
||||
|
||||
Changed BIOSUtility.parse_format() to return a boolean
|
||||
Changed 7-Zip and EFI decompressors to return booleans
|
||||
Apple EFI Package Extractor support for InstallAssistant
|
||||
Apple EFI Image Identifier support for Apple ROM Version
|
||||
Added Apple EFI Image Identifier class instance attributes
|
||||
Improved flow of non-PATH external executable dependencies
|
||||
Fixed crash when attempting to clear read-only attribute
|
||||
Fixed incompatibility with Python versions prior to 3.12
|
||||
Performance improvements when initializing BIOSUtilities
|
||||
Improved argument naming and definitions of "main" script
|
||||
Improved the README with new "main" and Apple EFI changes
|
||||
|
||||
24.10.03
|
||||
|
||||
Added external executable dependencies non-PATH flow (#51)
|
||||
|
|
24
README.md
24
README.md
|
@ -11,19 +11,19 @@ Various BIOS/UEFI-related utilities which aid in modding and/or research
|
|||
The "main" script provides a simple way to check and parse each of the user provided files against all utilities, in succession. It is ideal for quick drag & drop operations but lacks the finer control of the BIOSUtility method. If needed, a few options can be set, by using the command line:
|
||||
|
||||
``` bash
|
||||
usage: main.py [-h] [--output-folder OUTPUT_FOLDER] [--pause-exit] input_files [input_files ...]
|
||||
usage: [-h] [-e] [-o OUTPUT_DIR] paths [paths ...]
|
||||
|
||||
positional arguments:
|
||||
input_files
|
||||
paths
|
||||
|
||||
options:
|
||||
-h, --help show help message and exit
|
||||
--output-folder OUTPUT_FOLDER extraction folder
|
||||
--pause-exit pause on exit
|
||||
-h, --help show help and exit
|
||||
-e, --auto-exit do not pause on exit
|
||||
-o OUTPUT_DIR, --output-dir OUTPUT_DIR extraction directory
|
||||
```
|
||||
|
||||
``` bash
|
||||
python ./main.py "/path/to/input/file.bin" --output-folder "/path/to/file extractions"
|
||||
python ./main.py "/path/to/input/file.bin" --output-dir "/path/to/file extractions"
|
||||
```
|
||||
|
||||
### BIOSUtility
|
||||
|
@ -37,7 +37,7 @@ positional arguments:
|
|||
paths
|
||||
|
||||
options:
|
||||
-h, --help show help message and exit
|
||||
-h, --help show help and exit
|
||||
-e, --auto-exit skip user action prompts
|
||||
-o OUTPUT_DIR, --output-dir OUTPUT_DIR output extraction directory
|
||||
```
|
||||
|
@ -108,7 +108,7 @@ It also allows to use directly the four public methods which are inherited by ev
|
|||
Run utility after checking for supported format
|
||||
|
||||
``` python
|
||||
run_utility(padding: int = 0) -> int
|
||||
run_utility(padding: int = 0) -> bool
|
||||
```
|
||||
|
||||
#### check_format
|
||||
|
@ -124,7 +124,7 @@ check_format(input_object: str | bytes | bytearray) -> bool
|
|||
Process input object as a specific supported format
|
||||
|
||||
``` python
|
||||
parse_format(input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int | None
|
||||
parse_format(input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool
|
||||
```
|
||||
|
||||
#### show_version
|
||||
|
@ -148,7 +148,7 @@ python -m pip install --upgrade -r requirements.txt
|
|||
```
|
||||
|
||||
``` bash
|
||||
python -m pip install --upgrade pefile
|
||||
python -m pip install --upgrade pefile dissect.util
|
||||
```
|
||||
|
||||
### External Executables / Scripts
|
||||
|
@ -269,7 +269,7 @@ To run the utility, you do not need any prerequisites.
|
|||
|
||||
#### **Description**
|
||||
|
||||
Parses Apple EFI images and identifies them based on Intel's official $IBIOSI$ tag, which contains info such as Model, Version, Build, Date and Time. Optionally, the utility can rename the input Apple EFI image based on the retrieved $IBIOSI$ tag info, while also making sure to differentiate any EFI images with the same $IBIOSI$ tag (e.g. Production, Pre-Production) by appending a checksum of their data.
|
||||
Parses Apple EFI images and identifies them based on Intel's official "IBIOSI" tag, which contains info such as Model, Version, Build, Date and Time. Additionally, the utility can provide both "IBIOSI" and "Apple ROM Version" structure info, when available, as well as a suggested EFI image filename, while also making sure to differentiate any EFI images with the same "IBIOSI" tag (e.g. Production, Pre-Production) by appending a checksum of their data.
|
||||
|
||||
#### **Usage**
|
||||
|
||||
|
@ -290,7 +290,7 @@ To run the utility, you must have the following 3rd party tools at PATH or "exte
|
|||
|
||||
#### **Description**
|
||||
|
||||
Parses Apple EFI PKG firmware packages (i.e. FirmwareUpdate.pkg, BridgeOSUpdateCustomer.pkg), extracts their EFI images, splits those in IM4P format and identifies/renames the final Intel SPI/BIOS images accordingly. The output comprises only final firmware components which are directly usable by end users.
|
||||
Parses Apple EFI PKG firmware packages (e.g. FirmwareUpdate.pkg, BridgeOSUpdateCustomer.pkg, InstallAssistant.pkg, iMacEFIUpdate.pkg, iMacFirmwareUpdate.tar), extracts their EFI images, splits those in IM4P format and identifies/renames the final Intel SPI/BIOS images accordingly. The output comprises only final firmware components which are directly usable by end users.
|
||||
|
||||
#### **Usage**
|
||||
|
||||
|
|
|
@ -5,4 +5,4 @@
|
|||
Copyright (C) 2018-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
__version__ = '24.10.03'
|
||||
__version__ = '24.10.06'
|
||||
|
|
|
@ -12,7 +12,7 @@ import os
|
|||
import re
|
||||
import struct
|
||||
|
||||
from typing import Any, Type
|
||||
from typing import Any, Final, Type
|
||||
|
||||
from biosutilities.common.externals import big_script_tool
|
||||
from biosutilities.common.paths import extract_suffix, extract_folder, make_dirs, path_name, safe_name
|
||||
|
@ -71,6 +71,7 @@ class IntelBiosGuardHeader(ctypes.LittleEndianStructure):
|
|||
id_text: str = re.sub(r'[\n\t\r\x00 ]', '', id_byte.decode(encoding='utf-8', errors='ignore'))
|
||||
|
||||
id_hexs: str = f'{int.from_bytes(bytes=id_byte, byteorder="big"):0{0x10 * 2}X}'
|
||||
|
||||
id_guid: str = f'{{{id_hexs[:8]}-{id_hexs[8:12]}-{id_hexs[12:16]}-{id_hexs[16:20]}-{id_hexs[20:]}}}'
|
||||
|
||||
return f'{id_text} {id_guid}'
|
||||
|
@ -205,12 +206,12 @@ class AmiPfatExtract(BIOSUtility):
|
|||
|
||||
TITLE: str = 'AMI BIOS Guard Extractor'
|
||||
|
||||
PFAT_AMI_HDR_LEN: int = ctypes.sizeof(AmiBiosGuardHeader)
|
||||
PFAT_INT_HDR_LEN: int = ctypes.sizeof(IntelBiosGuardHeader)
|
||||
PFAT_INT_SIG_HDR_LEN: int = ctypes.sizeof(IntelBiosGuardSignatureHeader)
|
||||
PFAT_INT_SIG_R2K_LEN: int = ctypes.sizeof(IntelBiosGuardSignatureRsa2k)
|
||||
PFAT_INT_SIG_R3K_LEN: int = ctypes.sizeof(IntelBiosGuardSignatureRsa3k)
|
||||
PFAT_INT_SIG_MAX_LEN: int = PFAT_INT_SIG_HDR_LEN + PFAT_INT_SIG_R3K_LEN
|
||||
PFAT_AMI_HDR_LEN: Final[int] = ctypes.sizeof(AmiBiosGuardHeader)
|
||||
PFAT_INT_HDR_LEN: Final[int] = ctypes.sizeof(IntelBiosGuardHeader)
|
||||
PFAT_INT_SIG_HDR_LEN: Final[int] = ctypes.sizeof(IntelBiosGuardSignatureHeader)
|
||||
PFAT_INT_SIG_R2K_LEN: Final[int] = ctypes.sizeof(IntelBiosGuardSignatureRsa2k)
|
||||
PFAT_INT_SIG_R3K_LEN: Final[int] = ctypes.sizeof(IntelBiosGuardSignatureRsa3k)
|
||||
PFAT_INT_SIG_MAX_LEN: Final[int] = PFAT_INT_SIG_HDR_LEN + PFAT_INT_SIG_R3K_LEN
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
""" Check if input is AMI BIOS Guard """
|
||||
|
@ -219,7 +220,7 @@ class AmiPfatExtract(BIOSUtility):
|
|||
|
||||
return bool(self._get_ami_pfat(input_object=input_buffer))
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Process and store AMI BIOS Guard output file """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
|
@ -317,7 +318,7 @@ class AmiPfatExtract(BIOSUtility):
|
|||
with open(file=in_all_path, mode='wb') as out_all:
|
||||
out_all.write(in_all_data + pfat_oob_data)
|
||||
|
||||
return 0
|
||||
return True
|
||||
|
||||
def parse_bg_sign(self, input_data: bytes, sign_offset: int, sign_length: int = 0,
|
||||
print_info: bool = False, padding: int = 0) -> int:
|
||||
|
|
|
@ -13,12 +13,12 @@ import os
|
|||
import re
|
||||
import struct
|
||||
|
||||
from typing import Any
|
||||
from typing import Any, Final
|
||||
|
||||
from biosutilities.common.checksums import checksum_16
|
||||
from biosutilities.common.compression import efi_decompress, is_efi_compressed
|
||||
from biosutilities.common.paths import agnostic_path, extract_folder, make_dirs, safe_name, safe_path
|
||||
from biosutilities.common.patterns import PAT_AMI_UCP, PAT_INTEL_ENG
|
||||
from biosutilities.common.patterns import PAT_AMI_UCP, PAT_INTEL_ENGINE
|
||||
from biosutilities.common.structs import CHAR, ctypes_struct, UINT8, UINT16, UINT32
|
||||
from biosutilities.common.system import printer
|
||||
from biosutilities.common.templates import BIOSUtility
|
||||
|
@ -101,11 +101,12 @@ class UiiHeader(ctypes.LittleEndianStructure):
|
|||
# 0x10
|
||||
]
|
||||
|
||||
SBI: dict[int, str] = {1: 'ALL', 2: 'AMIBIOS8', 3: 'UEFI', 4: 'AMIBIOS8/UEFI'}
|
||||
SOS: dict[int, str] = {1: 'DOS', 2: 'EFI', 3: 'Windows', 4: 'Linux', 5: 'FreeBSD', 6: 'MacOS', 128: 'Multi-OS'}
|
||||
DBW: dict[int, str] = {1: '16b', 2: '16/32b', 3: '32b', 4: '64b'}
|
||||
PTP: dict[int, str] = {1: 'Executable', 2: 'Library', 3: 'Driver'}
|
||||
PMD: dict[int, str] = {1: 'API', 2: 'Console', 3: 'GUI', 4: 'Console/GUI'}
|
||||
SBI: Final[dict[int, str]] = {1: 'ALL', 2: 'AMIBIOS8', 3: 'UEFI', 4: 'AMIBIOS8/UEFI'}
|
||||
SOS: Final[dict[int, str]] = {1: 'DOS', 2: 'EFI', 3: 'Windows', 4: 'Linux', 5: 'FreeBSD',
|
||||
6: 'MacOS', 128: 'Multi-Platform'}
|
||||
DBW: Final[dict[int, str]] = {1: '16b', 2: '16/32b', 3: '32b', 4: '64b'}
|
||||
PTP: Final[dict[int, str]] = {1: 'Executable', 2: 'Library', 3: 'Driver'}
|
||||
PMD: Final[dict[int, str]] = {1: 'API', 2: 'Console', 3: 'GUI', 4: 'Console/GUI'}
|
||||
|
||||
def struct_print(self, description: str, padding: int = 0) -> None:
|
||||
""" Display structure information """
|
||||
|
@ -160,8 +161,8 @@ class DisModule(ctypes.LittleEndianStructure):
|
|||
# 0x122
|
||||
]
|
||||
|
||||
ENDIS: dict[int, str] = {0: 'Disabled', 1: 'Enabled'}
|
||||
SHOWN: dict[int, str] = {0: 'Hidden', 1: 'Shown', 2: 'Shown Only'}
|
||||
ENDIS: Final[dict[int, str]] = {0: 'Disabled', 1: 'Enabled'}
|
||||
SHOWN: Final[dict[int, str]] = {0: 'Hidden', 1: 'Shown', 2: 'Shown Only'}
|
||||
|
||||
def struct_print(self, padding: int = 0) -> None:
|
||||
""" Display structure information """
|
||||
|
@ -187,14 +188,14 @@ class AmiUcpExtract(BIOSUtility):
|
|||
]
|
||||
|
||||
# Get common ctypes Structure Sizes
|
||||
UAF_HDR_LEN: int = ctypes.sizeof(UafHeader)
|
||||
UAF_MOD_LEN: int = ctypes.sizeof(UafModule)
|
||||
DIS_HDR_LEN: int = ctypes.sizeof(DisHeader)
|
||||
DIS_MOD_LEN: int = ctypes.sizeof(DisModule)
|
||||
UII_HDR_LEN: int = ctypes.sizeof(UiiHeader)
|
||||
UAF_HDR_LEN: Final[int] = ctypes.sizeof(UafHeader)
|
||||
UAF_MOD_LEN: Final[int] = ctypes.sizeof(UafModule)
|
||||
DIS_HDR_LEN: Final[int] = ctypes.sizeof(DisHeader)
|
||||
DIS_MOD_LEN: Final[int] = ctypes.sizeof(DisModule)
|
||||
UII_HDR_LEN: Final[int] = ctypes.sizeof(UiiHeader)
|
||||
|
||||
# AMI UCP Tag Dictionary
|
||||
UAF_TAG_DICT: dict[str, list[str]] = {
|
||||
UAF_TAG_DICT: Final[dict[str, list[str]]] = {
|
||||
'@3FI': ['HpBiosUpdate32.efi', 'HpBiosUpdate32.efi', ''],
|
||||
'@3S2': ['HpBiosUpdate32.s12', 'HpBiosUpdate32.s12', ''],
|
||||
'@3S4': ['HpBiosUpdate32.s14', 'HpBiosUpdate32.s14', ''],
|
||||
|
@ -259,7 +260,7 @@ class AmiUcpExtract(BIOSUtility):
|
|||
|
||||
return bool(self._get_ami_ucp(input_object=buffer)[0])
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> None:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Parse & Extract AMI UCP structures """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
|
@ -301,6 +302,8 @@ class AmiUcpExtract(BIOSUtility):
|
|||
nal_dict = self._uaf_extract(buffer=ucp_buffer, extract_path=extract_path, mod_info=mod_info,
|
||||
nal_dict=nal_dict, padding=padding + 8)
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _chk16_validate(data: bytes | bytearray, tag: str, padding: int = 0) -> None:
|
||||
""" Validate UCP Module Checksum-16 """
|
||||
|
@ -482,7 +485,7 @@ class AmiUcpExtract(BIOSUtility):
|
|||
# Decompressed @UAF|@HPU Module file path
|
||||
dec_fname: str = uaf_fname.replace('.temp', uaf_fext)
|
||||
|
||||
if efi_decompress(in_path=uaf_fname, out_path=dec_fname, padding=padding + 4) == 0:
|
||||
if efi_decompress(in_path=uaf_fname, out_path=dec_fname, padding=padding + 4):
|
||||
with open(file=dec_fname, mode='rb') as dec:
|
||||
uaf_data_raw = dec.read() # Read back the @UAF|@HPU Module decompressed Raw data
|
||||
|
||||
|
@ -558,26 +561,30 @@ class AmiUcpExtract(BIOSUtility):
|
|||
# Assign a file path & name to each Tag
|
||||
nal_dict[info_tag] = (info_path, info_name)
|
||||
|
||||
insyde_ifd_extract: InsydeIfdExtract = InsydeIfdExtract()
|
||||
|
||||
# Parse Insyde BIOS @UAF|@HPU Module (@INS)
|
||||
if uaf_tag == '@INS' and InsydeIfdExtract().check_format(input_object=uaf_fname):
|
||||
if uaf_tag == '@INS' and insyde_ifd_extract.check_format(input_object=uaf_fname):
|
||||
# Generate extraction directory
|
||||
ins_dir: str = os.path.join(extract_path, safe_name(in_name=f'{uaf_tag}_nested-IFD'))
|
||||
|
||||
if InsydeIfdExtract().parse_format(input_object=uaf_fname, extract_path=extract_folder(ins_dir),
|
||||
padding=padding + 4) == 0:
|
||||
if insyde_ifd_extract.parse_format(input_object=uaf_fname, extract_path=extract_folder(ins_dir),
|
||||
padding=padding + 4):
|
||||
os.remove(path=uaf_fname) # Delete raw nested Insyde IFD image after successful extraction
|
||||
|
||||
ami_pfat_extract: AmiPfatExtract = AmiPfatExtract()
|
||||
|
||||
# Detect & Unpack AMI BIOS Guard (PFAT) BIOS image
|
||||
if AmiPfatExtract().check_format(input_object=uaf_data_raw):
|
||||
if ami_pfat_extract.check_format(input_object=uaf_data_raw):
|
||||
pfat_dir: str = os.path.join(extract_path, safe_name(in_name=uaf_name))
|
||||
|
||||
AmiPfatExtract().parse_format(input_object=uaf_data_raw, extract_path=extract_folder(pfat_dir),
|
||||
ami_pfat_extract.parse_format(input_object=uaf_data_raw, extract_path=extract_folder(pfat_dir),
|
||||
padding=padding + 4)
|
||||
|
||||
os.remove(path=uaf_fname) # Delete raw PFAT BIOS image after successful extraction
|
||||
|
||||
# Detect Intel Engine firmware image and show ME Analyzer advice
|
||||
if uaf_tag.startswith('@ME') and PAT_INTEL_ENG.search(string=uaf_data_raw):
|
||||
if uaf_tag.startswith('@ME') and PAT_INTEL_ENGINE.search(string=uaf_data_raw):
|
||||
printer(message='Intel Management Engine (ME) Firmware:\n', padding=padding + 4)
|
||||
printer(message='Use "ME Analyzer" from https://github.com/platomav/MEAnalyzer',
|
||||
padding=padding + 8, new_line=False)
|
||||
|
|
|
@ -14,12 +14,13 @@ import struct
|
|||
import subprocess
|
||||
import zlib
|
||||
|
||||
from collections import defaultdict
|
||||
from re import Match
|
||||
from typing import Any, Final
|
||||
|
||||
from biosutilities.common.externals import uefiextract_path, uefifind_path
|
||||
from biosutilities.common.paths import delete_dirs, delete_file, path_suffixes, runtime_root
|
||||
from biosutilities.common.patterns import PAT_APPLE_EFI
|
||||
from biosutilities.common.patterns import PAT_INTEL_IBIOSI, PAT_APPLE_ROM_VER
|
||||
from biosutilities.common.structs import CHAR, ctypes_struct, UINT8
|
||||
from biosutilities.common.system import printer
|
||||
from biosutilities.common.templates import BIOSUtility
|
||||
|
@ -58,8 +59,10 @@ class IntelBiosId(ctypes.LittleEndianStructure):
|
|||
def _decode(field: bytes) -> str:
|
||||
return struct.pack('B' * len(field), *field).decode(encoding='utf-16', errors='ignore').strip('\x00 ')
|
||||
|
||||
def get_bios_id(self) -> tuple:
|
||||
""" Create Apple EFI BIOS ID """
|
||||
def get_bios_id(self) -> dict[str, str]:
|
||||
""" Get Apple/Intel EFI BIOS ID """
|
||||
|
||||
intel_sig: str = self.Signature.decode(encoding='utf-8')
|
||||
|
||||
board_id: str = self._decode(field=self.BoardID)
|
||||
board_ext: str = self._decode(field=self.BoardExt)
|
||||
|
@ -72,26 +75,40 @@ class IntelBiosId(ctypes.LittleEndianStructure):
|
|||
build_hour: str = self._decode(field=self.Hour)
|
||||
build_minute: str = self._decode(field=self.Minute)
|
||||
|
||||
build_date: str = f'20{build_year}-{build_month}-{build_day}'
|
||||
build_time: str = f'{build_hour}-{build_minute}'
|
||||
efi_name_id: str = (f'{board_id}_{board_ext}_{version_major}_{build_type}{version_minor}'
|
||||
f'_20{build_year}-{build_month}-{build_day}_{build_hour}-{build_minute}')
|
||||
|
||||
return board_id, board_ext, version_major, build_type, version_minor, build_date, build_time
|
||||
return {
|
||||
'intel_sig': intel_sig,
|
||||
'board_id': board_id,
|
||||
'board_ext': board_ext,
|
||||
'version_major': version_major,
|
||||
'version_minor': version_minor,
|
||||
'build_type': build_type,
|
||||
'build_year': build_year,
|
||||
'build_month': build_month,
|
||||
'build_day': build_day,
|
||||
'build_hour': build_hour,
|
||||
'build_minute': build_minute,
|
||||
'efi_name_id': efi_name_id
|
||||
}
|
||||
|
||||
def struct_print(self, padding: int = 0) -> None:
|
||||
""" Display structure information """
|
||||
|
||||
board_id, board_ext, version_major, build_type, version_minor, build_date, build_time = self.get_bios_id()
|
||||
ibiosi: dict[str, str] = self.get_bios_id()
|
||||
|
||||
intel_id: str = self.Signature.decode(encoding='utf-8')
|
||||
ibiosi_date: str = f'20{ibiosi["build_year"]}-{ibiosi["build_month"]}-{ibiosi["build_day"]}'
|
||||
ibiosi_time: str = f'{ibiosi["build_hour"]}:{ibiosi["build_minute"]}'
|
||||
|
||||
printer(message=['Intel Signature:', intel_id], padding=padding, new_line=False)
|
||||
printer(message=['Board Identity: ', board_id], padding=padding, new_line=False)
|
||||
printer(message=['Apple Identity: ', board_ext], padding=padding, new_line=False)
|
||||
printer(message=['Major Version: ', version_major], padding=padding, new_line=False)
|
||||
printer(message=['Minor Version: ', version_minor], padding=padding, new_line=False)
|
||||
printer(message=['Build Type: ', build_type], padding=padding, new_line=False)
|
||||
printer(message=['Build Date: ', build_date], padding=padding, new_line=False)
|
||||
printer(message=['Build Time: ', build_time.replace('-', ':')], padding=padding, new_line=False)
|
||||
printer(message=['Intel Signature:', ibiosi['intel_sig']], padding=padding, new_line=False)
|
||||
printer(message=['Board Identity: ', ibiosi['board_id']], padding=padding, new_line=False)
|
||||
printer(message=['Apple Identity: ', ibiosi['board_ext']], padding=padding, new_line=False)
|
||||
printer(message=['Major Version: ', ibiosi['version_major']], padding=padding, new_line=False)
|
||||
printer(message=['Minor Version: ', ibiosi['version_minor']], padding=padding, new_line=False)
|
||||
printer(message=['Build Type: ', ibiosi['build_type']], padding=padding, new_line=False)
|
||||
printer(message=['Build Date: ', ibiosi_date], padding=padding, new_line=False)
|
||||
printer(message=['Build Time: ', ibiosi_time], padding=padding, new_line=False)
|
||||
|
||||
|
||||
class AppleEfiIdentify(BIOSUtility):
|
||||
|
@ -104,14 +121,16 @@ class AppleEfiIdentify(BIOSUtility):
|
|||
def __init__(self, arguments: list[str] | None = None) -> None:
|
||||
super().__init__(arguments=arguments)
|
||||
|
||||
self.efi_name_id: str = ''
|
||||
self.efi_file_name: str = ''
|
||||
self.intel_bios_info: dict[str, str] = {}
|
||||
self.apple_rom_version: defaultdict[str, set] = defaultdict(set)
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
""" Check if input is Apple EFI image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
|
||||
if PAT_APPLE_EFI.search(string=input_buffer):
|
||||
if PAT_INTEL_IBIOSI.search(string=input_buffer):
|
||||
return True
|
||||
|
||||
if isinstance(input_object, str) and os.path.isfile(path=input_object):
|
||||
|
@ -135,7 +154,7 @@ class AppleEfiIdentify(BIOSUtility):
|
|||
if input_path != input_object:
|
||||
delete_file(in_path=input_path)
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Parse & Identify (or Rename) Apple EFI image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
|
@ -148,7 +167,7 @@ class AppleEfiIdentify(BIOSUtility):
|
|||
with open(file=input_path, mode='wb') as parse_out:
|
||||
parse_out.write(input_buffer)
|
||||
|
||||
bios_id_match: Match[bytes] | None = PAT_APPLE_EFI.search(string=input_buffer)
|
||||
bios_id_match: Match[bytes] | None = PAT_INTEL_IBIOSI.search(string=input_buffer)
|
||||
|
||||
if bios_id_match:
|
||||
bios_id_res: str = f'0x{bios_id_match.start():X}'
|
||||
|
@ -171,7 +190,7 @@ class AppleEfiIdentify(BIOSUtility):
|
|||
body_buffer: bytes = raw_body.read()
|
||||
|
||||
# Detect decompressed $IBIOSI$ pattern
|
||||
bios_id_match = PAT_APPLE_EFI.search(string=body_buffer)
|
||||
bios_id_match = PAT_INTEL_IBIOSI.search(string=body_buffer)
|
||||
|
||||
if not bios_id_match:
|
||||
raise RuntimeError('Failed to detect decompressed $IBIOSI$ pattern!')
|
||||
|
@ -183,25 +202,56 @@ class AppleEfiIdentify(BIOSUtility):
|
|||
except Exception as error: # pylint: disable=broad-except
|
||||
printer(message=f'Error: Failed to parse compressed $IBIOSI$ pattern: {error}!', padding=padding)
|
||||
|
||||
return 1
|
||||
return False
|
||||
|
||||
printer(message=f'Detected $IBIOSI$ at {bios_id_res}\n', padding=padding)
|
||||
printer(message=f'Detected Intel BIOS Info at {bios_id_res}\n', padding=padding)
|
||||
|
||||
bios_id_hdr.struct_print(padding=padding + 4)
|
||||
|
||||
input_suffix: str = path_suffixes(input_path)[-1]
|
||||
self.intel_bios_info = bios_id_hdr.get_bios_id()
|
||||
|
||||
input_adler32: int = zlib.adler32(input_buffer)
|
||||
self.efi_file_name = (f'{self.intel_bios_info["efi_name_id"]}_{zlib.adler32(input_buffer):08X}'
|
||||
f'{path_suffixes(in_path=input_path)[-1]}')
|
||||
|
||||
fw_id, fw_ext, fw_major, fw_type, fw_minor, fw_date, fw_time = bios_id_hdr.get_bios_id()
|
||||
|
||||
self.efi_name_id = (f'{fw_id}_{fw_ext}_{fw_major}_{fw_type}{fw_minor}_{fw_date}_{fw_time}_'
|
||||
f'{input_adler32:08X}{input_suffix}')
|
||||
_ = self._apple_rom_version(input_buffer=input_buffer, padding=padding)
|
||||
|
||||
if input_path != input_object:
|
||||
delete_file(in_path=input_path)
|
||||
|
||||
return 0
|
||||
return True
|
||||
|
||||
def _apple_rom_version(self, input_buffer: bytes | bytearray, padding: int = 0) -> bool:
|
||||
rom_version_match: Match[bytes] | None = PAT_APPLE_ROM_VER.search(string=input_buffer)
|
||||
|
||||
if rom_version_match:
|
||||
rom_version_match_off: int = rom_version_match.start()
|
||||
|
||||
rom_version_header_len: int = input_buffer[rom_version_match_off:].find(b'\n')
|
||||
|
||||
if rom_version_header_len != -1:
|
||||
rom_version_data_bgn: int = rom_version_match_off + rom_version_header_len
|
||||
|
||||
rom_version_data_len: int = input_buffer[rom_version_data_bgn:].find(b'\x00')
|
||||
|
||||
if rom_version_data_len != -1:
|
||||
rom_version_data_end: int = rom_version_data_bgn + rom_version_data_len
|
||||
|
||||
rom_version_data: bytes = input_buffer[rom_version_data_bgn:rom_version_data_end]
|
||||
|
||||
rom_version_text: str = rom_version_data.decode('utf-8').strip('\n')
|
||||
|
||||
for rom_version_line in [line.strip() for line in rom_version_text.split('\n')]:
|
||||
rom_version_parts: list[str] = rom_version_line.split(sep=':', maxsplit=1)
|
||||
|
||||
self.apple_rom_version[rom_version_parts[0].strip()].add(rom_version_parts[1].strip())
|
||||
|
||||
printer(message=f'Detected Apple ROM Version at 0x{rom_version_match_off:X}', padding=padding)
|
||||
|
||||
printer(message=rom_version_text, strip=True, padding=padding + 4)
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -13,7 +13,7 @@ from re import Match
|
|||
from typing import Final
|
||||
|
||||
from biosutilities.common.paths import make_dirs, path_stem
|
||||
from biosutilities.common.patterns import PAT_APPLE_IM4P, PAT_INTEL_IFD
|
||||
from biosutilities.common.patterns import PAT_APPLE_IM4P, PAT_INTEL_FD
|
||||
from biosutilities.common.system import printer
|
||||
from biosutilities.common.templates import BIOSUtility
|
||||
from biosutilities.common.texts import file_to_bytes
|
||||
|
@ -32,15 +32,15 @@ class AppleEfiIm4pSplit(BIOSUtility):
|
|||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
|
||||
if PAT_APPLE_IM4P.search(string=input_buffer) and PAT_INTEL_IFD.search(string=input_buffer):
|
||||
if PAT_APPLE_IM4P.search(string=input_buffer) and PAT_INTEL_FD.search(string=input_buffer):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Parse & Split Apple EFI IM4P image """
|
||||
|
||||
exit_codes: list[int] = []
|
||||
parse_success: bool = True
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
|
||||
|
@ -50,7 +50,7 @@ class AppleEfiIm4pSplit(BIOSUtility):
|
|||
im4p_match: Match[bytes] | None = PAT_APPLE_IM4P.search(string=input_buffer)
|
||||
|
||||
if not im4p_match:
|
||||
return 1
|
||||
return False
|
||||
|
||||
# After IM4P mefi (0x15), multi EFI payloads have _MEFIBIN (0x100) but is difficult to RE w/o varying samples.
|
||||
# However, _MEFIBIN is not required for splitting SPI images due to Intel Flash Descriptor Components Density.
|
||||
|
@ -75,7 +75,7 @@ class AppleEfiIm4pSplit(BIOSUtility):
|
|||
input_buffer = input_buffer[efi_data_bgn:efi_data_bgn + efi_data_len]
|
||||
|
||||
# Parse Intel Flash Descriptor pattern matches
|
||||
for ifd in PAT_INTEL_IFD.finditer(string=input_buffer):
|
||||
for ifd in PAT_INTEL_FD.finditer(string=input_buffer):
|
||||
# Component Base Address from FD start (ICH8-ICH10 = 1, IBX = 2, CPT+ = 3)
|
||||
ifd_flmap0_fcba: int = input_buffer[ifd.start() + 0x4] * 0x10
|
||||
|
||||
|
@ -148,9 +148,9 @@ class AppleEfiIm4pSplit(BIOSUtility):
|
|||
printer(message=f'Error: Bad image size 0x{output_size:07X}, expected 0x{ifd_comp_all_size:07X}!',
|
||||
padding=padding + 4)
|
||||
|
||||
exit_codes.append(1)
|
||||
parse_success = False
|
||||
|
||||
return sum(exit_codes)
|
||||
return parse_success
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -56,9 +56,9 @@ class AppleEfiPbzxExtract(BIOSUtility):
|
|||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
|
||||
return bool(PAT_APPLE_PBZX.search(string=input_buffer[:0x4]))
|
||||
return bool(PAT_APPLE_PBZX.search(string=input_buffer, endpos=4))
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Parse & Extract Apple PBZX image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
|
@ -107,7 +107,7 @@ class AppleEfiPbzxExtract(BIOSUtility):
|
|||
if cpio_len != len(cpio_bin):
|
||||
printer(message='Error: Unexpected CPIO archive size!', padding=padding)
|
||||
|
||||
return 1
|
||||
return False
|
||||
|
||||
cpio_name: str = path_stem(in_path=input_object) if isinstance(input_object, str) else 'Payload'
|
||||
|
||||
|
@ -119,14 +119,14 @@ class AppleEfiPbzxExtract(BIOSUtility):
|
|||
# Decompress PBZX > CPIO archive with 7-Zip
|
||||
if is_szip_supported(in_path=cpio_path, padding=padding, args=['-tCPIO'], silent=False):
|
||||
if szip_decompress(in_path=cpio_path, out_path=extract_path, in_name='CPIO',
|
||||
padding=padding, args=['-tCPIO'], check=True) == 0:
|
||||
padding=padding, args=['-tCPIO']):
|
||||
os.remove(path=cpio_path) # Successful extraction, delete PBZX > CPIO archive
|
||||
else:
|
||||
return 3
|
||||
return False
|
||||
else:
|
||||
return 2
|
||||
return False
|
||||
|
||||
return 0
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -10,9 +10,9 @@ Copyright (C) 2019-2024 Plato Mavropoulos
|
|||
import os
|
||||
|
||||
from biosutilities.common.compression import is_szip_supported, szip_decompress
|
||||
from biosutilities.common.paths import (copy_file, delete_dirs, extract_folder, path_files,
|
||||
make_dirs, path_name, path_parent, runtime_root)
|
||||
from biosutilities.common.patterns import PAT_APPLE_PKG_TAR, PAT_APPLE_PKG_XAR
|
||||
from biosutilities.common.paths import (copy_file, delete_dirs, extract_folder, make_dirs, path_files,
|
||||
path_name, path_parent, path_suffixes, runtime_root)
|
||||
from biosutilities.common.patterns import PAT_APPLE_PKG_DMG, PAT_APPLE_PKG_TAR, PAT_APPLE_PKG_XAR
|
||||
from biosutilities.common.system import printer
|
||||
from biosutilities.common.templates import BIOSUtility
|
||||
from biosutilities.common.texts import file_to_bytes
|
||||
|
@ -42,19 +42,22 @@ class AppleEfiPkgExtract(BIOSUtility):
|
|||
with open(file=input_path, mode='wb') as input_path_object:
|
||||
input_path_object.write(input_buffer)
|
||||
|
||||
if is_szip_supported(in_path=input_path, args=['-tXAR']):
|
||||
if is_szip_supported(in_path=input_path, args=['-tXAR:s0']):
|
||||
if bool(PAT_APPLE_PKG_XAR.search(string=input_buffer, endpos=4)):
|
||||
is_apple_efi_pkg = True
|
||||
elif is_szip_supported(in_path=input_path, args=['-tTAR']):
|
||||
elif is_szip_supported(in_path=input_path, args=['-tTAR:s0']):
|
||||
if bool(PAT_APPLE_PKG_TAR.search(string=input_buffer)):
|
||||
is_apple_efi_pkg = True
|
||||
elif is_szip_supported(in_path=input_path, args=['-tDMG:s0']):
|
||||
if bool(PAT_APPLE_PKG_DMG.search(string=input_buffer, endpos=200)):
|
||||
is_apple_efi_pkg = True
|
||||
|
||||
if input_path != input_object:
|
||||
os.remove(path=input_path)
|
||||
|
||||
return is_apple_efi_pkg
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Parse & Extract Apple EFI PKG packages """
|
||||
|
||||
if isinstance(input_object, str) and os.path.isfile(path=input_object):
|
||||
|
@ -69,13 +72,15 @@ class AppleEfiPkgExtract(BIOSUtility):
|
|||
|
||||
working_dir: str = os.path.join(extract_path, 'temp')
|
||||
|
||||
for pkg_type in ('XAR', 'TAR'):
|
||||
make_dirs(in_path=working_dir, delete=True)
|
||||
|
||||
for pkg_type in ('XAR', 'TAR', 'DMG'):
|
||||
if is_szip_supported(in_path=input_path, padding=padding, args=[f'-t{pkg_type}']):
|
||||
if szip_decompress(in_path=input_path, out_path=working_dir, in_name=pkg_type, padding=padding,
|
||||
args=[f'-t{pkg_type}'], check=True) == 0:
|
||||
args=None if pkg_type == 'DMG' else [f'-t{pkg_type}']):
|
||||
break
|
||||
else:
|
||||
return 1
|
||||
return False
|
||||
|
||||
if input_path != input_object:
|
||||
os.remove(path=input_path)
|
||||
|
@ -83,22 +88,42 @@ class AppleEfiPkgExtract(BIOSUtility):
|
|||
for work_file in path_files(in_path=working_dir):
|
||||
self._pbzx_zip(input_path=work_file, extract_path=extract_path, padding=padding + 4)
|
||||
self._gzip_cpio(input_path=work_file, extract_path=extract_path, padding=padding + 4)
|
||||
self._tar_gzip(input_path=work_file, extract_path=extract_path, padding=padding + 4)
|
||||
self._dmg_zip(input_path=work_file, extract_path=extract_path, padding=padding + 4)
|
||||
self._xar_gzip(input_path=work_file, extract_path=extract_path, padding=padding + 4)
|
||||
|
||||
delete_dirs(in_path=working_dir)
|
||||
|
||||
return 0
|
||||
return True
|
||||
|
||||
def _tar_gzip(self, input_path: str, extract_path: str, padding: int = 0) -> None:
|
||||
""" TAR > GZIP """
|
||||
def _xar_gzip(self, input_path: str, extract_path: str, padding: int = 0) -> None:
|
||||
""" XAR/TAR > GZIP """
|
||||
|
||||
if is_szip_supported(in_path=input_path, padding=padding, args=['-tTAR']):
|
||||
tar_path: str = extract_folder(in_path=input_path)
|
||||
for pkg_type in ('XAR', 'TAR'):
|
||||
if is_szip_supported(in_path=input_path, padding=padding, args=[f'-t{pkg_type}']):
|
||||
pkg_path: str = extract_folder(in_path=input_path, suffix=f'_{pkg_type.lower()}_gzip')
|
||||
|
||||
if szip_decompress(in_path=input_path, out_path=tar_path, in_name='TAR', padding=padding,
|
||||
args=['-tTAR'], check=False) == 0:
|
||||
for tar_file in path_files(in_path=tar_path):
|
||||
self._gzip_cpio(input_path=tar_file, extract_path=extract_path, padding=padding + 4)
|
||||
if szip_decompress(in_path=input_path, out_path=pkg_path, in_name=pkg_type,
|
||||
padding=padding, args=[f'-t{pkg_type}']):
|
||||
for pkg_file in path_files(in_path=pkg_path):
|
||||
self._gzip_cpio(input_path=pkg_file, extract_path=extract_path, padding=padding + 4)
|
||||
|
||||
break
|
||||
|
||||
def _dmg_zip(self, input_path: str, extract_path: str, padding: int = 0) -> None:
|
||||
""" DMG > ZIP """
|
||||
|
||||
if is_szip_supported(in_path=input_path, padding=padding, args=['-tDMG']):
|
||||
dmg_path: str = extract_folder(in_path=input_path, suffix='_dmg_zip')
|
||||
|
||||
if szip_decompress(in_path=input_path, out_path=dmg_path, in_name='DMG', padding=padding, args=None):
|
||||
for dmg_file in path_files(in_path=dmg_path):
|
||||
if is_szip_supported(in_path=dmg_file, padding=padding + 4, args=['-tZIP']):
|
||||
zip_path: str = extract_folder(in_path=dmg_file)
|
||||
|
||||
if szip_decompress(in_path=dmg_file, out_path=zip_path, in_name='ZIP',
|
||||
padding=padding + 4, args=['-tZIP']):
|
||||
for zip_file in path_files(in_path=zip_path):
|
||||
self._im4p_id(input_path=zip_file, output_path=extract_path, padding=padding + 8)
|
||||
|
||||
def _pbzx_zip(self, input_path: str, extract_path: str, padding: int = 0) -> None:
|
||||
""" PBZX > ZIP """
|
||||
|
@ -108,9 +133,9 @@ class AppleEfiPkgExtract(BIOSUtility):
|
|||
if pbzx_module.check_format(input_object=input_path):
|
||||
printer(message=f'Extracting PBZX via {pbzx_module.title}', padding=padding)
|
||||
|
||||
pbzx_path: str = extract_folder(in_path=input_path)
|
||||
pbzx_path: str = extract_folder(in_path=input_path, suffix='_pbzx_zip')
|
||||
|
||||
if pbzx_module.parse_format(input_object=input_path, extract_path=pbzx_path, padding=padding + 4) == 0:
|
||||
if pbzx_module.parse_format(input_object=input_path, extract_path=pbzx_path, padding=padding + 4):
|
||||
printer(message=f'Successful PBZX extraction via {pbzx_module.title}!', padding=padding)
|
||||
|
||||
for pbzx_file in path_files(in_path=pbzx_path):
|
||||
|
@ -118,7 +143,7 @@ class AppleEfiPkgExtract(BIOSUtility):
|
|||
zip_path: str = extract_folder(in_path=pbzx_file)
|
||||
|
||||
if szip_decompress(in_path=pbzx_file, out_path=zip_path, in_name='ZIP',
|
||||
padding=padding + 4, args=['-tZIP'], check=False) == 0:
|
||||
padding=padding + 4, args=['-tZIP']):
|
||||
for zip_file in path_files(in_path=zip_path):
|
||||
self._im4p_id(input_path=zip_file, output_path=extract_path, padding=padding + 8)
|
||||
|
||||
|
@ -126,16 +151,16 @@ class AppleEfiPkgExtract(BIOSUtility):
|
|||
""" GZIP > CPIO """
|
||||
|
||||
if is_szip_supported(in_path=input_path, padding=padding, args=['-tGZIP']):
|
||||
gzip_path: str = extract_folder(in_path=input_path)
|
||||
gzip_path: str = extract_folder(in_path=input_path, suffix='_gzip_cpio')
|
||||
|
||||
if szip_decompress(in_path=input_path, out_path=gzip_path, in_name='GZIP', padding=padding,
|
||||
args=['-tGZIP'], check=True) == 0:
|
||||
if szip_decompress(in_path=input_path, out_path=gzip_path, in_name='GZIP',
|
||||
padding=padding, args=['-tGZIP']):
|
||||
for gzip_file in path_files(in_path=gzip_path):
|
||||
if is_szip_supported(in_path=gzip_file, padding=padding + 4, args=['-tCPIO']):
|
||||
cpio_path: str = extract_folder(in_path=gzip_file)
|
||||
|
||||
if szip_decompress(in_path=gzip_file, out_path=cpio_path, in_name='CPIO',
|
||||
padding=padding + 4, args=['-tCPIO'], check=False) == 0:
|
||||
padding=padding + 4, args=['-tCPIO']):
|
||||
for cpio_file in path_files(in_path=cpio_path):
|
||||
self._im4p_id(input_path=cpio_file, output_path=extract_path, padding=padding + 8)
|
||||
|
||||
|
@ -143,6 +168,9 @@ class AppleEfiPkgExtract(BIOSUtility):
|
|||
def _im4p_id(input_path: str, output_path: str, padding: int = 0) -> None:
|
||||
""" Split IM4P (if applicable), identify and rename EFI """
|
||||
|
||||
if path_suffixes(in_path=input_path)[-1].lower() not in ('.fd', '.scap', '.im4p'):
|
||||
return None
|
||||
|
||||
if not AppleEfiIdentify().check_format(input_object=input_path):
|
||||
return None
|
||||
|
||||
|
@ -169,11 +197,9 @@ class AppleEfiPkgExtract(BIOSUtility):
|
|||
if efi_id_module.check_format(input_object=efi_source):
|
||||
printer(message=f'Identifying EFI via {efi_id_module.title}', padding=padding + 4)
|
||||
|
||||
efi_id_exit: int = efi_id_module.parse_format(
|
||||
input_object=efi_source, extract_path=extract_folder(in_path=efi_source), padding=padding + 8)
|
||||
|
||||
if efi_id_exit == 0:
|
||||
efi_dest: str = os.path.join(path_parent(in_path=efi_source), efi_id_module.efi_name_id)
|
||||
if efi_id_module.parse_format(input_object=efi_source, extract_path=extract_folder(in_path=efi_source),
|
||||
padding=padding + 8):
|
||||
efi_dest: str = os.path.join(path_parent(in_path=efi_source), efi_id_module.efi_file_name)
|
||||
|
||||
os.rename(src=efi_source, dst=efi_dest)
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ class AwardBiosExtract(BIOSUtility):
|
|||
|
||||
return bool(PAT_AWARD_LZH.search(string=in_buffer))
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> None:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Parse & Extract Award BIOS image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
|
@ -74,7 +74,7 @@ class AwardBiosExtract(BIOSUtility):
|
|||
|
||||
# 7-Zip returns critical exit code (i.e. 2) if LZH CRC is wrong, do not check result
|
||||
szip_decompress(in_path=lzh_path, out_path=extract_path, in_name=lzh_text,
|
||||
padding=padding + 4, check=False)
|
||||
padding=padding + 4)
|
||||
|
||||
# Manually check if 7-Zip extracted LZH due to its CRC check issue
|
||||
if os.path.isfile(path=mod_path):
|
||||
|
@ -88,6 +88,8 @@ class AwardBiosExtract(BIOSUtility):
|
|||
self.parse_format(input_object=mod_path, extract_path=extract_folder(mod_path),
|
||||
padding=padding + 8)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
AwardBiosExtract().run_utility()
|
||||
|
|
|
@ -41,7 +41,7 @@ def is_szip_supported(in_path: str, padding: int = 0, args: list | None = None,
|
|||
|
||||
|
||||
def szip_decompress(in_path: str, out_path: str, in_name: str | None, padding: int = 0, args: list | None = None,
|
||||
check: bool = False, silent: bool = False) -> int:
|
||||
check: bool = False, silent: bool = False) -> bool:
|
||||
""" Archive decompression via 7-Zip """
|
||||
|
||||
if not in_name:
|
||||
|
@ -64,12 +64,12 @@ def szip_decompress(in_path: str, out_path: str, in_name: str | None, padding: i
|
|||
if not silent:
|
||||
printer(message=f'Error: 7-Zip could not extract {in_name} file {in_path}: {error}!', padding=padding)
|
||||
|
||||
return 1
|
||||
return False
|
||||
|
||||
if not silent:
|
||||
printer(message=f'Successful {in_name} decompression via 7-Zip!', padding=padding)
|
||||
|
||||
return 0
|
||||
return True
|
||||
|
||||
|
||||
def efi_compress_sizes(data: bytes | bytearray) -> tuple[int, int]:
|
||||
|
@ -98,7 +98,7 @@ def is_efi_compressed(data: bytes | bytearray, strict: bool = True) -> bool:
|
|||
|
||||
|
||||
def efi_decompress(in_path: str, out_path: str, padding: int = 0, silent: bool = False,
|
||||
comp_type: str = '--uefi') -> int:
|
||||
comp_type: str = '--uefi') -> bool:
|
||||
""" EFI/Tiano Decompression via TianoCompress """
|
||||
|
||||
try:
|
||||
|
@ -114,9 +114,9 @@ def efi_decompress(in_path: str, out_path: str, padding: int = 0, silent: bool =
|
|||
if not silent:
|
||||
printer(message=f'Error: TianoCompress could not extract file {in_path}: {error}!', padding=padding)
|
||||
|
||||
return 1
|
||||
return False
|
||||
|
||||
if not silent:
|
||||
printer(message='Successful EFI decompression via TianoCompress!', padding=padding)
|
||||
|
||||
return 0
|
||||
return True
|
||||
|
|
|
@ -20,7 +20,7 @@ from biosutilities.common.paths import project_root
|
|||
from biosutilities.common.texts import to_string
|
||||
|
||||
|
||||
def get_external_path(cmd: str | list | tuple, raise_on_error: bool = True) -> str | None:
|
||||
def get_external_path(cmd: str | list | tuple) -> str:
|
||||
""" Get external dependency path (PATH environment variable or "external" directory) """
|
||||
|
||||
external_root: str = os.path.join(project_root(), 'external')
|
||||
|
@ -33,18 +33,15 @@ def get_external_path(cmd: str | list | tuple, raise_on_error: bool = True) -> s
|
|||
if command_path and os.path.isfile(path=command_path):
|
||||
return command_path
|
||||
|
||||
if raise_on_error:
|
||||
raise OSError(f'{to_string(in_object=cmd, sep_char=", ")} could not be found!')
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def big_script_tool() -> Type | None:
|
||||
""" Get Intel BIOS Guard Script Tool class """
|
||||
|
||||
bgst: str | None = get_external_path(cmd='big_script_tool', raise_on_error=False)
|
||||
try:
|
||||
bgst: str = get_external_path(cmd='big_script_tool')
|
||||
|
||||
if bgst is not None:
|
||||
bgst_spec: ModuleSpec | None = spec_from_file_location(
|
||||
name='big_script_tool', location=re.sub(r'\.PY$', '.py', bgst))
|
||||
|
||||
|
@ -57,35 +54,37 @@ def big_script_tool() -> Type | None:
|
|||
bgst_spec.loader.exec_module(module=bgst_module)
|
||||
|
||||
return getattr(bgst_module, 'BigScript')
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def comextract_path() -> str | None:
|
||||
def comextract_path() -> str:
|
||||
""" Get ToshibaComExtractor path """
|
||||
|
||||
return get_external_path(cmd='comextract')
|
||||
|
||||
|
||||
def szip_path() -> str | None:
|
||||
def szip_path() -> str:
|
||||
""" Get 7-Zip path """
|
||||
|
||||
return get_external_path(cmd=['7zzs', '7zz', '7z'])
|
||||
|
||||
|
||||
def tiano_path() -> str | None:
|
||||
def tiano_path() -> str:
|
||||
""" Get TianoCompress path """
|
||||
|
||||
return get_external_path(cmd='TianoCompress')
|
||||
|
||||
|
||||
def uefifind_path() -> str | None:
|
||||
def uefifind_path() -> str:
|
||||
""" Get UEFIFind path """
|
||||
|
||||
return get_external_path(cmd='UEFIFind')
|
||||
|
||||
|
||||
def uefiextract_path() -> str | None:
|
||||
def uefiextract_path() -> str:
|
||||
""" Get UEFIExtract path """
|
||||
|
||||
return get_external_path(cmd='UEFIExtract')
|
||||
|
|
|
@ -132,7 +132,7 @@ def delete_dirs(in_path: str) -> None:
|
|||
""" Delete folder(s), if present """
|
||||
|
||||
if Path(in_path).is_dir():
|
||||
shutil.rmtree(path=in_path, onexc=clear_readonly_callback)
|
||||
shutil.rmtree(path=in_path, onerror=clear_readonly_callback) # pylint: disable=deprecated-argument
|
||||
|
||||
|
||||
def delete_file(in_path: str) -> None:
|
||||
|
@ -164,7 +164,7 @@ def clear_readonly_callback(in_func: Callable, in_path: str, _) -> None:
|
|||
|
||||
clear_readonly(in_path=in_path)
|
||||
|
||||
in_func(in_path=in_path)
|
||||
in_func(path=in_path)
|
||||
|
||||
|
||||
def path_files(in_path: str, follow_links: bool = False) -> list[str]:
|
||||
|
|
|
@ -19,9 +19,8 @@ PAT_AMI_UCP: Final[re.Pattern[bytes]] = re.compile(
|
|||
flags=re.DOTALL
|
||||
)
|
||||
|
||||
PAT_APPLE_EFI: Final[re.Pattern[bytes]] = re.compile(
|
||||
pattern=br'\$IBIOSI\$.{16}\x2E\x00.{6}\x2E\x00.{8}\x2E\x00.{6}\x2E\x00.{20}\x00{2}',
|
||||
flags=re.DOTALL
|
||||
PAT_APPLE_ROM_VER: Final[re.Pattern[bytes]] = re.compile(
|
||||
pattern=br'Apple ROM Version\x0A\x20{2}'
|
||||
)
|
||||
|
||||
PAT_APPLE_IM4P: Final[re.Pattern[bytes]] = re.compile(
|
||||
|
@ -32,14 +31,18 @@ PAT_APPLE_PBZX: Final[re.Pattern[bytes]] = re.compile(
|
|||
pattern=br'pbzx'
|
||||
)
|
||||
|
||||
PAT_APPLE_PKG_XAR: Final[re.Pattern[bytes]] = re.compile(
|
||||
pattern=br'xar!'
|
||||
PAT_APPLE_PKG_DMG: Final[re.Pattern[bytes]] = re.compile(
|
||||
pattern=br'EFI PART'
|
||||
)
|
||||
|
||||
PAT_APPLE_PKG_TAR: Final[re.Pattern[bytes]] = re.compile(
|
||||
pattern=br'<key>IFPkgDescriptionDescription</key>'
|
||||
)
|
||||
|
||||
PAT_APPLE_PKG_XAR: Final[re.Pattern[bytes]] = re.compile(
|
||||
pattern=br'xar!'
|
||||
)
|
||||
|
||||
PAT_AWARD_LZH: Final[re.Pattern[bytes]] = re.compile(
|
||||
pattern=br'-lh[04567]-'
|
||||
)
|
||||
|
@ -71,16 +74,21 @@ PAT_INSYDE_SFX: Final[re.Pattern[bytes]] = re.compile(
|
|||
pattern=br'\x0D\x0A;!@InstallEnd@!\x0D\x0A(7z\xBC\xAF\x27|\x6E\xF4\x79\x5F\x4E)'
|
||||
)
|
||||
|
||||
PAT_INTEL_ENG: Final[re.Pattern[bytes]] = re.compile(
|
||||
PAT_INTEL_ENGINE: Final[re.Pattern[bytes]] = re.compile(
|
||||
pattern=br'\x04\x00{3}[\xA1\xE1]\x00{3}.{8}\x86\x80.{9}\x00\$((MN2)|(MAN))',
|
||||
flags=re.DOTALL
|
||||
)
|
||||
|
||||
PAT_INTEL_IFD: Final[re.Pattern[bytes]] = re.compile(
|
||||
PAT_INTEL_FD: Final[re.Pattern[bytes]] = re.compile(
|
||||
pattern=br'\x5A\xA5\xF0\x0F.{172}\xFF{16}',
|
||||
flags=re.DOTALL
|
||||
)
|
||||
|
||||
PAT_INTEL_IBIOSI: Final[re.Pattern[bytes]] = re.compile(
|
||||
pattern=br'\$IBIOSI\$.{16}\x2E\x00.{6}\x2E\x00.{8}\x2E\x00.{6}\x2E\x00.{20}\x00{2}',
|
||||
flags=re.DOTALL
|
||||
)
|
||||
|
||||
PAT_MICROSOFT_CAB: Final[re.Pattern[bytes]] = re.compile(
|
||||
pattern=br'MSCF\x00{4}'
|
||||
)
|
||||
|
|
|
@ -30,17 +30,19 @@ def python_version() -> tuple:
|
|||
|
||||
|
||||
def printer(message: str | list | tuple | None = None, padding: int = 0, new_line: bool = True,
|
||||
pause: bool = False, sep_char: str = ' ') -> None:
|
||||
""" Show message(s), controlling padding, newline, pausing & separator """
|
||||
pause: bool = False, sep_char: str = ' ', strip: bool = False) -> None:
|
||||
""" Show message(s), controlling padding, newline, stripping, pausing & separating """
|
||||
|
||||
message_string: str = to_string(in_object='' if message is None else message, sep_char=sep_char)
|
||||
|
||||
message_output: str = '\n' if new_line else ''
|
||||
|
||||
for line_index, line_text in enumerate(iterable=message_string.split('\n')):
|
||||
line_newline: str = '' if line_index == 0 else '\n'
|
||||
for message_line_index, message_line_text in enumerate(iterable=message_string.split('\n')):
|
||||
line_new: str = '' if message_line_index == 0 else '\n'
|
||||
|
||||
message_output += f'{line_newline}{" " * padding}{line_text}'
|
||||
line_text: str = message_line_text.strip() if strip else message_line_text
|
||||
|
||||
message_output += f'{line_new}{" " * padding}{line_text}'
|
||||
|
||||
if pause:
|
||||
input(message_output)
|
||||
|
|
|
@ -30,10 +30,6 @@ class BIOSUtility:
|
|||
MIN_PYTHON_VER: Final[tuple[int, int]] = (3, 10)
|
||||
|
||||
def __init__(self, arguments: list[str] | None = None) -> None:
|
||||
self._check_sys_py()
|
||||
|
||||
self._check_sys_os()
|
||||
|
||||
self.title: str = f'{self.TITLE.strip()} v{__version__}'
|
||||
|
||||
argparser: ArgumentParser = ArgumentParser(allow_abbrev=False)
|
||||
|
@ -53,9 +49,13 @@ class BIOSUtility:
|
|||
|
||||
self._output_path: str = ''
|
||||
|
||||
def run_utility(self, padding: int = 0) -> int:
|
||||
def run_utility(self, padding: int = 0) -> bool:
|
||||
""" Run utility after checking for supported format """
|
||||
|
||||
self._check_sys_py()
|
||||
|
||||
self._check_sys_os()
|
||||
|
||||
self.show_version(padding=padding)
|
||||
|
||||
self._setup_input_files(padding=padding)
|
||||
|
@ -85,8 +85,7 @@ class BIOSUtility:
|
|||
|
||||
break
|
||||
|
||||
if self.parse_format(input_object=input_file, extract_path=extract_path,
|
||||
padding=padding + 8) in [0, None]:
|
||||
if self.parse_format(input_object=input_file, extract_path=extract_path, padding=padding + 8):
|
||||
exit_code -= 1
|
||||
|
||||
if is_empty_dir(in_path=extract_path):
|
||||
|
@ -94,14 +93,14 @@ class BIOSUtility:
|
|||
|
||||
printer(message='Done!\n' if not self.arguments.auto_exit else None, pause=not self.arguments.auto_exit)
|
||||
|
||||
return exit_code
|
||||
return exit_code == 0
|
||||
|
||||
def show_version(self, is_boxed: bool = True, padding: int = 0) -> None:
|
||||
""" Show title and version of utility """
|
||||
|
||||
printer(message=to_boxed(in_text=self.title) if is_boxed else self.title, new_line=False, padding=padding)
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int | None:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Process input object as a specific supported format """
|
||||
|
||||
raise NotImplementedError(f'Method "parse_format" not implemented at {__name__}')
|
||||
|
@ -112,6 +111,8 @@ class BIOSUtility:
|
|||
raise NotImplementedError(f'Method "check_format" not implemented at {__name__}')
|
||||
|
||||
def _setup_input_files(self, padding: int = 0) -> None:
|
||||
self._input_files = []
|
||||
|
||||
input_paths: list[str] = self.arguments.paths
|
||||
|
||||
if not input_paths:
|
||||
|
@ -126,6 +127,8 @@ class BIOSUtility:
|
|||
self._input_files.append(input_path_real)
|
||||
|
||||
def _setup_output_dir(self, padding: int = 0) -> None:
|
||||
self._output_path = ''
|
||||
|
||||
output_path: str = self.arguments.output_dir
|
||||
|
||||
if not output_path:
|
||||
|
|
|
@ -15,7 +15,7 @@ import os
|
|||
import zlib
|
||||
|
||||
from re import Match
|
||||
from typing import Any
|
||||
from typing import Any, Final
|
||||
|
||||
from biosutilities.common.checksums import checksum_8_xor
|
||||
from biosutilities.common.compression import is_szip_supported, szip_decompress
|
||||
|
@ -231,13 +231,13 @@ class DellPfsExtract(BIOSUtility):
|
|||
(['-s', '--structure'], {'help': 'show PFS structure information', 'action': 'store_true'})
|
||||
]
|
||||
|
||||
PFS_HEAD_LEN: int = ctypes.sizeof(DellPfsHeader)
|
||||
PFS_FOOT_LEN: int = ctypes.sizeof(DellPfsFooter)
|
||||
PFS_INFO_LEN: int = ctypes.sizeof(DellPfsInfo)
|
||||
PFS_NAME_LEN: int = ctypes.sizeof(DellPfsName)
|
||||
PFS_META_LEN: int = ctypes.sizeof(DellPfsMetadata)
|
||||
PFS_PFAT_LEN: int = ctypes.sizeof(DellPfsPfatMetadata)
|
||||
PFAT_HDR_LEN: int = ctypes.sizeof(IntelBiosGuardHeader)
|
||||
PFS_HEAD_LEN: Final[int] = ctypes.sizeof(DellPfsHeader)
|
||||
PFS_FOOT_LEN: Final[int] = ctypes.sizeof(DellPfsFooter)
|
||||
PFS_INFO_LEN: Final[int] = ctypes.sizeof(DellPfsInfo)
|
||||
PFS_NAME_LEN: Final[int] = ctypes.sizeof(DellPfsName)
|
||||
PFS_META_LEN: Final[int] = ctypes.sizeof(DellPfsMetadata)
|
||||
PFS_PFAT_LEN: Final[int] = ctypes.sizeof(DellPfsPfatMetadata)
|
||||
PFAT_HDR_LEN: Final[int] = ctypes.sizeof(IntelBiosGuardHeader)
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
""" Check if input is Dell PFS/PKG image """
|
||||
|
@ -252,7 +252,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
|
||||
return False
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> None:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Parse & Extract Dell PFS Update image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
|
@ -280,6 +280,8 @@ class DellPfsExtract(BIOSUtility):
|
|||
pfs_name=pfs_name, pfs_index=pfs_index, pfs_count=1, is_rec=False,
|
||||
padding=padding)
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _is_pfs_pkg(input_object: str | bytes | bytearray) -> bool:
|
||||
"""
|
||||
|
@ -353,7 +355,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
|
||||
if is_szip_supported(in_path=pkg_tar_path, padding=0, args=['-tTAR']):
|
||||
if szip_decompress(in_path=pkg_tar_path, out_path=working_path, in_name='TAR', padding=0,
|
||||
args=['-tTAR'], check=True, silent=True) == 0:
|
||||
args=['-tTAR'], check=True, silent=True):
|
||||
os.remove(path=pkg_tar_path)
|
||||
else:
|
||||
return pfs_results
|
||||
|
@ -1005,6 +1007,8 @@ class DellPfsExtract(BIOSUtility):
|
|||
# Get PFAT Header Flags (SFAM, ProtectEC, GFXMitDis, FTU, Reserved)
|
||||
is_sfam, _, _, _, _ = pfat_hdr.get_flags()
|
||||
|
||||
ami_pfat_extract: AmiPfatExtract = AmiPfatExtract()
|
||||
|
||||
# Parse sub-PFS PFAT Signature, if applicable (only when PFAT Header > SFAM flag is set)
|
||||
if is_sfam:
|
||||
if self.arguments.structure:
|
||||
|
@ -1014,13 +1018,13 @@ class DellPfsExtract(BIOSUtility):
|
|||
if _pfat_sign_len == 0:
|
||||
_pfat_sign_sig: bytes = pfat_hdr.get_hdr_marker()
|
||||
_pfat_sign_pfs: int = pfs_entry_size + self.PFS_PFAT_LEN
|
||||
_pfat_sign_max: int = _pfat_sign_pfs + AmiPfatExtract.PFAT_INT_SIG_MAX_LEN + len(_pfat_sign_sig)
|
||||
_pfat_sign_max: int = _pfat_sign_pfs + ami_pfat_extract.PFAT_INT_SIG_MAX_LEN + len(_pfat_sign_sig)
|
||||
_pfat_sign_lim: int = pfat_payload_end + _pfat_sign_max
|
||||
_pfat_sign_off: int = pfat_payload.find(_pfat_sign_sig, pfat_payload_end, _pfat_sign_lim)
|
||||
_pfat_sign_len = _pfat_sign_off - pfat_payload_end - _pfat_sign_pfs
|
||||
|
||||
# Get sub-PFS PFAT Signature Structure values
|
||||
pfat_sign_len: int = AmiPfatExtract().parse_bg_sign(
|
||||
pfat_sign_len: int = ami_pfat_extract.parse_bg_sign(
|
||||
input_data=pfat_payload, sign_offset=pfat_payload_end, sign_length=_pfat_sign_len,
|
||||
print_info=self.arguments.structure, padding=padding + 16)
|
||||
|
||||
|
@ -1033,7 +1037,7 @@ class DellPfsExtract(BIOSUtility):
|
|||
if self.arguments.structure:
|
||||
printer(message=f'PFAT Block {pfat_entry_idx_ord} - Script:\n', padding=padding + 12)
|
||||
|
||||
_ = AmiPfatExtract().parse_bg_script(script_data=pfat_script_data, padding=padding + 16)
|
||||
_ = ami_pfat_extract.parse_bg_script(script_data=pfat_script_data, padding=padding + 16)
|
||||
|
||||
# The payload of sub-PFS PFAT Entries is not in proper order by default
|
||||
# We can get each payload's order from PFAT Script > OpCode #2 (set I0 imm)
|
||||
|
|
|
@ -30,7 +30,7 @@ class FujitsuSfxExtract(BIOSUtility):
|
|||
|
||||
return bool(PAT_FUJITSU_SFX.search(string=input_buffer))
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Parse & Extract Fujitsu SFX image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
|
@ -39,7 +39,7 @@ class FujitsuSfxExtract(BIOSUtility):
|
|||
match_cab: re.Match[bytes] | None = PAT_FUJITSU_SFX.search(string=input_buffer)
|
||||
|
||||
if not match_cab:
|
||||
return 1
|
||||
return False
|
||||
|
||||
printer(message='Detected obfuscated CAB archive!', padding=padding)
|
||||
|
||||
|
@ -78,14 +78,14 @@ class FujitsuSfxExtract(BIOSUtility):
|
|||
|
||||
if is_szip_supported(in_path=cab_path, padding=padding + 8, silent=False):
|
||||
if szip_decompress(in_path=cab_path, out_path=extract_path, in_name='FjSfxBinay CAB',
|
||||
padding=padding + 8, check=True) == 0:
|
||||
padding=padding + 8, check=True):
|
||||
os.remove(path=cab_path)
|
||||
else:
|
||||
return 3
|
||||
return False
|
||||
else:
|
||||
return 2
|
||||
return False
|
||||
|
||||
return 0
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -34,7 +34,7 @@ class FujitsuUpcExtract(BIOSUtility):
|
|||
|
||||
return is_upc
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Parse & Extract Fujitsu UPC image """
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
|
@ -56,12 +56,12 @@ class FujitsuUpcExtract(BIOSUtility):
|
|||
|
||||
output_path: str = os.path.join(extract_path, f'{input_name}.bin')
|
||||
|
||||
efi_code: int = efi_decompress(in_path=input_path, out_path=output_path, padding=padding)
|
||||
efi_status: bool = efi_decompress(in_path=input_path, out_path=output_path, padding=padding)
|
||||
|
||||
if input_path != input_object:
|
||||
os.remove(path=input_path)
|
||||
|
||||
return efi_code
|
||||
return efi_status
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -11,7 +11,7 @@ import ctypes
|
|||
import os
|
||||
import re
|
||||
|
||||
from typing import Any
|
||||
from typing import Any, Final
|
||||
|
||||
from biosutilities.common.compression import is_szip_supported, szip_decompress
|
||||
from biosutilities.common.paths import extract_folder, path_files, make_dirs, path_name, safe_name
|
||||
|
@ -58,10 +58,10 @@ class InsydeIfdExtract(BIOSUtility):
|
|||
TITLE: str = 'Insyde iFlash/iFdPacker Extractor'
|
||||
|
||||
# Insyde iFdPacker known 7-Zip SFX Password
|
||||
INS_SFX_PWD: str = 'Y`t~i!L@i#t$U%h^s7A*l(f)E-d=y+S_n?i'
|
||||
INS_SFX_PWD: Final[str] = 'Y`t~i!L@i#t$U%h^s7A*l(f)E-d=y+S_n?i'
|
||||
|
||||
# Insyde iFlash known Image Names
|
||||
INS_IFL_IMG: dict = {
|
||||
INS_IFL_IMG: Final[dict[str, list[str]]] = {
|
||||
'BIOSCER': ['Certificate', 'bin'],
|
||||
'BIOSCR2': ['Certificate 2nd', 'bin'],
|
||||
'BIOSIMG': ['BIOS-UEFI', 'bin'],
|
||||
|
@ -77,7 +77,7 @@ class InsydeIfdExtract(BIOSUtility):
|
|||
}
|
||||
|
||||
# Get common ctypes Structure Sizes
|
||||
INS_IFL_LEN: int = ctypes.sizeof(IflashHeader)
|
||||
INS_IFL_LEN: Final[int] = ctypes.sizeof(IflashHeader)
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
""" Check if input is Insyde iFlash/iFdPacker Update image """
|
||||
|
@ -92,7 +92,7 @@ class InsydeIfdExtract(BIOSUtility):
|
|||
|
||||
return False
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Parse & Extract Insyde iFlash/iFdPacker Update images """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(in_object=input_object)
|
||||
|
@ -105,7 +105,7 @@ class InsydeIfdExtract(BIOSUtility):
|
|||
ifdpack_code: int = self._insyde_packer_extract(input_buffer=input_buffer, extract_path=ifdpack_path,
|
||||
padding=padding)
|
||||
|
||||
return iflash_code and ifdpack_code
|
||||
return (iflash_code and ifdpack_code) == 0
|
||||
|
||||
def _insyde_iflash_detect(self, input_buffer: bytes) -> list:
|
||||
""" Detect Insyde iFlash Update image """
|
||||
|
@ -219,7 +219,7 @@ class InsydeIfdExtract(BIOSUtility):
|
|||
|
||||
if is_szip_supported(in_path=sfx_path, padding=padding + 8, args=[f'-p{self.INS_SFX_PWD}'], silent=False):
|
||||
if szip_decompress(in_path=sfx_path, out_path=extract_path, in_name='Insyde iFdPacker > 7-Zip SFX',
|
||||
padding=padding + 8, args=[f'-p{self.INS_SFX_PWD}'], check=True) == 0:
|
||||
padding=padding + 8, args=[f'-p{self.INS_SFX_PWD}'], check=True):
|
||||
os.remove(path=sfx_path)
|
||||
else:
|
||||
return 125
|
||||
|
@ -232,10 +232,10 @@ class InsydeIfdExtract(BIOSUtility):
|
|||
if self.check_format(input_object=sfx_file):
|
||||
printer(message=path_name(in_path=sfx_file), padding=padding + 12)
|
||||
|
||||
ifd_code: int = self.parse_format(input_object=sfx_file, extract_path=extract_folder(sfx_file),
|
||||
ifd_status: int = self.parse_format(input_object=sfx_file, extract_path=extract_folder(sfx_file),
|
||||
padding=padding + 16)
|
||||
|
||||
exit_codes.append(ifd_code)
|
||||
exit_codes.append(0 if ifd_status else 1)
|
||||
|
||||
return sum(exit_codes)
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ import logging
|
|||
import os
|
||||
import re
|
||||
|
||||
from typing import Final
|
||||
|
||||
import pefile
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
|
@ -33,9 +35,9 @@ class PanasonicBiosExtract(BIOSUtility):
|
|||
|
||||
TITLE: str = 'Panasonic BIOS Package Extractor'
|
||||
|
||||
PAN_PE_DESC_UNP: str = 'UNPACK UTILITY'
|
||||
PAN_PE_DESC_UNP: Final[str] = 'UNPACK UTILITY'
|
||||
|
||||
PAN_PE_DESC_UPD: str = 'BIOS UPDATE'
|
||||
PAN_PE_DESC_UPD: Final[str] = 'BIOS UPDATE'
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
""" Check if input is Panasonic BIOS Package PE """
|
||||
|
@ -51,7 +53,7 @@ class PanasonicBiosExtract(BIOSUtility):
|
|||
|
||||
return True
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Parse & Extract Panasonic BIOS Package PE """
|
||||
|
||||
upd_pe_file: pefile.PE = ms_pe(in_file=input_object, padding=padding) # type: ignore
|
||||
|
@ -89,7 +91,7 @@ class PanasonicBiosExtract(BIOSUtility):
|
|||
is_upd_extracted = self._panasonic_img_extract(pe_file=upd_pe_file, extract_path=extract_path,
|
||||
pe_name=upd_pe_name, padding=upd_padding + 8)
|
||||
|
||||
return 0 if is_upd_extracted else 1
|
||||
return is_upd_extracted
|
||||
|
||||
@staticmethod
|
||||
def _panasonic_pkg_name(input_object: str | bytes | bytearray) -> str:
|
||||
|
@ -123,7 +125,7 @@ class PanasonicBiosExtract(BIOSUtility):
|
|||
printer(message=f'Panasonic BIOS Package > PE > CAB {cab_tag}', padding=padding)
|
||||
|
||||
if szip_decompress(in_path=cab_path, out_path=extract_path, in_name='CAB',
|
||||
padding=padding + 4, check=True) == 0:
|
||||
padding=padding + 4, check=True):
|
||||
os.remove(path=cab_path) # Successful extraction, delete CAB archive
|
||||
|
||||
for extracted_file_path in path_files(in_path=extract_path):
|
||||
|
@ -181,11 +183,13 @@ class PanasonicBiosExtract(BIOSUtility):
|
|||
|
||||
printer(message='Successful PE Resource extraction!', padding=padding + 4)
|
||||
|
||||
ami_pfat_extract: AmiPfatExtract = AmiPfatExtract()
|
||||
|
||||
# Detect & Unpack AMI BIOS Guard (PFAT) BIOS image
|
||||
if AmiPfatExtract().check_format(input_object=res_raw):
|
||||
if ami_pfat_extract.check_format(input_object=res_raw):
|
||||
pfat_dir: str = os.path.join(extract_path, res_tag)
|
||||
|
||||
AmiPfatExtract().parse_format(input_object=res_raw, extract_path=pfat_dir,
|
||||
ami_pfat_extract.parse_format(input_object=res_raw, extract_path=pfat_dir,
|
||||
padding=padding + 8)
|
||||
else:
|
||||
if is_ms_pe(in_file=res_raw):
|
||||
|
|
|
@ -110,7 +110,7 @@ class PhoenixTdkExtract(BIOSUtility):
|
|||
|
||||
return bool(self._get_phoenix_tdk(in_buffer=input_buffer)[1] is not None)
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Parse & Extract Phoenix Tools Development Kit (TDK) Packer """
|
||||
|
||||
exit_code: int = 0
|
||||
|
@ -199,7 +199,7 @@ class PhoenixTdkExtract(BIOSUtility):
|
|||
with open(file=mod_file, mode='wb') as out_file:
|
||||
out_file.write(mod_data)
|
||||
|
||||
return exit_code
|
||||
return exit_code == 0
|
||||
|
||||
@staticmethod
|
||||
def _get_tdk_base(in_buffer: bytes | bytearray, pack_off: int) -> int | None:
|
||||
|
|
|
@ -11,6 +11,7 @@ import logging
|
|||
import os
|
||||
|
||||
from re import Match
|
||||
from typing import Final
|
||||
|
||||
from pefile import PE
|
||||
|
||||
|
@ -28,7 +29,7 @@ class PortwellEfiExtract(BIOSUtility):
|
|||
|
||||
TITLE: str = 'Portwell EFI Update Extractor'
|
||||
|
||||
FILE_NAMES: dict[int, str] = {
|
||||
FILE_NAMES: Final[dict[int, str]] = {
|
||||
0: 'Flash.efi',
|
||||
1: 'Fparts.txt',
|
||||
2: 'Update.nsh',
|
||||
|
@ -56,7 +57,7 @@ class PortwellEfiExtract(BIOSUtility):
|
|||
|
||||
return False
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> None:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Parse & Extract Portwell UEFI Unpacker """
|
||||
|
||||
# Initialize EFI Payload file chunks
|
||||
|
@ -83,6 +84,8 @@ class PortwellEfiExtract(BIOSUtility):
|
|||
|
||||
self._parse_efi_files(extract_path=extract_path, efi_files=efi_files, padding=padding)
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _get_portwell_pe(in_buffer: bytes) -> tuple:
|
||||
""" Get PE of Portwell EFI executable """
|
||||
|
@ -161,7 +164,7 @@ class PortwellEfiExtract(BIOSUtility):
|
|||
os.replace(src=file_path, dst=comp_fname)
|
||||
|
||||
# Successful decompression, delete compressed file
|
||||
if efi_decompress(in_path=comp_fname, out_path=file_path, padding=padding + 8) == 0:
|
||||
if efi_decompress(in_path=comp_fname, out_path=file_path, padding=padding + 8):
|
||||
os.remove(path=comp_fname)
|
||||
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ class ToshibaComExtract(BIOSUtility):
|
|||
|
||||
return bool(PAT_TOSHIBA_COM.search(string=input_buffer, endpos=0x100))
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Parse & Extract Toshiba BIOS COM image """
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
|
@ -55,14 +55,14 @@ class ToshibaComExtract(BIOSUtility):
|
|||
except Exception as error: # pylint: disable=broad-except
|
||||
printer(message=f'Error: ToshibaComExtractor could not extract {input_path}: {error}!', padding=padding)
|
||||
|
||||
return 1
|
||||
return False
|
||||
|
||||
if input_path != input_object:
|
||||
os.remove(path=input_path)
|
||||
|
||||
printer(message='Successful extraction via ToshibaComExtractor!', padding=padding)
|
||||
|
||||
return 0
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -31,7 +31,7 @@ class VaioPackageExtract(BIOSUtility):
|
|||
|
||||
return bool(PAT_VAIO_CFG.search(string=input_buffer))
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> bool:
|
||||
""" Parse & Extract or Unlock VAIO Packaging Manager """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(input_object)
|
||||
|
@ -47,9 +47,9 @@ class VaioPackageExtract(BIOSUtility):
|
|||
else:
|
||||
printer(message='Error: Failed to Extract or Unlock executable!', padding=padding)
|
||||
|
||||
return 1
|
||||
return False
|
||||
|
||||
return 0
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _vaio_cabinet(name: str, buffer: bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
|
@ -94,7 +94,7 @@ class VaioPackageExtract(BIOSUtility):
|
|||
|
||||
if is_szip_supported(in_path=cab_path, padding=padding + 8, silent=False):
|
||||
if szip_decompress(in_path=cab_path, out_path=extract_path, in_name='VAIO CAB',
|
||||
padding=padding + 8, check=True) == 0:
|
||||
padding=padding + 8, check=True):
|
||||
os.remove(path=cab_path)
|
||||
else:
|
||||
return 3
|
||||
|
|
16
main.py
16
main.py
|
@ -29,18 +29,18 @@ from biosutilities.vaio_package_extract import VaioPackageExtract
|
|||
if __name__ == '__main__':
|
||||
main_argparser: ArgumentParser = ArgumentParser(allow_abbrev=False)
|
||||
|
||||
main_argparser.add_argument('input_files', nargs='+')
|
||||
main_argparser.add_argument('--output-folder', help='extraction folder')
|
||||
main_argparser.add_argument('--pause-exit', help='pause on exit', action='store_false')
|
||||
main_argparser.add_argument('paths', nargs='+')
|
||||
main_argparser.add_argument('-e', '--auto-exit', help='do not pause on exit', action='store_true')
|
||||
main_argparser.add_argument('-o', '--output-dir', help='extraction directory')
|
||||
|
||||
main_arguments: Namespace = main_argparser.parse_args()
|
||||
|
||||
if main_arguments.output_folder:
|
||||
output_folder: Path = Path(main_arguments.output_folder)
|
||||
if main_arguments.output_dir:
|
||||
output_folder: Path = Path(main_arguments.output_dir)
|
||||
else:
|
||||
output_folder = Path(main_arguments.input_files[0]).parent
|
||||
output_folder = Path(main_arguments.paths[0]).parent
|
||||
|
||||
util_arguments: list[str] = [*main_arguments.input_files, '-e', '-o', str(output_folder.absolute())]
|
||||
util_arguments: list[str] = [*main_arguments.paths, '-e', '-o', str(output_folder.absolute())]
|
||||
|
||||
AmiUcpExtract(arguments=util_arguments).run_utility()
|
||||
AmiPfatExtract(arguments=util_arguments).run_utility()
|
||||
|
@ -59,5 +59,5 @@ if __name__ == '__main__':
|
|||
AppleEfiIm4pSplit(arguments=util_arguments).run_utility()
|
||||
AppleEfiIdentify(arguments=util_arguments).run_utility()
|
||||
|
||||
if main_arguments.pause_exit:
|
||||
if not main_arguments.auto_exit:
|
||||
input('Press any key to exit...')
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue