From e562a3933212d42b6868e343e1aead35a2fd5c41 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Wed, 23 Feb 2022 19:30:23 -0300 Subject: [PATCH] Add Apricot update extractor --- biostools/__main__.py | 1 + biostools/extractors.py | 125 +++++++++++++++++++++++++++++----------- 2 files changed, 91 insertions(+), 35 deletions(-) diff --git a/biostools/__main__.py b/biostools/__main__.py index ad028de..5125eab 100644 --- a/biostools/__main__.py +++ b/biostools/__main__.py @@ -104,6 +104,7 @@ def extract_process(queue, dir_number_path, next_dir_number_path): extractors.ArchiveExtractor(), extractors.HexExtractor(), extractors.ImageExtractor(), + extractors.ApricotExtractor(), extractors.DellExtractor(), extractors.IntelExtractor(), extractors.OMFExtractor(), diff --git a/biostools/extractors.py b/biostools/extractors.py index 4989a6c..528ecd9 100644 --- a/biostools/extractors.py +++ b/biostools/extractors.py @@ -38,6 +38,61 @@ class Extractor: print('{0}:'.format(self.__class__.__name__), *args, file=sys.stderr) +class ApricotExtractor(Extractor): + """Extract Apricot BIOS recovery files. Only one instance of this format + (Trimond Trent) has been observed, let us know if you find any other!""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self._apricot_pattern = re.compile(b'''@\\(#\\)Apricot ''') + + def extract(self, file_path, file_header, dest_dir, dest_dir_0): + # Stop if this isn't a slightly-bigger-than-power-of-two file. + # The only observed file has a 2071-byte header. + try: + file_size = os.path.getsize(file_path) + except: + return False + pow2 = 1 << math.floor(math.log2(file_size)) + if file_size < 4096 or file_size <= pow2 or file_size > pow2 + 4096: + return False + + # Look for the Apricot signature as a safety net. + if not self._apricot_pattern.search(file_header): + return False + + # Create destination directory and stop if it couldn't be created. + if not util.try_makedirs(dest_dir): + return True + + # Separate file and header. + try: + # Open Apricot file. + in_f = open(file_path, 'rb') + + # Copy header. + out_f = open(os.path.join(dest_dir, ':header:'), 'wb') + out_f.write(in_f.read(file_size - pow2)) + out_f.close() + + # Copy payload. + out_f = open(os.path.join(dest_dir, 'apricot.bin'), 'wb') + data = b' ' + while data: + data = in_f.read(1048576) + out_f.write(data) + out_f.close() + + # Remove Apricot file. + in_f.close() + os.remove(file_path) + except: + pass + + # Return destination directory path. + return dest_dir + class ArchiveExtractor(Extractor): """Extract known archive types.""" @@ -777,20 +832,20 @@ class ISOExtractor(ArchiveExtractor): match = self._eltorito_pattern.search(file_header) if match: # Start a new El Torito extraction file. - f_o = open(elt_path, 'wb') + out_f = open(elt_path, 'wb') # Copy the entire ISO data starting from the boot offset. # Parsing the MBR would have pitfalls of its own... - f_i = open(file_path, 'rb') - f_i.seek(struct.unpack('