X

by
4 views af9bce68...

Description

X

How to Use

Download the script and run it with Frida CLI:

Download Script

Then run with Frida:

frida -U -f YOUR_PACKAGE_NAME -l x.js

Replace YOUR_PACKAGE_NAME with the target app's package name.

Source Code

JavaScript
/*
Original author: Daniele Linguaglossa
28/07/2021 - Edited by Simone Quatrini
Code amended to correctly run on the latest frida version
Added controls to exclude Magisk Manager

Modified by Cube Inc
Date: December 04, 2024
Added hooks to prevent emulator detection
*/

Java.perform(function() {
    var RootPackages = ["com.noshufou.android.su", "com.noshufou.android.su.elite", "eu.chainfire.supersu",
        "com.koushikdutta.superuser", "com.thirdparty.superuser", "com.yellowes.su", "com.koushikdutta.rommanager",
        "com.koushikdutta.rommanager.license", "com.dimonvideo.luckypatcher", "com.chelpus.lackypatch",
        "com.ramdroid.appquarantine", "com.ramdroid.appquarantinepro", "com.devadvance.rootcloak", "com.devadvance.rootcloakplus",
        "de.robv.android.xposed.installer", "com.saurik.substrate", "com.zachspong.temprootremovejb", "com.amphoras.hidemyroot",
        "com.amphoras.hidemyrootadfree", "com.formyhm.hiderootPremium", "com.formyhm.hideroot", "me.phh.superuser",
        "eu.chainfire.supersu.pro", "com.kingouser.com", "com.topjohnwu.magisk"
    ];

    var RootBinaries = ["su", "busybox", "supersu", "Superuser.apk", "KingoUser.apk", "SuperSu.apk", "magisk", "frida-server", "frida-agent"];

    var EmulatorFiles = [
        "/dev/qemu_pipe",
        "/dev/socket/qemud",
        "/system/lib/libc_malloc_debug_qemu.so",
        "/sys/qemu_trace",
        "/system/bin/qemu-props"
    ];

    var CustomProperties = {
        // Root detection properties
        "ro.build.selinux": "1",
        "ro.debuggable": "0",
        "service.adb.root": "0",
        "ro.secure": "1",
        // Emulator detection properties
        "ro.kernel.qemu": "0",
        "ro.build.fingerprint": "samsung/starltexx/starlte:10/QP1A.190711.020/G960FXXSDFUG5:user/release-keys",
        "ro.product.brand": "samsung",
        "ro.product.manufacturer": "samsung",
        "ro.product.model": "SM-G960F",
        "ro.product.device": "starlte",
        "ro.hardware": "samsungexynos9810",
        "ro.product.name": "starltexx",
        "ro.serialno": "R28M30XXXX",
        "ro.build.tags": "release-keys",
        "ro.build.type": "user"
    };

    var CustomPropertiesKeys = [];

    for (var k in CustomProperties) CustomPropertiesKeys.push(k);

    var PackageManager = Java.use("android.app.ApplicationPackageManager");

    var Runtime = Java.use('java.lang.Runtime');

    var NativeFile = Java.use('java.io.File');

    var String = Java.use('java.lang.String');

    var SystemProperties = Java.use('android.os.SystemProperties');

    var BufferedReader = Java.use('java.io.BufferedReader');

    var ProcessBuilder = Java.use('java.lang.ProcessBuilder');

    var TelephonyManager = Java.use('android.telephony.TelephonyManager');

    var Secure = Java.use('android.provider.Settings$Secure');

    var Build = Java.use('android.os.Build');

    // Modify Build properties to simulate a real device
    Build.DEVICE.value = "starlte";
    Build.MANUFACTURER.value = "samsung";
    Build.BRAND.value = "samsung";
    Build.MODEL.value = "SM-G960F"; // Galaxy S9
    Build.HARDWARE.value = "samsungexynos9810";
    Build.PRODUCT.value = "starltexx";
    Build.FINGERPRINT.value = "samsung/starltexx/starlte:10/QP1A.190711.020/G960FXXSDFUG5:user/release-keys";
    Build.TAGS.value = "release-keys";
    Build.SERIAL.value = "R28M30XXXX";
    Build.SUPPORTED_ABIS.value = ["armeabi-v7a", "armeabi"];
    Build.CPU_ABI.value = "armeabi-v7a";
    Build.CPU_ABI2.value = "armeabi";

    // Hook TelephonyManager methods to return valid values
    TelephonyManager.getDeviceId.overload().implementation = function() {
        send("Bypass getDeviceId");
        return "359872070XXXXXX";
    };

    if (TelephonyManager.getImei) {
        TelephonyManager.getImei.overload().implementation = function() {
            send("Bypass getImei");
            return "359872070XXXXXX";
        };
    }

    TelephonyManager.getSubscriberId.overload().implementation = function() {
        send("Bypass getSubscriberId");
        return "310260000000000";
    };

    TelephonyManager.getNetworkOperatorName.overload().implementation = function() {
        send("Bypass getNetworkOperatorName");
        return "T-Mobile";
    };

    TelephonyManager.getSimOperatorName.overload().implementation = function() {
        send("Bypass getSimOperatorName");
        return "T-Mobile";
    };

    TelephonyManager.getPhoneType.overload().implementation = function() {
        send("Bypass getPhoneType");
        return this.PHONE_TYPE_GSM.value;
    };

    TelephonyManager.getNetworkCountryIso.overload().implementation = function() {
        send("Bypass getNetworkCountryIso");
        return "us";
    };

    TelephonyManager.getSimCountryIso.overload().implementation = function() {
        send("Bypass getSimCountryIso");
        return "us";
    };

    // Hook Settings.Secure.getString to return a valid Android ID
    Secure.getString.overload('android.content.ContentResolver', 'java.lang.String').implementation = function(contentResolver, name) {
        if (name == Secure.ANDROID_ID.value) {
            send("Bypass getString ANDROID_ID");
            return "9774d56d682e549c";
        }
        return this.getString(contentResolver, name);
    };

    PackageManager.getPackageInfo.overload('java.lang.String', 'int').implementation = function(pname, flags) {
        var shouldFakePackage = (RootPackages.indexOf(pname) > -1);
        if (shouldFakePackage) {
            send("Bypass root check for package: " + pname);
            pname = "set.package.name.to.a.fake.one.so.we.can.bypass.it";
        }
        return this.getPackageInfo.overload('java.lang.String', 'int').call(this, pname, flags);
    };

    NativeFile.exists.implementation = function() {
        var path = NativeFile.getAbsolutePath.call(this);
        var name = NativeFile.getName.call(this);
        var shouldFakeReturn = (RootBinaries.indexOf(name) > -1 || EmulatorFiles.indexOf(path) > -1);
        if (shouldFakeReturn) {
            send("Bypass return value for file: " + path);
            return false;
        } else {
            return this.exists.call(this);
        }
    };

    var exec = Runtime.exec.overload('[Ljava.lang.String;');
    var exec1 = Runtime.exec.overload('java.lang.String');
    var exec2 = Runtime.exec.overload('java.lang.String', '[Ljava.lang.String;');
    var exec3 = Runtime.exec.overload('[Ljava.lang.String;', '[Ljava.lang.String;');
    var exec4 = Runtime.exec.overload('[Ljava.lang.String;', '[Ljava.lang.String;', 'java.io.File');
    var exec5 = Runtime.exec.overload('java.lang.String', '[Ljava.lang.String;', 'java.io.File');

    var suspiciousCommands = ["getprop", "mount", "build.prop", "id", "sh", "cat /proc/cpuinfo", "ifconfig", "ip addr"];

    function shouldBypassCommand(cmd) {
        for (var i = 0; i < suspiciousCommands.length; i++) {
            if (cmd.indexOf(suspiciousCommands[i]) != -1) {
                return true;
            }
        }
        return false;
    }

    exec1.implementation = function(cmd) {
        if (shouldBypassCommand(cmd)) {
            var fakeCmd = "grep";
            send("Bypass " + cmd + " command");
            return exec1.call(this, fakeCmd);
        }
        if (cmd == "su") {
            var fakeCmd = "invalid_command";
            send("Bypass " + cmd + " command");
            return exec1.call(this, fakeCmd);
        }
        return exec1.call(this, cmd);
    };

    exec.overloads.forEach(function(overload) {
        overload.implementation = function() {
            var cmd = arguments[0];
            if (cmd instanceof Array) {
                for (var i = 0; i < cmd.length; i++) {
                    if (shouldBypassCommand(cmd[i])) {
                        var fakeCmd = ["grep"];
                        send("Bypass " + cmd.join(" ") + " command");
                        return exec.call(this, fakeCmd);
                    }
                    if (cmd[i] == "su") {
                        var fakeCmd = ["invalid_command"];
                        send("Bypass " + cmd.join(" ") + " command");
                        return exec.call(this, fakeCmd);
                    }
                }
            } else if (typeof cmd === 'string') {
                if (shouldBypassCommand(cmd)) {
                    var fakeCmd = "grep";
                    send("Bypass " + cmd + " command");
                    return exec.call(this, fakeCmd);
                }
                if (cmd == "su") {
                    var fakeCmd = "invalid_command";
                    send("Bypass " + cmd + " command");
                    return exec.call(this, fakeCmd);
                }
            }
            return exec.apply(this, arguments);
        };
    });

    String.contains.implementation = function(name) {
        if (name == "test-keys") {
            send("Bypass test-keys check");
            return false;
        }
        return this.contains.call(this, name);
    };

    var get = SystemProperties.get.overload('java.lang.String');

    get.implementation = function(name) {
        if (CustomPropertiesKeys.indexOf(name) != -1) {
            send("Bypass " + name);
            return CustomProperties[name];
        }
        return this.get.call(this, name);
    };

    Interceptor.attach(Module.findExportByName("libc.so", "fopen"), {
        onEnter: function(args) {
            var path = Memory.readCString(args[0]);
            var shouldFakeReturn = (RootBinaries.indexOf(path) > -1 || EmulatorFiles.indexOf(path) > -1);
            if (shouldFakeReturn) {
                Memory.writeUtf8String(args[0], "/notexists");
                send("Bypass native fopen for " + path);
            }
        },
        onLeave: function(retval) {

        }
    });

    Interceptor.attach(Module.findExportByName("libc.so", "system"), {
        onEnter: function(args) {
            var cmd = Memory.readCString(args[0]);
            send("SYSTEM CMD: " + cmd);
            if (shouldBypassCommand(cmd)) {
                send("Bypass native system: " + cmd);
                Memory.writeUtf8String(args[0], "grep");
            }
            if (cmd == "su") {
                send("Bypass native system: " + cmd);
                Memory.writeUtf8String(args[0], "invalid_command");
            }
        },
        onLeave: function(retval) {

        }
    });

    BufferedReader.readLine.overload('boolean').implementation = function() {
        var text = this.readLine.overload('boolean').call(this);
        if (text === null) {
            // do nothing
        } else {
            if (text.indexOf("ro.build.tags=test-keys") > -1) {
                send("Bypass build.prop file read");
                text = text.replace("ro.build.tags=test-keys", "ro.build.tags=release-keys");
            }
            var suspiciousWords = ["goldfish", "ranchu", "generic", "intel", "qemu"];
            for (var i = 0; i < suspiciousWords.length; i++) {
                if (text.indexOf(suspiciousWords[i]) > -1) {
                    send("Bypass emulator detection in file read");
                    text = "";
                    break;
                }
            }
        }
        return text;
    };

    ProcessBuilder.start.implementation = function() {
        var cmd = this.command.call(this);
        var shouldModifyCommand = false;
        for (var i = 0; i < cmd.size(); i++) {
            var tmp_cmd = cmd.get(i).toString();
            if (shouldBypassCommand(tmp_cmd)) {
                shouldModifyCommand = true;
                break;
            }
            if (tmp_cmd == "su") {
                shouldModifyCommand = true;
                break;
            }
        }
        if (shouldModifyCommand) {
            send("Bypass ProcessBuilder " + cmd);
            this.command.call(this, ["grep"]);
            return this.start.call(this);
        }
        return this.start.call(this);
    };

    // Additional hooks can be added below as needed

});

