Back

M8:2016 Code Tampering - Overview

What is Code Tampering?

Code Tampering is when an attacker modifies a mobile application's binary, its resources, or its behaviour in memory on a device they control—then runs the altered app to make it do something the developer never intended. Because the app executes on hardware the attacker fully owns (usually rooted or jailbroken), every byte of the code, every asset, and every value in RAM is reachable and rewritable. The trust boundary that a server enjoys simply does not exist on the client.

This is the eighth risk in the OWASP Mobile Top 10 (2016). It captures a family of techniques: repackaging (decompile, patch, resign, redistribute a trojanized app), binary patching (flip a single check—license, root, or "premium" flag), resource and asset modification, and runtime manipulation (hooking methods, editing memory, and rewriting return values while the app runs). The unifying idea is that the shipped artifact is not the artifact that runs.

Core principle: any logic, secret, or security decision that lives only on the device can be observed and changed. Code tampering is not a bug in one function—it is the consequence of trusting a runtime you do not control.

Core Concept

The developer ships:            The attacker runs:

  signed APK / IPA               repackaged APK / patched IPA
  isPremium() -> false           isPremium() -> true      (binary patch)
  isRooted()  -> true            isRooted()  -> false     (hook return value)
  license check enforced         license check NOP-ed out (smali edit)
  key stays in memory            key dumped via Frida hook
  server response respected      response rewritten in transit / in RAM

  ---------------------------------------------------------------
  Same icon, same name, DIFFERENT behaviour. The signature is the
  only thing that changed for the user; often they never notice.

The Four Faces of Tampering

Why Does This Matter?

Business Impact

Technical Impact

Technical Context

Why the Client Cannot Be Trusted

On a rooted Android device or a jailbroken iPhone, the attacker has privileges equal to or greater than the app itself. They can read the app's memory, attach a debugger, load their own libraries into the process, replace system frameworks, and rewrite the binary on disk. No check the app performs is authoritative, because the code performing the check can itself be patched or hooked away.

Trust reality on a device the attacker controls:

  App code            -> readable, patchable, re-signable
  App memory          -> readable and writable at runtime
  System APIs         -> hookable / replaceable
  "Secure" checks     -> only as strong as the code that runs them
  The user's identity -> may be the attacker themselves

  => The ONLY value that is authoritative is one your SERVER
     computes and enforces on infrastructure YOU control.

1. Repackaging an Android APK

# Conceptual flow (tools shown for understanding, not instruction)
apktool d target.apk            # decode resources + smali
# ... edit smali so a check returns the attacker's preferred value ...
apktool b target -o patched.apk # rebuild
# re-sign with an attacker-generated key (original signing key is unknown)
apksigner sign --ks attacker.keystore patched.apk

Key fact: the attacker cannot reproduce the developer's signing key, so the repackaged app carries a different signature. That difference is the single most reliable server-side and on-device signal that an app was repackaged.

2. Binary Patching a Check

# A license/premium gate in smali often looks like a branch:
    invoke-static {}, Lcom/app/License;->isPremium()Z
    move-result v0
    if-eqz v0, :locked          # attacker flips/removes this branch
    # ... premium feature ...

# Or in a native (Mach-O/ELF) function, one comparison is
# rewritten so the "denied" path is never taken.

3. Runtime Hooking (No File Change)

// Frida: force a root check to report "not rooted" at runtime
Java.perform(function () {
  var Sec = Java.use('com.app.security.RootCheck');
  Sec.isDeviceRooted.implementation = function () {
    return false;               // return value rewritten in memory
  };
});

Frida (Android + iOS), Xposed and its successors (LSPosed), Cydia Substrate, and objection let an attacker intercept any method, read its arguments, replace its body, and dump data—while the on-disk binary and signature stay untouched. This defeats naive integrity checks that only hash the file.

4. Memory Patching and Return-Value Modification

Tools in the "GameGuardian" class scan process memory for a value (score, coins, health), then rewrite it directly. More generally, an attacker locates a decision variable or a function's return register and edits it in place, changing behaviour with no persistent modification at all.

Where Tampering Attacks the App

TargetTechniqueAttacker goal
DEX / smali bytecodeRepackage, patch, re-signFlip a flag, inject code, redistribute
Native library (.so / Mach-O)Binary patch a comparison/branchBypass a check compiled to native
Bundled resources / assetsEdit strings, config, pinned certsChange behaviour, defeat pinning
Running process (methods)Frida / Xposed / Substrate hookRewrite return values, dump secrets
Process memoryMemory scan & editChange scores, coins, decision flags
Signing identityRe-sign with attacker keyMake the tampered app installable

