DEV Community

chatmay
chatmay

Posted on

I Analyzed an XopProtector-Protected APK with apktool: The Shell Was There, but the Business Code Wasn't

I Analyzed an XopProtector-Protected APK with apktool: The Shell Was There, but the Business Code Wasn't

My Android reverse-engineering workflow has stayed largely the same for years: unpack the APK with apktool, open it in jadx, inspect the Application entry point, and then search for business logic related to login, payments, and authorization.

With an unprotected Release APK, this workflow can often reveal meaningful business code in just a few minutes.

This time, I analyzed an APK protected by XopProtector. I used exactly the same tools, and the analysis windows were full of content. But the original application's business logic was nowhere to be found.

1. The Tools Worked, but the DEX Contained No Business Logic

apktool unpacked the APK successfully, and AndroidManifest.xml was readable. When an APK can be unpacked without errors, it is tempting to assume that its protection is relatively weak.

However, inspecting classes.dex revealed a different picture. The entry point had been replaced with the shell's ProxyApplication. The code that jadx could decompile primarily consisted of the shell's startup logic and bridge code.

Some strings had also been processed using rolling XOR transformations. Searching for business-related keywords no longer led directly to the original source-level logic.

The original business classes and method bodies were no longer present in the visible DEX.

In some cases, another file named classes2.dex was also present, containing filler code. During packaging, the business DEX had been removed from its original location, so the APK root directory no longer contained the original plaintext classesN.dex files.

The first takeaway was straightforward:

Successful decompilation does not mean the application's business code has been exposed.

2. The File Named ZIP Wasn't a Normal Archive

The shell stored the business DEX data under assets/protector/, using a file named dexes.zip.

Naturally, my first instinct was to extract it as a regular ZIP archive. But checking the file header revealed that it did not begin with the standard ZIP signature, PK.

The protection process first packages the DEX files into a ZIP archive, then encrypts the entire archive, producing encrypted data with a PDX1 header. As a result, apktool cannot automatically restore the original DEX files.

The accompanying code.bin file is not ordinary Dalvik bytecode that can be directly decompiled by jadx either.

The config.json file is different. It is readable text containing configuration switches and integrity-related fields. However, the decryption key is not simply stored in that JSON file.

At runtime, relevant keys are derived using the master key, signing certificate, and package name. Configuration integrity is also verified.

If a risk-control setting is modified and the configuration check subsequently fails, the shell will not simply continue running with the altered configuration.

This differs significantly from simpler protection schemes. Finding a file named dexes.zip under assets does not mean its contents can be extracted using an ordinary ZIP utility.

3. The SO Files Were Present, but Their Critical Code Sections Weren't Plaintext

The APK contained native libraries, including libprotector.so, under the relevant ABI directories.

The basic ELF structure remained recognizable, but the shell could encrypt its own critical code sections during packaging. When inspecting the file statically, the contents were not necessarily the original machine instructions that could be followed directly in a disassembler.

For business libraries protected by SO encryption, the .text section could likewise contain ciphertext rather than the original plaintext code generated by the compiler.

Of course, hardening does not mean every native library must receive identical treatment. System libraries and certain third-party engines that might become unstable if their code were encrypted can be excluded according to the actual configuration.

The goal is to protect business code without sacrificing application startup and runtime compatibility.

Where runtime decryption is supported, code sections can be restored in memory before execution, rather than leaving decrypted code permanently exposed in an obvious directory on disk.

Consequently, the contents visible during static analysis may differ from the code actually executed by the process.

4. Debuggers and Hooks Don't Automatically Work

Once static analysis reaches a dead end, the next common step is to use Frida, attach a debugger, or employ other dynamic analysis tools to observe the shell's execution and look for opportunities to capture the decrypted business DEX.

XopProtector provides runtime protection mechanisms, including detection of common Frida artifacts, debuggers, and Xposed environments, as well as integrity checks for the shell's own native libraries.

Root and emulator detection can be configured independently; not every environment check has to be enabled by default. This is particularly important for applications running on industrial devices, customized vendor devices, and other specialized Android environments where false positives can create compatibility problems.

Depending on the configured policy, a detected risk can trigger an alert, a degraded operating mode, or immediate termination.

Application signing can also participate in runtime verification. When signature verification is enabled, replacing the APK's signing certificate can cause verification to fail, preventing the protected business code from being released through the normal execution path.

Dynamic analysis therefore involves more than finding the right Hook point. It also requires understanding the shell's execution mechanism, verification logic, and risk-control policies.

