Table of Contents
- Toolchain Setup & Version Requirements
- Decompilation & Recompilation Workflow
- Split APK Handling (.apks / .apkm / .xapk)
- Signing: Schemes, Pitfalls, and Fix Procedures
- Dependency Hell: Frameworks, AndroidX, OEM Resources
- GApps-Dependent APKs: Patching & Workarounds
- Testing Without a Physical Device
- Local Testing: Redroid & Waydroid
- Advanced Patching Techniques → references/advanced-patching.md
- Adding Features to an APK → references/adding-features.md
- Delivering the APK to an End User → references/delivery.md
- End-to-End Workflow: Zero to Delivery → references/end-to-end-workflow.md
- Troubleshooting: Common Failures & Resolutions → references/troubleshooting.md
- Quick Reference Cheat Sheet → references/quick-reference.md
1. Toolchain Setup & Version Requirements
1.1 Mandatory Tools (Current Stable Versions)
| Tool | Version | Purpose | URL |
|---|---|---|---|
| Apktool | v2.11.0 (Jan 2025) | Decompile/recompile APKs | https://github.com/iBotPeaches/Apktool |
| jadx | v1.5.1 | Read-only Java decompiler | https://github.com/skylot/jadx |
| smali/baksmali | v3.0.9 (bundled in Apktool 2.11.0) | DEX bytecode assembly/disassembly | https://github.com/baksmali/smali |
| APKEditor | Latest | Merge/split APK handling | https://github.com/pxb1988/APKEditor |
| apksigner | SDK Build Tools 35.0.x | APK signing (v1/v2/v3/v4) | Part of Android SDK |
| zipalign | SDK Build Tools 35.0.x | APK alignment before signing | Part of Android SDK |
| Frida | v17.9.0 | Dynamic instrumentation | https://frida.re |
| ADB | Platform Tools 35.0.x | Device/emulator communication | Part of Android SDK |
1.2 Why Old Tool Versions Will Fail You
- Apktool < 2.9.0 — Cannot handle API 34+ resources. Decompilation of Android 14+ APKs will crash with obscure AAPT errors or produce corrupted
resources.arscoutput. - Apktool < 2.6.0 — Uses legacy AAPT (v1) instead of AAPT2. Causes massive resource recompilation failures with any app using AndroidX, Material Components, or modern resource features.
- smali < 3.0.5 — Cannot disassemble DEX format 038+ (Android 13+). baksmali will fail with
"Unsupported DEX format"on any app compiled withminSdkVersion 33+. - jarsigner (any version) — Only produces v1 (JAR) signatures. Modern Android (7.0+) requires at minimum v2 signatures. Using jarsigner will result in
INSTALL_PARSE_FAILED_NO_CERTIFICATESor silent signature rejection.
1.3 Environment Setup
# Java 11+ required (Java 17 recommended)
java -version # verify
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
# Install Apktool
# Download latest from https://github.com/iBotPeaches/Apktool/releases
# Move apktool.jar to PATH
chmod +x apktool
sudo mv apktool /usr/local/bin/
# Install Android SDK Build Tools (for apksigner + zipalign)
sdkmanager "build-tools;35.0.0"
sdkmanager "platform-tools"
# Install jadx
# Download from https://github.com/skylot/jadx/releases
# Extract and add bin/ to PATH
# Install Frida
pip install frida-tools
# Install APKEditor
# Download APKEditor.jar from https://github.com/pxb1988/APKEditor/releases
2. Decompilation & Recompilation Workflow
2.1 Standard Single APK Workflow
# Step 1: Analyze with jadx first (understand what you're modifying)
jadx-gui target.apk # GUI for exploration
# or
jadx -d analysis_output/ target.apk # CLI
# Step 2: Decompile with apktool
apktool d target.apk -o decompiled/ -f
# -f = force overwrite existing output directory
# --api 35 = force target API level (use if resource errors occur)
# Step 3: Make modifications
# - Edit smali files in decompiled/smali*/ (Java bytecode)
# - Edit resources in decompiled/res/ (layouts, strings, drawables)
# - Edit AndroidManifest.xml (permissions, components)
# - Edit assets/ (bundled files, web content)
# Step 4: Recompile
apktool b decompiled/ -o rebuilt.apk
# Use --use-aapt2 flag if not on 2.11.0 (now default)
# Step 5: Align (CRITICAL - must be BEFORE signing)
zipalign -v -p 4 rebuilt.apk rebuilt_aligned.apk
# Step 6: Sign
apksigner sign --ks ~/.android/debug.keystore \
--ks-pass pass:android \
--key-pass pass:android \
rebuilt_aligned.apk
# Step 7: Verify
apksigner verify --verbose --print-certs rebuilt_aligned.apk
# Step 8: Install
adb install -r rebuilt_aligned.apk
2.2 Important Decompilation Flags
# Force specific API level (fixes many resource resolution issues)
apktool d target.apk -o out/ -f --api 35
# Ignore missing resources (new in 2.11.x, for XAPK/base APK decompilation)
apktool d target.apk -o out/ -f --ignore-missing-resources
# Keep original sources only (skip resources, faster)
apktool d target.apk -o out/ -f -s
# Decode only resources (skip smali, for resource-only mods)
apktool d target.apk -o out/ -f -r
# For Samsung/Android 15 framework JARs with DEX format 039
apktool d framework.jar -api 29 -o out/
# Use a custom framework file (for OEM system APKs)
apktool if /path/to/oem-framework.apk
apktool d system_app.apk -o out/ -f
2.3 Understanding the Decompiled Structure
decompiled/
├── AndroidManifest.xml # App manifest (XML format, editable)
├── apktool.yml # Apktool metadata (DO NOT manually edit version fields)
├── assets/ # Raw asset files
├── lib/ # Native libraries (.so files) by architecture
│ ├── arm64-v8a/
│ ├── armeabi-v7a/
│ └── x86_64/
├── original/ # Original files (META-INF, etc.)
├── res/ # Compiled resources (XML format, editable)
│ ├── values/ # String values, styles, colors, dimensions
│ ├── layout/ # Layout XML files
│ ├── drawable/ # Vector drawables, layer lists
│ ├── xml/ # Preferences, searchable config, etc.
│ └── mipmap-*/ # App icons by density
├── smali/ # Disassembled DEX bytecode (MAIN CODE)
│ ├── com/
│ │ └── example/
│ │ └── app/
│ │ ├── MainActivity.smali
│ │ └── ...
│ └── android/
│ └── ...
└── smali_classes2/ # Additional DEX files (multi-dex apps)
└── ...
2.4 Smali Editing Basics
Smali is a human-readable representation of DEX bytecode. Key patterns you'll encounter:
# Class declaration
.class public Lcom/example/app/MainActivity;
.super Landroidx/appcompat/app/AppCompatActivity;
# Method declaration
.method public onCreate(Landroid/os/Bundle;)V
.registers 4
# Calling super
invoke-super {p0, p1}, Landroidx/appcompat/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V
# Setting a field
const-string v0, "patched_value"
iput-object v0, p0, Lcom/example/app/MainActivity;->mStatus:Ljava/lang/String;
# Conditional check
if-eqz v0, :cond_skip
# ... code ...
:cond_skip
return-void
.end method
# Common patches:
# 1. Force a boolean method to always return true
.method public isPremium()Z
.registers 2
const/4 v0, 0x1 # true
return v0
.end method
# 2. NOP out a method call (comment effect by replacing with nop)
# Replace: invoke-virtual {v0}, Lcom/example/Payroll;->show()V
# With: nop
# 3. Remove a permission check
# Replace: if-eqz v0, :cond_deny
# With: nop # always falls through
2.5 Resource Editing
Resources are stored as compiled XML (AXML) that apktool decodes. Common modifications:
<!-- res/values/strings.xml - Change app name -->
<string name="app_name">My Patched App</string>
<!-- res/values/colors.xml - Change theme colors -->
<color name="primary">#FF6200EE</color>
<!-- AndroidManifest.xml - Add permissions -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<!-- AndroidManifest.xml - Export an activity (make it launchable) -->
<activity android:name=".HiddenActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- res/layout/activity_main.xml - Modify UI -->
<TextView
android:id="@+id/status_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PATCHED" />
3. Split APK Handling
3.1 Understanding Split APK Formats
Modern Play Store apps use Android App Bundles (AAB), which are split into multiple APKs:
| Format | Source | Extension | Contents |
|---|---|---|---|
| Bundletool | Google Play / developer tools | .apks |
ZIP with base + splits + toc.pb (protobuf metadata) |
| APKMirror | APKMirror downloads | .apkm |
Same as .apks, renamed |
| APKPure | APKPure downloads | .xapk |
Similar concept, slightly different metadata |
| Individual | Manual extraction | .apk each |
Each split as separate APK file |
Split types you'll encounter:
base.apk— Core app code, shared resources, manifestsplit_config.arm64_v8a.apk— ARM64 native librariessplit_config.armeabi_v7a.apk— ARMv7 native librariessplit_config.x86_64.apk— x86_64 native librariessplit_config.xxhdpi.apk— Screen density-specific resourcessplit_config.en.apk— Language-specific resourcessplit_config.google_apis.apk— Google APIs integration codesplit_config.firebase.apk— Firebase integration code
3.2 Extraction & Merging Workflow
# Step 1: Extract splits from bundle format
# .apks and .apkm are just ZIP files
mkdir splits/ && cd splits/
unzip ../input.apks # or .apkm
# This produces: base.apk, split_config.*.apk, toc.pb, etc.
# For .xapk, same approach:
unzip ../input.xapk
# Step 2: Merge into a single "fat APK" using APKEditor
java -jar APKEditor.jar m -i base.apk split_config.arm64_v8a.apk \
split_config.xxhdpi.apk split_config.en.apk -o merged.apk
# APKEditor intelligently merges resource tables from all splits.
# The resulting merged.apk is self-contained for decompilation.
# Step 3: Decompile the merged APK
apktool d merged.apk -o decompiled/ -f
# Step 4: Make your modifications (smali, resources, manifest)
# Step 5: Recompile
apktool b decompiled/ -o modified_base.apk
# Step 6: Align and sign (see Section 4)
zipalign -v -p 4 modified_base.apk modified_base_aligned.apk
apksigner sign --ks ~/.android/debug.keystore \
--ks-pass pass:android --key-pass pass:android \
modified_base_aligned.apk
3.3 Re-installing Modified Split APKs
Option A: Replace base APK only (recommended when possible)
If you only modified the base APK (most common scenario), you can keep the original split APKs:
# Uninstall the app first (signature mismatch will block update)
adb uninstall com.example.app
# Install base + original splits together
adb install-multiple modified_base_aligned.apk \
splits/split_config.arm64_v8a.apk \
splits/split_config.xxhdpi.apk \
splits/split_config.en.apk
Option B: Re-sign ALL APKs with the same key
If splits fail because of signature mismatch (some splits embed signature checks):
# Sign every APK with the same keystore
for apk in modified_base_aligned.apk splits/split_config.*.apk; do
apksigner sign --ks ~/.android/debug.keystore \
--ks-pass pass:android --key-pass pass:android \
"$apk"
done
# Install all at once
adb install-multiple modified_base_aligned.apk splits/split_config.*.apk
Option C: Create a new bundle for distribution
# Create .apks bundle using bundletool
java -jar bundletool.jar build-bundle \
--modules=modified_base_aligned.apk,splits/split_config.arm64_v8a_apk \
--output=patched.apks
3.4 Split APK Pitfalls
| Pitfall | Description | Fix |
|---|---|---|
| Missing native libs after merge | APKEditor may not merge all lib/ directories from arch splits |
Manually copy missing .so files into merged APK's lib/ before decompiling |
| Resource conflicts during merge | Two splits define same resource ID with different values | Merge only base + arch split first, then add density/language splits one at a time |
toc.pb metadata mismatch |
Modified APK doesn't match original bundle metadata | Don't use bundletool for installation — use adb install-multiple directly |
| Feature module splits | Some splits contain code (not just resources) | Decompile the feature split separately, understand its code, and merge smali directories |
Base APK decompilation produces null resources |
Known apktool issue with some split APK bases | Merge with APKEditor first, or use --ignore-missing-resources flag |
4. Signing: Schemes, Pitfalls, and Fix Procedures
4.1 APK Signature Schemes Explained
| Scheme | Since | Mechanism | Tamper Resistance |
|---|---|---|---|
| v1 (JAR signing) | API 1 (all Android) | Signs individual file entries inside META-INF/ |
Weak — can strip and re-sign without app knowing |
| v2 (APK Signature Scheme v2) | API 24 (Android 7.0) | Binary signature block before ZIP central directory; verifies entire APK | Strong — any byte modification (even metadata) invalidates it |
| v3 (APK Signature Scheme v3) | API 28 (Android 9.0) | Same as v2 + supports key rotation via proof-of-rotation lineage | Same integrity + allows changing signing keys |
| v4 (APK Signature Scheme v4) | API 30 (Android 11) | Merkle hash tree stored separately; enables streaming verification during incremental installs | Same integrity + incremental install support |
Critical rule: v2/v3 are whole-APK signatures. Modifying even a single byte of smali code, a single resource string, or even the ZIP comment field invalidates them. This is why every recompilation requires a full re-sign.
Android verification order: v4 → v3 → v2 → v1 (falls back through each level).
4.2 apksigner vs jarsigner — Why jarsigner Is Dead
| Feature | apksigner | jarsigner |
|---|---|---|
| Signature schemes | v1, v2, v3, v4 | v1 only |
| Whole-APK verification | Yes | No |
| Key rotation support | Yes (v3) | No |
| Required for API 24+ | Yes | No — will be rejected |
| Alignment awareness | Yes | No |
| Source | Android SDK Build Tools | JDK |
NEVER use jarsigner for APK signing. Period. It produces v1-only signatures that modern Android (7.0+) silently rejects or explicitly denies installation.
4.3 Proper Signing Procedure
# === Creating a Custom Keystore (one-time) ===
keytool -genkey -v -keystore ~/release.keystore \
-alias myapp \
-keyalg RSA \
-keysize 2048 \
-validity 10000
# You'll be prompted for:
# - Keystore password (use something STRONG and memorable)
# - Key password (same or different)
# - Your name, organization, city, state, country code
# Store the keystore and passwords securely — you CANNOT change the key later
# === Using the Android Debug Keystore (for testing only) ===
# Location: ~/.android/debug.keystore
# Password: android
# Alias: androiddebugkey
# This keystore is identical on ALL Android SDK installations.
# DO NOT use for production — apps signed with it cannot be published or updated.
# === Full Signing Workflow ===
# Step 1: Align the APK (MUST be before signing)
zipalign -v -p 4 rebuilt.apk rebuilt_aligned.apk
# Step 2: Sign with apksigner (all three schemes for maximum compatibility)
apksigner sign \
--ks ~/release.keystore \
--ks-key-alias myapp \
--ks-pass pass:your_keystore_password \
--key-pass pass:your_key_password \
--v1-signing-enabled true \
--v2-signing-enabled true \
--v3-signing-enabled true \
rebuilt_aligned.apk
# Note: v4 is NOT signed here — it's computed on-demand during installation
# Step 3: Verify the signature
apksigner verify --verbose --print-certs rebuilt_aligned.apk
# Expected output:
# Verifies
# Verified using v1 scheme (JAR signing): true
# Verified using v2 scheme (APK Signature Scheme v2): true
# Verified using v3 scheme (APK Signature Scheme v3): true
# Number of signers: 1
# Step 4: Optional — Check what schemes are present
apksigner verify --print-certs -v rebuilt_aligned.apk
4.4 Signature-Related Failures and Fixes
| Error Message | Root Cause | Fix |
|---|---|---|
INSTALL_FAILED_UPDATE_INCOMPATIBLE |
App already installed with different signing key | adb uninstall com.example.app then reinstall |
INSTALL_PARSE_FAILED_NO_CERTIFICATES |
APK not signed at all | Sign with apksigner |
INSTALL_FAILED_VERIFICATION_FAILURE |
Device requires verified installs (MIUI, Samsung Knox) | Disable MIUI optimization, or enable "Install via USB" in Developer Options |
| App installs but immediately crashes | Runtime signature self-check (PackageManager.getPackageInfo() with GET_SIGNATURES) |
See Section 9.3 (Frida signature bypass) or patch smali signature check |
SecurityException: Signature not valid |
App explicitly checks caller signature | Patch the smali signature check, or use signature spoofing (see 6.4) |
INSTALL_FAILED_INVALID_APK |
APK corrupted or malformed after recompilation | Check for AAPT2 errors during build, verify zip integrity |
| Play Store says "App not installed" | Custom signature not recognized by Play Store | Expected — Play Store only accepts Google-signed updates for apps it manages |
Splits fail with INSTALL_FAILED_SESSION_INVALID |
Splits signed with different keys | Sign ALL splits with the same keystore before install-multiple |
4.5 Key Rotation with v3 Signing
If you need to change signing keys (e.g., you lost your keystore):
# Sign with the NEW key, providing the OLD key's lineage
apksigner sign \
--ks new-release.keystore \
--ks-key-alias newkey \
--ks-pass pass:new_password \
--v1-signing-enabled true \
--v2-signing-enabled true \
--v3-signing-enabled true \
--lineage /path/to/lineage-file \
rebuilt_aligned.apk
# To extract lineage from the OLD keystore:
apksigner sign \
--ks old-release.keystore \
--ks-key-alias oldkey \
--ks-pass pass:old_password \
--v3-signing-enabled true \
--lineage /path/to/lineage-file \
--next-signer new-release.keystore newkey pass:new_password \
dummy.apk
Caveat: Key rotation only works if the app was PREVIOUSLY signed with v3. If the original app only has v1/v2, you're out of luck — you must uninstall and reinstall.
5. Dependency Hell: Frameworks, AndroidX, OEM Resources
5.1 Common Resource Dependency Errors
# Error 1: Missing AndroidX resources
error: resource 'com.example:id/material_textinput' not found
# Error 2: Styleable attribute not found
error: style attribute 'attr/layout_constraintBaseline_toBaselineOf' not found
# Error 3: AAPT2 crash
ERROR: AAPT2 aapt2(...) exited with code 1
brut.androlib.AndrolibException
# Error 4: Duplicate resource
error: resource 'style/Theme.MaterialComponents' has already been defined
# Error 5: Missing framework resource
error: resource '@android:configui_configFlags' not found
5.2 Systematic Dependency Resolution
Step 1: Install ROM-specific frameworks
For most third-party apps, Apktool's bundled framework (since 2.5.0) is sufficient. But for system apps, OEM apps, or apps that reference framework resources not in AOSP, you need:
# Install the standard Android framework (usually automatic)
apktool if framework-res.apk
# Pull framework files from a rooted device
adb pull /system/framework/ ./device_frameworks/
# Install OEM-specific frameworks
# Samsung:
apktool if device_frameworks/sec_platform_library.jar
apktool if device_frameworks/com.samsung.device.jar
apktool if device_frameworks/frameworksSamsungSDK.jar
# Xiaomi/MIUI:
apktool if device_frameworks/framework-miui-res.apk
apktool if device_frameworks/MiuiSdk.jar
apktool if device_frameworks/framework-mi.jar
# Huawei:
apktool if device_frameworks/HWExtension.jar
apktool if device_frameworks/hwsdk.jar
# OnePlus/Oplus:
apktool if device_frameworks/oplus-framework.jar
apktool if device_frameworks/oplus-res.apk
# Check what frameworks are currently installed
ls ~/.local/share/apktool/framework/
Step 2: Install shared library APKs
Some apps depend on vendor-shared library APKs:
# Find shared library APKs on device
adb shell pm list packages -f | grep shared
adb shell pm list packages -f | grep library
# Pull and install as framework
adb pull /system/app/SomeSharedLib/SomeSharedLib.apk
apktool if SomeSharedLib.apk
Step 3: Force API level to resolve resource conflicts
# If resources from newer APIs aren't found
apktool d target.apk -o out/ -f --api 35
# If dealing with legacy frameworks
apktool d target.apk -o out/ -f --api 29
Step 4: Use the ignore flag for known non-critical missing resources
# New in Apktool 2.11.x
apktool d target.apk -o out/ -f --ignore-missing-resources
5.3 AndroidX-Specific Issues
Issue A: Cross-library resource references
AndroidX migrated all android.support.* classes to androidx.* namespaces. This causes two major categories of issues:
- Cross-library resource references: AndroidX libraries reference resources across package boundaries. When decompiled, these may resolve incorrectly.
- R8-obfuscated resource names: When R8 shrinks an app, it may remove unused AndroidX resources. On recompilation, the remaining code may reference resources that were stripped.
Fix: Ensure Apktool 2.11.0+ is used (correctly handles AndroidX cross-references). For R8-obfuscated apps, manually add the required library .aar files as frameworks.
Issue B: Obfuscation
R8/ProGuard/DexGuard can strip resources. This is extremely hard to resolve automatically—your best options are:
- Decompile the original APK without modification and immediately recompile (if that fails too, it's a tool limitation)
- Manually add the missing resource definition to
res/values/files - Use
--ignore-missing-resourcesand accept that some features may break
5.4 AAPT2 Error Catalog
| Error | Cause | Fix |
|---|---|---|
resource has already been defined |
Duplicate resource entry in AAPT2 | Apktool 2.11.0+ handles most cases. If persists, search for duplicate definitions in res/values/*.xml |
failed to compile values files |
Style/attr references to undefined resources | Check attrs.xml, styles.xml, themes.xml for references to removed resources |
malformed compiled jar |
Samsung DEX format 039 in framework | apktool d -api 29 |
no resource identifier found for attribute |
Missing library dependency | Install the required library APK as framework |
invalid file path |
Windows path length limit or special characters in resources | Move project to short path, rename files |
unmarshalling resource table |
Corrupted resources.arsc in original APK |
Try --ignore-missing-resources, or use APKEditor to decompile instead |
Error: java.nio.BufferOverflowException |
Very large resource table (>2GB) | Use 64-bit Java and increase heap: JAVA_OPTS="-Xmx4g" apktool b ... |
6. GApps-Dependent APKs: Patching & Workarounds
6.1 The GApps Dependency Problem
Apps that depend on Google Play Services are the hardest to patch because:
- Split APK hell: Play Services is distributed as ~100+ dynamic feature modules via split APKs
- Runtime signature verification: Both the OS and other GApps verify Play Services' signature at runtime
- DroidGuard attestation: Google's attestation service is deeply integrated into Play Services core
- Automatic updates: The Play Store auto-updates Play Services, overwriting any patches
- Play Integrity API: Modern apps use Play Integrity to verify device and app integrity — impossible to fully bypass on emulators
Bottom line: Do NOT try to directly patch com.google.android.gms. It's a losing battle. Instead, use the approaches in the following sections.
6.2 Strategy 1: MicroG (Recommended for GApps-Free Patching)
MicroG is a lightweight, open-source replacement for Google Play Services.
# MicroG provides:
# - Location services (via UnifiedNlp / OpenStreetMap backends)
# - Push notifications (via ntfy or custom push servers)
# - Device registration
# - Maps API (via OpenStreetMap)
# - Limited Google account support
# MicroG does NOT provide:
# - Play Integrity passing
# - Full DroidGuard attestation
# - SafetyNet compatibility
# - All Google API parity
# - Some Play Games features
# Setup MicroG:
# 1. Download GmsCore.apk from https://github.com/microg/GmsCore/releases
# Or use "MicroG RE" (Redesign for ReVanced) for better compatibility
# 2. Install on your device/emulator
# 3. Install a push notification provider (e.g., ntfy)
# 4. Configure signature spoofing (see 6.4)
6.3 Strategy 2: Patching Individual GApps (YouTube, Maps, etc.)
ReVanced (the successor to Vanced) provides pre-made patches:
- https://github.com/ReVanced/revanced-patches
- Patches for: YouTube, YouTube Music, Google Photos, etc.
- Handles signature bypass, ad removal, background play, etc.
Manual patching of GApps:
The key challenge: GApps perform runtime signature verification. They check their own signature against a known Google certificate fingerprint and refuse to function if the signature doesn't match.
# The patching approach:
# 1. Decompile the app
apktool d google_app.apk -o decompiled/
# 2. Find the signature verification code
# Search smali for these patterns:
# getPackageInfo
# GET_SIGNATURES
# PackageManager
# MessageDigest
# equals (for signature comparison)
# toByteArray (for signature byte extraction)
# 3. Patch or NOP the signature check
# Common pattern in smali:
# const-string v0, "308203e3..."
# invoke-virtual {v0}, Ljava/lang/String;->getBytes()[B
# invoke-static {...}, Ljava/security/MessageDigest;->getInstance(...)L...
# # Replace with: const/4 v_result, 0x1 (return true/valid)
6.4 Strategy 3: Signature Spoofing
What it does: Makes MicroG's signature appear as the real Google Play Services signature to other apps.
ROM-level support (cleanest approach):
- LineageOS: Settings → System → Developer options → "Allow signature spoofing"
- AXP.OS, /e/OS, CalyxOS: Built-in with per-app control
Magisk module approach (for stock ROMs):
- Patches
framework.jarat runtime to allow apps to declare spoofing support - MicroG declares spoofing support in its manifest
- Other apps query MicroG's signature and receive Google's signature bytes
# Install signature spoofing Magisk module:
# Search for "FakeGApps" or "sigspoof" in Magisk Modules repository
# Or use: https://github.com/microg/android_packages_apps_GmsCore/wiki/Installation
6.5 Strategy 4: Play Integrity Bypass (2025 State)
| Tool | Type | Works On | Status |
|---|---|---|---|
| TrickyStore | Magisk module | Android 8+ (with Zygisk) | Active — spoofs device attestation key |
| Play Integrity Fix NEXT | Magisk module | Android 8+ (with Zygisk) | Active — aims for valid attestation |
| PIFork | Magisk module | Pre-Android 13 | Active fork by osm0sis |
Stack for passing Play Integrity (2025):
- Root via Magisk / KernelSU / APatch
- Install ZygiskNext
- Install Play Integrity Fix (or NEXT/fork)
- Install TrickyStore + TrickyStore Addon
- Do NOT put
com.google.android.gmson Magisk DenyList
CRITICAL LIMITATION: Play Integrity does NOT pass on any emulator. Emulators are detected via hardware attestation checks that cannot be spoofed in software. For Play Integrity testing, you MUST use a physical device.
7. Testing Without a Physical Device
7.1 Emulator Selection Matrix
| Emulator | Root Support | ARM Translation | GApps Support | Best For | Verdict |
|---|---|---|---|---|---|
| Android Studio AVD | Magisk via rootAVD | Native (ARM images) / Translated (x86) | Google APIs / Google Play images | Dev, security testing, patch verification | Best choice |
| Genymotion | Built-in basic root | libhoudini (not included by default since 3.x) | Requires manual GApps flash | Professional CI/CD testing | Good for automated testing |
| Waydroid | Magisk via script | libndk_translation (SSE 4.2 required) | Manual via waydroid_script | Linux desktop Android apps | Good for Linux users |
| BlueStacks | Difficult, no Magisk | Built-in | Pre-installed | Gaming only | Not recommended for dev work |
| Nox | Clunky, unreliable | Built-in | Partial | Gaming/automation | Not recommended for dev work |
7.2 Setting Up Android Studio AVD for APK Testing
# Step 1: Install Android Studio
# Download from https://developer.android.com/studio
# Step 2: Create an AVD (Virtual Device)
# Android Studio → Tools → Device Manager → Create Device
# Recommended configuration:
# - Device: Pixel 7 (or any recent phone profile)
# - System Image: API 35 (Android 15), x86_64 (for speed)
# OR ARM64 if testing ARM-specific native libs
# - RAM: 4096 MB minimum (8192 MB preferred)
# - AVD Name: "test_device_api35"
# Step 3: Root the AVD with rootAVD
pip3 install rootAVD
rootAVD -s test_device_api35 # Root by AVD name
# Step 4: Verify root
emulator -avd test_device_api35 -no-snapshot-load
adb shell su -c "id"
# Expected: uid=0(root) gid=0(root)
# Step 5: Install Magisk modules (if needed)
# Push module zip to device and flash via Magisk app
adb push module.zip /sdcard/Download/
# Then flash in Magisk Manager app on the emulator
# Step 6: Install your patched APK
adb install -r patched.apk
# For split APKs:
adb install-multiple base.apk split_config.arm64_v8a.apk split_config.en.apk
# Force downgrade if version conflict:
adb install -r -d -t patched.apk
# Clean install if signature mismatch:
adb uninstall com.example.app
adb install patched.apk
7.3 GApps Testing on Emulator
Option A: AVD with Google APIs image
# Create AVD with "Google APIs" system image
# Android Studio → Device Manager → Create Device
# Choose system image: "Google APIs" (not "Google Play")
# This includes Play Services but allows more flexibility
# Caveat: Pre-installed Google Play Services CANNOT be replaced
# with patched versions because the system expects Google's signature
Option B: AVD without Google APIs + MicroG (recommended for GApps-free testing)
# Create AVD with "No Google APIs" / AOSP system image
# This gives you a clean slate
# Install MicroG:
adb install GmsCore.apk
adb install ntfy.apk # Push notification provider
# Install your patched app:
adb install patched.apk
# Verify MicroG self-check:
# Open MicroG app → Self-check → All should be green (except Play Integrity)
7.4 x86 vs ARM Translation Issues
Android Studio AVD:
- x86_64 images: ~10x faster than ARM emulation. Uses native x86 execution.
- ARM64 images: Slower but runs ARM native libraries directly.
- ARM translation: When running x86_64 image with ARM APKs, the emulator translates ARM instructions to x86 transparently.
- Problem: Some native libraries (anti-tamper, anti-debug, DRM) detect the translation layer and refuse to run.
- Fix: Use ARM64 system images when testing apps with ARM-specific native code that performs translation detection.
Genymotion:
- Runs x86 images by default
- libhoudini for ARM translation (not included since Genymotion 3.x)
- Install via: https://github.com/vnsh01/Nyx (for Android 11)
- macOS Apple Silicon devices don't need translation (native ARM64)
Waydroid:
- Uses host Linux kernel directly (container, not emulation)
- ARM translation via libndk_translation
- Requires SSE 4.2 CPU instructions
- Install via waydroid_script: https://github.com/casualsnek/waydroid_script
7.5 Frida Setup on Emulator
# Install Frida tools
pip install frida-tools
# Download frida-server for the emulator's architecture
# Check architecture: adb shell getprop ro.product.cpu.abi
# For x86_64 emulator: download frida-server-x86_64.xz
# For arm64 emulator: download frida-server-arm64.xz
# Push and run frida-server
adb push frida-server /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "/data/local/tmp/frida-server -D &"
# Verify connection
frida-ps -U # Should list running processes
# Run Frida REPL against a running app
frida -U -n com.example.app
# Spawn app with Frida injected
frida -U -f com.example.app -l hook.js --no-pause
# Run a JavaScript hook script
frida -U -l bypass_signature.js -n com.example.app
7.6 ADB Commands Reference for Testing
# === Installation ===
adb install -r app.apk # Replace existing app
adb install -d app.apk # Allow version downgrade
adb install -t app.apk # Allow test APKs
adb install-multiple base.apk s1.apk s2.apk # Split APKs
adb uninstall com.example.app # Remove app + data
# === Debugging ===
adb logcat # View logs
adb logcat -s "ActivityManager" # Filter by tag
adb logcat | grep -i "FATAL" # Filter crash logs
adb shell dumpsys package com.example.app # Package info
# === File operations ===
adb push file.txt /sdcard/ # Upload file
adb pull /sdcard/file.txt ./ # Download file
# === Root operations ===
adb shell su -c "command" # Run as root
adb shell su -c "mount -o rw,remount /system"
adb push patched.apk /system/app/TargetApp/target.apk
adb shell su -c "chmod 644 /system/app/TargetApp/target.apk"
# === Emulator-specific ===
emulator -avd NAME -no-snapshot-load # Start emulator fresh
adb reboot # Reboot emulator
7.7 ADB Install Flags Reference
| Flag | Meaning | Use Case |
|---|---|---|
-r |
Replace/reinstall existing app | Updating a patched APK |
-d |
Allow version downgrade | Installing older patched version |
-t |
Allow test APKs (android:testOnly="true") |
Debug/test builds |
-g |
Grant all runtime permissions | Testing with all permissions |
--force-install |
Force install (Android 13+) | Bypass signature/version checks |
-p |
Partial install (split APK mode) | Internal use for install-multiple |
8. Local Testing: Redroid & Waydroid
8.1 Signing for Local Testing: Everything You Need to Know
8.1.1 The Debug Keystore (Quick & Dirty)
Location: ~/.android/debug.keystore
Password: android
Alias: androiddebugkey
Key pass: android
When to use: Local-only testing on redroid, Waydroid, your own devices, emulators.
When NOT to use: Distributing APKs to others, publishing to Play Store.
8.1.2 Custom Keystore for Reproducible Signing
# One-time creation
keytool -genkey -v \
-keystore ~/apk-testing.keystore \
-alias testing \
-keyalg RSA \
-keysize 2048 \
-validity 10000
# You'll be prompted for:
# - Keystore password (use something STRONG and memorable)
# - Key password (same or different)
# - Your name, organization, city, state, country code
#
# BACKUP THIS FILE. If you lose it, you'll have to uninstall and reinstall every app you ever signed.
8.1.3 The Critical Order: zipalign THEN sign
# CORRECT ORDER:
zipalign -v -p 4 rebuilt.apk rebuilt_aligned.apk # Step 1: ALIGN first
apksigner sign --ks key.keystore aligned.apk # Step 2: SIGN second
# WRONG ORDER (will break v2/v3 signatures):
apksigner sign --ks key.keystore rebuilt.apk # Signing first
zipalign -v -p 4 rebuilt.apk aligned.apk # Then aligning BREAKS the signature!
Why: v2/v3 signatures are computed over the entire APK binary, including the position of entries within the ZIP. zipalign moves entries around to 4-byte boundaries, which invalidates any signature computed before alignment.
8.1.4 Signing Split APKs with the Same Key
When testing split APKs on redroid/Waydroid, ALL splits in a bundle must share the same signing key:
# Extract splits from .apks/.apkm
unzip input.apks -d splits/
# Align all APKs
for apk in splits/*.apk; do
zipalign -v -p 4 "$apk" "${apk%.apk}_aligned.apk"
done
# Sign ALL splits with THE SAME key
for apk in splits/*_aligned.apk; do
apksigner sign \
--ks ~/apk-testing.keystore \
--ks-key-alias testing \
--ks-pass pass:your_password \
--key-pass pass:your_password \
"$apk"
done
# Install all at once via ADB
adb install-multiple splits/base_aligned.apk \
splits/split_config.arm64_v8a_aligned.apk \
splits/split_config.en_aligned.apk
8.1.5 uber-apk-signer: All-in-One Alternative
If you want to skip the manual zipalign + apksigner dance:
# Download from https://github.com/patrickfav/uber-apk-signer/releases
java -jar uber-apk-signer.jar --apks rebuilt.apk
# Handles: zipalign + sign with debug keystore + verify
# Output: rebuilt-aligned-debugSigned.apk
# With custom keystore
java -jar uber-apk-signer.jar \
--apks rebuilt.apk \
--ks ~/apk-testing.keystore \
--ksAlias testing \
--ksPass your_password
# Sign multiple APKs at once (splits)
java -jar uber-apk-signer.jar --apks splits/
8.1.6 Common Signing Mistakes & Symptoms
| Mistake | What Happens | Fix |
|---|---|---|
| zipalign AFTER sign | INSTALL_FAILED_VERIFICATION |
Always zipalign first |
| Used jarsigner instead of apksigner | v2/v3 signatures missing; Android 7.0+ rejects | Use apksigner |
| Split APKs signed with different keys | INSTALL_FAILED_SESSION_INVALID |
Sign ALL with same keystore |
| Modified APK after signing | Signature broken | Never modify post-sign |
| Wrong keystore password | Keystore was tampered with, or password was incorrect |
Verify with keytool -list |
| Not aligned at all | App installs but runs slow; INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION on some ROMs |
Always run zipalign -v -p 4 |
| Missing v2 scheme on API 24+ | Silent rejection or INSTALL_PARSE_FAILED_NO_CERTIFICATES |
apksigner defaults to v1+v2; verify with apksigner verify -v |
Best Practices
- Always backup the original APK: Keep an unmodified copy before any patching—recovery is impossible without it.
- Test on multiple Android versions: Verify compatibility across API levels (e.g., 29, 33, 35) before distribution.
- Use proper signing keys (don't lose them): Store keystore files securely with strong passwords; losing your key means you can never update the app.
- Keep the original package name unless deliberately changing it: Changing package name creates a new app identity, preventing updates.
- Verify split APK alignment: Use
zipalign -v -p 4on all splits before signing; misalignment causes runtime crashes. - Test GApps-dependent APKs in an emulator first: Use Android Studio AVD or redroid to catch GApps integration issues before physical device testing.
- Document all modifications made: Keep a changelog of smali/resource edits for debugging and future patches.
8.2 Redroid: A
…(truncated)