diff --git a/volatility3/framework/constants/linux/__init__.py b/volatility3/framework/constants/linux/__init__.py index 6e49e6f37..f3a13f2a5 100644 --- a/volatility3/framework/constants/linux/__init__.py +++ b/volatility3/framework/constants/linux/__init__.py @@ -352,3 +352,6 @@ NSEC_PER_SEC = 1e9 MODULE_MAXIMUM_CORE_SIZE = 20000000 MODULE_MAXIMUM_CORE_TEXT_SIZE = 20000000 MODULE_MINIMUM_SIZE = 4096 + +# Kallsyms +KSYM_NAME_LEN = 512 diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 34d0fcba9..28fbb3fc5 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -213,30 +213,37 @@ class module(generic.GenericIntelProcess): ) return elf_table_name - def get_symbols(self): + def get_symbols( + self, + ) -> Iterable[Tuple[int, interfaces.objects.ObjectInterface]]: """Get symbols of the module Yields: - A symbol object + A tuple containing the ELF symbol index and the corresponding ELF symbol object """ - if not hasattr(self, "_elf_table_name"): - self._elf_table_name = self.get_elf_table_name() - if symbols.symbol_table_is_64bit(self._context, self.get_symbol_table_name()): - prefix = "Elf64_" - else: - prefix = "Elf32_" - syms = self._context.object( - self.get_symbol_table_name() + constants.BANG + "array", + if not self.section_strtab or self.num_symtab < 1: + return None + + elf_table_name = self.get_elf_table_name() + symbol_table_name = self.get_symbol_table_name() + + is_64bit = symbols.symbol_table_is_64bit(self._context, symbol_table_name) + sym_name = "Elf64_Sym" if is_64bit else "Elf32_Sym" + sym_type = self._context.symbol_space.get_type( + elf_table_name + constants.BANG + sym_name + ) + elf_syms = self._context.object( + symbol_table_name + constants.BANG + "array", layer_name=self.vol.layer_name, offset=self.section_symtab, - subtype=self._context.symbol_space.get_type( - self._elf_table_name + constants.BANG + prefix + "Sym" - ), - count=self.num_symtab + 1, + subtype=sym_type, + count=self.num_symtab, ) - if self.section_strtab: - yield from syms + for elf_sym_num, elf_sym_obj in enumerate(elf_syms): + # Prepare the symbol object for methods like get_name() + elf_sym_obj.cached_strtab = self.section_strtab + yield elf_sym_num, elf_sym_obj def get_symbols_names_and_addresses(self) -> Iterable[Tuple[str, int]]: """Get names and addresses for each symbol of the module @@ -244,34 +251,25 @@ class module(generic.GenericIntelProcess): Yields: A tuple for each symbol containing the symbol name and its corresponding value """ - - for sym in self.get_symbols(): - sym_arr = self._context.object( - self.get_symbol_table_name() + constants.BANG + "array", - layer_name=self.vol.native_layer_name, - offset=self.section_strtab + sym.st_name, - ) - try: - sym_name = utility.array_to_string( - sym_arr, 512 - ) # 512 is the value of KSYM_NAME_LEN kernel constant - except exceptions.InvalidAddressException: + layer = self._context.layers[self.vol.layer_name] + for _sym_num, sym in self.get_symbols(): + sym_name = sym.get_name() + if not sym_name: continue - if sym_name != "": - # Normalize sym.st_value offset, which is an address pointing to the symbol value - mask = self._context.layers[self.vol.layer_name].address_mask - sym_address = sym.st_value & mask - yield (sym_name, sym_address) - def get_symbol(self, wanted_sym_name): - """Get symbol value for a given symbol name""" + # Normalize sym.st_value offset, which is an address pointing to the symbol value + sym_address = sym.st_value & layer.address_mask + yield (sym_name, sym_address) + + def get_symbol(self, wanted_sym_name) -> Optional[int]: + """Get symbol address for a given symbol name""" for sym_name, sym_address in self.get_symbols_names_and_addresses(): if wanted_sym_name == sym_name: return sym_address return None - def get_symbol_by_address(self, wanted_sym_address): + def get_symbol_by_address(self, wanted_sym_address) -> Optional[str]: """Get symbol name for a given symbol address""" for sym_name, sym_address in self.get_symbols_names_and_addresses(): if wanted_sym_address == sym_address: @@ -285,6 +283,7 @@ class module(generic.GenericIntelProcess): return self.kallsyms.symtab elif self.has_member("symtab"): return self.symtab + raise AttributeError("Unable to get symtab") @property @@ -293,6 +292,7 @@ class module(generic.GenericIntelProcess): return int(self.kallsyms.num_symtab) elif self.has_member("num_symtab"): return int(self.member("num_symtab")) + raise AttributeError("Unable to determine number of symbols") @property @@ -303,6 +303,7 @@ class module(generic.GenericIntelProcess): # Older kernels elif self.has_member("strtab"): return self.strtab + raise AttributeError("Unable to get strtab") diff --git a/volatility3/framework/symbols/linux/extensions/elf.py b/volatility3/framework/symbols/linux/extensions/elf.py index eadcbbae0..4de73d952 100644 --- a/volatility3/framework/symbols/linux/extensions/elf.py +++ b/volatility3/framework/symbols/linux/extensions/elf.py @@ -2,13 +2,14 @@ # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # -from typing import Dict, Tuple +from typing import Dict, Tuple, Optional import logging from volatility3.framework import constants from volatility3.framework.constants.linux import ( ELF_IDENT, ELF_CLASS, + KSYM_NAME_LEN, ) from volatility3.framework import objects, interfaces, exceptions @@ -328,22 +329,29 @@ class elf_sym(objects.StructType): def cached_strtab(self, cached_strtab): self._cached_strtab = cached_strtab - def get_name(self): + def get_name(self, max_size=KSYM_NAME_LEN) -> Optional[str]: + """Returns the symbol name + + Args: + max_size: Maximum length for a symbol name string. Defaults to KSYM_NAME_LEN (512 bytes). + + Returns: + The symbol name + """ + addr = self._cached_strtab + self.st_name - # Just get the first 255 characters, it should be enough for a symbol name - name_bytes = self._context.layers[self.vol.layer_name].read(addr, 255, pad=True) - - if name_bytes: - idx = name_bytes.find(b"\x00") - if idx != -1: - name_bytes = name_bytes[:idx] - return name_bytes.decode("utf-8", errors="ignore") - else: - # If we cannot read the name from the address space, - # we return None. + layer = self._context.layers[self.vol.layer_name] + name_bytes = layer.read(addr, max_size, pad=True) + if not name_bytes: return None + idx = name_bytes.find(b"\x00") + if idx != -1: + name_bytes = name_bytes[:idx] + + return name_bytes.decode("utf-8", errors="ignore") + class elf_phdr(objects.StructType): """An elf program header"""