DEV Community

Cover image for How Hackers Mod APKs: Android Reverse Engineering Explained
Sh Raj
Sh Raj

Posted on

How Hackers Mod APKs: Android Reverse Engineering Explained

How Hackers Mod APKs: Inside Android Reverse Engineering, Runtime Analysis, and App Integrity

By StackShade · Android Security · Reverse Engineering

How does a modified Android application emerge even after developers add obfuscation, server-side validation, and integrity checks? The answer lies in understanding where an application executes, where its trust decisions live, and what an attacker can observe or control.


1. Introduction: The Endless Battle Over Modified APKs

In the video How Hackers Mod APKs, Manguman opens with the recurring problem of modified versions of popular applications breaking after backend changes, only for new versions to appear later.

The video uses Spotify, Netflix, and mobile games to illustrate a broader security problem: developers invest heavily in protecting their applications, yet unauthorized modifications remain possible.

The apparent contradiction disappears when we distinguish between making software difficult to modify and making unauthorized behavior impossible to authorize.

An Android application is a package of executable code, resources, configuration, and sometimes native libraries. Much of its behavior executes on a device that the developer does not physically control. A determined analyst can inspect the package, observe execution, and experiment with the application in a controlled environment.

Developers therefore use several layers of protection:

  • Code obfuscation to increase the cost of understanding an application.
  • Server-side authorization to keep valuable decisions outside the client.
  • Transport security to protect communications.
  • Integrity verification to identify potentially modified applications or untrusted environments.
  • Runtime defenses and abuse detection to respond to suspicious behavior.

None of these controls solves every problem independently.

The central security principle

If an attacker controls the client environment, any security decision made exclusively by that client should be treated as potentially manipulable.

This does not mean every Android device is compromised or every local check is useless. It means that client-side checks should not be the sole authority for valuable server-controlled operations.


2. Understanding the Android APK

Before examining reverse engineering, we need to understand what an APK actually contains.

An APK, or Android Package Kit, is an archive containing the files needed to install an Android application. Depending on the application, these can include:

  • AndroidManifest.xml — application components, permissions, and configuration.
  • classes.dex — compiled Android bytecode.
  • res/ — compiled resources such as layouts and drawable assets.
  • assets/ — application-specific files.
  • lib/ — native libraries, often compiled for particular CPU architectures.
  • META-INF/ and signing-related information — package metadata and signature-related artifacts, depending on the package and signing scheme.

An APK is not simply a Java source-code directory compressed into a ZIP file.

2.1 From Java or Kotlin to executable Android code

Consider a simplified Android build pipeline:

flowchart TD
    A["Java / Kotlin source"] --> B["Compiler output"]
    B --> C["DEX bytecode"]
    C --> D["APK packaging"]
    D --> E["APK signing"]
    E --> F["Installation and execution"]

This is a conceptual view rather than a complete build pipeline. Android Gradle Plugin, D8, R8, resource processing, desugaring, native compilation, and signing may introduce additional stages.

The important point is that the installed package contains compiled representations, not necessarily the original source code.

Android Studio's APK Analyzer can inspect the final package, its DEX files, resources, manifest, and size distribution. This is useful both for debugging your own build and for authorized security analysis.

Technical reference: Android Developers, Analyze your build with APK Analyzer.

2.2 DEX versus Smali

The video introduces DEX and Smali as different representations of application logic.

DEX, or Dalvik Executable, is a bytecode format designed for Android's runtime environment. ART executes DEX-based application code.

Smali is a human-readable assembly-like representation of DEX instructions. It exposes operations such as register access, method invocation, conditional branching, and returning values.

For example, the following is an illustrative Smali-like fragment:

.method public isFeatureEnabled()Z
    .locals 1

    const/4 v0, 0x1
    return v0
.end method
Enter fullscreen mode Exit fullscreen mode

The method returns a boolean value of true.

This example demonstrates the representation, not a modification to any real commercial application.

A corresponding high-level method might look like this:

boolean isFeatureEnabled() {
    return true;
}
Enter fullscreen mode Exit fullscreen mode

