From af320025940e9ae527d4e1a672ec8836f3bb21d7 Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Tue, 8 Oct 2024 14:54:20 -0400 Subject: [PATCH] apk: Allow gadget config file customization from CLI Add a -c/--gadget-config flag, valid when --gadget is also in use, that adds arbitrary entries to the injected gadget's config file's "interaction" section[1]. [1]: https://frida.re/docs/gadget/ --- completions/frida.fish | 1 + frida_tools/apk.py | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/completions/frida.fish b/completions/frida.fish index 58b02b6..90c5695 100755 --- a/completions/frida.fish +++ b/completions/frida.fish @@ -184,6 +184,7 @@ complete --command frida-create --no-files --require-parameter --short-option=t add_base_arguments frida-apk complete --command frida-apk --force-files --short-option=o --long-option=output --description="output path" complete --command frida-apk --force-files --short-option=g --long-option=gadget --description="inject the specified gadget library" +complete --command frida-apk --force-files --short-option=c --long-option=gadget-config --description="set the given key=value gadget interaction config" ######## frida-compile ######## diff --git a/frida_tools/apk.py b/frida_tools/apk.py index ac14de3..284994c 100644 --- a/frida_tools/apk.py +++ b/frida_tools/apk.py @@ -1,11 +1,12 @@ from __future__ import annotations import argparse +import json import os import struct from enum import IntEnum from io import BufferedReader -from typing import BinaryIO, List +from typing import BinaryIO, Dict, List from zipfile import ZipFile GADGET_NAME = "libfridagadget.so" @@ -14,6 +15,11 @@ LD_PRELOAD="$(dirname "$0")/{GADGET_NAME}" "$@" """ +GADGET_INTERACTION_CONFIG = { + "type": "listen", + "on_load": "wait", +} + def main() -> None: from frida_tools.application import ConsoleApplication @@ -31,6 +37,22 @@ def _add_options(self, parser: argparse.ArgumentParser) -> None: help="inject the specified gadget library", metavar="GADGET", ) + + def key_val_type(arg: str) -> tuple[str, str]: + split = arg.split("=", 1) + if len(split) == 1: + raise argparse.ArgumentTypeError("config entry must be of form key=value") + return (split[0], split[1]) + + parser.add_argument( + "-c", + "--gadget-config", + type=key_val_type, + action="append", + help="set the given key=value gadget interaction config", + metavar="GADGET_CONFIG", + ) + parser.add_argument("apk", help="apk file") def _needs_device(self) -> bool: @@ -40,6 +62,10 @@ def _initialize(self, parser: argparse.ArgumentParser, options: argparse.Namespa self._output_path = options.output self._path = options.apk self._gadget = options.gadget + self._gadget_config = options.gadget_config + + if self._gadget_config and self._gadget is None: + parser.error("cannot configure gadget without injecting gadget") if not self._path.endswith(".apk"): parser.error("path must end in .apk") @@ -53,7 +79,10 @@ def _start(self) -> None: if self._gadget is not None: gadget_arch = get_gadget_arch(self._gadget) lib_dir = f"lib/{gadget_arch}/" - inject(self._gadget.name, lib_dir, self._output_path) + + config = {"interaction": {**GADGET_INTERACTION_CONFIG, **dict(self._gadget_config or [])}} + + inject(self._gadget.name, lib_dir, config, self._output_path) except Exception as e: self._update_status(f"Error: {e}") self._exit(1) @@ -114,9 +143,11 @@ def debug(path: str, output_path: str) -> None: oz.writestr(info.filename, f.read(), info.compress_type) -def inject(gadget_so: str, lib_dir: str, output_apk: str) -> None: +def inject(gadget_so: str, lib_dir: str, config: Dict[str, Dict[str, str]], output_apk: str) -> None: + config_name = GADGET_NAME.removesuffix(".so") + ".config.so" with ZipFile(output_apk, "a") as oz: oz.writestr(lib_dir + "wrap.sh", WRAP_SCRIPT) + oz.writestr(lib_dir + config_name, json.dumps(config)) oz.write(gadget_so, lib_dir + GADGET_NAME)