mirror of
https://github.com/platomav/BIOSUtilities.git
synced 2025-05-21 18:55:22 -04:00
Merge pull request #38 from platomav/improvements_2022.4-2024.1
Additions, Improvements and Fixes from Q4 2022 to Q1 2024
This commit is contained in:
commit
6ae1587ec9
37 changed files with 2897 additions and 2174 deletions
.gitignore.mypy.ini.pylintrcAMI_PFAT_Extract.pyAMI_UCP_Extract.pyApple_EFI_ID.pyApple_EFI_IM4P.pyApple_EFI_PBZX.pyApple_EFI_PKG.pyAward_BIOS_Extract.pyDell_PFS_Extract.pyFujitsu_SFX_Extract.pyFujitsu_UPC_Extract.pyInsyde_IFD_Extract.pyLICENSEPanasonic_BIOS_Extract.pyPhoenix_TDK_Extract.pyPortwell_EFI_Extract.pyREADME.mdToshiba_COM_Extract.pyVAIO_Package_Extract.py__init__.py
common
__init__.pychecksums.pycomp_efi.pycomp_szip.pyexternals.pynum_ops.pypath_ops.pypatterns.pype_ops.pystruct_ops.pysystem.pytemplates.pytext_ops.py
external
requirements.txt
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -1,5 +1,4 @@
|
|||
# Skip all external files
|
||||
external/*
|
||||
|
||||
# Keep external > requirements file
|
||||
!external/requirements.txt
|
||||
/.idea/
|
||||
/.mypy_cache/
|
||||
/external/
|
||||
/venv/
|
||||
|
|
4
.mypy.ini
Normal file
4
.mypy.ini
Normal file
|
@ -0,0 +1,4 @@
|
|||
[mypy]
|
||||
|
||||
explicit_package_bases = True
|
||||
mypy_path = $MYPY_CONFIG_FILE_DIR/
|
19
.pylintrc
Normal file
19
.pylintrc
Normal file
|
@ -0,0 +1,19 @@
|
|||
[MAIN]
|
||||
|
||||
init-hook="import sys; sys.path.append('./')"
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
disable=
|
||||
duplicate-code,
|
||||
invalid-name,
|
||||
line-too-long,
|
||||
too-few-public-methods,
|
||||
too-many-arguments,
|
||||
too-many-branches,
|
||||
too-many-instance-attributes,
|
||||
too-many-lines,
|
||||
too-many-locals,
|
||||
too-many-nested-blocks,
|
||||
too-many-return-statements,
|
||||
too-many-statements
|
|
@ -1,191 +1,301 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
AMI PFAT Extract
|
||||
AMI BIOS Guard Extractor
|
||||
Copyright (C) 2018-2022 Plato Mavropoulos
|
||||
Copyright (C) 2018-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'AMI BIOS Guard Extractor v4.0_a12'
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import ctypes
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
from common.externals import get_bgs_tool
|
||||
from common.num_ops import get_ordinal
|
||||
from common.path_ops import make_dirs, safe_name, get_extract_path, extract_suffix
|
||||
from common.path_ops import extract_suffix, get_extract_path, make_dirs, path_name, safe_name
|
||||
from common.patterns import PAT_AMI_PFAT
|
||||
from common.struct_ops import char, get_struct, uint8_t, uint16_t, uint32_t
|
||||
from common.struct_ops import Char, get_struct, UInt8, UInt16, UInt32
|
||||
from common.system import printer
|
||||
from common.templates import BIOSUtility
|
||||
from common.text_ops import file_to_bytes
|
||||
from common.text_ops import bytes_to_hex, file_to_bytes
|
||||
|
||||
TITLE = 'AMI BIOS Guard Extractor v5.0'
|
||||
|
||||
|
||||
class AmiBiosGuardHeader(ctypes.LittleEndianStructure):
|
||||
""" AMI BIOS Guard Header """
|
||||
|
||||
_pack_ = 1
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
_fields_ = [
|
||||
('Size', uint32_t), # 0x00 Header + Entries
|
||||
('Checksum', uint32_t), # 0x04 ?
|
||||
('Tag', char*8), # 0x04 _AMIPFAT
|
||||
('Flags', uint8_t), # 0x10 ?
|
||||
('Size', UInt32), # 0x00 Header + Entries
|
||||
('Checksum', UInt32), # 0x04 ?
|
||||
('Tag', Char * 8), # 0x04 _AMIPFAT
|
||||
('Flags', UInt8), # 0x10 ?
|
||||
# 0x11
|
||||
]
|
||||
|
||||
def struct_print(self, p):
|
||||
printer(['Size :', f'0x{self.Size:X}'], p, False)
|
||||
printer(['Checksum:', f'0x{self.Checksum:04X}'], p, False)
|
||||
printer(['Tag :', self.Tag.decode('utf-8')], p, False)
|
||||
printer(['Flags :', f'0x{self.Flags:02X}'], p, False)
|
||||
def struct_print(self, padd: int) -> None:
|
||||
""" Display structure information """
|
||||
|
||||
printer(['Size :', f'0x{self.Size:X}'], padd, False)
|
||||
printer(['Checksum:', f'0x{self.Checksum:04X}'], padd, False)
|
||||
printer(['Tag :', self.Tag.decode('utf-8')], padd, False)
|
||||
printer(['Flags :', f'0x{self.Flags:02X}'], padd, False)
|
||||
|
||||
|
||||
class IntelBiosGuardHeader(ctypes.LittleEndianStructure):
|
||||
""" Intel BIOS Guard Header """
|
||||
|
||||
_pack_ = 1
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
_fields_ = [
|
||||
('BGVerMajor', uint16_t), # 0x00
|
||||
('BGVerMinor', uint16_t), # 0x02
|
||||
('PlatformID', uint8_t*16), # 0x04
|
||||
('Attributes', uint32_t), # 0x14
|
||||
('ScriptVerMajor', uint16_t), # 0x16
|
||||
('ScriptVerMinor', uint16_t), # 0x18
|
||||
('ScriptSize', uint32_t), # 0x1C
|
||||
('DataSize', uint32_t), # 0x20
|
||||
('BIOSSVN', uint32_t), # 0x24
|
||||
('ECSVN', uint32_t), # 0x28
|
||||
('VendorInfo', uint32_t), # 0x2C
|
||||
('BGVerMajor', UInt16), # 0x00
|
||||
('BGVerMinor', UInt16), # 0x02
|
||||
('PlatformID', UInt8 * 16), # 0x04
|
||||
('Attributes', UInt32), # 0x14
|
||||
('ScriptVerMajor', UInt16), # 0x16
|
||||
('ScriptVerMinor', UInt16), # 0x18
|
||||
('ScriptSize', UInt32), # 0x1C
|
||||
('DataSize', UInt32), # 0x20
|
||||
('BIOSSVN', UInt32), # 0x24
|
||||
('ECSVN', UInt32), # 0x28
|
||||
('VendorInfo', UInt32), # 0x2C
|
||||
# 0x30
|
||||
]
|
||||
|
||||
def get_platform_id(self):
|
||||
id_byte = bytes(self.PlatformID)
|
||||
def get_platform_id(self) -> str:
|
||||
""" Get Intel BIOS Guard Platform ID """
|
||||
|
||||
id_text = re.sub(r'[\n\t\r\x00 ]', '', id_byte.decode('utf-8','ignore'))
|
||||
id_byte: bytes = bytes(self.PlatformID)
|
||||
|
||||
id_hexs = f'{int.from_bytes(id_byte, "big"):0{0x10 * 2}X}'
|
||||
id_guid = f'{{{id_hexs[:8]}-{id_hexs[8:12]}-{id_hexs[12:16]}-{id_hexs[16:20]}-{id_hexs[20:]}}}'
|
||||
id_text: str = re.sub(r'[\n\t\r\x00 ]', '', id_byte.decode('utf-8', 'ignore'))
|
||||
|
||||
id_hexs: str = f'{int.from_bytes(id_byte, "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}'
|
||||
|
||||
def get_flags(self):
|
||||
def get_flags(self) -> tuple:
|
||||
""" Get Intel BIOS Guard Header Attributes """
|
||||
|
||||
attr = IntelBiosGuardHeaderGetAttributes()
|
||||
attr.asbytes = self.Attributes
|
||||
|
||||
attr.asbytes = self.Attributes # pylint: disable=W0201
|
||||
|
||||
return attr.b.SFAM, attr.b.ProtectEC, attr.b.GFXMitDis, attr.b.FTU, attr.b.Reserved
|
||||
|
||||
def struct_print(self, p):
|
||||
no_yes = ['No','Yes']
|
||||
f1,f2,f3,f4,f5 = self.get_flags()
|
||||
def struct_print(self, padd: int) -> None:
|
||||
""" Display structure information """
|
||||
|
||||
no_yes: dict[int, str] = {0: 'No', 1: 'Yes'}
|
||||
|
||||
sfam, ec_opc, gfx_dis, ft_upd, attr_res = self.get_flags()
|
||||
|
||||
printer(['BIOS Guard Version :', f'{self.BGVerMajor}.{self.BGVerMinor}'], padd, False)
|
||||
printer(['Platform Identity :', self.get_platform_id()], padd, False)
|
||||
printer(['Signed Flash Address Map :', no_yes[sfam]], padd, False)
|
||||
printer(['Protected EC OpCodes :', no_yes[ec_opc]], padd, False)
|
||||
printer(['Graphics Security Disable :', no_yes[gfx_dis]], padd, False)
|
||||
printer(['Fault Tolerant Update :', no_yes[ft_upd]], padd, False)
|
||||
printer(['Attributes Reserved :', f'0x{attr_res:X}'], padd, False)
|
||||
printer(['Script Version :', f'{self.ScriptVerMajor}.{self.ScriptVerMinor}'], padd, False)
|
||||
printer(['Script Size :', f'0x{self.ScriptSize:X}'], padd, False)
|
||||
printer(['Data Size :', f'0x{self.DataSize:X}'], padd, False)
|
||||
printer(['BIOS Security Version Number:', f'0x{self.BIOSSVN:X}'], padd, False)
|
||||
printer(['EC Security Version Number :', f'0x{self.ECSVN:X}'], padd, False)
|
||||
printer(['Vendor Information :', f'0x{self.VendorInfo:X}'], padd, False)
|
||||
|
||||
printer(['BIOS Guard Version :', f'{self.BGVerMajor}.{self.BGVerMinor}'], p, False)
|
||||
printer(['Platform Identity :', self.get_platform_id()], p, False)
|
||||
printer(['Signed Flash Address Map :', no_yes[f1]], p, False)
|
||||
printer(['Protected EC OpCodes :', no_yes[f2]], p, False)
|
||||
printer(['Graphics Security Disable :', no_yes[f3]], p, False)
|
||||
printer(['Fault Tolerant Update :', no_yes[f4]], p, False)
|
||||
printer(['Attributes Reserved :', f'0x{f5:X}'], p, False)
|
||||
printer(['Script Version :', f'{self.ScriptVerMajor}.{self.ScriptVerMinor}'], p, False)
|
||||
printer(['Script Size :', f'0x{self.ScriptSize:X}'], p, False)
|
||||
printer(['Data Size :', f'0x{self.DataSize:X}'], p, False)
|
||||
printer(['BIOS Security Version Number:', f'0x{self.BIOSSVN:X}'], p, False)
|
||||
printer(['EC Security Version Number :', f'0x{self.ECSVN:X}'], p, False)
|
||||
printer(['Vendor Information :', f'0x{self.VendorInfo:X}'], p, False)
|
||||
|
||||
class IntelBiosGuardHeaderAttributes(ctypes.LittleEndianStructure):
|
||||
""" Intel BIOS Guard Header Attributes """
|
||||
|
||||
_pack_ = 1
|
||||
|
||||
_fields_ = [
|
||||
('SFAM', uint32_t, 1), # Signed Flash Address Map
|
||||
('ProtectEC', uint32_t, 1), # Protected EC OpCodes
|
||||
('GFXMitDis', uint32_t, 1), # GFX Security Disable
|
||||
('FTU', uint32_t, 1), # Fault Tolerant Update
|
||||
('Reserved', uint32_t, 28) # Reserved/Unknown
|
||||
('SFAM', UInt32, 1), # Signed Flash Address Map
|
||||
('ProtectEC', UInt32, 1), # Protected EC OpCodes
|
||||
('GFXMitDis', UInt32, 1), # GFX Security Disable
|
||||
('FTU', UInt32, 1), # Fault Tolerant Update
|
||||
('Reserved', UInt32, 28) # Reserved/Unknown
|
||||
]
|
||||
|
||||
|
||||
class IntelBiosGuardHeaderGetAttributes(ctypes.Union):
|
||||
""" Intel BIOS Guard Header Attributes Getter """
|
||||
|
||||
_pack_ = 1
|
||||
|
||||
_fields_ = [
|
||||
('b', IntelBiosGuardHeaderAttributes),
|
||||
('asbytes', uint32_t)
|
||||
('asbytes', UInt32)
|
||||
]
|
||||
|
||||
class IntelBiosGuardSignature2k(ctypes.LittleEndianStructure):
|
||||
|
||||
class IntelBiosGuardSignatureHeader(ctypes.LittleEndianStructure):
|
||||
""" Intel BIOS Guard Signature Header """
|
||||
|
||||
_pack_ = 1
|
||||
|
||||
_fields_ = [
|
||||
('Unknown0', uint32_t), # 0x000
|
||||
('Unknown1', uint32_t), # 0x004
|
||||
('Modulus', uint32_t*64), # 0x008
|
||||
('Exponent', uint32_t), # 0x108
|
||||
('Signature', uint32_t*64), # 0x10C
|
||||
# 0x20C
|
||||
('Unknown0', UInt32), # 0x000
|
||||
('Unknown1', UInt32), # 0x004
|
||||
# 0x8
|
||||
]
|
||||
|
||||
def struct_print(self, p):
|
||||
Modulus = f'{int.from_bytes(self.Modulus, "little"):0{0x100 * 2}X}'
|
||||
Signature = f'{int.from_bytes(self.Signature, "little"):0{0x100 * 2}X}'
|
||||
def struct_print(self, padd: int) -> None:
|
||||
""" Display structure information """
|
||||
|
||||
printer(['Unknown 0:', f'0x{self.Unknown0:X}'], p, False)
|
||||
printer(['Unknown 1:', f'0x{self.Unknown1:X}'], p, False)
|
||||
printer(['Modulus :', f'{Modulus[:32]} [...]'], p, False)
|
||||
printer(['Exponent :', f'0x{self.Exponent:X}'], p, False)
|
||||
printer(['Signature:', f'{Signature[:32]} [...]'], p, False)
|
||||
printer(['Unknown 0:', f'0x{self.Unknown0:X}'], padd, False)
|
||||
printer(['Unknown 1:', f'0x{self.Unknown1:X}'], padd, False)
|
||||
|
||||
def is_ami_pfat(input_file):
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
||||
class IntelBiosGuardSignatureRsa2k(ctypes.LittleEndianStructure):
|
||||
""" Intel BIOS Guard Signature Block 2048-bit """
|
||||
|
||||
_pack_ = 1
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
_fields_ = [
|
||||
('Modulus', UInt8 * 256), # 0x000
|
||||
('Exponent', UInt32), # 0x100
|
||||
('Signature', UInt8 * 256), # 0x104
|
||||
# 0x204
|
||||
]
|
||||
|
||||
def struct_print(self, padd: int) -> None:
|
||||
""" Display structure information """
|
||||
|
||||
printer(['Modulus :', f'{bytes_to_hex(self.Modulus, "little", 0x100, 32)} [...]'], padd, False)
|
||||
printer(['Exponent :', f'0x{self.Exponent:X}'], padd, False)
|
||||
printer(['Signature:', f'{bytes_to_hex(self.Signature, "little", 0x100, 32)} [...]'], padd, False)
|
||||
|
||||
|
||||
class IntelBiosGuardSignatureRsa3k(ctypes.LittleEndianStructure):
|
||||
""" Intel BIOS Guard Signature Block 3072-bit """
|
||||
|
||||
_pack_ = 1
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
_fields_ = [
|
||||
('Modulus', UInt8 * 384), # 0x000
|
||||
('Exponent', UInt32), # 0x180
|
||||
('Signature', UInt8 * 384), # 0x184
|
||||
# 0x304
|
||||
]
|
||||
|
||||
def struct_print(self, padd: int) -> None:
|
||||
""" Display structure information """
|
||||
|
||||
printer(['Modulus :', f'{int.from_bytes(self.Modulus, "little"):0{0x180 * 2}X}'[:64]], padd, False)
|
||||
printer(['Exponent :', f'0x{self.Exponent:X}'], padd, False)
|
||||
printer(['Signature:', f'{int.from_bytes(self.Signature, "little"):0{0x180 * 2}X}'[:64]], padd, False)
|
||||
|
||||
|
||||
def is_ami_pfat(input_object: str | bytes | bytearray) -> bool:
|
||||
""" Check if input is AMI BIOS Guard """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(input_object)
|
||||
|
||||
return bool(get_ami_pfat(input_buffer))
|
||||
|
||||
def get_ami_pfat(input_file):
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
||||
def get_ami_pfat(input_object: str | bytes | bytearray) -> bytes:
|
||||
""" Get actual AMI BIOS Guard buffer """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(input_object)
|
||||
|
||||
match = PAT_AMI_PFAT.search(input_buffer)
|
||||
|
||||
return input_buffer[match.start() - 0x8:] if match else b''
|
||||
|
||||
def get_file_name(index, name):
|
||||
|
||||
def get_file_name(index: int, name: str) -> str:
|
||||
""" Create AMI BIOS Guard output filename """
|
||||
|
||||
return safe_name(f'{index:02d} -- {name}')
|
||||
|
||||
def parse_bg_script(script_data, padding=0):
|
||||
is_opcode_div = len(script_data) % 8 == 0
|
||||
|
||||
def parse_bg_script(script_data: bytes, padding: int = 0) -> int:
|
||||
""" Process Intel BIOS Guard Script """
|
||||
|
||||
is_opcode_div: bool = len(script_data) % 8 == 0
|
||||
|
||||
if not is_opcode_div:
|
||||
printer('Error: Script is not divisible by OpCode length!', padding, False)
|
||||
printer('Error: BIOS Guard script is not divisible by OpCode length!', padding, False)
|
||||
|
||||
return 1
|
||||
|
||||
is_begin_end = script_data[:8] + script_data[-8:] == b'\x01' + b'\x00' * 7 + b'\xFF' + b'\x00' * 7
|
||||
is_begin_end: bool = script_data[:8] + script_data[-8:] == b'\x01' + b'\x00' * 7 + b'\xFF' + b'\x00' * 7
|
||||
|
||||
if not is_begin_end:
|
||||
printer('Error: Script lacks Begin and/or End OpCodes!', padding, False)
|
||||
printer('Error: BIOS Guard script lacks Begin and/or End OpCodes!', padding, False)
|
||||
|
||||
return 2
|
||||
|
||||
BigScript = get_bgs_tool()
|
||||
big_script = get_bgs_tool()
|
||||
|
||||
if not BigScript:
|
||||
if not big_script:
|
||||
printer('Note: BIOS Guard Script Tool optional dependency is missing!', padding, False)
|
||||
|
||||
return 3
|
||||
|
||||
script = BigScript(code_bytes=script_data).to_string().replace('\t',' ').split('\n')
|
||||
script = big_script(code_bytes=script_data).to_string().replace('\t', ' ').split('\n')
|
||||
|
||||
for opcode in script:
|
||||
if opcode.endswith(('begin','end')): spacing = padding
|
||||
elif opcode.endswith(':'): spacing = padding + 4
|
||||
else: spacing = padding + 12
|
||||
if opcode.endswith(('begin', 'end')):
|
||||
spacing: int = padding
|
||||
elif opcode.endswith(':'):
|
||||
spacing = padding + 4
|
||||
else:
|
||||
spacing = padding + 12
|
||||
|
||||
operands = [operand for operand in opcode.split(' ') if operand]
|
||||
printer(('{:<12s}' + '{:<11s}' * (len(operands) - 1)).format(*operands), spacing, False)
|
||||
|
||||
# Largest opcode length is 11 (erase64kblk) and largest operand length is 10 (0xAABBCCDD).
|
||||
printer(f'{operands[0]:11s}{"".join((f" {o:10s}" for o in operands[1:]))}', spacing, False)
|
||||
|
||||
return 0
|
||||
|
||||
def parse_pfat_hdr(buffer, padding=0):
|
||||
block_all = []
|
||||
|
||||
def parse_bg_sign(input_data: bytes, sign_offset: int, print_info: bool = False, padding: int = 0) -> int:
|
||||
""" Process Intel BIOS Guard Signature """
|
||||
|
||||
bg_sig_hdr = get_struct(input_data, sign_offset, IntelBiosGuardSignatureHeader)
|
||||
|
||||
if bg_sig_hdr.Unknown0 == 1:
|
||||
# Unknown0 = 1, Unknown1 = 1
|
||||
bg_sig_rsa_struct = IntelBiosGuardSignatureRsa2k
|
||||
else:
|
||||
# Unknown0 = 2, Unknown1 = 3
|
||||
bg_sig_rsa_struct = IntelBiosGuardSignatureRsa3k
|
||||
|
||||
bg_sig_rsa = get_struct(input_data, sign_offset + PFAT_BLK_SIG_LEN, bg_sig_rsa_struct)
|
||||
|
||||
if print_info:
|
||||
bg_sig_hdr.struct_print(padding)
|
||||
|
||||
bg_sig_rsa.struct_print(padding)
|
||||
|
||||
# Total size of Signature Header and RSA Structure
|
||||
return PFAT_BLK_SIG_LEN + ctypes.sizeof(bg_sig_rsa_struct)
|
||||
|
||||
|
||||
def parse_pfat_hdr(buffer: bytes | bytearray, padding: int = 0) -> tuple:
|
||||
""" Parse AMI BIOS Guard Header """
|
||||
|
||||
block_all: list = []
|
||||
|
||||
pfat_hdr = get_struct(buffer, 0x0, AmiBiosGuardHeader)
|
||||
|
||||
hdr_size = pfat_hdr.Size
|
||||
hdr_data = buffer[PFAT_AMI_HDR_LEN:hdr_size]
|
||||
hdr_text = hdr_data.decode('utf-8').splitlines()
|
||||
hdr_size: int = pfat_hdr.Size
|
||||
|
||||
hdr_data: bytes = buffer[PFAT_AMI_HDR_LEN:hdr_size]
|
||||
|
||||
hdr_text: list[str] = hdr_data.decode('utf-8').splitlines()
|
||||
|
||||
printer('AMI BIOS Guard Header:\n', padding)
|
||||
|
||||
|
@ -193,27 +303,31 @@ def parse_pfat_hdr(buffer, padding=0):
|
|||
|
||||
hdr_title, *hdr_files = hdr_text
|
||||
|
||||
files_count = len(hdr_files)
|
||||
files_count: int = len(hdr_files)
|
||||
|
||||
hdr_tag, *hdr_indexes = hdr_title.split('II')
|
||||
|
||||
printer(hdr_tag + '\n', padding + 4)
|
||||
|
||||
bgt_indexes = [int(h, 16) for h in re.findall(r'.{1,4}', hdr_indexes[0])] if hdr_indexes else []
|
||||
bgt_indexes: list = [int(h, 16) for h in re.findall(r'.{1,4}', hdr_indexes[0])] if hdr_indexes else []
|
||||
|
||||
for index, entry in enumerate(hdr_files):
|
||||
entry_parts = entry.split(';')
|
||||
entry_parts: list = entry.split(';')
|
||||
|
||||
info = entry_parts[0].split()
|
||||
name = entry_parts[1]
|
||||
info: list = entry_parts[0].split()
|
||||
|
||||
flags = int(info[0])
|
||||
param = info[1]
|
||||
count = int(info[2])
|
||||
name: str = entry_parts[1]
|
||||
|
||||
order = get_ordinal((bgt_indexes[index] if bgt_indexes else index) + 1)
|
||||
flags: int = int(info[0])
|
||||
|
||||
desc = f'{name} (Index: {index + 1:02d}, Flash: {order}, Parameter: {param}, Flags: 0x{flags:X}, Blocks: {count})'
|
||||
param: str = info[1]
|
||||
|
||||
count: int = int(info[2])
|
||||
|
||||
order: str = get_ordinal((bgt_indexes[index] if bgt_indexes else index) + 1)
|
||||
|
||||
desc = f'{name} (Index: {index + 1:02d}, Flash: {order}, ' \
|
||||
f'Parameter: {param}, Flags: 0x{flags:X}, Blocks: {count})'
|
||||
|
||||
block_all += [(desc, name, order, param, flags, index, i, count) for i in range(count)]
|
||||
|
||||
|
@ -221,15 +335,19 @@ def parse_pfat_hdr(buffer, padding=0):
|
|||
|
||||
return block_all, hdr_size, files_count
|
||||
|
||||
def parse_pfat_file(input_file, extract_path, padding=0):
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
||||
pfat_buffer = get_ami_pfat(input_buffer)
|
||||
def parse_pfat_file(input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
""" Process and store AMI BIOS Guard output file """
|
||||
|
||||
file_path = ''
|
||||
all_blocks_dict = {}
|
||||
input_buffer: bytes = file_to_bytes(input_object)
|
||||
|
||||
extract_name = os.path.basename(extract_path).rstrip(extract_suffix())
|
||||
pfat_buffer: bytes = get_ami_pfat(input_buffer)
|
||||
|
||||
file_path: str = ''
|
||||
|
||||
all_blocks_dict: dict = {}
|
||||
|
||||
extract_name: str = path_name(extract_path).removesuffix(extract_suffix())
|
||||
|
||||
make_dirs(extract_path, delete=True)
|
||||
|
||||
|
@ -245,7 +363,7 @@ def parse_pfat_file(input_file, extract_path, padding=0):
|
|||
|
||||
all_blocks_dict[file_index] = b''
|
||||
|
||||
block_status = f'{block_index + 1}/{block_count}'
|
||||
block_status: str = f'{block_index + 1}/{block_count}'
|
||||
|
||||
bg_hdr = get_struct(pfat_buffer, block_off, IntelBiosGuardHeader)
|
||||
|
||||
|
@ -253,46 +371,42 @@ def parse_pfat_file(input_file, extract_path, padding=0):
|
|||
|
||||
bg_hdr.struct_print(padding + 12)
|
||||
|
||||
bg_script_bgn = block_off + PFAT_BLK_HDR_LEN
|
||||
bg_script_end = bg_script_bgn + bg_hdr.ScriptSize
|
||||
bg_script_bin = pfat_buffer[bg_script_bgn:bg_script_end]
|
||||
bg_script_bgn: int = block_off + PFAT_BLK_HDR_LEN
|
||||
bg_script_end: int = bg_script_bgn + bg_hdr.ScriptSize
|
||||
|
||||
bg_data_bgn = bg_script_end
|
||||
bg_data_end = bg_data_bgn + bg_hdr.DataSize
|
||||
bg_data_bin = pfat_buffer[bg_data_bgn:bg_data_end]
|
||||
bg_data_bgn: int = bg_script_end
|
||||
bg_data_end: int = bg_data_bgn + bg_hdr.DataSize
|
||||
|
||||
block_off = bg_data_end # Assume next block starts at data end
|
||||
bg_data_bin: bytes = pfat_buffer[bg_data_bgn:bg_data_end]
|
||||
|
||||
block_off: int = bg_data_end # Assume next block starts at data end
|
||||
|
||||
is_sfam, _, _, _, _ = bg_hdr.get_flags() # SFAM, ProtectEC, GFXMitDis, FTU, Reserved
|
||||
|
||||
if is_sfam:
|
||||
bg_sig_bgn = bg_data_end
|
||||
bg_sig_end = bg_sig_bgn + PFAT_BLK_S2K_LEN
|
||||
bg_sig_bin = pfat_buffer[bg_sig_bgn:bg_sig_end]
|
||||
|
||||
if len(bg_sig_bin) == PFAT_BLK_S2K_LEN:
|
||||
bg_sig = get_struct(bg_sig_bin, 0x0, IntelBiosGuardSignature2k)
|
||||
|
||||
printer(f'Intel BIOS Guard {block_status} Signature:\n', padding + 8)
|
||||
|
||||
bg_sig.struct_print(padding + 12)
|
||||
|
||||
block_off = bg_sig_end # Adjust next block to start at data + signature end
|
||||
# Adjust next block to start after current block Data + Signature
|
||||
block_off += parse_bg_sign(pfat_buffer, bg_data_end, True, padding + 12)
|
||||
|
||||
printer(f'Intel BIOS Guard {block_status} Script:\n', padding + 8)
|
||||
|
||||
_ = parse_bg_script(bg_script_bin, padding + 12)
|
||||
_ = parse_bg_script(pfat_buffer[bg_script_bgn:bg_script_end], padding + 12)
|
||||
|
||||
with open(file_path, 'ab') as out_dat:
|
||||
out_dat.write(bg_data_bin)
|
||||
|
||||
all_blocks_dict[file_index] += bg_data_bin
|
||||
|
||||
pfat_oob_data = pfat_buffer[block_off:] # Store out-of-bounds data after the end of PFAT files
|
||||
if block_index + 1 == block_count:
|
||||
if is_ami_pfat(all_blocks_dict[file_index]):
|
||||
parse_pfat_file(all_blocks_dict[file_index], get_extract_path(file_path), padding + 8)
|
||||
|
||||
pfat_oob_name = get_file_name(file_count + 1, f'{extract_name}_OOB.bin')
|
||||
pfat_oob_data: bytes = pfat_buffer[block_off:] # Store out-of-bounds data after the end of PFAT files
|
||||
|
||||
pfat_oob_path = os.path.join(extract_path, pfat_oob_name)
|
||||
pfat_oob_name: str = get_file_name(file_count + 1, f'{extract_name}_OOB.bin')
|
||||
|
||||
pfat_oob_path: str = os.path.join(extract_path, pfat_oob_name)
|
||||
|
||||
with open(pfat_oob_path, 'wb') as out_oob:
|
||||
out_oob.write(pfat_oob_data)
|
||||
|
@ -300,20 +414,21 @@ def parse_pfat_file(input_file, extract_path, padding=0):
|
|||
if is_ami_pfat(pfat_oob_data):
|
||||
parse_pfat_file(pfat_oob_data, get_extract_path(pfat_oob_path), padding)
|
||||
|
||||
in_all_data = b''.join([block[1] for block in sorted(all_blocks_dict.items())])
|
||||
in_all_data: bytes = b''.join([block[1] for block in sorted(all_blocks_dict.items())])
|
||||
|
||||
in_all_name = get_file_name(0, f'{extract_name}_ALL.bin')
|
||||
in_all_name: str = get_file_name(0, f'{extract_name}_ALL.bin')
|
||||
|
||||
in_all_path = os.path.join(extract_path, in_all_name)
|
||||
in_all_path: str = os.path.join(extract_path, in_all_name)
|
||||
|
||||
with open(in_all_path, 'wb') as out_all:
|
||||
out_all.write(in_all_data + pfat_oob_data)
|
||||
|
||||
return 0
|
||||
|
||||
PFAT_AMI_HDR_LEN = ctypes.sizeof(AmiBiosGuardHeader)
|
||||
PFAT_BLK_HDR_LEN = ctypes.sizeof(IntelBiosGuardHeader)
|
||||
PFAT_BLK_S2K_LEN = ctypes.sizeof(IntelBiosGuardSignature2k)
|
||||
|
||||
PFAT_AMI_HDR_LEN: int = ctypes.sizeof(AmiBiosGuardHeader)
|
||||
PFAT_BLK_HDR_LEN: int = ctypes.sizeof(IntelBiosGuardHeader)
|
||||
PFAT_BLK_SIG_LEN: int = ctypes.sizeof(IntelBiosGuardSignatureHeader)
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIOSUtility(TITLE, is_ami_pfat, parse_pfat_file).run_utility()
|
||||
BIOSUtility(title=TITLE, check=is_ami_pfat, main=parse_pfat_file).run_utility()
|
||||
|
|
|
@ -1,29 +1,23 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
AMI UCP Extract
|
||||
AMI UCP Update Extractor
|
||||
Copyright (C) 2021-2022 Plato Mavropoulos
|
||||
Copyright (C) 2021-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'AMI UCP Update Extractor v2.0_a20'
|
||||
|
||||
import contextlib
|
||||
import ctypes
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import struct
|
||||
import ctypes
|
||||
import contextlib
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
from common.checksums import get_chk_16
|
||||
from common.comp_efi import efi_decompress, is_efi_compressed
|
||||
from common.path_ops import agnostic_path, make_dirs, safe_name, safe_path, get_extract_path
|
||||
from common.path_ops import agnostic_path, get_extract_path, make_dirs, safe_name, safe_path
|
||||
from common.patterns import PAT_AMI_UCP, PAT_INTEL_ENG
|
||||
from common.struct_ops import char, get_struct, uint8_t, uint16_t, uint32_t
|
||||
from common.struct_ops import Char, get_struct, UInt8, UInt16, UInt32
|
||||
from common.system import printer
|
||||
from common.templates import BIOSUtility
|
||||
from common.text_ops import file_to_bytes, to_string
|
||||
|
@ -31,15 +25,20 @@ from common.text_ops import file_to_bytes, to_string
|
|||
from AMI_PFAT_Extract import is_ami_pfat, parse_pfat_file
|
||||
from Insyde_IFD_Extract import insyde_ifd_extract, is_insyde_ifd
|
||||
|
||||
TITLE = 'AMI UCP Update Extractor v3.0'
|
||||
|
||||
|
||||
class UafHeader(ctypes.LittleEndianStructure):
|
||||
""" UAF Header """
|
||||
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('ModuleTag', char*4), # 0x00
|
||||
('ModuleSize', uint32_t), # 0x04
|
||||
('Checksum', uint16_t), # 0x08
|
||||
('Unknown0', uint8_t), # 0x0A
|
||||
('Unknown1', uint8_t), # 0x0A
|
||||
('Reserved', uint8_t*4), # 0x0C
|
||||
('ModuleTag', Char * 4), # 0x00
|
||||
('ModuleSize', UInt32), # 0x04
|
||||
('Checksum', UInt16), # 0x08
|
||||
('Unknown0', UInt8), # 0x0A
|
||||
('Unknown1', UInt8), # 0x0A
|
||||
('Reserved', UInt8 * 4), # 0x0C
|
||||
# 0x10
|
||||
]
|
||||
|
||||
|
@ -54,41 +53,51 @@ class UafHeader(ctypes.LittleEndianStructure):
|
|||
|
||||
return f'{res_hex}{res_txt}'
|
||||
|
||||
def struct_print(self, p):
|
||||
printer(['Tag :', self.ModuleTag.decode('utf-8')], p, False)
|
||||
printer(['Size :', f'0x{self.ModuleSize:X}'], p, False)
|
||||
printer(['Checksum :', f'0x{self.Checksum:04X}'], p, False)
|
||||
printer(['Unknown 0 :', f'0x{self.Unknown0:02X}'], p, False)
|
||||
printer(['Unknown 1 :', f'0x{self.Unknown1:02X}'], p, False)
|
||||
printer(['Reserved :', self._get_reserved()], p, False)
|
||||
def struct_print(self, padd):
|
||||
""" Display structure information """
|
||||
|
||||
printer(['Tag :', self.ModuleTag.decode('utf-8')], padd, False)
|
||||
printer(['Size :', f'0x{self.ModuleSize:X}'], padd, False)
|
||||
printer(['Checksum :', f'0x{self.Checksum:04X}'], padd, False)
|
||||
printer(['Unknown 0 :', f'0x{self.Unknown0:02X}'], padd, False)
|
||||
printer(['Unknown 1 :', f'0x{self.Unknown1:02X}'], padd, False)
|
||||
printer(['Reserved :', self._get_reserved()], padd, False)
|
||||
|
||||
|
||||
class UafModule(ctypes.LittleEndianStructure):
|
||||
""" UAF Module """
|
||||
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('CompressSize', uint32_t), # 0x00
|
||||
('OriginalSize', uint32_t), # 0x04
|
||||
('CompressSize', UInt32), # 0x00
|
||||
('OriginalSize', UInt32), # 0x04
|
||||
# 0x08
|
||||
]
|
||||
|
||||
def struct_print(self, p, filename, description):
|
||||
printer(['Compress Size:', f'0x{self.CompressSize:X}'], p, False)
|
||||
printer(['Original Size:', f'0x{self.OriginalSize:X}'], p, False)
|
||||
printer(['Filename :', filename], p, False)
|
||||
printer(['Description :', description], p, False)
|
||||
def struct_print(self, padd, filename, description):
|
||||
""" Display structure information """
|
||||
|
||||
printer(['Compress Size:', f'0x{self.CompressSize:X}'], padd, False)
|
||||
printer(['Original Size:', f'0x{self.OriginalSize:X}'], padd, False)
|
||||
printer(['Filename :', filename], padd, False)
|
||||
printer(['Description :', description], padd, False)
|
||||
|
||||
|
||||
class UiiHeader(ctypes.LittleEndianStructure):
|
||||
""" UII Header """
|
||||
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('UIISize', uint16_t), # 0x00
|
||||
('Checksum', uint16_t), # 0x02
|
||||
('UtilityVersion', uint32_t), # 0x04 AFU|BGT (Unknown, Signed)
|
||||
('InfoSize', uint16_t), # 0x08
|
||||
('SupportBIOS', uint8_t), # 0x0A
|
||||
('SupportOS', uint8_t), # 0x0B
|
||||
('DataBusWidth', uint8_t), # 0x0C
|
||||
('ProgramType', uint8_t), # 0x0D
|
||||
('ProgramMode', uint8_t), # 0x0E
|
||||
('SourceSafeRel', uint8_t), # 0x0F
|
||||
('UIISize', UInt16), # 0x00
|
||||
('Checksum', UInt16), # 0x02
|
||||
('UtilityVersion', UInt32), # 0x04 AFU|BGT (Unknown, Signed)
|
||||
('InfoSize', UInt16), # 0x08
|
||||
('SupportBIOS', UInt8), # 0x0A
|
||||
('SupportOS', UInt8), # 0x0B
|
||||
('DataBusWidth', UInt8), # 0x0C
|
||||
('ProgramType', UInt8), # 0x0D
|
||||
('ProgramMode', UInt8), # 0x0E
|
||||
('SourceSafeRel', UInt8), # 0x0F
|
||||
# 0x10
|
||||
]
|
||||
|
||||
|
@ -98,76 +107,94 @@ class UiiHeader(ctypes.LittleEndianStructure):
|
|||
PTP = {1: 'Executable', 2: 'Library', 3: 'Driver'}
|
||||
PMD = {1: 'API', 2: 'Console', 3: 'GUI', 4: 'Console/GUI'}
|
||||
|
||||
def struct_print(self, p, description):
|
||||
SupportBIOS = self.SBI.get(self.SupportBIOS, f'Unknown ({self.SupportBIOS})')
|
||||
SupportOS = self.SOS.get(self.SupportOS, f'Unknown ({self.SupportOS})')
|
||||
DataBusWidth = self.DBW.get(self.DataBusWidth, f'Unknown ({self.DataBusWidth})')
|
||||
ProgramType = self.PTP.get(self.ProgramType, f'Unknown ({self.ProgramType})')
|
||||
ProgramMode = self.PMD.get(self.ProgramMode, f'Unknown ({self.ProgramMode})')
|
||||
def struct_print(self, padd, description):
|
||||
""" Display structure information """
|
||||
|
||||
support_bios = self.SBI.get(self.SupportBIOS, f'Unknown ({self.SupportBIOS})')
|
||||
support_os = self.SOS.get(self.SupportOS, f'Unknown ({self.SupportOS})')
|
||||
data_bus_width = self.DBW.get(self.DataBusWidth, f'Unknown ({self.DataBusWidth})')
|
||||
program_type = self.PTP.get(self.ProgramType, f'Unknown ({self.ProgramType})')
|
||||
program_mode = self.PMD.get(self.ProgramMode, f'Unknown ({self.ProgramMode})')
|
||||
|
||||
printer(['UII Size :', f'0x{self.UIISize:X}'], padd, False)
|
||||
printer(['Checksum :', f'0x{self.Checksum:04X}'], padd, False)
|
||||
printer(['Tool Version :', f'0x{self.UtilityVersion:08X}'], padd, False)
|
||||
printer(['Info Size :', f'0x{self.InfoSize:X}'], padd, False)
|
||||
printer(['Supported BIOS:', support_bios], padd, False)
|
||||
printer(['Supported OS :', support_os], padd, False)
|
||||
printer(['Data Bus Width:', data_bus_width], padd, False)
|
||||
printer(['Program Type :', program_type], padd, False)
|
||||
printer(['Program Mode :', program_mode], padd, False)
|
||||
printer(['SourceSafe Tag:', f'{self.SourceSafeRel:02d}'], padd, False)
|
||||
printer(['Description :', description], padd, False)
|
||||
|
||||
printer(['UII Size :', f'0x{self.UIISize:X}'], p, False)
|
||||
printer(['Checksum :', f'0x{self.Checksum:04X}'], p, False)
|
||||
printer(['Tool Version :', f'0x{self.UtilityVersion:08X}'], p, False)
|
||||
printer(['Info Size :', f'0x{self.InfoSize:X}'], p, False)
|
||||
printer(['Supported BIOS:', SupportBIOS], p, False)
|
||||
printer(['Supported OS :', SupportOS], p, False)
|
||||
printer(['Data Bus Width:', DataBusWidth], p, False)
|
||||
printer(['Program Type :', ProgramType], p, False)
|
||||
printer(['Program Mode :', ProgramMode], p, False)
|
||||
printer(['SourceSafe Tag:', f'{self.SourceSafeRel:02d}'], p, False)
|
||||
printer(['Description :', description], p, False)
|
||||
|
||||
class DisHeader(ctypes.LittleEndianStructure):
|
||||
""" DIS Header """
|
||||
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('PasswordSize', uint16_t), # 0x00
|
||||
('EntryCount', uint16_t), # 0x02
|
||||
('Password', char*12), # 0x04
|
||||
('PasswordSize', UInt16), # 0x00
|
||||
('EntryCount', UInt16), # 0x02
|
||||
('Password', Char * 12), # 0x04
|
||||
# 0x10
|
||||
]
|
||||
|
||||
def struct_print(self, p):
|
||||
printer(['Password Size:', f'0x{self.PasswordSize:X}'], p, False)
|
||||
printer(['Entry Count :', self.EntryCount], p, False)
|
||||
printer(['Password :', self.Password.decode('utf-8')], p, False)
|
||||
def struct_print(self, padd):
|
||||
""" Display structure information """
|
||||
|
||||
printer(['Password Size:', f'0x{self.PasswordSize:X}'], padd, False)
|
||||
printer(['Entry Count :', self.EntryCount], padd, False)
|
||||
printer(['Password :', self.Password.decode('utf-8')], padd, False)
|
||||
|
||||
|
||||
class DisModule(ctypes.LittleEndianStructure):
|
||||
""" DIS Module """
|
||||
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('EnabledDisabled', uint8_t), # 0x00
|
||||
('ShownHidden', uint8_t), # 0x01
|
||||
('Command', char*32), # 0x02
|
||||
('Description', char*256), # 0x22
|
||||
('EnabledDisabled', UInt8), # 0x00
|
||||
('ShownHidden', UInt8), # 0x01
|
||||
('Command', Char * 32), # 0x02
|
||||
('Description', Char * 256), # 0x22
|
||||
# 0x122
|
||||
]
|
||||
|
||||
ENDIS = {0: 'Disabled', 1: 'Enabled'}
|
||||
SHOWN = {0: 'Hidden', 1: 'Shown', 2: 'Shown Only'}
|
||||
|
||||
def struct_print(self, p):
|
||||
EnabledDisabled = self.ENDIS.get(self.EnabledDisabled, f'Unknown ({self.EnabledDisabled})')
|
||||
ShownHidden = self.SHOWN.get(self.ShownHidden, f'Unknown ({self.ShownHidden})')
|
||||
def struct_print(self, padd):
|
||||
""" Display structure information """
|
||||
|
||||
enabled_disabled = self.ENDIS.get(self.EnabledDisabled, f'Unknown ({self.EnabledDisabled})')
|
||||
shown_hidden = self.SHOWN.get(self.ShownHidden, f'Unknown ({self.ShownHidden})')
|
||||
|
||||
printer(['State :', enabled_disabled], padd, False)
|
||||
printer(['Display :', shown_hidden], padd, False)
|
||||
printer(['Command :', self.Command.decode('utf-8').strip()], padd, False)
|
||||
printer(['Description:', self.Description.decode('utf-8').strip()], padd, False)
|
||||
|
||||
printer(['State :', EnabledDisabled], p, False)
|
||||
printer(['Display :', ShownHidden], p, False)
|
||||
printer(['Command :', self.Command.decode('utf-8').strip()], p, False)
|
||||
printer(['Description:', self.Description.decode('utf-8').strip()], p, False)
|
||||
|
||||
# Validate UCP Module Checksum-16
|
||||
def chk16_validate(data, tag, padd=0):
|
||||
""" Validate UCP Module Checksum-16 """
|
||||
|
||||
if get_chk_16(data) != 0:
|
||||
printer(f'Error: Invalid UCP Module {tag} Checksum!', padd, pause=True)
|
||||
else:
|
||||
printer(f'Checksum of UCP Module {tag} is valid!', padd)
|
||||
|
||||
# Check if input is AMI UCP image
|
||||
|
||||
def is_ami_ucp(in_file):
|
||||
""" Check if input is AMI UCP image """
|
||||
|
||||
buffer = file_to_bytes(in_file)
|
||||
|
||||
return bool(get_ami_ucp(buffer)[0] is not None)
|
||||
|
||||
# Get all input file AMI UCP patterns
|
||||
|
||||
def get_ami_ucp(in_file):
|
||||
""" Get all input file AMI UCP patterns """
|
||||
|
||||
buffer = file_to_bytes(in_file)
|
||||
|
||||
uaf_len_max = 0x0 # Length of largest detected @UAF|@HPU
|
||||
|
@ -179,14 +206,19 @@ def get_ami_ucp(in_file):
|
|||
|
||||
if uaf_len_cur > uaf_len_max:
|
||||
uaf_len_max = uaf_len_cur
|
||||
|
||||
uaf_hdr_off = uaf.start()
|
||||
|
||||
uaf_buf_bin = buffer[uaf_hdr_off:uaf_hdr_off + uaf_len_max]
|
||||
|
||||
uaf_buf_tag = uaf.group(0)[:4].decode('utf-8', 'ignore')
|
||||
|
||||
return uaf_buf_bin, uaf_buf_tag
|
||||
|
||||
# Get list of @UAF|@HPU Modules
|
||||
|
||||
def get_uaf_mod(buffer, uaf_off=0x0):
|
||||
""" Get list of @UAF|@HPU Modules """
|
||||
|
||||
uaf_all = [] # Initialize list of all @UAF|@HPU Modules
|
||||
|
||||
while buffer[uaf_off] == 0x40: # ASCII of @ is 0x40
|
||||
|
@ -211,8 +243,10 @@ def get_uaf_mod(buffer, uaf_off=0x0):
|
|||
|
||||
return uaf_all
|
||||
|
||||
# Parse & Extract AMI UCP structures
|
||||
|
||||
def ucp_extract(in_file, extract_path, padding=0, checksum=False):
|
||||
""" Parse & Extract AMI UCP structures """
|
||||
|
||||
input_buffer = file_to_bytes(in_file)
|
||||
|
||||
nal_dict = {} # Initialize @NAL Dictionary per UCP
|
||||
|
@ -235,6 +269,7 @@ def ucp_extract(in_file, extract_path, padding=0, checksum=False):
|
|||
uaf_mod = get_struct(fake, 0x0, UafModule) # Parse @UAF|@HPU Module EFI Structure
|
||||
|
||||
uaf_name = UAF_TAG_DICT[ucp_tag][0] # Get @UAF|@HPU Module Filename
|
||||
|
||||
uaf_desc = UAF_TAG_DICT[ucp_tag][1] # Get @UAF|@HPU Module Description
|
||||
|
||||
uaf_mod.struct_print(padding + 8, uaf_name, uaf_desc) # Print @UAF|@HPU Module EFI Info
|
||||
|
@ -247,8 +282,10 @@ def ucp_extract(in_file, extract_path, padding=0, checksum=False):
|
|||
for mod_info in uaf_all:
|
||||
nal_dict = uaf_extract(ucp_buffer, extract_path, mod_info, padding + 8, checksum, nal_dict)
|
||||
|
||||
# Parse & Extract AMI UCP > @UAF|@HPU Module/Section
|
||||
|
||||
def uaf_extract(buffer, extract_path, mod_info, padding=0, checksum=False, nal_dict=None):
|
||||
""" Parse & Extract AMI UCP > @UAF|@HPU Module/Section """
|
||||
|
||||
if nal_dict is None:
|
||||
nal_dict = {}
|
||||
|
||||
|
@ -296,14 +333,20 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, checksum=False, nal_d
|
|||
uaf_mod.struct_print(padding + 4, uaf_name + uaf_fext, uaf_fdesc) # Print @UAF|@HPU Module EFI Info
|
||||
|
||||
# Check if unknown @UAF|@HPU Module Tag is present in @NAL but not in built-in dictionary
|
||||
if uaf_tag in nal_dict and uaf_tag not in UAF_TAG_DICT and not uaf_tag.startswith(('@ROM','@R0','@S0','@DR','@DS')):
|
||||
printer(f'Note: Detected new AMI UCP Module {uaf_tag} ({nal_dict[uaf_tag][1]}) in @NAL!', padding + 4, pause=True)
|
||||
if uaf_tag in nal_dict and uaf_tag not in UAF_TAG_DICT and \
|
||||
not uaf_tag.startswith(('@ROM', '@R0', '@S0', '@DR', '@DS')):
|
||||
|
||||
printer(f'Note: Detected new AMI UCP Module {uaf_tag} ({nal_dict[uaf_tag][1]}) in @NAL!',
|
||||
padding + 4, pause=True)
|
||||
|
||||
# Generate @UAF|@HPU Module File name, depending on whether decompression will be required
|
||||
uaf_sname = safe_name(uaf_name + ('.temp' if is_comp else uaf_fext))
|
||||
|
||||
if uaf_tag in nal_dict:
|
||||
uaf_npath = safe_path(extract_path, nal_dict[uaf_tag][0])
|
||||
|
||||
make_dirs(uaf_npath, exist_ok=True)
|
||||
|
||||
uaf_fname = safe_path(uaf_npath, uaf_sname)
|
||||
else:
|
||||
uaf_fname = safe_path(extract_path, uaf_sname)
|
||||
|
@ -337,11 +380,15 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, checksum=False, nal_d
|
|||
# Some Compressed @UAF|@HPU Module EFI data lack necessary EOF padding
|
||||
if uaf_mod.CompressSize > len(uaf_data_raw):
|
||||
comp_padd = b'\x00' * (uaf_mod.CompressSize - len(uaf_data_raw))
|
||||
uaf_data_raw = uaf_data_mod[:UAF_MOD_LEN] + uaf_data_raw + comp_padd # Add missing padding for decompression
|
||||
|
||||
# Add missing padding for decompression
|
||||
uaf_data_raw = uaf_data_mod[:UAF_MOD_LEN] + uaf_data_raw + comp_padd
|
||||
else:
|
||||
uaf_data_raw = uaf_data_mod[:UAF_MOD_LEN] + uaf_data_raw # Add the EFI/Tiano Compression info before Raw Data
|
||||
# Add the EFI/Tiano Compression info before Raw Data
|
||||
uaf_data_raw = uaf_data_mod[:UAF_MOD_LEN] + uaf_data_raw
|
||||
else:
|
||||
uaf_data_raw = uaf_data_raw[:uaf_mod.OriginalSize] # No compression, extend to end of Original @UAF|@HPU Module size
|
||||
# No compression, extend to end of Original @UAF|@HPU Module size
|
||||
uaf_data_raw = uaf_data_raw[:uaf_mod.OriginalSize]
|
||||
|
||||
# Store/Save @UAF|@HPU Module file
|
||||
if uaf_tag != '@UII': # Skip @UII binary, already parsed
|
||||
|
@ -363,6 +410,7 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, checksum=False, nal_d
|
|||
# Process and Print known text only @UAF|@HPU Modules (after EFI/Tiano Decompression)
|
||||
if uaf_tag in UAF_TAG_DICT and UAF_TAG_DICT[uaf_tag][2] == 'Text':
|
||||
printer(f'{UAF_TAG_DICT[uaf_tag][1]}:', padding + 4)
|
||||
|
||||
printer(uaf_data_raw.decode('utf-8', 'ignore'), padding + 8)
|
||||
|
||||
# Parse Default Command Status @UAF|@HPU Module (@DIS)
|
||||
|
@ -392,6 +440,7 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, checksum=False, nal_d
|
|||
with open(uaf_fname[:-3] + 'txt', 'a', encoding='utf-8') as dis:
|
||||
with contextlib.redirect_stdout(dis):
|
||||
printer()
|
||||
|
||||
dis_mod.struct_print(4) # Store @DIS Module Entry Info
|
||||
|
||||
os.remove(uaf_fname) # Delete @DIS Module binary, info exported as text
|
||||
|
@ -409,7 +458,9 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, checksum=False, nal_d
|
|||
printer(f'{info_tag} : {info_value}', padding + 8, False) # Print @NAL Module Tag-Path Info
|
||||
|
||||
info_part = agnostic_path(info_value).parts # Split OS agnostic path in parts
|
||||
|
||||
info_path = to_string(info_part[1:-1], os.sep) # Get path without drive/root or file
|
||||
|
||||
info_name = info_part[-1] # Get file from last path part
|
||||
|
||||
nal_dict[info_tag] = (info_path, info_name) # Assign a file path & name to each Tag
|
||||
|
@ -444,6 +495,7 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, checksum=False, nal_d
|
|||
|
||||
return nal_dict
|
||||
|
||||
|
||||
# Get common ctypes Structure Sizes
|
||||
UAF_HDR_LEN = ctypes.sizeof(UafHeader)
|
||||
UAF_MOD_LEN = ctypes.sizeof(UafModule)
|
||||
|
@ -507,9 +559,12 @@ UAF_TAG_DICT = {
|
|||
'@VXD': ['amifldrv.vxd', 'amifldrv.vxd', ''],
|
||||
'@W32': ['amifldrv32.sys', 'amifldrv32.sys', ''],
|
||||
'@W64': ['amifldrv64.sys', 'amifldrv64.sys', ''],
|
||||
'@D64': ['amifldrv64.sys', 'amifldrv64.sys', ''],
|
||||
}
|
||||
|
||||
if __name__ == '__main__':
|
||||
utility = BIOSUtility(TITLE, is_ami_ucp, ucp_extract)
|
||||
utility.parse_argument('-c', '--checksum', help='verify AMI UCP Checksums (slow)', action='store_true')
|
||||
utility_args = [(['-c', '--checksum'], {'help': 'verify AMI UCP Checksums (slow)', 'action': 'store_true'})]
|
||||
|
||||
utility = BIOSUtility(title=TITLE, check=is_ami_ucp, main=ucp_extract, args=utility_args)
|
||||
|
||||
utility.run_utility()
|
||||
|
|
128
Apple_EFI_ID.py
128
Apple_EFI_ID.py
|
@ -1,85 +1,91 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Apple EFI ID
|
||||
Apple EFI Image Identifier
|
||||
Copyright (C) 2018-2022 Plato Mavropoulos
|
||||
Copyright (C) 2018-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'Apple EFI Image Identifier v2.0_a5'
|
||||
|
||||
import os
|
||||
import sys
|
||||
import zlib
|
||||
import struct
|
||||
import ctypes
|
||||
import logging
|
||||
import os
|
||||
import struct
|
||||
import subprocess
|
||||
import zlib
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
from common.externals import get_uefifind_path, get_uefiextract_path
|
||||
from common.externals import get_uefiextract_path, get_uefifind_path
|
||||
from common.path_ops import del_dirs, path_parent, path_suffixes
|
||||
from common.patterns import PAT_APPLE_EFI
|
||||
from common.struct_ops import char, get_struct, uint8_t
|
||||
from common.struct_ops import Char, get_struct, UInt8
|
||||
from common.system import printer
|
||||
from common.templates import BIOSUtility
|
||||
from common.text_ops import file_to_bytes
|
||||
|
||||
TITLE = 'Apple EFI Image Identifier v3.0'
|
||||
|
||||
|
||||
class IntelBiosId(ctypes.LittleEndianStructure):
|
||||
""" Intel BIOS ID Structure """
|
||||
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Signature', char*8), # 0x00
|
||||
('BoardID', uint8_t*16), # 0x08
|
||||
('Dot1', uint8_t*2), # 0x18
|
||||
('BoardExt', uint8_t*6), # 0x1A
|
||||
('Dot2', uint8_t*2), # 0x20
|
||||
('VersionMajor', uint8_t*8), # 0x22
|
||||
('Dot3', uint8_t*2), # 0x2A
|
||||
('BuildType', uint8_t*2), # 0x2C
|
||||
('VersionMinor', uint8_t*4), # 0x2E
|
||||
('Dot4', uint8_t*2), # 0x32
|
||||
('Year', uint8_t*4), # 0x34
|
||||
('Month', uint8_t*4), # 0x38
|
||||
('Day', uint8_t*4), # 0x3C
|
||||
('Hour', uint8_t*4), # 0x40
|
||||
('Minute', uint8_t*4), # 0x44
|
||||
('NullTerminator', uint8_t*2), # 0x48
|
||||
('Signature', Char * 8), # 0x00
|
||||
('BoardID', UInt8 * 16), # 0x08
|
||||
('Dot1', UInt8 * 2), # 0x18
|
||||
('BoardExt', UInt8 * 6), # 0x1A
|
||||
('Dot2', UInt8 * 2), # 0x20
|
||||
('VersionMajor', UInt8 * 8), # 0x22
|
||||
('Dot3', UInt8 * 2), # 0x2A
|
||||
('BuildType', UInt8 * 2), # 0x2C
|
||||
('VersionMinor', UInt8 * 4), # 0x2E
|
||||
('Dot4', UInt8 * 2), # 0x32
|
||||
('Year', UInt8 * 4), # 0x34
|
||||
('Month', UInt8 * 4), # 0x38
|
||||
('Day', UInt8 * 4), # 0x3C
|
||||
('Hour', UInt8 * 4), # 0x40
|
||||
('Minute', UInt8 * 4), # 0x44
|
||||
('NullTerminator', UInt8 * 2), # 0x48
|
||||
# 0x4A
|
||||
]
|
||||
|
||||
# https://github.com/tianocore/edk2-platforms/blob/master/Platform/Intel/BoardModulePkg/Include/Guid/BiosId.h
|
||||
|
||||
@staticmethod
|
||||
def decode(field):
|
||||
def _decode(field):
|
||||
return struct.pack('B' * len(field), *field).decode('utf-16', 'ignore').strip('\x00 ')
|
||||
|
||||
def get_bios_id(self):
|
||||
BoardID = self.decode(self.BoardID)
|
||||
BoardExt = self.decode(self.BoardExt)
|
||||
VersionMajor = self.decode(self.VersionMajor)
|
||||
BuildType = self.decode(self.BuildType)
|
||||
VersionMinor = self.decode(self.VersionMinor)
|
||||
BuildDate = f'20{self.decode(self.Year)}-{self.decode(self.Month)}-{self.decode(self.Day)}'
|
||||
BuildTime = f'{self.decode(self.Hour)}-{self.decode(self.Minute)}'
|
||||
""" Create Apple EFI BIOS ID """
|
||||
|
||||
return BoardID, BoardExt, VersionMajor, BuildType, VersionMinor, BuildDate, BuildTime
|
||||
board_id = self._decode(self.BoardID)
|
||||
board_ext = self._decode(self.BoardExt)
|
||||
version_major = self._decode(self.VersionMajor)
|
||||
build_type = self._decode(self.BuildType)
|
||||
version_minor = self._decode(self.VersionMinor)
|
||||
build_date = f'20{self._decode(self.Year)}-{self._decode(self.Month)}-{self._decode(self.Day)}'
|
||||
build_time = f'{self._decode(self.Hour)}-{self._decode(self.Minute)}'
|
||||
|
||||
def struct_print(self, p):
|
||||
BoardID,BoardExt,VersionMajor,BuildType,VersionMinor,BuildDate,BuildTime = self.get_bios_id()
|
||||
return board_id, board_ext, version_major, build_type, version_minor, build_date, build_time
|
||||
|
||||
def struct_print(self, padd):
|
||||
""" Display structure information """
|
||||
|
||||
board_id, board_ext, version_major, build_type, version_minor, build_date, build_time = self.get_bios_id()
|
||||
|
||||
printer(['Intel Signature:', self.Signature.decode('utf-8')], padd, False)
|
||||
printer(['Board Identity: ', board_id], padd, False)
|
||||
printer(['Apple Identity: ', board_ext], padd, False)
|
||||
printer(['Major Version: ', version_major], padd, False)
|
||||
printer(['Minor Version: ', version_minor], padd, False)
|
||||
printer(['Build Type: ', build_type], padd, False)
|
||||
printer(['Build Date: ', build_date], padd, False)
|
||||
printer(['Build Time: ', build_time.replace('-', ':')], padd, False)
|
||||
|
||||
printer(['Intel Signature:', self.Signature.decode('utf-8')], p, False)
|
||||
printer(['Board Identity: ', BoardID], p, False)
|
||||
printer(['Apple Identity: ', BoardExt], p, False)
|
||||
printer(['Major Version: ', VersionMajor], p, False)
|
||||
printer(['Minor Version: ', VersionMinor], p, False)
|
||||
printer(['Build Type: ', BuildType], p, False)
|
||||
printer(['Build Date: ', BuildDate], p, False)
|
||||
printer(['Build Time: ', BuildTime.replace('-',':')], p, False)
|
||||
|
||||
# Check if input is Apple EFI image
|
||||
def is_apple_efi(input_file):
|
||||
""" Check if input is Apple EFI image """
|
||||
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
||||
if PAT_APPLE_EFI.search(input_buffer):
|
||||
|
@ -93,11 +99,15 @@ def is_apple_efi(input_file):
|
|||
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
return True
|
||||
except Exception:
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
logging.debug('Error: Could not check if input is Apple EFI image: %s', error)
|
||||
|
||||
return False
|
||||
|
||||
# Parse & Identify (or Rename) Apple EFI image
|
||||
|
||||
def apple_efi_identify(input_file, extract_path, padding=0, rename=False):
|
||||
""" Parse & Identify (or Rename) Apple EFI image """
|
||||
|
||||
if not os.path.isfile(input_file):
|
||||
printer('Error: Could not find input file path!', padding)
|
||||
|
||||
|
@ -130,8 +140,8 @@ def apple_efi_identify(input_file, extract_path, padding=0, rename=False):
|
|||
bios_id_hdr = get_struct(body_buffer, bios_id_match.start(), IntelBiosId)
|
||||
|
||||
del_dirs(extract_path) # Successful UEFIExtract extraction, remove its output (temp) folder
|
||||
except Exception:
|
||||
printer('Error: Failed to parse compressed $IBIOSI$ pattern!', padding)
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
printer(f'Error: Failed to parse compressed $IBIOSI$ pattern: {error}!', padding)
|
||||
|
||||
return 2
|
||||
|
||||
|
@ -146,9 +156,10 @@ def apple_efi_identify(input_file, extract_path, padding=0, rename=False):
|
|||
|
||||
input_adler32 = zlib.adler32(input_buffer)
|
||||
|
||||
ID,Ext,Major,Type,Minor,Date,Time = bios_id_hdr.get_bios_id()
|
||||
fw_id, fw_ext, fw_major, fw_type, fw_minor, fw_date, fw_time = bios_id_hdr.get_bios_id()
|
||||
|
||||
output_name = f'{ID}_{Ext}_{Major}_{Type}{Minor}_{Date}_{Time}_{input_adler32:08X}{input_suffix}'
|
||||
output_name = f'{fw_id}_{fw_ext}_{fw_major}_{fw_type}{fw_minor}_{fw_date}_{fw_time}_' \
|
||||
f'{input_adler32:08X}{input_suffix}'
|
||||
|
||||
output_file = os.path.join(input_parent, output_name)
|
||||
|
||||
|
@ -159,9 +170,12 @@ def apple_efi_identify(input_file, extract_path, padding=0, rename=False):
|
|||
|
||||
return 0
|
||||
|
||||
|
||||
PAT_UEFIFIND = f'244942494F534924{"." * 32}2E00{"." * 12}2E00{"." * 16}2E00{"." * 12}2E00{"." * 40}0000'
|
||||
|
||||
if __name__ == '__main__':
|
||||
utility = BIOSUtility(TITLE, is_apple_efi, apple_efi_identify)
|
||||
utility.parse_argument('-r', '--rename', help='rename EFI image based on its tag', action='store_true')
|
||||
utility_args = [(['-r', '--rename'], {'help': 'rename EFI image based on its tag', 'action': 'store_true'})]
|
||||
|
||||
utility = BIOSUtility(title=TITLE, check=is_apple_efi, main=apple_efi_identify, args=utility_args)
|
||||
|
||||
utility.run_utility()
|
||||
|
|
|
@ -1,19 +1,13 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Apple EFI IM4P
|
||||
Apple EFI IM4P Splitter
|
||||
Copyright (C) 2018-2022 Plato Mavropoulos
|
||||
Copyright (C) 2018-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'Apple EFI IM4P Splitter v3.0_a5'
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
from common.path_ops import make_dirs, path_stem
|
||||
from common.patterns import PAT_APPLE_IM4P, PAT_INTEL_IFD
|
||||
|
@ -21,8 +15,12 @@ from common.system import printer
|
|||
from common.templates import BIOSUtility
|
||||
from common.text_ops import file_to_bytes
|
||||
|
||||
# Check if input is Apple EFI IM4P image
|
||||
TITLE = 'Apple EFI IM4P Splitter v4.0'
|
||||
|
||||
|
||||
def is_apple_im4p(input_file):
|
||||
""" Check if input is Apple EFI IM4P image """
|
||||
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
||||
is_im4p = PAT_APPLE_IM4P.search(input_buffer)
|
||||
|
@ -31,8 +29,10 @@ def is_apple_im4p(input_file):
|
|||
|
||||
return bool(is_im4p and is_ifd)
|
||||
|
||||
# Parse & Split Apple EFI IM4P image
|
||||
|
||||
def apple_im4p_split(input_file, extract_path, padding=0):
|
||||
""" Parse & Split Apple EFI IM4P image """
|
||||
|
||||
exit_codes = []
|
||||
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
@ -116,6 +116,7 @@ def apple_im4p_split(input_file, extract_path, padding=0):
|
|||
|
||||
ifd_data_bgn = ifd_match_start
|
||||
ifd_data_end = ifd_data_bgn + ifd_comp_all_size
|
||||
|
||||
ifd_data_txt = f'0x{ifd_data_bgn:07X}-0x{ifd_data_end:07X}'
|
||||
|
||||
output_data = input_buffer[ifd_data_bgn:ifd_data_end]
|
||||
|
@ -138,8 +139,9 @@ def apple_im4p_split(input_file, extract_path, padding=0):
|
|||
|
||||
return sum(exit_codes)
|
||||
|
||||
|
||||
# Intel Flash Descriptor Component Sizes (4MB, 8MB, 16MB and 32MB)
|
||||
IFD_COMP_LEN = {3: 0x400000, 4: 0x800000, 5: 0x1000000, 6: 0x2000000}
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIOSUtility(TITLE, is_apple_im4p, apple_im4p_split).run_utility()
|
||||
BIOSUtility(title=TITLE, check=is_apple_im4p, main=apple_im4p_split).run_utility()
|
||||
|
|
|
@ -1,59 +1,66 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Apple PBZX Extract
|
||||
Apple EFI PBZX Extractor
|
||||
Copyright (C) 2021-2022 Plato Mavropoulos
|
||||
Copyright (C) 2021-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'Apple EFI PBZX Extractor v1.0_a5'
|
||||
|
||||
import os
|
||||
import sys
|
||||
import lzma
|
||||
import ctypes
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
import logging
|
||||
import lzma
|
||||
import os
|
||||
|
||||
from common.comp_szip import is_szip_supported, szip_decompress
|
||||
from common.path_ops import make_dirs, path_stem
|
||||
from common.patterns import PAT_APPLE_PBZX
|
||||
from common.struct_ops import get_struct, uint32_t
|
||||
from common.struct_ops import get_struct, UInt32
|
||||
from common.system import printer
|
||||
from common.templates import BIOSUtility
|
||||
from common.text_ops import file_to_bytes
|
||||
|
||||
TITLE = 'Apple EFI PBZX Extractor v2.0'
|
||||
|
||||
|
||||
class PbzxChunk(ctypes.BigEndianStructure):
|
||||
""" PBZX Chunk Header """
|
||||
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Reserved0', uint32_t), # 0x00
|
||||
('InitSize', uint32_t), # 0x04
|
||||
('Reserved1', uint32_t), # 0x08
|
||||
('CompSize', uint32_t), # 0x0C
|
||||
('Reserved0', UInt32), # 0x00
|
||||
('InitSize', UInt32), # 0x04
|
||||
('Reserved1', UInt32), # 0x08
|
||||
('CompSize', UInt32), # 0x0C
|
||||
# 0x10
|
||||
]
|
||||
|
||||
def struct_print(self, p):
|
||||
printer(['Reserved 0 :', f'0x{self.Reserved0:X}'], p, False)
|
||||
printer(['Initial Size :', f'0x{self.InitSize:X}'], p, False)
|
||||
printer(['Reserved 1 :', f'0x{self.Reserved1:X}'], p, False)
|
||||
printer(['Compressed Size:', f'0x{self.CompSize:X}'], p, False)
|
||||
def struct_print(self, padd):
|
||||
""" Display structure information """
|
||||
|
||||
printer(['Reserved 0 :', f'0x{self.Reserved0:X}'], padd, False)
|
||||
printer(['Initial Size :', f'0x{self.InitSize:X}'], padd, False)
|
||||
printer(['Reserved 1 :', f'0x{self.Reserved1:X}'], padd, False)
|
||||
printer(['Compressed Size:', f'0x{self.CompSize:X}'], padd, False)
|
||||
|
||||
|
||||
# Check if input is Apple PBZX image
|
||||
def is_apple_pbzx(input_file):
|
||||
""" Check if input is Apple PBZX image """
|
||||
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
||||
return bool(PAT_APPLE_PBZX.search(input_buffer[:0x4]))
|
||||
|
||||
# Parse & Extract Apple PBZX image
|
||||
|
||||
def apple_pbzx_extract(input_file, extract_path, padding=0):
|
||||
""" Parse & Extract Apple PBZX image """
|
||||
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
||||
make_dirs(extract_path, delete=True)
|
||||
|
||||
cpio_bin = b'' # Initialize PBZX > CPIO Buffer
|
||||
|
||||
cpio_len = 0x0 # Initialize PBZX > CPIO Length
|
||||
|
||||
chunk_off = 0xC # First PBZX Chunk starts at 0xC
|
||||
|
@ -77,7 +84,9 @@ def apple_pbzx_extract(input_file, extract_path, padding=0):
|
|||
cpio_bin += lzma.LZMADecompressor().decompress(comp_bin)
|
||||
|
||||
printer('Successful LZMA decompression!', padding + 8)
|
||||
except Exception:
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
logging.debug('Error: Failed to LZMA decompress PBZX Chunk 0x%X: %s', chunk_off, error)
|
||||
|
||||
# Otherwise, Chunk data is not compressed
|
||||
cpio_bin += comp_bin
|
||||
|
||||
|
@ -111,8 +120,9 @@ def apple_pbzx_extract(input_file, extract_path, padding=0):
|
|||
|
||||
return 0
|
||||
|
||||
|
||||
# Get common ctypes Structure Sizes
|
||||
PBZX_CHUNK_HDR_LEN = ctypes.sizeof(PbzxChunk)
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIOSUtility(TITLE, is_apple_pbzx, apple_pbzx_extract).run_utility()
|
||||
BIOSUtility(title=TITLE, check=is_apple_pbzx, main=apple_pbzx_extract).run_utility()
|
||||
|
|
|
@ -1,22 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Apple EFI PKG
|
||||
Apple EFI Package Extractor
|
||||
Copyright (C) 2019-2022 Plato Mavropoulos
|
||||
Copyright (C) 2019-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'Apple EFI Package Extractor v2.0_a5'
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
from common.comp_szip import is_szip_supported, szip_decompress
|
||||
from common.path_ops import copy_file, del_dirs, get_path_files, make_dirs, path_name, path_parent, get_extract_path
|
||||
from common.path_ops import copy_file, del_dirs, get_extract_path, get_path_files, make_dirs, path_name, path_parent
|
||||
from common.patterns import PAT_APPLE_PKG
|
||||
from common.system import printer
|
||||
from common.templates import BIOSUtility
|
||||
|
@ -26,30 +20,41 @@ from Apple_EFI_ID import apple_efi_identify, is_apple_efi
|
|||
from Apple_EFI_IM4P import apple_im4p_split, is_apple_im4p
|
||||
from Apple_EFI_PBZX import apple_pbzx_extract, is_apple_pbzx
|
||||
|
||||
# Check if input is Apple EFI PKG package
|
||||
TITLE = 'Apple EFI Package Extractor v3.0'
|
||||
|
||||
|
||||
def is_apple_pkg(input_file):
|
||||
""" Check if input is Apple EFI PKG package """
|
||||
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
||||
return bool(PAT_APPLE_PKG.search(input_buffer[:0x4]))
|
||||
|
||||
# Split Apple EFI image (if applicable) and Rename
|
||||
|
||||
def efi_split_rename(in_file, out_path, padding=0):
|
||||
""" Split Apple EFI image (if applicable) and Rename """
|
||||
|
||||
exit_codes = []
|
||||
|
||||
working_dir = get_extract_path(in_file)
|
||||
|
||||
if is_apple_im4p(in_file):
|
||||
printer(f'Splitting IM4P via {is_apple_im4p.__module__}...', padding)
|
||||
|
||||
im4p_exit = apple_im4p_split(in_file, working_dir, padding + 4)
|
||||
|
||||
exit_codes.append(im4p_exit)
|
||||
else:
|
||||
make_dirs(working_dir, delete=True)
|
||||
|
||||
copy_file(in_file, working_dir, True)
|
||||
|
||||
for efi_file in get_path_files(working_dir):
|
||||
if is_apple_efi(efi_file):
|
||||
printer(f'Renaming EFI via {is_apple_efi.__module__}...', padding)
|
||||
|
||||
name_exit = apple_efi_identify(efi_file, efi_file, padding + 4, True)
|
||||
|
||||
exit_codes.append(name_exit)
|
||||
|
||||
for named_file in get_path_files(working_dir):
|
||||
|
@ -59,10 +64,13 @@ def efi_split_rename(in_file, out_path, padding=0):
|
|||
|
||||
return sum(exit_codes)
|
||||
|
||||
# Parse & Extract Apple EFI PKG packages
|
||||
|
||||
def apple_pkg_extract(input_file, extract_path, padding=0):
|
||||
""" Parse & Extract Apple EFI PKG packages """
|
||||
|
||||
if not os.path.isfile(input_file):
|
||||
printer('Error: Could not find input file path!', padding)
|
||||
|
||||
return 1
|
||||
|
||||
make_dirs(extract_path, delete=True)
|
||||
|
@ -79,35 +87,48 @@ def apple_pkg_extract(input_file, extract_path, padding=0):
|
|||
for xar_file in get_path_files(xar_path):
|
||||
if path_name(xar_file) == 'Payload':
|
||||
pbzx_module = is_apple_pbzx.__module__
|
||||
|
||||
if is_apple_pbzx(xar_file):
|
||||
printer(f'Extracting PBZX via {pbzx_module}...', padding + 4)
|
||||
|
||||
pbzx_path = get_extract_path(xar_file)
|
||||
|
||||
if apple_pbzx_extract(xar_file, pbzx_path, padding + 8) == 0:
|
||||
printer(f'Succesfull PBZX extraction via {pbzx_module}!', padding + 4)
|
||||
|
||||
for pbzx_file in get_path_files(pbzx_path):
|
||||
if path_name(pbzx_file) == 'UpdateBundle.zip':
|
||||
if is_szip_supported(pbzx_file, padding + 8, args=['-tZIP'], check=True):
|
||||
zip_path = get_extract_path(pbzx_file)
|
||||
if szip_decompress(pbzx_file, zip_path, 'ZIP', padding + 8, args=['-tZIP'], check=True) == 0:
|
||||
|
||||
if szip_decompress(pbzx_file, zip_path, 'ZIP', padding + 8, args=['-tZIP'],
|
||||
check=True) == 0:
|
||||
for zip_file in get_path_files(zip_path):
|
||||
if path_name(path_parent(zip_file)) == 'MacEFI':
|
||||
printer(path_name(zip_file), padding + 12)
|
||||
|
||||
if efi_split_rename(zip_file, extract_path, padding + 16) != 0:
|
||||
printer(f'Error: Could not split and rename {path_name(zip_file)}!', padding)
|
||||
printer(f'Error: Could not split and rename {path_name(zip_file)}!',
|
||||
padding)
|
||||
|
||||
return 10
|
||||
else:
|
||||
return 9
|
||||
else:
|
||||
return 8
|
||||
|
||||
break # ZIP found, stop
|
||||
else:
|
||||
printer('Error: Could not find "UpdateBundle.zip" file!', padding)
|
||||
|
||||
return 7
|
||||
else:
|
||||
printer(f'Error: Failed to extract PBZX file via {pbzx_module}!', padding)
|
||||
|
||||
return 6
|
||||
else:
|
||||
printer(f'Error: Failed to detect file as PBZX via {pbzx_module}!', padding)
|
||||
|
||||
return 5
|
||||
|
||||
break # Payload found, stop searching
|
||||
|
@ -115,16 +136,22 @@ def apple_pkg_extract(input_file, extract_path, padding=0):
|
|||
if path_name(xar_file) == 'Scripts':
|
||||
if is_szip_supported(xar_file, padding + 4, args=['-tGZIP'], check=True):
|
||||
gzip_path = get_extract_path(xar_file)
|
||||
|
||||
if szip_decompress(xar_file, gzip_path, 'GZIP', padding + 4, args=['-tGZIP'], check=True) == 0:
|
||||
for gzip_file in get_path_files(gzip_path):
|
||||
if is_szip_supported(gzip_file, padding + 8, args=['-tCPIO'], check=True):
|
||||
cpio_path = get_extract_path(gzip_file)
|
||||
if szip_decompress(gzip_file, cpio_path, 'CPIO', padding + 8, args=['-tCPIO'], check=True) == 0:
|
||||
|
||||
if szip_decompress(gzip_file, cpio_path, 'CPIO', padding + 8, args=['-tCPIO'],
|
||||
check=True) == 0:
|
||||
for cpio_file in get_path_files(cpio_path):
|
||||
if path_name(path_parent(cpio_file)) == 'EFIPayloads':
|
||||
printer(path_name(cpio_file), padding + 12)
|
||||
|
||||
if efi_split_rename(cpio_file, extract_path, padding + 16) != 0:
|
||||
printer(f'Error: Could not split and rename {path_name(cpio_file)}!', padding)
|
||||
printer(f'Error: Could not split and rename {path_name(cpio_file)}!',
|
||||
padding)
|
||||
|
||||
return 15
|
||||
else:
|
||||
return 14
|
||||
|
@ -138,11 +165,13 @@ def apple_pkg_extract(input_file, extract_path, padding=0):
|
|||
break # Scripts found, stop searching
|
||||
else:
|
||||
printer('Error: Could not find "Payload" or "Scripts" file!', padding)
|
||||
|
||||
return 4
|
||||
|
||||
del_dirs(xar_path) # Delete temporary/working XAR folder
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIOSUtility(TITLE, is_apple_pkg, apple_pkg_extract).run_utility()
|
||||
BIOSUtility(title=TITLE, check=is_apple_pkg, main=apple_pkg_extract).run_utility()
|
||||
|
|
|
@ -1,41 +1,43 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Award BIOS Extract
|
||||
Award BIOS Module Extractor
|
||||
Copyright (C) 2018-2022 Plato Mavropoulos
|
||||
Copyright (C) 2018-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'Award BIOS Module Extractor v2.0_a5'
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
import stat
|
||||
|
||||
from common.comp_szip import szip_decompress
|
||||
from common.path_ops import make_dirs, safe_name, get_extract_path
|
||||
from common.path_ops import get_extract_path, make_dirs, safe_name
|
||||
from common.patterns import PAT_AWARD_LZH
|
||||
from common.system import printer
|
||||
from common.templates import BIOSUtility
|
||||
from common.text_ops import file_to_bytes
|
||||
|
||||
# Check if input is Award BIOS image
|
||||
TITLE = 'Award BIOS Module Extractor v3.0'
|
||||
|
||||
|
||||
def is_award_bios(in_file):
|
||||
""" Check if input is Award BIOS image """
|
||||
|
||||
in_buffer = file_to_bytes(in_file)
|
||||
|
||||
return bool(PAT_AWARD_LZH.search(in_buffer))
|
||||
|
||||
# Parse & Extract Award BIOS image
|
||||
|
||||
def award_bios_extract(input_file, extract_path, padding=0):
|
||||
""" Parse & Extract Award BIOS image """
|
||||
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
||||
make_dirs(extract_path, delete=True)
|
||||
|
||||
for lzh_match in PAT_AWARD_LZH.finditer(input_buffer):
|
||||
lzh_type = lzh_match.group(0).decode('utf-8')
|
||||
|
||||
lzh_text = f'LZH-{lzh_type.strip("-").upper()}'
|
||||
|
||||
lzh_bgn = lzh_match.start()
|
||||
|
@ -44,15 +46,20 @@ def award_bios_extract(input_file, extract_path, padding=0):
|
|||
hdr_len = input_buffer[mod_bgn]
|
||||
mod_len = int.from_bytes(input_buffer[mod_bgn + 0x7:mod_bgn + 0xB], 'little')
|
||||
mod_end = lzh_bgn + hdr_len + mod_len
|
||||
|
||||
mod_bin = input_buffer[mod_bgn:mod_end]
|
||||
|
||||
tag_bgn = mod_bgn + 0x16
|
||||
tag_end = tag_bgn + input_buffer[mod_bgn + 0x15]
|
||||
tag_txt = input_buffer[tag_bgn:tag_end].decode('utf-8','ignore')
|
||||
if len(mod_bin) != 0x2 + hdr_len + mod_len:
|
||||
printer(f'Error: Skipped incomplete LZH stream at 0x{mod_bgn:X}!', padding, False)
|
||||
|
||||
continue
|
||||
|
||||
tag_txt = safe_name(mod_bin[0x16:0x16 + mod_bin[0x15]].decode('utf-8', 'ignore').strip())
|
||||
|
||||
printer(f'{lzh_text} > {tag_txt} [0x{mod_bgn:06X}-0x{mod_end:06X}]', padding)
|
||||
|
||||
mod_path = os.path.join(extract_path, safe_name(tag_txt))
|
||||
mod_path = os.path.join(extract_path, tag_txt)
|
||||
|
||||
lzh_path = f'{mod_path}.lzh'
|
||||
|
||||
with open(lzh_path, 'wb') as lzh_file:
|
||||
|
@ -63,6 +70,8 @@ def award_bios_extract(input_file, extract_path, padding=0):
|
|||
|
||||
# Manually check if 7-Zip extracted LZH due to its CRC check issue
|
||||
if os.path.isfile(mod_path):
|
||||
os.chmod(lzh_path, stat.S_IWRITE)
|
||||
|
||||
os.remove(lzh_path) # Successful extraction, delete LZH archive
|
||||
|
||||
# Extract any nested LZH archives
|
||||
|
@ -70,5 +79,6 @@ def award_bios_extract(input_file, extract_path, padding=0):
|
|||
# Recursively extract nested Award BIOS modules
|
||||
award_bios_extract(mod_path, get_extract_path(mod_path), padding + 8)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIOSUtility(TITLE, is_award_bios, award_bios_extract).run_utility()
|
||||
BIOSUtility(title=TITLE, check=is_award_bios, main=award_bios_extract).run_utility()
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,19 +1,13 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Fujitsu SFX Extractor
|
||||
Fujitsu SFX BIOS Extractor
|
||||
Copyright (C) 2019-2022 Plato Mavropoulos
|
||||
Copyright (C) 2019-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'Fujitsu SFX BIOS Extractor v3.0_a3'
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
from common.comp_szip import is_szip_supported, szip_decompress
|
||||
from common.path_ops import make_dirs
|
||||
|
@ -22,14 +16,20 @@ from common.system import printer
|
|||
from common.templates import BIOSUtility
|
||||
from common.text_ops import file_to_bytes
|
||||
|
||||
# Check if input is Fujitsu SFX image
|
||||
TITLE = 'Fujitsu SFX BIOS Extractor v4.0'
|
||||
|
||||
|
||||
def is_fujitsu_sfx(in_file):
|
||||
""" Check if input is Fujitsu SFX image """
|
||||
|
||||
buffer = file_to_bytes(in_file)
|
||||
|
||||
return bool(PAT_FUJITSU_SFX.search(buffer))
|
||||
|
||||
# Extract Fujitsu SFX image
|
||||
|
||||
def fujitsu_cabinet(in_file, extract_path, padding=0):
|
||||
""" Extract Fujitsu SFX image """
|
||||
|
||||
buffer = file_to_bytes(in_file)
|
||||
|
||||
match_cab = PAT_FUJITSU_SFX.search(buffer) # Microsoft CAB Header XOR 0xFF
|
||||
|
@ -43,14 +43,14 @@ def fujitsu_cabinet(in_file, extract_path, padding=0):
|
|||
cab_start = match_cab.start() + 0xA
|
||||
|
||||
# Determine the Microsoft CAB image size
|
||||
cab_size = int.from_bytes(buffer[cab_start + 0x8:cab_start + 0xC], 'little') # Get LE XOR-ed CAB size
|
||||
cab_size = int.from_bytes(buffer[cab_start + 0x8:cab_start + 0xC], 'little') # Get LE XOR CAB size
|
||||
xor_size = int.from_bytes(b'\xFF' * 0x4, 'little') # Create CAB size XOR value
|
||||
cab_size ^= xor_size # Perform XOR 0xFF and get actual CAB size
|
||||
|
||||
printer('Removing obfuscation...', padding + 4)
|
||||
|
||||
# Determine the Microsoft CAB image Data
|
||||
cab_data = int.from_bytes(buffer[cab_start:cab_start + cab_size], 'big') # Get BE XOR-ed CAB data
|
||||
cab_data = int.from_bytes(buffer[cab_start:cab_start + cab_size], 'big') # Get BE XOR- CAB data
|
||||
xor_data = int.from_bytes(b'\xFF' * cab_size, 'big') # Create CAB data XOR value
|
||||
cab_data = (cab_data ^ xor_data).to_bytes(cab_size, 'big') # Perform XOR 0xFF and get actual CAB data
|
||||
|
||||
|
@ -62,7 +62,7 @@ def fujitsu_cabinet(in_file, extract_path, padding=0):
|
|||
cab_file.write(cab_data) # Create temporary CAB archive
|
||||
|
||||
if is_szip_supported(cab_path, padding + 8, check=True):
|
||||
if szip_decompress(cab_path, extract_path, 'CAB', padding + 8, check=True) == 0:
|
||||
if szip_decompress(cab_path, extract_path, 'FjSfxBinay CAB', padding + 8, check=True) == 0:
|
||||
os.remove(cab_path) # Successful extraction, delete temporary CAB archive
|
||||
else:
|
||||
return 3
|
||||
|
@ -71,8 +71,10 @@ def fujitsu_cabinet(in_file, extract_path, padding=0):
|
|||
|
||||
return 0
|
||||
|
||||
# Parse & Extract Fujitsu SFX image
|
||||
|
||||
def fujitsu_sfx_extract(in_file, extract_path, padding=0):
|
||||
""" Parse & Extract Fujitsu SFX image """
|
||||
|
||||
buffer = file_to_bytes(in_file)
|
||||
|
||||
make_dirs(extract_path, delete=True)
|
||||
|
@ -81,9 +83,11 @@ def fujitsu_sfx_extract(in_file, extract_path, padding=0):
|
|||
printer('Successfully Extracted!', padding)
|
||||
else:
|
||||
printer('Error: Failed to Extract image!', padding)
|
||||
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIOSUtility(TITLE, is_fujitsu_sfx, fujitsu_sfx_extract).run_utility()
|
||||
BIOSUtility(title=TITLE, check=is_fujitsu_sfx, main=fujitsu_sfx_extract).run_utility()
|
||||
|
|
|
@ -1,27 +1,25 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Fujitsu UPC Extract
|
||||
Fujitsu UPC BIOS Extractor
|
||||
Copyright (C) 2021-2022 Plato Mavropoulos
|
||||
Copyright (C) 2021-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'Fujitsu UPC BIOS Extractor v2.0_a5'
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
from common.comp_efi import efi_decompress, is_efi_compressed
|
||||
from common.path_ops import make_dirs, path_suffixes
|
||||
from common.templates import BIOSUtility
|
||||
from common.text_ops import file_to_bytes
|
||||
|
||||
# Check if input is Fujitsu UPC image
|
||||
TITLE = 'Fujitsu UPC BIOS Extractor v3.0'
|
||||
|
||||
|
||||
def is_fujitsu_upc(in_file):
|
||||
""" Check if input is Fujitsu UPC image """
|
||||
|
||||
in_buffer = file_to_bytes(in_file)
|
||||
|
||||
is_ext = path_suffixes(in_file)[-1].upper() == '.UPC' if os.path.isfile(in_file) else True
|
||||
|
@ -30,15 +28,20 @@ def is_fujitsu_upc(in_file):
|
|||
|
||||
return is_ext and is_efi
|
||||
|
||||
# Parse & Extract Fujitsu UPC image
|
||||
|
||||
def fujitsu_upc_extract(input_file, extract_path, padding=0):
|
||||
""" Parse & Extract Fujitsu UPC image """
|
||||
|
||||
make_dirs(extract_path, delete=True)
|
||||
|
||||
image_base = os.path.basename(input_file)
|
||||
|
||||
image_name = image_base[:-4] if image_base.upper().endswith('.UPC') else image_base
|
||||
|
||||
image_path = os.path.join(extract_path, f'{image_name}.bin')
|
||||
|
||||
return efi_decompress(input_file, image_path, padding)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIOSUtility(TITLE, is_fujitsu_upc, fujitsu_upc_extract).run_utility()
|
||||
BIOSUtility(title=TITLE, check=is_fujitsu_upc, main=fujitsu_upc_extract).run_utility()
|
||||
|
|
|
@ -1,81 +1,92 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Insyde IFD Extract
|
||||
Insyde iFlash/iFdPacker Extractor
|
||||
Copyright (C) 2022 Plato Mavropoulos
|
||||
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'Insyde iFlash/iFdPacker Extractor v2.0_a11'
|
||||
|
||||
import os
|
||||
import sys
|
||||
import ctypes
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
import os
|
||||
|
||||
from common.comp_szip import is_szip_supported, szip_decompress
|
||||
from common.path_ops import get_path_files, make_dirs, safe_name, get_extract_path
|
||||
from common.path_ops import get_extract_path, get_path_files, make_dirs, safe_name
|
||||
from common.patterns import PAT_INSYDE_IFL, PAT_INSYDE_SFX
|
||||
from common.struct_ops import char, get_struct, uint32_t
|
||||
from common.struct_ops import Char, get_struct, UInt32
|
||||
from common.system import printer
|
||||
from common.templates import BIOSUtility
|
||||
from common.text_ops import file_to_bytes
|
||||
|
||||
TITLE = 'Insyde iFlash/iFdPacker Extractor v3.0'
|
||||
|
||||
|
||||
class IflashHeader(ctypes.LittleEndianStructure):
|
||||
""" Insyde iFlash Header """
|
||||
|
||||
_pack_ = 1
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
_fields_ = [
|
||||
('Signature', char*8), # 0x00 $_IFLASH
|
||||
('ImageTag', char*8), # 0x08
|
||||
('TotalSize', uint32_t), # 0x10 from header end
|
||||
('ImageSize', uint32_t), # 0x14 from header end
|
||||
('Signature', Char * 8), # 0x00 $_IFLASH
|
||||
('ImageTag', Char * 8), # 0x08
|
||||
('TotalSize', UInt32), # 0x10 from header end
|
||||
('ImageSize', UInt32), # 0x14 from header end
|
||||
# 0x18
|
||||
]
|
||||
|
||||
def _get_padd_len(self):
|
||||
def _get_padd_len(self) -> int:
|
||||
return self.TotalSize - self.ImageSize
|
||||
|
||||
def get_image_tag(self):
|
||||
def get_image_tag(self) -> str:
|
||||
""" Get Insyde iFlash image tag """
|
||||
|
||||
return self.ImageTag.decode('utf-8', 'ignore').strip('_')
|
||||
|
||||
def struct_print(self, p):
|
||||
printer(['Signature :', self.Signature.decode('utf-8')], p, False)
|
||||
printer(['Image Name:', self.get_image_tag()], p, False)
|
||||
printer(['Image Size:', f'0x{self.ImageSize:X}'], p, False)
|
||||
printer(['Total Size:', f'0x{self.TotalSize:X}'], p, False)
|
||||
printer(['Padd Size :', f'0x{self._get_padd_len():X}'], p, False)
|
||||
def struct_print(self, padd: int) -> None:
|
||||
""" Display structure information """
|
||||
|
||||
# Check if input is Insyde iFlash/iFdPacker Update image
|
||||
def is_insyde_ifd(input_file):
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
printer(['Signature :', self.Signature.decode('utf-8')], padd, False)
|
||||
printer(['Image Name:', self.get_image_tag()], padd, False)
|
||||
printer(['Image Size:', f'0x{self.ImageSize:X}'], padd, False)
|
||||
printer(['Total Size:', f'0x{self.TotalSize:X}'], padd, False)
|
||||
printer(['Padd Size :', f'0x{self._get_padd_len():X}'], padd, False)
|
||||
|
||||
is_ifl = bool(insyde_iflash_detect(input_buffer))
|
||||
|
||||
is_sfx = bool(PAT_INSYDE_SFX.search(input_buffer))
|
||||
def is_insyde_ifd(input_object: str | bytes | bytearray) -> bool:
|
||||
""" Check if input is Insyde iFlash/iFdPacker Update image """
|
||||
|
||||
input_buffer: bytes = file_to_bytes(input_object)
|
||||
|
||||
is_ifl: bool = bool(insyde_iflash_detect(input_buffer))
|
||||
|
||||
is_sfx: bool = bool(PAT_INSYDE_SFX.search(input_buffer))
|
||||
|
||||
return is_ifl or is_sfx
|
||||
|
||||
# Parse & Extract Insyde iFlash/iFdPacker Update images
|
||||
def insyde_ifd_extract(input_file, extract_path, padding=0):
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
||||
iflash_code = insyde_iflash_extract(input_buffer, extract_path, padding)
|
||||
def insyde_ifd_extract(input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
""" Parse & Extract Insyde iFlash/iFdPacker Update images """
|
||||
|
||||
ifdpack_path = os.path.join(extract_path, 'Insyde iFdPacker SFX')
|
||||
input_buffer: bytes = file_to_bytes(input_object)
|
||||
|
||||
ifdpack_code = insyde_packer_extract(input_buffer, ifdpack_path, padding)
|
||||
iflash_code: int = insyde_iflash_extract(input_buffer, extract_path, padding)
|
||||
|
||||
ifdpack_path: str = os.path.join(extract_path, 'Insyde iFdPacker SFX')
|
||||
|
||||
ifdpack_code: int = insyde_packer_extract(input_buffer, ifdpack_path, padding)
|
||||
|
||||
return iflash_code and ifdpack_code
|
||||
|
||||
# Detect Insyde iFlash Update image
|
||||
def insyde_iflash_detect(input_buffer):
|
||||
iflash_match_all = []
|
||||
iflash_match_nan = [0x0,0xFFFFFFFF]
|
||||
|
||||
def insyde_iflash_detect(input_buffer: bytes) -> list:
|
||||
""" Detect Insyde iFlash Update image """
|
||||
|
||||
iflash_match_all: list = []
|
||||
iflash_match_nan: list = [0x0, 0xFFFFFFFF]
|
||||
|
||||
for iflash_match in PAT_INSYDE_IFL.finditer(input_buffer):
|
||||
ifl_bgn = iflash_match.start()
|
||||
ifl_bgn: int = iflash_match.start()
|
||||
|
||||
if len(input_buffer[ifl_bgn:]) <= INS_IFL_LEN:
|
||||
continue
|
||||
|
@ -92,9 +103,11 @@ def insyde_iflash_detect(input_buffer):
|
|||
|
||||
return iflash_match_all
|
||||
|
||||
# Extract Insyde iFlash Update image
|
||||
def insyde_iflash_extract(input_buffer, extract_path, padding=0):
|
||||
insyde_iflash_all = insyde_iflash_detect(input_buffer)
|
||||
|
||||
def insyde_iflash_extract(input_buffer: bytes, extract_path: str, padding: int = 0) -> int:
|
||||
""" Extract Insyde iFlash Update image """
|
||||
|
||||
insyde_iflash_all: list = insyde_iflash_detect(input_buffer)
|
||||
|
||||
if not insyde_iflash_all:
|
||||
return 127
|
||||
|
@ -103,24 +116,24 @@ def insyde_iflash_extract(input_buffer, extract_path, padding=0):
|
|||
|
||||
make_dirs(extract_path, delete=True)
|
||||
|
||||
exit_codes = []
|
||||
exit_codes: list = []
|
||||
|
||||
for insyde_iflash in insyde_iflash_all:
|
||||
exit_code = 0
|
||||
exit_code: int = 0
|
||||
|
||||
ifl_bgn, ifl_hdr = insyde_iflash
|
||||
|
||||
img_bgn = ifl_bgn + INS_IFL_LEN
|
||||
img_end = img_bgn + ifl_hdr.ImageSize
|
||||
img_bin = input_buffer[img_bgn:img_end]
|
||||
img_bgn: int = ifl_bgn + INS_IFL_LEN
|
||||
img_end: int = img_bgn + ifl_hdr.ImageSize
|
||||
img_bin: bytes = input_buffer[img_bgn:img_end]
|
||||
|
||||
if len(img_bin) != ifl_hdr.ImageSize:
|
||||
exit_code = 1
|
||||
|
||||
img_val = [ifl_hdr.get_image_tag(), 'bin']
|
||||
img_val: list = [ifl_hdr.get_image_tag(), 'bin']
|
||||
img_tag, img_ext = INS_IFL_IMG.get(img_val[0], img_val)
|
||||
|
||||
img_name = f'{img_tag} [0x{img_bgn:08X}-0x{img_end:08X}]'
|
||||
img_name: str = f'{img_tag} [0x{img_bgn:08X}-0x{img_end:08X}]'
|
||||
|
||||
printer(f'{img_name}\n', padding + 4)
|
||||
|
||||
|
@ -129,9 +142,9 @@ def insyde_iflash_extract(input_buffer, extract_path, padding=0):
|
|||
if img_val == [img_tag, img_ext]:
|
||||
printer(f'Note: Detected new Insyde iFlash tag {img_tag}!', padding + 12, pause=True)
|
||||
|
||||
out_name = f'{img_name}.{img_ext}'
|
||||
out_name: str = f'{img_name}.{img_ext}'
|
||||
|
||||
out_path = os.path.join(extract_path, safe_name(out_name))
|
||||
out_path: str = os.path.join(extract_path, safe_name(out_name))
|
||||
|
||||
with open(out_path, 'wb') as out_image:
|
||||
out_image.write(img_bin)
|
||||
|
@ -142,8 +155,10 @@ def insyde_iflash_extract(input_buffer, extract_path, padding=0):
|
|||
|
||||
return sum(exit_codes)
|
||||
|
||||
# Extract Insyde iFdPacker 7-Zip SFX 7z Update image
|
||||
def insyde_packer_extract(input_buffer, extract_path, padding=0):
|
||||
|
||||
def insyde_packer_extract(input_buffer: bytes, extract_path: str, padding: int = 0) -> int:
|
||||
""" Extract Insyde iFdPacker 7-Zip SFX 7z Update image """
|
||||
|
||||
match_sfx = PAT_INSYDE_SFX.search(input_buffer)
|
||||
|
||||
if not match_sfx:
|
||||
|
@ -153,7 +168,7 @@ def insyde_packer_extract(input_buffer, extract_path, padding=0):
|
|||
|
||||
make_dirs(extract_path, delete=True)
|
||||
|
||||
sfx_buffer = bytearray(input_buffer[match_sfx.end() - 0x5:])
|
||||
sfx_buffer: bytearray = bytearray(input_buffer[match_sfx.end() - 0x5:])
|
||||
|
||||
if sfx_buffer[:0x5] == b'\x6E\xF4\x79\x5F\x4E':
|
||||
printer('Detected Insyde iFdPacker > 7-Zip SFX > Obfuscation!', padding + 4)
|
||||
|
@ -167,9 +182,10 @@ def insyde_packer_extract(input_buffer, extract_path, padding=0):
|
|||
|
||||
if bytes(INS_SFX_PWD, 'utf-16le') in input_buffer[:match_sfx.start()]:
|
||||
printer('Detected Insyde iFdPacker > 7-Zip SFX > Password!', padding + 8)
|
||||
|
||||
printer(INS_SFX_PWD, padding + 12)
|
||||
|
||||
sfx_path = os.path.join(extract_path, 'Insyde_iFdPacker_SFX.7z')
|
||||
sfx_path: str = os.path.join(extract_path, 'Insyde_iFdPacker_SFX.7z')
|
||||
|
||||
with open(sfx_path, 'wb') as sfx_file:
|
||||
sfx_file.write(sfx_buffer)
|
||||
|
@ -189,17 +205,18 @@ def insyde_packer_extract(input_buffer, extract_path, padding=0):
|
|||
if is_insyde_ifd(sfx_file):
|
||||
printer(f'{os.path.basename(sfx_file)}', padding + 12)
|
||||
|
||||
ifd_code = insyde_ifd_extract(sfx_file, get_extract_path(sfx_file), padding + 16)
|
||||
ifd_code: int = insyde_ifd_extract(sfx_file, get_extract_path(sfx_file), padding + 16)
|
||||
|
||||
exit_codes.append(ifd_code)
|
||||
|
||||
return sum(exit_codes)
|
||||
|
||||
|
||||
# Insyde iFdPacker known 7-Zip SFX Password
|
||||
INS_SFX_PWD = 'Y`t~i!L@i#t$U%h^s7A*l(f)E-d=y+S_n?i'
|
||||
INS_SFX_PWD: 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 = {
|
||||
INS_IFL_IMG: dict = {
|
||||
'BIOSCER': ['Certificate', 'bin'],
|
||||
'BIOSCR2': ['Certificate 2nd', 'bin'],
|
||||
'BIOSIMG': ['BIOS-UEFI', 'bin'],
|
||||
|
@ -211,7 +228,7 @@ INS_IFL_IMG = {
|
|||
}
|
||||
|
||||
# Get common ctypes Structure Sizes
|
||||
INS_IFL_LEN = ctypes.sizeof(IflashHeader)
|
||||
INS_IFL_LEN: int = ctypes.sizeof(IflashHeader)
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIOSUtility(TITLE, is_insyde_ifd, insyde_ifd_extract).run_utility()
|
||||
BIOSUtility(title=TITLE, check=is_insyde_ifd, main=insyde_ifd_extract).run_utility()
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2019-2022 Plato Mavropoulos
|
||||
Copyright (c) 2019-2024 Plato Mavropoulos
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Panasonic BIOS Extract
|
||||
Panasonic BIOS Package Extractor
|
||||
Copyright (C) 2018-2022 Plato Mavropoulos
|
||||
Copyright (C) 2018-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'Panasonic BIOS Package Extractor v2.0_a10'
|
||||
|
||||
import os
|
||||
import io
|
||||
import sys
|
||||
import lznt1
|
||||
import logging
|
||||
import os
|
||||
|
||||
import pefile
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
from dissect.util.compression import lznt1
|
||||
|
||||
from common.comp_szip import is_szip_supported, szip_decompress
|
||||
from common.path_ops import get_path_files, make_dirs, path_stem, safe_name
|
||||
|
@ -28,16 +25,20 @@ from common.text_ops import file_to_bytes
|
|||
|
||||
from AMI_PFAT_Extract import is_ami_pfat, parse_pfat_file
|
||||
|
||||
# Check if input is Panasonic BIOS Package PE
|
||||
TITLE = 'Panasonic BIOS Package Extractor v3.0'
|
||||
|
||||
|
||||
def is_panasonic_pkg(in_file):
|
||||
""" Check if input is Panasonic BIOS Package PE """
|
||||
|
||||
in_buffer = file_to_bytes(in_file)
|
||||
|
||||
pe_file = get_pe_file(in_buffer, fast=True)
|
||||
pe_file = get_pe_file(in_buffer, silent=True)
|
||||
|
||||
if not pe_file:
|
||||
return False
|
||||
|
||||
pe_info = get_pe_info(pe_file)
|
||||
pe_info = get_pe_info(pe_file, silent=True)
|
||||
|
||||
if not pe_info:
|
||||
return False
|
||||
|
@ -50,14 +51,18 @@ def is_panasonic_pkg(in_file):
|
|||
|
||||
return True
|
||||
|
||||
# Search and Extract Panasonic BIOS Package PE CAB archive
|
||||
|
||||
def panasonic_cab_extract(buffer, extract_path, padding=0):
|
||||
""" Search and Extract Panasonic BIOS Package PE CAB archive """
|
||||
|
||||
pe_path, pe_file, pe_info = [None] * 3
|
||||
|
||||
cab_bgn = PAT_MICROSOFT_CAB.search(buffer).start()
|
||||
cab_len = int.from_bytes(buffer[cab_bgn + 0x8:cab_bgn + 0xC], 'little')
|
||||
cab_end = cab_bgn + cab_len
|
||||
|
||||
cab_bin = buffer[cab_bgn:cab_end]
|
||||
|
||||
cab_tag = f'[0x{cab_bgn:06X}-0x{cab_end:06X}]'
|
||||
|
||||
cab_path = os.path.join(extract_path, f'CAB_{cab_tag}.cab')
|
||||
|
@ -76,19 +81,24 @@ def panasonic_cab_extract(buffer, extract_path, padding=0):
|
|||
return pe_path, pe_file, pe_info
|
||||
|
||||
for file_path in get_path_files(extract_path):
|
||||
pe_file = get_pe_file(file_path, fast=True)
|
||||
pe_file = get_pe_file(file_path, padding, silent=True)
|
||||
|
||||
if pe_file:
|
||||
pe_info = get_pe_info(pe_file)
|
||||
pe_info = get_pe_info(pe_file, padding, silent=True)
|
||||
|
||||
if pe_info.get(b'FileDescription', b'').upper() == b'BIOS UPDATE':
|
||||
pe_path = file_path
|
||||
|
||||
break
|
||||
else:
|
||||
return pe_path, pe_file, pe_info
|
||||
|
||||
return pe_path, pe_file, pe_info
|
||||
|
||||
# Extract & Decompress Panasonic BIOS Update PE RCDATA (LZNT1)
|
||||
|
||||
def panasonic_res_extract(pe_name, pe_file, extract_path, padding=0):
|
||||
""" Extract & Decompress Panasonic BIOS Update PE RCDATA (LZNT1) """
|
||||
|
||||
is_rcdata = False
|
||||
|
||||
# When fast_load is used, IMAGE_DIRECTORY_ENTRY_RESOURCE must be parsed prior to RCDATA Directories
|
||||
|
@ -98,12 +108,16 @@ def panasonic_res_extract(pe_name, pe_file, extract_path, padding=0):
|
|||
for entry in pe_file.DIRECTORY_ENTRY_RESOURCE.entries:
|
||||
if entry.struct.name == 'IMAGE_RESOURCE_DIRECTORY_ENTRY' and entry.struct.Id == 0xA:
|
||||
is_rcdata = True
|
||||
|
||||
for resource in entry.directory.entries:
|
||||
res_bgn = resource.directory.entries[0].data.struct.OffsetToData
|
||||
res_len = resource.directory.entries[0].data.struct.Size
|
||||
res_end = res_bgn + res_len
|
||||
|
||||
res_bin = pe_file.get_data(res_bgn, res_len)
|
||||
|
||||
res_tag = f'{pe_name} [0x{res_bgn:06X}-0x{res_end:06X}]'
|
||||
|
||||
res_out = os.path.join(extract_path, f'{res_tag}')
|
||||
|
||||
printer(res_tag, padding + 4)
|
||||
|
@ -111,8 +125,13 @@ def panasonic_res_extract(pe_name, pe_file, extract_path, padding=0):
|
|||
try:
|
||||
res_raw = lznt1.decompress(res_bin[0x8:])
|
||||
|
||||
printer('Succesfull LZNT1 decompression via lznt1!', padding + 8)
|
||||
except Exception:
|
||||
if len(res_raw) != int.from_bytes(res_bin[0x4:0x8], 'little'):
|
||||
raise ValueError('LZNT1_DECOMPRESS_BAD_SIZE')
|
||||
|
||||
printer('Succesfull LZNT1 decompression via Dissect!', padding + 8)
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
logging.debug('Error: LZNT1 decompression of %s failed: %s', res_tag, error)
|
||||
|
||||
res_raw = res_bin
|
||||
|
||||
printer('Succesfull PE Resource extraction!', padding + 8)
|
||||
|
@ -132,8 +151,10 @@ def panasonic_res_extract(pe_name, pe_file, extract_path, padding=0):
|
|||
|
||||
if res_ext == 'txt':
|
||||
printer(new_line=False)
|
||||
|
||||
for line in io.BytesIO(res_raw).readlines():
|
||||
line_text = line.decode('utf-8', 'ignore').rstrip()
|
||||
|
||||
printer(line_text, padding + 12, new_line=False)
|
||||
|
||||
with open(f'{res_out}.{res_ext}', 'wb') as out_file:
|
||||
|
@ -141,15 +162,22 @@ def panasonic_res_extract(pe_name, pe_file, extract_path, padding=0):
|
|||
|
||||
return is_rcdata
|
||||
|
||||
# Extract Panasonic BIOS Update PE Data when RCDATA is not available
|
||||
|
||||
def panasonic_img_extract(pe_name, pe_path, pe_file, extract_path, padding=0):
|
||||
""" Extract Panasonic BIOS Update PE Data when RCDATA is not available """
|
||||
|
||||
pe_data = file_to_bytes(pe_path)
|
||||
|
||||
sec_bgn = pe_file.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_SECURITY']].VirtualAddress
|
||||
sec_bgn = pe_file.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY[
|
||||
'IMAGE_DIRECTORY_ENTRY_SECURITY']].VirtualAddress
|
||||
|
||||
img_bgn = pe_file.OPTIONAL_HEADER.BaseOfData + pe_file.OPTIONAL_HEADER.SizeOfInitializedData
|
||||
img_end = sec_bgn or len(pe_data)
|
||||
|
||||
img_bin = pe_data[img_bgn:img_end]
|
||||
|
||||
img_tag = f'{pe_name} [0x{img_bgn:X}-0x{img_end:X}]'
|
||||
|
||||
img_out = os.path.join(extract_path, f'{img_tag}.bin')
|
||||
|
||||
printer(img_tag, padding + 4)
|
||||
|
@ -161,18 +189,20 @@ def panasonic_img_extract(pe_name, pe_path, pe_file, extract_path, padding=0):
|
|||
|
||||
return bool(img_bin)
|
||||
|
||||
# Parse & Extract Panasonic BIOS Package PE
|
||||
|
||||
def panasonic_pkg_extract(input_file, extract_path, padding=0):
|
||||
""" Parse & Extract Panasonic BIOS Package PE """
|
||||
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
||||
make_dirs(extract_path, delete=True)
|
||||
|
||||
pkg_pe_file = get_pe_file(input_buffer, fast=True)
|
||||
pkg_pe_file = get_pe_file(input_buffer, padding)
|
||||
|
||||
if not pkg_pe_file:
|
||||
return 2
|
||||
|
||||
pkg_pe_info = get_pe_info(pkg_pe_file)
|
||||
pkg_pe_info = get_pe_info(pkg_pe_file, padding)
|
||||
|
||||
if not pkg_pe_info:
|
||||
return 3
|
||||
|
@ -205,5 +235,6 @@ def panasonic_pkg_extract(input_file, extract_path, padding=0):
|
|||
|
||||
return 0 if is_upd_res or is_upd_img else 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIOSUtility(TITLE, is_panasonic_pkg, panasonic_pkg_extract).run_utility()
|
||||
BIOSUtility(title=TITLE, check=is_panasonic_pkg, main=panasonic_pkg_extract).run_utility()
|
||||
|
|
|
@ -1,55 +1,60 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Phoenix TDK Extract
|
||||
Phoenix TDK Packer Extractor
|
||||
Copyright (C) 2021-2022 Plato Mavropoulos
|
||||
Copyright (C) 2021-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'Phoenix TDK Packer Extractor v2.0_a10'
|
||||
|
||||
import os
|
||||
import sys
|
||||
import lzma
|
||||
import ctypes
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
import logging
|
||||
import lzma
|
||||
import os
|
||||
|
||||
from common.path_ops import make_dirs, safe_name
|
||||
from common.pe_ops import get_pe_file, get_pe_info
|
||||
from common.patterns import PAT_MICROSOFT_MZ, PAT_MICROSOFT_PE, PAT_PHOENIX_TDK
|
||||
from common.struct_ops import char, get_struct, uint32_t
|
||||
from common.struct_ops import Char, get_struct, UInt32
|
||||
from common.system import printer
|
||||
from common.templates import BIOSUtility
|
||||
from common.text_ops import file_to_bytes
|
||||
|
||||
TITLE = 'Phoenix TDK Packer Extractor v3.0'
|
||||
|
||||
|
||||
class PhoenixTdkHeader(ctypes.LittleEndianStructure):
|
||||
""" Phoenix TDK Header """
|
||||
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Tag', char*8), # 0x00
|
||||
('Size', uint32_t), # 0x08
|
||||
('Count', uint32_t), # 0x0C
|
||||
('Tag', Char * 8), # 0x00
|
||||
('Size', UInt32), # 0x08
|
||||
('Count', UInt32), # 0x0C
|
||||
# 0x10
|
||||
]
|
||||
|
||||
def _get_tag(self):
|
||||
return self.Tag.decode('utf-8', 'ignore').strip()
|
||||
|
||||
def struct_print(self, p):
|
||||
printer(['Tag :', self._get_tag()], p, False)
|
||||
printer(['Size :', f'0x{self.Size:X}'], p, False)
|
||||
printer(['Entries:', self.Count], p, False)
|
||||
def struct_print(self, padd):
|
||||
""" Display structure information """
|
||||
|
||||
printer(['Tag :', self._get_tag()], padd, False)
|
||||
printer(['Size :', f'0x{self.Size:X}'], padd, False)
|
||||
printer(['Entries:', self.Count], padd, False)
|
||||
|
||||
|
||||
class PhoenixTdkEntry(ctypes.LittleEndianStructure):
|
||||
""" Phoenix TDK Entry """
|
||||
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Name', char*256), # 0x000
|
||||
('Offset', uint32_t), # 0x100
|
||||
('Size', uint32_t), # 0x104
|
||||
('Compressed', uint32_t), # 0x108
|
||||
('Reserved', uint32_t), # 0x10C
|
||||
('Name', Char * 256), # 0x000
|
||||
('Offset', UInt32), # 0x100
|
||||
('Size', UInt32), # 0x104
|
||||
('Compressed', UInt32), # 0x108
|
||||
('Reserved', UInt32), # 0x10C
|
||||
# 0x110
|
||||
]
|
||||
|
||||
|
@ -57,26 +62,37 @@ class PhoenixTdkEntry(ctypes.LittleEndianStructure):
|
|||
|
||||
def __init__(self, mz_base, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.Base = mz_base
|
||||
|
||||
self.mz_base = mz_base
|
||||
|
||||
def get_name(self):
|
||||
""" Get TDK Entry decoded name """
|
||||
|
||||
return self.Name.decode('utf-8', 'replace').strip()
|
||||
|
||||
def get_offset(self):
|
||||
return self.Base + self.Offset
|
||||
""" Get TDK Entry absolute offset """
|
||||
|
||||
return self.mz_base + self.Offset
|
||||
|
||||
def get_compression(self):
|
||||
""" Get TDK Entry compression type """
|
||||
|
||||
return self.COMP.get(self.Compressed, f'Unknown ({self.Compressed})')
|
||||
|
||||
def struct_print(self, p):
|
||||
printer(['Name :', self.get_name()], p, False)
|
||||
printer(['Offset :', f'0x{self.get_offset():X}'], p, False)
|
||||
printer(['Size :', f'0x{self.Size:X}'], p, False)
|
||||
printer(['Compression:', self.get_compression()], p, False)
|
||||
printer(['Reserved :', f'0x{self.Reserved:X}'], p, False)
|
||||
def struct_print(self, padd):
|
||||
""" Display structure information """
|
||||
|
||||
printer(['Name :', self.get_name()], padd, False)
|
||||
printer(['Offset :', f'0x{self.get_offset():X}'], padd, False)
|
||||
printer(['Size :', f'0x{self.Size:X}'], padd, False)
|
||||
printer(['Compression:', self.get_compression()], padd, False)
|
||||
printer(['Reserved :', f'0x{self.Reserved:X}'], padd, False)
|
||||
|
||||
|
||||
# Get Phoenix TDK Executable (MZ) Base Offset
|
||||
def get_tdk_base(in_buffer, pack_off):
|
||||
""" Get Phoenix TDK Executable (MZ) Base Offset """
|
||||
|
||||
tdk_base_off = None # Initialize Phoenix TDK Base MZ Offset
|
||||
|
||||
# Scan input file for all Microsoft executable patterns (MZ) before TDK Header Offset
|
||||
|
@ -90,8 +106,8 @@ def get_tdk_base(in_buffer, pack_off):
|
|||
mz_ord = [mz_all[0]] + list(reversed(mz_all[1:]))
|
||||
|
||||
# Parse each detected MZ
|
||||
for mz in mz_ord:
|
||||
mz_off = mz.start()
|
||||
for mz_match in mz_ord:
|
||||
mz_off = mz_match.start()
|
||||
|
||||
# MZ (DOS) > PE (NT) image Offset is found at offset 0x3C-0x40 relative to MZ base
|
||||
pe_off = mz_off + int.from_bytes(in_buffer[mz_off + 0x3C:mz_off + 0x40], 'little')
|
||||
|
@ -104,15 +120,17 @@ def get_tdk_base(in_buffer, pack_off):
|
|||
if PAT_MICROSOFT_PE.search(in_buffer[pe_off:pe_off + 0x4]):
|
||||
try:
|
||||
# Parse detected MZ > PE > Image, quickly (fast_load)
|
||||
pe_file = get_pe_file(in_buffer[mz_off:], fast=True)
|
||||
pe_file = get_pe_file(in_buffer[mz_off:], silent=True)
|
||||
|
||||
# Parse detected MZ > PE > Info
|
||||
pe_info = get_pe_info(pe_file)
|
||||
pe_info = get_pe_info(pe_file, silent=True)
|
||||
|
||||
# Parse detected MZ > PE > Info > Product Name
|
||||
pe_name = pe_info.get(b'ProductName', b'')
|
||||
except Exception:
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
# Any error means no MZ > PE > Info > Product Name
|
||||
logging.debug('Error: Invalid potential MZ > PE match at 0x%X: %s', pe_off, error)
|
||||
|
||||
pe_name = b''
|
||||
|
||||
# Check for valid Phoenix TDK Packer PE > Product Name
|
||||
|
@ -130,8 +148,10 @@ def get_tdk_base(in_buffer, pack_off):
|
|||
|
||||
return tdk_base_off
|
||||
|
||||
# Scan input buffer for valid Phoenix TDK image
|
||||
|
||||
def get_phoenix_tdk(in_buffer):
|
||||
""" Scan input buffer for valid Phoenix TDK image """
|
||||
|
||||
# Scan input buffer for Phoenix TDK pattern
|
||||
tdk_match = PAT_PHOENIX_TDK.search(in_buffer)
|
||||
|
||||
|
@ -146,14 +166,18 @@ def get_phoenix_tdk(in_buffer):
|
|||
|
||||
return tdk_base_off, tdk_pack_off
|
||||
|
||||
# Check if input contains valid Phoenix TDK image
|
||||
|
||||
def is_phoenix_tdk(in_file):
|
||||
""" Check if input contains valid Phoenix TDK image """
|
||||
|
||||
buffer = file_to_bytes(in_file)
|
||||
|
||||
return bool(get_phoenix_tdk(buffer)[1] is not None)
|
||||
|
||||
# Parse & Extract Phoenix Tools Development Kit (TDK) Packer
|
||||
|
||||
def phoenix_tdk_extract(input_file, extract_path, padding=0):
|
||||
""" Parse & Extract Phoenix Tools Development Kit (TDK) Packer """
|
||||
|
||||
exit_code = 0
|
||||
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
@ -169,11 +193,13 @@ def phoenix_tdk_extract(input_file, extract_path, padding=0):
|
|||
|
||||
# Print TDK Header structure info
|
||||
printer('Phoenix TDK Header:\n', padding + 4)
|
||||
|
||||
tdk_hdr.struct_print(padding + 8)
|
||||
|
||||
# Check if reported TDK Header Size matches manual TDK Entry Count calculation
|
||||
if tdk_hdr.Size != TDK_HDR_LEN + TDK_DUMMY_LEN + tdk_hdr.Count * TDK_MOD_LEN:
|
||||
printer('Error: Phoenix TDK Header Size & Entry Count mismatch!\n', padding + 8, pause=True)
|
||||
|
||||
exit_code = 1
|
||||
|
||||
# Store TDK Entries offset after the placeholder data
|
||||
|
@ -186,6 +212,7 @@ def phoenix_tdk_extract(input_file, extract_path, padding=0):
|
|||
|
||||
# Print TDK Entry structure info
|
||||
printer(f'Phoenix TDK Entry ({entry_index + 1}/{tdk_hdr.Count}):\n', padding + 8)
|
||||
|
||||
tdk_mod.struct_print(padding + 12)
|
||||
|
||||
# Get TDK Entry raw data Offset (TDK Base + Entry Offset)
|
||||
|
@ -194,6 +221,7 @@ def phoenix_tdk_extract(input_file, extract_path, padding=0):
|
|||
# Check if TDK Entry raw data Offset is valid
|
||||
if mod_off >= len(input_buffer):
|
||||
printer('Error: Phoenix TDK Entry > Offset is out of bounds!\n', padding + 12, pause=True)
|
||||
|
||||
exit_code = 2
|
||||
|
||||
# Store TDK Entry raw data (relative to TDK Base, not TDK Header)
|
||||
|
@ -202,19 +230,22 @@ def phoenix_tdk_extract(input_file, extract_path, padding=0):
|
|||
# Check if TDK Entry raw data is complete
|
||||
if len(mod_data) != tdk_mod.Size:
|
||||
printer('Error: Phoenix TDK Entry > Data is truncated!\n', padding + 12, pause=True)
|
||||
|
||||
exit_code = 3
|
||||
|
||||
# Check if TDK Entry Reserved is present
|
||||
if tdk_mod.Reserved:
|
||||
printer('Error: Phoenix TDK Entry > Reserved is not empty!\n', padding + 12, pause=True)
|
||||
|
||||
exit_code = 4
|
||||
|
||||
# Decompress TDK Entry raw data, when applicable (i.e. LZMA)
|
||||
if tdk_mod.get_compression() == 'LZMA':
|
||||
try:
|
||||
mod_data = lzma.LZMADecompressor().decompress(mod_data)
|
||||
except Exception:
|
||||
printer('Error: Phoenix TDK Entry > LZMA decompression failed!\n', padding + 12, pause=True)
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
printer(f'Error: Phoenix TDK Entry > LZMA decompression failed: {error}!\n', padding + 12, pause=True)
|
||||
|
||||
exit_code = 5
|
||||
|
||||
# Generate TDK Entry file name, avoid crash if Entry data is bad
|
||||
|
@ -224,7 +255,8 @@ def phoenix_tdk_extract(input_file, extract_path, padding=0):
|
|||
mod_file = os.path.join(extract_path, safe_name(mod_name))
|
||||
|
||||
# Account for potential duplicate file names
|
||||
if os.path.isfile(mod_file): mod_file += f'_{entry_index + 1:02d}'
|
||||
if os.path.isfile(mod_file):
|
||||
mod_file += f'_{entry_index + 1:02d}'
|
||||
|
||||
# Save TDK Entry data to output file
|
||||
with open(mod_file, 'wb') as out_file:
|
||||
|
@ -232,6 +264,7 @@ def phoenix_tdk_extract(input_file, extract_path, padding=0):
|
|||
|
||||
return exit_code
|
||||
|
||||
|
||||
# Get ctypes Structure Sizes
|
||||
TDK_HDR_LEN = ctypes.sizeof(PhoenixTdkHeader)
|
||||
TDK_MOD_LEN = ctypes.sizeof(PhoenixTdkEntry)
|
||||
|
@ -240,4 +273,4 @@ TDK_MOD_LEN = ctypes.sizeof(PhoenixTdkEntry)
|
|||
TDK_DUMMY_LEN = 0x200
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIOSUtility(TITLE, is_phoenix_tdk, phoenix_tdk_extract).run_utility()
|
||||
BIOSUtility(title=TITLE, check=is_phoenix_tdk, main=phoenix_tdk_extract).run_utility()
|
||||
|
|
|
@ -1,19 +1,14 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Portwell EFI Extract
|
||||
Portwell EFI Update Extractor
|
||||
Copyright (C) 2021-2022 Plato Mavropoulos
|
||||
Copyright (C) 2021-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'Portwell EFI Update Extractor v2.0_a12'
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
from common.comp_efi import efi_decompress, is_efi_compressed
|
||||
from common.path_ops import make_dirs, safe_name
|
||||
|
@ -23,6 +18,8 @@ from common.system import printer
|
|||
from common.templates import BIOSUtility
|
||||
from common.text_ops import file_to_bytes
|
||||
|
||||
TITLE = 'Portwell EFI Update Extractor v3.0'
|
||||
|
||||
FILE_NAMES = {
|
||||
0: 'Flash.efi',
|
||||
1: 'Fparts.txt',
|
||||
|
@ -31,13 +28,17 @@ FILE_NAMES = {
|
|||
4: 'SaveDmiData.efi'
|
||||
}
|
||||
|
||||
# Check if input is Portwell EFI executable
|
||||
|
||||
def is_portwell_efi(in_file):
|
||||
""" Check if input is Portwell EFI executable """
|
||||
|
||||
in_buffer = file_to_bytes(in_file)
|
||||
|
||||
try:
|
||||
pe_buffer = get_portwell_pe(in_buffer)[1]
|
||||
except Exception:
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
logging.debug('Error: Could not check if input is Portwell EFI executable: %s', error)
|
||||
|
||||
pe_buffer = b''
|
||||
|
||||
is_mz = PAT_MICROSOFT_MZ.search(in_buffer[:0x2]) # EFI images start with PE Header MZ
|
||||
|
@ -46,16 +47,20 @@ def is_portwell_efi(in_file):
|
|||
|
||||
return bool(is_mz and is_uu)
|
||||
|
||||
# Get PE of Portwell EFI executable
|
||||
|
||||
def get_portwell_pe(in_buffer):
|
||||
pe_file = get_pe_file(in_buffer, fast=True) # Analyze EFI Portable Executable (PE)
|
||||
""" Get PE of Portwell EFI executable """
|
||||
|
||||
pe_file = get_pe_file(in_buffer, silent=True) # Analyze EFI Portable Executable (PE)
|
||||
|
||||
pe_data = in_buffer[pe_file.OPTIONAL_HEADER.SizeOfImage:] # Skip EFI executable (pylint: disable=E1101)
|
||||
|
||||
return pe_file, pe_data
|
||||
|
||||
# Parse & Extract Portwell UEFI Unpacker
|
||||
|
||||
def portwell_efi_extract(input_file, extract_path, padding=0):
|
||||
""" Parse & Extract Portwell UEFI Unpacker """
|
||||
|
||||
efi_files = [] # Initialize EFI Payload file chunks
|
||||
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
@ -70,15 +75,19 @@ def portwell_efi_extract(input_file, extract_path, padding=0):
|
|||
|
||||
# Split EFI Payload into <UU> file chunks
|
||||
efi_list = list(PAT_PORTWELL_EFI.finditer(pe_data))
|
||||
|
||||
for idx, val in enumerate(efi_list):
|
||||
efi_bgn = val.end()
|
||||
efi_end = len(pe_data) if idx == len(efi_list) - 1 else efi_list[idx + 1].start()
|
||||
|
||||
efi_files.append(pe_data[efi_bgn:efi_end])
|
||||
|
||||
parse_efi_files(extract_path, efi_files, padding)
|
||||
|
||||
# Get Portwell UEFI Unpacker tag
|
||||
|
||||
def get_unpacker_tag(input_buffer, pe_file):
|
||||
""" Get Portwell UEFI Unpacker tag """
|
||||
|
||||
unpacker_tag_txt = 'UEFI Unpacker'
|
||||
|
||||
for pe_section in pe_file.sections:
|
||||
|
@ -92,8 +101,10 @@ def get_unpacker_tag(input_buffer, pe_file):
|
|||
|
||||
# Search .data for UEFI Unpacker tag
|
||||
unpacker_tag_bgn = pe_data_txt.find(unpacker_tag_txt)
|
||||
|
||||
if unpacker_tag_bgn != -1:
|
||||
unpacker_tag_len = pe_data_txt[unpacker_tag_bgn:].find('=')
|
||||
|
||||
if unpacker_tag_len != -1:
|
||||
unpacker_tag_end = unpacker_tag_bgn + unpacker_tag_len
|
||||
unpacker_tag_raw = pe_data_txt[unpacker_tag_bgn:unpacker_tag_end]
|
||||
|
@ -105,8 +116,10 @@ def get_unpacker_tag(input_buffer, pe_file):
|
|||
|
||||
return unpacker_tag_txt
|
||||
|
||||
# Process Portwell UEFI Unpacker payload files
|
||||
|
||||
def parse_efi_files(extract_path, efi_files, padding):
|
||||
""" Process Portwell UEFI Unpacker payload files """
|
||||
|
||||
for file_index, file_data in enumerate(efi_files):
|
||||
if file_data in (b'', b'NULL'):
|
||||
continue # Skip empty/unused files
|
||||
|
@ -116,7 +129,7 @@ def parse_efi_files(extract_path, efi_files, padding):
|
|||
printer(f'[{file_index}] {file_name}', padding + 4) # Print EFI file name, indicate progress
|
||||
|
||||
if file_name.startswith('Unknown_'):
|
||||
printer(f'Note: Detected new Portwell EFI file ID {file_index}!', padding + 8, pause=True) # Report new EFI files
|
||||
printer(f'Note: Detected new Portwell EFI file ID {file_index}!', padding + 8, pause=True)
|
||||
|
||||
file_path = os.path.join(extract_path, safe_name(file_name)) # Store EFI file output path
|
||||
|
||||
|
@ -132,5 +145,6 @@ def parse_efi_files(extract_path, efi_files, padding):
|
|||
if efi_decompress(comp_fname, file_path, padding + 8) == 0:
|
||||
os.remove(comp_fname) # Successful decompression, delete compressed file
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIOSUtility(TITLE, is_portwell_efi, portwell_efi_extract).run_utility()
|
||||
BIOSUtility(title=TITLE, check=is_portwell_efi, main=portwell_efi_extract).run_utility()
|
||||
|
|
34
README.md
34
README.md
|
@ -42,7 +42,7 @@ You can either Drag & Drop or manually enter AMI BIOS Guard (PFAT) image file(s)
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
|
@ -75,7 +75,7 @@ You can either Drag & Drop or manually enter AMI UCP Update executable file(s).
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
|
@ -112,7 +112,7 @@ You can either Drag & Drop or manually enter Apple EFI IM4P file(s). Optional ar
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
|
@ -143,7 +143,7 @@ You can either Drag & Drop or manually enter Apple EFI image file(s). Optional a
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
|
@ -176,7 +176,7 @@ You can either Drag & Drop or manually enter Apple EFI PKG package file(s). Opti
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
|
@ -208,7 +208,7 @@ You can either Drag & Drop or manually enter Apple EFI PBZX image file(s). Optio
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
|
@ -240,7 +240,7 @@ You can either Drag & Drop or manually enter Award BIOS image file(s). Optional
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
|
@ -274,7 +274,7 @@ You can either Drag & Drop or manually enter Dell PFS Update images(s). Optional
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
|
@ -306,7 +306,7 @@ You can either Drag & Drop or manually enter Fujitsu SFX BIOS image file(s). Opt
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
|
@ -338,7 +338,7 @@ You can either Drag & Drop or manually enter Fujitsu UPC BIOS image file(s). Opt
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
|
@ -370,7 +370,7 @@ You can either Drag & Drop or manually enter Insyde iFlash/iFdPacker Update imag
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
|
@ -400,14 +400,14 @@ You can either Drag & Drop or manually enter Panasonic BIOS Package executable f
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
To run the utility, you must have the following 3rd party Python modules installed:
|
||||
|
||||
* [pefile](https://pypi.org/project/pefile/)
|
||||
* [lznt1](https://pypi.org/project/lznt1/)
|
||||
* [dissect.util](https://pypi.org/project/dissect.util/)
|
||||
|
||||
Moreover, you must have the following 3rd party tool at the "external" project directory:
|
||||
|
||||
|
@ -437,7 +437,7 @@ You can either Drag & Drop or manually enter Phoenix Tools Development Kit (TDK)
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
|
@ -469,7 +469,7 @@ You can either Drag & Drop or manually enter Portwell UEFI Unpacker EFI executab
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
|
@ -507,7 +507,7 @@ You can either Drag & Drop or manually enter Toshiba BIOS COM image file(s). Opt
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
|
@ -539,7 +539,7 @@ You can either Drag & Drop or manually enter VAIO Packaging Manager executable f
|
|||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 or newer support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
|
|
|
@ -1,21 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Toshiba COM Extract
|
||||
Toshiba BIOS COM Extractor
|
||||
Copyright (C) 2018-2022 Plato Mavropoulos
|
||||
Copyright (C) 2018-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'Toshiba BIOS COM Extractor v2.0_a4'
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
from common.externals import get_comextract_path
|
||||
from common.path_ops import make_dirs, path_stem, path_suffixes
|
||||
from common.patterns import PAT_TOSHIBA_COM
|
||||
|
@ -23,8 +17,12 @@ from common.system import printer
|
|||
from common.templates import BIOSUtility
|
||||
from common.text_ops import file_to_bytes
|
||||
|
||||
# Check if input is Toshiba BIOS COM image
|
||||
TITLE = 'Toshiba BIOS COM Extractor v3.0'
|
||||
|
||||
|
||||
def is_toshiba_com(in_file):
|
||||
""" Check if input is Toshiba BIOS COM image """
|
||||
|
||||
buffer = file_to_bytes(in_file)
|
||||
|
||||
is_ext = path_suffixes(in_file)[-1].upper() == '.COM' if os.path.isfile(in_file) else True
|
||||
|
@ -33,8 +31,10 @@ def is_toshiba_com(in_file):
|
|||
|
||||
return is_ext and is_com
|
||||
|
||||
# Parse & Extract Toshiba BIOS COM image
|
||||
|
||||
def toshiba_com_extract(input_file, extract_path, padding=0):
|
||||
""" Parse & Extract Toshiba BIOS COM image """
|
||||
|
||||
if not os.path.isfile(input_file):
|
||||
printer('Error: Could not find input file path!', padding)
|
||||
|
||||
|
@ -43,15 +43,16 @@ def toshiba_com_extract(input_file, extract_path, padding=0):
|
|||
make_dirs(extract_path, delete=True)
|
||||
|
||||
output_name = path_stem(input_file)
|
||||
|
||||
output_file = os.path.join(extract_path, f'{output_name}.bin')
|
||||
|
||||
try:
|
||||
subprocess.run([get_comextract_path(), input_file, output_file], check=True, stdout=subprocess.DEVNULL)
|
||||
|
||||
if not os.path.isfile(output_file):
|
||||
raise Exception('EXTRACT_FILE_MISSING')
|
||||
except Exception:
|
||||
printer(f'Error: ToshibaComExtractor could not extract file {input_file}!', padding)
|
||||
raise ValueError('EXTRACT_FILE_MISSING')
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
printer(f'Error: ToshibaComExtractor could not extract file {input_file}: {error}!', padding)
|
||||
|
||||
return 2
|
||||
|
||||
|
@ -59,5 +60,6 @@ def toshiba_com_extract(input_file, extract_path, padding=0):
|
|||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIOSUtility(TITLE, is_toshiba_com, toshiba_com_extract).run_utility()
|
||||
BIOSUtility(title=TITLE, check=is_toshiba_com, main=toshiba_com_extract).run_utility()
|
||||
|
|
|
@ -1,19 +1,13 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
VAIO Package Extractor
|
||||
VAIO Packaging Manager Extractor
|
||||
Copyright (C) 2019-2022 Plato Mavropoulos
|
||||
Copyright (C) 2019-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'VAIO Packaging Manager Extractor v3.0_a8'
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
from common.comp_szip import is_szip_supported, szip_decompress
|
||||
from common.path_ops import make_dirs
|
||||
|
@ -22,14 +16,20 @@ from common.system import printer
|
|||
from common.templates import BIOSUtility
|
||||
from common.text_ops import file_to_bytes
|
||||
|
||||
# Check if input is VAIO Packaging Manager
|
||||
TITLE = 'VAIO Packaging Manager Extractor v4.0'
|
||||
|
||||
|
||||
def is_vaio_pkg(in_file):
|
||||
""" Check if input is VAIO Packaging Manager """
|
||||
|
||||
buffer = file_to_bytes(in_file)
|
||||
|
||||
return bool(PAT_VAIO_CFG.search(buffer))
|
||||
|
||||
# Extract VAIO Packaging Manager executable
|
||||
|
||||
def vaio_cabinet(name, buffer, extract_path, padding=0):
|
||||
""" Extract VAIO Packaging Manager executable """
|
||||
|
||||
match_cab = PAT_VAIO_CAB.search(buffer) # Microsoft CAB Header XOR 0xFF
|
||||
|
||||
if not match_cab:
|
||||
|
@ -38,15 +38,19 @@ def vaio_cabinet(name, buffer, extract_path, padding=0):
|
|||
printer('Detected obfuscated CAB archive!', padding)
|
||||
|
||||
# Determine the Microsoft CAB image size
|
||||
cab_size = int.from_bytes(buffer[match_cab.start() + 0x8:match_cab.start() + 0xC], 'little') # Get LE XOR-ed CAB size
|
||||
cab_size = int.from_bytes(buffer[match_cab.start() + 0x8:match_cab.start() + 0xC], 'little') # Get LE XOR CAB size
|
||||
|
||||
xor_size = int.from_bytes(b'\xFF' * 0x4, 'little') # Create CAB size XOR value
|
||||
|
||||
cab_size ^= xor_size # Perform XOR 0xFF and get actual CAB size
|
||||
|
||||
printer('Removing obfuscation...', padding + 4)
|
||||
|
||||
# Determine the Microsoft CAB image Data
|
||||
cab_data = int.from_bytes(buffer[match_cab.start():match_cab.start() + cab_size], 'big') # Get BE XOR-ed CAB data
|
||||
cab_data = int.from_bytes(buffer[match_cab.start():match_cab.start() + cab_size], 'big') # Get BE XOR CAB data
|
||||
|
||||
xor_data = int.from_bytes(b'\xFF' * cab_size, 'big') # Create CAB data XOR value
|
||||
|
||||
cab_data = (cab_data ^ xor_data).to_bytes(cab_size, 'big') # Perform XOR 0xFF and get actual CAB data
|
||||
|
||||
printer('Extracting archive...', padding + 4)
|
||||
|
@ -57,7 +61,7 @@ def vaio_cabinet(name, buffer, extract_path, padding=0):
|
|||
cab_file.write(cab_data) # Create temporary CAB archive
|
||||
|
||||
if is_szip_supported(cab_path, padding + 8, check=True):
|
||||
if szip_decompress(cab_path, extract_path, 'CAB', padding + 8, check=True) == 0:
|
||||
if szip_decompress(cab_path, extract_path, 'VAIO CAB', padding + 8, check=True) == 0:
|
||||
os.remove(cab_path) # Successful extraction, delete temporary CAB archive
|
||||
else:
|
||||
return 3
|
||||
|
@ -66,8 +70,10 @@ def vaio_cabinet(name, buffer, extract_path, padding=0):
|
|||
|
||||
return 0
|
||||
|
||||
# Unlock VAIO Packaging Manager executable
|
||||
|
||||
def vaio_unlock(name, buffer, extract_path, padding=0):
|
||||
""" Unlock VAIO Packaging Manager executable """
|
||||
|
||||
match_cfg = PAT_VAIO_CFG.search(buffer)
|
||||
|
||||
if not match_cfg:
|
||||
|
@ -87,32 +93,38 @@ def vaio_unlock(name, buffer, extract_path, padding=0):
|
|||
for info in cfg_info:
|
||||
if info.startswith(b'ExtractPathByUser='):
|
||||
cfg_false = bytearray(b'0' if info[18:] in (b'0', b'1') else info[18:]) # Should be 0/No/False
|
||||
|
||||
if info.startswith(b'UseCompression='):
|
||||
cfg_true = bytearray(b'1' if info[15:] in (b'0', b'1') else info[15:]) # Should be 1/Yes/True
|
||||
|
||||
# Check if valid True/False values have been retrieved
|
||||
if cfg_false == cfg_true or not cfg_false or not cfg_true:
|
||||
printer('Error: Could not retrieve True/False values!', padding + 8)
|
||||
|
||||
return 2
|
||||
|
||||
printer('Adjusting UseVAIOCheck entry...', padding + 4)
|
||||
|
||||
# Find and replace UseVAIOCheck entry from 1/Yes/True to 0/No/False
|
||||
vaio_check = PAT_VAIO_CHK.search(buffer[cfg_bgn:])
|
||||
|
||||
if vaio_check:
|
||||
buffer[cfg_bgn + vaio_check.end():cfg_bgn + vaio_check.end() + len(cfg_true)] = cfg_false
|
||||
else:
|
||||
printer('Error: Could not find entry UseVAIOCheck!', padding + 8)
|
||||
|
||||
return 3
|
||||
|
||||
printer('Adjusting ExtractPathByUser entry...', padding + 4)
|
||||
|
||||
# Find and replace ExtractPathByUser entry from 0/No/False to 1/Yes/True
|
||||
user_path = PAT_VAIO_EXT.search(buffer[cfg_bgn:])
|
||||
|
||||
if user_path:
|
||||
buffer[cfg_bgn + user_path.end():cfg_bgn + user_path.end() + len(cfg_false)] = cfg_true
|
||||
else:
|
||||
printer('Error: Could not find entry ExtractPathByUser!', padding + 8)
|
||||
|
||||
return 4
|
||||
|
||||
printer('Storing unlocked executable...', padding + 4)
|
||||
|
@ -120,13 +132,16 @@ def vaio_unlock(name, buffer, extract_path, padding=0):
|
|||
# Store Unlocked VAIO Packaging Manager executable
|
||||
if vaio_check and user_path:
|
||||
unlock_path = os.path.join(extract_path, f'{name}_Unlocked.exe')
|
||||
|
||||
with open(unlock_path, 'wb') as unl_file:
|
||||
unl_file.write(buffer)
|
||||
|
||||
return 0
|
||||
|
||||
# Parse & Extract or Unlock VAIO Packaging Manager
|
||||
|
||||
def vaio_pkg_extract(input_file, extract_path, padding=0):
|
||||
""" Parse & Extract or Unlock VAIO Packaging Manager """
|
||||
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
||||
input_name = os.path.basename(input_file)
|
||||
|
@ -139,9 +154,11 @@ def vaio_pkg_extract(input_file, extract_path, padding=0):
|
|||
printer('Successfully Unlocked!', padding)
|
||||
else:
|
||||
printer('Error: Failed to Extract or Unlock executable!', padding)
|
||||
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIOSUtility(TITLE, is_vaio_pkg, vaio_pkg_extract).run_utility()
|
||||
BIOSUtility(title=TITLE, check=is_vaio_pkg, main=vaio_pkg_extract).run_utility()
|
||||
|
|
6
__init__.py
Normal file
6
__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Copyright (C) 2019-2024 Plato Mavropoulos
|
||||
"""
|
6
common/__init__.py
Normal file
6
common/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Copyright (C) 2019-2024 Plato Mavropoulos
|
||||
"""
|
|
@ -1,22 +1,28 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Copyright (C) 2022 Plato Mavropoulos
|
||||
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
|
||||
# Get Checksum 16-bit
|
||||
def get_chk_16(data, value=0, order='little'):
|
||||
""" Calculate Checksum-16 of data, controlling IV and Endianess """
|
||||
|
||||
for idx in range(0, len(data), 2):
|
||||
# noinspection PyTypeChecker
|
||||
value += int.from_bytes(data[idx:idx + 2], order)
|
||||
value += int.from_bytes(data[idx:idx + 2], byteorder=order)
|
||||
|
||||
value &= 0xFFFF
|
||||
|
||||
return value
|
||||
|
||||
|
||||
# Get Checksum 8-bit XOR
|
||||
def get_chk_8_xor(data, value=0):
|
||||
""" Calculate Checksum-8 XOR of data, controlling IV """
|
||||
|
||||
for byte in data:
|
||||
value ^= byte
|
||||
|
||||
|
|
|
@ -1,23 +1,29 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Copyright (C) 2022 Plato Mavropoulos
|
||||
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from common.path_ops import project_root, safe_path
|
||||
from common.system import get_os_ver, printer
|
||||
from common.externals import get_tiano_path
|
||||
from common.system import printer
|
||||
|
||||
|
||||
def get_compress_sizes(data):
|
||||
""" Get EFI compression sizes """
|
||||
|
||||
size_compress = int.from_bytes(data[0x0:0x4], 'little')
|
||||
size_original = int.from_bytes(data[0x4:0x8], 'little')
|
||||
|
||||
return size_compress, size_original
|
||||
|
||||
|
||||
def is_efi_compressed(data, strict=True):
|
||||
""" Check if data is EFI compressed, controlling EOF padding """
|
||||
|
||||
size_comp, size_orig = get_compress_sizes(data)
|
||||
|
||||
check_diff = size_comp < size_orig
|
||||
|
@ -29,25 +35,22 @@ def is_efi_compressed(data, strict=True):
|
|||
|
||||
return check_diff and check_size
|
||||
|
||||
# Get TianoCompress path
|
||||
def get_tiano_path():
|
||||
exec_name = f'TianoCompress{".exe" if get_os_ver()[1] else ""}'
|
||||
|
||||
return safe_path(project_root(), ['external',exec_name])
|
||||
|
||||
# EFI/Tiano Decompression via TianoCompress
|
||||
def efi_decompress(in_path, out_path, padding=0, silent=False, comp_type='--uefi'):
|
||||
""" EFI/Tiano Decompression via TianoCompress """
|
||||
|
||||
try:
|
||||
subprocess.run([get_tiano_path(), '-d', in_path, '-o', out_path, '-q', comp_type], check=True, stdout=subprocess.DEVNULL)
|
||||
subprocess.run([get_tiano_path(), '-d', in_path, '-o', out_path, '-q', comp_type],
|
||||
check=True, stdout=subprocess.DEVNULL)
|
||||
|
||||
with open(in_path, 'rb') as file:
|
||||
_, size_orig = get_compress_sizes(file.read())
|
||||
|
||||
if os.path.getsize(out_path) != size_orig:
|
||||
raise Exception('EFI_DECOMPRESS_ERROR')
|
||||
except Exception:
|
||||
raise OSError('EFI decompressed file & header size mismatch!')
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
if not silent:
|
||||
printer(f'Error: TianoCompress could not extract file {in_path}!', padding)
|
||||
printer(f'Error: TianoCompress could not extract file {in_path}: {error}!', padding)
|
||||
|
||||
return 1
|
||||
|
||||
|
|
|
@ -1,29 +1,27 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Copyright (C) 2022 Plato Mavropoulos
|
||||
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from common.path_ops import project_root, safe_path
|
||||
from common.system import get_os_ver, printer
|
||||
from common.externals import get_szip_path
|
||||
from common.system import printer
|
||||
|
||||
# Get 7-Zip path
|
||||
def get_szip_path():
|
||||
exec_name = '7z.exe' if get_os_ver()[1] else '7zzs'
|
||||
|
||||
return safe_path(project_root(), ['external',exec_name])
|
||||
|
||||
# Check 7-Zip bad exit codes (0 OK, 1 Warning)
|
||||
def check_bad_exit_code(exit_code):
|
||||
if exit_code not in (0,1):
|
||||
raise Exception(f'BAD_EXIT_CODE_{exit_code}')
|
||||
""" Check 7-Zip bad exit codes (0 OK, 1 Warning) """
|
||||
|
||||
if exit_code not in (0, 1):
|
||||
raise ValueError(f'Bad exit code: {exit_code}')
|
||||
|
||||
|
||||
# Check if file is 7-Zip supported
|
||||
def is_szip_supported(in_path, padding=0, args=None, check=False, silent=False):
|
||||
""" Check if file is 7-Zip supported """
|
||||
|
||||
try:
|
||||
if args is None:
|
||||
args = []
|
||||
|
@ -34,16 +32,18 @@ def is_szip_supported(in_path, padding=0, args=None, check=False, silent=False):
|
|||
|
||||
if check:
|
||||
check_bad_exit_code(szip_t.returncode)
|
||||
except Exception:
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
if not silent:
|
||||
printer(f'Error: 7-Zip could not check support for file {in_path}!', padding)
|
||||
printer(f'Error: 7-Zip could not check support for file {in_path}: {error}!', padding)
|
||||
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
# Archive decompression via 7-Zip
|
||||
|
||||
def szip_decompress(in_path, out_path, in_name, padding=0, args=None, check=False, silent=False):
|
||||
""" Archive decompression via 7-Zip """
|
||||
|
||||
if not in_name:
|
||||
in_name = 'archive'
|
||||
|
||||
|
@ -59,10 +59,10 @@ def szip_decompress(in_path, out_path, in_name, padding=0, args=None, check=Fals
|
|||
check_bad_exit_code(szip_x.returncode)
|
||||
|
||||
if not os.path.isdir(out_path):
|
||||
raise Exception('EXTRACT_DIR_MISSING')
|
||||
except Exception:
|
||||
raise OSError(f'Extraction directory not found: {out_path}')
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
if not silent:
|
||||
printer(f'Error: 7-Zip could not extract {in_name} file {in_path}!', padding)
|
||||
printer(f'Error: 7-Zip could not extract {in_name} file {in_path}: {error}!', padding)
|
||||
|
||||
return 1
|
||||
|
||||
|
|
|
@ -1,38 +1,66 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Copyright (C) 2022 Plato Mavropoulos
|
||||
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
from common.path_ops import project_root, safe_path
|
||||
from common.system import get_os_ver
|
||||
|
||||
# https://github.com/allowitsme/big-tool by Dmitry Frolov
|
||||
# https://github.com/platomav/BGScriptTool by Plato Mavropoulos
|
||||
|
||||
def get_bgs_tool():
|
||||
"""
|
||||
https://github.com/allowitsme/big-tool by Dmitry Frolov
|
||||
https://github.com/platomav/BGScriptTool by Plato Mavropoulos
|
||||
"""
|
||||
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
from external.big_script_tool import BigScript # pylint: disable=E0401,E0611
|
||||
except Exception:
|
||||
BigScript = None
|
||||
from external.big_script_tool import BigScript # pylint: disable=C0415
|
||||
|
||||
return BigScript
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_comextract_path() -> str:
|
||||
""" Get ToshibaComExtractor path """
|
||||
|
||||
exec_name = f'comextract{".exe" if get_os_ver()[1] else ""}'
|
||||
|
||||
return safe_path(project_root(), ['external', exec_name])
|
||||
|
||||
|
||||
def get_szip_path() -> str:
|
||||
""" Get 7-Zip path """
|
||||
|
||||
exec_name = '7z.exe' if get_os_ver()[1] else '7zzs'
|
||||
|
||||
return safe_path(project_root(), ['external', exec_name])
|
||||
|
||||
|
||||
def get_tiano_path() -> str:
|
||||
""" Get TianoCompress path """
|
||||
|
||||
exec_name = f'TianoCompress{".exe" if get_os_ver()[1] else ""}'
|
||||
|
||||
return safe_path(project_root(), ['external', exec_name])
|
||||
|
||||
|
||||
def get_uefifind_path() -> str:
|
||||
""" Get UEFIFind path """
|
||||
|
||||
# Get UEFIFind path
|
||||
def get_uefifind_path():
|
||||
exec_name = f'UEFIFind{".exe" if get_os_ver()[1] else ""}'
|
||||
|
||||
return safe_path(project_root(), ['external', exec_name])
|
||||
|
||||
# Get UEFIExtract path
|
||||
def get_uefiextract_path():
|
||||
|
||||
def get_uefiextract_path() -> str:
|
||||
""" Get UEFIExtract path """
|
||||
|
||||
exec_name = f'UEFIExtract{".exe" if get_os_ver()[1] else ""}'
|
||||
|
||||
return safe_path(project_root(), ['external', exec_name])
|
||||
|
||||
# Get ToshibaComExtractor path
|
||||
def get_comextract_path():
|
||||
exec_name = f'comextract{".exe" if get_os_ver()[1] else ""}'
|
||||
|
||||
return safe_path(project_root(), ['external', exec_name])
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Copyright (C) 2022 Plato Mavropoulos
|
||||
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
# https://leancrew.com/all-this/2020/06/ordinals-in-python/ by Dr. Drang
|
||||
|
||||
def get_ordinal(number):
|
||||
s = ('th', 'st', 'nd', 'rd') + ('th',) * 10
|
||||
"""
|
||||
Get ordinal (textual) representation of input numerical value
|
||||
https://leancrew.com/all-this/2020/06/ordinals-in-python/ by Dr. Drang
|
||||
"""
|
||||
|
||||
v = number % 100
|
||||
txt = ('th', 'st', 'nd', 'rd') + ('th',) * 10
|
||||
|
||||
return f'{number}{s[v % 10]}' if v > 13 else f'{number}{s[v]}'
|
||||
val = number % 100
|
||||
|
||||
return f'{number}{txt[val % 10]}' if val > 13 else f'{number}{txt[val]}'
|
||||
|
|
|
@ -1,27 +1,38 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Copyright (C) 2022 Plato Mavropoulos
|
||||
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import stat
|
||||
import shutil
|
||||
import stat
|
||||
import sys
|
||||
|
||||
from pathlib import Path, PurePath
|
||||
|
||||
from common.system import get_os_ver
|
||||
from common.text_ops import is_encased, to_string
|
||||
|
||||
# Fix illegal/reserved Windows characters
|
||||
MAX_WIN_COMP_LEN = 255
|
||||
|
||||
|
||||
def safe_name(in_name):
|
||||
"""
|
||||
Fix illegal/reserved Windows characters
|
||||
Can also be used to nuke dangerous paths
|
||||
"""
|
||||
|
||||
name_repr = repr(in_name).strip("'")
|
||||
|
||||
return re.sub(r'[\\/:"*?<>|]+', '_', name_repr)
|
||||
|
||||
# Check and attempt to fix illegal/unsafe OS path traversals
|
||||
|
||||
def safe_path(base_path, user_paths):
|
||||
""" Check and attempt to fix illegal/unsafe OS path traversals """
|
||||
|
||||
# Convert base path to absolute path
|
||||
base_path = real_path(base_path)
|
||||
|
||||
|
@ -43,10 +54,12 @@ def safe_path(base_path, user_paths):
|
|||
return nuked_path
|
||||
|
||||
# Still illegal, raise exception to halt execution
|
||||
raise Exception(f'ILLEGAL_PATH_TRAVERSAL: {user_path}')
|
||||
raise OSError(f'Encountered illegal path traversal: {user_path}')
|
||||
|
||||
|
||||
# Check for illegal/unsafe OS path traversal
|
||||
def is_safe_path(base_path, target_path):
|
||||
""" Check for illegal/unsafe OS path traversal """
|
||||
|
||||
base_path = real_path(base_path)
|
||||
|
||||
target_path = real_path(target_path)
|
||||
|
@ -55,64 +68,102 @@ def is_safe_path(base_path, target_path):
|
|||
|
||||
return base_path == common_path
|
||||
|
||||
# Create normalized base path + OS separator + user path
|
||||
|
||||
def norm_path(base_path, user_path):
|
||||
""" Create normalized base path + OS separator + user path """
|
||||
|
||||
return os.path.normpath(base_path + os.sep + user_path)
|
||||
|
||||
# Get absolute path, resolving any symlinks
|
||||
|
||||
def real_path(in_path):
|
||||
""" Get absolute path, resolving any symlinks """
|
||||
|
||||
return os.path.realpath(in_path)
|
||||
|
||||
# Get Windows/Posix OS agnostic path
|
||||
|
||||
def agnostic_path(in_path):
|
||||
""" Get Windows/Posix OS agnostic path """
|
||||
|
||||
return PurePath(in_path.replace('\\', os.sep))
|
||||
|
||||
# Get absolute parent of path
|
||||
|
||||
def path_parent(in_path):
|
||||
""" Get absolute parent of path """
|
||||
|
||||
return Path(in_path).parent.absolute()
|
||||
|
||||
# Get final path component, with suffix
|
||||
def path_name(in_path):
|
||||
return PurePath(in_path).name
|
||||
|
||||
# Get final path component, w/o suffix
|
||||
def path_name(in_path, limit=False):
|
||||
""" Get final path component, with suffix """
|
||||
|
||||
comp_name = PurePath(in_path).name
|
||||
|
||||
if limit and get_os_ver()[1]:
|
||||
comp_name = comp_name[:MAX_WIN_COMP_LEN - len(extract_suffix())]
|
||||
|
||||
return comp_name
|
||||
|
||||
|
||||
def path_stem(in_path):
|
||||
""" Get final path component, w/o suffix """
|
||||
|
||||
return PurePath(in_path).stem
|
||||
|
||||
# Get list of path file extensions
|
||||
|
||||
def path_suffixes(in_path):
|
||||
""" Get list of path file extensions """
|
||||
|
||||
return PurePath(in_path).suffixes or ['']
|
||||
|
||||
# Check if path is absolute
|
||||
|
||||
def is_path_absolute(in_path):
|
||||
""" Check if path is absolute """
|
||||
|
||||
return Path(in_path).is_absolute()
|
||||
|
||||
# Create folder(s), controlling parents, existence and prior deletion
|
||||
|
||||
def make_dirs(in_path, parents=True, exist_ok=False, delete=False):
|
||||
""" Create folder(s), controlling parents, existence and prior deletion """
|
||||
|
||||
if delete:
|
||||
del_dirs(in_path)
|
||||
|
||||
Path.mkdir(Path(in_path), parents=parents, exist_ok=exist_ok)
|
||||
|
||||
# Delete folder(s), if present
|
||||
def del_dirs(in_path):
|
||||
if Path(in_path).is_dir():
|
||||
shutil.rmtree(in_path, onerror=clear_readonly)
|
||||
|
||||
# Copy file to path with or w/o metadata
|
||||
def del_dirs(in_path):
|
||||
""" Delete folder(s), if present """
|
||||
|
||||
if Path(in_path).is_dir():
|
||||
shutil.rmtree(in_path, onerror=clear_readonly_callback)
|
||||
|
||||
|
||||
def copy_file(in_path, out_path, meta=False):
|
||||
""" Copy file to path with or w/o metadata """
|
||||
|
||||
if meta:
|
||||
shutil.copy2(in_path, out_path)
|
||||
else:
|
||||
shutil.copy(in_path, out_path)
|
||||
|
||||
# Clear read-only file attribute (on shutil.rmtree error)
|
||||
def clear_readonly(in_func, in_path, _):
|
||||
|
||||
def clear_readonly(in_path):
|
||||
""" Clear read-only file attribute """
|
||||
|
||||
os.chmod(in_path, stat.S_IWRITE)
|
||||
|
||||
|
||||
def clear_readonly_callback(in_func, in_path, _):
|
||||
""" Clear read-only file attribute (on shutil.rmtree error) """
|
||||
|
||||
clear_readonly(in_path)
|
||||
|
||||
in_func(in_path)
|
||||
|
||||
# Walk path to get all files
|
||||
|
||||
def get_path_files(in_path):
|
||||
""" Walk path to get all files """
|
||||
|
||||
path_files = []
|
||||
|
||||
for root, _, files in os.walk(in_path):
|
||||
|
@ -121,8 +172,10 @@ def get_path_files(in_path):
|
|||
|
||||
return path_files
|
||||
|
||||
# Get path without leading/trailing quotes
|
||||
|
||||
def get_dequoted_path(in_path):
|
||||
""" Get path without leading/trailing quotes """
|
||||
|
||||
out_path = to_string(in_path).strip()
|
||||
|
||||
if len(out_path) >= 2 and is_encased(out_path, ("'", '"')):
|
||||
|
@ -130,22 +183,28 @@ def get_dequoted_path(in_path):
|
|||
|
||||
return out_path
|
||||
|
||||
# Set utility extraction stem
|
||||
|
||||
def extract_suffix():
|
||||
""" Set utility extraction stem """
|
||||
|
||||
return '_extracted'
|
||||
|
||||
# Get utility extraction path
|
||||
|
||||
def get_extract_path(in_path, suffix=extract_suffix()):
|
||||
""" Get utility extraction path """
|
||||
|
||||
return f'{in_path}{suffix}'
|
||||
|
||||
# Get project's root directory
|
||||
|
||||
def project_root():
|
||||
root = Path(__file__).parent.parent
|
||||
""" Get project's root directory """
|
||||
|
||||
return real_path(Path(__file__).parent.parent)
|
||||
|
||||
return real_path(root)
|
||||
|
||||
# Get runtime's root directory
|
||||
def runtime_root():
|
||||
""" Get runtime's root directory """
|
||||
|
||||
if getattr(sys, 'frozen', False):
|
||||
root = Path(sys.executable).parent
|
||||
else:
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Copyright (C) 2022 Plato Mavropoulos
|
||||
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
import re
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Copyright (C) 2022 Plato Mavropoulos
|
||||
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
import pefile
|
||||
|
@ -10,40 +10,57 @@ import pefile
|
|||
from common.system import printer
|
||||
from common.text_ops import file_to_bytes
|
||||
|
||||
# Check if input is a PE file
|
||||
def is_pe_file(in_file):
|
||||
return bool(get_pe_file(in_file))
|
||||
|
||||
# Get pefile object from PE file
|
||||
def get_pe_file(in_file, fast=True):
|
||||
def is_pe_file(in_file: str | bytes) -> bool:
|
||||
""" Check if input is a PE file """
|
||||
|
||||
return bool(get_pe_file(in_file, silent=True))
|
||||
|
||||
|
||||
def get_pe_file(in_file: str | bytes, padding: int = 0, fast: bool = True, silent: bool = False) -> pefile.PE | None:
|
||||
""" Get pefile object from PE file """
|
||||
|
||||
in_buffer = file_to_bytes(in_file)
|
||||
|
||||
pe_file = None
|
||||
|
||||
try:
|
||||
# Analyze detected MZ > PE image buffer
|
||||
pe_file = pefile.PE(data=in_buffer, fast_load=fast)
|
||||
except Exception:
|
||||
pe_file = None
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
if not silent:
|
||||
_filename = in_file if type(in_file).__name__ == 'string' else 'buffer'
|
||||
|
||||
printer(f'Error: Could not get pefile object from {_filename}: {error}!', padding)
|
||||
|
||||
return pe_file
|
||||
|
||||
# Get PE info from pefile object
|
||||
def get_pe_info(pe_file):
|
||||
|
||||
def get_pe_info(pe_file: pefile.PE, padding: int = 0, silent: bool = False) -> dict:
|
||||
""" Get PE info from pefile object """
|
||||
|
||||
pe_info = {}
|
||||
|
||||
try:
|
||||
# When fast_load is used, IMAGE_DIRECTORY_ENTRY_RESOURCE must be parsed prior to FileInfo > StringTable
|
||||
pe_file.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE']])
|
||||
|
||||
# Retrieve MZ > PE > FileInfo > StringTable information
|
||||
pe_info = pe_file.FileInfo[0][0].StringTable[0].entries
|
||||
except Exception:
|
||||
pe_info = {}
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
if not silent:
|
||||
printer(f'Error: Could not get PE info from pefile object: {error}!', padding)
|
||||
|
||||
return pe_info
|
||||
|
||||
# Print PE info from pefile StringTable
|
||||
def show_pe_info(pe_info, padding=0):
|
||||
if type(pe_info).__name__ == 'dict':
|
||||
|
||||
def show_pe_info(pe_info: dict, padding: int = 0) -> None:
|
||||
""" Print PE info from pefile StringTable """
|
||||
|
||||
if isinstance(pe_info, dict):
|
||||
for title, value in pe_info.items():
|
||||
info_title = title.decode('utf-8', 'ignore').strip()
|
||||
info_value = value.decode('utf-8', 'ignore').strip()
|
||||
|
||||
if info_title and info_value:
|
||||
printer(f'{info_title}: {info_value}', padding, new_line=False)
|
||||
|
|
|
@ -1,26 +1,32 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Copyright (C) 2022 Plato Mavropoulos
|
||||
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
import ctypes
|
||||
|
||||
char = ctypes.c_char
|
||||
uint8_t = ctypes.c_ubyte
|
||||
uint16_t = ctypes.c_ushort
|
||||
uint32_t = ctypes.c_uint
|
||||
uint64_t = ctypes.c_uint64
|
||||
Char: type[ctypes.c_char] | int = ctypes.c_char
|
||||
UInt8: type[ctypes.c_ubyte] | int = ctypes.c_ubyte
|
||||
UInt16: type[ctypes.c_ushort] | int = ctypes.c_ushort
|
||||
UInt32: type[ctypes.c_uint] | int = ctypes.c_uint
|
||||
UInt64: type[ctypes.c_uint64] | int = ctypes.c_uint64
|
||||
|
||||
|
||||
# https://github.com/skochinsky/me-tools/blob/master/me_unpack.py by Igor Skochinsky
|
||||
def get_struct(buffer, start_offset, class_name, param_list=None):
|
||||
if param_list is None:
|
||||
param_list = []
|
||||
"""
|
||||
https://github.com/skochinsky/me-tools/blob/master/me_unpack.py by Igor Skochinsky
|
||||
"""
|
||||
|
||||
parameters = [] if param_list is None else param_list
|
||||
|
||||
structure = class_name(*parameters) # Unpack parameter list
|
||||
|
||||
structure = class_name(*param_list) # Unpack parameter list
|
||||
struct_len = ctypes.sizeof(structure)
|
||||
|
||||
struct_data = buffer[start_offset:start_offset + struct_len]
|
||||
|
||||
fit_len = min(len(struct_data), struct_len)
|
||||
|
||||
ctypes.memmove(ctypes.addressof(structure), struct_data, fit_len)
|
||||
|
|
|
@ -1,33 +1,42 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Copyright (C) 2022 Plato Mavropoulos
|
||||
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from common.text_ops import padder, to_string
|
||||
|
||||
# Get Python Version (tuple)
|
||||
|
||||
def get_py_ver():
|
||||
""" Get Python Version (tuple) """
|
||||
|
||||
return sys.version_info
|
||||
|
||||
# Get OS Platform (string)
|
||||
|
||||
def get_os_ver():
|
||||
""" Get OS Platform (string) """
|
||||
|
||||
sys_os = sys.platform
|
||||
|
||||
is_win = sys_os == 'win32'
|
||||
|
||||
is_lnx = sys_os.startswith('linux') or sys_os == 'darwin' or sys_os.find('bsd') != -1
|
||||
|
||||
return sys_os, is_win, is_win or is_lnx
|
||||
|
||||
# Check for --auto-exit|-e
|
||||
|
||||
def is_auto_exit():
|
||||
""" Check for --auto-exit|-e """
|
||||
|
||||
return bool('--auto-exit' in sys.argv or '-e' in sys.argv)
|
||||
|
||||
# Check Python Version
|
||||
|
||||
def check_sys_py():
|
||||
""" # Check Python Version """
|
||||
|
||||
sys_py = get_py_ver()
|
||||
|
||||
if sys_py < (3, 10):
|
||||
|
@ -39,8 +48,10 @@ def check_sys_py():
|
|||
|
||||
sys.exit(125)
|
||||
|
||||
# Check OS Platform
|
||||
|
||||
def check_sys_os():
|
||||
""" Check OS Platform """
|
||||
|
||||
os_tag, os_win, os_sup = get_os_ver()
|
||||
|
||||
if not os_sup:
|
||||
|
@ -53,16 +64,21 @@ def check_sys_os():
|
|||
|
||||
# Fix Windows Unicode console redirection
|
||||
if os_win:
|
||||
# noinspection PyUnresolvedReferences
|
||||
sys.stdout.reconfigure(encoding='utf-8')
|
||||
|
||||
# Show message(s) while controlling padding, newline, pausing & separator
|
||||
def printer(in_message='', padd_count=0, new_line=True, pause=False, sep_char=' '):
|
||||
message = to_string(in_message, sep_char)
|
||||
|
||||
padding = padder(padd_count)
|
||||
def printer(message=None, padd=0, new_line=True, pause=False, sep_char=' '):
|
||||
""" Show message(s), controlling padding, newline, pausing & separator """
|
||||
|
||||
message_input = '' if message is None else message
|
||||
|
||||
string = to_string(message_input, sep_char)
|
||||
|
||||
padding = padder(padd)
|
||||
|
||||
newline = '\n' if new_line else ''
|
||||
|
||||
output = newline + padding + message
|
||||
message_output = newline + padding + string
|
||||
|
||||
(input if pause and not is_auto_exit() else print)(output)
|
||||
(input if pause and not is_auto_exit() else print)(message_output)
|
||||
|
|
|
@ -1,30 +1,36 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Copyright (C) 2022 Plato Mavropoulos
|
||||
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import ctypes
|
||||
import os
|
||||
import sys
|
||||
import ctypes
|
||||
import argparse
|
||||
import traceback
|
||||
|
||||
from common.num_ops import get_ordinal
|
||||
from common.path_ops import get_dequoted_path, get_extract_path, get_path_files, is_path_absolute, path_parent, runtime_root, safe_path
|
||||
from common.path_ops import (get_dequoted_path, get_extract_path, get_path_files,
|
||||
is_path_absolute, path_name, path_parent, real_path, runtime_root)
|
||||
from common.system import check_sys_os, check_sys_py, get_os_ver, is_auto_exit, printer
|
||||
|
||||
|
||||
class BIOSUtility:
|
||||
""" Template utility class for BIOSUtilities """
|
||||
|
||||
MAX_FAT32_ITEMS = 65535
|
||||
|
||||
def __init__(self, title, check, main, padding=0):
|
||||
def __init__(self, title, check, main, args=None, padding=0):
|
||||
self._title = title
|
||||
self._main = main
|
||||
self._check = check
|
||||
self._arg_defs = args if args is not None else []
|
||||
self._padding = padding
|
||||
|
||||
self._arguments_kw = {}
|
||||
self._arguments_kw_dest = []
|
||||
|
||||
# Initialize argparse argument parser
|
||||
self._argparser = argparse.ArgumentParser()
|
||||
|
@ -35,7 +41,15 @@ class BIOSUtility:
|
|||
self._argparser.add_argument('-o', '--output-dir', help='extract in given output directory')
|
||||
self._argparser.add_argument('-i', '--input-dir', help='extract from given input directory')
|
||||
|
||||
self._arguments,self._arguments_unk = self._argparser.parse_known_args()
|
||||
for _arg_def in self._arg_defs:
|
||||
_action = self._argparser.add_argument(*_arg_def[0], **_arg_def[1])
|
||||
|
||||
self._arguments_kw_dest.append(_action.dest)
|
||||
|
||||
self._arguments, _ = self._argparser.parse_known_args()
|
||||
|
||||
for _arg_dest in self._arguments_kw_dest:
|
||||
self._arguments_kw.update({_arg_dest: self._arguments.__dict__[_arg_dest]})
|
||||
|
||||
# Managed Python exception handler
|
||||
sys.excepthook = self._exception_handler
|
||||
|
@ -63,14 +77,11 @@ class BIOSUtility:
|
|||
# Count input files for exit code
|
||||
self._exit_code = len(self._input_files)
|
||||
|
||||
def parse_argument(self, *args, **kwargs):
|
||||
_dest = self._argparser.add_argument(*args, **kwargs).dest
|
||||
self._arguments = self._argparser.parse_known_args(self._arguments_unk)[0]
|
||||
self._arguments_kw.update({_dest: self._arguments.__dict__[_dest]})
|
||||
|
||||
def run_utility(self):
|
||||
""" Run utility after checking for supported format """
|
||||
|
||||
for _input_file in self._input_files:
|
||||
_input_name = os.path.basename(_input_file)
|
||||
_input_name = path_name(_input_file, limit=True)
|
||||
|
||||
printer(['***', _input_name], self._padding)
|
||||
|
||||
|
@ -105,7 +116,7 @@ class BIOSUtility:
|
|||
# Drag & Drop or CLI
|
||||
if self._arguments.input_dir:
|
||||
_input_path_user = self._arguments.input_dir
|
||||
_input_path_full = self._get_input_path(_input_path_user) if _input_path_user else ''
|
||||
_input_path_full = self._get_user_path(_input_path_user) if _input_path_user else ''
|
||||
self._input_files = get_path_files(_input_path_full)
|
||||
else:
|
||||
# Parse list of input files (i.e. argparse FileType objects)
|
||||
|
@ -123,16 +134,16 @@ class BIOSUtility:
|
|||
else:
|
||||
# Script w/o parameters
|
||||
_input_path_user = get_dequoted_path(input('\nEnter input directory path: '))
|
||||
_input_path_full = self._get_input_path(_input_path_user) if _input_path_user else ''
|
||||
_input_path_full = self._get_user_path(_input_path_user) if _input_path_user else ''
|
||||
self._input_files = get_path_files(_input_path_full)
|
||||
|
||||
_output_path = get_dequoted_path(input('\nEnter output directory path: '))
|
||||
|
||||
self._output_path = self._get_input_path(_output_path)
|
||||
self._output_path = self._get_user_path(_output_path)
|
||||
|
||||
# Get absolute input file path
|
||||
# Get absolute user file path
|
||||
@staticmethod
|
||||
def _get_input_path(input_path):
|
||||
def _get_user_path(input_path):
|
||||
if not input_path:
|
||||
# Use runtime directory if no user path is specified
|
||||
absolute_path = runtime_root()
|
||||
|
@ -142,7 +153,7 @@ class BIOSUtility:
|
|||
absolute_path = input_path
|
||||
# Otherwise, make it runtime directory relative
|
||||
else:
|
||||
absolute_path = safe_path(runtime_root(), input_path)
|
||||
absolute_path = real_path(input_path)
|
||||
|
||||
return absolute_path
|
||||
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Copyright (C) 2022 Plato Mavropoulos
|
||||
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
# Generate padding (spaces or tabs)
|
||||
|
||||
def padder(padd_count, tab=False):
|
||||
""" Generate padding (spaces or tabs) """
|
||||
|
||||
return ('\t' if tab else ' ') * padd_count
|
||||
|
||||
# Get String from given input object
|
||||
|
||||
def to_string(in_object, sep_char=''):
|
||||
""" Get String from given input object """
|
||||
|
||||
if type(in_object).__name__ in ('list', 'tuple'):
|
||||
out_string = sep_char.join(map(str, in_object))
|
||||
else:
|
||||
|
@ -18,8 +22,10 @@ def to_string(in_object, sep_char=''):
|
|||
|
||||
return out_string
|
||||
|
||||
# Get Bytes from given buffer or file path
|
||||
|
||||
def file_to_bytes(in_object):
|
||||
""" Get Bytes from given buffer or file path """
|
||||
|
||||
object_bytes = in_object
|
||||
|
||||
if type(in_object).__name__ not in ('bytes', 'bytearray'):
|
||||
|
@ -28,6 +34,15 @@ def file_to_bytes(in_object):
|
|||
|
||||
return object_bytes
|
||||
|
||||
# Check if string starts and ends with given character(s)
|
||||
|
||||
def bytes_to_hex(buffer: bytes, order: str, data_len: int, slice_len: int | None = None) -> str:
|
||||
""" Converts bytes to hex string, controlling endianess, data size and string slicing """
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
return f'{int.from_bytes(buffer, order):0{data_len * 2}X}'[:slice_len]
|
||||
|
||||
|
||||
def is_encased(in_string, chars):
|
||||
""" Check if string starts and ends with given character(s) """
|
||||
|
||||
return in_string.startswith(chars) and in_string.endswith(chars)
|
||||
|
|
2
external/requirements.txt
vendored
2
external/requirements.txt
vendored
|
@ -1,2 +0,0 @@
|
|||
lznt1 >= 0.2
|
||||
pefile >= 2022.5.30
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
dissect.util == 3.15
|
||||
pefile == 2023.2.7
|
Loading…
Add table
Add a link
Reference in a new issue