linux: module symbols: Fixes and improve code.

- Fixed an issue causing the generation of an invalid, extra symbol.
- Reuse ELF sym API instead of reimplemented it
- Updated the function to return the symbol index, enabling the use of additional module tables.
- Ensured the ELF symbol object has `cached_strtab` set, allowing retrieval of critical symbol information like names.
- Added typing hints
This commit is contained in:
Gustavo Moreira
2025-01-02 17:58:51 +11:00
parent e0869da092
commit ac299b0cfc
3 changed files with 61 additions and 49 deletions
@@ -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
@@ -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")
@@ -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"""