CLI tool
luoyesiqiu/dpt-shell avatar
luoyesiqiu/dpt-shell

dpt-shell: an Android dex protection shell for APKs and AABs

An android Dex protection shell implementation

1,156 stars362 forksJavaMIT

At a glance

What is it?
dpt-shell is a Java tool that hollows out the method implementations in an Android dex and reconstructs them at runtime, so the code is not sitting in the package where a decompiler can read it. The package is protected with a shell, and the tool also carries anti-debug and Frida detection, with switches to disable each of them when you are debugging.
Who is it for?
Adopt dpt-shell if you ship an Android application whose dex you do not want readable by a decompiler, and you are prepared to own the operational consequences, because the project itself warns that it has few tests and that production use is at your own risk.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 53 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Hollowing out the dex

dpt-shell is an Android Dex protection shell that hollows out Dex method implementations and reconstructs them at runtime.

That one sentence describes the entire mechanism, and it is the classic approach to this problem. The methods in the dex are not stored as compiled code in the package; their bodies are extracted, and the shell that is packed into the application restores them when the runtime needs them. A decompiler pointed at the package finds empty methods, and the actual instructions exist only in memory, briefly, while the app is running.

The tool is a command line program rather than a Gradle plugin, which is the design decision worth noting. You point it at a package file, it produces a protected package, and the protected package is what you sign and ship. The shell/ directory in the repository is the shell itself, and the dpt/ directory holds the tooling, with a doc/ directory for documentation and a top-level build.gradle and settings.gradle for assembling them.

The primary language is Java, the licence is MIT, the badge in the README states Android 5.0 and later, and the project is not archived. The release history shows a busy August 2026, with v2.19.2 on 2026-08-21, v2.20.0 on 2026-08-22 and v2.21.0 on 2026-08-31, and the last push to the repository on the same day as the newest tag.

Protecting a package in one command

The quick route is the released archive. You go to the releases page, download the file named executable.zip, unzip it, and run the jar against the package you want to protect:

shell
java -jar dpt.jar -f /path/to/android-package-file

The -f option is the one you always need. It takes the package file to protect, and the help text is explicit that it accepts both .apk and .aab files, so the same command covers an APK build and an app bundle build.

Everything else is a modifier on that operation. The -o option sets the output directory for the protected package, so a pipeline can keep the original and the protected build apart. The -c option points at a protect config file, which is how you keep a configuration across many builds. The -v option prints the program's version number.

Two options relate to signing, and they are the ones to get right before you ship anything. The -vs option enables runtime app signature verification, with the certificate SHA-256 computed automatically from the signing keystore, which means the protected app can check at runtime that it has not been repackaged. The -x option does the opposite and tells the tool not to sign the package at all, which is what you want when your own build pipeline signs it afterwards.

The -r option takes a rules file listing class names that will not be protected, which is the escape hatch for code that has to remain readable, for example code that is inspected at runtime by another component.

Building it from source, and why the clone is recursive

The manual build is three commands, and one of them has a flag that matters.

shell
git clone --recursive https://github.com/luoyesiqiu/dpt-shell
cd dpt-shell
./gradlew assemble
cd executable
java -jar dpt.jar -f /path/to/android-package-file

The --recursive flag on the clone is not optional decoration. The repository has a .gitmodules file, so the native components the shell depends on are Git submodules, and a shallow clone without them will fail during the Gradle build rather than at the point where you would expect.

The Gradle wrapper is used to assemble, and the output lands in the executable directory, which is the same directory the released zip contains. That is a useful detail, because it means the artefact you build yourself and the artefact you download are produced by the same path, so a local build and a published build behave the same way.

The build is a standard Gradle project with gradle.properties, gradlew and gradlew.bat for the wrapper, so it builds the same way on Linux, macOS and Windows, with the Java toolchain the project expects.

The switches that turn the protection off

A protection tool is only usable if you can turn it off, and the command line options are unusually well organised around that.

There are four disable flags, one per check. --disable-anti-debug turns off the runtime anti-debug, --disable-frida-detect turns off the runtime Frida detection, --disable-crc-detect turns off the runtime libc .text CRC detection, and --disable-acf disables the app component factory, with the help text noting that this one is just for debug.

That last check is the one that needs a word of explanation, because it is not a detection mechanism in the same sense as the others. A libc .text CRC check verifies the integrity of the runtime library's code section at execution time, which is a way of noticing that something has patched the process underneath the app, for example with a hooking framework. The app component factory is the Android mechanism for instantiating components, and the help text marks its disable switch as a debugging convenience.

The remaining flags are for working rather than protection. --debug makes the package debuggable, which is what you want in a build you are going to attach a debugger to. --noisy-log opens verbose logging, which is how you find out what the shell is doing during a first run. And --dump-code dumps the code item of the dex and saves it to .json files, which is the diagnostic you want when a protected app misbehaves and you need to see what the shell recovered.

The combination you want for a first attempt is --debug with the three detection switches off, and then you turn them on one at a time as you confirm that the app still starts and behaves.

Size, startup speed and which ABIs you keep

Three options manage the trade-offs, and each of them is a real trade rather than a switch with an obvious right answer.

The -S option, --smaller, trades some of the app's performance for a smaller app size. Since the shell is what has to be unpacked and reconstructed at runtime, the size and the work at startup are related, and this option accepts a slower start in exchange for a smaller download.

