BIOSUtilities/AMI UCP BIOS Extractor/AMI_UCP_Extract.py
Plato Mavropoulos 7c238274b7 AMI UCP BIOS Extractor v1.2
Fixed crash when parsing compressed text only UAF Modules
2021-08-21 19:34:40 +03:00

610 lines
No EOL
24 KiB
Python

#!/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)