From d34733ab6c4b111131acaf4f93a84086712f596a 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, 29 insertions(+), 9 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 83da24c..c04a313 100644 --- a/frida_tools/apk.py +++ b/frida_tools/apk.py @@ -6,7 +6,7 @@ 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 @@ -16,11 +16,9 @@ LD_PRELOAD="$(dirname "$0")/{GADGET_NAME}" "$@" """ -GADGET_CONFIG = { - "interaction": { - "type": "listen", - "on_load": "wait", - }, +GADGET_INTERACTION_CONFIG = { + "type": "listen", + "on_load": "wait", } @@ -35,6 +33,15 @@ def _add_options(self, parser: argparse.ArgumentParser) -> None: parser.add_argument("-o", "--output", help="output path", metavar="OUTPUT") parser.add_argument("-g", "--gadget", type=argparse.FileType("rb"), 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: @@ -44,6 +51,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") @@ -57,7 +68,15 @@ 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) + } + } + + inject(self._gadget.name, lib_dir, config, self._output_path) except Exception as e: self._update_status(f"Error: {e}") self._exit(1) @@ -118,11 +137,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(GADGET_CONFIG)) + oz.writestr(lib_dir + config_name, json.dumps(config)) oz.write(gadget_so, lib_dir + GADGET_NAME)