From 0e170334c642c829befa1a8292fe9c647de1a892 Mon Sep 17 00:00:00 2001
From: platomav <platomav@users.noreply.github.com>
Date: Sun, 28 Aug 2022 20:02:55 +0300
Subject: [PATCH] Added Apple EFI Package Extractor v2.0_a4

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
---
 Apple_EFI_Identify.py => Apple_EFI_ID.py |   8 +-
 Apple_EFI_Split.py => Apple_EFI_IM4P.py  |  22 ++-
 Apple_EFI_PBZX.py                        | 143 +++++++++++++++++++
 Apple_EFI_PKG.py                         | 172 +++++++++++++++++++++++
 Insyde_IFD_Extract.py                    |   6 +-
 README.md                                | 124 +++++++++++++++-
 common/comp_szip.py                      |  14 +-
 common/path_ops.py                       |  11 ++
 common/patterns.py                       |   2 +
 9 files changed, 482 insertions(+), 20 deletions(-)
 rename Apple_EFI_Identify.py => Apple_EFI_ID.py (97%)
 rename Apple_EFI_Split.py => Apple_EFI_IM4P.py (91%)
 create mode 100644 Apple_EFI_PBZX.py
 create mode 100644 Apple_EFI_PKG.py

diff --git a/Apple_EFI_Identify.py b/Apple_EFI_ID.py
similarity index 97%
rename from Apple_EFI_Identify.py
rename to Apple_EFI_ID.py
index d203d5b..1897560 100644
--- a/Apple_EFI_Identify.py
+++ b/Apple_EFI_ID.py
@@ -2,12 +2,12 @@
 #coding=utf-8
 
 """
-Apple EFI Identify
+Apple EFI ID
 Apple EFI Image Identifier
 Copyright (C) 2018-2022 Plato Mavropoulos
 """
 
-TITLE = 'Apple EFI Image Identifier v2.0_a3'
+TITLE = 'Apple EFI Image Identifier v2.0_a4'
 
 import os
 import sys
@@ -143,7 +143,7 @@ def apple_efi_identify(input_file, output_path, padding=0, rename=False):
     if rename:
         input_parent = path_parent(input_file)
         
-        input_suffix = path_suffixes(input_file)[0]
+        input_suffix = path_suffixes(input_file)[-1]
         
         input_adler32 = zlib.adler32(input_buffer)
         
@@ -156,7 +156,7 @@ def apple_efi_identify(input_file, output_path, padding=0, rename=False):
         if not os.path.isfile(output_file):
             os.replace(input_file, output_file) # Rename input file based on its EFI tag
         
-        printer(f'Renamed input to {output_name}', padding)
+        printer(f'Renamed to {output_name}', padding)
     
     return 0
 
diff --git a/Apple_EFI_Split.py b/Apple_EFI_IM4P.py
similarity index 91%
rename from Apple_EFI_Split.py
rename to Apple_EFI_IM4P.py
index 930d54e..fe2bd3e 100644
--- a/Apple_EFI_Split.py
+++ b/Apple_EFI_IM4P.py
@@ -2,12 +2,12 @@
 #coding=utf-8
 
 """
-Apple EFI Split
+Apple EFI IM4P
 Apple EFI IM4P Splitter
 Copyright (C) 2018-2022 Plato Mavropoulos
 """
 
-TITLE = 'Apple EFI IM4P Splitter v3.0_a2'
+TITLE = 'Apple EFI IM4P Splitter v3.0_a4'
 
 import os
 import sys
@@ -32,6 +32,8 @@ def is_apple_im4p(input_file):
 
 # Parse & Split Apple EFI IM4P image
 def apple_im4p_split(input_file, output_path, padding=0):    
+    exit_codes = []
+    
     input_buffer = file_to_bytes(input_file)
     
     extract_path = os.path.join(f'{output_path}_extracted')
@@ -48,7 +50,7 @@ def apple_im4p_split(input_file, output_path, padding=0):
     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')
+    mefi_data_len = int.from_bytes(input_buffer[im4p_match.end() + 0x5:im4p_match.end() + 0x9], 'big')
     
     # Check if mefi is followed by _MEFIBIN
     mefibin_exist = input_buffer[mefi_data_bgn:mefi_data_bgn + 0x8] == b'_MEFIBIN'
@@ -119,6 +121,8 @@ def apple_im4p_split(input_file, output_path, padding=0):
         
         output_data = input_buffer[ifd_data_bgn:ifd_data_end]
         
