def build_pids(self): if self._config.PROC_NAME: # PROC_NAME name_list = self._config.PROC_NAME.split(",") pid_list = [] for name in name_list: for task in tasks.pslist(self.addr_space): if name in str(task.ImageFileName): pid_list.append(task.UniqueProcessId) pids = ','.join(map(str, pid_list)) else: # PROC_NAME_MATCH name_list = self._config.PROC_NAME_MATCH.split(",") pid_list = [] for name in name_list: for task in tasks.pslist(self.addr_space): if name == str(task.ImageFileName): pid_list.append(task.UniqueProcessId) pids = ','.join(map(str, pid_list)) if pids == '': debug.error("No process matches given name. Please specify a valid name or PID.") return pids
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) addr_space.profile.add_types(sde_types) if addr_space.profile.metadata.get("major", 0) == 5 and addr_space.profile.metadata.get("minor", 0) == 2: addr_space.profile.add_types(sdt_types_2k3) else: addr_space.profile.add_types(sdt_types) ## Get a sorted list of module addresses mods = dict((mod.DllBase.v(), mod) for mod in modules.lsmod(addr_space)) mod_addrs = sorted(mods.keys()) # Gather up all SSDTs referenced by threads print "Gathering all referenced SSDTs from KTHREADs..." ssdts = set() for proc in tasks.pslist(addr_space): for thread in proc.ThreadListHead.list_of_type("_ETHREAD", "ThreadListEntry"): ssdt_obj = thread.Tcb.ServiceTable.dereference_as("_SERVICE_DESCRIPTOR_TABLE") ssdts.add(ssdt_obj) # Get a list of *unique* SSDT entries. Typically we see only two. tables = set() for ssdt_obj in ssdts: for i, desc in enumerate(ssdt_obj.Descriptors): # Apply some extra checks - KiServiceTable should reside in kernel memory and ServiceLimit # should be greater than 0 but not unbelievably high if ( desc.is_valid() and desc.ServiceLimit > 0 and desc.ServiceLimit < 0xFFFF and desc.KiServiceTable > 0x80000000 ): tables.add((i, desc.KiServiceTable.v(), desc.ServiceLimit.v())) print "Finding appropriate address space for tables..." tables_with_vm = [] for idx, table, n in tables: found = False for p in tasks.pslist(addr_space): ## This is the process address space ps_ad = p.get_process_address_space() ## Is the table accessible from the process AS? if ps_ad.is_valid_address(table): tables_with_vm.append((idx, table, n, ps_ad)) found = True break ## If not we use the kernel address space if not found: # Any VM is equally bad... tables_with_vm.append((idx, table, n, addr_space)) for idx, table, n, vm in sorted(tables_with_vm, key=itemgetter(0)): yield idx, table, n, vm, mods, mod_addrs
def calculate(self): addr_space = utils.load_as(self._config) ## Get a sorted list of module addresses mods = dict((addr_space.address_mask(mod.DllBase), mod) for mod in modules.lsmod(addr_space)) mod_addrs = sorted(mods.keys()) ssdts = set() if addr_space.profile.metadata.get("memory_model", "32bit") == "32bit": # Gather up all SSDTs referenced by threads print "[x86] Gathering all referenced SSDTs from KTHREADs..." for proc in tasks.pslist(addr_space): for thread in proc.ThreadListHead.list_of_type("_ETHREAD", "ThreadListEntry"): ssdt_obj = thread.Tcb.ServiceTable.dereference_as("_SERVICE_DESCRIPTOR_TABLE") ssdts.add(ssdt_obj) else: print "[x64] Gathering all referenced SSDTs from KeAddSystemServiceTable..." # The NT module always loads first ntos = list(modules.lsmod(addr_space))[0] func_rva = ntos.getprocaddress("KeAddSystemServiceTable") if func_rva == None: raise StopIteration("Cannot locate KeAddSystemServiceTable") KeAddSystemServiceTable = ntos.DllBase + func_rva for table_rva in find_tables(KeAddSystemServiceTable, addr_space): ssdt_obj = obj.Object("_SERVICE_DESCRIPTOR_TABLE", ntos.DllBase + table_rva, addr_space) ssdts.add(ssdt_obj) # Get a list of *unique* SSDT entries. Typically we see only two. tables = set() for ssdt_obj in ssdts: for i, desc in enumerate(ssdt_obj.Descriptors): # Apply some extra checks - KiServiceTable should reside in kernel memory and ServiceLimit # should be greater than 0 but not unbelievably high if ( desc.is_valid() and desc.ServiceLimit > 0 and desc.ServiceLimit < 0xFFFF and desc.KiServiceTable > 0x80000000 ): tables.add((i, desc.KiServiceTable.v(), desc.ServiceLimit.v())) print "Finding appropriate address space for tables..." tables_with_vm = [] procs = list(tasks.pslist(addr_space)) for idx, table, n in tables: vm = tasks.find_space(addr_space, procs, table) if vm: tables_with_vm.append((idx, table, n, vm)) else: debug.debug("[SSDT not resident at 0x{0:08X}]\n".format(table)) for idx, table, n, vm in sorted(tables_with_vm, key=itemgetter(0)): yield idx, table, n, vm, mods, mod_addrs
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 calculate(self): addr_space = utils.load_as(self._config) if not has_yara: debug.error("You must install yara to use this plugin") if not self._config.DUMP_DIR: debug.error("You must supply a --dump-dir parameter") if self._config.PHYSICAL: # Find the FileAddressSpace while addr_space.__class__.__name__ != "FileAddressSpace": addr_space = addr_space.base scanner = malfind.DiscontigYaraScanner(address_space = addr_space, rules = DumpCerts.rules) for hit, address in scanner.scan(): cert = obj.Object(DumpCerts.type_map.get(hit.rule), vm = scanner.address_space, offset = address, ) if cert.is_valid(): yield None, cert else: for process in self.filter_tasks(tasks.pslist(addr_space)): scanner = malfind.VadYaraScanner(task = process, rules = DumpCerts.rules) for hit, address in scanner.scan(): cert = obj.Object(DumpCerts.type_map.get(hit.rule), vm = scanner.address_space, offset = address, ) if cert.is_valid(): yield process, cert
def calculate(self): if self._config.OUTPUT == "xlsx" and not has_openpyxl: debug.error("You must install OpenPyxl for xlsx format:\n\thttps://bitbucket.org/ericgazoni/openpyxl/wiki/Home") elif self._config.OUTPUT == "xlsx" and not self._config.OUTPUT_FILE: debug.error("You must specify an output *.xlsx file!\n\t(Example: --output-file=OUTPUT.xlsx)") addr_space = utils.load_as(self._config) all_tasks = list(tasks.pslist(addr_space)) ps_sources = {} # The keys are names of process sources. The values # are dictionaries whose keys are physical process # offsets and the values are _EPROCESS objects. ps_sources['pslist'] = self.check_pslist(all_tasks) ps_sources['psscan'] = self.check_psscan() ps_sources['thrdproc'] = self.check_thrdproc(addr_space) ps_sources['csrss'] = self.check_csrss_handles(all_tasks) ps_sources['pspcid'] = self.check_pspcid(addr_space) ps_sources['session'] = self.check_sessions(addr_space) if addr_space.profile.metadata.get('major', 0) == 6 and addr_space.profile.metadata.get('minor', 0) >= 2: ps_sources['deskthrd'] = {} else: ps_sources['deskthrd'] = self.check_desktop_thread(addr_space) # Build a list of offsets from all sources seen_offsets = [] for source in ps_sources.values(): for offset in source.keys(): if offset not in seen_offsets: seen_offsets.append(offset) yield offset, source[offset], ps_sources
def calculate(self): addr_space = utils.load_as(self._config) if not self.is_valid_profile(addr_space.profile): debug.error("This plugin only works on XP and 2003") ## When verbose is specified, we recalculate the list of SIDs for ## services in the registry. Otherwise, we take the list from the ## pre-populated dictionary in getservicesids.py if self._config.VERBOSE: ssids = getservicesids.GetServiceSids(self._config).calculate() for sid, service in ssids: self.extrasids[sid] = " (Service: " + service + ")" else: for sid, service in getservicesids.servicesids.items(): self.extrasids[sid] = " (Service: " + service + ")" ## Get the user's SIDs from the registry self.load_user_sids() for proc in tasks.pslist(addr_space): if str(proc.ImageFileName).lower() == "services.exe": for vad, process_space in proc.get_vads(vad_filter = proc._mapped_file_filter): if vad.FileObject.FileName: name = str(vad.FileObject.FileName).lower() if name.endswith(".evt"): ## Maybe check the length is reasonable, though probably there won't ## ever be event logs that are multiple GB or TB in size. data = process_space.zread(vad.Start, vad.Length) yield name, data
def scan(service_path, profile_name, queue_results): # Find Yara signatures, if file is not available, we need to terminate. yara_path = os.path.join(os.getcwd(), 'signatures.yar') if not os.path.exists(yara_path): yara_path = get_resource(os.path.join('rules', 'signatures.yar')) if not os.path.exists(yara_path): raise DetectorError("Unable to find a valid Yara signatures file!") log.info("Selected Yara signature file at %s", yara_path) # Retrieve adress space. space = get_address_space(service_path, profile_name, yara_path) if space == None: log.info("Cannot generate address space") else: log.info("Address space: {0}, Base: {1}".format(space, space.base)) log.info("Profile: {0}, DTB: {1:#x}".format(space.profile, space.dtb)) rules = yara.compile(yara_path) log.info("Starting yara scanner...") matched = [] for process in tasks.pslist(space): # Skip ourselves. if process.UniqueProcessId == os.getpid(): continue try: process_name = process.ImageFileName except: process_name = '' try: try: log.debug("Scanning process %s, pid: %d, ppid: %d, exe: %s, cmdline: %s", process_name, process.UniqueProcessId, process.InheritedFromUniqueProcessId, process.ImagePathName, process.CommandLine) except: log.debug("Scanning process %s, pid: %d", process_name, process.UniqueProcessId) for hit in rules.match(pid=process.UniqueProcessId): log.warning("Process %s (pid: %d) matched: %s, Values:", process_name, process.UniqueProcessId, hit.rule) for entry in hit.strings: log.warning("\t%d, %s, %s", entry[0], entry[1], entry[2]) # We only store unique results, it's pointless to store results # for the same rule. if not hit.rule in matched: # Add rule to the list of unique matches. matched.append(hit.rule) # Add match to the list of results. queue_results.put(dict( rule=hit.rule, detection=hit.meta.get('detection'), )) except Exception as e: log.debug("Unable to scan process: %s", e)
def calculate(self): self.kernel_address_space = utils.load_as(self._config) self.flat_address_space = utils.load_as(self._config, astype = 'physical') if not(bool(self._config.DIR)): debug.error("--dir needs to be present") if not(bool(self._config.pid) ^ bool(self._config.eproc) ^ bool(self._config.fobj) ^ bool(self._config.pool)): if not(bool(self._config.pid) or bool(self._config.eproc) or bool(self._config.fobj) or bool(self._config.pool)): debug.error("exactly *ONE* of the options --pid, --eproc, --fobj or --pool must be specified (you have not specified _any_ of these options)") else: debug.error("exactly *ONE* of the options --pid, --eproc, --fobj or --pool must be specified (you have used _multiple_ such options)") if bool(self._config.pid): # --pid eproc_matches = [ eproc for eproc in tasks.pslist(self.kernel_address_space) if eproc.UniqueProcessId == self._config.pid ] if len(eproc_matches) != 1: debug.error("--pid needs to take a *VALID* PID argument (could not find PID {0} in the process listing for this memory image)".format(self._config.pid)) return self.dump_from_eproc(eproc_matches[0]) elif bool(self._config.eproc): # --eproc return self.dump_from_eproc(obj.Object("_EPROCESS", offset = self._config.eproc, vm = self.kernel_address_space)) elif bool(self._config.fobj): # --fobj try: file_object = obj.Object("_FILE_OBJECT", offset = self._config.fobj, vm = self.flat_address_space) if bool(self._config.reconstruct): # --reconstruct return [ (file_object, self.parse_string(file_object.FileName)) ] else: return filter(None, [ self.dump_file_object(file_object) ]) except ExportException as exn: debug.error(exn) else: # --pool return self.dump_from_pool()
def shimcache_xp(address_space): """Enumerate entries from the shared memory section on XP systems.""" seen = [] shim = lambda x : (x.Tag == "Vad " and x.VadFlags.Protection == 4) for process in tasks.pslist(address_space): for vad, space in process.get_vads(vad_filter = shim): if space.read(vad.Start, 4) != "\xEF\xBE\xAD\xDE": continue records = obj.Object("ShimRecords", offset = vad.Start, vm = space) for entry in records.Entries: if not entry.is_valid(): continue entry_offset = space.vtop(entry.obj_offset) if entry_offset in seen: continue seen.append(entry_offset) yield entry.Path, entry.LastModified, entry.LastUpdate
def calculate(self): eproc = {} found = {} cmdline = {} pathname = {} # Brute force search for eproc blocks in pool memory for eprocess in filescan.PSScan(self._config).calculate(): eproc[eprocess.obj_offset] = eprocess found[eprocess.obj_offset] = 1 # Walking the active process list. # Remove any tasks we find here from the brute force search if the --short option is set. # Anything left is something which was hidden/terminated/of interest. address_space = utils.load_as(self._config) for task in tasks.pslist(address_space): phys = address_space.vtop(task.obj_offset) if phys in eproc: if self._config.SHORT: del eproc[phys] del found[phys] else: found[phys] = 0 # Grab command line and parameters peb = task.Peb if peb: cmdline[phys] = peb.ProcessParameters.CommandLine pathname[phys] = peb.ProcessParameters.ImagePathName ret = [eproc, found, cmdline, pathname] return ret
def calculate(self): addr_space = utils.load_as(self._config) addr_space.profile.add_types(evt_log_types) if addr_space.profile.metadata.get('major', 0) != 5: print "This plugin only works on XP and 2K3" return if self._config.VERBOSE: self.reset_current() self.set_current("SYSTEM") ssids = getservicesids.GetServiceSids.calculate(self) for sid, service in ssids: self.extrasids[sid] = " (Service: " + service + ")" else: for sid in self.extrasids: self.extrasids[sid] = " (Service: " + self.extrasids[sid] + ")" self.reset_current() self.set_current("SOFTWARE") for k1 in self.reg_enum_key('SOFTWARE', 'Microsoft\\Windows NT\\CurrentVersion\\ProfileList'): val = self.reg_get_value('SOFTWARE', k1, 'ProfileImagePath') sid = k1.split("\\")[-1] if val != None: self.extrasids[sid] = " (User: "******"\\")[-1] + ")" for proc in tasks.pslist(addr_space): if str(proc.ImageFileName).lower() == "services.exe": map = self.list_mapped_files(proc, pe_only=False, get_data=True) for key, (name, buf) in map.items(): if name and buf: name = str(name).lower() if name.endswith(".evt"): yield name, buf
def calculate(self): addr_space = utils.load_as(self._config) tasklist = [] modslist = [] if self._config.SCAN: if not self._config.KERNEL_ONLY: for t in filescan.PSScan(self._config).calculate(): v = self.virtual_process_from_physical_offset(addr_space, t.obj_offset) if v: tasklist.append(v) if not self._config.PROCESS_ONLY: modslist = [m for m in modscan.ModScan(self._config).calculate()] else: if not self._config.KERNEL_ONLY: tasklist = [t for t in tasks.pslist(addr_space)] if not self._config.PROCESS_ONLY: modslist = [m for m in modules.lsmod(addr_space)] for task in tasklist: for mod in task.get_load_modules(): yield task, mod for mod in modslist: yield None, mod
def calculate(self): addr_space = utils.load_as(self._config) if self._config.REGEX: try: if self._config.IGNORE_CASE: mod_re = re.compile(self._config.REGEX, re.I) else: mod_re = re.compile(self._config.REGEX) except re.error as e: debug.error('Error parsing regular expression: %s' % e) mods = dict((mod.DllBase.v(), mod) for mod in modules.lsmod(addr_space)) # We need the process list to find spaces for some drivers. Enumerate them here # instead of inside the find_space function, so we only have to do it once. procs = list(tasks.pslist(addr_space)) if self._config.BASE: if self._config.BASE in mods: mod_name = mods[self._config.BASE].BaseDllName else: mod_name = "UNKNOWN" yield addr_space, procs, int(self._config.BASE), mod_name else: for mod in list(mods.values()): if self._config.REGEX: if not mod_re.search(str(mod.FullDllName or '')) and not mod_re.search(str(mod.BaseDllName or '')): continue yield addr_space, procs, mod.DllBase.v(), mod.BaseDllName
def _pydeep_page(self): """Run pydeep and return the hash""" page_sig = None try: if self._config.SSDEEP_SIG: # 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}' # }) pass elif self._config.SSDEEP_FILE: # rules = yara.compile(self._config.YARA_FILE) pass elif self._config.SSDEEP_PIDOFF: (pid, base) = self._config.SSDEEP_PIDOFF.split(":") for proc in tasks.pslist(self._addr_space): if proc.UniqueProcessId == int(pid): process_space = proc.get_process_address_space() page_data = process_space.zread(int(base, 16), 0x1000) page_sig = pydeep.hash_buf(page_data) if page_sig == "3::": debug.error("PID XXX and OFFSET YYY null or not found") else: debug.error("You must specify an ssdeep hash (-Y), a file to hash (-y), or a PID:BASE pair (-T)") except Exception as why: debug.error("Cannot compile rules: {0}".format(str(why))) return page_sig
def calculate(self): """Determines the address space""" addr_space = utils.load_as(self._config) result = None adrs = addr_space while adrs: if adrs.__class__.__name__ == 'WindowsHiberFileSpace32': sr = adrs.ProcState.SpecialRegisters peb = obj.NoneObject("Cannot locate a valid PEB") # Find the PEB by cycling through processes. This method works # on all versions of Windows x86 and x64. for task in tasks.pslist(addr_space): if task.Peb: peb = task.Peb break result = {'header': adrs.get_header(), 'sr': sr, 'peb': peb, 'adrs': adrs } adrs = adrs.base if result == None: debug.error("Memory Image could not be identified or did not contain hiberation information") return result
def calculate(self): result = {} ## Load a new address space addr_space = utils.load_as(self._config) addr_space.profile.add_types(pslist_types) for task in tasks.pslist(addr_space): task_info = {} task_info['eprocess'] = task task_info['image_file_name'] = task.ImageFileName or 'UNKNOWN' task_info['process_id'] = task.UniqueProcessId task_info['active_threads'] = task.ActiveThreads task_info['inherited_from'] = task.InheritedFromUniqueProcessId task_info['handle_count'] = task.ObjectTable.HandleCount task_info['create_time'] = task.CreateTime ## Get the Process Environment Block - Note that _EPROCESS ## will automatically switch to process address space by ## itself. if self._config.VERBOSE > 1: peb = task.Peb if peb: task_info['command_line'] = peb.ProcessParameters.CommandLine task_info['ImagePathName'] = peb.ProcessParameters.ImagePathName task_info['Audit ImageFileName'] = task.SeAuditProcessCreationInfo.ImageFileName.Name or 'UNKNOWN' result[int(task_info['process_id'])] = task_info return result
def find_scouts(self): """ Find all 'Scout' level implants using their distinctive watermarks - these index the configuration files, allowing us to obtain AES key information """ scouts = [] # Dynamically generate Yara rules from watermark if not has_yara: debug.error("Yara must be installed for this plugin") 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.") rules = self.gen_yara_rules() for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = malfind.VadYaraScanner(task = task, rules = rules) for hit, address in scanner.scan(): hitdata = scanner.address_space.zread(address, 8) # Second hit from Yara rule is the 'FIRST_WI' string that we use to differentiate from Elite implants # This is a wide string, so the second character is a '\x00' - the first hit is on the watermark that we want to use. if hitdata[1] != "\x00": scouts.append({"watermark":hitdata, "confidence":4, "pid":str(task.UniqueProcessId), "task":task, "process_name":str(task.ImageFileName), "address_space":scanner.address_space, "address":address, "implant_type":"Scout", "threat_actor":hit.rule.split('__')[2]}) return scouts
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.") for task in self.filter_tasks(tasks.pslist(addr_space)): task_space = task.get_process_address_space() # We must have a process AS if not task_space: continue winsock = None # Locate the winsock DLL for mod in task.get_load_modules(): if str(mod.BaseDllName or "").lower() == "ws2_32.dll": winsock = mod break if not winsock: continue # Resolve the closesocket API closesocket = winsock.getprocaddress("closesocket") if not closesocket: continue for vad, process_space in task.get_vads(vad_filter=self._zeus_filter): if obj.Object("_IMAGE_DOS_HEADER", offset=vad.Start, vm=process_space).e_magic != 0x5A4D: continue data = process_space.zread(vad.Start, vad.Length) scanner = impscan.ImpScan(self._config).call_scan calls = list(scanner(task_space, vad.Start, data)) for (_, iat_loc, call_dest) in calls: if call_dest != closesocket: continue # Read the DWORD directly after closesocket struct_base = obj.Object("Pointer", offset=iat_loc + 4, vm=task_space) # To be valid, it must point within the vad segment if struct_base < vad.Start or struct_base > (vad.Start + vad.End): continue # Grab the key data key = task_space.read(struct_base + 0x2A, RC4_KEYSIZE) # Greg's sanity check if len(key) != RC4_KEYSIZE or key[-2:] != "\x00\x00": continue yield task, struct_base, key
def calculate(self): kernel_space = utils.load_as(self._config) ## Select the tags to scan for. Always find visited URLs, ## but make freed and redirected records optional. tags = ["URL "] if self._config.LEAK: tags.append("LEAK") if self._config.REDR: tags.append("REDR") ## Define the record type based on the tag tag_records = { "URL " : "_URL_RECORD", "LEAK" : "_URL_RECORD", "REDR" : "_REDR_RECORD"} ## Enumerate processes based on the --pid and --offset for proc in self.filter_tasks(tasks.pslist(kernel_space)): ## Acquire a process specific AS ps_as = proc.get_process_address_space() for hit in proc.search_process_memory(tags): ## Get a preview of the data to see what tag was detected tag = ps_as.read(hit, 4) ## Create the appropriate object type based on the tag record = obj.Object(tag_records[tag], offset = hit, vm = ps_as) if record.is_valid(): yield proc, record
def calculate(self): kernel_space = utils.load_as(self._config) #This should be collected from online eks r0ar.net/memory/whitelist.txt #check for efficiency stuff - process slow atm whitelist = open('/Users/Lunde/Documents/mrwhite.txt').read().splitlines() matchlist = [] for line in whitelist: for process in tasks.pslist(kernel_space): if(str(line) == str(process.ImageFileName)): matchlist.append(line) matchlist = sorted(set(matchlist)) for process in tasks.pslist(kernel_space): if(str(process.ImageFileName) not in matchlist): yield process
def calculate(self): addr_space = utils.load_as(self._config) self.mscarvecontrol = MsCarveDisplayControl(self.config) self.mscarvecontrol.runconfig() for proc in tasks.pslist(addr_space): #process_space = proc.get_process_address_space() yield proc
def calculate(self): """Calculate and carry out any processing that may take time upon the image""" # Load the address space addr_space = utils.load_as(self._config) #find some browsery processes for proc in tasks.pslist(addr_space): if str(proc.ImageFileName).lower() in("iexplore.exe","firefox","firefox.exe","chrome","chrome.exe"): yield proc
def calculate(self): if not has_yara: debug.error("You must install yara") 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.") rules = yara.compile(sources=self.signatures) for task in self.filter_tasks(tasks.pslist(addr_space)): task_space = task.get_process_address_space() # We must have a process AS if not task_space: continue for vad, process_space in task.get_vads(): if obj.Object("_IMAGE_DOS_HEADER", offset=vad.Start, vm=process_space).e_magic != 0x5A4D: continue data = process_space.zread(vad.Start, vad.Length) # check for the signature with YARA, both hits must be present matches = rules.match(data=data) if len(matches) < 2: continue try: dos_header = obj.Object("_IMAGE_DOS_HEADER", offset=vad.Start, vm=task_space) nt_header = dos_header.get_nt_header() except (ValueError, exceptions.SanityCheckException): continue # There must be more than 2 sections if nt_header.FileHeader.NumberOfSections < 2: continue # Get the last PE section's data sections = list(nt_header.get_sections(False)) last_sec = sections[-1] last_sec_data = task_space.zread((last_sec.VirtualAddress + vad.Start), last_sec.Misc.VirtualSize) success = self.check_matches(task_space, vad, matches, last_sec_data) if not success: continue success = self.scan_key(task_space) if not success: continue yield task, vad, self.params
def calculate(self): """Calculate and carry out any processing that may take time upon the image""" # Load the address space addr_space = utils.load_as(self._config) # Call a subfunction so that it can be used by other plugins for proc in tasks.pslist(addr_space): if str(proc.ImageFileName).lower() in("iexplore.exe","firefox","firefox.exe","chrome","chrome.exe"): yield proc
def calculate(self): ## Load a new address space addr_space = utils.load_as(self._config) return dict( (int(task.UniqueProcessId), task) for task in tasks.pslist(addr_space) )
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=rat_9002_sig) for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = malfind.VadYaraScanner(task = task, rules = rules) for hit, address in scanner.scan(): yield (task, address, hit, scanner.address_space.zread(address, 1024))
def calculate(self): """Calculate and carry out any processing that may take time upon the image""" # Load the address space addr_space = utils.load_as(self._config) print("Bulk Exractor Starting") print("Note: data is extracted using regex on a dirty dump of memory and may miss a minor percentage of edge cases.") # Call a subfunction so that it can be used by other plugins for proc in tasks.pslist(addr_space): if self._config.PID == proc.UniqueProcessId: yield proc
def get_ghost_process(self, magic, mal_process, add_space): rule = "rule Gh0strat_process {strings: $any_variant = " + '"' + magic + '"'+ " condition: $any_variant}" ghost_proc_sig = {'ghostrat_process' : rule } rules = yara.compile(sources=ghost_proc_sig) for task in self.filter_tasks(tasks.pslist(add_space)): scanner = malfind.VadYaraScanner(task = task, rules = rules) for hit, address in scanner.scan(): if task.obj_name == "_EPROCESS": process = str(task.ImageFileName) pid = task.UniqueProcessId mal_process[process] = pid
def calculate(self): addr_space = utils.load_as(self._config) # we currently don't use this on x64 because for some reason the # x64 version actually doesn't create a DisplayVersion value memory_model = addr_space.profile.metadata.get('memory_model') if memory_model == '32bit': regapi = registryapi.RegistryApi(self._config) regapi.reset_current() regapi.set_current(hive_name = "software") x86key = "Microsoft\\Windows\\CurrentVersion\\Uninstall" x64key = "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall" for subkey in regapi.reg_get_all_subkeys(None, key = x86key): if str(subkey.Name) == "TrueCrypt": subpath = x86key + "\\" + subkey.Name version = regapi.reg_get_value("software", key = subpath, value = "DisplayVersion") if version: yield "Registry Version", "{0} Version {1}".format( str(subkey.Name), version) scanner = TrueCryptPassphrase(self._config) for offset, passphrase in scanner.calculate(): yield "Password", "{0} at offset {1:#x}".format( passphrase, offset) for proc in tasks.pslist(addr_space): if str(proc.ImageFileName).lower() == "truecrypt.exe": yield "Process", "{0} at {1:#x} pid {2}".format( proc.ImageFileName, proc.obj_offset, proc.UniqueProcessId) scanner = svcscan.SvcScan(self._config) for service in scanner.calculate(): name = str(service.ServiceName.dereference()) if name == "truecrypt": yield "Service", "{0} state {1}".format( name, service.State) for mod in modules.lsmod(addr_space): basename = str(mod.BaseDllName or '').lower() fullname = str(mod.FullDllName or '').lower() if (basename.endswith("truecrypt.sys") or fullname.endswith("truecrypt.sys")): yield "Kernel Module", "{0} at {1:#x} - {2:#x}".format( mod.BaseDllName, mod.DllBase, mod.DllBase + mod.SizeOfImage) scanner = filescan.SymLinkScan(self._config) for symlink in scanner.calculate(): object_header = symlink.get_object_header() if "TrueCryptVolume" in str(symlink.LinkTarget or ''): yield "Symbolic Link", "{0} -> {1} mounted {2}".format( str(object_header.NameInfo.Name or ''), str(symlink.LinkTarget or ''), str(symlink.CreationTime or '')) scanner = filescan.FileScan(self._config) for fileobj in scanner.calculate(): filename = str(fileobj.file_name_with_device() or '') if "TrueCryptVolume" in filename: yield "File Object", "{0} at {1:#x}".format( filename, fileobj.obj_offset) scanner = filescan.DriverScan(self._config) for driver in scanner.calculate(): object_header = driver.get_object_header() driverext = driver.DriverExtension drivername = str(driver.DriverName or '') servicekey = str(driverext.ServiceKeyName or '') if (drivername.endswith("truecrypt") or servicekey.endswith("truecrypt")): yield "Driver", "{0} at {1:#x} range {2:#x} - {3:#x}".format( drivername, driver.obj_offset, driver.DriverStart, driver.DriverStart + driver.DriverSize) for device in driver.devices(): header = device.get_object_header() devname = str(header.NameInfo.Name or '') type = devicetree.DEVICE_CODES.get(device.DeviceType.v()) yield "Device", "{0} at {1:#x} type {2}".format( devname or "<HIDDEN>", device.obj_offset, type or "UNKNOWN") if type == "FILE_DEVICE_DISK": data = addr_space.read(device.DeviceExtension, 2000) ## the file-hosted container path. no other fields in ## the struct are character based, so we should not ## hit false positives on this scan. offset = data.find("\\\x00?\x00?\x00\\\x00") if offset == -1: container = "<HIDDEN>" else: container = obj.Object("String", length = 255, offset = device.DeviceExtension + offset, encoding = "utf16", vm = addr_space) yield "Container", "Path: {0}".format(container)
def calculate(self): if not has_yara: debug.error("Yara must be installed for this plugin") addr_space = utils.load_as(self._config) os, memory_model = self.is_valid_profile(addr_space.profile) if not os: debug.error("This command does not support the selected profile.") rules = yara.compile(sources=tscookie_sig) for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = malfind.VadYaraScanner(task=task, rules=rules) for hit, address in scanner.scan(): vad_base_addr, end = self.get_vad_base(task, address) proc_addr_space = task.get_process_address_space() data = proc_addr_space.zread(vad_base_addr, end - vad_base_addr) config_data = [] dll_index = data.find(MZ_HEADER) if dll_index: dll_data = data[dll_index:] dll = pefile.PE(data=dll_data) else: continue if "TSCookie" in str(hit): d = 2 else: d = 0 for pattern in CONFIG_PATTERNS: mc = re.search(pattern, dll_data) if mc: try: (config_rva, ) = unpack( "=I", dll_data[mc.start() + d + 1:mc.start() + d + 5]) config_addr = dll.get_physical_by_rva( config_rva - dll.NT_HEADERS.OPTIONAL_HEADER.ImageBase) break except: print("[!] Not found config data.\n") config_size = 0 enc = [] while not ( dll_data[config_addr + config_size] == "\x00" and dll_data[config_addr + config_size + 1] == "\x00" and dll_data[config_addr + config_size + 2] == "\x00"): enc.append(dll_data[config_addr + config_size]) config_size += 1 enc_config_data = "".join(enc) if config_size == 0x8D4: rc4key_length = 4 else: rc4key_length = 0x80 try: enc_config = enc_config_data[rc4key_length:] rc4key = enc_config_data[:rc4key_length] config = self.rc4(enc_config, rc4key) if len(config) > 0: if "TSCookie" in str(hit): config_data.append(self.parse_config(config)) else: config_data.append( self.parse_loader_config(config)) except: print("[!] Not found config data.\n") yield task, vad_base_addr, end, hit, memory_model, config_data break
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 if self._config.PID: pidlist = [int(p) for p in self._config.PID.split(',')] else: pidlist = [] # 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()) # Gather processes all_tasks = list(tasks.pslist(addr_space)) # 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 self.filter_tasks(all_tasks): 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 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): 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): self.DumpFolder = (self._config.DumpFolder or None) self.logg = open(self._config.FailFile, mode="w+", buffering=8192) self.logg.write( "On Windows, use \"type [Filename]\" for best results (Win10) {} JIT hash log file\n" .format(fg("cornflower_blue"))) # get the null hash (at runtime in case a different hash is used etc..) null_page = bytearray(4096) self.null_hash = self.HashPage(null_page) addr_space = utils.load_as(self._config) if isinstance( addr_space, volatility.plugins.addrspaces.intel.IA32PagedMemory ) and not isinstance( addr_space, volatility.plugins.addrspaces.intel.IA32PagedMemoryPae): raise "The memory model of this memory dump dates from the 1990's and does not support execute protection." outputJobs = None taski = 0 taskCnt = 0 tasklist = tasks.pslist(addr_space) for _ in tasks.pslist(addr_space): taskCnt += 1 print("{}{}{} [{}]{}".format( fg("chartreuse_1"), "pdb2json JIT PageHash calls under way... endpoint ", fg("hot_pink_1b"), self.JITHashServer, fg("sky_blue_1"), attrs=["bold"])) bformat = "{elapsed}{l_bar}{postfix}{bar}" self.TotalBar = tqdm( desc="{}TotalProgress".format(fg("cornflower_blue"), total=taskCnt, position=0, mininterval=0.5, bar_format=bformat)) # The timer is reset here since were not counting the coldstartup time self.StartTime = time.time() for task in tasklist: taski += 1 proc_as = task.get_process_address_space() mods = [] # Volatility workaround as there is not a consistant interface I know of # to handle AS the same way for kernel & user if task.UniqueProcessId == 4: mods = list(modules.lsmod(addr_space)) proc_as = addr_space else: mods = list(task.get_load_modules()) TaskName = "[" + task.ImageFileName + "-" + str( task.UniqueProcessId) + "]" taskBar = tqdm(desc=TaskName, total=len(mods), position=1, leave=False, mininterval=0.5, bar_format=bformat) p = dict({ "Name": TaskName, "Task": task, "TaskBlockCount": 0, "ModContext": [], "bar": taskBar }) for mod in mods: #@ taskBar.set_postfix_str('{} modules'.format(len(mods), refresh=True) hashAddr = [] hashVal = [] for vpage, nx in self.mod_get_ptes(mod, proc_as): if (nx): continue data = proc_as.read(vpage, 4096) if data is None or data is self.null_hash: continue hashAddr.append(str(vpage)) hashVal.append(self.HashPage(data)) # these statements are yet another workaround for volatility # for some unknown reason these data structures have never been written into Volatility... # of course you can acquire the timestamp by reading the nt_header/fileheader/etc but that data is # significantly lower quality given that it can be modified at any time. The kernel data structure # remains valid unless the attacker kills the process etc... In any event (hah) since this value has never changed # I hard coded it here for simplicity. Perhaps I should enforce always using it, will circle back 360 on that.. :O timevalue = mod.TimeDateStamp #this should only work for kernel space modules if timevalue == 0 and task.UniqueProcessId == 4: timeLoc = self.to_int64(mod.v() + 0x9c) redInBytes = addr_space.read(timeLoc, 4) if redInBytes is not None and len(redInBytes) == 4: timevalue = unpack("<L", redInBytes)[0] req_hdr = { "ModuleName": str(mod.FullDllName or ''), "ImageSize": str(mod.SizeOfImage), "BaseAddress": str(mod.DllBase), "AllocationBase": str(mod.DllBase), "TimeDateStamp": str(int(timevalue)), "HdrHash": self.HashPage(proc_as.read(mod.DllBase, 4096)), "HashSet": [{ "Address": a, "Hash": h } for a, h in zip(hashAddr, hashVal)] } if req_hdr["ModuleName"] is '': self.logg.write( "{}{}{}: Unable to scan anonymous executable memory. {:#x} length: {:#x}{}.\n" .format(bg("black"), fg("yellow_2"), TaskName, mod.DllBase, mod.SizeOfImage, fg("cornflower_blue"))) filename = "{}/{}-{:#x}".format(self.DumpFolder, TaskName, mod.DllBase) open(filename, 'w').close() for vpage in range(mod.DllBase, mod.DllBase + mod.SizeOfImage, 4096): data = proc_as.read(vpage, 4096) if self.DumpFolder is not None and data is not None: with open(filename, 'ab') as block: block.write(bytearray(data)) else: LocalMod = dict({ "Module": mod, "Ctx": p, "ModBlockCount": hashAddr.count, "json": req_hdr, "AS": addr_space }) p["TaskBlockCount"] = p["TaskBlockCount"] + len(hashAddr) taskBar.update(1) self.pool.spawn(self.pozt, LocalMod) #= [gevent.spawn(self.pozt, cx) for cx in p["ModContext"]] #gevent.wait(outputJobs) self.TotalBar.update(1)
def calculate(self): if not has_yara: debug.error("Yara must be installed for this plugin") addr_space = utils.load_as(self._config) os, memory_model = self.is_valid_profile(addr_space.profile) if not os: debug.error("This command does not support the selected profile.") rules = yara.compile(sources=datper_sig) for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = malfind.VadYaraScanner(task=task, rules=rules) for hit, address in scanner.scan(): vad_base_addr, end = self.get_vad_base(task, address) proc_addr_space = task.get_process_address_space() data = proc_addr_space.zread(vad_base_addr, end - vad_base_addr) config_data = [] try: pe = pefile.PE(data=data) except: sys.exit("[!] could not parse as a PE file") config_size = CONFSIZE if pe.FILE_HEADER.Machine in ( pefile.MACHINE_TYPE['IMAGE_FILE_MACHINE_IA64'], pefile.MACHINE_TYPE['IMAGE_FILE_MACHINE_AMD64']): enc = self.get_config_data_64(data, pe) else: enc = self.get_config_data_32(data, pe, vad_base_addr) dec = "" for key in RC4KEY: for rc4key_seed in range(0xFF): dec = self.custom_rc4(enc, key, rc4key_seed) dec = self.decrypt(dec) for dline in config_delimiter: if dline in dec: break else: continue break else: continue break if dec == "": dec = self.decrypt(enc) for dline in config_delimiter: if dline in dec: key = "NULL" rc4key_seed = "NULL" break p_data = OrderedDict() if dec != "": p_data["RC4 key"] = key p_data["RC4 Sbox seed"] = rc4key_seed p_data["Config delimiter"] = dline idx = 0 for e in (dec.split(dline)): try: p_data[idx_list[idx]] = e except: p_data["Unknown " + str(idx)] = e idx += 1 else: outfd.write("[!] failed to decrypt\n") config_data.append(p_data) yield task, vad_base_addr, end, hit, memory_model, config_data break
def calculate(self): space = utils.load_as(self._config) # enumerate system threads (0x00000010 = PS_CROSS_THREAD_FLAGS_SYSTEM) system_threads = [ t for t in modscan.ThrdScan(self._config).calculate() if t.CrossThreadFlags & 0x00000010 ] # find and dump the malicious kernel driver for item in filescan.DriverScan(self._config).calculate(): # unpack the parameters (object, driver, extension, object_name) = item # looking for unnamed driver objects if driver.DriverName.Length != 0: continue # the first and only device should be ACPI#PNP[...] device = obj.Object("_DEVICE_OBJECT", offset=driver.DeviceObject, vm=space) # get the device's object header object = obj.Object("_OBJECT_HEADER", \ offset = device.obj_offset - \ device.obj_vm.profile.get_obj_offset("_OBJECT_HEADER", "Body"), \ vm = space) object.kas = space device_name = object.get_object_name() # did we find zeroaccess? if not str(device_name).startswith("ACPI#PNP"): continue sys.stdout.write("DriverObject: {0:#x}\n".format( device.DriverObject)) sys.stdout.write(" DriverStart: {0:#x}\n".format( driver.DriverStart)) sys.stdout.write(" DriverSize: {0:#x}\n".format( driver.DriverSize)) sys.stdout.write("DeviceObject: {0:#x} {1}\n".format( device.obj_offset, device_name)) # dump the driver file_name = "Driver.{0:#x}.sys".format(driver.DriverStart) self.dump_pe(space, driver.DriverStart, file_name) # now what we know the memory range, look for bad threads for thread in system_threads: if thread.StartAddress > driver.DriverStart and \ thread.StartAddress < driver.DriverStart + driver.DriverSize: sys.stdout.write("Bad Thread: {0:#x} Tid {1}\n".format(\ thread.obj_offset, thread.Cid.UniqueThread)) # now find and dump the fake usermode ADS process for proc in tasks.pslist(space): for dll in proc.Peb.Ldr.InLoadOrderModuleList.list_of_type(\ "_LDR_DATA_TABLE_ENTRY", "InLoadOrderLinks"): # look for the ADS name if str(dll.BaseDllName).find(":") != -1: sys.stdout.write("Fake ADS EXE: {0} pid {1} base {2:#x}\n".format(\ proc.ImageFileName, proc.UniqueProcessId, dll.DllBase)) file_name = "Dll.{0:#x}.{1:#x}.dll".format( proc.obj_offset, dll.DllBase) self.dump_pe(proc.get_process_address_space(), dll.DllBase, file_name)
def find_lsass(self): addr_space = utils.load_as(self._config) for task in tasks.pslist(addr_space): if str(task.ImageFileName) == 'lsass.exe': return task
class ModDump(procdump.ProcDump): """Dump a kernel driver to an executable file sample""" def __init__(self, config, *args, **kwargs): procdump.ProcDump.__init__(self, config, *args, **kwargs) config.remove_option("PID") config.remove_option("OFFSET") config.add_option('REGEX', short_option='r', help='Dump modules matching REGEX', action='store', type='string') config.add_option('IGNORE-CASE', short_option='i', help='Ignore case in pattern match', action='store_true', default=False) config.add_option('BASE', short_option='b', default=None, help='Dump driver with BASE address (in hex)', action='store', type='int') @cache.CacheDecorator( lambda self: "tests/moddump/regex={0}/ignore-case={1}/base={2}".format( self._config.REGEX, self._config.IGNORE_CASE, self._config.BASE)) def calculate(self): if self._config.DUMP_DIR == None: debug.error("Please specify a dump directory (--dump-dir)") if not os.path.isdir(self._config.DUMP_DIR): debug.error(self._config.DUMP_DIR + " is not a directory") addr_space = utils.load_as(self._config) if self._config.REGEX: try: if self._config.IGNORE_CASE: mod_re = re.compile(self._config.REGEX, re.I) else: mod_re = re.compile(self._config.REGEX) except re.error, e: debug.error('Error parsing regular expression: {0}'.format(e)) mods = dict( (mod.DllBase.v(), mod) for mod in modules.lsmod(addr_space)) # We need the process list to find spaces for some drivers. Enumerate them here # instead of inside the find_space function, so we only have to do it once. procs = list(tasks.pslist(addr_space)) if self._config.BASE: if mods.has_key(self._config.BASE): mod_name = mods[self._config.BASE].BaseDllName else: mod_name = "UNKNOWN" yield addr_space, procs, int(self._config.BASE), mod_name else: for mod in mods.values(): if self._config.REGEX: if not mod_re.search(str(mod.FullDllName or '')) and not mod_re.search( str(mod.BaseDllName or '')): continue yield addr_space, procs, mod.DllBase.v(), mod.BaseDllName
def update_vads(self): ''' Call volatility to obtain VADS. ''' if self.unpickled: return import volatility.obj as obj import volatility.win32.tasks as tasks import volatility.plugins.vadinfo as vadinfo from utils import get_addr_space addr_space = get_addr_space(self.get_pgd()) eprocs = [ t for t in tasks.pslist(addr_space) if t.UniqueProcessId == self.pid ] for task in eprocs: heaps = task.Peb.ProcessHeaps.dereference() modules = [mod.DllBase for mod in task.get_load_modules()] stacks = [] for thread in task.ThreadListHead.list_of_type( "_ETHREAD", "ThreadListEntry"): teb = obj.Object("_TEB", offset=thread.Tcb.Teb, vm=task.get_process_address_space()) if teb: stacks.append(teb.NtTib.StackBase) for vad in task.VadRoot.traverse(): if vad is not None: vad_type = "" if vad.Start in heaps: # Heaps vad_type = "H" elif vad.Start in modules: # Module vad_type = "M" elif vad.Start in stacks: # Stacks vad_type = "S" else: vad_type = "-" try: protection = vadinfo.PROTECT_FLAGS.get( vad.VadFlags.Protection.v(), "") except Exception: traceback.print_exc() fileNameWithDevice = "" try: control_area = vad.ControlArea # even if the ControlArea is not NULL, it is only meaningful # for shared (non private) memory sections. if vad.VadFlags.PrivateMemory != 1 and control_area: if control_area: file_object = vad.FileObject if file_object: fileNameWithDevice = file_object.file_name_with_device( ) except AttributeError: pass try: new_vad = VADRegion(vad.Start, (vad.End - vad.Start), self, fileNameWithDevice, str(vad.Tag), vad_type, (vad.VadFlags.PrivateMemory == 1), protection) except Exception: traceback.print_exc() if new_vad not in self.vads: self.vads.append(new_vad)
def dump(pgd_list, pyrebox_print, path="/tmp/unpacker_results"): ''' Dump the process, modules, vads..., given a list of process address spaces and a path. ''' import volatility.constants as constants import volatility.exceptions as exceptions import volatility.obj as obj import volatility.win32.tasks as tasks from utils import get_addr_space import api # Delete contents, and create directory under path if os.path.isdir(path): shutil.rmtree(path) os.mkdir(path) try: # Get volatility address space addr_space = get_addr_space() # Get list of processes (tasks) from volatility eprocs = [ t for t in tasks.pslist(addr_space) if t.Pcb.DirectoryTableBase.v() in pgd_list ] # For every selected task for task in eprocs: # Code adapted from procdump (volatility) task_space = task.get_process_address_space() if task_space is None: pyrebox_print("Error: Cannot acquire process AS") return elif task.Peb is None: # we must use m() here, because any other attempt to # reference task.Peb will try to instantiate the _PEB pyrebox_print( "Error: PEB at {0:#x} is unavailable (possibly due to paging)" .format(task.m('Peb'))) return elif task_space.vtop(task.Peb.ImageBaseAddress) is None: pyrebox_print( "Error: ImageBaseAddress at {0:#x} is unavailable" + "(possibly due to paging)".format( task.Peb.ImageBaseAddress)) return else: dump_file = os.path.join( path, "PID_%x.executable.ex_" % (task.UniqueProcessId)) of = open(dump_file, 'wb') pe_file = obj.Object("_IMAGE_DOS_HEADER", offset=task.Peb.ImageBaseAddress, vm=task_space) try: for offset, code in pe_file.get_image(unsafe=True, memory=False, fix=True): of.seek(offset) of.write(code) except ValueError, ve: pyrebox_print("Error: {0}".format(ve)) return except exceptions.SanityCheckException, ve: pyrebox_print("Error: {0} Try -u/--unsafe".format(ve)) return
def calculate(self): if not has_distorm: debug.error("You must install distorm3") addr_space = utils.load_as(self._config) all_mods = [] if self._config.OFFSET != None: all_tasks = [ taskmods.DllList.virtual_process_from_physical_offset( addr_space, self._config.OFFSET) ] else: all_tasks = list(tasks.pslist(addr_space)) all_mods = list(modules.lsmod(addr_space)) # Operate in kernel mode if pid is not supplied if not self._config.PID and not self._config.OFFSET: if not self._config.BASE: debug.error("You must specify --BASE") base_address = self._config.BASE size_to_read = self._config.SIZE # Get the size from the module list if its not supplied if not size_to_read: for module in all_mods: if module.DllBase == base_address: size_to_read = module.SizeOfImage break # Alternately, try the size from the PE header if not size_to_read: pefile = obj.Object("_IMAGE_DOS_HEADER", offset=base_address, vm=addr_space) try: nt_header = pefile.get_nt_header() size_to_read = nt_header.OptionalHeader.SizeOfImage except ValueError: pass if not size_to_read: debug.error("You must specify --SIZE") kernel_space = tasks.find_space(addr_space, all_tasks, base_address) if not kernel_space: debug.error("Cannot read supplied address") data = kernel_space.zread(base_address, size_to_read) apis = self.enum_apis(all_mods) addr_space = kernel_space is_wow64 = False else: # In process mode, we find the process by PID task = None for atask in all_tasks: if (self._config.OFFSET or atask.UniqueProcessId == self._config.PID): task = atask break if not task: debug.error("You must supply an active PID") task_space = task.get_process_address_space() if not task_space: debug.error("Cannot acquire process AS") all_mods = list(task.get_load_modules()) # PEB is paged or no DLLs loaded if not all_mods: debug.error("Cannot load DLLs in process AS") # If an address is supplied with a size, try to get # the size from the vad node. If neither are supplied, # assume we should carve the main process executable. if self._config.BASE: base_address = self._config.BASE size_to_read = self._config.SIZE if not size_to_read: for vad in task.VadRoot.traverse(): if (base_address >= vad.Start and base_address <= vad.End): size_to_read = vad.Length if not size_to_read: debug.error("You must specify --SIZE") else: # Its OK to blindly take the 0th element because the # executable is always the first module to load. base_address = all_mods[0].DllBase size_to_read = all_mods[0].SizeOfImage is_wow64 = task.IsWow64 data = task_space.zread(base_address, size_to_read) apis = self.enum_apis(all_mods) addr_space = task_space # This is a dictionary of confirmed API calls. calls_imported = dict((iat, call) for ( _, iat, call) in self.call_scan(addr_space, base_address, data, is_wow64) if call in apis) # Scan forward self._vicinity_scan( addr_space, calls_imported, apis, base_address, len(data), is_wow64, forward=True, ) # Scan reverse self._vicinity_scan( addr_space, calls_imported, apis, base_address, len(data), is_wow64, forward=False, ) for iat, call in sorted(calls_imported.items()): yield iat, call, apis[call][0], apis[call][1]
def calculate(self): addr_space = malware.get_malware_space(self._config) addr_space.profile.add_types(zeus_types) RC4_KEYSIZE = 0x102 # cycle the processes for p in self.filter_tasks(tasks.pslist(addr_space)): # get the process address space ps_ad = p.get_process_address_space() if ps_ad == None: continue rules = yara.compile(sources=zeus_key_sigs) # traverse the VAD for vad in p.VadRoot.traverse(): if vad == None: continue # find the start and end range ## for Volatility 2.0 use the following start = vad.StartingVpn << 12 end = ((vad.EndingVpn + 1) << 12) - 1 data = malware.get_vad_data(ps_ad, start, end) ## For Volatility >= 2.1 use the following #start = vad.get_start() #end = vad.get_end() #data = vad.get_data() # last check for PE headers at the base if data[0:2] != 'MZ': continue # check for the signature with YARA, both hits must be present matches = rules.match(data=data) if len(matches) != 2: continue # get the NT header dos_header = obj.Object("_IMAGE_DOS_HEADER", start, ps_ad) nt_header = dos_header.get_nt_header() # there must be more than 2 sections if nt_header.FileHeader.NumberOfSections < 2: continue # get the last PE section's data sections = list(nt_header.get_sections(unsafe=False)) last_sec = sections[-1] last_sec_data = ps_ad.read((last_sec.VirtualAddress + start), last_sec.Misc.VirtualSize) # contains C2 URL, RC4 key for decoding local.ds and the magic buffer decoded_config = '' # contains hw lock info, the user.ds RC4 key, and XOR key encoded_magic = '' for match in matches: sigaddr = (match.strings[0][0] + start) debug.debug('Found {0} at {1:#x}'.format( match.rule, sigaddr)) if match.rule == 'z1': encoded_config = ps_ad.read( obj.Object('unsigned long', offset=sigaddr + 8, vm=ps_ad), obj.Object('unsigned long', offset=sigaddr + 2, vm=ps_ad)) decoded_config = self.decode_config( encoded_config, last_sec_data) elif match.rule == 'z2': config_ptr = obj.Object('unsigned long', offset=sigaddr + 26, vm=ps_ad) config_ptr = obj.Object('unsigned long', offset=config_ptr, vm=ps_ad) encoded_config = ps_ad.read(config_ptr, 0x3c8) decoded_config = self.rc4( self.rc4_init(encoded_config), last_sec_data[2:]) elif match.rule == 'z5': encoded_config = ps_ad.read( obj.Object('unsigned long', offset=sigaddr + 8, vm=ps_ad), obj.Object('unsigned long', offset=sigaddr + 2, vm=ps_ad)) decoded_config = self.decode_config( encoded_config, last_sec_data) elif match.rule == 'z3': encoded_magic = ps_ad.read( obj.Object('unsigned long', offset=sigaddr + 30, vm=ps_ad), addr_space.profile.get_obj_size('_ZEUS_MAGIC')) elif match.rule == 'z4': encoded_magic = ps_ad.read( obj.Object('unsigned long', offset=sigaddr + 31, vm=ps_ad), addr_space.profile.get_obj_size('_ZEUS_MAGIC')) if not decoded_config or not encoded_magic: continue debug.debug("encoded_config:\n{0}\n".format( self.get_hex(encoded_config))) debug.debug("decoded_config:\n{0}\n".format( self.get_hex(decoded_config))) debug.debug("encoded_magic:\n{0}\n".format( self.get_hex(encoded_magic))) offset = 0 decoded_magic = '' config_key = '' found = False while offset < len(decoded_config) - RC4_KEYSIZE: config_key = decoded_config[offset:offset + RC4_KEYSIZE] decoded_magic = self.rc4(config_key, encoded_magic) # when the first four bytes of the decoded magic buffer equal the size # of the magic buffer, then we've found a winning RC4 key (struct_size, ) = struct.unpack("=I", decoded_magic[0:4]) if struct_size == addr_space.profile.get_obj_size( '_ZEUS_MAGIC'): found = True break offset += 1 if not found: debug.debug('Error, cannot decode magic') continue debug.debug("decoded_magic:\n{0}\n".format( self.get_hex(decoded_magic))) debug.debug("config_key:\n{0}\n".format( self.get_hex(config_key))) # grab the URL from the decoded buffer url = decoded_config[decoded_config.find("http"):] url = url[:url.find('\x00')] # report what we've found rc4_offset = addr_space.profile.get_obj_offset( '_ZEUS_MAGIC', 'rc4key') creds_key = decoded_magic[rc4_offset:rc4_offset + RC4_KEYSIZE] yield p, start, url, config_key, creds_key, decoded_config, decoded_magic
def get_vads(pgd): ''' Get list of VAD regions using volatility ''' import volatility.obj as obj import volatility.win32.tasks as tasks import volatility.plugins.vadinfo as vadinfo from utils import get_addr_space # Get volatility address space using the function in utils addr_space = get_addr_space(pgd) # Get list of Task objects using volatility (EPROCESS executive objects) eprocs = [ t for t in tasks.pslist(addr_space) if t.Pcb.DirectoryTableBase.v() == pgd ] # Traverse the list of selected EPROCESSes for task in eprocs: # Get heap base for every process HEAP heaps = task.Peb.ProcessHeaps.dereference() # Get base for every DLL modules = [mod.DllBase for mod in task.get_load_modules()] # Get Stack base for every THREAD stacks = [] for thread in task.ThreadListHead.list_of_type("_ETHREAD", "ThreadListEntry"): teb = obj.Object("_TEB", offset=thread.Tcb.Teb, vm=task.get_process_address_space()) if teb: stacks.append(teb.NtTib.StackBase) # Traverse VAD tree for vad in task.VadRoot.traverse(): if vad is not None: # Determine if the VAD is a HEAP, STACK, or MODULE vad_type = "" if vad.Start in heaps: # Heaps vad_type = "H" elif vad.Start in modules: # Module vad_type = "M" elif vad.Start in stacks: # Stacks vad_type = "S" else: vad_type = "-" # Get protection flags try: protection = vadinfo.PROTECT_FLAGS.get( vad.VadFlags.Protection.v(), "") except Exception: traceback.print_exc() # Get mapped file file_name = "" try: control_area = vad.ControlArea # even if the ControlArea is not NULL, it is only meaningful # for shared (non private) memory sections. if vad.VadFlags.PrivateMemory != 1 and control_area: if control_area: file_object = vad.FileObject if file_object: file_name = file_object.file_name_with_device() except AttributeError: pass # Return VAD regions yield VADRegion(vad.Start, vad.End, file_name, str(vad.Tag), vad_type, (vad.VadFlags.PrivateMemory == 1), protection)
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))
def _scan_process_memory(self, addr_space, rules): 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 dll_dump(self, pids): """ Generate dump files containing all modules loaded by a process @param pids: pid list to dump @returns a list of DLLObject sorted by (pid, mod.BaseAddress) """ if self._config.MODULE_NAME: dlls_expression = '{0}$'.format( self._config.MODULE_NAME.replace(',', '$|')) else: dlls_expression = None re_case_type = 0 if self._config.SEARCH_CASE: re_case_type = re.IGNORECASE if self._config.DERELOCATION or self._config.GUIDED_DERELOCATION: # acquiring all dlls and exes that were opened in system acquire_sys_file_handlers(self, conf) if self._config.LOG_MEMORY_PAGES: if not self._config.SECTION or self._config.SECTION == 'all' or 'PE' in self._config.SECTION: logfile = open(self._config.LOG_MEMORY_PAGES, "w") else: debug.warning('Warning: PE is not being dumped') for task in tasks.pslist(self.addr_space): if task.UniqueProcessId in pids: task_space = task.get_process_address_space() mods = dict( (mod.DllBase.v(), mod) for mod in task.get_load_modules()) for mod in mods.values(): mod_base = mod.DllBase.v() mod_end = mod_base + mod.SizeOfImage if task_space.is_valid_address(mod_base): mod_name = mod.BaseDllName if dlls_expression: if not re.match(dlls_expression, str(mod_name), flags=re_case_type): continue valid_pages = [ task_space.vtop(mod.DllBase + i) for i in range(0, mod.SizeOfImage, PAGE_SIZE) ] start = time.time() pe = PeMemory( task_space.zread(mod.DllBase, mod.SizeOfImage), mod.DllBase, valid_pages) end = time.time() pe_memory_time = end - start pe.__modul_name__ = mod_name if self._config.LIST_SECTIONS: yield PESection(mod_name, self.get_pe_sections(pe), task.UniqueProcessId, mod_base) else: reloc = None pre_processing_time = None if self._config.DERELOCATION or self._config.GUIDED_DERELOCATION: # Retrieving reloc for module for text section reloc = get_reloc_section(self, mod) if reloc: start = time.time() guided_derelocation(pe, reloc) end = time.time() pre_processing_time = end - start else: debug.warning( 'Warning: {0}\'s reloc section cannot be found.' .format(mod_name)) if self._config.GUIDED_DERELOCATION: continue if (self._config.DERELOCATION and not reloc ) or self._config.LINEAR_SWEEP_DERELOCATION: start = time.time() linear_sweep_derelocation(pe) end = time.time() pre_processing_time = end - start # Generate one dump Object for every section/header specified # Set the list of sections that match with -S expression sections = self.process_section( task, self._config.SECTION, pe) for sec in sections: for engine in self.hash_engines: vinfo = obj.Object( "_IMAGE_DOS_HEADER", mod.DllBase, task_space).get_version_info() create_time = str( task.CreateTime ) if self._config.HUMAN_READABLE else int( task.CreateTime) yield DLLObject( task, sec.data, engine, mod_base, mod_end, mod_name, sec.Name, create_time, vinfo.FileInfo.file_version() if vinfo else '', vinfo.FileInfo.product_version() if vinfo else '', mod.FullDllName, time=self._config.TIME and not (self._config.COMPARE_HASH or self._config.COMPARE_FILE), offset=sec.VirtualAddress, size=sec.real_size, pe_memory_time='{0:.20f}'.format( pe_memory_time), pre_processing_time='{0:.20f}'.format( pre_processing_time) if pre_processing_time else None) dump_path = os.path.join( self._config.DUMP_DIR, '{0}-{1}-{2}-{3}-{4:x}.dmp'.format( self.get_exe_module( task).BaseDllName, task.UniqueProcessId, mod_name, re.sub( r'\x00', r'', re.sub(r'\/', r'.', sec.Name)), mod_base)) if self._config.DUMP_DIR: self.backup_file(dump_path, sec.data) if self._config.LOG_MEMORY_PAGES and sec.Name == 'PE': if not self._config.DUMP_DIR: debug.warning( 'Warning: Modules are not being dumped to file' ) logfile.write( '{},{},{},{},{}:{}\n'.format( self._config.optparse_opts. location[7:], dump_path, hashlib.md5( pe.__data__[0:PAGE_SIZE]). hexdigest(), vinfo.FileInfo.file_version(), len(valid_pages), ', '.join([ str(i) for i in range( 0, len(valid_pages)) if valid_pages[i] ]))) if 'logfile' in locals(): logfile.close()
def calculate(self): if not has_yara: debug.error("Yara must be installed for this plugin") if not has_crypto: debug.error("pycrypto must be installed for this plugin") if not has_pbkdf2: debug.error("pbkdf2 must be installed for this plugin") addr_space = utils.load_as(self._config) os, memory_model = self.is_valid_profile(addr_space.profile) if not os: debug.error("This command does not support the selected profile.") rules = yara.compile(sources=quasar_sig) for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = malfind.VadYaraScanner(task=task, rules=rules) for hit, address in scanner.scan(): vad_base_addr, end = self.get_vad_base(task, address) proc_addr_space = task.get_process_address_space() data = proc_addr_space.zread(vad_base_addr, end - vad_base_addr) config_data = [] offset = 0 for pattern in CONFIG_PATTERNS: mc = re.search(pattern, data) if mc: offset = mc.end() if ord(data[offset]) == 0x0: offset += 1 configs = [] if offset > 0: while 1: strings = [] string_len = ord(data[offset]) if ord(data[offset]) == 0x80 or ord( data[offset]) == 0x81: string_len = ord(data[offset + 1]) + ( (ord(data[offset]) - 0x80) * 256) offset += 1 offset += 1 for i in range(string_len): if data[offset + i] != "\x00": strings.append(data[offset + i]) configs.append("".join(strings)) offset = offset + string_len if ord(data[offset]) < 0x20: break config_data.append(self.parse_config(configs)) yield task, vad_base_addr, end, hit, memory_model, config_data break
def calculate(self): if not has_distorm: debug.error("You must install distorm3") addr_space = utils.load_as(self._config) all_tasks = list(tasks.pslist(addr_space)) all_mods = list(modules.lsmod(addr_space)) # Operate in kernel mode if pid is not supplied if not self._config.PID: if not self._config.BASE: debug.error("You must specify --BASE") base_address = self._config.BASE size_to_read = self._config.SIZE # Get the size from the module list if its not supplied if not size_to_read: for module in all_mods: if module.DllBase == base_address: size_to_read = module.SizeOfImage break if not size_to_read: debug.error("You must specify --SIZE") kernel_space = tasks.find_space(addr_space, all_tasks, base_address) if not kernel_space: debug.error("Cannot read supplied address") data = kernel_space.zread(base_address, size_to_read) apis = self.enum_apis(all_mods) addr_space = kernel_space else: # In process mode, we find the process by PID task = None for atask in all_tasks: if atask.UniqueProcessId == self._config.PID: task = atask break if not task: debug.error("You must supply an active PID") task_space = task.get_process_address_space() if not task_space: debug.error("Cannot acquire process AS") all_mods = list(task.get_load_modules()) # PEB is paged or no DLLs loaded if not all_mods: debug.error("Cannot load DLLs in process AS") # If an address is supplied with a size, try to get # the size from the vad node. If neither are supplied, # assume we should carve the main process executable. if self._config.BASE: base_address = self._config.BASE size_to_read = self._config.SIZE if not size_to_read: for vad in task.VadRoot.traverse(): if base_address >= vad.Start and base_address <= vad.End: size_to_read = vad.Length if not size_to_read: debug.error("You must specify --SIZE") else: # Its OK to blindly take the 0th element because the # executable is always the first module to load. base_address = all_mods[0].DllBase size_to_read = all_mods[0].SizeOfImage if not task_space.is_valid_address(base_address): debug.error("Address is not valid in process AS") data = task_space.zread(base_address, size_to_read) apis = self.enum_apis(all_mods) addr_space = task_space # This is a dictionary of confirmed API calls. calls_imported = dict( (iat, call) for (_, iat, call) in self.call_scan(addr_space, base_address, data) if call in apis ) # Scan forward self._vicinity_scan(addr_space, calls_imported, apis, base_address, len(data), forward = True) # Scan reverse self._vicinity_scan(addr_space, calls_imported, apis, base_address, len(data), forward = False) for iat, call in sorted(calls_imported.items()): yield iat, call, apis[call][0], apis[call][1]
def calculate(self): if not has_yara: debug.error("Yara must be installed for this plugin") addr_space = utils.load_as(self._config) os, memory_model = self.is_valid_profile(addr_space.profile) if not os: debug.error("This command does not support the selected profile.") rules = yara.compile(sources=remcos_sig) for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = malfind.VadYaraScanner(task=task, rules=rules) for hit, address in scanner.scan(): vad_base_addr, end = self.get_vad_base(task, address) proc_addr_space = task.get_process_address_space() data = proc_addr_space.zread(vad_base_addr, end - vad_base_addr) config_data = [] # resource PE search dll_index = data.rfind(MZ_HEADER) dll_data = data[dll_index:] try: pe = pefile.PE(data=dll_data) except: outfd.write("[!] Can't mapped PE.\n") continue rc_data = "" for idx in pe.DIRECTORY_ENTRY_RESOURCE.entries: for entry in idx.directory.entries: if str(entry.name) in "SETTINGS": try: data_rva = entry.directory.entries[ 0].data.struct.OffsetToData size = entry.directory.entries[ 0].data.struct.Size rc_data = dll_data[data_rva:data_rva + size] print("[*] Found SETTINGS resource.") except: debug.error("Faild to load SETTINGS resource.") if not len(rc_data): for pattern in RESOURCE_PATTERNS: mc = re.search(pattern, dll_data) if mc: try: config_end = mc.end() + 1 while dll_data[config_end:config_end + 2] != "\x00\x00": config_end += 1 rc_data = dll_data[mc.end():config_end - 1] except: debug.error("Remcos resource not found.") config_data.append(self.parse_config(rc_data)) yield task, vad_base_addr, end, hit, memory_model, config_data break
def calculate(self): if not has_yara: debug.error("Yara must be installed for this plugin") addr_space = utils.load_as(self._config) os, memory_model = self.is_valid_profile(addr_space.profile) if not os: debug.error("This command does not support the selected profile.") rules = yara.compile(sources=smokeloader_sig) for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = malfind.VadYaraScanner(task=task, rules=rules) for hit, address in scanner.scan(): vad_base_addr, end = self.get_vad_base(task, address) proc_addr_space = task.get_process_address_space() dll_data = proc_addr_space.zread(vad_base_addr, end - vad_base_addr) config_data = [] mz_magic = unpack_from("=2s", dll_data, 0x0)[0] nt_magic = unpack_from("<H", dll_data, 0x3c)[0] if mz_magic == "\x00\x00": dll_data = "\x4d\x5a" + dll_data[2:] dll_data = dll_data[:nt_magic] + "\x50\x45" + dll_data[ nt_magic + 2:] p_data = OrderedDict() url_base = [] for pattern in CONFIG_PATTERNS: mc = re.search(pattern, dll_data) if mc: offset = mc.end() + 1 while dll_data[offset] != "\x8B": offset += 1 config_rva = unpack( "=I", dll_data[offset + 3:offset + 7])[0] - vad_base_addr d = 0 while dll_data[config_rva + d:config_rva + d + 4] != "\x00\x00\x00\x00": url_base.append( unpack( "=I", dll_data[config_rva + d:config_rva + d + 4])[0]) d += 4 i = 1 for base in url_base: base -= vad_base_addr size = ord(dll_data[base]) key = unpack( "=I", dll_data[base + size + 1:base + size + 5])[0] enc_data = dll_data[base + 1:base + size + 1] url = self.decode(enc_data, key) p_data["Static URL " + str(i)] = url i += 1 for pattern in STRINGS_PATTERNS: mc = re.search(pattern, dll_data) if mc: offset = mc.start() + 2 config_rva = unpack( "=I", dll_data[offset:offset + 4])[0] - vad_base_addr key = dll_data[config_rva - 4:config_rva] enc = [] while dll_data[config_rva:config_rva + 2] != "\x00\x00": enc.append(dll_data[config_rva]) config_rva += 1 enc_strings = "".join(enc) x = 0 i = 1 while x < len(enc_strings): size = ord(enc_strings[x]) strings = self.rc4(enc_strings[x + 1:x + size + 1], key) x = x + size + 1 p_data["Encoded string " + str(i)] = strings i += 1 config_data.append(p_data) yield task, vad_base_addr, end, hit, memory_model, config_data break
def calculate(self): if not has_yara: debug.error("Yara must be installed for this plugin") addr_space = utils.load_as(self._config) os, memory_model = self.is_valid_profile(addr_space.profile) if not os: debug.error("This command does not support the selected profile.") rules = yara.compile(sources=redleaves_sig) for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = malfind.VadYaraScanner(task=task, rules=rules) for hit, address in scanner.scan(): vad_base_addr, end = self.get_vad_base(task, address) proc_addr_space = task.get_process_address_space() data = proc_addr_space.zread(vad_base_addr, end - vad_base_addr) config_data = [] c_pt = CONF_PATTERNS[str(hit)] if re.search(c_pt, data): m_conf = re.search(c_pt, data) else: continue offset_conf = m_conf.start() if "RedLeaves" in str(hit): config_size = 2100 offset_conf -= 1 while data[offset_conf] != "\xC7" and data[ offset_conf] != "\xBE" and data[ offset_conf] != "\xBF": offset_conf -= 1 if data[offset_conf] != "\xC7" and data[ offset_conf] != "\xBE" and data[ offset_conf] != "\xBF": continue if data[offset_conf] == "\xC7" and data[ offset_conf + 1] != "\x85" and data[offset_conf + 1] != "\x45": offset_conf -= 6 # get address if data[offset_conf] == "\xC7" and data[offset_conf + 1] != "\x85": (config_addr, ) = unpack( "=I", data[offset_conf + 3:offset_conf + 7]) elif data[offset_conf] == "\xC7" and data[offset_conf + 1] == "\x85": (config_addr, ) = unpack( "=I", data[offset_conf + 6:offset_conf + 10]) else: (config_addr, ) = unpack( "=I", data[offset_conf + 1:offset_conf + 5]) if config_addr < vad_base_addr: continue config_addr -= vad_base_addr config = data[config_addr:config_addr + config_size] if len(config) > 0: config_data.append( self.parse_config(config, config_size, config_addr)) if str(hit) in [ "Himawari", "Lavender", "Armadill", "zark20rk" ]: offset_conf += 6 if str(hit) in ["zark20rk"]: offset_conf += 6 config_size = 880 # get address (config_addr, ) = unpack("=I", data[offset_conf:offset_conf + 4]) if config_addr < vad_base_addr: continue config_addr -= vad_base_addr config = data[config_addr:config_addr + config_size] if len(config) > 0: config_data.append( self.parse_config_himawari(config, config_size, config_addr)) yield task, vad_base_addr, end, hit, memory_model, config_data break
def calculate(self): if not has_yara: debug.error("Yara must be installed for this plugin") addr_space = utils.load_as(self._config) os, memory_model = self.is_valid_profile(addr_space.profile) if not os: debug.error("This command does not support the selected profile.") rules = yara.compile(sources=formbook_sig) for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = malfind.VadYaraScanner(task=task, rules=rules) for hit, address in scanner.scan(): vad_base_addr, end = self.get_vad_base(task, address) proc_addr_space = task.get_process_address_space() data = proc_addr_space.zread(vad_base_addr, end - vad_base_addr) config_data = [] try: pe = pefile.PE(data=data) except: continue for pattern in CONFIG_PATTERNS: offset = re.search(pattern, data).start() offset += 6 key1_offset = unpack("=I", data[offset:offset + 4])[0] + offset + 11 key1 = data[key1_offset:key1_offset + (0x14 * 2)] offset += 23 key2_offset = unpack("=I", data[offset:offset + 4])[0] + offset + 11 key2 = data[key2_offset:key2_offset + (0x14 * 2)] offset += 21 config_size = unpack("=I", data[offset:offset + 4])[0] offset += 5 config_offset = unpack("=I", data[offset:offset + 4])[0] + offset + 11 config = data[config_offset:config_offset + (config_size * 2)] offset += 33 url_size = unpack("b", data[offset])[0] for pattern in STRINGS_PATTERNS: offset = re.search(pattern, data).start() offset += 19 strings_size = unpack("=I", data[offset:offset + 4])[0] offset += 5 strings_offset = unpack("=I", data[offset:offset + 4])[0] + offset + 11 strings_data = data[strings_offset:strings_offset + (strings_size * 2)] for pattern in HASHS_PATTERNS: offset = re.search(pattern, data).start() offset += 1 hashs_size = unpack("=I", data[offset:offset + 4])[0] offset += 11 hashs_offset = unpack("=I", data[offset:offset + 4])[0] + offset + 11 hashs_data = data[hashs_offset:hashs_offset + (hashs_size * 2)] config_data.append(self.formbook_decrypt(key1, key2, config, config_size, strings_data, strings_size, url_size, hashs_data, hashs_size)) yield task, vad_base_addr, end, hit, memory_model, config_data break
def windows_read_memory_mapped(pgd, addr, size, pte, is_pae, bitness): # Step 1: Traverse the VAD tree for the process with PGD, # and get the VAD that overlaps addr (if any) # Step 2: Check if the VAD has a ControlArea and a FilePointer, # and get the file path. # Step 3: Get Segment (pointed to by ControlArea), and get the pointer # to the first PrototypePTE. # Step 4: Compute offset of address with respect to the beginning # of the VAD, and compute which PrototypePTE corresponds to the address # No need to consider if the PTE points to the Prototype PTE here. # Step 6: Compute the offset in file for such PrototypePTE by looking at the # subsections pointed by the ControlArea. # Step 7: Finally, open the file, read the contents, and return them. import volatility.obj as obj import volatility.win32.tasks as tasks import volatility.plugins.vadinfo as vadinfo from utils import get_addr_space addr_space = get_addr_space(pgd) eprocs = [ t for t in tasks.pslist(addr_space) if t.Pcb.DirectoryTableBase.v() == pgd ] if len(eprocs) != 1: return None task = eprocs[0] vad = None # File name and offset for vad in task.VadRoot.traverse(): if addr >= vad.Start and addr < vad.End: break if vad is None: return None filename = None if vad.ControlArea is not None and vad.FileObject is not None: filename = str(vad.ControlArea.FileObject.FileName) if vad.ControlArea.Segment is None: return None # Compute page offset with respect to Start of the VAD, # and the corresponding prototype Page Table Entry pointed # by the Segment offset_on_vad = addr - vad.Start page_offset_on_vad = (offset_on_vad - (offset_on_vad & 0xFFF)) # Consider 4 KiB pages ppte_index = page_offset_on_vad / 0x1000 if ppte_index >= vad.ControlArea.Segment.TotalNumberOfPtes.v(): return None if bitness == 32 and is_pae: ppte_addr = vad.ControlArea.Segment.PrototypePte.v() + (ppte_index * 8) else: ppte_addr = vad.ControlArea.Segment.PrototypePte.v() + ( ppte_index * addr_space.profile.vtypes["_MMPTE_PROTOTYPE"][0]) # Read Subsections pointed by ControlArea visited_subsections = {} if "Subsection" in vad.members: subsect = vad.Subsection # There is no Subsection pointer in VAD # structure, so we just read after the ControlArea else: subsect = obj.Object( "_SUBSECTION", offset=(vad.ControlArea.v() + addr_space.profile.vtypes["_CONTROL_AREA"][0]), vm=addr_space) file_offset_to_read = None while file_offset_to_read is None and subsect is not None or subsect.v( ) != 0 and subsect.v() not in visited_subsections: visited_subsections.append(subsect.v()) # Get the PPTE address where the Subsection starts, # and compute the virtual address that it corresponds # to. ppte_addr = subsect.SubsectionBase.v() if bitness == 32 and is_pae: ppte_index = (subsect.SubsectionBase.v() - vad.ControlArea.Segment.PrototypePte.v()) / 8 else: ppte_index = (subsect.SubsectionBase.v() - vad.ControlArea.Segment.PrototypePte.v() ) / addr_space.profile.vtypes["_MMPTE_PROTOTYPE"][0] subsection_base = vad.Start + (ppte_index * 0x1000) subsection_size = subsect.PtesInSubsection.v() * 0x1000 subsection_file_offset = subsect.StartingSector.v() * 512 subsection_file_size = vad.Subsection.NumberOfFullSectors.v() * 512 visited_subsections[subsect.v()] = (subsection_base, subsection_size, subsection_file_offset, subsection_file_size) if (addr >= subsection_base) and (addr < (subsection_base + subsection_size)): file_offset_to_read = (addr - subsection_base) + subsection_file_offset subsect = subsect.NextSubsection f = None for fs in api.get_filesystems(): try: f = api.open_guest_path(fs["index"], filename) break except: # The file cannot be open on such filesystem pass if not f: raise RuntimeError( "Could not read memory from pagefile: file not found") print("Reading file %s at offset %x - Size: %x" % (filename, file_offset_to_read, size)) f.seek(file_offset_to_read) data = f.read(size=size) f.close()
def calculate(self): if not has_yara: debug.error("Yara must be installed for this plugin") addr_space = utils.load_as(self._config) os, memory_model = self.is_valid_profile(addr_space.profile) if not os: debug.error("This command does not support the selected profile.") rules = yara.compile(sources=wellmess_sig) for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = malfind.VadYaraScanner(task=task, rules=rules) for hit, address in scanner.scan(): vad_base_addr, end = self.get_vad_base(task, address) proc_addr_space = task.get_process_address_space() data = proc_addr_space.zread(vad_base_addr, end - vad_base_addr) pe = pefile.PE(data=data) config_data = [] configs = [] for pattern in CONFIG_PATTERNS: mc = list(re.finditer(pattern, data)) if mc: for m in mc: hit_adderss = m.span() config_rva = unpack("=I", m.groups()[1])[0] if pe.FILE_HEADER.Machine == 0x14C: # for 32bit config_offset = config_rva - pe.NT_HEADERS.OPTIONAL_HEADER.ImageBase #config_offset = pe.get_physical_by_rva(config_rva - pe.NT_HEADERS.OPTIONAL_HEADER.ImageBase) + 0x1000 else: # for 64bit config_offset = config_rva + hit_adderss[0] + 26 configs.append(data[config_offset:config_offset + ord(m.groups()[0])]) for pattern in CONFIG_PATTERNS_DOTNET: mc = re.search(pattern, data) if mc: offset = mc.end() for i in range(6): strings = [] string_len = ord(data[offset]) if ord(data[offset]) == 0x80 or ord(data[offset]) == 0x83: string_len = ord(data[offset + 1]) + ((ord(data[offset]) - 0x80) * 256) offset += 1 offset += 1 for i in range(string_len): if data[offset + i] != "\x00": strings.append(data[offset + i]) if string_len != 1: configs.append("".join(strings)) offset = offset + string_len config_data.append(self.parse_config(configs)) yield task, vad_base_addr, end, hit, memory_model, config_data break
def calculate(self): addr_space = utils.load_as(self._config) if not has_distorm3: debug.error("Install distorm3 code.google.com/p/distorm/") if not self._config.SKIP_PROCESS: for proc in self.filter_tasks(tasks.pslist(addr_space)): process_name = str(proc.ImageFileName).lower() if (self._config.QUICK and process_name not in self.critical_process): #debug.debug("Skipping non-critical process {0} ({1})".format( # process_name, proc.UniqueProcessId)) continue process_space = proc.get_process_address_space() if not process_space: #debug.debug("Cannot acquire process AS for {0} ({1})".format( # process_name, proc.UniqueProcessId)) continue module_group = ModuleGroup(proc.get_load_modules()) for dll in module_group.mods: if not process_space.is_valid_address(dll.DllBase): continue dll_name = str(dll.BaseDllName or '').lower() if (self._config.QUICK and dll_name not in self.critical_dlls and dll.DllBase != proc.Peb.ImageBaseAddress): #debug.debug("Skipping non-critical dll {0} at {1:#x}".format( # dll_name, dll.DllBase)) continue #debug.debug("Analyzing {0}!{1}".format(process_name, dll_name)) for hook in self.get_hooks(HOOK_MODE_USER, process_space, dll, module_group): if not self._config.NO_WHITELIST: if self.whitelist(hook.hook_mode | hook.hook_type, str(proc.ImageFileName), hook.VictimModule, hook.HookModule, hook.Function): continue yield proc, dll, hook if not self._config.SKIP_KERNEL: process_list = list(tasks.pslist(addr_space)) module_group = ModuleGroup(modules.lsmod(addr_space)) for mod in module_group.mods: #module_name = str(mod.BaseDllName or '') #debug.debug("Analyzing {0}".format(module_name)) kernel_space = tasks.find_space(addr_space, process_list, mod.DllBase) if not kernel_space: #debug.debug("No kernel AS for {0} at {1:#x}".format( # module_name, mod.DllBase)) continue for hook in self.get_hooks(HOOK_MODE_KERNEL, kernel_space, mod, module_group): if not self._config.NO_WHITELIST: if self.whitelist(hook.hook_mode | hook.hook_type, "", hook.VictimModule, hook.HookModule, hook.Function): continue yield None, mod, hook
def calculate(self): if not has_yara: debug.error("Yara must be installed for this plugin") if not has_aplib: debug.error("Aplib must be installed for this plugin") addr_space = utils.load_as(self._config) os, memory_model = self.is_valid_profile(addr_space.profile) if not os: debug.error("This command does not support the selected profile.") rules = yara.compile(sources=ursnif_sig) for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = malfind.VadYaraScanner(task=task, rules=rules) for hit, address in scanner.scan(): vad_base_addr, end = self.get_vad_base(task, address) proc_addr_space = task.get_process_address_space() data = proc_addr_space.zread(vad_base_addr, end - vad_base_addr) config_data = [] # Parse standard Ursnif config_data = self.parse_joinned_data(data) # Parse static configuration type Ursnif if not config_data: p_data = OrderedDict() data = self.pe_magic_check(data) try: pe = pefile.PE(data=data) except: continue imagebase = pe.NT_HEADERS.OPTIONAL_HEADER.ImageBase for pattern in CONFIG_PATTERNS: m = re.search(pattern, data) if m: if pe.FILE_HEADER.Machine in (pefile.MACHINE_TYPE['IMAGE_FILE_MACHINE_IA64'], pefile.MACHINE_TYPE['IMAGE_FILE_MACHINE_AMD64']): c2_num = unpack("b", data[m.start(7) + 19])[0] else: c2_num = unpack("b", data[m.start(6)])[0] if c2_num >= 16: c2_num = 1 for i in range(c2_num): if pe.FILE_HEADER.Machine in (pefile.MACHINE_TYPE['IMAGE_FILE_MACHINE_IA64'], pefile.MACHINE_TYPE['IMAGE_FILE_MACHINE_AMD64']): c2_addr = m.start(4) + unpack("=I", data[m.start(3):m.start(3) + 4])[0] c2_table_offset = unpack("=Q", data[c2_addr + (8 * i):c2_addr + 8 + (8 * i)])[0] - imagebase else: c2_addr = unpack("=I", data[m.start(4):m.start(4) + 4])[0] - imagebase c2_table_offset = unpack("=I", data[c2_addr + (4 * i):c2_addr + 4 + (4 * i)])[0] - imagebase try: c2 = self.decode_data(data, pe, c2_table_offset) except: c2 = "Decode fail" p_data["Server " + str(i)] = c2 if pe.FILE_HEADER.Machine in (pefile.MACHINE_TYPE['IMAGE_FILE_MACHINE_IA64'], pefile.MACHINE_TYPE['IMAGE_FILE_MACHINE_AMD64']): serpent_key_offset = m.start(8) + unpack("=I", data[m.start(7):m.start(7) + 4])[0] else: serpent_key_offset = unpack("=I", data[m.start(8):m.start(8) + 4])[0] - imagebase try: serpent_key = self.decode_data(data, pe, serpent_key_offset) except: serpent_key = "Decode fail" p_data["Serpent key"] = serpent_key for pattern in RSA_PATTERNS: m = re.search(pattern, data) if m: if pe.FILE_HEADER.Machine in (pefile.MACHINE_TYPE['IMAGE_FILE_MACHINE_IA64'], pefile.MACHINE_TYPE['IMAGE_FILE_MACHINE_AMD64']): rsa_key_offset = m.start(2) + unpack("=I", data[m.start(1):m.start(1) + 4])[0] rsa_key = data[rsa_key_offset + 4:rsa_key_offset + 0x44] rsa_mod = data[rsa_key_offset + 0x44:rsa_key_offset + 0x84] else: rsa_key_offset = unpack("=I", data[m.start(1):m.start(1) + 4])[0] - imagebase rsa_key = data[rsa_key_offset:rsa_key_offset + 0x40] mod_offset = unpack("=I", data[m.start(4):m.start(4) + 4])[0] - imagebase rsa_mod = data[mod_offset:mod_offset + 0x40] p_data["RSA key"] = rsa_key.encode("hex") p_data["RSA modulus"] = rsa_mod.encode("hex") config_data.append(p_data) yield task, vad_base_addr, end, hit, memory_model, config_data break
def calculate(self): """Search memory for credentials""" kernel_memory = utils.load_as(self._config) # Find all OpenVPN processes processes = tasks.pslist(kernel_memory) processes = filter( lambda p: str(p.ImageFileName).lower() == "openvpn.exe", processes) # Search for credentials in each process for process in processes: process_memory = process.get_process_address_space() # Get some basic process information pid = int(process.UniqueProcessId) image_base = process.Peb.ImageBaseAddress dos_header = obj.Object("_IMAGE_DOS_HEADER", offset=image_base, vm=process_memory) nt_header = dos_header.get_nt_header() # Find the .data section sections = nt_header.get_sections(True) sections = filter(lambda s: str(s.Name) == ".data", sections) if len(sections) != 1: # Section may be unavailable continue # Determine dimensions of section data_section = sections[0] data_start = data_section.VirtualAddress + image_base data_end = data_start + data_section.Misc.VirtualSize # Search static user_pass struct # Assumptions: # - Struct is aligned on 16-byte boundary # - Bool fields are 4 bytes long # - Username and password buffers are 4096 bytes long for creds_start in xrange(data_start, data_end, 16): creds = process_memory.read(creds_start, 9) if not creds: continue # Try to unpack and verify the beginning of the struct defined, nocache, username = struct.unpack("IIc", creds) if defined > 1 or nocache > 1 or username == "\0": continue # Completely unpack the struct creds = process_memory.zread(creds_start, 4 + 4 + 4096 + 4096) defined, nocache, username, password = \ struct.unpack("II4096s4096s", creds) # Truncate string padding username, _, _ = username.partition("\0") password = password.rstrip("\0") # CENSOR PASSWORD #password = "******" * len(password) yield (pid, username, password) # Stop searching in current process break
def calculate(self): if not has_yara: debug.error("Yara must be installed for this plugin") if not has_yara: debug.error("Aplib must be installed for this plugin") addr_space = utils.load_as(self._config) os, memory_model = self.is_valid_profile(addr_space.profile) if not os: debug.error("This command does not support the selected profile.") rules = yara.compile(sources=ursnif_sig) for task in self.filter_tasks(tasks.pslist(addr_space)): scanner = malfind.VadYaraScanner(task=task, rules=rules) for hit, address in scanner.scan(): vad_base_addr, end = self.get_vad_base(task, address) proc_addr_space = task.get_process_address_space() data = proc_addr_space.zread(vad_base_addr, end - vad_base_addr) config_data = [] mz_magic = unpack_from("=2s", data, 0x0)[0] nt_magic = unpack_from("<H", data, 0x3c)[0] if mz_magic == "\x00\x00": data = "\x4d\x5a" + data[2:] data = data[:nt_magic] + "\x50\x45" + data[nt_magic + 2:] fnames = [] for m in re.finditer(magic + "\x00.", data): xor_dword = 0 magic_dword = data[m.start():m.start() + 4] if (magic_dword[0:1] == "J1" or magic_dword[3] == "\0"): (flags, crc32_name, addr, size) = unpack_from("<LLLL", data, m.start() + 4) print( "[+] magic: {0} flags: 0x{1:X} crc32_name: 0x{2:X} addr: 0x{3:X} size: 0x{4:X}\n" .format(repr(magic_dword), flags, crc32_name, addr, size)) elif (magic_dword[0:1] == "JJ" or (ord(magic_dword[3]) & 1) == 1): (xor_dword, crc32_name, addr, size) = unpack_from("<LLLL", data, m.start() + 4) print( "[+] magic: {0} xor: 0x{1:X} crc32_name: 0x{2:X} addr: 0x{3:X} size: 0x{4:X}\n" .format(repr(magic_dword), xor_dword, crc32_name, addr, size)) else: raise ValueError("Unknown joiner header") if size > 0x80000: print("[!] size is too large, skipped this entry\n") continue try: offset = addr except: print("[!] This PE is old Ursnif (not DreamBot)\n") (addr, size, crc32_name, flags) = unpack_from("<LLLL", data, m.start() + 4) print( "[+] magic: {0} addr: 0x{1:X} size: 0x{2:X} crc32_name: 0x{3:X} flags: 0x{4:X}\n" .format(repr(magic_dword), addr, size, crc32_name, flags)) offset = addr joined_res = data[offset:offset + size] try: dec_data = aplib.decompress(joined_res).do()[0] except: print("[!] Cann't decode data.\n") continue if (xor_dword != 0): mod_data = "" for i in range(min(4, size + 1)): mod_data += chr( ord(dec_data[i]) ^ ((xor_dword >> 8 * i) & 0xff)) if (size >= 4): mod_data += dec_data[4:] dec_data = mod_data if crc32_name in (0x4f75cea7, 0x9e154a0c): fname = "ursnif_client32.bin" open(fname, "wb").write(dec_data) print("[+] dumped 32 bit client dll: {0}\n".format( fname)) fnames.append(fname) elif crc32_name in (0x90f8aab4, 0x41982e1f): fname = "ursnif_client64.bin" open(fname, "wb").write(dec_data) print("[+] dumped 64 bit client dll: {0}\n".format( fname)) # fnames.append(fname) elif crc32_name in (0xe1285e64, ): fname = "ursnif_public_key.bin" open(fname, "wb").write(dec_data) print("[+] dumped public key: {0}\n".format(fname)) elif crc32_name in (0xd722afcb, 0x8365b957, 0x8fb1dde1): fname = "ursnif_st_config.bin" open(fname, "wb").write(dec_data) print("[+] dumped static config: {0}\n".format(fname)) config_data.append(self.parse_config(dec_data)) else: fname = "ursnif_" + hex(addr) + "_ap32_dec.bin" open(fname, "wb").write(dec_data) print("[+] dumped: {0}".format(fname)) for fname in fnames: parse_joinned_data(fname, magic) yield task, vad_base_addr, end, hit, memory_model, config_data break