def test_create_args(self): arg_factory = ArgFactory(lambda arg: "") args = arg_factory.gen_args( [("int", "fd"), ("void*", "buf"), ("size_t", "count")], [0x4, 0xDEADBEEF, 0x10], ) self.assertEqual(args.fd, 0x4) self.assertEqual(args.buf, 0xDEADBEEF) self.assertEqual(args.count, 0x10)
class LinuxKernel(IKernel): def __init__(self, arch, engine): super(LinuxKernel, self).__init__(engine) self.arch = arch self.call_map = self.__load_linux_syscall_maps(arch) self._name2syscall_func = self._load_linux_syscall_funcs() self.rev_map = {v: k for k, v in self.call_map.items()} self.arg_factory = ArgFactory(functools.partial( get_arg_string, self.z)) self.socketcall_dict = { 1: "socket", 2: "bind", 3: "connect", 4: "listen", 5: "accept", 6: "getsockname", 7: "getpeername", 8: "socketpair", 9: "send", 10: "recv", 11: "sendto", 12: "recvfrom", 13: "shutdown", 14: "setsockopt", 15: "getsockopt", 16: "sendmsg", 17: "recvmsg", 18: "accept4", 19: "recvmmsg", 20: "sendmmsg", } # These are processes that are exited, and so a parent process # can wait on them. # parent_pid -> child_pid self.child_state_changes = defaultdict(list) def _load_linux_syscall_funcs( self, ) -> Dict[str, Callable[[any], Optional[int]]]: """ Returns map of name -> syscall implementation. """ from .syscalls import syscalls as linux_syscall_module linux_syscalls = inspect.getmembers(linux_syscall_module, inspect.isfunction) return { name.partition("sys_")[-1]: func for name, func in linux_syscalls if name.startswith("sys_") } def __load_linux_syscall_maps(self, arch): """ Loads the list of supported syscalls from the syscalls table. """ from .syscalls.syscalls_table import cols, table try: i = cols.index(arch) except ValueError: raise ZelosException(f"Invalid architecture '{arch}'") return {k: v[i] for (k, v) in table.items() if v[i] != -1} def handle_syscall(self, process): """ Additionally translate `socketcall` syscalls to their target socket system call, e.g. `recv`, for the purpose of syscall breaks. This ensures that breakpoints for `recv` will be triggered both for `recv` and `socketcall` syscalls that invoke `recv`, etc. """ sys_num = self.get_syscall_number() sys_name = self.find_syscall_name_by_number(sys_num) if sys_name == "socketcall": socketcall_args = self.get_last_syscall_args() args = self.get_args( [("int", "call"), ("unsigned long *", "callargs")], sys_num=sys_num, ) status = super(LinuxKernel, self).handle_syscall(process) if sys_name == "socketcall": socketcall = self.socketcall_dict.get(args.call, None) if socketcall is not None: self.last_syscall_args = socketcall_args self._handle_syscall_break(socketcall) return status def set_errno(self, val): pass def _get_socketcall_args(self, process, func_name, args_addr, arg_list, arg_string_overrides={}): # If calling these syscalls directly, get_args the old # fashioned way. if args_addr < 0: return self.get_args(arg_list, arg_string_overrides) arg_vals = [ process.memory.read_int(args_addr + i * 4) for i in range(len(arg_list)) ] args = self.arg_factory.gen_args( arg_list, arg_vals, arg_string_overrides=arg_string_overrides) self.last_syscall_args = args self._print_socket_syscall(func_name, args) return args def _print_socket_syscall(self, func_name, args): s = colored(f"{func_name}", "white", attrs=["bold"]) + f" ( {args} )" self.z.plugins.trace.print("SOCKET SYSCALL", s) #################### # HELPER FUNCTIONS # #################### def get_args(self, arg_list, arg_string_overrides={}, sys_num=None): """ Gets arguments according to linux syscall calling convention """ z = self.z if sys_num is None: sys_num = self.get_syscall_number() reg_list = self._REG_ARGS arg_regs = reg_list[:len(arg_list)] arg_vals = [z.current_thread.get_reg(arg) for arg in arg_regs] # Get the rest of the arguments off of the stack i = len(arg_vals) while len(arg_vals) < len(arg_list): arg_vals.append(self.emu.getstack(i)) i += 1 args = self.arg_factory.gen_args( arg_list, arg_vals, arg_string_overrides=arg_string_overrides) self.last_syscall_args = args return args