However, decompilation is not a perfect reversal of compilation. The reconstructed Java may have different names, missing comments, altered control flow, synthetic methods, or other differences from the original source.

Important distinction: Smali is not the original Java source code, and a decompiler cannot reliably recover information that was discarded during compilation.


3. Static Analysis: Understanding an Application Without Running It

Static analysis examines an application or its code without requiring the relevant functionality to execute.

The video describes a workflow in which an analyst decompiles an APK, searches through the resulting representation, and tries to understand how particular features work.

The underlying technique is legitimate and widely used in malware analysis, vulnerability research, compatibility testing, and software auditing.

3.1 Why static analysis works

Compiled code still has structure.

Even when meaningful identifiers disappear, an analyst may be able to infer functionality from:

  • Method parameters and return types.
  • Conditional branches and control flow.
  • String constants and resource identifiers.
  • Network endpoints and request models.
  • Calls into Android framework APIs.
  • Serialization and deserialization logic.
  • References between classes and methods.

For example, a method that reads a subscription status, parses a response, and selects a user interface state may remain recognizable from its surrounding data flow, even if its original class and method names are unavailable.

This is why renaming methods alone does not eliminate the information contained in program behavior.

3.2 The danger of trusting local feature flags

Suppose an application contains a local feature flag:

boolean canUseAdvancedFeature = false;
Enter fullscreen mode Exit fullscreen mode

If the application uses this value as the sole authority for a valuable operation, the architecture is weak.

A local boolean can influence what the interface displays. But displaying a premium button and authorizing a premium transaction are fundamentally different operations.

A more robust architecture asks the backend to authorize the actual operation.

flowchart TD
    A["User requests a feature"] --> B["Android client"]
    B --> C["Authenticated API request"]
    C --> D["Backend authorization"]
    D --> E{"Entitled?"}
    E -->|Yes| F["Perform authorized operation"]
    E -->|No| G["Reject operation"]

The server must enforce the decision independently of the interface.

A modified interface might still display an enabled button, but that should not grant access to protected data or change a server-controlled account balance.

3.3 Obfuscation is not encryption

The video correctly identifies obfuscation as a technique for making code harder to understand.

Modern Android builds commonly use R8 to optimize and obfuscate application code. Depending on configuration, this can include renaming classes and methods, removing unreachable code, and simplifying program structure.

Consider the conceptual difference:

Technique Primary purpose
Minification Reduce application size by removing unnecessary code
Obfuscation Make program structure and intent harder to understand
Encryption Protect data from unauthorized disclosure when correctly applied
Integrity verification Detect or establish evidence of modification
Server-side authorization Decide whether an operation is permitted

Obfuscation changes how understandable the implementation is; it does not inherently make the application's behavior secret.

Encryption also requires care. If an application contains the decryption key and must decrypt a payload locally, a sufficiently privileged analyst may be able to inspect the key or the plaintext at runtime. Encrypting code or resources can increase complexity, but it is not a substitute for a sound trust model.

Additional technical context: OWASP's mobile security guidance treats obfuscation and anti-tampering as resilience measures, not replacements for core security controls. See OWASP MASVS: Resilience Against Reverse Engineering and Tampering.


4. Dynamic Analysis: Observing What Happens at Runtime

Static analysis answers questions about code structure. Dynamic analysis answers questions about actual behavior during execution.

The video explains that analysts can observe which functions execute when a user performs an action, even when the corresponding methods have meaningless names.

This is an important distinction: obscuring a method's name does not obscure the fact that it executes or the effects it produces.

4.1 What runtime analysis can reveal

In an authorized test environment, runtime analysis can help establish:

  • Which methods execute after a particular interaction.
  • Which values are passed between components.
  • Whether a security check actually affects a sensitive operation.
  • How network requests and responses influence application state.
  • Whether a supposedly protected value appears in memory.
  • Whether the application behaves differently on a modified or instrumented device.

The exact observations depend on the runtime, device privileges, available instrumentation, and the application's defenses.

4.2 Ghidra, IDA Pro, and runtime instrumentation

The video mentions Ghidra and IDA Pro as professional tools for analyzing applications.

