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:
Plato Mavropoulos 2024-04-24 01:22:53 +03:00
parent 03ae0cf070
commit d85a7f82dc
37 changed files with 2897 additions and 2174 deletions

View file

@ -1,22 +1,19 @@
#!/usr/bin/env python3
#coding=utf-8
#!/usr/bin/env python3 -B
# coding=utf-8
"""
Panasonic BIOS Extract
Panasonic BIOS Package Extractor
Copyright (C) 2018-2022 Plato Mavropoulos
Copyright (C) 2018-2024 Plato Mavropoulos
"""
TITLE = 'Panasonic BIOS Package Extractor v2.0_a10'
import os
import io
import sys
import lznt1
import logging
import os
import pefile
# Stop __pycache__ generation
sys.dont_write_bytecode = True
from dissect.util.compression import lznt1
from common.comp_szip import is_szip_supported, szip_decompress
from common.path_ops import get_path_files, make_dirs, path_stem, safe_name
@ -28,182 +25,216 @@ from common.text_ops import file_to_bytes
from AMI_PFAT_Extract import is_ami_pfat, parse_pfat_file
# Check if input is Panasonic BIOS Package PE
TITLE = 'Panasonic BIOS Package Extractor v3.0'
def is_panasonic_pkg(in_file):
""" Check if input is Panasonic BIOS Package PE """
in_buffer = file_to_bytes(in_file)
pe_file = get_pe_file(in_buffer, fast=True)
pe_file = get_pe_file(in_buffer, silent=True)
if not pe_file:
return False
pe_info = get_pe_info(pe_file)
pe_info = get_pe_info(pe_file, silent=True)
if not pe_info:
return False
if pe_info.get(b'FileDescription',b'').upper() != b'UNPACK UTILITY':
if pe_info.get(b'FileDescription', b'').upper() != b'UNPACK UTILITY':
return False
if not PAT_MICROSOFT_CAB.search(in_buffer):
return False
return True
# Search and Extract Panasonic BIOS Package PE CAB archive
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_len = int.from_bytes(buffer[cab_bgn + 0x8:cab_bgn + 0xC], 'little')
cab_end = cab_bgn + cab_len
cab_bin = buffer[cab_bgn:cab_end]
cab_tag = f'[0x{cab_bgn:06X}-0x{cab_end:06X}]'
cab_path = os.path.join(extract_path, f'CAB_{cab_tag}.cab')
with open(cab_path, 'wb') as cab_file:
cab_file.write(cab_bin) # Store CAB archive
cab_file.write(cab_bin) # Store CAB archive
if is_szip_supported(cab_path, padding, check=True):
printer(f'Panasonic BIOS Package > PE > CAB {cab_tag}', padding)
if szip_decompress(cab_path, extract_path, 'CAB', padding + 4, check=True) == 0:
os.remove(cab_path) # Successful extraction, delete CAB archive
os.remove(cab_path) # Successful extraction, delete CAB archive
else:
return pe_path, pe_file, pe_info
else:
return pe_path, pe_file, pe_info
for file_path in get_path_files(extract_path):
pe_file = get_pe_file(file_path, fast=True)
pe_file = get_pe_file(file_path, padding, silent=True)
if pe_file:
pe_info = get_pe_info(pe_file)
if pe_info.get(b'FileDescription',b'').upper() == b'BIOS UPDATE':
pe_info = get_pe_info(pe_file, padding, silent=True)
if pe_info.get(b'FileDescription', b'').upper() == b'BIOS UPDATE':
pe_path = file_path
break
else:
return pe_path, pe_file, pe_info
return pe_path, pe_file, pe_info
# Extract & Decompress Panasonic BIOS Update PE RCDATA (LZNT1)
def panasonic_res_extract(pe_name, pe_file, extract_path, padding=0):
""" Extract & Decompress Panasonic BIOS Update PE RCDATA (LZNT1) """
is_rcdata = False
# When fast_load is used, IMAGE_DIRECTORY_ENTRY_RESOURCE must be parsed prior to RCDATA Directories
pe_file.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE']])
# Parse all Resource Data Directories > RCDATA (ID = 10)
for entry in pe_file.DIRECTORY_ENTRY_RESOURCE.entries:
if entry.struct.name == 'IMAGE_RESOURCE_DIRECTORY_ENTRY' and entry.struct.Id == 0xA:
is_rcdata = True
for resource in entry.directory.entries:
res_bgn = resource.directory.entries[0].data.struct.OffsetToData
res_len = resource.directory.entries[0].data.struct.Size
res_end = res_bgn + res_len
res_bin = pe_file.get_data(res_bgn, res_len)
res_tag = f'{pe_name} [0x{res_bgn:06X}-0x{res_end:06X}]'
res_out = os.path.join(extract_path, f'{res_tag}')
printer(res_tag, padding + 4)
try:
res_raw = lznt1.decompress(res_bin[0x8:])
printer('Succesfull LZNT1 decompression via lznt1!', padding + 8)
except Exception:
if len(res_raw) != int.from_bytes(res_bin[0x4:0x8], 'little'):
raise ValueError('LZNT1_DECOMPRESS_BAD_SIZE')
printer('Succesfull LZNT1 decompression via Dissect!', padding + 8)
except Exception as error: # pylint: disable=broad-except
logging.debug('Error: LZNT1 decompression of %s failed: %s', res_tag, error)
res_raw = res_bin
printer('Succesfull PE Resource extraction!', padding + 8)
# Detect & Unpack AMI BIOS Guard (PFAT) BIOS image
if is_ami_pfat(res_raw):
pfat_dir = os.path.join(extract_path, res_tag)
parse_pfat_file(res_raw, pfat_dir, padding + 12)
else:
if is_pe_file(res_raw):
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'
else:
res_ext = 'bin'
if res_ext == 'txt':
printer(new_line=False)
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)
with open(f'{res_out}.{res_ext}', 'wb') as out_file:
out_file.write(res_raw)
return is_rcdata
# Extract Panasonic BIOS Update PE Data when RCDATA is not available
def panasonic_img_extract(pe_name, pe_path, pe_file, extract_path, padding=0):
""" Extract Panasonic BIOS Update PE Data when RCDATA is not available """
pe_data = file_to_bytes(pe_path)
sec_bgn = pe_file.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_SECURITY']].VirtualAddress
sec_bgn = pe_file.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY[
'IMAGE_DIRECTORY_ENTRY_SECURITY']].VirtualAddress
img_bgn = pe_file.OPTIONAL_HEADER.BaseOfData + pe_file.OPTIONAL_HEADER.SizeOfInitializedData
img_end = sec_bgn or len(pe_data)
img_bin = pe_data[img_bgn:img_end]
img_tag = f'{pe_name} [0x{img_bgn:X}-0x{img_end:X}]'
img_out = os.path.join(extract_path, f'{img_tag}.bin')
printer(img_tag, padding + 4)
with open(img_out, 'wb') as out_img:
out_img.write(img_bin)
printer('Succesfull PE Data extraction!', padding + 8)
return bool(img_bin)
# Parse & Extract Panasonic BIOS Package PE
def panasonic_pkg_extract(input_file, extract_path, padding=0):
""" Parse & Extract Panasonic BIOS Package PE """
input_buffer = file_to_bytes(input_file)
make_dirs(extract_path, delete=True)
pkg_pe_file = get_pe_file(input_buffer, fast=True)
pkg_pe_file = get_pe_file(input_buffer, padding)
if not pkg_pe_file:
return 2
pkg_pe_info = get_pe_info(pkg_pe_file)
pkg_pe_info = get_pe_info(pkg_pe_file, padding)
if not pkg_pe_info:
return 3
pkg_pe_name = path_stem(input_file)
printer(f'Panasonic BIOS Package > PE ({pkg_pe_name})\n', padding)
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):
return 4
upd_pe_name = safe_name(path_stem(upd_pe_path))
printer(f'Panasonic BIOS Update > PE ({upd_pe_name})\n', padding + 12)
show_pe_info(upd_pe_info, padding + 16)
is_upd_res, is_upd_img = False, False
is_upd_res = panasonic_res_extract(upd_pe_name, upd_pe_file, extract_path, padding + 16)
if not is_upd_res:
is_upd_img = panasonic_img_extract(upd_pe_name, upd_pe_path, upd_pe_file, extract_path, padding + 16)
os.remove(upd_pe_path)
return 0 if is_upd_res or is_upd_img else 1
if __name__ == '__main__':
BIOSUtility(TITLE, is_panasonic_pkg, panasonic_pkg_extract).run_utility()
BIOSUtility(title=TITLE, check=is_panasonic_pkg, main=panasonic_pkg_extract).run_utility()