mirror of
https://github.com/platomav/BIOSUtilities.git
synced 2025-05-13 14:44:46 -04:00
Portwell EFI BIOS Extractor v2.0_a4
Replaced any assertions
This commit is contained in:
parent
1480d663be
commit
8d262318dd
5 changed files with 229 additions and 6 deletions
|
@ -159,7 +159,7 @@ def chk16_validate(data, tag, padd=0):
|
|||
else:
|
||||
printer('Checksum of UCP Module %s is valid!' % tag, padd)
|
||||
|
||||
# Check if input path or buffer is AMI UCP image
|
||||
# Check if input is AMI UCP image
|
||||
def is_ami_ucp(in_file):
|
||||
buffer = file_to_bytes(in_file)
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ Dell PFS Update Extractor
|
|||
Copyright (C) 2018-2022 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'Dell PFS Update Extractor v6.0_a8'
|
||||
TITLE = 'Dell PFS Update Extractor v6.0_a9'
|
||||
|
||||
import os
|
||||
import io
|
||||
|
@ -206,7 +206,7 @@ def is_pfs_hdr(in_buffer):
|
|||
def is_pfs_ftr(in_buffer):
|
||||
return PAT_DELL_FTR.search(in_buffer)
|
||||
|
||||
# Check if input path or buffer is Dell PFS/PKG image
|
||||
# Check if input is Dell PFS/PKG image
|
||||
def is_dell_pfs(in_file):
|
||||
in_buffer = file_to_bytes(in_file)
|
||||
|
||||
|
@ -304,7 +304,7 @@ def pfs_section_parse(zlib_data, zlib_start, output_path, pfs_name, pfs_index, p
|
|||
|
||||
# Decompress PFS ZLIB section payload
|
||||
try:
|
||||
assert not is_zlib_error # ZLIB errors are critical
|
||||
if is_zlib_error: raise Exception('ZLIB_ERROR') # ZLIB errors are critical
|
||||
section_data = zlib.decompress(compressed_data) # ZLIB decompression
|
||||
except:
|
||||
section_data = zlib_data # Fallback to raw ZLIB data upon critical error
|
||||
|
|
153
Portwell_EFI_Extract.py
Normal file
153
Portwell_EFI_Extract.py
Normal file
|
@ -0,0 +1,153 @@
|
|||
#!/usr/bin/env python3
|
||||
#coding=utf-8
|
||||
|
||||
"""
|
||||
Portwell EFI Extract
|
||||
Portwell EFI BIOS Extractor
|
||||
Copyright (C) 2021-2022 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
TITLE = 'Portwell EFI BIOS Extractor v2.0_a4'
|
||||
|
||||
import os
|
||||
import sys
|
||||
import pefile
|
||||
|
||||
# Stop __pycache__ generation
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
from common.efi_comp import efi_decompress, is_efi_compressed
|
||||
from common.path_ops import safe_name, make_dirs
|
||||
from common.system import script_init, argparse_init, printer
|
||||
from common.text_ops import file_to_bytes
|
||||
|
||||
PEFI_MAGIC = br'MZ'
|
||||
|
||||
FILE_MAGIC = br'<UU>'
|
||||
|
||||
FILE_NAMES = {
|
||||
0 : 'Flash.efi',
|
||||
1 : 'Fparts.txt',
|
||||
2 : 'Update.nsh',
|
||||
3 : 'Temp.bin',
|
||||
4 : 'SaveDmiData.efi'
|
||||
}
|
||||
|
||||
# Check if input is Portwell EFI executable
|
||||
def is_portwell_efi(in_file):
|
||||
in_buffer = file_to_bytes(in_file)
|
||||
|
||||
try: pe_buffer = get_portwell_pe(in_buffer)[1]
|
||||
except: pe_buffer = b''
|
||||
|
||||
is_mz = in_buffer.startswith(PEFI_MAGIC) # EFI images start with PE Header MZ
|
||||
is_uu = pe_buffer.startswith(FILE_MAGIC) # Portwell EFI files start with <UU>
|
||||
|
||||
return is_mz and is_uu
|
||||
|
||||
# Get PE of Portwell EFI executable
|
||||
def get_portwell_pe(in_buffer):
|
||||
pe_file = pefile.PE(data=in_buffer, fast_load=True) # Analyze EFI Portable Executable (PE)
|
||||
|
||||
pe_data = in_buffer[pe_file.OPTIONAL_HEADER.SizeOfImage:] # Skip EFI executable (pylint: disable=E1101)
|
||||
|
||||
return pe_file, pe_data
|
||||
|
||||
# Parse & Extract Portwell UEFI Unpacker
|
||||
def portwell_efi_extract(input_buffer, out_path, padding=0):
|
||||
extract_path = os.path.join(out_path + '_extracted')
|
||||
|
||||
make_dirs(extract_path, delete=True)
|
||||
|
||||
pe_file,pe_data = get_portwell_pe(input_buffer)
|
||||
|
||||
efi_title = get_unpacker_tag(input_buffer, pe_file)
|
||||
|
||||
printer(efi_title, padding)
|
||||
|
||||
efi_files = pe_data.split(FILE_MAGIC) # Split EFI Payload into <UU> file chunks
|
||||
|
||||
parse_efi_files(extract_path, efi_files[1:], padding)
|
||||
|
||||
# Get Portwell UEFI Unpacker tag
|
||||
def get_unpacker_tag(input_buffer, pe_file):
|
||||
unpacker_tag_txt = 'UEFI Unpacker'
|
||||
|
||||
for pe_section in pe_file.sections:
|
||||
# Unpacker Tag, Version, Strings etc are found in .data PE section
|
||||
if pe_section.Name.startswith(b'.data'):
|
||||
pe_data_bgn = pe_section.PointerToRawData
|
||||
pe_data_end = pe_data_bgn + pe_section.SizeOfRawData
|
||||
|
||||
# Decode any valid UTF-16 .data PE section info to a parsable text buffer
|
||||
pe_data_txt = input_buffer[pe_data_bgn:pe_data_end].decode('utf-16','ignore')
|
||||
|
||||
# Search .data for UEFI Unpacker tag
|
||||
unpacker_tag_bgn = pe_data_txt.find(unpacker_tag_txt)
|
||||
if unpacker_tag_bgn != -1:
|
||||
unpacker_tag_len = pe_data_txt[unpacker_tag_bgn:].find('=')
|
||||
if unpacker_tag_len != -1:
|
||||
unpacker_tag_end = unpacker_tag_bgn + unpacker_tag_len
|
||||
unpacker_tag_raw = pe_data_txt[unpacker_tag_bgn:unpacker_tag_end]
|
||||
|
||||
# Found full UEFI Unpacker tag, store and slightly beautify the resulting text
|
||||
unpacker_tag_txt = unpacker_tag_raw.strip().replace(' ',' ').replace('<',' <')
|
||||
|
||||
break # Found PE .data section, skip the rest
|
||||
|
||||
return unpacker_tag_txt
|
||||
|
||||
# Process Portwell UEFI Unpacker payload files
|
||||
def parse_efi_files(extract_path, efi_files, padding):
|
||||
for file_index,file_data in enumerate(efi_files):
|
||||
if file_data in (b'', b'NULL'): continue # Skip empty/unused files
|
||||
|
||||
file_name = FILE_NAMES.get(file_index, f'Unknown_{file_index}.bin') # Assign Name to EFI file
|
||||
|
||||
printer(file_name, padding + 4) # Print EFI file name, indicate progress
|
||||
|
||||
if file_name.startswith('Unknown_'):
|
||||
printer(f'Note: Detected new Portwell EFI file ID {file_index}!', padding + 8, pause=True) # Report new EFI files
|
||||
|
||||
file_path = os.path.join(extract_path, safe_name(file_name)) # Store EFI file output path
|
||||
|
||||
with open(file_path, 'wb') as out_file: out_file.write(file_data) # Store EFI file data to drive
|
||||
|
||||
# Attempt to detect EFI compression & decompress when applicable
|
||||
if is_efi_compressed(file_data):
|
||||
comp_fname = file_path + '.temp' # Store temporary compressed file name
|
||||
|
||||
os.replace(file_path, comp_fname) # Rename initial/compressed file
|
||||
|
||||
if efi_decompress(comp_fname, file_path, padding + 8) == 0:
|
||||
os.remove(comp_fname) # Successful decompression, delete compressed file
|
||||
|
||||
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)
|
||||
|
||||
with open(input_file, 'rb') as in_file: input_buffer = in_file.read()
|
||||
|
||||
if not is_portwell_efi(input_buffer):
|
||||
printer('Error: This is not a Portwell EFI Update Package!', padding)
|
||||
|
||||
continue # Next input file
|
||||
|
||||
extract_path = os.path.join(output_path, input_name)
|
||||
|
||||
portwell_efi_extract(input_buffer, extract_path, padding)
|
||||
|
||||
exit_code -= 1
|
||||
|
||||
printer('Done!', pause=True)
|
||||
|
||||
sys.exit(exit_code)
|
72
README.md
72
README.md
|
@ -8,6 +8,7 @@
|
|||
* [**Dell PFS Update Extractor**](#dell-pfs-update-extractor)
|
||||
* [**AMI UCP BIOS Extractor**](#ami-ucp-bios-extractor)
|
||||
* [**AMI BIOS Guard Extractor**](#ami-bios-guard-extractor)
|
||||
* [**Portwell EFI BIOS Extractor**](#portwell-efi-bios-extractor)
|
||||
|
||||
## **Dell PFS Update Extractor**
|
||||
|
||||
|
@ -98,7 +99,7 @@ Should work at all Windows, Linux or macOS operating systems which have Python 3
|
|||
|
||||
To run the utility, you must have the following 3rd party tools at the "external" project directory:
|
||||
|
||||
* [TianoCompress](https://github.com/tianocore/edk2/tree/master/BaseTools/Source/C/TianoCompress/) (e.g. [TianoCompress.exe for Windows](https://github.com/tianocore/edk2-BaseTools-win32/) or TianoCompress)
|
||||
* [TianoCompress](https://github.com/tianocore/edk2/tree/master/BaseTools/Source/C/TianoCompress/) (e.g. [TianoCompress.exe for Windows](https://github.com/tianocore/edk2-BaseTools-win32/) or TianoCompress for Linux)
|
||||
* [7-Zip Console](https://www.7-zip.org/) (i.e. 7z.exe for Windows or 7zz|7zzs for Linux)
|
||||
|
||||
Optionally, to decompile the AMI UCP \> AMI PFAT \> Intel BIOS Guard Scripts (when applicable), you must have the following 3rd party utility at the "external" project directory:
|
||||
|
@ -198,5 +199,74 @@ Some Anti-Virus software may claim that the built/frozen/compiled executable con
|
|||
|
||||
![]()
|
||||
|
||||
## **Portwell EFI BIOS Extractor**
|
||||
|
||||
![]()
|
||||
|
||||
#### **Description**
|
||||
|
||||
Parses Portwell UEFI Unpacker EFI executables (usually named "Update.efi") and extracts their firmware (e.g. SPI, BIOS/UEFI, EC etc) and utilities (e.g. Flasher etc) components. It supports all known Portwell UEFI Unpacker revisions (v1.1, v1.2, v2.0) and formats (used, empty, null), including those which contain EFI compressed files. 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 Portwell UEFI Unpacker EFI executable 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
|
||||
* --static : use static-built external dependencies
|
||||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.8 support.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
To run the utility, you must have the following 3rd party Python module installed:
|
||||
|
||||
* [pefile](https://pypi.org/project/pefile/)
|
||||
|
||||
> pip3 install pefile
|
||||
|
||||
Moreover, you must have the following 3rd party tool at the "external" project directory:
|
||||
|
||||
* [TianoCompress](https://github.com/tianocore/edk2/tree/master/BaseTools/Source/C/TianoCompress/) (e.g. [TianoCompress.exe for Windows](https://github.com/tianocore/edk2-BaseTools-win32/) or TianoCompress 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.8.0 or newer is installed:
|
||||
|
||||
> python --version
|
||||
|
||||
2. Use pip to install PyInstaller:
|
||||
|
||||
> pip3 install pyinstaller
|
||||
|
||||
3. Use pip to install pefile:
|
||||
|
||||
> pip3 install pefile
|
||||
|
||||
4. Place prerequisite at the "external" project directory:
|
||||
|
||||
> TianoCompress
|
||||
|
||||
5. Build/Freeze/Compile:
|
||||
|
||||
> pyinstaller --add-data="external/*;external/" --noupx --onefile \<path-to-project\>\/Portwell_EFI_Extract.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**
|
||||
|
||||
![]()
|
||||
|
||||
###### _Donate Button Card Image: [Credit and Loan Pack](https://flaticon.com/free-icon/credit-card_3898076) by **Freepik** under Flaticon license_
|
||||
###### _Donate Button Paypal Image: [Credit Cards Pack](https://flaticon.com/free-icon/paypal_349278) by **Freepik** under Flaticon license_
|
|
@ -47,6 +47,6 @@ def efi_decompress(in_path, out_path, padding=0, comp_type='--uefi'):
|
|||
|
||||
return 1
|
||||
|
||||
printer('Succesfull EFI/Tiano decompression via TianoCompress!', padding)
|
||||
printer('Succesfull EFI decompression via TianoCompress!', padding)
|
||||
|
||||
return 0
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue