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
This commit is contained in:
c7zero
2016-07-15 01:52:27 -07:00
parent 97c8fa4a51
commit 4fd9ee8276
4 changed files with 337 additions and 153 deletions
+326 -144
View File
@@ -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) )
+2 -1
View File
@@ -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
+7 -7
View File
@@ -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
@@ -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