Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

linux: module symbols: Fixes and code improvements #1509

3 changes: 3 additions & 0 deletions volatility3/framework/constants/linux/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,9 @@ def flags(self) -> str:
MODULE_MAXIMUM_CORE_TEXT_SIZE = 20000000
MODULE_MINIMUM_SIZE = 4096

# Kallsyms
KSYM_NAME_LEN = 512

# VMCOREINFO
VMCOREINFO_MAGIC = b"VMCOREINFO\x00"
# Aligned to 4 bytes. See storenote() in kernels < 4.19 or append_kcore_note() in kernels >= 4.19
Expand Down
71 changes: 33 additions & 38 deletions volatility3/framework/symbols/linux/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,65 +212,57 @@ def get_elf_table_name(self):
)
return elf_table_name

def get_symbols(self):
"""Get symbols of the module
def get_symbols(self) -> Iterable[interfaces.objects.ObjectInterface]:
"""Get ELF symbol objects for this module"""

Yields:
A symbol object
"""
if not self.section_strtab or self.num_symtab < 1:
return None

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",
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,
ikelos marked this conversation as resolved.
Show resolved Hide resolved
)
if self.section_strtab:
yield from syms
for elf_sym_obj in elf_syms:
# Prepare the symbol object for methods like get_name()
elf_sym_obj.cached_strtab = self.section_strtab
ikelos marked this conversation as resolved.
Show resolved Hide resolved
yield elf_sym_obj

def get_symbols_names_and_addresses(self) -> Iterable[Tuple[str, int]]:
"""Get names and addresses for each symbol of the module

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 elf_sym_obj in self.get_symbols():
sym_name = elf_sym_obj.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)
sym_address = elf_sym_obj.st_value & layer.address_mask
yield (sym_name, sym_address)

def get_symbol(self, wanted_sym_name):
"""Get symbol value for a given symbol name"""
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:
Expand All @@ -284,6 +276,7 @@ def section_symtab(self):
return self.kallsyms.symtab
elif self.has_member("symtab"):
return self.symtab

raise AttributeError("Unable to get symtab")

@property
Expand All @@ -292,6 +285,7 @@ def num_symtab(self):
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
Expand All @@ -302,6 +296,7 @@ def section_strtab(self):
# Older kernels
elif self.has_member("strtab"):
return self.strtab

raise AttributeError("Unable to get strtab")


Expand Down
42 changes: 20 additions & 22 deletions volatility3/framework/symbols/linux/extensions/elf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#

from typing import Dict, Tuple
import logging
from typing import Dict, Optional, Tuple

from volatility3.framework import constants
from volatility3.framework.constants.linux import (
ELF_IDENT,
ELF_CLASS,
)
from volatility3.framework import objects, interfaces, exceptions
from volatility3.framework import constants, exceptions, interfaces, objects
from volatility3.framework.constants import linux as linux_constants

vollog = logging.getLogger(__name__)

Expand Down Expand Up @@ -63,13 +59,13 @@ def __init__(
ei_class = self._context.object(
symbol_table_name + constants.BANG + "unsigned char",
layer_name=layer_name,
offset=object_info.offset + ELF_IDENT.EI_CLASS,
offset=object_info.offset + linux_constants.ELF_IDENT.EI_CLASS,
)

if ei_class == ELF_CLASS.ELFCLASS32:
if ei_class == linux_constants.ELF_CLASS.ELFCLASS32:
self._type_prefix = "Elf32_"
self._ei_class_size = 32
elif ei_class == ELF_CLASS.ELFCLASS64:
elif ei_class == linux_constants.ELF_CLASS.ELFCLASS64:
self._type_prefix = "Elf64_"
self._ei_class_size = 64
else:
Expand Down Expand Up @@ -316,6 +312,8 @@ def get_symbols(self):
class elf_sym(objects.StructType):
"""An elf symbol entry"""

_MAX_NAME_LENGTH = linux_constants.KSYM_NAME_LEN

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._cached_strtab = None
Expand All @@ -328,22 +326,22 @@ def cached_strtab(self):
def cached_strtab(self, cached_strtab):
self._cached_strtab = cached_strtab

def get_name(self):
addr = self._cached_strtab + self.st_name
def get_name(self) -> Optional[str]:
"""Returns the symbol 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)
addr = self._cached_strtab + self.st_name

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, self._MAX_NAME_LENGTH, 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"""
Expand Down
Loading