mirror of
https://github.com/platomav/BIOSUtilities.git
synced 2025-05-13 06:34:42 -04:00

Added Apple EFI PBZX Extractor v1.0_a4 Updated Apple EFI Image Identifier v2.0_a4 Updated Apple EFI IM4P Splitter v3.0_a4 Updated Insyde iFlash/iFdPacker Extractor v2.0_a10 Improved 7-Zip parameter control
193 lines
7.1 KiB
Python
193 lines
7.1 KiB
Python
#!/usr/bin/env python3
|
|
#coding=utf-8
|
|
|
|
"""
|
|
Apple EFI ID
|
|
Apple EFI Image Identifier
|
|
Copyright (C) 2018-2022 Plato Mavropoulos
|
|
"""
|
|
|
|
TITLE = 'Apple EFI Image Identifier v2.0_a4'
|
|
|
|
import os
|
|
import sys
|
|
import zlib
|
|
import struct
|
|
import ctypes
|
|
import subprocess
|
|
|
|
# Stop __pycache__ generation
|
|
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.patterns import PAT_APPLE_EFI
|
|
from common.struct_ops import char, get_struct, uint8_t
|
|
from common.system import argparse_init, printer, script_init
|
|
from common.text_ops import file_to_bytes
|
|
|
|
class IntelBiosId(ctypes.LittleEndianStructure):
|
|
_pack_ = 1
|
|
_fields_ = [
|
|
('Signature', char*8), # 0x00
|
|
('BoardID', uint8_t*16), # 0x08
|
|
('Dot1', uint8_t*2), # 0x18
|
|
('BoardExt', uint8_t*6), # 0x1A
|
|
('Dot2', uint8_t*2), # 0x20
|
|
('VersionMajor', uint8_t*8), # 0x22
|
|
('Dot3', uint8_t*2), # 0x2A
|
|
('BuildType', uint8_t*2), # 0x2C
|
|
('VersionMinor', uint8_t*4), # 0x2E
|
|
('Dot4', uint8_t*2), # 0x32
|
|
('Year', uint8_t*4), # 0x34
|
|
('Month', uint8_t*4), # 0x38
|
|
('Day', uint8_t*4), # 0x3C
|
|
('Hour', uint8_t*4), # 0x40
|
|
('Minute', uint8_t*4), # 0x44
|
|
('NullTerminator', uint8_t*2), # 0x48
|
|
# 0x4A
|
|
]
|
|
|
|
# https://github.com/tianocore/edk2-platforms/blob/master/Platform/Intel/BoardModulePkg/Include/Guid/BiosId.h
|
|
|
|
@staticmethod
|
|
def decode(field):
|
|
return struct.pack('B' * len(field), *field).decode('utf-16','ignore').strip('\x00 ')
|
|
|
|
def get_bios_id(self):
|
|
BoardID = self.decode(self.BoardID)
|
|
BoardExt = self.decode(self.BoardExt)
|
|
VersionMajor = self.decode(self.VersionMajor)
|
|
BuildType = self.decode(self.BuildType)
|
|
VersionMinor = self.decode(self.VersionMinor)
|
|
BuildDate = f'20{self.decode(self.Year)}-{self.decode(self.Month)}-{self.decode(self.Day)}'
|
|
BuildTime = f'{self.decode(self.Hour)}-{self.decode(self.Minute)}'
|
|
|
|
return BoardID, BoardExt, VersionMajor, BuildType, VersionMinor, BuildDate, BuildTime
|
|
|
|
def struct_print(self, p):
|
|
BoardID,BoardExt,VersionMajor,BuildType,VersionMinor,BuildDate,BuildTime = self.get_bios_id()
|
|
|
|
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):
|
|
input_buffer = file_to_bytes(input_file)
|
|
|
|
if PAT_APPLE_EFI.search(input_buffer):
|
|
return True
|
|
|
|
if not os.path.isfile(input_file):
|
|
return False
|
|
|
|
try:
|
|
_ = subprocess.run([get_uefifind_path(), input_file, 'body', 'list', PAT_UEFIFIND],
|
|
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
|
|
return True
|
|
except:
|
|
return False
|
|
|
|
# Parse & Identify (or Rename) Apple EFI image
|
|
def apple_efi_identify(input_file, output_path, padding=0, rename=False):
|
|
if not os.path.isfile(input_file):
|
|
printer('Error: Could not find input file path!', padding)
|
|
|
|
return 1
|
|
|
|
input_buffer = file_to_bytes(input_file)
|
|
|
|
bios_id_match = PAT_APPLE_EFI.search(input_buffer) # Detect $IBIOSI$ pattern
|
|
|
|
if bios_id_match:
|
|
bios_id_res = f'0x{bios_id_match.start():X}'
|
|
|
|
bios_id_hdr = get_struct(input_buffer, bios_id_match.start(), IntelBiosId)
|
|
else:
|
|
# The $IBIOSI$ pattern is within EFI compressed modules so we need to use UEFIFind and UEFIExtract
|
|
try:
|
|
bios_id_res = subprocess.check_output([get_uefifind_path(), input_file, 'body', 'list', PAT_UEFIFIND],
|
|
text=True)[:36]
|
|
|
|
temp_dir = os.path.join(f'{output_path}_uefiextract')
|
|
|
|
del_dirs(temp_dir) # UEFIExtract must create its output folder itself, make sure it is not present
|
|
|
|
_ = subprocess.run([get_uefiextract_path(), input_file, bios_id_res, '-o', temp_dir, '-m', 'body'],
|
|
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
|
|
with open(os.path.join(temp_dir, 'body.bin'), 'rb') as raw_body:
|
|
body_buffer = raw_body.read()
|
|
|
|
bios_id_match = PAT_APPLE_EFI.search(body_buffer) # Detect decompressed $IBIOSI$ pattern
|
|
|
|
bios_id_hdr = get_struct(body_buffer, bios_id_match.start(), IntelBiosId)
|
|
|
|
del_dirs(temp_dir) # Successful UEFIExtract extraction, remove its output (temp) folder
|
|
except:
|
|
printer('Error: Failed to parse compressed $IBIOSI$ pattern!', padding)
|
|
|
|
return 2
|
|
|
|
printer(f'Detected $IBIOSI$ at {bios_id_res}\n', padding)
|
|
|
|
bios_id_hdr.struct_print(padding + 4)
|
|
|
|
if rename:
|
|
input_parent = path_parent(input_file)
|
|
|
|
input_suffix = path_suffixes(input_file)[-1]
|
|
|
|
input_adler32 = zlib.adler32(input_buffer)
|
|
|
|
ID,Ext,Major,Type,Minor,Date,Time = bios_id_hdr.get_bios_id()
|
|
|
|
output_name = f'{ID}_{Ext}_{Major}_{Type}{Minor}_{Date}_{Time}_{input_adler32:08X}{input_suffix}'
|
|
|
|
output_file = os.path.join(input_parent, output_name)
|
|
|
|
if not os.path.isfile(output_file):
|
|
os.replace(input_file, output_file) # Rename input file based on its EFI tag
|
|
|
|
printer(f'Renamed to {output_name}', padding)
|
|
|
|
return 0
|
|
|
|
PAT_UEFIFIND = f'244942494F534924{"."*32}2E00{"."*12}2E00{"."*16}2E00{"."*12}2E00{"."*40}0000'
|
|
|
|
if __name__ == '__main__':
|
|
# Set argparse Arguments
|
|
argparser = argparse_init()
|
|
argparser.add_argument('-r', '--rename', help='rename EFI image based on its tag', action='store_true')
|
|
arguments = argparser.parse_args()
|
|
|
|
rename = arguments.rename # Set EFI image tag renaming optional argument
|
|
|
|
# Initialize script (must be after argparse)
|
|
exit_code,input_files,output_path,padding = script_init(TITLE, arguments, 4)
|
|
|
|
for input_file in input_files:
|
|
input_name = os.path.basename(input_file)
|
|
|
|
printer(['***', input_name], padding - 4)
|
|
|
|
if not is_apple_efi(input_file):
|
|
printer('Error: This is not an Apple EFI image!', padding)
|
|
|
|
continue # Next input file
|
|
|
|
extract_path = os.path.join(output_path, input_name)
|
|
|
|
if apple_efi_identify(input_file, extract_path, padding, rename) == 0:
|
|
exit_code -= 1
|
|
|
|
printer('Done!', pause=True)
|
|
|
|
sys.exit(exit_code)
|