mirror of
https://github.com/platomav/BIOSUtilities.git
synced 2025-05-09 13:52:00 -04:00
Added Apple EFI IM4P Splitter v3.0_a2
This commit is contained in:
parent
c144ad804c
commit
389c30bb65
3 changed files with 215 additions and 0 deletions
160
Apple_EFI_Split.py
Normal file
160
Apple_EFI_Split.py
Normal file
|
@ -0,0 +1,160 @@
|
|||
#!/usr/bin/env python3
|
||||
#coding=utf-8
|
||||
|
||||
"""
|
||||
Apple EFI Split
|
||||
Apple EFI IM4P Splitter
|
||||
Copyright (C) 2018-2022 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'Apple EFI IM4P Splitter v3.0_a2'
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
from common.path_ops import make_dirs, path_stem
|
||||
from common.patterns import PAT_APPLE_IM4P, PAT_INTEL_IFD
|
||||
from common.system import argparse_init, printer, script_init
|
||||
from common.text_ops import file_to_bytes
|
||||
|
||||
# Check if input is Apple EFI IM4P image
|
||||
def is_apple_im4p(input_file):
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
||||
is_im4p = PAT_APPLE_IM4P.search(input_buffer)
|
||||
|
||||
is_ifd = PAT_INTEL_IFD.search(input_buffer)
|
||||
|
||||
return bool(is_im4p and is_ifd)
|
||||
|
||||
# Parse & Split Apple EFI IM4P image
|
||||
def apple_im4p_split(input_file, output_path, padding=0):
|
||||
input_buffer = file_to_bytes(input_file)
|
||||
|
||||
extract_path = os.path.join(f'{output_path}_extracted')
|
||||
|
||||
make_dirs(extract_path, delete=True)
|
||||
|
||||
# Detect IM4P EFI pattern
|
||||
im4p_match = PAT_APPLE_IM4P.search(input_buffer)
|
||||
|
||||
# After IM4P mefi (0x15), multi EFI payloads have _MEFIBIN (0x100) but is difficult to RE w/o varying samples.
|
||||
# However, _MEFIBIN is not required for splitting SPI images due to Intel Flash Descriptor Components Density.
|
||||
|
||||
# IM4P mefi payload start offset
|
||||
mefi_data_bgn = im4p_match.start() + input_buffer[im4p_match.start() - 0x1]
|
||||
|
||||
# IM4P mefi payload size
|
||||
mefi_data_len = int.from_bytes(input_buffer[im4p_match.end() + 0x9:im4p_match.end() + 0xD], 'big')
|
||||
|
||||
# Check if mefi is followed by _MEFIBIN
|
||||
mefibin_exist = input_buffer[mefi_data_bgn:mefi_data_bgn + 0x8] == b'_MEFIBIN'
|
||||
|
||||
# Actual multi EFI payloads start after _MEFIBIN
|
||||
efi_data_bgn = mefi_data_bgn + 0x100 if mefibin_exist else mefi_data_bgn
|
||||
|
||||
# Actual multi EFI payloads size without _MEFIBIN
|
||||
efi_data_len = mefi_data_len - 0x100 if mefibin_exist else mefi_data_len
|
||||
|
||||
# Adjust input file buffer to actual multi EFI payloads data
|
||||
input_buffer = input_buffer[efi_data_bgn:efi_data_bgn + efi_data_len]
|
||||
|
||||
# Parse Intel Flash Descriptor pattern matches
|
||||
for ifd in PAT_INTEL_IFD.finditer(input_buffer):
|
||||
# Component Base Address from FD start (ICH8-ICH10 = 1, IBX = 2, CPT+ = 3)
|
||||
ifd_flmap0_fcba = input_buffer[ifd.start() + 0x4] * 0x10
|
||||
|
||||
# I/O Controller Hub (ICH)
|
||||
if ifd_flmap0_fcba == 0x10:
|
||||
# At ICH, Flash Descriptor starts at 0x0
|
||||
ifd_bgn_substruct = 0x0
|
||||
|
||||
# 0xBC for [0xAC] + 0xFF * 16 sanity check
|
||||
ifd_end_substruct = 0xBC
|
||||
|
||||
# Platform Controller Hub (PCH)
|
||||
else:
|
||||
# At PCH, Flash Descriptor starts at 0x10
|
||||
ifd_bgn_substruct = 0x10
|
||||
|
||||
# 0xBC for [0xAC] + 0xFF * 16 sanity check
|
||||
ifd_end_substruct = 0xBC
|
||||
|
||||
# Actual Flash Descriptor Start Offset
|
||||
ifd_match_start = ifd.start() - ifd_bgn_substruct
|
||||
|
||||
# Actual Flash Descriptor End Offset
|
||||
ifd_match_end = ifd.end() - ifd_end_substruct
|
||||
|
||||
# Calculate Intel Flash Descriptor Flash Component Total Size
|
||||
|
||||
# Component Count (00 = 1, 01 = 2)
|
||||
ifd_flmap0_nc = ((int.from_bytes(input_buffer[ifd_match_end:ifd_match_end + 0x4], 'little') >> 8) & 3) + 1
|
||||
|
||||
# PCH/ICH Strap Length (ME 2-8 & TXE 0-2 & SPS 1-2 <= 0x12, ME 9+ & TXE 3+ & SPS 3+ >= 0x13)
|
||||
ifd_flmap1_isl = input_buffer[ifd_match_end + 0x7]
|
||||
|
||||
# Component Density Byte (ME 2-8 & TXE 0-2 & SPS 1-2 = 0:5, ME 9+ & TXE 3+ & SPS 3+ = 0:7)
|
||||
ifd_comp_den = input_buffer[ifd_match_start + ifd_flmap0_fcba]
|
||||
|
||||
# Component 1 Density Bits (ME 2-8 & TXE 0-2 & SPS 1-2 = 3, ME 9+ & TXE 3+ & SPS 3+ = 4)
|
||||
ifd_comp_1_bitwise = 0xF if ifd_flmap1_isl >= 0x13 else 0x7
|
||||
|
||||
# Component 2 Density Bits (ME 2-8 & TXE 0-2 & SPS 1-2 = 3, ME 9+ & TXE 3+ & SPS 3+ = 4)
|
||||
ifd_comp_2_bitwise = 0x4 if ifd_flmap1_isl >= 0x13 else 0x3
|
||||
|
||||
# Component 1 Density (FCBA > C0DEN)
|
||||
ifd_comp_all_size = IFD_COMP_LEN[ifd_comp_den & ifd_comp_1_bitwise]
|
||||
|
||||
# Component 2 Density (FCBA > C1DEN)
|
||||
if ifd_flmap0_nc == 2:
|
||||
ifd_comp_all_size += IFD_COMP_LEN[ifd_comp_den >> ifd_comp_2_bitwise]
|
||||
|
||||
ifd_data_bgn = ifd_match_start
|
||||
ifd_data_end = ifd_data_bgn + ifd_comp_all_size
|
||||
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_name = path_stem(input_file) if os.path.isfile(input_file) else 'Part'
|
||||
|
||||
output_path = os.path.join(extract_path, f'{output_name}_[{ifd_data_txt}].fd')
|
||||
|
||||
with open(output_path, 'wb') as output_image:
|
||||
output_image.write(output_data)
|
||||
|
||||
printer(f'Split Apple EFI image at {ifd_data_txt}!', padding)
|
||||
|
||||
# Intel Flash Descriptor Component Sizes (4MB, 8MB, 16MB and 32MB)
|
||||
IFD_COMP_LEN = {3: 0x400000, 4: 0x800000, 5: 0x1000000, 6: 0x2000000}
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Set argparse Arguments
|
||||
argparser = argparse_init()
|
||||
arguments = argparser.parse_args()
|
||||
|
||||
# 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_im4p(input_file):
|
||||
printer('Error: This is not an Apple EFI IM4P image!', padding)
|
||||
|
||||
continue # Next input file
|
||||
|
||||
extract_path = os.path.join(output_path, input_name)
|
||||
|
||||
apple_im4p_split(input_file, extract_path, padding)
|
||||
|
||||
exit_code -= 1
|
||||
|
||||
printer('Done!', pause=True)
|
||||
|
||||
sys.exit(exit_code)
|
53
README.md
53
README.md
|
@ -5,6 +5,7 @@
|
|||
|
||||
* [**AMI BIOS Guard Extractor**](#ami-bios-guard-extractor)
|
||||
* [**AMI UCP Update Extractor**](#ami-ucp-update-extractor)
|
||||
* [**Apple EFI IM4P Splitter**](#apple-efi-im4p-splitter)
|
||||
* [**Apple EFI Image Identifier**](#apple-efi-image-identifier)
|
||||
* [**Award BIOS Module Extractor**](#award-bios-module-extractor)
|
||||
* [**Dell PFS/PKG Update Extractor**](#dell-pfspkg-update-extractor)
|
||||
|
@ -143,6 +144,58 @@ Some Anti-Virus software may claim that the built/frozen/compiled executable con
|
|||
|
||||
![]()
|
||||
|
||||
## **Apple EFI IM4P Splitter**
|
||||
|
||||
![]()
|
||||
|
||||
#### **Description**
|
||||
|
||||
Parses Apple IM4P multi-EFI files and splits all detected EFI firmware into separate Intel SPI/BIOS images.
|
||||
|
||||
#### **Usage**
|
||||
|
||||
You can either Drag & Drop or manually enter Apple EFI IM4P file(s). Optional arguments:
|
||||
|
||||
* -h or --help : show help message and exit
|
||||
* -v or --version : show utility name and version
|
||||
* -i or --input-dir : extract from given input directory
|
||||
* -o or --output-dir : extract in given output directory
|
||||
* -e or --auto-exit : skip press enter to exit prompts
|
||||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.10 support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
To run the utility, you do not need any prerequisites.
|
||||
|
||||
#### **Build/Freeze/Compile with PyInstaller**
|
||||
|
||||
PyInstaller can build/freeze/compile the utility at all three supported platforms, it is simple to run and gets updated often.
|
||||
|
||||
1. Make sure Python 3.10.0 or newer is installed:
|
||||
|
||||
> python --version
|
||||
|
||||
2. Use pip to install PyInstaller:
|
||||
|
||||
> pip3 install pyinstaller
|
||||
|
||||
3. Build/Freeze/Compile:
|
||||
|
||||
> pyinstaller --noupx --onefile \<path-to-project\>\/Apple_EFI_Split.py
|
||||
|
||||
At dist folder you should find the final utility executable
|
||||
|
||||
#### **Anti-Virus False Positives**
|
||||
|
||||
Some Anti-Virus software may claim that the built/frozen/compiled executable contains viruses. Any such detections are false positives, usually of PyInstaller. You can switch to a better Anti-Virus software, report the false positive to their support, add the executable to the exclusions, build/freeze/compile yourself or use the Python script directly.
|
||||
|
||||
#### **Pictures**
|
||||
|
||||
![]()
|
||||
|
||||
## **Apple EFI Image Identifier**
|
||||
|
||||
![]()
|
||||
|
|
|
@ -10,6 +10,7 @@ import re
|
|||
PAT_AMI_PFAT = re.compile(br'_AMIPFAT.AMI_BIOS_GUARD_FLASH_CONFIGURATIONS', re.DOTALL)
|
||||
PAT_AMI_UCP = re.compile(br'@(UAF|HPU).{12}@', re.DOTALL)
|
||||
PAT_APPLE_EFI = re.compile(br'\$IBIOSI\$.{16}\x2E\x00.{6}\x2E\x00.{8}\x2E\x00.{6}\x2E\x00.{20}\x00{2}', re.DOTALL)
|
||||
PAT_APPLE_IM4P = re.compile(br'\x16\x04IM4P\x16\x04mefi')
|
||||
PAT_AWARD_LZH = re.compile(br'-lh[04567]-')
|
||||
PAT_DELL_FTR = re.compile(br'\xEE\xAA\xEE\x8F\x49\x1B\xE8\xAE\x14\x37\x90')
|
||||
PAT_DELL_HDR = re.compile(br'\xEE\xAA\x76\x1B\xEC\xBB\x20\xF1\xE6\x51.\x78\x9C', re.DOTALL)
|
||||
|
@ -18,6 +19,7 @@ PAT_FUJITSU_SFX = re.compile(br'FjSfxBinay\xB2\xAC\xBC\xB9\xFF{4}.{4}\xFF{4}.{4}
|
|||
PAT_INSYDE_IFL = re.compile(br'\$_IFLASH')
|
||||
PAT_INSYDE_SFX = re.compile(br'\x0D\x0A;!@InstallEnd@!\x0D\x0A(7z\xBC\xAF\x27|\x6E\xF4\x79\x5F\x4E)')
|
||||
PAT_INTEL_ENG = re.compile(br'\x04\x00{3}[\xA1\xE1]\x00{3}.{8}\x86\x80.{9}\x00\$((MN2)|(MAN))', re.DOTALL)
|
||||
PAT_INTEL_IFD = re.compile(br'\x5A\xA5\xF0\x0F.{172}\xFF{16}', re.DOTALL)
|
||||
PAT_MICROSOFT_CAB = re.compile(br'MSCF\x00{4}')
|
||||
PAT_MICROSOFT_MZ = re.compile(br'MZ')
|
||||
PAT_MICROSOFT_PE = re.compile(br'PE\x00{2}')
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue