def __enter__(self): # init LibVMI self.vmi = Libvmi(self.vm_name, init_flags=INIT_DOMAINNAME | INIT_EVENTS, partial=True) self.vmi.init_paging(flags=0) # catch every exception to force a clean exit with __exit__ # where vmi.destroy() must be called try: # determine debug context if not self.process: self.ctx = RawDebugContext(self.vmi) else: self.vmi.init_os() ostype = self.vmi.get_ostype() if ostype == VMIOS.WINDOWS: self.ctx = WindowsDebugContext(self.vmi, self.process) elif ostype == VMIOS.LINUX: self.ctx = LinuxDebugContext(self.vmi, self.process) else: raise RuntimeError('unhandled ostype: {}'.format( ostype.value)) self.ctx.attach() self.attached = True except: logging.exception('Exception while initializing debug context') return self
def __init__(self, vm_name, process_name): self.log = logging.getLogger(__class__.__name__) self.vm_name = vm_name self.target_name = process_name self.target_pid = None self.target_dtb = None self.vmi = Libvmi(self.vm_name, INIT_DOMAINNAME | INIT_EVENTS) self.kernel_base = self.get_kernel_base() logging.info('kernel base address: %s', hex(self.kernel_base))
def __init__(self, base, config, layered=False, **kwargs): self.as_assert(libvmi, "The LibVMI python bindings must be installed") addrspace.BaseAddressSpace.__init__(self, base, config, **kwargs) self.as_assert(base is None or layered, 'Must be first Address Space') self.as_assert(config.LOCATION.startswith("vmi://"), "Location doesn't start with vmi://") domain = config.LOCATION[len("vmi://"):] self.vmi = Libvmi(domain, partial=True) self.dtb = self.vmi.get_vcpu_reg(CR3, 0)
def get_backend(domain, listener, syscall_filtering): """Return backend based on libvmi configuration. If analyze if False, returns a dummy backend that does not analyze system calls. Returns None if the backend is missing """ libvmi = Libvmi(domain.name()) os_type = libvmi.get_ostype() try: return BACKENDS[os_type](domain, libvmi, listener, syscall_filtering) except KeyError: raise BackendNotFoundError('Unable to find an appropritate backend for' 'this OS: {}'.format(os_type))
def __init__(self, base=None, filename=None, session=None, **kwargs): self.as_assert(libvmi, "The LibVMI python bindings must be installed") self.as_assert(base is None, "must be first Address Space") self.session = session url = filename or (session and session.GetParameter("filename")) self.as_assert(url, "Filename must be specified in session (e.g. " "session.SetParameter('filename', 'vmi://domain').") self.as_assert(url.startswith(URL_PREFIX), "The domain must be passed with the URL prefix {}".format(URL_PREFIX)) domain = url[len(URL_PREFIX):] self.as_assert(domain, "domain name missing after {}".format(URL_PREFIX)) super(VMIAddressSpace, self).__init__(base=base, session=session, **kwargs) self.vmi = Libvmi(domain, partial=True)
def __init__(self, vm_name, process_name): self.log = logging.getLogger(__class__.__name__) self.vm_name = vm_name self.full_system_mode = False self.target_name = process_name self.target_pid = None if process_name is None: self.full_system_mode = True self.target_name = 'kernel' # kernel space is represented by PID 0 in LibVMI self.target_pid = 0 self.target_dtb = None self.vmi = Libvmi(self.vm_name, INIT_DOMAINNAME | INIT_EVENTS) self.kernel_base = self.get_kernel_base() if self.kernel_base: logging.info('kernel base address: %s', hex(self.kernel_base))
def main(args): vm_name = args['<vm_name>'] symbol = args['<symbol>'] # register SIGINT signal.signal(signal.SIGINT, signal_handler) with Libvmi(vm_name, INIT_DOMAINNAME | INIT_EVENTS) as vmi: vaddr = vmi.translate_ksym2v(symbol) debug_event = DebugEvent(debug_callback) num_vcpus = vmi.get_num_vcpus() sstep_event = SingleStepEvent(range(num_vcpus), enable=False, callback=sstep_callback) with pause(vmi): vmi.register_event(debug_event) vmi.register_event(sstep_event) # set dr0 to our address vmi.set_vcpureg(vaddr, X86Reg.DR0.value, 0) toggle_dr0(vmi, True) # listen while not interrupted: print("Waiting for events") vmi.listen(1000) print("Stop listening") with pause(vmi): vmi.clear_event(debug_event) vmi.clear_event(sstep_event)
def get_backend(domain, listener, syscall_filtering): """ Return a suitable backend based on guest operating system. :param domain: libvirt domain :returns: new backend instance :rtype: Backend :raises: BackendNotFoundError """ libvmi = Libvmi(domain.name()) os_type = libvmi.get_ostype() try: return BACKENDS[os_type](domain, libvmi, listener, syscall_filtering) except KeyError: raise BackendNotFoundError('Unable to find an appropritate backend for' 'this OS: {}'.format(os_type))
def main(args): logging.basicConfig(level=logging.INFO) vm_name = args['<vm_name>'] # register SIGINT signal.signal(signal.SIGINT, signal_handler) kvm_socket = {VMIInitData.KVMI_SOCKET: args['--kvmi-socket']} if args['--kvmi-socket'] else None with Libvmi(vm_name, INIT_DOMAINNAME | INIT_EVENTS, init_data=kvm_socket, partial=True) as vmi: # init paging to translate virtual addresses vmi.init_paging(0) with pause(vmi): # get current RIP on VCPU 0 rip = vmi.get_vcpureg(X86Reg.RIP.value, 0) # get DTB cr3 = vmi.get_vcpureg(X86Reg.CR3.value, 0) dtb = cr3 & ~0xfff # get gpa paddr = vmi.pagetable_lookup(dtb, rip) gfn = paddr >> 12 # define callback def cb_mem_event(vmi, event): logging.info("Mem event at RIP: %s, frame: %s, offset: %s, permissions: %s", hex(event.x86_regs.rip), hex(event.gla), hex(event.offset), event.out_access.name) mem_event = MemEvent(MemAccess.X, cb_mem_event, gfn=gfn) vmi.register_event(mem_event) # listen while not interrupted: vmi.listen(3000) logging.info("stop listening")
def main(args): logging.basicConfig(level=logging.INFO) vm_name = args['<vm_name>'] # register SIGINT signal.signal(signal.SIGINT, signal_handler) # 1 - init LibVMI kvm_socket = { VMIInitData.KVMI_SOCKET: args['--kvmi-socket'] } if args['--kvmi-socket'] else None with Libvmi(vm_name, INIT_DOMAINNAME | INIT_EVENTS, init_data=kvm_socket, partial=True) as vmi: counter = Counter() # 2 - define CR3 callback def cr3_callback(vmi, event): cr3_value = event.value logging.info("CR3 change: %s", hex(cr3_value)) counter[hex(cr3_value)] += 1 # 3 - define and register CR3-write event with pause(vmi): # register CR3-write event reg_event = RegEvent(X86Reg.CR3, RegAccess.W, cr3_callback) vmi.register_event(reg_event) # 4 - listen for events for i in range(0, 100): vmi.listen(500) logging.info(counter)
def vmiev(domain): vm_config = DOMAIN_CONFIGS[domain.name()] with Libvmi(domain.name(), INIT_DOMAINNAME | INIT_EVENTS, config_mode=VMIConfig.DICT, config=vm_config) as vmi: yield vmi
def main(args): if len(args) != 3: print("./mem_vmi.py <process_name> <string_addr>") return 1 vm_name = "ubuntu16.04" process_name = args[1] string_addr = args[2] with Libvmi( vm_name ) as vmi: #class Libvmi,the default parameters calls vmi_init_complete #init offsets values, init libvmi library tasks_offset = vmi.get_offset("linux_tasks") name_offset = vmi.get_offset("linux_name") pid_offset = vmi.get_offset("linux_pid") #pause vm for consistent memory access with pause(vmi): pids = get_pid_by_name(vmi, process_name) if not pids: print("Cannot find process %s" % process_name) pid = pids[0] print("process %s:%d" % (process_name, pid)) #get output string of exp string = vmi.read_str_va(int(string_addr, 16), pid) print("output:%s" % string) #alter output string of exp vmi.write_8_va(int(string_addr, 16), pid, 0x77) return 0
class VMIAddressSpace(addrspace.BaseAddressSpace): """ This address space can be used in conjunction with LibVMI and the Python bindings for LibVMI. The end result is that you can connect Volatility to view the memory of a running virtual machine from any virtualization platform that LibVMI supports. For this AS to be instantiated, we need the VM name to connect to. """ order = 90 def __init__(self, base, config, layered=False, **kwargs): self.as_assert(libvmi, "The LibVMI python bindings must be installed") addrspace.BaseAddressSpace.__init__(self, base, config, **kwargs) self.as_assert(base is None or layered, 'Must be first Address Space') self.as_assert(config.LOCATION.startswith("vmi://"), "Location doesn't start with vmi://") domain = config.LOCATION[len("vmi://"):] self.vmi = Libvmi(domain, partial=True) self.dtb = self.vmi.get_vcpu_reg(CR3, 0) def close(self): self.vmi.destroy() def read(self, addr, length): buffer, bytes_read = self.vmi.read_pa(addr, length) if bytes_read != length: raise RuntimeError('Error while reading physical memory at ' '{}'.format(hex(addr))) return buffer def zread(self, addr, length): buffer, bytes_read = self.vmi.read_pa(addr, length) if bytes_read != length: # fill with zeroes buffer += bytes(length - bytes_read).decode() return buffer def write(self, addr, data): bytes_written = self.vmi.write_pa(addr, data) if bytes_written != len(data): return False return True def is_valid_address(self, addr): if addr is None: return False return 4096 < addr < self.vmi.get_memsize() - 1 def get_available_addresses(self): yield (0, self.vmi.get_memsize())
def main(args): if len(args) != 2: print('./process-list.py <vm_name>') return 1 vm_name = args[1] with Libvmi(vm_name) as vmi: # get ostype os = vmi.get_ostype() # init offsets values tasks_offset = None name_offset = None pid_offset = None if os == VMIOS.LINUX: tasks_offset = vmi.get_offset("linux_tasks") name_offset = vmi.get_offset("linux_name") pid_offset = vmi.get_offset("linux_pid") elif os == VMIOS.WINDOWS: tasks_offset = vmi.get_offset("win_tasks") name_offset = vmi.get_offset("win_pname") pid_offset = vmi.get_offset("win_pid") else: logging.info("Unknown OS") return 1 # pause vm with pause(vmi): # demonstrate name and id accessors name = vmi.get_name() id = vmi.get_vmid() logging.info("Process listing for VM %s (id: %s)", name, id) if os == VMIOS.LINUX: list_head = vmi.translate_ksym2v("init_task") list_head += tasks_offset elif os == VMIOS.WINDOWS: list_head = vmi.read_addr_ksym("PsActiveProcessHead") else: return 1 cur_list_entry = list_head next_list_entry = vmi.read_addr_va(cur_list_entry, 0) while True: current_process = cur_list_entry - tasks_offset pid = vmi.read_32_va(current_process + pid_offset, 0) procname = vmi.read_str_va(current_process + name_offset, 0) logging.info("[%s] %s (struct addr:%s)", pid, procname, hex(current_process)) cur_list_entry = next_list_entry next_list_entry = vmi.read_addr_va(cur_list_entry, 0) if os == VMIOS.WINDOWS and next_list_entry == list_head: break elif os == VMIOS.LINUX and cur_list_entry == list_head: break
def main(args): if len(args) != 2: print('./module-list.py <vm_name>') return 1 vm_name = args[1] with Libvmi(vm_name) as vmi: # pause vm for consistent memory access with pause(vmi): next_module = None # get ostype os = vmi.get_ostype() if os == VMIOS.LINUX: next_module = vmi.read_addr_ksym("modules") elif os == VMIOS.WINDOWS: next_module = vmi.read_addr_ksym("PsLoadedModuleList") else: logging.info("Unknown OS") list_head = next_module # walk the module list while True: # follow the next pointer tmp_next = vmi.read_addr_va(next_module, 0) # if we are back at the list head, we are done if list_head == tmp_next: break modname = None # print out the module name if os == VMIOS.LINUX: if page_mode == PageMode.IA32E: modname = vmi.read_str_va(next_module + 16, 0) else: modname = vmi.read_str_va(next_module + 8, 0) elif os == VMIOS.WINDOWS: page_mode = vmi.get_page_mode(0) if page_mode == PageMode.IA32E: modname = vmi.read_unicode_str_va( next_module + 0x58, 0) else: modname = vmi.read_unicode_str_va( next_module + 0x2c, 0) else: logging.info("Unkown OS") if modname is not None: logging.info(modname) next_module = tmp_next
def __enter__(self): # init LibVMI self.vmi = Libvmi(self.vm_name, init_flags=INIT_DOMAINNAME | INIT_EVENTS, partial=True) self.vmi.init_paging(flags=0) # catch every exception to force a clean exit with __exit__ # where vmi.destroy() must be called try: # determine debug context if not self.process: self.ctx = RawDebugContext(self.vmi) else: self.vmi.init_os() ostype = self.vmi.get_ostype() if ostype == VMIOS.WINDOWS: self.ctx = WindowsDebugContext(self.vmi, self.process) elif ostype == VMIOS.LINUX: self.ctx = LinuxDebugContext(self.vmi, self.process) else: raise RuntimeError('unhandled ostype: {}'.format( ostype.value)) # register some events # register interrupt event self.int_event = IntEvent(self.cb_on_int3) self.vmi.register_event(self.int_event) # single step event to handle wrong hits by sw breakpoints # enabled via EventResponse.TOGGLE_SINGLESTEP num_vcpus = self.vmi.get_num_vcpus() self.ss_event_recoil = SingleStepEvent(range(num_vcpus), self.cb_on_sstep_recoil, enable=False) self.vmi.register_event(self.ss_event_recoil) self.ctx.attach() self.attached = True except: logging.exception('Exception while initializing debug context') return self
def __init__(self, base=None, filename=None, session=None, **kwargs): self.as_assert(libvmi, "The LibVMI python bindings must be installed") self.as_assert(base is None, "must be first Address Space") self.session = session url = filename or (session and session.GetParameter("filename")) self.as_assert( url, "Filename must be specified in session (e.g. " "session.SetParameter('filename', 'vmi:///domain').") vmi_url = urlparse(url) self.as_assert(vmi_url.scheme == SCHEME, "URL scheme must be vmi://") self.as_assert(vmi_url.path, "No domain name specified") domain = vmi_url.path[1:] # hypervisor specified ? self.mode = None hypervisor = vmi_url.netloc if hypervisor: self.mode = VMIMode[hypervisor.upper()] # query parameters ? self.volatile = True if vmi_url.query: params = parse_qs(vmi_url.query, strict_parsing=True) try: self.volatile = strtobool((params['volatile'][0])) except KeyError: raise RuntimeError('Invalid query parameters in vmi:// URI') # build Libvmi instance super(VMIAddressSpace, self).__init__(base=base, session=session, **kwargs) self.vmi = Libvmi(domain, mode=self.mode, partial=True) self.min_addr = 0 self.max_addr = self.vmi.get_memsize() - 1 # pause in case volatile has been disabled if not self.volatile: self.vmi.pause_vm() # register flush hook to destroy vmi instance when session.Flush() is called session.register_flush_hook(self, self.close)
def _init_vmi(self): """ Initialize LibVMI """ self._vmi = Libvmi(self._vm.name) # get ostype self._os = self._vmi.get_ostype() # init offsets values self._tasks_offset = None self._name_offset = None self._pid_offset = None if self._os == VMIOS.LINUX: self._tasks_offset = self._vmi.get_offset("linux_tasks") self._name_offset = self._vmi.get_offset("linux_name") self._pid_offset = self._vmi.get_offset("linux_pid") elif self._os == VMIOS.WINDOWS: self._tasks_offset = self._vmi.get_offset("win_tasks") self._name_offset = self._vmi.get_offset("win_pname") self._pid_offset = self._vmi.get_offset("win_pid") else: self.logger.error("Unknown OS") return 0 return 1
def main(args): if len(args) != 3: print('Incorrect number of args', args) print('arg 2', args[1]) return init_config() vm_name = args[1] p_id = args[2] print('Received : vm_name = ' + vm_name + ' , p_id = ' + p_id) vmi = Libvmi(vm_name) result = change_uid_process(vmi, p_id) if result: print("uid changed successfully!") else: print("something went wrong!")
class VMIAddressSpace(addrspace.BaseAddressSpace): """An address space which operates on top of Libvmi's interface.""" __abstract = False __name = "vmi" order = 90 __image = True def __init__(self, base=None, filename=None, session=None, **kwargs): self.as_assert(libvmi, "The LibVMI python bindings must be installed") self.as_assert(base is None, "must be first Address Space") self.session = session url = filename or (session and session.GetParameter("filename")) self.as_assert(url, "Filename must be specified in session (e.g. " "session.SetParameter('filename', 'vmi://domain').") self.as_assert(url.startswith(URL_PREFIX), "The domain must be passed with the URL prefix {}".format(URL_PREFIX)) domain = url[len(URL_PREFIX):] self.as_assert(domain, "domain name missing after {}".format(URL_PREFIX)) super(VMIAddressSpace, self).__init__(base=base, session=session, **kwargs) self.vmi = Libvmi(domain, partial=True) def close(self): self.vmi.destroy() def read(self, addr, size): buffer, bytes_read = self.vmi.read_pa(addr, size) if bytes_read != size: raise RuntimeError('Error while reading physical memory at {}'.format(hex(addr))) return buffer def write(self, addr, data): bytes_written = self.vmi.write_pa(addr, data) if bytes_written != len(data): return False return True def is_valid_address(self, addr): if addr is None: return False return 4096 < addr < self.vmi.get_memsize() - 1 def get_available_addresses(self): yield (4096, self.vmi.get_memsize() - 4096) def get_mappings(self, start=0, end=2 ** 64): yield addrspace.Run(start=0, end=self.vmi.get_memsize(), file_offset=0, address_space=self)
def main(args): if len(args) != 2: print('./memaccess-event.py <vm_name>') return 1 vm_name = args[1] # register SIGINT signal.signal(signal.SIGINT, signal_handler) with Libvmi(vm_name, INIT_DOMAINNAME | INIT_EVENTS) as vmi: reg_event = RegEvent(X86Reg.CR3, RegAccess.W, callback) vmi.register_event(reg_event) # listen while not interrupted: print("Waiting for events") vmi.listen(3000) print("Stop listening")
def main(args): vm_name = args['<vm_name>'] symbol = args['<symbol>'] sstep_enabled = args['--sstep'] # register SIGINT signal.signal(signal.SIGINT, signal_handler) with Libvmi(vm_name, INIT_DOMAINNAME | INIT_EVENTS) as vmi: vaddr = vmi.translate_ksym2v(symbol) paddr = vmi.translate_kv2p(vaddr) frame = paddr >> 12 print("symbol: {} vaddr: {} paddr: {} frame: {}".format( symbol, hex(vaddr), hex(paddr), hex(frame))) user_data = { 'symbol': symbol, 'target_vaddr': vaddr, 'target_gfn': frame, 'mem_event': None, 'sstep': sstep_enabled } num_vcpus = vmi.get_num_vcpus() ss_event = SingleStepEvent(range(num_vcpus), cb_ss_event, data=user_data, enable=False) mem_event = MemEvent(MemAccess.X, cb_mem_event, gfn=frame, data=user_data) user_data['mem_event'] = mem_event with pause(vmi): vmi.register_event(ss_event) vmi.register_event(mem_event) # listen while not interrupted: print("Waiting for events ({})".format(vmi.are_events_pending())) vmi.listen(3000) print("Stop listening") with pause(vmi): vmi.clear_event(mem_event)
def main(args): if len(args) != 2: print('./singlestep-event.py <vm_name>') return 1 vm_name = args[1] # register SIGINT signal.signal(signal.SIGINT, signal_handler) counter = 0 with Libvmi(vm_name, INIT_DOMAINNAME | INIT_EVENTS) as vmi: num_vcpus = vmi.get_num_vcpus() ss_event = SingleStepEvent(range(num_vcpus), callback, data=counter) vmi.register_event(ss_event) # listen while not interrupted: print("Waiting for events") vmi.listen(500) print("Stop listening") counter = ss_event.data print("Singlestepped {} instructions".format(counter))
def main(args): logging.basicConfig(level=logging.INFO) vm_name = args['<vm_name>'] # register SIGINT signal.signal(signal.SIGINT, signal_handler) kvm_socket = { VMIInitData.KVMI_SOCKET: args['--kvmi-socket'] } if args['--kvmi-socket'] else None with Libvmi(vm_name, INIT_DOMAINNAME | INIT_EVENTS, init_data=kvm_socket, partial=True) as vmi: msr_counter = Counter() def msr_callback(vmi, event): try: name = MSR_NAME[event.msr] except KeyError: name = 'MSR' logging.info("%s %s = %s", name, hex(event.msr), hex(event.value)) msr_counter[event.msr] += 1 # EternalBlue exploitation ? if msr_counter[0x176] > 1: logging.warn('MSR 0x176 modified %s times !', msr_counter[0x176]) with pause(vmi): # register MSR event reg_event = RegEvent(MSR.ALL, RegAccess.W, msr_callback) vmi.register_event(reg_event) logging.info("listening") while not interrupted: vmi.listen(500)
def __init__(self, base=None, filename=None, session=None, **kwargs): self.as_assert(libvmi, "The LibVMI python bindings must be installed") self.as_assert(base is None, "must be first Address Space") self.session = session url = filename or (session and session.GetParameter("filename")) self.as_assert(url, "Filename must be specified in session (e.g. " "session.SetParameter('filename', 'vmi:///domain').") vmi_url = urlparse(url) self.as_assert(vmi_url.scheme == SCHEME, "URL scheme must be vmi://") self.as_assert(vmi_url.path, "No domain name specified") domain = vmi_url.path[1:] # hypervisor specified ? self.mode = None hypervisor = vmi_url.netloc if hypervisor: self.mode = VMIMode[hypervisor.upper()] # query parameters ? self.volatile = True if vmi_url.query: params = parse_qs(vmi_url.query, strict_parsing=True) try: self.volatile = strtobool((params['volatile'][0])) except KeyError: raise RuntimeError('Invalid query parameters in vmi:// URI') # build Libvmi instance super(VMIAddressSpace, self).__init__(base=base, session=session, **kwargs) self.vmi = Libvmi(domain, mode=self.mode, partial=True) self.min_addr = 0 self.max_addr = self.vmi.get_memsize() - 1 # pause in case volatile has been disabled if not self.volatile: self.vmi.pause_vm() # register flush hook to destroy vmi instance when session.Flush() is called session.register_flush_hook(self, self.close)
"python /usr/src/volatility/vol.py --profile=LinuxDebian8x64 -f /mnt/mem linux_hidden_modules" ).read() loaded_mods = os.popen( "python /usr/src/volatility/vol.py --profile=LinuxDebian8x64 -f /mnt/mem linux_lsmod" ).read() sp_network = os.popen( "python /usr/src/volatility/vol.py --profile=LinuxDebian8x64 -f /mnt/mem linux_netstat -U | grep -o '[a-z0-9A-Z]*/[0-9]*' | awk -F'/' '{print $2}' | sort | uniq" ).read() sp_lsmod = os.popen( "python /usr/src/volatility/vol.py --profile=LinuxDebian8x64 -f /mnt/mem linux_lsmod | awk '{print $2" "$3}'").read() return (network, hidden_mods, loaded_mods, sp_network, sp_lsmod) if (len(sys.argv) == 4): vmi = Libvmi(sys.argv[1]) f = open(sys.argv[3], 'w') f1 = open("net_be", 'w') f2 = open("lsmod_be", "w") f3 = open("pslist_be", "w") for pid, procname in list_processes(vmi): f.write("%s %s\n" % (pid, procname)) f3.write("%s %s\n" % (pid, procname)) (network, hidden_mods, loaded_mods, sp_network, sp_lsmod) = list_other() f.write("\n%s\n" % network) f.write("%s\n" % hidden_mods) f.write("%s" % loaded_mods) f1.write("%s" % sp_network) f2.write("%s" % sp_lsmod)
class VMIAddressSpace(addrspace.BaseAddressSpace): """An address space which operates on top of Libvmi's interface.""" __abstract = False __name = "vmi" order = 90 volatile = True __image = True def __init__(self, base=None, filename=None, session=None, **kwargs): self.as_assert(libvmi, "The LibVMI python bindings must be installed") self.as_assert(base is None, "must be first Address Space") self.session = session url = filename or (session and session.GetParameter("filename")) self.as_assert( url, "Filename must be specified in session (e.g. " "session.SetParameter('filename', 'vmi:///domain').") vmi_url = urlparse(url) self.as_assert(vmi_url.scheme == SCHEME, "URL scheme must be vmi://") self.as_assert(vmi_url.path, "No domain name specified") domain = vmi_url.path[1:] # hypervisor specified ? self.mode = None hypervisor = vmi_url.netloc if hypervisor: self.mode = VMIMode[hypervisor.upper()] # query parameters ? self.volatile = True if vmi_url.query: params = parse_qs(vmi_url.query, strict_parsing=True) try: self.volatile = strtobool((params['volatile'][0])) except KeyError: raise RuntimeError('Invalid query parameters in vmi:// URI') # build Libvmi instance super(VMIAddressSpace, self).__init__(base=base, session=session, **kwargs) self.vmi = Libvmi(domain, mode=self.mode, partial=True) self.min_addr = 0 self.max_addr = self.vmi.get_memsize() - 1 # pause in case volatile has been disabled if not self.volatile: self.vmi.pause_vm() # register flush hook to destroy vmi instance when session.Flush() is called session.register_flush_hook(self, self.close) def close(self): if not self.volatile: self.vmi.resume_vm() self.vmi.destroy() def read(self, addr, size): buffer, _ = self.vmi.read_pa(addr, size, padding=True) return buffer def write(self, addr, data): bytes_written = self.vmi.write_pa(addr, data) if bytes_written != len(data): return False return True def is_valid_address(self, addr): if addr is None: return False return self.min_addr <= addr <= self.max_addr def get_available_addresses(self): yield (self.min_addr, self.max_addr) def get_mappings(self, start=0, end=2**64): yield addrspace.Run(start=self.min_addr, end=self.max_addr, file_offset=0, address_space=self)
class VMIAddressSpace(addrspace.BaseAddressSpace): """An address space which operates on top of Libvmi's interface.""" __abstract = False __name = "vmi" order = 90 volatile = True __image = True def __init__(self, base=None, filename=None, session=None, **kwargs): self.as_assert(libvmi, "The LibVMI python bindings must be installed") self.as_assert(base is None, "must be first Address Space") self.session = session url = filename or (session and session.GetParameter("filename")) self.as_assert(url, "Filename must be specified in session (e.g. " "session.SetParameter('filename', 'vmi:///domain').") vmi_url = urlparse(url) self.as_assert(vmi_url.scheme == SCHEME, "URL scheme must be vmi://") self.as_assert(vmi_url.path, "No domain name specified") domain = vmi_url.path[1:] # hypervisor specified ? self.mode = None hypervisor = vmi_url.netloc if hypervisor: self.mode = VMIMode[hypervisor.upper()] # query parameters ? self.volatile = True if vmi_url.query: params = parse_qs(vmi_url.query, strict_parsing=True) try: self.volatile = strtobool((params['volatile'][0])) except KeyError: raise RuntimeError('Invalid query parameters in vmi:// URI') # build Libvmi instance super(VMIAddressSpace, self).__init__(base=base, session=session, **kwargs) self.vmi = Libvmi(domain, mode=self.mode, partial=True) self.min_addr = 0 self.max_addr = self.vmi.get_memsize() - 1 # pause in case volatile has been disabled if not self.volatile: self.vmi.pause_vm() # register flush hook to destroy vmi instance when session.Flush() is called session.register_flush_hook(self, self.close) def close(self): if not self.volatile: self.vmi.resume_vm() self.vmi.destroy() def read(self, addr, size): buffer, _ = self.vmi.read_pa(addr, size, padding=True) return buffer def write(self, addr, data): bytes_written = self.vmi.write_pa(addr, data) if bytes_written != len(data): return False return True def is_valid_address(self, addr): if addr is None: return False return self.min_addr <= addr <= self.max_addr def get_available_addresses(self): yield (self.min_addr, self.max_addr) def get_mappings(self, start=0, end=2 ** 64): yield addrspace.Run(start=self.min_addr, end=self.max_addr, file_offset=0, address_space=self)
def vmi(domain): vm_config = DOMAIN_CONFIGS[domain.name()] with Libvmi(domain.name(), config_mode=VMIConfig.DICT, config=vm_config) as vmi: yield vmi
class LibVMIStub(GDBStub): def __init__(self, conn, addr, vm_name, process): super().__init__(conn, addr) self.vm_name = vm_name self.process = process self.cmd_to_handler = { GDBCmd.GEN_QUERY_GET: self.gen_query_get, GDBCmd.GEN_QUERY_SET: self.gen_query_set, GDBCmd.SET_THREAD_ID: self.set_thread_id, GDBCmd.TARGET_STATUS: self.target_status, GDBCmd.READ_REGISTERS: self.read_registers, GDBCmd.WRITE_REGISTERS: self.write_registers, GDBCmd.DETACH: self.detach, GDBCmd.READ_MEMORY: self.read_memory, GDBCmd.WRITE_MEMORY: self.write_memory, GDBCmd.WRITE_DATA_MEMORY: self.write_data_memory, GDBCmd.CONTINUE: self.cont_execution, GDBCmd.SINGLESTEP: self.singlestep, GDBCmd.IS_THREAD_ALIVE: self.is_thread_alive, GDBCmd.REMOVE_XPOINT: self.remove_xpoint, GDBCmd.INSERT_XPOINT: self.insert_xpoint, GDBCmd.BREAKIN: self.breakin, GDBCmd.V_FEATURES: self.v_features, GDBCmd.KILL_REQUEST: self.kill_request, } self.features = { b'multiprocess': False, b'swbreak': True, b'hwbreak': False, b'qRelocInsn': False, b'fork-events': False, b'vfork-events': False, b'exec-events': False, b'vContSupported': True, b'QThreadEvents': False, b'QStartNoAckMode': True, b'no-resumed': False, b'xmlRegisters': False, b'qXfer:memory-map:read': True } def __enter__(self): # init LibVMI self.vmi = Libvmi(self.vm_name, init_flags=INIT_DOMAINNAME | INIT_EVENTS, partial=True) self.vmi.init_paging(flags=0) # catch every exception to force a clean exit with __exit__ # where vmi.destroy() must be called try: # determine debug context if not self.process: self.ctx = RawDebugContext(self.vmi) else: self.vmi.init_os() ostype = self.vmi.get_ostype() if ostype == VMIOS.WINDOWS: self.ctx = WindowsDebugContext(self.vmi, self.process) elif ostype == VMIOS.LINUX: self.ctx = LinuxDebugContext(self.vmi, self.process) else: raise RuntimeError('unhandled ostype: {}'.format( ostype.value)) self.ctx.attach() self.attached = True except: logging.exception('Exception while initializing debug context') return self def __exit__(self, type, value, traceback): try: self.ctx.detach() self.attached = False self.ctx.bpm.restore_opcodes() except: logging.exception('Exception while detaching from debug context') finally: self.vmi.destroy() @lru_cache(maxsize=None) def get_memory_map_xml(self): # retrieve list of maps root = etree.Element('memory-map') for page_info in self.vmi.get_va_pages(self.ctx.get_dtb()): # <memory type="ram" start="addr" length="length"/> addr = str(hex(page_info.vaddr)) size = str(hex(page_info.size)) region = etree.Element('memory', type='ram', start=addr, length=size) root.append(region) doctype = '<!DOCTYPE memory-map ' \ 'PUBLIC "+//IDN gnu.org//DTD GDB Memory Map V1.0//EN"' \ ' "http://sourceware.org/gdb/gdb-memory-map.dtd">' xml = etree.tostring(root, xml_declaration=True, doctype=doctype, encoding='UTF-8') return xml # commands def gen_query_get(self, packet_data): if re.match(b'Supported', packet_data): reply = self.set_supported_features(packet_data) pkt = GDBPacket(reply) self.send_packet(pkt) return True if re.match(b'TStatus', packet_data): # Ask the stub if there is a trace experiment running right now # reply: No trace has been run yet self.send_packet(GDBPacket(b'T0;tnotrun:0')) return True if re.match(b'TfV', packet_data): # TODO return False if re.match(b'fThreadInfo', packet_data): reply = b'm' for thread in self.ctx.list_threads(): if reply != b'm': reply += b',' reply += b'%x' % thread.id self.send_packet(GDBPacket(reply)) return True if re.match(b'sThreadInfo', packet_data): # send end of thread list self.send_packet(GDBPacket(b'l')) return True m = re.match(b'ThreadExtraInfo,(?P<thread_id>.+)', packet_data) if m: tid = int(m.group('thread_id'), 16) thread = self.ctx.get_thread(tid) if not thread: return False self.send_packet(GDBPacket(thread.name.encode())) return True if re.match(b'Attached', packet_data): # attach existing process: 0 # attach new process: 1 self.send_packet(GDBPacket(b'0')) return True if re.match(b'C', packet_data): # return current thread id self.send_packet(GDBPacket(b'QC%x' % self.ctx.cur_tid)) return True m = re.match(b'Xfer:memory-map:read::(?P<offset>.*),(?P<length>.*)', packet_data) if m: offset = int(m.group('offset'), 16) length = int(m.group('length'), 16) xml = self.get_memory_map_xml() chunk = xml[offset:offset + length] msg = b'm%s' % chunk if len(chunk) < length or offset + length >= len(xml): # last chunk msg = b'l%s' % chunk self.send_packet(GDBPacket(msg)) return True return False def gen_query_set(self, packet_data): if re.match(b'StartNoAckMode', packet_data): self.no_ack = True self.send_packet(GDBPacket(b'OK')) # read last ack c = self.sock.recv(1) if c == b'+': return True else: return False return False def set_thread_id(self, packet_data): m = re.match(b'(?P<op>[cg])(?P<tid>([0-9a-f])+|-1)', packet_data) if m: op = m.group('op') tid = int(m.group('tid'), 16) self.log.debug('Current thread: %s', tid) self.ctx.cur_tid = tid # TODO op, Enn self.send_packet(GDBPacket(b'OK')) return True return False def target_status(self, packet_data): msg = b'S%.2x' % GDBSignal.TRAP.value self.send_packet(GDBPacket(msg)) return True def kill_request(self, packet_data): self.attached = False return True def read_registers(self, packet_data): addr_width = self.vmi.get_address_width() if addr_width == 4: pack_fmt = '@I' else: pack_fmt = '@Q' cur_thread = self.ctx.get_thread() regs = cur_thread.read_registers() gen_regs_32 = [ X86Reg.RAX, X86Reg.RCX, X86Reg.RDX, X86Reg.RBX, X86Reg.RSP, X86Reg.RBP, X86Reg.RSI, X86Reg.RDI, X86Reg.RIP ] gen_regs_64 = [ X86Reg.R9, X86Reg.R10, X86Reg.R11, X86Reg.R12, X86Reg.R13, X86Reg.R14, X86Reg.R15 ] # not available through libvmi seg_regs = [x + 1 for x in range(0, 6)] # write general registers msg = b''.join( [hexlify(struct.pack(pack_fmt, regs[r])) for r in gen_regs_32]) if addr_width == 8: msg += b''.join( [hexlify(struct.pack(pack_fmt, regs[r])) for r in gen_regs_64]) # write eflags msg += hexlify(struct.pack(pack_fmt, regs[X86Reg.RFLAGS])) # write segment registers msg += b''.join([hexlify(struct.pack(pack_fmt, r)) for r in seg_regs]) self.send_packet(GDBPacket(msg)) return True def write_registers(self, packet_data): addr_width = self.vmi.get_address_width() if addr_width == 4: pack_fmt = '@I' else: pack_fmt = '@Q' gen_regs_32 = [ X86Reg.RAX, X86Reg.RCX, X86Reg.RDX, X86Reg.RBX, X86Reg.RSP, X86Reg.RBP, X86Reg.RSI, X86Reg.RDI, X86Reg.RIP ] gen_regs_64 = [ X86Reg.R9, X86Reg.R10, X86Reg.R11, X86Reg.R12, X86Reg.R13, X86Reg.R14, X86Reg.R15 ] # TODO parse the entire buffer # regs = Registers() regs = self.vmi.get_vcpuregs(0) iter = struct.iter_unpack(pack_fmt, unhexlify(packet_data)) for r in gen_regs_32: value, *rest = next(iter) logging.debug('%s: %x', r.name, value) regs[r] = value # 64 bits ? if addr_width == 8: for r in gen_regs_64: value, *rest = next(iter) logging.debug('%s: %x', r.name, value) regs[r] = value # eflags value, *rest = next(iter) regs[X86Reg.RFLAGS] = value # TODO segment registers try: self.vmi.set_vcpuregs(regs, 0) except LibvmiError: return False else: self.send_packet(GDBPacket(b'OK')) return True def detach(self, packet_data): # detach self.attached = False try: self.vmi.resume_vm() except LibvmiError: pass self.send_packet(GDBPacket(b'OK')) return True def read_memory(self, packet_data): m = re.match(b'(?P<addr>.*),(?P<length>.*)', packet_data) if m: addr = int(m.group('addr'), 16) length = int(m.group('length'), 16) # TODO partial read try: buffer, bytes_read = self.vmi.read( self.ctx.get_access_context(addr), length) except LibvmiError: return False else: self.send_packet(GDBPacket(hexlify(buffer))) return True return False def write_memory(self, packet_data): m = re.match(b'(?P<addr>.+),(?P<length>.+):(?P<data>.+)', packet_data) if m: addr = int(m.group('addr'), 16) length = int(m.group('length'), 16) data = unhexlify(m.group('data')) # TODO partial write try: bytes_written = self.vmi.write( self.ctx.get_access_context(addr), data) except LibvmiError: return False else: self.send_packet(GDBPacket(b'OK')) return True return False def write_data_memory(self, packet_data): # ‘X addr,length:XX…’ m = re.match(b'(?P<addr>.+),(?P<length>.+):(?P<data>.*)', packet_data) if m: addr = int(m.group('addr'), 16) length = int(m.group('length'), 16) data = m.group('data') # TODO partial write try: bytes_written = self.vmi.write( self.ctx.get_access_context(addr), data) except LibvmiError: return False else: self.send_packet(GDBPacket(b'OK')) return True return False def cont_execution(self, packet_data): # TODO resume execution at addr addr = None m = re.match(b'(?P<addr>.+)', packet_data) if m: addr = int(m.group('addr'), 16) return False self.action_continue() self.send_packet(GDBPacket(b'OK')) # TODO race condition if listen thread started by action_continue # sends a packet before our 'OK' reply return True def singlestep(self, packet_data): # TODO resume execution at addr addr = None m = re.match(b'(?P<addr>.+)', packet_data) if m: addr = int(m.group('addr'), 16) return False self.ctx.bpm.wait_process_scheduled() self.ctx.bpm.singlestep_once() msg = b'S%.2x' % GDBSignal.TRAP.value self.send_packet(GDBPacket(msg)) return True def is_thread_alive(self, packet_data): m = re.match(b'(?P<tid>.+)', packet_data) if m: tid = int(m.group('tid'), 16) thread = self.ctx.get_thread(tid) if not thread: # TODO Err XX return False reply = None if thread.is_alive(): reply = b'OK' else: # TODO thread is dead reply = b'EXX' self.send_packet(GDBPacket(reply)) return True return False def remove_xpoint(self, packet_data): # ‘z type,addr,kind’ m = re.match(b'(?P<type>[0-9]),(?P<addr>.+),(?P<kind>.+)', packet_data) if not m: return False btype = int(m.group('type')) addr = int(m.group('addr'), 16) # kind -> size of breakpoint kind = int(m.group('kind'), 16) if btype == 0: # software breakpoint self.ctx.bpm.del_swbp(addr) self.send_packet(GDBPacket(b'OK')) return True return False def insert_xpoint(self, packet_data): # ‘Z type,addr,kind’ m = re.match(b'(?P<type>[0-9]),(?P<addr>.+),(?P<kind>.+)', packet_data) if not m: return False btype = int(m.group('type')) addr = int(m.group('addr'), 16) # kind -> size of breakpoint kind = int(m.group('kind'), 16) if btype == 0: # software breakpoint cb_data = { 'stub': self, 'stop_listen': self.ctx.bpm.stop_listen, } self.ctx.bpm.add_swbp(addr, kind, self.ctx.cb_on_swbreak, cb_data) self.send_packet(GDBPacket(b'OK')) return True return False def breakin(self, packet_data): # stop event thread self.ctx.bpm.stop_listening() self.ctx.attach() msg = b'S%.2x' % GDBSignal.TRAP.value self.send_packet(GDBPacket(msg)) return True def v_features(self, packet_data): if re.match(b'MustReplyEmpty', packet_data): # reply empty string # TODO refactoring, this should be treated as an unknown packet self.send_packet(GDBPacket(b'')) return True if re.match(b'Cont\?', packet_data): # query the list of supported actions for vCont # reply: vCont[;action…] # we do not support continue or singlestep with a signal # but we have to advertise this to GDB, otherwise it won't use vCont self.send_packet(GDBPacket(b'vCont;c;C;s;S')) return True m = re.match(b'Cont(;(?P<action>[sc])(:(?P<tid>.*?))?).*', packet_data) if m: # vCont[;action[:thread-id]]… # we don't support threads action = m.group('action') if action == b's': self.ctx.bpm.wait_process_scheduled() self.ctx.bpm.singlestep_once() self.send_packet_noack( GDBPacket(b'T%.2x' % GDBSignal.TRAP.value)) return True if action == b'c': self.action_continue() return True if re.match(b'Kill;(?P<pid>[a-fA-F0-9]).+', packet_data): # vKill;pid # ignore pid, and don't kill the process anyway # just detach from the target # sent when GDB client has a ^D self.attached = False self.send_packet(GDBPacket(b'OK')) return True return False # helpers def action_continue(self): self.vmi.resume_vm() # start listening on VMI events, asynchronously self.ctx.bpm.listen(block=False) def set_supported_features(self, packet_data): # split string and get features in a list # trash 'Supported req_features = re.split(b'[:|;]', packet_data)[1:] for f in req_features: if f[-1:] in [b'+', b'-']: name = f[:-1] value = True if f[-1:] == b'+' else False else: groups = f.split(b'=') name = groups[0] value = groups[1] # TODO check supported features reply_msg = b'PacketSize=%x' % PACKET_SIZE for name, value in self.features.items(): if isinstance(value, bool): reply_msg += b';%s%s' % (name, b'+' if value else b'-') else: reply_msg += b';%s=%s' % (name, value) return reply_msg
class ProcessChecker: whlte_list = ['sshd', 'insmod', 'kworker/0:2', 'kworker/u2:2'] def __init__(self, vm, callback=None, interval=10): self._vm = vm self._callback = callback self._init_vmi() self.logger = setup_logger( self._vm.name, self._vm.name + '/' + self._vm.name + '.log', logging.INFO) self._ori_ps_list = self._get_process_list() self._ori_ps_set = set(self._ori_ps_list.keys()) self._timer = RepeatableTimer(interval, self.check_process) self._dump_enabled = False def _init_vmi(self): """ Initialize LibVMI """ self._vmi = Libvmi(self._vm.name) # get ostype self._os = self._vmi.get_ostype() # init offsets values self._tasks_offset = None self._name_offset = None self._pid_offset = None if self._os == VMIOS.LINUX: self._tasks_offset = self._vmi.get_offset("linux_tasks") self._name_offset = self._vmi.get_offset("linux_name") self._pid_offset = self._vmi.get_offset("linux_pid") elif self._os == VMIOS.WINDOWS: self._tasks_offset = self._vmi.get_offset("win_tasks") self._name_offset = self._vmi.get_offset("win_pname") self._pid_offset = self._vmi.get_offset("win_pid") else: self.logger.error("Unknown OS") return 0 return 1 def _get_process_list(self): """ Get the process list inside the VM :return: process list of getting the page successfully or None. """ # pause vm with pause(self._vmi): # demonstrate name and id accessors name = self._vmi.get_name() id = self._vmi.get_vmid() self.logger.debug("Process listing for VM %s (id: %s)", name, id) if self._os == VMIOS.LINUX: list_head = self._vmi.translate_ksym2v("init_task") list_head += self._tasks_offset elif self._os == VMIOS.WINDOWS: list_head = self._vmi.read_addr_ksym("PsActiveProcessHead") else: self.logger.error("Unknown OS") return None process_list = dict() cur_list_entry = list_head next_list_entry = self._vmi.read_addr_va(cur_list_entry, 0) while True: current_process = cur_list_entry - self._tasks_offset pid = self._vmi.read_32_va(current_process + self._pid_offset, 0) procname = self._vmi.read_str_va( current_process + self._name_offset, 0) process_list[pid] = (procname, hex(current_process)) self.logger.debug("[%s] %s (struct addr:%s)", pid, procname, hex(current_process)) cur_list_entry = next_list_entry next_list_entry = self._vmi.read_addr_va(cur_list_entry, 0) if self._os == VMIOS.WINDOWS and next_list_entry == list_head: break elif self._os == VMIOS.LINUX and cur_list_entry == list_head: break return process_list def check_process(self): self._timer.start() ps_list = self._get_process_list() #ps_list = self._filter_white_list(ps_list) ps_set = set(ps_list.keys()) new_ps = ps_set - self._ori_ps_set reduce_ps = self._ori_ps_set - ps_set if new_ps: for pid in new_ps: if ps_list[pid][0] not in self.whlte_list: if not self._vm.dump_enabled: self._vm.dump_enabled = True self._callback() self.logger.warning( "detected new process. [%s] %s (struct addr:%s)", pid, ps_list[pid][0], ps_list[pid][1]) if reduce_ps: for pid in reduce_ps: if self._ori_ps_list[pid][0] not in self.whlte_list: self.logger.warning( "detected process leave. [%s] %s (struct addr:%s)", pid, self._ori_ps_list[pid][0], self._ori_ps_list[pid][1]) self._ori_ps_list = ps_list self._ori_ps_set = ps_set def start(self): """Start a thread that compares both of origin process list and a new one""" self._timer.start() def stop(self): """stop process checker""" self._timer.cancel() self._vmi.destroy()
import volatility.obj as obj import volatility.addrspace as addrspace import volatility.registry as registry registry.PluginImporter() registry.register_global_options(config, addrspace.BaseAddressSpace) ## Main program starts here: # Initialize address space (same as a=addrspace() in linux_volshell) a=utils.load_as(config) p=a.profile # Lookup kernel symbol pointing to first task task_addr = p.get_symbol("init_task") # Create python object for this task # Note that the "init_task" symbol does not point to the start of the # task_struct data structure "list" element of the data structures. init_task = obj.Object("task_struct", vm=a, offset=task_addr) l = list(init_task.tasks) from libvmi import Libvmi vmi = Libvmi(sys.argv[5]) t_cred_init = l[0].real_cred for t in l: if (t.pid == int(sys.argv[6])): t_cred_pa = a.vtop(t.real_cred.obj_offset) vmi.write_64_pa(t_cred_pa, t_cred_init)