Understanding Android Signature Verification
Android’s security model heavily relies on application signatures. Every Android Application Package (APK) must be digitally signed with a developer’s private key. This signature serves multiple critical purposes: it verifies the application’s author, ensures the app’s integrity (meaning it hasn’t been tampered with since being signed), and facilitates updates, as Android only permits updates from an APK signed with the same key as the currently installed version. Without a valid signature, an Android device will refuse to install or update an application, making signature verification a fundamental security gatekeeper.
However, understanding how this mechanism works also opens avenues for security research and, unfortunately, potential malicious exploitation. While outright bypassing signature verification for an already installed app without root access or system-level exploits is generally infeasible, an attacker can modify an existing APK and re-sign it with their own key, effectively creating a new, malicious version of the application that Android will trust to install.
The Android Manifest: Gateway to Application Behavior
At the heart of every Android application lies the AndroidManifest.xml file. This declarative XML file provides essential information about the application to the Android system. It declares the app’s package name, permissions it requires, hardware and software features it uses, and crucially, all its components: activities, services, broadcast receivers, and content providers. Manipulating this file can drastically alter an application’s behavior, even without modifying a single line of its original Java/Kotlin source code.
For an attacker, the manifest is a prime target. By injecting new components or altering existing ones, they can:
- Request additional, often dangerous, permissions.
- Inject new malicious activities that can be launched internally or externally.
- Register new broadcast receivers to intercept system events or inter-app communications.
- Introduce new services for background operations, data exfiltration, or remote control.
- Manipulate intent filters to hijack existing functionalities or expose new ones.
The Attack Vector: Manifest Manipulation & Re-signing
The core concept of this bypass involves decompiling an existing, legitimate application, modifying its AndroidManifest.xml and potentially injecting minimal Smali code, then recompiling and re-signing the entire package. Since Android’s signature verification occurs during installation, if an attacker provides a new, valid signature (even if it’s their own), the system will treat the modified APK as a legitimate, albeit different, application.
The process typically involves these steps:
- Decompilation: Extracting the APK’s contents, including the compiled resources and Smali code, using tools like Apktool.
- Manifest Modification: Editing the
AndroidManifest.xmlto introduce new components or alter existing permissions and attributes. - Code Injection (Optional but Recommended): If new components are added (e.g., a new activity), corresponding Smali code must be created and placed in the appropriate directory to give the component functionality.
- Recompilation: Rebuilding the APK with the modified manifest and injected code.
- Re-signing: Digitally signing the newly compiled APK with a new cryptographic key pair generated by the attacker.
- Zipalign: Optimizing the APK for better resource access and reduced RAM consumption.
- Installation: Attempting to install the modified and re-signed APK on a target device.
Practical Example: Injecting a Malicious Activity
Let’s illustrate this with a step-by-step example where we inject a simple malicious activity into an existing APK.
1. Decompile the Target APK
First, we need to decompile the target application. Let’s assume the original application is named original.apk.
apktool d original.apk -o original_decompiled
2. Modify AndroidManifest.xml
Navigate to the original_decompiled directory and open AndroidManifest.xml. We’ll add a new activity that could, for example, collect device information or display a phishing UI. For simplicity, let’s add a basic activity definition.
Locate the <application> tag and add a new <activity> entry. Assume our malicious activity is named .MaliciousActivity.
<application ...> ... <activity android:name=".MaliciousActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> ...</application>
Note: Making it a LAUNCHER activity means it can be started directly from the home screen. Remove the original LAUNCHER activity’s intent filter if you want to replace the app’s entry point.
3. Inject Smali Code for the New Activity
We need to create the Smali code for our MaliciousActivity. Create a new file, for example, original_decompiled/smali/com/example/originalapp/MaliciousActivity.smali. A very basic example would be:
.class public Lcom/example/originalapp/MaliciousActivity;.super Landroid/app/Activity;.method public constructor <init>()V .locals 0 invoke-direct {p0}, Landroid/app/Activity;-><init>()V return-void.end method.method protected onCreate(Landroid/os/Bundle;)V .locals 2 .param p1, "savedInstanceState" # Landroid/os/Bundle; invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V const-string v0, "MaliciousApp" const-string v1, "Malicious Activity Launched!" invoke-static {p0, v1, v0}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast; move-result-object v0 invoke-virtual {v0}, Landroid/widget/Toast;->show()V return-void.end method
This simple Smali code will show a toast message when the activity is launched. In a real attack, this would contain code for data exfiltration, C2 communication, etc.
4. Recompile the APK
Now, rebuild the APK with the modifications:
apktool b original_decompiled -o modified.apk
5. Generate a New Signing Key
If you don’t have a signing key, generate one:
keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
Follow the prompts to set passwords and details.
6. Sign the Modified APK
Sign the modified.apk using your newly generated keystore. For older Android versions (pre-Android 9), `jarsigner` is common:
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore modified.apk alias_name
For modern Android (API 28+), `apksigner` is recommended and usually found in the Android SDK build tools:
apksigner sign --ks my-release-key.keystore --ks-key-alias alias_name modified.apk
7. Zipalign the Signed APK
This step optimizes the APK, making sure all uncompressed data starts at a particular alignment. This allows the OS to read resources directly without decompressing them.
zipalign -v 4 modified.apk signed_aligned.apk
8. Install the Modified APK
Finally, install the re-signed and aligned APK on an Android device or emulator:
adb install signed_aligned.apk
If the package name is the same as an already installed app, you’ll need to uninstall the original first, as the signatures won’t match for an update. However, if the package name was changed or it’s being installed as a fresh app, it should install successfully, proving the signature bypass in the context of creating a new, installable version.
Implications and Mitigation
This technique demonstrates a powerful way to inject functionality into existing applications. Attackers can leverage this for various malicious purposes, including:
- Spyware: Injecting code to record calls, log keystrokes, or steal sensitive data.
- Adware: Forcing unwanted advertisements onto users.
- Ransomware: Encrypting user data or locking the device.
- Phishing: Displaying fake login screens to steal credentials.
- Bypassing Restrictions: Adding permissions or components that the original developer never intended.
Defensive strategies include:
- App Integrity Checks: Implementing runtime checks within the app to verify its own signature, checksums, or sensitive file hashes. If a mismatch is detected, the app can refuse to run or trigger an alert.
- Root Detection: Many advanced tampering techniques require a rooted device. Detecting root can be an early warning.
- Obfuscation and Anti-Tampering Tools: Using commercial solutions that make reverse engineering and modification more difficult.
- Certificate Pinning: For network communications, ensuring the app only trusts specific server certificates, preventing Man-in-the-Middle attacks even if the app’s integrity is compromised.
- User Education: Warning users against installing apps from untrusted sources or sideloading APKs.
While Android’s signature verification is robust at the installation stage, understanding how attackers can re-sign and re-distribute modified applications is crucial for developing resilient security countermeasures. Developers must proactively integrate runtime integrity checks to protect their applications against such manifest-level code injection attacks.
Android Mobile Specs & Compare Directory
Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!
Compare Devices Specs →