hackedteam/vector-edk

View on GitHub
vector-uefi/fd/tool/chipsec/hal/uefi.py

Summary

Maintainability
D
2 days
Test Coverage
#!/usr/local/bin/python
#CHIPSEC: Platform Security Assessment Framework
#Copyright (c) 2010-2014, Intel Corporation
# 
#This program is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public License
#as published by the Free Software Foundation; Version 2.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
#Contact information:
#chipsec@intel.com
#



# -------------------------------------------------------------------------------
#
# CHIPSEC: Platform Hardware Security Assessment Framework
# (c) 2010-2012 Intel Corporation
#
# -------------------------------------------------------------------------------
## \addtogroup hal
# chipsec/hal/uefi.py
# ============================
# Main UEFI component using platform specific and common UEFI functionality
#
#
#
__version__ = '1.0'

import struct
import sys

from collections import namedtuple
import collections

from chipsec.hal.uefi_common import *
from chipsec.hal.uefi_platform import *

from chipsec.logger import *
from chipsec.hal.mmio import *
from chipsec.hal.spi import *
from chipsec.file import *


EFI_VAR_NAME_PK         = 'PK'
EFI_VAR_NAME_KEK        = 'KEK'
EFI_VAR_NAME_db         = 'db'
EFI_VAR_NAME_dbx        = 'dbx'
EFI_VAR_NAME_SecureBoot = 'SecureBoot'
EFI_VAR_NAME_SetupMode  = 'SetupMode'
EFI_VAR_NAME_CustomMode = 'CustomMode'

EFI_VAR_GUID_SecureBoot = '8BE4DF61-93CA-11D2-AA0D-00E098032B8C'
EFI_VAR_GUID_db         = 'D719B2CB-3D3A-4596-A3BC-DAD00E67656F'

EFI_VARIABLE_DICT = {
EFI_VAR_NAME_PK        : EFI_VAR_GUID_SecureBoot,
EFI_VAR_NAME_KEK       : EFI_VAR_GUID_SecureBoot,
EFI_VAR_NAME_db        : EFI_VAR_GUID_db,
EFI_VAR_NAME_dbx       : EFI_VAR_GUID_db,
EFI_VAR_NAME_SecureBoot: EFI_VAR_GUID_SecureBoot,
EFI_VAR_NAME_SetupMode : EFI_VAR_GUID_SecureBoot,
EFI_VAR_NAME_CustomMode: EFI_VAR_GUID_SecureBoot
}


SECURE_BOOT_KEY_VARIABLES = (EFI_VAR_NAME_PK, EFI_VAR_NAME_KEK, EFI_VAR_NAME_db, EFI_VAR_NAME_dbx)
SECURE_BOOT_VARIABLES     = (EFI_VAR_NAME_SecureBoot, EFI_VAR_NAME_SetupMode, EFI_VAR_NAME_CustomMode) + SECURE_BOOT_KEY_VARIABLES
AUTHENTICATED_VARIABLES   = ('AuthVarKeyDatabase', 'certdb') + SECURE_BOOT_VARIABLES
SUPPORTED_EFI_VARIABLES   = ('BootOrder', 'Boot####', 'DriverOrder', 'Driver####') + AUTHENTICATED_VARIABLES


def get_attr_string( attr ):
    attr_str = ' '
    if IS_VARIABLE_ATTRIBUTE( attr, EFI_VARIABLE_NON_VOLATILE ):
        attr_str = attr_str + 'NV+'
    if IS_VARIABLE_ATTRIBUTE( attr, EFI_VARIABLE_BOOTSERVICE_ACCESS ):
        attr_str = attr_str + 'BS+'
    if IS_VARIABLE_ATTRIBUTE( attr, EFI_VARIABLE_RUNTIME_ACCESS ):
        attr_str = attr_str + 'RT+'
    if IS_VARIABLE_ATTRIBUTE( attr, EFI_VARIABLE_HARDWARE_ERROR_RECORD ):
        attr_str = attr_str + 'HER+'
    if IS_VARIABLE_ATTRIBUTE( attr, EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS ):
        attr_str = attr_str + 'AWS+'
    if IS_VARIABLE_ATTRIBUTE( attr, EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS ):
        attr_str = attr_str + 'TBAWS+'
    if IS_VARIABLE_ATTRIBUTE( attr, EFI_VARIABLE_APPEND_WRITE ):
        attr_str = attr_str + 'AW+'
    return attr_str[:-1].lstrip()



