# Volatility
# Copyright (C) 2007-2013 Volatility Foundation
#
# This file is part of Volatility.
#
# Volatility is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License Version 2 as
# published by the Free Software Foundation. You may not use, modify or
# distribute this program under any other version of the GNU General
# Public License.
#
# Volatility 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 Volatility. If not, see .
#
"""
@author: Andrew Case
@license: GNU General Public License 2.0
@contact: atcuno@gmail.com
@organization:
"""
import re, os, struct
import volatility.obj as obj
import volatility.debug as debug
import volatility.plugins.linux.common as linux_common
class linux_lsmod(linux_common.AbstractLinuxCommand):
"""Gather loaded kernel modules"""
def __init__(self, config, *args, **kwargs):
linux_common.AbstractLinuxCommand.__init__(self, config, *args, **kwargs)
self._config.add_option('SECTIONS', short_option = 'T', default = None, help = 'show section addresses', action = 'store_true')
self._config.add_option('PARAMS', short_option = 'P', default = None, help = 'show module parameters', action = 'store_true')
self._config.add_option('BASE', short_option = 'b', default = None, help = 'Dump driver with BASE address (in hex)', action = 'store', type = 'int')
self._config.add_option('IDC', short_option = 'c', default = None, help = 'Path to IDC file to be created for module', action = 'store', type = 'str')
def _get_modules(self):
if self._config.BASE:
module_address = int(self._config.BASE)
yield obj.Object("module", offset = module_address, vm = self.addr_space)
else:
modules_addr = self.addr_space.profile.get_symbol("modules")
modules = obj.Object("list_head", vm = self.addr_space, offset = modules_addr)
# walk the modules list
for module in modules.list_of_type("module", "list"):
yield module
def calculate(self):
linux_common.set_plugin_members(self)
for module in self._get_modules():
if self._config.PARAMS:
if not hasattr(module, "kp"):
debug.error("Gathering module parameters is not supported in this profile.")
params = module.get_params()
else:
params = ""
if self._config.SECTIONS:
sections = module.get_sections()
else:
sections = []
yield (module, sections, params)
def render_text(self, outfd, data):
for (module, sections, params) in data:
if self._config.IDC:
fd = open(self._config.IDC, "w")
fd.write("#include \nstatic main(void) {\n")
for (sname, saddr) in module.get_symbols():
fd.write(" MakeDword(0x{0:08X});\n".format(saddr))
fd.write(" MakeName(0x{0:08X}, \"{1}\");\n".format(saddr, sname))
fd.write("}")
outfd.write("{2:x} {0:s} {1:d}\n".format(module.name, module.init_size + module.core_size, module.obj_offset))
# will be empty list if not set on command line
for sect in sections:
outfd.write("\t{0:30s} {1:#x}\n".format(sect.sect_name, sect.address))
# will be "" if not set, otherwise will be space seperated
if params != "":
for param in params.split():
outfd.write("\t{0:100s}\n".format(param))
def get_module(self, name):
ret = None
for (module, _, _) in self.calculate():
if str(module.name) == name:
ret = module
break
return ret
# returns a list of tuples of (name, .text start, .text end) for each module
# include_list can contain a list of only the modules wanted by a plugin
def get_modules(self, include_list = None):
if not include_list:
include_list = []
ret = []
for (module, _sections, _params) in self.calculate():
if len(include_list) == 0 or str(module.name) in include_list:
start = module.module_core
end = start + module.core_size
ret.append(("%s" % module.name, start, end))
return ret
class linux_moddump(linux_common.AbstractLinuxCommand):
"""Extract loaded kernel modules"""
def __init__(self, config, *args, **kwargs):
self.name_idx = 1
self.idc_started = False
linux_common.AbstractLinuxCommand.__init__(self, config, *args, **kwargs)
config.add_option('DUMP-DIR', short_option = 'D', default = None,
help = 'Directory in which to dump the files',
action = 'store', type = 'string')
config.add_option('REGEX', short_option = 'r',
help = 'Dump modules matching REGEX',
action = 'store', type = 'string')
config.add_option('IGNORE-CASE', short_option = 'i',
help = 'Ignore case in pattern match',
action = 'store_true', default = False)
config.add_option('BASE', short_option = 'b', default = None,
help = 'Dump driver with BASE address (in hex)',
action = 'store', type = 'int')
def calculate(self):
linux_common.set_plugin_members(self)
if self._config.REGEX:
try:
if self._config.IGNORE_CASE:
mod_re = re.compile(self._config.REGEX, re.I)
else:
mod_re = re.compile(self._config.REGEX)
except re.error, e:
debug.error('Error parsing regular expression: {0}'.format(e))
if self._config.BASE:
module_address = int(self._config.BASE)
yield obj.Object("module", offset = module_address, vm = self.addr_space)
else:
# walk the modules list
modules_addr = self.addr_space.profile.get_symbol("modules")
modules = obj.Object("list_head", vm = self.addr_space, offset = modules_addr)
for module in modules.list_of_type("module", "list"):
if self._config.REGEX:
if not mod_re.search(str(module.name)):
continue
yield module
def _get_header_64(self, load_addr, sect_hdr_offset, num_sects):
e_ident = "\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00"
e_type = "\x01\x00" # relocateble
e_machine = "\x03\x00"
e_version = "\x01\x00\x00\x00"
e_entry = "\x00" * 8
e_phoff = "\x00" * 8
e_shoff = struct.pack(" 0 and not module.obj_vm.profile.get_symbol_by_address("kernel", sym.st_value):
val_map[sym.st_value.v()] = self._find_sec(sections_info, sym.st_value)
for (i, sect) in enumerate(module.get_sections()):
name_idx_map[str(sect.sect_name)] = (i + 1, sect.address) ### account for null segment
syms = obj.Object(theType="Array", targetType=sym_type, count=module.num_symtab, vm = module.obj_vm, offset = module.symtab)
for sym in syms:
# fix absolute addresses
st_value_int = sym.st_value.v()
if st_value_int > 0 and st_value_int in val_map:
secname = val_map[st_value_int]
if secname in name_idx_map:
sect_addr = name_idx_map[secname][1]
# LOOK_HERE
st_value_sub = st_value_int - sect_addr
st_value_full = st_value_int
else:
st_value_sub = st_value_int
st_value_full = st_value_int
st_value = struct.pack(st_value_fmt, st_value_sub)
#### fix bindings ####
# moved out of the sections part
if sym.st_name > 0:
first_name = True
if first_name:
bind = 1 # STB_GLOBAL
if sym.st_value == 0:
stype = 0
elif module.obj_vm.profile.get_symbol_by_address("kernel", sym.st_value):
stype = 0 # STT_NOTYPE
else:
secname = val_map[sym.st_value.v()]
# a .text. section but not relocations
if secname.find(".text") != -1 and secname.find(".rela") == -1:
stype = 2 # STT_FUNC
else:
stype = 1 # STT_OBJECT
else:
bind = 0 # STB_LOCAL
stype = 3 # STT_SECTION
b = (bind << 4) & 0xf0
t = stype & 0xf
st_info = (b | t) & 0xff
#print "st_info: %x : %x | %x || %d | %x" % (sym.st_value, b, t, st_info, st_info)
st_info = struct.pack("B", st_info)
#### fix indexes ####
if sym.st_value > 0 and sym.st_value.v() in val_map:
secname = val_map[sym.st_value.v()]
if secname in name_idx_map:
st_shndx = name_idx_map[secname][0]
st_shndx = struct.pack("