mirror of
https://github.com/platomav/BIOSUtilities.git
synced 2025-05-13 14:44:46 -04:00
Added AMI PFAT RSA 3K signed blocks support
Added AMI PFAT nested detection at each file Added Award BIOS payload naming at each file Switched Panasonic BIOS LZNT1 external library Improved Panasonic LZNT1 detection and length Improved Dell PFS code structure and fixed bugs Improved code exception handling (raise, catch) Improved code definitions (PEP8, docs, types) Fixed some arguments missing from help screens
This commit is contained in:
parent
03ae0cf070
commit
d85a7f82dc
37 changed files with 2897 additions and 2174 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -1,5 +1,4 @@
|
||||||
# Skip all external files
|
/.idea/
|
||||||
external/*
|
/.mypy_cache/
|
||||||
|
/external/
|
||||||
# Keep external > requirements file
|
/venv/
|
||||||
!external/requirements.txt
|
|
||||||
|
|
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,219 +1,333 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3 -B
|
||||||
#coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
AMI PFAT Extract
|
AMI PFAT Extract
|
||||||
AMI BIOS Guard Extractor
|
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 os
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
import ctypes
|
|
||||||
|
|
||||||
# Stop __pycache__ generation
|
|
||||||
sys.dont_write_bytecode = True
|
|
||||||
|
|
||||||
from common.externals import get_bgs_tool
|
from common.externals import get_bgs_tool
|
||||||
from common.num_ops import get_ordinal
|
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.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.system import printer
|
||||||
from common.templates import BIOSUtility
|
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):
|
class AmiBiosGuardHeader(ctypes.LittleEndianStructure):
|
||||||
|
""" AMI BIOS Guard Header """
|
||||||
|
|
||||||
_pack_ = 1
|
_pack_ = 1
|
||||||
|
|
||||||
|
# noinspection PyTypeChecker
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('Size', uint32_t), # 0x00 Header + Entries
|
('Size', UInt32), # 0x00 Header + Entries
|
||||||
('Checksum', uint32_t), # 0x04 ?
|
('Checksum', UInt32), # 0x04 ?
|
||||||
('Tag', char*8), # 0x04 _AMIPFAT
|
('Tag', Char * 8), # 0x04 _AMIPFAT
|
||||||
('Flags', uint8_t), # 0x10 ?
|
('Flags', UInt8), # 0x10 ?
|
||||||
# 0x11
|
# 0x11
|
||||||
]
|
]
|
||||||
|
|
||||||
def struct_print(self, p):
|
def struct_print(self, padd: int) -> None:
|
||||||
printer(['Size :', f'0x{self.Size:X}'], p, False)
|
""" Display structure information """
|
||||||
printer(['Checksum:', f'0x{self.Checksum:04X}'], p, False)
|
|
||||||
printer(['Tag :', self.Tag.decode('utf-8')], p, False)
|
printer(['Size :', f'0x{self.Size:X}'], padd, False)
|
||||||
printer(['Flags :', f'0x{self.Flags:02X}'], p, 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):
|
class IntelBiosGuardHeader(ctypes.LittleEndianStructure):
|
||||||
|
""" Intel BIOS Guard Header """
|
||||||
|
|
||||||
_pack_ = 1
|
_pack_ = 1
|
||||||
|
|
||||||
|
# noinspection PyTypeChecker
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('BGVerMajor', uint16_t), # 0x00
|
('BGVerMajor', UInt16), # 0x00
|
||||||
('BGVerMinor', uint16_t), # 0x02
|
('BGVerMinor', UInt16), # 0x02
|
||||||
('PlatformID', uint8_t*16), # 0x04
|
('PlatformID', UInt8 * 16), # 0x04
|
||||||
('Attributes', uint32_t), # 0x14
|
('Attributes', UInt32), # 0x14
|
||||||
('ScriptVerMajor', uint16_t), # 0x16
|
('ScriptVerMajor', UInt16), # 0x16
|
||||||
('ScriptVerMinor', uint16_t), # 0x18
|
('ScriptVerMinor', UInt16), # 0x18
|
||||||
('ScriptSize', uint32_t), # 0x1C
|
('ScriptSize', UInt32), # 0x1C
|
||||||
('DataSize', uint32_t), # 0x20
|
('DataSize', UInt32), # 0x20
|
||||||
('BIOSSVN', uint32_t), # 0x24
|
('BIOSSVN', UInt32), # 0x24
|
||||||
('ECSVN', uint32_t), # 0x28
|
('ECSVN', UInt32), # 0x28
|
||||||
('VendorInfo', uint32_t), # 0x2C
|
('VendorInfo', UInt32), # 0x2C
|
||||||
# 0x30
|
# 0x30
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_platform_id(self):
|
def get_platform_id(self) -> str:
|
||||||
id_byte = bytes(self.PlatformID)
|
""" 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_text: str = re.sub(r'[\n\t\r\x00 ]', '', id_byte.decode('utf-8', 'ignore'))
|
||||||
id_guid = f'{{{id_hexs[:8]}-{id_hexs[8:12]}-{id_hexs[12:16]}-{id_hexs[16:20]}-{id_hexs[20:]}}}'
|
|
||||||
|
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}'
|
return f'{id_text} {id_guid}'
|
||||||
|
|
||||||
def get_flags(self):
|
def get_flags(self) -> tuple:
|
||||||
|
""" Get Intel BIOS Guard Header Attributes """
|
||||||
|
|
||||||
attr = IntelBiosGuardHeaderGetAttributes()
|
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
|
return attr.b.SFAM, attr.b.ProtectEC, attr.b.GFXMitDis, attr.b.FTU, attr.b.Reserved
|
||||||
|
|
||||||
def struct_print(self, p):
|
def struct_print(self, padd: int) -> None:
|
||||||
no_yes = ['No','Yes']
|
""" Display structure information """
|
||||||
f1,f2,f3,f4,f5 = self.get_flags()
|
|
||||||
|
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):
|
class IntelBiosGuardHeaderAttributes(ctypes.LittleEndianStructure):
|
||||||
|
""" Intel BIOS Guard Header Attributes """
|
||||||
|
|
||||||
|
_pack_ = 1
|
||||||
|
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('SFAM', uint32_t, 1), # Signed Flash Address Map
|
('SFAM', UInt32, 1), # Signed Flash Address Map
|
||||||
('ProtectEC', uint32_t, 1), # Protected EC OpCodes
|
('ProtectEC', UInt32, 1), # Protected EC OpCodes
|
||||||
('GFXMitDis', uint32_t, 1), # GFX Security Disable
|
('GFXMitDis', UInt32, 1), # GFX Security Disable
|
||||||
('FTU', uint32_t, 1), # Fault Tolerant Update
|
('FTU', UInt32, 1), # Fault Tolerant Update
|
||||||
('Reserved', uint32_t, 28) # Reserved/Unknown
|
('Reserved', UInt32, 28) # Reserved/Unknown
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class IntelBiosGuardHeaderGetAttributes(ctypes.Union):
|
class IntelBiosGuardHeaderGetAttributes(ctypes.Union):
|
||||||
|
""" Intel BIOS Guard Header Attributes Getter """
|
||||||
|
|
||||||
|
_pack_ = 1
|
||||||
|
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('b', IntelBiosGuardHeaderAttributes),
|
('b', IntelBiosGuardHeaderAttributes),
|
||||||
('asbytes', uint32_t)
|
('asbytes', UInt32)
|
||||||
]
|
]
|
||||||
|
|
||||||
class IntelBiosGuardSignature2k(ctypes.LittleEndianStructure):
|
|
||||||
|
class IntelBiosGuardSignatureHeader(ctypes.LittleEndianStructure):
|
||||||
|
""" Intel BIOS Guard Signature Header """
|
||||||
|
|
||||||
_pack_ = 1
|
_pack_ = 1
|
||||||
|
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('Unknown0', uint32_t), # 0x000
|
('Unknown0', UInt32), # 0x000
|
||||||
('Unknown1', uint32_t), # 0x004
|
('Unknown1', UInt32), # 0x004
|
||||||
('Modulus', uint32_t*64), # 0x008
|
# 0x8
|
||||||
('Exponent', uint32_t), # 0x108
|
|
||||||
('Signature', uint32_t*64), # 0x10C
|
|
||||||
# 0x20C
|
|
||||||
]
|
]
|
||||||
|
|
||||||
def struct_print(self, p):
|
def struct_print(self, padd: int) -> None:
|
||||||
Modulus = f'{int.from_bytes(self.Modulus, "little"):0{0x100 * 2}X}'
|
""" Display structure information """
|
||||||
Signature = f'{int.from_bytes(self.Signature, "little"):0{0x100 * 2}X}'
|
|
||||||
|
|
||||||
printer(['Unknown 0:', f'0x{self.Unknown0:X}'], p, False)
|
printer(['Unknown 0:', f'0x{self.Unknown0:X}'], padd, False)
|
||||||
printer(['Unknown 1:', f'0x{self.Unknown1:X}'], p, False)
|
printer(['Unknown 1:', f'0x{self.Unknown1:X}'], padd, False)
|
||||||
printer(['Modulus :', f'{Modulus[:32]} [...]'], p, False)
|
|
||||||
printer(['Exponent :', f'0x{self.Exponent:X}'], p, False)
|
|
||||||
printer(['Signature:', f'{Signature[:32]} [...]'], p, 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))
|
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)
|
match = PAT_AMI_PFAT.search(input_buffer)
|
||||||
|
|
||||||
return input_buffer[match.start() - 0x8:] if match else b''
|
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}')
|
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:
|
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
|
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:
|
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
|
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)
|
printer('Note: BIOS Guard Script Tool optional dependency is missing!', padding, False)
|
||||||
|
|
||||||
return 3
|
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:
|
for opcode in script:
|
||||||
if opcode.endswith(('begin','end')): spacing = padding
|
if opcode.endswith(('begin', 'end')):
|
||||||
elif opcode.endswith(':'): spacing = padding + 4
|
spacing: int = padding
|
||||||
else: spacing = padding + 12
|
elif opcode.endswith(':'):
|
||||||
|
spacing = padding + 4
|
||||||
|
else:
|
||||||
|
spacing = padding + 12
|
||||||
|
|
||||||
operands = [operand for operand in opcode.split(' ') if operand]
|
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
|
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)
|
pfat_hdr = get_struct(buffer, 0x0, AmiBiosGuardHeader)
|
||||||
|
|
||||||
hdr_size = pfat_hdr.Size
|
hdr_size: int = pfat_hdr.Size
|
||||||
hdr_data = buffer[PFAT_AMI_HDR_LEN:hdr_size]
|
|
||||||
hdr_text = hdr_data.decode('utf-8').splitlines()
|
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)
|
printer('AMI BIOS Guard Header:\n', padding)
|
||||||
|
|
||||||
pfat_hdr.struct_print(padding + 4)
|
pfat_hdr.struct_print(padding + 4)
|
||||||
|
|
||||||
hdr_title,*hdr_files = hdr_text
|
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')
|
hdr_tag, *hdr_indexes = hdr_title.split('II')
|
||||||
|
|
||||||
printer(hdr_tag + '\n', padding + 4)
|
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):
|
for index, entry in enumerate(hdr_files):
|
||||||
entry_parts = entry.split(';')
|
entry_parts: list = entry.split(';')
|
||||||
|
|
||||||
info = entry_parts[0].split()
|
info: list = entry_parts[0].split()
|
||||||
name = entry_parts[1]
|
|
||||||
|
|
||||||
flags = int(info[0])
|
name: str = entry_parts[1]
|
||||||
param = info[1]
|
|
||||||
count = int(info[2])
|
|
||||||
|
|
||||||
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)]
|
block_all += [(desc, name, order, param, flags, index, i, count) for i in range(count)]
|
||||||
|
|
||||||
|
@ -221,22 +335,26 @@ def parse_pfat_hdr(buffer, padding=0):
|
||||||
|
|
||||||
return block_all, hdr_size, files_count
|
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 = ''
|
input_buffer: bytes = file_to_bytes(input_object)
|
||||||
all_blocks_dict = {}
|
|
||||||
|
|
||||||
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)
|
make_dirs(extract_path, delete=True)
|
||||||
|
|
||||||
block_all,block_off,file_count = parse_pfat_hdr(pfat_buffer, padding)
|
block_all, block_off, file_count = parse_pfat_hdr(pfat_buffer, padding)
|
||||||
|
|
||||||
for block in block_all:
|
for block in block_all:
|
||||||
file_desc,file_name,_,_,_,file_index,block_index,block_count = block
|
file_desc, file_name, _, _, _, file_index, block_index, block_count = block
|
||||||
|
|
||||||
if block_index == 0:
|
if block_index == 0:
|
||||||
printer(file_desc, padding + 4)
|
printer(file_desc, padding + 4)
|
||||||
|
@ -245,7 +363,7 @@ def parse_pfat_file(input_file, extract_path, padding=0):
|
||||||
|
|
||||||
all_blocks_dict[file_index] = b''
|
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)
|
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_hdr.struct_print(padding + 12)
|
||||||
|
|
||||||
bg_script_bgn = block_off + PFAT_BLK_HDR_LEN
|
bg_script_bgn: int = block_off + PFAT_BLK_HDR_LEN
|
||||||
bg_script_end = bg_script_bgn + bg_hdr.ScriptSize
|
bg_script_end: int = bg_script_bgn + bg_hdr.ScriptSize
|
||||||
bg_script_bin = pfat_buffer[bg_script_bgn:bg_script_end]
|
|
||||||
|
|
||||||
bg_data_bgn = bg_script_end
|
bg_data_bgn: int = bg_script_end
|
||||||
bg_data_end = bg_data_bgn + bg_hdr.DataSize
|
bg_data_end: int = bg_data_bgn + bg_hdr.DataSize
|
||||||
bg_data_bin = pfat_buffer[bg_data_bgn:bg_data_end]
|
|
||||||
|
|
||||||
block_off = bg_data_end # Assume next block starts at data end
|
bg_data_bin: bytes = pfat_buffer[bg_data_bgn:bg_data_end]
|
||||||
|
|
||||||
is_sfam,_,_,_,_ = bg_hdr.get_flags() # SFAM, ProtectEC, GFXMitDis, FTU, Reserved
|
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:
|
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)
|
printer(f'Intel BIOS Guard {block_status} Signature:\n', padding + 8)
|
||||||
|
|
||||||
bg_sig.struct_print(padding + 12)
|
# Adjust next block to start after current block Data + Signature
|
||||||
|
block_off += parse_bg_sign(pfat_buffer, bg_data_end, True, padding + 12)
|
||||||
block_off = bg_sig_end # Adjust next block to start at data + signature end
|
|
||||||
|
|
||||||
printer(f'Intel BIOS Guard {block_status} Script:\n', padding + 8)
|
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:
|
with open(file_path, 'ab') as out_dat:
|
||||||
out_dat.write(bg_data_bin)
|
out_dat.write(bg_data_bin)
|
||||||
|
|
||||||
all_blocks_dict[file_index] += 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:
|
with open(pfat_oob_path, 'wb') as out_oob:
|
||||||
out_oob.write(pfat_oob_data)
|
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):
|
if is_ami_pfat(pfat_oob_data):
|
||||||
parse_pfat_file(pfat_oob_data, get_extract_path(pfat_oob_path), padding)
|
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:
|
with open(in_all_path, 'wb') as out_all:
|
||||||
out_all.write(in_all_data + pfat_oob_data)
|
out_all.write(in_all_data + pfat_oob_data)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
PFAT_AMI_HDR_LEN = ctypes.sizeof(AmiBiosGuardHeader)
|
|
||||||
PFAT_BLK_HDR_LEN = ctypes.sizeof(IntelBiosGuardHeader)
|
PFAT_AMI_HDR_LEN: int = ctypes.sizeof(AmiBiosGuardHeader)
|
||||||
PFAT_BLK_S2K_LEN = ctypes.sizeof(IntelBiosGuardSignature2k)
|
PFAT_BLK_HDR_LEN: int = ctypes.sizeof(IntelBiosGuardHeader)
|
||||||
|
PFAT_BLK_SIG_LEN: int = ctypes.sizeof(IntelBiosGuardSignatureHeader)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
AMI UCP Extract
|
AMI UCP Extract
|
||||||
AMI UCP Update Extractor
|
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 os
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
import struct
|
import struct
|
||||||
import ctypes
|
|
||||||
import contextlib
|
|
||||||
|
|
||||||
# Stop __pycache__ generation
|
|
||||||
sys.dont_write_bytecode = True
|
|
||||||
|
|
||||||
from common.checksums import get_chk_16
|
from common.checksums import get_chk_16
|
||||||
from common.comp_efi import efi_decompress, is_efi_compressed
|
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.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.system import printer
|
||||||
from common.templates import BIOSUtility
|
from common.templates import BIOSUtility
|
||||||
from common.text_ops import file_to_bytes, to_string
|
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 AMI_PFAT_Extract import is_ami_pfat, parse_pfat_file
|
||||||
from Insyde_IFD_Extract import insyde_ifd_extract, is_insyde_ifd
|
from Insyde_IFD_Extract import insyde_ifd_extract, is_insyde_ifd
|
||||||
|
|
||||||
|
TITLE = 'AMI UCP Update Extractor v3.0'
|
||||||
|
|
||||||
|
|
||||||
class UafHeader(ctypes.LittleEndianStructure):
|
class UafHeader(ctypes.LittleEndianStructure):
|
||||||
|
""" UAF Header """
|
||||||
|
|
||||||
_pack_ = 1
|
_pack_ = 1
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('ModuleTag', char*4), # 0x00
|
('ModuleTag', Char * 4), # 0x00
|
||||||
('ModuleSize', uint32_t), # 0x04
|
('ModuleSize', UInt32), # 0x04
|
||||||
('Checksum', uint16_t), # 0x08
|
('Checksum', UInt16), # 0x08
|
||||||
('Unknown0', uint8_t), # 0x0A
|
('Unknown0', UInt8), # 0x0A
|
||||||
('Unknown1', uint8_t), # 0x0A
|
('Unknown1', UInt8), # 0x0A
|
||||||
('Reserved', uint8_t*4), # 0x0C
|
('Reserved', UInt8 * 4), # 0x0C
|
||||||
# 0x10
|
# 0x10
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -48,47 +47,57 @@ class UafHeader(ctypes.LittleEndianStructure):
|
||||||
|
|
||||||
res_hex = f'0x{int.from_bytes(res_bytes, "big"):0{0x4 * 2}X}'
|
res_hex = f'0x{int.from_bytes(res_bytes, "big"):0{0x4 * 2}X}'
|
||||||
|
|
||||||
res_str = re.sub(r'[\n\t\r\x00 ]', '', res_bytes.decode('utf-8','ignore'))
|
res_str = re.sub(r'[\n\t\r\x00 ]', '', res_bytes.decode('utf-8', 'ignore'))
|
||||||
|
|
||||||
res_txt = f' ({res_str})' if len(res_str) else ''
|
res_txt = f' ({res_str})' if len(res_str) else ''
|
||||||
|
|
||||||
return f'{res_hex}{res_txt}'
|
return f'{res_hex}{res_txt}'
|
||||||
|
|
||||||
def struct_print(self, p):
|
def struct_print(self, padd):
|
||||||
printer(['Tag :', self.ModuleTag.decode('utf-8')], p, False)
|
""" Display structure information """
|
||||||
printer(['Size :', f'0x{self.ModuleSize:X}'], p, False)
|
|
||||||
printer(['Checksum :', f'0x{self.Checksum:04X}'], p, False)
|
printer(['Tag :', self.ModuleTag.decode('utf-8')], padd, False)
|
||||||
printer(['Unknown 0 :', f'0x{self.Unknown0:02X}'], p, False)
|
printer(['Size :', f'0x{self.ModuleSize:X}'], padd, False)
|
||||||
printer(['Unknown 1 :', f'0x{self.Unknown1:02X}'], p, False)
|
printer(['Checksum :', f'0x{self.Checksum:04X}'], padd, False)
|
||||||
printer(['Reserved :', self._get_reserved()], p, 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):
|
class UafModule(ctypes.LittleEndianStructure):
|
||||||
|
""" UAF Module """
|
||||||
|
|
||||||
_pack_ = 1
|
_pack_ = 1
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('CompressSize', uint32_t), # 0x00
|
('CompressSize', UInt32), # 0x00
|
||||||
('OriginalSize', uint32_t), # 0x04
|
('OriginalSize', UInt32), # 0x04
|
||||||
# 0x08
|
# 0x08
|
||||||
]
|
]
|
||||||
|
|
||||||
def struct_print(self, p, filename, description):
|
def struct_print(self, padd, filename, description):
|
||||||
printer(['Compress Size:', f'0x{self.CompressSize:X}'], p, False)
|
""" Display structure information """
|
||||||
printer(['Original Size:', f'0x{self.OriginalSize:X}'], p, False)
|
|
||||||
printer(['Filename :', filename], p, False)
|
printer(['Compress Size:', f'0x{self.CompressSize:X}'], padd, False)
|
||||||
printer(['Description :', description], p, 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):
|
class UiiHeader(ctypes.LittleEndianStructure):
|
||||||
|
""" UII Header """
|
||||||
|
|
||||||
_pack_ = 1
|
_pack_ = 1
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('UIISize', uint16_t), # 0x00
|
('UIISize', UInt16), # 0x00
|
||||||
('Checksum', uint16_t), # 0x02
|
('Checksum', UInt16), # 0x02
|
||||||
('UtilityVersion', uint32_t), # 0x04 AFU|BGT (Unknown, Signed)
|
('UtilityVersion', UInt32), # 0x04 AFU|BGT (Unknown, Signed)
|
||||||
('InfoSize', uint16_t), # 0x08
|
('InfoSize', UInt16), # 0x08
|
||||||
('SupportBIOS', uint8_t), # 0x0A
|
('SupportBIOS', UInt8), # 0x0A
|
||||||
('SupportOS', uint8_t), # 0x0B
|
('SupportOS', UInt8), # 0x0B
|
||||||
('DataBusWidth', uint8_t), # 0x0C
|
('DataBusWidth', UInt8), # 0x0C
|
||||||
('ProgramType', uint8_t), # 0x0D
|
('ProgramType', UInt8), # 0x0D
|
||||||
('ProgramMode', uint8_t), # 0x0E
|
('ProgramMode', UInt8), # 0x0E
|
||||||
('SourceSafeRel', uint8_t), # 0x0F
|
('SourceSafeRel', UInt8), # 0x0F
|
||||||
# 0x10
|
# 0x10
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -98,76 +107,94 @@ class UiiHeader(ctypes.LittleEndianStructure):
|
||||||
PTP = {1: 'Executable', 2: 'Library', 3: 'Driver'}
|
PTP = {1: 'Executable', 2: 'Library', 3: 'Driver'}
|
||||||
PMD = {1: 'API', 2: 'Console', 3: 'GUI', 4: 'Console/GUI'}
|
PMD = {1: 'API', 2: 'Console', 3: 'GUI', 4: 'Console/GUI'}
|
||||||
|
|
||||||
def struct_print(self, p, description):
|
def struct_print(self, padd, description):
|
||||||
SupportBIOS = self.SBI.get(self.SupportBIOS, f'Unknown ({self.SupportBIOS})')
|
""" Display structure information """
|
||||||
SupportOS = self.SOS.get(self.SupportOS, f'Unknown ({self.SupportOS})')
|
|
||||||
DataBusWidth = self.DBW.get(self.DataBusWidth, f'Unknown ({self.DataBusWidth})')
|
support_bios = self.SBI.get(self.SupportBIOS, f'Unknown ({self.SupportBIOS})')
|
||||||
ProgramType = self.PTP.get(self.ProgramType, f'Unknown ({self.ProgramType})')
|
support_os = self.SOS.get(self.SupportOS, f'Unknown ({self.SupportOS})')
|
||||||
ProgramMode = self.PMD.get(self.ProgramMode, f'Unknown ({self.ProgramMode})')
|
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):
|
class DisHeader(ctypes.LittleEndianStructure):
|
||||||
|
""" DIS Header """
|
||||||
|
|
||||||
_pack_ = 1
|
_pack_ = 1
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('PasswordSize', uint16_t), # 0x00
|
('PasswordSize', UInt16), # 0x00
|
||||||
('EntryCount', uint16_t), # 0x02
|
('EntryCount', UInt16), # 0x02
|
||||||
('Password', char*12), # 0x04
|
('Password', Char * 12), # 0x04
|
||||||
# 0x10
|
# 0x10
|
||||||
]
|
]
|
||||||
|
|
||||||
def struct_print(self, p):
|
def struct_print(self, padd):
|
||||||
printer(['Password Size:', f'0x{self.PasswordSize:X}'], p, False)
|
""" Display structure information """
|
||||||
printer(['Entry Count :', self.EntryCount], p, False)
|
|
||||||
printer(['Password :', self.Password.decode('utf-8')], p, False)
|
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):
|
class DisModule(ctypes.LittleEndianStructure):
|
||||||
|
""" DIS Module """
|
||||||
|
|
||||||
_pack_ = 1
|
_pack_ = 1
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('EnabledDisabled', uint8_t), # 0x00
|
('EnabledDisabled', UInt8), # 0x00
|
||||||
('ShownHidden', uint8_t), # 0x01
|
('ShownHidden', UInt8), # 0x01
|
||||||
('Command', char*32), # 0x02
|
('Command', Char * 32), # 0x02
|
||||||
('Description', char*256), # 0x22
|
('Description', Char * 256), # 0x22
|
||||||
# 0x122
|
# 0x122
|
||||||
]
|
]
|
||||||
|
|
||||||
ENDIS = {0: 'Disabled', 1: 'Enabled'}
|
ENDIS = {0: 'Disabled', 1: 'Enabled'}
|
||||||
SHOWN = {0: 'Hidden', 1: 'Shown', 2: 'Shown Only'}
|
SHOWN = {0: 'Hidden', 1: 'Shown', 2: 'Shown Only'}
|
||||||
|
|
||||||
def struct_print(self, p):
|
def struct_print(self, padd):
|
||||||
EnabledDisabled = self.ENDIS.get(self.EnabledDisabled, f'Unknown ({self.EnabledDisabled})')
|
""" Display structure information """
|
||||||
ShownHidden = self.SHOWN.get(self.ShownHidden, f'Unknown ({self.ShownHidden})')
|
|
||||||
|
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):
|
def chk16_validate(data, tag, padd=0):
|
||||||
|
""" Validate UCP Module Checksum-16 """
|
||||||
|
|
||||||
if get_chk_16(data) != 0:
|
if get_chk_16(data) != 0:
|
||||||
printer(f'Error: Invalid UCP Module {tag} Checksum!', padd, pause=True)
|
printer(f'Error: Invalid UCP Module {tag} Checksum!', padd, pause=True)
|
||||||
else:
|
else:
|
||||||
printer(f'Checksum of UCP Module {tag} is valid!', padd)
|
printer(f'Checksum of UCP Module {tag} is valid!', padd)
|
||||||
|
|
||||||
# Check if input is AMI UCP image
|
|
||||||
def is_ami_ucp(in_file):
|
def is_ami_ucp(in_file):
|
||||||
|
""" Check if input is AMI UCP image """
|
||||||
|
|
||||||
buffer = file_to_bytes(in_file)
|
buffer = file_to_bytes(in_file)
|
||||||
|
|
||||||
return bool(get_ami_ucp(buffer)[0] is not None)
|
return bool(get_ami_ucp(buffer)[0] is not None)
|
||||||
|
|
||||||
# Get all input file AMI UCP patterns
|
|
||||||
def get_ami_ucp(in_file):
|
def get_ami_ucp(in_file):
|
||||||
|
""" Get all input file AMI UCP patterns """
|
||||||
|
|
||||||
buffer = file_to_bytes(in_file)
|
buffer = file_to_bytes(in_file)
|
||||||
|
|
||||||
uaf_len_max = 0x0 # Length of largest detected @UAF|@HPU
|
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:
|
if uaf_len_cur > uaf_len_max:
|
||||||
uaf_len_max = uaf_len_cur
|
uaf_len_max = uaf_len_cur
|
||||||
|
|
||||||
uaf_hdr_off = uaf.start()
|
uaf_hdr_off = uaf.start()
|
||||||
|
|
||||||
uaf_buf_bin = buffer[uaf_hdr_off:uaf_hdr_off + uaf_len_max]
|
uaf_buf_bin = buffer[uaf_hdr_off:uaf_hdr_off + uaf_len_max]
|
||||||
uaf_buf_tag = uaf.group(0)[:4].decode('utf-8','ignore')
|
|
||||||
|
uaf_buf_tag = uaf.group(0)[:4].decode('utf-8', 'ignore')
|
||||||
|
|
||||||
return uaf_buf_bin, uaf_buf_tag
|
return uaf_buf_bin, uaf_buf_tag
|
||||||
|
|
||||||
# Get list of @UAF|@HPU Modules
|
|
||||||
def get_uaf_mod(buffer, uaf_off=0x0):
|
def get_uaf_mod(buffer, uaf_off=0x0):
|
||||||
|
""" Get list of @UAF|@HPU Modules """
|
||||||
|
|
||||||
uaf_all = [] # Initialize list of all @UAF|@HPU Modules
|
uaf_all = [] # Initialize list of all @UAF|@HPU Modules
|
||||||
|
|
||||||
while buffer[uaf_off] == 0x40: # ASCII of @ is 0x40
|
while buffer[uaf_off] == 0x40: # ASCII of @ is 0x40
|
||||||
|
@ -203,7 +235,7 @@ def get_uaf_mod(buffer, uaf_off=0x0):
|
||||||
|
|
||||||
# Check if @UAF|@HPU Module @NAL exists and place it first
|
# Check if @UAF|@HPU Module @NAL exists and place it first
|
||||||
# Parsing @NAL first allows naming all @UAF|@HPU Modules
|
# Parsing @NAL first allows naming all @UAF|@HPU Modules
|
||||||
for mod_idx,mod_val in enumerate(uaf_all):
|
for mod_idx, mod_val in enumerate(uaf_all):
|
||||||
if mod_val[0] == '@NAL':
|
if mod_val[0] == '@NAL':
|
||||||
uaf_all.insert(1, uaf_all.pop(mod_idx)) # After UII for visual purposes
|
uaf_all.insert(1, uaf_all.pop(mod_idx)) # After UII for visual purposes
|
||||||
|
|
||||||
|
@ -211,8 +243,10 @@ def get_uaf_mod(buffer, uaf_off=0x0):
|
||||||
|
|
||||||
return uaf_all
|
return uaf_all
|
||||||
|
|
||||||
# Parse & Extract AMI UCP structures
|
|
||||||
def ucp_extract(in_file, extract_path, padding=0, checksum=False):
|
def ucp_extract(in_file, extract_path, padding=0, checksum=False):
|
||||||
|
""" Parse & Extract AMI UCP structures """
|
||||||
|
|
||||||
input_buffer = file_to_bytes(in_file)
|
input_buffer = file_to_bytes(in_file)
|
||||||
|
|
||||||
nal_dict = {} # Initialize @NAL Dictionary per UCP
|
nal_dict = {} # Initialize @NAL Dictionary per UCP
|
||||||
|
@ -222,7 +256,7 @@ def ucp_extract(in_file, extract_path, padding=0, checksum=False):
|
||||||
make_dirs(extract_path, delete=True)
|
make_dirs(extract_path, delete=True)
|
||||||
|
|
||||||
# Get best AMI UCP Pattern match based on @UAF|@HPU Size
|
# Get best AMI UCP Pattern match based on @UAF|@HPU Size
|
||||||
ucp_buffer,ucp_tag = get_ami_ucp(input_buffer)
|
ucp_buffer, ucp_tag = get_ami_ucp(input_buffer)
|
||||||
|
|
||||||
uaf_hdr = get_struct(ucp_buffer, 0, UafHeader) # Parse @UAF|@HPU Header Structure
|
uaf_hdr = get_struct(ucp_buffer, 0, UafHeader) # Parse @UAF|@HPU Header Structure
|
||||||
|
|
||||||
|
@ -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_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_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_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
|
uaf_mod.struct_print(padding + 8, uaf_name, uaf_desc) # Print @UAF|@HPU Module EFI Info
|
||||||
|
@ -247,12 +282,14 @@ def ucp_extract(in_file, extract_path, padding=0, checksum=False):
|
||||||
for mod_info in uaf_all:
|
for mod_info in uaf_all:
|
||||||
nal_dict = uaf_extract(ucp_buffer, extract_path, mod_info, padding + 8, checksum, nal_dict)
|
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):
|
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:
|
if nal_dict is None:
|
||||||
nal_dict = {}
|
nal_dict = {}
|
||||||
|
|
||||||
uaf_tag,uaf_off,uaf_hdr = mod_info
|
uaf_tag, uaf_off, uaf_hdr = mod_info
|
||||||
|
|
||||||
uaf_data_all = buffer[uaf_off:uaf_off + uaf_hdr.ModuleSize] # @UAF|@HPU Module Entire Data
|
uaf_data_all = buffer[uaf_off:uaf_off + uaf_hdr.ModuleSize] # @UAF|@HPU Module Entire Data
|
||||||
|
|
||||||
|
@ -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
|
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
|
# 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')):
|
if uaf_tag in nal_dict and uaf_tag not in UAF_TAG_DICT and \
|
||||||
printer(f'Note: Detected new AMI UCP Module {uaf_tag} ({nal_dict[uaf_tag][1]}) in @NAL!', padding + 4, pause=True)
|
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
|
# 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))
|
uaf_sname = safe_name(uaf_name + ('.temp' if is_comp else uaf_fext))
|
||||||
|
|
||||||
if uaf_tag in nal_dict:
|
if uaf_tag in nal_dict:
|
||||||
uaf_npath = safe_path(extract_path, nal_dict[uaf_tag][0])
|
uaf_npath = safe_path(extract_path, nal_dict[uaf_tag][0])
|
||||||
|
|
||||||
make_dirs(uaf_npath, exist_ok=True)
|
make_dirs(uaf_npath, exist_ok=True)
|
||||||
|
|
||||||
uaf_fname = safe_path(uaf_npath, uaf_sname)
|
uaf_fname = safe_path(uaf_npath, uaf_sname)
|
||||||
else:
|
else:
|
||||||
uaf_fname = safe_path(extract_path, uaf_sname)
|
uaf_fname = safe_path(extract_path, uaf_sname)
|
||||||
|
@ -315,10 +358,10 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, checksum=False, nal_d
|
||||||
if uaf_tag == '@UII':
|
if uaf_tag == '@UII':
|
||||||
info_hdr = get_struct(uaf_data_raw, 0, UiiHeader) # Parse @UII Module Raw Structure
|
info_hdr = get_struct(uaf_data_raw, 0, UiiHeader) # Parse @UII Module Raw Structure
|
||||||
|
|
||||||
info_data = uaf_data_raw[max(UII_HDR_LEN,info_hdr.InfoSize):info_hdr.UIISize] # @UII Module Info Data
|
info_data = uaf_data_raw[max(UII_HDR_LEN, info_hdr.InfoSize):info_hdr.UIISize] # @UII Module Info Data
|
||||||
|
|
||||||
# Get @UII Module Info/Description text field
|
# Get @UII Module Info/Description text field
|
||||||
info_desc = info_data.decode('utf-8','ignore').strip('\x00 ')
|
info_desc = info_data.decode('utf-8', 'ignore').strip('\x00 ')
|
||||||
|
|
||||||
printer('Utility Identification Information:\n', padding + 4)
|
printer('Utility Identification Information:\n', padding + 4)
|
||||||
|
|
||||||
|
@ -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
|
# Some Compressed @UAF|@HPU Module EFI data lack necessary EOF padding
|
||||||
if uaf_mod.CompressSize > len(uaf_data_raw):
|
if uaf_mod.CompressSize > len(uaf_data_raw):
|
||||||
comp_padd = b'\x00' * (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:
|
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:
|
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
|
# Store/Save @UAF|@HPU Module file
|
||||||
if uaf_tag != '@UII': # Skip @UII binary, already parsed
|
if uaf_tag != '@UII': # Skip @UII binary, already parsed
|
||||||
|
@ -363,7 +410,8 @@ 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)
|
# 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':
|
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(f'{UAF_TAG_DICT[uaf_tag][1]}:', padding + 4)
|
||||||
printer(uaf_data_raw.decode('utf-8','ignore'), padding + 8)
|
|
||||||
|
printer(uaf_data_raw.decode('utf-8', 'ignore'), padding + 8)
|
||||||
|
|
||||||
# Parse Default Command Status @UAF|@HPU Module (@DIS)
|
# Parse Default Command Status @UAF|@HPU Module (@DIS)
|
||||||
if len(uaf_data_raw) and uaf_tag == '@DIS':
|
if len(uaf_data_raw) and uaf_tag == '@DIS':
|
||||||
|
@ -392,27 +440,30 @@ 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 open(uaf_fname[:-3] + 'txt', 'a', encoding='utf-8') as dis:
|
||||||
with contextlib.redirect_stdout(dis):
|
with contextlib.redirect_stdout(dis):
|
||||||
printer()
|
printer()
|
||||||
|
|
||||||
dis_mod.struct_print(4) # Store @DIS Module Entry Info
|
dis_mod.struct_print(4) # Store @DIS Module Entry Info
|
||||||
|
|
||||||
os.remove(uaf_fname) # Delete @DIS Module binary, info exported as text
|
os.remove(uaf_fname) # Delete @DIS Module binary, info exported as text
|
||||||
|
|
||||||
# Parse Name List @UAF|@HPU Module (@NAL)
|
# Parse Name List @UAF|@HPU Module (@NAL)
|
||||||
if len(uaf_data_raw) >= 5 and (uaf_tag,uaf_data_raw[0],uaf_data_raw[4]) == ('@NAL',0x40,0x3A):
|
if len(uaf_data_raw) >= 5 and (uaf_tag, uaf_data_raw[0], uaf_data_raw[4]) == ('@NAL', 0x40, 0x3A):
|
||||||
nal_info = uaf_data_raw.decode('utf-8','ignore').replace('\r','').strip().split('\n')
|
nal_info = uaf_data_raw.decode('utf-8', 'ignore').replace('\r', '').strip().split('\n')
|
||||||
|
|
||||||
printer('AMI UCP Module Name List:\n', padding + 4)
|
printer('AMI UCP Module Name List:\n', padding + 4)
|
||||||
|
|
||||||
# Parse all @NAL Module Entries
|
# Parse all @NAL Module Entries
|
||||||
for info in nal_info:
|
for info in nal_info:
|
||||||
info_tag,info_value = info.split(':',1)
|
info_tag, info_value = info.split(':', 1)
|
||||||
|
|
||||||
printer(f'{info_tag} : {info_value}', padding + 8, False) # Print @NAL Module Tag-Path Info
|
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_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_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
|
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
|
nal_dict[info_tag] = (info_path, info_name) # Assign a file path & name to each Tag
|
||||||
|
|
||||||
# Parse Insyde BIOS @UAF|@HPU Module (@INS)
|
# Parse Insyde BIOS @UAF|@HPU Module (@INS)
|
||||||
if uaf_tag == '@INS' and is_insyde_ifd(uaf_fname):
|
if uaf_tag == '@INS' and is_insyde_ifd(uaf_fname):
|
||||||
|
@ -444,6 +495,7 @@ def uaf_extract(buffer, extract_path, mod_info, padding=0, checksum=False, nal_d
|
||||||
|
|
||||||
return nal_dict
|
return nal_dict
|
||||||
|
|
||||||
|
|
||||||
# Get common ctypes Structure Sizes
|
# Get common ctypes Structure Sizes
|
||||||
UAF_HDR_LEN = ctypes.sizeof(UafHeader)
|
UAF_HDR_LEN = ctypes.sizeof(UafHeader)
|
||||||
UAF_MOD_LEN = ctypes.sizeof(UafModule)
|
UAF_MOD_LEN = ctypes.sizeof(UafModule)
|
||||||
|
@ -453,63 +505,66 @@ UII_HDR_LEN = ctypes.sizeof(UiiHeader)
|
||||||
|
|
||||||
# AMI UCP Tag Dictionary
|
# AMI UCP Tag Dictionary
|
||||||
UAF_TAG_DICT = {
|
UAF_TAG_DICT = {
|
||||||
'@3FI' : ['HpBiosUpdate32.efi', 'HpBiosUpdate32.efi', ''],
|
'@3FI': ['HpBiosUpdate32.efi', 'HpBiosUpdate32.efi', ''],
|
||||||
'@3S2' : ['HpBiosUpdate32.s12', 'HpBiosUpdate32.s12', ''],
|
'@3S2': ['HpBiosUpdate32.s12', 'HpBiosUpdate32.s12', ''],
|
||||||
'@3S4' : ['HpBiosUpdate32.s14', 'HpBiosUpdate32.s14', ''],
|
'@3S4': ['HpBiosUpdate32.s14', 'HpBiosUpdate32.s14', ''],
|
||||||
'@3S9' : ['HpBiosUpdate32.s09', 'HpBiosUpdate32.s09', ''],
|
'@3S9': ['HpBiosUpdate32.s09', 'HpBiosUpdate32.s09', ''],
|
||||||
'@3SG' : ['HpBiosUpdate32.sig', 'HpBiosUpdate32.sig', ''],
|
'@3SG': ['HpBiosUpdate32.sig', 'HpBiosUpdate32.sig', ''],
|
||||||
'@AMI' : ['UCP_Nested.bin', 'Nested AMI UCP', ''],
|
'@AMI': ['UCP_Nested.bin', 'Nested AMI UCP', ''],
|
||||||
'@B12' : ['BiosMgmt.s12', 'BiosMgmt.s12', ''],
|
'@B12': ['BiosMgmt.s12', 'BiosMgmt.s12', ''],
|
||||||
'@B14' : ['BiosMgmt.s14', 'BiosMgmt.s14', ''],
|
'@B14': ['BiosMgmt.s14', 'BiosMgmt.s14', ''],
|
||||||
'@B32' : ['BiosMgmt32.s12', 'BiosMgmt32.s12', ''],
|
'@B32': ['BiosMgmt32.s12', 'BiosMgmt32.s12', ''],
|
||||||
'@B34' : ['BiosMgmt32.s14', 'BiosMgmt32.s14', ''],
|
'@B34': ['BiosMgmt32.s14', 'BiosMgmt32.s14', ''],
|
||||||
'@B39' : ['BiosMgmt32.s09', 'BiosMgmt32.s09', ''],
|
'@B39': ['BiosMgmt32.s09', 'BiosMgmt32.s09', ''],
|
||||||
'@B3E' : ['BiosMgmt32.efi', 'BiosMgmt32.efi', ''],
|
'@B3E': ['BiosMgmt32.efi', 'BiosMgmt32.efi', ''],
|
||||||
'@BM9' : ['BiosMgmt.s09', 'BiosMgmt.s09', ''],
|
'@BM9': ['BiosMgmt.s09', 'BiosMgmt.s09', ''],
|
||||||
'@BME' : ['BiosMgmt.efi', 'BiosMgmt.efi', ''],
|
'@BME': ['BiosMgmt.efi', 'BiosMgmt.efi', ''],
|
||||||
'@CKV' : ['Check_Version.txt', 'Check Version', 'Text'],
|
'@CKV': ['Check_Version.txt', 'Check Version', 'Text'],
|
||||||
'@CMD' : ['AFU_Command.txt', 'AMI AFU Command', 'Text'],
|
'@CMD': ['AFU_Command.txt', 'AMI AFU Command', 'Text'],
|
||||||
'@CML' : ['CMOSD4.txt', 'CMOS Item Number-Value (MSI)', 'Text'],
|
'@CML': ['CMOSD4.txt', 'CMOS Item Number-Value (MSI)', 'Text'],
|
||||||
'@CMS' : ['CMOSD4.exe', 'Get or Set CMOS Item (MSI)', ''],
|
'@CMS': ['CMOSD4.exe', 'Get or Set CMOS Item (MSI)', ''],
|
||||||
'@CPM' : ['AC_Message.txt', 'Confirm Power Message', ''],
|
'@CPM': ['AC_Message.txt', 'Confirm Power Message', ''],
|
||||||
'@DCT' : ['DevCon32.exe', 'Device Console WIN32', ''],
|
'@DCT': ['DevCon32.exe', 'Device Console WIN32', ''],
|
||||||
'@DCX' : ['DevCon64.exe', 'Device Console WIN64', ''],
|
'@DCX': ['DevCon64.exe', 'Device Console WIN64', ''],
|
||||||
'@DFE' : ['HpDevFwUpdate.efi', 'HpDevFwUpdate.efi', ''],
|
'@DFE': ['HpDevFwUpdate.efi', 'HpDevFwUpdate.efi', ''],
|
||||||
'@DFS' : ['HpDevFwUpdate.s12', 'HpDevFwUpdate.s12', ''],
|
'@DFS': ['HpDevFwUpdate.s12', 'HpDevFwUpdate.s12', ''],
|
||||||
'@DIS' : ['Command_Status.bin', 'Default Command Status', ''],
|
'@DIS': ['Command_Status.bin', 'Default Command Status', ''],
|
||||||
'@ENB' : ['ENBG64.exe', 'ENBG64.exe', ''],
|
'@ENB': ['ENBG64.exe', 'ENBG64.exe', ''],
|
||||||
'@HPU' : ['UCP_Main.bin', 'Utility Auxiliary File (HP)', ''],
|
'@HPU': ['UCP_Main.bin', 'Utility Auxiliary File (HP)', ''],
|
||||||
'@INS' : ['Insyde_Nested.bin', 'Nested Insyde SFX', ''],
|
'@INS': ['Insyde_Nested.bin', 'Nested Insyde SFX', ''],
|
||||||
'@M32' : ['HpBiosMgmt32.s12', 'HpBiosMgmt32.s12', ''],
|
'@M32': ['HpBiosMgmt32.s12', 'HpBiosMgmt32.s12', ''],
|
||||||
'@M34' : ['HpBiosMgmt32.s14', 'HpBiosMgmt32.s14', ''],
|
'@M34': ['HpBiosMgmt32.s14', 'HpBiosMgmt32.s14', ''],
|
||||||
'@M39' : ['HpBiosMgmt32.s09', 'HpBiosMgmt32.s09', ''],
|
'@M39': ['HpBiosMgmt32.s09', 'HpBiosMgmt32.s09', ''],
|
||||||
'@M3I' : ['HpBiosMgmt32.efi', 'HpBiosMgmt32.efi', ''],
|
'@M3I': ['HpBiosMgmt32.efi', 'HpBiosMgmt32.efi', ''],
|
||||||
'@MEC' : ['FWUpdLcl.txt', 'Intel FWUpdLcl Command', 'Text'],
|
'@MEC': ['FWUpdLcl.txt', 'Intel FWUpdLcl Command', 'Text'],
|
||||||
'@MED' : ['FWUpdLcl_DOS.exe', 'Intel FWUpdLcl DOS', ''],
|
'@MED': ['FWUpdLcl_DOS.exe', 'Intel FWUpdLcl DOS', ''],
|
||||||
'@MET' : ['FWUpdLcl_WIN32.exe', 'Intel FWUpdLcl WIN32', ''],
|
'@MET': ['FWUpdLcl_WIN32.exe', 'Intel FWUpdLcl WIN32', ''],
|
||||||
'@MFI' : ['HpBiosMgmt.efi', 'HpBiosMgmt.efi', ''],
|
'@MFI': ['HpBiosMgmt.efi', 'HpBiosMgmt.efi', ''],
|
||||||
'@MS2' : ['HpBiosMgmt.s12', 'HpBiosMgmt.s12', ''],
|
'@MS2': ['HpBiosMgmt.s12', 'HpBiosMgmt.s12', ''],
|
||||||
'@MS4' : ['HpBiosMgmt.s14', 'HpBiosMgmt.s14', ''],
|
'@MS4': ['HpBiosMgmt.s14', 'HpBiosMgmt.s14', ''],
|
||||||
'@MS9' : ['HpBiosMgmt.s09', 'HpBiosMgmt.s09', ''],
|
'@MS9': ['HpBiosMgmt.s09', 'HpBiosMgmt.s09', ''],
|
||||||
'@NAL' : ['UCP_List.txt', 'AMI UCP Module Name List', ''],
|
'@NAL': ['UCP_List.txt', 'AMI UCP Module Name List', ''],
|
||||||
'@OKM' : ['OK_Message.txt', 'OK Message', ''],
|
'@OKM': ['OK_Message.txt', 'OK Message', ''],
|
||||||
'@PFC' : ['BGT_Command.txt', 'AMI BGT Command', 'Text'],
|
'@PFC': ['BGT_Command.txt', 'AMI BGT Command', 'Text'],
|
||||||
'@R3I' : ['CryptRSA32.efi', 'CryptRSA32.efi', ''],
|
'@R3I': ['CryptRSA32.efi', 'CryptRSA32.efi', ''],
|
||||||
'@RFI' : ['CryptRSA.efi', 'CryptRSA.efi', ''],
|
'@RFI': ['CryptRSA.efi', 'CryptRSA.efi', ''],
|
||||||
'@UAF' : ['UCP_Main.bin', 'Utility Auxiliary File (AMI)', ''],
|
'@UAF': ['UCP_Main.bin', 'Utility Auxiliary File (AMI)', ''],
|
||||||
'@UFI' : ['HpBiosUpdate.efi', 'HpBiosUpdate.efi', ''],
|
'@UFI': ['HpBiosUpdate.efi', 'HpBiosUpdate.efi', ''],
|
||||||
'@UII' : ['UCP_Info.txt', 'Utility Identification Information', ''],
|
'@UII': ['UCP_Info.txt', 'Utility Identification Information', ''],
|
||||||
'@US2' : ['HpBiosUpdate.s12', 'HpBiosUpdate.s12', ''],
|
'@US2': ['HpBiosUpdate.s12', 'HpBiosUpdate.s12', ''],
|
||||||
'@US4' : ['HpBiosUpdate.s14', 'HpBiosUpdate.s14', ''],
|
'@US4': ['HpBiosUpdate.s14', 'HpBiosUpdate.s14', ''],
|
||||||
'@US9' : ['HpBiosUpdate.s09', 'HpBiosUpdate.s09', ''],
|
'@US9': ['HpBiosUpdate.s09', 'HpBiosUpdate.s09', ''],
|
||||||
'@USG' : ['HpBiosUpdate.sig', 'HpBiosUpdate.sig', ''],
|
'@USG': ['HpBiosUpdate.sig', 'HpBiosUpdate.sig', ''],
|
||||||
'@VER' : ['OEM_Version.txt', 'OEM Version', 'Text'],
|
'@VER': ['OEM_Version.txt', 'OEM Version', 'Text'],
|
||||||
'@VXD' : ['amifldrv.vxd', 'amifldrv.vxd', ''],
|
'@VXD': ['amifldrv.vxd', 'amifldrv.vxd', ''],
|
||||||
'@W32' : ['amifldrv32.sys', 'amifldrv32.sys', ''],
|
'@W32': ['amifldrv32.sys', 'amifldrv32.sys', ''],
|
||||||
'@W64' : ['amifldrv64.sys', 'amifldrv64.sys', ''],
|
'@W64': ['amifldrv64.sys', 'amifldrv64.sys', ''],
|
||||||
|
'@D64': ['amifldrv64.sys', 'amifldrv64.sys', ''],
|
||||||
}
|
}
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
utility = BIOSUtility(TITLE, is_ami_ucp, ucp_extract)
|
utility_args = [(['-c', '--checksum'], {'help': 'verify AMI UCP Checksums (slow)', 'action': 'store_true'})]
|
||||||
utility.parse_argument('-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()
|
utility.run_utility()
|
||||||
|
|
134
Apple_EFI_ID.py
134
Apple_EFI_ID.py
|
@ -1,85 +1,91 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3 -B
|
||||||
#coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Apple EFI ID
|
Apple EFI ID
|
||||||
Apple EFI Image Identifier
|
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 ctypes
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import struct
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import zlib
|
||||||
|
|
||||||
# Stop __pycache__ generation
|
from common.externals import get_uefiextract_path, get_uefifind_path
|
||||||
sys.dont_write_bytecode = True
|
|
||||||
|
|
||||||
from common.externals import get_uefifind_path, get_uefiextract_path
|
|
||||||
from common.path_ops import del_dirs, path_parent, path_suffixes
|
from common.path_ops import del_dirs, path_parent, path_suffixes
|
||||||
from common.patterns import PAT_APPLE_EFI
|
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.system import printer
|
||||||
from common.templates import BIOSUtility
|
from common.templates import BIOSUtility
|
||||||
from common.text_ops import file_to_bytes
|
from common.text_ops import file_to_bytes
|
||||||
|
|
||||||
|
TITLE = 'Apple EFI Image Identifier v3.0'
|
||||||
|
|
||||||
|
|
||||||
class IntelBiosId(ctypes.LittleEndianStructure):
|
class IntelBiosId(ctypes.LittleEndianStructure):
|
||||||
|
""" Intel BIOS ID Structure """
|
||||||
|
|
||||||
_pack_ = 1
|
_pack_ = 1
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('Signature', char*8), # 0x00
|
('Signature', Char * 8), # 0x00
|
||||||
('BoardID', uint8_t*16), # 0x08
|
('BoardID', UInt8 * 16), # 0x08
|
||||||
('Dot1', uint8_t*2), # 0x18
|
('Dot1', UInt8 * 2), # 0x18
|
||||||
('BoardExt', uint8_t*6), # 0x1A
|
('BoardExt', UInt8 * 6), # 0x1A
|
||||||
('Dot2', uint8_t*2), # 0x20
|
('Dot2', UInt8 * 2), # 0x20
|
||||||
('VersionMajor', uint8_t*8), # 0x22
|
('VersionMajor', UInt8 * 8), # 0x22
|
||||||
('Dot3', uint8_t*2), # 0x2A
|
('Dot3', UInt8 * 2), # 0x2A
|
||||||
('BuildType', uint8_t*2), # 0x2C
|
('BuildType', UInt8 * 2), # 0x2C
|
||||||
('VersionMinor', uint8_t*4), # 0x2E
|
('VersionMinor', UInt8 * 4), # 0x2E
|
||||||
('Dot4', uint8_t*2), # 0x32
|
('Dot4', UInt8 * 2), # 0x32
|
||||||
('Year', uint8_t*4), # 0x34
|
('Year', UInt8 * 4), # 0x34
|
||||||
('Month', uint8_t*4), # 0x38
|
('Month', UInt8 * 4), # 0x38
|
||||||
('Day', uint8_t*4), # 0x3C
|
('Day', UInt8 * 4), # 0x3C
|
||||||
('Hour', uint8_t*4), # 0x40
|
('Hour', UInt8 * 4), # 0x40
|
||||||
('Minute', uint8_t*4), # 0x44
|
('Minute', UInt8 * 4), # 0x44
|
||||||
('NullTerminator', uint8_t*2), # 0x48
|
('NullTerminator', UInt8 * 2), # 0x48
|
||||||
# 0x4A
|
# 0x4A
|
||||||
]
|
]
|
||||||
|
|
||||||
# https://github.com/tianocore/edk2-platforms/blob/master/Platform/Intel/BoardModulePkg/Include/Guid/BiosId.h
|
# https://github.com/tianocore/edk2-platforms/blob/master/Platform/Intel/BoardModulePkg/Include/Guid/BiosId.h
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def decode(field):
|
def _decode(field):
|
||||||
return struct.pack('B' * len(field), *field).decode('utf-16','ignore').strip('\x00 ')
|
return struct.pack('B' * len(field), *field).decode('utf-16', 'ignore').strip('\x00 ')
|
||||||
|
|
||||||
def get_bios_id(self):
|
def get_bios_id(self):
|
||||||
BoardID = self.decode(self.BoardID)
|
""" Create Apple EFI BIOS ID """
|
||||||
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)}'
|
|
||||||
|
|
||||||
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):
|
return board_id, board_ext, version_major, build_type, version_minor, build_date, build_time
|
||||||
BoardID,BoardExt,VersionMajor,BuildType,VersionMinor,BuildDate,BuildTime = self.get_bios_id()
|
|
||||||
|
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):
|
def is_apple_efi(input_file):
|
||||||
|
""" Check if input is Apple EFI image """
|
||||||
|
|
||||||
input_buffer = file_to_bytes(input_file)
|
input_buffer = file_to_bytes(input_file)
|
||||||
|
|
||||||
if PAT_APPLE_EFI.search(input_buffer):
|
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)
|
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
return True
|
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
|
return False
|
||||||
|
|
||||||
# Parse & Identify (or Rename) Apple EFI image
|
|
||||||
def apple_efi_identify(input_file, extract_path, padding=0, rename=False):
|
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):
|
if not os.path.isfile(input_file):
|
||||||
printer('Error: Could not find input file path!', padding)
|
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)
|
bios_id_hdr = get_struct(body_buffer, bios_id_match.start(), IntelBiosId)
|
||||||
|
|
||||||
del_dirs(extract_path) # Successful UEFIExtract extraction, remove its output (temp) folder
|
del_dirs(extract_path) # Successful UEFIExtract extraction, remove its output (temp) folder
|
||||||
except Exception:
|
except Exception as error: # pylint: disable=broad-except
|
||||||
printer('Error: Failed to parse compressed $IBIOSI$ pattern!', padding)
|
printer(f'Error: Failed to parse compressed $IBIOSI$ pattern: {error}!', padding)
|
||||||
|
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
|
@ -146,9 +156,10 @@ def apple_efi_identify(input_file, extract_path, padding=0, rename=False):
|
||||||
|
|
||||||
input_adler32 = zlib.adler32(input_buffer)
|
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)
|
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
|
return 0
|
||||||
|
|
||||||
PAT_UEFIFIND = f'244942494F534924{"."*32}2E00{"."*12}2E00{"."*16}2E00{"."*12}2E00{"."*40}0000'
|
|
||||||
|
PAT_UEFIFIND = f'244942494F534924{"." * 32}2E00{"." * 12}2E00{"." * 16}2E00{"." * 12}2E00{"." * 40}0000'
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
utility = BIOSUtility(TITLE, is_apple_efi, apple_efi_identify)
|
utility_args = [(['-r', '--rename'], {'help': 'rename EFI image based on its tag', 'action': 'store_true'})]
|
||||||
utility.parse_argument('-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()
|
utility.run_utility()
|
||||||
|
|
|
@ -1,19 +1,13 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3 -B
|
||||||
#coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Apple EFI IM4P
|
Apple EFI IM4P
|
||||||
Apple EFI IM4P Splitter
|
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 os
|
||||||
import sys
|
|
||||||
|
|
||||||
# Stop __pycache__ generation
|
|
||||||
sys.dont_write_bytecode = True
|
|
||||||
|
|
||||||
from common.path_ops import make_dirs, path_stem
|
from common.path_ops import make_dirs, path_stem
|
||||||
from common.patterns import PAT_APPLE_IM4P, PAT_INTEL_IFD
|
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.templates import BIOSUtility
|
||||||
from common.text_ops import file_to_bytes
|
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):
|
def is_apple_im4p(input_file):
|
||||||
|
""" Check if input is Apple EFI IM4P image """
|
||||||
|
|
||||||
input_buffer = file_to_bytes(input_file)
|
input_buffer = file_to_bytes(input_file)
|
||||||
|
|
||||||
is_im4p = PAT_APPLE_IM4P.search(input_buffer)
|
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)
|
return bool(is_im4p and is_ifd)
|
||||||
|
|
||||||
# Parse & Split Apple EFI IM4P image
|
|
||||||
def apple_im4p_split(input_file, extract_path, padding=0):
|
def apple_im4p_split(input_file, extract_path, padding=0):
|
||||||
|
""" Parse & Split Apple EFI IM4P image """
|
||||||
|
|
||||||
exit_codes = []
|
exit_codes = []
|
||||||
|
|
||||||
input_buffer = file_to_bytes(input_file)
|
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_bgn = ifd_match_start
|
||||||
ifd_data_end = ifd_data_bgn + ifd_comp_all_size
|
ifd_data_end = ifd_data_bgn + ifd_comp_all_size
|
||||||
|
|
||||||
ifd_data_txt = f'0x{ifd_data_bgn:07X}-0x{ifd_data_end:07X}'
|
ifd_data_txt = f'0x{ifd_data_bgn:07X}-0x{ifd_data_end:07X}'
|
||||||
|
|
||||||
output_data = input_buffer[ifd_data_bgn:ifd_data_end]
|
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)
|
return sum(exit_codes)
|
||||||
|
|
||||||
|
|
||||||
# Intel Flash Descriptor Component Sizes (4MB, 8MB, 16MB and 32MB)
|
# Intel Flash Descriptor Component Sizes (4MB, 8MB, 16MB and 32MB)
|
||||||
IFD_COMP_LEN = {3: 0x400000, 4: 0x800000, 5: 0x1000000, 6: 0x2000000}
|
IFD_COMP_LEN = {3: 0x400000, 4: 0x800000, 5: 0x1000000, 6: 0x2000000}
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Apple PBZX Extract
|
Apple PBZX Extract
|
||||||
Apple EFI PBZX Extractor
|
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
|
import ctypes
|
||||||
|
import logging
|
||||||
# Stop __pycache__ generation
|
import lzma
|
||||||
sys.dont_write_bytecode = True
|
import os
|
||||||
|
|
||||||
from common.comp_szip import is_szip_supported, szip_decompress
|
from common.comp_szip import is_szip_supported, szip_decompress
|
||||||
from common.path_ops import make_dirs, path_stem
|
from common.path_ops import make_dirs, path_stem
|
||||||
from common.patterns import PAT_APPLE_PBZX
|
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.system import printer
|
||||||
from common.templates import BIOSUtility
|
from common.templates import BIOSUtility
|
||||||
from common.text_ops import file_to_bytes
|
from common.text_ops import file_to_bytes
|
||||||
|
|
||||||
|
TITLE = 'Apple EFI PBZX Extractor v2.0'
|
||||||
|
|
||||||
|
|
||||||
class PbzxChunk(ctypes.BigEndianStructure):
|
class PbzxChunk(ctypes.BigEndianStructure):
|
||||||
|
""" PBZX Chunk Header """
|
||||||
|
|
||||||
_pack_ = 1
|
_pack_ = 1
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('Reserved0', uint32_t), # 0x00
|
('Reserved0', UInt32), # 0x00
|
||||||
('InitSize', uint32_t), # 0x04
|
('InitSize', UInt32), # 0x04
|
||||||
('Reserved1', uint32_t), # 0x08
|
('Reserved1', UInt32), # 0x08
|
||||||
('CompSize', uint32_t), # 0x0C
|
('CompSize', UInt32), # 0x0C
|
||||||
# 0x10
|
# 0x10
|
||||||
]
|
]
|
||||||
|
|
||||||
def struct_print(self, p):
|
def struct_print(self, padd):
|
||||||
printer(['Reserved 0 :', f'0x{self.Reserved0:X}'], p, False)
|
""" Display structure information """
|
||||||
printer(['Initial Size :', f'0x{self.InitSize:X}'], p, False)
|
|
||||||
printer(['Reserved 1 :', f'0x{self.Reserved1:X}'], p, False)
|
printer(['Reserved 0 :', f'0x{self.Reserved0:X}'], padd, False)
|
||||||
printer(['Compressed Size:', f'0x{self.CompSize:X}'], p, 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):
|
def is_apple_pbzx(input_file):
|
||||||
|
""" Check if input is Apple PBZX image """
|
||||||
|
|
||||||
input_buffer = file_to_bytes(input_file)
|
input_buffer = file_to_bytes(input_file)
|
||||||
|
|
||||||
return bool(PAT_APPLE_PBZX.search(input_buffer[:0x4]))
|
return bool(PAT_APPLE_PBZX.search(input_buffer[:0x4]))
|
||||||
|
|
||||||
# Parse & Extract Apple PBZX image
|
|
||||||
def apple_pbzx_extract(input_file, extract_path, padding=0):
|
def apple_pbzx_extract(input_file, extract_path, padding=0):
|
||||||
|
""" Parse & Extract Apple PBZX image """
|
||||||
|
|
||||||
input_buffer = file_to_bytes(input_file)
|
input_buffer = file_to_bytes(input_file)
|
||||||
|
|
||||||
make_dirs(extract_path, delete=True)
|
make_dirs(extract_path, delete=True)
|
||||||
|
|
||||||
cpio_bin = b'' # Initialize PBZX > CPIO Buffer
|
cpio_bin = b'' # Initialize PBZX > CPIO Buffer
|
||||||
|
|
||||||
cpio_len = 0x0 # Initialize PBZX > CPIO Length
|
cpio_len = 0x0 # Initialize PBZX > CPIO Length
|
||||||
|
|
||||||
chunk_off = 0xC # First PBZX Chunk starts at 0xC
|
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)
|
cpio_bin += lzma.LZMADecompressor().decompress(comp_bin)
|
||||||
|
|
||||||
printer('Successful LZMA decompression!', padding + 8)
|
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
|
# Otherwise, Chunk data is not compressed
|
||||||
cpio_bin += comp_bin
|
cpio_bin += comp_bin
|
||||||
|
|
||||||
|
@ -111,8 +120,9 @@ def apple_pbzx_extract(input_file, extract_path, padding=0):
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
# Get common ctypes Structure Sizes
|
# Get common ctypes Structure Sizes
|
||||||
PBZX_CHUNK_HDR_LEN = ctypes.sizeof(PbzxChunk)
|
PBZX_CHUNK_HDR_LEN = ctypes.sizeof(PbzxChunk)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Apple EFI PKG
|
Apple EFI PKG
|
||||||
Apple EFI Package Extractor
|
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 os
|
||||||
import sys
|
|
||||||
|
|
||||||
# Stop __pycache__ generation
|
|
||||||
sys.dont_write_bytecode = True
|
|
||||||
|
|
||||||
from common.comp_szip import is_szip_supported, szip_decompress
|
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.patterns import PAT_APPLE_PKG
|
||||||
from common.system import printer
|
from common.system import printer
|
||||||
from common.templates import BIOSUtility
|
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_IM4P import apple_im4p_split, is_apple_im4p
|
||||||
from Apple_EFI_PBZX import apple_pbzx_extract, is_apple_pbzx
|
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):
|
def is_apple_pkg(input_file):
|
||||||
|
""" Check if input is Apple EFI PKG package """
|
||||||
|
|
||||||
input_buffer = file_to_bytes(input_file)
|
input_buffer = file_to_bytes(input_file)
|
||||||
|
|
||||||
return bool(PAT_APPLE_PKG.search(input_buffer[:0x4]))
|
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):
|
def efi_split_rename(in_file, out_path, padding=0):
|
||||||
|
""" Split Apple EFI image (if applicable) and Rename """
|
||||||
|
|
||||||
exit_codes = []
|
exit_codes = []
|
||||||
|
|
||||||
working_dir = get_extract_path(in_file)
|
working_dir = get_extract_path(in_file)
|
||||||
|
|
||||||
if is_apple_im4p(in_file):
|
if is_apple_im4p(in_file):
|
||||||
printer(f'Splitting IM4P via {is_apple_im4p.__module__}...', padding)
|
printer(f'Splitting IM4P via {is_apple_im4p.__module__}...', padding)
|
||||||
|
|
||||||
im4p_exit = apple_im4p_split(in_file, working_dir, padding + 4)
|
im4p_exit = apple_im4p_split(in_file, working_dir, padding + 4)
|
||||||
|
|
||||||
exit_codes.append(im4p_exit)
|
exit_codes.append(im4p_exit)
|
||||||
else:
|
else:
|
||||||
make_dirs(working_dir, delete=True)
|
make_dirs(working_dir, delete=True)
|
||||||
|
|
||||||
copy_file(in_file, working_dir, True)
|
copy_file(in_file, working_dir, True)
|
||||||
|
|
||||||
for efi_file in get_path_files(working_dir):
|
for efi_file in get_path_files(working_dir):
|
||||||
if is_apple_efi(efi_file):
|
if is_apple_efi(efi_file):
|
||||||
printer(f'Renaming EFI via {is_apple_efi.__module__}...', padding)
|
printer(f'Renaming EFI via {is_apple_efi.__module__}...', padding)
|
||||||
|
|
||||||
name_exit = apple_efi_identify(efi_file, efi_file, padding + 4, True)
|
name_exit = apple_efi_identify(efi_file, efi_file, padding + 4, True)
|
||||||
|
|
||||||
exit_codes.append(name_exit)
|
exit_codes.append(name_exit)
|
||||||
|
|
||||||
for named_file in get_path_files(working_dir):
|
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)
|
return sum(exit_codes)
|
||||||
|
|
||||||
# Parse & Extract Apple EFI PKG packages
|
|
||||||
def apple_pkg_extract(input_file, extract_path, padding=0):
|
def apple_pkg_extract(input_file, extract_path, padding=0):
|
||||||
|
""" Parse & Extract Apple EFI PKG packages """
|
||||||
|
|
||||||
if not os.path.isfile(input_file):
|
if not os.path.isfile(input_file):
|
||||||
printer('Error: Could not find input file path!', padding)
|
printer('Error: Could not find input file path!', padding)
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
make_dirs(extract_path, delete=True)
|
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):
|
for xar_file in get_path_files(xar_path):
|
||||||
if path_name(xar_file) == 'Payload':
|
if path_name(xar_file) == 'Payload':
|
||||||
pbzx_module = is_apple_pbzx.__module__
|
pbzx_module = is_apple_pbzx.__module__
|
||||||
|
|
||||||
if is_apple_pbzx(xar_file):
|
if is_apple_pbzx(xar_file):
|
||||||
printer(f'Extracting PBZX via {pbzx_module}...', padding + 4)
|
printer(f'Extracting PBZX via {pbzx_module}...', padding + 4)
|
||||||
|
|
||||||
pbzx_path = get_extract_path(xar_file)
|
pbzx_path = get_extract_path(xar_file)
|
||||||
|
|
||||||
if apple_pbzx_extract(xar_file, pbzx_path, padding + 8) == 0:
|
if apple_pbzx_extract(xar_file, pbzx_path, padding + 8) == 0:
|
||||||
printer(f'Succesfull PBZX extraction via {pbzx_module}!', padding + 4)
|
printer(f'Succesfull PBZX extraction via {pbzx_module}!', padding + 4)
|
||||||
|
|
||||||
for pbzx_file in get_path_files(pbzx_path):
|
for pbzx_file in get_path_files(pbzx_path):
|
||||||
if path_name(pbzx_file) == 'UpdateBundle.zip':
|
if path_name(pbzx_file) == 'UpdateBundle.zip':
|
||||||
if is_szip_supported(pbzx_file, padding + 8, args=['-tZIP'], check=True):
|
if is_szip_supported(pbzx_file, padding + 8, args=['-tZIP'], check=True):
|
||||||
zip_path = get_extract_path(pbzx_file)
|
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):
|
for zip_file in get_path_files(zip_path):
|
||||||
if path_name(path_parent(zip_file)) == 'MacEFI':
|
if path_name(path_parent(zip_file)) == 'MacEFI':
|
||||||
printer(path_name(zip_file), padding + 12)
|
printer(path_name(zip_file), padding + 12)
|
||||||
|
|
||||||
if efi_split_rename(zip_file, extract_path, padding + 16) != 0:
|
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
|
return 10
|
||||||
else:
|
else:
|
||||||
return 9
|
return 9
|
||||||
else:
|
else:
|
||||||
return 8
|
return 8
|
||||||
|
|
||||||
break # ZIP found, stop
|
break # ZIP found, stop
|
||||||
else:
|
else:
|
||||||
printer('Error: Could not find "UpdateBundle.zip" file!', padding)
|
printer('Error: Could not find "UpdateBundle.zip" file!', padding)
|
||||||
|
|
||||||
return 7
|
return 7
|
||||||
else:
|
else:
|
||||||
printer(f'Error: Failed to extract PBZX file via {pbzx_module}!', padding)
|
printer(f'Error: Failed to extract PBZX file via {pbzx_module}!', padding)
|
||||||
|
|
||||||
return 6
|
return 6
|
||||||
else:
|
else:
|
||||||
printer(f'Error: Failed to detect file as PBZX via {pbzx_module}!', padding)
|
printer(f'Error: Failed to detect file as PBZX via {pbzx_module}!', padding)
|
||||||
|
|
||||||
return 5
|
return 5
|
||||||
|
|
||||||
break # Payload found, stop searching
|
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 path_name(xar_file) == 'Scripts':
|
||||||
if is_szip_supported(xar_file, padding + 4, args=['-tGZIP'], check=True):
|
if is_szip_supported(xar_file, padding + 4, args=['-tGZIP'], check=True):
|
||||||
gzip_path = get_extract_path(xar_file)
|
gzip_path = get_extract_path(xar_file)
|
||||||
|
|
||||||
if szip_decompress(xar_file, gzip_path, 'GZIP', padding + 4, args=['-tGZIP'], check=True) == 0:
|
if szip_decompress(xar_file, gzip_path, 'GZIP', padding + 4, args=['-tGZIP'], check=True) == 0:
|
||||||
for gzip_file in get_path_files(gzip_path):
|
for gzip_file in get_path_files(gzip_path):
|
||||||
if is_szip_supported(gzip_file, padding + 8, args=['-tCPIO'], check=True):
|
if is_szip_supported(gzip_file, padding + 8, args=['-tCPIO'], check=True):
|
||||||
cpio_path = get_extract_path(gzip_file)
|
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):
|
for cpio_file in get_path_files(cpio_path):
|
||||||
if path_name(path_parent(cpio_file)) == 'EFIPayloads':
|
if path_name(path_parent(cpio_file)) == 'EFIPayloads':
|
||||||
printer(path_name(cpio_file), padding + 12)
|
printer(path_name(cpio_file), padding + 12)
|
||||||
|
|
||||||
if efi_split_rename(cpio_file, extract_path, padding + 16) != 0:
|
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
|
return 15
|
||||||
else:
|
else:
|
||||||
return 14
|
return 14
|
||||||
|
@ -138,11 +165,13 @@ def apple_pkg_extract(input_file, extract_path, padding=0):
|
||||||
break # Scripts found, stop searching
|
break # Scripts found, stop searching
|
||||||
else:
|
else:
|
||||||
printer('Error: Could not find "Payload" or "Scripts" file!', padding)
|
printer('Error: Could not find "Payload" or "Scripts" file!', padding)
|
||||||
|
|
||||||
return 4
|
return 4
|
||||||
|
|
||||||
del_dirs(xar_path) # Delete temporary/working XAR folder
|
del_dirs(xar_path) # Delete temporary/working XAR folder
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Award BIOS Extract
|
Award BIOS Extract
|
||||||
Award BIOS Module Extractor
|
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 os
|
||||||
import sys
|
import stat
|
||||||
|
|
||||||
# Stop __pycache__ generation
|
|
||||||
sys.dont_write_bytecode = True
|
|
||||||
|
|
||||||
from common.comp_szip import szip_decompress
|
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.patterns import PAT_AWARD_LZH
|
||||||
from common.system import printer
|
from common.system import printer
|
||||||
from common.templates import BIOSUtility
|
from common.templates import BIOSUtility
|
||||||
from common.text_ops import file_to_bytes
|
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):
|
def is_award_bios(in_file):
|
||||||
|
""" Check if input is Award BIOS image """
|
||||||
|
|
||||||
in_buffer = file_to_bytes(in_file)
|
in_buffer = file_to_bytes(in_file)
|
||||||
|
|
||||||
return bool(PAT_AWARD_LZH.search(in_buffer))
|
return bool(PAT_AWARD_LZH.search(in_buffer))
|
||||||
|
|
||||||
# Parse & Extract Award BIOS image
|
|
||||||
def award_bios_extract(input_file, extract_path, padding=0):
|
def award_bios_extract(input_file, extract_path, padding=0):
|
||||||
|
""" Parse & Extract Award BIOS image """
|
||||||
|
|
||||||
input_buffer = file_to_bytes(input_file)
|
input_buffer = file_to_bytes(input_file)
|
||||||
|
|
||||||
make_dirs(extract_path, delete=True)
|
make_dirs(extract_path, delete=True)
|
||||||
|
|
||||||
for lzh_match in PAT_AWARD_LZH.finditer(input_buffer):
|
for lzh_match in PAT_AWARD_LZH.finditer(input_buffer):
|
||||||
lzh_type = lzh_match.group(0).decode('utf-8')
|
lzh_type = lzh_match.group(0).decode('utf-8')
|
||||||
|
|
||||||
lzh_text = f'LZH-{lzh_type.strip("-").upper()}'
|
lzh_text = f'LZH-{lzh_type.strip("-").upper()}'
|
||||||
|
|
||||||
lzh_bgn = lzh_match.start()
|
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]
|
hdr_len = input_buffer[mod_bgn]
|
||||||
mod_len = int.from_bytes(input_buffer[mod_bgn + 0x7:mod_bgn + 0xB], 'little')
|
mod_len = int.from_bytes(input_buffer[mod_bgn + 0x7:mod_bgn + 0xB], 'little')
|
||||||
mod_end = lzh_bgn + hdr_len + mod_len
|
mod_end = lzh_bgn + hdr_len + mod_len
|
||||||
|
|
||||||
mod_bin = input_buffer[mod_bgn:mod_end]
|
mod_bin = input_buffer[mod_bgn:mod_end]
|
||||||
|
|
||||||
tag_bgn = mod_bgn + 0x16
|
if len(mod_bin) != 0x2 + hdr_len + mod_len:
|
||||||
tag_end = tag_bgn + input_buffer[mod_bgn + 0x15]
|
printer(f'Error: Skipped incomplete LZH stream at 0x{mod_bgn:X}!', padding, False)
|
||||||
tag_txt = input_buffer[tag_bgn:tag_end].decode('utf-8','ignore')
|
|
||||||
|
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)
|
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'
|
lzh_path = f'{mod_path}.lzh'
|
||||||
|
|
||||||
with open(lzh_path, 'wb') as lzh_file:
|
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
|
# Manually check if 7-Zip extracted LZH due to its CRC check issue
|
||||||
if os.path.isfile(mod_path):
|
if os.path.isfile(mod_path):
|
||||||
|
os.chmod(lzh_path, stat.S_IWRITE)
|
||||||
|
|
||||||
os.remove(lzh_path) # Successful extraction, delete LZH archive
|
os.remove(lzh_path) # Successful extraction, delete LZH archive
|
||||||
|
|
||||||
# Extract any nested LZH archives
|
# 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
|
# Recursively extract nested Award BIOS modules
|
||||||
award_bios_extract(mod_path, get_extract_path(mod_path), padding + 8)
|
award_bios_extract(mod_path, get_extract_path(mod_path), padding + 8)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Fujitsu SFX Extractor
|
Fujitsu SFX Extractor
|
||||||
Fujitsu SFX BIOS 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 os
|
||||||
import sys
|
|
||||||
|
|
||||||
# Stop __pycache__ generation
|
|
||||||
sys.dont_write_bytecode = True
|
|
||||||
|
|
||||||
from common.comp_szip import is_szip_supported, szip_decompress
|
from common.comp_szip import is_szip_supported, szip_decompress
|
||||||
from common.path_ops import make_dirs
|
from common.path_ops import make_dirs
|
||||||
|
@ -22,14 +16,20 @@ from common.system import printer
|
||||||
from common.templates import BIOSUtility
|
from common.templates import BIOSUtility
|
||||||
from common.text_ops import file_to_bytes
|
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):
|
def is_fujitsu_sfx(in_file):
|
||||||
|
""" Check if input is Fujitsu SFX image """
|
||||||
|
|
||||||
buffer = file_to_bytes(in_file)
|
buffer = file_to_bytes(in_file)
|
||||||
|
|
||||||
return bool(PAT_FUJITSU_SFX.search(buffer))
|
return bool(PAT_FUJITSU_SFX.search(buffer))
|
||||||
|
|
||||||
# Extract Fujitsu SFX image
|
|
||||||
def fujitsu_cabinet(in_file, extract_path, padding=0):
|
def fujitsu_cabinet(in_file, extract_path, padding=0):
|
||||||
|
""" Extract Fujitsu SFX image """
|
||||||
|
|
||||||
buffer = file_to_bytes(in_file)
|
buffer = file_to_bytes(in_file)
|
||||||
|
|
||||||
match_cab = PAT_FUJITSU_SFX.search(buffer) # Microsoft CAB Header XOR 0xFF
|
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
|
cab_start = match_cab.start() + 0xA
|
||||||
|
|
||||||
# Determine the Microsoft CAB image size
|
# 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
|
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
|
cab_size ^= xor_size # Perform XOR 0xFF and get actual CAB size
|
||||||
|
|
||||||
printer('Removing obfuscation...', padding + 4)
|
printer('Removing obfuscation...', padding + 4)
|
||||||
|
|
||||||
# Determine the Microsoft CAB image Data
|
# 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
|
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
|
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
|
cab_file.write(cab_data) # Create temporary CAB archive
|
||||||
|
|
||||||
if is_szip_supported(cab_path, padding + 8, check=True):
|
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
|
os.remove(cab_path) # Successful extraction, delete temporary CAB archive
|
||||||
else:
|
else:
|
||||||
return 3
|
return 3
|
||||||
|
@ -71,8 +71,10 @@ def fujitsu_cabinet(in_file, extract_path, padding=0):
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# Parse & Extract Fujitsu SFX image
|
|
||||||
def fujitsu_sfx_extract(in_file, extract_path, padding=0):
|
def fujitsu_sfx_extract(in_file, extract_path, padding=0):
|
||||||
|
""" Parse & Extract Fujitsu SFX image """
|
||||||
|
|
||||||
buffer = file_to_bytes(in_file)
|
buffer = file_to_bytes(in_file)
|
||||||
|
|
||||||
make_dirs(extract_path, delete=True)
|
make_dirs(extract_path, delete=True)
|
||||||
|
@ -81,9 +83,11 @@ def fujitsu_sfx_extract(in_file, extract_path, padding=0):
|
||||||
printer('Successfully Extracted!', padding)
|
printer('Successfully Extracted!', padding)
|
||||||
else:
|
else:
|
||||||
printer('Error: Failed to Extract image!', padding)
|
printer('Error: Failed to Extract image!', padding)
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Fujitsu UPC Extract
|
Fujitsu UPC Extract
|
||||||
Fujitsu UPC BIOS Extractor
|
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 os
|
||||||
import sys
|
|
||||||
|
|
||||||
# Stop __pycache__ generation
|
|
||||||
sys.dont_write_bytecode = True
|
|
||||||
|
|
||||||
from common.comp_efi import efi_decompress, is_efi_compressed
|
from common.comp_efi import efi_decompress, is_efi_compressed
|
||||||
from common.path_ops import make_dirs, path_suffixes
|
from common.path_ops import make_dirs, path_suffixes
|
||||||
from common.templates import BIOSUtility
|
from common.templates import BIOSUtility
|
||||||
from common.text_ops import file_to_bytes
|
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):
|
def is_fujitsu_upc(in_file):
|
||||||
|
""" Check if input is Fujitsu UPC image """
|
||||||
|
|
||||||
in_buffer = file_to_bytes(in_file)
|
in_buffer = file_to_bytes(in_file)
|
||||||
|
|
||||||
is_ext = path_suffixes(in_file)[-1].upper() == '.UPC' if os.path.isfile(in_file) else True
|
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
|
return is_ext and is_efi
|
||||||
|
|
||||||
# Parse & Extract Fujitsu UPC image
|
|
||||||
def fujitsu_upc_extract(input_file, extract_path, padding=0):
|
def fujitsu_upc_extract(input_file, extract_path, padding=0):
|
||||||
|
""" Parse & Extract Fujitsu UPC image """
|
||||||
|
|
||||||
make_dirs(extract_path, delete=True)
|
make_dirs(extract_path, delete=True)
|
||||||
|
|
||||||
image_base = os.path.basename(input_file)
|
image_base = os.path.basename(input_file)
|
||||||
|
|
||||||
image_name = image_base[:-4] if image_base.upper().endswith('.UPC') else image_base
|
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')
|
image_path = os.path.join(extract_path, f'{image_name}.bin')
|
||||||
|
|
||||||
return efi_decompress(input_file, image_path, padding)
|
return efi_decompress(input_file, image_path, padding)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Insyde IFD Extract
|
Insyde IFD Extract
|
||||||
Insyde iFlash/iFdPacker Extractor
|
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
|
import ctypes
|
||||||
|
import os
|
||||||
# Stop __pycache__ generation
|
|
||||||
sys.dont_write_bytecode = True
|
|
||||||
|
|
||||||
from common.comp_szip import is_szip_supported, szip_decompress
|
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.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.system import printer
|
||||||
from common.templates import BIOSUtility
|
from common.templates import BIOSUtility
|
||||||
from common.text_ops import file_to_bytes
|
from common.text_ops import file_to_bytes
|
||||||
|
|
||||||
|
TITLE = 'Insyde iFlash/iFdPacker Extractor v3.0'
|
||||||
|
|
||||||
|
|
||||||
class IflashHeader(ctypes.LittleEndianStructure):
|
class IflashHeader(ctypes.LittleEndianStructure):
|
||||||
|
""" Insyde iFlash Header """
|
||||||
|
|
||||||
_pack_ = 1
|
_pack_ = 1
|
||||||
|
|
||||||
|
# noinspection PyTypeChecker
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('Signature', char*8), # 0x00 $_IFLASH
|
('Signature', Char * 8), # 0x00 $_IFLASH
|
||||||
('ImageTag', char*8), # 0x08
|
('ImageTag', Char * 8), # 0x08
|
||||||
('TotalSize', uint32_t), # 0x10 from header end
|
('TotalSize', UInt32), # 0x10 from header end
|
||||||
('ImageSize', uint32_t), # 0x14 from header end
|
('ImageSize', UInt32), # 0x14 from header end
|
||||||
# 0x18
|
# 0x18
|
||||||
]
|
]
|
||||||
|
|
||||||
def _get_padd_len(self):
|
def _get_padd_len(self) -> int:
|
||||||
return self.TotalSize - self.ImageSize
|
return self.TotalSize - self.ImageSize
|
||||||
|
|
||||||
def get_image_tag(self):
|
def get_image_tag(self) -> str:
|
||||||
return self.ImageTag.decode('utf-8','ignore').strip('_')
|
""" Get Insyde iFlash image tag """
|
||||||
|
|
||||||
def struct_print(self, p):
|
return self.ImageTag.decode('utf-8', 'ignore').strip('_')
|
||||||
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)
|
|
||||||
|
|
||||||
# Check if input is Insyde iFlash/iFdPacker Update image
|
def struct_print(self, padd: int) -> None:
|
||||||
def is_insyde_ifd(input_file):
|
""" Display structure information """
|
||||||
input_buffer = file_to_bytes(input_file)
|
|
||||||
|
|
||||||
is_ifl = bool(insyde_iflash_detect(input_buffer))
|
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_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
|
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
|
return iflash_code and ifdpack_code
|
||||||
|
|
||||||
# Detect Insyde iFlash Update image
|
|
||||||
def insyde_iflash_detect(input_buffer):
|
def insyde_iflash_detect(input_buffer: bytes) -> list:
|
||||||
iflash_match_all = []
|
""" Detect Insyde iFlash Update image """
|
||||||
iflash_match_nan = [0x0,0xFFFFFFFF]
|
|
||||||
|
iflash_match_all: list = []
|
||||||
|
iflash_match_nan: list = [0x0, 0xFFFFFFFF]
|
||||||
|
|
||||||
for iflash_match in PAT_INSYDE_IFL.finditer(input_buffer):
|
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:
|
if len(input_buffer[ifl_bgn:]) <= INS_IFL_LEN:
|
||||||
continue
|
continue
|
||||||
|
@ -92,9 +103,11 @@ def insyde_iflash_detect(input_buffer):
|
||||||
|
|
||||||
return iflash_match_all
|
return iflash_match_all
|
||||||
|
|
||||||
# Extract Insyde iFlash Update image
|
|
||||||
def insyde_iflash_extract(input_buffer, extract_path, padding=0):
|
def insyde_iflash_extract(input_buffer: bytes, extract_path: str, padding: int = 0) -> int:
|
||||||
insyde_iflash_all = insyde_iflash_detect(input_buffer)
|
""" Extract Insyde iFlash Update image """
|
||||||
|
|
||||||
|
insyde_iflash_all: list = insyde_iflash_detect(input_buffer)
|
||||||
|
|
||||||
if not insyde_iflash_all:
|
if not insyde_iflash_all:
|
||||||
return 127
|
return 127
|
||||||
|
@ -103,35 +116,35 @@ def insyde_iflash_extract(input_buffer, extract_path, padding=0):
|
||||||
|
|
||||||
make_dirs(extract_path, delete=True)
|
make_dirs(extract_path, delete=True)
|
||||||
|
|
||||||
exit_codes = []
|
exit_codes: list = []
|
||||||
|
|
||||||
for insyde_iflash in insyde_iflash_all:
|
for insyde_iflash in insyde_iflash_all:
|
||||||
exit_code = 0
|
exit_code: int = 0
|
||||||
|
|
||||||
ifl_bgn,ifl_hdr = insyde_iflash
|
ifl_bgn, ifl_hdr = insyde_iflash
|
||||||
|
|
||||||
img_bgn = ifl_bgn + INS_IFL_LEN
|
img_bgn: int = ifl_bgn + INS_IFL_LEN
|
||||||
img_end = img_bgn + ifl_hdr.ImageSize
|
img_end: int = img_bgn + ifl_hdr.ImageSize
|
||||||
img_bin = input_buffer[img_bgn:img_end]
|
img_bin: bytes = input_buffer[img_bgn:img_end]
|
||||||
|
|
||||||
if len(img_bin) != ifl_hdr.ImageSize:
|
if len(img_bin) != ifl_hdr.ImageSize:
|
||||||
exit_code = 1
|
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_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)
|
printer(f'{img_name}\n', padding + 4)
|
||||||
|
|
||||||
ifl_hdr.struct_print(padding + 8)
|
ifl_hdr.struct_print(padding + 8)
|
||||||
|
|
||||||
if img_val == [img_tag,img_ext]:
|
if img_val == [img_tag, img_ext]:
|
||||||
printer(f'Note: Detected new Insyde iFlash tag {img_tag}!', padding + 12, pause=True)
|
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:
|
with open(out_path, 'wb') as out_image:
|
||||||
out_image.write(img_bin)
|
out_image.write(img_bin)
|
||||||
|
@ -142,8 +155,10 @@ def insyde_iflash_extract(input_buffer, extract_path, padding=0):
|
||||||
|
|
||||||
return sum(exit_codes)
|
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)
|
match_sfx = PAT_INSYDE_SFX.search(input_buffer)
|
||||||
|
|
||||||
if not match_sfx:
|
if not match_sfx:
|
||||||
|
@ -153,12 +168,12 @@ def insyde_packer_extract(input_buffer, extract_path, padding=0):
|
||||||
|
|
||||||
make_dirs(extract_path, delete=True)
|
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':
|
if sfx_buffer[:0x5] == b'\x6E\xF4\x79\x5F\x4E':
|
||||||
printer('Detected Insyde iFdPacker > 7-Zip SFX > Obfuscation!', padding + 4)
|
printer('Detected Insyde iFdPacker > 7-Zip SFX > Obfuscation!', padding + 4)
|
||||||
|
|
||||||
for index,byte in enumerate(sfx_buffer):
|
for index, byte in enumerate(sfx_buffer):
|
||||||
sfx_buffer[index] = byte // 2 + (128 if byte % 2 else 0)
|
sfx_buffer[index] = byte // 2 + (128 if byte % 2 else 0)
|
||||||
|
|
||||||
printer('Removed Insyde iFdPacker > 7-Zip SFX > Obfuscation!', padding + 8)
|
printer('Removed Insyde iFdPacker > 7-Zip SFX > Obfuscation!', padding + 8)
|
||||||
|
@ -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()]:
|
if bytes(INS_SFX_PWD, 'utf-16le') in input_buffer[:match_sfx.start()]:
|
||||||
printer('Detected Insyde iFdPacker > 7-Zip SFX > Password!', padding + 8)
|
printer('Detected Insyde iFdPacker > 7-Zip SFX > Password!', padding + 8)
|
||||||
|
|
||||||
printer(INS_SFX_PWD, padding + 12)
|
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:
|
with open(sfx_path, 'wb') as sfx_file:
|
||||||
sfx_file.write(sfx_buffer)
|
sfx_file.write(sfx_buffer)
|
||||||
|
@ -189,29 +205,30 @@ def insyde_packer_extract(input_buffer, extract_path, padding=0):
|
||||||
if is_insyde_ifd(sfx_file):
|
if is_insyde_ifd(sfx_file):
|
||||||
printer(f'{os.path.basename(sfx_file)}', padding + 12)
|
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)
|
exit_codes.append(ifd_code)
|
||||||
|
|
||||||
return sum(exit_codes)
|
return sum(exit_codes)
|
||||||
|
|
||||||
|
|
||||||
# Insyde iFdPacker known 7-Zip SFX Password
|
# 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
|
# Insyde iFlash known Image Names
|
||||||
INS_IFL_IMG = {
|
INS_IFL_IMG: dict = {
|
||||||
'BIOSCER' : ['Certificate', 'bin'],
|
'BIOSCER': ['Certificate', 'bin'],
|
||||||
'BIOSCR2' : ['Certificate 2nd', 'bin'],
|
'BIOSCR2': ['Certificate 2nd', 'bin'],
|
||||||
'BIOSIMG' : ['BIOS-UEFI', 'bin'],
|
'BIOSIMG': ['BIOS-UEFI', 'bin'],
|
||||||
'DRV_IMG' : ['isflash', 'efi'],
|
'DRV_IMG': ['isflash', 'efi'],
|
||||||
'EC_IMG' : ['Embedded Controller', 'bin'],
|
'EC_IMG': ['Embedded Controller', 'bin'],
|
||||||
'INI_IMG' : ['platform', 'ini'],
|
'INI_IMG': ['platform', 'ini'],
|
||||||
'ME_IMG' : ['Management Engine', 'bin'],
|
'ME_IMG': ['Management Engine', 'bin'],
|
||||||
'OEM_ID' : ['OEM Identifier', 'bin'],
|
'OEM_ID': ['OEM Identifier', 'bin'],
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get common ctypes Structure Sizes
|
# Get common ctypes Structure Sizes
|
||||||
INS_IFL_LEN = ctypes.sizeof(IflashHeader)
|
INS_IFL_LEN: int = ctypes.sizeof(IflashHeader)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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:
|
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
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Panasonic BIOS Extract
|
Panasonic BIOS Extract
|
||||||
Panasonic BIOS Package Extractor
|
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 io
|
||||||
import sys
|
import logging
|
||||||
import lznt1
|
import os
|
||||||
|
|
||||||
import pefile
|
import pefile
|
||||||
|
|
||||||
# Stop __pycache__ generation
|
from dissect.util.compression import lznt1
|
||||||
sys.dont_write_bytecode = True
|
|
||||||
|
|
||||||
from common.comp_szip import is_szip_supported, szip_decompress
|
from common.comp_szip import is_szip_supported, szip_decompress
|
||||||
from common.path_ops import get_path_files, make_dirs, path_stem, safe_name
|
from common.path_ops import get_path_files, make_dirs, path_stem, safe_name
|
||||||
|
@ -28,21 +25,25 @@ from common.text_ops import file_to_bytes
|
||||||
|
|
||||||
from AMI_PFAT_Extract import is_ami_pfat, parse_pfat_file
|
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):
|
def is_panasonic_pkg(in_file):
|
||||||
|
""" Check if input is Panasonic BIOS Package PE """
|
||||||
|
|
||||||
in_buffer = file_to_bytes(in_file)
|
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:
|
if not pe_file:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
pe_info = get_pe_info(pe_file)
|
pe_info = get_pe_info(pe_file, silent=True)
|
||||||
|
|
||||||
if not pe_info:
|
if not pe_info:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if pe_info.get(b'FileDescription',b'').upper() != b'UNPACK UTILITY':
|
if pe_info.get(b'FileDescription', b'').upper() != b'UNPACK UTILITY':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not PAT_MICROSOFT_CAB.search(in_buffer):
|
if not PAT_MICROSOFT_CAB.search(in_buffer):
|
||||||
|
@ -50,14 +51,18 @@ def is_panasonic_pkg(in_file):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Search and Extract Panasonic BIOS Package PE CAB archive
|
|
||||||
def panasonic_cab_extract(buffer, extract_path, padding=0):
|
def panasonic_cab_extract(buffer, extract_path, padding=0):
|
||||||
pe_path,pe_file,pe_info = [None] * 3
|
""" 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_bgn = PAT_MICROSOFT_CAB.search(buffer).start()
|
||||||
cab_len = int.from_bytes(buffer[cab_bgn + 0x8:cab_bgn + 0xC], 'little')
|
cab_len = int.from_bytes(buffer[cab_bgn + 0x8:cab_bgn + 0xC], 'little')
|
||||||
cab_end = cab_bgn + cab_len
|
cab_end = cab_bgn + cab_len
|
||||||
|
|
||||||
cab_bin = buffer[cab_bgn:cab_end]
|
cab_bin = buffer[cab_bgn:cab_end]
|
||||||
|
|
||||||
cab_tag = f'[0x{cab_bgn:06X}-0x{cab_end:06X}]'
|
cab_tag = f'[0x{cab_bgn:06X}-0x{cab_end:06X}]'
|
||||||
|
|
||||||
cab_path = os.path.join(extract_path, f'CAB_{cab_tag}.cab')
|
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
|
return pe_path, pe_file, pe_info
|
||||||
|
|
||||||
for file_path in get_path_files(extract_path):
|
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:
|
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':
|
|
||||||
|
if pe_info.get(b'FileDescription', b'').upper() == b'BIOS UPDATE':
|
||||||
pe_path = file_path
|
pe_path = file_path
|
||||||
|
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
return pe_path, pe_file, pe_info
|
return pe_path, pe_file, pe_info
|
||||||
|
|
||||||
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):
|
def panasonic_res_extract(pe_name, pe_file, extract_path, padding=0):
|
||||||
|
""" Extract & Decompress Panasonic BIOS Update PE RCDATA (LZNT1) """
|
||||||
|
|
||||||
is_rcdata = False
|
is_rcdata = False
|
||||||
|
|
||||||
# When fast_load is used, IMAGE_DIRECTORY_ENTRY_RESOURCE must be parsed prior to RCDATA Directories
|
# 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:
|
for entry in pe_file.DIRECTORY_ENTRY_RESOURCE.entries:
|
||||||
if entry.struct.name == 'IMAGE_RESOURCE_DIRECTORY_ENTRY' and entry.struct.Id == 0xA:
|
if entry.struct.name == 'IMAGE_RESOURCE_DIRECTORY_ENTRY' and entry.struct.Id == 0xA:
|
||||||
is_rcdata = True
|
is_rcdata = True
|
||||||
|
|
||||||
for resource in entry.directory.entries:
|
for resource in entry.directory.entries:
|
||||||
res_bgn = resource.directory.entries[0].data.struct.OffsetToData
|
res_bgn = resource.directory.entries[0].data.struct.OffsetToData
|
||||||
res_len = resource.directory.entries[0].data.struct.Size
|
res_len = resource.directory.entries[0].data.struct.Size
|
||||||
res_end = res_bgn + res_len
|
res_end = res_bgn + res_len
|
||||||
|
|
||||||
res_bin = pe_file.get_data(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_tag = f'{pe_name} [0x{res_bgn:06X}-0x{res_end:06X}]'
|
||||||
|
|
||||||
res_out = os.path.join(extract_path, f'{res_tag}')
|
res_out = os.path.join(extract_path, f'{res_tag}')
|
||||||
|
|
||||||
printer(res_tag, padding + 4)
|
printer(res_tag, padding + 4)
|
||||||
|
@ -111,8 +125,13 @@ def panasonic_res_extract(pe_name, pe_file, extract_path, padding=0):
|
||||||
try:
|
try:
|
||||||
res_raw = lznt1.decompress(res_bin[0x8:])
|
res_raw = lznt1.decompress(res_bin[0x8:])
|
||||||
|
|
||||||
printer('Succesfull LZNT1 decompression via lznt1!', padding + 8)
|
if len(res_raw) != int.from_bytes(res_bin[0x4:0x8], 'little'):
|
||||||
except Exception:
|
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
|
res_raw = res_bin
|
||||||
|
|
||||||
printer('Succesfull PE Resource extraction!', padding + 8)
|
printer('Succesfull PE Resource extraction!', padding + 8)
|
||||||
|
@ -125,15 +144,17 @@ def panasonic_res_extract(pe_name, pe_file, extract_path, padding=0):
|
||||||
else:
|
else:
|
||||||
if is_pe_file(res_raw):
|
if is_pe_file(res_raw):
|
||||||
res_ext = 'exe'
|
res_ext = 'exe'
|
||||||
elif res_raw.startswith(b'[') and res_raw.endswith((b'\x0D\x0A',b'\x0A')):
|
elif res_raw.startswith(b'[') and res_raw.endswith((b'\x0D\x0A', b'\x0A')):
|
||||||
res_ext = 'txt'
|
res_ext = 'txt'
|
||||||
else:
|
else:
|
||||||
res_ext = 'bin'
|
res_ext = 'bin'
|
||||||
|
|
||||||
if res_ext == 'txt':
|
if res_ext == 'txt':
|
||||||
printer(new_line=False)
|
printer(new_line=False)
|
||||||
|
|
||||||
for line in io.BytesIO(res_raw).readlines():
|
for line in io.BytesIO(res_raw).readlines():
|
||||||
line_text = line.decode('utf-8','ignore').rstrip()
|
line_text = line.decode('utf-8', 'ignore').rstrip()
|
||||||
|
|
||||||
printer(line_text, padding + 12, new_line=False)
|
printer(line_text, padding + 12, new_line=False)
|
||||||
|
|
||||||
with open(f'{res_out}.{res_ext}', 'wb') as out_file:
|
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
|
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):
|
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)
|
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_bgn = pe_file.OPTIONAL_HEADER.BaseOfData + pe_file.OPTIONAL_HEADER.SizeOfInitializedData
|
||||||
img_end = sec_bgn or len(pe_data)
|
img_end = sec_bgn or len(pe_data)
|
||||||
|
|
||||||
img_bin = pe_data[img_bgn:img_end]
|
img_bin = pe_data[img_bgn:img_end]
|
||||||
|
|
||||||
img_tag = f'{pe_name} [0x{img_bgn:X}-0x{img_end:X}]'
|
img_tag = f'{pe_name} [0x{img_bgn:X}-0x{img_end:X}]'
|
||||||
|
|
||||||
img_out = os.path.join(extract_path, f'{img_tag}.bin')
|
img_out = os.path.join(extract_path, f'{img_tag}.bin')
|
||||||
|
|
||||||
printer(img_tag, padding + 4)
|
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)
|
return bool(img_bin)
|
||||||
|
|
||||||
# Parse & Extract Panasonic BIOS Package PE
|
|
||||||
def panasonic_pkg_extract(input_file, extract_path, padding=0):
|
def panasonic_pkg_extract(input_file, extract_path, padding=0):
|
||||||
|
""" Parse & Extract Panasonic BIOS Package PE """
|
||||||
|
|
||||||
input_buffer = file_to_bytes(input_file)
|
input_buffer = file_to_bytes(input_file)
|
||||||
|
|
||||||
make_dirs(extract_path, delete=True)
|
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:
|
if not pkg_pe_file:
|
||||||
return 2
|
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:
|
if not pkg_pe_info:
|
||||||
return 3
|
return 3
|
||||||
|
@ -183,7 +213,7 @@ def panasonic_pkg_extract(input_file, extract_path, padding=0):
|
||||||
|
|
||||||
show_pe_info(pkg_pe_info, padding + 4)
|
show_pe_info(pkg_pe_info, padding + 4)
|
||||||
|
|
||||||
upd_pe_path,upd_pe_file,upd_pe_info = panasonic_cab_extract(input_buffer, extract_path, padding + 4)
|
upd_pe_path, upd_pe_file, upd_pe_info = panasonic_cab_extract(input_buffer, extract_path, padding + 4)
|
||||||
|
|
||||||
if not (upd_pe_path and upd_pe_file and upd_pe_info):
|
if not (upd_pe_path and upd_pe_file and upd_pe_info):
|
||||||
return 4
|
return 4
|
||||||
|
@ -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
|
return 0 if is_upd_res or is_upd_img else 1
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Phoenix TDK Extract
|
Phoenix TDK Extract
|
||||||
Phoenix TDK Packer Extractor
|
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
|
import ctypes
|
||||||
|
import logging
|
||||||
# Stop __pycache__ generation
|
import lzma
|
||||||
sys.dont_write_bytecode = True
|
import os
|
||||||
|
|
||||||
from common.path_ops import make_dirs, safe_name
|
from common.path_ops import make_dirs, safe_name
|
||||||
from common.pe_ops import get_pe_file, get_pe_info
|
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.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.system import printer
|
||||||
from common.templates import BIOSUtility
|
from common.templates import BIOSUtility
|
||||||
from common.text_ops import file_to_bytes
|
from common.text_ops import file_to_bytes
|
||||||
|
|
||||||
|
TITLE = 'Phoenix TDK Packer Extractor v3.0'
|
||||||
|
|
||||||
|
|
||||||
class PhoenixTdkHeader(ctypes.LittleEndianStructure):
|
class PhoenixTdkHeader(ctypes.LittleEndianStructure):
|
||||||
|
""" Phoenix TDK Header """
|
||||||
|
|
||||||
_pack_ = 1
|
_pack_ = 1
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('Tag', char*8), # 0x00
|
('Tag', Char * 8), # 0x00
|
||||||
('Size', uint32_t), # 0x08
|
('Size', UInt32), # 0x08
|
||||||
('Count', uint32_t), # 0x0C
|
('Count', UInt32), # 0x0C
|
||||||
# 0x10
|
# 0x10
|
||||||
]
|
]
|
||||||
|
|
||||||
def _get_tag(self):
|
def _get_tag(self):
|
||||||
return self.Tag.decode('utf-8','ignore').strip()
|
return self.Tag.decode('utf-8', 'ignore').strip()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
class PhoenixTdkEntry(ctypes.LittleEndianStructure):
|
class PhoenixTdkEntry(ctypes.LittleEndianStructure):
|
||||||
|
""" Phoenix TDK Entry """
|
||||||
|
|
||||||
_pack_ = 1
|
_pack_ = 1
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('Name', char*256), # 0x000
|
('Name', Char * 256), # 0x000
|
||||||
('Offset', uint32_t), # 0x100
|
('Offset', UInt32), # 0x100
|
||||||
('Size', uint32_t), # 0x104
|
('Size', UInt32), # 0x104
|
||||||
('Compressed', uint32_t), # 0x108
|
('Compressed', UInt32), # 0x108
|
||||||
('Reserved', uint32_t), # 0x10C
|
('Reserved', UInt32), # 0x10C
|
||||||
# 0x110
|
# 0x110
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -57,26 +62,37 @@ class PhoenixTdkEntry(ctypes.LittleEndianStructure):
|
||||||
|
|
||||||
def __init__(self, mz_base, *args, **kwargs):
|
def __init__(self, mz_base, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.Base = mz_base
|
|
||||||
|
self.mz_base = mz_base
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
return self.Name.decode('utf-8','replace').strip()
|
""" Get TDK Entry decoded name """
|
||||||
|
|
||||||
|
return self.Name.decode('utf-8', 'replace').strip()
|
||||||
|
|
||||||
def get_offset(self):
|
def get_offset(self):
|
||||||
return self.Base + self.Offset
|
""" Get TDK Entry absolute offset """
|
||||||
|
|
||||||
|
return self.mz_base + self.Offset
|
||||||
|
|
||||||
def get_compression(self):
|
def get_compression(self):
|
||||||
|
""" Get TDK Entry compression type """
|
||||||
|
|
||||||
return self.COMP.get(self.Compressed, f'Unknown ({self.Compressed})')
|
return self.COMP.get(self.Compressed, f'Unknown ({self.Compressed})')
|
||||||
|
|
||||||
def struct_print(self, p):
|
def struct_print(self, padd):
|
||||||
printer(['Name :', self.get_name()], p, False)
|
""" Display structure information """
|
||||||
printer(['Offset :', f'0x{self.get_offset():X}'], p, False)
|
|
||||||
printer(['Size :', f'0x{self.Size:X}'], p, False)
|
printer(['Name :', self.get_name()], padd, False)
|
||||||
printer(['Compression:', self.get_compression()], p, False)
|
printer(['Offset :', f'0x{self.get_offset():X}'], padd, False)
|
||||||
printer(['Reserved :', f'0x{self.Reserved:X}'], p, 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):
|
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
|
tdk_base_off = None # Initialize Phoenix TDK Base MZ Offset
|
||||||
|
|
||||||
# Scan input file for all Microsoft executable patterns (MZ) before TDK Header 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:]))
|
mz_ord = [mz_all[0]] + list(reversed(mz_all[1:]))
|
||||||
|
|
||||||
# Parse each detected MZ
|
# Parse each detected MZ
|
||||||
for mz in mz_ord:
|
for mz_match in mz_ord:
|
||||||
mz_off = mz.start()
|
mz_off = mz_match.start()
|
||||||
|
|
||||||
# MZ (DOS) > PE (NT) image Offset is found at offset 0x3C-0x40 relative to MZ base
|
# 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')
|
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]):
|
if PAT_MICROSOFT_PE.search(in_buffer[pe_off:pe_off + 0x4]):
|
||||||
try:
|
try:
|
||||||
# Parse detected MZ > PE > Image, quickly (fast_load)
|
# 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
|
# 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
|
# Parse detected MZ > PE > Info > Product Name
|
||||||
pe_name = pe_info.get(b'ProductName',b'')
|
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
|
# 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''
|
pe_name = b''
|
||||||
|
|
||||||
# Check for valid Phoenix TDK Packer PE > Product Name
|
# 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
|
return tdk_base_off
|
||||||
|
|
||||||
# Scan input buffer for valid Phoenix TDK image
|
|
||||||
def get_phoenix_tdk(in_buffer):
|
def get_phoenix_tdk(in_buffer):
|
||||||
|
""" Scan input buffer for valid Phoenix TDK image """
|
||||||
|
|
||||||
# Scan input buffer for Phoenix TDK pattern
|
# Scan input buffer for Phoenix TDK pattern
|
||||||
tdk_match = PAT_PHOENIX_TDK.search(in_buffer)
|
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
|
return tdk_base_off, tdk_pack_off
|
||||||
|
|
||||||
# Check if input contains valid Phoenix TDK image
|
|
||||||
def is_phoenix_tdk(in_file):
|
def is_phoenix_tdk(in_file):
|
||||||
|
""" Check if input contains valid Phoenix TDK image """
|
||||||
|
|
||||||
buffer = file_to_bytes(in_file)
|
buffer = file_to_bytes(in_file)
|
||||||
|
|
||||||
return bool(get_phoenix_tdk(buffer)[1] is not None)
|
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):
|
def phoenix_tdk_extract(input_file, extract_path, padding=0):
|
||||||
|
""" Parse & Extract Phoenix Tools Development Kit (TDK) Packer """
|
||||||
|
|
||||||
exit_code = 0
|
exit_code = 0
|
||||||
|
|
||||||
input_buffer = file_to_bytes(input_file)
|
input_buffer = file_to_bytes(input_file)
|
||||||
|
@ -162,18 +186,20 @@ def phoenix_tdk_extract(input_file, extract_path, padding=0):
|
||||||
|
|
||||||
printer('Phoenix Tools Development Kit Packer', padding)
|
printer('Phoenix Tools Development Kit Packer', padding)
|
||||||
|
|
||||||
base_off,pack_off = get_phoenix_tdk(input_buffer)
|
base_off, pack_off = get_phoenix_tdk(input_buffer)
|
||||||
|
|
||||||
# Parse TDK Header structure
|
# Parse TDK Header structure
|
||||||
tdk_hdr = get_struct(input_buffer, pack_off, PhoenixTdkHeader)
|
tdk_hdr = get_struct(input_buffer, pack_off, PhoenixTdkHeader)
|
||||||
|
|
||||||
# Print TDK Header structure info
|
# Print TDK Header structure info
|
||||||
printer('Phoenix TDK Header:\n', padding + 4)
|
printer('Phoenix TDK Header:\n', padding + 4)
|
||||||
|
|
||||||
tdk_hdr.struct_print(padding + 8)
|
tdk_hdr.struct_print(padding + 8)
|
||||||
|
|
||||||
# Check if reported TDK Header Size matches manual TDK Entry Count calculation
|
# 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:
|
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)
|
printer('Error: Phoenix TDK Header Size & Entry Count mismatch!\n', padding + 8, pause=True)
|
||||||
|
|
||||||
exit_code = 1
|
exit_code = 1
|
||||||
|
|
||||||
# Store TDK Entries offset after the placeholder data
|
# 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
|
# Print TDK Entry structure info
|
||||||
printer(f'Phoenix TDK Entry ({entry_index + 1}/{tdk_hdr.Count}):\n', padding + 8)
|
printer(f'Phoenix TDK Entry ({entry_index + 1}/{tdk_hdr.Count}):\n', padding + 8)
|
||||||
|
|
||||||
tdk_mod.struct_print(padding + 12)
|
tdk_mod.struct_print(padding + 12)
|
||||||
|
|
||||||
# Get TDK Entry raw data Offset (TDK Base + Entry Offset)
|
# 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
|
# Check if TDK Entry raw data Offset is valid
|
||||||
if mod_off >= len(input_buffer):
|
if mod_off >= len(input_buffer):
|
||||||
printer('Error: Phoenix TDK Entry > Offset is out of bounds!\n', padding + 12, pause=True)
|
printer('Error: Phoenix TDK Entry > Offset is out of bounds!\n', padding + 12, pause=True)
|
||||||
|
|
||||||
exit_code = 2
|
exit_code = 2
|
||||||
|
|
||||||
# Store TDK Entry raw data (relative to TDK Base, not TDK Header)
|
# 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
|
# Check if TDK Entry raw data is complete
|
||||||
if len(mod_data) != tdk_mod.Size:
|
if len(mod_data) != tdk_mod.Size:
|
||||||
printer('Error: Phoenix TDK Entry > Data is truncated!\n', padding + 12, pause=True)
|
printer('Error: Phoenix TDK Entry > Data is truncated!\n', padding + 12, pause=True)
|
||||||
|
|
||||||
exit_code = 3
|
exit_code = 3
|
||||||
|
|
||||||
# Check if TDK Entry Reserved is present
|
# Check if TDK Entry Reserved is present
|
||||||
if tdk_mod.Reserved:
|
if tdk_mod.Reserved:
|
||||||
printer('Error: Phoenix TDK Entry > Reserved is not empty!\n', padding + 12, pause=True)
|
printer('Error: Phoenix TDK Entry > Reserved is not empty!\n', padding + 12, pause=True)
|
||||||
|
|
||||||
exit_code = 4
|
exit_code = 4
|
||||||
|
|
||||||
# Decompress TDK Entry raw data, when applicable (i.e. LZMA)
|
# Decompress TDK Entry raw data, when applicable (i.e. LZMA)
|
||||||
if tdk_mod.get_compression() == 'LZMA':
|
if tdk_mod.get_compression() == 'LZMA':
|
||||||
try:
|
try:
|
||||||
mod_data = lzma.LZMADecompressor().decompress(mod_data)
|
mod_data = lzma.LZMADecompressor().decompress(mod_data)
|
||||||
except Exception:
|
except Exception as error: # pylint: disable=broad-except
|
||||||
printer('Error: Phoenix TDK Entry > LZMA decompression failed!\n', padding + 12, pause=True)
|
printer(f'Error: Phoenix TDK Entry > LZMA decompression failed: {error}!\n', padding + 12, pause=True)
|
||||||
|
|
||||||
exit_code = 5
|
exit_code = 5
|
||||||
|
|
||||||
# Generate TDK Entry file name, avoid crash if Entry data is bad
|
# 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))
|
mod_file = os.path.join(extract_path, safe_name(mod_name))
|
||||||
|
|
||||||
# Account for potential duplicate file names
|
# 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
|
# Save TDK Entry data to output file
|
||||||
with open(mod_file, 'wb') as out_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
|
return exit_code
|
||||||
|
|
||||||
|
|
||||||
# Get ctypes Structure Sizes
|
# Get ctypes Structure Sizes
|
||||||
TDK_HDR_LEN = ctypes.sizeof(PhoenixTdkHeader)
|
TDK_HDR_LEN = ctypes.sizeof(PhoenixTdkHeader)
|
||||||
TDK_MOD_LEN = ctypes.sizeof(PhoenixTdkEntry)
|
TDK_MOD_LEN = ctypes.sizeof(PhoenixTdkEntry)
|
||||||
|
@ -240,4 +273,4 @@ TDK_MOD_LEN = ctypes.sizeof(PhoenixTdkEntry)
|
||||||
TDK_DUMMY_LEN = 0x200
|
TDK_DUMMY_LEN = 0x200
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Portwell EFI Extract
|
Portwell EFI Extract
|
||||||
Portwell EFI Update Extractor
|
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 os
|
||||||
import sys
|
|
||||||
|
|
||||||
# Stop __pycache__ generation
|
|
||||||
sys.dont_write_bytecode = True
|
|
||||||
|
|
||||||
from common.comp_efi import efi_decompress, is_efi_compressed
|
from common.comp_efi import efi_decompress, is_efi_compressed
|
||||||
from common.path_ops import make_dirs, safe_name
|
from common.path_ops import make_dirs, safe_name
|
||||||
|
@ -23,21 +18,27 @@ from common.system import printer
|
||||||
from common.templates import BIOSUtility
|
from common.templates import BIOSUtility
|
||||||
from common.text_ops import file_to_bytes
|
from common.text_ops import file_to_bytes
|
||||||
|
|
||||||
|
TITLE = 'Portwell EFI Update Extractor v3.0'
|
||||||
|
|
||||||
FILE_NAMES = {
|
FILE_NAMES = {
|
||||||
0 : 'Flash.efi',
|
0: 'Flash.efi',
|
||||||
1 : 'Fparts.txt',
|
1: 'Fparts.txt',
|
||||||
2 : 'Update.nsh',
|
2: 'Update.nsh',
|
||||||
3 : 'Temp.bin',
|
3: 'Temp.bin',
|
||||||
4 : 'SaveDmiData.efi'
|
4: 'SaveDmiData.efi'
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check if input is Portwell EFI executable
|
|
||||||
def is_portwell_efi(in_file):
|
def is_portwell_efi(in_file):
|
||||||
|
""" Check if input is Portwell EFI executable """
|
||||||
|
|
||||||
in_buffer = file_to_bytes(in_file)
|
in_buffer = file_to_bytes(in_file)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pe_buffer = get_portwell_pe(in_buffer)[1]
|
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''
|
pe_buffer = b''
|
||||||
|
|
||||||
is_mz = PAT_MICROSOFT_MZ.search(in_buffer[:0x2]) # EFI images start with PE Header MZ
|
is_mz = PAT_MICROSOFT_MZ.search(in_buffer[:0x2]) # EFI images start with PE Header MZ
|
||||||
|
@ -46,23 +47,27 @@ def is_portwell_efi(in_file):
|
||||||
|
|
||||||
return bool(is_mz and is_uu)
|
return bool(is_mz and is_uu)
|
||||||
|
|
||||||
# Get PE of Portwell EFI executable
|
|
||||||
def get_portwell_pe(in_buffer):
|
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)
|
pe_data = in_buffer[pe_file.OPTIONAL_HEADER.SizeOfImage:] # Skip EFI executable (pylint: disable=E1101)
|
||||||
|
|
||||||
return pe_file, pe_data
|
return pe_file, pe_data
|
||||||
|
|
||||||
# Parse & Extract Portwell UEFI Unpacker
|
|
||||||
def portwell_efi_extract(input_file, extract_path, padding=0):
|
def portwell_efi_extract(input_file, extract_path, padding=0):
|
||||||
|
""" Parse & Extract Portwell UEFI Unpacker """
|
||||||
|
|
||||||
efi_files = [] # Initialize EFI Payload file chunks
|
efi_files = [] # Initialize EFI Payload file chunks
|
||||||
|
|
||||||
input_buffer = file_to_bytes(input_file)
|
input_buffer = file_to_bytes(input_file)
|
||||||
|
|
||||||
make_dirs(extract_path, delete=True)
|
make_dirs(extract_path, delete=True)
|
||||||
|
|
||||||
pe_file,pe_data = get_portwell_pe(input_buffer)
|
pe_file, pe_data = get_portwell_pe(input_buffer)
|
||||||
|
|
||||||
efi_title = get_unpacker_tag(input_buffer, pe_file)
|
efi_title = get_unpacker_tag(input_buffer, pe_file)
|
||||||
|
|
||||||
|
@ -70,15 +75,19 @@ def portwell_efi_extract(input_file, extract_path, padding=0):
|
||||||
|
|
||||||
# Split EFI Payload into <UU> file chunks
|
# Split EFI Payload into <UU> file chunks
|
||||||
efi_list = list(PAT_PORTWELL_EFI.finditer(pe_data))
|
efi_list = list(PAT_PORTWELL_EFI.finditer(pe_data))
|
||||||
for idx,val in enumerate(efi_list):
|
|
||||||
|
for idx, val in enumerate(efi_list):
|
||||||
efi_bgn = val.end()
|
efi_bgn = val.end()
|
||||||
efi_end = len(pe_data) if idx == len(efi_list) - 1 else efi_list[idx + 1].start()
|
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])
|
efi_files.append(pe_data[efi_bgn:efi_end])
|
||||||
|
|
||||||
parse_efi_files(extract_path, efi_files, padding)
|
parse_efi_files(extract_path, efi_files, padding)
|
||||||
|
|
||||||
# Get Portwell UEFI Unpacker tag
|
|
||||||
def get_unpacker_tag(input_buffer, pe_file):
|
def get_unpacker_tag(input_buffer, pe_file):
|
||||||
|
""" Get Portwell UEFI Unpacker tag """
|
||||||
|
|
||||||
unpacker_tag_txt = 'UEFI Unpacker'
|
unpacker_tag_txt = 'UEFI Unpacker'
|
||||||
|
|
||||||
for pe_section in pe_file.sections:
|
for pe_section in pe_file.sections:
|
||||||
|
@ -88,26 +97,30 @@ def get_unpacker_tag(input_buffer, pe_file):
|
||||||
pe_data_end = pe_data_bgn + pe_section.SizeOfRawData
|
pe_data_end = pe_data_bgn + pe_section.SizeOfRawData
|
||||||
|
|
||||||
# Decode any valid UTF-16 .data PE section info to a parsable text buffer
|
# Decode any valid UTF-16 .data PE section info to a parsable text buffer
|
||||||
pe_data_txt = input_buffer[pe_data_bgn:pe_data_end].decode('utf-16','ignore')
|
pe_data_txt = input_buffer[pe_data_bgn:pe_data_end].decode('utf-16', 'ignore')
|
||||||
|
|
||||||
# Search .data for UEFI Unpacker tag
|
# Search .data for UEFI Unpacker tag
|
||||||
unpacker_tag_bgn = pe_data_txt.find(unpacker_tag_txt)
|
unpacker_tag_bgn = pe_data_txt.find(unpacker_tag_txt)
|
||||||
|
|
||||||
if unpacker_tag_bgn != -1:
|
if unpacker_tag_bgn != -1:
|
||||||
unpacker_tag_len = pe_data_txt[unpacker_tag_bgn:].find('=')
|
unpacker_tag_len = pe_data_txt[unpacker_tag_bgn:].find('=')
|
||||||
|
|
||||||
if unpacker_tag_len != -1:
|
if unpacker_tag_len != -1:
|
||||||
unpacker_tag_end = unpacker_tag_bgn + unpacker_tag_len
|
unpacker_tag_end = unpacker_tag_bgn + unpacker_tag_len
|
||||||
unpacker_tag_raw = pe_data_txt[unpacker_tag_bgn:unpacker_tag_end]
|
unpacker_tag_raw = pe_data_txt[unpacker_tag_bgn:unpacker_tag_end]
|
||||||
|
|
||||||
# Found full UEFI Unpacker tag, store and slightly beautify the resulting text
|
# Found full UEFI Unpacker tag, store and slightly beautify the resulting text
|
||||||
unpacker_tag_txt = unpacker_tag_raw.strip().replace(' ',' ').replace('<',' <')
|
unpacker_tag_txt = unpacker_tag_raw.strip().replace(' ', ' ').replace('<', ' <')
|
||||||
|
|
||||||
break # Found PE .data section, skip the rest
|
break # Found PE .data section, skip the rest
|
||||||
|
|
||||||
return unpacker_tag_txt
|
return unpacker_tag_txt
|
||||||
|
|
||||||
# Process Portwell UEFI Unpacker payload files
|
|
||||||
def parse_efi_files(extract_path, efi_files, padding):
|
def parse_efi_files(extract_path, efi_files, padding):
|
||||||
for file_index,file_data in enumerate(efi_files):
|
""" Process Portwell UEFI Unpacker payload files """
|
||||||
|
|
||||||
|
for file_index, file_data in enumerate(efi_files):
|
||||||
if file_data in (b'', b'NULL'):
|
if file_data in (b'', b'NULL'):
|
||||||
continue # Skip empty/unused files
|
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
|
printer(f'[{file_index}] {file_name}', padding + 4) # Print EFI file name, indicate progress
|
||||||
|
|
||||||
if file_name.startswith('Unknown_'):
|
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
|
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:
|
if efi_decompress(comp_fname, file_path, padding + 8) == 0:
|
||||||
os.remove(comp_fname) # Successful decompression, delete compressed file
|
os.remove(comp_fname) # Successful decompression, delete compressed file
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ You can either Drag & Drop or manually enter AMI UCP Update executable file(s).
|
||||||
|
|
||||||
#### **Compatibility**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ You can either Drag & Drop or manually enter Apple EFI IM4P file(s). Optional ar
|
||||||
|
|
||||||
#### **Compatibility**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ You can either Drag & Drop or manually enter Apple EFI image file(s). Optional a
|
||||||
|
|
||||||
#### **Compatibility**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ You can either Drag & Drop or manually enter Apple EFI PKG package file(s). Opti
|
||||||
|
|
||||||
#### **Compatibility**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ You can either Drag & Drop or manually enter Apple EFI PBZX image file(s). Optio
|
||||||
|
|
||||||
#### **Compatibility**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
|
@ -240,7 +240,7 @@ You can either Drag & Drop or manually enter Award BIOS image file(s). Optional
|
||||||
|
|
||||||
#### **Compatibility**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
|
@ -274,7 +274,7 @@ You can either Drag & Drop or manually enter Dell PFS Update images(s). Optional
|
||||||
|
|
||||||
#### **Compatibility**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
|
@ -306,7 +306,7 @@ You can either Drag & Drop or manually enter Fujitsu SFX BIOS image file(s). Opt
|
||||||
|
|
||||||
#### **Compatibility**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
|
@ -338,7 +338,7 @@ You can either Drag & Drop or manually enter Fujitsu UPC BIOS image file(s). Opt
|
||||||
|
|
||||||
#### **Compatibility**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
|
@ -370,7 +370,7 @@ You can either Drag & Drop or manually enter Insyde iFlash/iFdPacker Update imag
|
||||||
|
|
||||||
#### **Compatibility**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
|
@ -400,14 +400,14 @@ You can either Drag & Drop or manually enter Panasonic BIOS Package executable f
|
||||||
|
|
||||||
#### **Compatibility**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
To run the utility, you must have the following 3rd party Python modules installed:
|
To run the utility, you must have the following 3rd party Python modules installed:
|
||||||
|
|
||||||
* [pefile](https://pypi.org/project/pefile/)
|
* [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:
|
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**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
|
@ -469,7 +469,7 @@ You can either Drag & Drop or manually enter Portwell UEFI Unpacker EFI executab
|
||||||
|
|
||||||
#### **Compatibility**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
|
@ -507,7 +507,7 @@ You can either Drag & Drop or manually enter Toshiba BIOS COM image file(s). Opt
|
||||||
|
|
||||||
#### **Compatibility**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
|
@ -539,7 +539,7 @@ You can either Drag & Drop or manually enter VAIO Packaging Manager executable f
|
||||||
|
|
||||||
#### **Compatibility**
|
#### **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**
|
#### **Prerequisites**
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,15 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3 -B
|
||||||
#coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Toshiba COM Extract
|
Toshiba COM Extract
|
||||||
Toshiba BIOS COM Extractor
|
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 os
|
||||||
import sys
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
# Stop __pycache__ generation
|
|
||||||
sys.dont_write_bytecode = True
|
|
||||||
|
|
||||||
from common.externals import get_comextract_path
|
from common.externals import get_comextract_path
|
||||||
from common.path_ops import make_dirs, path_stem, path_suffixes
|
from common.path_ops import make_dirs, path_stem, path_suffixes
|
||||||
from common.patterns import PAT_TOSHIBA_COM
|
from common.patterns import PAT_TOSHIBA_COM
|
||||||
|
@ -23,8 +17,12 @@ from common.system import printer
|
||||||
from common.templates import BIOSUtility
|
from common.templates import BIOSUtility
|
||||||
from common.text_ops import file_to_bytes
|
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):
|
def is_toshiba_com(in_file):
|
||||||
|
""" Check if input is Toshiba BIOS COM image """
|
||||||
|
|
||||||
buffer = file_to_bytes(in_file)
|
buffer = file_to_bytes(in_file)
|
||||||
|
|
||||||
is_ext = path_suffixes(in_file)[-1].upper() == '.COM' if os.path.isfile(in_file) else True
|
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
|
return is_ext and is_com
|
||||||
|
|
||||||
# Parse & Extract Toshiba BIOS COM image
|
|
||||||
def toshiba_com_extract(input_file, extract_path, padding=0):
|
def toshiba_com_extract(input_file, extract_path, padding=0):
|
||||||
|
""" Parse & Extract Toshiba BIOS COM image """
|
||||||
|
|
||||||
if not os.path.isfile(input_file):
|
if not os.path.isfile(input_file):
|
||||||
printer('Error: Could not find input file path!', padding)
|
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)
|
make_dirs(extract_path, delete=True)
|
||||||
|
|
||||||
output_name = path_stem(input_file)
|
output_name = path_stem(input_file)
|
||||||
|
|
||||||
output_file = os.path.join(extract_path, f'{output_name}.bin')
|
output_file = os.path.join(extract_path, f'{output_name}.bin')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run([get_comextract_path(), input_file, output_file], check=True, stdout=subprocess.DEVNULL)
|
subprocess.run([get_comextract_path(), input_file, output_file], check=True, stdout=subprocess.DEVNULL)
|
||||||
|
|
||||||
if not os.path.isfile(output_file):
|
if not os.path.isfile(output_file):
|
||||||
raise Exception('EXTRACT_FILE_MISSING')
|
raise ValueError('EXTRACT_FILE_MISSING')
|
||||||
except Exception:
|
except Exception as error: # pylint: disable=broad-except
|
||||||
printer(f'Error: ToshibaComExtractor could not extract file {input_file}!', padding)
|
printer(f'Error: ToshibaComExtractor could not extract file {input_file}: {error}!', padding)
|
||||||
|
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
|
@ -59,5 +60,6 @@ def toshiba_com_extract(input_file, extract_path, padding=0):
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
VAIO Package Extractor
|
VAIO Package Extractor
|
||||||
VAIO Packaging Manager 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 os
|
||||||
import sys
|
|
||||||
|
|
||||||
# Stop __pycache__ generation
|
|
||||||
sys.dont_write_bytecode = True
|
|
||||||
|
|
||||||
from common.comp_szip import is_szip_supported, szip_decompress
|
from common.comp_szip import is_szip_supported, szip_decompress
|
||||||
from common.path_ops import make_dirs
|
from common.path_ops import make_dirs
|
||||||
|
@ -22,14 +16,20 @@ from common.system import printer
|
||||||
from common.templates import BIOSUtility
|
from common.templates import BIOSUtility
|
||||||
from common.text_ops import file_to_bytes
|
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):
|
def is_vaio_pkg(in_file):
|
||||||
|
""" Check if input is VAIO Packaging Manager """
|
||||||
|
|
||||||
buffer = file_to_bytes(in_file)
|
buffer = file_to_bytes(in_file)
|
||||||
|
|
||||||
return bool(PAT_VAIO_CFG.search(buffer))
|
return bool(PAT_VAIO_CFG.search(buffer))
|
||||||
|
|
||||||
# Extract VAIO Packaging Manager executable
|
|
||||||
def vaio_cabinet(name, buffer, extract_path, padding=0):
|
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
|
match_cab = PAT_VAIO_CAB.search(buffer) # Microsoft CAB Header XOR 0xFF
|
||||||
|
|
||||||
if not match_cab:
|
if not match_cab:
|
||||||
|
@ -38,15 +38,19 @@ def vaio_cabinet(name, buffer, extract_path, padding=0):
|
||||||
printer('Detected obfuscated CAB archive!', padding)
|
printer('Detected obfuscated CAB archive!', padding)
|
||||||
|
|
||||||
# Determine the Microsoft CAB image size
|
# 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
|
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
|
cab_size ^= xor_size # Perform XOR 0xFF and get actual CAB size
|
||||||
|
|
||||||
printer('Removing obfuscation...', padding + 4)
|
printer('Removing obfuscation...', padding + 4)
|
||||||
|
|
||||||
# Determine the Microsoft CAB image Data
|
# 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
|
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
|
cab_data = (cab_data ^ xor_data).to_bytes(cab_size, 'big') # Perform XOR 0xFF and get actual CAB data
|
||||||
|
|
||||||
printer('Extracting archive...', padding + 4)
|
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
|
cab_file.write(cab_data) # Create temporary CAB archive
|
||||||
|
|
||||||
if is_szip_supported(cab_path, padding + 8, check=True):
|
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
|
os.remove(cab_path) # Successful extraction, delete temporary CAB archive
|
||||||
else:
|
else:
|
||||||
return 3
|
return 3
|
||||||
|
@ -66,8 +70,10 @@ def vaio_cabinet(name, buffer, extract_path, padding=0):
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# Unlock VAIO Packaging Manager executable
|
|
||||||
def vaio_unlock(name, buffer, extract_path, padding=0):
|
def vaio_unlock(name, buffer, extract_path, padding=0):
|
||||||
|
""" Unlock VAIO Packaging Manager executable """
|
||||||
|
|
||||||
match_cfg = PAT_VAIO_CFG.search(buffer)
|
match_cfg = PAT_VAIO_CFG.search(buffer)
|
||||||
|
|
||||||
if not match_cfg:
|
if not match_cfg:
|
||||||
|
@ -76,43 +82,49 @@ def vaio_unlock(name, buffer, extract_path, padding=0):
|
||||||
printer('Attempting to Unlock executable!', padding)
|
printer('Attempting to Unlock executable!', padding)
|
||||||
|
|
||||||
# Initialize VAIO Package Configuration file variables (assume overkill size of 0x500)
|
# Initialize VAIO Package Configuration file variables (assume overkill size of 0x500)
|
||||||
cfg_bgn,cfg_end,cfg_false,cfg_true = [match_cfg.start(), match_cfg.start() + 0x500, b'', b'']
|
cfg_bgn, cfg_end, cfg_false, cfg_true = [match_cfg.start(), match_cfg.start() + 0x500, b'', b'']
|
||||||
|
|
||||||
# Get VAIO Package Configuration file info, split at new_line and stop at payload DOS header (EOF)
|
# Get VAIO Package Configuration file info, split at new_line and stop at payload DOS header (EOF)
|
||||||
cfg_info = buffer[cfg_bgn:cfg_end].split(b'\x0D\x0A\x4D\x5A')[0].replace(b'\x0D',b'').split(b'\x0A')
|
cfg_info = buffer[cfg_bgn:cfg_end].split(b'\x0D\x0A\x4D\x5A')[0].replace(b'\x0D', b'').split(b'\x0A')
|
||||||
|
|
||||||
printer('Retrieving True/False values...', padding + 4)
|
printer('Retrieving True/False values...', padding + 4)
|
||||||
|
|
||||||
# Determine VAIO Package Configuration file True & False values
|
# Determine VAIO Package Configuration file True & False values
|
||||||
for info in cfg_info:
|
for info in cfg_info:
|
||||||
if info.startswith(b'ExtractPathByUser='):
|
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
|
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='):
|
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
|
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
|
# Check if valid True/False values have been retrieved
|
||||||
if cfg_false == cfg_true or not cfg_false or not cfg_true:
|
if cfg_false == cfg_true or not cfg_false or not cfg_true:
|
||||||
printer('Error: Could not retrieve True/False values!', padding + 8)
|
printer('Error: Could not retrieve True/False values!', padding + 8)
|
||||||
|
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
printer('Adjusting UseVAIOCheck entry...', padding + 4)
|
printer('Adjusting UseVAIOCheck entry...', padding + 4)
|
||||||
|
|
||||||
# Find and replace UseVAIOCheck entry from 1/Yes/True to 0/No/False
|
# Find and replace UseVAIOCheck entry from 1/Yes/True to 0/No/False
|
||||||
vaio_check = PAT_VAIO_CHK.search(buffer[cfg_bgn:])
|
vaio_check = PAT_VAIO_CHK.search(buffer[cfg_bgn:])
|
||||||
|
|
||||||
if vaio_check:
|
if vaio_check:
|
||||||
buffer[cfg_bgn + vaio_check.end():cfg_bgn + vaio_check.end() + len(cfg_true)] = cfg_false
|
buffer[cfg_bgn + vaio_check.end():cfg_bgn + vaio_check.end() + len(cfg_true)] = cfg_false
|
||||||
else:
|
else:
|
||||||
printer('Error: Could not find entry UseVAIOCheck!', padding + 8)
|
printer('Error: Could not find entry UseVAIOCheck!', padding + 8)
|
||||||
|
|
||||||
return 3
|
return 3
|
||||||
|
|
||||||
printer('Adjusting ExtractPathByUser entry...', padding + 4)
|
printer('Adjusting ExtractPathByUser entry...', padding + 4)
|
||||||
|
|
||||||
# Find and replace ExtractPathByUser entry from 0/No/False to 1/Yes/True
|
# Find and replace ExtractPathByUser entry from 0/No/False to 1/Yes/True
|
||||||
user_path = PAT_VAIO_EXT.search(buffer[cfg_bgn:])
|
user_path = PAT_VAIO_EXT.search(buffer[cfg_bgn:])
|
||||||
|
|
||||||
if user_path:
|
if user_path:
|
||||||
buffer[cfg_bgn + user_path.end():cfg_bgn + user_path.end() + len(cfg_false)] = cfg_true
|
buffer[cfg_bgn + user_path.end():cfg_bgn + user_path.end() + len(cfg_false)] = cfg_true
|
||||||
else:
|
else:
|
||||||
printer('Error: Could not find entry ExtractPathByUser!', padding + 8)
|
printer('Error: Could not find entry ExtractPathByUser!', padding + 8)
|
||||||
|
|
||||||
return 4
|
return 4
|
||||||
|
|
||||||
printer('Storing unlocked executable...', padding + 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
|
# Store Unlocked VAIO Packaging Manager executable
|
||||||
if vaio_check and user_path:
|
if vaio_check and user_path:
|
||||||
unlock_path = os.path.join(extract_path, f'{name}_Unlocked.exe')
|
unlock_path = os.path.join(extract_path, f'{name}_Unlocked.exe')
|
||||||
|
|
||||||
with open(unlock_path, 'wb') as unl_file:
|
with open(unlock_path, 'wb') as unl_file:
|
||||||
unl_file.write(buffer)
|
unl_file.write(buffer)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# Parse & Extract or Unlock VAIO Packaging Manager
|
|
||||||
def vaio_pkg_extract(input_file, extract_path, padding=0):
|
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_buffer = file_to_bytes(input_file)
|
||||||
|
|
||||||
input_name = os.path.basename(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)
|
printer('Successfully Unlocked!', padding)
|
||||||
else:
|
else:
|
||||||
printer('Error: Failed to Extract or Unlock executable!', padding)
|
printer('Error: Failed to Extract or Unlock executable!', padding)
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Copyright (C) 2022 Plato Mavropoulos
|
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# Get Checksum 16-bit
|
# Get Checksum 16-bit
|
||||||
def get_chk_16(data, value=0, order='little'):
|
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):
|
for idx in range(0, len(data), 2):
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
value += int.from_bytes(data[idx:idx + 2], order)
|
value += int.from_bytes(data[idx:idx + 2], byteorder=order)
|
||||||
|
|
||||||
value &= 0xFFFF
|
value &= 0xFFFF
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
# Get Checksum 8-bit XOR
|
# Get Checksum 8-bit XOR
|
||||||
def get_chk_8_xor(data, value=0):
|
def get_chk_8_xor(data, value=0):
|
||||||
|
""" Calculate Checksum-8 XOR of data, controlling IV """
|
||||||
|
|
||||||
for byte in data:
|
for byte in data:
|
||||||
value ^= byte
|
value ^= byte
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,30 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3 -B
|
||||||
#coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Copyright (C) 2022 Plato Mavropoulos
|
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from common.path_ops import project_root, safe_path
|
from common.externals import get_tiano_path
|
||||||
from common.system import get_os_ver, printer
|
from common.system import printer
|
||||||
|
|
||||||
|
|
||||||
def get_compress_sizes(data):
|
def get_compress_sizes(data):
|
||||||
|
""" Get EFI compression sizes """
|
||||||
|
|
||||||
size_compress = int.from_bytes(data[0x0:0x4], 'little')
|
size_compress = int.from_bytes(data[0x0:0x4], 'little')
|
||||||
size_original = int.from_bytes(data[0x4:0x8], 'little')
|
size_original = int.from_bytes(data[0x4:0x8], 'little')
|
||||||
|
|
||||||
return size_compress, size_original
|
return size_compress, size_original
|
||||||
|
|
||||||
|
|
||||||
def is_efi_compressed(data, strict=True):
|
def is_efi_compressed(data, strict=True):
|
||||||
size_comp,size_orig = get_compress_sizes(data)
|
""" Check if data is EFI compressed, controlling EOF padding """
|
||||||
|
|
||||||
|
size_comp, size_orig = get_compress_sizes(data)
|
||||||
|
|
||||||
check_diff = size_comp < size_orig
|
check_diff = size_comp < size_orig
|
||||||
|
|
||||||
|
@ -29,25 +35,22 @@ def is_efi_compressed(data, strict=True):
|
||||||
|
|
||||||
return check_diff and check_size
|
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'):
|
def efi_decompress(in_path, out_path, padding=0, silent=False, comp_type='--uefi'):
|
||||||
|
""" EFI/Tiano Decompression via TianoCompress """
|
||||||
|
|
||||||
try:
|
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:
|
with open(in_path, 'rb') as file:
|
||||||
_,size_orig = get_compress_sizes(file.read())
|
_, size_orig = get_compress_sizes(file.read())
|
||||||
|
|
||||||
if os.path.getsize(out_path) != size_orig:
|
if os.path.getsize(out_path) != size_orig:
|
||||||
raise Exception('EFI_DECOMPRESS_ERROR')
|
raise OSError('EFI decompressed file & header size mismatch!')
|
||||||
except Exception:
|
except Exception as error: # pylint: disable=broad-except
|
||||||
if not silent:
|
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
|
return 1
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,27 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3 -B
|
||||||
#coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Copyright (C) 2022 Plato Mavropoulos
|
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from common.path_ops import project_root, safe_path
|
from common.externals import get_szip_path
|
||||||
from common.system import get_os_ver, printer
|
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):
|
def check_bad_exit_code(exit_code):
|
||||||
if exit_code not in (0,1):
|
""" Check 7-Zip bad exit codes (0 OK, 1 Warning) """
|
||||||
raise Exception(f'BAD_EXIT_CODE_{exit_code}')
|
|
||||||
|
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):
|
def is_szip_supported(in_path, padding=0, args=None, check=False, silent=False):
|
||||||
|
""" Check if file is 7-Zip supported """
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if args is None:
|
if args is None:
|
||||||
args = []
|
args = []
|
||||||
|
@ -34,16 +32,18 @@ def is_szip_supported(in_path, padding=0, args=None, check=False, silent=False):
|
||||||
|
|
||||||
if check:
|
if check:
|
||||||
check_bad_exit_code(szip_t.returncode)
|
check_bad_exit_code(szip_t.returncode)
|
||||||
except Exception:
|
except Exception as error: # pylint: disable=broad-except
|
||||||
if not silent:
|
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 False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Archive decompression via 7-Zip
|
|
||||||
def szip_decompress(in_path, out_path, in_name, padding=0, args=None, check=False, silent=False):
|
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:
|
if not in_name:
|
||||||
in_name = 'archive'
|
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)
|
check_bad_exit_code(szip_x.returncode)
|
||||||
|
|
||||||
if not os.path.isdir(out_path):
|
if not os.path.isdir(out_path):
|
||||||
raise Exception('EXTRACT_DIR_MISSING')
|
raise OSError(f'Extraction directory not found: {out_path}')
|
||||||
except Exception:
|
except Exception as error: # pylint: disable=broad-except
|
||||||
if not silent:
|
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
|
return 1
|
||||||
|
|
||||||
|
|
|
@ -1,38 +1,66 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3 -B
|
||||||
#coding=utf-8
|
# 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.path_ops import project_root, safe_path
|
||||||
from common.system import get_os_ver
|
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():
|
def get_bgs_tool():
|
||||||
|
"""
|
||||||
|
https://github.com/allowitsme/big-tool by Dmitry Frolov
|
||||||
|
https://github.com/platomav/BGScriptTool by Plato Mavropoulos
|
||||||
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from external.big_script_tool import BigScript # pylint: disable=E0401,E0611
|
from external.big_script_tool import BigScript # pylint: disable=C0415
|
||||||
except Exception:
|
|
||||||
BigScript = None
|
|
||||||
|
|
||||||
return BigScript
|
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 ""}'
|
exec_name = f'UEFIFind{".exe" if get_os_ver()[1] else ""}'
|
||||||
|
|
||||||
return safe_path(project_root(), ['external', exec_name])
|
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 ""}'
|
exec_name = f'UEFIExtract{".exe" if get_os_ver()[1] else ""}'
|
||||||
|
|
||||||
return safe_path(project_root(), ['external', exec_name])
|
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
|
# 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):
|
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
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Copyright (C) 2022 Plato Mavropoulos
|
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
import stat
|
|
||||||
import shutil
|
import shutil
|
||||||
|
import stat
|
||||||
|
import sys
|
||||||
|
|
||||||
from pathlib import Path, PurePath
|
from pathlib import Path, PurePath
|
||||||
|
|
||||||
|
from common.system import get_os_ver
|
||||||
from common.text_ops import is_encased, to_string
|
from common.text_ops import is_encased, to_string
|
||||||
|
|
||||||
# Fix illegal/reserved Windows characters
|
MAX_WIN_COMP_LEN = 255
|
||||||
|
|
||||||
|
|
||||||
def safe_name(in_name):
|
def safe_name(in_name):
|
||||||
|
"""
|
||||||
|
Fix illegal/reserved Windows characters
|
||||||
|
Can also be used to nuke dangerous paths
|
||||||
|
"""
|
||||||
|
|
||||||
name_repr = repr(in_name).strip("'")
|
name_repr = repr(in_name).strip("'")
|
||||||
|
|
||||||
return re.sub(r'[\\/:"*?<>|]+', '_', name_repr)
|
return re.sub(r'[\\/:"*?<>|]+', '_', name_repr)
|
||||||
|
|
||||||
# Check and attempt to fix illegal/unsafe OS path traversals
|
|
||||||
def safe_path(base_path, user_paths):
|
def safe_path(base_path, user_paths):
|
||||||
|
""" Check and attempt to fix illegal/unsafe OS path traversals """
|
||||||
|
|
||||||
# Convert base path to absolute path
|
# Convert base path to absolute path
|
||||||
base_path = real_path(base_path)
|
base_path = real_path(base_path)
|
||||||
|
|
||||||
|
@ -43,10 +54,12 @@ def safe_path(base_path, user_paths):
|
||||||
return nuked_path
|
return nuked_path
|
||||||
|
|
||||||
# Still illegal, raise exception to halt execution
|
# 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):
|
def is_safe_path(base_path, target_path):
|
||||||
|
""" Check for illegal/unsafe OS path traversal """
|
||||||
|
|
||||||
base_path = real_path(base_path)
|
base_path = real_path(base_path)
|
||||||
|
|
||||||
target_path = real_path(target_path)
|
target_path = real_path(target_path)
|
||||||
|
@ -55,64 +68,102 @@ def is_safe_path(base_path, target_path):
|
||||||
|
|
||||||
return base_path == common_path
|
return base_path == common_path
|
||||||
|
|
||||||
# Create normalized base path + OS separator + user path
|
|
||||||
def norm_path(base_path, 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)
|
return os.path.normpath(base_path + os.sep + user_path)
|
||||||
|
|
||||||
# Get absolute path, resolving any symlinks
|
|
||||||
def real_path(in_path):
|
def real_path(in_path):
|
||||||
|
""" Get absolute path, resolving any symlinks """
|
||||||
|
|
||||||
return os.path.realpath(in_path)
|
return os.path.realpath(in_path)
|
||||||
|
|
||||||
# Get Windows/Posix OS agnostic path
|
|
||||||
def agnostic_path(in_path):
|
def agnostic_path(in_path):
|
||||||
|
""" Get Windows/Posix OS agnostic path """
|
||||||
|
|
||||||
return PurePath(in_path.replace('\\', os.sep))
|
return PurePath(in_path.replace('\\', os.sep))
|
||||||
|
|
||||||
# Get absolute parent of path
|
|
||||||
def path_parent(in_path):
|
def path_parent(in_path):
|
||||||
|
""" Get absolute parent of path """
|
||||||
|
|
||||||
return Path(in_path).parent.absolute()
|
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):
|
def path_stem(in_path):
|
||||||
|
""" Get final path component, w/o suffix """
|
||||||
|
|
||||||
return PurePath(in_path).stem
|
return PurePath(in_path).stem
|
||||||
|
|
||||||
# Get list of path file extensions
|
|
||||||
def path_suffixes(in_path):
|
def path_suffixes(in_path):
|
||||||
|
""" Get list of path file extensions """
|
||||||
|
|
||||||
return PurePath(in_path).suffixes or ['']
|
return PurePath(in_path).suffixes or ['']
|
||||||
|
|
||||||
# Check if path is absolute
|
|
||||||
def is_path_absolute(in_path):
|
def is_path_absolute(in_path):
|
||||||
|
""" Check if path is absolute """
|
||||||
|
|
||||||
return Path(in_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):
|
def make_dirs(in_path, parents=True, exist_ok=False, delete=False):
|
||||||
|
""" Create folder(s), controlling parents, existence and prior deletion """
|
||||||
|
|
||||||
if delete:
|
if delete:
|
||||||
del_dirs(in_path)
|
del_dirs(in_path)
|
||||||
|
|
||||||
Path.mkdir(Path(in_path), parents=parents, exist_ok=exist_ok)
|
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):
|
def copy_file(in_path, out_path, meta=False):
|
||||||
|
""" Copy file to path with or w/o metadata """
|
||||||
|
|
||||||
if meta:
|
if meta:
|
||||||
shutil.copy2(in_path, out_path)
|
shutil.copy2(in_path, out_path)
|
||||||
else:
|
else:
|
||||||
shutil.copy(in_path, out_path)
|
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)
|
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)
|
in_func(in_path)
|
||||||
|
|
||||||
# Walk path to get all files
|
|
||||||
def get_path_files(in_path):
|
def get_path_files(in_path):
|
||||||
|
""" Walk path to get all files """
|
||||||
|
|
||||||
path_files = []
|
path_files = []
|
||||||
|
|
||||||
for root, _, files in os.walk(in_path):
|
for root, _, files in os.walk(in_path):
|
||||||
|
@ -121,31 +172,39 @@ def get_path_files(in_path):
|
||||||
|
|
||||||
return path_files
|
return path_files
|
||||||
|
|
||||||
# Get path without leading/trailing quotes
|
|
||||||
def get_dequoted_path(in_path):
|
def get_dequoted_path(in_path):
|
||||||
|
""" Get path without leading/trailing quotes """
|
||||||
|
|
||||||
out_path = to_string(in_path).strip()
|
out_path = to_string(in_path).strip()
|
||||||
|
|
||||||
if len(out_path) >= 2 and is_encased(out_path, ("'",'"')):
|
if len(out_path) >= 2 and is_encased(out_path, ("'", '"')):
|
||||||
out_path = out_path[1:-1]
|
out_path = out_path[1:-1]
|
||||||
|
|
||||||
return out_path
|
return out_path
|
||||||
|
|
||||||
# Set utility extraction stem
|
|
||||||
def extract_suffix():
|
def extract_suffix():
|
||||||
|
""" Set utility extraction stem """
|
||||||
|
|
||||||
return '_extracted'
|
return '_extracted'
|
||||||
|
|
||||||
# Get utility extraction path
|
|
||||||
def get_extract_path(in_path, suffix=extract_suffix()):
|
def get_extract_path(in_path, suffix=extract_suffix()):
|
||||||
|
""" Get utility extraction path """
|
||||||
|
|
||||||
return f'{in_path}{suffix}'
|
return f'{in_path}{suffix}'
|
||||||
|
|
||||||
# Get project's root directory
|
|
||||||
def project_root():
|
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():
|
def runtime_root():
|
||||||
|
""" Get runtime's root directory """
|
||||||
|
|
||||||
if getattr(sys, 'frozen', False):
|
if getattr(sys, 'frozen', False):
|
||||||
root = Path(sys.executable).parent
|
root = Path(sys.executable).parent
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3 -B
|
||||||
#coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Copyright (C) 2022 Plato Mavropoulos
|
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3 -B
|
||||||
#coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Copyright (C) 2022 Plato Mavropoulos
|
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import pefile
|
import pefile
|
||||||
|
@ -10,40 +10,57 @@ import pefile
|
||||||
from common.system import printer
|
from common.system import printer
|
||||||
from common.text_ops import file_to_bytes
|
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 is_pe_file(in_file: str | bytes) -> bool:
|
||||||
def get_pe_file(in_file, fast=True):
|
""" 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)
|
in_buffer = file_to_bytes(in_file)
|
||||||
|
|
||||||
|
pe_file = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Analyze detected MZ > PE image buffer
|
# Analyze detected MZ > PE image buffer
|
||||||
pe_file = pefile.PE(data=in_buffer, fast_load=fast)
|
pe_file = pefile.PE(data=in_buffer, fast_load=fast)
|
||||||
except Exception:
|
except Exception as error: # pylint: disable=broad-except
|
||||||
pe_file = None
|
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
|
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:
|
try:
|
||||||
# When fast_load is used, IMAGE_DIRECTORY_ENTRY_RESOURCE must be parsed prior to FileInfo > StringTable
|
# 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']])
|
pe_file.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE']])
|
||||||
|
|
||||||
# Retrieve MZ > PE > FileInfo > StringTable information
|
# Retrieve MZ > PE > FileInfo > StringTable information
|
||||||
pe_info = pe_file.FileInfo[0][0].StringTable[0].entries
|
pe_info = pe_file.FileInfo[0][0].StringTable[0].entries
|
||||||
except Exception:
|
except Exception as error: # pylint: disable=broad-except
|
||||||
pe_info = {}
|
if not silent:
|
||||||
|
printer(f'Error: Could not get PE info from pefile object: {error}!', padding)
|
||||||
|
|
||||||
return pe_info
|
return pe_info
|
||||||
|
|
||||||
# Print PE info from pefile StringTable
|
|
||||||
def show_pe_info(pe_info, padding=0):
|
def show_pe_info(pe_info: dict, padding: int = 0) -> None:
|
||||||
if type(pe_info).__name__ == 'dict':
|
""" Print PE info from pefile StringTable """
|
||||||
for title,value in pe_info.items():
|
|
||||||
info_title = title.decode('utf-8','ignore').strip()
|
if isinstance(pe_info, dict):
|
||||||
info_value = value.decode('utf-8','ignore').strip()
|
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:
|
if info_title and info_value:
|
||||||
printer(f'{info_title}: {info_value}', padding, new_line=False)
|
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
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Copyright (C) 2022 Plato Mavropoulos
|
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import ctypes
|
import ctypes
|
||||||
|
|
||||||
char = ctypes.c_char
|
Char: type[ctypes.c_char] | int = ctypes.c_char
|
||||||
uint8_t = ctypes.c_ubyte
|
UInt8: type[ctypes.c_ubyte] | int = ctypes.c_ubyte
|
||||||
uint16_t = ctypes.c_ushort
|
UInt16: type[ctypes.c_ushort] | int = ctypes.c_ushort
|
||||||
uint32_t = ctypes.c_uint
|
UInt32: type[ctypes.c_uint] | int = ctypes.c_uint
|
||||||
uint64_t = ctypes.c_uint64
|
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):
|
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_len = ctypes.sizeof(structure)
|
||||||
|
|
||||||
struct_data = buffer[start_offset:start_offset + struct_len]
|
struct_data = buffer[start_offset:start_offset + struct_len]
|
||||||
|
|
||||||
fit_len = min(len(struct_data), struct_len)
|
fit_len = min(len(struct_data), struct_len)
|
||||||
|
|
||||||
ctypes.memmove(ctypes.addressof(structure), struct_data, fit_len)
|
ctypes.memmove(ctypes.addressof(structure), struct_data, fit_len)
|
||||||
|
|
|
@ -1,36 +1,45 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3 -B
|
||||||
#coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Copyright (C) 2022 Plato Mavropoulos
|
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from common.text_ops import padder, to_string
|
from common.text_ops import padder, to_string
|
||||||
|
|
||||||
# Get Python Version (tuple)
|
|
||||||
def get_py_ver():
|
def get_py_ver():
|
||||||
|
""" Get Python Version (tuple) """
|
||||||
|
|
||||||
return sys.version_info
|
return sys.version_info
|
||||||
|
|
||||||
# Get OS Platform (string)
|
|
||||||
def get_os_ver():
|
def get_os_ver():
|
||||||
|
""" Get OS Platform (string) """
|
||||||
|
|
||||||
sys_os = sys.platform
|
sys_os = sys.platform
|
||||||
|
|
||||||
is_win = sys_os == 'win32'
|
is_win = sys_os == 'win32'
|
||||||
|
|
||||||
is_lnx = sys_os.startswith('linux') or sys_os == 'darwin' or sys_os.find('bsd') != -1
|
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
|
return sys_os, is_win, is_win or is_lnx
|
||||||
|
|
||||||
# Check for --auto-exit|-e
|
|
||||||
def is_auto_exit():
|
def is_auto_exit():
|
||||||
|
""" Check for --auto-exit|-e """
|
||||||
|
|
||||||
return bool('--auto-exit' in sys.argv or '-e' in sys.argv)
|
return bool('--auto-exit' in sys.argv or '-e' in sys.argv)
|
||||||
|
|
||||||
# Check Python Version
|
|
||||||
def check_sys_py():
|
def check_sys_py():
|
||||||
|
""" # Check Python Version """
|
||||||
|
|
||||||
sys_py = get_py_ver()
|
sys_py = get_py_ver()
|
||||||
|
|
||||||
if sys_py < (3,10):
|
if sys_py < (3, 10):
|
||||||
sys.stdout.write(f'\nError: Python >= 3.10 required, not {sys_py[0]}.{sys_py[1]}!')
|
sys.stdout.write(f'\nError: Python >= 3.10 required, not {sys_py[0]}.{sys_py[1]}!')
|
||||||
|
|
||||||
if not is_auto_exit():
|
if not is_auto_exit():
|
||||||
|
@ -39,9 +48,11 @@ def check_sys_py():
|
||||||
|
|
||||||
sys.exit(125)
|
sys.exit(125)
|
||||||
|
|
||||||
# Check OS Platform
|
|
||||||
def check_sys_os():
|
def check_sys_os():
|
||||||
os_tag,os_win,os_sup = get_os_ver()
|
""" Check OS Platform """
|
||||||
|
|
||||||
|
os_tag, os_win, os_sup = get_os_ver()
|
||||||
|
|
||||||
if not os_sup:
|
if not os_sup:
|
||||||
printer(f'Error: Unsupported platform "{os_tag}"!')
|
printer(f'Error: Unsupported platform "{os_tag}"!')
|
||||||
|
@ -53,16 +64,21 @@ def check_sys_os():
|
||||||
|
|
||||||
# Fix Windows Unicode console redirection
|
# Fix Windows Unicode console redirection
|
||||||
if os_win:
|
if os_win:
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
sys.stdout.reconfigure(encoding='utf-8')
|
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 ''
|
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
|
# coding=utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Copyright (C) 2022 Plato Mavropoulos
|
Copyright (C) 2022-2024 Plato Mavropoulos
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import ctypes
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import ctypes
|
|
||||||
import argparse
|
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from common.num_ops import get_ordinal
|
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
|
from common.system import check_sys_os, check_sys_py, get_os_ver, is_auto_exit, printer
|
||||||
|
|
||||||
|
|
||||||
class BIOSUtility:
|
class BIOSUtility:
|
||||||
|
""" Template utility class for BIOSUtilities """
|
||||||
|
|
||||||
MAX_FAT32_ITEMS = 65535
|
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._title = title
|
||||||
self._main = main
|
self._main = main
|
||||||
self._check = check
|
self._check = check
|
||||||
|
self._arg_defs = args if args is not None else []
|
||||||
self._padding = padding
|
self._padding = padding
|
||||||
|
|
||||||
self._arguments_kw = {}
|
self._arguments_kw = {}
|
||||||
|
self._arguments_kw_dest = []
|
||||||
|
|
||||||
# Initialize argparse argument parser
|
# Initialize argparse argument parser
|
||||||
self._argparser = argparse.ArgumentParser()
|
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('-o', '--output-dir', help='extract in given output directory')
|
||||||
self._argparser.add_argument('-i', '--input-dir', help='extract from given input 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
|
# Managed Python exception handler
|
||||||
sys.excepthook = self._exception_handler
|
sys.excepthook = self._exception_handler
|
||||||
|
@ -63,14 +77,11 @@ class BIOSUtility:
|
||||||
# Count input files for exit code
|
# Count input files for exit code
|
||||||
self._exit_code = len(self._input_files)
|
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):
|
def run_utility(self):
|
||||||
|
""" Run utility after checking for supported format """
|
||||||
|
|
||||||
for _input_file in self._input_files:
|
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)
|
printer(['***', _input_name], self._padding)
|
||||||
|
|
||||||
|
@ -105,7 +116,7 @@ class BIOSUtility:
|
||||||
# Drag & Drop or CLI
|
# Drag & Drop or CLI
|
||||||
if self._arguments.input_dir:
|
if self._arguments.input_dir:
|
||||||
_input_path_user = 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)
|
self._input_files = get_path_files(_input_path_full)
|
||||||
else:
|
else:
|
||||||
# Parse list of input files (i.e. argparse FileType objects)
|
# Parse list of input files (i.e. argparse FileType objects)
|
||||||
|
@ -123,16 +134,16 @@ class BIOSUtility:
|
||||||
else:
|
else:
|
||||||
# Script w/o parameters
|
# Script w/o parameters
|
||||||
_input_path_user = get_dequoted_path(input('\nEnter input directory path: '))
|
_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)
|
self._input_files = get_path_files(_input_path_full)
|
||||||
|
|
||||||
_output_path = get_dequoted_path(input('\nEnter output directory path: '))
|
_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
|
@staticmethod
|
||||||
def _get_input_path(input_path):
|
def _get_user_path(input_path):
|
||||||
if not input_path:
|
if not input_path:
|
||||||
# Use runtime directory if no user path is specified
|
# Use runtime directory if no user path is specified
|
||||||
absolute_path = runtime_root()
|
absolute_path = runtime_root()
|
||||||
|
@ -142,7 +153,7 @@ class BIOSUtility:
|
||||||
absolute_path = input_path
|
absolute_path = input_path
|
||||||
# Otherwise, make it runtime directory relative
|
# Otherwise, make it runtime directory relative
|
||||||
else:
|
else:
|
||||||
absolute_path = safe_path(runtime_root(), input_path)
|
absolute_path = real_path(input_path)
|
||||||
|
|
||||||
return absolute_path
|
return absolute_path
|
||||||
|
|
||||||
|
|
|
@ -1,33 +1,48 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3 -B
|
||||||
#coding=utf-8
|
# 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):
|
def padder(padd_count, tab=False):
|
||||||
|
""" Generate padding (spaces or tabs) """
|
||||||
|
|
||||||
return ('\t' if tab else ' ') * padd_count
|
return ('\t' if tab else ' ') * padd_count
|
||||||
|
|
||||||
# Get String from given input object
|
|
||||||
def to_string(in_object, sep_char=''):
|
def to_string(in_object, sep_char=''):
|
||||||
if type(in_object).__name__ in ('list','tuple'):
|
""" Get String from given input object """
|
||||||
|
|
||||||
|
if type(in_object).__name__ in ('list', 'tuple'):
|
||||||
out_string = sep_char.join(map(str, in_object))
|
out_string = sep_char.join(map(str, in_object))
|
||||||
else:
|
else:
|
||||||
out_string = str(in_object)
|
out_string = str(in_object)
|
||||||
|
|
||||||
return out_string
|
return out_string
|
||||||
|
|
||||||
# Get Bytes from given buffer or file path
|
|
||||||
def file_to_bytes(in_object):
|
def file_to_bytes(in_object):
|
||||||
|
""" Get Bytes from given buffer or file path """
|
||||||
|
|
||||||
object_bytes = in_object
|
object_bytes = in_object
|
||||||
|
|
||||||
if type(in_object).__name__ not in ('bytes','bytearray'):
|
if type(in_object).__name__ not in ('bytes', 'bytearray'):
|
||||||
with open(to_string(in_object), 'rb') as object_data:
|
with open(to_string(in_object), 'rb') as object_data:
|
||||||
object_bytes = object_data.read()
|
object_bytes = object_data.read()
|
||||||
|
|
||||||
return object_bytes
|
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):
|
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)
|
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