Improved Intel BIOS Guard Signature parsing

AMI BIOS Guard Extractor can now attempt to detect Intel BG Signature via a pattern, when the Signature header is unknown or obfuscated (Panasonic).
This commit is contained in:
Plato Mavropoulos 2024-05-16 01:03:15 +03:00
parent c5e8638f92
commit fdfdab011d
4 changed files with 50 additions and 20 deletions

View file

@ -10,6 +10,7 @@ Copyright (C) 2018-2024 Plato Mavropoulos
import ctypes
import os
import re
import struct
from common.externals import get_bgs_tool
from common.num_ops import get_ordinal
@ -20,7 +21,7 @@ from common.system import printer
from common.templates import BIOSUtility
from common.text_ops import bytes_to_hex, file_to_bytes
TITLE = 'AMI BIOS Guard Extractor v5.0'
TITLE = 'AMI BIOS Guard Extractor v6.0'
class AmiBiosGuardHeader(ctypes.LittleEndianStructure):
@ -79,6 +80,11 @@ class IntelBiosGuardHeader(ctypes.LittleEndianStructure):
return f'{id_text} {id_guid}'
def get_hdr_marker(self) -> bytes:
""" Get Intel BIOS Guard Header Marker """
return struct.pack('<HH16B', self.BGVerMajor, self.BGVerMinor, *self.PlatformID)
def get_flags(self) -> tuple:
""" Get Intel BIOS Guard Header Attributes """
@ -261,19 +267,30 @@ def parse_bg_script(script_data: bytes, padding: int = 0) -> int:
return 0
def parse_bg_sign(input_data: bytes, sign_offset: int, print_info: bool = False, padding: int = 0) -> int:
def parse_bg_sign(input_data: bytes, sign_offset: int, sign_length: int = 0,
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 # Unknown0 = 1, Unknown1 = 1
elif bg_sig_hdr.Unknown0 == 2:
bg_sig_rsa_struct = IntelBiosGuardSignatureRsa3k # Unknown0 = 2, Unknown1 = 3
elif sign_length == PFAT_INT_SIG_HDR_LEN + PFAT_INT_SIG_R2K_LEN:
bg_sig_rsa_struct = IntelBiosGuardSignatureRsa2k
else:
# Unknown0 = 2, Unknown1 = 3
printer('Warning: Detected Intel BIOS Guard Signature 2K length via pattern!\n', padding, False)
elif sign_length == PFAT_INT_SIG_HDR_LEN + PFAT_INT_SIG_R3K_LEN:
bg_sig_rsa_struct = IntelBiosGuardSignatureRsa3k
bg_sig_rsa = get_struct(input_data, sign_offset + PFAT_BLK_SIG_LEN, bg_sig_rsa_struct)
printer('Warning: Detected Intel BIOS Guard Signature 3K length via pattern!\n', padding, False)
else:
bg_sig_rsa_struct = IntelBiosGuardSignatureRsa3k
printer('Error: Could not detect Intel BIOS Guard Signature length, assuming 3K!\n', padding, False, pause=True)
bg_sig_rsa = get_struct(input_data, sign_offset + PFAT_INT_SIG_HDR_LEN, bg_sig_rsa_struct)
if print_info:
bg_sig_hdr.struct_print(padding)
@ -281,7 +298,7 @@ def parse_bg_sign(input_data: bytes, sign_offset: int, print_info: bool = False,
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)
return PFAT_INT_SIG_HDR_LEN + ctypes.sizeof(bg_sig_rsa_struct)
def parse_pfat_hdr(buffer: bytes | bytearray, padding: int = 0) -> tuple:
@ -347,6 +364,8 @@ def parse_pfat_file(input_object: str | bytes | bytearray, extract_path: str, pa
all_blocks_dict: dict = {}
bg_sign_len: int = 0
extract_name: str = path_name(extract_path).removesuffix(extract_suffix())
make_dirs(extract_path, delete=True)
@ -371,7 +390,7 @@ def parse_pfat_file(input_object: str | bytes | bytearray, extract_path: str, pa
bg_hdr.struct_print(padding + 12)
bg_script_bgn: int = block_off + PFAT_BLK_HDR_LEN
bg_script_bgn: int = block_off + PFAT_INT_HDR_LEN
bg_script_end: int = bg_script_bgn + bg_hdr.ScriptSize
bg_data_bgn: int = bg_script_end
@ -379,15 +398,19 @@ def parse_pfat_file(input_object: str | bytes | bytearray, extract_path: str, pa
bg_data_bin: bytes = pfat_buffer[bg_data_bgn:bg_data_end]
block_off: int = bg_data_end # Assume next block starts at data end
block_off = bg_data_end # Assume next block starts at data end
is_sfam, _, _, _, _ = bg_hdr.get_flags() # SFAM, ProtectEC, GFXMitDis, FTU, Reserved
if is_sfam:
printer(f'Intel BIOS Guard {block_status} Signature:\n', padding + 8)
if bg_sign_len == 0:
bg_sign_len = pfat_buffer.find(bg_hdr.get_hdr_marker(), bg_data_end,
bg_data_end + PFAT_INT_SIG_MAX_LEN) - bg_data_end
# 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 += parse_bg_sign(pfat_buffer, bg_data_end, bg_sign_len, True, padding + 12)
printer(f'Intel BIOS Guard {block_status} Script:\n', padding + 8)
@ -427,8 +450,11 @@ def parse_pfat_file(input_object: str | bytes | bytearray, extract_path: str, pa
PFAT_AMI_HDR_LEN: int = ctypes.sizeof(AmiBiosGuardHeader)
PFAT_BLK_HDR_LEN: int = ctypes.sizeof(IntelBiosGuardHeader)
PFAT_BLK_SIG_LEN: int = ctypes.sizeof(IntelBiosGuardSignatureHeader)
PFAT_INT_HDR_LEN: int = ctypes.sizeof(IntelBiosGuardHeader)
PFAT_INT_SIG_HDR_LEN: int = ctypes.sizeof(IntelBiosGuardSignatureHeader)
PFAT_INT_SIG_R2K_LEN: int = ctypes.sizeof(IntelBiosGuardSignatureRsa2k)
PFAT_INT_SIG_R3K_LEN: int = ctypes.sizeof(IntelBiosGuardSignatureRsa3k)
PFAT_INT_SIG_MAX_LEN: int = PFAT_INT_SIG_HDR_LEN + PFAT_INT_SIG_R3K_LEN
if __name__ == '__main__':
BIOSUtility(title=TITLE, check=is_ami_pfat, main=parse_pfat_file).run_utility()

View file

@ -24,9 +24,9 @@ from common.system import printer
from common.templates import BIOSUtility
from common.text_ops import file_to_bytes
from AMI_PFAT_Extract import IntelBiosGuardHeader, parse_bg_script, parse_bg_sign
from AMI_PFAT_Extract import IntelBiosGuardHeader, parse_bg_script, parse_bg_sign, PFAT_INT_SIG_MAX_LEN
TITLE = 'Dell PFS Update Extractor v7.0'
TITLE = 'Dell PFS Update Extractor v7.1'
class DellPfsHeader(ctypes.LittleEndianStructure):
@ -934,10 +934,14 @@ def parse_pfat_pfs(entry_hdr, entry_data, padding=0, structure=True):
if structure:
printer(f'PFAT Block {pfat_entry_idx_ord} - Signature:\n', padding + 12)
# Get sub-PFS PFAT Signature Structure values
bg_sign_len = parse_bg_sign(pfat_payload, pfat_payload_end, structure, padding + 16)
# Get sub-PFS PFAT Signature length from Header pattern (not needed for Dell PFS)
_pfat_sign_len = pfat_payload.find(pfat_hdr.get_hdr_marker(), pfat_payload_end,
pfat_payload_end + PFAT_INT_SIG_MAX_LEN) - pfat_payload_end
if len(pfat_payload[pfat_payload_end:pfat_payload_end + bg_sign_len]) != bg_sign_len:
# Get sub-PFS PFAT Signature Structure values
pfat_sign_len = parse_bg_sign(pfat_payload, pfat_payload_end, _pfat_sign_len, structure, padding + 16)
if len(pfat_payload[pfat_payload_end:pfat_payload_end + pfat_sign_len]) != pfat_sign_len:
printer(f'Error: Detected sub-PFS PFAT Block {pfat_entry_idx_ord} Signature Size mismatch!',
padding + 12)

View file

@ -318,7 +318,7 @@ To run the utility, you do not need any prerequisites.
#### **Description**
Parses Panasonic BIOS Package executables and extracts their firmware (e.g. SPI, BIOS/UEFI, EC etc) and utilities (e.g. winprom, configuration etc) components. It supports all Panasonic BIOS Package revisions and formats, including those which contain LZNT1 compressed files. The output comprises only final firmware components which are directly usable by end users.
Parses Panasonic BIOS Package executables and extracts their firmware (e.g. SPI, BIOS/UEFI, EC etc) and utilities (e.g. winprom, configuration etc) components. It supports all Panasonic BIOS Package revisions and formats, including those which contain LZNT1 compressed files and/or AMI PFAT payloads. The output comprises only final firmware components which are directly usable by end users.
#### **Usage**

View file

@ -1,2 +1,2 @@
dissect.util == 3.15
pefile == 2023.2.7
dissect.util==3.16
pefile==2023.2.7