mirror of
https://github.com/chipsec/chipsec
synced 2026-06-08 13:31:00 +00:00
2fa9152c05
Signed-off-by: brentholtsclaw <brent.holtsclaw@intel.com>
241 lines
8.1 KiB
Python
241 lines
8.1 KiB
Python
# CHIPSEC: Platform Security Assessment Framework
|
|
# Copyright (c) 2010-2021, 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
|
|
#
|
|
|
|
|
|
import re
|
|
import os
|
|
import json
|
|
import traceback
|
|
from typing import Dict, Any, List, Optional, Tuple
|
|
import chipsec.module_common
|
|
import chipsec.library.logger
|
|
from chipsec.library.file import get_main_dir
|
|
from chipsec.library.url import url
|
|
from chipsec.library.returncode import ModuleResult, generate_hash_id, get_module_ids_dictionary
|
|
|
|
_importlib = True
|
|
try:
|
|
import importlib
|
|
except ImportError:
|
|
_importlib = False
|
|
|
|
MODPATH_RE = re.compile(r'^\w+(\.\w+)*$')
|
|
|
|
|
|
class Module:
|
|
"""
|
|
CHIPSEC module wrapper class for dynamic module loading and execution.
|
|
|
|
This class provides a standardized interface for loading, managing, and
|
|
executing CHIPSEC security assessment modules.
|
|
"""
|
|
|
|
def __init__(self, name: str) -> None:
|
|
"""
|
|
Initialize a new Module instance.
|
|
|
|
Args:
|
|
name: The fully qualified module name (e.g., 'chipsec.modules.common.bios_wp')
|
|
"""
|
|
self.logger = chipsec.library.logger.logger()
|
|
self.name = name
|
|
self.module: Optional[Any] = None
|
|
self.mod_obj: Optional[Any] = None
|
|
self.module_ids: Dict[str, str] = get_module_ids_dictionary()
|
|
self.url = url()
|
|
|
|
def __lt__(self, other: 'Module') -> bool:
|
|
"""Compare modules by name for sorting."""
|
|
return self.name < other.name
|
|
|
|
def __le__(self, other: 'Module') -> bool:
|
|
"""Compare modules by name for sorting."""
|
|
return self.name <= other.name
|
|
|
|
def __gt__(self, other: 'Module') -> bool:
|
|
"""Compare modules by name for sorting."""
|
|
return self.name > other.name
|
|
|
|
def __ge__(self, other: 'Module') -> bool:
|
|
"""Compare modules by name for sorting."""
|
|
return self.name >= other.name
|
|
|
|
def get_name(self) -> str:
|
|
"""
|
|
Get the module name.
|
|
|
|
Returns:
|
|
The fully qualified module name
|
|
"""
|
|
return self.name
|
|
|
|
def do_import(self) -> bool:
|
|
"""
|
|
Import the module dynamically.
|
|
|
|
Returns:
|
|
True if the module was successfully imported, False otherwise
|
|
|
|
Raises:
|
|
BaseException: If module import fails and DEBUG is enabled
|
|
"""
|
|
loaded = False
|
|
if not MODPATH_RE.match(self.get_name()):
|
|
self.logger.log_error(f'Invalid module path: {self.name}')
|
|
else:
|
|
try:
|
|
if _importlib:
|
|
self.module = importlib.import_module(self.name)
|
|
loaded = True
|
|
if self.logger.DEBUG:
|
|
self.logger.log_good(f'imported: {self.name}')
|
|
except BaseException as msg:
|
|
error_msg = (f"Exception occurred during import of {self.name}: "
|
|
f"'{str(msg)}'")
|
|
self.logger.log_error(error_msg)
|
|
if self.logger.DEBUG:
|
|
self.logger.log_bad(traceback.format_exc())
|
|
raise msg
|
|
return loaded
|
|
|
|
def update_module_ids_file(self) -> None:
|
|
"""Update the module IDs file with current module mappings."""
|
|
module_ids_path = os.path.join(get_main_dir(), 'chipsec', 'library',
|
|
'module_ids.json')
|
|
with open(module_ids_path, 'w') as module_ids_file:
|
|
module_ids_file.write(json.dumps(self.module_ids))
|
|
|
|
def get_module_id(self, module_name: str) -> str:
|
|
"""
|
|
Get or generate a unique ID for the module.
|
|
|
|
Args:
|
|
module_name: The name of the module
|
|
|
|
Returns:
|
|
A unique string identifier for the module
|
|
"""
|
|
if module_name in self.module_ids:
|
|
module_id = self.module_ids[module_name]
|
|
else:
|
|
module_id = generate_hash_id(module_name)
|
|
self.module_ids[module_name] = module_id
|
|
self.update_module_ids_file()
|
|
return module_id
|
|
|
|
def run(self, module_argv: Optional[List[str]] = None) -> int:
|
|
"""
|
|
Run the module with the given arguments.
|
|
|
|
Args:
|
|
module_argv: Optional list of arguments to pass to the module
|
|
|
|
Returns:
|
|
The module's return code
|
|
"""
|
|
self.get_module_object()
|
|
|
|
if module_argv:
|
|
self.logger.log(f'[*] Module arguments ({len(module_argv):d}):')
|
|
self.logger.log(module_argv)
|
|
else:
|
|
module_argv = []
|
|
|
|
if isinstance(self.mod_obj, chipsec.module_common.BaseModule):
|
|
self.mod_obj.result.id = self.get_module_id(self.name)
|
|
self.mod_obj.result.url = self.url.get_module_url(self.name)
|
|
if self.mod_obj.is_supported():
|
|
result = self.mod_obj.run(module_argv)
|
|
else:
|
|
self.mod_obj.result.setStatusBit(
|
|
self.mod_obj.result.status.NOT_APPLICABLE)
|
|
result = self.mod_obj.result.getReturnCode(
|
|
ModuleResult.NOTAPPLICABLE)
|
|
skip_msg = (f'Skipping module {self.name} since it is not '
|
|
f'applicable in this environment and/or platform')
|
|
self.logger.log(skip_msg)
|
|
|
|
return result
|
|
|
|
def get_module_object(self) -> None:
|
|
"""
|
|
Get the module object by introspecting the loaded module.
|
|
|
|
This method finds the appropriate BaseModule subclass within the
|
|
loaded module and instantiates it.
|
|
"""
|
|
if self.mod_obj is None:
|
|
try:
|
|
if _importlib:
|
|
pkg = getattr(self.module, "__package__")
|
|
class_name = getattr(self.module, "__name__")
|
|
if pkg:
|
|
class_name = class_name.replace(pkg, '')
|
|
if class_name.startswith('.'):
|
|
class_name = class_name.replace('.', '')
|
|
for iname, iref in self.module.__dict__.items():
|
|
if isinstance(iref, type):
|
|
if issubclass(iref,
|
|
chipsec.module_common.BaseModule):
|
|
if iname.lower() == class_name.lower():
|
|
self.mod_obj = iref()
|
|
if self.mod_obj is None:
|
|
raise ModuleNotFoundError(self.module)
|
|
except (AttributeError, TypeError, ModuleNotFoundError):
|
|
self.logger.chipsecLogger.exception(
|
|
'Error getting module object')
|
|
|
|
def get_location(self) -> str:
|
|
"""
|
|
Get the file location of the module.
|
|
|
|
Returns:
|
|
The file path of the module, or empty string if not available
|
|
"""
|
|
myfile = ''
|
|
try:
|
|
if _importlib:
|
|
myfile = getattr(self.module, '__file__')
|
|
except (AttributeError, TypeError):
|
|
pass
|
|
return myfile
|
|
|
|
def get_tags(self) -> Tuple[List[str], List[str]]:
|
|
"""
|
|
Get the tags and metadata tags for the module.
|
|
|
|
Returns:
|
|
A tuple containing (module_tags, metadata_tags)
|
|
"""
|
|
module_tags = []
|
|
metadata_tags = []
|
|
try:
|
|
if _importlib:
|
|
module_tags = getattr(self.module, 'TAGS')
|
|
metadata_tags = getattr(self.module, 'METADATA_TAGS')
|
|
except (AttributeError, TypeError):
|
|
pass
|
|
|
|
return module_tags, metadata_tags
|
|
|
|
def __str__(self) -> str:
|
|
"""Return the string representation of the module."""
|
|
return self.get_name()
|