From cd2704f7435042374da5b0f1b357ced3e0b4cc4d Mon Sep 17 00:00:00 2001 From: platomav Date: Thu, 30 Jun 2022 01:20:21 +0300 Subject: [PATCH] Added Insyde iFlash Update Extractor v2.0_a2 Added Toshiba BIOS COM Extractor v2.0_a2 --- Fujitsu_UPC_Extract.py | 8 +-- Insyde_iFlash_Extract.py | 148 +++++++++++++++++++++++++++++++++++++++ README.md | 116 +++++++++++++++++++++++++++++- Toshiba_COM_Extract.py | 89 +++++++++++++++++++++++ common/comp_efi.py | 3 +- common/comp_szip.py | 3 +- common/patterns.py | 2 + 7 files changed, 359 insertions(+), 10 deletions(-) create mode 100644 Insyde_iFlash_Extract.py create mode 100644 Toshiba_COM_Extract.py diff --git a/Fujitsu_UPC_Extract.py b/Fujitsu_UPC_Extract.py index 55929a8..229e8e0 100644 --- a/Fujitsu_UPC_Extract.py +++ b/Fujitsu_UPC_Extract.py @@ -7,7 +7,7 @@ Fujitsu UPC BIOS Extractor Copyright (C) 2021-2022 Plato Mavropoulos """ -TITLE = 'Fujitsu UPC BIOS Extractor v2.0_a2' +TITLE = 'Fujitsu UPC BIOS Extractor v2.0_a3' import os import sys @@ -17,18 +17,18 @@ sys.dont_write_bytecode = True from common.comp_efi import efi_decompress, is_efi_compressed from common.path_ops import make_dirs, path_suffixes -from common.system import script_init, argparse_init, printer +from common.system import argparse_init, printer, script_init from common.text_ops import file_to_bytes # Check if input is Fujitsu UPC image def is_fujitsu_upc(in_file): in_buffer = file_to_bytes(in_file) - is_upc = path_suffixes(in_file)[-1].upper() == '.UPC' if os.path.isfile(in_file) else True + is_ext = path_suffixes(in_file)[-1].upper() == '.UPC' if os.path.isfile(in_file) else True is_efi = is_efi_compressed(in_buffer) - return is_upc and is_efi + return is_ext and is_efi # Parse & Extract Fujitsu UPC image def fujitsu_upc_extract(input_file, output_path, padding=0): diff --git a/Insyde_iFlash_Extract.py b/Insyde_iFlash_Extract.py new file mode 100644 index 0000000..7489e60 --- /dev/null +++ b/Insyde_iFlash_Extract.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +#coding=utf-8 + +""" +Insyde iFlash Extract +Insyde iFlash Update Extractor +Copyright (C) 2022 Plato Mavropoulos +""" + +TITLE = 'Insyde iFlash Update Extractor v2.0_a2' + +import os +import sys +import ctypes + +# Stop __pycache__ generation +sys.dont_write_bytecode = True + +from common.path_ops import make_dirs, safe_name +from common.patterns import PAT_INSYDE_IFL +from common.struct_ops import get_struct, char, uint32_t +from common.system import script_init, argparse_init, printer +from common.text_ops import file_to_bytes + +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 struct_print(self, p): + printer(['Signature :', self.Signature.decode('utf-8','ignore')], p, False) + printer(['Image Name:', self.ImageTag.decode('utf-8','ignore')], p, False) + printer(['Image Size:', f'0x{self.ImageSize:X}'], p, False) + printer(['Total Size:', f'0x{self.TotalSize:X}'], p, False) + +# Parse & Extract Insyde iFlash Update image +def insyde_iflash_extract(input_buffer, ins_ifl_all, output_path, padding=0): + extract_path = os.path.join(f'{output_path}_extracted') + + make_dirs(extract_path, delete=True) + + for ins_ifl_val in ins_ifl_all: + ins_ifl_off,ins_ifl_hdr = ins_ifl_val + + mod_bgn = ins_ifl_off + IFL_HDR_LEN + mod_end = mod_bgn + ins_ifl_hdr.ImageSize + mod_bin = input_buffer[mod_bgn:mod_end] + + mod_val = [ins_ifl_hdr.ImageTag.decode('utf-8','ignore'), 'bin'] + mod_tag,mod_ext = IFL_MOD_NAMES.get(mod_val[0], mod_val) + + mod_name = f'{mod_tag} [0x{mod_bgn:08X}-0x{mod_end:08X}]' + + printer(f'{mod_name}\n', padding) + + ins_ifl_hdr.struct_print(padding + 4) + + if mod_val == [mod_tag,mod_ext]: + printer(f'Note: Detected new Insyde iFlash image tag {mod_tag}!', padding + 8, pause=True) + + out_name = f'{mod_name}.{mod_ext}' + + out_path = os.path.join(extract_path, safe_name(out_name)) + + with open(out_path, 'wb') as out: out.write(mod_bin) + + printer('Succesfull Insyde iFlash image extraction!', padding + 8) + +# Get Insyde iFlash Update image matches +def get_insyde_iflash(in_file): + ins_ifl_all = [] + ins_ifl_nan = [0x0,0xFFFFFFFF] + + buffer = file_to_bytes(in_file) + + for ins_ifl_match in PAT_INSYDE_IFL.finditer(buffer): + ins_ifl_off = ins_ifl_match.start() + + if len(buffer[ins_ifl_off:]) <= IFL_HDR_LEN: + continue + + ins_ifl_hdr = get_struct(buffer, ins_ifl_off, IflashHeader) + + if ins_ifl_hdr.TotalSize in ins_ifl_nan \ + or ins_ifl_hdr.ImageSize in ins_ifl_nan \ + or ins_ifl_hdr.TotalSize <= ins_ifl_hdr.ImageSize: + continue + + ins_ifl_all.append([ins_ifl_off, ins_ifl_hdr]) + + return ins_ifl_all + +# Check if input is Insyde iFlash Update image +def is_insyde_iflash(in_file): + buffer = file_to_bytes(in_file) + + return bool(get_insyde_iflash(buffer)) + +IFL_MOD_NAMES = { + 'DRV_IMG' : ['isflash', 'efi'], + 'INI_IMG' : ['platform', 'ini'], + 'BIOSIMG' : ['BIOS-UEFI', 'bin'], + 'ME_IMG_' : ['Management Engine', 'bin'], + 'EC_IMG_' : ['Embedded Controller', 'bin'], + 'OEM_ID_' : ['OEM Identifier', 'bin'], + 'BIOSCER' : ['Certificate', 'bin'], + 'BIOSCR2' : ['Certificate 2nd', 'bin'], + } + +# Get common ctypes Structure Sizes +IFL_HDR_LEN = ctypes.sizeof(IflashHeader) + +if __name__ == '__main__': + # Set argparse Arguments + argparser = argparse_init() + arguments = argparser.parse_args() + + # Initialize script (must be after argparse) + exit_code,input_files,output_path,padding = script_init(TITLE, arguments, 4) + + for input_file in input_files: + input_name = os.path.basename(input_file) + + printer(['***', input_name], padding - 4) + + with open(input_file, 'rb') as in_file: input_buffer = in_file.read() + + ins_ifl_all = get_insyde_iflash(input_buffer) + + if not ins_ifl_all: + printer('Error: This is not an Insyde iFlash Update image!', padding) + + continue # Next input file + + extract_path = os.path.join(output_path, input_name) + + insyde_iflash_extract(input_buffer, ins_ifl_all, extract_path, padding) + + exit_code -= 1 + + printer('Done!', pause=True) + + sys.exit(exit_code) diff --git a/README.md b/README.md index 27d926b..17dbfef 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,11 @@ * [**Award BIOS Module Extractor**](#award-bios-module-extractor) * [**Dell PFS Update Extractor**](#dell-pfs-update-extractor) * [**Fujitsu UPC BIOS Extractor**](#fujitsu-upc-bios-extractor) +* [**Insyde iFlash Update Extractor**](#insyde-iflash-update-extractor) * [**Panasonic BIOS Package Extractor**](#panasonic-bios-package-extractor) * [**Phoenix TDK Packer Extractor**](#phoenix-tdk-packer-extractor) * [**Portwell EFI Update Extractor**](#portwell-efi-update-extractor) +* [**Toshiba BIOS COM Extractor**](#toshiba-bios-com-extractor) * [**VAIO Packaging Manager Extractor**](#vaio-packaging-manager-extractor) ## **AMI BIOS Guard Extractor** @@ -263,11 +265,11 @@ Some Anti-Virus software may claim that the built/frozen/compiled executable con #### **Description** -Parses Fujitsu UPC images and extracts their EFI compressed SPI/BIOS/UEFI firmware component. The output comprises only a final firmware component which is directly usable by end users. +Parses Fujitsu UPC BIOS images and extracts their EFI compressed SPI/BIOS/UEFI firmware component. The output comprises only a final firmware component which is directly usable by end users. #### **Usage** -You can either Drag & Drop or manually enter Panasonic BIOS Package executable file(s). Optional arguments: +You can either Drag & Drop or manually enter Fujitsu UPC BIOS image file(s). Optional arguments: * -h or --help : show help message and exit * -v or --version : show utility name and version @@ -315,6 +317,58 @@ Some Anti-Virus software may claim that the built/frozen/compiled executable con ![]() +## **Insyde iFlash Update Extractor** + +![]() + +#### **Description** + +Parses Insyde iFlash Update images and extracts their firmware (e.g. SPI, BIOS/UEFI, EC, ME etc) and utilities (e.g. Flasher, Configuration etc) components. The output comprises only final firmware components which are directly usable by end users. + +#### **Usage** + +You can either Drag & Drop or manually enter Insyde iFlash Update image file(s). Optional arguments: + +* -h or --help : show help message and exit +* -v or --version : show utility name and version +* -i or --input-dir : extract from given input directory +* -o or --output-dir : extract in given output directory +* -e or --auto-exit : skip press enter to exit prompts + +#### **Compatibility** + +Should work at all Windows, Linux or macOS operating systems which have Python 3.8 support. + +#### **Prerequisites** + +To run the utility, you do not need any prerequisites. + +#### **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.8.0 or newer is installed: + +> python --version + +2. Use pip to install PyInstaller: + +> pip3 install pyinstaller + +3. Build/Freeze/Compile: + +> pyinstaller --noupx --onefile \\/Insyde_iFlash_Extract.py + +At dist folder you should find the final utility executable + +#### **Anti-Virus False Positives** + +Some Anti-Virus software may claim that the built/frozen/compiled executable contains viruses. Any such detections are false positives, usually of PyInstaller. You can switch to a better Anti-Virus software, report the false positive to their support, add the executable to the exclusions, build/freeze/compile yourself or use the Python script directly. + +#### **Pictures** + +![]() + ## **Panasonic BIOS Package Extractor** ![]() @@ -508,6 +562,64 @@ Some Anti-Virus software may claim that the built/frozen/compiled executable con ![]() +## **Toshiba BIOS COM Extractor** + +![]() + +#### **Description** + +Parses Toshiba BIOS COM images and extracts their raw or compressed SPI/BIOS/UEFI firmware component. This utility is basically an easy to use python wrapper around [ToshibaComExtractor by LongSoft](https://github.com/LongSoft/ToshibaComExtractor). The output comprises only a final firmware component which is directly usable by end users. + +#### **Usage** + +You can either Drag & Drop or manually enter Toshiba BIOS COM image file(s). Optional arguments: + +* -h or --help : show help message and exit +* -v or --version : show utility name and version +* -i or --input-dir : extract from given input directory +* -o or --output-dir : extract in given output directory +* -e or --auto-exit : skip press enter to exit prompts + +#### **Compatibility** + +Should work at all Windows, Linux or macOS operating systems which have Python 3.8 support. + +#### **Prerequisites** + +To run the utility, you must have the following 3rd party tool at the "external" project directory: + +* [ToshibaComExtractor](https://github.com/LongSoft/ToshibaComExtractor) (e.g. comextract.exe for Windows or comextract for Linux) + +#### **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.8.0 or newer is installed: + +> python --version + +2. Use pip to install PyInstaller: + +> pip3 install pyinstaller + +3. Place prerequisite at the "external" project directory: + +> ToshibaComExtractor + +4. Build/Freeze/Compile: + +> pyinstaller --add-data="external/*;external/" --noupx --onefile \\/Toshiba_COM_Extract.py + +You should find the final utility executable at "dist" folder + +#### **Anti-Virus False Positives** + +Some Anti-Virus software may claim that the built/frozen/compiled executable contains viruses. Any such detections are false positives, usually of PyInstaller. You can switch to a better Anti-Virus software, report the false positive to their support, add the executable to the exclusions, build/freeze/compile yourself or use the Python script directly. + +#### **Pictures** + +![]() + ## **VAIO Packaging Manager Extractor** ![]() diff --git a/Toshiba_COM_Extract.py b/Toshiba_COM_Extract.py new file mode 100644 index 0000000..6af0770 --- /dev/null +++ b/Toshiba_COM_Extract.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +#coding=utf-8 + +""" +Toshiba COM Extract +Toshiba BIOS COM Extractor +Copyright (C) 2018-2022 Plato Mavropoulos +""" + +TITLE = 'Toshiba BIOS COM Extractor v2.0_a2' + +import os +import sys +import subprocess + +# Stop __pycache__ generation +sys.dont_write_bytecode = True + +from common.path_ops import make_dirs, path_stem, path_suffixes, project_root, safe_path +from common.patterns import PAT_TOSHIBA_COM +from common.system import argparse_init, get_os_ver, printer, script_init +from common.text_ops import file_to_bytes + +# Check if input is Toshiba BIOS COM image +def is_toshiba_com(in_file): + buffer = file_to_bytes(in_file) + + is_ext = path_suffixes(in_file)[-1].upper() == '.COM' if os.path.isfile(in_file) else True + + is_com = PAT_TOSHIBA_COM.search(buffer) + + return is_ext and is_com + +# Get ToshibaComExtractor path +def get_comextract_path(): + exec_name = 'comextract.exe' if get_os_ver()[1] else 'comextract' + + return safe_path(project_root(), ['external',exec_name]) + +# Parse & Extract Toshiba BIOS COM image +def toshiba_com_extract(input_file, output_path, padding=0): + extract_path = os.path.join(f'{output_path}_extracted') + + make_dirs(extract_path, delete=True) + + output_name = path_stem(input_file) + output_file = os.path.join(extract_path, f'{output_name}.bin') + + try: + subprocess.run([get_comextract_path(), input_file, output_file], check=True, stdout=subprocess.DEVNULL) + + if not os.path.isfile(output_file): raise Exception('EXTRACT_FILE_MISSING') + except: + printer(f'Error: ToshibaComExtractor could not extract file {input_file}!', padding) + + return 1 + + printer(f'Succesfull {output_name} extraction via ToshibaComExtractor!', padding) + + return 0 + +if __name__ == '__main__': + # Set argparse Arguments + argparser = argparse_init() + arguments = argparser.parse_args() + + # Initialize script (must be after argparse) + exit_code,input_files,output_path,padding = script_init(TITLE, arguments, 4) + + for input_file in input_files: + input_name = os.path.basename(input_file) + + printer(['***', input_name], padding - 4) + + with open(input_file, 'rb') as in_file: input_buffer = in_file.read() + + if not is_toshiba_com(input_file): + printer('Error: This is not a Toshiba BIOS COM image!', padding) + + continue # Next input file + + extract_path = os.path.join(output_path, input_name) + + if toshiba_com_extract(input_file, extract_path, padding) == 0: + exit_code -= 1 + + printer('Done!', pause=True) + + sys.exit(exit_code) diff --git a/common/comp_efi.py b/common/comp_efi.py index d84ae75..da2f214 100644 --- a/common/comp_efi.py +++ b/common/comp_efi.py @@ -9,8 +9,7 @@ import os import subprocess from common.path_ops import project_root, safe_path -from common.system import get_os_ver -from common.system import printer +from common.system import get_os_ver, printer def get_compress_sizes(data): size_compress = int.from_bytes(data[0x0:0x4], 'little') diff --git a/common/comp_szip.py b/common/comp_szip.py index e786622..4ac053a 100644 --- a/common/comp_szip.py +++ b/common/comp_szip.py @@ -9,8 +9,7 @@ import os import subprocess from common.path_ops import project_root, safe_path -from common.system import get_os_ver -from common.system import printer +from common.system import get_os_ver, printer # Get 7-Zip path def get_szip_path(): diff --git a/common/patterns.py b/common/patterns.py index 47846f2..797d467 100644 --- a/common/patterns.py +++ b/common/patterns.py @@ -13,12 +13,14 @@ PAT_AWARD_LZH = re.compile(br'-lh[04567]-') PAT_DELL_FTR = re.compile(br'\xEE\xAA\xEE\x8F\x49\x1B\xE8\xAE\x14\x37\x90') PAT_DELL_HDR = re.compile(br'\xEE\xAA\x76\x1B\xEC\xBB\x20\xF1\xE6\x51.\x78\x9C', re.DOTALL) PAT_DELL_PKG = re.compile(br'\x72\x13\x55\x00.{45}7zXZ', re.DOTALL) +PAT_INSYDE_IFL = re.compile(br'\$_IFLASH_') PAT_INTEL_ENG = re.compile(br'\x04\x00{3}[\xA1\xE1]\x00{3}.{8}\x86\x80.{9}\x00\$((MN2)|(MAN))', re.DOTALL) PAT_MICROSOFT_CAB = re.compile(br'MSCF\x00{4}') PAT_MICROSOFT_MZ = re.compile(br'MZ') PAT_MICROSOFT_PE = re.compile(br'PE\x00{2}') PAT_PHOENIX_TDK = re.compile(br'\$PACK\x00{3}..\x00{2}.\x00{3}', re.DOTALL) PAT_PORTWELL_EFI = re.compile(br'') +PAT_TOSHIBA_COM = re.compile(br'\x00{2}[\x00-\x02]BIOS.{20}[\x00\x01]', re.DOTALL) PAT_VAIO_CAB = re.compile(br'\xB2\xAC\xBC\xB9\xFF{4}.{4}\xFF{4}.{4}\xFF{4}\xFC\xFE', re.DOTALL) PAT_VAIO_CFG = re.compile(br'\[Setting]\x0D\x0A') PAT_VAIO_CHK = re.compile(br'\x0AUseVAIOCheck=')