The terminology needs some precision:

  • Static disassemblers and decompilers, including Ghidra and IDA, help analysts inspect machine code and reconstruct program structure.
  • Debuggers help inspect execution state, breakpoints, registers, and memory.
  • Dynamic instrumentation tools can observe or instrument running applications when the environment and protections permit it.

These capabilities overlap in some toolchains, but they are not interchangeable. A static disassembler does not, by itself, provide a complete live view of every function executing on an Android device.

4.3 Why runtime observation defeats some obfuscation strategies

Imagine a method named a() that returns an integer. Its name reveals almost nothing.

But suppose repeated observations establish that:

  1. The method executes when an account screen opens.
  2. Its result changes when the account's entitlement changes.
  3. Its output influences a local interface decision.

The analyst has learned something about the method through behavior rather than its identifier.

This illustrates a broader principle from software security: program behavior can leak semantic information that names and source-code structure conceal.

It does not imply that every relevant function can be identified easily. Large applications, native code, asynchronous execution, reflection, and runtime protections can make analysis substantially harder.


5. Server-Side Validation: Moving Trust Away from the Device

The video next discusses a more meaningful architectural defense: moving sensitive logic and authorization to a backend.

Consider a streaming service. The mobile client sends a request to play protected content. The backend verifies the account's entitlement and determines whether the requested operation is allowed.

A simplified request might contain:

{
  "content_id": "example-content-123",
  "session_id": "authenticated-session"
}
Enter fullscreen mode Exit fullscreen mode

This is an illustrative payload, not a real service API.

The server should not accept a client's assertion that the account is premium merely because the client says so.

Instead, it should establish the user's identity, retrieve authoritative entitlement data, evaluate the requested operation, and enforce the result.

5.1 The difference between authentication and authorization

These terms are related but distinct:

  • Authentication: Who is making the request?
  • Authorization: Is that identity permitted to perform the requested operation?
  • Integrity verification: Is there evidence that the client or execution environment satisfies expected integrity conditions?

A valid login does not automatically establish a paid entitlement. A valid subscription does not necessarily authorize every resource. A genuine application binary does not guarantee that the account has permission to access a particular feature.

A robust design evaluates the relevant conditions separately.

5.2 Backend authority and entitlement checks

For a subscription-based application, a backend can follow this pattern:

receive request
    ↓
validate session and identity
    ↓
load authoritative entitlement
    ↓
check resource and operation permissions
    ↓
apply rate limits and abuse rules
    ↓
allow or reject the operation
Enter fullscreen mode Exit fullscreen mode

The client can cache information for responsiveness, but the backend must revalidate sensitive operations where appropriate.

This is especially important for purchases, virtual currency, competitive game results, protected content, account changes, and access to private data.

5.3 What server-side validation does not solve

Moving logic to the backend does not make all attacks disappear.

For example, a client may still:

  • Display an interface that suggests an unauthorized feature is available.
  • Extract information already delivered to the device.
  • Automate legitimate endpoints in abusive ways.
  • Exploit broken authorization rules on the server.
  • Misuse an account with otherwise valid access.

Server-side enforcement protects operations and data that remain under the server's control. It cannot retroactively make information secret after that information has been delivered to an untrusted client.

This is why server-side authorization must be combined with data minimization, appropriate session management, abuse prevention, and secure API design.


6. Network Interception and Response Tampering

The video describes an attacker intercepting a server response, changing an entitlement-related value, and returning the modified response to the client.

This is a useful conceptual illustration of why clients should not be trusted blindly. However, it simplifies the network-security problem.

6.1 HTTPS changes the situation

With correctly implemented HTTPS, traffic is encrypted and authenticated in transit. A passive observer on the network cannot simply read and modify the response without detection.

To inspect or alter HTTPS traffic, an analyst generally needs an additional capability or weakness, such as:

  • Control of the client or its runtime.
  • A test environment configured to trust an interception proxy.
  • A certificate-validation weakness.
  • A compromised endpoint or trust configuration.
  • Another application or protocol flaw that exposes the relevant data.

The specific possibilities depend on the implementation and the analyst's level of access.

6.2 Certificate validation and pinning

