# 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("