/* 
   Android SSL Re-pinning frida script v0.2 030417-pier 

   $ adb push burpca-cert-der.crt /data/local/tmp/cert-der.crt
   $ frida -U -f it.app.mobile -l frida-android-repinning.js --no-pause

   https://techblog.mediaservice.net/2017/07/universal-android-ssl-pinning-bypass-with-frida/
   
   UPDATE 20191605: Fixed undeclared var. Thanks to @oleavr and @ehsanpc9999 !
*/

setTimeout(function() {
    Java.perform(function() {
        console.log("");
        console.log("[.] Cert Pinning Bypass/Re-Pinning");

        var CertificateFactory = Java.use("java.security.cert.CertificateFactory");
        var FileInputStream = Java.use("java.io.FileInputStream");
        var BufferedInputStream = Java.use("java.io.BufferedInputStream");
        var X509Certificate = Java.use("java.security.cert.X509Certificate");
        var KeyStore = Java.use("java.security.KeyStore");
        var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
        var SSLContext = Java.use("javax.net.ssl.SSLContext");

        // Load CAs from an InputStream
        console.log("[+] Loading our CA...")
        var cf = CertificateFactory.getInstance("X.509");

        try {
            var fileInputStream = FileInputStream.$new("/data/local/tmp/cert-der.crt");
        } catch (err) {
            console.log("[o] " + err);
            // Add this line to return or throw an error to stop the script
            return; // Or throw an error:  throw new Error("Failed to load certificate: " + err);
        }

        // Only proceed if fileInputStream is valid
        if (fileInputStream) {
            var bufferedInputStream = BufferedInputStream.$new(fileInputStream);
            var ca = cf.generateCertificate(bufferedInputStream);
            bufferedInputStream.close();
            var certInfo = Java.cast(ca, X509Certificate);
            console.log("[o] Our CA Info: " + certInfo.getSubjectDN());
        } else {
            console.log("[o] FileInputStream was null, exiting");
            return; // Or throw an error if you prefer
        }

        // Create a KeyStore containing our trusted CAs
        console.log("[+] Creating a KeyStore for our CA...");
        var keyStoreType = KeyStore.getDefaultType();
        var keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);

        // Create a TrustManager that trusts the CAs in our KeyStore
        console.log("[+] Creating a TrustManager that trusts the CA in our KeyStore...");
        var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        var tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);
        console.log("[+] Our TrustManager is ready...");

        console.log("[+] Hijacking SSLContext methods now...")
        console.log("[-] Waiting for the app to invoke SSLContext.init()...")

        SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function(a, b, c) {
            console.log("[o] App invoked javax.net.ssl.SSLContext.init...");
            SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").call(this, a, tmf.getTrustManagers(), c);
            console.log("[+] SSLContext initialized with our custom TrustManager!");
        }
    });
}, 0);
Share this script:
Twitter LinkedIn

Comments

Login or Sign up to leave a comment.
Loading comments...