Add elf parsing and symbol retrieval for linux kernel modules

This commit is contained in:
xabiugarte
2020-07-28 16:46:40 +01:00
committed by Mike Auty
parent a980374eb8
commit 01dea6f3a8
4 changed files with 1017 additions and 15 deletions
+646 -2
View File
@@ -2,6 +2,23 @@
"symbols": {
},
"user_types": {
"Elf": {
"fields": {
"e_ident": {
"offset": 0,
"type": {
"count": 16,
"kind": "array",
"subtype": {
"kind": "base",
"name": "unsigned char"
}
}
}
},
"kind": "struct",
"size": 16
},
"Elf64_Ehdr": {
"fields": {
"e_ident": {
@@ -18,8 +35,8 @@
"e_type": {
"offset": 16,
"type": {
"kind": "base",
"name": "unsigned short"
"kind": "enum",
"name": "EtypeEnum"
}
},
"e_machine": {
@@ -247,9 +264,635 @@
},
"kind": "struct",
"size": 56
},
"Elf64_Dyn": {
"fields": {
"d_tag": {
"offset": 0,
"type": {
"kind": "base",
"name": "long long"
}
},
"d_ptr": {
"offset": 8,
"type": {
"kind": "base",
"name": "unsigned long long"
}
}
},
"kind": "struct",
"size": 16
},
"Elf64_Note": {
"fields": {
"n_namesz": {
"offset": 0,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"n_descsz": {
"offset": 4,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"n_type": {
"offset": 8,
"type": {
"kind": "base",
"name": "unsigned long"
}
}
},
"kind": "struct",
"size": 12
},
"Elf64_Sym": {
"fields": {
"st_name": {
"offset": 0,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"st_info": {
"offset": 4,
"type": {
"kind": "base",
"name": "unsigned char"
}
},
"st_other": {
"offset": 5,
"type": {
"kind": "base",
"name": "unsigned char"
}
},
"st_shndx": {
"offset": 6,
"type": {
"kind": "base",
"name": "unsigned short"
}
},
"st_value": {
"offset": 8,
"type": {
"kind": "base",
"name": "unsigned long long"
}
},
"st_size": {
"offset": 16,
"type": {
"kind": "base",
"name": "unsigned long long"
}
}
},
"kind": "struct",
"size": 24
},
"Elf64_LinkMap": {
"fields": {
"l_addr": {
"offset": 0,
"type": {
"kind": "base",
"name": "unsigned long long"
}
},
"l_name": {
"offset": 8,
"type": {
"kind": "base",
"name": "unsigned long long"
}
},
"l_ld": {
"offset": 16,
"type": {
"kind": "base",
"name": "unsigned long long"
}
},
"l_next": {
"offset": 24,
"type": {
"kind": "base",
"name": "unsigned long long"
}
},
"l_prev": {
"offset": 32,
"type": {
"kind": "base",
"name": "unsigned long long"
}
}
},
"kind": "struct",
"size": 40
},
"Elf64_Rel": {
"fields": {
"r_offset": {
"offset": 0,
"type": {
"kind": "base",
"name": "unsigned long long"
}
},
"r_info": {
"offset": 8,
"type": {
"kind": "base",
"name": "unsigned long long"
}
}
},
"kind": "struct",
"size": 16
},
"Elf64_Rela": {
"fields": {
"r_offset": {
"offset": 0,
"type": {
"kind": "base",
"name": "unsigned long long"
}
},
"r_info": {
"offset": 8,
"type": {
"kind": "base",
"name": "unsigned long long"
}
},
"r_addend": {
"offset": 16,
"type": {
"kind": "base",
"name": "long long"
}
}
},
"kind": "struct",
"size": 24
},
"Elf32_Ehdr": {
"fields": {
"e_ident": {
"offset": 0,
"type": {
"count": 16,
"kind": "array",
"subtype": {
"kind": "base",
"name": "unsigned char"
}
}
},
"e_type": {
"offset": 16,
"type": {
"kind": "enum",
"name": "EtypeEnum"
}
},
"e_machine": {
"offset": 18,
"type": {
"kind": "base",
"name": "unsigned short"
}
},
"e_version": {
"offset": 20,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"e_entry": {
"offset": 24,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"e_phoff": {
"offset": 28,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"e_shoff": {
"offset": 32,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"e_flags": {
"offset": 36,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"e_ehsize": {
"offset": 40,
"type": {
"kind": "base",
"name": "unsigned short"
}
},
"e_phentsize": {
"offset": 42,
"type": {
"kind": "base",
"name": "unsigned short"
}
},
"e_phnum": {
"offset": 44,
"type": {
"kind": "base",
"name": "unsigned short"
}
},
"e_shentsize": {
"offset": 46,
"type": {
"kind": "base",
"name": "unsigned short"
}
},
"e_shnum": {
"offset": 48,
"type": {
"kind": "base",
"name": "unsigned short"
}
},
"e_shstrndx": {
"offset": 50,
"type": {
"kind": "base",
"name": "unsigned short"
}
}
},
"kind": "struct",
"size": 52
},
"Elf32_Shdr": {
"fields": {
"sh_name": {
"offset": 0,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"sh_type": {
"offset": 4,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"sh_flags": {
"offset": 8,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"sh_addr": {
"offset": 12,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"sh_offset": {
"offset": 16,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"sh_size": {
"offset": 20,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"sh_link": {
"offset": 24,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"sh_info": {
"offset": 28,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"sh_addralign": {
"offset": 32,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"sh_entsize": {
"offset": 36,
"type": {
"kind": "base",
"name": "unsigned long"
}
}
},
"kind": "struct",
"size": 40
},
"Elf32_Phdr": {
"fields": {
"p_type": {
"offset": 0,
"type": {
"kind": "enum",
"name": "PtypeEnum"
}
},
"p_offset": {
"offset": 4,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"p_vaddr": {
"offset": 8,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"p_paddr": {
"offset": 12,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"p_filesz": {
"offset": 16,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"p_memsz": {
"offset": 20,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"p_flags": {
"offset": 24,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"p_align": {
"offset": 28,
"type": {
"kind": "base",
"name": "unsigned long"
}
}
},
"kind": "struct",
"size": 32
},
"Elf32_Dyn": {
"fields": {
"d_tag": {
"offset": 0,
"type": {
"kind": "base",
"name": "long"
}
},
"d_ptr": {
"offset": 4,
"type": {
"kind": "base",
"name": "unsigned long"
}
}
},
"kind": "struct",
"size": 8
},
"Elf32_Note": {
"fields": {
"n_namesz": {
"offset": 0,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"n_descsz": {
"offset": 4,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"n_type": {
"offset": 8,
"type": {
"kind": "base",
"name": "unsigned long"
}
}
},
"kind": "struct",
"size": 12
},
"Elf32_Sym": {
"fields": {
"st_name": {
"offset": 0,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"st_value": {
"offset": 4,
"type": {
"kind": "base",
"name": "unsigned long long"
}
},
"st_size": {
"offset": 8,
"type": {
"kind": "base",
"name": "unsigned long long"
}
},
"st_info": {
"offset": 12,
"type": {
"kind": "base",
"name": "unsigned char"
}
},
"st_other": {
"offset": 13,
"type": {
"kind": "base",
"name": "unsigned char"
}
},
"st_shndx": {
"offset": 14,
"type": {
"kind": "base",
"name": "unsigned short"
}
}
},
"kind": "struct",
"size": 16
},
"Elf32_LinkMap": {
"fields": {
"l_addr": {
"offset": 0,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"l_name": {
"offset": 4,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"l_ld": {
"offset": 8,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"l_next": {
"offset": 12,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"l_prev": {
"offset": 16,
"type": {
"kind": "base",
"name": "unsigned long"
}
}
},
"kind": "struct",
"size": 20
},
"Elf32_Rel": {
"fields": {
"r_offset": {
"offset": 0,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"r_info": {
"offset": 4,
"type": {
"kind": "base",
"name": "unsigned long"
}
}
},
"kind": "struct",
"size": 8
},
"Elf32_Rela": {
"fields": {
"r_offset": {
"offset": 0,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"r_info": {
"offset": 4,
"type": {
"kind": "base",
"name": "unsigned long"
}
},
"r_addend": {
"offset": 8,
"type": {
"kind": "base",
"name": "long"
}
}
},
"kind": "struct",
"size": 12
}
},
"enums": {
"EtypeEnum": {
"base": "unsigned short",
"constants": {
"ET_NONE": 0,
"ET_REL": 1,
"ET_EXEC": 2,
"ET_DYN": 3,
"ET_CORE": 4,
"ET_LOPROC": 65280,
"ET_HIPROC": 65535
},
"size": 2
},
"PtypeEnum": {
"base": "unsigned long",
"constants": {
@@ -260,6 +903,7 @@
"PT_NOTE": 4,
"PT_SHLIB": 5,
"PT_PHDR": 6,
"PT_TLS": 7,
"PT_LOOS": 1610612736,
"PT_HIOS": 1879048191,
"PT_LOWPROC": 1879048192,
+18
View File
@@ -0,0 +1,18 @@
# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#
from volatility.framework.symbols import intermed
from volatility.framework.symbols.linux.extensions import elf
class ElfIntermedSymbols(intermed.IntermediateSymbolTable):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_type_class('Elf', elf.elf)
self.set_type_class('Elf64_Phdr', elf.elf_phdr)
self.set_type_class('Elf32_Phdr', elf.elf_phdr)
self.set_type_class('Elf32_Sym', elf.elf_sym)
self.set_type_class('Elf64_Sym', elf.elf_sym)
@@ -7,9 +7,11 @@ import logging
from typing import Generator, Iterable, Iterator, Optional, Tuple
from volatility.framework import constants
from volatility.framework import exceptions, objects, interfaces
from volatility.framework import exceptions, objects, interfaces, symbols
from volatility.framework.layers import linear
from volatility.framework.symbols import generic, linux
from volatility.framework.objects import utility
from volatility.framework.symbols.linux.elf import ElfIntermedSymbols
vollog = logging.getLogger(__name__)
@@ -36,6 +38,113 @@ class module(generic.GenericIntelProcess):
raise AttributeError("module -> get_core_size: Unable to determine initial size of module")
def get_module_core(self):
if self.has_member("core_layout"):
return self.core_layout.base
elif self.has_member("module_core"):
return self.module_core
raise AttributeError("module -> get_module_core: Unable to get module core")
def get_module_init(self):
if self.has_member("init_layout"):
return self.init_layout.base
elif self.has_member("module_init"):
return self.module_init
raise AttributeError("module -> get_module_core: Unable to get module init")
def get_name(self):
""" Get the name of the module as a string """
return utility.array_to_string(self.name)
def _get_sect_count(self, grp):
""" Try to determine the number of valid sections """
arr = self._context.object(self.get_symbol_table().name + constants.BANG + "array",
layer_name = self.vol.layer_name,
offset = grp.attrs,
subtype = self._context.symbol_space.get_type(self.get_symbol_table().name + constants.BANG + "pointer"),
count = 25)
idx = 0
while arr[idx]:
idx = idx + 1
return idx
def get_sections(self):
""" Get sections of the module """
if self.sect_attrs.has_member("nsections"):
num_sects = self.sect_attrs.nsections
else:
num_sects = self._get_sect_count(self.sect_attrs.grp)
arr = self._context.object(self.get_symbol_table().name + constants.BANG + "array",
layer_name = self.vol.layer_name,
offset = self.sect_attrs.attrs.vol.offset,
subtype = self._context.symbol_space.get_type(self.get_symbol_table().name + constants.BANG + 'module_sect_attr'),
count = num_sects)
for attr in arr:
yield attr
def get_symbols(self):
ret_syms = []
if symbols.symbol_table_is_64bit(self._context, self.get_symbol_table().name):
prefix = "Elf64_"
else:
prefix = "Elf32_"
elf_table_name = ElfIntermedSymbols.create(self._context, "", "linux", "elf")
syms = self._context.object(self.get_symbol_table().name + constants.BANG + "array",
layer_name = self.vol.layer_name,
offset = self.section_symtab,
subtype = self._context.symbol_space.get_type(elf_table_name + constants.BANG + prefix + "Sym"),
count = self.num_symtab + 1)
if self.section_strtab:
for sym in syms:
sym.set_cached_strtab(self.section_strtab)
yield sym
def get_symbol(self, wanted_sym_name):
""" Get value for a given symbol name """
for sym in self.get_symbols():
sym_name = sym.get_name()
sym_addr = sym.st_value
if wanted_sym_name == sym_name:
return sym_addr
@property
def section_symtab(self):
if self.has_member("kallsyms"):
return self.kallsyms.symtab
elif self.has_member("symtab"):
return self.symtab
raise AttributeError("module -> symtab: Unable to get symtab")
@property
def num_symtab(self):
if self.has_member("kallsyms"):
return int(self.kallsyms.num_symtab)
elif self.has_member("num_symtab"):
return int(self.num_symtab)
raise AttributeError("module -> num_symtab: Unable to determine number of symbols")
@property
def section_strtab(self):
#Newer kernels
if self.has_member("kallsyms"):
return self.kallsyms.strtab
#Older kernels
elif self.has_member("strtab"):
strtab = self.strtab
raise AttributeError("module -> strtab: Unable to get strtab")
class task_struct(generic.GenericIntelProcess):
@@ -387,15 +496,3 @@ class vfsmount(objects.StructType):
def get_mnt_root(self):
return self.mnt_root
class kobject(objects.StructType):
def reference_count(self):
refcnt = self.kref.refcount
if self.has_member("counter"):
ret = refcnt.counter
else:
ret = refcnt.refs.counter
return ret
@@ -0,0 +1,243 @@
# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#
from volatility.framework import exceptions, constants
from volatility.framework import objects, interfaces
from volatility.framework.objects import utility
from volatility.framework.renderers import conversion
from typing import Any, Dict, List, Optional, Tuple, Union, Type
class elf(objects.StructType):
'''
Class used to create elf objects. It overrides the typename to Elf32_ or Elf64_,
depending on the corresponding value on e_ident
'''
def __init__(self, context: interfaces.context.ContextInterface, type_name: str,
object_info: interfaces.objects.ObjectInformation, size: int,
members: Dict[str, Tuple[int, interfaces.objects.Template]]) -> None:
super().__init__(context = context,
type_name = type_name,
object_info = object_info,
size = size,
members = members)
layer_name = self.vol.layer_name
symbol_table_name = self.get_symbol_table().name
# We read the MAGIC: (0x0 to 0x4) 0x7f 0x45 0x4c 0x46
magic = self._context.object(symbol_table_name + constants.BANG + "unsigned long",
layer_name = layer_name,
offset = object_info.offset)
# Check validity
if magic != 0x464c457f:
return None
# We need to read the EI_CLASS (0x4 offset)
ei_class = self._context.object(symbol_table_name + constants.BANG + "unsigned char",
layer_name = layer_name,
offset = object_info.offset + 0x4)
if ei_class == 1:
self.__type_prefix = "Elf32_"
elif ei_class == 2:
self.__type_prefix = "Elf64_"
else:
self.__type_prefix = None
# Construct the full header
self.__hdr = self._context.object(symbol_table_name + constants.BANG + self.__type_prefix + "Ehdr",
layer_name = layer_name,
offset = object_info.offset)
self.__offset = object_info.offset
self.cached_symtab = None
self.cached_strtab = None
def is_valid(self):
'''
Determine whether it is a valid object
'''
return self.__type_prefix is not None and self.__hdr is not None
def __getattr__(self, name):
# Just redirect to the corresponding header
if name[0:2] == "e_" and name in dir(self.__hdr):
return self.__hdr.__getattr__(name)
else:
return self.__getattribute__(name)
def __dir__(self):
return self.__hdr.__dir__() + ["get_program_headers", "is_valid", "get_section_headers", "get_symbols", "__dir__"]
def get_program_headers(self):
program_headers = self._context.object(self.get_symbol_table().name + constants.BANG + "array",
layer_name = self.vol.layer_name,
offset = self.__offset + self.e_phoff,
subtype = self._context.symbol_space.get_type(self.get_symbol_table().name + constants.BANG + self.__type_prefix + "Phdr"),
count = self.e_phnum)
for prog_header in program_headers:
prog_header.set_parent_e_type(self.e_type)
prog_header.set_parent_offset(self.__offset)
prog_header.set_type_prefix(self.__type_prefix)
yield prog_header
def get_section_headers(self):
section_headers = self._context.object(self.get_symbol_table().name + constants.BANG + "array",
layer_name = self.vol.layer_name,
offset = self.__offset + self.e_shoff,
subtype =
self._context.symbol_space.get_type(self.get_symbol_table().name + constants.BANG + self.__type_prefix + "Shdr"),
count = self.e_shnum)
return section_headers
def _find_symbols(self):
dt_strtab = None
dt_symtab = None
dt_strent = None
for phdr in self.get_program_headers():
try:
# Find PT_DYNAMIC segment
if str(phdr.p_type.description) != 'PT_DYNAMIC':
continue
except ValueError:
# If the p_type value is outside the ones declared in the enumeration, an
# exception is raised
return None
# This section contains pointers to the strtab, symtab, and strent sections
for dsec in phdr.dynamic_sections():
if dsec.d_tag == 5:
dt_strtab = dsec.d_ptr
elif dsec.d_tag == 6:
dt_symtab = dsec.d_ptr
elif dsec.d_tag == 11:
# Size of the symtab symbol entry
dt_strent = dsec.d_ptr
break
if dt_strtab == None or dt_symtab == None or dt_strent == None:
return None
self.cached_symtab = dt_symtab
self.cached_strtab = dt_strtab
# Calculate number of symbol entries assuming that strtab follows symtab
if dt_symtab < dt_strtab:
self.cached_numsyms = int((int(dt_strtab) - int(dt_symtab)) / dt_strent)
else:
self.cached_numsyms = 1024
def get_symbols(self):
if self.cached_symtab == None:
self._find_symbols()
if self.cached_symtab == None:
return
symtab_arr = self._context.object(self.get_symbol_table().name + constants.BANG + "array",
layer_name = self.vol.layer_name,
offset = self.cached_symtab,
subtype = self._context.symbol_space.get_type(self.get_symbol_table().name + constants.BANG + self.__type_prefix + "Sym"),
count = self.cached_numsyms)
for sym in symtab_arr:
sym.set_cached_strtab(self.cached_strtab)
yield sym
class elf_sym(objects.StructType):
""" An elf symbol entry"""
def set_cached_strtab(self, cached_strtab):
self.cached_strtab = cached_strtab
def get_name(self):
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]
else:
return None
return name_bytes.decode('utf-8', errors='ignore')
class elf_phdr(objects.StructType):
""" An elf program header """
def __init__(self, context: interfaces.context.ContextInterface, type_name: str,
object_info: interfaces.objects.ObjectInformation, size: int,
members: Dict[str, Tuple[int, interfaces.objects.Template]]) -> None:
super().__init__(context = context,
type_name = type_name,
object_info = object_info,
size = size,
members = members)
self.__parent_e_type = None
self.__parent_offset = None
self.__type_prefix = None
def set_parent_e_type(self, e_type):
self.__parent_e_type = e_type
def set_parent_offset(self, offset):
self.__parent_offset = offset
def set_type_prefix(self, prefix):
self.__type_prefix = prefix
def get_vaddr(self):
offset = self.__getattr__("p_vaddr")
if self.__parent_e_type == 3: # ET_DYN
offset = self.__parent_offset + offset
return offset
def dynamic_sections(self):
# sanity check
try:
if str(self.p_type.description) != 'PT_DYNAMIC':
return None
except ValueError:
# If the value is outside the ones declared in the enumeration, an
# exception is raised
return None
# the buffer of array starts at elf_base + our virtual address ( offset )
arr_start = self.get_vaddr()
symbol_table_name = self.get_symbol_table().name
rtsize = self._context.symbol_space.get_type(symbol_table_name + \
constants.BANG + \
self.__type_prefix + "Dyn").size
for i in range(256):
# use the real size
idx = i * rtsize
dyn = self._context.object(symbol_table_name + constants.BANG + self.__type_prefix + "Dyn",
layer_name = self.vol.layer_name,
offset = arr_start + idx)
yield dyn
if dyn.d_tag == 0:
break