mirror of
https://github.com/platomav/BIOSUtilities.git
synced 2025-05-09 13:52:00 -04:00
cleanup old files
This commit is contained in:
parent
132457afda
commit
7c00479a9e
17 changed files with 0 additions and 3774 deletions
2
.gitattributes
vendored
2
.gitattributes
vendored
|
@ -1,2 +0,0 @@
|
|||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
|
@ -1,321 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
#coding=utf-8
|
||||
|
||||
"""
|
||||
AMI PFAT Extract
|
||||
AMI BIOS Guard Extractor
|
||||
Copyright (C) 2018-2021 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
print('AMI BIOS Guard Extractor v3.2')
|
||||
|
||||
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 ctypes
|
||||
import shutil
|
||||
import traceback
|
||||
|
||||
# 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: ABGE crashed, please report the following:\n')
|
||||
traceback.print_exception(exc_type, exc_value, tb)
|
||||
input('\nPress enter to exit')
|
||||
sys.exit(1)
|
||||
|
||||
# Pause after any unexpected python exception
|
||||
sys.excepthook = show_exception_and_exit
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
# https://github.com/allowitsme/big-tool by Dmitry Frolov
|
||||
try :
|
||||
from big_script_tool import BigScript
|
||||
is_bgst = True
|
||||
except :
|
||||
is_bgst = False
|
||||
|
||||
# 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 PFAT_Header(ctypes.LittleEndianStructure) :
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Size', uint32_t), # 0x00
|
||||
('Checksum', uint32_t), # 0x04 Unknown 16-bits
|
||||
('Tag', char*8), # 0x04 _AMIPFAT
|
||||
('Flags', uint8_t), # 0x10
|
||||
# 0x11
|
||||
]
|
||||
|
||||
def pfat_print(self) :
|
||||
print('\n PFAT Main Header:\n')
|
||||
print(' Size : 0x%X' % self.Size)
|
||||
print(' Checksum : 0x%0.4X' % self.Checksum)
|
||||
print(' Tag : %s' % self.Tag.decode('utf-8'))
|
||||
print(' Flags : 0x%0.2X' % self.Flags)
|
||||
|
||||
class PFAT_Block_Header(ctypes.LittleEndianStructure) :
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('PFATVerMajor', uint16_t), # 0x00
|
||||
('PFATVerMinor', uint16_t), # 0x02
|
||||
('PlatformID', uint8_t*16), # 0x04
|
||||
('Attributes', uint32_t), # 0x14
|
||||
('ScriptVerMajor', uint16_t), # 0x16
|
||||
('ScriptVerMinor', uint16_t), # 0x18
|
||||
('ScriptSize', uint32_t), # 0x1C
|
||||
('DataSize', uint32_t), # 0x20
|
||||
('BIOSSVN', uint32_t), # 0x24
|
||||
('ECSVN', uint32_t), # 0x28
|
||||
('VendorInfo', uint32_t), # 0x2C
|
||||
# 0x30
|
||||
]
|
||||
|
||||
def __init__(self, count, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.count = count
|
||||
|
||||
def get_flags(self) :
|
||||
attr = PFAT_Block_Header_GetAttributes()
|
||||
attr.asbytes = self.Attributes
|
||||
|
||||
return attr.b.SFAM, attr.b.ProtectEC, attr.b.GFXMitDis, attr.b.FTU, attr.b.Reserved
|
||||
|
||||
def pfat_print(self) :
|
||||
no_yes = ['No','Yes']
|
||||
f1,f2,f3,f4,f5 = self.get_flags()
|
||||
|
||||
PlatformID = bytes(self.PlatformID).strip(b'\x00')
|
||||
if PlatformID.isalpha() : # STRING
|
||||
PlatformID = PlatformID.decode('utf-8', 'ignore')
|
||||
else : # GUID
|
||||
PlatformID = '%0.*X' % (0x10 * 2, int.from_bytes(self.PlatformID, 'big'))
|
||||
PlatformID = '{%s-%s-%s-%s-%s}' % (PlatformID[:8], PlatformID[8:12], PlatformID[12:16], PlatformID[16:20], PlatformID[20:])
|
||||
|
||||
print('\n PFAT Block %s Header:\n' % self.count)
|
||||
print(' PFAT Version : %d.%d' % (self.PFATVerMajor, self.PFATVerMinor))
|
||||
print(' Platform ID : %s' % PlatformID)
|
||||
print(' Signed Flash Address Map : %s' % no_yes[f1])
|
||||
print(' Protected EC OpCodes : %s' % no_yes[f2])
|
||||
print(' Graphics Security Disable : %s' % no_yes[f3])
|
||||
print(' Fault Tolerant Update : %s' % no_yes[f4])
|
||||
print(' Attributes Reserved : 0x%X' % f5)
|
||||
print(' Script Version : %d.%d' % (self.ScriptVerMajor, self.ScriptVerMinor))
|
||||
print(' Script Size : 0x%X' % self.ScriptSize)
|
||||
print(' Data Size : 0x%X' % self.DataSize)
|
||||
print(' BIOS SVN : 0x%X' % self.BIOSSVN)
|
||||
print(' EC SVN : 0x%X' % self.ECSVN)
|
||||
print(' Vendor Info : 0x%X' % self.VendorInfo)
|
||||
|
||||
class PFAT_Block_Header_Attributes(ctypes.LittleEndianStructure):
|
||||
_fields_ = [
|
||||
('SFAM', uint32_t, 1), # Signed Flash Address Map
|
||||
('ProtectEC', uint32_t, 1), # Protected EC OpCodes
|
||||
('GFXMitDis', uint32_t, 1), # GFX Security Disable
|
||||
('FTU', uint32_t, 1), # Fault Tolerant Update
|
||||
('Reserved', uint32_t, 28)
|
||||
]
|
||||
|
||||
class PFAT_Block_Header_GetAttributes(ctypes.Union):
|
||||
_fields_ = [
|
||||
('b', PFAT_Block_Header_Attributes),
|
||||
('asbytes', uint32_t)
|
||||
]
|
||||
|
||||
class PFAT_Block_RSA(ctypes.LittleEndianStructure) :
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Unknown0', uint32_t), # 0x00
|
||||
('Unknown1', uint32_t), # 0x04
|
||||
('PublicKey', uint32_t*64), # 0x08
|
||||
('Exponent', uint32_t), # 0x108
|
||||
('Signature', uint32_t*64), # 0x10C
|
||||
# 0x20C
|
||||
]
|
||||
|
||||
def __init__(self, count, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.count = count
|
||||
|
||||
def pfat_print(self) :
|
||||
PublicKey = '%0.*X' % (0x100 * 2, int.from_bytes(self.PublicKey, 'little'))
|
||||
Signature = '%0.*X' % (0x100 * 2, int.from_bytes(self.Signature, 'little'))
|
||||
|
||||
print('\n PFAT Block %s Signature:\n' % self.count)
|
||||
print(' Unknown 0 : 0x%X' % self.Unknown0)
|
||||
print(' Unknown 1 : 0x%X' % self.Unknown1)
|
||||
print(' Public Key : %s [...]' % PublicKey[:8])
|
||||
print(' Exponent : 0x%X' % self.Exponent)
|
||||
print(' Signature : %s [...]' % Signature[:8])
|
||||
|
||||
# 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) :
|
||||
input('\n Error: Offset 0x%X out of bounds at %s, possibly incomplete image!' % (start_offset, class_name.__name__))
|
||||
sys.exit(1)
|
||||
|
||||
ctypes.memmove(ctypes.addressof(structure), struct_data, fit_len)
|
||||
|
||||
return structure
|
||||
|
||||
if len(sys.argv) >= 2 :
|
||||
# Drag & Drop or CLI
|
||||
ami_pfat = sys.argv[1:]
|
||||
else :
|
||||
# Folder path
|
||||
ami_pfat = []
|
||||
in_path = input('\nEnter the full folder path: ')
|
||||
print('\nWorking...')
|
||||
for root, _, files in os.walk(in_path):
|
||||
for name in files :
|
||||
ami_pfat.append(os.path.join(root, name))
|
||||
|
||||
pfat_index = 1
|
||||
input_name = ''
|
||||
input_extension = ''
|
||||
output_path = ''
|
||||
block_hdr_size = ctypes.sizeof(PFAT_Block_Header)
|
||||
block_rsa_size = ctypes.sizeof(PFAT_Block_RSA)
|
||||
pfat_pat = re.compile(b'_AMIPFAT.AMI_BIOS_GUARD_FLASH_CONFIGURATIONS', re.DOTALL)
|
||||
|
||||
for input_file in ami_pfat :
|
||||
file_data = b''
|
||||
final_data = b''
|
||||
block_name = ''
|
||||
block_count = 0
|
||||
file_index = 0
|
||||
blocks = []
|
||||
|
||||
with open(input_file, 'rb') as in_file : buffer = in_file.read()
|
||||
|
||||
pfat_match = pfat_pat.search(buffer)
|
||||
|
||||
if pfat_index == 1 :
|
||||
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))
|
||||
|
||||
if not pfat_match :
|
||||
print('\n Error: This is not an AMI BIOS Guard (PFAT) image!')
|
||||
continue
|
||||
|
||||
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
|
||||
|
||||
if not pfat_match : continue
|
||||
|
||||
buffer = buffer[pfat_match.start() - 0x8:]
|
||||
|
||||
pfat_hdr = get_struct(buffer, 0, PFAT_Header)
|
||||
|
||||
hdr_size = pfat_hdr.Size
|
||||
hdr_data = buffer[0x11:hdr_size].decode('utf-8').splitlines()
|
||||
|
||||
pfat_hdr.pfat_print()
|
||||
print(' Title : %s' % hdr_data[0])
|
||||
|
||||
file_path = os.path.join(output_path, '%s%s -- %d' % (input_name, input_extension, pfat_index))
|
||||
|
||||
for entry in hdr_data[1:] :
|
||||
entry_data = entry.split(' ')
|
||||
entry_data = [s for s in entry_data if s != '']
|
||||
entry_flags = int(entry_data[0])
|
||||
entry_param = entry_data[1]
|
||||
entry_blocks = int(entry_data[2])
|
||||
entry_name = entry_data[3][1:]
|
||||
|
||||
for i in range(entry_blocks) : blocks.append([entry_name, entry_param, entry_flags, i + 1, entry_blocks])
|
||||
|
||||
block_count += entry_blocks
|
||||
|
||||
block_start = hdr_size
|
||||
for i in range(block_count) :
|
||||
is_file_start = blocks[i][0] != block_name
|
||||
|
||||
if is_file_start : print('\n %s (Parameter: %s, Flags: 0x%X)' % (blocks[i][0], blocks[i][1], blocks[i][2]))
|
||||
|
||||
block_hdr = get_struct(buffer, block_start, PFAT_Block_Header, ['%d/%d' % (blocks[i][3], blocks[i][4])])
|
||||
block_hdr.pfat_print()
|
||||
|
||||
block_script_size = block_hdr.ScriptSize
|
||||
block_script_data = buffer[block_start + block_hdr_size:block_start + block_hdr_size + block_script_size]
|
||||
block_data_start = block_start + block_hdr_size + block_script_size
|
||||
block_data_end = block_data_start + block_hdr.DataSize
|
||||
block_data = buffer[block_data_start:block_data_end]
|
||||
|
||||
block_rsa = get_struct(buffer, block_data_end, PFAT_Block_RSA, ['%d/%d' % (blocks[i][3], blocks[i][4])])
|
||||
block_rsa.pfat_print()
|
||||
|
||||
print('\n PFAT Block %d/%d Script:\n' % (blocks[i][3], blocks[i][4]))
|
||||
is_opcode_div = len(block_script_data) % 8 == 0
|
||||
is_begin_end = block_script_data[:8] + block_script_data[-8:] == b'\x01' + b'\x00' * 7 + b'\xFF' + b'\x00' * 7
|
||||
if is_opcode_div and is_begin_end and is_bgst :
|
||||
block_script_decomp = BigScript(code_bytes=block_script_data)
|
||||
block_script_lines = block_script_decomp.to_string().replace('\t',' ').split('\n')
|
||||
for line in block_script_lines :
|
||||
spacing = ' ' * 16 if line.endswith(('begin','end',':')) else ' ' * 24
|
||||
operands = [op for op in line.split(' ') if op != '']
|
||||
print(spacing + ('{:<12s}' + '{:<11s}' * (len(operands) - 1)).format(*operands))
|
||||
elif not is_opcode_div :
|
||||
print(' Error: Script not divisible by OpCode length!')
|
||||
elif not is_begin_end :
|
||||
print(' Error: Script lacks Begin and/or End OpCodes!')
|
||||
elif not is_bgst :
|
||||
print(' Error: BIOS Guard Script Tool dependency missing!')
|
||||
|
||||
file_data += block_data
|
||||
final_data += block_data
|
||||
|
||||
if i and is_file_start and file_data :
|
||||
file_index += 1
|
||||
with open('%s_%0.2d -- %s' % (file_path, file_index, block_name), 'wb') as o : o.write(file_data)
|
||||
file_data = b''
|
||||
|
||||
block_name = blocks[i][0]
|
||||
block_start = block_data_end + block_rsa_size
|
||||
|
||||
with open('%s_%0.2d -- %s' % (file_path, file_index + 1, block_name), 'wb') as o : o.write(file_data) # Last File
|
||||
|
||||
eof_data = buffer[block_start:] # Store any data after the end of PFAT
|
||||
|
||||
with open('%s_00 -- AMI_PFAT_%d_DATA_ALL.bin' % (file_path, pfat_index), 'wb') as final : final.write(final_data + eof_data)
|
||||
|
||||
if eof_data[:-0x100] != b'\xFF' * (len(eof_data) - 0x100) :
|
||||
eof_path = '%s_%0.2d -- AMI_PFAT_%d_DATA_END.bin' % (file_path, file_index + 2, pfat_index)
|
||||
with open(eof_path, 'wb') as final : final.write(eof_data)
|
||||
|
||||
if pfat_pat.search(eof_data) :
|
||||
pfat_index += 1
|
||||
ami_pfat_index = ami_pfat.index(input_file) + 1
|
||||
ami_pfat.insert(ami_pfat_index, eof_path)
|
||||
else :
|
||||
pfat_index = 1
|
||||
|
||||
input('\nDone!')
|
|
@ -1,610 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
#coding=utf-8
|
||||
|
||||
"""
|
||||
AMI UCP Extract
|
||||
AMI UCP BIOS Extractor
|
||||
Copyright (C) 2021 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
title = 'AMI UCP BIOS Extractor v1.2'
|
||||
|
||||
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 shutil
|
||||
import ctypes
|
||||
import argparse
|
||||
import traceback
|
||||
import subprocess
|
||||
import contextlib
|
||||
|
||||
# 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
|
||||
ucp_parser = argparse.ArgumentParser()
|
||||
ucp_parser.add_argument('executables', type=argparse.FileType('r'), nargs='*')
|
||||
ucp_parser.add_argument('-p', '--path', help='parse files within given folder', type=str)
|
||||
ucp_parser.add_argument('-c', '--checksum', help='verify AMI UCP Checksums (slow)', action='store_true')
|
||||
ucp_params = ucp_parser.parse_args()
|
||||
|
||||
verify_chk16 = bool(ucp_params.checksum) # Get Checksum16 Verification optional argument
|
||||
|
||||
# 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(ucp_params.path) :
|
||||
ucp_exec = get_files(ucp_params.path) # CLI with --path
|
||||
else :
|
||||
ucp_exec = []
|
||||
for executable in ucp_params.executables :
|
||||
ucp_exec.append(executable.name) # Drag & Drop
|
||||
else :
|
||||
in_path = input('\nEnter the full folder path: ')
|
||||
ucp_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 UAF_HDR(ctypes.LittleEndianStructure) :
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('ModuleTag', char*4), # 0x00
|
||||
('ModuleSize', uint32_t), # 0x04
|
||||
('Checksum', uint16_t), # 0x08
|
||||
('Unknown0', uint8_t), # 0x0A
|
||||
('Unknown1', uint8_t), # 0x0A
|
||||
('Reserved', uint32_t), # 0x0C
|
||||
# 0x10
|
||||
]
|
||||
|
||||
def __init__(self, padd, *args, **kwargs) :
|
||||
super().__init__(*args, **kwargs)
|
||||
self.p = padd
|
||||
|
||||
def ucp_print(self, chk16) :
|
||||
print('\n%s Utility Auxiliary File:\n' % self.p)
|
||||
print('%s Module Tag : %s' % (self.p, self.ModuleTag.decode('utf-8')))
|
||||
print('%s Module Size : 0x%X' % (self.p, self.ModuleSize))
|
||||
print('%s Checksum : 0x%0.4X (%s)' % (self.p, self.Checksum, chk16))
|
||||
print('%s Unknown 0 : 0x%0.2X' % (self.p, self.Unknown0))
|
||||
print('%s Unknown 1 : 0x%0.2X' % (self.p, self.Unknown1))
|
||||
print('%s Reserved : 0x%0.8X' % (self.p, self.Reserved))
|
||||
|
||||
class UAF_MOD(ctypes.LittleEndianStructure) :
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('CompressSize', uint32_t), # 0x00
|
||||
('OriginalSize', uint32_t), # 0x04
|
||||
# 0x08
|
||||
]
|
||||
|
||||
def __init__(self, padd, *args, **kwargs) :
|
||||
super().__init__(*args, **kwargs)
|
||||
self.p = padd
|
||||
|
||||
def ucp_print(self) :
|
||||
print('%s Compress Size : 0x%X' % (self.p, self.CompressSize))
|
||||
print('%s Original Size : 0x%X' % (self.p, self.OriginalSize))
|
||||
|
||||
class UII_HDR(ctypes.LittleEndianStructure) :
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('UIISize', uint16_t), # 0x00
|
||||
('Checksum', uint16_t), # 0x02
|
||||
('UtilityVersion', uint32_t), # 0x04 i.e. AFU (Unknown Encoding, Signed)
|
||||
('InfoSize', uint16_t), # 0x08
|
||||
('SupportBIOS', uint8_t), # 0x0A
|
||||
('SupportOS', uint8_t), # 0x0B
|
||||
('DataBusWidth', uint8_t), # 0x0C
|
||||
('ProgramType', uint8_t), # 0x0D
|
||||
('ProgramMode', uint8_t), # 0x0E
|
||||
('SourceSafeRelease', uint8_t), # 0x0F
|
||||
# 0x10
|
||||
]
|
||||
|
||||
def __init__(self, padd, *args, **kwargs) :
|
||||
super().__init__(*args, **kwargs)
|
||||
self.p = padd
|
||||
|
||||
def ucp_print(self, chk16) :
|
||||
sbios = {1: 'ALL', 2: 'AMIBIOS8', 3: 'UEFI', 4: 'AMIBIOS8/UEFI'}
|
||||
sos = {1: 'DOS', 2: 'EFI', 3: 'Windows', 4: 'Linux', 5: 'FreeBSD', 6: 'MacOS', 128: 'Multi-Platform'}
|
||||
dbwidth = {1: '16b', 2: '16/32b', 3: '32b', 4: '64b'}
|
||||
ptype = {1: 'Executable', 2: 'Library', 3: 'Driver'}
|
||||
pmode = {1: 'API', 2: 'Console', 3: 'GUI', 4: 'Console/GUI'}
|
||||
|
||||
SupportBIOS = sbios[self.SupportBIOS] if self.SupportBIOS in sbios else 'Unknown (%d)' % self.SupportBIOS
|
||||
SupportOS = sos[self.SupportOS] if self.SupportOS in sos else 'Unknown (%d)' % self.SupportOS
|
||||
DataBusWidth = dbwidth[self.DataBusWidth] if self.DataBusWidth in dbwidth else 'Unknown (%d)' % self.DataBusWidth
|
||||
ProgramType = ptype[self.ProgramType] if self.ProgramType in ptype else 'Unknown (%d)' % self.ProgramType
|
||||
ProgramMode = pmode[self.ProgramMode] if self.ProgramMode in pmode else 'Unknown (%d)' % self.ProgramMode
|
||||
|
||||
print('\n%s Utility Identification Information:\n' % self.p)
|
||||
print('%s UII Size : 0x%X' % (self.p, self.UIISize))
|
||||
print('%s Checksum : 0x%0.4X (%s)' % (self.p, self.Checksum, chk16))
|
||||
print('%s Tool Version : 0x%0.8X (Unknown)' % (self.p, self.UtilityVersion))
|
||||
print('%s Info Size : 0x%X' % (self.p, self.InfoSize))
|
||||
print('%s Supported BIOS : %s' % (self.p, SupportBIOS))
|
||||
print('%s Supported OS : %s' % (self.p, SupportOS))
|
||||
print('%s Data Bus Width : %s' % (self.p, DataBusWidth))
|
||||
print('%s Program Type : %s' % (self.p, ProgramType))
|
||||
print('%s Program Mode : %s' % (self.p, ProgramMode))
|
||||
print('%s SourceSafe Tag : %0.2d' % (self.p, self.SourceSafeRelease))
|
||||
|
||||
class DIS_HDR(ctypes.LittleEndianStructure) :
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('PasswordSize', uint16_t), # 0x00
|
||||
('EntryCount', uint16_t), # 0x02
|
||||
('Password', char*12), # 0x04
|
||||
# 0x10
|
||||
]
|
||||
|
||||
def __init__(self, padd, *args, **kwargs) :
|
||||
super().__init__(*args, **kwargs)
|
||||
self.p = padd
|
||||
|
||||
def ucp_print(self) :
|
||||
print('\n%s Default Command Status Header:\n' % self.p)
|
||||
print('%s Password Size : 0x%X' % (self.p, self.PasswordSize))
|
||||
print('%s Entry Count : %d' % (self.p, self.EntryCount))
|
||||
print('%s Password : %s' % (self.p, self.Password.decode('utf-8')))
|
||||
|
||||
class DIS_MOD(ctypes.LittleEndianStructure) :
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('EnabledDisabled', uint8_t), # 0x00
|
||||
('ShownHidden', uint8_t), # 0x01
|
||||
('Command', char*32), # 0x02
|
||||
('Description', char*256), # 0x22
|
||||
# 0x122
|
||||
]
|
||||
|
||||
def __init__(self, padd, *args, **kwargs) :
|
||||
super().__init__(*args, **kwargs)
|
||||
self.p = padd
|
||||
|
||||
def ucp_print(self) :
|
||||
enabled = {0: 'Disabled', 1: 'Enabled'}
|
||||
shown = {0: 'Hidden', 1: 'Shown', 2: 'Shown Only'}
|
||||
|
||||
EnabledDisabled = enabled[self.EnabledDisabled] if self.EnabledDisabled in enabled else 'Unknown (%d)' % self.EnabledDisabled
|
||||
ShownHidden = shown[self.ShownHidden] if self.ShownHidden in shown else 'Unknown (%d)' % self.ShownHidden
|
||||
|
||||
print('\n%s Default Command Status Entry:\n' % self.p)
|
||||
print('%s State : %s' % (self.p, EnabledDisabled))
|
||||
print('%s Display : %s' % (self.p, ShownHidden))
|
||||
print('%s Command : %s' % (self.p, self.Command.decode('utf-8').strip()))
|
||||
print('%s Description : %s' % (self.p, self.Description.decode('utf-8').strip()))
|
||||
|
||||
# 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
|
||||
|
||||
# Get Checksum16 validity result
|
||||
def checksum16(buffer, check) :
|
||||
if not check : return 'Skipped'
|
||||
|
||||
chk16 = 0
|
||||
|
||||
for idx in range(0, len(buffer), 2) :
|
||||
chk16 += int.from_bytes(buffer[idx:idx + 2], 'little')
|
||||
|
||||
chk16 &= 0xFFFF
|
||||
|
||||
return 'Good' if chk16 == 0 else 'Bad'
|
||||
|
||||
# Get all input file AMI UCP patterns
|
||||
def get_matches(buffer) :
|
||||
uaf_len_max = 0 # Length of largest detected @UAF
|
||||
uaf_hdr_off = 0 # Offset of largest detected @UAF
|
||||
|
||||
for uaf in ami_ucp_pat.finditer(buffer) :
|
||||
uaf_len_cur = int.from_bytes(buffer[uaf.start() + 0x4:uaf.start() + 0x8], 'little')
|
||||
|
||||
if uaf_len_cur > uaf_len_max :
|
||||
uaf_len_max = uaf_len_cur
|
||||
uaf_hdr_off = uaf.start()
|
||||
|
||||
return uaf_hdr_off, uaf_len_max
|
||||
|
||||
# Parse & Extract AMI UCP structures
|
||||
def ucp_extract(buffer, out_dir, level, padd) :
|
||||
nal_dict = {} # Initialize @NAL Dictionary per UCP
|
||||
|
||||
uaf_hdr = get_struct(buffer, 0, UAF_HDR, [padd]) # Parse @UAF Header Structure
|
||||
|
||||
uaf_chk = checksum16(buffer, verify_chk16) # Get @UAF Header Checksum16
|
||||
|
||||
# Print @UAF Header Info
|
||||
uaf_hdr.ucp_print(uaf_chk)
|
||||
print('%s Compress Size : 0x%X' % (padd, len(buffer)))
|
||||
print('%s Original Size : 0x%X' % (padd, len(buffer)))
|
||||
print('%s Module Name : %s' % (padd, tag_dict['UAF']))
|
||||
|
||||
if uaf_chk == 'Bad' :
|
||||
input('\n%s Error: Invalid AMI UCP Module UAF Checksum!' % padd)
|
||||
|
||||
uaf_off = uaf_hdr_len # Parsed @UAF, next Modules
|
||||
uaf_all = [] # Initialize list of all UAF Modules
|
||||
is_pfat = False # Initialize PFAT BIOS detection
|
||||
is_dual = False # Initialize AMI/Insyde detection
|
||||
|
||||
while buffer[uaf_off] == 0x40 : # ASCII of @ is 0x40
|
||||
uaf_hdr = get_struct(buffer, uaf_off, UAF_HDR, [padd]) # Parse UAF Module Structure
|
||||
|
||||
uaf_tag = uaf_hdr.ModuleTag.decode('utf-8')[1:] # Get unique UAF Module Tag
|
||||
|
||||
if uaf_tag == 'PFC' : is_pfat = True # Detect if UAF Module has PFAT BIOS
|
||||
|
||||
if uaf_tag == 'AMI' : is_dual = True # Detect if UAF Module has dual AMI/Insyde BIOS
|
||||
|
||||
uaf_all.append([uaf_tag, uaf_off, uaf_hdr]) # Store UAF Module Info
|
||||
|
||||
uaf_off += uaf_hdr.ModuleSize # Adjust to next UAF Module offset
|
||||
|
||||
if uaf_off >= len(buffer) : break # Stop parsing at EOF
|
||||
|
||||
# Check if UAF Module NAL exists and place it first
|
||||
# Parsing NAL first allows naming all UAF Modules
|
||||
for i in range(len(uaf_all)) :
|
||||
if uaf_all[i][0] == 'NAL' :
|
||||
uaf_all.insert(1, uaf_all.pop(i)) # After UII for visual purposes
|
||||
break # NAL found, skip the rest
|
||||
|
||||
# Parse all UAF Modules
|
||||
for uaf in uaf_all :
|
||||
uaf_tag = uaf[0] # Store UAF Module Tag
|
||||
uaf_off = uaf[1] # Store UAF Module Offset
|
||||
uaf_hdr = uaf[2] # Store UAF Module Struct
|
||||
|
||||
uaf_data_all = buffer[uaf_off:uaf_off + uaf_hdr.ModuleSize] # UAF Module Entire Data
|
||||
|
||||
uaf_data_mod = uaf_data_all[uaf_hdr_len:] # UAF Module EFI Data
|
||||
|
||||
uaf_data_raw = uaf_data_mod[uaf_mod_len:] # UAF Module Raw Data
|
||||
|
||||
uaf_chk = checksum16(uaf_data_all, verify_chk16) # Get UAF Module Checksum16
|
||||
|
||||
uaf_hdr.ucp_print(uaf_chk) # Print UAF Module Info
|
||||
|
||||
uaf_mod = get_struct(buffer, uaf_off + uaf_hdr_len, UAF_MOD, [padd]) # Parse UAF Module EFI Structure
|
||||
|
||||
uaf_mod.ucp_print() # Print UAF Module EFI Info
|
||||
|
||||
is_comp = uaf_mod.CompressSize != uaf_mod.OriginalSize # Detect UAF Module EFI Compression
|
||||
|
||||
rom_name = 'PFAT' if is_pfat else 'BIOS' # Set UAF Module BIOS/ROM name based on PFAT state
|
||||
|
||||
if uaf_tag in nal_dict : uaf_name = nal_dict[uaf_tag] # Always prefer NAL naming first
|
||||
elif uaf_tag in tag_dict : uaf_name = tag_dict[uaf_tag] # Otherwise use built-in naming
|
||||
elif uaf_tag == 'ROM' : uaf_name = '%s.bin' % rom_name # BIOS/PFAT Firmware
|
||||
elif uaf_tag.startswith('R0') : uaf_name = '%s_0%s.bin' % (rom_name, uaf_tag[2:]) # BIOS/PFAT Firmware
|
||||
elif uaf_tag.startswith('S0') : uaf_name = '%s_0%s.sig' % (rom_name, uaf_tag[2:]) # BIOS/PFAT Signature
|
||||
elif uaf_tag.startswith('DR') : uaf_name = 'DROM_0%s.bin' % uaf_tag[2:] # Thunderbolt Retimer Firmware
|
||||
elif uaf_tag.startswith('DS') : uaf_name = 'DROM_0%s.sig' % uaf_tag[2:] # Thunderbolt Retimer Signature
|
||||
elif uaf_tag.startswith('EC') : uaf_name = 'EC_0%s.bin' % uaf_tag[2:] # Embedded Controller Firmware
|
||||
elif uaf_tag.startswith('ME') : uaf_name = 'ME_0%s.bin' % uaf_tag[2:] # Management Engine Firmware
|
||||
else : uaf_name = uaf_tag # Could not name the UAF Module, use Tag instead
|
||||
|
||||
if uaf_name != uaf_tag :
|
||||
uaf_fext = '' # File extension included in name
|
||||
print('%s Module Name : %s' % (padd, uaf_name))
|
||||
elif uaf_tag in ['CMD','PFC','VER','MEC','NAL','CKV'] :
|
||||
uaf_fext = '.txt' # Known Text files
|
||||
print('%s Module Name : %s%s (Unknown)' % (padd, uaf_name, uaf_fext))
|
||||
else :
|
||||
uaf_fext = '.bin' # Unknown files, assume binary
|
||||
print('%s Module Name : %s%s (Unknown)' % (padd, uaf_name, uaf_fext))
|
||||
|
||||
# Check if unknown UAF Module Tag is present in NAL but not in built-in dictionary
|
||||
if uaf_tag in nal_dict and uaf_tag not in tag_dict and not uaf_tag.startswith(('ROM','R0','S0','DR','DS')) :
|
||||
input('\n%s Note: Detected new AMI UCP Module %s (%s) in NAL!' % (padd, uaf_tag, nal_dict[uaf_tag]))
|
||||
|
||||
# Generate UAF Module File name, depending on whether decompression will be required
|
||||
uaf_fname = os.path.join(out_dir, '%s%s' % (uaf_name, '.temp' if is_comp else uaf_fext))
|
||||
|
||||
if uaf_chk == 'Bad' :
|
||||
input('\n%s Error: Invalid AMI UCP Module %s Checksum!' % (padd, uaf_tag))
|
||||
|
||||
# Parse Utility Identification Information UAF Module (UII)
|
||||
if uaf_tag == 'UII' :
|
||||
info_hdr = get_struct(uaf_data_raw, 0, UII_HDR, [padd]) # Parse UII Module Raw Structure
|
||||
|
||||
info_chk = checksum16(uaf_data_raw, verify_chk16) # Get UII Module Checksum16
|
||||
|
||||
info_hdr.ucp_print(info_chk) # Print UII Module Info
|
||||
|
||||
# Get UII Module Description text field
|
||||
desc = uaf_data_raw[info_hdr.InfoSize:info_hdr.UIISize].strip(b'\x00').decode('utf-8')
|
||||
|
||||
print('%s Description : %s' % (padd, desc)) # Print UII Module Description
|
||||
|
||||
if info_chk == 'Bad' :
|
||||
input('\n%s Error: Invalid AMI UCP Module %s > Info Checksum!' % (padd, uaf_tag))
|
||||
|
||||
# Store/Save UII Module Info in file
|
||||
with open(uaf_fname[:-3] + 'txt', 'a') as uii :
|
||||
with contextlib.redirect_stdout(uii) :
|
||||
info_hdr.ucp_print(info_chk) # Store UII Module Info
|
||||
|
||||
print('%s Description : %s' % (padd, desc)) # Store UII Module Description
|
||||
|
||||
# Adjust UAF Module Raw Data for extraction
|
||||
if is_comp :
|
||||
# Some Compressed UAF Module EFI data lack necessary padding in the end
|
||||
if uaf_mod.CompressSize > len(uaf_data_raw) :
|
||||
comp_padd = b'\x00' * (uaf_mod.CompressSize - len(uaf_data_raw))
|
||||
uaf_data_raw = uaf_data_mod[:uaf_mod_len] + uaf_data_raw + comp_padd # Add missing padding for decompression
|
||||
else :
|
||||
uaf_data_raw = uaf_data_mod[:uaf_mod_len] + uaf_data_raw # Add the EFI/Tiano Compression info before Raw Data
|
||||
else :
|
||||
uaf_data_raw = uaf_data_raw[:uaf_mod.OriginalSize] # No compression, extend to end of Original UAF Module size
|
||||
|
||||
# Store/Save UAF Module file
|
||||
if uaf_tag != 'UII' : # Skip UII binary, already parsed
|
||||
with open(uaf_fname, 'wb') as out : out.write(uaf_data_raw)
|
||||
|
||||
# UAF Module EFI/Tiano Decompression
|
||||
if is_comp :
|
||||
try :
|
||||
dec_fname = uaf_fname[:-5] + uaf_fext # Decompressed UAF Module file path
|
||||
subprocess.run(['TianoCompress', '-d', uaf_fname, '-o', dec_fname, '--uefi', '-q'], check = True, stdout = subprocess.DEVNULL)
|
||||
|
||||
with open(dec_fname, 'rb') as dec : uaf_data_raw = dec.read() # Read back the UAF Module decompressed Raw data
|
||||
|
||||
if len(uaf_data_raw) == 0 : raise Exception('DECOMP_OUT_EMPTY') # If decompressed file is empty, something went wrong
|
||||
|
||||
os.remove(uaf_fname) # Successful decompression, delete compressed UAF Module file
|
||||
|
||||
uaf_fname = dec_fname # Adjust UAF Module file path to the decompressed one
|
||||
except :
|
||||
print('\n%s Error: Could not extract AMI UCP Module %s via TianoCompress!' % (padd, uaf_tag))
|
||||
input('%s Make sure that "TianoCompress" executable exists!' % padd)
|
||||
|
||||
# Process and Print known text only UAF Modules (after EFI/Tiano Decompression)
|
||||
if uaf_tag in ['CMD','PFC','VER','MEC','CKV'] : # Always referenced in tag_desc
|
||||
text_data = uaf_data_raw.decode('utf-8')
|
||||
print('\n%s %s:\n\n%s %s' % (padd, tag_desc[uaf_tag], padd, text_data))
|
||||
|
||||
# Parse Default Command Status UAF Module (DIS)
|
||||
if len(uaf_data_raw) and uaf_tag == 'DIS' :
|
||||
dis_hdr = get_struct(uaf_data_raw, 0, DIS_HDR, [padd]) # Parse DIS Module Raw Header Structure
|
||||
dis_hdr.ucp_print() # Print DIS Module Raw Header Info
|
||||
|
||||
# Store/Save DIS Module Header Info in file
|
||||
with open(uaf_fname[:-3] + 'txt', 'a') as dis :
|
||||
with contextlib.redirect_stdout(dis) :
|
||||
dis_hdr.ucp_print() # Store DIS Module Header Info
|
||||
|
||||
dis_data = uaf_data_raw[uaf_hdr_len:] # DIS Module Entries Data
|
||||
|
||||
# Parse all DIS Module Entries
|
||||
for e_idx in range(dis_hdr.EntryCount) :
|
||||
dis_mod = get_struct(dis_data, e_idx * 0x122, DIS_MOD, [padd]) # Parse DIS Module Raw Entry Structure
|
||||
dis_mod.ucp_print() # Print DIS Module Raw Entry Info
|
||||
|
||||
# Store/Save DIS Module Entry Info in file
|
||||
with open(uaf_fname[:-3] + 'txt', 'a') as dis :
|
||||
with contextlib.redirect_stdout(dis) :
|
||||
dis_mod.ucp_print() # Store DIS Module Entry Info
|
||||
|
||||
os.remove(uaf_fname) # Delete DIS Module binary, info exported as text
|
||||
|
||||
# Parse Non-AMI List (?) UAF Module (NAL)
|
||||
if len(uaf_data_raw) >= 5 and (uaf_tag,uaf_data_raw[0],uaf_data_raw[4]) == ('NAL',0x40,0x3A) :
|
||||
nal_info = uaf_data_raw.decode('utf-8').strip().replace('\r','').split('\n')
|
||||
|
||||
print('\n%s UAF List:\n' % padd)
|
||||
|
||||
# Parse all NAL Module Entries
|
||||
for info in nal_info :
|
||||
print('%s %s : %s' % (padd, info[1:4], info[5:])) # Print NAL Module Tag-Path Info
|
||||
nal_dict[info[1:4]] = os.path.basename(info[5:]) # Assign a file name (w/o path) to each Tag
|
||||
|
||||
# Parse Insyde BIOS UAF Module (INS)
|
||||
if len(uaf_data_raw) >= 2 and (uaf_tag,is_dual,uaf_data_raw[:2]) == ('INS',True,b'\x4D\x5A') :
|
||||
ins_dir = os.path.join(out_dir, '%s_extracted (SFX)' % uaf_tag) # Generate extraction directory
|
||||
|
||||
print('\n%s Insyde BIOS 7-Zip SFX Archive:\n\n%s 7-Zip will be used for extraction' % (padd, padd))
|
||||
|
||||
# INS Module extraction
|
||||
try :
|
||||
subprocess.run(['7z', 'x', '-aou', '-bso0', '-bse0', '-bsp0', '-o' + ins_dir, uaf_fname], check = True, stdout = subprocess.DEVNULL)
|
||||
|
||||
if not os.path.isdir(ins_dir) : raise Exception('EXTR_DIR_MISSING') # If extraction folder is missing, something went wrong
|
||||
|
||||
os.remove(uaf_fname) # Successful extraction, delete archived INS Module file
|
||||
except :
|
||||
print('\n%s Error: Could not extract AMI UCP Module %s via 7-Zip!' % (padd, uaf_tag))
|
||||
input('%s Make sure that "7z" executable exists!' % padd)
|
||||
|
||||
# Detect AMI BIOS Guard (PFAT) image and print extraction instructions/utility
|
||||
if len(uaf_data_raw) >= 16 and (is_pfat,uaf_data_raw[0x8:0x10]) == (True,b'_AMIPFAT') :
|
||||
print('\n%s AMI BIOS Guard (PFAT) Image:\n' % padd)
|
||||
print('%s Use "AMI BIOS Guard Extractor" from https://github.com/platomav/BIOSUtilities' % padd)
|
||||
|
||||
# Detect Intel Management Engine (ME) image and print parsing instructions/utility
|
||||
if len(uaf_data_raw) and uaf_name.startswith('ME_0') :
|
||||
print('\n%s Intel Management Engine (ME) Firmware:\n' % padd)
|
||||
print('%s Use "ME Analyzer" from https://github.com/platomav/MEAnalyzer' % padd)
|
||||
|
||||
# Get best Nested AMI UCP Pattern match based on @UAF Size
|
||||
uaf_hdr_off,uaf_len_max = get_matches(uaf_data_raw)
|
||||
|
||||
# Parse Nested AMI UCP Structure
|
||||
if uaf_hdr_off :
|
||||
level += 1 # Increase structure Level to control output padding
|
||||
uaf_dir = os.path.join(out_dir, '%s_extracted (UCP)' % uaf_tag) # Generate extraction directory
|
||||
os.mkdir(uaf_dir) # Create extraction directory
|
||||
ucp_extract(uaf_data_raw[uaf_hdr_off:uaf_hdr_off + uaf_len_max], uaf_dir, level, ' ' * level) # Call recursively
|
||||
os.remove(uaf_fname) # Delete raw nested AMI UCP Structure after successful recursion/extraction
|
||||
|
||||
# Utility Auxiliary File (@UAF) and Utility Identification Information (@UII)
|
||||
ami_ucp_pat = re.compile(br'\x40\x55\x41\x46.{12}\x40\x55\x49\x49', re.DOTALL)
|
||||
|
||||
# Get common ctypes Structure Sizes
|
||||
uaf_hdr_len = ctypes.sizeof(UAF_HDR)
|
||||
uaf_mod_len = ctypes.sizeof(UAF_MOD)
|
||||
|
||||
# User friendly Tag Descriptions
|
||||
tag_desc = {
|
||||
'CMD' : 'AMI AFU Command',
|
||||
'PFC' : 'AMI BGT Command',
|
||||
'VER' : 'OEM Version',
|
||||
'CKV' : 'Check Version',
|
||||
'MEC' : 'ME FWUpdLcl Command',
|
||||
}
|
||||
|
||||
# AMI UCP Tag-File Dictionary
|
||||
tag_dict = {
|
||||
'W32' : 'amifldrv32.sys',
|
||||
'W64' : 'amifldrv64.sys',
|
||||
'VXD' : 'amifldrv.vxd',
|
||||
'DCT' : 'DevCon32.exe',
|
||||
'DCX' : 'DevCon64.exe',
|
||||
'CMD' : 'AFU_Command.txt',
|
||||
'PFC' : 'BGT_Command.txt',
|
||||
'VER' : 'OEM_Version.txt',
|
||||
'CKV' : 'Check_Version.txt',
|
||||
'OKM' : 'OK_Message.txt',
|
||||
'CPM' : 'AC_Message.txt',
|
||||
'DIS' : 'Command_Status.bin',
|
||||
'UAF' : 'UCP_Main.bin',
|
||||
'UII' : 'UCP_Info.txt',
|
||||
'NAL' : 'UAF_List.txt',
|
||||
'MEC' : 'FWUpdLcl.txt',
|
||||
'MED' : 'FWUpdLcl_DOS.exe',
|
||||
'MET' : 'FWUpdLcl_WIN.exe',
|
||||
'AMI' : 'UCP_Nested.bin',
|
||||
'INS' : 'Insyde_Nested.bin',
|
||||
'RFI' : 'CryptRSA.efi',
|
||||
'R3I' : 'CryptRSA32.efi',
|
||||
'UFI' : 'HpBiosUpdate.efi',
|
||||
'US9' : 'HpBiosUpdate.s09',
|
||||
'US2' : 'HpBiosUpdate.s12',
|
||||
'USG' : 'HpBiosUpdate.sig',
|
||||
'3FI' : 'HpBiosUpdate32.efi',
|
||||
'3S9' : 'HpBiosUpdate32.s09',
|
||||
'3S2' : 'HpBiosUpdate32.s12',
|
||||
'3SG' : 'HpBiosUpdate32.sig',
|
||||
'MFI' : 'HpBiosMgmt.efi',
|
||||
'MS9' : 'HpBiosMgmt.s09',
|
||||
'MS2' : 'HpBiosMgmt.s12',
|
||||
'US4' : 'HpBiosUpdate.s14',
|
||||
'3S4' : 'HpBiosUpdate32.s14',
|
||||
'MS4' : 'HpBiosMgmt.s14',
|
||||
'M3I' : 'HpBiosMgmt32.efi',
|
||||
'M39' : 'HpBiosMgmt32.s09',
|
||||
'M32' : 'HpBiosMgmt32.s12',
|
||||
'M34' : 'HpBiosMgmt32.s14',
|
||||
'BME' : 'BiosMgmt.efi',
|
||||
'BM9' : 'BiosMgmt.s09',
|
||||
'B12' : 'BiosMgmt.s12',
|
||||
'B14' : 'BiosMgmt.s14',
|
||||
'B3E' : 'BiosMgmt32.efi',
|
||||
'B39' : 'BiosMgmt32.s09',
|
||||
'B32' : 'BiosMgmt32.s12',
|
||||
'B34' : 'BiosMgmt32.s14',
|
||||
'DFE' : 'HpDevFwUpdate.efi',
|
||||
'DFS' : 'HpDevFwUpdate.s12',
|
||||
'ENB' : 'ENBG64.exe',
|
||||
}
|
||||
|
||||
# Process each input AMI UCP BIOS executable
|
||||
for input_file in ucp_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()
|
||||
|
||||
# Get best AMI UCP Pattern match based on @UAF Size
|
||||
uaf_hdr_off,uaf_len_max = get_matches(input_data)
|
||||
|
||||
# Check if AMI UCP Pattern was found on executable
|
||||
if not uaf_hdr_off :
|
||||
print('\n Error: This is not an AMI UCP 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 AMI Utility Configuration Program')
|
||||
|
||||
level = 0 # Set initial AMI UCP structure Level to control padding in nested ones
|
||||
|
||||
ucp_extract(input_data[uaf_hdr_off:uaf_hdr_off + uaf_len_max], output_path, level, '') # Call the AMI UCP Extractor function
|
||||
|
||||
print('\n Extracted AMI UCP BIOS executable!')
|
||||
|
||||
input('\nDone!')
|
||||
|
||||
sys.exit(0)
|
|
@ -1,104 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Apple EFI Rename
|
||||
Apple EFI File Renamer
|
||||
Copyright (C) 2018-2019 Plato Mavropoulos
|
||||
https://github.com/tianocore/edk2/blob/master/Vlv2TbltDevicePkg/Include/Library/BiosIdLib.h
|
||||
"""
|
||||
|
||||
print('Apple EFI File Renamer v1.3\n')
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import zlib
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
pattern = re.compile(br'\x24\x49\x42\x49\x4F\x53\x49\x24') # Intel $IBIOSI$
|
||||
|
||||
if len(sys.argv) >= 3 and sys.argv[1] == '-skip' :
|
||||
# Called via Apple_EFI_Package
|
||||
apple_efi = sys.argv[2:]
|
||||
skip_pause = True
|
||||
elif len(sys.argv) >= 2 :
|
||||
# Drag & Drop or CLI
|
||||
apple_efi = sys.argv[1:]
|
||||
skip_pause = False
|
||||
else :
|
||||
# Folder path
|
||||
apple_efi = []
|
||||
skip_pause = False
|
||||
in_path = input('\nEnter the full folder path: ')
|
||||
print('\nWorking...\n')
|
||||
for root, dirs, files in os.walk(in_path):
|
||||
for name in files :
|
||||
apple_efi.append(os.path.join(root, name))
|
||||
|
||||
for input_file in apple_efi :
|
||||
file_path = os.path.abspath(input_file)
|
||||
file_name = os.path.basename(input_file)
|
||||
file_dir = os.path.dirname(file_path)
|
||||
file_ext = os.path.splitext(file_path)[1]
|
||||
error = False
|
||||
|
||||
with open(input_file, 'rb') as in_file : buffer = in_file.read()
|
||||
|
||||
is_ibiosi = pattern.search(buffer) # Detect $IBIOSI$ pattern
|
||||
|
||||
if not is_ibiosi :
|
||||
|
||||
# On some Apple EFI, the $IBIOSI$ pattern is within compressed modules so we need to use UEFIFind and UEFIExtract
|
||||
|
||||
try :
|
||||
uefifind = subprocess.check_output(['UEFIFind', file_path, 'body', 'list', '244942494F534924'], universal_newlines=True)
|
||||
uefiextr = subprocess.run(['UEFIExtract', file_path, uefifind[0:36], '-o', '_$IBIOSI$_', '-m', 'body'], stdout=subprocess.DEVNULL)
|
||||
|
||||
with open(os.path.join('_$IBIOSI$_', 'body.bin'), 'rb') as in_file : buffer = in_file.read()
|
||||
|
||||
is_ibiosi = pattern.search(buffer) # Detect decompressed $IBIOSI$ pattern
|
||||
|
||||
shutil.rmtree('_$IBIOSI$_') # Remove temporary folder
|
||||
|
||||
except :
|
||||
error = True
|
||||
|
||||
if not error :
|
||||
|
||||
bios_info = buffer[is_ibiosi.end():is_ibiosi.end() + 0x42].decode('utf-16')
|
||||
|
||||
BoardID = bios_info[:7].strip()
|
||||
BoardRev = bios_info[7]
|
||||
OEMID = bios_info[9:12] # 88Z
|
||||
MajorVer = bios_info[13:17]
|
||||
BuildType = bios_info[18] # B
|
||||
MinorVer = bios_info[19:21]
|
||||
Year = bios_info[22:24]
|
||||
Month = bios_info[24:26]
|
||||
Day = bios_info[26:28]
|
||||
Hour = bios_info[28:30]
|
||||
Minute = bios_info[30:32]
|
||||
|
||||
file_chk = zlib.adler32(buffer) # Checksum for EFI with same $IBIOSI$ but different PRD/PRE status
|
||||
|
||||
new_name = '%s%s_%s_%s%s_20%s-%s-%s_%s-%s_%0.8X%s' % (BoardID, BoardRev, MajorVer, BuildType, MinorVer, Year, Month, Day, Hour, Minute, file_chk, file_ext)
|
||||
|
||||
file_path_new = os.path.join(file_dir, new_name)
|
||||
|
||||
if not os.path.isfile(file_path_new) : os.replace(file_path, file_path_new) # Rename input EFI with proper name
|
||||
|
||||
print(new_name)
|
||||
print('\nBoard Identity: %s%s' % (BoardID, BoardRev))
|
||||
print('Apple Identity: %s' % OEMID)
|
||||
print('Major Version: %s' % MajorVer)
|
||||
print('Minor Version: %s' % MinorVer)
|
||||
print('Build Type: %s' % BuildType)
|
||||
print('Build Date: 20%s-%s-%s' % (Year, Month, Day))
|
||||
print('Build Time: %s:%s\n' % (Hour, Minute))
|
||||
|
||||
else :
|
||||
print('\nError: Could not find $IBIOSI$ pattern at %s!' % file_name)
|
||||
print(' Make sure that "UEFIFind" and "UEFIExtract" executables exist!\n')
|
||||
|
||||
if not skip_pause : input('Done!')
|
|
@ -1,146 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Apple EFI Split
|
||||
Apple EFI IM4P Splitter
|
||||
Copyright (C) 2018-2020 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
title = 'Apple EFI IM4P Splitter v2.1'
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
im4p = re.compile(br'\x16\x04\x49\x4D\x34\x50\x16\x04') # Apple IM4P
|
||||
ifd = re.compile(br'\x5A\xA5\xF0\x0F.{172}\xFF{16}', re.DOTALL) # Intel Flash Descriptor (Z¥π. + [0xAC] + 0xFF * 16)
|
||||
|
||||
# Flash Descriptor Component Sizes
|
||||
comp_dict = {
|
||||
0 : 0x80000, # 512 KB
|
||||
1 : 0x100000, # 1 MB
|
||||
2 : 0x200000, # 2 MB
|
||||
3 : 0x400000, # 4 MB
|
||||
4 : 0x800000, # 8 MB
|
||||
5 : 0x1000000, # 16 MB
|
||||
6 : 0x2000000, # 32 MB
|
||||
7 : 0x4000000, # 64 MB
|
||||
8 : 0x8000000, # 128 MB
|
||||
9 : 0x10000000, # 256 MB
|
||||
}
|
||||
|
||||
# Get input catalog file paths
|
||||
if len(sys.argv) >= 3 and sys.argv[1] == '-skip' :
|
||||
# Called via Apple_EFI_Package
|
||||
apple_im4p = sys.argv[2:]
|
||||
skip_pause = True
|
||||
skip_space = ' '
|
||||
print('\n%s%s' % (skip_space, title)) # Print Title
|
||||
elif len(sys.argv) >= 2 :
|
||||
# Drag & Drop or CLI
|
||||
apple_im4p = sys.argv[1:]
|
||||
skip_pause = False
|
||||
skip_space = ''
|
||||
print('\n%s%s' % (skip_space, title)) # Print Title
|
||||
else :
|
||||
# Folder path
|
||||
apple_im4p = []
|
||||
skip_pause = False
|
||||
skip_space = ''
|
||||
print('\n%s%s' % (skip_space, title)) # Print Title
|
||||
in_path = input('\nEnter the full folder path: ')
|
||||
print('\nWorking...')
|
||||
for root, dirs, files in os.walk(in_path):
|
||||
for name in files :
|
||||
apple_im4p.append(os.path.join(root, name))
|
||||
|
||||
for input_file in apple_im4p :
|
||||
file_path = os.path.abspath(input_file)
|
||||
file_name = os.path.basename(input_file)
|
||||
file_dir = os.path.dirname(file_path)
|
||||
file_ext = os.path.splitext(file_path)[1]
|
||||
|
||||
print('\n%sFile: %s' % (skip_space, file_name)) # Print File Name
|
||||
|
||||
# Must be IM4P file because its size is 0x0 dependent
|
||||
if file_ext not in ('.im4p','.IM4P') :
|
||||
print('\n%s Error: Could not find IM4P file extension at %s!' % (skip_space, file_name))
|
||||
continue # Critical error
|
||||
|
||||
with open(input_file, 'rb') as in_file : buffer = in_file.read()
|
||||
|
||||
is_im4p = im4p.search(buffer) # Detect IM4P pattern
|
||||
|
||||
if not is_im4p :
|
||||
print('\n%s Error: Could not find IM4P pattern at %s!' % (skip_space, file_name))
|
||||
continue # Critical error
|
||||
|
||||
im4p_size = int.from_bytes(buffer[2:is_im4p.start()], 'big') # Variable, from 0x2 - IM4P
|
||||
im4p_type = buffer[is_im4p.end():is_im4p.end() + 0x4].decode('utf-8') # mefi
|
||||
|
||||
if im4p_type != 'mefi' :
|
||||
print('\n%s Error: Could not find "mefi" IM4P Type at %s!' % (skip_space, file_name))
|
||||
continue # Critical error
|
||||
|
||||
# After IM4P mefi (0x15), multi EFI payloads have _MEFIBIN (0x100) which is difficult to reverse without varying samples.
|
||||
# However, _MEFIBIN is not required for splitting SPI/EFI images because Intel Flash Descriptor Component Density exists.
|
||||
mefi_data_start = is_im4p.start() + buffer[is_im4p.start() - 0x1] # IM4P mefi payload start offset
|
||||
mefi_data_size = int.from_bytes(buffer[is_im4p.end() + 0x9:is_im4p.end() + 0xD], 'big') # IM4P mefi payload size
|
||||
mefibin_exist = buffer[mefi_data_start:mefi_data_start + 0x8] == b'_MEFIBIN' # Check if mefi is followed by _MEFIBIN
|
||||
efi_data_start = mefi_data_start + 0x100 if mefibin_exist else mefi_data_start # Actual multi EFI payloads start after _MEFIBIN
|
||||
efi_data_size = mefi_data_size - 0x100 if mefibin_exist else mefi_data_size # Actual multi EFI payloads size without _MEFIBIN
|
||||
buffer = buffer[efi_data_start:efi_data_start + efi_data_size] # Adjust input file buffer to actual multi EFI payloads data
|
||||
|
||||
fd_matches = list(ifd.finditer(buffer)) # Find Intel Flash Descriptor pattern matches
|
||||
fd_count = len(fd_matches) # Count found Intel Flash Descriptor pattern matches
|
||||
fd_final = [] # Initialize final Intel Flash Descriptor info storage
|
||||
|
||||
# Parse Intel Flash Descriptor pattern matches
|
||||
for fd_idx in range(fd_count) :
|
||||
fd = fd_matches[fd_idx] # Get Intel Flash Descriptor match object
|
||||
|
||||
fd_flmap0_fcba = buffer[fd.start() + 0x4] * 0x10 # Component Base Address from FD start (ICH8-ICH10 = 1, IBX = 2, CPT+ = 3)
|
||||
|
||||
# I/O Controller Hub (ICH)
|
||||
if fd_flmap0_fcba == 0x10 :
|
||||
start_substruct = 0x0 # At ICH, Flash Descriptor starts at 0x0
|
||||
end_substruct = 0xBC # 0xBC for [0xAC] + 0xFF * 16 sanity check
|
||||
# Platform Controller Hub (PCH)
|
||||
else :
|
||||
start_substruct = 0x10 # At PCH, Flash Descriptor starts at 0x10
|
||||
end_substruct = 0xBC # 0xBC for [0xAC] + 0xFF * 16 sanity check
|
||||
|
||||
fd_match_start = fd.start() - start_substruct # Actual Flash Descriptor Start Offset
|
||||
fd_match_end = fd.end() - end_substruct # Actual Flash Descriptor End Offset
|
||||
|
||||
# Calculate Intel Flash Descriptor Flash Component Total Size
|
||||
fd_flmap0_nc = ((int.from_bytes(buffer[fd_match_end:fd_match_end + 0x4], 'little') >> 8) & 3) + 1 # Component Count (00 = 1, 01 = 2)
|
||||
fd_flmap1_isl = buffer[fd_match_end + 0x7] # PCH/ICH Strap Length (ME 2-8 & TXE 0-2 & SPS 1-2 <= 0x12, ME 9+ & TXE 3+ & SPS 3+ >= 0x13)
|
||||
fd_comp_den = buffer[fd_match_start + fd_flmap0_fcba] # Component Density Byte (ME 2-8 & TXE 0-2 & SPS 1-2 = 0:5, ME 9+ & TXE 3+ & SPS 3+ = 0:7)
|
||||
fd_comp_1_bitwise = 0xF if fd_flmap1_isl >= 0x13 else 0x7 # Component 1 Density Bits (ME 2-8 & TXE 0-2 & SPS 1-2 = 3, ME 9+ & TXE 3+ & SPS 3+ = 4)
|
||||
fd_comp_2_bitwise = 0x4 if fd_flmap1_isl >= 0x13 else 0x3 # Component 2 Density Bits (ME 2-8 & TXE 0-2 & SPS 1-2 = 3, ME 9+ & TXE 3+ & SPS 3+ = 4)
|
||||
fd_comp_all_size = comp_dict[fd_comp_den & fd_comp_1_bitwise] # Component 1 Density (FCBA > C0DEN)
|
||||
if fd_flmap0_nc == 2 : fd_comp_all_size += comp_dict[fd_comp_den >> fd_comp_2_bitwise] # Component 2 Density (FCBA > C1DEN)
|
||||
|
||||
fd_final.append((fd_match_start,fd_comp_all_size)) # Store Intel Flash Descriptor final info
|
||||
|
||||
# Split IM4P via the final Intel Flash Descriptor matches
|
||||
for fd_idx in range(fd_count) :
|
||||
fd = fd_final[fd_idx] # Get Intel Flash Descriptor final info [FD Start, FD Component(s) Size]
|
||||
|
||||
# The Intel Flash Descriptor Flash Component Total Size should be enough to split the IM4P.
|
||||
# However, for sanity, its Size can be compared to the Size different of Next - Current FD.
|
||||
fd_diff_size = len(buffer) - fd[0] if fd_idx == fd_count - 1 else fd_final[fd_idx + 1][0] - fd[0] # Last FD ends at mefi payload end
|
||||
if fd[1] != fd_diff_size : # FD Total Component Size should be equal to Next-Current FD Difference Size
|
||||
print('\n%s Error: Intel FD %d/%d Component Size 0x%X != Next-Current FD Size Difference 0x%X!'
|
||||
% (skip_space, fd_idx + 1, fd_count, fd[1], fd_diff_size))
|
||||
|
||||
file_data = buffer[fd[0]:fd[0] + max(fd[1], fd_diff_size)] # Split EFI image file data (use largest FD Size just in case)
|
||||
|
||||
file_path_new = os.path.join(file_dir, '%s_%d.fd' % (file_name[:-5], fd_idx + 1)) # Split EFI image file path
|
||||
|
||||
with open(file_path_new, 'wb') as spi_image : spi_image.write(file_data) # Store split EFI image file
|
||||
|
||||
print('\n%s Split IM4P into %d EFI image(s)!' % (skip_space, fd_count))
|
||||
|
||||
if not skip_pause : input('\nDone!')
|
|
@ -1,101 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Apple EFI Package
|
||||
Apple EFI Package Extractor
|
||||
Copyright (C) 2019 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
print('Apple EFI Package Extractor v1.1')
|
||||
|
||||
import os
|
||||
import sys
|
||||
import zlib
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
if len(sys.argv) >= 2 :
|
||||
pkg = sys.argv[1:]
|
||||
else :
|
||||
pkg = []
|
||||
in_path = input('\nEnter the full folder path: ')
|
||||
print('\nWorking...')
|
||||
for root, dirs, files in os.walk(in_path):
|
||||
for name in files :
|
||||
pkg.append(os.path.join(root, name))
|
||||
|
||||
anytoiso_path = 'C:\\Program Files (x86)\\AnyToISO\\anytoiso.exe'
|
||||
|
||||
final_path = os.path.join(os.getcwd(), 'AppleEFI')
|
||||
if os.path.exists(final_path) : shutil.rmtree(final_path)
|
||||
|
||||
for input_file in pkg :
|
||||
file_path = os.path.abspath(input_file)
|
||||
file_name = os.path.basename(input_file)
|
||||
file_dir = os.path.dirname(file_path)
|
||||
file_ext = os.path.splitext(file_path)[1]
|
||||
|
||||
print('\nFile: %s\n' % file_name)
|
||||
|
||||
with open(input_file, 'rb') as in_buff : file_adler = zlib.adler32(in_buff.read()) & 0xFFFFFFFF
|
||||
|
||||
pkg_payload = os.path.join(final_path, '%s_%0.8X' % (file_name, file_adler))
|
||||
pkg_temp = os.path.join(final_path, '__TEMP_%s_%0.8X' % (file_name, file_adler))
|
||||
os.makedirs(pkg_temp)
|
||||
|
||||
subprocess.run([anytoiso_path, '/extract', file_path, pkg_temp], check = True, stdout=subprocess.DEVNULL)
|
||||
|
||||
if os.path.isfile(os.path.join(pkg_temp, 'Scripts')) :
|
||||
scripts_init = os.path.join(pkg_temp, 'Scripts')
|
||||
scripts_cpgz = os.path.join(pkg_temp, 'Scripts.cpgz')
|
||||
scripts_extr = os.path.join(pkg_temp, 'Scripts', '')
|
||||
efi_path = os.path.join(scripts_extr, 'Tools', 'EFIPayloads', '')
|
||||
|
||||
os.replace(scripts_init, scripts_cpgz)
|
||||
|
||||
subprocess.run([anytoiso_path, '/extract', scripts_cpgz, scripts_extr], check = True, stdout=subprocess.DEVNULL)
|
||||
|
||||
shutil.copytree(efi_path, pkg_payload)
|
||||
|
||||
elif os.path.isfile(os.path.join(pkg_temp, 'Payload')) :
|
||||
payload_init = os.path.join(pkg_temp, 'Payload')
|
||||
payload_pbzx = os.path.join(pkg_temp, 'Payload.pbzx')
|
||||
payload_extr = os.path.join(pkg_temp, 'Payload', '')
|
||||
zip_path = os.path.join(payload_extr, 'usr', 'standalone', 'firmware', 'bridgeOSCustomer.bundle', 'Contents', 'Resources', 'UpdateBundle')
|
||||
efi_path = os.path.join(zip_path, 'boot', 'Firmware', 'MacEFI', '')
|
||||
|
||||
os.replace(payload_init, payload_pbzx)
|
||||
|
||||
subprocess.run([anytoiso_path, '/extract', payload_pbzx, payload_extr], check = True, stdout=subprocess.DEVNULL)
|
||||
|
||||
subprocess.run([anytoiso_path, '/extract', zip_path + '.zip', zip_path], check = True, stdout=subprocess.DEVNULL)
|
||||
|
||||
if os.path.exists(efi_path) : shutil.copytree(efi_path, pkg_payload)
|
||||
|
||||
shutil.rmtree(pkg_temp)
|
||||
|
||||
im4p_files = []
|
||||
for root, dirs, files in os.walk(pkg_payload):
|
||||
for name in files :
|
||||
if name.endswith('.im4p') :
|
||||
im4p_files.append(os.path.join(root, name))
|
||||
|
||||
if im4p_files : subprocess.run(['python', 'Apple_EFI_Split.py', '-skip', *im4p_files], check = True, stdout=subprocess.DEVNULL)
|
||||
for im4p in im4p_files : os.remove(im4p)
|
||||
|
||||
final_files = []
|
||||
for root, dirs, files in os.walk(pkg_payload):
|
||||
for name in files :
|
||||
final_files.append(os.path.join(root, name))
|
||||
|
||||
if final_files : subprocess.run(['python', 'Apple_EFI_Rename.py', '-skip', *final_files], check = True, stdout=subprocess.DEVNULL)
|
||||
|
||||
for root, dirs, files in os.walk(pkg_payload):
|
||||
for name in files :
|
||||
if not os.path.isfile(os.path.join(final_path, name)) :
|
||||
shutil.copy2(os.path.join(root, name), os.path.join(final_path, name))
|
||||
|
||||
shutil.rmtree(pkg_payload)
|
||||
|
||||
else :
|
||||
input('\nDone!')
|
|
@ -1,4 +0,0 @@
|
|||
2021-01-01 00:00:00
|
||||
|
||||
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
#coding=utf-8
|
||||
|
||||
"""
|
||||
Apple EFI Grab
|
||||
Apple EFI Package Grabber
|
||||
Copyright (C) 2018-2021 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
title = 'Apple EFI Package Grabber v2.0'
|
||||
|
||||
print('\n' + title)
|
||||
|
||||
import sys
|
||||
|
||||
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 traceback
|
||||
|
||||
def show_exception_and_exit(exc_type, exc_value, tb):
|
||||
if exc_type is KeyboardInterrupt:
|
||||
print('\nNote: Keyboard Interrupt!')
|
||||
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)
|
||||
|
||||
sys.excepthook = show_exception_and_exit
|
||||
|
||||
import datetime
|
||||
import urllib.request
|
||||
from multiprocessing.pool import ThreadPool
|
||||
|
||||
def fetch_cat_info(name):
|
||||
url = cat_url[:-len('others/')] + name if name in ['index.sucatalog','index-1.sucatalog'] else cat_url + name
|
||||
with urllib.request.urlopen(urllib.request.Request(url, method='HEAD')) as head : mod = head.headers['last-modified']
|
||||
|
||||
return name, url, mod
|
||||
|
||||
def fetch_cat_links(cat_file):
|
||||
cat_links = []
|
||||
|
||||
with urllib.request.urlopen(cat_file[1]) as link: fdata = link.readlines()
|
||||
|
||||
cat_lines = [l.decode('utf-8').strip('\n') for l in fdata]
|
||||
|
||||
for line in cat_lines:
|
||||
if ('.pkg' in line or '.tar' in line) and ('FirmwareUpd' in line or '/BridgeOSUpdateCustomer' in line or 'EFIUpd' in line) \
|
||||
and 'Bluetooth' not in line and 'DPVGA' not in line and 'Thunderbolt' not in line and 'PMG5' not in line and 'HardDrive' not in line:
|
||||
down_link = line[line.find('http'):(line.find('.pkg') if '.pkg' in line else line.find('.tar')) + 4]
|
||||
down_link = down_link.replace('http:','https:')
|
||||
cat_links.append(down_link)
|
||||
|
||||
return cat_links
|
||||
|
||||
dat_db = 'Apple_EFI_Grab.dat'
|
||||
cat_url = 'https://swscan.apple.com/content/catalogs/others/'
|
||||
apple_cat = []
|
||||
down_links = []
|
||||
svr_date = None
|
||||
thread_num = 2
|
||||
|
||||
with open(dat_db, 'r', encoding='utf-8') as dat: db_lines = dat.readlines()
|
||||
db_lines = [line.strip('\n') for line in db_lines]
|
||||
|
||||
db_date = datetime.datetime.strptime(db_lines[0], '%Y-%m-%d %H:%M:%S')
|
||||
db_links = set([line for line in db_lines if line.startswith('https')])
|
||||
db_sucat = [line for line in db_lines if line.startswith('index')]
|
||||
|
||||
print('\nGetting Catalog Listing...')
|
||||
|
||||
if not db_sucat:
|
||||
input('\nError: Failed to retrieve Catalogs from DB!\n\nDone!')
|
||||
sys.exit(1)
|
||||
|
||||
apple_mod = ThreadPool(thread_num).imap_unordered(fetch_cat_info, db_sucat)
|
||||
|
||||
for name, url, mod in apple_mod:
|
||||
dt = datetime.datetime.strptime(mod, '%a, %d %b %Y %H:%M:%S %Z')
|
||||
if not svr_date or dt > svr_date : svr_date = dt
|
||||
|
||||
apple_cat.append((name, url, dt))
|
||||
|
||||
if not svr_date:
|
||||
input('\nError: Failed to retrieve Current Catalog Datetime!\n\nDone!')
|
||||
sys.exit(1)
|
||||
|
||||
print('\n Previous Catalog Datetime :', db_date)
|
||||
print(' Current Catalog Datetime :', svr_date)
|
||||
|
||||
if svr_date <= db_date:
|
||||
input('\nNothing new since %s!\n\nDone!' % db_date)
|
||||
sys.exit()
|
||||
|
||||
print('\nGetting Catalog Links...')
|
||||
|
||||
down_links = ThreadPool(thread_num).imap_unordered(fetch_cat_links, apple_cat)
|
||||
down_links = [item for sublist in down_links for item in sublist]
|
||||
|
||||
if not down_links:
|
||||
input('\nError: Failed to retrieve Catalog Links!\n\nDone!')
|
||||
sys.exit(1)
|
||||
|
||||
new_links = sorted(list(dict.fromkeys([link for link in down_links if link not in db_links])))
|
||||
|
||||
if new_links:
|
||||
print('\nFound %d new link(s) between %s and %s!' % (len(new_links), db_date, svr_date))
|
||||
|
||||
cur_date = datetime.datetime.utcnow().isoformat(timespec='seconds').replace('-','').replace('T','').replace(':','') # Local UTC Unix
|
||||
|
||||
with open('Apple_%s.txt' % cur_date, 'w', encoding='utf-8') as lout: lout.write('\n'.join(map(str, new_links)))
|
||||
else:
|
||||
print('\nThere are no new links between %s and %s!' % (db_date, svr_date))
|
||||
|
||||
new_db_sucat = '\n'.join(map(str, db_sucat))
|
||||
|
||||
new_db_links = '\n'.join(map(str, sorted(list(dict.fromkeys(down_links)))))
|
||||
|
||||
new_db_lines = '%s\n\n%s\n\n%s' % (svr_date, new_db_sucat, new_db_links)
|
||||
|
||||
with open(dat_db, 'w', encoding='utf-8') as dbout: dbout.write(new_db_lines)
|
||||
|
||||
input('\nDone!')
|
|
@ -1,65 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Award BIOS Extract
|
||||
Award BIOS Module Extractor
|
||||
Copyright (C) 2018-2019 Plato Mavropoulos
|
||||
http://www.onicos.com/staff/iz/formats/lzh.html
|
||||
https://ist.uwaterloo.ca/~schepers/formats/LHA.TXT
|
||||
https://sites.google.com/site/pinczakko/pinczakko-s-guide-to-award-bios-reverse-engineering
|
||||
"""
|
||||
|
||||
print('Award BIOS Module Extractor v1.2\n')
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
if len(sys.argv) >= 2 :
|
||||
# Drag & Drop or CLI
|
||||
awd_images = sys.argv[1:]
|
||||
else :
|
||||
# Folder path
|
||||
awd_images = []
|
||||
in_path = input('\nEnter the full folder path: ')
|
||||
print('\nWorking...')
|
||||
for root, dirs, files in os.walk(in_path):
|
||||
for name in files :
|
||||
awd_images.append(os.path.join(root, name))
|
||||
|
||||
pat_lzh = re.compile(br'\x2D\x6C((\x68(([\x30-\x37])|(\x64)))|(\x7A([\x34\x73])))\x2D') # All 11 LZH Method IDs (Award probably used LH0 and LH5 only)
|
||||
|
||||
# Create output folder
|
||||
extr_path = os.path.join(os.getcwd(), 'AWD_Extracted')
|
||||
if not os.path.exists(extr_path) : os.makedirs(extr_path)
|
||||
|
||||
for in_file in awd_images :
|
||||
file_path = os.path.abspath(in_file)
|
||||
file_name = os.path.basename(in_file)
|
||||
file_dir = os.path.dirname(file_path)
|
||||
file_ext = os.path.splitext(file_path)[1]
|
||||
match_lzh_list = []
|
||||
|
||||
print('\nFile: %s%s' % (file_name, file_ext))
|
||||
|
||||
with open(in_file, 'rb') as awd_img : buffer = awd_img.read()
|
||||
|
||||
match_lzh_list += pat_lzh.finditer(buffer) # Detect LZH patterns
|
||||
|
||||
for match_lzh in match_lzh_list :
|
||||
hdr_size = buffer[match_lzh.start() - 0x2] # From LZH Tag (0x2+)
|
||||
comp_size = int.from_bytes(buffer[match_lzh.end():match_lzh.end() + 0x4], 'little') # From LZH Header end
|
||||
mod_data = buffer[match_lzh.start() - 0x2:match_lzh.start() + hdr_size + comp_size]
|
||||
|
||||
with open('mod_temp.bin', 'wb') as lzh_img : lzh_img.write(mod_data)
|
||||
|
||||
try :
|
||||
decomp = subprocess.run(['7z', 'x', '-aou', '-bso0', '-bse0', '-bsp0', '-o%s' % os.path.join(extr_path, file_name), 'mod_temp.bin']) # 7-Zip
|
||||
except :
|
||||
print('Error: Could not decompress LZH image at %s!' % file_name)
|
||||
print(' Make sure that "7z" executable exists!\n')
|
||||
|
||||
os.remove('mod_temp.bin') # Remove temporary LZH image
|
||||
|
||||
input('\nDone!')
|
File diff suppressed because it is too large
Load diff
|
@ -1,82 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Fujitsu SFX Extractor
|
||||
Fujitsu SFX BIOS Extractor
|
||||
Copyright (C) 2019-2021 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
print('Fujitsu SFX BIOS Extractor v2.1')
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
if len(sys.argv) >= 2 :
|
||||
# Drag & Drop or CLI
|
||||
fjsfx_exec = sys.argv[1:]
|
||||
else :
|
||||
# Folder path
|
||||
fjsfx_exec = []
|
||||
in_path = input('\nEnter the full folder path: ')
|
||||
print('\nWorking...')
|
||||
for root, dirs, files in os.walk(in_path):
|
||||
for name in files :
|
||||
fjsfx_exec.append(os.path.join(root, name))
|
||||
|
||||
# "FjSfxBinay" + Microsoft CAB Header XOR 0xFF (Tag[4] + Res[4] + Size[4] + Res[4] + Offset[4] + Res[4] + Ver[2]) pattern
|
||||
mscf_pattern = re.compile(br'\x46\x6A\x53\x66\x78\x42\x69\x6E\x61\x79\xB2\xAC\xBC\xB9\xFF{4}.{4}\xFF{4}.{4}\xFF{4}\xFC\xFE', re.DOTALL)
|
||||
|
||||
for input_file in fjsfx_exec :
|
||||
file_path = os.path.abspath(input_file)
|
||||
file_dir = os.path.dirname(file_path)
|
||||
file_name = os.path.basename(file_path)
|
||||
|
||||
print('\nFile: ' + file_name)
|
||||
|
||||
# Open Fujitsu SFX Binary Packager executable as mutable bytearray
|
||||
with open(input_file, 'rb') as in_file : FjSfx = bytearray(in_file.read())
|
||||
|
||||
match_mscf = mscf_pattern.search(FjSfx) # Search for Fujitsu Microsoft CAB Header XOR 0xFF pattern
|
||||
|
||||
# Check if Microsoft CAB Header XOR 0xFF pattern exists
|
||||
if match_mscf :
|
||||
print('\n Detected Obfuscation!')
|
||||
|
||||
mscf_start = match_mscf.start() + 0xA # Microsoft CAB Header XOR 0xFF starts after "FjSfxBinay" signature
|
||||
|
||||
# Determine the Microsoft CAB image Size
|
||||
cab_size = int.from_bytes(FjSfx[mscf_start + 0x8:mscf_start + 0xC], 'little') # Get LE XOR-ed CAB Size
|
||||
xor_size = int.from_bytes(b'\xFF' * 0x4, 'little') # Create CAB Size XOR value
|
||||
cab_size = cab_size ^ xor_size # Perform XOR 0xFF and get actual CAB Size
|
||||
|
||||
print('\n Removing Obfuscation...')
|
||||
|
||||
# Determine the Microsoft CAB image Data
|
||||
cab_data = int.from_bytes(FjSfx[mscf_start:mscf_start + cab_size], 'big') # Get BE XOR-ed CAB Data
|
||||
xor_data = int.from_bytes(b'\xFF' * cab_size, 'big') # Create CAB Data XOR value
|
||||
cab_data = (cab_data ^ xor_data).to_bytes(cab_size, 'big') # Perform XOR 0xFF and get actual CAB Data
|
||||
|
||||
print('\n Extracting...')
|
||||
|
||||
with open('fjsfx_temp.cab', 'wb') as cab_file : cab_file.write(cab_data) # Create temporary CAB image
|
||||
|
||||
extr_path = os.path.join(file_dir, file_name[:-4], '') # Create CAB image extraction path
|
||||
|
||||
try :
|
||||
decomp = subprocess.run(['7z', 'x', '-aou', '-bso0', '-bse0', '-bsp0', '-o' + extr_path, 'fjsfx_temp.cab']) # 7-Zip
|
||||
|
||||
print('\n Extracted!')
|
||||
except :
|
||||
print('\n Error: Could not decompress Microsoft CAB image!')
|
||||
print(' Make sure that "7z" executable exists!')
|
||||
|
||||
os.remove('fjsfx_temp.cab') # Remove temporary CAB image
|
||||
|
||||
else :
|
||||
print('\n Error: This is not a Fujitsu SFX BIOS image!')
|
||||
continue # Next input file
|
||||
|
||||
else :
|
||||
input('\nDone!')
|
|
@ -1,108 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
#coding=utf-8
|
||||
|
||||
"""
|
||||
Fujitsu UPC Extract
|
||||
Fujitsu UPC BIOS Extractor
|
||||
Copyright (C) 2021 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
title = 'Fujitsu UPC 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 ctypes
|
||||
import argparse
|
||||
import traceback
|
||||
import subprocess
|
||||
|
||||
# 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
|
||||
upc_parser = argparse.ArgumentParser()
|
||||
upc_parser.add_argument('upc', type=argparse.FileType('r'), nargs='*')
|
||||
upc_parser.add_argument('-p', '--path', help='parse files within given folder', type=str)
|
||||
upc_params = upc_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(upc_params.path) :
|
||||
upc_exec = get_files(upc_params.path) # CLI with --path
|
||||
else :
|
||||
upc_exec = []
|
||||
for executable in upc_params.upc :
|
||||
upc_exec.append(executable.name) # Drag & Drop
|
||||
else :
|
||||
in_path = input('\nEnter the full folder path: ')
|
||||
upc_exec = get_files(in_path) # Direct Run
|
||||
|
||||
# Process each input Fujitsu UPC BIOS image
|
||||
for input_file in upc_exec :
|
||||
input_name,input_extension = os.path.splitext(os.path.basename(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 : upc_data = in_file.read()
|
||||
|
||||
if input_extension.upper() != '.UPC' or int.from_bytes(upc_data[0x0:0x4], 'little') + 0x8 != len(upc_data) :
|
||||
print('\n Error: This is not a Fujitsu UPC BIOS image!')
|
||||
continue # Next input file
|
||||
|
||||
output_file = input_file[:-4] + '.bin' # Decompressed filename
|
||||
|
||||
# EFI/Tiano Decompression
|
||||
try :
|
||||
subprocess.run(['TianoCompress', '-d', input_file, '-o', output_file, '--uefi', '-q'], check = True, stdout = subprocess.DEVNULL)
|
||||
|
||||
if os.path.getsize(output_file) != int.from_bytes(upc_data[0x4:0x8], 'little') : raise Exception('EFI_DECOMP_ERROR')
|
||||
except :
|
||||
print('\n Error: Could not extract input file via TianoCompress!')
|
||||
input(' Make sure that "TianoCompress" executable exists!')
|
||||
|
||||
print('\n Extracted Fujitsu UPC BIOS image!')
|
||||
|
||||
input('\nDone!')
|
||||
|
||||
sys.exit(0)
|
|
@ -1,270 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
#coding=utf-8
|
||||
|
||||
"""
|
||||
Insyde iFlash Extract
|
||||
Insyde iFlash Image Extractor
|
||||
Copyright (C) 2022 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
title = 'Insyde iFlash Image Extractor v1.0'
|
||||
|
||||
import sys
|
||||
|
||||
# Detect Python version
|
||||
sys_py = sys.version_info
|
||||
|
||||
# Check Python version
|
||||
if sys_py < (3,7):
|
||||
sys.stdout.write('%s\n\nError: Python >= 3.7 required, not %d.%d!\n' % (title, sys_py[0], sys_py[1]))
|
||||
|
||||
if '--auto-exit' not in sys.argv and '-e' not in sys.argv:
|
||||
(raw_input if sys_py[0] <= 2 else input)('\nPress enter to exit') # pylint: disable=E0602
|
||||
|
||||
sys.exit(1)
|
||||
|
||||
# Detect OS platform
|
||||
sys_os = sys.platform
|
||||
|
||||
# Check OS platform
|
||||
if sys_os == 'win32':
|
||||
sys.stdout.reconfigure(encoding='utf-8') # Fix Windows Unicode console redirection
|
||||
elif sys_os.startswith('linux') or sys_os == 'darwin' or sys_os.find('bsd') != -1:
|
||||
pass # Supported/Tested
|
||||
else:
|
||||
print('%s\n\nError: Unsupported platform "%s"!\n' % (title, sys_os))
|
||||
|
||||
if '--auto-exit' not in sys.argv and '-e' not in sys.argv: input('Press enter to exit')
|
||||
|
||||
sys.exit(1)
|
||||
|
||||
# Python imports
|
||||
import os
|
||||
import re
|
||||
import ctypes
|
||||
import inspect
|
||||
import pathlib
|
||||
import argparse
|
||||
import traceback
|
||||
|
||||
# Set ctypes Structure types
|
||||
char = ctypes.c_char
|
||||
uint32_t = ctypes.c_uint
|
||||
|
||||
class IflashHeader(ctypes.LittleEndianStructure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Signature', char*9), # 0x00 $_IFLASH_
|
||||
('ImageTag', char*7), # 0x08
|
||||
('TotalSize', uint32_t), # 0x10 from header end
|
||||
('ImageSize', uint32_t), # 0x14 from header end
|
||||
# 0x18
|
||||
]
|
||||
|
||||
def ifl_print(self, padd):
|
||||
p = ' ' * (padd - 1)
|
||||
|
||||
print(p, 'Signature : %s' % self.Signature.decode('utf-8','ignore'))
|
||||
print(p, 'Image Name: %s' % self.ImageTag.decode('utf-8','ignore'))
|
||||
print(p, 'Total Size: 0x%X' % self.TotalSize)
|
||||
print(p, 'Image Size: 0x%X' % self.ImageSize)
|
||||
|
||||
class InsydeIflash:
|
||||
def __init__(self, in_data, out_path, in_padd, in_verbose):
|
||||
self.fw_data = in_data
|
||||
self.ex_path = out_path
|
||||
self.padding = in_padd
|
||||
self.verbose = in_verbose
|
||||
|
||||
self.hdr_len = ctypes.sizeof(IflashHeader)
|
||||
|
||||
self.mod_names = {
|
||||
'DRV_IMG':['isflash','efi'],
|
||||
'INI_IMG':['platform','ini'],
|
||||
'BIOSIMG':['BIOS','bin'],
|
||||
'ME_IMG_':['ME','bin'],
|
||||
'EC_IMG_':['EC','bin'],
|
||||
'OEM_ID_':['OEM_ID','bin'],
|
||||
'BIOSCER':['Certificate','bin'],
|
||||
'BIOSCR2':['Certificate_2','bin'],
|
||||
}
|
||||
|
||||
def iflash_parse(self):
|
||||
all_ins_ifl = pat_ins_ifl.finditer(self.fw_data)
|
||||
|
||||
if not all_ins_ifl: return 1
|
||||
|
||||
if not os.path.isdir(self.ex_path): os.mkdir(self.ex_path)
|
||||
|
||||
for ins_ifl in all_ins_ifl:
|
||||
ifl_off = ins_ifl.start()
|
||||
|
||||
ifl_hdr = get_struct(self.fw_data, ifl_off, IflashHeader)
|
||||
|
||||
if self.verbose:
|
||||
print('\n%sInsyde iFlash Module @ 0x%0.8X\n' % (' ' * self.padding, ifl_off))
|
||||
|
||||
ifl_hdr.ifl_print(self.padding + 4)
|
||||
|
||||
mod_bgn = ifl_off + self.hdr_len
|
||||
mod_end = mod_bgn + ifl_hdr.ImageSize
|
||||
mod_bin = self.fw_data[mod_bgn:mod_end]
|
||||
|
||||
if not mod_bin: continue # Empty/Missing Module
|
||||
|
||||
mod_tag = ifl_hdr.ImageTag.decode('utf-8','ignore')
|
||||
out_tag = self.mod_names[mod_tag][0] if mod_tag in self.mod_names else mod_tag
|
||||
out_ext = self.mod_names[mod_tag][1] if mod_tag in self.mod_names else 'bin'
|
||||
|
||||
out_name = get_safe_name('%s [0x%0.8X-0x%0.8X].%s' % (out_tag, mod_bgn, mod_end, out_ext))
|
||||
out_path = os.path.join(self.ex_path, out_name)
|
||||
|
||||
with open(out_path, 'wb') as out: out.write(mod_bin)
|
||||
|
||||
print('\n%sExtracted' % (' ' * (self.padding + 8 if self.verbose else self.padding)), out_name)
|
||||
|
||||
return 0
|
||||
|
||||
# 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)
|
||||
|
||||
ctypes.memmove(ctypes.addressof(structure), struct_data, fit_len)
|
||||
|
||||
return structure
|
||||
|
||||
# Get absolute file path (argparse object)
|
||||
def get_absolute_path(argparse_path):
|
||||
if not argparse_path:
|
||||
absolute_path = get_script_dir() # Use input file directory if no user path is specified
|
||||
else:
|
||||
# Check if user specified path is absolute, otherwise convert it to input file relative
|
||||
if pathlib.Path(argparse_path).is_absolute(): absolute_path = argparse_path
|
||||
else: absolute_path = os.path.join(get_script_dir(), argparse_path)
|
||||
|
||||
return absolute_path
|
||||
|
||||
# Get list of files from absolute path
|
||||
def get_path_files(abs_path):
|
||||
file_list = [] # Initialize list of files
|
||||
|
||||
# Traverse input absolute path
|
||||
for root,_,files in os.walk(abs_path):
|
||||
file_list = [os.path.join(root, name) for name in files]
|
||||
|
||||
return file_list
|
||||
|
||||
# Fix illegal/reserved Windows characters
|
||||
def get_safe_name(file_name):
|
||||
raw_name = repr(file_name).strip("'")
|
||||
|
||||
return re.sub(r'[\\/*?:"<>|]', '_', raw_name)
|
||||
|
||||
# Get python script working directory
|
||||
# https://stackoverflow.com/a/22881871 by jfs
|
||||
def get_script_dir(follow_symlinks=True):
|
||||
if getattr(sys, 'frozen', False):
|
||||
path = os.path.abspath(sys.executable)
|
||||
else:
|
||||
path = inspect.getabsfile(get_script_dir)
|
||||
if follow_symlinks:
|
||||
path = os.path.realpath(path)
|
||||
|
||||
return os.path.dirname(path)
|
||||
|
||||
# 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)
|
||||
if not bool(args.auto_exit): input('\nPress enter to exit')
|
||||
|
||||
sys.exit(1) # Crash exceptions are critical
|
||||
|
||||
# Insyde iFlash Section Signature
|
||||
pat_ins_ifl = re.compile(br'\$_IFLASH_')
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Show script title
|
||||
print('\n' + title)
|
||||
|
||||
# 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
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('images', type=argparse.FileType('r'), nargs='*')
|
||||
parser.add_argument('-v', '--verbose', help='show iFlash structure information', action='store_true')
|
||||
parser.add_argument('-e', '--auto-exit', help='skip press enter to exit prompts', action='store_true')
|
||||
parser.add_argument('-o', '--output-dir', help='extract in given output directory')
|
||||
parser.add_argument('-i', '--input-dir', help='extract from given input directory')
|
||||
args = parser.parse_args()
|
||||
|
||||
# Set pause-able Python exception handler (must be after args)
|
||||
sys.excepthook = show_exception_and_exit
|
||||
|
||||
# Initialize Dell PFS input file list
|
||||
iflash_input_images = []
|
||||
|
||||
# Process input files
|
||||
if len(sys.argv) >= 2:
|
||||
# Drag & Drop or CLI
|
||||
if args.input_dir:
|
||||
input_path_user = get_absolute_path(args.input_dir)
|
||||
iflash_input_images = get_path_files(input_path_user)
|
||||
else:
|
||||
iflash_input_images = [image.name for image in args.images]
|
||||
|
||||
output_path_user = get_absolute_path(args.output_dir or args.input_dir)
|
||||
else:
|
||||
# Script w/o parameters
|
||||
input_path_user = get_absolute_path(input('\nEnter input directory path: '))
|
||||
iflash_input_images = get_path_files(input_path_user)
|
||||
|
||||
output_path_user = get_absolute_path(input('\nEnter output directory path: '))
|
||||
|
||||
# Initialize global variables
|
||||
exit_code = len(iflash_input_images) # Initialize exit code with input file count
|
||||
is_verbose = bool(args.verbose) # Set Verbose output mode optional argument
|
||||
|
||||
for input_file in iflash_input_images:
|
||||
input_name = os.path.basename(input_file)
|
||||
input_padd = 8
|
||||
|
||||
print('\n*** %s' % input_name)
|
||||
|
||||
# Check if input file exists
|
||||
if not os.path.isfile(input_file):
|
||||
print('\n%sError: This input file does not exist!' % (' ' * input_padd))
|
||||
continue # Next input file
|
||||
|
||||
with open(input_file, 'rb') as in_file: input_data = in_file.read()
|
||||
|
||||
# Search input image for Insyde iFlash Sections
|
||||
is_ins_ifl = pat_ins_ifl.search(input_data)
|
||||
|
||||
if not is_ins_ifl:
|
||||
print('\n%sError: This is not an Insyde iFlash image!' % (' ' * input_padd))
|
||||
continue # Next input file
|
||||
|
||||
# Set main extraction path (optional user specified path taken into account)
|
||||
output_path = os.path.join(output_path_user, input_name + '_extracted')
|
||||
|
||||
InsydeIflash(input_data, output_path, input_padd, is_verbose).iflash_parse()
|
||||
|
||||
exit_code -= 1 # Adjust exit code to reflect extraction progress
|
||||
|
||||
if not bool(args.auto_exit): input('\nDone!')
|
||||
|
||||
sys.exit(exit_code)
|
|
@ -1,74 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Panasonic BIOS Extract
|
||||
Panasonic BIOS Update Extractor
|
||||
Copyright (C) 2018 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
print('Panasonic BIOS Update Extractor v1.0')
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import pefile
|
||||
import subprocess
|
||||
|
||||
if len(sys.argv) >= 2 :
|
||||
# Drag & Drop or CLI
|
||||
panasonic = sys.argv[1:]
|
||||
else :
|
||||
# Folder path
|
||||
panasonic = []
|
||||
in_path = input('\nEnter the full folder path: ')
|
||||
print('\nWorking...')
|
||||
for root, dirs, files in os.walk(in_path):
|
||||
for name in files :
|
||||
panasonic.append(os.path.join(root, name))
|
||||
|
||||
for input_file in panasonic :
|
||||
file_path = os.path.abspath(input_file)
|
||||
file_name = os.path.basename(input_file)
|
||||
file_dir = os.path.dirname(file_path)
|
||||
file_ext = os.path.splitext(file_path)[1]
|
||||
|
||||
# Create output folder
|
||||
extr_path = os.path.join(os.getcwd(), 'RCDATA')
|
||||
if os.path.exists(extr_path) : shutil.rmtree(extr_path)
|
||||
os.makedirs(extr_path)
|
||||
|
||||
max_size = 0
|
||||
max_file = None
|
||||
pe = pefile.PE(input_file) # Analyze Portable Executable (PE)
|
||||
for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries :
|
||||
# Parse Resource Data directories only
|
||||
if entry.struct.name == 'IMAGE_RESOURCE_DIRECTORY_ENTRY' and entry.struct.Id == 10 : # RCDATA ID = 10
|
||||
for resource in entry.directory.entries :
|
||||
offset = resource.directory.entries[0].data.struct.OffsetToData
|
||||
size = resource.directory.entries[0].data.struct.Size
|
||||
data = pe.get_data(offset, size)
|
||||
file = os.path.join(extr_path, '%X_%X.bin' % (offset, size))
|
||||
with open(file, 'wb') as out_file : out_file.write(data)
|
||||
|
||||
# Remember largest resource (SPI/BIOS)
|
||||
if size > max_size :
|
||||
max_size = size
|
||||
max_file = file
|
||||
|
||||
if not max_file :
|
||||
print('\nError: No Panasonic BIOS Update at %s!' % file_name)
|
||||
shutil.rmtree(extr_path) # Remove temporary folder
|
||||
continue # Next input file
|
||||
|
||||
# Call Rustam Abdullaev's unpack_lznt1 to extract the LZNT1-compressed SPI/BIOS resource at 0x8 onwards
|
||||
try :
|
||||
subprocess.run(['unpack_lznt1', max_file, os.path.join(file_dir, file_name[:-4] + '.bin'), '8'], check = True, stdout = subprocess.DEVNULL)
|
||||
print('\nExtracted %s via unpack_lznt1' % (file_name[:-4] + '.bin'))
|
||||
except :
|
||||
print('\nError: Could not extract %s via unpack_lznt1!' % (file_name[:-4] + '.bin'))
|
||||
print(' Make sure that "unpack_lznt1.exe" executable exists!')
|
||||
|
||||
shutil.rmtree(extr_path) # Remove temporary folder
|
||||
|
||||
else :
|
||||
input('\nDone!')
|
|
@ -1,213 +0,0 @@
|
|||
#!/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)
|
|
@ -1,174 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
#coding=utf-8
|
||||
|
||||
"""
|
||||
Portwell EFI Extract
|
||||
Portwell EFI BIOS Extractor
|
||||
Copyright (C) 2021 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
title = 'Portwell EFI 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 pefile
|
||||
import shutil
|
||||
import ctypes
|
||||
import argparse
|
||||
import traceback
|
||||
import subprocess
|
||||
|
||||
# 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
|
||||
efi_parser = argparse.ArgumentParser()
|
||||
efi_parser.add_argument('efi', type=argparse.FileType('r'), nargs='*')
|
||||
efi_parser.add_argument('-p', '--path', help='parse files within given folder', type=str)
|
||||
efi_params = efi_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(efi_params.path) :
|
||||
efi_exec = get_files(efi_params.path) # CLI with --path
|
||||
else :
|
||||
efi_exec = []
|
||||
for executable in efi_params.efi :
|
||||
efi_exec.append(executable.name) # Drag & Drop
|
||||
else :
|
||||
in_path = input('\nEnter the full folder path: ')
|
||||
efi_exec = get_files(in_path) # Direct Run
|
||||
|
||||
# Portwell UEFI Unpacker File Names (v1.1 - v1.2)
|
||||
file_names = {0 : 'Flash.efi', 1 : 'Fparts.txt', 2 : 'Update.nsh', 3 : 'Temp.bin', 4 : 'SaveDmiData.efi'}
|
||||
|
||||
# Process each input Portwell EFI Update Package
|
||||
for input_file in efi_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()
|
||||
|
||||
try :
|
||||
assert input_data[0x0:0x2] == b'\x4D\x5A' # EFI images start with DOS Header MZ
|
||||
|
||||
pe = pefile.PE(input_file, fast_load=True) # Analyze EFI Portable Executable (PE)
|
||||
|
||||
payload_data = input_data[pe.OPTIONAL_HEADER.SizeOfImage:] # Skip EFI executable (pylint: disable=E1101)
|
||||
|
||||
assert payload_data[0x0:0x4] == b'\x3C\x55\x55\x3E' # Portwell EFI files start with <UU>
|
||||
except :
|
||||
print('\n Error: This is not a Portwell EFI Update Package!')
|
||||
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
|
||||
|
||||
pack_tag = 'UEFI Unpacker' # Initialize Portwell UEFI Unpacker tag
|
||||
|
||||
# Get Portwell UEFI Unpacker tag
|
||||
for s in pe.sections :
|
||||
if s.Name.startswith(b'.data') : # Unpacker Tag, Version, Strings etc are found in .data PE section
|
||||
# Decode any valid UTF-16 .data PE section info to a parsable text buffer
|
||||
info = input_data[s.PointerToRawData:s.PointerToRawData + s.SizeOfRawData].decode('utf-16','ignore')
|
||||
|
||||
# Search .data for UEFI Unpacker tag
|
||||
pack_tag_off = info.find('UEFI Unpacker')
|
||||
if pack_tag_off != -1 :
|
||||
pack_tag_len = info[pack_tag_off:].find('=')
|
||||
if pack_tag_len != -1 :
|
||||
# Found full UEFI Unpacker tag, store and slightly beautify the resulting text
|
||||
pack_tag = info[pack_tag_off:pack_tag_off + pack_tag_len].strip().replace(' ',' ').replace('<',' <')
|
||||
|
||||
break # Found PE .data section, skip the rest
|
||||
|
||||
print('\n Portwell %s' % pack_tag) # Print Portwell UEFI Unpacker tag
|
||||
|
||||
efi_files = payload_data.split(b'\x3C\x55\x55\x3E')[1:] # Split EFI Payload into <UU> file chunks
|
||||
|
||||
# Parse each EFI Payload File
|
||||
for i in range(len(efi_files)) :
|
||||
file_data = efi_files[i] # Store EFI File data
|
||||
|
||||
if len(file_data) == 0 or file_data == b'NULL' : continue # Skip empty/unused files
|
||||
|
||||
is_known = i in file_names # Check if EFI file is known & Store result
|
||||
|
||||
file_name = file_names[i] if is_known else 'Unknown_%d.bin' % i # Assign Name to EFI file
|
||||
|
||||
print('\n %s' % file_name) # Print EFI file name, indicate progress
|
||||
|
||||
if not is_known : input('\n Note: Detected unknown Portwell EFI file with ID %d!' % i) # Report new EFI files
|
||||
|
||||
file_path = os.path.join(output_path, file_name) # Store EFI file output path
|
||||
|
||||
with open(file_path, 'wb') as o : o.write(file_data) # Store EFI file data to drive
|
||||
|
||||
# Attempt to detect EFI/Tiano Compression & Decompress when applicable
|
||||
if int.from_bytes(file_data[0x0:0x4], 'little') + 0x8 == len(file_data) :
|
||||
try :
|
||||
comp_fname = file_path + '.temp' # Store temporary compressed file name
|
||||
|
||||
os.replace(file_path, comp_fname) # Rename initial/compressed file
|
||||
|
||||
subprocess.run(['TianoCompress', '-d', comp_fname, '-o', file_path, '--uefi', '-q'], check = True, stdout = subprocess.DEVNULL)
|
||||
|
||||
if os.path.getsize(file_path) != int.from_bytes(file_data[0x4:0x8], 'little') : raise Exception('EFI_DECOMP_ERROR')
|
||||
|
||||
os.remove(comp_fname) # Successful decompression, delete initial/compressed file
|
||||
|
||||
except :
|
||||
print('\n Error: Could not extract file %s via TianoCompress!' % file_name)
|
||||
input(' Make sure that "TianoCompress" executable exists!')
|
||||
|
||||
print('\n Extracted Portwell EFI Update Package!')
|
||||
|
||||
input('\nDone!')
|
||||
|
||||
sys.exit(0)
|
|
@ -1,126 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
VAIO Package Extractor
|
||||
VAIO Packaging Manager Extractor
|
||||
Copyright (C) 2019-2020 Plato Mavropoulos
|
||||
"""
|
||||
|
||||
print('VAIO Packaging Manager Extractor v2.0')
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
if len(sys.argv) >= 2 :
|
||||
# Drag & Drop or CLI
|
||||
vaio_exec = sys.argv[1:]
|
||||
else :
|
||||
# Folder path
|
||||
vaio_exec = []
|
||||
in_path = input('\nEnter the full folder path: ')
|
||||
print('\nWorking...')
|
||||
for root, dirs, files in os.walk(in_path):
|
||||
for name in files :
|
||||
vaio_exec.append(os.path.join(root, name))
|
||||
|
||||
# Microsoft CAB Header XOR 0xFF (Tag[4] + Res[4] + Size[4] + Res[4] + Offset[4] + Res[4] + Ver[2]) pattern
|
||||
mscf_pattern = re.compile(br'\xB2\xAC\xBC\xB9\xFF{4}.{4}\xFF{4}.{4}\xFF{4}\xFC\xFE', re.DOTALL)
|
||||
|
||||
# VAIO Packaging Manager Configuration file ("[Setting]" + Windows_new_line) pattern
|
||||
vaio_pattern = re.compile(br'\x5B\x53\x65\x74\x74\x69\x6E\x67\x5D\x0D\x0A')
|
||||
|
||||
# VAIO Packaging Manager Configuration file entry "UseVAIOCheck" pattern
|
||||
check_pattern = re.compile(br'\x0A\x55\x73\x65\x56\x41\x49\x4F\x43\x68\x65\x63\x6B\x3D')
|
||||
|
||||
# VAIO Packaging Manager Configuration file entry "ExtractPathByUser" pattern
|
||||
path_pattern = re.compile(br'\x0A\x45\x78\x74\x72\x61\x63\x74\x50\x61\x74\x68\x42\x79\x55\x73\x65\x72\x3D')
|
||||
|
||||
for input_file in vaio_exec :
|
||||
file_path = os.path.abspath(input_file)
|
||||
file_dir = os.path.dirname(file_path)
|
||||
file_name = os.path.basename(file_path)
|
||||
|
||||
print('\nFile: ' + file_name)
|
||||
|
||||
# Open Locked VAIO Packaging Manager executable as mutable bytearray
|
||||
with open(input_file, 'rb') as in_file : vaio_data = bytearray(in_file.read())
|
||||
|
||||
match_mscf = mscf_pattern.search(vaio_data) # Search for Microsoft CAB Header XOR 0xFF pattern
|
||||
|
||||
match_vaio = vaio_pattern.search(vaio_data) if not match_mscf else None # Search for VAIO Packaging Manager Configuration file
|
||||
|
||||
# Check if Microsoft CAB Header XOR 0xFF pattern exists
|
||||
if match_mscf :
|
||||
print('\n Detected Obfuscation!')
|
||||
|
||||
# Determine the Microsoft CAB image Size
|
||||
cab_size = int.from_bytes(vaio_data[match_mscf.start() + 0x8:match_mscf.start() + 0xC], 'little') # Get LE XOR-ed CAB Size
|
||||
xor_size = int.from_bytes(b'\xFF' * 0x4, 'little') # Create CAB Size XOR value
|
||||
cab_size = cab_size ^ xor_size # Perform XOR 0xFF and get actual CAB Size
|
||||
|
||||
print('\n Removing Obfuscation...')
|
||||
|
||||
# Determine the Microsoft CAB image Data
|
||||
cab_data = int.from_bytes(vaio_data[match_mscf.start():match_mscf.start() + cab_size], 'big') # Get BE XOR-ed CAB Data
|
||||
xor_data = int.from_bytes(b'\xFF' * cab_size, 'big') # Create CAB Data XOR value
|
||||
cab_data = (cab_data ^ xor_data).to_bytes(cab_size, 'big') # Perform XOR 0xFF and get actual CAB Data
|
||||
|
||||
print('\n Extracting...')
|
||||
|
||||
with open('vaio_temp.cab', 'wb') as cab_file : cab_file.write(cab_data) # Create temporary CAB image
|
||||
|
||||
extr_path = os.path.join(file_dir, file_name[:-4], '') # Create CAB image extraction path
|
||||
|
||||
try :
|
||||
decomp = subprocess.run(['7z', 'x', '-aou', '-bso0', '-bse0', '-bsp0', '-o' + extr_path, 'vaio_temp.cab']) # 7-Zip
|
||||
|
||||
print('\n Extracted!')
|
||||
except :
|
||||
print('\n Error: Could not decompress Microsoft CAB image!')
|
||||
print(' Make sure that "7z" executable exists!')
|
||||
|
||||
os.remove('vaio_temp.cab') # Remove temporary CAB image
|
||||
|
||||
# Check if VAIO Packaging Manager Configuration file pattern exists
|
||||
elif match_vaio :
|
||||
print('\n Error: Failed to Extract, attempting to Unlock instead...')
|
||||
|
||||
# Initialize VAIO Package Configuration file variables (assume overkill size of 0x500)
|
||||
info_start, info_end, val_false, val_true = [match_vaio.start(), match_vaio.start() + 0x500, b'', b'']
|
||||
|
||||
# Get VAIO Package Configuration file info, split at new_line and stop at payload DOS header (EOF)
|
||||
vaio_info = vaio_data[info_start:info_end].split(b'\x0D\x0A\x4D\x5A')[0].replace(b'\x0D',b'').split(b'\x0A')
|
||||
|
||||
# Determine VAIO Package Configuration file True & False values
|
||||
for info in vaio_info :
|
||||
if info.startswith(b'ExtractPathByUser=') : val_false = bytearray(b'0' if info[18:] in (b'0',b'1') else info[18:]) # Should be 0/No/False
|
||||
if info.startswith(b'UseCompression=') : val_true = bytearray(b'1' if info[15:] in (b'0',b'1') else info[15:]) # Should be 1/Yes/True
|
||||
else :
|
||||
if val_false == val_true or not val_false or not val_true :
|
||||
print('\n Error: Could not determine True/False values!')
|
||||
print(' Please report this VAIO Packaging Manager!')
|
||||
continue # Next input file
|
||||
|
||||
# Find and replace UseVAIOCheck entry from 1/Yes/True to 0/No/False
|
||||
UseVAIOCheck = check_pattern.search(vaio_data[info_start:])
|
||||
if UseVAIOCheck : vaio_data[info_start + UseVAIOCheck.end():info_start + UseVAIOCheck.end() + len(val_false)] = val_false
|
||||
else : print('\n Error: Could not find UseVAIOCheck entry!')
|
||||
|
||||
# Find and replace ExtractPathByUser entry from 0/No/False to 1/Yes/True
|
||||
ExtractPathByUser = path_pattern.search(vaio_data[info_start:])
|
||||
if ExtractPathByUser : vaio_data[info_start + ExtractPathByUser.end():info_start + ExtractPathByUser.end() + len(val_false)] = val_true
|
||||
else : print('\n Error: Could not find ExtractPathByUser entry!')
|
||||
|
||||
# Store Unlocked VAIO Packaging Manager executable
|
||||
if UseVAIOCheck and ExtractPathByUser :
|
||||
with open(os.path.join(file_dir, file_name + '_Unlocked.exe'), 'wb') as unl_file : unl_file.write(vaio_data)
|
||||
print('\n Unlocked!')
|
||||
|
||||
else :
|
||||
print('\n Error: No VAIO Packaging Manager found!')
|
||||
continue # Next input file
|
||||
|
||||
else :
|
||||
input('\nDone!')
|
Loading…
Add table
Add a link
Reference in a new issue