5. Extracting DEX from Memory Doesn't Mean Every Method Has Been Recovered

Even if the correct runtime moment is identified and DEX data is successfully captured from memory, it does not follow that every method has been restored to its original Java-level implementation.

It is important to distinguish between two types of VMP protection.

5.1 Bytecode Restoration-Based Protection

This approach removes certain original method instructions from the ordinary execution path and restores or writes them back at runtime.

If the capture occurs after the bytecode has been restored, it may be possible to recover the corresponding DEX instructions from memory.

The practical effectiveness of this approach therefore depends on factors such as method coverage, restoration timing, and the runtime implementation.

5.2 Native Interpreter-Based Protection

Another approach avoids relying on restoring the complete method body and writing it back into the DEX. Instead, it executes transformed custom instructions through a native interpreter.

In this mode, a protected method may retain only a bridge or jump stub leading to VmBridge in the DEX. The actual instructions are executed by the native interpreter.

Even if the DEX is captured, the result may still contain only the bridge stub rather than the original method body.

If the custom instruction encoding is also changed during each packaging operation, the instruction mappings can differ between APK builds, making static analysis and automated reconstruction more difficult.

5.3 Protection Coverage Still Depends on Configuration

Whole-package encryption and VMP are not the same thing.

Ordinary business code may briefly appear in an analyzable DEX form at runtime, while selected methods protected by native interpretation can avoid having their complete method bodies written back into the conventional DEX.

Therefore, it would be inaccurate to assume that every business method is protected by VMP.

Depending on the project configuration, developers can prioritize sensitive logic such as payments, authorization, activation, and cryptographic operations, while attempting to limit protection to the application's own business packages.

The real impact on reverse-engineering difficulty depends not only on whether the DEX is encrypted, but also on how sensitive methods are executed and which methods are actually covered.

6. What Does XopProtector Actually Defend Against?

Looking at a conventional static analysis workflow, XopProtector's value comes primarily from combining multiple protection mechanisms.

Analysis Stage Impact of Protection
Unpacking with apktool The APK can be unpacked, but protected business code is not directly recovered
Decompiling with jadx The visible code primarily consists of shell logic, startup code, and bridge stubs
Searching for business strings String transformations make straightforward keyword searches less effective
Extracting DEX from assets Encrypted data cannot be extracted as an ordinary ZIP archive
Static SO analysis Protected code sections may not contain plaintext instructions
Frida and debugger analysis Runtime detection raises the difficulty of dynamic analysis
Capturing runtime DEX Obtaining DEX does not necessarily recover native-interpreted method bodies
Modifying configuration or re-signing Integrity and signature checks can prevent unauthorized modifications

For analysts who primarily rely on apktool, jadx, and conventional static searches, these mechanisms can significantly increase the effort required to obtain usable business code.

Going further requires dealing with the native shell, key derivation, integrity verification, runtime loading, and potentially a custom instruction interpreter.

The focus of the work shifts from simply reading Java code to understanding the hardening framework itself.

7. Application Hardening Does Not Mean Absolute Protection Against Cracking

It is important to acknowledge the limitations.

XopProtector aims to increase the cost of reverse engineering, not to guarantee that an APK can never be cracked.

Features such as resource encryption, resource path obfuscation, proxy detection, and certificate pinning must be evaluated according to the actual project configuration. VMP also protects selected methods rather than automatically covering every line of Java code.

The final outcome depends on the protection mode, method coverage, Android version, and runtime environment.

For applications containing payment logic, authorization mechanisms, proprietary algorithms, or industry-specific protocols, combining DEX encryption, SO protection, runtime integrity checks, and native interpreter-based protection can make it considerably harder for conventional static analysis to reveal valuable business logic directly.

Conclusion

After analyzing an XopProtector-protected APK with apktool and jadx, the most obvious lesson was this:

Being able to unpack an APK does not mean its business code can be directly decompiled. Likewise, obtaining DEX does not mean its sensitive methods have been recovered.

XopProtector combines mechanisms such as DEX encryption, SO protection, runtime integrity verification, and VMP to make the conventional process of turning an APK into readable business source code substantially more difficult.

For Android developers who want to reduce the risk of exposing core algorithms, payment logic, authorization mechanisms, and industry-specific business protocols, this kind of layered protection offers more than relying on code obfuscation alone.

Project repository: https://github.com/xopJack/XopProtector

For a meaningful evaluation, verify the implementation details against the source code, configuration, and test results of the specific version being used. Startup performance, runtime stability, and compatibility should also be tested on the target devices.

Top comments (0)