mirror of
https://github.com/chipsec/chipsec
synced 2026-06-08 13:31:00 +00:00
51a1de1a7e
Add self.name and self.driverpath to Helper baseclass Add get_info function into Helper baseclass which returns self.name and self.driverpath Modify helpers to have proper name and driver location for driver loaded Signed-off-by: BrentHoltsclaw <brent.holtsclaw@intel.com>
500 lines
22 KiB
Python
Executable File
500 lines
22 KiB
Python
Executable File
#!/usr/bin/env python
|
|
#CHIPSEC: Platform Security Assessment Framework
|
|
#Copyright (c) 2010-2019, 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
|
|
#
|
|
|
|
|
|
|
|
"""
|
|
Main application logic and automation functions
|
|
"""
|
|
|
|
## These are for debugging imports
|
|
import inspect
|
|
import sys
|
|
if sys.version[0] == 2:
|
|
import __builtin__
|
|
savimp = __builtin__.__import__
|
|
elif sys.version[0] == 3:
|
|
import builtins
|
|
savimp = builtins.__import__
|
|
|
|
|
|
def newimp(name, *x):
|
|
caller = inspect.currentframe().f_back
|
|
if 'chipsec' in name:
|
|
print ("{:35} -> {}".format( (caller.f_globals.get('__name__')), name))
|
|
|
|
return savimp(name, *x)
|
|
## Uncomment the following line to display the imports that chipsec calls
|
|
#__builtin__.__import__ = newimp
|
|
## END DEBUG
|
|
|
|
import fnmatch
|
|
import argparse
|
|
import json
|
|
import os
|
|
import re
|
|
import platform
|
|
|
|
import time
|
|
import traceback
|
|
from collections import OrderedDict
|
|
try:
|
|
import zipfile
|
|
except:
|
|
pass
|
|
|
|
import chipsec.file
|
|
import chipsec.module
|
|
import chipsec.result_deltas
|
|
from chipsec import defines
|
|
from chipsec import module_common
|
|
from chipsec import chipset
|
|
from chipsec.helper import oshelper
|
|
from chipsec.logger import logger
|
|
from chipsec.testcase import *
|
|
|
|
try:
|
|
import importlib
|
|
except ImportError:
|
|
pass
|
|
|
|
class ChipsecMain:
|
|
|
|
def __init__(self, argv):
|
|
self.CHIPSEC_FOLDER = os.path.abspath(chipsec.file.get_main_dir())
|
|
self.CHIPSEC_LOADED_AS_EXE = chipsec.file.main_is_frozen()
|
|
self.ZIP_MODULES_RE = None
|
|
self.Import_Path = "chipsec.modules."
|
|
self.Modules_Path = os.path.join(self.CHIPSEC_FOLDER,"chipsec","modules")
|
|
self.Loaded_Modules = []
|
|
self.AVAILABLE_TAGS = []
|
|
self.MODPATH_RE = re.compile(r"^\w+(\.\w+)*$")
|
|
self.version = defines.get_version()
|
|
|
|
self.argv = argv
|
|
self.parse_args()
|
|
|
|
def init_cs(self):
|
|
self._cs = chipset.cs()
|
|
|
|
def print_banner(self):
|
|
"""
|
|
Prints chipsec banner
|
|
"""
|
|
logger().log( "################################################################\n"
|
|
"## ##\n"
|
|
"## CHIPSEC: Platform Hardware Security Assessment Framework ##\n"
|
|
"## ##\n"
|
|
"################################################################" )
|
|
logger().log( "[CHIPSEC] Version {}".format(self.version) )
|
|
logger().log( "[CHIPSEC] Arguments: {}".format( " ".join(self.argv) ) )
|
|
|
|
##################################################################################
|
|
# Module API
|
|
##################################################################################
|
|
def f_mod(self,x):
|
|
return ( x.find('__init__') == -1 and ZIP_MODULES_RE.match(x) )
|
|
|
|
def map_modname(self,x):
|
|
return (x.rpartition('.')[0]).replace('/','.')
|
|
#return ((x.split('/', 2)[2]).rpartition('.')[0]).replace('/','.')
|
|
|
|
def map_pass(self,x):
|
|
return x
|
|
|
|
def import_module(self,module_path):
|
|
module = None
|
|
if not self.MODPATH_RE.match(module_path):
|
|
logger().error( "Invalid module path: {}".format(module_path) )
|
|
else:
|
|
try:
|
|
module = importlib.import_module( module_path )
|
|
except BaseException as msg:
|
|
logger().error( "Exception occurred during import of {}: '{}'".format(module_path, str(msg)) )
|
|
if logger().DEBUG: logger().log_bad(traceback.format_exc())
|
|
if self.failfast: raise msg
|
|
return module
|
|
|
|
def verify_module_tags(self,module):
|
|
run_it = True
|
|
if len(self.USER_MODULE_TAGS) > 0 or self._list_tags:
|
|
run_it = False
|
|
module_tags= module.get_tags()
|
|
for mt in module_tags:
|
|
if self._list_tags:
|
|
if mt not in self.AVAILABLE_TAGS: self.AVAILABLE_TAGS.append(mt)
|
|
elif mt in self.USER_MODULE_TAGS:
|
|
run_it = True
|
|
return run_it
|
|
|
|
|
|
def run_module( self, modx, module_argv ):
|
|
result = None
|
|
try:
|
|
if not modx.do_import(): return module_common.ModuleResult.ERROR
|
|
if logger().DEBUG and not self._list_tags: logger().log( "[*] Module path: {}".format(modx.get_location()) )
|
|
|
|
if self.verify_module_tags( modx ):
|
|
result = modx.run( module_argv )
|
|
else:
|
|
return module_common.ModuleResult.SKIPPED
|
|
except BaseException as msg:
|
|
if logger().DEBUG: logger().log_bad(traceback.format_exc())
|
|
logger().log_error_check( "Exception occurred during {}.run(): '{}'".format(modx.get_name(), str(msg)) )
|
|
raise msg
|
|
return result
|
|
|
|
##
|
|
# full_path can be one of three things:
|
|
# 1. the actual full path to the py or pyc file i.e. c:\some_path\chipsec\modules\common\bios_wp.py
|
|
# 2. a path to the pyc file inside a zip file i.e. chipsec/modules/common/bios_wp.pyc
|
|
# 3. the name of the module i.e. chipsec.modules.common.bios_wp
|
|
def get_module_name( self, full_path):
|
|
name = full_path
|
|
# case #1, the full path: remove prefix
|
|
if full_path.startswith(self.CHIPSEC_FOLDER+os.path.sep):
|
|
name = full_path.replace ( self.CHIPSEC_FOLDER+os.path.sep, '')
|
|
else:
|
|
for path in self.IMPORT_PATHS:
|
|
if full_path.startswith(os.path.abspath(path)+os.path.sep):
|
|
name = full_path.replace ( os.path.abspath(path)+os.path.sep, '')
|
|
# case #1 and #2: remove the extension
|
|
if name.lower().endswith('.py') : name = name[:-3]
|
|
if name.lower().endswith('.pyc'): name = name[:-4]
|
|
# case #1: replace slashes with dots
|
|
name = name.replace( os.path.sep, '.' )
|
|
# case #2: when in a zip it is always forward slash
|
|
name = name.replace( '/', '.' )
|
|
|
|
# Add 'chipsec.modules.' if shor module name was provided and alternative import paths were not specified
|
|
if [] == self.IMPORT_PATHS and not name.startswith( self.Import_Path ):
|
|
name = self.Import_Path + name
|
|
|
|
return name
|
|
|
|
|
|
|
|
#
|
|
# module_path is a file path relative to chipsec
|
|
# E.g. chipsec/modules/common/module.py
|
|
#
|
|
def load_module( self, module_path, module_argv ):
|
|
module_name = self.get_module_name(module_path)
|
|
module = chipsec.module.Module(module_name)
|
|
|
|
if module not in self.Loaded_Modules:
|
|
self.Loaded_Modules.append( (module,module_argv) )
|
|
if not self._list_tags: logger().log( "[+] loaded {}".format(module.get_name()) )
|
|
return True
|
|
|
|
def load_modules_from_path( self, from_path, recursive = True ):
|
|
if logger().DEBUG: logger().log( "[*] Path: {}".format(os.path.abspath( from_path )) )
|
|
for dirname, subdirs, mod_fnames in os.walk( os.path.abspath( from_path ) ) :
|
|
if not recursive:
|
|
while len(subdirs) > 0:
|
|
subdirs.pop()
|
|
for modx in mod_fnames:
|
|
if fnmatch.fnmatch( modx, '*.py' ) and not fnmatch.fnmatch( modx, '__init__.py' ):
|
|
self.load_module( os.path.join( dirname, modx ), self._module_argv )
|
|
|
|
def load_my_modules(self):
|
|
#
|
|
# Step 1.
|
|
# Load modules common to all supported platforms
|
|
#
|
|
common_path = os.path.join( self.Modules_Path, 'common' )
|
|
logger().log( "[*] loading common modules from \"{}\" ..".format(common_path.replace(os.getcwd(),'.')) )
|
|
self.load_modules_from_path( common_path )
|
|
#
|
|
# Step 2.
|
|
# Load platform-specific modules from the corresponding platform module directory
|
|
#
|
|
chipset_path = os.path.join( self.Modules_Path, self._cs.code.lower() )
|
|
if (chipset.CHIPSET_ID_UNKNOWN != self._cs.id) and os.path.exists( chipset_path ):
|
|
logger().log( "[*] loading platform specific modules from \"{}\" ..".format(chipset_path.replace(os.getcwd(),'.')) )
|
|
self.load_modules_from_path( chipset_path )
|
|
else:
|
|
logger().log( "[*] No platform specific modules to load" )
|
|
#
|
|
# Step 3.
|
|
# Enumerate all modules from the root module directory
|
|
logger().log( "[*] loading modules from \"{}\" ..".format(self.Modules_Path.replace(os.getcwd(),'.')) )
|
|
self.load_modules_from_path( self.Modules_Path, False )
|
|
|
|
|
|
def load_user_modules(self):
|
|
for import_path in self.IMPORT_PATHS:
|
|
logger().log( "[*] loading modules from \"{}\" ..".format(import_path) )
|
|
self.load_modules_from_path(import_path)
|
|
|
|
def clear_loaded_modules(self):
|
|
del self.Loaded_Modules[:]
|
|
|
|
|
|
def print_loaded_modules(self):
|
|
if self.Loaded_Modules == []:
|
|
logger().log( "No modules have been loaded" )
|
|
for (modx,modx_argv) in self.Loaded_Modules:
|
|
logger().log( modx )
|
|
|
|
|
|
def run_loaded_modules(self):
|
|
|
|
results = logger().Results
|
|
results.add_properties(self.properties())
|
|
|
|
if not self._list_tags: logger().log( "[*] running loaded modules .." )
|
|
|
|
t = time.time()
|
|
for (modx,modx_argv) in self.Loaded_Modules:
|
|
test_result = TestCase(modx.get_name())
|
|
results.add_testcase(test_result)
|
|
logger().start_module( modx.get_name( ) )
|
|
|
|
# Run the module
|
|
try:
|
|
result = self.run_module( modx, modx_argv )
|
|
except BaseException:
|
|
results.add_exception(modx)
|
|
result = module_common.ModuleResult.ERROR
|
|
if logger().DEBUG: logger().log_bad(traceback.format_exc())
|
|
if self.failfast: raise
|
|
|
|
# Module uses the old API display warning and try to run anyways
|
|
if result == module_common.ModuleResult.DEPRECATED:
|
|
logger().error( 'Module {} does not inherit BaseModule class'.format(str(modx)) )
|
|
|
|
# Populate results
|
|
|
|
test_result.add_result( module_common.getModuleResultName(result) )
|
|
if modx_argv: test_result.add_arg( modx_argv )
|
|
|
|
logger().end_module( modx.get_name() )
|
|
|
|
if self._json_out:
|
|
chipsec.file.write_file(self._json_out, results.json_full())
|
|
|
|
if self._xml_out:
|
|
chipsec.file.write_file(self._xml_out, results.xml_full(self._xml_out))
|
|
|
|
test_deltas = None
|
|
if self._deltas_file is not None:
|
|
prev_results = chipsec.result_deltas.get_json_results(self._deltas_file)
|
|
if prev_results is None:
|
|
logger().error("Delta processing disabled. Displaying results summary.")
|
|
else:
|
|
test_deltas = chipsec.result_deltas.compute_result_deltas(prev_results, results.get_results())
|
|
|
|
if test_deltas is not None:
|
|
chipsec.result_deltas.display_deltas(test_deltas, self.no_time, t)
|
|
elif not self._list_tags:
|
|
summary = results.order_summary()
|
|
logger().log( "\n[CHIPSEC] *************************** SUMMARY ***************************" )
|
|
if not self.no_time:
|
|
logger().log( "[CHIPSEC] Time elapsed {:.3f}".format(time.time()-t) )
|
|
for k in summary.keys():
|
|
if k == 'total':
|
|
logger().log( '[CHIPSEC] Modules {:16}{:d}'.format(k,summary[k]) )
|
|
elif k == 'warnings':
|
|
logger().log( '[CHIPSEC] Modules with {:11}{:d}:'.format(k,len(summary[k])) )
|
|
for mod in summary[k]:
|
|
logger().log_warning(mod)
|
|
elif k == 'exceptions':
|
|
if len(summary[k]) > 0:
|
|
logger().log( '[CHIPSEC] Modules with {:11}{:d}:'.format(k,len(summary[k])) )
|
|
for mod in summary[k]:
|
|
logger().error(mod)
|
|
else:
|
|
logger().log( '[CHIPSEC] Modules {:16}{:d}:'.format(k,len(summary[k])) )
|
|
for mod in summary[k]:
|
|
if k == 'failed to run':
|
|
logger().error(mod)
|
|
elif k == 'passed':
|
|
logger().log_passed(mod)
|
|
elif k == 'information':
|
|
logger().log_information(mod)
|
|
elif k == 'failed':
|
|
logger().log_failed(mod)
|
|
elif k == 'not implemented':
|
|
logger().log_skipped(mod)
|
|
elif k == 'not applicable':
|
|
logger().log_not_applicable(mod)
|
|
logger().log ('[CHIPSEC] *****************************************************************')
|
|
else:
|
|
logger().log( "[*] Available tags are:" )
|
|
for at in self.AVAILABLE_TAGS: logger().log(" {}".format(at))
|
|
|
|
return results.get_return_code()
|
|
|
|
##################################################################################
|
|
# Running all relevant modules
|
|
##################################################################################
|
|
|
|
def run_all_modules(self):
|
|
if self.CHIPSEC_LOADED_AS_EXE:
|
|
myzip = zipfile.ZipFile( os.path.join(self.CHIPSEC_FOLDER, "library.zip" ))
|
|
global ZIP_MODULES_RE
|
|
ZIP_MODULES_RE = re.compile(r"^chipsec\/modules\/\w+\.pyc$|^chipsec\/modules\/common\/(\w+\/)*\w+\.pyc$|^chipsec\/modules\/"+self._cs.code.lower()+r"\/\w+\.pyc$", re.IGNORECASE|re.VERBOSE)
|
|
zip_modules = []
|
|
zip_modules.extend( map(self.map_pass, filter(self.f_mod, myzip.namelist())) )
|
|
logger().log( "Loaded modules from ZIP:" )
|
|
for zmodx in zip_modules:
|
|
module_name = self.get_module_name(zmodx)
|
|
mod = chipsec.module.Module(module_name)
|
|
logger().log(mod.get_name())
|
|
self.Loaded_Modules.append( (mod,None) )
|
|
else:
|
|
self.load_my_modules()
|
|
self.load_user_modules()
|
|
return self.run_loaded_modules()
|
|
|
|
def parse_args(self):
|
|
"""Parse the arguments provided on the command line.
|
|
|
|
Returns: a pair (continue, exit_code). If continue is False,
|
|
the exit_code should be returned.
|
|
"""
|
|
parser = argparse.ArgumentParser(usage='%(prog)s [options]',formatter_class=argparse.RawDescriptionHelpFormatter,epilog=ExitCode.help_epilog,add_help=False)
|
|
options = parser.add_argument_group('Options')
|
|
options.add_argument('-h', '--help', help="show this message and exit",action='store_true')
|
|
options.add_argument('-m', '--module',dest='_module', help='specify module to run (example: -m common.bios_wp)')
|
|
options.add_argument('-a','--module_args', nargs='*', dest="_module_argv", help="additional module arguments")
|
|
options.add_argument('-v','--verbose', help='verbose mode', action='store_true')
|
|
options.add_argument('--hal', help='HAL mode', action='store_true')
|
|
options.add_argument('-d','--debug', help='debug mode', action='store_true')
|
|
options.add_argument('-l','--log', help='output to log file')
|
|
adv_options = parser.add_argument_group('Advanced Options')
|
|
adv_options.add_argument('-p','--platform',dest='_platform', help='explicitly specify platform code',choices=chipset.Chipset_Code, type=str.upper)
|
|
adv_options.add_argument('--pch',dest='_pch', help='explicitly specify PCH code',choices=chipset.pch_codes, type=str.upper)
|
|
adv_options.add_argument('-n', '--no_driver',dest='_no_driver', help="chipsec won't need kernel mode functions so don't load chipsec driver", action='store_true')
|
|
adv_options.add_argument('-i', '--ignore_platform',dest='_unknownPlatform', help='run chipsec even if the platform is not recognized', action='store_false')
|
|
adv_options.add_argument('-j', '--json',dest='_json_out', help='specify filename for JSON output')
|
|
adv_options.add_argument('-x', '--xml', dest='_xml_out',help='specify filename for xml output (JUnit style)')
|
|
adv_options.add_argument('-t', '--moduletype',dest='USER_MODULE_TAGS', help='run tests of a specific type (tag)',type=str.upper,default=[])
|
|
adv_options.add_argument('--list_tags',dest='_list_tags', help='list all the available options for -t,--moduletype',action='store_true')
|
|
adv_options.add_argument('-I', '--include',dest='IMPORT_PATHS', help='specify additional path to load modules from',default=[])
|
|
adv_options.add_argument('--failfast', help="fail on any exception and exit (don't mask exceptions)",action='store_true')
|
|
adv_options.add_argument('--no_time', help="don't log timestamps",action='store_true')
|
|
adv_options.add_argument('--deltas',dest='_deltas_file', help='specifies a JSON log file to compute result deltas from')
|
|
adv_options.add_argument('--record',dest='_to_file',help='run chipsec and clone helper results into JSON file')
|
|
adv_options.add_argument('--replay',dest='_from_file', help='replay a chipsec run with JSON file')
|
|
adv_options.add_argument('--helper',dest='_driver_exists', help='specify OS Helper', choices=[i for i in oshelper.avail_helpers])
|
|
|
|
parser.parse_args(self.argv,namespace=ChipsecMain)
|
|
|
|
if self.help:
|
|
parser.print_help()
|
|
if self.verbose:
|
|
logger().VERBOSE = True
|
|
if self.hal:
|
|
logger().HAL = True
|
|
if self.debug:
|
|
logger().DEBUG = True
|
|
if self.log:
|
|
logger().set_log_file( self.log )
|
|
if self._module_argv and len(self._module_argv) == 1 and self._module_argv[0].count(','):
|
|
logger().log( "[*] Use of the -a command no longer needs to have arguments concatenated with ','")
|
|
self._module_argv = self._module_argv[0].split(',')
|
|
if self._unknownPlatform is False:
|
|
logger().log( "[*] Ignoring unsupported platform warning and continue execution" )
|
|
if self._from_file:
|
|
self._driver_exists = "FileHelper"
|
|
|
|
def properties( self ):
|
|
ret = OrderedDict()
|
|
ret["OS"] = "{} {} {} {}".format(self._cs.helper.os_system, self._cs.helper.os_release, self._cs.helper.os_version, self._cs.helper.os_machine)
|
|
ret["Python"] = "Python {}".format(platform.python_version())
|
|
ret["Platform"] = "{}, VID: {:04X}, DID: {:04X}, RID: {:02X}".format(self._cs.longname, self._cs.vid, self._cs.did, self._cs.rid)
|
|
ret["PCH"] = "{}, VID: {:04X}, DID: {:04X} RID: {:02X}".format(self._cs.pch_longname, self._cs.pch_vid, self._cs.pch_did, self._cs.pch_rid)
|
|
ret["Version"] ="{}".format(self.version)
|
|
return ret
|
|
|
|
def log_properties( self ):
|
|
logger().log("[CHIPSEC] OS : {} {} {} {}".format(self._cs.helper.os_system, self._cs.helper.os_release, self._cs.helper.os_version, self._cs.helper.os_machine) )
|
|
logger().log("[CHIPSEC] Python : {} ({})".format(platform.python_version(),"64-bit" if sys.maxsize > 2**32 else "32-bit"))
|
|
logger().log("[CHIPSEC] Helper : {} ({})".format(*self._cs.helper.helper.get_info()))
|
|
logger().log("[CHIPSEC] Platform: {}\n[CHIPSEC] VID: {:04X}\n[CHIPSEC] DID: {:04X}\n[CHIPSEC] RID: {:02X}".format(self._cs.longname, self._cs.vid, self._cs.did, self._cs.rid))
|
|
logger().log("[CHIPSEC] PCH : {}\n[CHIPSEC] VID: {:04X}\n[CHIPSEC] DID: {:04X}\n[CHIPSEC] RID: {:02X}".format(self._cs.pch_longname, self._cs.pch_vid, self._cs.pch_did, self._cs.pch_rid))
|
|
|
|
##################################################################################
|
|
# Entry point for command-line execution
|
|
##################################################################################
|
|
|
|
def main ( self ):
|
|
if self.help:
|
|
return ExitCode.OK
|
|
|
|
self.init_cs()
|
|
|
|
self.print_banner()
|
|
|
|
for import_path in self.IMPORT_PATHS:
|
|
sys.path.append(os.path.abspath( import_path ) )
|
|
|
|
#if self._no_driver and self._driver_exists:
|
|
# logger().error( "incompatible options: --no_driver and --exists" )
|
|
# return ExitCode.EXCEPTION
|
|
|
|
try:
|
|
self._cs.init( self._platform, self._pch, (not self._no_driver), self._driver_exists, self._to_file, self._from_file )
|
|
except chipset.UnknownChipsetError as msg:
|
|
logger().error( "Platform is not supported ({}).".format(str(msg)) )
|
|
if self._unknownPlatform:
|
|
logger().error( 'To run anyways please use -i command-line option\n\n' )
|
|
if logger().DEBUG: logger().log_bad(traceback.format_exc())
|
|
if self.failfast: raise msg
|
|
return ExitCode.EXCEPTION
|
|
logger().warn("Platform dependent functionality is likely to be incorrect")
|
|
except oshelper.OsHelperError as os_helper_error:
|
|
logger().error(str(os_helper_error))
|
|
if logger().DEBUG: logger().log_bad(traceback.format_exc())
|
|
if self.failfast: raise os_helper_error
|
|
return ExitCode.EXCEPTION
|
|
except BaseException as be:
|
|
logger().log_bad(traceback.format_exc())
|
|
if self.failfast: raise be
|
|
return ExitCode.EXCEPTION
|
|
|
|
self.log_properties()
|
|
|
|
logger().log( " " )
|
|
|
|
if logger().DEBUG: logger().log("[*] Running from {}".format(os.getcwd()))
|
|
|
|
modules_failed = 0
|
|
if self._module:
|
|
self.load_module( self._module, self._module_argv )
|
|
modules_failed = self.run_loaded_modules()
|
|
else:
|
|
modules_failed = self.run_all_modules()
|
|
|
|
self._cs.destroy( (not self._no_driver) )
|
|
del self._cs
|
|
logger().disable()
|
|
return modules_failed
|
|
|
|
def main(argv=None):
|
|
chipsecMain = ChipsecMain( argv if argv else sys.argv[1:] )
|
|
return chipsecMain.main()
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit( main() )
|