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
Metadata
Release files for apkpatcher 0.1.43
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| apkpatcher-0.1.43.tar.gz | 63.5 kB | Without provenance | Details for apkpatcher-0.1.43.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| apkpatcher-0.1.43-py3-none-any.whl | Python 3 | none | any | Details for apkpatcher-0.1.43-py3-none-any.whl |
Total release size: 122.5 kB
Release files / apkpatcher-0.1.43.tar.gz
| Download URL | apkpatcher-0.1.43.tar.gz |
|---|---|
| Size | 63.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9d6e83d230e3ae5546a283ec4adc91528b94525087fa65a48a8b3bb1eda1a40d
|
|
BLAKE2b-256 checksum How to use checksums |
469cc38d1907047cdc750dfb8be06082c4002bccce0efdebcbca3973e6385bf6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.0
|
Release files / apkpatcher-0.1.43-py3-none-any.whl
| Download URL | apkpatcher-0.1.43-py3-none-any.whl |
|---|---|
| Size | 59.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4b63e6eba5cb3dd89140908c41913dfb6b48e84258fd1d66f68eea0daa1eda47
|
|
BLAKE2b-256 checksum How to use checksums |
9aeae1e456b6e586b8f1b93db2912258d5028262af19e1fcf0c034beb3168a42
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.0
|
Security for apkpatcher 0.1.43
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.