def calculate(self): linux_common.set_plugin_members(self) # Automatically initialize task_struct offsets task_struct.init_offsets(self.addr_space) if not all([task_struct.is_offset_defined(memname) for memname in ['comm', 'tasks', 'mm']]): debug.warning("Some of required members of 'task_struct' structure were not found.") return ksymbol_command = linux_auto_ksymbol(self._config) init_task_addr = ksymbol_command.get_symbol('init_task') if init_task_addr is None: debug.warning("Can't locate the first process (swapper).") return init_task = obj.Object('task_struct', offset=init_task_addr, vm=self.addr_space) tasks_dtb_list = [] for task in init_task.tasks: if mm_struct.is_offset_defined('pgd'): pgd = task.mm.pgd if pgd: tasks_dtb_list.append(self.addr_space.vtop(pgd)) yield task # List unnamed potentially hidden or terminated processes # auto-discovered by dtblist command. dtblist_command = linux_auto_dtblist(self._config) for dtb in dtblist_command.calculate(): if dtb not in tasks_dtb_list: yield dtb
def calculate(self): linux_common.set_plugin_members(self) knl_addr = self.addr_space.profile.get_symbol("keyboard_notifier_list") if not knl_addr: debug.error("Symbol keyboard_notifier_list not found in kernel") knl = obj.Object("atomic_notifier_head", offset = knl_addr, vm = self.addr_space) symbol_cache = {} for call_back in linux_common.walk_internal_list("notifier_block", "next", knl.head): call_addr = call_back.notifier_call if symbol_cache.has_key(call_addr): sym_name = symbol_cache[call_addr] hooked = 0 else: sym_name = self.profile.get_symbol_by_address("kernel", call_addr) if not sym_name: sym_name = "HOOKED" module = obj.Object("module", offset = 0xffffffffa03a15d0, vm = self.addr_space) sym = module.get_symbol_for_address(call_addr) sym_name = "%s: %s/%s" % (sym_name, module.name, sym) hooked = 1 symbol_cache[call_addr] = sym_name yield call_addr, sym_name, hooked
def calculate(self): linux_common.set_plugin_members(self) for cache in self.get_all_kmem_caches(): if cache.get_type() == "slab": active_objs = 0 active_slabs = 0 num_slabs = 0 # shared_avail = 0 for slab in cache._get_full_list(): active_objs += cache.num active_slabs += 1 for slab in cache._get_partial_list(): active_objs += slab.inuse active_slabs += 1 for slab in cache._get_free_list(): num_slabs += 1 num_slabs += active_slabs num_objs = num_slabs * cache.num yield [cache.get_name(), active_objs, num_objs, cache.buffer_size, cache.num, 1 << cache.gfporder, active_slabs, num_slabs]
def calculate(self): """ Get all the python strings for a task, and assume those strings might be keys of a dictionary entry. Return the valid dictionary entries from that pool of maybes. This repeats a lot of linux_python_strings's code, but we want to get python strings per task, so we can optimize the bytstring search. """ linux_common.set_plugin_members(self) tasks = [task for task in linux_pslist.linux_pslist.calculate(self) if _is_python_task(task)] for task in tasks: addr_space = task.get_process_address_space() memory_model = addr_space.profile.metadata.get('memory_model', '32bit') pack_format = "I" if memory_model == '32bit' else "Q" bytestrings = [ # the hash as bytes struct.pack(pack_format.lower(), py_string.ob_shash) + # the pointer the PyStringObject as bytes struct.pack(pack_format, py_string.obj_offset) for py_string in find_python_strings(task) ] for address in task.search_process_memory(bytestrings, heap_only=True): py_dict_entry = obj.Object("_PyDictEntry", offset=address, vm=addr_space) if py_dict_entry.is_valid(): yield task, py_dict_entry
def calculate(self): linux_common.set_plugin_members(self) tasks = linux_pslist.linux_pslist.calculate(self) for task in tasks: for elf, elf_start, elf_end, soname, needed in task.elfs(): yield task, elf, elf_start, elf_end, soname, needed
def calculate(self): linux_common.set_plugin_members(self) modules = linux_lsmod.linux_lsmod(self._config).get_modules() op_members = self.profile.types['file_operations'].keywords["members"].keys() seq_members = self.profile.types['seq_operations'].keywords["members"].keys() tcp = ("tcp_seq_afinfo", ["tcp6_seq_afinfo", "tcp4_seq_afinfo"]) udp = ("udp_seq_afinfo", ["udplite6_seq_afinfo", "udp6_seq_afinfo", "udplite4_seq_afinfo", "udp4_seq_afinfo"]) protocols = [tcp, udp] for proto in protocols: struct_type = proto[0] for global_var_name in proto[1]: global_var_addr = self.addr_space.profile.get_symbol(global_var_name) if not global_var_addr: continue global_var = obj.Object(struct_type, offset = global_var_addr, vm = self.addr_space) for (name, member, address) in self.check_afinfo(global_var_name, global_var, op_members, seq_members, modules): yield (name, member, address)
def calculate(self): linux_common.set_plugin_members(self) modules_addr = self.get_profile_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"): #if str(module.name) == "rootkit": # continue if self._config.PARAMS: if not hasattr(module, "kp"): debug.error("Gathering module parameters is not supported in this profile.") params = self.get_params(module) else: params = "" if self._config.SECTIONS: sections = self.get_sections(module) else: sections = [] yield (module, sections, params)
def render_text(self, outfd, data): linux_common.set_plugin_members(self) self.edir = self._config.DUMP_DIR if not self.edir: debug.error("No output directory given.") if not os.path.isdir(self.edir): debug.error(self.edir + " is not a directory") for task in linux_netstat.linux_netstat(self._config).calculate(): sfop = task.obj_vm.profile.get_symbol("socket_file_ops") dfop = task.obj_vm.profile.get_symbol("sockfs_dentry_operations") for (filp, fdnum) in task.lsof(): if filp.f_op == sfop or filp.dentry.d_op == dfop: iaddr = filp.dentry.d_inode skt = task.SOCKET_I(iaddr) sk = skt.sk for msg in self.process_queue( "receive", task.pid, fdnum, sk.sk_receive_queue): outfd.write(msg + "\n") for msg in self.process_queue( "write", task.pid, fdnum, sk.sk_write_queue): outfd.write(msg + "\n")
def calculate(self): linux_common.set_plugin_members(self) find_file = self._config.FIND inode_addr = self._config.inode outfile = self._config.outfile listfiles = self._config.LISTFILES if listfiles: for (_, _, file_path, file_dentry) in self.walk_sbs(): yield (file_path, file_dentry.d_inode) elif find_file and len(find_file): for (_, _, file_path, file_dentry) in self.walk_sbs(): if file_path == find_file: yield (file_path, file_dentry.d_inode) break elif inode_addr and inode_addr > 0 and outfile and len(outfile) > 0: inode = obj.Object("inode", offset = inode_addr, vm = self.addr_space) f = open(outfile, "wb") for page in self.get_file_contents(inode): f.write(page) f.close() else: debug.error("Incorrect command line parameters given.")
def calculate(self): linux_common.set_plugin_members(self) for dentry_offset in self._compare_filps(): dentry = obj.Object("dentry", offset = dentry_offset, vm = self.addr_space) if dentry.d_count > 0 and dentry.d_inode.is_reg() and dentry.d_flags == 128: yield dentry
def get_file_contents(self, inode): linux_common.set_plugin_members(self) data = "" file_size = inode.i_size if not inode.is_valid() or file_size == None: raise StopIteration extra = file_size % 4096 idxs = file_size / 4096 if extra > 0: extra = 4096 - extra idxs = idxs + 1 if idxs > 1000000000: raise StopIteration for idx in range(0, idxs): data = self.get_page_contents(inode, idx) # this is to chop off any extra data on the last page if idx == idxs - 1: if extra > 0: extra = extra * -1 data = data[:extra] yield data
def get_file_contents(self, inode): linux_common.set_plugin_members(self) if self.addr_space.profile.metadata.get('memory_model', '32bit') == "32bit": self.ptr_size = 4 else: self.ptr_size = 8 data = "" file_size = inode.i_size if not inode.is_valid() or file_size == None: raise StopIteration extra = file_size % 4096 idxs = file_size / 4096 if extra > 0: extra = 4096 - extra idxs = idxs + 1 if idxs > 1000000000: raise StopIteration for idx in range(0, idxs): data = self.get_page_contents(inode, idx) # this is to chop off any extra data on the last page if idx == idxs - 1: if extra > 0: extra = extra * -1 data = data[:extra] yield data
def calculate(self): linux_common.set_plugin_members(self) for (_, _, file_path, file_dentry)in linux_find_file.linux_find_file(self._config).walk_sbs(): inode = file_dentry.d_inode yield inode, inode.i_ino, file_path
def _walk_xarray_pids(self): ff = find_file.linux_find_file(self._config) linux_common.set_plugin_members(ff) self.XARRAY_TAG_MASK = 3 self.XARRAY_TAG_INTERNAL = 2 self.XA_CHUNK_SHIFT = 6 self.XA_CHUNK_SIZE = 1 << self.XA_CHUNK_SHIFT self.XA_CHUNK_MASK = self.XA_CHUNK_SIZE - 1 ns_addr = self.addr_space.profile.get_symbol("init_pid_ns") ns = obj.Object("pid_namespace", offset = ns_addr, vm = self.addr_space) xarray = ns.idr.idr_rt if not xarray.is_valid(): return root = xarray.xa_head.v() is_internal = ff.xa_is_internal(root) if root & self.XARRAY_TAG_MASK != 0: root = root & ~self.XARRAY_TAG_MASK height = 0 node = obj.Object("xa_node", offset = root, vm = self.addr_space) if is_internal and hasattr(node, "shift"): height = (node.shift / self.XA_CHUNK_SHIFT) + 1 for node in self._do_walk_xarray(ff, node, height, 0): if node and node.is_valid(): yield node
def calculate(self): ## we need this module imported if not has_yara: debug.error("Please install Yara from https://plusvic.github.io/yara/") ## leveraged from the windows yarascan plugin rules = self._compile_rules() ## set the linux plugin address spaces linux_common.set_plugin_members(self) if self._config.KERNEL: ## the start of kernel memory taken from VolatilityLinuxIntelValidAS if self.addr_space.profile.metadata.get('memory_model', '32bit') == "32bit": kernel_start = 0xc0000000 else: kernel_start = 0xffffffff80000000 scanner = malfind.DiscontigYaraScanner(rules = rules, address_space = self.addr_space) for hit, address in scanner.scan(start_offset = kernel_start): yield (None, address - self._config.REVERSE, hit, scanner.address_space.zread(address - self._config.REVERSE, self._config.SIZE)) else: tasks = self.filter_tasks() for task in tasks: scanner = VmaYaraScanner(task = task, rules = rules) for hit, address in scanner.scan(): yield (task, address - self._config.REVERSE, hit, scanner.address_space.zread(address - self._config.REVERSE, self._config.SIZE))
def calculate(self): ## we need this module imported if not has_yara: debug.error("Please install Yara from code.google.com/p/yara-project") ## leveraged from the windows yarascan plugin rules = self._compile_rules() ## set the linux plugin address spaces linux_common.set_plugin_members(self) if self._config.KERNEL: ## the start of kernel memory taken from VolatilityLinuxIntelValidAS if self.addr_space.profile.metadata.get('memory_model', '32bit') == "32bit": kernel_start = 0xc0000000 else: kernel_start = 0xffffffff80000000 scanner = malfind.DiscontigYaraScanner(rules = rules, address_space = self.addr_space) for hit, address in scanner.scan(start_offset = kernel_start): yield (None, address, hit, scanner.address_space.zread(address, 64)) else: for task in pslist.linux_pslist(self._config).calculate(): scanner = VmaYaraScanner(task = task, rules = rules) for hit, address in scanner.scan(): yield (task, address, hit, scanner.address_space.zread(address, 64))
def calculate(self): """ This works by walking the system call table and verifies that each is a symbol in the kernel """ linux_common.set_plugin_members(self) num_syscalls = self._get_syscall_table_size() syscall_addr = self._get_syscall_table_address() sym_addrs = self.profile.get_all_addresses() table = obj.Object("Array", offset = syscall_addr, vm = self.addr_space, targetType = "unsigned int", count = num_syscalls) for (i, call_addr) in enumerate(table): if not call_addr: continue # have to treat them as 'long' so need to mask call_addr = call_addr & 0xffffffff if not call_addr in sym_addrs: yield(i, call_addr, 1) else: yield(i, call_addr, 0)
def calculate(self): linux_common.set_plugin_members(self) find_file = self._config.FIND inode_addr = self._config.inode outfile = self._config.outfile listfiles = self._config.LISTFILES if listfiles: for (_, _, file_path, file_dentry) in self.walk_sbs(): yield (file_path, file_dentry.d_inode) elif find_file and len(find_file): for (_, _, file_path, file_dentry) in self.walk_sbs(): if file_path == find_file: yield (file_path, file_dentry.d_inode) break elif inode_addr and inode_addr > 0 and outfile and len(outfile) > 0: inode = obj.Object("inode", offset = inode_addr, vm = self.addr_space) try: f = open(outfile, "wb") except IOError, e: debug.error("Unable to open output file (%s): %s" % (outfile, str(e))) for page in self.get_file_contents(inode): f.write(page) f.close()
def calculate(self): linux_common.set_plugin_members(self) fs_types = self._get_filesystem_types() # newer kernels if self.profile.has_type("mount"): mnttype = "mount" cache = linux_slabinfo(self._config).get_kmem_cache(mnttype, self._config.UNALLOCATED) for task in linux_pslist.linux_pslist(self._config).calculate(): if task.pid == 1: ns = task.nsproxy.mnt_ns break else: cache = linux_slabinfo(self._config).get_kmem_cache( "mnt_cache", self._config.UNALLOCATED, struct_name="vfsmount" ) ns = None for mnt in cache: ret = self._parse_mnt(mnt, ns, fs_types) if ret: (mnt_sb, dev_name, path, fstype, rr, mnt_string) = ret if not (dev_name == "devtmpfs" and path == "/"): yield (mnt_sb, dev_name, path, fstype, rr, mnt_string)
def calculate(self): linux_common.set_plugin_members(self) io_ptr = self.addr_space.profile.get_symbol("iomem_resource") for r in self.yield_resource(io_ptr): yield r
def render_text(self, outfd, data): linux_common.set_plugin_members(self) self.table_header(outfd, [("Task", "10"), ("ELF Start", "[addrpad]"), ("ELF Name", "24"), ("Symbol", "24"), ("Resolved Address", "[addrpad]"), ("H", "1"), ("Target Info", "")]) ignore = frozenset(self._config.IGNORE) for task in data: for soname, elf, elf_start, elf_end, addr, symbol_name, hookdesc, hooked in task.plt_hook_info(): if not hooked and not self._config.ALL: continue if hookdesc in ignore: continue if hookdesc == '[RTLD_LAZY]' and not self._config.ALL: continue self.table_row(outfd, task.pid, elf_start, soname if soname else '[main]', \ symbol_name, addr, '!' if hooked else ' ', hookdesc)
def calculate(self): linux_common.set_plugin_members(self) hook_names = ["PRE_ROUTING", "LOCAL_IN", "FORWARD", "LOCAL_OUT", "POST_ROUTING"] proto_names = ["", "", "IPV4", "", "", "", "", "", "", "", "" , "", "", ""] # struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS] # NFPROTO_NUMPROTO = 12 # NF_MAX_HOOKS = 7 nf_hooks_addr = self.addr_space.profile.get_symbol("nf_hooks") if nf_hooks_addr == None: debug.error("Unable to analyze NetFilter. It is either disabled or compiled as a module.") modules = linux_lsmod.linux_lsmod(self._config).get_modules() list_head_size = self.addr_space.profile.get_obj_size("list_head") for outer in range(13): arr = nf_hooks_addr + (outer * (list_head_size * 8)) for inner in range(7): list_head = obj.Object("list_head", offset = arr + (inner * list_head_size), vm = self.addr_space) for hook_ops in list_head.list_of_type("nf_hook_ops", "list"): if self.is_known_address(hook_ops.hook.v(), modules): hooked = "False" else: hooked = "True" yield proto_names[outer], hook_names[inner], hook_ops.hook.v(), hooked
def get_file_contents(self, inode): linux_common.set_plugin_members(self) data = "" file_size = inode.i_size extra = file_size % 4096 idxs = file_size / 4096 if extra != 0: extra = 4096 - extra idxs = idxs + 1 for idx in range(0, idxs): data = data + self.get_page_contents(inode, idx) # this is chop off any extra data on the last page if extra != 0: extra = extra * -1 data = data[:extra] return data
def calculate(self): linux_common.set_plugin_members(self) ps_sources = {} # The keys are names of process sources # The values are the virtual offset of the task_struct ps_sources['pslist'] = self._get_pslist() ps_sources['pid_hash'] = self._get_pid_hash() ps_sources['kmem_cache'] = self._get_kmem_cache() ps_sources['parents'] = self._get_task_parents() ps_sources['thread_leaders'] = self._get_thread_leaders() # Build a list of offsets from all sources seen_offsets = [] for source in ps_sources: tasks = ps_sources[source] for offset in tasks: if offset not in seen_offsets: seen_offsets.append(offset) yield offset, obj.Object("task_struct", offset = offset, vm = self.addr_space), ps_sources
def calculate(self): linux_common.set_plugin_members(self) ntables_ptr = obj.Object("Pointer", offset = self.get_profile_symbol("neigh_tables"), vm = self.addr_space) for ntable in linux_common.walk_internal_list("neigh_table", "next", ntables_ptr): yield self.handle_table(ntable)
def calculate(self): linux_common.set_plugin_members(self) modules = linux_lsmod.linux_lsmod(self._config).get_modules() f_op_members = self.profile.types["file_operations"].keywords["members"].keys() f_op_members.remove("owner") if self._config.INODE: inode = obj.Object("inode", offset=self._config.INODE, vm=self.addr_space) if not inode.is_valid(): debug.error( "Invalid inode address given. Please use linux_find_file to determine valid inode addresses." ) for (hooked_member, hook_address) in self.verify_ops(inode.i_fop, f_op_members, modules): yield ("inode at {0:x}".format(inode.obj_offset), hooked_member, hook_address) else: funcs = [self.check_open_files_fop, self.check_proc_fop, self.check_proc_root_fops, self.check_file_cache] for func in funcs: for (name, member, address) in func(f_op_members, modules): yield (name, member, address)
def calculate(self): linux_common.set_plugin_members(self) find_file = self._config.FIND inode_addr = self._config.inode outfile = self._config.outfile if find_file and len(find_file): wanted_dentry = self.walk_sbs(find_file) if wanted_dentry: yield wanted_dentry elif inode_addr and inode_addr > 0 and outfile and len(outfile) > 0: inode = obj.Object("inode", offset=inode_addr, vm=self.addr_space) contents = self.get_file_contents(inode) f = open(outfile, "wb") f.write(contents) f.close() else: debug.error("Incorrect command line parameters given.")
def calculate(self): linux_common.set_plugin_members(self) init_task_addr = self.get_profile_symbol("init_task") init_task = obj.Object("task_struct", vm = self.addr_space, offset = init_task_addr) pidlist = self._config.PID pnamelist = self._config.PROCNAMES #pdb.set_trace if pidlist: pidlist = [int(p) for p in self._config.PID.split(',')] if pnamelist: pnamelist = [str(q) for q in self._config.PROCNAMES.split(',')] print pidlist print pnamelist # walk the ->tasks list, note that this will *not* display "swapper" for task in init_task.tasks: type(task.comm) #print task.comm if not pidlist and not pnamelist: yield task else: if pidlist and task.pid in pidlist: yield task if pnamelist and str(task.comm) in pnamelist: yield task
def calculate(self): linux_common.set_plugin_members(self) phys_addr_space = utils.load_as(self._config, astype="physical") if phys_addr_space.profile.metadata.get("memory_model", "32bit") == "32bit": fmt = "<I" else: fmt = "<Q" needles = [] for sym in phys_addr_space.profile.get_all_symbol_names("kernel"): if sym.find("_sched_class") != -1: addr = phys_addr_space.profile.get_symbol(sym) needles.append(struct.pack(fmt, addr)) if len(needles) == 0: debug.error("Unable to scan for processes. Please file a bug report.") back_offset = phys_addr_space.profile.get_obj_offset("task_struct", "sched_class") scanner = poolscan.MultiPoolScanner(needles) for _, offset in scanner.scan(phys_addr_space): ptask = obj.Object("task_struct", offset=offset - back_offset, vm=phys_addr_space) if not ptask.exit_state.v() in [0, 16, 32, 16 | 32]: continue if not (0 < ptask.pid < 66000): continue yield ptask
def calculate(self): linux_common.set_plugin_members(self) # a list of root directory entries if self._config.DUMP_DIR and self._config.SB: if not os.path.isdir(self._config.DUMP_DIR): debug.error(self._config.DUMP_DIR + " is not a directory") # this path never 'yield's, just writes the filesystem to disk tmpfs_sbs = self.get_tmpfs_sbs() sb_idx = self._config.SB - 1 if sb_idx >= len(tmpfs_sbs): debug.error("Invalid superblock number given. Please use the -L option to determine valid numbers.") root_dentry = tmpfs_sbs[sb_idx][0].s_root self.walk_sb(root_dentry) elif self._config.LIST_SBS: # vfsmnt.mnt_sb.s_root tmpfs_sbs = self.get_tmpfs_sbs() for (i, (_sb, path)) in enumerate(tmpfs_sbs): yield (i + 1, path) else: debug.error("No sb number/output directory combination given and list superblocks not given")
def calculate(self): linux_common.set_plugin_members(self) func = self.determine_func() for task in func(): yield task
def calculate(self): global PAGE_SIZE self.read_logs() linux_common.set_plugin_members(self) tasks = linux_pslist.linux_pslist.calculate(self) phys_addr_space = utils.load_as(self._config, astype = 'physical') for task in tasks: if task.mm: for vma in task.get_proc_maps(): (fname, major, minor, ino, pgoff) = vma.info(task) t_start, t_end = vma.vm_start, vma.vm_end cur_start, cur_end = None, None p_res, p_all = 0, 0 file_offset_start, file_offset_end = 0x0, 0x0 proc_as = task.get_process_address_space() # ########################################################## # Ooutput vectors and running parameters vma_one_hot = "" vma_trans_offsets = [] vma_trans_range = [] vma_to_file = [] matched_files = matched_set() # ########################################################## # We process the ranges on page-size while t_start < t_end: p_all += 1 temp = proc_as.translate(t_start) if temp != None: p_res += 1 temp_file = proc_as.base.translate(temp) vma_trans_offsets.append((t_start,temp,temp_file)) vma_to_file.append(temp_file) vma_one_hot += "1" result = self.bin_search(self.comp_list,temp_file) if result: matched_files.insert(result) # First Start if cur_start == None: cur_start = temp cur_end = temp else: cur_end = temp else: vma_one_hot += "0" if cur_start != None: vma_trans_range.append((cur_start,cur_end)) cur_start, cur_end = None, None t_start += PAGE_SIZE if cur_start != None: vma_trans_range.append((cur_start,cur_end)) vma_file = {} matched_files.do_sort() vma_file["FOffsets"] = str(matched_files) vma_file["PAllocs"] = str(p_res)+"/"+str(p_all) yield task, vma, vma_file
def calculate(self): """ This works by walking the system call table and verifies that each is a symbol in the kernel """ linux_common.set_plugin_members(self) if not has_distorm: debug.warning( "distorm not installed. The best method to calculate the system call table size will not be used." ) if self._config.SYSCALL_INDEXES: if not os.path.exists(self._config.SYSCALL_INDEXES): debug.error("Given syscall indexes file does not exist!") index_names = {} for line in open(self._config.SYSCALL_INDEXES, "r").readlines(): ents = line.split() if len(ents) == 3 and ents[0] == "#define": name = ents[1].replace("__NR_", "") # "(__NR_timer_create+1)" index = ents[2] if index[0] == "(": index = self._find_index(index_names, index) else: index = int(index) index_names[index] = name else: index_names = None hidden_mods = list( linux_hidden_modules.linux_hidden_modules( self._config).calculate()) visible_mods = linux_lsmod.linux_lsmod(self._config).calculate() table_name = self.addr_space.profile.metadata.get( 'memory_model', '32bit') sym_addrs = self.profile.get_all_addresses() sys_call_info = self._get_table_info("sys_call_table") addrs = [(table_name, sys_call_info)] # 64 bit systems with 32 bit emulation ia32 = self.addr_space.profile.get_symbol("ia32_sys_call_table") if ia32: ia32_info = self._get_table_info("ia32_sys_call_table") addrs.append(("32bit", ia32_info)) for (table_name, (tableaddr, tblsz)) in addrs: table = obj.Object(theType='Array', offset=tableaddr, vm=self.addr_space, targetType='unsigned long', count=tblsz) for (i, call_addr) in enumerate(table): if not call_addr: continue if index_names: idx_name = self._index_name(index_names, i) else: idx_name = "" call_addr = int(call_addr) if not call_addr in sym_addrs: hooked = 1 sym_name = self._compute_hook_sym_name( visible_mods, hidden_mods, call_addr) else: hooked = 0 sym_name = self.profile.get_symbol_by_address( "kernel", call_addr) yield (tableaddr, table_name, i, idx_name, call_addr, sym_name, hooked)
def calculate(self): linux_common.set_plugin_members(self) tasks = linux_pslist.linux_pslist(self._config).calculate() for task in tasks: if self.init_for_task(task): if self.profile.metadata.get('memory_model') == '64bit': self.profile.vtypes.update(ZshProfile64().zshprofile) else: # default/fallback profile self.profile.vtypes.update(ZshProfile32().zshprofile) self.profile.compile() chunks_dict = dict() chunk_data_pointers = list() chunk_size = self.get_aligned_size( self.profile.get_obj_size('histent')) data_offset = self.profile.get_obj_offset("malloc_chunk", "fd") for chunk in self.get_all_allocated_chunks(): chunks_dict[chunk.v() + data_offset] = chunk chunk_data_pointers.append(chunk.v() + data_offset) commands_dict = dict() valid_histentry = None # we first try to find a chunk that most probably contains a # histent struct for chunk in self.get_all_allocated_chunks(): if not chunk.chunksize() == chunk_size: continue histent = obj.Object('histent', offset=chunk.v() + data_offset, vm=self.process_as) # we test if the current histent struct seems to be valid # first test: do we know the chunks where relevant # pointers point to pointers = [ histent.node.nam.v(), histent.down.v(), histent.up.v() ] if not len(set(pointers) & set(chunk_data_pointers)) \ == len(pointers): continue # second test: points the previous/next histent entry to # this histent entry? if not histent.up.down == histent or not histent.down.up \ == histent: continue # we hopefully found one valid_histentry = histent break if valid_histentry: debug.debug( "We probably found a valid histent chunk and now " "start walking.") # entries are linked circular so walking in one direction # should be sufficient for histent in heap_analysis.iterate_through_linked_list( valid_histentry, lambda x: x.down): command = '' try: command = chunks_dict[histent.node.nam.v()] command = command.to_string() command = command[:command.index("\x00")] except KeyError: debug.warning( "Unexpected error: chunk for given " "command-reference does not seem to exist.") except ValueError: pass if histent.stim == histent.ftim == 0 and command == '': histent_vma = heap_analysis.get_vma_for_offset( self.vmas, histent.v()) if histent_vma not in self.heap_vmas: # we most probably found the "curline" histent # struct located in zsh's .bss section. as it # doesn't contain an actual executed command, # we are skipping it continue command_number = histent.histnum start = obj.Object('UnixTimeStamp', offset=histent.stim.obj_offset, vm=self.process_as) end = obj.Object('UnixTimeStamp', offset=histent.stim.obj_offset, vm=self.process_as) commands_dict[command_number] = [ start, end, repr(command) ] for key, value in sorted(commands_dict.items()): yield (task.pid, key, value[0], value[1], value[2])
def __init__(self, config, *args, **kwargs): linux_pslist.linux_pslist.__init__(self, config, *args, **kwargs) linux_common.set_plugin_members(self) self.thread_offset = self.profile.vtypes['task_struct'][1][ 'thread_group'][0]
def calculate(self): linux_common.set_plugin_members(self) for mod in self.walk_modules_address_space(self.addr_space): yield mod
def calculate(self): linux_common.set_plugin_members(self) mntptr = obj.Object( "Pointer", offset=self.addr_space.profile.get_symbol("mount_hashtable"), vm=self.addr_space) mnt_list = obj.Object(theType="Array", offset=mntptr, vm=self.addr_space, targetType="list_head", count=8200) if self.profile.has_type("mount"): mnttype = "mount" else: mnttype = "vfsmount" ns = None fs_types = self._get_filesystem_types() hash_mnts = {} seen_outer = {} for (idx, outerlist) in enumerate(mnt_list): if outerlist == None or outerlist.next == None: continue if outerlist.next.v() in seen_outer: continue seen_outer[outerlist.next.v()] = 1 if outerlist == outerlist.next or not outerlist.m( "next").is_valid(): continue seen = {} mseen = {} for mnt in outerlist.list_of_type(mnttype, "mnt_hash"): if mnt.v() in seen: break seen[mnt.v()] = 1 if len(seen.keys()) > 1024: break if mnt.is_valid(): mkey = mnt.v() if not mkey in mseen: hash_mnts[mnt] = 1 mseen[mkey] = 1 else: break if mnt.mnt_parent.is_valid(): mkey = mnt.mnt_parent.v() if not mkey in mseen: hash_mnts[mnt.mnt_parent] = 1 mseen[mkey] = 1 if mnt.mnt_parent.mnt_parent.is_valid(): mkey = mnt.mnt_parent.mnt_parent.v() if not mkey in mseen: hash_mnts[mnt.mnt_parent.mnt_parent] = 1 mseen[mkey] = 1 child_mnts = {} for mnt in hash_mnts: cseen = {} for child_mnt in mnt.mnt_child.list_of_type(mnttype, "mnt_child"): if not child_mnt.is_valid(): break child_mnts[child_mnt] = 1 if child_mnt.v() in cseen: break if len(child_mnts.keys()) > 1024: break cseen[child_mnt.v()] = 1 if child_mnt.mnt_parent.is_valid(): child_mnts[child_mnt.mnt_parent] = 1 if child_mnt.mnt_parent.mnt_parent.is_valid(): child_mnts[child_mnt.mnt_parent.mnt_parent] = 1 tmp_mnts = list(set(hash_mnts.keys() + child_mnts.keys())) all_mnts = [] for t in tmp_mnts: tt = t.mnt_devname.dereference_as( "String", length=linux_common.MAX_STRING_LENGTH) if tt: if len(str(tt)) > 2 or (len(str(tt)) > 1 and str(tt)[0] == '/'): all_mnts.append(t) list_mnts = {} seen_m = {} for mnt in all_mnts: if mnt.v() in seen_m: continue else: seen_m[mnt.v()] = 1 for (idx, child_mnt) in enumerate( mnt.mnt_list.list_of_type(mnttype, "mnt_list")): if idx > 20: break if child_mnt.is_valid(): list_mnts[child_mnt] = 1 if child_mnt.mnt_parent.is_valid(): list_mnts[child_mnt.mnt_parent] = 1 if child_mnt.mnt_parent.mnt_parent.is_valid(): list_mnts[child_mnt.mnt_parent.mnt_parent] = 1 all_mnts = list(set(all_mnts + list_mnts.keys())) seen = {} for (idx, mnt) in enumerate(all_mnts): if mnt.mnt_sb.v() not in seen: ret = self._parse_mnt(mnt, ns, fs_types) mark = False if ret: (mnt_sb, dev_name, path, fstype, rr, mnt_string) = ret if not (dev_name == "devtmpfs" and path == "/"): yield (mnt_sb, dev_name, path, fstype, rr, mnt_string) mark = True if mark: seen[mnt.mnt_sb.v()] = 1
def calculate(self): linux_common.set_plugin_members(self) tasks = linux_pslist.linux_pslist(self._config).calculate() for task in tasks: if self.init_for_task(task): chunks_dict = dict() data_offset = self.profile.get_obj_offset("malloc_chunk", "fd") for chunk in self.get_all_allocated_chunks(): chunks_dict[chunk.v() + data_offset] = chunk if self.profile.metadata.get('memory_model') == '64bit': string_offset = 26 relevant_chunk_size = 192 pointer_offsets = [16, 24, 32, 64] else: string_offset = 18 relevant_chunk_size = 96 pointer_offsets = [12, 16, 20, 36] entry_number = 1 for chunk in chunks_dict.values(): try: # chunks containing refs to password entries typically # have a size of 96 in the tested 32 bit environment if not chunk.chunksize() == relevant_chunk_size: continue p_entry_data = chunk.to_string() field_strings = [] # the pointers to title, username and so on are at # these offsets for i in pointer_offsets: if self.profile.metadata.get( 'memory_model') == '32bit': pointer = struct.unpack( 'I', p_entry_data[i:i + 4])[0] else: pointer = struct.unpack( 'Q', p_entry_data[i:i + 8])[0] # if there is no chunk for the given pointer, we # most probably have a wrong chunk. this will # throw a KeyError exception and we proceed with # the next chunk curr_chunk_data = chunks_dict[pointer].to_string() string_size = struct.unpack( 'I', curr_chunk_data[8:12])[0] string_size *= 2 curr_string = curr_chunk_data[ string_offset:string_offset + string_size] curr_string = curr_string.decode('utf-16-le') field_strings.append(repr(curr_string)) yield (task.pid, entry_number, field_strings[0], field_strings[1], field_strings[2], field_strings[3]) entry_number += 1 except (KeyError, UnicodeDecodeError): # a password entry struct not containing a pointer to # a chunk => out of scope pass
def get_syscalls(self, index_lines=None, get_hidden=False): linux_common.set_plugin_members(self) if get_hidden: hidden_mods = list( linux_hidden_modules.linux_hidden_modules( self._config).calculate()) else: hidden_mods = [] visible_mods = linux_lsmod.linux_lsmod(self._config).calculate() if not index_lines: index_lines = self._find_and_parse_index_file() if index_lines: index_names = {} for line in index_lines.split("\n"): ents = line.split() if len(ents) == 3 and ents[0] == "#define": name = ents[1].replace("__NR_", "") # "(__NR_timer_create+1)" index = ents[2] if index[0] == "(": index = self._find_index(index_names, index) else: index = int(index) index_names[index] = name else: index_names = None table_name = self.addr_space.profile.metadata.get( 'memory_model', '32bit') sym_addrs = self.profile.get_all_addresses() sys_call_info = self._get_table_info("sys_call_table") addrs = [(table_name, sys_call_info)] # 64 bit systems with 32 bit emulation ia32 = self.addr_space.profile.get_symbol("ia32_sys_call_table") if ia32: ia32_info = self._get_table_info("ia32_sys_call_table") addrs.append(("32bit", ia32_info)) for (table_name, (tableaddr, tblsz)) in addrs: table = obj.Object(theType='Array', offset=tableaddr, vm=self.addr_space, targetType='unsigned long', count=tblsz) for (i, call_addr) in enumerate(table): if not call_addr: continue if index_names: idx_name = self._index_name(index_names, i) else: idx_name = "" call_addr = int(call_addr) if not call_addr in sym_addrs: hooked = 1 sym_name = self._compute_hook_sym_name( visible_mods, hidden_mods, call_addr) else: hooked = 0 sym_name = self.profile.get_symbol_by_address( "kernel", call_addr) yield (tableaddr, table_name, i, idx_name, call_addr, sym_name, hooked)
def calculate(self): linux_common.set_plugin_members(self) tasks = linux_pslist.linux_pslist(self._config).calculate() for task in tasks: proc_as = task.get_process_address_space() # In cases when mm is an invalid pointer if not proc_as: continue if not self._config.HISTORY_LIST: # Do we scan everything or just /bin/bash instances? if not (self._config.SCAN_ALL or str(task.comm) == "bash"): continue # Keep a bucket of history objects so we can order them history_entries = [] # Brute force the history list of an address isn't provided ts_offset = proc_as.profile.get_obj_offset( "_hist_entry", "timestamp") # Are we dealing with 32 or 64-bit pointers if proc_as.profile.metadata.get('memory_model', '32bit') == '32bit': pack_format = "I" else: pack_format = "Q" # Look for strings that begin with pound/hash on the process heap for ptr_hash in task.search_process_memory(["#"], heap_only=True): # Find pointers to this strings address, also on the heap addr = struct.pack(pack_format, ptr_hash) for ptr_string in task.search_process_memory( [addr], heap_only=True): # Check if we found a valid history entry object hist = obj.Object("_hist_entry", offset=ptr_string - ts_offset, vm=proc_as) if hist.is_valid(): history_entries.append(hist) # We can terminate this inner loop now break # Report everything we found in order for hist in sorted(history_entries, key=attrgetter('time_as_integer')): yield task, hist else: the_history_addr = the_history_addr = self._config.HISTORY_LIST the_history = obj.Object("Pointer", vm=proc_as, offset=the_history_addr) max_ents = 2001 the_history = obj.Object(theType='Array', offset=the_history, vm=proc_as, targetType='Pointer', count=max_ents) for ptr in the_history: if not ptr: if self._config.PRINTUNALLOC: continue else: break hist = ptr.dereference_as("_hist_entry") if hist.is_valid(): yield task, hist
def calculate(self): linux_common.set_plugin_members(self) for (_, _, file_path, _) in linux_find_file.linux_find_file(self._config).walk_sbs(): yield file_path
def __init__(self, config, *args, **kwargs): linux_common.AbstractLinuxCommand.__init__(self, config, *args, **kwargs) linux_common.set_plugin_members(self)
def get_syscalls(self, index_info=None, get_hidden=False, compute_name=True): linux_common.set_plugin_members(self) if get_hidden: hidden_mods = list( linux_hidden_modules.linux_hidden_modules( self._config).calculate()) else: hidden_mods = [] if compute_name: visible_mods = linux_lsmod.linux_lsmod(self._config).calculate() else: visible_mods = [] if index_info == None: index_info = self._find_and_parse_index_file() table_name = self.addr_space.profile.metadata.get( 'memory_model', '32bit') sym_addrs = self.profile.get_all_addresses() sys_call_info = self._get_table_info("sys_call_table") addrs = [(table_name, sys_call_info)] # 64 bit systems with 32 bit emulation ia32 = self.addr_space.profile.get_symbol("ia32_sys_call_table") if ia32: ia32_info = self._get_table_info("ia32_sys_call_table") addrs.append(("32bit", ia32_info)) for (table_name, (tableaddr, tblsz)) in addrs: table = obj.Object( theType='Array', offset=tableaddr, vm=self.addr_space, targetType='unsigned long', count=tblsz + 1, ) for (i, call_addr) in enumerate(table): if not call_addr: continue idx_name = self._index_name(table_name, index_info, i) call_addr = int(call_addr) if not call_addr in sym_addrs: hooked = 1 sym_name = self._compute_hook_sym_name( visible_mods, hidden_mods, call_addr) else: hooked = 0 sym_name = self.profile.get_symbol_by_address( "kernel", call_addr) yield ( tableaddr, table_name, i, idx_name, call_addr, sym_name, hooked, )
def calculate(self): if not has_yara: debug.error( "Please install Yara from https://plusvic.github.io/yara/") linux_common.set_plugin_members(self) ## the start of kernel memory taken from VolatilityLinuxIntelValidAS if (self.addr_space.profile.metadata.get('memory_model', '32bit') == "32bit"): kernel_start = 0xC0000000 pack_size = 4 pack_fmt = "<I" else: kernel_start = 0xFFFF880000000000 pack_size = 8 pack_fmt = "<Q" checks = [ self.check_family, self.check_proto, self.check_socket_back_pointer, self.check_pointers, ] destruct_offset = self.addr_space.profile.get_obj_offset( "sock", "sk_destruct") # sk_destruct pointer value of sock func_addr = self.addr_space.profile.get_symbol("inet_sock_destruct") vals = struct.pack(pack_fmt, func_addr) s = "{ " + " ".join(["%.02x" % v for v in vals]) + " }" rules = yara.compile( sources={'n': 'rule r1 {strings: $a = ' + s + ' condition: $a}'}) scanner = malfind.DiscontigYaraScanner(rules=rules, address_space=self.addr_space) for _, address in scanner.scan(start_offset=kernel_start): base_address = address - destruct_offset i = obj.Object("inet_sock", offset=base_address, vm=self.addr_space) valid = True for check in checks: if check(i) == False: valid = False break if valid: state = i.state if i.protocol == "TCP" else "" family = (i.sk.__sk_common.skc_family ) # pylint: disable-msg=W0212 sport = i.src_port dport = i.dst_port saddr = i.src_addr daddr = i.dst_addr if (str(saddr) == "0.0.0.0" and str(daddr) == "0.0.0.0" and sport == 6 and dport == 0): continue yield (i, i.protocol, saddr, sport, daddr, dport, state)
def calculate(self): linux_common.set_plugin_members(self) mntptr = obj.Object( "Pointer", offset=self.addr_space.profile.get_symbol("mount_hashtable"), vm=self.addr_space) mnt_list = obj.Object(theType="Array", offset=mntptr, vm=self.addr_space, targetType="list_head", count=8200) if self.profile.has_type("mount"): mnttype = "mount" else: mnttype = "vfsmount" ns = None fs_types = self._get_filesystem_types() hash_mnts = {} for (idx, outerlist) in enumerate(mnt_list): if outerlist == outerlist.next or not outerlist.m( "next").is_valid(): continue for mnt in outerlist.list_of_type(mnttype, "mnt_hash"): if mnt.is_valid(): hash_mnts[mnt] = 1 else: break if mnt.mnt_parent.is_valid(): hash_mnts[mnt.mnt_parent] = 1 if mnt.mnt_parent.mnt_parent.is_valid(): hash_mnts[mnt.mnt_parent.mnt_parent] = 1 child_mnts = {} for mnt in hash_mnts: for child_mnt in mnt.mnt_child.list_of_type(mnttype, "mnt_child"): if not child_mnt.is_valid(): break child_mnts[child_mnt] = 1 if child_mnt.mnt_parent.is_valid(): child_mnts[child_mnt.mnt_parent] = 1 if child_mnt.mnt_parent.mnt_parent.is_valid(): child_mnts[child_mnt.mnt_parent.mnt_parent] = 1 all_mnts = list(set(hash_mnts.keys() + child_mnts.keys())) list_mnts = {} seen_m = {} for mnt in all_mnts: if mnt.v() in seen_m: continue else: seen_m[mnt.v()] = 1 for (idx, child_mnt) in enumerate( mnt.mnt_list.list_of_type(mnttype, "mnt_list")): if idx > 20: break if child_mnt.is_valid(): list_mnts[child_mnt] = 1 if child_mnt.mnt_parent.is_valid(): list_mnts[child_mnt.mnt_parent] = 1 if child_mnt.mnt_parent.mnt_parent.is_valid(): list_mnts[child_mnt.mnt_parent.mnt_parent] = 1 all_mnts = list(set(all_mnts + list_mnts.keys())) seen = {} for (idx, mnt) in enumerate(all_mnts): if mnt.mnt_sb.v() not in seen: ret = self._parse_mnt(mnt, ns, fs_types) mark = False if ret: (mnt_sb, dev_name, path, fstype, rr, mnt_string) = ret if not (dev_name == "devtmpfs" and path == "/"): yield (mnt_sb, dev_name, path, fstype, rr, mnt_string) mark = True if mark: seen[mnt.mnt_sb.v()] = 1
def calculate(self): linux_common.set_plugin_members(self) if not self._config.BASE: debug.error("No base address specified.") if not self._config.PATH: debug.error("No known-good path specified.") fd = open(self._config.PATH, "rb") known_good = fd.read() fd.close() bufferas = addrspace.BufferAddressSpace(self._config, data=known_good) elf_hdr = obj.Object("elf_hdr", offset=0, vm=bufferas) tasks = linux_pslist.linux_pslist.calculate(self) for task in tasks: proc_as = task.get_process_address_space() for vma in task.get_proc_maps(): if self._config.BASE != vma.vm_start: continue for sym in elf_hdr.symbols(): if sym.st_value == 0 or (sym.st_info & 0xF) != 2: continue symname = elf_hdr.symbol_name(sym) sym_offset = sym.st_value # in the same vma if vma.vm_start < sym.st_value < vma.vm_end: vm_start = vma.vm_start sym_offset = sym_offset - vm_start full_address = sym.st_value else: next_vma = vma.vm_next if next_vma.vm_start < sym.st_value < next_vma.vm_end: vm_start = next_vma.vm_start sym_offset = sym.st_value - vm_start full_address = sym.st_value else: full_address = vma.vm_start + sym.st_value mem_buffer = proc_as.read(vm_start + sym_offset, sym.st_size) if sym.st_value > vma.vm_start: disk_off = sym.st_value - vm_start else: disk_off = sym.st_value disk_buffer = bufferas.read(disk_off, sym.st_size) # bad if mem_buffer != None and disk_buffer != mem_buffer: yield task, symname, full_address elif mem_buffer == None: print(f"Function {symname} paged out in memory")
def calculate(self): linux_common.set_plugin_members(self) ## the start of kernel memory taken from VolatilityLinuxIntelValidAS if self.addr_space.profile.metadata.get('memory_model', '32bit') == "32bit": kernel_start = 0xc0000000 pack_size = 4 pack_fmt = "<I" else: kernel_start = 0xffffffff80000000 pack_size = 8 pack_fmt = "<Q" checks = [ self.check_family, self.check_proto, self.check_socket_back_pointer, self.check_pointers ] destruct_offset = self.addr_space.profile.get_obj_offset( "sock", "sk_destruct") # sk_destruct pointer value of sock func_addr = self.addr_space.profile.get_symbol("inet_sock_destruct") vals = [] # convert address into a yara hex rule for bit in range(pack_size): idx = (pack_size - bit - 1) * 8 mask = 0xff << idx val = ((func_addr & mask) >> idx) & 0xff vals.insert(0, val) s = "{" + " ".join(["%.02x" % v for v in vals]) + " }" rules = yara.compile( sources={'n': 'rule r1 {strings: $a = ' + s + ' condition: $a}'}) scanner = malfind.DiscontigYaraScanner(rules=rules, address_space=self.addr_space) for _, address in scanner.scan(start_offset=kernel_start): base_address = address - destruct_offset i = obj.Object("inet_sock", offset=base_address, vm=self.addr_space) valid = True for check in checks: if check(i) == False: valid = False break if valid: state = i.state if i.protocol == "TCP" else "" family = i.sk.__sk_common.skc_family #pylint: disable-msg=W0212 sport = i.src_port dport = i.dst_port saddr = i.src_addr daddr = i.dst_addr yield (i, i.protocol, saddr, sport, daddr, dport, state)
def calculate(self): linux_common.set_plugin_members(self) # Get instructions executed when an inturrupt exception occurs swi = obj.Object("unsigned int", offset=self.SWI_BASE, vm=self.addr_space) # Get offset of address to vector_swi offset = (swi & 0x0FFF) + 8 # Verify that instruction hasn't been modified (should be: ldr pc, [pc, #???] (e59ff???)) if (swi & 0xFFFFF000) == 0xE59FF000: yield ( "SWI Offset Instruction", "PASS", "Offset: {0}".format(offset), ) else: yield ("SWI Offset Instruction", "FAIL", "{0:X}".format(swi)) return # Get vector_swi_addr from table vector_swi_addr = obj.Object("unsigned int", offset=self.SWI_BASE + (offset), vm=self.addr_space) # Check to see if vector_swi handler has been hooked if vector_swi_addr == self.addr_space.profile.get_symbol("vector_swi"): yield ( "vector_swi address", "PASS", "0x{0:X}".format(vector_swi_addr), ) else: yield ( "vector_swi address", "FAIL", "0x{0:X}".format(vector_swi_addr), ) return # Check for hooking of sys_call table pointer sc_opcode = None max_opcodes_to_check = 1024 while max_opcodes_to_check: opcode = obj.Object("unsigned int", offset=vector_swi_addr, vm=self.addr_space) if (opcode & 0xFFFFFF00) == 0xE28F8000: sc_opcode = opcode break vector_swi_addr += 4 max_opcodes_to_check -= 1 if sc_opcode: yield ( "vector_swi code modification", "PASS", "{0:X}".format(sc_opcode), ) else: yield ( "vector_swi code modification", "FAIL", "Opcode E28F80?? not found", ) return
def calculate(self): linux_common.set_plugin_members(self) tasks = linux_pslist.linux_pslist(self._config).calculate() # Are we dealing with 32 or 64-bit pointers if self.addr_space.profile.metadata.get('memory_model', '32bit') == '32bit': pack_format = "<I" addr_sz = 4 else: pack_format = "<Q" addr_sz = 8 for task in tasks: proc_as = task.get_process_address_space() # In cases when mm is an invalid pointer if not proc_as: continue procvars = [] for vma in task.get_proc_maps(): if not (vma.vm_file and str(vma.vm_flags) == "rw-" and linux_common.get_path( task, vma.vm_file).find("bash") != -1): continue env_start = 0 for off in range(vma.vm_start, vma.vm_end): # check the first index addrstr = proc_as.read(off, addr_sz) if not addrstr or len(addrstr) != addr_sz: continue addr = struct.unpack(pack_format, addrstr)[0] # check first idx... if addr: firstaddrstr = proc_as.read(addr, addr_sz) if not firstaddrstr or len(firstaddrstr) != addr_sz: continue firstaddr = struct.unpack(pack_format, firstaddrstr)[0] buf = proc_as.read(firstaddr, 64) if not buf: continue eqidx = buf.find("=") if eqidx > 0: nullidx = buf.find("\x00") # single char name, = if nullidx >= eqidx: env_start = addr if env_start == 0: continue envars = obj.Object(theType="Array", targetType="Pointer", vm=proc_as, offset=env_start, count=256) for var in envars: if var: varstr = proc_as.read(var, 1600) eqidx = varstr.find("=") idx = varstr.find("\x00") if idx == -1 or eqidx == -1 or idx < eqidx: break varstr = varstr[:idx] procvars.append(varstr) yield task, " ".join(procvars) break
def calculate(self): """ This works by walking the IDT table for the entries that Linux uses and verifies that each is a symbol in the kernel """ linux_common.set_plugin_members(self) if self.profile.metadata['arch'] not in ["x64", "x86"]: debug.error( "This plugin is only supported on Intel-based memory captures") tblsz = 256 sym_addrs = self.profile.get_all_addresses() # hw handlers + system call check_idxs = list(range(0, 20)) + [128] if self.profile.metadata.get('memory_model', '32bit') == "32bit": idt_type = "desc_struct" else: if self.profile.has_type("gate_struct64"): idt_type = "gate_struct64" else: idt_type = "idt_desc" # this is written as a list b/c there are supposdly kernels with per-CPU IDTs # but I haven't found one yet... addrs = [self.addr_space.profile.get_symbol("idt_table")] for tableaddr in addrs: table = obj.Object(theType='Array', offset=tableaddr, vm=self.addr_space, targetType=idt_type, count=tblsz) for i in check_idxs: ent = table[i] if not ent: continue if hasattr(ent, "Address"): idt_addr = ent.Address else: low = ent.offset_low middle = ent.offset_middle high = ent.offset_high idt_addr = (high << 32) | (middle << 16) | low if idt_addr != 0: if not idt_addr in sym_addrs: hooked = 1 sym_name = "HOOKED" else: hooked = 0 sym_name = self.profile.get_symbol_by_address( "kernel", idt_addr) yield (i, ent, idt_addr, sym_name, hooked)
def calculate(self): def read_rel(offset, len_): return obj.Object( "list_head", offset=device_private__device__ptr__addr - 152 + offset, vm=self.addr_space).obj_vm.read( addr=device_private__device__ptr__addr - 152 + offset, length=len_) def read_abs(offset, len_): return obj.Object("list_head", offset=offset, vm=self.addr_space).obj_vm.read(addr=offset, length=len_) linux_common.set_plugin_members(self) io_ptr = self.addr_space.profile.get_symbol("iomem_resource") io_res = obj.Object("resource", offset=io_ptr, vm=self.addr_space) io_pci_ptr = self.addr_space.profile.get_symbol("pci_bus_type") io_pci_res = obj.Object("bus_type", offset=io_pci_ptr, vm=self.addr_space) import struct # readin klist_devices->k_list->next pointer from SUBSYS_PRIVATE struct #---------------------------------------|------------------------------------------------------------------|----------------------------------------------|------|---| subsys__klist_devices__k_list__next__addr = struct.unpack( 'L', obj.Object("bus_type", offset=io_pci_res.p + 168 + 8, vm=self.addr_space).obj_vm.read(addr=io_pci_res.p + 168 + 8, length=8))[0] print "subsys__klist_devices__k_list__next__addr relies at--\t" + str( subsys__klist_devices__k_list__next__addr) + ";\thex =\t" + str( hex(subsys__klist_devices__k_list__next__addr)) # so now we can get list_head *next pointer # get pointers device_private__n_node__next__addr = struct.unpack( 'L', obj.Object("list_head", offset=subsys__klist_devices__k_list__next__addr, vm=self.addr_space).obj_vm.read( addr=subsys__klist_devices__k_list__next__addr, length=8))[0] print "device_private__n_node__next__addr list_head next relies at--\t" + str( device_private__n_node__next__addr) + ";\thex =\t" + str( hex(device_private__n_node__next__addr)) device_private__n_node__prev__addr = struct.unpack( 'L', obj.Object("list_head", offset=subsys__klist_devices__k_list__next__addr + 8, vm=self.addr_space).obj_vm.read( addr=subsys__klist_devices__k_list__next__addr + 8, length=8))[0] print "device_private__n_node__prev__addr prev relies at--\t" + str( device_private__n_node__prev__addr) + ";\thex =\t" + str( hex(device_private__n_node__prev__addr)) start = device_private__n_node__next__addr resource_data = [] #========== start cycle of lookin at the devices classes ====look through the list== for i in range(150): print "\nRound {0}".format(i) # so now we can get list_head *next pointer # so now we can get struct device *device of a device_private structure device_private__device__ptr__addr = struct.unpack( 'L', obj.Object("list_head", offset=device_private__n_node__next__addr + 40, vm=self.addr_space).obj_vm.read( addr=device_private__n_node__next__addr + 40, length=8))[0] print "device_private__device__ptr__addr relies at--\t" + str( device_private__device__ptr__addr) + ";\thex =\t" + str( hex(device_private__device__ptr__addr)) #so now we can extract class pci_dev__class__ptr = struct.unpack( 'I', obj.Object( "list_head", offset=device_private__device__ptr__addr - 152 + 68, vm=self.addr_space).obj_vm.read( addr=device_private__device__ptr__addr - 152 + 68, length=4))[0] print "class device\t" + str( hex(pci_dev__class__ptr) ) #struct.unpack('I', obj.Object("list_head", offset = device_private__device__ptr__addr - 152 + 68, vm = self.addr_space).obj_vm.read(addr = device_private__device__ptr__addr - 152 + 68, length = 4)[0]; for i in range(17): resource_data.append((struct.unpack('L', read_rel(888 + i*56, 8))[0],\ struct.unpack('L', read_rel(888 + i*56 + 8, 8))[0],\ struct.unpack('L', read_rel(888 + i*56 + 16, 8))[0] )) print resource_data[i] print "pointer to name " + str( struct.unpack('L', read_rel(152 + 80, 8))[0]) # print obj.Object("list_head", offset = struct.unpack('L', read_rel(152+80, 8))[0], vm = self.addr_space).obj_vm.read(addr = struct.unpack('L', read_rel(152+80, 8))[0], length = 8) # resource_data.append(obj.Object("list_head", offset = device_private__n_node__next__addr - 152, vm = self.addr_space).obj_vm.read(addr = device_private__n_node__next__addr - 152, length = 2272)) # print "device resources --\t" + str(resource_data) + ";\thex =\t" + str(hex(resource_data)) + "\n\n" # io_res device_private__n_node__next__addr = struct.unpack( 'L', obj.Object("list_head", offset=device_private__n_node__next__addr, vm=self.addr_space).obj_vm.read( addr=device_private__n_node__next__addr, length=8))[0] print "next device_private struct pointer --\t" + str( device_private__n_node__next__addr) + ";\thex =\t" + str( hex(device_private__n_node__next__addr)) + "\n\n" if start == device_private__n_node__next__addr: break # import ipdb # ipdb.set_trace() # io_pci_dat # print "pci_bus_type output" # # print len(s) # dir(io_res) # dir(io_res.child) for r in self.yield_resource(io_res.child): yield r
def calculate(self): # load the address space linux_common.set_plugin_members(self) # update the vtypes self._add_vtyptes() name_buckets = self.addr_space.profile.get_symbol("_name_buckets") for i in range(64): # compute the location in the array of our list_head nb_offset = i * self.addr_space.profile.get_obj_size( "list_head") + name_buckets # instantiate the list_head listHead = obj.Object("list_head", offset=nb_offset, vm=self.addr_space) cur = obj.Object("list_head", offset=listHead.next, vm=self.addr_space) # if list_head.next == list_head, the list is empty while cur != listHead: # get the hash_cell struct that this list_head is embedded in hc = obj.Object("hash_cell", offset=cur.obj_offset, vm=self.addr_space) hc_name = str( obj.Object("String", offset=hc.name.v(), vm=self.addr_space, length=32)) type_name = str( obj.Object("String", offset=hc.md.map.targets.type.name.v(), vm=self.addr_space, length=16)) # we use these fields in every case to get the start and size # of the mapping start = hc.md.map.targets.begin ssize = hc.md.map.targets.len if type_name == "crypt": c = obj.Object("crypt_config", offset=hc.md.map.targets.private.v(), vm=self.addr_space) device = self._dev_name(c.dev) # turn the key into ASCII hex key = binascii.hexlify( bytearray( self.addr_space.read(addr=c.key.obj_offset, length=c.key_size))) cipher_string = str( obj.Object("String", offset=c.cipher_string.v(), vm=self.addr_space, length=16)) # format is: # start size target cipher_string key iv_offset device offset # start is always 0, offset = c.start yield "{}: {} {} crypt {} {} {} {} {}".format( hc_name, start, ssize, cipher_string, key, c.iv_offset, device, c.start, ) elif type_name == "linear": l = obj.Object("linear_c", offset=hc.md.map.targets.private.v(), vm=self.addr_space) device = self._dev_name(l.dev) # format is: # start size target device offset yield "{}: {} {} {} {} {}".format(hc_name, start, ssize, type_name, device, l.start) # generic type else: yield "{}: {} {} {}".format(hc_name, start, ssize, type_name) cur = obj.Object("list_head", offset=cur.next, vm=self.addr_space)
def calculate(self): linux_common.set_plugin_members(self) elfs = dict() ignore = frozenset(self._config.IGNORE) for task, elf, elf_start, elf_end, soname, needed in linux_elfs.linux_elfs.calculate( self): elfs[(task, soname)] = (elf, elf_start, elf_end, needed) for k, v in elfs.iteritems(): task, soname = k elf, elf_start, elf_end, needed = v if elf._get_typename("hdr") == "elf32_hdr": elf_arch = 32 else: elf_arch = 64 needed_expanded = set([soname]) if (task, None) in elfs: needed_expanded.add(None) # jmp slot can point to ELF itself if the fn hasn't been called yet (RTLD_LAZY) # can point to main binary (None above) if this is a plugin-style symbol while len(needed) > 0: dep = needed.pop(0) needed_expanded.add(dep) try: needed += set(elfs[(task, dep)][3]) - needed_expanded except KeyError: needed_expanded.remove(dep) for reloc in elf.relocations(): rsym = elf.relocation_symbol(reloc) if rsym == None: continue symbol_name = elf.symbol_name(rsym) if symbol_name == None: symbol_name = "<N/A>" offset = reloc.r_offset if offset < elf_start: offset = elf_start + offset if elf_arch == 32: addr = obj.Object("unsigned int", offset=offset, vm=elf.obj_vm) else: addr = obj.Object("unsigned long long", offset=offset, vm=elf.obj_vm) match = False for dep in needed_expanded: _, dep_start, dep_end, _ = elfs[(task, dep)] if addr >= dep_start and addr < dep_end: match = dep hookdesc = '' vma = None for i in task.get_proc_maps(): if addr >= i.vm_start and addr < i.vm_end: vma = i break if vma: if vma.vm_file: hookdesc = linux_common.get_path(task, vma.vm_file) if hookdesc in ignore: continue else: hookdesc = '[{0:x}:{1:x},{2}]'.format( vma.vm_start, vma.vm_end, vma.vm_flags) if hookdesc == "": hookdesc = 'invalid memory' if match != False: if self._config.ALL and match == soname: hookdesc = '[RTLD_LAZY]' hooked = False else: hooked = True yield task, soname, elf, elf_start, elf_end, addr, symbol_name, hookdesc, hooked