Android supports Network Security Configuration, which lets developers configure trust anchors and other network-security settings. Certificate pinning can further restrict which certificates or public keys an application accepts.

These controls can make certain interception scenarios more difficult. They must be implemented carefully, with backup pins and a tested certificate-rotation strategy where pinning is used.

See the Android Network Security Configuration documentation.

However, transport security and authorization address different questions:

  • TLS helps protect the authenticity and confidentiality of communication.
  • Certificate validation helps establish whether the peer's certificate chain is trusted.
  • Backend authorization determines whether an operation is permitted.

Even a perfectly encrypted connection does not fix an API that grants access based on a client-controlled entitlement field.

6.3 A safer mental model

flowchart TD
    A["Android client"] --> B["TLS-protected request"]
    B --> C["Backend"]
    C --> D["Authenticate identity"]
    D --> E["Authorize operation"]
    E --> F["Return permitted data"]
    F --> G["Client displays result"]

The backend's authorization decision must remain authoritative. The client should not be able to turn an unauthorized request into an authorized one merely by changing a locally interpreted response flag.


7. Google Play Integrity: What It Does and Does Not Do

The video describes Google Play integrity protection as a mechanism that checks whether the installed application matches its expected compiled state. It then suggests that removing the relevant library can make a modified application work again.

This description captures the general purpose of integrity protection, but it is not an accurate account of the complete Play Integrity architecture.

7.1 What the Play Integrity API actually provides

Google Play Integrity helps a developer assess whether requests or user actions are associated with a recognized application binary, an expected installation context, and a device environment that meets configured integrity criteria.

Depending on the API response and enabled capabilities, relevant signals can include:

  • App integrity: Whether the application binary is recognized as expected.
  • Account details: Information relevant to the app's installation or acquisition context.
  • Device integrity: Signals about the device environment.
  • Additional risk signals: Optional verdicts that can help identify suspicious access, known malware, or anomalous activity.

The exact verdicts depend on the API, configuration, and environment.

Official documentation: Overview of the Play Integrity API.

7.2 The backend is part of the design

A typical integrity-protected operation follows this sequence:

sequenceDiagram
    participant U as User
    participant A as Android App
    participant P as Google Play Integrity
    participant B as Application Backend

    U->>A: Request sensitive action
    A->>P: Request integrity token
    P-->>A: Return integrity token
    A->>B: Send action and token
    B->>P: Verify/decode token
    P-->>B: Return integrity verdict
    B->>B: Check verdict and authorize action
    B-->>A: Allow or reject

This is a simplified conceptual sequence. The precise verification flow differs by API request type and integration details.

The crucial architectural detail is that the application backend evaluates the integrity evidence. The token should not be treated as an arbitrary client-provided string or a permanent guarantee that every future request is safe.

For standard requests, developers should bind relevant request data to the integrity request and verify that binding on the backend. Otherwise, a valid verdict may not provide the intended assurance about the particular operation being authorized.

Google's guidance also recommends requesting verdicts close to the protected action and using a tiered enforcement strategy rather than treating every non-ideal signal identically.

7.3 Why a client-side integrity check is not enough

An integrity-related SDK running in the client is one component of a larger verification flow.

If the backend grants access solely because the client claims that an integrity check succeeded, the architecture has placed too much trust in the component it is trying to assess.

Instead, the backend should validate the integrity token using the documented verification process and combine the resulting verdict with authentication, authorization, and abuse controls.

This distinction matters because a local check, an integrity token, and a backend authorization decision are three different things.

7.4 Can integrity checks be bypassed?

The video suggests that removing or stubbing an integrity library can defeat the protection.

That may describe some poorly designed client-side checks or a simplified demonstration. It should not be generalized to the Play Integrity API as a whole.

Removing an SDK from a modified APK does not, by itself, produce a valid Google-issued integrity verdict or cause a properly configured backend to accept the modified application.

An application may still have weaknesses if it relies on local checks, mishandles tokens, accepts replayed or incorrectly bound requests, or fails to enforce the backend decision. The outcome depends on the actual implementation.

