Anti-Tampering and the Integrity Chain
Signature self-checks, file checksums and component verification — how to detect a repackaged build before it does damage.
Repackaging remains the single most common attack on Android apps, and it works precisely because the platform's own signature check cannot detect it. An attacker unpacks your APK, patches a licence check or injects an ad SDK, re-signs with their own key, and distributes it. The platform sees a validly signed package from a new author and installs it without complaint. Detecting this requires the app to verify its own identity from the inside.
The first layer is the signature fingerprint check. Read PackageManager.GET_SIGNING_CERTIFICATES, take apkContentsSigners, hash the certificate, and compare against the release fingerprint baked into the build. It is three lines of Kotlin — and three lines an attacker can delete. Which is why a Java-layer check is only ever a fast reject, never the real defence.
The real check moves to native code. A JNI-side verifier is far more expensive to locate and patch, but it must be more than a boolean: return a value the app depends on cryptographically, or feed a key derivation, so NOP-ing the return produces a broken app rather than a bypassed one. Pair it with checksums over classes.dex and critical .so files computed at build time and re-validated at runtime. VALLUM's SignSeal layer embeds this as a multi-layer integrity chain covering APK signature, file checksums, and component authenticity.
Component authenticity is the part most teams skip. Android resolves activities, services, and receivers by name, and a repackaged build can add or replace them to intercept intents. Verify at runtime that the manifest you expect matches the manifest that was installed: enumerate PackageInfo.activities, services and receivers and compare the set against a build-time digest. It is cheap, and it closes the injection path that signature checks alone leave open.
Design the response carefully, because an over-eager tamper check is a self-inflicted outage. Vendor ROMs, app stores that re-sign, and corporate MDM wrappers all produce legitimate signature mismatches. Prefer graceful degradation — disable sensitive features, report the anomaly to your backend, show a warning — over an immediate kill that turns an edge case into a one-star review. Reserve hard aborts for the highest-value paths, and always ship them behind a server-side flag so you can stand them down without a release.
Treat all of this as raising cost, not achieving impossibility. A determined attacker with the device in hand will eventually find the check; the goal is to make that take longer than the value of the asset. Integrity verification earns its keep when it is combined with obfuscation (so the check is hard to find), function extraction (so the logic is not sitting in the DEX to read), and RASP (so patching it at runtime is detected) — the layering argument again.