def create_name_filter( cls, name_list: List[str] = None, exclude: bool = False ) -> Callable[[interfaces.objects.ObjectInterface], bool]: """A factory for producing filter functions that filter based on a list of process names. Args: name_list: A list of process names that are acceptable, all other processes will be filtered out exclude: Accept only tasks that are not in name_list Returns: Filter function for passing to the `list_processes` method """ filter_func = lambda _: False # FIXME: mypy #4973 or #2608 name_list = name_list or [] filter_list = [x for x in name_list if x is not None] if filter_list: if exclude: filter_func = lambda x: utility.array_to_string( x.ImageFileName) in filter_list else: filter_func = lambda x: utility.array_to_string( x.ImageFileName) not in filter_list return filter_func
def _generator(self, procs): type_map = self.get_type_map(context=self.context, layer_name=self.config["primary"], symbol_table=self.config["nt_symbols"]) cookie = self.find_cookie(context=self.context, layer_name=self.config["primary"], symbol_table=self.config["nt_symbols"]) for proc in procs: try: object_table = proc.ObjectTable except exceptions.InvalidAddressException: vollog.log( constants.LOGLEVEL_VVV, "Cannot access _EPROCESS.ObjectType at {0:#x}".format( proc.vol.offset)) continue process_name = utility.array_to_string(proc.ImageFileName) for entry in self.handles(object_table): try: obj_type = entry.get_object_type(type_map, cookie) if obj_type is None: continue if obj_type == "File": item = entry.Body.cast("_FILE_OBJECT") obj_name = item.file_name_with_device() elif obj_type == "Process": item = entry.Body.cast("_EPROCESS") obj_name = "{} Pid {}".format( utility.array_to_string(proc.ImageFileName), item.UniqueProcessId) elif obj_type == "Thread": item = entry.Body.cast("_ETHREAD") obj_name = "Tid {} Pid {}".format( item.Cid.UniqueThread, item.Cid.UniqueProcess) elif obj_type == "Key": item = entry.Body.cast("_CM_KEY_BODY") obj_name = item.get_full_key_name() else: try: obj_name = entry.NameInfo.Name.String except (ValueError, exceptions.InvalidAddressException): obj_name = "" except (exceptions.InvalidAddressException): vollog.log( constants.LOGLEVEL_VVV, "Cannot access _OBJECT_HEADER at {0:#x}".format( entry.vol.offset)) continue yield (0, (proc.UniqueProcessId, process_name, format_hints.Hex(entry.Body.vol.offset), format_hints.Hex(entry.HandleValue), obj_type, format_hints.Hex(entry.GrantedAccess), obj_name))
def get_dict_lines(self, info) -> Generator[str, None, None]: dict_text = utility.array_to_string(info.dev_info.subsystem) if dict_text: yield f" SUBSYSTEM={dict_text}" dict_text = utility.array_to_string(info.dev_info.device) if dict_text: yield f" DEVICE={dict_text}"
def _generator(self): for mount in self.list_mounts(self.context, self.config['kernel']): vfs = mount.mnt_vfsstat device_name = utility.array_to_string(vfs.f_mntonname) mount_point = utility.array_to_string(vfs.f_mntfromname) mount_type = utility.array_to_string(vfs.f_fstypename) yield 0, (device_name, mount_point, mount_type)
def _get_task_fields( self, task: interfaces.objects.ObjectInterface, decorate_comm: bool = False) -> Tuple[int, int, int, str]: """Extract the fields needed for the final output Args: task: A task object from where to get the fields. decorate_comm: If True, it decorates the comm string of - User threads: in curly brackets, - Kernel threads: in square brackets Defaults to False. Returns: A tuple with the fields to show in the plugin output. """ pid = task.tgid tid = task.pid ppid = task.parent.tgid if task.parent else 0 name = utility.array_to_string(task.comm) if decorate_comm: if task.is_kernel_thread: name = f"[{name}]" elif task.is_user_thread: name = f"{{{name}}}" task_fields = (format_hints.Hex(task.vol.offset), pid, tid, ppid, name) return task_fields
def is_valid(self): try: cmd = self.get_command() ts = utility.array_to_string(self.timestamp.dereference()) except exceptions.InvalidAddressException: return False if not cmd or len(cmd) == 0: return False if not ts or len(ts) == 0: return False # At this point in time, the epoc integer size will # never be less than 10 characters, and the stamp is # always preceded by a pound/hash character. if len(ts) < 10 or str(ts)[0] != "#": return False # The final check is to make sure the entire string # is composed of numbers. Try to convert to an int. try: int(str(ts)[1:]) except ValueError: return False return True
def yield_processes(pid): proc = self._processes[pid] row = (proc.pid, proc.parent.pid, utility.array_to_string(proc.comm)) yield (self._levels[pid] - 1, row) for child_pid in self._children.get(pid, []): yield from yield_processes(child_pid)
def _generator(self, procs): for proc in procs: try: process_name = utility.array_to_string(proc.ImageFileName) except: continue proc_id = "Unknown" try: proc_id = proc.UniqueProcessId result_text = self.get_cmdline(self.context, self.config["nt_symbols"], proc) except exceptions.SwappedInvalidAddressException as exp: result_text = "Required memory at {0:#x} is inaccessible (swapped)".format( exp.invalid_address) except exceptions.PagedInvalidAddressException as exp: result_text = "Required memory at {0:#x} is not valid (process exited?)".format( exp.invalid_address) except exceptions.InvalidAddressException as exp: result_text = "Process {}: Required memory at {:#x} is not valid (incomplete layer {}?)".format( proc_id, exp.invalid_address, exp.layer_name) yield (0, (proc.UniqueProcessId, process_name, result_text))
def list_kernel_events(cls, context: interfaces.context.ContextInterface, kernel_module_name: str, filter_func: Callable[[int], bool] = lambda _: False) -> \ Iterable[Tuple[interfaces.objects.ObjectInterface, interfaces.objects.ObjectInterface, interfaces.objects.ObjectInterface]]: """ Returns the kernel event filters registered Return values: A tuple of 3 elements: 1) The name of the process that registered the filter 2) The process ID of the process that registered the filter 3) The object of the associated kernel event filter """ kernel = context.modules[kernel_module_name] list_tasks = pslist.PsList.get_list_tasks( pslist.PsList.pslist_methods[0]) for task in list_tasks(context, kernel_module_name, filter_func): task_name = utility.array_to_string(task.p_comm) pid = task.p_pid for kn in cls._get_task_kevents(kernel, task): yield task_name, pid, kn
def _generator(self, tasks): # determine if we're on a 32 or 64 bit kernel if self.context.symbol_space.get_type(self.config["vmlinux"] + constants.BANG + "pointer").size == 4: is_32bit_arch = True else: is_32bit_arch = False for task in tasks: process_name = utility.array_to_string(task.comm) for vma, data in self._list_injections(task): if is_32bit_arch: architecture = "intel" else: architecture = "intel64" disasm = interfaces.renderers.Disassembly( data, vma.vm_start, architecture) yield (0, (task.pid, process_name, format_hints.Hex(vma.vm_start), format_hints.Hex(vma.vm_end), vma.get_protection(), format_hints.HexBytes(data), disasm))
def _generator(self): filter_func = pslist.PsList.create_pid_filter( self.config.get('pid', None)) for task_name, pid, socket in self.list_sockets( self.context, self.config['primary'], self.config['darwin'], filter_func=filter_func): family = socket.get_family() if family == 1: try: upcb = socket.so_pcb.dereference().cast("unpcb") path = utility.array_to_string(upcb.unp_addr.sun_path) except exceptions.InvalidAddressException: continue yield (0, (format_hints.Hex(socket.vol.offset), "UNIX", path, 0, "", 0, "", "{}/{:d}".format(task_name, pid))) elif family in [2, 30]: state = socket.get_state() proto = socket.get_protocol_as_string() vals = socket.get_converted_connection_info() if vals: (lip, lport, rip, rport) = vals yield (0, (format_hints.Hex(socket.vol.offset), proto, lip, lport, rip, rport, state, "{}/{:d}".format(task_name, pid)))
def _generator(self): for module in self.list_modules(self.context, self.config['kernel']): mod_name = utility.array_to_string(module.name) mod_size = module.size yield 0, (format_hints.Hex(module.vol.offset), mod_name, mod_size)
def _generator(self, tasks: Iterator[Any]) -> Generator[Tuple[int, Tuple[int, str, int, str]], None, None]: for task in tasks: proc_layer_name = task.add_process_layer() if proc_layer_name is None: continue proc_layer = self.context.layers[proc_layer_name] argsstart = task.user_stack - task.p_argslen if not proc_layer.is_valid(argsstart) or task.p_argslen == 0 or task.p_argc == 0: continue # Add one because the first two are usually duplicates argc = task.p_argc + 1 # smear protection if argc > 1024: continue task_name = utility.array_to_string(task.p_comm) args: List[bytes] = [] while argc > 0: try: arg = proc_layer.read(argsstart, 256) except exceptions.InvalidAddressException: break idx = arg.find(b'\x00') if idx != -1: arg = arg[:idx] argsstart += len(str(arg)) + 1 # deal with the stupid alignment (leading nulls) and arg duplication if len(args) == 0: while argsstart < task.user_stack: try: check = proc_layer.read(argsstart, 1) except exceptions.InvalidAddressException: break if check != b"\x00": break argsstart = argsstart + 1 args.append(arg) # also check for initial duplicates since OS X is painful elif arg != args[0]: args.append(arg) argc = argc - 1 args_str = " ".join([s.decode("utf-8", errors = 'replace') for s in args]) yield (0, (task.p_pid, task_name, task.p_argc, args_str))
def _generator(self, tasks): for task in tasks: if not task.mm: continue name = utility.array_to_string(task.comm) for vma in task.mm.get_mmap_iter(): flags = vma.get_protection() page_offset = vma.get_page_offset() major = 0 minor = 0 inode = 0 if vma.vm_file != 0: dentry = vma.vm_file.get_dentry() if dentry != 0: inode_object = dentry.d_inode major = inode_object.i_sb.major minor = inode_object.i_sb.minor inode = inode_object.i_ino path = vma.get_name(self.context, task) yield (0, (task.pid, name, format_hints.Hex(vma.vm_start), format_hints.Hex(vma.vm_end), flags, format_hints.Hex(page_offset), major, minor, inode, path))
def _generator(self): """ Lists the registered VFS event watching processes Also lists which event(s) a process is registered for """ kernel = contexts.Module(self.context, self.config['darwin'], self.config['primary'], 0) watcher_table = kernel.object_from_symbol("watcher_table") for watcher in watcher_table: if watcher == 0: continue task_name = utility.array_to_string(watcher.proc_name) task_pid = watcher.pid events = [] try: event_array = kernel.object(object_type = "array", offset = watcher.event_list, count = 13, subtype = kernel.get_type("unsigned char")) except exceptions.InvalidAddressException: continue for i, event in enumerate(event_array): if event == 1: events.append(self.event_types[i]) if events != []: yield (0, (task_name, task_pid, ",".join(events)))
def _generator(self, tasks): vmlinux = self.context.modules[self.config["kernel"]] is_32bit = not symbols.symbol_table_is_64bit(self.context, vmlinux.symbol_table_name) if is_32bit: pack_format = "I" bash_json_file = "bash32" else: pack_format = "Q" bash_json_file = "bash64" bash_table_name = BashIntermedSymbols.create(self.context, self.config_path, "linux", bash_json_file) ts_offset = self.context.symbol_space.get_type( bash_table_name + constants.BANG + "hist_entry").relative_child_offset("timestamp") for task in tasks: task_name = utility.array_to_string(task.comm) if task_name not in ["bash", "sh", "dash"]: continue proc_layer_name = task.add_process_layer() if not proc_layer_name: continue proc_layer = self.context.layers[proc_layer_name] bang_addrs = [] # find '#' values on the heap for address in proc_layer.scan( self.context, scanners.BytesScanner(b"#"), sections=task.get_process_memory_sections(heap_only=True)): bang_addrs.append(struct.pack(pack_format, address)) history_entries = [] if bang_addrs: for address, _ in proc_layer.scan( self.context, scanners.MultiStringScanner(bang_addrs), sections=task.get_process_memory_sections( heap_only=True)): hist = self.context.object(bash_table_name + constants.BANG + "hist_entry", offset=address - ts_offset, layer_name=proc_layer_name) if hist.is_valid(): history_entries.append(hist) for hist in sorted(history_entries, key=lambda x: x.get_time_as_integer()): yield (0, (task.pid, task_name, hist.get_time_object(), hist.get_command()))
def _generator(self): kset_modules = self.get_kset_modules(self.context, self.config['kernel']) lsmod_modules = set( str(utility.array_to_string(modules.name)) for modules in lsmod.Lsmod.list_modules(self.context, self.config['kernel'])) for mod_name in set(kset_modules.keys()).difference(lsmod_modules): yield (0, (format_hints.Hex(kset_modules[mod_name]), str(mod_name)))
def _generator(self): kernel = self.context.modules[self.config['kernel']] filter_func = pslist.PsList.create_pid_filter( self.config.get('pid', None)) # Collect all the values as we will want to group them later sessions = {} for proc in pslist.PsList.list_processes(self.context, kernel.layer_name, kernel.symbol_table_name, filter_func=filter_func): session_id = proc.get_session_id() # Detect RDP, Console or set default value session_type = renderers.NotAvailableValue() # Construct Username from Process Env user_domain = '' user_name = '' for var, val in proc.environment_variables(): if var.lower() == 'username': user_name = val elif var.lower() == 'userdomain': user_domain = val if var.lower() == 'sessionname': session_type = val # Concat Domain and User full_user = f'{user_domain}/{user_name}' if full_user == '/': full_user = renderers.NotAvailableValue() # Collect all the values in to a row we can yield after sorting. row = { "session_id": session_id, "process_id": proc.UniqueProcessId, "process_name": utility.array_to_string(proc.ImageFileName), "user_name": full_user, "process_start": proc.get_create_time(), "session_type": session_type } # Add row to correct session so we can sort it later if session_id in sessions: sessions[session_id].append(row) else: sessions[session_id] = [row] # Group and yield each row for rows in sessions.values(): for row in rows: yield 0, (row.get('session_id'), row.get('session_type'), row.get('process_id'), row.get('process_name'), row.get('user_name'), row.get('process_start'))
def mask_mods_list(cls, context: interfaces.context.ContextInterface, layer_name: str, mods: Iterator[interfaces.objects.ObjectInterface]) -> List[Tuple[str, int, int]]: """ A helper function to mask the starting and end address of kernel modules """ mask = context.layers[layer_name].address_mask return [(utility.array_to_string(mod.name), mod.get_module_base() & mask, (mod.get_module_base() & mask) + mod.get_core_size()) for mod in mods]
def _lsass_proc_filter(self, proc): """ Used to filter to only lsass.exe processes There should only be one of these, but malware can/does make lsass.exe named processes to blend in or uses lsass.exe as a process hollowing target """ process_name = utility.array_to_string(proc.ImageFileName) return process_name != "lsass.exe"
def _check_header(self) -> Optional[Tuple[str, interfaces.objects.ObjectInterface]]: """Verifies the header of the PDB file and returns the version of the file.""" for header in self._headers: header_type = self.pdb_symbol_table + constants.BANG + header current_header = self.context.object(header_type, self._base_layer, 0) if utility.array_to_string(current_header.Magic) == self._headers[header]: if not (current_header.PageSize < 0x100 or current_header.PageSize > (128 * 0x10000)): return header, current_header return None
def _generator(self): for task in self.list_tasks(self.context, self.config['kernel'], filter_func = self.create_pid_filter(self.config.get('pid', None))): pid = task.pid ppid = 0 if task.parent: ppid = task.parent.pid name = utility.array_to_string(task.comm) yield (0, (pid, ppid, name))
def _generator(self): list_tasks = self.get_list_tasks( self.config.get('pslist_method', self.pslist_methods[0])) for task in list_tasks(self.context, self.config['kernel'], filter_func=self.create_pid_filter( self.config.get('pid', None))): pid = task.p_pid ppid = task.p_ppid name = utility.array_to_string(task.p_comm) yield (0, (pid, ppid, name))
def _generator(self, layer: crash.WindowsCrashDump32Layer): header = layer.get_header() uptime = datetime.timedelta(microseconds=int(header.SystemUpTime) / 10) if header.DumpType == 0x1: dump_type = "Full Dump (0x1)" elif header.DumpType == 0x5: dump_type = "Bitmap Dump (0x5)" else: # this should never happen since the crash layer only accepts 0x1 and 0x5 dump_type = "Unknown/Unsupported ({:#x})".format(header.DumpType) if header.DumpType == 0x5: summary_header = layer.get_summary_header() bitmap_header_size = format_hints.Hex(summary_header.HeaderSize) bitmap_size = format_hints.Hex(summary_header.BitmapSize) bitmap_pages = format_hints.Hex(summary_header.Pages) else: bitmap_header_size = bitmap_size = bitmap_pages = renderers.NotApplicableValue( ) yield (0, ( utility.array_to_string(header.Signature), header.MajorVersion, header.MinorVersion, format_hints.Hex(header.DirectoryTableBase), format_hints.Hex(header.PfnDataBase), format_hints.Hex(header.PsLoadedModuleList), format_hints.Hex(header.PsActiveProcessHead), header.MachineImageType, header.NumberProcessors, format_hints.Hex(header.KdDebuggerDataBlock), dump_type, str(uptime), utility.array_to_string(header.Comment), conversion.wintime_to_datetime(header.SystemTime), bitmap_header_size, bitmap_size, bitmap_pages, ))
def _generator(self): vmlinux = contexts.Module(self.context, self.config['vmlinux'], self.config['primary'], 0) modules = lsmod.Lsmod.list_modules(self.context, self.config['primary'], self.config['vmlinux']) handlers = linux.LinuxUtilities.generate_kernel_handler_info( self.context, self.config['primary'], self.config['vmlinux'], modules) try: tty_drivers = vmlinux.object_from_symbol("tty_drivers").cast( "list_head") except exceptions.SymbolError: tty_drivers = None if not tty_drivers: raise TypeError( "This plugin requires the tty_drivers structure." "This structure is not present in the supplied symbol table." "This means you are either analyzing an unsupported kernel version or that your symbol table is corrupt." ) for tty in tty_drivers.to_list( vmlinux.name + constants.BANG + "tty_driver", "tty_drivers"): try: ttys = utility.array_of_pointers(tty.ttys.dereference(), count=tty.num, subtype=vmlinux.name + constants.BANG + "tty_struct", context=self.context) except exceptions.PagedInvalidAddressException: continue for tty_dev in ttys: if tty_dev == 0: continue name = utility.array_to_string(tty_dev.name) recv_buf = tty_dev.ldisc.ops.receive_buf module_name, symbol_name = linux.LinuxUtilities.lookup_module_address( self.context, handlers, recv_buf) yield (0, (name, format_hints.Hex(recv_buf), module_name, symbol_name))
def _generator(self): vmlinux = contexts.Module(self.context, self.config['vmlinux'], self.config['primary'], 0) kset_modules = self.get_kset_modules(vmlinux) lsmod_modules = set( str(utility.array_to_string(modules.name)) for modules in lsmod.Lsmod.list_modules( self.context, self.config['primary'], self.config['vmlinux'])) for mod_name in set(kset_modules.keys()).difference(lsmod_modules): yield (0, (format_hints.Hex(kset_modules[mod_name]), str(mod_name)))
def _generator(self, tasks): for task in tasks: process_name = utility.array_to_string(task.p_comm) process_pid = task.p_pid for vma in task.get_map_iter(): path = vma.get_path(self.context, self.config['darwin']) if path == "": path = vma.get_special_path() yield (0, (process_pid, process_name, format_hints.Hex(vma.links.start), format_hints.Hex(vma.links.end), vma.get_perms(), path))
def _generator(self, tasks): symbol_table = None for task in tasks: if symbol_table is None: if constants.BANG not in task.vol.type_name: raise ValueError("Task is not part of a symbol table") symbol_table = task.vol.type_name.split(constants.BANG)[0] name = utility.array_to_string(task.comm) pid = int(task.pid) for fd_num, _, full_path in linux.LinuxUtilities.files_descriptors_for_process( self.context, symbol_table, task): yield (0, (pid, name, fd_num, full_path))
def _generator(self, procs): # determine if we're on a 32 or 64 bit kernel kernel = self.context.modules[self.config['kernel']] is_32bit_arch = not symbols.symbol_table_is_64bit( self.context, kernel.symbol_table_name) for proc in procs: process_name = utility.array_to_string(proc.ImageFileName) for vad, data in self.list_injections(self.context, kernel.layer_name, kernel.symbol_table_name, proc): # if we're on a 64 bit kernel, we may still need 32 bit disasm due to wow64 if is_32bit_arch or proc.get_is_wow64(): architecture = "intel" else: architecture = "intel64" disasm = interfaces.renderers.Disassembly( data, vad.get_start(), architecture) file_output = "Disabled" if self.config['dump']: file_output = "Error outputting to file" try: file_handle = vadinfo.VadInfo.vad_dump( self.context, proc, vad, self.open) file_handle.close() file_output = file_handle.preferred_filename except (exceptions.InvalidAddressException, OverflowError) as excp: vollog.debug( "Unable to dump PE with pid {0}.{1:#x}: {2}". format(proc.UniqueProcessId, vad.get_start(), excp)) yield (0, (proc.UniqueProcessId, process_name, format_hints.Hex(vad.get_start()), format_hints.Hex(vad.get_end()), vad.get_tag(), vad.get_protection( vadinfo.VadInfo.protect_values( self.context, kernel.layer_name, kernel.symbol_table_name), vadinfo.winnt_protections), vad.get_commit_charge(), vad.get_private_memory(), file_output, format_hints.HexBytes(data), disasm))
def _generator(self): try: for module in self.list_modules(self.context, self.config['kernel']): mod_size = module.get_init_size() + module.get_core_size() mod_name = utility.array_to_string(module.name) yield 0, (format_hints.Hex(module.vol.offset), mod_name, mod_size) except exceptions.SymbolError: vollog.debug( "The required symbol 'module' is not present in symbol table. Please check that kernel modules are enabled for the system under analysis." )