From 4fd9ee82767d0cb86ed5c5bd6462c847c8fddcc3 Mon Sep 17 00:00:00 2001 From: c7zero Date: Fri, 15 Jul 2016 01:52:27 -0700 Subject: [PATCH] UEFI parsing changes 1. Added ability to calculate hashes of EFI binaries 2. Added ability to search EFI binaries by UI name, GUID, hashes or contents matching regular expressions 3. Refactored UEFI firmware image parsing 4. Fixed get_tools_path in helpers --- source/tool/chipsec/hal/spi_uefi.py | 470 ++++++++++++------ source/tool/chipsec/helper/linux/helper.py | 3 +- source/tool/chipsec/helper/oshelper.py | 14 +- source/tool/chipsec/helper/win/win32helper.py | 3 +- 4 files changed, 337 insertions(+), 153 deletions(-) diff --git a/source/tool/chipsec/hal/spi_uefi.py b/source/tool/chipsec/hal/spi_uefi.py index 28cd632e..494ce194 100644 --- a/source/tool/chipsec/hal/spi_uefi.py +++ b/source/tool/chipsec/hal/spi_uefi.py @@ -18,8 +18,8 @@ #Contact information: #chipsec@intel.com # - - + + # ------------------------------------------------------------------------------- # @@ -43,6 +43,8 @@ import struct import sys import time import collections +import hashlib +import re #import phex from chipsec.helper.oshelper import helper @@ -58,43 +60,18 @@ CMD_UEFI_FILE_INSERT_BEFORE = 1 CMD_UEFI_FILE_INSERT_AFTER = 2 CMD_UEFI_FILE_REPLACE = 3 -def save_vol_info( FvOffset, FsGuid, FvLength, FvAttributes, FvHeaderLength, FvChecksum, ExtHeaderOffset, file_path, CalcSum ): - schecksum = '' - if (CalcSum != FvChecksum): schecksum = ' *** checksum mismatch ***' - info = ("Volume offset : 0x%08X\n" % FvOffset) +\ - ("File system GUID : %s\n" % FsGuid) + \ - ("Volume length : 0x%08X (%d)\n" % (FvLength, FvLength)) + \ - ("Attributes : 0x%08X\n" % FvAttributes) + \ - ("Header length : 0x%08X\n" % FvHeaderLength) + \ - ("Checksum : 0x%04X (0x%04X)%s\n" % (FvChecksum, CalcSum, schecksum)) + \ - ("Extended Header Offset : 0x%08X\n" % ExtHeaderOffset) - logger().log( info ) - #write_file( file_path, info, True ) -def save_file_info( cur_offset, Name, Type, Attributes, State, Checksum, Size, file_path, fCalcSum ): - schecksum = '' - if (fCalcSum != Checksum): schecksum = ' *** checksum mismatch ***' - info = ("\tFile offset : 0x%08X\n" % (cur_offset)) + \ - ("\tName : %s\n" % (Name)) + \ - ("\tType : 0x%02X\n" % (Type)) + \ - ("\tAttributes : 0x%08X\n" % (Attributes)) + \ - ("\tState : 0x%02X\n" % (State)) + \ - ("\tChecksum : 0x%04X (0x%04X)%s\n" % (Checksum, fCalcSum, schecksum)) + \ - ("\tSize : 0x%06X (%d)\n" % (Size, Size)) - logger().log( info ) - #write_file( file_path, info, True ) - -def save_section_info( cur_offset, Name, Type, file_path ): - info = ("\t\tSection offset : 0x%08X\n" % (cur_offset)) + \ - ("\t\tName : %s\n" % (Name)) + \ - ("\t\tType : 0x%02X\n" % (Type)) - logger().log( info ) - -def decompress_section_data( _uefi, section_dir_path, sec_fs_name, compressed_data, compression_type ): +def decompress_section_data( _uefi, section_dir_path, sec_fs_name, compressed_data, compression_type, remove_files=False ): compressed_name = os.path.join(section_dir_path, "%s.gz" % sec_fs_name) uncompressed_name = os.path.join(section_dir_path, sec_fs_name) write_file(compressed_name, compressed_data) - return _uefi.decompress_EFI_binary( compressed_name, uncompressed_name, compression_type ) + uncompressed_image = _uefi.decompress_EFI_binary( compressed_name, uncompressed_name, compression_type ) + if remove_files: + try: + os.remove(compressed_name) + os.remove(uncompressed_name) + except: pass + return uncompressed_image def compress_image( _uefi, image, compression_type ): precomress_file = 'uefi_file.raw.comp' @@ -106,66 +83,6 @@ def compress_image( _uefi, image, compression_type ): os.remove(compressed_file) return compressed_image -def parse_uefi_section( _uefi, data, Size, offset, polarity, parent_offset, parent_path, decode_log_path ): - sec_offset, next_sec_offset, SecName, SecType, SecBody, SecHeaderSize = NextFwFileSection(data, Size, offset, polarity) - secn = 0 - ui_string = None - efi_file = None - while next_sec_offset != None: - if (SecName != None): - save_section_info( parent_offset + sec_offset, SecName, SecType, decode_log_path ) - sec_fs_name = "%02d_%s" % (secn, SecName) - section_path = os.path.join(parent_path, sec_fs_name) - if (SecType in (EFI_SECTION_PE32, EFI_SECTION_TE, EFI_SECTION_PIC, EFI_SECTION_COMPATIBILITY16)): - type2ext = {EFI_SECTION_PE32: 'pe32', EFI_SECTION_TE: 'te', EFI_SECTION_PIC: 'pic', EFI_SECTION_COMPATIBILITY16: 'c16'} - sec_fs_name = "%02d_%s.%s.efi" % (secn, SecName, type2ext[SecType]) - if ui_string != None: - sec_fs_name = ui_string - ui_string = None - efi_file = sec_fs_name - section_path = os.path.join(parent_path, sec_fs_name) - write_file( section_path, SecBody[SecHeaderSize:] ) - else: - write_file( section_path, SecBody[SecHeaderSize:] ) - if (SecType == EFI_SECTION_USER_INTERFACE): - ui_string = unicode(SecBody[SecHeaderSize:], "utf-16-le")[:-1] - if (ui_string[-4:] != '.efi'): ui_string = "%s.efi" % ui_string - #print ui_string - if efi_file != None: - os.rename(os.path.join(parent_path, efi_file), os.path.join(parent_path, ui_string)) - efi_file = None - if (SecType in (EFI_SECTION_COMPRESSION, EFI_SECTION_GUID_DEFINED, EFI_SECTION_FIRMWARE_VOLUME_IMAGE, EFI_SECTION_RAW)): - section_dir_path = "%s.dir" % section_path - os.makedirs( section_dir_path ) - if (SecType == EFI_SECTION_COMPRESSION): - UncompressedLength, CompressionType = struct.unpack(EFI_COMPRESSION_SECTION, SecBody[SecHeaderSize:SecHeaderSize+EFI_COMPRESSION_SECTION_size]) - decompressed = decompress_section_data(_uefi, section_dir_path, sec_fs_name, SecBody[SecHeaderSize+EFI_COMPRESSION_SECTION_size:], CompressionType) - if decompressed: - parse_uefi_section(_uefi, decompressed, len(decompressed), 0, polarity, 0, section_dir_path, decode_log_path) - pass - elif (SecType == EFI_SECTION_GUID_DEFINED): - # TODO: decode section based on its GUID - # Only CRC32 guided sectioni can be decoded for now - guid0, guid1, guid2, guid3, DataOffset, Attributes = struct.unpack(EFI_GUID_DEFINED_SECTION, SecBody[SecHeaderSize:SecHeaderSize+EFI_GUID_DEFINED_SECTION_size]) - sguid = guid_str(guid0, guid1, guid2, guid3) - logger().log("\t\t\tDefinition guid: %s\n" % sguid +\ - "\t\t\tData offset : 0x%04X\n" % DataOffset +\ - "\t\t\tAttributes : 0x%04X\n" % Attributes \ - ) - if (sguid == EFI_CRC32_GUIDED_SECTION_EXTRACTION_PROTOCOL_GUID): - parse_uefi_section(_uefi, SecBody[DataOffset:], Size - DataOffset, 0, polarity, 0, section_dir_path, decode_log_path) - elif (sguid == LZMA_CUSTOM_DECOMPRESS_GUID): - decompressed = decompress_section_data(_uefi, section_dir_path, sec_fs_name, SecBody[DataOffset:], 2) - if decompressed: - parse_uefi_section(_uefi, decompressed, len(decompressed), 0, polarity, 0, section_dir_path, decode_log_path) - #else: - # write_file( os.path.join(section_dir_path, "%s-%04X" % (sguid, Attributes)), SecBody[DataOffset:] ) - pass - elif (SecType == EFI_SECTION_FIRMWARE_VOLUME_IMAGE or SecType == EFI_SECTION_RAW): - parse_uefi_region(_uefi, SecBody[SecHeaderSize:], section_dir_path) - sec_offset, next_sec_offset, SecName, SecType, SecBody, SecHeaderSize = NextFwFileSection(data, Size, next_sec_offset, polarity) - secn = secn + 1 - def modify_uefi_region(data, command, guid, uefi_file = ''): RgLengthChange = 0 @@ -222,70 +139,335 @@ def modify_uefi_region(data, command, guid, uefi_file = ''): FvOffset, FsGuid, FvLength, FvAttributes, FvHeaderLength, FvChecksum, ExtHeaderOffset, FvImage, CalcSum = NextFwVolume(data, FvOffset + FvLength) return data -def parse_uefi_region( _uefi, data, uefi_region_path ): - voln = 0 - FvOffset, FsGuid, FvLength, FvAttributes, FvHeaderLength, FvChecksum, ExtHeaderOffset, FvImage, CalcSum = NextFwVolume(data) - while FvOffset != None: - decode_log_path = os.path.join(uefi_region_path, "efi_firmware_volumes.log") - volume_file_path = os.path.join( uefi_region_path, "%02d_%s" % (voln, FsGuid) ) - volume_path = os.path.join( uefi_region_path, "%02d_%s.dir" % (voln, FsGuid) ) - if not os.path.exists( volume_path ): - os.makedirs( volume_path ) - write_file( volume_file_path, FvImage ) - save_vol_info( FvOffset, FsGuid, FvLength, FvAttributes, FvHeaderLength, FvChecksum, ExtHeaderOffset, decode_log_path, CalcSum ) - polarity = bit_set(FvAttributes, EFI_FVB2_ERASE_POLARITY) - if (FsGuid == ADDITIONAL_NV_STORE_GUID): - nvram_fname = os.path.join(volume_path, 'SHADOW_NVRAM') - _uefi.parse_EFI_variables( nvram_fname, FvImage, False, 'evsa' ) - elif ((FsGuid == EFI_FIRMWARE_FILE_SYSTEM2_GUID) or (FsGuid == EFI_FIRMWARE_FILE_SYSTEM_GUID)): - cur_offset, next_offset, Name, Type, Attributes, State, Checksum, Size, FileImage, HeaderSize, UD, fCalcSum = NextFwFile(FvImage, FvLength, FvHeaderLength, polarity) - while next_offset != None: - #print "File: offset=%08X, next_offset=%08X, UD=%s\n" % (cur_offset, next_offset, UD) - if (Name != None): - file_type_str = "UNKNOWN_%02X" % Type - if Type in FILE_TYPE_NAMES.keys(): - file_type_str = FILE_TYPE_NAMES[Type] - file_path = os.path.join( volume_path, "%s.%s-%02X" % (Name, file_type_str, Type)) - if os.path.exists( file_path ): - file_path = file_path + ("_%08X" % cur_offset) - write_file( file_path, FileImage ) - file_dir_path = "%s.dir" % file_path - save_file_info( FvOffset + cur_offset, Name, Type, Attributes, State, Checksum, Size, decode_log_path, fCalcSum) - if (Type not in (EFI_FV_FILETYPE_ALL, EFI_FV_FILETYPE_RAW, EFI_FV_FILETYPE_FFS_PAD)): - os.makedirs( file_dir_path ) - parse_uefi_section(_uefi, FileImage, Size, HeaderSize, polarity, FvOffset + cur_offset, file_dir_path, decode_log_path) - elif (Type == EFI_FV_FILETYPE_RAW): - if ((Name == NVAR_NVRAM_FS_FILE) and UD): - nvram_fname = os.path.join(file_dir_path, 'SHADOW_NVRAM') - _uefi.parse_EFI_variables( nvram_fname, FvImage, False, 'nvar' ) - cur_offset, next_offset, Name, Type, Attributes, State, Checksum, Size, FileImage, HeaderSize, UD, fCalcSum = NextFwFile(FvImage, FvLength, next_offset, polarity) - FvOffset, FsGuid, FvLength, FvAttributes, FvHeaderLength, FvChecksum, ExtHeaderOffset, FvImage, CalcSum = NextFwVolume(data, FvOffset+FvLength) - voln = voln + 1 +DEF_INDENT = " " +class EFI_MODULE(object): + def __init__(self, Offset, Guid, HeaderSize, Attributes, Image): + self.Offset = Offset + self.Guid = Guid + self.HeaderSize = HeaderSize + self.Attributes = Attributes + self.Image = Image + + self.clsname = "EFI module" + self.indent = '' + + self.MD5 = '' + self.SHA1 = '' + self.SHA256 = '' + + def __str__(self): + _ind = self.indent + DEF_INDENT + return "%sMD5 : %s\n%sSHA1 : %s\n%sSHA256: %s\n" % (_ind,self.MD5,_ind,self.SHA1,_ind,self.SHA256) + + +class EFI_FV(EFI_MODULE): + def __init__(self, Offset, Guid, Size, Attributes, HeaderSize, Checksum, ExtHeaderOffset, Image, CalcSum): + EFI_MODULE.__init__(self, Offset, Guid, HeaderSize, Attributes, Image) + self.clsname = "EFI firmware volume" + self.Size = Size + self.Checksum = Checksum + self.ExtHeaderOffset = ExtHeaderOffset + self.CalcSum = CalcSum + + def __str__(self): + schecksum = ('%04Xh (%04Xh) *** checksum mismatch ***' % (self.Checksum,self.CalcSum)) if self.CalcSum != self.Checksum else ('%04Xh' % self.Checksum) + _s = "\n%s%s +%08Xh {%s}: Size %08Xh, Attr %08Xh, HdrSize %04Xh, ExtHdrOffset %08Xh, Checksum %s" % (self.indent,self.clsname,self.Offset,self.Guid,self.Size,self.Attributes,self.HeaderSize,self.ExtHeaderOffset,schecksum) + _s += ("\n" + super(EFI_FV, self).__str__()) + return _s + +class EFI_FILE(EFI_MODULE): + def __init__(self, Offset, Name, Type, Attributes, State, Checksum, Size, Image, HeaderSize, UD, CalcSum): + EFI_MODULE.__init__(self, Offset, Name, HeaderSize, Attributes, Image) + self.clsname = "EFI binary" + self.Name = Name + self.Type = Type + self.State = State + self.Size = Size + self.Checksum = Checksum + self.UD = UD + self.CalcSum = CalcSum + + def __str__(self): + schecksum = ('%04Xh (%04Xh) *** checksum mismatch ***' % (self.Checksum,self.CalcSum)) if self.CalcSum != self.Checksum else ('%04Xh' % self.Checksum) + _s = "\n%s%s +%08Xh {%s}: Type %02Xh, Attr %08Xh, State %02Xh, Size %06Xh, Checksum %s" % (self.indent,self.clsname,self.Offset,self.Guid,self.Type,self.Attributes,self.State,self.Size,schecksum) + _s += ("\n" + super(EFI_FILE, self).__str__()) + return _s + +class EFI_SECTION(EFI_MODULE): + def __init__(self, Offset, Name, Type, Image, HeaderSize): + EFI_MODULE.__init__(self, Offset, None, HeaderSize, None, Image) + self.clsname = "EFI section" + self.Name = Name + self.Type = Type + + self.ui_string = '' + self.DataOffset = None + + def __str__(self): + _s = "%s%s +%08Xh %-16s: Type %02Xh %s" % (self.indent,self.clsname,self.Offset,self.Name,self.Type,self.ui_string) + if self.Guid: _s += ", GUID {%s}" % self.Guid + if self.Attributes: _s += ", Attr %04Xh" % self.Attributes + if self.DataOffset: _s += ", DataOffset %04Xh" % self.DataOffset + return _s + +def dump_fw_file( fwbin, volume_path ): + type_s = FILE_TYPE_NAMES[fwbin.Type] if fwbin.Type in FILE_TYPE_NAMES.keys() else ("UNKNOWN_%02X" % fwbin.Type) + pth = os.path.join( volume_path, "%s.%s-%02X" % (fwbin.Name, type_s, fwbin.Type)) + if os.path.exists( pth ): pth += ("_%08X" % fwbin.Offset) + write_file( pth, fwbin.Image ) + if fwbin.MD5 != '': write_file( ("%s.md5" % pth), fwbin.MD5 ) + if fwbin.SHA1 != '': write_file( ("%s.sha1" % pth), fwbin.SHA1 ) + if fwbin.SHA256 != '': write_file( ("%s.sha256" % pth), fwbin.SHA256 ) + return ("%s.dir" % pth) + +def dump_fv( fv, voln, uefi_region_path ): + fv_pth = os.path.join( uefi_region_path, "%02d_%s" % (voln, fv.Guid) ) + write_file( fv_pth, fv.Image ) + if fv.MD5 != '': write_file( ("%s.md5" % fv_pth), fv.MD5 ) + if fv.SHA1 != '': write_file( ("%s.sha1" % fv_pth), fv.SHA1 ) + if fv.SHA256 != '': write_file( ("%s.sha256" % fv_pth), fv.SHA256 ) + volume_path = os.path.join( uefi_region_path, "%02d_%s.dir" % (voln, fv.Guid) ) + if not os.path.exists( volume_path ): os.makedirs( volume_path ) + return volume_path + +def dump_section( sec, secn, parent_path, efi_file ): + if sec.Name is not None: + sec_fs_name = "%02d_%s" % (secn, sec.Name) + section_path = os.path.join(parent_path, sec_fs_name) + if sec.Type in (EFI_SECTION_PE32, EFI_SECTION_TE, EFI_SECTION_PIC, EFI_SECTION_COMPATIBILITY16): + type2ext = {EFI_SECTION_PE32: 'pe32', EFI_SECTION_TE: 'te', EFI_SECTION_PIC: 'pic', EFI_SECTION_COMPATIBILITY16: 'c16'} + sec_fs_name = "%02d_%s.%s.efi" % (secn, sec.Name, type2ext[sec.Type]) + efi_file = sec_fs_name + section_path = os.path.join(parent_path, sec_fs_name) + write_file( section_path, sec.Image[sec.HeaderSize:] ) + else: + write_file( section_path, sec.Image[sec.HeaderSize:] ) + if sec.Type == EFI_SECTION_USER_INTERFACE: + ui_string = unicode(sec.Image[sec.HeaderSize:], "utf-16-le")[:-1] + if ui_string[-4:] != '.efi': ui_string = "%s.efi" % ui_string + if efi_file is not None: + os.rename(os.path.join(parent_path, efi_file), os.path.join(parent_path, ui_string)) + efi_file = None + + section_dir_path = "%s.dir" % section_path + return sec_fs_name,section_dir_path,efi_file + +def add_hashes( efi ): + if efi.Image is None: return + hmd5 = hashlib.md5() + hmd5.update( efi.Image ) + efi.MD5 = hmd5.hexdigest() + hsha1 = hashlib.sha1() + hsha1.update( efi.Image ) + efi.SHA1 = hsha1.hexdigest() + hsha256 = hashlib.sha256() + hsha256.update( efi.Image ) + efi.SHA256 = hsha256.hexdigest() + +# +# Format of EFI binaries match rules (any field can be empty or missing): +# - Individual rules are OR'ed +# - match criteria within a given rule are AND'ed +# +# Example: +# { +# "rule00": { "name": "module0", "guid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" } +# "rule01": { "md5": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "sha1": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "sha256": "", "regexp": "" } +# } +# +# Above search configuration will result in a match if the following EFI module is found: +# - module with name "module0" AND guid "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" +# OR +# - module with md5 hash "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" AND sha1 hash "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +# +MATCH_NAME = 0x1 +MATCH_GUID = (0x1 << 1) +MATCH_REGEXP = (0x1 << 2) +MATCH_HASH_MD5 = (0x1 << 3) +MATCH_HASH_SHA1 = (0x1 << 4) +MATCH_HASH_SHA256 = (0x1 << 5) + +def check_match_criteria( efi, match_criteria ): + bfound = False + _log = '' + + for k in match_criteria.keys(): + match_mask = 0x00000000 + match_result = 0x00000000 + rule = match_criteria[k] + # + # Determine which criteria are defined in the current rule + # + if ('name' in rule) and (rule['name'] != ''): match_mask |= MATCH_NAME + if ('guid' in rule) and (rule['guid'] != ''): match_mask |= MATCH_GUID + if ('regexp' in rule) and (rule['regexp'] != ''): match_mask |= MATCH_REGEXP + if ('md5' in rule) and (rule['md5'] != ''): match_mask |= MATCH_HASH_MD5 + if ('sha1' in rule) and (rule['sha1'] != ''): match_mask |= MATCH_HASH_SHA1 + if ('sha256' in rule) and (rule['sha256'] != ''): match_mask |= MATCH_HASH_SHA256 + + _s = "[uefi] found match %s: %s" % (k,efi.clsname) + # + # Check criteria defined in the current rule against the current EFI module + # + if (match_mask & MATCH_NAME) == MATCH_NAME: + if type(efi) is EFI_SECTION and efi.ui_string == rule['name']: match_result |= MATCH_NAME + if (match_mask & MATCH_GUID) == MATCH_GUID: + if ((type(efi) is EFI_FILE) and (efi.Name == rule['guid'])) or (efi.Guid == rule['guid']): match_result |= MATCH_GUID + if (match_mask & MATCH_REGEXP) == MATCH_REGEXP: + m = re.compile(rule['regexp']).search( efi.Image ) + if m: + match_result |= MATCH_REGEXP + _log = "%s contains '%s' at [%Xh:%Xh] matching regexp '%s' " % (_s,m.group(0),m.start(),m.end(),rule['regexp']) + if (match_mask & MATCH_HASH_MD5) == MATCH_HASH_MD5: + if efi.MD5 == rule['md5']: match_result |= MATCH_HASH_MD5 + if (match_mask & MATCH_HASH_SHA1) == MATCH_HASH_SHA1: + if efi.SHA1 == rule['sha1']: match_result |= MATCH_HASH_SHA1 + if (match_mask & MATCH_HASH_SHA256) == MATCH_HASH_SHA256: + if efi.SHA256 == rule['sha256']: match_result |= MATCH_HASH_SHA256 + + brule_match = ((match_result & match_mask) == match_mask) + bfound = bfound or brule_match + if brule_match: + if (match_result & MATCH_NAME ) == MATCH_NAME : logger().log( "%s with name = '%s'" % (_s,rule['name']) ) + if (match_result & MATCH_GUID ) == MATCH_GUID : logger().log( "%s with GUID = {%s}" % (_s,rule['guid']) ) + if (match_result & MATCH_REGEXP ) == MATCH_REGEXP : logger().log( _log ) + if (match_result & MATCH_HASH_MD5 ) == MATCH_HASH_MD5 : logger().log( "%s has MD5 = %s" % (_s,rule['md5']) ) + if (match_result & MATCH_HASH_SHA1 ) == MATCH_HASH_SHA1 : logger().log( "%s has SHA-1 = %s" % (_s,rule['sha1']) ) + if (match_result & MATCH_HASH_SHA256) == MATCH_HASH_SHA256: logger().log( "%s has SHA-256 = %s" % (_s,rule['sha256']) ) + + if bfound: logger().log( "[uefi] matching EFI module:\n%s" % efi ) + return bfound + + +def traverse_uefi_section( _uefi, data, Size, offset, polarity, parent_offset, printall=True, dumpall=True, parent_path='', match_criteria=None ): + secn, efi_file, section_dir_path = 0, None, '' + found = False + bsearch = (match_criteria is not None) + + _off, next_offset, _name, _type, _img, _hdrsz = NextFwFileSection( data, Size, offset, polarity ) + sec = EFI_SECTION( _off, _name, _type, _img, _hdrsz ) + sec.indent = DEF_INDENT*2 + #add_hashes( sec ) + while next_offset is not None: + sec_fs_name = "%02d_%s" % (secn, sec.Name) + if sec.Type == EFI_SECTION_USER_INTERFACE: + sec.ui_string = unicode(sec.Image[sec.HeaderSize:], "utf-16-le")[:-1] + + if printall: logger().log( sec ) + if dumpall: sec_fs_name,section_dir_path,efi_file = dump_section( sec, secn, parent_path, efi_file ) + if bsearch and check_match_criteria( sec, match_criteria ): return True + + if sec.Type in (EFI_SECTION_COMPRESSION, EFI_SECTION_GUID_DEFINED, EFI_SECTION_FIRMWARE_VOLUME_IMAGE, EFI_SECTION_RAW): + if dumpall: os.makedirs( section_dir_path ) + if sec.Type == EFI_SECTION_COMPRESSION: + ul, ct = struct.unpack(EFI_COMPRESSION_SECTION, sec.Image[sec.HeaderSize:sec.HeaderSize+EFI_COMPRESSION_SECTION_size]) + d = decompress_section_data( _uefi, section_dir_path, sec_fs_name, sec.Image[sec.HeaderSize+EFI_COMPRESSION_SECTION_size:], ct, True ) + if d: + found = traverse_uefi_section( _uefi, d, len(d), 0, polarity, 0, printall, dumpall, section_dir_path, match_criteria ) + if bsearch and found: return True + elif sec.Type == EFI_SECTION_GUID_DEFINED: + guid0, guid1, guid2, guid3, sec.DataOffset, sec.Attributes = struct.unpack(EFI_GUID_DEFINED_SECTION, sec.Image[sec.HeaderSize:sec.HeaderSize+EFI_GUID_DEFINED_SECTION_size]) + sec.Guid = guid_str(guid0, guid1, guid2, guid3) + if sec.Guid == EFI_CRC32_GUIDED_SECTION_EXTRACTION_PROTOCOL_GUID: + found = traverse_uefi_section( _uefi, sec.Image[sec.DataOffset:], Size - sec.DataOffset, 0, polarity, 0, printall, dumpall, section_dir_path,match_criteria ) + if bsearch and found: return True + elif sec.Guid == LZMA_CUSTOM_DECOMPRESS_GUID: + d = decompress_section_data( _uefi, section_dir_path, sec_fs_name, sec.Image[sec.DataOffset:], 2, True ) + if d: + found = traverse_uefi_section( _uefi, d, len(d), 0, polarity, 0, printall, dumpall, section_dir_path, match_criteria ) + if bsearch and found: return True + elif sec.Type in (EFI_SECTION_FIRMWARE_VOLUME_IMAGE, EFI_SECTION_RAW): + found = traverse_uefi_region( _uefi, sec.Image[sec.HeaderSize:], section_dir_path, printall, dumpall, match_criteria ) + if bsearch and found: return True + + _off, next_offset, _name, _type, _img, _hdrsz = NextFwFileSection( data, Size, next_offset, polarity ) + sec = EFI_SECTION( _off, _name, _type, _img, _hdrsz ) + sec.indent = DEF_INDENT*2 + #add_hashes( sec ) + secn += 1 + return found + +# +# traverse_uefi_region - searches for a specific EFI binary by its file/UI name, EFI GUID or hash +# +# Input arguments: +# _uefi - instance of chipsec.hal.uefi.UEFI class +# data - an image containing UEFI firmware volumes +# printall - print EFI binaries hierarchy +# dumpall - dump all EFI binaries onto the file system +# uefi_path - root path for EFI hierarchy (used if dumpall==True) +# match_criteria - criteria to search for sepecific node in EFI hierarchy (Name, GUID, hash, etc.) +# +def traverse_uefi_region( _uefi, data, uefi_path='', printall=True, dumpall=True, match_criteria=None ): + voln, fwbin_dir = 0, '' + found = False + bsearch = (match_criteria is not None) + + fv_off, fv_guid, fv_size, fv_attr, fv_hdrsz, fv_csum, fv_hdroff, fv_img, fv_calccsum = NextFwVolume( data ) + fv = EFI_FV( fv_off, fv_guid, fv_size, fv_attr, fv_hdrsz, fv_csum, fv_hdroff, fv_img, fv_calccsum ) + add_hashes( fv ) + while fv.Offset is not None: + if printall: logger().log( fv ) + if dumpall: volume_path = dump_fv( fv, voln, uefi_path ) + if bsearch and check_match_criteria( fv, match_criteria ): return True + + polarity = bit_set( fv.Attributes, EFI_FVB2_ERASE_POLARITY ) + if fv.Guid == ADDITIONAL_NV_STORE_GUID: + if dumpall: _uefi.parse_EFI_variables( os.path.join(volume_path, 'SHADOW_NVRAM'), fv.Image, False, FWType.EFI_FW_TYPE_EVSA ) + elif fv.Guid == EFI_FIRMWARE_FILE_SYSTEM2_GUID or fv.Guid == EFI_FIRMWARE_FILE_SYSTEM_GUID: + foff, next_offset, fname, ftype, fattr, fstate, fcsum, fsz, fimg, fhdrsz, fUD, fcalcsum = NextFwFile( fv.Image, fv.Size, fv.HeaderSize, polarity ) + fwbin = EFI_FILE( foff, fname, ftype, fattr, fstate, fcsum, fsz, fimg, fhdrsz, fUD, fcalcsum ) + fwbin.indent = DEF_INDENT + add_hashes( fwbin ) + while next_offset is not None: + if fwbin.Name is not None: + if printall: logger().log( fwbin ) + if dumpall: fwbin_dir = dump_fw_file( fwbin, volume_path ) + if bsearch and check_match_criteria( fwbin, match_criteria ): return True + + if fwbin.Type not in (EFI_FV_FILETYPE_ALL, EFI_FV_FILETYPE_RAW, EFI_FV_FILETYPE_FFS_PAD): + if dumpall: os.makedirs( fwbin_dir ) + found = traverse_uefi_section( _uefi, fwbin.Image, fwbin.Size, fwbin.HeaderSize, polarity, fv.Offset + fwbin.Offset, printall, dumpall, fwbin_dir, match_criteria ) + if bsearch and found: + #logger().log( "[uefi] " + str(fwbin) ) + #logger().log( "[uefi] " + str(fv) ) + return True + elif fwbin.Type == EFI_FV_FILETYPE_RAW: + if fwbin.Name == NVAR_NVRAM_FS_FILE and fwbin.UD: + if dumpall: _uefi.parse_EFI_variables( os.path.join(file_dir_path, 'SHADOW_NVRAM'), FvImage, False, FWType.EFI_FW_TYPE_NVAR ) + + foff, next_offset, fname, ftype, fattr, fstate, fcsum, fsz, fimg, fhdrsz, fUD, fcalcsum = NextFwFile( fv.Image, fv.Size, next_offset, polarity ) + fwbin = EFI_FILE( foff, fname, ftype, fattr, fstate, fcsum, fsz, fimg, fhdrsz, fUD, fcalcsum ) + fwbin.indent = DEF_INDENT + add_hashes( fwbin ) + + fv_off, fv_guid, fv_size, fv_attr, fv_hdrsz, fv_csum, fv_hdroff, fv_img, fv_calccsum = NextFwVolume( data, fv.Offset + fv.Size ) + fv = EFI_FV( fv_off, fv_guid, fv_size, fv_attr, fv_hdrsz, fv_csum, fv_hdroff, fv_img, fv_calccsum ) + add_hashes( fv ) + voln += 1 + return found def parse_uefi_region_from_file( _uefi, filename, outpath = None): - if outpath is None: - outpath = os.path.join( helper().getcwd(), filename + ".dir" ) - if not os.path.exists( outpath ): - os.makedirs( outpath ) - - #uefi_region_path = os.path.join( os.getcwd(), filename + "_UEFI_region" ) - #if not os.path.exists( uefi_region_path ): - # os.makedirs( uefi_region_path ) - + if outpath is None: outpath = os.path.join( helper().getcwd(), filename + ".dir" ) + if not os.path.exists( outpath ): os.makedirs( outpath ) rom = read_file( filename ) - parse_uefi_region( _uefi, rom, outpath ) + traverse_uefi_region( _uefi, rom, outpath, True, True ) def decode_uefi_region(_uefi, pth, fname, fwtype): + bios_pth = os.path.join( pth, fname + '.dir' ) if not os.path.exists( bios_pth ): os.makedirs( bios_pth ) fv_pth = os.path.join( bios_pth, 'FV' ) if not os.path.exists( fv_pth ): os.makedirs( fv_pth ) + + # Decoding UEFI Firmware Volumes parse_uefi_region_from_file( _uefi, fname, fv_pth ) + # Decoding EFI Variables NVRAM region_data = read_file( fname ) nvram_fname = os.path.join( bios_pth, ('nvram_%s' % fwtype) ) diff --git a/source/tool/chipsec/helper/linux/helper.py b/source/tool/chipsec/helper/linux/helper.py index f4a8155d..a45e8c88 100644 --- a/source/tool/chipsec/helper/linux/helper.py +++ b/source/tool/chipsec/helper/linux/helper.py @@ -762,7 +762,8 @@ class LinuxHelper(Helper): # File system # def get_tools_path( self ): - return os.path.join('..','..','tools','edk2','linux') + p = os.path.join(chipsec.file.get_main_dir(), "..", "..", 'tools','edk2','linux') + return os.path.normpath(p) def get_compression_tool_path( self, compression_type ): tool = None diff --git a/source/tool/chipsec/helper/oshelper.py b/source/tool/chipsec/helper/oshelper.py index 72298172..5f8f7451 100644 --- a/source/tool/chipsec/helper/oshelper.py +++ b/source/tool/chipsec/helper/oshelper.py @@ -322,17 +322,17 @@ class OsHelper: # Decompress binary with OS specific tools # def decompress_file( self, CompressedFileName, OutputFileName, CompressionType ): - from subprocess import call + import subprocess if (CompressionType == 0): # not compressed shutil.copyfile(CompressedFileName, OutputFileName) else: exe = self.helper.get_compression_tool_path( CompressionType ) if exe is None: return None try: - call( '%s -d -o %s %s' % (exe,OutputFileName,CompressedFileName) ) + subprocess.call( '%s -d -o %s %s' % (exe,OutputFileName,CompressedFileName), stdout=open(os.devnull, 'wb') ) except BaseException, msg: logger().error( str(msg) ) - if logger().VERBOSE: logger().log_bad( traceback.format_exc() ) + if logger().DEBUG: logger().log_bad( traceback.format_exc() ) return None return chipsec.file.read_file( OutputFileName ) @@ -341,17 +341,17 @@ class OsHelper: # Compress binary with OS specific tools # def compress_file( self, FileName, OutputFileName, CompressionType ): - from subprocess import call + import subprocess if (CompressionType == 0): # not compressed shutil.copyfile(FileName, OutputFileName) else: exe = self.helper.get_compression_tool_path( CompressionType ) if exe is None: return None try: - call( '%s -e -o %s %s' % (exe,OutputFileName,FileName) ) + subprocess.call( '%s -e -o %s %s' % (exe,OutputFileName,FileName), stdout=open(os.devnull, 'wb') ) except BaseException, msg: logger().error( str(msg) ) - if logger().VERBOSE: logger().log_bad( traceback.format_exc() ) + if logger().DEBUG: logger().log_bad( traceback.format_exc() ) return None return chipsec.file.read_file( OutputFileName ) @@ -365,6 +365,6 @@ def helper(): _helper = OsHelper() except BaseException, msg: logger().error( str(msg) ) - if logger().VERBOSE: logger().log_bad(traceback.format_exc()) + if logger().DEBUG: logger().log_bad(traceback.format_exc()) raise return _helper diff --git a/source/tool/chipsec/helper/win/win32helper.py b/source/tool/chipsec/helper/win/win32helper.py index ad25ed0e..d5fd04d2 100644 --- a/source/tool/chipsec/helper/win/win32helper.py +++ b/source/tool/chipsec/helper/win/win32helper.py @@ -918,7 +918,8 @@ class Win32Helper(Helper): # File system # def get_tools_path( self ): - return os.path.join('..','..','tools','edk2','win') + p = os.path.join(chipsec.file.get_main_dir(), "..", "..", 'tools','edk2','win') + return os.path.normpath(p) def get_compression_tool_path( self, compression_type ): tool = None