def render_text(self, outfd, data): for task in data: proc_as = task.get_process_address_space() for map in task.get_proc_maps(): if map.is_suspicious(): fname = map.get_path() prots = map.get_perms() content = proc_as.zread(map.start, 64) outfd.write("Process: {0} Pid: {1} Address: {2:#x} File: {3}\n".format( task.p_comm, task.p_pid, map.start, fname)) outfd.write("Protection: {0}\n".format(prots)) outfd.write("\n") outfd.write("{0}\n".format("\n".join( ["{0:#010x} {1:<48} {2}".format(map.start + o, h, ''.join(c)) for o, h, c in utils.Hexdump(content) ]))) outfd.write("\n") outfd.write("\n".join( ["{0:#x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(content, map.start) ])) outfd.write("\n\n")
def parse_yarascan_data(data, out, output="text"): import volatility.plugins.malware.malfind as malfind import volatility.utils as utils datas = getinfos(data, plugin_cols["yarascan"]["cols"]) if output == "json": out.write("{}\n\n".format(datas)) return elif output == "text": mode = "32bit" if platform.machine() == "AMD64": mode = "64bit" for rule, owner, addr, content in datas: out.write("Rule: {0}\n".format(rule)) if owner == None: out.write("Owner: (Unknown Kernel Memory)\n") else: out.write("Owner: {0}\n".format(owner)) out.write("Hexdump:\n") out.write("".join([ "{0:#010x} {1:<48} {2}\n".format(addr, h, ''.join(c)) for offset, h, c in utils.Hexdump(content.decode("hex")) ])) out.write("\n\nDisassembly:\n") out.write("\n".join([ "{0:#x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(content.decode("hex"), int(addr), mode) ])) out.write("\n\n") else: for rule, owner, addr, content in datas: out.write("{0},{1},{2}\n".format(rule, owner, addr, content)) out.write("\n\n") out.write("\n\n")
def parse_malfind_data(data, out, output = "text"): import volatility.plugins.malware.malfind as malfind import volatility.utils as utils datas = getinfos(data, plugin_cols["malfind"]["cols"]) if output == "json": out.write("{}\n\n".format(datas)) return elif output == "text": mode = "32bit" if platform.machine() == "AMD64": mode = "64bit" for proc, pid, address, vadtag, protection, flags, data in datas: out.write("Process: {}, Pid: {}\n".format(proc, pid)) out.write("VadTag: {}, Protection: {}, Flags: {}\n\n".format(vadtag, protection, flags)) out.write("Raw data at address {0:#x}: {1}\n\n".format(address, data)) out.write("Disassembly:\n") out.write("\n".join( ["{0:#x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(data.decode("hex"), int(address), mode) ])) out.write("\n\nHexdump:\n") out.write("{0}\n".format("\n".join( ["{0:#010x} {1:<48} {2}".format(int(address) + o, h, ''.join(c)) for o, h, c in utils.Hexdump(data.decode("hex")) ]))) out.write("\n\n") else: for proc, pid, address, vadtag, protection, flags, data in datas: out.write("{},{},{},{},{},{},{}\n".format(proc, pid, address, vadtag, protection, flags, data)) out.write("\n\n") out.write("\n\n")
def render_text(self, outfd, data): for process, module, hook in data: outfd.write("*" * 72 + "\n") outfd.write("Hook mode: {0}\n".format(hook.Mode)) outfd.write("Hook type: {0}\n".format(hook.Type)) if process: outfd.write('Process: {0} ({1})\n'.format( process.UniqueProcessId, process.ImageFileName)) outfd.write("Victim module: {0} ({1:#x} - {2:#x})\n".format( str(module.BaseDllName or '') or ntpath.basename(str(module.FullDllName or '')), module.DllBase, module.DllBase + module.SizeOfImage)) outfd.write("Function: {0}\n".format(hook.Detail)) outfd.write("Hook address: {0:#x}\n".format(hook.hook_address)) outfd.write("Hooking module: {0}\n\n".format(hook.HookModule)) for n, info in enumerate(hook.disassembled_hops): (address, data) = info s = ["{0:#x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(data, int(address), bits = "32bit" if hook.decode_bits == distorm3.Decode32Bits else "64bit") ] outfd.write("Disassembly({0}):\n{1}".format(n, "\n".join(s))) outfd.write("\n\n")
def render_text(self, outfd, data): self.table_header(outfd, [ ('CPU', '>6X'), ('Index', '>6X'), ('Selector', '[addr]'), ('Value', '[addrpad]'), ('Module', '20'), ('Section', '12'), ]) for n, entry, addr, module in data: if module: module_name = str(module.BaseDllName or '') sect_name = self.get_section_name(module, addr) else: module_name = "UNKNOWN" sect_name = '' # The parent is IDT. The grand-parent is _KPCR. cpu_number = entry.obj_parent.obj_parent.ProcessorBlock.Number self.table_row(outfd, cpu_number, n, entry.Selector, addr, module_name, sect_name) if self._config.verbose: data = entry.obj_vm.zread(addr, 32) outfd.write("\n".join([ "{0:#x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(data=data, start=addr, stoponret=True) ])) outfd.write("\n")
def render_text(self, outfd, data): for process, module, hook in data: outfd.write("*" * 72 + "\n") outfd.write(f"Hook mode: {hook.Mode}\n") outfd.write(f"Hook type: {hook.Type}\n") if process: outfd.write( f"Process: {process.UniqueProcessId} ({process.ImageFileName})\n" ) dll_name = str(module.BaseDllName or '') or ntpath.basename( str(module.FullDllName or '')) outfd.write( f"Victim module: {dll_name} ({module.DllBase:#x} - {module.DllBase+module.SizeOfImage:#x})\n" ) outfd.write(f"Function: {hook.Detail}\n") outfd.write(f"Hook address: {hook.hook_address:#x}\n") outfd.write(f"Hooking module: {hook.HookModule}\n\n") for n, info in enumerate(hook.disassembled_hops): (address, data) = info s = [ f"{o:#x} {h:<16} {i}" for o, i, h in malfind.Disassemble( data, int(address), bits="32bit" if hook.decode_bits == distorm3.Decode32Bits else "64bit", ) ] outfd.write(f"Disassembly({n}):\n") outfd.write('\n'.join(s)) outfd.write("\n\n")
def render_text(self, outfd, data): addr_space = utils.load_as(self._config) # Compile the regular expression for filtering by driver name if self._config.regex != None: mod_re = re.compile(self._config.regex, re.I) else: mod_re = None mods = dict((addr_space.address_mask(mod.DllBase), mod) for mod in modules.lsmod(addr_space)) mod_addrs = sorted(mods.keys()) bits = addr_space.profile.metadata.get('memory_model', '32bit') self.table_header(None, [('i', ">4"), ('Funcs', "36"), ('addr', '[addrpad]'), ('name', '')]) for object_obj, driver_obj, _ in data: driver_name = str(object_obj.NameInfo.Name or '') # Continue if a regex was supplied and it doesn't match if mod_re != None: if not (mod_re.search(driver_name) or mod_re.search(driver_name)): continue # Write the standard header for each driver object outfd.write("{0}\n".format("-" * 50)) outfd.write("DriverName: {0}\n".format(driver_name)) outfd.write("DriverStart: {0:#x}\n".format(driver_obj.DriverStart)) outfd.write("DriverSize: {0:#x}\n".format(driver_obj.DriverSize)) outfd.write("DriverStartIo: {0:#x}\n".format( driver_obj.DriverStartIo)) # Write the address and owner of each IRP function for i, function in enumerate(driver_obj.MajorFunction): function = driver_obj.MajorFunction[i] module = tasks.find_module(mods, mod_addrs, addr_space.address_mask(function)) if module: module_name = str(module.BaseDllName or '') else: module_name = "Unknown" # This is where we check for inline hooks once the # ApiHooks plugin is ported to 2.1. self.table_row(outfd, i, MAJOR_FUNCTIONS[i], function, module_name) if self._config.verbose: data = addr_space.zread(function, 64) outfd.write("\n".join([ "{0:#x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(data=data, start=function, bits=bits, stoponret=True) ])) outfd.write("\n")
def disAssembleInstr(instrStream, instrLocation, opcodeCount): toRet = [] for _, i, _ in malfind.Disassemble(instrStream, instrLocation, True): toRet.append(i) opcodeCount = opcodeCount - 1 if opcodeCount == 0: break return "; ".join(toRet)
def get_json(self, data): results = {"Hooks":[]} for process, module, hook in data: if not self._config.NO_WHITELIST: if process: process_name = str(process.ImageFileName) process_id = int(process.UniqueProcessId) else: process_name = '' process_id = -1 if self.whitelist(hook.hook_mode | hook.hook_type, process_name, hook.VictimModule, hook.HookModule, hook.Function): #debug.debug("Skipping whitelisted function: {0} {1} {2} {3}".format( # process_name, hook.VictimModule, hook.HookModule, # hook.Function)) continue row = {'ImageFileName':process_name or '', 'Mode':str(hook.Mode), 'Type':str(hook.Type), 'UniqueProcessId':process_id, 'BaseDllName':str(module.BaseDllName or ''), 'SizeOfImage':int(module.SizeOfImage), 'FullDllName':ntpath.basename(str(module.FullDllName or '')), 'DllBase':long(module.DllBase), 'DllSize': "{}".format(module.SizeOfImage), 'DllEndAddress': module.DllBase + module.SizeOfImage, 'Detail': str(hook.Detail), 'HookAddress':long(hook.hook_address), 'HookModule':str(hook.HookModule), 'Disassembly':[] } for n, info in enumerate(hook.disassembled_hops): (address, data) = info s = [{'Address':int(o), "Bytes":str(h), "Instruction":str(i)} for o, i, h in malfind.Disassemble(data, int(address), bits = "32bit" if hook.decode_bits == distorm3.Decode32Bits else "64bit") ] disassembled = {'Disassembly':s, 'Address':"{}".format(address), 'Hop':n} row['Disassembly'].append(disassembled) results['Hooks'].append(row) return results
def printUnicornContext(pas, nextIns, unicornEng): print "Unicorn context: " instrStream = pas.read(nextIns, 20) for _, i, _ in malfind.Disassemble(instrStream, nextIns): print "\t %s" % i print "ESP = %s" % hex(unicornEng.reg_read(UC_X86_REG_ESP)) print "EAX = %s" % hex(unicornEng.reg_read(UC_X86_REG_EAX)) print "EBX = %s" % hex(unicornEng.reg_read(UC_X86_REG_EBX)) print "ECX = %s" % hex(unicornEng.reg_read(UC_X86_REG_ECX)) print "EDX = %s" % hex(unicornEng.reg_read(UC_X86_REG_EDX)) print "ESI = %s" % hex(unicornEng.reg_read(UC_X86_REG_ESI)) print "EDI = %s" % hex(unicornEng.reg_read(UC_X86_REG_EDI)) print "EFLAGS = %s" % hex(unicornEng.reg_read(UC_X86_REG_EFLAGS)) print ""
def generate_output(self, outfd, vad, task, file_object_name, peb_image_path_name): # this function will output data for a given VAD passed to it content = None outfd.write("Process: {0} Pid: {1} Ppid: {2}\n".format( task.ImageFileName, task.UniqueProcessId, task.InheritedFromUniqueProcessId)) outfd.write("Address: {0:#x} Protection: {1}\n".format( vad.Start, vadinfo.PROTECT_FLAGS.get(vad.VadFlags.Protection.v(), ""))) if peb_image_path_name != None: outfd.write("Initially mapped file object: {0}\n".format( peb_image_path_name)) else: outfd.write("Initially mapped file object: {0}\n".format("None")) if file_object_name != None: outfd.write( "Currently mapped file object: {0}\n".format(file_object_name)) else: outfd.write("Currently mapped file object: {0}\n".format("None")) address_space = task.get_process_address_space() content = address_space.zread(vad.Start, 64) if content: outfd.write("{0}\n".format("\n".join([ "{0:#010x} {1:<48} {2}".format(vad.Start + o, h, ''.join(c)) for o, h, c in utils.Hexdump(content) ]))) outfd.write("\n") outfd.write("\n".join([ "{0:#010x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(content, vad.Start) ])) outfd.write("\n\n") # dump vad incase -D was specified if self._config.DUMP_DIR: filename = os.path.join( self._config.DUMP_DIR, "process.{0:#x}.{1:#x}.dmp".format(task.obj_offset, vad.Start)) self.dump_vad(filename, vad, address_space)
def disassemble(self, address_space, entry_point): """ :param address_space: process's address space object :param entry_point: Start address :return: A string of the disassembled code Disassemble the 64 bytes of code by giving the process's address space and the start address """ entry_point = int(entry_point) content = address_space.read(entry_point, 64) # Check if we could have read from memory, might be paged if content: disassemble_code = "\t" disassemble_code += ("{0}\n\n".format("\n\t".join([ "{0:#010x} {1:<48} {2}".format(entry_point + o, h, ''.join(c)) for o, h, c in utils.Hexdump(content) ]))) disassemble_code += "\t" # Rather disassemble with distrom3 than malfind if has_distorm: # Get OS profile mode = address_space.profile.metadata.get('memory_model') if mode == '64bit': mode = distorm3.Decode64Bits else: mode = distorm3.Decode32Bits disassemble_code += "\n\t".join(["{0:<#010x} {1:<16} {2}".format(o, h, i) \ for o, _size, i, h in \ distorm3.DecodeGenerator(entry_point, content, mode)]) else: disassemble_code += "\n\t".join([ "{0:#010x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(content, entry_point) ]) disassemble_code += "\n" else: disassemble_code = "\t** Couldn't read memory\n" return disassemble_code
def render_text(self, outfd, data): linux_common.set_plugin_members(self) if self.addr_space.profile.metadata.get('memory_model', '32bit') == '32bit': bits = '32bit' else: bits = '64bit' for task in data: proc_as = task.get_process_address_space() for vma in task.get_proc_maps(): if vma.is_suspicious(): fname = vma.vm_name(task) if fname == "[vdso]": continue prots = vma.protection() flags = vma.flags() content = proc_as.zread(vma.vm_start, 64) outfd.write( "Process: {0} Pid: {1} Address: {2:#x} File: {3}\n". format(task.comm, task.pid, vma.vm_start, fname)) outfd.write("Protection: {0}\n".format(prots)) outfd.write("Flags: {0}\n".format(str(flags))) outfd.write("\n") outfd.write("{0}\n".format("\n".join([ "{0:#016x} {1:<48} {2}".format( vma.vm_start + o, h, ''.join(c)) for o, h, c in utils.Hexdump(content) ]))) outfd.write("\n") outfd.write("\n".join([ "{0:#x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(content, vma.vm_start, bits=bits) ])) outfd.write("\n\n")
def render_text(self, outfd, data): for process, module, hook in data: if not self._config.NO_WHITELIST: if process: process_name = str(process.ImageFileName) else: process_name = '' if self.whitelist(hook.hook_mode | hook.hook_type, process_name, hook.VictimModule, hook.HookModule, hook.Function): #debug.debug("Skipping whitelisted function: {0} {1} {2} {3}".format( # process_name, hook.VictimModule, hook.HookModule, # hook.Function)) continue outfd.write("*" * 72 + "\n") outfd.write("Hook mode: {0}\n".format(hook.Mode)) outfd.write("Hook type: {0}\n".format(hook.Type)) if process: outfd.write('Process: {0} ({1})\n'.format( process.UniqueProcessId, process.ImageFileName)) outfd.write("Victim module: {0} ({1:#x} - {2:#x})\n".format( str(module.BaseDllName or '') or ntpath.basename(str(module.FullDllName or '')), module.DllBase, module.DllBase + module.SizeOfImage)) outfd.write("Function: {0}\n".format(hook.Detail)) outfd.write("Hook address: {0:#x}\n".format(hook.hook_address)) outfd.write("Hooking module: {0}\n\n".format(hook.HookModule)) for n, info in enumerate(hook.disassembled_hops): (address, data) = info s = [ "{0:#x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(data, int(address), bits="32bit" if hook.decode_bits == distorm3.Decode32Bits else "64bit") ] outfd.write("Disassembly({0}):\n{1}".format(n, "\n".join(s))) outfd.write("\n\n")
def parse_malfind_data(data, out, output = "text"): import volatility.plugins.malware.malfind as malfind datas = getinfos(data, plugin_cols["malfind"]["cols"]) if output == "json": out.write("{}\n\n".format(datas)) return elif output == "text": for proc, address, data in datas: out.write("Process: {}\n\n".format(proc)) out.write("Raw data at address {0:#x}: {1}\n\n".format(address, data)) out.write("Disassembly:\n") out.write("\n".join( ["{0:#x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(data.decode("hex"), int(address)) ])) out.write("\n\n") else: for proc, address, data in datas: out.write("{0},{1},{2}\n".format(proc, address, data)) out.write("\n\n") out.write("\n\n")
def render_text(self, outfd, data): for task in data: proc_as = task.get_process_address_space() for vma in task.get_proc_maps(): if vma.is_suspicious(): fname = vma.vm_name(task) if fname == "[vdso]": continue prots = vma.protection() flags = vma.flags() content = proc_as.zread(vma.vm_start, 64) outfd.write( "Process: {0} Pid: {1} Address: {2:#x} File: {3}\n". format(task.comm, task.pid, vma.vm_start, fname)) outfd.write("Protection: {0}\n".format(prots)) outfd.write("Flags: {0}\n".format(str(flags))) outfd.write("\n") outfd.write("{0}\n".format("\n".join([ "{0:#010x} {1:<48} {2}".format( vma.vm_start + o, h, ''.join(c)) for o, h, c in utils.Hexdump(content) ]))) outfd.write("\n") outfd.write("\n".join([ "{0:#x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble( content, vma.vm_start) ])) outfd.write("\n\n")
def generator(self, data): # Determine which filters the user wants to see if self._config.FILTER: filters = set(self._config.FILTER.split(',')) else: filters = set() for thread, addr_space, mods, mod_addrs, \ instances, hooked_tables, system_range, owner_name in data: # If the user didn't set filters, display all results. If # the user set one or more filters, only show threads # with matching results. tags = set([t for t, v in instances.items() if v.check()]) if filters and not filters & tags: continue values = [] values.append(Address(thread.obj_offset)) values.append(int(thread.Cid.UniqueProcess)) values.append(int(thread.Cid.UniqueThread)) values.append(','.join(tags)) values.append(str(thread.CreateTime)) if thread.ExitTime > 0: values.append(str(thread.ExitTime)) else: values.append('') values.append(str(thread.owning_process().ImageFileName)) values.append(str(thread.attached_process().ImageFileName)) # Lookup the thread's state state = str(thread.Tcb.State) # Find the wait reason if state == 'Waiting': state_reason = str(thread.Tcb.WaitReason) else: state_reason = '' values.append(state) values.append(state_reason) values.append(int(thread.Tcb.BasePriority)) values.append(int(thread.Tcb.Priority)) values.append(Address(thread.Tcb.Teb)) values.append(Address(thread.StartAddress)) values.append(owner_name) # Check the flag which indicates whether Win32StartAddress is valid if thread.SameThreadApcFlags & 1: values.append(Address(thread.Win32StartAddress)) else: values.append(Address(-1)) values.append(Address(thread.Tcb.Win32Thread)) values.append(str(thread.CrossThreadFlags)) # Disasemble the start address if possible dis = '' process_space = thread.owning_process().get_process_address_space() if process_space.is_valid_address(thread.StartAddress): buf = process_space.zread(thread.StartAddress, 24) mode = "32bit" if self.bits32 else "64bit" dis += "\n".join(["{0:#x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(buf, thread.StartAddress.v(), mode)]) if self.bits32: # Print the registers if possible trapframe = thread.Tcb.TrapFrame.dereference_as("_KTRAP_FRAME") if trapframe: for r in trapframe.Eip, trapframe.Eax, trapframe.Ebx, \ trapframe.Ecx, trapframe.Edx, trapframe.Esi, \ trapframe.Edi, trapframe.HardwareEsp, \ trapframe.Ebp, trapframe.ErrCode, trapframe.SegCs, \ trapframe.HardwareSegSs, trapframe.SegDs, \ trapframe.SegEs, trapframe.SegGs, trapframe.SegFs, \ trapframe.EFlags, trapframe.Dr0, trapframe.Dr1, \ trapframe.Dr2, trapframe.Dr3, trapframe.Dr6, \ trapframe.Dr7 : values.append(Hex(r)) else: values.extend( [Hex(-1)] * 23 ) values.append(Address(thread.Tcb.ServiceTable)) ssdt_obj = obj.Object("_SERVICE_DESCRIPTOR_TABLE", offset = thread.Tcb.ServiceTable, vm = addr_space ) if ssdt_obj != None: for i, desc in enumerate(ssdt_obj.Descriptors): if desc.is_valid(): service_table = Address(desc.KiServiceTable.v()) else: service_table = Address(-1) # Show exactly which functions are hooked table = desc.KiServiceTable.v() if table not in hooked_tables.keys(): yield (0, values + [i, service_table, -1, '', Address(-1), '', dis]) continue yielded=False for (j, func_name, func_addr, mod_name) in hooked_tables[table]: yielded=True yield(0, values + [i, service_table, j, func_name, Address(func_addr), mod_name, dis]) if not yielded: yield (0, values + [i, service_table, -1, '', Address(-1), '', dis]) else: values.extend([ -1, Address(-1), -1, '', Address(-1), '', dis ]) yield (0, values) else: # registers values.extend( [Hex(-1)] * 23 ) # ssdt values.extend([ Address(-1), -1, Address(-1), -1, '', Address(-1), '', dis ]) yield (0, values)
def render_text(self, outfd, data): # Determine which filters the user wants to see if self._config.FILTER: filters = set(self._config.FILTER.split(',')) else: filters = set() for thread, addr_space, mods, mod_addrs, instances, hooked_tables in data: # If the user didn't set filters, display all results. If # the user set one or more filters, only show threads # with matching results. tags = set([t for t, v in list(instances.items()) if v.check()]) if filters and not filters & tags: continue s = "------\n" s += "ETHREAD: {0:#010x} Pid: {1} Tid: {2}\n".format( thread.obj_offset, thread.Cid.UniqueProcess, thread.Cid.UniqueThread) s += "Tags: {0}\n".format(','.join(tags)) s += "Created: {0}\n".format(thread.CreateTime) s += "Exited: {0}\n".format(thread.ExitTime) s += "Owning Process: {0}\n".format( thread.owning_process().ImageFileName) s += "Attached Process: {0}\n".format( thread.attached_process().ImageFileName) # Lookup the thread's state state = str(thread.Tcb.State) # Append the wait reason if state == 'Waiting': state = state + ':' + str(thread.Tcb.WaitReason) s += "State: {0}\n".format(state) s += "BasePriority: {0:#x}\n".format(thread.Tcb.BasePriority) s += "Priority: {0:#x}\n".format(thread.Tcb.Priority) s += "TEB: {0:#010x}\n".format(thread.Tcb.Teb) # If its a system thread, get the owning module if "SystemThread" in tags: owner = tasks.find_module(mods, mod_addrs, thread.StartAddress) else: owner = None if owner: owner_name = str(owner.BaseDllName or '') else: owner_name = "UNKNOWN" s += "StartAddress: {0:#010x} {1}\n".format( thread.StartAddress, owner_name) # Check the flag which indicates whether Win32StartAddress is valid if thread.SameThreadApcFlags & 1: s += "Win32StartAddress: {0:#010x}\n".format( thread.Win32StartAddress) if self.bits32: s += "ServiceTable: {0:#010x}\n".format( thread.Tcb.ServiceTable) ssdt_obj = obj.Object("_SERVICE_DESCRIPTOR_TABLE", offset=thread.Tcb.ServiceTable, vm=addr_space) if ssdt_obj != None: for i, desc in enumerate(ssdt_obj.Descriptors): if desc.is_valid(): s += " [{0}] {1:#010x}\n".format( i, desc.KiServiceTable.v()) else: s += " [{0}] -\n".format(i) # Show exactly which functions are hooked table = desc.KiServiceTable.v() if table not in list(hooked_tables.keys()): continue for (i, func_name, func_addr, mod_name) in hooked_tables[table]: s += " [{0:#x}] {1} {2:#x} {3}\n".format( i, func_name, func_addr, mod_name) s += "Win32Thread: {0:#010x}\n".format(thread.Tcb.Win32Thread) s += "CrossThreadFlags: {0}\n".format(thread.CrossThreadFlags) # Print the registers if possible trapframe = thread.Tcb.TrapFrame.dereference_as("_KTRAP_FRAME") if trapframe and self.bits32: s += "Eip: {0:#10x}\n".format(trapframe.Eip) s += " eax={0:#010x} ebx={1:#010x} ecx={2:#010x}".format( trapframe.Eax, trapframe.Ebx, trapframe.Ecx) s += " edx={0:#010x} esi={1:#010x} edi={2:#010x}\n".format( trapframe.Edx, trapframe.Esi, trapframe.Edi) s += " eip={0:#010x} esp={1:#010x} ebp={2:#010x} err={3:#010x}\n".format( trapframe.Eip, trapframe.HardwareEsp, trapframe.Ebp, trapframe.ErrCode) s += " cs={0:#04x} ss={1:#04x} ds={2:#04x}".format( trapframe.SegCs, trapframe.HardwareSegSs, trapframe.SegDs) s += " es={0:#04x} gs={1:#04x} efl={2:#010x}\n".format( trapframe.SegEs, trapframe.SegGs, trapframe.EFlags) s += " dr0={0:#010x} dr1={1:#010x} dr2={2:#010x}".format( trapframe.Dr0, trapframe.Dr1, trapframe.Dr2) s += " dr3={0:#010x} dr6={1:#010x} dr7={2:#010x}\n".format( trapframe.Dr3, trapframe.Dr6, trapframe.Dr7) # Disasemble the start address if possible if addr_space.is_valid_address(thread.StartAddress): buf = addr_space.zread(thread.StartAddress, 24) s += "\n".join([ "{0:#x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(buf, thread.StartAddress.v()) ]) outfd.write("{0}\n".format(s))
def render_text(self, outfd, data): for (hol_proc_peb_info, hol_proc_vad_info, hol_pid, hol_type, similar_procs, parent_proc_info) in data: (proc, pid, proc_name, ppid, create_time, proc_cmd_line, proc_image_baseaddr, mod_baseaddr, mod_size, mod_basename, mod_fullname) = hol_proc_peb_info (vad_filename, vad_baseaddr, vad_size, vad_protection, vad_tag) = hol_proc_vad_info (parent_name, parent_id) = parent_proc_info outfd.write("Hollowed Process Information:\n") outfd.write("\tProcess: {0} PID: {1}\n".format(proc_name, hol_pid)) outfd.write("\tParent Process: {0} PPID: {1}\n".format(parent_name, ppid)) outfd.write("\tCreation Time: {0}\n".format(create_time)) outfd.write("\tProcess Base Name(PEB): {0}\n".format(mod_basename)) outfd.write("\tCommand Line(PEB): {0}\n".format(proc_cmd_line)) outfd.write("\tHollow Type: {0}\n".format(hollow_types[hol_type])) outfd.write("\n") outfd.write( "VAD and PEB Comparison:\n") outfd.write( "\tBase Address(VAD): {0:#x}\n".format(vad_baseaddr)) outfd.write( "\tProcess Path(VAD): {0}\n".format(vad_filename)) outfd.write( "\tVad Protection: {0}\n".format(vad_protection)) outfd.write( "\tVad Tag: {0}\n".format(vad_tag)) outfd.write("\n") if hol_type == 0: addr_space = proc.get_process_address_space() dos_header = obj.Object("_IMAGE_DOS_HEADER", offset=proc_image_baseaddr, vm=addr_space) nt_header = dos_header.get_nt_header() optional_header = obj.Object("_IMAGE_OPTIONAL_HEADER", offset=nt_header.obj_offset+0x18, vm=addr_space) ep_addr = proc_image_baseaddr + optional_header.AddressOfEntryPoint content = addr_space.read(ep_addr, 64) outfd.write("\tBase Address(PEB): {0:#x}\n".format(proc_image_baseaddr)) outfd.write("\tProcess Path(PEB): {0}\n" .format(mod_fullname)) outfd.write("\tMemory Protection: {0}\n".format(vad_protection)) outfd.write("\tMemory Tag: {0}\n".format(vad_tag)) outfd.write("\n") outfd.write("Disassembly(Entry Point):\n") if content != None: outfd.write("\n".join(["\t{0:#010x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(content, ep_addr) ])) else: outfd.write("\tNo Disassembly: Memory Unreadable at {0:#010x}\n".format(ep_addr)) outfd.write("\n\n") if (hol_type == 1) or (hol_type == 2): for vad, addr_space in proc.get_vads(): if vad.Start == proc_image_baseaddr: content = addr_space.read(vad.Start, 64) outfd.write("\tBase Address(PEB): {0:#x}\n".format(proc_image_baseaddr)) outfd.write("\tProcess Path(PEB): {0}\n" .format(mod_fullname)) outfd.write("\tMemory Protection: {0}\n".format(str(vadinfo.PROTECT_FLAGS.get(vad.VadFlags.Protection.v()) or ""))) outfd.write("\tMemory Tag: {0}\n".format(str(vad.Tag or ""))) outfd.write("\n") if content != None: outfd.write("".join(["{0:#010x} {1:<48} {2}\n".format(vad.Start + o, h, ''.join(c)) for o, h, c in utils.Hexdump(content)])) else: outfd.write("\tNo Hexdump: Memory Unreadable at {0:#010x}\n".format(vad.Start)) outfd.write("\n") outfd.write("Similar Processes:\n") for similar_proc in similar_procs: (process_name, process_id, parent_name, parent_id, creation_time) = similar_proc outfd.write("\t{0}({1}) Parent:{2}({3}) Start:{4}\n".format(process_name, process_id, parent_name, parent_id, creation_time)) outfd.write("\n") outfd.write("Suspicious Memory Regions:\n") for vad, addr_space in proc.get_vads(): content = addr_space.read(vad.Start, 64) if content == None: continue vad_prot = str(vadinfo.PROTECT_FLAGS.get(vad.VadFlags.Protection.v())) if obj.Object("_IMAGE_DOS_HEADER", offset = vad.Start, vm = addr_space).e_magic != 0x5A4D: flag = "No PE/Possibly Code" if (vad_prot == "PAGE_EXECUTE_READWRITE"): sus_addr = vad.Start outfd.write("\t{0:#x}({1}) Protection: {2} Tag: {3}\n".format(vad.Start, flag, vad_prot, str(vad.Tag or ""))) if self._config.DUMP_DIR: filename = os.path.join(self._config.DUMP_DIR,"process.{0}.{1:#x}.dmp".format(hol_pid, sus_addr)) self.dump_vad(filename, vad, addr_space) elif (vad_prot == "PAGE_EXECUTE_WRITECOPY"): sus_addr = vad.Start outfd.write("\t{0:#x}({1}) Protection: {2} Tag: {3}\n".format(sus_addr, flag, vad_prot, str(vad.Tag or ""))) if self._config.DUMP_DIR: filename = os.path.join(self._config.DUMP_DIR,"process.{0}.{1:#x}.dmp".format(hol_pid, sus_addr)) self.dump_vad(filename, vad, addr_space) else: if vad_prot == "PAGE_EXECUTE_READWRITE": flag = "PE Found" sus_addr = vad.Start outfd.write("\t{0:#x}({1}) Protection: {2} Tag: {3}\n".format(sus_addr, flag, vad_prot, str(vad.Tag or ""))) if self._config.DUMP_DIR: filename = os.path.join(self._config.DUMP_DIR,"process.{0}.{1:#x}.dmp".format(hol_pid, sus_addr)) self.dump_vad(filename, vad, addr_space) elif (vad_prot == "PAGE_EXECUTE_WRITECOPY") and (not bool(vad.FileObject)): flag = "PE - No Mapped File" sus_addr = vad.Start outfd.write("\t{0:#x}({1}) Protection: {2} Tag: {3}\n".format(sus_addr, flag, vad_prot, str(vad.Tag or ""))) if self._config.DUMP_DIR: filename = os.path.join(self._config.DUMP_DIR,"process.{0}.{1:#x}.dmp".format(hol_pid, sus_addr)) self.dump_vad(filename, vad, addr_space) outfd.write("---------------------------------------------------\n\n")
def render_text(self, outfd, data): if not has_distorm3: debug.warning("For best results please install distorm3") if self._config.DUMP_DIR and not os.path.isdir(self._config.DUMP_DIR): debug.error(self._config.DUMP_DIR + " is not a directory") for task in data: for vad, address_space in task.get_vads(vad_filter = task._injection_filter): if self._is_vad_empty(vad, address_space): continue if self._config.SSDEEP and has_pydeep: skip = False # read the first page of the VAD then hash it inject_buf = address_space.zread(vad.Start, 0x1000) inject_hash = pydeep.hash_buf(inject_buf) # loop through all the whitelist hashes and compare for (whitelist_name, whitelist_hash) in whitelist_ssdeep: alike = pydeep.compare(inject_hash, whitelist_hash) # the comparison is greater than the threshold so display an informational message # then skip the rest of the output in normal malfind if alike > self._config.THRESHOLD: outfd.write("Process: {0} Pid: {1} Address: {2:#x}\n".format( task.ImageFileName, task.UniqueProcessId, vad.Start)) outfd.write("Injection is {0}% similar to whitelist hook {1}\n".format(alike, whitelist_name)) #outfd.write(" hook: {0}\n".format(inject_hash)) #outfd.write(" whitelist: {0}\n".format(whitelist_hash)) skip = True continue if skip: continue content = address_space.zread(vad.Start, 64) outfd.write("Process: {0} Pid: {1} Address: {2:#x}\n".format( task.ImageFileName, task.UniqueProcessId, vad.Start)) outfd.write("Vad Tag: {0} Protection: {1}\n".format( vad.Tag, vadinfo.PROTECT_FLAGS.get(vad.u.VadFlags.Protection.v(), ""))) outfd.write("Flags: {0}\n".format(str(vad.u.VadFlags))) outfd.write("\n") outfd.write("{0}\n".format("\n".join( ["{0:#010x} {1:<48} {2}".format(vad.Start + o, h, ''.join(c)) for o, h, c in utils.Hexdump(content) ]))) outfd.write("\n") outfd.write("\n".join( ["{0:#x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(content, vad.Start) ])) # Dump the data if --dump-dir was supplied if self._config.DUMP_DIR: filename = os.path.join(self._config.DUMP_DIR, "process.{0:#x}.{1:#x}.dmp".format( task.obj_offset, vad.Start)) self.dump_vad(filename, vad, address_space) outfd.write("\n\n")
def render_text(self, outfd, data): for process, module, hook, addr_space in data: if not self._config.NO_WHITELIST: if process: process_name = str(process.ImageFileName) else: process_name = '' if self.whitelist(hook.hook_mode | hook.hook_type, process_name, hook.VictimModule, hook.HookModule, hook.Function): #debug.debug("Skipping whitelisted function: {0} {1} {2} {3}".format( # process_name, hook.VictimModule, hook.HookModule, # hook.Function)) continue if self._config.SSDEEP and has_pydeep: skip = False # read from the start of the page containing the hook, then hash it page_address = hook.hook_address & 0xFFFFF000 hook_buf = addr_space.zread(page_address, 0x1000) hook_hash = pydeep.hash_buf(hook_buf) # loop through all the whitelist hashes and compare for (whitelist_name, whitelist_hash) in whitelist_ssdeep: alike = pydeep.compare(hook_hash, whitelist_hash) # the comparison is greater than the threshold so display an informational message # then skip the rest of the output in normal malfind if alike > self._config.THRESHOLD: if process: outfd.write('Process: {0} ({1})\n'.format( process.UniqueProcessId, process.ImageFileName)) outfd.write( "Hook at 0x{0:x} in page 0x{1:x} is {2}% similar to whitelist hook {3}\n" .format(hook.hook_address, page_address, alike, whitelist_name)) #outfd.write(" hook: {0}\n".format(hook_hash)) #outfd.write(" whitelist: {0}\n".format(whitelist_hash)) outfd.write("\n") skip = True continue if skip: continue outfd.write("*" * 72 + "\n") outfd.write("Hook mode: {0}\n".format(hook.Mode)) outfd.write("Hook type: {0}\n".format(hook.Type)) if process: outfd.write('Process: {0} ({1})\n'.format( process.UniqueProcessId, process.ImageFileName)) outfd.write("Victim module: {0} ({1:#x} - {2:#x})\n".format( str(module.BaseDllName or '') or ntpath.basename(str(module.FullDllName or '')), module.DllBase, module.DllBase + module.SizeOfImage)) outfd.write("Function: {0}\n".format(hook.Detail)) outfd.write("Hook address: {0:#x}\n".format(hook.hook_address)) outfd.write("Hooking module: {0}\n\n".format(hook.HookModule)) for n, info in enumerate(hook.disassembled_hops): (address, data) = info s = [ "{0:#x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(data, int(address), bits="32bit" if hook.decode_bits == distorm3.Decode32Bits else "64bit") ] outfd.write("Disassembly({0}):\n{1}".format(n, "\n".join(s))) outfd.write("\n\n")
def check_for_jmp(self, thread, proc, addr_space): """ :param thread: _ETHREAD Object :param proc: _EPROCESS Object :param addr_space: Process's address space object :return: None, JMP/CALL is determined if thread.jmp_data exists Try to detect if there is a suspicious JMP or CALL in the first 12 bytes of the thread's entry point JMP/CALL that points to the same vad are considered to be legitimate. When we detect a JMP/CALL to another allocated memory in a new vad we might suspect someone tampered and injected malicious code. """ vad = thread.vad_object content = addr_space.read(thread.entry_point, 12) # Can't read thread's entry point from memory, might be paged if not content: return False # disassemble with distorm3, more accurate if has_distorm: # Get OS profile mode = addr_space.profile.metadata.get('memory_model') if mode == '64bit': mode = distorm3.Decode64Bits else: mode = distorm3.Decode32Bits disassemble_data = "\n".join(["{0:<#8x} {1:<32} {2}".format(o, h, i) \ for o, _size, i, h in \ distorm3.DecodeGenerator(int(thread.entry_point),content, mode)]) else: disassemble_data = "\n".join( ["{0:#010x} {1:<16} {2}".format(o, h, i) for o, i, h in malfind.Disassemble(content, int(thread.entry_point))]) # First check - if there is a jmp to an address jmp_list_to_addr = re.findall("\s*(?:call|jmp)\s*(0x[0-9a-f]+)\s*\n\s*", disassemble_data, re.I) # Second check - if there is a move and then jmp or call to the register jmp_list_to_register = re.findall("(0x[0-9a-f]+)\n0x[0-9a-f]+\s*[0-9a-f]+\s*(?:call|jmp)\s*[a-z]+\n", disassemble_data, re.I) jmp_list = jmp_list_to_addr + jmp_list_to_register # Check if we found a jmp if jmp_list: # Pass on every jmp for address in jmp_list: if not address: continue address = int(address, 16) # Check if the address is in the vad if vad.Start <= address <= vad.End: continue # Suspicious jump outside the vad range else: in_vad_range = self.check_where_in_vad(address) # Found a matching vad for the JMP address if in_vad_range: vad, vad_addr_space = in_vad_range thread.jmp_data.append(("vad", vad, address)) # Found a suspicious JMP, but didn't find a matching vad in process memory checking kernel else: in_kernel_space = self.check_where_in_kernel(address) if in_kernel_space: module_name, module_start = in_kernel_space thread.jmp_data.append(("kernel", (module_name, module_start), address)) else: thread.jmp_data.append(("Couldn't find location", '', address))
def get_json(self, data): results = {} addr_space = utils.load_as(self._config) # Compile the regular expression for filtering by driver name if self._config.regex != None: mod_re = re.compile(self._config.regex, re.I) else: mod_re = None mods = dict((mod.DllBase, mod) for mod in modules.lsmod(addr_space)) mod_addrs = sorted(mods.keys()) bits = addr_space.profile.metadata.get('memory_model', '32bit') for driver in data: header = driver.get_object_header() driver_name = str(header.NameInfo.Name or '') # Continue if a regex was supplied and it doesn't match if mod_re != None: if not (mod_re.search(driver_name) or mod_re.search(driver_name)): continue row = { 'DriverName': driver_name, 'DriverStart': int(driver.DriverStart), 'DriverSize': int(driver.DriverSize), 'DriverStartIo': int(driver.DriverStartIo), 'IrpFunctions': [], 'Devices': [], } # Write the address and owner of each IRP function for i, function in enumerate(driver.MajorFunction): function = driver.MajorFunction[i] module = tasks.find_module(mods, mod_addrs, function) irp = { 'index': int(i), 'FunctionName': str(MAJOR_FUNCTIONS[i]), 'FunctionAddress': int(function), 'Disassembly': [] } if module: irp['BaseDllName'] = str(module.BaseDllName or '') else: irp['BaseDllName'] = '' if self._config.verbose: data = addr_space.zread(function, 64) irp["Disassembly"] = [{ 'Address': int(o), "Bytes": str(h), "Instruction": str(i) } for o, i, h in malfind.Disassemble( data=data, start=function, bits=bits, stoponret=True)] row['IrpFunctions'].append(irp) for device in driver.devices(): device_header = obj.Object( "_OBJECT_HEADER", offset=device.obj_offset - device.obj_vm.profile.get_obj_offset( "_OBJECT_HEADER", "Body"), vm=device.obj_vm, native_vm=device.obj_native_vm) device_name = str(device_header.NameInfo.Name or '') dev = { 'Offset': int(device.obj_offset), 'DeviceName': device_name, 'DeviceCodes': DEVICE_CODES.get(device.DeviceType.v(), "UNKNOWN"), 'AttachedDevices': [] } level = 0 for att_device in device.attached_devices(): device_header = obj.Object( "_OBJECT_HEADER", offset=att_device.obj_offset - att_device.obj_vm.profile.get_obj_offset( "_OBJECT_HEADER", "Body"), vm=att_device.obj_vm, native_vm=att_device.obj_native_vm) device_name = str(device_header.NameInfo.Name or '') name = (device_name + " - " + str(att_device.DriverObject.DriverName or '')) attached_device = { 'Offset': att_device.obj_offset, 'DeviceName': name, 'DeviceCodes': DEVICE_CODES.get(att_device.DeviceType.v(), "UNKNOWN"), 'Level': level } level += 1 dev['AttachedDevices'].append(attached_device) row['Devices'].append(dev) _addr = str(row['DriverStart']) results[_addr] = row return results
def get_json(self, data): results = {} # Determine which filters the user wants to see if self._config.FILTER: filters = set(self._config.FILTER.split(',')) else: filters = set() for thread, addr_space, mods, mod_addrs, \ instances, hooked_tables, system_range, owner_name in data: # If the user didn't set filters, display all results. If # the user set one or more filters, only show threads # with matching results. tags = set([t for t, v in instances.items() if v.check()]) if filters and not filters & tags: continue row = { 'offset': int(thread.obj_offset), 'UniqueProcess': int(thread.Cid.UniqueProcess), 'UniqueThread': int(thread.Cid.UniqueThread), 'Tags': [str(i) for i in tags], 'CreateTime': str(thread.CreateTime), 'ExitTime': str(thread.ExitTime), 'ImageFileName': str(thread.owning_process().ImageFileName), 'Attached': str(thread.attached_process().ImageFileName), 'BasePriority': int(thread.Tcb.BasePriority), 'Priority': int(thread.Tcb.Priority), 'Teb': int(thread.Tcb.Teb), 'State': str(thread.Tcb.State), 'WaitReason': str(thread.Tcb.WaitReason), 'StartAddress': int(thread.StartAddress), "SystemThread": bool("SystemThread" in tags), "OwnerName": "", "Win32StartAddressValid": thread.SameThreadApcFlags & 1, "Win32StartAddress": int(thread.Win32StartAddress), 'ServiceTable': int(thread.Tcb.ServiceTable) if self.bits32 else "", "Win32Thread": int(thread.Tcb.Win32Thread), "CrossThreadFlags": int(thread.CrossThreadFlags), "TrapFrame": {}, 'SSDT': {}, "Disassembly": [] } # Print the registers if possible trapframe = thread.Tcb.TrapFrame.dereference_as("_KTRAP_FRAME") if trapframe and self.bits32: row["TrapFrame"] = { key: int(eval('trapframe.%s' % key, {'trapframe': trapframe})) for key in trapframe.members.keys() } # Disasemble the start address if possible if addr_space.is_valid_address(thread.StartAddress): buf = addr_space.zread(thread.StartAddress, 24) row["Disassembly"] = [{ 'Address': int(o), "Bytes": str(h), "Instruction": str(i) } for o, i, h in malfind.Disassemble(buf, thread.StartAddress.v())] # If its a system thread, get the owning module if row["SystemThread"]: owner = tasks.find_module(mods, mod_addrs, thread.StartAddress) if not owner is None: row["OwnerName"] = str(owner.BaseDllName or owner_name) if self.bits32: ssdt_obj = obj.Object("_SERVICE_DESCRIPTOR_TABLE", offset=thread.Tcb.ServiceTable, vm=addr_space) if ssdt_obj is not None: for i, desc in enumerate(ssdt_obj.Descriptors): row['SSDT'][str(i)] = { 'index': i, 'KiServiceTable': '', 'HookedSSDT': [] } if desc.is_valid(): row['SSDT'][str(i)][ 'KiServiceTable'] = desc.KiServiceTable.v() table = desc.KiServiceTable.v() if table not in hooked_tables.keys(): continue # no use for this data so far # # for (j, func_name, func_addr, mod_name) in hooked_tables[table]: # row['SSDT'][str(i)]['HookedSSDT'].append({'index': j, # 'FunctionName': func_name, # 'FunctionAddress': func_addr, # 'ModuleName': str(mod_name)}) results[str(row['offset'])] = row return results
def examine(self, addr_space, process, thread, routine, apc, timer): # We will now emulate through the instruction stream, starting at the APC handler, and see if anything fishy # goes on. Specifically, we will see if the APC calls VirtualProtect. If it does, we will see if it also # tries to jump to the newly-VirtualProtect'ed memory - a sure sign of Gargoyle-ness. VirtualProtect = self.findExport(process, addr_space, "KERNEL32.DLL", "VirtualProtect") VirtualProtectEx = self.findExport(process, addr_space, "KERNEL32.DLL", "VirtualProtectEx") # We'll need to set the process address space so that our badmem callback can use it later on. self.pas = process.get_process_address_space() self.emulationFaulted = None result = timerResult(process, thread, routine) self.dbgMsg("Timer %s APC %s routine %s in process %s ('%s') thread %s" % (hex(int(timer.obj_offset)), hex(int(apc.obj_offset)), hex(routine), hex(int(process.obj_offset)), process.ImageFileName, hex(thread.StartAddress))) unicornEng = Uc(UC_ARCH_X86, UC_MODE_32) # Populate the context from which to start emulating. # We use an arbitrary ESP, with a magic value to signify that the APC handler has returned. initialStackBase = 0x00000000f0000000 unicornEng.mem_map(initialStackBase, 2 * 1024 * 1024) unicornEng.mem_write(initialStackBase + 0x100 + 0, "\xbe\xba\xde\xc0") # We push the argument which the APC handler is given unicornEng.mem_write(initialStackBase + 0x100 + 4, apc.NormalContext.obj_vm.read(apc.NormalContext.obj_offset, 4)) unicornEng.reg_write(UC_X86_REG_ESP, initialStackBase + 0x100) # Set up our handlers, which will map memory on-demand from the debuggee unicornEng.hook_add(UC_HOOK_MEM_READ_UNMAPPED, self.badmem) unicornEng.hook_add(UC_HOOK_MEM_WRITE_INVALID, self.badmem) unicornEng.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED, self.badmem) # There's not really much point mapping the GDT, since Unicorn doesn't properly support paging. # See Unicorn's bug #947, "(x86) Emulated CPU is not translating virtual memory addresses". # Now, lets emulate some instructions! We won't get many, because Unicorn can't emulate a lot of things (like # segment-prefixed instructions, as used by wow64) but we'll get enough to detect most ROP chains. instrEmulated = 0 nextIns = routine memoryRange = None while instrEmulated < 10000: if self._config.VERBOSE > 0: print "Before instruction %d at %s:" % (instrEmulated, hex(nextIns)) print "CS:IP = %s:%s SS:SP = %s:%s" % ( hex(unicornEng.reg_read(UC_X86_REG_CS)), hex(unicornEng.reg_read(UC_X86_REG_EIP)), hex(unicornEng.reg_read(UC_X86_REG_SS)), hex(unicornEng.reg_read(UC_X86_REG_ESP))) instrStream = self.pas.read(nextIns, 15) for _, i, _ in malfind.Disassemble(instrStream, nextIns): print "\t%s\t%s" % (hex(nextIns), i) break # Attempt to emulate a single instruction try: unicornEng.emu_start(nextIns, nextIns + 0x10, count = 1) except unicorn.UcError as e1: break if self.emulationFaulted != None: break # Great, we emulated an instruction. Move on to the next instruction. nextIns = unicornEng.reg_read(UC_X86_REG_EIP) instrEmulated = instrEmulated + 1 # If we're now at our magic address, then our APC has completed executing entirely. That's all, folks. if nextIns == 0xc0debabe: break # Now we can check for some suspicious circumstance. esp = unicornEng.reg_read(UC_X86_REG_ESP) if esp == int(apc.NormalContext): result.didROP = "True" self.dbgMsg("APC has performed stack pivot; new stack is its context pointer") if VirtualProtect == None: # If we didn't find VirtualProtect, we can't go any further. I guess a stack pivot is a pretty big # red flag anyway. break if VirtualProtectEx != None: if nextIns == VirtualProtectEx: result.didAdjustPerms = "True" # Read the arguments to VirtualProtect, and the return address, from the stack returnAddress = struct.unpack("I", unicornEng.mem_read(esp - 0, 4))[0] memoryRange = struct.unpack("I", unicornEng.mem_read(esp + 8, 4))[0] result.adjustedAddresses.append(memoryRange) self.dbgMsg("VirtualProtectEx: Timer routine is adjusting memory permissions of range %s" % hex(memoryRange)) # Set the return address to whatever VirtualProtect would've returned to nextIns = returnAddress unicornEng.reg_write(UC_X86_REG_EIP, returnAddress) # Pop five args plus the return address off the (32bit) stack unicornEng.reg_write(UC_X86_REG_ESP, esp + (6*4)) if VirtualProtect != None: if nextIns == VirtualProtect: result.didAdjustPerms = "True" # Read the arguments to VirtualProtect, and the return address, from the stack returnAddress = struct.unpack("I", unicornEng.mem_read(esp - 0, 4))[0] memoryRange = struct.unpack("I", unicornEng.mem_read(esp + 4, 4))[0] result.adjustedAddresses.append(memoryRange) self.dbgMsg("VirtualProtect: Timer routine is adjusting memory permissions of range %s" % hex(memoryRange)) # Set the return address to whatever VirtualProtect would've returned to nextIns = returnAddress unicornEng.reg_write(UC_X86_REG_EIP, returnAddress) # Pop four args plus the return address off the (32bit) stack unicornEng.reg_write(UC_X86_REG_ESP, esp + (5 * 4)) if nextIns in result.adjustedAddresses: result.didJumpToAdjusted = "True" result.probablePayload = nextIns self.dbgMsg( "Timer routine is jumping to newly-executable code at %s!" % hex(memoryRange)) break yield result