Exemplo n.º 1
0
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()
Exemplo n.º 2
0
class DebugContext:
    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 get_kernel_base(self):
        if self.vmi.get_ostype() == VMIOS.LINUX:
            return self.vmi.translate_ksym2v('start_kernel')
        if self.vmi.get_ostype() == VMIOS.WINDOWS:
            # small hack with rekall JSON profile to get the kernel base address
            # LibVMI should provide an API to query it
            profile_path = self.vmi.get_rekall_path()
            if not profile_path:
                raise RuntimeError('Cannot get rekall profile from LibVMI')
            with open(profile_path) as f:
                profile = json.load(f)
                ps_head_rva = profile['$CONSTANTS']['PsActiveProcessHead']
                ps_head_va = self.vmi.translate_ksym2v('PsActiveProcessHead')
                return ps_head_va - ps_head_rva
        return None

    def attach(self):
        self.log.info('attaching on %s', self.target_name)
        # VM must be running
        self.vmi.pause_vm()
        if self.full_system_mode:
            # no need to intercept a specific process
            regs = self.vmi.get_vcpuregs(0)
            self.target_dtb = regs[X86Reg.CR3]
            return

        cb_data = {'interrupted': False}

        def cb_on_cr3_load(vmi, event):
            pname = dtb_to_pname(vmi, event.cffi_event.reg_event.value)
            self.log.info('intercepted %s', pname)

            pattern = re.escape(self.target_name)
            if re.match(pattern, pname, re.IGNORECASE):
                vmi.pause_vm()
                self.target_dtb = event.cffi_event.reg_event.value
                self.target_pid = vmi.dtb_to_pid(self.target_dtb)
                cb_data['interrupted'] = True

        reg_event = RegEvent(X86Reg.CR3, RegAccess.W, cb_on_cr3_load)
        self.vmi.register_event(reg_event)
        self.vmi.resume_vm()

        while not cb_data['interrupted']:
            self.vmi.listen(1000)
        # clear queue
        self.vmi.listen(0)
        # clear event
        self.vmi.clear_event(reg_event)

    def detach(self):
        try:
            logging.info('resuming VM execution')
            self.vmi.resume_vm()
        except LibvmiError:
            # already in running state
            pass
        self.vmi.destroy()