+        output_size = len(output_data)
+        
         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')
@@ -127,6 +131,13 @@ def apple_im4p_split(input_file, output_path, padding=0):
             output_image.write(output_data)
         
         printer(f'Split Apple EFI image at {ifd_data_txt}!', padding)
+        
+        if output_size != ifd_comp_all_size:
+            printer(f'Error: Bad image size 0x{output_size:07X}, expected 0x{ifd_comp_all_size:07X}!', padding + 4)
+            
+            exit_codes.append(1)
+    
+    return sum(exit_codes)
 
 # Intel Flash Descriptor Component Sizes (4MB, 8MB, 16MB and 32MB)
 IFD_COMP_LEN = {3: 0x400000, 4: 0x800000, 5: 0x1000000, 6: 0x2000000}
@@ -151,9 +162,8 @@ if __name__ == '__main__':
         
         extract_path = os.path.join(output_path, input_name)
         
-        apple_im4p_split(input_file, extract_path, padding)
-        
-        exit_code -= 1
+        if apple_im4p_split(input_file, extract_path, padding) == 0:
+            exit_code -= 1
     
     printer('Done!', pause=True)
     
diff --git a/Apple_EFI_PBZX.py b/Apple_EFI_PBZX.py
new file mode 100644
index 0000000..05e5eae
--- /dev/null
+++ b/Apple_EFI_PBZX.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python3
+#coding=utf-8
+
+"""
+Apple PBZX Extract
+Apple EFI PBZX Extractor
+Copyright (C) 2021-2022 Plato Mavropoulos
+"""
+
+TITLE = 'Apple EFI PBZX Extractor v1.0_a4'
+
+import os
+import sys
+import lzma
+import ctypes
+
+# Stop __pycache__ generation
+sys.dont_write_bytecode = True
+
+from common.comp_szip import is_szip_supported, szip_decompress
+from common.path_ops import make_dirs, path_stem
+from common.patterns import PAT_APPLE_PBZX
+from common.struct_ops import get_struct, uint32_t
+from common.system import argparse_init, printer, script_init
+from common.text_ops import file_to_bytes
+
+class PbzxChunk(ctypes.BigEndianStructure):
+    _pack_ = 1
+    _fields_ = [
+        ('Reserved0',       uint32_t),      # 0x00
+        ('InitSize',        uint32_t),      # 0x04
+        ('Reserved1',       uint32_t),      # 0x08
+        ('CompSize',        uint32_t),      # 0x0C
+        # 0x10
+    ]
+    
+    def struct_print(self, p):
+        printer(['Reserved 0     :', f'0x{self.Reserved0:X}'], p, False)
+        printer(['Initial Size   :', f'0x{self.InitSize:X}'], p, False)
+        printer(['Reserved 1     :', f'0x{self.Reserved1:X}'], p, False)
+        printer(['Compressed Size:', f'0x{self.CompSize:X}'], p, False)
+
+# Check if input is Apple PBZX image
+def is_apple_pbzx(input_file):
+    input_buffer = file_to_bytes(input_file)
+    
+    return bool(PAT_APPLE_PBZX.search(input_buffer[:0x4]))
+
+# Parse & Extract Apple PBZX image
+def apple_pbzx_extract(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)
+    
+    cpio_bin = b'' # Initialize PBZX > CPIO Buffer
+    cpio_len = 0x0 # Initialize PBZX > CPIO Length
+    
+    chunk_off = 0xC # First PBZX Chunk starts at 0xC
+    while chunk_off < len(input_buffer):
+        chunk_hdr = get_struct(input_buffer, chunk_off, PbzxChunk)
+        
+        printer(f'PBZX Chunk at 0x{chunk_off:08X}\n', padding)
+        
+        chunk_hdr.struct_print(padding + 4)
+        
+        # PBZX Chunk data starts after its Header
+        comp_bgn = chunk_off + PBZX_CHUNK_HDR_LEN
+        
+        # To avoid a potential infinite loop, double-check Compressed Size
+        comp_end = comp_bgn + max(chunk_hdr.CompSize, PBZX_CHUNK_HDR_LEN)
+        
+        comp_bin = input_buffer[comp_bgn:comp_end]
+        
+        try:
+            # Attempt XZ decompression, if applicable to Chunk data
+            cpio_bin += lzma.LZMADecompressor().decompress(comp_bin)
+            
+            printer('Successful LZMA decompression!', padding + 8)
+        except:
+            # Otherwise, Chunk data is not compressed
+            cpio_bin += comp_bin
+        
+        # Final CPIO size should match the sum of all Chunks > Initial Size
+        cpio_len += chunk_hdr.InitSize
+        
+        # Next Chunk starts at the end of current Chunk's data
+        chunk_off = comp_end
+    
+    # Check that CPIO size is valid based on all Chunks > Initial Size
+    if cpio_len != len(cpio_bin):
+        printer('Error: Unexpected CPIO archive size!', padding)
+        
+        return 1
+    
+    cpio_name = path_stem(input_file) if os.path.isfile(input_file) else 'Payload'
+    
+    cpio_path = os.path.join(extract_path, f'{cpio_name}.cpio')
+    
+    with open(cpio_path, 'wb') as cpio_object:
+        cpio_object.write(cpio_bin)
+    
+    # Decompress PBZX > CPIO archive with 7-Zip
+    if is_szip_supported(cpio_path, padding, args=['-tCPIO'], check=True):
+        if szip_decompress(cpio_path, extract_path, 'CPIO', padding, args=['-tCPIO'], check=True) == 0:
+            os.remove(cpio_path) # Successful extraction, delete PBZX > CPIO archive
+        else:
+            return 3
+    else:
+        return 2
+    
+    return 0
+
+# Get common ctypes Structure Sizes
+PBZX_CHUNK_HDR_LEN = ctypes.sizeof(PbzxChunk)
+
+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_pbzx(input_file):
+            printer('Error: This is not an Apple PBZX image!', padding)
+            
+            continue # Next input file
+        
+        extract_path = os.path.join(output_path, input_name)
+        
+        if apple_pbzx_extract(input_file, extract_path, padding) == 0:
+            exit_code -= 1
+    
+    printer('Done!', pause=True)
+    
+    sys.exit(exit_code)
diff --git a/Apple_EFI_PKG.py b/Apple_EFI_PKG.py
new file mode 100644
index 0000000..636f99e
--- /dev/null
+++ b/Apple_EFI_PKG.py
@@ -0,0 +1,172 @@
+#!/usr/bin/env python3
+#coding=utf-8
+
+"""
+Apple EFI PKG
+Apple EFI Package Extractor
+Copyright (C) 2019-2022 Plato Mavropoulos
+"""
+
+TITLE = 'Apple EFI Package Extractor v2.0_a4'
+
+import os
+import sys
+
+# Stop __pycache__ generation
+sys.dont_write_bytecode = True
+
+from common.comp_szip import is_szip_supported, szip_decompress
+from common.path_ops import copy_file, del_dirs, get_path_files, make_dirs, path_name, path_parent
+from common.patterns import PAT_APPLE_PKG
+from common.system import argparse_init, printer, script_init
+from common.text_ops import file_to_bytes
+
+from Apple_EFI_ID import apple_efi_identify, is_apple_efi
+from Apple_EFI_IM4P import apple_im4p_split, is_apple_im4p
+from Apple_EFI_PBZX import apple_pbzx_extract, is_apple_pbzx
+
+# Check if input is Apple EFI PKG package
+def is_apple_pkg(input_file):
+    input_buffer = file_to_bytes(input_file)
+    
+    return bool(PAT_APPLE_PKG.search(input_buffer[:0x4]))
+
+# Split Apple EFI image (if applicable) and Rename
+def efi_split_rename(in_file, out_path, padding=0):
+    exit_codes = []
+    
+    working_dir = f'{in_file}_extracted'
+    
+    if is_apple_im4p(in_file):
+        printer(f'Splitting IM4P via {is_apple_im4p.__module__}...', padding)
+        im4p_exit = apple_im4p_split(in_file, in_file, padding + 4)
+        exit_codes.append(im4p_exit)
+    else:
+        make_dirs(working_dir, delete=True)
+        copy_file(in_file, working_dir, True)
+    
+    for efi_file in get_path_files(working_dir):
+        if is_apple_efi(efi_file):
+            printer(f'Renaming EFI via {is_apple_efi.__module__}...', padding)
+            name_exit = apple_efi_identify(efi_file, efi_file, padding + 4, True)
+            exit_codes.append(name_exit)
+    
+    for named_file in get_path_files(working_dir):
+        copy_file(named_file, out_path, True)
+    
+    del_dirs(working_dir)
+    
+    return sum(exit_codes)
+
+# Parse & Extract Apple EFI PKG packages
+def apple_pkg_extract(input_file, output_path, padding=0):
+    if not os.path.isfile(input_file):
+        printer('Error: Could not find input file path!', padding)
+        return 1
+    
+    extract_path = os.path.join(f'{output_path}_extracted')
+    
+    make_dirs(extract_path, delete=True)
+    
+    xar_path = os.path.join(extract_path, 'xar')
+    
+    # Decompress PKG > XAR archive with 7-Zip
+    if is_szip_supported(input_file, padding, args=['-tXAR'], check=True):
+        if szip_decompress(input_file, xar_path, 'XAR', padding, args=['-tXAR'], check=True) != 0:
+            return 3
+    else:
+        return 2
+    
+    for xar_file in get_path_files(xar_path):
+        if path_name(xar_file) == 'Payload':
+            pbzx_module = is_apple_pbzx.__module__
+            if is_apple_pbzx(xar_file):
+                printer(f'Extracting PBZX via {pbzx_module}...', padding + 4)
+                if apple_pbzx_extract(xar_file, xar_file, padding + 8) == 0:
+                    printer(f'Succesfull PBZX extraction via {pbzx_module}!', padding + 4)
+                    for pbzx_file in get_path_files(f'{xar_file}_extracted'):
+                        if path_name(pbzx_file) == 'UpdateBundle.zip':
+                            if is_szip_supported(pbzx_file, padding + 8, args=['-tZIP'], check=True):
+                                zip_path = f'{pbzx_file}_extracted'
+                                if szip_decompress(pbzx_file, zip_path, 'ZIP', padding + 8, args=['-tZIP'], check=True) == 0:
+                                    for zip_file in get_path_files(zip_path):
+                                        if path_name(path_parent(zip_file)) == 'MacEFI':
+                                            printer(path_name(zip_file), padding + 12)
+                                            if efi_split_rename(zip_file, extract_path, padding + 16) != 0:
+                                                printer(f'Error: Could not split and rename {path_name(zip_file)}!', padding)
+                                                return 10
+                                else:
+                                    return 9
+                            else:
+                                return 8
+                            break # ZIP found, stop
+                    else:
+                        printer('Error: Could not find "UpdateBundle.zip" file!', padding)
+                        return 7
+                else:
+                    printer(f'Error: Failed to extract PBZX file via {pbzx_module}!', padding)
+                    return 6
+            else:
+                printer(f'Error: Failed to detect file as PBZX via {pbzx_module}!', padding)
+                return 5
+            
+            break # Payload found, stop searching
+        
+        if path_name(xar_file) == 'Scripts':
+            if is_szip_supported(xar_file, padding + 4, args=['-tGZIP'], check=True):
+                gzip_path = f'{xar_file}_extracted'
+                if szip_decompress(xar_file, gzip_path, 'GZIP', padding + 4, args=['-tGZIP'], check=True) == 0:
+                    for gzip_file in get_path_files(gzip_path):
+                        if is_szip_supported(gzip_file, padding + 8, args=['-tCPIO'], check=True):
+                            cpio_path = f'{gzip_file}_extracted'
+                            if szip_decompress(gzip_file, cpio_path, 'CPIO', padding + 8, args=['-tCPIO'], check=True) == 0:
+                                for cpio_file in get_path_files(cpio_path):
+                                    if path_name(path_parent(cpio_file)) == 'EFIPayloads':
+                                        printer(path_name(cpio_file), padding + 12)
+                                        if efi_split_rename(cpio_file, extract_path, padding + 16) != 0:
+                                            printer(f'Error: Could not split and rename {path_name(cpio_file)}!', padding)
+                                            return 15
+                            else:
+                                return 14
+                        else:
+                            return 13
+                else:
+                    return 12
+            else:
+                return 11
+            
+            break # Scripts found, stop searching
+    else:
+        printer('Error: Could not find "Payload" or "Scripts" file!', padding)
+        return 4
+    
+    del_dirs(xar_path) # Delete temporary/working XAR folder
+    
+    return 0
+
+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_pkg(input_file):
+            printer('Error: This is not an Apple EFI PKG package!', padding)
+            
+            continue # Next input file
+        
+        extract_path = os.path.join(output_path, input_name)
+        
+        if apple_pkg_extract(input_file, extract_path, padding) == 0:
+            exit_code -= 1
+    
+    printer('Done!', pause=True)
+    
+    sys.exit(exit_code)
diff --git a/Insyde_IFD_Extract.py b/Insyde_IFD_Extract.py
index ba9c28e..c68d4d4 100644
--- a/Insyde_IFD_Extract.py
+++ b/Insyde_IFD_Extract.py
@@ -7,7 +7,7 @@ Insyde iFlash/iFdPacker Extractor
 Copyright (C) 2022 Plato Mavropoulos
 """
 
-TITLE = 'Insyde iFlash/iFdPacker Extractor v2.0_a9'
+TITLE = 'Insyde iFlash/iFdPacker Extractor v2.0_a10'
 
 import os
 import sys
@@ -175,9 +175,9 @@ def insyde_packer_extract(input_buffer, extract_path, padding=0):
     with open(sfx_path, 'wb') as sfx_file:
         sfx_file.write(sfx_buffer)
     
-    if is_szip_supported(sfx_path, padding + 8, password=INS_SFX_PWD, check=True):
+    if is_szip_supported(sfx_path, padding + 8, args=[f'-p{INS_SFX_PWD}'], check=True):
         if szip_decompress(sfx_path, extract_path, 'Insyde iFdPacker > 7-Zip SFX',
-        padding + 8, password=INS_SFX_PWD, check=True) == 0:
+        padding + 8, args=[f'-p{INS_SFX_PWD}'], check=True) == 0:
             os.remove(sfx_path)
         else:
             return 125
diff --git a/README.md b/README.md
index 897f3ad..0df5ebb 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,8 @@
 * [**AMI UCP Update Extractor**](#ami-ucp-update-extractor)
 * [**Apple EFI IM4P Splitter**](#apple-efi-im4p-splitter)
 * [**Apple EFI Image Identifier**](#apple-efi-image-identifier)
+* [**Apple EFI Package Extractor**](#apple-efi-package-extractor)
+* [**Apple EFI PBZX Extractor**](#apple-efi-pbzx-extractor)
 * [**Award BIOS Module Extractor**](#award-bios-module-extractor)
 * [**Dell PFS/PKG Update Extractor**](#dell-pfspkg-update-extractor)
 * [**Fujitsu SFX BIOS Extractor**](#fujitsu-sfx-bios-extractor)
@@ -150,7 +152,7 @@ Some Anti-Virus software may claim that the built/frozen/compiled executable con
 
 #### **Description**
 
-Parses Apple IM4P multi-EFI files and splits all detected EFI firmware into separate Intel SPI/BIOS images.
+Parses Apple IM4P multi-EFI files and splits all detected EFI firmware into separate Intel SPI/BIOS images. The output comprises only final firmware components and utilities which are directly usable by end users.
 
 #### **Usage**
 
@@ -184,7 +186,7 @@ PyInstaller can build/freeze/compile the utility at all three supported platform
 
 3. Build/Freeze/Compile:
 
-> pyinstaller --noupx --onefile \<path-to-project\>\/Apple_EFI_Split.py
+> pyinstaller --noupx --onefile \<path-to-project\>\/Apple_EFI_IM4P.py
 
 At dist folder you should find the final utility executable
 
@@ -245,7 +247,123 @@ PyInstaller can build/freeze/compile the utility at all three supported platform
 
 4. Build/Freeze/Compile:
 
-> pyinstaller --add-data="external/*;external/" --noupx --onefile \<path-to-project\>\/Apple_EFI_Identify.py
+> pyinstaller --add-data="external/*;external/" --noupx --onefile \<path-to-project\>\/Apple_EFI_ID.py
+
+You should find the final utility executable at "dist" folder
+
+#### **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 Package Extractor**
+
+![]()
+
+#### **Description**
+
+Parses Apple EFI PKG firmware packages (i.e. FirmwareUpdate.pkg, BridgeOSUpdateCustomer.pkg), extracts their EFI images, splits those in IM4P format and identifies/renames the final Intel SPI/BIOS images accordingly. The output comprises only final firmware components which are directly usable by end users.
+
+#### **Usage**
+
+You can either Drag & Drop or manually enter Apple EFI PKG package 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 must have the following 3rd party tools at the "external" project directory:
+
+* [7-Zip Console](https://www.7-zip.org/) (i.e. 7z.exe for Windows or 7zzs for Linux)
+
+#### **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. Place prerequisites at the "external" project directory:
+
+> 7-Zip Console
+
+4. Build/Freeze/Compile:
+
+> pyinstaller --add-data="external/*;external/" --noupx --onefile \<path-to-project\>\/Apple_EFI_PKG.py
+
+You should find the final utility executable at "dist" folder
+
+#### **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 PBZX Extractor**
+
+![]()
+
+#### **Description**
+
+Parses Apple EFI PBZX images, re-assembles their CPIO payload and extracts its firmware components (e.g. IM4P, EFI, Utilities, Scripts etc). It supports CPIO re-assembly from both Raw and XZ compressed PBZX Chunks. The output comprises only final firmware components and utilities which are directly usable by end users.
+
+#### **Usage**
+
+You can either Drag & Drop or manually enter Apple EFI PBZX image 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 must have the following 3rd party tools at the "external" project directory:
+
+* [7-Zip Console](https://www.7-zip.org/) (i.e. 7z.exe for Windows or 7zzs for Linux)
+
+#### **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. Place prerequisites at the "external" project directory:
+
+> 7-Zip Console
+
+4. Build/Freeze/Compile:
+
+> pyinstaller --add-data="external/*;external/" --noupx --onefile \<path-to-project\>\/Apple_EFI_PBZX.py
 
 You should find the final utility executable at "dist" folder
 
diff --git a/common/comp_szip.py b/common/comp_szip.py
index 4a3d26b..3db03a6 100644
--- a/common/comp_szip.py
+++ b/common/comp_szip.py
@@ -23,9 +23,12 @@ def check_bad_exit_code(exit_code):
         raise Exception(f'BAD_EXIT_CODE_{exit_code}')
 
 # Check if file is 7-Zip supported
-def is_szip_supported(in_path, padding=0, password='', check=False, silent=False):
+def is_szip_supported(in_path, padding=0, args=None, check=False, silent=False):
     try:
-        szip_c = [get_szip_path(), 't', in_path, f'-p{password}', '-bso0', '-bse0', '-bsp0']
+        if args is None:
+            args = []
+        
+        szip_c = [get_szip_path(), 't', in_path, *args, '-bso0', '-bse0', '-bsp0']
         
         szip_t = subprocess.run(szip_c, check=False)
         
@@ -40,12 +43,15 @@ def is_szip_supported(in_path, padding=0, password='', check=False, silent=False
     return True
 
 # Archive decompression via 7-Zip
-def szip_decompress(in_path, out_path, in_name, padding=0, password='', check=False, silent=False):
+def szip_decompress(in_path, out_path, in_name, padding=0, args=None, check=False, silent=False):
     if not in_name:
         in_name = 'archive'
     
     try:
-        szip_c = [get_szip_path(), 'x', f'-p{password}', '-aou', '-bso0', '-bse0', '-bsp0', f'-o{out_path}', in_path]
+        if args is None:
+            args = []
+        
+        szip_c = [get_szip_path(), 'x', *args, '-aou', '-bso0', '-bse0', '-bsp0', f'-o{out_path}', in_path]
         
         szip_x = subprocess.run(szip_c, check=False)
         
diff --git a/common/path_ops.py b/common/path_ops.py
index 8e093fe..e2420e6 100644
--- a/common/path_ops.py
+++ b/common/path_ops.py
@@ -71,6 +71,10 @@ def agnostic_path(in_path):
 def path_parent(in_path):
     return Path(in_path).parent.absolute()
 
+# Get final path component, with suffix
+def path_name(in_path):
+    return PurePath(in_path).name
+
 # Get final path component, w/o suffix
 def path_stem(in_path):
     return PurePath(in_path).stem
@@ -95,6 +99,13 @@ def del_dirs(in_path):
     if Path(in_path).is_dir():
         shutil.rmtree(in_path, onerror=clear_readonly)
 
+# Copy file to path with or w/o metadata
+def copy_file(in_path, out_path, meta=False):
+    if meta:
+        shutil.copy2(in_path, out_path)
+    else:
+        shutil.copy(in_path, out_path)
+
 # Clear read-only file attribute (on shutil.rmtree error)
 def clear_readonly(in_func, in_path, _):
     os.chmod(in_path, stat.S_IWRITE)
diff --git a/common/patterns.py b/common/patterns.py
index 27405e8..ecdde39 100644
--- a/common/patterns.py
+++ b/common/patterns.py
@@ -11,6 +11,8 @@ PAT_AMI_PFAT = re.compile(br'_AMIPFAT.AMI_BIOS_GUARD_FLASH_CONFIGURATIONS', re.D
 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_APPLE_PBZX = re.compile(br'pbzx')
+PAT_APPLE_PKG = re.compile(br'xar!')
 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)