Skip to main content

Stitch

Stitch is a powerful APK patching python library that allows you to inject your own java module into any APK, bundle or XAPK file.

How it works

Will be added soon.

Projects using Stitch:

How to use

Installation

You can install Stitch using pip:

# For now, install the test version from TestPyPI
pip install stitch

Basic Usage

Create an Android Gradle project like my smali_generator (Check it out in on of the examples) that generates the java module you want to inject.

Then, use the following code to patch an APK:

from stitch import Stitch
from stitch.common import ExternalModule
from pathlib import Path

with Stitch(
        apk_path='./input.apk',
        output_apk='./output.apk',
        external_modules=[ExternalModule(Path(__file__).parent / './smali_generator',
                                         'com.smali_generator.InitProvider')]
) as stitch:
    stitch.patch()

And that's it! Your APK will be patched with the injected module.

The second argument to ExternalModule is the fully qualified name of a ContentProvider in your module. Stitch registers it in the target's AndroidManifest.xml with a very high initOrder, so its onCreate runs before the app's own components and your patch is loaded as early as possible.

Registering activities and other components

Stitch merges your module's own AndroidManifest.xml into the target's. Declare the component in the Gradle module the way you normally would:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.INTERNET" />

    <application>
        <activity
            android:name=".SettingsActivity"
            android:exported="true"
            android:theme="@android:style/Theme.Material.Dialog" />
    </application>

</manifest>

and it lands in the patched APK. activity, activity-alias, service, receiver and provider are carried over from <application>, along with root-level uses-permission, uses-permission-sdk-23, uses-feature and queries. The target's own <application> and <uses-sdk> attributes are never touched, and a component the target already declares is not added twice.

Two things to keep in mind:

  • Manifest attributes cannot point at your module's resources. The target's manifest is read against the target's resource table, so android:label="@string/app_name" or android:icon="@mipmap/ic_launcher" would resolve to something unrelated. Stitch drops those attributes with a warning; framework resources (@android:style/...) are kept. Your layouts and drawables are still available at runtime -- see Using your module's resources.
  • A component with an <intent-filter> must declare android:exported, otherwise Android 12+ refuses to install the patched APK. Stitch raises rather than guessing.

Pass merge_module_manifests=False to Stitch to turn the merge off and register only the provider.

Adding assets

Anything under src/main/assets/ in your module is copied into the patched APK, so you can ship data files alongside your code and read them at runtime the usual way:

InputStream stream = context.getAssets().open("my_module/config.json");

Each file keeps the compression AGP chose for it, which matters for assets that have to stay uncompressed to be memory-mapped. If the app you are patching already has a file at the same path, the module's copy wins and Stitch prints the path it replaced -- handy when you want to swap out an asset of the target app on purpose. When several modules ship the same path, the last ExternalModule wins.

Assets merge cleanly because AssetManager reads them by path and they have no entry in resources.arsc.

Using your module's resources

Your module's res/ cannot be merged into the target's resource table: both are numbered from 0x7f, so the same id means different things in each. In a patched 2048, the R.layout.activity_demo of the example module (0x7f020000) is animator/fragment_close_enter in the target -- setContentView would inflate the wrong thing.

Instead, Stitch injects your module's resource table as its own APK at assets/stitch/<your.package>.apk, and the module loads it at runtime into a separate Resources. Because that Resources contains only your table, your generated R constants mean exactly what they meant at compile time. StitchResources in the example module does this for you:

public class DemoActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Context moduleContext = StitchResources.wrap(this);
        View root = LayoutInflater.from(this).cloneInContext(moduleContext).inflate(R.layout.activity_demo, null);
        setContentView(root);

        Button button = root.findViewById(R.id.demo_button);
    }
}

StitchResources.wrap returns a context whose resources are your module's; StitchResources.get returns the Resources on its own if you want to look something up directly.

Two caveats:

  • It builds the AssetManager by reflection, since there is no public way to construct one. addAssetPath is a non-SDK API on the unsupported list rather than the blocked one, and it works on Android 16 as of writing. If it is ever blocked, the replacement is ResourcesLoader (public since API 30) with the module built under a reserved package id.
  • Theme attributes (?attr/...) in your layouts resolve against the theme of the activity, which belongs to the target app. Stick to explicit values or framework attributes.

Pass inject_module_resources=False to Stitch to skip shipping the table.

Contributing

I will be happy if you want to contribute to this project. Feel free to open issues or submit pull requests.

Disclaimer

For educational purpose only or something like that. I am not responsible for any misuse of this software.

Metadata

Release files for stitch 1.4.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for stitch 1.4.2
File Size Uploaded
stitch-1.4.2.tar.gz 16.2 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for stitch 1.4.2
File Interpreter ABI Platform
stitch-1.4.2-py3-none-any.whl Python 3 none any Details

Total release size: 32.5 MB

Release files / stitch-1.4.2.tar.gz

Download URL stitch-1.4.2.tar.gz
Size 16.2 MB
Tags Source
SHA-256 checksum
How to use checksums
bd3d1ecc6850d6faed821f38df998e18611b92c344dc4c9920a9ceb79672ac0f
BLAKE2b-256 checksum
How to use checksums
0cbc969e3d53d29706b5e67bdbb88c4f2b73d42119eff6c343c2370911dcfb7b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.4

Release files / stitch-1.4.2-py3-none-any.whl

Download URL stitch-1.4.2-py3-none-any.whl
Size 16.2 MB
Tags Python 3
SHA-256 checksum
How to use checksums
f312e2cac872255da556e89ccf30d3ac8a7fefd1496fb463cd93181e2a385a82
BLAKE2b-256 checksum
How to use checksums
0b0ff98366b7748cf96bb0a10f630422b07bef60006c4f0d7c6321c7c25c10e4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.4

Release history Release notifications | RSS feed

1.4.4

2 release files

1.4.3

2 release files

This release

1.4.2 This release

2 release files

1.4.1

2 release files

1.3.0

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.0.21

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page