Real-World Impact

To stay honest, the cases below describe well-documented classes of incident rather than inventing specific CVEs or figures. Each is a recurring, widely reported pattern in mobile security.

Class 1: Repackaged "Modded" Apps and Games

Pattern: A popular paid app or game is decompiled, its license or in-app-purchase check is patched out, and a "MOD"/"cracked"/"premium unlocked" build is posted to third-party stores and file-sharing sites.

Class 2: Trojanized Banking and Utility Apps

Pattern: Malware authors take a legitimate app, inject a malicious SDK or overlay component, re-sign it, and distribute it via phishing or lookalike stores. The app still works, so the victim does not suspect it.

Class 3: Runtime Bypass of Client-Side Security Checks

Pattern: On a rooted/jailbroken device, an attacker uses Frida or Xposed to defeat root detection and certificate pinning, then intercepts and modifies the app's traffic and behaviour.

Class 4: Game-Economy and Anti-Cheat Evasion

Pattern: Memory editors and hooking frameworks change in-game currency, stats, or unlock flags that the game trusts locally.

Prevalence and Detectability

Code tampering is ubiquitous wherever an app has value worth extracting—paid apps, games, finance, streaming, and anything with embedded secrets. The tooling (apktool, Frida, Xposed/LSPosed, objection, memory editors) is mature, free, and well documented, so the barrier to entry is low.

Rather than cite invented statistics, the defensible picture is:

Note: exact crack rates and malware counts vary by report and year. The durable takeaway is that a determined attacker will tamper with an app that runs on their device—so the control that matters is the one enforced where they have no privileges: your server.

Common Misunderstandings

Myth 1: "We check for root/jailbreak, so we're protected"

Reality: The root check is code, and code on the attacker's device can be hooked to return "not rooted." Detection raises cost and filters casual users; it is not a boundary.

Myth 2: "The app is signed, so it can't be modified"

Reality: An attacker simply re-signs with their own key after modifying it. Signing proves who built this copy, not that it is your copy—unless something verifies the signing certificate against the value you expect.

Myth 3: "Obfuscation makes the app tamper-proof"

Reality: Obfuscation raises the time and skill needed to understand and patch code. It never makes tampering impossible—it makes it more expensive. That is valuable defense-in-depth, not a guarantee.

Myth 4: "Our integrity check hashes the binary, so hooking won't work"

Reality: Runtime hooking (Frida/Xposed) changes behaviour in memory without altering the file, so a file-hash check sees a pristine binary. And the integrity check itself can be hooked.

Myth 5: "It only matters for games and paid apps"

Reality: Any app with secrets, entitlements, anti-fraud logic, or a valuable protocol is a target. Banking, streaming, healthcare, and enterprise apps are repackaged and hooked constantly.

Myth 6: "If we make the client strong enough, we don't need server checks"

Reality: There is no client strong enough on hardware the attacker owns. Client protections buy time and raise cost; only server-side enforcement is authoritative.

How Code Tampering Relates to Neighbouring Risks

AspectM8 Code Tampering (2016)Reverse Engineering (M9:2016)Insufficient Binary Protections (M7:2024)
FocusChanging how the app runsUnderstanding how the app worksMissing hardening that enables both
Attacker actionPatch, re-sign, hook, edit memoryDecompile, analyse, read secretsExploits weak/absent anti-tamper
Typical outcomeBypassed checks, trojan, fraudLeaked logic, extracted keysFaster, cheaper tampering
Primary defenseServer-side enforcement + integrityKeep secrets off the clientObfuscation, detection, attestation

The 2024 revision of the Mobile Top 10 merged tampering and reverse-engineering concerns into M7: Insufficient Binary Protections. The underlying lesson is unchanged: the client is untrusted.

Key Takeaways

  1. The client is fully controlled by the attacker—binary, resources, and memory are all rewritable on a rooted/jailbroken device.
  2. Every on-device security decision is bypassable by patching or hooking the code that makes it.
  3. Signing proves the builder, not the origin—a repackaged app is validly signed by the attacker's key.
  4. Client protections raise cost; the server enforces truth—keep secrets and enforcement server-side.
  5. Detection, obfuscation, and attestation are defense-in-depth, valuable together and insufficient alone.

How to Identify if You're Vulnerable

Ask these questions about your app:

If you answered "no" or "not sure" to several of these—especially the server-side ones—a determined user can already bypass your client-side controls.

Next Steps