Last active
January 6, 2022 15:40
-
-
Save stevejubx/fecf7a1305ca7b32cf5a750b0244ed03 to your computer and use it in GitHub Desktop.
Root detection & certificate pinning bypass with Frida Framework
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| setTimeout(function(){ | |
| 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.android.vending.billing.InAppBillingService.COIN","com.topjohnwu.magisk" | |
| ]; | |
| var RootBinaries = ["su", "busybox", "supersu", "Superuser.apk", "KingoUser.apk", "SuperSu.apk","magisk"]; | |
| var RootProperties = { | |
| "ro.build.selinux": "1", | |
| "ro.debuggable": "0", | |
| "service.adb.root": "0", | |
| "ro.secure": "1" | |
| }; | |
| var RootPropertiesKeys = []; | |
| for (var k in RootProperties) RootPropertiesKeys.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 StringBuffer = Java.use('java.lang.StringBuffer'); | |
| var loaded_classes = Java.enumerateLoadedClassesSync(); | |
| send("Loaded " + loaded_classes.length + " classes!"); | |
| var useKeyInfo = false; | |
| var useProcessManager = false; | |
| send("loaded: " + loaded_classes.indexOf('java.lang.ProcessManager')); | |
| if (loaded_classes.indexOf('java.lang.ProcessManager') != -1) { | |
| try { | |
| //useProcessManager = true; | |
| //var ProcessManager = Java.use('java.lang.ProcessManager'); | |
| } catch (err) { | |
| send("ProcessManager Hook failed: " + err); | |
| } | |
| } else { | |
| send("ProcessManager hook not loaded"); | |
| } | |
| var KeyInfo = null; | |
| if (loaded_classes.indexOf('android.security.keystore.KeyInfo') != -1) { | |
| try { | |
| //useKeyInfo = true; | |
| //var KeyInfo = Java.use('android.security.keystore.KeyInfo'); | |
| } catch (err) { | |
| send("KeyInfo Hook failed: " + err); | |
| } | |
| } else { | |
| send("KeyInfo hook not loaded"); | |
| } | |
| 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.call(this, pname, flags); | |
| }; | |
| NativeFile.exists.implementation = function() { | |
| var name = NativeFile.getName.call(this); | |
| var shouldFakeReturn = (RootBinaries.indexOf(name) > -1); | |
| if (shouldFakeReturn) { | |
| send("Bypass return value for binary: " + name); | |
| 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'); | |
| exec5.implementation = function(cmd, env, dir) { | |
| if (cmd.indexOf("getprop") != -1 || cmd == "mount" || cmd.indexOf("build.prop") != -1 || cmd == "id" || cmd == "sh") { | |
| var fakeCmd = "grep"; | |
| send("Bypass " + cmd + " command"); | |
| return exec1.call(this, fakeCmd); | |
| } | |
| if (cmd == "su") { | |
| var fakeCmd = "justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"; | |
| send("Bypass " + cmd + " command"); | |
| return exec1.call(this, fakeCmd); | |
| } | |
| if (cmd == "which") { | |
| var fakeCmd = "justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"; | |
| send("Bypass which command"); | |
| return exec1.call(this, fakeCmd); | |
| } | |
| return exec5.call(this, cmd, env, dir); | |
| }; | |
| exec4.implementation = function(cmdarr, env, file) { | |
| for (var i = 0; i < cmdarr.length; i = i + 1) { | |
| var tmp_cmd = cmdarr[i]; | |
| if (tmp_cmd.indexOf("getprop") != -1 || tmp_cmd == "mount" || tmp_cmd.indexOf("build.prop") != -1 || tmp_cmd == "id" || tmp_cmd == "sh") { | |
| var fakeCmd = "grep"; | |
| send("Bypass " + cmdarr + " command"); | |
| return exec1.call(this, fakeCmd); | |
| } | |
| if (tmp_cmd == "su") { | |
| var fakeCmd = "justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"; | |
| send("Bypass " + cmdarr + " command"); | |
| return exec1.call(this, fakeCmd); | |
| } | |
| } | |
| return exec4.call(this, cmdarr, env, file); | |
| }; | |
| exec3.implementation = function(cmdarr, envp) { | |
| for (var i = 0; i < cmdarr.length; i = i + 1) { | |
| var tmp_cmd = cmdarr[i]; | |
| if (tmp_cmd.indexOf("getprop") != -1 || tmp_cmd == "mount" || tmp_cmd.indexOf("build.prop") != -1 || tmp_cmd == "id" || tmp_cmd == "sh") { | |
| var fakeCmd = "grep"; | |
| send("Bypass " + cmdarr + " command"); | |
| return exec1.call(this, fakeCmd); | |
| } | |
| if (tmp_cmd == "su") { | |
| var fakeCmd = "justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"; | |
| send("Bypass " + cmdarr + " command"); | |
| return exec1.call(this, fakeCmd); | |
| } | |
| } | |
| return exec3.call(this, cmdarr, envp); | |
| }; | |
| exec2.implementation = function(cmd, env) { | |
| if (cmd.indexOf("getprop") != -1 || cmd == "mount" || cmd.indexOf("build.prop") != -1 || cmd == "id" || cmd == "sh") { | |
| var fakeCmd = "grep"; | |
| send("Bypass " + cmd + " command"); | |
| return exec1.call(this, fakeCmd); | |
| } | |
| if (cmd == "su") { | |
| var fakeCmd = "justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"; | |
| send("Bypass " + cmd + " command"); | |
| return exec1.call(this, fakeCmd); | |
| } | |
| return exec2.call(this, cmd, env); | |
| }; | |
| exec.implementation = function(cmd) { | |
| for (var i = 0; i < cmd.length; i = i + 1) { | |
| var tmp_cmd = cmd[i]; | |
| if (tmp_cmd.indexOf("getprop") != -1 || tmp_cmd == "mount" || tmp_cmd.indexOf("build.prop") != -1 || tmp_cmd == "id" || tmp_cmd == "sh") { | |
| var fakeCmd = "grep"; | |
| send("Bypass " + cmd + " command"); | |
| return exec1.call(this, fakeCmd); | |
| } | |
| if (tmp_cmd == "su") { | |
| var fakeCmd = "justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"; | |
| send("Bypass " + cmd + " command"); | |
| return exec1.call(this, fakeCmd); | |
| } | |
| } | |
| return exec.call(this, cmd); | |
| }; | |
| exec1.implementation = function(cmd) { | |
| if (cmd.indexOf("getprop") != -1 || cmd == "mount" || cmd.indexOf("build.prop") != -1 || cmd == "id" || cmd == "sh") { | |
| var fakeCmd = "grep"; | |
| send("Bypass " + cmd + " command"); | |
| return exec1.call(this, fakeCmd); | |
| } | |
| if (cmd == "su") { | |
| var fakeCmd = "justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"; | |
| send("Bypass " + cmd + " command"); | |
| return exec1.call(this, fakeCmd); | |
| } | |
| return exec1.call(this, cmd); | |
| }; | |
| 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 (RootPropertiesKeys.indexOf(name) != -1) { | |
| send("Bypass " + name); | |
| return RootProperties[name]; | |
| } | |
| return this.get.call(this, name); | |
| }; | |
| Interceptor.attach(Module.findExportByName("libc.so", "fopen"), { | |
| onEnter: function(args) { | |
| var path1 = Memory.readCString(args[0]); | |
| var path = path1.split("/"); | |
| var executable = path[path.length - 1]; | |
| var shouldFakeReturn = (RootBinaries.indexOf(executable) > -1) | |
| if (shouldFakeReturn) { | |
| Memory.writeUtf8String(args[0], "/ggezxxx"); | |
| send("Bypass native fopen >> "+path1); | |
| } | |
| }, | |
| onLeave: function(retval) { | |
| } | |
| }); | |
| Interceptor.attach(Module.findExportByName("libc.so", "fopen"), { | |
| onEnter: function(args) { | |
| var path1 = Memory.readCString(args[0]); | |
| var path = path1.split("/"); | |
| var executable = path[path.length - 1]; | |
| var shouldFakeReturn = (RootBinaries.indexOf(executable) > -1) | |
| if (shouldFakeReturn) { | |
| Memory.writeUtf8String(args[0], "/ggezxxx"); | |
| send("Bypass native fopen >> "+path1); | |
| } | |
| }, | |
| onLeave: function(retval) { | |
| } | |
| }); | |
| Interceptor.attach(Module.findExportByName("libc.so", "system"), { | |
| onEnter: function(args) { | |
| var cmd = Memory.readCString(args[0]); | |
| send("SYSTEM CMD: " + cmd); | |
| if (cmd.indexOf("getprop") != -1 || cmd == "mount" || cmd.indexOf("build.prop") != -1 || cmd == "id") { | |
| send("Bypass native system: " + cmd); | |
| Memory.writeUtf8String(args[0], "grep"); | |
| } | |
| if (cmd == "su") { | |
| send("Bypass native system: " + cmd); | |
| Memory.writeUtf8String(args[0], "justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"); | |
| } | |
| }, | |
| onLeave: function(retval) { | |
| } | |
| }); | |
| /* | |
| TO IMPLEMENT: | |
| Exec Family | |
| int execl(const char *path, const char *arg0, ..., const char *argn, (char *)0); | |
| int execle(const char *path, const char *arg0, ..., const char *argn, (char *)0, char *const envp[]); | |
| int execlp(const char *file, const char *arg0, ..., const char *argn, (char *)0); | |
| int execlpe(const char *file, const char *arg0, ..., const char *argn, (char *)0, char *const envp[]); | |
| int execv(const char *path, char *const argv[]); | |
| int execve(const char *path, char *const argv[], char *const envp[]); | |
| int execvp(const char *file, char *const argv[]); | |
| int execvpe(const char *file, char *const argv[], char *const envp[]); | |
| */ | |
| BufferedReader.readLine.overload().implementation = function() { | |
| var text = this.readLine.call(this); | |
| if (text === null) { | |
| // just pass , i know it's ugly as hell but test != null won't work :( | |
| } else { | |
| var shouldFakeRead = (text.indexOf("ro.build.tags=test-keys") > -1); | |
| if (shouldFakeRead) { | |
| send("Bypass build.prop file read"); | |
| text = text.replace("ro.build.tags=test-keys", "ro.build.tags=release-keys"); | |
| } | |
| } | |
| return text; | |
| }; | |
| var executeCommand = ProcessBuilder.command.overload('java.util.List'); | |
| ProcessBuilder.start.implementation = function() { | |
| var cmd = this.command.call(this); | |
| var shouldModifyCommand = false; | |
| for (var i = 0; i < cmd.size(); i = i + 1) { | |
| var tmp_cmd = cmd.get(i).toString(); | |
| if (tmp_cmd.indexOf("getprop") != -1 || tmp_cmd.indexOf("mount") != -1 || tmp_cmd.indexOf("build.prop") != -1 || tmp_cmd.indexOf("id") != -1) { | |
| shouldModifyCommand = true; | |
| } | |
| } | |
| if (shouldModifyCommand) { | |
| send("Bypass ProcessBuilder " + cmd); | |
| this.command.call(this, ["grep"]); | |
| return this.start.call(this); | |
| } | |
| if (cmd.indexOf("su") != -1) { | |
| send("Bypass ProcessBuilder " + cmd); | |
| this.command.call(this, ["justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"]); | |
| return this.start.call(this); | |
| } | |
| return this.start.call(this); | |
| }; | |
| if (useProcessManager) { | |
| var ProcManExec = ProcessManager.exec.overload('[Ljava.lang.String;', '[Ljava.lang.String;', 'java.io.File', 'boolean'); | |
| var ProcManExecVariant = ProcessManager.exec.overload('[Ljava.lang.String;', '[Ljava.lang.String;', 'java.lang.String', 'java.io.FileDescriptor', 'java.io.FileDescriptor', 'java.io.FileDescriptor', 'boolean'); | |
| ProcManExec.implementation = function(cmd, env, workdir, redirectstderr) { | |
| var fake_cmd = cmd; | |
| for (var i = 0; i < cmd.length; i = i + 1) { | |
| var tmp_cmd = cmd[i]; | |
| if (tmp_cmd.indexOf("getprop") != -1 || tmp_cmd == "mount" || tmp_cmd.indexOf("build.prop") != -1 || tmp_cmd == "id") { | |
| var fake_cmd = ["grep"]; | |
| send("Bypass " + cmdarr + " command"); | |
| } | |
| if (tmp_cmd == "su") { | |
| var fake_cmd = ["justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"]; | |
| send("Bypass " + cmdarr + " command"); | |
| } | |
| } | |
| return ProcManExec.call(this, fake_cmd, env, workdir, redirectstderr); | |
| }; | |
| ProcManExecVariant.implementation = function(cmd, env, directory, stdin, stdout, stderr, redirect) { | |
| var fake_cmd = cmd; | |
| for (var i = 0; i < cmd.length; i = i + 1) { | |
| var tmp_cmd = cmd[i]; | |
| if (tmp_cmd.indexOf("getprop") != -1 || tmp_cmd == "mount" || tmp_cmd.indexOf("build.prop") != -1 || tmp_cmd == "id") { | |
| var fake_cmd = ["grep"]; | |
| send("Bypass " + cmdarr + " command"); | |
| } | |
| if (tmp_cmd == "su") { | |
| var fake_cmd = ["justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"]; | |
| send("Bypass " + cmdarr + " command"); | |
| } | |
| } | |
| return ProcManExecVariant.call(this, fake_cmd, env, directory, stdin, stdout, stderr, redirect); | |
| }; | |
| } | |
| if (useKeyInfo) { | |
| KeyInfo.isInsideSecureHardware.implementation = function() { | |
| send("Bypass isInsideSecureHardware"); | |
| return true; | |
| } | |
| } | |
| 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); | |
| } | |
| 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()); | |
| // 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); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment