Added AMI BIOS Guard Extractor

Parses AMI BIOS Guard (a.k.a. PFAT) images and extracts a proper SPI/BIOS image.
This commit is contained in:
Plato Mavropoulos 2018-10-13 23:48:34 +03:00
parent 09c02caa9a
commit d702d7f8a8
2 changed files with 227 additions and 2 deletions

View file

@ -0,0 +1,185 @@
#!/usr/bin/env python3
"""
AMI PFAT Extract
AMI BIOS Guard Extractor
Copyright (C) 2018 Plato Mavropoulos
"""
print('AMI BIOS Guard Extractor v1.0')
import os
import sys
import ctypes
import struct
# 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
# noinspection PyTypeChecker
class PFAT_Header(ctypes.LittleEndianStructure) :
_pack_ = 1
_fields_ = [
('Size', uint32_t), # 0x00
('Validation', uint32_t), # 0x04 Unknown 16-bit Checksum ?
('Tag', char*8), # 0x04 _AMIPFAT
('Control', uint8_t), # 0x10 0x4
# 0x11
]
def pfat_print(self) :
print('\nPFAT Main Header:\n')
print(' Size : 0x%X' % self.Size)
print(' Validation : 0x%X' % self.Validation)
print(' Tag : %s' % self.Tag.decode('utf-8'))
print(' Control : 0x%X' % self.Control)
# noinspection PyTypeChecker
class PFAT_Block_Header(ctypes.LittleEndianStructure) :
_pack_ = 1
_fields_ = [
('Revision', uint32_t), # 0x00 PFAT
('Platform', char*16), # 0x04
('Unknown0', uint32_t), # 0x14
('Unknown1', uint32_t), # 0x18
('FlagsSize', uint32_t), # 0x1C From Block Header end
('DataSize', uint32_t), # 0x20 From Block Flags end
('Unknown2', uint32_t), # 0x24
('Unknown3', uint32_t), # 0x28
('Unknown4', uint32_t), # 0x2C
# 0x30
]
def __init__(self, count, *args, **kwargs):
super().__init__(*args, **kwargs)
self.count = count
def pfat_print(self) :
print('\n PFAT Block %s Header:\n' % self.count)
print(' Revision : %d' % self.Revision)
print(' Platform : %s' % self.Platform.decode('utf-8'))
print(' Unknown 0 : 0x%X' % self.Unknown0)
print(' Unknown 1 : 0x%X' % self.Unknown1)
print(' Flags Size : 0x%X' % self.FlagsSize)
print(' Data Size : 0x%X' % self.DataSize)
print(' Unknown 2 : 0x%X' % self.Unknown2)
print(' Unknown 3 : 0x%X' % self.Unknown3)
print(' Unknown 4 : 0x%X' % self.Unknown4)
# noinspection PyTypeChecker
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) :
RSAPublicKey = ''.join('%0.8X' % int.from_bytes(struct.pack('<I', val), 'little') for val in reversed(self.PublicKey))
RSASignature = ''.join('%0.8X' % int.from_bytes(struct.pack('<I', val), 'little') for val in reversed(self.Signature))
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 [...]' % RSAPublicKey[:8])
print(' Exponent : 0x%X' % self.Exponent)
print(' Signature : %s [...]' % RSASignature[:8])
# Process ctypes Structure Classes
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('Error: Offset 0x%X out of bounds at %s, possibly incomplete image!' % (start_offset, class_name))
sys.exit(1)
ctypes.memmove(ctypes.addressof(structure), struct_data, fit_len)
return structure
if len(sys.argv) >= 2 :
# Drag & Drop or CLI
pfat = sys.argv[1:]
else :
# Folder path
pfat = []
in_path = input('\nEnter the full folder path: ')
print('\nWorking...')
for root, dirs, files in os.walk(in_path):
for name in files :
pfat.append(os.path.join(root, name))
for input_file in pfat :
with open(input_file, 'rb') as in_file : buffer = in_file.read()
final_image = b''
block_name = ''
block_count = 0
blocks = []
pfat_hdr = get_struct(buffer, 0, PFAT_Header)
if pfat_hdr.Tag.decode('utf-8', 'ignore') != '_AMIPFAT' : continue
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])
for entry in hdr_data[1:] :
entry_data = entry.split(' ')
entry_data = [s for s in entry_data if s != '']
entry_flash = 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_flash, i + 1, entry_blocks])
block_count += entry_blocks
block_start = hdr_size
for i in range(block_count) :
if blocks[i][0] != block_name : print('\n%s (Parameter: %s, Update: %s)' % (blocks[i][0], blocks[i][1], ['No','Yes'][blocks[i][2]]))
block_hdr = get_struct(buffer, block_start, PFAT_Block_Header, ['%d/%d' % (blocks[i][3], blocks[i][4])])
block_hdr_size = ctypes.sizeof(PFAT_Block_Header)
block_flag_size = block_hdr.FlagsSize
flag_data = buffer[block_start + block_hdr_size:block_start + block_hdr_size + block_flag_size] # Flags not parsed
block_data_start = block_start + block_hdr_size + block_flag_size
block_data_end = block_data_start + block_hdr.DataSize
block_hdr.pfat_print()
block_rsa = get_struct(buffer, block_data_end, PFAT_Block_RSA, ['%d/%d' % (blocks[i][3], blocks[i][4])])
block_rsa_size = ctypes.sizeof(PFAT_Block_RSA)
#block_rsa_exp = block_rsa.Exponent
#block_rsa_pkey = int((''.join('%0.8X' % int.from_bytes(struct.pack('<I', val), 'little') for val in reversed(block_rsa.PublicKey))), 16)
#block_rsa_sign = int((''.join('%0.8X' % int.from_bytes(struct.pack('<I', val), 'little') for val in reversed(block_rsa.Signature))), 16)
#block_rsa_sign_dec = '%X' % pow(block_rsa_sign, block_rsa_exp, block_rsa_pkey) # Decrypted signature is 4096 bits
block_rsa.pfat_print()
final_image += buffer[block_data_start:block_data_end]
block_name = blocks[i][0]
block_start = block_data_end + block_rsa_size
with open('%s_unpacked.bin' % os.path.basename(input_file), 'wb') as final : final.write(final_image)
else :
input('\nDone!')