Play Integrity is a risk signal and integrity mechanism, not a magic shield. Its security value depends on correct server-side verification and enforcement.


8. Why the Cycle Continues: A Layered Security Model

The video repeatedly returns to the same pattern: developers introduce a defense, and analysts find new ways to study the application or its surrounding system.

The deeper lesson is not that every defense is useless. It is that each defense addresses a different part of the problem.

Defense What it helps with What it cannot guarantee
R8 obfuscation Makes code structure harder to understand That code cannot be analyzed
Resource protection Reduces exposure of selected resources That runtime plaintext remains secret
Runtime checks Detects some forms of instrumentation or tampering That every hostile environment is detected
TLS and certificate validation Protects communications against many network attacks That the client itself is trustworthy
Play Integrity Supplies evidence about app and device integrity That every request is authorized or every attack is prevented
Backend authorization Enforces permissions for server-controlled operations That already-delivered information stays secret
Abuse detection and rate limiting Helps identify and constrain suspicious behavior That every abusive action is detected immediately

OWASP's mobile security guidance emphasizes defense in depth and explicitly warns against using anti-tampering or obfuscation as a substitute for sound security architecture.

Reference: OWASP MASVS — Resilience.

8.1 The economics of reverse engineering

Security controls also change the cost of an attack.

An analyst may spend minutes inspecting an uncomplicated application, hours understanding an obfuscated codebase, or substantially longer investigating a complex application with native components, runtime checks, and backend dependencies.

These are illustrative possibilities, not predictable timelines.

The defender's goal is to make unauthorized modification more expensive, limit the value of any successful modification, and detect or constrain abuse when prevention fails.

A successful defense therefore need not make reverse engineering impossible. It can ensure that reverse engineering alone does not grant access to protected server-side capabilities.


9. A Defensive Architecture for Android Developers

For developers building subscription-based applications, games, or other Android products, a layered design is more reliable than relying on a single anti-modification technique.

Layer 1: Harden the application build

  • Enable appropriate R8 optimization and obfuscation for release builds.
  • Avoid shipping debug artifacts, unnecessary secrets, or internal endpoints.
  • Review resources and bundled assets for sensitive information.
  • Verify the final artifact rather than assuming the build configuration produced the intended result.

Layer 2: Keep authoritative state on the backend

  • Store subscription entitlements and virtual currency balances on the server.
  • Validate purchase information using the appropriate trusted purchase-verification flow.
  • Perform sensitive state transitions transactionally.
  • Do not accept client-reported prices, balances, or entitlement flags as authoritative.

Layer 3: Secure network communication

  • Use HTTPS with correct certificate validation.
  • Configure Android network security deliberately.
  • Consider certificate pinning when the threat model justifies its operational costs.
  • Never ship private server credentials or long-lived privileged secrets inside the APK.

Layer 4: Integrate integrity signals

  • Use Play Integrity where appropriate for the distribution model and risk profile.
  • Verify integrity tokens on the backend according to Google's documented flow.
  • Bind the verdict to the relevant request data.
  • Use verdicts in proportion to the sensitivity of the action being protected.

Layer 5: Detect and constrain abuse

  • Apply per-account and per-action rate limits.
  • Detect anomalous gameplay, transaction patterns, and request volumes.
  • Log security-relevant decisions without unnecessarily collecting sensitive user data.
  • Revoke sessions or restrict actions when evidence justifies intervention.
  • Provide recovery paths for legitimate users incorrectly classified as risky.

Layer 6: Test the system, not only the APK

A secure-looking client can still communicate with an insecure API. Security assessments should therefore examine both the Android package and the backend.

For a structured assessment, use the OWASP Mobile Application Security Verification Standard and its companion testing resources.


10. A Safe Reverse-Engineering Lab for Learners

The concepts in the video are best learned using an application you own or are explicitly authorized to test.

A small educational project can demonstrate compilation, decompilation, obfuscation, runtime behavior, and backend authorization without targeting a commercial application or defeating its access controls.

