mirror of
https://github.com/platomav/BIOSUtilities.git
synced 2025-05-13 06:34:42 -04:00
BIOSUtilities v24.10.01
Complete repository overhaul into python project Re-designed BIOSUtility base template class flow Re-structured utilities as BIOSUtility inherited Re-structured project for 3rd-party compatibility Unified project requirements and package version Code overhaul with type hints and linting support Switched external executable dependencies via PATH BIOSUtility enforces simple check and parse methods Utilities now work with both path and buffer inputs Adjusted class, method, function names and parameters Improved Dell PFS Update Extractor sub-PFAT processing Improved Award BIOS Module Extractor corruption handling Improved Apple EFI Image Identifier to expose the EFI ID Improved Insyde iFlash/iFdPacker Extractor with ISH & PDT Re-written Apple EFI Package Extractor to support all PKG
This commit is contained in:
parent
ef50b75ae1
commit
cda2fbd0b1
65 changed files with 6239 additions and 5233 deletions
243
biosutilities/panasonic_bios_extract.py
Normal file
243
biosutilities/panasonic_bios_extract.py
Normal file
|
@ -0,0 +1,243 @@
|
|||
#!/usr/bin/env python3 -B
|
||||
# coding=utf-8
|
||||
|
||||
"""
|
||||
Panasonic BIOS Extract
|
||||
Panasonic BIOS Package Extractor
|
||||
Copyright (C) 2018-2024 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
import io
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
import pefile
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
from dissect.util.compression import lznt1
|
||||
|
||||
from biosutilities.common.compression import is_szip_supported, szip_decompress
|
||||
from biosutilities.common.paths import path_files, make_dirs, path_stem, safe_name
|
||||
from biosutilities.common.executables import ms_pe_desc, ms_pe, is_ms_pe, ms_pe_info_show
|
||||
from biosutilities.common.patterns import PAT_MICROSOFT_CAB
|
||||
from biosutilities.common.system import printer
|
||||
from biosutilities.common.templates import BIOSUtility
|
||||
from biosutilities.common.texts import file_to_bytes
|
||||
|
||||
from biosutilities.ami_pfat_extract import AmiPfatExtract
|
||||
|
||||
|
||||
class PanasonicBiosExtract(BIOSUtility):
|
||||
""" Panasonic BIOS Package Extractor """
|
||||
|
||||
TITLE: str = 'Panasonic BIOS Package Extractor'
|
||||
|
||||
PAN_PE_DESC_UNP: str = 'UNPACK UTILITY'
|
||||
|
||||
PAN_PE_DESC_UPD: str = 'BIOS UPDATE'
|
||||
|
||||
def check_format(self, input_object: str | bytes | bytearray) -> bool:
|
||||
""" Check if input is Panasonic BIOS Package PE """
|
||||
|
||||
pe_file: pefile.PE | None = ms_pe(in_file=input_object, silent=True)
|
||||
|
||||
if not pe_file:
|
||||
return False
|
||||
|
||||
if ms_pe_desc(pe_file=pe_file, silent=True).decode(encoding='utf-8', errors='ignore').upper() not in (
|
||||
self.PAN_PE_DESC_UNP, self.PAN_PE_DESC_UPD):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def parse_format(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> int:
|
||||
""" Parse & Extract Panasonic BIOS Package PE """
|
||||
|
||||
upd_pe_file: pefile.PE = ms_pe(in_file=input_object, padding=padding) # type: ignore
|
||||
|
||||
upd_pe_name: str = self._panasonic_pkg_name(input_object=input_object)
|
||||
|
||||
printer(message=f'Panasonic BIOS Package > PE ({upd_pe_name})\n'.replace(' ()', ''), padding=padding)
|
||||
|
||||
ms_pe_info_show(pe_file=upd_pe_file, padding=padding + 4)
|
||||
|
||||
make_dirs(in_path=extract_path, delete=True)
|
||||
|
||||
upd_pe_path: str = self._panasonic_cab_extract(input_object=input_object,
|
||||
extract_path=extract_path, padding=padding + 8)
|
||||
|
||||
upd_padding: int = padding
|
||||
|
||||
if upd_pe_path:
|
||||
upd_padding = padding + 16
|
||||
|
||||
upd_pe_name = self._panasonic_pkg_name(input_object=upd_pe_path)
|
||||
|
||||
printer(message=f'Panasonic BIOS Update > PE ({upd_pe_name})\n'.replace(' ()', ''), padding=upd_padding)
|
||||
|
||||
upd_pe_file = ms_pe(in_file=upd_pe_path, padding=upd_padding) # type: ignore
|
||||
|
||||
ms_pe_info_show(pe_file=upd_pe_file, padding=upd_padding + 4)
|
||||
|
||||
os.remove(path=upd_pe_path)
|
||||
|
||||
is_upd_extracted: bool = self._panasonic_res_extract(pe_file=upd_pe_file, extract_path=extract_path,
|
||||
pe_name=upd_pe_name, padding=upd_padding + 8)
|
||||
|
||||
if not is_upd_extracted:
|
||||
is_upd_extracted = self._panasonic_img_extract(pe_file=upd_pe_file, extract_path=extract_path,
|
||||
pe_name=upd_pe_name, padding=upd_padding + 8)
|
||||
|
||||
return 0 if is_upd_extracted else 1
|
||||
|
||||
@staticmethod
|
||||
def _panasonic_pkg_name(input_object: str | bytes | bytearray) -> str:
|
||||
""" Get Panasonic BIOS Package file name, when applicable """
|
||||
|
||||
if isinstance(input_object, str) and os.path.isfile(path=input_object):
|
||||
return safe_name(in_name=path_stem(in_path=input_object))
|
||||
|
||||
return ''
|
||||
|
||||
def _panasonic_cab_extract(self, input_object: str | bytes | bytearray, extract_path: str, padding: int = 0) -> str:
|
||||
""" Search and Extract Panasonic BIOS Package PE CAB archive """
|
||||
|
||||
input_data: bytes = file_to_bytes(in_object=input_object)
|
||||
|
||||
cab_match: re.Match[bytes] | None = PAT_MICROSOFT_CAB.search(string=input_data)
|
||||
|
||||
if cab_match:
|
||||
cab_bgn: int = cab_match.start()
|
||||
|
||||
cab_end: int = cab_bgn + int.from_bytes(bytes=input_data[cab_bgn + 0x8:cab_bgn + 0xC], byteorder='little')
|
||||
|
||||
cab_tag: str = f'[0x{cab_bgn:06X}-0x{cab_end:06X}]'
|
||||
|
||||
cab_path: str = os.path.join(extract_path, f'CAB_{cab_tag}.cab')
|
||||
|
||||
with open(file=cab_path, mode='wb') as cab_file_object:
|
||||
cab_file_object.write(input_data[cab_bgn:cab_end])
|
||||
|
||||
if is_szip_supported(in_path=cab_path, padding=padding, silent=False):
|
||||
printer(message=f'Panasonic BIOS Package > PE > CAB {cab_tag}', padding=padding)
|
||||
|
||||
if szip_decompress(in_path=cab_path, out_path=extract_path, in_name='CAB',
|
||||
padding=padding + 4, check=True) == 0:
|
||||
os.remove(path=cab_path) # Successful extraction, delete CAB archive
|
||||
|
||||
for extracted_file_path in path_files(in_path=extract_path):
|
||||
extracted_pe_file: pefile.PE | None = ms_pe(
|
||||
in_file=extracted_file_path, padding=padding, silent=True)
|
||||
|
||||
if extracted_pe_file:
|
||||
extracted_pe_desc: bytes = ms_pe_desc(pe_file=extracted_pe_file, silent=True)
|
||||
|
||||
if extracted_pe_desc.decode(encoding='utf-8', errors='ignore'
|
||||
).upper() == self.PAN_PE_DESC_UPD:
|
||||
return extracted_file_path
|
||||
|
||||
return ''
|
||||
|
||||
@staticmethod
|
||||
def _panasonic_res_extract(pe_file: pefile.PE, extract_path: str, pe_name: str = '', padding: int = 0) -> bool:
|
||||
""" Extract & Decompress Panasonic BIOS Update PE RCDATA (LZNT1) """
|
||||
|
||||
is_rcdata: bool = False
|
||||
|
||||
# When fast_load is used, IMAGE_DIRECTORY_ENTRY_RESOURCE must be parsed prior to RCDATA Directories
|
||||
pe_file.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE']])
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
for entry in pe_file.DIRECTORY_ENTRY_RESOURCE.entries:
|
||||
# Parse all Resource Data Directories > RCDATA (ID = 10)
|
||||
if entry.struct.name == 'IMAGE_RESOURCE_DIRECTORY_ENTRY' and entry.struct.Id == 0xA:
|
||||
is_rcdata = True
|
||||
|
||||
for resource in entry.directory.entries:
|
||||
res_bgn: int = resource.directory.entries[0].data.struct.OffsetToData
|
||||
res_len: int = resource.directory.entries[0].data.struct.Size
|
||||
res_end: int = res_bgn + res_len
|
||||
|
||||
res_bin: bytes = pe_file.get_data(res_bgn, res_len)
|
||||
|
||||
res_tag: str = f'{pe_name} [0x{res_bgn:06X}-0x{res_end:06X}]'.strip()
|
||||
|
||||
res_out: str = os.path.join(extract_path, f'{res_tag}')
|
||||
|
||||
printer(message=res_tag, padding=padding)
|
||||
|
||||
try:
|
||||
res_raw: bytes = lznt1.decompress(src=res_bin[0x8:])
|
||||
|
||||
if len(res_raw) != int.from_bytes(bytes=res_bin[0x4:0x8], byteorder='little'):
|
||||
raise ValueError('LZNT1_DECOMPRESS_BAD_SIZE')
|
||||
|
||||
printer(message='Successful LZNT1 decompression via Dissect!', padding=padding + 4)
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
logging.debug('Error: LZNT1 decompression of %s failed: %s', res_tag, error)
|
||||
|
||||
res_raw = res_bin
|
||||
|
||||
printer(message='Successful PE Resource extraction!', padding=padding + 4)
|
||||
|
||||
# Detect & Unpack AMI BIOS Guard (PFAT) BIOS image
|
||||
if AmiPfatExtract().check_format(input_object=res_raw):
|
||||
pfat_dir: str = os.path.join(extract_path, res_tag)
|
||||
|
||||
AmiPfatExtract().parse_format(input_object=res_raw, extract_path=pfat_dir,
|
||||
padding=padding + 8)
|
||||
else:
|
||||
if is_ms_pe(in_file=res_raw):
|
||||
res_ext: str = 'exe'
|
||||
elif res_raw.startswith(b'[') and res_raw.endswith((b'\x0D\x0A', b'\x0A')):
|
||||
res_ext = 'txt'
|
||||
else:
|
||||
res_ext = 'bin'
|
||||
|
||||
if res_ext == 'txt':
|
||||
printer(message=None, new_line=False)
|
||||
|
||||
for line in io.BytesIO(res_raw).readlines():
|
||||
line_text: str = line.decode(encoding='utf-8', errors='ignore').rstrip()
|
||||
|
||||
printer(message=line_text, padding=padding + 8, new_line=False)
|
||||
|
||||
with open(file=f'{res_out}.{res_ext}', mode='wb') as out_file_object:
|
||||
out_file_object.write(res_raw)
|
||||
|
||||
return is_rcdata
|
||||
|
||||
@staticmethod
|
||||
def _panasonic_img_extract(pe_file: pefile.PE, extract_path: str, pe_name: str = '',
|
||||
padding: int = 0) -> bool:
|
||||
""" Extract Panasonic BIOS Update PE Data when RCDATA is not available """
|
||||
|
||||
pe_data: bytes = bytes(pe_file.__data__)
|
||||
|
||||
sec_bgn: int = pe_file.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY[
|
||||
'IMAGE_DIRECTORY_ENTRY_SECURITY']].VirtualAddress
|
||||
|
||||
img_bgn: int = (pe_file.OPTIONAL_HEADER.BaseOfData + # type: ignore
|
||||
pe_file.OPTIONAL_HEADER.SizeOfInitializedData)
|
||||
|
||||
img_end: int = sec_bgn or len(pe_data)
|
||||
|
||||
img_bin: bytes = pe_data[img_bgn:img_end]
|
||||
|
||||
img_tag: str = f'{pe_name} [0x{img_bgn:X}-0x{img_end:X}]'.strip()
|
||||
|
||||
img_out: str = os.path.join(extract_path, f'{img_tag}.bin')
|
||||
|
||||
printer(message=img_tag, padding=padding)
|
||||
|
||||
with open(file=img_out, mode='wb') as out_img_object:
|
||||
out_img_object.write(img_bin)
|
||||
|
||||
printer(message='Successful PE Data extraction!', padding=padding + 4)
|
||||
|
||||
return bool(img_bin)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
PanasonicBiosExtract().run_utility()
|
Loading…
Add table
Add a link
Reference in a new issue