def hook_winapi(self, ql: Qiling, address: int, size: int): if address in ql.loader.import_symbols: entry = ql.loader.import_symbols[address] api_name = entry['name'] if api_name is None: api_name = const.Mapper[entry['dll']][entry['ordinal']] else: api_name = api_name.decode() api_func = self.user_defined_api[QL_INTERCEPT.CALL].get(api_name) if not api_func: api_func = getattr(api, f'hook_{api_name}', None) self.syscall_count.setdefault(api_name, 0) self.syscall_count[api_name] += 1 if api_func: try: api_func(ql, address, api_name) except Exception as ex: ql.log.exception(ex) ql.log.debug("%s Exception Found" % api_name) raise QlErrorSyscallError("Windows API Implementation Error") else: ql.log.warning(f'api {api_name} is not implemented') if ql.debug_stop: raise QlErrorSyscallNotFound("Windows API implementation not found")
def hook_kernel_api(ql: Qiling, address: int, size: int): # call kernel api if address in ql.loader.import_symbols: api_name = ql.loader.import_symbols[address] # print("OK, found hook for %s" %api_name) api_func = ql.os.user_defined_api[QL_INTERCEPT.CALL].get(api_name) if not api_func: api_func = getattr(api, f'hook_{api_name}', None) if api_func: try: api_func(ql, address, api_name) except Exception: ql.log.exception("") ql.log.debug("%s Exception Found" % api_name) raise QlErrorSyscallError( "MacOS kernel API Implementation Error") else: ql.log.warning(f'api {api_name} is not implemented') if ql.debug_stop: raise QlErrorSyscallNotFound( "MacOS kernel API implementation not found")
def load_syscall(self): syscall_id = self.get_syscall() syscall_name = self.syscall_mapper(syscall_id) # get syscall on-enter hook (if any) hooks_dict = self.posix_syscall_hooks[QL_INTERCEPT.ENTER] onenter_hook = hooks_dict.get(syscall_name) or hooks_dict.get(syscall_id) # get syscall on-exit hook (if any) hooks_dict = self.posix_syscall_hooks[QL_INTERCEPT.EXIT] onexit_hook = hooks_dict.get(syscall_name) or hooks_dict.get(syscall_id) # get syscall replacement hook (if any) hooks_dict = self.posix_syscall_hooks[QL_INTERCEPT.CALL] syscall_hook = hooks_dict.get(syscall_name) or hooks_dict.get(syscall_id) if not syscall_hook: def __get_os_module(osname: str): return ql_get_module(f'.os.{osname.lower()}.syscall') os_syscalls = __get_os_module(self.type.name) posix_syscalls = __get_os_module('posix') # look in os-specific and posix syscall hooks syscall_hook = getattr(os_syscalls, syscall_name, None) or getattr(posix_syscalls, syscall_name, None) if syscall_hook: syscall_name = syscall_hook.__name__ # extract the parameters list from hook signature param_names = tuple(signature(syscall_hook).parameters.values()) # skip first arg (always 'ql') and filter out python special args (*args and **kwargs) param_names = [info.name for info in param_names[1:] if info.kind == Parameter.POSITIONAL_OR_KEYWORD] # read parameter values params = [self.__syscall_cc.getRawParam(i) for i in range(len(param_names))] try: # if set, fire up the on-enter hook and let it override original args set if onenter_hook: overrides = onenter_hook(self.ql, *params) if overrides is not None: _, params = overrides # perform syscall retval = syscall_hook(self.ql, *params) # if set, fire up the on-exit hook and let it override the return value if onexit_hook: override = onexit_hook(self.ql, *params, retval) if override is not None: retval = override # set return value if retval is not None: self.__syscall_cc.setReturnValue(retval) except KeyboardInterrupt: raise except Exception as e: self.ql.log.exception(f'Syscall ERROR: {syscall_name} DEBUG: {e}') raise e # print out log entry syscall_basename = syscall_name[len(SYSCALL_PREF) if syscall_name.startswith(SYSCALL_PREF) else 0:] args = [] for name, value in zip(param_names, params): # cut the first part of the arg if it is of form fstatat64_fd if name.startswith(f'{syscall_basename}_'): name = name.partition('_')[-1] args.append((name, f'{value:#x}')) sret = QlOsPosix.getNameFromErrorCode(retval) self.utils.print_function(self.ql.arch.regs.arch_pc, syscall_basename, args, sret, False) # record syscall statistics self.stats.log_api_call(self.ql.arch.regs.arch_pc, syscall_name, dict(zip(param_names, params)), retval, None) else: self.ql.log.warning(f'{self.ql.arch.regs.arch_pc:#x}: syscall {syscall_name} number = {syscall_id:#x}({syscall_id:d}) not implemented') if self.ql.debug_stop: raise QlErrorSyscallNotFound(f'Syscall not found: {syscall_name}')
def load_syscall(self): # import syscall mapping function map_syscall = ql_syscall_mapping_function(self.ql.ostype) syscall = self.syscall syscall_name = map_syscall(self.ql, syscall) # get syscall on-enter hook (if any) hooks_dict = self.posix_syscall_hooks[QL_INTERCEPT.ENTER] onenter_hook = hooks_dict.get(syscall_name) or hooks_dict.get(syscall) # get syscall on-exit hook (if any) hooks_dict = self.posix_syscall_hooks[QL_INTERCEPT.EXIT] onexit_hook = hooks_dict.get(syscall_name) or hooks_dict.get(syscall) # get syscall replacement hook (if any) hooks_dict = self.posix_syscall_hooks[QL_INTERCEPT.CALL] syscall_hook = hooks_dict.get(syscall_name) or hooks_dict.get(syscall) if syscall_hook: syscall_name = syscall_hook.__name__ else: _ostype_str = ostype_convert_str(self.ql.ostype) _posix_syscall = ql_get_module_function(f"qiling.os.posix", "syscall") _os_syscall = ql_get_module_function( f"qiling.os.{_ostype_str.lower()}", "syscall") if syscall_name in dir(_posix_syscall) or syscall_name in dir( _os_syscall): syscall_hook = eval(syscall_name) syscall_name = syscall_hook.__name__ else: syscall_hook = None if syscall_hook: args = self.get_syscall_args() self.utils.syscalls.setdefault(syscall_name, []).append({ "params": { "param0": args[0], "param1": args[1], "param2": args[2], "param3": args[3], "param4": args[4], "param5": args[5] }, "result": None, "address": self.ql.reg.arch_pc, "return_address": None, "position": self.utils.syscalls_counter }) self.utils.syscalls_counter += 1 try: if onenter_hook is not None: onenter_hook(self.ql, *self.get_syscall_args()) syscall_basename = syscall_hook.__name__[len(SYSCALL_PREF):] args = [] # ignore first arg, which is 'ql' arg_names = tuple( signature(syscall_hook).parameters.values())[1:] arg_values = self.get_syscall_args() for name, value in zip(arg_names, arg_values): name = str(name) # ignore python special args if name in ('*args', '**kw', '**kwargs'): continue # cut the first part of the arg if it is of form fstatat64_fd if name.startswith(f'{syscall_basename}_'): name = name.partition('_')[-1] args.append(f'{name} = {value:#x}') faddr = f'{self.ql.reg.arch_pc:#0{self.ql.archbit // 4 + 2}x}: ' if self.ql.verbose >= QL_VERBOSE.DEBUG else '' fargs = ', '.join(args) log = f'{faddr}{syscall_basename}({fargs})' if self.ql.verbose >= QL_VERBOSE.DEBUG: self.ql.log.debug(log) else: self.ql.log.info(log) ret = syscall_hook(self.ql, *arg_values) if ret is not None and type(ret) is int: # each name has a list of calls, we want the last one and we want to update the return value self.utils.syscalls[syscall_name][-1]["result"] = ret ret = self.set_syscall_return(ret) self.ql.log.debug( f'{syscall_basename}() = {QlOsPosix.getNameFromErrorCode(ret)}' ) if onexit_hook is not None: onexit_hook(self.ql, *self.get_syscall_args()) except KeyboardInterrupt: raise except Exception as e: self.ql.log.exception("") self.ql.log.info(f'Syscall ERROR: {syscall_name} DEBUG: {e}') raise e else: self.ql.log.warning( f'{self.ql.reg.arch_pc:#x}: syscall {syscall_name} number = {syscall:#x}({syscall:d}) not implemented' ) if self.ql.debug_stop: raise QlErrorSyscallNotFound("Syscall Not Found")