README
GENERAL INFO
Project: Library to patch apk (inject frida gadget)
Author: MadSquirrel
License: GNU General Public License v3.0
Version: see apkpatcher.conf.VERSION (or apkpatcher --version)
GOAL
Library to patch apk (inject frida gadget) this code is inspired by this project :https://github.com/badadaf/apkpatcher.
The improvements added by this fork are the following:
- modification of xml files such as AndroidManifest without extracting the resources. Extracting the resources usually prevents to rebuild the apk.
- Use as an API
- Installation as a package
This project has received funding from the OSINT Got Talent program of EPIEOS.
USAGE
To use as library you just need to:
import apkpatcher
patcher = apkpatcher.Patcher(<apk_path>, <version_buildtools>, <sdktools>)
patcher.set_arch(<arch>)
patcher.patching(<path_gadget>, output_file=<output_file>, user_certificate=<true|false>)
To use as a program you just need to:
apkpatcher -a <apk_path> -g <path_gadget> -s <sdktools> -b <version> -r <arch> -o <output_file>
You could use it as docker with this command line:
docker run --rm -v .:/pwd -it madsquirrels/apkpatcher -a base.apk --download_frida_version 16.3.3
Editing the APK mid-patch inside Docker (-p/--pause)
-p/--pause stops apkpatcher right before repackaging so you can hand-edit
the unpacked APK tree (smali, resources, manifest, ...). That unpacked tree
lives under /tmp (Python's tempfile default) -- inside the container,
that's invisible from the host unless you also mount a volume on /tmp:
mkdir -p ./work
docker run --rm -it \
--user "$(id -u):$(id -g)" \
-v .:/pwd \
-v ./work:/tmp \
madsquirrels/apkpatcher \
-a base.apk -p -o base-patched.apk
When apkpatcher logs You can modify the apk here: /tmp/tmpXXXXXXXX, that
same directory is ./work/tmpXXXXXXXX on the host -- edit files there, then
press Enter in the container's terminal to resume; it repackages and signs
the APK with your changes included. Without the second -v ./work:/tmp,
that directory only exists inside the container and disappears with it, so
there's nothing on the host to edit.
--user "$(id -u):$(id -g)" matters: the image otherwise runs as a fixed
non-root user, which generally won't match whoever owns ./work on the
host, so it can't write the extracted files there. Running the container
as your own uid/gid instead means the plain mkdir above (unmodified,
default permissions) already has the right owner -- no chmod needed.
Verified end-to-end: a completely default-permissions mkdir, --user "$(id -u):$(id -g)", --pause, editing from the host, and resuming all
worked with zero permission changes.
For more information please visit https://apkpatcher.ci-yow.com/
Quick examples
Each example uses only the flags actually required to work: -b is never
required (build-tools version auto-detects from sdktools); -s is only
needed when something requires signing/build-tools (not --only-unpack);
and -g with a plain, non-arch-suffixed filename needs -r to say which
single architecture that file is for -- otherwise apkpatcher looks for
gadget_arm.so/gadget_arm64.so/... and finds none of them.
# Simple Frida injection
apkpatcher -a app.apk -s /opt/android-sdk -g gadget.so -r arm64 -o app-frida.apk
# Simple community patch
apkpatcher -a app.apk -s /opt/android-sdk --patch flutter-cert-pinning-bypass -o app-patched.apk
# Simple unpack (only -a is needed -- no signing/build-tools involved)
apkpatcher -a app.apk --only-unpack ./unpacked
# Simple repack (from a tree an earlier --only-unpack produced)
apkpatcher -a app.apk -s /opt/android-sdk --only-repack ./unpacked
These same four examples are also shown in apkpatcher --help's epilog.
EXEMPLE
import apkpatcher
patcher = apkpatcher.Patcher(<apk_path>, <version_buildtools>, <sdktools>)
# not mandatory
patcher.add_network_certificate(<custom_certificate>)
patcher.set_arch(<arch>)
patcher.pause = <True|False>
# end not mandatory
patcher.patching(<path_gadget>, output_file=<output_file>, user_certificate=<true|false>)
INSTALL
sudo python3 -m pip install .
Requirement
setup your sktools as follow:
- https://asthook.ci-yow.com/how.install.html#setup-sdktools install:
- apktool
- pip install -r requirements.txt
CLI REFERENCE
apkpatcher --help groups every flag the same way this table does. Run it
for the live, authoritative list (including which flags are [required]);
this table is a companion overview, organized by intent.
apk : target APK, SDK, and output
Required flags come first in this table, matching --help's own ordering.
| Flag | Purpose |
|---|---|
-a/--apk PATH |
APK to patch (required) |
-s/--sdktools PATH |
Android SDK root (required unless $ANDROID_SDK_ROOT is set, or --only-unpack is used instead) |
-m/--multiple_split PATH... |
Split APKs belonging to the same app |
-b/--version_buildtools VERSION |
build-tools version under sdktools/build-tools/ (auto-detected if omitted) |
-o/--output-file PATH |
Output APK path |
Library equivalents: Patcher(apk, version_buildtools, sdktools, ...),
patching(output_file=..., splits_apk=[...]).
library injection : inject a native library (e.g. a Frida gadget)
-g/--gadget isn't Frida-specific: it injects any native library. -r
and --entrypoint describe whatever's injected here, Frida or not.
| Flag | Purpose |
|---|---|
-g/--gadget PATH |
Native library (.so) to inject |
-r/--arch {arm,arm64,x86,x64} |
Target architecture -- required to disambiguate a single, non-arch-suffixed library file (e.g. -g gadget.so); without it apkpatcher looks for gadget_arm.so/gadget_arm64.so/... instead |
--entrypoint CLASS |
Class to inject the library loader into (auto-detected if omitted) |
Library equivalents: the gadget path as patching()'s first positional
argument, Patcher.set_arch(), patching(entrypoint=...).
frida : subsection of library injection: Frida-specific gadget config
These only make sense once a gadget has been injected via -g/--download_frida* above.
| Flag | Purpose |
|---|---|
--download_frida_version VERSION |
Download a specific frida-gadget version instead of supplying -g |
--download_frida |
Download whichever frida-gadget version matches the frida Python package installed locally |
--frida-no-wait |
Gadget config: app starts immediately instead of waiting for a Frida client to attach |
--frida-script PATH |
Embed a local .js script the gadget auto-loads on startup, no network interaction needed |
--frida-config PATH |
Supply a full custom gadget config JSON (mutually exclusive with the two flags above) |
Library equivalents: Patcher.set_use_download_frida(),
Patcher.set_use_download_frida_from_installed(), Patcher.set_frida_no_wait(),
Patcher.set_frida_script(), Patcher.set_frida_config().
network certificates
| Flag | Purpose |
|---|---|
-e/--enable-user-certificates |
Let the app trust user-installed CA certificates |
-c/--custom-certificate PATH |
Install a custom network certificate inside the APK |
Library equivalents: patching(user_certificate=True),
Patcher.add_network_certificate().
manifest & dex
| Flag | Purpose |
|---|---|
--enable-debug |
Set android:debuggable="true" |
--keep-debug-info |
Keep .line/.local/.parameter smali debug directives (off by default) |
--disable-dex-split |
Fail on a 65536-method-reference overflow instead of auto-splitting into a second dex |
--add-permissions PERM... |
Add one or more <uses-permission> entries |
Library equivalents: Patcher.set_debug(), Patcher.keep_debug_info(),
Patcher.disable_dex_split(), patching(permissions=[...]).
community patches
Community patches are shareable, reviewed --plugin-style scripts fetched
from a static repository (default: https://apkpatcher-patches.ci-yow.com,
configured in ~/.config/apkpatcher/config.toml).
| Flag | Purpose |
|---|---|
--plugin PATH |
Run a local plugin script (repeatable) |
--patch ID |
Fetch and run a community patch by id (repeatable) |
--list-patches |
List available patches, then exit |
--search-patches QUERY |
Search available patches by name/description/tags, then exit |
--refresh-patches |
Bypass the local patch-index cache |
--repo URL |
Add an extra patch repository for this run |
--config PATH |
Use a different patch-repository config file |
-y/--yes |
Skip the trust-on-first-use prompt |
apkpatcher --list-patches
apkpatcher --search-patches flutter
apkpatcher -a app.apk -s /opt/android-sdk --patch flutter-cert-pinning-bypass -o out.apk
Library equivalents: Patcher.add_plugin()/Patcher.set_plugin() for
--plugin; for --patch, apkpatcher.patchrepo.fetch_all(repos) to fetch
the index and apkpatcher.patchrepo.resolve_and_apply_patches(patcher, patch_ids, all_patches, satisfied_capabilities) to fetch/verify/apply one
or more patches by id, without going through the CLI at all:
from apkpatcher import Patcher, patchrepo
patcher = Patcher(<apk_path>, <version_buildtools>, <sdktools>)
repos = patchrepo.load_config()
all_patches = patchrepo.fetch_all(repos)
patchrepo.resolve_and_apply_patches(
patcher, ["flutter-cert-pinning-bypass"], all_patches,
satisfied_capabilities=set(), auto_trust=True,
)
patcher.patching(output_file=<output_file>)
packaging
| Flag | Purpose |
|---|---|
--compression-level N |
Output zip compression level, 0-9 (default: 9) |
--compression-method {STORED,DEFLATED} |
Output zip compression method (default: DEFLATED) |
Library equivalent: Patcher.set_compression(zipfile.ZIP_DEFLATED, 9)
(takes the raw zipfile module constants directly).
unpack/repack
| Flag | Purpose |
|---|---|
--only-unpack PATH |
Extract without repackaging (only -a is needed -- no signing/build-tools involved) |
--only-repack PATH |
Repackage a tree a prior --only-unpack produced |
Library equivalents: Patcher.unpack_to(target_dir) and
Patcher.repack_from(source_dir, output_apk).
misc
| Flag | Purpose |
|---|---|
-v/--verbosity {0,1,2,3} |
Logging verbosity: 3=DEBUG, 2/unset=INFO, 0-1=ERROR |
-p/--pause |
Pause right before repackaging, to hand-edit the unpacked tree (inside Docker, mount a volume on /tmp -- see above) |
-V/--version |
Print the installed apkpatcher version |
--download-jars |
Pre-download the bundled smali/baksmali jars, then exit |
-j/--nb-jobs N |
Parallel worker count for dex processing (default: 4) |
Library equivalents: apkpatcher.new_logger(logging.DEBUG), Patcher.pause
(a plain property), apkpatcher.conf.VERSION, the nb_jobs constructor
argument.
signing : certificate for signing the output APK
Kept last: distinct from the network certificates above (which the app trusts at runtime), this is the keystore apkpatcher signs the output APK with.
| Flag | Purpose |
|---|---|
--keycertificate/--keyalias/--keypass |
Use a specific keystore to sign the output (all three together) |
--keep-keycertificate |
Keep an auto-generated signing keystore instead of deleting it afterward |
--v4 PATH |
Also produce a v4 signature file |
Library equivalents: Patcher.add_certificate(cert, alias, pass),
Patcher.keep_certificate(), Patcher.enable_v4_signature().
CHANGELOG
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file apkpatcher-0.1.43.tar.gz.
File metadata
- Download URL: apkpatcher-0.1.43.tar.gz
- Upload date:
- Size: 63.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d6e83d230e3ae5546a283ec4adc91528b94525087fa65a48a8b3bb1eda1a40d
|
|
| MD5 |
37f9f6d90d2de4fcce3f333f7f9e2793
|
|
| BLAKE2b-256 |
469cc38d1907047cdc750dfb8be06082c4002bccce0efdebcbca3973e6385bf6
|
File details
Details for the file apkpatcher-0.1.43-py3-none-any.whl.
File metadata
- Download URL: apkpatcher-0.1.43-py3-none-any.whl
- Upload date:
- Size: 59.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4b63e6eba5cb3dd89140908c41913dfb6b48e84258fd1d66f68eea0daa1eda47
|
|
| MD5 |
ada3ad4714e1310e2259762e2c0f1367
|
|
| BLAKE2b-256 |
9aeae1e456b6e586b8f1b93db2912258d5028262af19e1fcf0c034beb3168a42
|