Lab A: Inspect your own APK

  1. Create a minimal Android application with a non-sensitive feature flag.
  2. Build a debug APK and inspect it using Android Studio's APK Analyzer.
  3. Examine the manifest, DEX contents, resources, and package structure.
  4. Compare the readable structure with a release build using R8.
  5. Record which names, resources, and structural details remain visible.

Learning objective: Understand what compilation and obfuscation change—and what they do not.

Lab B: Observe runtime behavior

  1. Add a harmless diagnostic method to your own application.
  2. Log a feature decision and its inputs in a debug-only build.
  3. Set breakpoints and observe the call sequence.
  4. Compare source-level names with the optimized release representation.
  5. Document the limits of what you can infer from a single execution.

Learning objective: Understand the difference between static code inspection and dynamic analysis.

Lab C: Compare client-side and server-side authorization

Build a small application with two feature implementations:

  • A local demonstration feature controlled by a boolean.
  • A protected test endpoint that independently checks an authenticated user's entitlement.

In the second implementation, make the backend authoritative. Test that an enabled interface does not grant access when the backend denies the operation.

Learning objective: Demonstrate why changing interface state must not change server-side permissions.

Lab D: Evaluate integrity decisions

In a development environment, document how your application responds to different integrity verdicts and how the backend handles missing, invalid, or mismatched verification data.

Use Google's official Play Integrity testing and integration guidance. Do not treat the absence of a verdict as proof that a user is malicious; design an appropriate fallback or restricted mode for your threat model.

Learning objective: Understand how integrity evidence contributes to a larger authorization decision.


11. Common Misconceptions to Avoid

Misconception 1: Decompiled code is the original source code.

No. Decompiled output is a reconstruction. It may be incomplete or structurally different from the original implementation.

Misconception 2: Obfuscation makes code impossible to understand.

No. Obfuscation increases the effort needed to interpret a program but does not guarantee secrecy.

Misconception 3: HTTPS prevents all response tampering.

HTTPS protects data in transit under its security assumptions. It does not prevent a sufficiently privileged analyst from manipulating a client or eliminate application-level authorization flaws.

Misconception 4: Keeping logic on the server makes everything secure.

Server-side enforcement protects operations controlled by the backend. It does not protect secrets that have already been delivered to the client or fix vulnerabilities in the backend itself.

Misconception 5: Play Integrity is just a library that crashes modified applications.

No. Play Integrity provides integrity verdicts through a documented verification flow. The backend's correct interpretation and enforcement of those verdicts are essential.

Misconception 6: If a mod works, the developer's entire security system has failed.

Not necessarily. A modified interface may appear to unlock a feature without obtaining the protected resource or successfully performing the protected operation. The security impact depends on what the modification actually enables.


12. Conclusion: Design for an Untrusted Client

Manguman's video provides an accessible introduction to a genuine and important area of Android security: the ongoing effort to understand and modify compiled applications.

Its main narrative moves through static analysis, obfuscation, runtime analysis, backend validation, and integrity checks. These techniques are useful concepts for developers and security learners to understand.

The technical nuance is that no individual technique establishes complete security. Obfuscation raises the cost of inspection. TLS protects communication. Integrity verdicts provide evidence about the application and environment. Backend authorization determines whether protected operations should proceed.

These mechanisms become more effective when they reinforce one another.

For Android developers, the most valuable design principle is straightforward:

Assume that the client can be inspected, that local state can be manipulated, and that requests can be replayed or automated unless your architecture prevents it. Then enforce security where the authority and valuable resources actually reside.

That approach does not eliminate reverse engineering. It ensures that understanding an application is not, by itself, enough to defeat its security model.


References and Further Reading

  1. Android Developers — Analyze your build with APK Analyzer
  2. Android Developers — Overview of the Play Integrity API
  3. Android Developers — Make a standard Play Integrity API request
  4. Android Developers — Network Security Configuration
  5. OWASP — MASVS: Resilience Against Reverse Engineering and Tampering
  6. OWASP — Mobile Application Security Verification Standard

Editorial note: The discussion of the Spotify incident and the individual techniques attributed to attackers follows the supplied transcript. Those claims are not independently verified here. The explanations of DEX, Smali, network security, and Play Integrity provide additional technical context based on the references above.

Top comments (0)