diff --git a/AMI BIOS Guard Extractor/AMI_PFAT_Extract.py b/AMI BIOS Guard Extractor/AMI_PFAT_Extract.py index ae2a82e..a3277a2 100644 --- a/AMI BIOS Guard Extractor/AMI_PFAT_Extract.py +++ b/AMI BIOS Guard Extractor/AMI_PFAT_Extract.py @@ -1,17 +1,51 @@ #!/usr/bin/env python3 +#coding=utf-8 """ AMI PFAT Extract AMI BIOS Guard Extractor -Copyright (C) 2018-2019 Plato Mavropoulos +Copyright (C) 2018-2020 Plato Mavropoulos """ -print('AMI BIOS Guard Extractor v2.0') +print('AMI BIOS Guard Extractor v3.0') + +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') + sys.exit(1) import os -import sys +import re import ctypes import struct +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 @@ -20,37 +54,37 @@ 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 ? + ('Checksum', uint32_t), # 0x04 Unknown 16-bits ('Tag', char*8), # 0x04 _AMIPFAT - ('Control', uint8_t), # 0x10 0x4 + ('Flags', uint8_t), # 0x10 # 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) + 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) -# 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 + ('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 ] @@ -58,19 +92,53 @@ class PFAT_Block_Header(ctypes.LittleEndianStructure) : 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) + def get_flags(self) : + attr = PFAT_Block_Header_GetAttributes() + attr.asbytes = self.Attributes -# noinspection PyTypeChecker + 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_ = [ @@ -87,17 +155,17 @@ class PFAT_Block_RSA(ctypes.LittleEndianStructure) : self.count = count def pfat_print(self) : - RSAPublicKey = ''.join('%0.8X' % int.from_bytes(struct.pack('= len(buffer)) or (fit_len < struct_len) : - print('Error: Offset 0x%X out of bounds at %s, possibly incomplete image!' % (start_offset, class_name)) + input('\nError: 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) @@ -116,72 +184,131 @@ def get_struct(buffer, start_offset, class_name, param_list = None) : if len(sys.argv) >= 2 : # Drag & Drop or CLI - pfat = sys.argv[1:] + ami_pfat = sys.argv[1:] else : # Folder path - pfat = [] + ami_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)) + ami_pfat.append(os.path.join(root, name)) -for input_file in pfat : - with open(input_file, 'rb') as in_file : buffer = in_file.read() +pfat_index = 1 +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 : + input_name,input_extension = os.path.splitext(os.path.basename(input_file)) + input_dir = os.path.dirname(os.path.abspath(input_file)) + + file_data = b'' final_image = b'' block_name = '' block_count = 0 + file_index = 0 blocks = [] - pfat_hdr = get_struct(buffer, 0, PFAT_Header) + with open(input_file, 'rb') as in_file : buffer = in_file.read() - if pfat_hdr.Tag.decode('utf-8', 'ignore') != '_AMIPFAT' : continue + pfat_match = pfat_pat.search(buffer) + + 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]) + print(' Title : %s' % hdr_data[0]) + + if pfat_index == 1 : + 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 + + file_path = os.path.join(output_path, '%d' % pfat_index) 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_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_flash, i + 1, entry_blocks]) + 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) : - if blocks[i][0] != block_name : print('\n%s (Parameter: %s, Update: %s)' % (blocks[i][0], blocks[i][1], ['No','Yes'][blocks[i][2]])) + 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_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_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_size = ctypes.sizeof(PFAT_Block_RSA) - #block_rsa_exp = block_rsa.Exponent - #block_rsa_pkey = int((''.join('%0.8X' % int.from_bytes(struct.pack(' pip3 install pyinstaller -3. Build/Freeze/Compile: +3. Copy BIOS Guard Script Tool dependency to build directory: + +> AMI_PFAT_Extract.py, big_script_tool.py + +4. Build/Freeze/Compile: > pyinstaller --noupx --onefile AMI_PFAT_Extract.py @@ -104,6 +112,10 @@ At dist folder you should find the final utility executable Some Anti-Virus software may claim that the built/frozen/compiled executable contains viruses. Any such detections are false positives, usually of PyInstaller. You can switch to a better Anti-Virus software, report the false positive to their support, add the executable to the exclusions, build/freeze/compile yourself or use the Python script directly. +#### **Pictures** + +![](https://i.imgur.com/iZD3GY0.png) + ## **Apple EFI Sucatalog Link Grabber** ![](https://i.imgur.com/zTVFs4I.png)