def calculate(self): addr_space = utils.load_as(self._config) # Currently we only support x86. The x64 does still have a IDT # but hooking is prohibited and results in bugcheck. if not self.is_valid_profile(addr_space.profile): debug.error("This command does not support the selected profile.") mods = dict((addr_space.address_mask(mod.DllBase), mod) for mod in modules.lsmod(addr_space)) mod_addrs = sorted(mods.keys()) for kpcr in tasks.get_kdbg(addr_space).kpcrs(): # Get the GDT for access to selector bases gdt = dict((i * 8, sd) for i, sd in kpcr.gdt_entries()) for i, entry in kpcr.idt_entries(): # Where the IDT entry points. addr = entry.Address # Per MITRE, add the GDT selector base if available. # This allows us to detect sneaky attempts to hook IDT # entries by changing the entry's GDT selector. gdt_entry = gdt.get(entry.Selector.v()) if gdt_entry != None and "Code" in gdt_entry.Type: addr += gdt_entry.Base # Lookup the function's owner module = tasks.find_module(mods, mod_addrs, addr_space.address_mask(addr)) yield i, entry, addr, module
def calculate(self): if not has_yara: debug.error( "Please install Yara from https://plusvic.github.io/yara/") addr_space = utils.load_as(self._config) rules = self._compile_rules() if self._config.KERNEL: # Find KDBG so we know where kernel memory begins. Do not assume # the starting range is 0x80000000 because we may be dealing with # an image with the /3GB boot switch. kdbg = tasks.get_kdbg(addr_space) start = kdbg.MmSystemRangeStart.dereference_as("Pointer") # Modules so we can map addresses to owners mods = dict((addr_space.address_mask(mod.DllBase), mod) for mod in modules.lsmod(addr_space)) mod_addrs = sorted(mods.keys()) # There are multiple views (GUI sessions) of kernel memory. # Since we're scanning virtual memory and not physical, # all sessions must be scanned for full coverage. This # really only has a positive effect if the data you're # searching for is in GUI memory. sessions = [] for proc in tasks.pslist(addr_space): sid = proc.SessionId # Skip sessions we've already seen if sid == None or sid in sessions: continue session_space = proc.get_process_address_space() if session_space == None: continue sessions.append(sid) scanner = DiscontigYaraScanner(address_space=session_space, rules=rules) for hit, address in scanner.scan(start_offset=start): module = tasks.find_module( mods, mod_addrs, addr_space.address_mask(address)) yield (module, address, hit, session_space.zread(address - self._config.REVERSE, self._config.SIZE)) else: for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = VadYaraScanner(task=task, rules=rules) for hit, address in scanner.scan(): yield (task, address, hit, scanner.address_space.zread( address - self._config.REVERSE, self._config.SIZE))
def generate_suggestions(self): """The nt!PoolBigPageTable and nt!PoolBigPageTableSize are found relative to nt!PoolTrackTable""" track_table = tasks.get_kdbg(self.obj_vm).PoolTrackTable for pair in self.distance: table_base = obj.Object("address", offset = track_table - pair[0], vm = self.obj_vm) table_size = obj.Object("address", offset = track_table - pair[1], vm = self.obj_vm) if (table_base % 0x1000 == 0 and self.obj_vm.is_valid_address(table_base) and table_size != 0 and table_size % 0x1000 == 0 and table_size < 0x1000000): break debug.debug("Distance Map: {0}".format(repr(self.distance))) debug.debug("PoolTrackTable: {0:#x}".format(track_table)) debug.debug("PoolBigPageTable: {0:#x} => {1:#x}".format(table_base.obj_offset, table_base)) debug.debug("PoolBigPageTableSize: {0:#x} => {1:#x}".format(table_size.obj_offset, table_size)) yield table_base, table_size
def generate_suggestions(self): """The nt!PoolBigPageTable and nt!PoolBigPageTableSize are found relative to nt!PoolTrackTable""" track_table = tasks.get_kdbg(self.obj_vm).PoolTrackTable for pair in self.distance: table_base = obj.Object("address", offset=track_table - pair[0], vm=self.obj_vm) table_size = obj.Object("address", offset=track_table - pair[1], vm=self.obj_vm) if (table_base % 0x1000 == 0 and self.obj_vm.is_valid_address(table_base) and table_size != 0 and table_size % 0x1000 == 0 and table_size < 0x1000000): break debug.debug("Distance Map: {0}".format(repr(self.distance))) debug.debug("PoolTrackTable: {0:#x}".format(track_table)) debug.debug("PoolBigPageTable: {0:#x} => {1:#x}".format( table_base.obj_offset, table_base)) debug.debug("PoolBigPageTableSize: {0:#x} => {1:#x}".format( table_size.obj_offset, table_size)) yield table_base, table_size
def calculate(self): kernel_space = utils.load_as(self._config) if not self.is_valid_profile(kernel_space.profile): debug.error("Windows XP/2003 does not track pool tags") knowntags = {} if self._config.TAGFILE and os.path.isfile(self._config.TAGFILE): taglines = open(self._config.TAGFILE).readlines() for tag in taglines: tag = tag.strip() if tag.startswith("rem") or tag.startswith(" ") or tag == "": continue info = tag.split("-", 2) try: key = info[0].strip() driver = info[1].strip() reason = info[2].strip() except IndexError: continue knowntags[key] = (driver, reason) track_table = tasks.get_kdbg(kernel_space).PoolTrackTable # not really an address, this is just a trick to get # a 32bit number on x86 and 64bit number on x64. the # size is always directly before the pool table. table_size = obj.Object("address", offset=track_table - kernel_space.profile.get_obj_size("address"), vm=kernel_space) track_table = track_table.dereference_as("address") entries = obj.Object("Array", targetType="_POOL_TRACKER_TABLE", offset=track_table, count=table_size, vm=kernel_space) if self._config.TAGS: tags = [tag for tag in self._config.TAGS.split(",")] else: tags = [] for entry in entries: if not self._config.SHOW_FREE: if entry.PagedBytes == 0 and entry.NonPagedBytes == 0: continue if not tags or entry.Key in tags: try: (driver, reason) = knowntags[str(entry.Key).strip()] if self._config.WHITELIST: continue except KeyError: (driver, reason) = ("", "") yield entry, driver, reason
def __init__(self, config, *args, **kwargs): commands.Command.__init__(self, config, *args, **kwargs) config.add_option("BUILD_SYMBOLS", default = False, action = 'store_true', cache_invalidator = False, help = "Build symbol table information") config.add_option("USE_SYMBOLS", default = False, action = 'store_true', cache_invalidator = False, help = "Use symbol servers to resolve process addresses to module names") self.kdbg = tasks.get_kdbg(utils.load_as(config)) self.build_symbols = getattr(config, 'BUILD_SYMBOLS', False) self.use_symbols = getattr(config, 'USE_SYMBOLS', False)
def calculate(self): kernel_space = utils.load_as(self._config) if not self.is_valid_profile(kernel_space.profile): debug.error("Windows XP/2003 does not track pool tags") knowntags = {} if self._config.TAGFILE and os.path.isfile(self._config.TAGFILE): taglines = open(self._config.TAGFILE).readlines() for tag in taglines: tag = tag.strip() if tag.startswith("rem") or tag.startswith(" ") or tag == "": continue info = tag.split("-", 2) try: key = info[0].strip() driver = info[1].strip() reason = info[2].strip() except IndexError: continue knowntags[key] = (driver, reason) track_table = tasks.get_kdbg(kernel_space).PoolTrackTable # not really an address, this is just a trick to get # a 32bit number on x86 and 64bit number on x64. the # size is always directly before the pool table. table_size = obj.Object("address", offset = track_table - kernel_space.profile.get_obj_size("address"), vm = kernel_space ) track_table = track_table.dereference_as("address") entries = obj.Object("Array", targetType = "_POOL_TRACKER_TABLE", offset = track_table, count = table_size, vm = kernel_space ) if self._config.TAGS: tags = [tag for tag in self._config.TAGS.split(",")] else: tags = [] for entry in entries: if not self._config.SHOW_FREE: if entry.PagedBytes == 0 and entry.NonPagedBytes == 0: continue if not tags or entry.Key in tags: try: (driver, reason) = knowntags[str(entry.Key).strip()] if self._config.WHITELIST: continue except KeyError: (driver, reason) = ("", "") yield entry, driver, reason
def calculate(self): if not has_pydeep: debug.error( "Please install ssdeep and pydeep from http://ssdeep.sourceforge.net/ and https://github.com/kbandla/pydeep" ) addr_space = utils.load_as(self._config) self._addr_space = addr_space page_sig = self._pydeep_page() if page_sig == None: debug.error("Pydeep was not able to hash the input") if self._config.KERNEL: # Find KDBG so we know where kernel memory begins. Do not assume # the starting range is 0x80000000 because we may be dealing with # an image with the /3GB boot switch. kdbg = tasks.get_kdbg(addr_space) start = kdbg.MmSystemRangeStart.dereference_as("Pointer") # Modules so we can map addresses to owners mods = dict((addr_space.address_mask(mod.DllBase), mod) for mod in modules.lsmod(addr_space)) mod_addrs = sorted(mods.keys()) # There are multiple views (GUI sessions) of kernel memory. # Since we're scanning virtual memory and not physical, # all sessions must be scanned for full coverage. This # really only has a positive effect if the data you're # searching for is in GUI memory. sessions = [] for proc in tasks.pslist(addr_space): sid = proc.SessionId # Skip sessions we've already seen if sid == None or sid in sessions: continue session_space = proc.get_process_address_space() if session_space == None: continue sessions.append(sid) scanner = DiscontigSSDeepScanner(address_space=session_space, rules=rules) for hit, address in scanner.scan(start_offset=start): module = tasks.find_module(mods, mod_addrs, addr_space.address_mask(address)) yield (module, address, hit, session_space.zread(address - self._config.REVERSE, self._config.SIZE)) else: for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = VadSSDeepScanner(task=task, pydeep_hash=page_sig) for sig, vStart, vLength, offset, alike in scanner.scan(): yield (task, sig, vStart, vLength, offset, alike, scanner.address_space.zread(offset, 0x1000))
def calculate(self): addr_space = utils.load_as(self._config) # Currently we only support x86. The x64 does still have a GDT # but hooking is prohibited and results in bugcheck. if not self.is_valid_profile(addr_space.profile): debug.error("This command does not support the selected profile.") for kpcr in tasks.get_kdbg(addr_space).kpcrs(): for i, entry in kpcr.gdt_entries(): yield i, entry
def calculate(self): if not has_yara: debug.error("Please install Yara from code.google.com/p/yara-project") addr_space = utils.load_as(self._config) rules = self._compile_rules() if self._config.KERNEL: # Find KDBG so we know where kernel memory begins. Do not assume # the starting range is 0x80000000 because we may be dealing with # an image with the /3GB boot switch. kdbg = tasks.get_kdbg(addr_space) start = kdbg.MmSystemRangeStart.dereference_as("Pointer") # Modules so we can map addresses to owners mods = dict((addr_space.address_mask(mod.DllBase), mod) for mod in modules.lsmod(addr_space)) mod_addrs = sorted(mods.keys()) # There are multiple views (GUI sessions) of kernel memory. # Since we're scanning virtual memory and not physical, # all sessions must be scanned for full coverage. This # really only has a positive effect if the data you're # searching for is in GUI memory. sessions = [] for proc in tasks.pslist(addr_space): sid = proc.SessionId # Skip sessions we've already seen if sid == None or sid in sessions: continue session_space = proc.get_process_address_space() if session_space == None: continue sessions.append(sid) scanner = DiscontigYaraScanner(address_space = session_space, rules = rules) for hit, address in scanner.scan(start_offset = start): module = tasks.find_module(mods, mod_addrs, addr_space.address_mask(address)) yield (module, address, hit, session_space.zread(address, 1024)) else: for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = VadYaraScanner(task = task, rules = rules) for hit, address in scanner.scan(): yield (task, address, hit, scanner.address_space.zread(address, 1024))
def get_bugcheck_callbacks(self): """ Enumerate generic Bugcheck callbacks. Note: These structures don't exist in tagged pools, but you can find them via KDDEBUGGER_DATA64 on all versions of Windows. """ kbcclh = tasks.get_kdbg(self.kern_space).KeBugCheckCallbackListHead.dereference_as('_KBUGCHECK_CALLBACK_RECORD') for l in kbcclh.Entry.list_of_type("_KBUGCHECK_CALLBACK_RECORD", "Entry"): yield "KeBugCheckCallbackListHead", l.CallbackRoutine, l.Component.dereference()
def lsmod(addr_space): """ A Generator for modules (uses _KPCR symbols) """ ## Locate the kpcr struct - either hard coded or specified by the command line PsLoadedModuleList = tasks.get_kdbg(addr_space).PsLoadedModuleList if PsLoadedModuleList.is_valid(): ## Try to iterate over the process list in PsActiveProcessHead ## (its really a pointer to a _LIST_ENTRY) for l in PsLoadedModuleList.dereference_as("_LIST_ENTRY").list_of_type( "_LDR_DATA_TABLE_ENTRY", "InLoadOrderLinks"): yield l
def get_thread_modules(thread, config): # get the loaded modules of the process containing the thread # this function also pays respect to already gathered modules # for increased performance global kernel_modules global enumerated_processes thread_modules = [] user_modules = [] addr_space = utils.load_as(config) system_range = tasks.get_kdbg( addr_space).MmSystemRangeStart.dereference_as("Pointer") if len(kernel_modules) == 0: for mod in modules.lsmod(addr_space): if mod: thread_modules.append(ThreadModule(mod)) thread_modules[-1].functions = get_module_exports( thread, thread_modules[-1].ldr_object) thread_modules[-1].functions = sorted( thread_modules[-1].functions, key=lambda item: item.address) thread_modules = sorted(thread_modules, key=lambda item: item.ldr_object.DllBase) kernel_modules = thread_modules else: pass owning_process = thread.owning_process() if not owning_process.is_valid(): owner = None else: try: user_modules = enumerated_processes[owning_process.obj_offset] except KeyError: for mod in owning_process.get_load_modules(): if mod: user_modules.append(ThreadModule(mod)) user_modules[-1].functions = get_module_exports( thread, user_modules[-1].ldr_object) user_modules[-1].functions = sorted( user_modules[-1].functions, key=lambda item: item.address) user_modules = sorted(user_modules, key=lambda item: item.ldr_object.DllBase) enumerated_processes[owning_process.obj_offset] = user_modules thread_modules = user_modules + kernel_modules return thread_modules
def calculate(self): # gets the kernel address space addr_space = utils.load_as(self._config) # updates vtype objects' size self.update_sizes(addr_space) kdbg = tasks.get_kdbg(addr_space) root_dir = self.get_root_directory(kdbg, addr_space) self.parse_directory(root_dir, addr_space, self.root_obj_list) # checks for the SUPPLY_ADDR option if self._config.SUPPLY_ADDR: addrs = self._config.SUPPLY_ADDR.split(",") for addr in addrs: l = [] # validates the address try: addr = eval(addr) # addr is not valid except (SyntaxError, NameError): continue obj_header = Obj.Object("_OBJECT_HEADER", addr - self.OBJECT_HEADER_SIZE, addr_space) name = self.GetName(obj_header, addr_space) # validates the directory if name: self.parse_directory(addr, addr_space, l) self.tables[name] = (addr, l) # checks for the FULL_PATH option elif self._config.FULL_PATH: # gets all dirs dirs = self._config.FULL_PATH.split(",") for path in dirs: self.SaveByPath(path, addr_space) # default option else: self.tables["/"] = (root_dir, self.root_obj_list) # checks for the PARSE_ALL option if self._config.PARSE_ALL: self.get_directory(addr_space)
def _scan_kernel_memory(self, addr_space, rules): # Find KDBG so we know where kernel memory begins. Do not assume # the starting range is 0x80000000 because we may be dealing with # an image with the /3GB boot switch. kdbg = tasks.get_kdbg(addr_space) start = kdbg.MmSystemRangeStart.dereference_as("Pointer") # Modules so we can map addresses to owners mods = dict( (addr_space.address_mask(mod.DllBase), mod) for mod in modules.lsmod(addr_space) ) mod_addrs = sorted(mods.keys()) # There are multiple views (GUI sessions) of kernel memory. # Since we're scanning virtual memory and not physical, # all sessions must be scanned for full coverage. This # really only has a positive effect if the data you're # searching for is in GUI memory. sessions = [] for proc in tasks.pslist(addr_space): sid = proc.SessionId # Skip sessions we've already seen if sid == None or sid in sessions: continue session_space = proc.get_process_address_space() if session_space == None: continue sessions.append(sid) scanner = DiscontigYaraScanner( address_space=session_space, rules=rules ) for hit, address in scanner.scan(start_offset=start): module = tasks.find_module( mods, mod_addrs, addr_space.address_mask(address) ) yield ( module, address - self._config.REVERSE, hit, session_space.zread( address - self._config.REVERSE, self._config.SIZE ), )
def find_smglobals(self, addrspace): """Find and read the nt!SmGlobals value. On success, return True and save the SmGlobals value in self._smglobals. On Failure, return False. This method must be called before performing any tasks that require SmGlobals. Otherwise reading compressed pages is out of the question. """ meta = addrspace.profile.metadata vers = (meta.get("major", 0), meta.get("minor", 0)) # This only applies to Windows 10 or greater if vers < (6, 4): return False # Prevent subsequent attempts from recalculating the existing value if self._smglobals: return True if not has_yara: debug.warning("Yara module is not installed") return False kdbg = tasks.get_kdbg(addrspace) if not kdbg: debug.warning("Cannot find KDBG") return False # First module should be ntoskrnl try: nt_mod = next(kdbg.modules()) except StopIteration: debug.warning("Cannot find ntoskrnl") return False if not nt_mod: debug.warning("Cannot find ntoskrnl") return False if meta.get('memory_model', '32bit') == '32bit': smglobals = self._get_smglobals(addrspace, nt_mod, self._x86_smglobals) else: smglobals = self._get_smglobals(addrspace, nt_mod, self._x64_smglobals) self._smglobals = smglobals return True
def check_pspcid(self, addr_space): """Enumerate processes by walking the PspCidTable""" ret = dict() # Follow the pointers to the table base kdbg = tasks.get_kdbg(addr_space) PspCidTable = kdbg.PspCidTable.dereference().dereference() # Walk the handle table for handle in PspCidTable.handles(): if handle.get_object_type() == "Process": process = handle.dereference_as("_EPROCESS") ret[process.obj_vm.vtop(process.obj_offset)] = process return ret
def __init__(self, addr_space, build_symbols): self.parser = NameParser() # Used to cache symbol/export/module information in an SQLite3 DB # NB: Volatility cache storeage area used, but we manage things manually. cache_sym_db_path = "{0}/symbols".format( addr_space._config.CACHE_DIRECTORY) if not os.path.exists(cache_sym_db_path): os.makedirs(cache_sym_db_path) self._sym_db_conn = sqlite3.connect("{0}/{1}.db".format( cache_sym_db_path, addr_space.profile.__class__.__name__)) db = self.get_cursor() db.execute("attach ':memory:' as volatility") self._sym_db_conn.commit() kdbg = tasks.get_kdbg(addr_space) # Build tables and indexes self.create_tables(db) # Insert kernel space module information debug.info("Building function symbol tables for kernel space...") for mod in kdbg.modules(): if build_symbols: # Module row created by add_exports if it doesn't already exist self.add_exports(db, mod) self.add_debug_symbols(db, kdbg.obj_vm, mod) # Link in system modules to each process for eproc in kdbg.processes(): self.add_module(db, eproc, mod) # Insert (per process) user space module information debug.info( "Building function symbol tables for each processes user space...") for eproc in kdbg.processes(): debug.info("Processing PID {0} ...".format( int(eproc.UniqueProcessId))) for mod in eproc.get_load_modules(): if build_symbols: # Module row created by add_exports if it doesn't already exist self.add_exports(db, mod) self.add_debug_symbols(db, eproc.get_process_address_space(), mod) # Link in user module to current process self.add_module(db, eproc, mod) debug.info("Symbol tables successfully built")
def start(self, args): if not with_volatility: raise RuntimeError("Volatility not found. Please install it") self.memdump = args["file"].value() self._config.updateCtx('location', "file://" + self.memdump.absolute()) self._config.updateCtx('filename', self.memdump.name()) self._config.updateCtx('debug', True) self.__processes = [] self.__dlls = {} self.__step = 1 self.__steps = 6 starttime = time.time() if args.has_key("profile"): self.stateinfo = "Using provided profile: " + args[ 'profile'].toString() self._config.updateCtx('profile', args['profile'].value()) self._aspace = utils.load_as(self._config) self._kdbg = tasks.get_kdbg(self._aspace) self._config.updateCtx('kdbg', self._kdbg.obj_offset) else: try: self.__guessProfile() except: traceback.print_exc() try: self.root = WinRootNode("Windows RAM", self.memdump, self) self.registerTree(self.memdump, self.root) self.__psxview = psxview.PsXview(self._config) self.__findConnections() self.__findProcesses() self.__createProcessTree() self.__createDlls() self.__createModules() self.__createProcessesVadTree() self.stateinfo = "" except: traceback.print_exc() aspace = self._aspace count = 0 if debug: while aspace: count += 1 print 'AS Layer', str( count), aspace.__class__.__name__, "(", aspace.name, ")" aspace = aspace.base print time.time() - starttime
def __init__(self, config, *args, **kwargs): commands.Command.__init__(self, config, *args, **kwargs) config.add_option("BUILD_SYMBOLS", default=False, action='store_true', cache_invalidator=False, help="Build symbol table information") config.add_option( "USE_SYMBOLS", default=False, action='store_true', cache_invalidator=False, help= "Use symbol servers to resolve process addresses to module names") self.kdbg = tasks.get_kdbg(utils.load_as(config)) self.build_symbols = getattr(config, 'BUILD_SYMBOLS', False) self.use_symbols = getattr(config, 'USE_SYMBOLS', False)
def calculate(self): if not has_yara: debug.error( "Please install Yara from code.google.com/p/yara-project") addr_space = utils.load_as(self._config) rules = yara.compile(sources=ghost_sig) decrypted_data = None mal_proc = {} kdbg = tasks.get_kdbg(addr_space) start = kdbg.MmSystemRangeStart.dereference_as("Pointer") mods = dict((addr_space.address_mask(mod.DllBase), mod) for mod in modules.lsmod(addr_space)) mod_addrs = sorted(mods.keys()) sessions = [] for proc in tasks.pslist(addr_space): sid = proc.SessionId if sid == None or sid in sessions: continue session_space = proc.get_process_address_space() if session_space == None: continue sessions.append(sid) scanner = malfind.DiscontigYaraScanner(address_space=session_space, rules=rules) for hit, address in scanner.scan(start_offset=start): module = tasks.find_module(mods, mod_addrs, addr_space.address_mask(address)) content = session_space.zread(address, 1024) header_size = content.find("\x78\x9c") magic_header_size = header_size - 8 magic_keyword = content[:magic_header_size] comp_uncomp_size = content[magic_header_size:header_size] s = struct.Struct("I I") comp_size, uncomp_size = s.unpack(comp_uncomp_size) enc_data = content[0:comp_size] to_decrypt = content[header_size:comp_size] dec_data = self.decrypt_communication(to_decrypt) if not mal_proc: self.get_ghost_process(magic_keyword, mal_proc, addr_space) os_version = self.get_os_version(addr_space) yield (mal_proc, address, magic_keyword, enc_data, dec_data, os_version)
def __init__(self, addr_space, build_symbols): self.parser = NameParser() # Used to cache symbol/export/module information in an SQLite3 DB # NB: Volatility cache storeage area used, but we manage things manually. cache_sym_db_path = "{0}/symbols".format(addr_space._config.CACHE_DIRECTORY) if not os.path.exists(cache_sym_db_path): os.makedirs(cache_sym_db_path) self._sym_db_conn = sqlite3.connect("{0}/{1}.db".format(cache_sym_db_path, addr_space.profile.__class__.__name__)) db = self.get_cursor() db.execute("attach ':memory:' as volatility") self._sym_db_conn.commit() kdbg = tasks.get_kdbg(addr_space) # Build tables and indexes self.create_tables(db) # Insert kernel space module information debug.info("Building function symbol tables for kernel space...") for mod in kdbg.modules(): if build_symbols: # Module row created by add_exports if it doesn't already exist self.add_exports(db, mod) self.add_debug_symbols(db, kdbg.obj_vm, mod) # Link in system modules to each process for eproc in kdbg.processes(): self.add_module(db, eproc, mod) # Insert (per process) user space module information debug.info("Building function symbol tables for each processes user space...") for eproc in kdbg.processes(): debug.info("Processing PID {0} ...".format(int(eproc.UniqueProcessId))) for mod in eproc.get_load_modules(): if build_symbols: # Module row created by add_exports if it doesn't already exist self.add_exports(db, mod) self.add_debug_symbols(db, eproc.get_process_address_space(), mod) # Link in user module to current process self.add_module(db, eproc, mod) debug.info("Symbol tables successfully built")
def calculate(self): if not has_yara: debug.error("Please install Yara from code.google.com/p/yara-project") addr_space = utils.load_as(self._config) rules = yara.compile(sources=ghost_sig) decrypted_data = None mal_proc = {} kdbg = tasks.get_kdbg(addr_space) start = kdbg.MmSystemRangeStart.dereference_as("Pointer") mods = dict((addr_space.address_mask(mod.DllBase), mod) for mod in modules.lsmod(addr_space)) mod_addrs = sorted(mods.keys()) sessions = [] for proc in tasks.pslist(addr_space): sid = proc.SessionId if sid == None or sid in sessions: continue session_space = proc.get_process_address_space() if session_space == None: continue sessions.append(sid) scanner = malfind.DiscontigYaraScanner(address_space = session_space, rules = rules) for hit, address in scanner.scan(start_offset = start): module = tasks.find_module(mods, mod_addrs, addr_space.address_mask(address)) content = session_space.zread(address,1024) header_size = content.find("\x78\x9c") magic_header_size = header_size - 8 magic_keyword = content[:magic_header_size] comp_uncomp_size = content[magic_header_size:header_size] s = struct.Struct("I I") comp_size, uncomp_size = s.unpack(comp_uncomp_size) enc_data = content[0:comp_size] to_decrypt = content[header_size:comp_size] dec_data = self.decrypt_communication(to_decrypt) if not mal_proc: self.get_ghost_process(magic_keyword, mal_proc, addr_space) os_version = self.get_os_version(addr_space) yield (mal_proc, address, magic_keyword, enc_data, dec_data, os_version)
def _scan_kernel_memory(self, addr_space, rules): # Find KDBG so we know where kernel memory begins. Do not assume # the starting range is 0x80000000 because we may be dealing with # an image with the /3GB boot switch. kdbg = tasks.get_kdbg(addr_space) start = kdbg.MmSystemRangeStart.dereference_as("Pointer") # Modules so we can map addresses to owners mods = dict((addr_space.address_mask(mod.DllBase), mod) for mod in modules.lsmod(addr_space)) mod_addrs = sorted(mods.keys()) # There are multiple views (GUI sessions) of kernel memory. # Since we're scanning virtual memory and not physical, # all sessions must be scanned for full coverage. This # really only has a positive effect if the data you're # searching for is in GUI memory. sessions = [] for proc in tasks.pslist(addr_space): sid = proc.SessionId # Skip sessions we've already seen if sid == None or sid in sessions: continue session_space = proc.get_process_address_space() if session_space == None: continue sessions.append(sid) scanner = DiscontigYaraScanner(address_space = session_space, rules = rules) for hit, address in scanner.scan(start_offset = start): module = tasks.find_module(mods, mod_addrs, addr_space.address_mask(address)) yield (module, address - self._config.REVERSE, hit, session_space.zread(address - self._config.REVERSE, self._config.SIZE))
def calculate(self): if not has_distorm3: debug.warning("For best results please install distorm3") # Checks that subclass AbstractThreadCheck checks = registry.get_plugin_classes(AbstractThreadCheck) # If --listtags is chosen, just print the tags and return if self._config.LISTTAGS: for cls_name, cls in checks.items(): sys.stdout.write("{0:<20} {1}\n".format(cls_name, pydoc.getdoc(cls))) return addr_space = utils.load_as(self._config) system_range = tasks.get_kdbg(addr_space).MmSystemRangeStart.dereference_as("Pointer") # Only show threads owned by particular processes pidlist = [] if self._config.PID: pidlist = [int(p) for p in self._config.PID.split(',')] elif self._config.OFFSET: process = self.virtual_process_from_physical_offset(addr_space, self._config.OFFSET) if process: pidlist = [int(process.UniqueProcessId)] # Get sorted list of kernel modules mods = dict((addr_space.address_mask(mod.DllBase), mod) for mod in modules.lsmod(addr_space)) mod_addrs = sorted(mods.keys()) # Are we on x86 or x64. Save this for render_text self.bits32 = addr_space.profile.metadata.\ get("memory_model", "32bit") == "32bit" # Get a list of hooked SSDTs but only on x86 if self.bits32: hooked_tables = self.get_hooked_tables(addr_space) else: hooked_tables = None # Dictionary to store threads. Keys are physical offsets of # ETHREAD objects. Values are tuples, where the first item is # a boolean specifying if the object was found by scanning and # the second item is the actual ETHREAD object. seen_threads = dict() # Gather threads by list traversal of active/linked processes for task in taskmods.DllList(self._config).calculate(): for thread in task.ThreadListHead.\ list_of_type("_ETHREAD", "ThreadListEntry"): seen_threads[thread.obj_vm.vtop(thread.obj_offset)] = (False, thread) # Now scan for threads and save any that haven't been seen for thread in modscan.ThrdScan(self._config).calculate(): if not seen_threads.has_key(thread.obj_offset): seen_threads[thread.obj_offset] = (True, thread) # Keep a record of processes whose DLLs we've already enumerated process_dll_info = {} for _offset, (found_by_scanner, thread) in seen_threads.items(): # Skip processes the user doesn't want to see if ((self._config.PID or self._config.OFFSET) and not pidlist) or (pidlist and thread.Cid.UniqueProcess not in pidlist): continue # Do we need to gather DLLs for module resolution if addr_space.address_compare(thread.StartAddress, system_range) != -1: owner = tasks.find_module(mods, mod_addrs, addr_space.address_mask(thread.StartAddress)) else: owning_process = thread.owning_process() if not owning_process.is_valid(): owner = None else: try: user_mod_addrs, user_mods = process_dll_info[owning_process.obj_offset] except KeyError: user_mods = dict((addr_space.address_mask(mod.DllBase), mod) for mod in owning_process.get_load_modules()) user_mod_addrs = sorted(user_mods.keys()) process_dll_info[owning_process.obj_offset] = (user_mod_addrs, user_mods) owner = tasks.find_module(user_mods, user_mod_addrs, addr_space.address_mask(thread.StartAddress)) if owner: owner_name = str(owner.BaseDllName or '') else: owner_name = "UNKNOWN" # Replace the dummy class with an instance instances = dict( (cls_name, cls(thread, mods, mod_addrs, hooked_tables, found_by_scanner)) for cls_name, cls in checks.items() ) yield thread, addr_space, mods, mod_addrs, \ instances, hooked_tables, system_range, owner_name
def calculate(self): addr_space = utils.load_as(self._config) if not self.is_valid_profile(addr_space.profile): debug.error("This command does not support the selected profile.") # Get the OS version we're analyzing version = (addr_space.profile.metadata.get('major', 0), addr_space.profile.metadata.get('minor', 0)) modlist = list(modules.lsmod(addr_space)) mods = dict((addr_space.address_mask(mod.DllBase), mod) for mod in modlist) mod_addrs = sorted(mods.keys()) # KTIMERs collected timers = [] # Valid KTIMER.Header.Type values TimerNotificationObject = 8 TimerSynchronizationObject = 9 valid_types = (TimerNotificationObject, TimerSynchronizationObject) if version == (5, 1) or (version == (5, 2) and addr_space.profile.metadata.get('build', 0) == 3789): # On XP SP0-SP3 x86 and Windows 2003 SP0, KiTimerTableListHead # is an array of 256 _LIST_ENTRY for _KTIMERs. KiTimerTableListHead = self.find_list_head(modlist[0], "KeUpdateSystemTime", "\x25\xFF\x00\x00\x00\x8D\x0C\xC5") lists = obj.Object("Array", offset = KiTimerTableListHead, vm = addr_space, targetType = '_LIST_ENTRY', count = 256) for l in lists: for t in l.list_of_type("_KTIMER", "TimerListEntry"): timers.append(t) elif version == (5, 2) or version == (6, 0): # On XP x64, Windows 2003 SP1-SP2, and Vista SP0-SP2, KiTimerTableListHead # is an array of 512 _KTIMER_TABLE_ENTRY structs. KiTimerTableListHead = self.find_list_head(modlist[0], "KeCancelTimer", "\xC1\xE7\x04\x81\xC7") lists = obj.Object("Array", offset = KiTimerTableListHead, vm = addr_space, targetType = '_KTIMER_TABLE_ENTRY', count = 512) for l in lists: for t in l.Entry.list_of_type("_KTIMER", "TimerListEntry"): timers.append(t) elif version == (6, 1): # On Windows 7, there is no more KiTimerTableListHead. The list is # at _KPCR.PrcbData.TimerTable.TimerEntries (credits to Matt Suiche # for this one. See http://pastebin.com/FiRsGW3f). for kpcr in tasks.get_kdbg(addr_space).kpcrs(): for table in kpcr.ProcessorBlock.TimerTable.TimerEntries: for t in table.Entry.list_of_type("_KTIMER", "TimerListEntry"): timers.append(t) for timer in timers: # Sanity check on the timer type if timer.Header.Type not in valid_types: continue # Ignore timers without DPCs if not timer.Dpc.is_valid() or not timer.Dpc.DeferredRoutine.is_valid(): continue # Lookup the module containing the DPC module = tasks.find_module(mods, mod_addrs, addr_space.address_mask(timer.Dpc.DeferredRoutine)) yield timer, module
def get_threads(self,process,vthreads,pslist1): result = [] result.append("False") result.append("False") result.append("False") result.append("False") thread1 = {} addr_space = utils.load_as(self._config) system_range = tasks.get_kdbg(addr_space).MmSystemRangeStart.dereference_as("Pointer") mods = dict((addr_space.address_mask(mod.DllBase), mod) for mod in moduless.lsmod(addr_space)) mod_addrs = sorted(mods.keys()) seen_threads = dict() ## Gather threads by list traversal of active/linked processes for thread in process.ThreadListHead.list_of_type("_ETHREAD", "ThreadListEntry"): seen_threads[thread.obj_vm.vtop(thread.obj_offset)] = (False, thread) process_dll_info = {} for _offset, (found_by_scanner, thread) in seen_threads.items(): # Do we need to gather DLLs for module resolution if addr_space.address_compare(thread.StartAddress, system_range) != -1: owner = tasks.find_module(mods, mod_addrs,addr_space.address_mask(thread.StartAddress)) else: owning_process = thread.owning_process() if not owning_process.is_valid(): owner = None else: try: user_mod_addrs, user_mods = process_dll_info[owning_process.obj_offset] except KeyError: user_mods = dict((addr_space.address_mask(mod.DllBase), mod) for mod in owning_process.get_load_modules()) user_mod_addrs = sorted(user_mods.keys()) process_dll_info[owning_process.obj_offset] = (user_mod_addrs, user_mods) owner = tasks.find_module(user_mods, user_mod_addrs, addr_space.address_mask(thread.StartAddress)) # Specific fields thread1["ThreadId"] = "" thread1["PsCrossThreadFlagsImpersonating"] = "False" thread1["PsCrossThreadFlagsHideFromDbg"] = "False" thread1["PsCrossThreadFlagsSystem"] = "False" thread1["PsCrossThreadFlagsTerminated"] = "False" thread1["PsCrossThreadFlagsDeadThread"] = "False" thread1["PsCrossThreadFlagsBreakOnTermination"] = "False" thread1["PsCrossThreadFlagsSkipCreationMsg"] = "False" thread1["PsCrossThreadFlagsSkipTerminationMsg"] = "False" thread1["StartAddress"] = "" thread1["State"] = "" thread1["WaitReason"] = "" thread1["CreateTime"] = "" thread1["ExitTime"] = "" thread1["OwnerName"] = "" thread1["OwningProcess"] = "" thread1["AttachedProcess"] = "" thread1["Win32StartAddress"] = "" # Process fields necessaries to a new idEvent thread1["idEvent"] = 101 thread1["ProcessId"] = pslist1["ProcessId"] thread1["ProcessGuid"] = pslist1["ProcessGuid"] thread1["SyspceId"] = pslist1["SyspceId"] thread1["Image"] = pslist1["Image"] thread1["Source"] = "Memory" thread1['computer'] = pslist1['computer'] thread1["OwningProcess"] = str(thread.owning_process().ImageFileName) thread1["AttachedProcess"] = str(thread.attached_process().ImageFileName) if "PS_CROSS_THREAD_FLAGS_IMPERSONATING" in str(thread.CrossThreadFlags): result[1] = "True" thread1["PsCrossThreadFlagsImpersonating"] = "True" if "PS_CROSS_THREAD_FLAGS_HIDEFROMDBG" in str(thread.CrossThreadFlags): result[2] = "True" thread1["PsCrossThreadFlagsHideFromDbg"] = "True" if "PS_CROSS_THREAD_FLAGS_SYSTEM" in str(thread.CrossThreadFlags): result[3] = "True" thread1["PsCrossThreadFlagsSystem"] = "True" if "PS_CROSS_THREAD_FLAGS_TERMINATED" in str(thread.CrossThreadFlags): thread1["PsCrossThreadFlagsTerminated"] = "True" if "PS_CROSS_THREAD_FLAGS_DEADTHREAD" in str(thread.CrossThreadFlags): thread1["PsCrossThreadFlagsDeadThread"] = "True" if "PS_CROSS_THREAD_FLAGS_BREAK_ON_TERMINATION" in str(thread.CrossThreadFlags): thread1["PsCrossThreadFlagsBreakOnTermination"] = "True" if "PS_CROSS_THREAD_FLAGS_SKIP_CREATION_MSG" in str(thread.CrossThreadFlags): thread1["PsCrossThreadFlagsSkipCreationMsg"] = "True" if "PS_CROSS_THREAD_FLAGS_SKIP_TERMINATION_MSG" in str(thread.CrossThreadFlags): thread1["PsCrossThreadFlagsSkipTerminationMsg"] = "True" if owner: owner_name = str(owner.BaseDllName or 'None') thread1["OwnerName"] = str(owner.BaseDllName or 'None') else: owner_name = "Unknown" thread1["OwnerName"] = "Unknown" result[0] = "True" thread1["StartAddress"] = str(thread.StartAddress) thread1["State"] = str(thread.Tcb.State) thread1["WaitReason"] = str(thread.Tcb.WaitReason) thread1["ThreadId"] = int(thread.Cid.UniqueThread) thread1["CreateTime"] = str(thread.CreateTime) thread1["ExitTime"] = str(thread.ExitTime) thread1["Win32StartAddress"] = str(thread.Win32StartAddress) vthreads.append(thread1) thread1 = {} return result
def calculate(self): if not has_yara: debug.error("Please install Yara from code.google.com/p/yara-project") addr_space = utils.load_as(self._config) if self._config.YARA_RULES: s = self._config.YARA_RULES # Don't wrap hex or regex rules in quotes if s[0] not in ("{", "/"): s = '"' + s + '"' # Scan for unicode strings if self._config.WIDE: s += "wide" rules = yara.compile(sources = { 'n' : 'rule r1 {strings: $a = ' + s + ' condition: $a}' }) elif self._config.YARA_FILE: rules = yara.compile(self._config.YARA_FILE) else: debug.error("You must specify a string (-Y) or a rules file (-y)") if self._config.KERNEL: # Find KDBG so we know where kernel memory begins. Do not assume # the starting range is 0x80000000 because we may be dealing with # an image with the /3GB boot switch. kdbg = tasks.get_kdbg(addr_space) start = kdbg.MmSystemRangeStart.dereference_as("Pointer") # Modules so we can map addresses to owners mods = dict((mod.DllBase, mod) for mod in modules.lsmod(addr_space)) mod_addrs = sorted(mods.keys()) # There are multiple views (GUI sessions) of kernel memory. # Since we're scanning virtual memory and not physical, # all sessions must be scanned for full coverage. This # really only has a positive effect if the data you're # searching for is in GUI memory. sessions = [] for proc in tasks.pslist(addr_space): sid = proc.SessionId # Skip sessions we've already seen if sid == None or sid in sessions: continue session_space = proc.get_process_address_space() if session_space == None: continue sessions.append(sid) scanner = DiscontigYaraScanner(address_space = session_space, rules = rules) for hit, address in scanner.scan(start_offset = start): module = tasks.find_module(mods, mod_addrs, address) yield (module, address, hit, session_space.zread(address, 1024)) else: for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = VadYaraScanner(task = task, rules = rules) for hit, address in scanner.scan(): yield (task, address, hit, scanner.address_space.zread(address, 1024))
def execute(self,options,config,yarapath): addr_space = utils.load_as(config) if not os.path.isfile("kdcopydatablock.txt"): if (addr_space.profile.metadata.get("os") == "windows" and addr_space.profile.metadata.get("memory_model") == "64bit" and addr_space.profile.metadata.get("major") >= 6 and addr_space.profile.metadata.get("minor") >= 2): kdbg = tasks.get_kdbg(addr_space) fout = open('kdcopydatablock.txt', 'w') kdblockaddr = '{0:#x}'.format(kdbg.KdCopyDataBlock) fout.write(kdblockaddr) fout.close() sys.argv.append("--kdbg") sys.argv.append(kdblockaddr) processList = tasks.pslist(addr_space) if adutils.getConfigValue(options,'sockets') == True: getSocketsDelegate = adsockets.getSocketsFactory(addr_space.profile) sockets = getSocketsDelegate(config,addr_space) if adutils.getConfigValue(options,'yarascan') == True: getYaraDelegate = adyarascan.getYaraFactory(addr_space.profile) config.update('YARA_RULES_DIRECTORY',yarapath) compiledrules = getYaraDelegate(config).compile_rules() list_head_offset = None has_service_table = False process_obj_list = datastructs.rootType() for processIndex, eprocess in enumerate(processList): config.process_id = eprocess.UniqueProcessId config.dtb = eprocess.Pcb.DirectoryTableBase all_mods = list(eprocess.get_load_modules()) # get Token for Privileges token = eprocess.Token.dereference_as('_TOKEN') if hasattr(token.Privileges, 'Present'): privileges = token.Privileges.Present else: # Current memory analysis erroneously points # to token.ModifiedId for privileges for XP # The line below will match what the current memory analysis collects: # privileges = token.ModifiedId.LowPart # I don't think this is correct, either. luid = token.Privileges.dereference_as('_LUID_AND_ATTRIBUTES') privileges = luid.Luid.LowPart validName = "Unknown" if eprocess.ImageFileName: validName = eprocess.ImageFileName name = self.get_full_name(name=validName, path=eprocess.Peb.ProcessParameters.ImagePathName or '') try: process_obj = process_obj_list.Process.add( resultitemtype=18, Name=name, Path=adutils.SmartUnicode(eprocess.Peb.ProcessParameters.ImagePathName or ""), StartTime=adutils.SmartUnicode(eprocess.CreateTime or ""), WorkingDir=adutils.SmartUnicode(eprocess.Peb.ProcessParameters.CurrentDirectory.DosPath or ""), CommandLine=adutils.SmartUnicode(eprocess.Peb.ProcessParameters.CommandLine or ""), LinkTime=0, Subsystem=long(eprocess.Peb.ImageSubsystem), Imagebase=long(eprocess.Peb.ImageBaseAddress), Characteristics=0, Checksum=0, KernelTime=long(eprocess.Pcb.KernelTime), UserTime=long(eprocess.Pcb.UserTime), Privileges=long(privileges), PID=int(eprocess.UniqueProcessId), ParentPID=int(eprocess.InheritedFromUniqueProcessId), User='', Group='', MD5=BLANK_MD5, SHA1=BLANK_SHA1, FuzzySize=0, Fuzzy='', Fuzzy2X='', KFFStatus=0, FromMemory='', EffectiveUser='', EffectiveGroup='', Size=calculate_image_size(eprocess), EProcBlockLoc=long(eprocess.obj_vm.vtop(eprocess.obj_offset)) or 0, WindowTitle=adutils.SmartUnicode(eprocess.Peb.ProcessParameters.WindowTitle or "") ) except: debug.info('Caught error in adding process, continuing') continue kthread = eprocess.Pcb.ThreadListHead.Flink.dereference_as('_KTHREAD') list_head_offset = kthread.ThreadListEntry.obj_offset - kthread.obj_offset kthread = obj.Object('_KTHREAD', offset=eprocess.Pcb.ThreadListHead.Flink - list_head_offset, vm=eprocess.obj_vm) if hasattr(kthread, 'ServiceTable'): SDTs = set() for i in range(eprocess.ActiveThreads): if _is_valid_service_table_address(address=kthread.ServiceTable, memory_model=eprocess.obj_vm.profile.metadata.get('memory_model', '32bit')): SDTs.add(long(kthread.ServiceTable)) kthread = obj.Object('_KTHREAD', offset=kthread.ThreadListEntry.Flink - list_head_offset, vm=eprocess.obj_vm) for sdt in SDTs: process_obj.SDT.append(sdt) if adutils.getConfigValue(options,'processdlls') == True: for moduleIndex, module in enumerate(all_mods): baseName = "Unknown" if module.BaseDllName: baseName = module.BaseDllName dll_obj = process_obj.Loaded_DLL_List.DLL.add( Name=adutils.SmartUnicode(baseName or ''), Description='', Path=adutils.SmartUnicode(module.FullDllName or ''), Version='', MD5=BLANK_MD5, SHA1=BLANK_SHA1, FuzzySize=0, Fuzzy='', Fuzzy2X='', CreateTime=u"0000-00-00 00:00:00", #adutils.SmartUnicode(module.TimeDateStamp), KFFStatus=0, PID=int(eprocess.UniqueProcessId), baseAddress=long(module.DllBase), ImageSize=long(module.SizeOfImage), ProcessName=name, FromMemory='' ) if adutils.getConfigValue(options,'sockets') == True: pid = int(eprocess.UniqueProcessId) if pid in sockets: process_obj.Open_Sockets_List.CopyFrom(sockets[pid]) if adutils.getConfigValue(options,'handles') == True: if eprocess.ObjectTable.HandleTableList: for handle in eprocess.ObjectTable.handles(): if not handle.is_valid(): continue handle_obj = process_obj.Open_Handles_List.OpenHandle.add( ID=long(handle.HandleValue), Type=adutils.SmartUnicode(handle.get_object_type()), Path=get_handle_name(handle), AccessMask=int(handle.GrantedAccess), Name='', PID=int(eprocess.UniqueProcessId), PointerCount=long(handle.PointerCount), ObjectAddress=long(handle.obj_offset), FromMemory='', Owner='', Group='', Permissions='' ) if adutils.getConfigValue(options,'vad') == True: for vad in eprocess.VadRoot.traverse(): longflags = 0 if hasattr(vad, 'u'): longflags = long(vad.u.LongFlags) elif hasattr(vad, 'Core'): longflags = long(vad.Core.u.LongFlags) vad_obj = process_obj.Vad_List.Vad.add( Protection=int(vad.VadFlags.Protection), StartVpn=long(vad.Start >> PAGE_SIZE), EndVpn=long(vad.End >> PAGE_SIZE), Address=long(vad.obj_offset), Flags=longflags, Mapped=u'False', ProcessName=process_obj.Name, PID=process_obj.PID, FromMemory='') if not vad.Tag in _MMVAD_SHORT_TAGS: if vad.FileObject and vad.FileObject.FileName: name = str(vad.FileObject.FileName) if len(name) > 0 and name[0] == '\\': vad_obj.Filename = adutils.SmartUnicode(name) vad_obj.Mapped = u'True' else: print name if adutils.getConfigValue(options,'yarascan') == True: pid = int(eprocess.UniqueProcessId) config.update('pid',str(pid)) yara = getYaraDelegate(config).calculateonvad(compiledrules, eprocess) try: for hit in yara: process_obj.YaraHits.YaraHit.add( id='', Name=hit[2].rule, Category='') except: debug.info('Caught error in adding yarahit, continuing') file = open(config.OUTPUT_PATH + "processes.xml", "w") #file.write(process_obj_list.SerializeToString()) file.write(proto2xml(process_obj_list,indent=0))
def findcookie(self, kernel_space): """Find and read the nt!ObHeaderCookie value. On success, return True and save the cookie value in self._cookie. On Failure, return False. This method must be called before performing any tasks that require object header validation including handles, psxview (due to pspcid) and the object scanning plugins (psscan, etc). NOTE: this cannot be implemented as a volatility "magic" class, because it must be persistent across various classes and sources. We don't want to recalculate the cookie value multiple times. """ meta = kernel_space.profile.metadata vers = (meta.get("major", 0), meta.get("minor", 0)) # this algorithm only applies to Windows 10 or greater if vers < (6, 4): return True # prevent subsequent attempts from recalculating the existing value if self._cookie: return True if not has_distorm: debug.warning("distorm3 module is not installed") return False kdbg = tasks.get_kdbg(kernel_space) nt_mod = list(kdbg.modules())[0] addr = nt_mod.getprocaddress("ObGetObjectType") if addr == None: debug.warning("Cannot find nt!ObGetObjectType") return False # produce an absolute address by adding the DLL base to the RVA addr += nt_mod.DllBase if not nt_mod.obj_vm.is_valid_address(addr): debug.warning("nt!ObGetObjectType at {0} is invalid".format(addr)) return False # in theory...but so far we haven't tested 32-bits model = meta.get("memory_model") if model == "32bit": mode = distorm3.Decode32Bits else: mode = distorm3.Decode64Bits data = nt_mod.obj_vm.read(addr, 100) ops = distorm3.Decompose(addr, data, mode, distorm3.DF_STOP_ON_RET) addr = None # search backwards from the RET and find the MOVZX if model == "32bit": # movzx ecx, byte ptr ds:_ObHeaderCookie for op in reversed(ops): if (op.size == 7 and 'FLAG_DST_WR' in op.flags and len(op.operands) == 2 and op.operands[0].type == 'Register' and op.operands[1].type == 'AbsoluteMemoryAddress' and op.operands[1].size == 8): addr = op.operands[1].disp & 0xFFFFFFFF break else: # movzx ecx, byte ptr cs:ObHeaderCookie for op in reversed(ops): if (op.size == 7 and 'FLAG_RIP_RELATIVE' in op.flags and len(op.operands) == 2 and op.operands[0].type == 'Register' and op.operands[1].type == 'AbsoluteMemory' and op.operands[1].size == 8): addr = op.address + op.size + op.operands[1].disp break if not addr: debug.warning("Cannot find nt!ObHeaderCookie") return False if not nt_mod.obj_vm.is_valid_address(addr): debug.warning("nt!ObHeaderCookie at {0} is not valid".format(addr)) return False cookie = obj.Object("unsigned int", offset = addr, vm = nt_mod.obj_vm) self._cookie = int(cookie) return True
def lsmod(addr_space): """ A Generator for modules """ for m in tasks.get_kdbg(addr_space).modules(): yield m
def findcookie(self, kernel_space): """Find and read the nt!ObHeaderCookie value. On success, return True and save the cookie value in self._cookie. On Failure, return False. This method must be called before performing any tasks that require object header validation including handles, psxview (due to pspcid) and the object scanning plugins (psscan, etc). NOTE: this cannot be implemented as a volatility "magic" class, because it must be persistent across various classes and sources. We don't want to recalculate the cookie value multiple times. """ meta = kernel_space.profile.metadata vers = (meta.get("major", 0), meta.get("minor", 0)) # this algorithm only applies to Windows 10 or greater if vers < (6, 4): return True # prevent subsequent attempts from recalculating the existing value if self._cookie: return True if not has_distorm: debug.warning("distorm3 module is not installed") return False kdbg = tasks.get_kdbg(kernel_space) if not kdbg: debug.warning("Cannot find KDBG") return False nt_mod = None for mod in kdbg.modules(): nt_mod = mod break if nt_mod == None: debug.warning("Cannot find NT module") return False addr = nt_mod.getprocaddress("ObGetObjectType") if addr == None: debug.warning("Cannot find nt!ObGetObjectType") return False # produce an absolute address by adding the DLL base to the RVA addr += nt_mod.DllBase if not nt_mod.obj_vm.is_valid_address(addr): debug.warning("nt!ObGetObjectType at {0} is invalid".format(addr)) return False # in theory...but so far we haven't tested 32-bits model = meta.get("memory_model") if model == "32bit": mode = distorm3.Decode32Bits else: mode = distorm3.Decode64Bits data = nt_mod.obj_vm.read(addr, 100) ops = distorm3.Decompose(addr, data, mode, distorm3.DF_STOP_ON_RET) addr = None # search backwards from the RET and find the MOVZX if model == "32bit": # movzx ecx, byte ptr ds:_ObHeaderCookie for op in reversed(ops): if (op.size == 7 and 'FLAG_DST_WR' in op.flags and len(op.operands) == 2 and op.operands[0].type == 'Register' and op.operands[1].type == 'AbsoluteMemoryAddress' and op.operands[1].size == 8): addr = op.operands[1].disp & 0xFFFFFFFF break else: # movzx ecx, byte ptr cs:ObHeaderCookie for op in reversed(ops): if (op.size == 7 and 'FLAG_RIP_RELATIVE' in op.flags and len(op.operands) == 2 and op.operands[0].type == 'Register' and op.operands[1].type == 'AbsoluteMemory' and op.operands[1].size == 8): addr = op.address + op.size + op.operands[1].disp break if not addr: debug.warning("Cannot find nt!ObHeaderCookie") return False if not nt_mod.obj_vm.is_valid_address(addr): debug.warning("nt!ObHeaderCookie at {0} is not valid".format(addr)) return False cookie = obj.Object("unsigned int", offset=addr, vm=nt_mod.obj_vm) self._cookie = int(cookie) return True
def calculate(self): if self._config.SLACK and (self._config.DSTS or self._config.PCAP): debug.error('SLACK can\'t be used with PCAP or DSTS') # Make sure the MAC address is valid if self._config.MAC: hex_mac = self.validate_mac(self._config.MAC) if not hex_mac: debug.error('Invalid MAC address') # Ensure PCAP file won't overwrite an existing file if self._config.PCAP and os.path.exists(self._config.PCAP): debug.error( '\'{}\' already exists. Cowardly refusing to overwrite it...'. format(self._config.PCAP)) # Ensure DSTS file won't overwrite an existing file if self._config.DSTS and os.path.exists(self._config.DSTS): debug.error( '\'{}\' already exists. Cowardly refusing to overwrite it...'. format(self._config.DSTS)) if self._config.SLACK: # Compiled RegEx = (tiny) increase in efficiency # NetBIOS code chars self.rx_nbchars = re.compile('^([ACDEFH][A-P])+$') # Get the start of kernel space addr_space = utils.load_as(self._config) kdbg = tasks.get_kdbg(addr_space) start = kdbg.MmSystemRangeStart.dereference_as("Pointer") # 64-bit to 48-bit adjustment # TODO: Is there a more Volatility-esque way of doing this? if start > 0xffff000000000000: start = start & 0x0000ffffffffffff macs = set() # Store the MAC addresses we find if self._config.MAC: macs.add(hex_mac) else: # No MAC provided, so we'd better try and find one # Attemp 1: NDsh new_macs = self.macfind_ndsh(addr_space, start) if new_macs: for new_mac in new_macs: macs.add(new_mac) if len(macs) < 1: debug.error('No MAC addresses found.') hits = [] _MAX_SLACK_LENGTH = 128 for session_space in self.gen_session_spaces(addr_space): for mac in macs: scanner = MacScanner(mac) for address in scanner.scan(session_space, offset=start): if address in hits: continue hits.append(address) eth = obj.Object('_ETHERNET', vm=session_space, offset=address) if eth.eth_type == 0x0800 or eth.eth_type == 0x86dd: raw = session_space.zread(address, 2048) # Parse ethernet's payload if eth.eth_type == 0x0800: # IPv4: eth_payload = obj.Object('_IPv4', vm=session_space, offset=eth.v() + 0x0E) end = 14 + eth_payload.length if self._config.SLACK: yield address, raw[end:end + _MAX_SLACK_LENGTH] continue raw = raw[:end] elif eth.eth_type == 0x86dd: # IPv6 eth_payload = obj.Object('_IPv6', vm=session_space, offset=eth.v() + 0x0E) end = 14 + 40 + eth_payload.pld_len if self._config.SLACK: yield address, raw[end:end + _MAX_SLACK_LENGTH] continue raw = raw[:end] else: # This shouldn't happen, but just in case continue # Parse ethernet's payload's payload if eth_payload.is_tcp(): payload = obj.Object( '_TCP', vm=session_space, offset=eth_payload.payload_offset()) elif eth_payload.is_udp(): payload = obj.Object( '_UDP', vm=session_space, offset=eth_payload.payload_offset()) else: yield raw, eth, eth_payload, None continue yield raw, eth, eth_payload, payload
def calculate(self): addr_space = utils.load_as(self._config) # Get the OS version we're analyzing version = (addr_space.profile.metadata.get('major', 0), addr_space.profile.metadata.get('minor', 0)) modlist = list(modules.lsmod(addr_space)) mods = dict( (addr_space.address_mask(mod.DllBase), mod) for mod in modlist) mod_addrs = sorted(mods.keys()) # KTIMERs collected timers = [] # Valid KTIMER.Header.Type values TimerNotificationObject = 8 TimerSynchronizationObject = 9 valid_types = (TimerNotificationObject, TimerSynchronizationObject) if version == (5, 1) or (version == (5, 2) and addr_space.profile.metadata.get( 'build', 0) == 3789): # On XP SP0-SP3 x86 and Windows 2003 SP0, KiTimerTableListHead # is an array of 256 _LIST_ENTRY for _KTIMERs. if self._config.LISTHEAD: KiTimerTableListHead = self._config.LISTHEAD else: KiTimerTableListHead = self.find_list_head( modlist[0], "KeUpdateSystemTime", "\x25\xFF\x00\x00\x00\x8D\x0C\xC5") if not KiTimerTableListHead: debug.warning("Cannot find KiTimerTableListHead") else: lists = obj.Object("Array", offset=KiTimerTableListHead, vm=addr_space, targetType='_LIST_ENTRY', count=256) for l in lists: for t in l.list_of_type("_KTIMER", "TimerListEntry"): timers.append(t) elif version == (5, 2) or version == (6, 0): # On XP x64, Windows 2003 SP1-SP2, and Vista SP0-SP2, KiTimerTableListHead # is an array of 512 _KTIMER_TABLE_ENTRY structs. if self._config.LISTHEAD: KiTimerTableListHead = self._config.LISTHEAD else: if addr_space.profile.metadata.get("memory_model") == "32bit": sigData = [ (False, "KeCancelTimer", "\xC1\xE7\x04\x81\xC7"), (True, "KeUpdateSystemTime", "\x48\xB9\x00\x00\x00\x00\x80\xF7\xFF\xFF\x4C\x8D\x1D" ) ] else: sigData = [ ( True, "KeCancelTimer", "\x48\x8D\x4C\x6D\x00\x48\x8D\x05" ), # lea rcx, [rbp+rbp*2+0] / lea rax, KiTimerTableListHead (nt.dll md5 825926D6AD714A529F4069D9EBBD1D3B) (True, "KeUpdateSystemTime", "\x48\xB9\x00\x00\x00\x00\x80\xF7\xFF\xFF\x4C\x8D\x1D" ) # mov rcx, 0FFFFF78000000000h / lea r11, KiTimerTableListHead (xp64sp1/2k3sp2 B1E08186348ED662D50118618F012445) ] for sig in sigData: if sig[0]: KiTimerTableListHead = self.find_list_head_offset( modlist[0], sig[1], sig[2]) else: KiTimerTableListHead = self.find_list_head( modlist[0], sig[1], sig[2]) if KiTimerTableListHead: break if not KiTimerTableListHead: debug.warning("Cannot find KiTimerTableListHead") else: lists = obj.Object("Array", offset=KiTimerTableListHead, vm=addr_space, targetType='_KTIMER_TABLE_ENTRY', count=512) for l in lists: # print "List at %s" % hex(int(l.obj_offset)) for t in l.Entry.list_of_type("_KTIMER", "TimerListEntry"): # print "Timer at %s" % hex(int(t.obj_offset)) timers.append(t) elif version >= (6, 1): # Starting with Windows 7, there is no more KiTimerTableListHead. The list is # at _KPCR.PrcbData.TimerTable.TimerEntries (credits to Matt Suiche # for this one. See http://pastebin.com/FiRsGW3f). for kpcr in tasks.get_kdbg(addr_space).kpcrs(): for table in kpcr.ProcessorBlock.TimerTable.TimerEntries: for t in table.Entry.list_of_type("_KTIMER", "TimerListEntry"): timers.append(t) for timer in timers: # Sanity check on the timer type if timer.Header.Type not in valid_types: # print 'bad type %d' % timer.Header.Type continue # Ignore timers without DPCs # if not timer.Dpc.is_valid(): # print 'dpc not valid' # continue # if not timer.Dpc.DeferredRoutine.is_valid(): # print 'dpc deferredroutine not valid' # continue # Lookup the module containing the DPC module = tasks.find_module( mods, mod_addrs, addr_space.address_mask(timer.Dpc.DeferredRoutine)) yield timer, module
def calculate(self): if self._config.SLACK and (self._config.DSTS or self._config.PCAP): debug.error('SLACK can\'t be used with PCAP or DSTS') # Make sure the MAC address is valid if self._config.MAC: hex_mac = self.validate_mac(self._config.MAC) if not hex_mac: debug.error('Invalid MAC address') # Ensure PCAP file won't overwrite an existing file if self._config.PCAP and os.path.exists(self._config.PCAP): debug.error('\'{}\' already exists. Cowardly refusing to overwrite it...'.format( self._config.PCAP)) # Ensure DSTS file won't overwrite an existing file if self._config.DSTS and os.path.exists(self._config.DSTS): debug.error('\'{}\' already exists. Cowardly refusing to overwrite it...'.format( self._config.DSTS)) if self._config.SLACK: # Compiled RegEx = (tiny) increase in efficiency # NetBIOS code chars self.rx_nbchars = re.compile('^([ACDEFH][A-P])+$') # Get the start of kernel space addr_space = utils.load_as(self._config) kdbg = tasks.get_kdbg(addr_space) start = kdbg.MmSystemRangeStart.dereference_as("Pointer") # 64-bit to 48-bit adjustment # TODO: Is there a more Volatility-esque way of doing this? if start > 0xffff000000000000: start = start & 0x0000ffffffffffff macs = set() # Store the MAC addresses we find if self._config.MAC: macs.add(hex_mac) else: # No MAC provided, so we'd better try and find one # Attemp 1: NDsh new_macs = self.macfind_ndsh(addr_space, start) if new_macs: for new_mac in new_macs: macs.add(new_mac) if len(macs) < 1: debug.error('No MAC addresses found.') hits = [] _MAX_SLACK_LENGTH = 128 for session_space in self.gen_session_spaces(addr_space): for mac in macs: scanner = MacScanner(mac) for address in scanner.scan(session_space, offset=start): if address in hits: continue hits.append(address) eth = obj.Object('_ETHERNET', vm=session_space, offset=address) if eth.eth_type == 0x0800 or eth.eth_type == 0x86dd: raw = session_space.zread(address, 2048) # Parse ethernet's payload if eth.eth_type == 0x0800: # IPv4: eth_payload = obj.Object('_IPv4', vm=session_space, offset = eth.v() + 0x0E) end = 14 + eth_payload.length if self._config.SLACK: yield address, raw[end:end+_MAX_SLACK_LENGTH] continue raw = raw[:end] elif eth.eth_type == 0x86dd: # IPv6 eth_payload = obj.Object('_IPv6', vm=session_space, offset = eth.v() + 0x0E) end = 14 + 40 + eth_payload.pld_len if self._config.SLACK: yield address, raw[end:end+_MAX_SLACK_LENGTH] continue raw = raw[:end] else: # This shouldn't happen, but just in case continue # Parse ethernet's payload's payload if eth_payload.is_tcp(): payload = obj.Object('_TCP', vm=session_space, offset=eth_payload.payload_offset()) elif eth_payload.is_udp(): payload = obj.Object('_UDP', vm=session_space, offset=eth_payload.payload_offset()) else: yield raw, eth, eth_payload, None continue yield raw, eth, eth_payload, payload
def calculate(self): ## Stop executing if the user did not mean to overwrite the file if os.path.isfile(self.save_location): resp = raw_input("Are you sure you want to overwrite {}? [Y/n] ".format(self.save_location)) if resp.upper() == 'N': self.abort = True return # Not continuing ## Read from existing target configuration (if modifying) if self._config.MODIFY: self.new_config.read(self.save_location) ## Attempt to automatically determine profile if self._config.AUTO: print("Determining profile based on KDBG search...") self.suglist = [ s for s, _ in kdbgscan.KDBGScan.calculate(self)] if self.suglist: self.new_config.set("DEFAULT", "profile", self.suglist[0]) ## Update profile so --offsets will work self._config.PROFILE = self.suglist[0] self._exclude_options.append('profile') else: print("Failed to determine profile") ## Read in current command line (precedence over settings in configs) for key in self._config.opts: if key not in self._exclude_options: self.new_config.set("DEFAULT", key, self._config.opts[key]) ## Add to excluded list so we do not overwrite them later self._exclude_options.append(key) ## Save options from configuration files (unless excluded by user) if self._config.EXCLUDE_CONF == False: for key in self._config.cnf_opts: if key not in self._exclude_options: self.new_config.set("DEFAULT", key, self._config.cnf_opts[key]) ## Get offsets (KDBG and DTB) if self._config.OFFSETS: addr_space = utils.load_as(self._config) kdbg = tasks.get_kdbg(addr_space) self.new_config.set("DEFAULT", "kdbg", str(kdbg.v())) if hasattr(addr_space, "dtb"): self.new_config.set("DEFAULT", "dtb", str(addr_space.dtb)) ## Ensure DTB and KDBG are converted properly at the last moment: ## Note, volatility will convert these to int when read from CNF_OPTS try: kdbg = self.new_config.get("DEFAULT", "kdbg") self.new_config.set("DEFAULT", "kdbg", str(hex(int(kdbg)))) except ConfigParser.NoOptionError: pass try: dtb = self.new_config.get("DEFAULT", "dtb") self.new_config.set("DEFAULT", "dtb", str(hex(int(dtb)))) except ConfigParser.NoOptionError: pass ## Write the new configuration file with open(self.save_location, "wb") as configfile: self.new_config.write(configfile)
def calculate(self): if not has_yara: debug.error("Please install Yara from code.google.com/p/yara-project") addr_space = utils.load_as(self._config) if self._config.YARA_RULES: s = self._config.YARA_RULES # Don't wrap hex or regex rules in quotes if s[0] not in ("{", "/"): s = '"' + s + '"' # Scan for unicode strings if self._config.WIDE: s += "wide" rules = yara.compile(sources = { 'n' : 'rule r1 {strings: $a = ' + s + ' condition: $a}' }) elif self._config.YARA_FILE: rules = yara.compile(self._config.YARA_FILE) else: debug.error("You must specify a string (-Y) or a rules file (-y)") if self._config.KERNEL: # Find KDBG so we know where kernel memory begins. Do not assume # the starting range is 0x80000000 because we may be dealing with # an image with the /3GB boot switch. kdbg = tasks.get_kdbg(addr_space) # FIXME: Addresses should be truncated to 48 bits. Currently # we do that in Pointer.__eq__ but not in Pointer.v(). This prevents # module lookups in yarascan's --kernel mode on x64 from working # properly because win32.tasks.find_module cannot match the truncated # address with non-truncated mod.DllBase.v(). Changing Pointer.v() # could have wide spread effects, so this yarascan issue is fixed # temporarily by giving YaraScan a private version of find_module # that performs the pointer truncation. After fixing this globally, # we can dereference MmSystemRangeStart as a Pointer instead of an # address and then remove the manual bitmask below. start = kdbg.MmSystemRangeStart.dereference_as("address") start = start & 0xffffffffffff # Modules so we can map addresses to owners mods = dict((mod.DllBase & 0xffffffffffff, mod) for mod in modules.lsmod(addr_space)) mod_addrs = sorted(mods.keys()) # There are multiple views (GUI sessions) of kernel memory. # Since we're scanning virtual memory and not physical, # all sessions must be scanned for full coverage. This # really only has a positive effect if the data you're # searching for is in GUI memory. sessions = [] for proc in tasks.pslist(addr_space): sid = proc.SessionId # Skip sessions we've already seen if sid == None or sid in sessions: continue session_space = proc.get_process_address_space() if session_space == None: continue sessions.append(sid) scanner = DiscontigYaraScanner(address_space = session_space, rules = rules) for hit, address in scanner.scan(start_offset = start): module = self.find_module(mods, mod_addrs, address) yield (module, address, hit, session_space.zread(address, 1024)) else: for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = VadYaraScanner(task = task, rules = rules) for hit, address in scanner.scan(): yield (task, address, hit, scanner.address_space.zread(address, 1024))