def print_efi_variable( offset, efi_var_buf, EFI_var_header, efi_var_name, efi_var_data, efi_var_guid, efi_var_attributes ):
        logger().log( '\n--------------------------------' )
        logger().log( 'EFI Variable (offset = 0x%x):' % offset )
        logger().log( '--------------------------------' )

        # Print Variable Name
        logger().log( 'Name      : %s' % efi_var_name )
        # Print Variable GUID
        logger().log( 'Guid      : %s' % efi_var_guid )

        # Print Variable State
        if EFI_var_header:
           if 'State' in EFI_var_header._fields:
              state = getattr(EFI_var_header, 'State')
              state_str = 'State     :'
              if IS_VARIABLE_STATE( state, VAR_IN_DELETED_TRANSITION ):
                 state_str = state_str + ' IN_DELETED_TRANSITION +'
              if IS_VARIABLE_STATE( state, VAR_DELETED ):
                 state_str = state_str + ' DELETED +'
              if IS_VARIABLE_STATE( state, VAR_ADDED ):
                 state_str = state_str + ' ADDED +'
              logger().log( state_str )
        
           # Print Variable Complete Header
           if logger().VERBOSE:
              if EFI_var_header.__str__:
                 logger().log( EFI_var_header )
              else:
                 logger().log( 'Decoded Header (%s):' % EFI_VAR_DICT[ self._FWType ]['name'] )
                 for attr in EFI_var_header._fields:
                    logger().log( '%s = %X' % ('{0:<16}'.format(attr), getattr(EFI_var_header, attr)) )
        
        attr_str = ('Attributes: 0x%X ( ' % efi_var_attributes) + get_attr_string( efi_var_attributes ) + ' )'
        logger().log( attr_str )
        
        # Print Variable Data
        logger().log( 'Data:' )
        print_buffer( efi_var_data )

        # Print Variable Full Contents
        if logger().VERBOSE:
           logger().log( 'Full Contents:' )
           print_buffer( efi_var_buf )


def print_sorted_EFI_variables( variables ):
    sorted_names = sorted(variables.keys())
    for name in sorted_names:
        for rec in variables[name]:
            #                   off,    buf,     hdr,         data,   guid,   attrs
            print_efi_variable( rec[0], rec[1], rec[2], name, rec[3], rec[4], rec[5] )

def decode_EFI_variables( efi_vars, nvram_pth ):
    # print decoded and sorted EFI variables into a log file
    print_sorted_EFI_variables( efi_vars )
    # write each EFI variable into its own binary file
    for name in efi_vars.keys():
        n = 0
        for (off, buf, hdr, data, guid, attrs) in efi_vars[name]:
            # efi_vars[name] = (off, buf, hdr, data, guid, attrs)
            attr_str = get_attr_string( attrs )
            var_fname = os.path.join( nvram_pth, '%s_%s_%s_%d.bin' % (name, guid, attr_str.strip(), n) )
            write_file( var_fname, data )
            #if name in SECURE_BOOT_VARIABLES:
            if name in AUTHENTICATED_VARIABLES:
                parse_efivar_file( var_fname, data )
            n = n+1