The -K option, --keep-classes, keeps some classes in the package, which can improve the app's startup speed to a certain extent, but the help text warns that it is not supported by some application packages. That warning is the important part, because it means the option interacts with whatever is already in the app, and a class that has to be protected but cannot be will fail in a way that is not obvious from the flag's name.

The -e option excludes specific ABIs, comma separated, with the supported list given as arm for armeabi-v7a, arm64 for arm64-v8a, x86 and x86_64. Since the shell is native code, it exists per architecture, and dropping the ones your users do not have is the fastest way to shrink the package. A project that has already moved to arm64 only can remove three of the four.

The --dump-code option is a debugging aid rather than a feature, and it is the one that can be expensive, because it writes the recovered code items out as JSON files.

The dependency stack, and what it borrows from

The README lists the projects this one follows, and the list explains most of the implementation.

For the dex side there is dx, the Dalvik tool from the Android platform tree, dexmaker from the same tree, and Obfuscate. For hooking there is Dobby and bhook, the latter from ByteDance, which is the mechanism that lets the shell intercept calls at runtime. For reading and writing packages there is ManifestEditor, zipalign-java, minizip-ng, JSON-java, zip4j and commons-cli for the command line parsing.

Two entries are struck through, which means they are no longer used. The list has libzip-android struck out, and Xpatch struck out, and in both cases the reason is not stated.

Two of these deserve a note. ManifestEditor is the tool that rewrites the AndroidManifest.xml inside the package, which is how the shell registers its own component to be loaded before the original application code. And zipalign is what has to run on the final package for the Android installer to accept it, which is why a zipalign implementation is in the list at all: the shell changes the package, and the package has to be realigned afterwards.

The DX and dexmaker entries are the most interesting for anyone reading the code, because they are the upstream Android tools for producing and manipulating dex rather than a third-party reimplementation of them. The project leans on the platform's own tooling for the format work and adds the shell and the runtime protection around it.

The notice, and where the project stands

The README has a Notice section, and it is one sentence that anyone deploying this should read before anything else.

This project has not too many tests, be careful use in prod environment. Otherwise, all consequences are at your own risk.

That is the project describing its own test coverage as thin, and it is worth taking seriously rather than as boilerplate. A dex protection shell runs before your application code does, it modifies the package in ways the platform only validates at install time, and it is the kind of component where the failure mode is a broken app on a subset of devices rather than a clean error. The tools that go into a build pipeline need a different level of regression testing than the ones that sit on a workstation, and the project is telling you that it has not had that investment.

The recent release cadence suggests active work on the tool itself, with three releases in the second half of August 2026. The repository is not archived and the last push was on 2026-08-31, the same day as the v2.21.0 tag.

The repository layout is small and legible, with the dpt/ directory for the tooling, the shell/ directory for the runtime shell, doc/ for documentation, the Gradle build files at the root, a .gitmodules for the native submodules, and both an English README and a Simplified Chinese one, with the English one linking to the Chinese as the other language of the document.

Editorial conclusion

Adopt dpt-shell if you ship an Android application whose dex you do not want readable by a decompiler, and you are prepared to own the operational consequences, because the project itself warns that it has few tests and that production use is at your own risk. Do not adopt it as a complete security boundary, since protecting the dex does not protect the data your app handles, and do not enable the anti-analysis checks in a build you intend to debug, since the flags to turn them off exist precisely because they get in the way. Before you start, run it on a debug build with --debug and the detection checks disabled, sign the output with your own keystore, and confirm that your analytics and crash reporting still see the app start correctly.

Frequently asked questions

What does dpt-shell do to an Android package?

It is an Android Dex protection shell that hollows out the Dex method implementations and reconstructs them at runtime, so the compiled code is not sitting in the package where a decompiler can read it. The protected package is then signed and shipped as usual.

How do I protect an APK or AAB with dpt-shell?

Download the executable.zip from the releases, unzip it, and run java -jar dpt.jar -f /path/to/android-package-file, where the package file argument accepts both .apk and .aab files. The -o option sets the output directory and the -c option points at a protect config file.

How do I build dpt-shell from source?

Run git clone --recursive https://github.com/luoyesiqiu/dpt-shell, then ./gradlew assemble, then run the jar from the executable directory with java -jar dpt.jar -f. The clone must be recursive because the native components are Git submodules.

How do I disable the anti-debug and Frida checks in dpt-shell?

There is a switch per check: --disable-anti-debug, --disable-frida-detect, --disable-crc-detect for the runtime libc .text CRC check, and --disable-acf for the app component factory, which the help text marks as a debug-only option. The --debug flag also makes the protected package debuggable.

What are the size and performance trade-offs in dpt-shell?

The -S option trades some of the app's performance for a smaller app size, the -K option keeps some classes in the package to improve startup speed but is not supported by some application packages, and the -e option excludes ABIs such as arm, arm64, x86 and x86_64. The README also warns that the project has few tests and should be used carefully in production.

Official sources

  1. Issues
  2. License: MIT
  3. luoyesiqiu/dpt-shell on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/luoyesiqiu-dpt-shell.svg)](https://hysenlabs.com/projects/luoyesiqiu-dpt-shell)

Was this useful?