← Back to posts

Bypassing Android Forced Update Dialogs with Frida

4mr
Amr En-Niari
@4mrr Β· 6 min read
Table of Contents
πŸ“‹ Scenario

During a mobile penetration testing engagement, the target Android application displays a forced update dialog on startup: "Une nouvelle version est disponible sur Play Store" with a single button "METTRE Γ€ JOUR" that redirects to the Play Store. The dialog is not dismissable β€” there is no close button and tapping outside does nothing. This blocks all access to the application's functionality.

This technique is for authorized penetration testing only. Always ensure you have written authorization before testing any application.

The Problem

Many mobile applications implement a forced update mechanism that compares the installed app version against the latest version available on the Play Store (or a backend API). If the installed version is outdated, the app shows a modal dialog that cannot be dismissed, effectively preventing the user β€” or in our case, the pentester β€” from accessing the application.

During a mobile pentest, we often work with an older or patched version of the APK (e.g., after SSL pinning bypass with apk-mitm). The patched APK will almost always trigger this check since it no longer matches the Play Store version.

How Forced Updates Work

Typically, the app performs one of these checks on startup:

  1. Play Store API check β€” queries Google Play for the latest version number
  2. Backend API check β€” calls a custom endpoint like /api/version/check
  3. Firebase Remote Config β€” reads a minimum version from Firebase
  4. In-App Updates API β€” uses Google's official AppUpdateManager

If the installed version is lower than the required version, the app creates an AlertDialog (or a custom dialog) with setCancelable(false), making it impossible to dismiss.

Method 1: Hooking Dialog.show() β€” The Quick Win

The fastest approach is to simply prevent any dialog from appearing by hooking the Dialog.show() method in Android's framework.

1
Create the Frida script

Save this as bypass-update.js:

Java.perform(function() {
    // Hook AlertDialog.show() to prevent it from displaying
    var AlertDialog = Java.use('android.app.AlertDialog');
    var Dialog = Java.use('android.app.Dialog');

    Dialog.show.implementation = function() {
        console.log("[*] Dialog blocked β€” forced update bypassed");
        // Simply don't call the original show()
    };

    console.log("[+] Update dialog bypass loaded");
});
2
Run with Frida
frida -U -f com.target.app -l bypass-update.js
3
Verify the bypass

In the Frida console, you should see:

[+] Update dialog bypass loaded
[*] Dialog blocked β€” forced update bypassed
[*] Dialog blocked β€” forced update bypassed

The app should now load normally without showing the update dialog.

This method works for most apps that use standard Android AlertDialog. It's quick, clean, and non-destructive.
If the app uses a custom dialog library (e.g., Material Dialogs, a custom View), this hook may not catch it. In that case, use Method 2 or 3 below.

Method 2: Blocking the Version Check Request

Instead of blocking the dialog, we can block the network request that fetches the latest version. Without a response, the app can't determine if an update is needed.

Java.perform(function() {
    var URL = Java.use('java.net.URL');

    URL.openConnection.overload().implementation = function() {
        var url = this.toString();

        if (url.includes("update") ||
            url.includes("version") ||
            url.includes("force") ||
            url.includes("play.google")) {
            console.log("[*] Blocked version check: " + url);
            throw Java.use('java.io.IOException').$new("blocked");
        }

        return this.openConnection();
    };

    console.log("[+] Version check network block loaded");
});

Method 3: Spoofing the App Version

Another approach is to fake the installed version so the app thinks it's already up to date. We hook PackageInfo to return a very high version number.

Java.perform(function() {
    var PackageManager = Java.use('android.app.ApplicationPackageManager');

    PackageManager.getPackageInfo.overload(
        'java.lang.String', 'int'
    ).implementation = function(pkg, flags) {
        var info = this.getPackageInfo(pkg, flags);

        // Spoof to very high version
        info.versionName.value = "99.99.99";
        info.versionCode.value = 999999;

        console.log("[*] Spoofed version for: " + pkg +
                    " β†’ 99.99.99 (code: 999999)");
        return info;
    };

    console.log("[+] Version spoof loaded");
});

Method 4: APK Patching (Permanent Fix)

If you need a permanent bypass that doesn't require Frida running, you can patch the APK directly by finding and modifying the update check logic in the smali code.

Step 1: Find the update check

# Decompile the APK
apktool d target.apk -o source

# Search for update-related strings
grep -rn "nouvelle version\|METTRE.*JOUR\|forceUpdate\|versionCheck\|updateRequired" \
  source/smali/ source/res/

Step 2: Identify the method

Look for the class that creates the dialog. Common patterns in smali:

# Look for setCancelable(false) near version check
invoke-virtual {v0, v1}, Landroid/app/AlertDialog$Builder;->setCancelable(Z)

# Or look for the update string resource
const-string v1, "Une nouvelle version"

Step 3: Patch the condition

# Change the comparison from "if not equal" to "if equal"
# Before (shows dialog when version is old):
if-ne v0, v1, :show_update_dialog

# After (never shows dialog):
if-eq v0, v1, :show_update_dialog

# Or simply make the method return immediately:
.method public checkForUpdate()V
    .locals 0
    return-void
.end method

Step 4: Rebuild and sign

apktool b source -o patched.apk

keytool -genkey -v -keystore test.keystore -alias test \
  -keyalg RSA -keysize 2048 -validity 10000 \
  -storepass password -keypass password -dname "CN=test"

apksigner sign --ks test.keystore --ks-pass pass:password patched.apk

adb install patched.apk

Method 5: Network-Level Block

The simplest approach β€” just block the device from reaching Play Store APIs entirely:

# Block Play Store version check endpoints
adb shell iptables -A OUTPUT -d play.googleapis.com -j DROP
adb shell iptables -A OUTPUT -d android.clients.google.com -j DROP

# To restore later:
adb shell iptables -D OUTPUT -d play.googleapis.com -j DROP
adb shell iptables -D OUTPUT -d android.clients.google.com -j DROP
This only works if the app checks the Play Store directly. If it uses a custom backend API for version checking, you'll need to identify and block that specific endpoint instead, or use Burp Suite to intercept and modify the response.

Results

Before β€” Forced update dialog blocking access:


Forced update dialog

After β€” Dialog blocked, full access to app:


Frida bypass result

Using Method 1 (Dialog hook), Frida successfully intercepted two Dialog.show() calls and prevented them from displaying. The application loaded normally, showing the login screen with "AccΓ©der Γ  mes comptes" β€” full access restored.

Key Takeaways

When encountering forced update dialogs during mobile pentests, try the methods in this order:

  1. Dialog hook (Method 1) β€” fastest, works in most cases
  2. Version spoof (Method 3) β€” if the app checks version internally
  3. Network block (Method 5) β€” if the app checks Play Store
  4. Request block (Method 2) β€” if you can identify the check endpoint
  5. APK patch (Method 4) β€” permanent fix, but more effort
From a security perspective, forced update mechanisms are a good practice β€” they ensure users run patched versions. However, the implementation should not rely solely on client-side checks, as demonstrated here. Server-side version enforcement on API calls is the more robust approach.
You can combine this bypass with SSL pinning bypass for a complete mobile pentest setup. Check out my SSL Pinning Bypass Guide for the full workflow.
← All posts Next: SSL Pinning Bypass β†’