class UEFI:
    def __init__( self, helper ):
        self.helper = helper
        self._FWType = FWType.EFI_FW_TYPE_UEFI

    ######################################################################
    # FWType defines platform/BIOS dependent formats like 
    # format of EFI NVRAM, format of FV, etc.
    #
    # FWType chooses an element from the EFI_VAR_DICT Dictionary
    #
    # Default current platform type is EFI_FW_TYPE_UEFI
    ######################################################################

    def set_FWType( self, efi_nvram_format ):
        if efi_nvram_format in fw_types:
            self._FWType = efi_nvram_format


    ######################################################################
    # EFI NVRAM Parsing Functions
    ######################################################################

    def dump_EFI_variables_from_SPI( self ):
        return self.read_EFI_variables_from_SPI( 0, 0x800000 )

    def read_EFI_variables_from_SPI( self, BIOS_region_base, BIOS_region_size ):
        rom = spi.read_spi( BIOS_region_base, BIOS_region_size )
        efi_var_store = self.find_EFI_Variable_Store( rom )
        return self.read_EFI_NVRAM_variables( efi_var_store )

    def read_EFI_variables_from_file( self, filename ):
        rom = read_file( filename )
        efi_var_store = self.find_EFI_Variable_Store( rom )
        return self.read_EFI_NVRAM_variables( efi_var_store )

    def find_EFI_variable_store( self, rom_buffer ):
        if ( rom_buffer is None ):
            logger().error( 'rom_buffer is None' )
            return None
        # Meh..
        rom = "".join( rom_buffer )
        offset       = 0
        size         = len(rom_buffer)
        nvram_header = None

        if EFI_VAR_DICT[ self._FWType ]['func_getnvstore']:
           (offset, size, nvram_header) = EFI_VAR_DICT[ self._FWType ]['func_getnvstore']( rom )
           if (-1 == offset):
              logger().error( "'func_getnvstore' is defined but could not find EFI NVRAM. Exiting.." )
              return None
        else:
           logger().log( "[uefi] 'func_getnvstore' is not defined in EFI_VAR_DICT. Assuming start offset 0.." )

        if -1 == size: size = len(rom_buffer)
        nvram_buf = rom[ offset : offset + size ]

        if logger().UTIL_TRACE:
            logger().log( '[uefi] Found EFI NVRAM at offset 0x%08X' % offset )
            logger().log( """
==================================================================
NVRAM: EFI Variable Store
==================================================================""")
            if nvram_header: logger().log( nvram_header )
        return nvram_buf


    # @TODO: Do not use, will be removed
    def read_EFI_variables( self, efi_var_store, authvars ):
        if ( efi_var_store is None ):
           logger().error( 'efi_var_store is None' )
           return None
        variables = EFI_VAR_DICT[ self._FWType ]['func_getefivariables']( efi_var_store )
        if logger().UTIL_TRACE: print_sorted_EFI_variables( variables )
        return variables


    def parse_EFI_variables( self, fname, rom, authvars, _fw_type=None ):
        if _fw_type in fw_types:
           logger().log( "[uefi] Using FW type (NVRAM format): %s" % _fw_type )
           self.set_FWType( _fw_type )
        else:
           logger().error( "Unrecognized FW type (NVRAM format) '%s'.." % _fw_type )
           return False

        logger().log( "[uefi] Searching for NVRAM in the binary.." )
        efi_vars_store = self.find_EFI_variable_store( rom )
        if efi_vars_store:
           nvram_fname = fname + '.nvram.bin'
           write_file( nvram_fname, efi_vars_store )
           nvram_pth = fname + '.nvram.dir'
           if not os.path.exists( nvram_pth ):
               os.makedirs( nvram_pth )
           logger().log( "[uefi] Extracting EFI Variables in the NVRAM.." )
           efi_vars = EFI_VAR_DICT[ self._FWType ]['func_getefivariables']( efi_vars_store )
           decode_EFI_variables( efi_vars, nvram_pth )
        else:
           logger().error( "Did not find NVRAM" )
           return False

        return True


    ######################################################################
    # Runtime Variable API Functions
    ######################################################################

    def list_EFI_variables( self ):
        return self.helper.list_EFI_variables()

    def get_EFI_variable( self, name, guid, filename=None ):
        var = self.helper.get_EFI_variable( name, guid )
        if var:
           if filename: write_file( filename, var )
           if logger().UTIL_TRACE or logger().VERBOSE:
              logger().log( '[uefi] EFI variable:' )
              logger().log( 'Name: %s' % name )
              logger().log( 'GUID: %s' % guid )
              logger().log( 'Data:' )
              print_buffer( var )
        return var

    def set_EFI_variable( self, name, guid, var, attrs=None ):
        if logger().UTIL_TRACE or logger().VERBOSE:
           logger().log( '[uefi] Writing EFI variable:' )
           logger().log( 'Name: %s' % name )
           logger().log( 'GUID: %s' % guid )
           if attrs is not None: logger().log( 'Attributes: %s' % attrs )
           #print_buffer( var )
        return self.helper.set_EFI_variable( name, guid, var, attrs )

    def set_EFI_variable_from_file( self, name, guid, filename, attrs=None ):
        if filename is None:
           logger().error( 'File with EFI variable is not specified' )
           return False
        var = read_file( filename )
        return self.set_EFI_variable( name, guid, var, attrs )

    def delete_EFI_variable( self, name, guid, attrs=None ):
        if logger().UTIL_TRACE or logger().VERBOSE:
           logger().log( '[uefi] Deleting EFI variable:' )
           logger().log( 'Name: %s' % name )
           logger().log( 'GUID: %s' % guid )
           if attrs is not None: logger().log( 'Attributes: %s' % attrs )
        return self.helper.set_EFI_variable( name, guid, None, attrs )