View file

@ -5,7 +5,7 @@ Various BIOS Utilities for Modding/Research
[![BIOS Utilities Donation](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DJDZD3PRGCSCL)
![](https://i.imgur.com/vHh8ir9.png)
![](https://i.imgur.com/ci3H4Y3.png)
## **Dell HDR Module Extractor**
@ -49,6 +49,46 @@ PyInstaller can build/freeze/compile the utility at all three supported platform
At dist folder you should find the final utility executable
## **AMI BIOS Guard Extractor**
#### **Description**
Parses AMI BIOS Guard (a.k.a. PFAT) images and extracts a proper SPI/BIOS image.
#### **Usage**
You can either Drag & Drop or manually enter the full path of a folder containing AMI PFAT images.
#### **Download**
An already built/frozen/compiled binary is provided by me for Windows only. Thus, **you don't need to manually build/freeze/compile it under Windows**. Instead, download the latest version from the [Releases](https://github.com/platomav/BIOSUtilities/releases) tab. To extract the already built/frozen/compiled archive, you need to use programs which support RAR5 compression. Note that you need to manually apply any prerequisites.
#### **Compatibility**
Should work at all Windows, Linux or macOS operating systems which have Python 3.6 support. Windows users who plan to use the already built/frozen/compiled binary must make sure that they have the latest Windows Updates installed which include all required "Universal C Runtime (CRT)" libraries.
#### **Prerequisites**
To run the utility, you do not need any 3rd party tool.
#### **Build/Freeze/Compile with PyInstaller**
PyInstaller can build/freeze/compile the utility at all three supported platforms, it is simple to run and gets updated often.
1. Make sure Python 3.6.0 or newer is installed:
> python --version
2. Use pip to install PyInstaller:
> pip3 install pyinstaller
3. Build/Freeze/Compile:
> pyinstaller --noupx --onefile AMI_PFAT_Extract.py
At dist folder you should find the final utility executable
## **Apple EFI Sucatalog Link Grabber**
#### **Description**
@ -93,7 +133,7 @@ At dist folder you should find the final utility executable
#### **Description**
Parses Apple EFI files and renames them based on Apple's official $IBIOSI$ tag as follows: Model_Version_Build_Year_Month_Day_Hour_Minute_Checksum. The checksum is calculated and added by the utility in order to differentiate any EFI files with the same $IBIOSI$ tag. In rare cases in which the $IBIOSI$ tag is compressed, the utility automatically first uses [LongSoft's UEFIFind and UEFIExtract](https://github.com/LongSoft/UEFITool) tools.
Parses Apple EFI files and renames them based on Intel's official $IBIOSI$ tag as follows: Model_Version_Build_Year_Month_Day_Hour_Minute_Checksum. The checksum is calculated and added by the utility in order to differentiate any EFI files with the same $IBIOSI$ tag. In rare cases in which the $IBIOSI$ tag is compressed, the utility automatically first uses [LongSoft's UEFIFind and UEFIExtract](https://github.com/LongSoft/UEFITool) tools.
#### **Usage**