mirror of
https://github.com/platomav/BIOSUtilities.git
synced 2025-05-09 13:52:00 -04:00
Phoenix SCT BIOS Extractor v1.0
Parses Phoenix SecureCore Technology (SCT) BIOS images and extracts their SPI/BIOS/UEFI firmware components. It supports all Phoenix SCT revisions and formats, including those which are originally LZMA compressed. The output comprises only final firmware components which are directly usable by end users.
This commit is contained in:
parent
b6117807ba
commit
49bd11de92
2 changed files with 269 additions and 0 deletions
213
Phoenix SCT BIOS Extractor/Phoenix_SCT_Extract.py
Normal file
213
Phoenix SCT BIOS Extractor/Phoenix_SCT_Extract.py
Normal file
|
@ -0,0 +1,213 @@
|
|||
#!/usr/bin/env python3
|
||||
#coding=utf-8
|
||||
|
||||
"""
|
||||
Phoenix SCT Extract
|
||||
Phoenix SCT BIOS Extractor
|
||||
Copyright (C) 2021 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
title = 'Phoenix SCT BIOS Extractor v1.0'
|
||||
|
||||
print('\n' + title) # Print script title
|
||||
|
||||
import sys
|
||||
|
||||
# Detect Python version
|
||||
sys_ver = sys.version_info
|
||||
if sys_ver < (3,7) :
|
||||
sys.stdout.write('\n\nError: Python >= 3.7 required, not %d.%d!\n' % (sys_ver[0], sys_ver[1]))
|
||||
(raw_input if sys_ver[0] <= 2 else input)('\nPress enter to exit') # pylint: disable=E0602
|
||||
sys.exit(1)
|
||||
|
||||
import os
|
||||
import re
|
||||
import lzma
|
||||
import shutil
|
||||
import ctypes
|
||||
import argparse
|
||||
import traceback
|
||||
|
||||
# Pause after any unexpected Python exception
|
||||
# https://stackoverflow.com/a/781074 by Torsten Marek
|
||||
def show_exception_and_exit(exc_type, exc_value, tb) :
|
||||
if exc_type is KeyboardInterrupt :
|
||||
print('\n')
|
||||
else :
|
||||
print('\nError: %s crashed, please report the following:\n' % title)
|
||||
traceback.print_exception(exc_type, exc_value, tb)
|
||||
input('\nPress enter to exit')
|
||||
|
||||
sys.exit(1)
|
||||
|
||||
# Set pause-able Python exception handler
|
||||
sys.excepthook = show_exception_and_exit
|
||||
|
||||
# Set console/shell window title
|
||||
user_os = sys.platform
|
||||
if user_os == 'win32' : ctypes.windll.kernel32.SetConsoleTitleW(title)
|
||||
elif user_os.startswith('linux') or user_os == 'darwin' or user_os.find('bsd') != -1 : sys.stdout.write('\x1b]2;' + title + '\x07')
|
||||
|
||||
# Set argparse Arguments
|
||||
sct_parser = argparse.ArgumentParser()
|
||||
sct_parser.add_argument('executables', type=argparse.FileType('r'), nargs='*')
|
||||
sct_parser.add_argument('-p', '--path', help='parse files within given folder', type=str)
|
||||
sct_params = sct_parser.parse_args()
|
||||
|
||||
# Get all files within path
|
||||
def get_files(path) :
|
||||
inputs = []
|
||||
|
||||
for root, _, files in os.walk(path):
|
||||
for name in files :
|
||||
inputs.append(os.path.join(root, name))
|
||||
|
||||
return inputs
|
||||
|
||||
if len(sys.argv) >= 2 :
|
||||
if bool(sct_params.path) :
|
||||
sct_exec = get_files(sct_params.path) # CLI with --path
|
||||
else :
|
||||
sct_exec = []
|
||||
for executable in sct_params.executables :
|
||||
sct_exec.append(executable.name) # Drag & Drop
|
||||
else :
|
||||
in_path = input('\nEnter the full folder path: ')
|
||||
sct_exec = get_files(in_path) # Direct Run
|
||||
|
||||
# Set ctypes Structure types
|
||||
char = ctypes.c_char
|
||||
uint8_t = ctypes.c_ubyte
|
||||
uint16_t = ctypes.c_ushort
|
||||
uint32_t = ctypes.c_uint
|
||||
uint64_t = ctypes.c_uint64
|
||||
|
||||
class SCT_HDR(ctypes.LittleEndianStructure) :
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Tag', char*8), # 0x00
|
||||
('Size', uint32_t), # 0x08
|
||||
('Count', uint32_t), # 0x0C
|
||||
# 0x10
|
||||
]
|
||||
|
||||
def sct_print(self) :
|
||||
print('\n Phoenix SCT Header:\n')
|
||||
print(' Tag : %s' % self.Tag.decode('utf-8','replace').strip())
|
||||
print(' Size : 0x%X' % self.Size)
|
||||
print(' Count : %d' % self.Count)
|
||||
|
||||
class SCT_MOD(ctypes.LittleEndianStructure) :
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Name', char*256), # 0x000
|
||||
('Offset', uint32_t), # 0x100
|
||||
('Size', uint32_t), # 0x104
|
||||
('Compressed', uint32_t), # 0x108
|
||||
('Reserved', uint32_t), # 0x10C
|
||||
# 0x110
|
||||
]
|
||||
|
||||
def sct_print(self) :
|
||||
print('\n Phoenix SCT Entry:\n')
|
||||
print(' Name : %s' % self.Name.decode('utf-8','replace').strip())
|
||||
print(' Offset : 0x%X' % self.Offset)
|
||||
print(' Size : 0x%X' % self.Size)
|
||||
print(' Compressed : %s' % ['No','Yes'][self.Compressed])
|
||||
print(' Reserved : 0x%X' % self.Reserved)
|
||||
|
||||
# Process ctypes Structure Classes
|
||||
# https://github.com/skochinsky/me-tools/blob/master/me_unpack.py by Igor Skochinsky
|
||||
def get_struct(buffer, start_offset, class_name, param_list = None) :
|
||||
if param_list is None : param_list = []
|
||||
|
||||
structure = class_name(*param_list) # Unpack parameter list
|
||||
struct_len = ctypes.sizeof(structure)
|
||||
struct_data = buffer[start_offset:start_offset + struct_len]
|
||||
fit_len = min(len(struct_data), struct_len)
|
||||
|
||||
if (start_offset >= len(buffer)) or (fit_len < struct_len) :
|
||||
print('\n Error: Offset 0x%X out of bounds at %s, possibly incomplete image!' % (start_offset, class_name.__name__))
|
||||
|
||||
input('\n Press enter to exit')
|
||||
|
||||
sys.exit(1)
|
||||
|
||||
ctypes.memmove(ctypes.addressof(structure), struct_data, fit_len)
|
||||
|
||||
return structure
|
||||
|
||||
# Phoenix SCT BIOS Package Pattern ($PACK + Size + Count)
|
||||
sct_pat = re.compile(br'\x24\x50\x41\x43\x4B\x00{3}..\x00{2}.\x00{3}', re.DOTALL)
|
||||
|
||||
# Get common ctypes Structure Sizes
|
||||
sct_hdr_len = ctypes.sizeof(SCT_HDR)
|
||||
sct_mod_len = ctypes.sizeof(SCT_MOD)
|
||||
|
||||
# Size of dummy/placeholder SCT Entries
|
||||
sct_dummy_len = 0x200 # Top 2, Names only
|
||||
|
||||
# Process each input Phoenix SCT BIOS executable
|
||||
for input_file in sct_exec :
|
||||
input_name,input_extension = os.path.splitext(os.path.basename(input_file))
|
||||
input_dir = os.path.dirname(os.path.abspath(input_file))
|
||||
|
||||
print('\n*** %s%s' % (input_name, input_extension))
|
||||
|
||||
# Check if input file exists
|
||||
if not os.path.isfile(input_file) :
|
||||
print('\n Error: This input file does not exist!')
|
||||
continue # Next input file
|
||||
|
||||
with open(input_file, 'rb') as in_file : input_data = in_file.read()
|
||||
|
||||
sct_match = sct_pat.search(input_data) # Search for Phoenix SCT BIOS Pattern
|
||||
|
||||
# Check if Phoenix SCT BIOS Pattern was found on executable
|
||||
if not sct_match :
|
||||
print('\n Error: This is not a Phoenix SCT BIOS executable!')
|
||||
continue # Next input file
|
||||
|
||||
output_path = os.path.join(input_dir, '%s%s' % (input_name, input_extension) + '_extracted') # Set extraction directory
|
||||
|
||||
if os.path.isdir(output_path) : shutil.rmtree(output_path) # Delete any existing extraction directory
|
||||
|
||||
os.mkdir(output_path) # Create extraction directory
|
||||
|
||||
print('\n Phoenix SecureCore Technology')
|
||||
|
||||
sct_hdr = get_struct(input_data, sct_match.start(), SCT_HDR) # Parse SCT Header Structure
|
||||
sct_hdr.sct_print() # Print SCT Header Info
|
||||
|
||||
# Check if reported SCT Header Size matches manual SCT Entry Count calculation
|
||||
if sct_hdr.Size != sct_hdr_len + sct_dummy_len + sct_hdr.Count * sct_mod_len :
|
||||
input('\n Error: This Phoenix SCT BIOS image is corrupted!')
|
||||
continue # Next input file
|
||||
|
||||
# Store all SCT $PACK Data w/o initial dummy/placeholder Entries
|
||||
pack_data = input_data[sct_match.end() + sct_dummy_len:sct_match.start() + sct_hdr.Size]
|
||||
|
||||
# Parse each SCT Entry
|
||||
for e_idx in range(sct_hdr.Count) :
|
||||
mod_hdr = get_struct(pack_data, e_idx * sct_mod_len, SCT_MOD) # Parse SCT Entry Structure
|
||||
mod_hdr.sct_print() # Print SCT Entry Info
|
||||
|
||||
mod_data = input_data[mod_hdr.Offset:mod_hdr.Offset + mod_hdr.Size] # Store SCT Entry Raw Data
|
||||
|
||||
# Check if SCT Entry Raw Data is complete
|
||||
if len(mod_data) != mod_hdr.Size :
|
||||
input('\n Error: This Phoenix SCT BIOS image is incomplete!')
|
||||
|
||||
# Store SCT Entry LZMA Decompressed Data, when applicable
|
||||
if mod_hdr.Compressed : mod_data = lzma.LZMADecompressor().decompress(mod_data)
|
||||
|
||||
# Replace common Windows reserved/illegal filename characters
|
||||
mod_fname = re.sub(r'[\\/*?:"<>|]', '_', mod_hdr.Name.decode('utf-8','replace').strip())
|
||||
|
||||
with open(os.path.join(output_path, mod_fname), 'wb') as out : out.write(mod_data) # Store SCT Entry Data/File
|
||||
|
||||
print('\n Extracted Phoenix SCT BIOS executable!')
|
||||
|
||||
input('\nDone!')
|
||||
|
||||
sys.exit(0)
|
56
README.md
56
README.md
|
@ -175,6 +175,62 @@ Some Anti-Virus software may claim that the built/frozen/compiled executable con
|
|||
|
||||

|
||||
|
||||
## **Phoenix SCT BIOS Extractor**
|
||||
|
||||

|
||||
|
||||
#### **Description**
|
||||
|
||||
Parses Phoenix SecureCore Technology (SCT) BIOS images and extracts their SPI/BIOS/UEFI firmware components. It supports all Phoenix SCT revisions and formats, including those which are originally LZMA compressed. The output comprises only final firmware components which are directly usable by end users.
|
||||
|
||||

|
||||
<sub><sup>*Icon owned by Phoenix*</sup></sub>
|
||||
|
||||
#### **Usage**
|
||||
|
||||
You can either Drag & Drop or manually enter the full path of a folder containing Phoenix SCT BIOS images. Optional arguments:
|
||||
|
||||
* -h or --help : show help message and exit
|
||||
* -p or --path : parse files within given folder
|
||||
|
||||
#### **Download**
|
||||
|
||||
An already built/frozen/compiled binary is provided by me for Windows only. Thus, **you don't need to manually build/freeze/compile it under Windows**. Instead, download the latest version from the [Releases](https://github.com/platomav/BIOSUtilities/releases) tab. To extract the already built/frozen/compiled archive, you need to use programs which support RAR5 compression. Note that you need to manually apply any prerequisites.
|
||||
|
||||
#### **Compatibility**
|
||||
|
||||
Should work at all Windows, Linux or macOS operating systems which have Python 3.7 support. Windows users who plan to use the already built/frozen/compiled binary must make sure that they have the latest Windows Updates installed which include all required "Universal C Runtime (CRT)" libraries.
|
||||
|
||||
#### **Prerequisites**
|
||||
|
||||
To run the utility, you do not need any 3rd party tool.
|
||||
|
||||
#### **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.7.0 or newer is installed:
|
||||
|
||||
> python --version
|
||||
|
||||
2. Use pip to install PyInstaller:
|
||||
|
||||
> pip3 install pyinstaller
|
||||
|
||||
3. Build/Freeze/Compile:
|
||||
|
||||
> pyinstaller --noupx --onefile Phoenix_SCT_Extract.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 Sucatalog Link Grabber**
|
||||
|
||||

|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue