def on_script_log(self, script: FridaScript, log: dict): """ 脚本打印日志回调 :param script: frida的脚本 :param log: 日志内容 """ level = log.get("level") or "debug" tag = log.get("tag") or "[*]" message = log.get("message") log_fn = logger.debug if level == "info": log_fn = logger.info if level == "warning": log_fn = logger.warning if level == "error": log_fn = logger.error if message is not None and isinstance(message, dict): stack = utils.pop_item(message, "stack") if not utils.is_empty(stack): log_fn(stack, tag=tag, fore=Fore.CYAN) arguments = utils.pop_item(message, "arguments") if not utils.is_empty(arguments): log_fn(arguments, tag=tag, fore=Fore.LIGHTMAGENTA_EX) if not utils.is_empty(message): log_fn(message, tag=tag)
def parse_device(): usbmux = context.usbmux devices = usbmux.device_list() if len(devices) == 0: raise MuxError("error: no devices/emulators found") if len(devices) == 1: return devices[0].udid logger.message("more than one device/emulator") offset = 1 for i in range(len(devices)): try: name = Device(devices[0].udid, usbmux).name except Exception: name = "" logger.message(f"%d: %-20s [%s]" % (i + offset, devices[i].udid, name)) while True: offset = 1 data = input("enter device index %d~%d (default %d): " % (offset, len(devices) + offset - 1, offset)) if utils.is_empty(data): return devices[0].udid index = utils.cast(int, data, offset - 1) - offset if 0 <= index < len(devices): return devices[index].udid
def get_current_package(self, **kwargs) -> str: """ 获取顶层包名 :return: 顶层包名 """ self._ignore_capture_output(kwargs) timeout_meter = utils.TimeoutMeter(kwargs.pop("timeout", None)) if self.uid < 10000: args = [ "dumpsys", "activity", "top", "|", "grep", "^TASK", "-A", "1", ] out = self.shell(*args, timeout=timeout_meter.get(), **kwargs) items = out.splitlines()[-1].split() if items is not None and len(items) >= 2: return items[1].split("/")[0].rstrip() # use agent instead of dumpsys out = self.call_agent("common", "--top-package", timeout=timeout_meter.get(), **kwargs) if not utils.is_empty(out): return out raise AdbError("can not fetch top package")
def get_packages(self, *package_names, system=False, non_system=False, basic_info=False, **kwargs) -> [Package]: """ 获取包信息 :param package_names: 需要匹配的所有包名,为空则匹配所有 :param system: 只匹配系统应用 :param non_system: 只匹配非系统应用 :param basic_info: 只获取基本信息 :return: 包信息 """ self._ignore_capture_output(kwargs) result = [] dex_args = ["package"] if not utils.is_empty(package_names): dex_args.extend(["--packages", *package_names]) if system: dex_args.append("--system") elif non_system: dex_args.append("--non-system") if basic_info: dex_args.append("--basic-info") objs = json.loads(self.call_agent(*dex_args, **kwargs)) for obj in objs: result.append(Package(obj)) return result
def parse_device(): devices = Adb.devices(alive=True) if len(devices) == 0: raise AdbError("error: no devices/emulators found") if len(devices) == 1: return devices[0] logger.message("more than one device/emulator") offset = 1 for i in range(len(devices)): try: name = Device(devices[i]).get_prop("ro.product.name", timeout=1) except Exception: name = "" logger.message("%d: %-20s [%s]" % (i + offset, devices[i], name)) while True: data = input("enter device index %d~%d (default %d): " % (offset, len(devices) + offset - 1, offset)) if utils.is_empty(data): return devices[0] index = utils.cast(int, data, offset - 1) - offset if 0 <= index < len(devices): return devices[index]
def main(): parser = ArgumentParser(description='match files with regular expression') parser.add_argument('-i', '--ignore-case', action='store_true', default=False, help='ignore case') parser.add_argument('pattern', action='store', default=None, help='regular expression') parser.add_argument('files', metavar="file", action='store', nargs='*', default=None, help='target files path') args = parser.parse_args() flags = 0 if args.ignore_case: flags = flags | re.I pattern = re.compile(bytes(args.pattern, encoding="utf8"), flags=flags) if utils.is_empty(args.files): args.files = ["."] for file in args.files: GrepMatcher(pattern).match(file)
def on_text(self, filename: str, mimetype: str): with open(filename, "rb") as fd: lines = fd.readlines() for i in range(0, len(lines)): out = self.match_content(lines[i].rstrip()) if not utils.is_empty(out): logger.message(Fore.CYAN, filename, Fore.RESET, ":", Fore.GREEN, i + 1, Fore.RESET, ": ", out)
def on_elf(self, filename: str, mimetype: str): file = lief.parse(filename) for symbol in file.imported_symbols: out = self.match_content(symbol.name) if not utils.is_empty(out): logger.message(Fore.CYAN, filename, Fore.RESET, ":", Fore.GREEN, "import_symbols", Fore.RESET, ": ", out, Fore.RESET, " match") for symbol in file.exported_symbols: out = self.match_content(symbol.name) if not utils.is_empty(out): logger.message(Fore.CYAN, filename, Fore.RESET, ":", Fore.GREEN, "export_symbols", Fore.RESET, ": ", out, Fore.RESET, " match") self.on_binary(filename, mimetype=mimetype)
def wrapper(): index = int(values) devices = Adb.devices(alive=True) if utils.is_empty(devices): raise AdbError("error: no devices/emulators found") if not 0 < index <= len(devices): raise AdbError("error: index %d out of range %d~%d" % (index, 1, len(devices))) index = index - 1 return devices[index]
def print_permissions(self, indent: int = 4): if not utils.is_empty(self.package.permissions): self.stream.print("Permissions:", indent=indent, level=self.stream.title) for permission in self.package.permissions: self._print_permission(self.stream, permission, indent=indent + 4, identity="Permission") self.stream.print_line()
def print_activities(self, indent: int = 4): if not utils.is_empty(self.package.activities): self.stream.print("Activities:", indent=indent, level=self.stream.title) for activity in self.package.activities: self._print_component(self.stream, activity, indent=indent + 4, identity="Activity") self.stream.print_line()
def wrapper(): index = int(values) usbmux = context.usbmux devices = usbmux.device_list() if utils.is_empty(devices): raise MuxError("error: no devices/emulators found") if not 0 < index <= len(devices): raise MuxError("error: index %d out of range %d~%d" % (index, 1, len(devices))) index = index - 1 return devices[index].udid
def print_services(self, indent: int = 4): if not utils.is_empty(self.package.services): self.stream.print("Services:", indent=indent, level=self.stream.title) for service in self.package.services: self._print_component(self.stream, service, indent=indent + 4, identity="Service") self.stream.print_line()
def print_providers(self, indent: int = 4): if not utils.is_empty(self.package.providers): self.stream.print("Providers:", indent=indent, level=self.stream.title) for provider in self.package.providers: self._print_component(self.stream, provider, indent=indent + 4, identity="Provider") self.stream.print_line()
def match_content(self, content): out, last = "", 0 if type(content) == str: content = bytes(content, encoding="utf-8") for match in self.pattern.finditer(content): start, end = match.span() out = out + Fore.RESET + str(content[last:start], encoding="utf-8") out = out + Fore.RED + str(content[start:end], encoding="utf-8") last = end if not utils.is_empty(out): out = out + Fore.RESET + str(content[last:], encoding="utf-8") return out
def print_requested_permissions(self, indent: int = 4): if not utils.is_empty(self.package.requestedPermissions): stream = self.stream.create(max_level=PrintLevel.normal) self.stream.print("RequestedPermissions:", indent=indent, level=self.stream.title) for permission in self.package.requestedPermissions: self._print_permission(stream, permission, indent=indent + 4, identity="RequestedPermission") self.stream.print_line()
def exec(cls, *args: [str], capture_output: bool = True, ignore_error: bool = False, **kwargs) -> str: """ 执行命令 :param args: 命令 :param capture_output: 捕获输出,填False使用标准输出 :param ignore_error: 忽略错误,报错不会抛异常 :return: 输出结果 """ process, out, err = tools.adb.exec(*args, capture_output=capture_output, **kwargs) if not ignore_error and process.returncode != 0 and not utils.is_empty( err): err = err.decode(errors='ignore') if not utils.is_empty(err): raise AdbError(err) return out.decode(errors='ignore') if out is not None else ""
def on_script_message(self, script: FridaScript, message: object, data: object): """ 脚本消息回调函数,默认按照格式打印 :param script: frida的脚本 :param message: frida server发送的数据 :param data: frida server发送的data """ if utils.get_item(message, "type") == "send": payload = utils.get_item(message, "payload") if payload is not None and isinstance(payload, dict): # log单独解析 log = payload.pop("log", None) if log is not None: self.on_script_log(script, log) # event单独解析 event = payload.pop("event", None) if event is not None: self.on_script_event(script, event, data) # 解析完log,解析其他的 while len(payload) > 0: key, value = payload.popitem() self.on_script_send(script, key, value, data) # 字符串类型,直接输出 if not utils.is_empty(payload): logger.info(payload, tag="[*]") elif utils.get_item(message, "type") == "error" and utils.is_contain( message, "stack"): logger.info(utils.get_item(message, "stack"), tag="[!]", fore=Fore.RED) else: logger.info(message, tag="[?]", fore=Fore.RED)
def main(): parser = AndroidArgumentParser(description='easy to use frida') parser.add_argument( '-p', '--package', action='store', default=None, help='target package (default: current running package)') parser.add_argument('--spawn', action='store_true', default=False, help='inject after spawn (default: false)') parser.add_argument("-P", "--parameters", help="user script parameters", metavar=("KEY", "VALUE"), action='append', nargs=2, dest="user_parameters", default=[]) parser.add_argument("-l", "--load", help="load user script", metavar="SCRIPT", action='append', dest="user_scripts", default=[]) parser.add_argument("-e", "--eval", help="evaluate code", metavar="CODE", action='store', dest="eval_code", default=None) group = parser.add_mutually_exclusive_group() group.add_argument("-c", "--codeshare", help="load share script url", metavar="URL", action='store', dest="share_script_url", default=None) group.add_argument("-cc", "--codeshare-cached", help="load share script url, use cache first", metavar="URL", action='store', dest="cached_share_script_url", default=None) parser.add_argument("-d", "--debug", action='store_true', default=False, help="debug mode") args = parser.parse_args() device = args.parse_device() package = args.package user_parameters = {p[0]: p[1] for p in args.user_parameters} user_scripts = args.user_scripts eval_code = args.eval_code share_script = None if args.share_script_url is not None: share_script = FridaShareScript(args.share_script_url, cached=False) elif args.cached_share_script_url is not None: share_script = FridaShareScript(args.cached_share_script_url, cached=True) class Application(FridaApplication): def on_spawn_added(self, spawn): logger.debug(f"Spawn added: {spawn}", tag="[✔]") if device.extract_package(spawn.identifier) == package: self.load_script(spawn.pid, resume=True) else: self.resume(spawn.pid) def on_session_detached(self, session, reason, crash) -> None: logger.info( f"Detach process: {session.process_name} ({session.pid}), reason={reason}", tag="[*]") if reason in ("connection-terminated", "device-lost"): self.stop() elif len(self._sessions) == 0: app.load_script(app.device.spawn(package), resume=True) with FridaAndroidServer(device=device) as server: app = Application( server, debug=args.debug, user_parameters=user_parameters, user_scripts=user_scripts, eval_code=eval_code, share_script=share_script, enable_spawn_gating=True, ) target_pids = set() if utils.is_empty(package): target_app = app.get_frontmost_application() if target_app is None: raise RuntimeError("unknown frontmost application") package = target_app.identifier if not args.spawn: # 匹配所有app for target_app in app.enumerate_applications(): if target_app.pid not in target_pids: if target_app.pid > 0 and target_app.identifier == package: app.load_script(target_app.pid) target_pids.add(target_app.pid) # 匹配所有进程 for target_process in app.enumerate_processes(): if target_process.pid > 0 and target_process.pid not in target_pids: if device.extract_package(target_process.name) == package: app.load_script(target_process.pid) target_pids.add(target_process.pid) if len(target_pids) == 0: # 直接启动进程 app.load_script(app.spawn(package), resume=True) app.run()
def _print_component(stream: PrintStreamWrapper, component: Component, indent: int = 0, identity: str = None): if not component.enabled: description = "disabled" level = stream.useless stream = stream.create(max_level=stream.useless) elif component.is_dangerous(): description = "exported" level = stream.dangerous if component.is_dangerous( ) else stream.normal stream = stream.create(min_level=stream.dangerous_normal) else: description = "exported" if component.exported else "" level = stream.normal stream = stream.create(max_level=stream.normal) stream.print("%s [%s] %s" % (identity, component, description), indent=indent, level=level) if isinstance(component, Activity) or isinstance( component, Service) or isinstance(component, Receiver): PackagePrinter._print_permission(stream, component.permission, indent=indent + 4, identity="Permission") elif isinstance(component, Provider): stream.print("Authority [%s]" % component.authority, indent=indent + 4, level=level) PackagePrinter._print_permission(stream, component.readPermission, indent=indent + 4, identity="ReadPermission") PackagePrinter._print_permission(stream, component.writePermission, indent=indent + 4, identity="writePermission") for pattern in component.uriPermissionPatterns: stream.print("UriPermissionPattern [%s]" % pattern, indent=indent + 4, level=level) for permission in component.pathPermissions: stream.print("PathPermission [%s]" % permission, indent=indent + 4, level=stream.dangerous if permission.is_dangerous() else stream.normal) PackagePrinter._print_permission(stream, permission.readPermission, indent=indent + 8, identity="ReadPermission") PackagePrinter._print_permission(stream, permission.writePermission, indent=indent + 8, identity="writePermission") if not utils.is_empty(component.intents): for intent in component.intents: PackagePrinter._print_intent(stream, intent, indent=indent + 4, level=level)
def main(): parser = AndroidArgumentParser(description='fetch application info') group = parser.add_mutually_exclusive_group(required=True) group.add_argument('-a', '--all', action='store_true', default=False, help='fetch all apps') group.add_argument('-t', '--top', action='store_true', default=False, help='fetch current running app only') group.add_argument('-p', '--packages', metavar="pkg", action='store', nargs='+', default=None, help='fetch target apps only') group.add_argument('--system', action='store_true', default=False, help='fetch system apps only') group.add_argument('--non-system', action='store_true', default=False, help='fetch non-system apps only') parser.add_argument('-b', '--basic-info', action='store_true', default=False, help='display basic info only') parser.add_argument( '-dang', '--dangerous', action='store_true', default=False, help='display dangerous permissions and components only') parser.add_argument('-o', '--order-by', metavar="field", action='store', nargs='+', default=['userId', 'name'], choices=[ 'name', 'appName', 'userId', 'sourceDir', 'enabled', 'system', 'debuggable', 'allowBackup' ], help='order by target field') args = parser.parse_args() device = args.parse_device() if args.top: packages = device.get_packages(device.get_current_package(), basic_info=args.basic_info) elif not utils.is_empty(args.packages): packages = device.get_packages(*args.packages, basic_info=args.basic_info) elif args.system: packages = device.get_packages(system=True, basic_info=args.basic_info) elif args.non_system: packages = device.get_packages(non_system=True, basic_info=args.basic_info) else: packages = device.get_packages(basic_info=args.basic_info) if not utils.is_empty(args.order_by): packages = sorted( packages, key=lambda x: [utils.get_item(x, k, default="") for k in args.order_by]) min_level = PrintLevel.min if args.dangerous: min_level = PrintLevel.dangerous_normal stream = PrintStream(min_level=min_level) for package in packages: printer = PackagePrinter(stream, package) if not args.dangerous: printer.print_package() printer.print_requested_permissions() printer.print_permissions() printer.print_activities() printer.print_services() printer.print_receivers() printer.print_providers() continue if package.is_dangerous(): printer.print_package() if package.has_dangerous_permission(): printer.print_permissions() if package.has_dangerous_activity(): printer.print_activities() if package.has_dangerous_service(): printer.print_services() if package.has_dangerous_receiver(): printer.print_receivers() if package.has_dangerous_provider(): printer.print_providers()
def main(): parser = AndroidArgumentParser(description='common intent action') group = parser.add_mutually_exclusive_group(required=True) group.add_argument('--setting', dest='package', action='store_true', help='start setting activity') group.add_argument('--setting-dev', dest='package', action='store_true', help='start development setting activity') group.add_argument('--setting-dev2', dest='package', action='store_true', help='start development setting activity') group.add_argument('--setting-app', dest='package', action='store', nargs='?', default="", help='start application setting activity [default current running package]') group.add_argument('--setting-cert', dest='path', action='store', default="", help='install cert (need \'/data/local/tmp\' write permission)') group.add_argument('--install', dest='path', action='store', default="", help='install apk file') group.add_argument('--browser', dest='url', action='store', default="", help='start browser activity and jump to url (need scheme, such as https://antiy.cn)') args = parser.parse_args() device = args.parse_device() if "--setting" in sys.argv: device.shell("am", "start", "--user", "0", "-a", "android.settings.SETTINGS", capture_output=False) elif "--setting-dev" in sys.argv: device.shell("am", "start", "--user", "0", "-a", "android.settings.APPLICATION_DEVELOPMENT_SETTINGS", capture_output=False) elif "--setting-dev2" in sys.argv: device.shell("am", "start", "--user", "0", "-a", "android.intent.action.View", "com.android.settings/com.android.settings.DevelopmentSettings", capture_output=False) elif "--setting-app" in sys.argv: package = args.package if not utils.is_empty(args.package) else device.get_current_package() device.shell("am", "start", "--user", "0", "-a", "android.settings.APPLICATION_DETAILS_SETTINGS", "-d", "package:%s" % package, capture_output=False) elif "--setting-cert" in sys.argv: path = "/data/local/tmp/%s/cert/%s" % (linktools.__name__, os.path.basename(args.path)) device.push(args.path, path, capture_output=False) device.shell("am", "start", "--user", "0", "-n", "com.android.certinstaller/.CertInstallerMain", "-a", "android.intent.action.VIEW", "-t", "application/x-x509-ca-cert", "-d", "file://%s" % path, capture_output=False) elif "--install" in sys.argv: path = device.get_storage_path(os.path.basename(args.path)) device.push(args.path, path, capture_output=False) device.shell("am", "start", "--user", "0", "-a", "android.intent.action.VIEW", "-t", "application/vnd.android.package-archive", "-d", "file://%s" % path, capture_output=False) elif "--browser" in sys.argv: device.shell("am", "start", "--user", "0", "-a", "android.intent.action.VIEW", "-d", args.url, capture_output=False)
def main(): parser = AndroidArgumentParser( description='show current running app\'s basic information') group = parser.add_mutually_exclusive_group() group.add_argument('-p', '--package', action='store_const', const=True, default=False, help='show current running package name') group.add_argument('-a', '--activity', action='store_const', const=True, default=False, help='show current running activity name') group.add_argument('--path', action='store_const', const=True, default=False, help='show current running package path') group.add_argument('--kill', action='store_const', const=True, default=False, help='kill current running package') group.add_argument('--apk', metavar='DEST', action='store', type=str, nargs='?', default=".", help='pull current running apk file') group.add_argument('--screen', metavar='DEST', action='store', type=str, nargs='?', default=".", help='capture screen and pull file') args = parser.parse_args() device = args.parse_device() if args.package: logger.message(device.get_current_package()) elif args.activity: logger.message(device.get_current_activity()) elif args.path: logger.message(device.get_apk_path(device.get_current_package())) elif args.kill: device.shell("am", "force-stop", device.get_current_package(), capture_output=False) elif "--apk" in sys.argv: package_name = device.get_current_package() logger.message("get current running package: {}".format(package_name)) package = utils.get_item( device.get_packages(package_name, basic_info=True), 0) if package is not None: logger.message("get current running package path: {}".format( package.sourceDir)) path = device.get_storage_path("{}_{}.apk".format( package.name, package.versionName)) dest = args.apk if not utils.is_empty(args.apk) else "." device.shell("mkdir", "-p", device.get_storage_path(), capture_output=False) device.shell("cp", package.sourceDir, path, capture_output=False) device.pull(path, dest, capture_output=False) device.shell("rm", path) elif "--screen" in sys.argv: now = datetime.datetime.now() path = device.get_storage_path("screenshot-" + now.strftime("%Y-%m-%d-%H-%M-%S") + ".png") dest = args.screen if not utils.is_empty(args.screen) else "." device.shell("mkdir", "-p", device.get_storage_path(), capture_output=False) device.shell("screencap", "-p", path, capture_output=False) device.pull(path, dest, capture_output=False) device.shell("rm", path) else: package = device.get_current_package() logger.message("package: ", package) logger.message("activity: ", device.get_current_activity()) logger.message("path: ", device.get_apk_path(package))
def is_defined(self): return not utils.is_empty(self.name)