文章部分摘自看雪高妍 及 凡墙总是门大佬的文章

attach spawn

# spawn
frida -U -f com.xx.xx -l xx.js

# attach
frida -U com.xx.xx -l xx.js

常用命令

# jar转dex, 或者用np管理器转
# 可以看这里https://blog.devilwst.top/xiaosheng/2425.html
dx --dex --output=ddex.dex ddex.jar
chmod 777 ddex.dex

frida改端口

app server

./frida14-2 -l 192.168.1.103:6677

# 也可以使用默认端口使用
./frida14-2 -l 192.168.1.103

client

# 使用自定义端口
frida -H 192.168.1.103:6677 com.xx.xx -l xx.js

# 使用默认端口
frida -H com.xx.xx -l xx.js

hook重载

function main(){
    Java.perform(function(){
        var UtilsClass = Java.use("com.xx.Utils");

        // 重载无参方法
        UtilsClass.test.overload().implementation = function () {
            console.log("hook overload no args");
            return this.test();
        }

        // 重载有参方法 - 基础数据类型
        UtilsClass.test.overload('int').implementation = function(num){
            console.log("hook overload int args");
            var myNum = 9999;
            var oriResult = this.test(num);
            console.log("oriResult is :" + oriResult);
            return this.test(myNum);
        }

        // 重载有参方法 - 引用数据类型Money
        UtilsClass.test.overload('com.xx.Money').implementation = function(money){
            console.log("hook Money args");
            return this.test(money);
        }

        // hook 指定test方法的所有重载
        var ClassName = Java.use("com.xx.Utils");
        var overloadsLength = ClassName.test.overloads.length;
        for (var i = 0; i < overloadsLength; i++){
            ClassName.test.overloads[i].implementation = function () {
                // 遍历打印 arguments 
                for (var a = 0; a < arguments.length; a++){
                    console.log(a + " : " + arguments[a]);
                }
                // 调用原方法
                return this.test.apply(this,arguments);
            }
        }
    })
}

setImmediate(main);

hook构造方法 & 主动调用

var mone = Java.use("com.xx.Money");
mone.init.overload().implementation = function(){
}

// 有参构造
var strClass = Java.use("java.lang.String")
mone.init.overload("java.lang.String",'int').implementation = function(x,y) {
    // 主动调用
    var myx = strClass.new("hi");    var myy = 9;
    this.init(myx,myy);
}

hook匿名类

        // hook 匿名类
        // 匿名类在 smail中以 1,2 等方式存在, 需要通过 java 行号去 smail 找到准确的匿名类名称 
        // new __接口__{} 可以理解成 new 了一个实现接口的匿名类, 在匿名类的内部(花括号内),实现了这个接口
        var NiMingClass = Java.use("com.xxxx.MainActivity$1");
        NiMingClass.getInfo.implementation = function (){
            return "hook匿名类";
        }

调用静态函数

private static void setstaticBoolVar(){
       FridaActivity2.staticBoolVar = true;
    }

hook代码

    var FridaActivity2 = Java.use(
      "com.xxxxxx.FridaActivity2");
    console.log("FridaActivity2.staticBoolVar:", FridaActivity2.staticBoolVar.value);
    FridaActivity2.setstaticBoolVar();  //调用静态函数
    console.log("FridaActivity2.staticBoolVar:", FridaActivity2.staticBoolVar.value);

调用非静态函数

    private void setboolVar(){
       this.boolVar = true;
    }

hook代码

    //调用非静态函数
    Java.choose("com.xxxxxy.FridaActivity2", {
      onMatch : function(instance) {
        console.log("FridaActivity2.boolVar:", instance.boolVar.value);
        instance.setboolVar();
        console.log("FridaActivity2.boolVar:", instance.boolVar.value);
      }, onComplete : function() {
      }
    })

方法和参数同名情况

    private boolean sameNameBoolVar;
    private boolean boolVar;

    private void sameNameBoolVar(){
       Log.d("Frida", FridaActivity3.staticBoolVar+" "+this.boolVar+" "+this.sameNameBoolVar);
    }

hook代码

    Java.choose("com.xxxxx.Activity.FridaActivity3", {
      onMatch : function(instance) {
        console.log("FridaActivity3.bool_var:", instance.boolVar.value);
        instance.bool_var.value = true;
        console.log("FridaActivity3.bool_var:", instance.boolVar.value);

        console.log("FridaActivity3._sameNameBoolVar:", instance._sameNameBoolVar.value);
        instance._sameNameBoolVar.value = true;
        console.log("FridaActivity3._sameNameBoolVar:", instance._sameNameBoolVar.value);
      }, onComplete : function() {
      }
    })

hook内部类

public class FridaActivity4{

    public static class InnerClasses    // class@00073d from classes.dex{
        public void InnerClasses(){
           super();
        }
        public static boolean check1(){
           return false;
        }
        public static boolean check2(){
           return false;
        }
        public static boolean check3(){
           return false;
        }
        public static boolean check4(){
           return false;
        }
        public static boolean check5(){
           return false;
        }
        public static boolean check6(){
           return false;
        }
    }

}

hook代码, $, 不同反编译器结果不一样,Jadx更偏向于开发时候写的内部类,GDA编译的内部类class FridaActivity4$InnerClasses 单独写在外部

var InnerClasses = Java.use(
      "com.xxxxx.FridaActivity4$InnerClasses")
   InnerClasses.check1.implementation = function() {
     console.log("InnerClasses.check1:");
     return true;
   }
或者hook一个类内部所有的方法


hook一个类内部所有的方法及重载

Java.perform(function(){
        // hook md5 class in app
        // 1. iterate classes
        var classList = Java.enumerateLoadedClassesSync();
        for (var i = 0; i < classList.length; i++){
            // 筛选过滤 只遍历 MD5 下面的方法
            if (classList[i].indexOf("com.xx.app.MD5") != -1){
                var className = classList[i];
                console.log("class name is :", className);

                // 2. get methods of the class
                // 返回一个 Methods对象的数组
                var methodsList = Java.use(className).class.getDeclaredMethods();
                for (var k=0; k<methodsList.length; k++){                    
                    // console.log("method is :",methodsList[k],typeof(methodsList[k]));

                    // 3. Method object.getName() --> methodName and class[methodName] to hook method
                    var methodName = methodsList[k].getName(); // 

                    // console.log('methodName',methodName);

                    // 4. use apply and arguments to implementation
                    var hookClass = Java.use(className);
                    // 5. 重载
                    for (var o = 0; o< hookClass[methodName].overloads.length; o++){
                        hookClass[methodName].overloads[o].implementation = function(){
                            for (var a=0; a<arguments.length; a++){
                                console.log('argument ',a,arguments[a]);
                            }
                            // return this[methodName].apply(this,arguments);
                            return "fucking the md5"
                        }
                    }
                }
            }
        }
    })

hook动态加载dex的类

private void loaddex(){
       DexClassLoader e2;
       File cacheDir = this.getCacheDir();
       if (!cacheDir.exists()) {
          cacheDir.mkdir();
       }
       String str = "DynamicPlugin.dex";
       String str1 = cacheDir.getAbsolutePath()+File.separator+str;
       try{
          File uFile = new File(str1);
          if (!uFile.exists()) {
             uFile.createNewFile();
             FridaActivity5.copyFiles(this, str, uFile);
          }
       }catch(java.io.IOException e2){
          e2.printStackTrace();
       }
       String absolutePath = cacheDir.getAbsolutePath();
       ClassLoader classLoader = this.getClassLoader();
       try{
          e2 = new DexClassLoader(str1, absolutePath, null, classLoader);
          this.DynamicDexCheck = e2.loadClass("com.xxxx.Dynamic.DynamicCheck").newInstance();
          if (this.DynamicDexCheck == null) {
             Toast.makeText(this, "loaddex Failed!", 1).show();
          }
       }catch(java.lang.Exception e0){
          e0.printStackTrace();
       }
       return;
    }
    public CheckInterface getDynamicDexCheck(){
       if (this.DynamicDexCheck == null) {
          this.loaddex();
       }
       return this.DynamicDexCheck;
    }

public interface abstract CheckInterface    // class@000745 from classes.dex
{
    boolean check();
}

hook代码

这个直接hook com.xxxx.Dynamic.DynamicCheck是hook不到的,要先找到加载该类的classLoader

  Java.perform(function() {
    Java.enumerateClassLoaders({
      onMatch : function(loader) {
        try {
          // 找到classLoader   loadClass 或者 findClass
          if (loader.findClass("com.xxxx.Dynamic.DynamicCheck")) {
            // 设置classLoader
            Java.classFactory.loader = loader;
            console.log(loader);
          }
        } catch (error) {

        }
      }, onComplete : function() {
      }
    });
    // 设置完classLoader就能hook到了 
    var DynamicCheck = Java.use("com.xxxxx.Dynamic.DynamicCheck");
    DynamicCheck.check.implementation = function() {
      var result = this.check();
      console.log("DynamicCheck.check:", result);
      return true;
    }
  })

但是在加壳的app中可能没法找到正常加载app类的classLoader,可以直接loadClass尝试来加载

function hook() {
    Java.perform(function () {
        Java.enumerateClassLoadersSync().forEach(function (classloader) {
            try {
                console.log("classloader", classloader);
                classloader.loadClass("com.xxxx.MainActivity");
                Java.classFactory.loader = classloader;
                var mainActivityClass = Java.use("com.kanxue.encrypt01.MainActivity");
                console.log("mainActivityClass", mainActivityClass);
            } catch (error) {
                console.log("error", error);
            }
        });
    })
}

强转Java.cast

var temp = null;  //用来存储找到的对象
var toCast = Java.use("comx.xx.xx.xx")
Java.choose("com.xx.xx", {
    onMatch:function(instance) {
        temp = instance;
        var toCastInstance = Java.cast(temp, toCast);
    },
    onComplete:function() {

    }
})

hook打印类 实现的接口

Java.enumerateLoadedClasses({
            onComplete: function(){},
            onMatch: function(name,handle){
                if (name.indexOf("com.xx.xx") > -1) { // 使用包名进行过滤
                    console.log("find class");
                    var targetClass = Java.use(name);
                    var interfaceList = targetClass.class.getInterfaces(); // 使用反射获取类实现的接口数组
                    if (interfaceList.length > 0) {
                        console.log(name) // 打印类名
                        for (var i in interfaceList) {
                            console.log("\t", interfaceList[i].toString()); // 直接打印接口名称
                        }
                    }
                }
            }
        })

hook网络类

过vpn检测

第一种

function main(){
    Java.perform(function (){
        Java.use("android.net.NetworkInfo").isConnected.implementation = function(){
            console.log("first called!")
            return false
        }

        Java.use("java.net.NetworkInterface").getName.implementation = function(){
            console.log("second called!")
            return ""
            // return null
        }

        Java.use("android.net.NetworkCapabilities").hasTransport.implementation=function(){
            console.log("third called!")
            return false
        }
        //android.net.ConnectivityManager.getNetworkCapabilities
    })
}

第二种

function main() {
    Java.perform(function () {
        var NetworkInfo = Java.use("android.net.NetworkInfo")
        NetworkInfo.isConnected.implementation = function(){
            console.log("first called!")
            var result = this.isConnected()
            console.log('result',result)
            return false
            // return result
        }
        var network = Java.use('java.net.NetworkInterface')
        network.getName.implementation = function () {
            console.log("second called!")
            var name = this.getName()
            console.log('name:' + name)
            if (name == "tun0") {
                // var result = Java.use('java.lang.String').new('rmnet_data0')
                var result = Java.use('java.lang.String').new('ccmni1')
                console.log('hook result:' + result)
                // return result
                return ''
            }
            // else if (name == "wlan0") {
            //     // var result = Java.use('java.lang.String').new('rmnet_data0')
            //     var result = Java.use('java.lang.String').new('ccmni2')
            //     console.log('hook result:' + result)
            //     return result
            // }
            else {
                return name
            }
        }

        var NetworkCapabilities = Java.use("android.net.NetworkCapabilities")
        NetworkCapabilities.hasTransport.overload('int').implementation = function (arg) {
            console.log("third called!",arg)
            var result = this.hasTransport(arg)
            console.log('result',result)
            // return result
            return false
        }
        var ConnectivityManager = Java.use("android.net.ConnectivityManager")
        ConnectivityManager.getNetworkCapabilities.implementation = function (arg) {
            console.log("four called!",arg)
            var result = this.getNetworkCapabilities(arg)
            console.log('result',result)
            return result
            // return false
        }
        // android.net.ConnectivityManager.getNetworkCapabilities
    })
}

hook枚举类

Java.choose("com.xxxx.OptEnum",{
            onComplete: function(){},
            onMatch: function(instance){
                console.log('find enum :',instance);
                console.log(instance.class.getName());
            }
        })

hook获取applicationContext

Java.perform(function(){
        var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
        console.log(currentApplication);
        var context = currentApplication.getApplicationContext();
        console.log(context);
        var packageName = context.getPackageName();
        console.log(packageName);
        console.log(currentApplication.getPackageName());
    })

hook打印char

        var CharClass = Java.use("java.lang.Character");
        CharClass.toString.overload("char").implementation = function(inputChar){
            var result = this.toString(inputChar);
            console.log("inputChar, result: ", inputChar, result);
            return result;
        }

hook打印char数组

        var ArrayClass = Java.use("java.util.Arrays");
        ArrayClass.toString.overload('[C').implementation = function(charArray){
            // 1. java.util.Arrays.toString()
            var result = this.toString(charArray);
            // 2. javascript JSON.stringify()
            var result1 = JSON.stringify(charArray);
            console.log('charArray, result : ', charArray, result);
            console.log('charArray, result :', charArray, result1);
        }

打印调用栈printStackTraces

function printstack(){
    send(Java.use('android.util.Log').getStackTraceString(Java.use('java.lang.Exception').$new()))
}

打印一些常用东西

dexTool封装自

    var StringClass = Java.use("java.lang.String");
    var StringClass = Java.use("java.lang.String");
    var byteArray = StringClass.new("Hello World").getBytes();

    Java.openClassFile("/data/local/tmp/xiaosheng-dex-tool.dex").load();
    var js = Java.use("com.xiaosheng.tool.json.Gson");
    var gson = js.new();

    console.log(gson.toJson(byteArray));

hook打印 non-ascii 和特殊字符

可以先通过encodeURIComponent编码再decodeURIComponent解码的方式进行 hook

int ֏(int x) {
        return x + 100;
    }
var targetClass = "com.xxxx.MainActivity";

var hookCls = Java.use(targetClass);
var methods = hookCls.class.getDeclaredMethods();

for (var i in methods) {
    console.log(methods[i].toString());
    console.log(encodeURIComponent(methods[i].toString().replace(/^.*?\.([^\s\.\(\)]+)\(.*?/, "1")));
}

// hook解码后的字符
hookCls[decodeURIComponent("%D6%8F")]
                .implementation = function (x) {
                    console.log("original call: fun(" + x + ")");
                    var result = this[decodeURIComponent("%D6%8F")](900);
                    return result;
                }

hook onClick()

var jclazz = null;
var jobj = null;

function getObjClassName(obj) {
    if (!jclazz) {
        var jclazz = Java.use("java.lang.Class");
    }
    if (!jobj) {
        var jobj = Java.use("java.lang.Object");
    }
    return jclazz.getName.call(jobj.getClass.call(obj));
}

function watch(obj, mtdName) {
    var listener_name = getObjClassName(obj);
    var target = Java.use(listener_name);
    if (!target || !mtdName in target) {
        return;
    }
    // send("[WatchEvent] hooking " + mtdName + ": " + listener_name);
    target[mtdName].overloads.forEach(function (overload) {
        overload.implementation = function () {
            //send("[WatchEvent] " + mtdName + ": " + getObjClassName(this));
            console.log("[WatchEvent] " + mtdName + ": " + getObjClassName(this))
            return this[mtdName].apply(this, arguments);
        };
    })
}

function OnClickListener() {
    Java.perform(function () {

        //以spawn启动进程的模式来attach的话
        Java.use("android.view.View").setOnClickListener.implementation = function (listener) {
            if (listener != null) {
                watch(listener, 'onClick');
            }
            return this.setOnClickListener(listener);
        };

        //如果frida以attach的模式进行attch的话
        Java.choose("android.view.View$ListenerInfo", {
            onMatch: function (instance) {
                instance = instance.mOnClickListener.value;
                if (instance) {
                    console.log("mOnClickListener name is :" + getObjClassName(instance));
                    watch(instance, 'onClick');
                }
            },
            onComplete: function () {
            }
        })
    })
}
setImmediate(OnClickListener);

hook 用frida实现activity跳转

Java.perform(function () {
        var context = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext();
        var intentClazz = Java.use("android.content.Intent");
        var activityClazz = Java.use("ctrip.android.hotel.view.UI.inquire.HotelInquireActivity");
        var intentObj = intentClazz.$new(context, activityClazz.class);
        intentObj.setFlags(0x10000000);
        context.startActivity(intentObj);
        console.log("startActivity");
    })

hook 禁止app退出

Java.perform(function(){
        console.log("[*] Starting hook exit");
        var exitClass = Java.use("java.lang.System");
        exitClass.exit.implementation = function(){
            console.log("[*] System.exit.called");
        }
        console.log("[*] hooking calls to System.exit");
    })

hook sslpinning

setTimeout(function() {
  Java.perform(function() {
    console.log('');
    console.log('======');
    console.log('[#] Android Bypass for various Certificate Pinning methods [#]');
    console.log('======');

    var errDict = {};

    // TrustManager (Android < 7) //
    ////////////////////////////////
    var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
    var SSLContext = Java.use('javax.net.ssl.SSLContext');
    var TrustManager = Java.registerClass({
      // Implement a custom TrustManager
      name: 'dev.asd.test.TrustManager',
      implements: [X509TrustManager],
      methods: {
        checkClientTrusted: function(chain, authType) {},
        checkServerTrusted: function(chain, authType) {},
        getAcceptedIssuers: function() {return []; }
      }
    });
    // Prepare the TrustManager array to pass to SSLContext.init()
    var TrustManagers = [TrustManager.new()];
    // Get a handle on the init() on the SSLContext class
    var SSLContext_init = SSLContext.init.overload(
      '[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom');
    try {
      // Override the init method, specifying the custom TrustManager
      SSLContext_init.implementation = function(keyManager, trustManager, secureRandom) {
        console.log('[+] Bypassing Trustmanager (Android<7) pinner');
        SSLContext_init.call(this, keyManager, TrustManagers, secureRandom);
      };
    } catch (err) {
      console.log('[-] TrustManager (Android<7) pinner not found');
      //console.log(err);
    }




    // OkHTTPv3 (quadruple bypass) //
    /////////////////////////////////
    try {
      // Bypass OkHTTPv3 {1}
      var okhttp3_Activity_1 = Java.use('okhttp3.CertificatePinner');
      okhttp3_Activity_1.check.overload('java.lang.String', 'java.util.List').implementation = function(a, b) {
        console.log('[+] Bypassing OkHTTPv3 {1}: ' + a);
        return;
      };
    } catch (err) {
      console.log('[-] OkHTTPv3 {1} pinner not found');
      //console.log(err);
      errDict[err] = ['okhttp3.CertificatePinner', 'check'];
    }
    try {
      // Bypass OkHTTPv3 {2}
      // This method of CertificatePinner.check is deprecated but could be found in some old Android apps
      var okhttp3_Activity_2 = Java.use('okhttp3.CertificatePinner');
      okhttp3_Activity_2.check.overload('java.lang.String', 'java.security.cert.Certificate').implementation = function(a, b) {
        console.log('[+] Bypassing OkHTTPv3 {2}: ' + a);
        return;
      };
    } catch (err) {
      console.log('[-] OkHTTPv3 {2} pinner not found');
      //console.log(err);
      //errDict[err] = ['okhttp3.CertificatePinner', 'check'];
    }
    try {
      // Bypass OkHTTPv3 {3}
      var okhttp3_Activity_3 = Java.use('okhttp3.CertificatePinner');
      okhttp3_Activity_3.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function(a, b) {
        console.log('[+] Bypassing OkHTTPv3 {3}: ' + a);
        return;
      };
    } catch(err) {
      console.log('[-] OkHTTPv3 {3} pinner not found');
      //console.log(err);
      errDict[err] = ['okhttp3.CertificatePinner', 'check'];
    }
    try {
      // Bypass OkHTTPv3 {4}
      var okhttp3_Activity_4 = Java.use('okhttp3.CertificatePinner');
      //okhttp3_Activity_4['checkokhttp'].implementation = function(a, b) {
      okhttp3_Activity_4.checkokhttp.overload('java.lang.String', 'kotlin.jvm.functions.Function0').implementation = function(a, b) {
        console.log('[+] Bypassing OkHTTPv3 {4}: ' + a);
        return;
      };
    } catch(err) {
      console.log('[-] OkHTTPv3 {4} pinner not found');
      //console.log(err);
      errDict[err] = ['okhttp3.CertificatePinner', 'checkokhttp'];
    }



    // Trustkit (triple bypass) //
    //////////////////////////////
    try {
      // Bypass Trustkit {1}
      var trustkit_Activity_1 = Java.use('com.datatheorem.android.trustkit.pinning.OkHostnameVerifier');
      trustkit_Activity_1.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function(a, b) {
        console.log('[+] Bypassing Trustkit {1}: ' + a);
        return true;
      };
    } catch (err) {
      console.log('[-] Trustkit {1} pinner not found');
      //console.log(err);
      errDict[err] = ['com.datatheorem.android.trustkit.pinning.OkHostnameVerifier', 'verify'];
    }
    try {
      // Bypass Trustkit {2}
      var trustkit_Activity_2 = Java.use('com.datatheorem.android.trustkit.pinning.OkHostnameVerifier');
      trustkit_Activity_2.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function(a, b) {
        console.log('[+] Bypassing Trustkit {2}: ' + a);
        return true;
      };
    } catch (err) {
      console.log('[-] Trustkit {2} pinner not found');
      //console.log(err);
      errDict[err] = ['com.datatheorem.android.trustkit.pinning.OkHostnameVerifier', 'verify'];
    }
    try {
      // Bypass Trustkit {3}
      var trustkit_PinningTrustManager = Java.use('com.datatheorem.android.trustkit.pinning.PinningTrustManager');
      trustkit_PinningTrustManager.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String').implementation = function(chain, authType) {
        console.log('[+] Bypassing Trustkit {3}');
      };
    } catch (err) {
      console.log('[-] Trustkit {3} pinner not found');
      //console.log(err);
      errDict[err] = ['com.datatheorem.android.trustkit.pinning.PinningTrustManager', 'checkServerTrusted'];
    }




    // TrustManagerImpl (Android > 7) //
    ////////////////////////////////////
    try {
      // Bypass TrustManagerImpl (Android > 7) {1}
      var array_list = Java.use("java.util.ArrayList");
      var TrustManagerImpl_Activity_1 = Java.use('com.android.org.conscrypt.TrustManagerImpl');
      TrustManagerImpl_Activity_1.checkTrustedRecursive.implementation = function(certs, ocspData, tlsSctData, host, clientAuth, untrustedChain, trustAnchorChain, used) {
        console.log('[+] Bypassing TrustManagerImpl (Android > 7) checkTrustedRecursive check for: '+ host);
        return array_list.new();
      };
    } catch (err) {
      console.log('[-] TrustManagerImpl (Android>7) checkTrustedRecursive check not found');
      //console.log(err);
      errDict[err] = ['com.android.org.conscrypt.TrustManagerImpl', 'checkTrustedRecursive'];
    }
    try {
      // Bypass TrustManagerImpl (Android>7) {2} (probably no more necessary)
      var TrustManagerImpl_Activity_2 = Java.use('com.android.org.conscrypt.TrustManagerImpl');
      TrustManagerImpl_Activity_2.verifyChain.implementation = function(untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
        console.log('[+] Bypassing TrustManagerImpl (Android>7) verifyChain check for: ' + host);
        return untrustedChain;
      };
    } catch (err) {
      console.log('[-] TrustManagerImpl (Android>7) verifyChain check not found');
      //console.log(err);
      errDict[err] = ['com.android.org.conscrypt.TrustManagerImpl', 'verifyChain'];
    }





    // Appcelerator Titanium PinningTrustManager //
    ///////////////////////////////////////////////
    try {
      var appcelerator_PinningTrustManager = Java.use('appcelerator.https.PinningTrustManager');
      appcelerator_PinningTrustManager.checkServerTrusted.implementation = function(chain, authType) {
        console.log('[+] Bypassing Appcelerator PinningTrustManager');
        return;
      };
    } catch (err) {
      console.log('[-] Appcelerator PinningTrustManager pinner not found');
      //console.log(err);
      errDict[err] = ['appcelerator.https.PinningTrustManager', 'checkServerTrusted'];
    }




    // Fabric PinningTrustManager //
    ////////////////////////////////
    try {
      var fabric_PinningTrustManager = Java.use('io.fabric.sdk.android.services.network.PinningTrustManager');
      fabric_PinningTrustManager.checkServerTrusted.implementation = function(chain, authType) {
        console.log('[+] Bypassing Fabric PinningTrustManager');
        return;
      };
    } catch (err) {
      console.log('[-] Fabric PinningTrustManager pinner not found');
      //console.log(err);
      errDict[err] = ['io.fabric.sdk.android.services.network.PinningTrustManager', 'checkServerTrusted'];
    }




    // OpenSSLSocketImpl Conscrypt (double bypass) //
    /////////////////////////////////////////////////
    try {
      var OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl');
      OpenSSLSocketImpl.verifyCertificateChain.implementation = function(certRefs, JavaObject, authMethod) {
        console.log('[+] Bypassing OpenSSLSocketImpl Conscrypt {1}');
      };
    } catch (err) {
      console.log('[-] OpenSSLSocketImpl Conscrypt {1} pinner not found');
      //console.log(err);
      errDict[err] = ['com.android.org.conscrypt.OpenSSLSocketImpl', 'verifyCertificateChain'];
    }
    try {
      var OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl');
      OpenSSLSocketImpl.verifyCertificateChain.implementation = function(certChain, authMethod) {
        console.log('[+] Bypassing OpenSSLSocketImpl Conscrypt {2}');
      };
    } catch (err) {
      console.log('[-] OpenSSLSocketImpl Conscrypt {2} pinner not found');
      //console.log(err);
      errDict[err] = ['com.android.org.conscrypt.OpenSSLSocketImpl', 'verifyCertificateChain'];
    }




    // OpenSSLEngineSocketImpl Conscrypt //
    ///////////////////////////////////////
    try {
      var OpenSSLEngineSocketImpl_Activity = Java.use('com.android.org.conscrypt.OpenSSLEngineSocketImpl');
      OpenSSLEngineSocketImpl_Activity.verifyCertificateChain.overload('[Ljava.lang.Long;', 'java.lang.String').implementation = function(a, b) {
        console.log('[+] Bypassing OpenSSLEngineSocketImpl Conscrypt: ' + b);
      };
    } catch (err) {
      console.log('[-] OpenSSLEngineSocketImpl Conscrypt pinner not found');
      //console.log(err);
      errDict[err] = ['com.android.org.conscrypt.OpenSSLEngineSocketImpl', 'verifyCertificateChain'];
    }




    // OpenSSLSocketImpl Apache Harmony //
    //////////////////////////////////////
    try {
      var OpenSSLSocketImpl_Harmony = Java.use('org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl');
      OpenSSLSocketImpl_Harmony.verifyCertificateChain.implementation = function(asn1DerEncodedCertificateChain, authMethod) {
        console.log('[+] Bypassing OpenSSLSocketImpl Apache Harmony');
      };
    } catch (err) {
      console.log('[-] OpenSSLSocketImpl Apache Harmony pinner not found');
      //console.log(err);
      errDict[err] = ['org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl', 'verifyCertificateChain'];
    }




    // PhoneGap sslCertificateChecker //
    ////////////////////////////////////
    try {
      var phonegap_Activity = Java.use('nl.xservices.plugins.sslCertificateChecker');
      phonegap_Activity.execute.overload('java.lang.String', 'org.json.JSONArray', 'org.apache.cordova.CallbackContext').implementation = function(a, b, c) {
        console.log('[+] Bypassing PhoneGap sslCertificateChecker: ' + a);
        return true;
      };
    } catch (err) {
      console.log('[-] PhoneGap sslCertificateChecker pinner not found');
      //console.log(err);
      errDict[err] = ['nl.xservices.plugins.sslCertificateChecker', 'execute'];
    }




    // IBM MobileFirst pinTrustedCertificatePublicKey (double bypass) //
    ////////////////////////////////////////////////////////////////////
    try {
      // Bypass IBM MobileFirst {1}
      var WLClient_Activity_1 = Java.use('com.worklight.wlclient.api.WLClient');
      WLClient_Activity_1.getInstance().pinTrustedCertificatePublicKey.overload('java.lang.String').implementation = function(cert) {
        console.log('[+] Bypassing IBM MobileFirst pinTrustedCertificatePublicKey {1}: ' + cert);
        return;
      };
      } catch (err) {
      console.log('[-] IBM MobileFirst pinTrustedCertificatePublicKey {1} pinner not found');
      //console.log(err);
      errDict[err] = ['com.worklight.wlclient.api.WLClient', 'pinTrustedCertificatePublicKey'];
    }
    try {
      // Bypass IBM MobileFirst {2}
      var WLClient_Activity_2 = Java.use('com.worklight.wlclient.api.WLClient');
      WLClient_Activity_2.getInstance().pinTrustedCertificatePublicKey.overload('[Ljava.lang.String;').implementation = function(cert) {
        console.log('[+] Bypassing IBM MobileFirst pinTrustedCertificatePublicKey {2}: ' + cert);
        return;
      };
    } catch (err) {
      console.log('[-] IBM MobileFirst pinTrustedCertificatePublicKey {2} pinner not found');
      //console.log(err);
      errDict[err] = ['com.worklight.wlclient.api.WLClient', 'pinTrustedCertificatePublicKey'];
    }



    // IBM WorkLight (ancestor of MobileFirst) HostNameVerifierWithCertificatePinning (quadruple bypass) //
    ///////////////////////////////////////////////////////////////////////////////////////////////////////
    try {
      // Bypass IBM WorkLight {1}
      var worklight_Activity_1 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning');
      worklight_Activity_1.verify.overload('java.lang.String', 'javax.net.ssl.SSLSocket').implementation = function(a, b) {
        console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {1}: ' + a);
        return;
      };
    } catch (err) {
      console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {1} pinner not found');
      //console.log(err);
      errDict[err] = ['com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning', 'verify'];
    }
    try {
      // Bypass IBM WorkLight {2}
      var worklight_Activity_2 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning');
      worklight_Activity_2.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function(a, b) {
        console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {2}: ' + a);
        return;
      };
    } catch (err) {
      console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {2} pinner not found');
      //console.log(err);
      errDict[err] = ['com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning', 'verify'];
    }
    try {
      // Bypass IBM WorkLight {3}
      var worklight_Activity_3 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning');
      worklight_Activity_3.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;').implementation = function(a, b) {
        console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {3}: ' + a);
        return;
      };
    } catch (err) {
      console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {3} pinner not found');
      //console.log(err);
      errDict[err] = ['com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning', 'verify'];
    }
    try {
      // Bypass IBM WorkLight {4}
      var worklight_Activity_4 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning');
      worklight_Activity_4.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function(a, b) {
        console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {4}: ' + a);
        return true;
      };
    } catch (err) {
      console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {4} pinner not found');
      //console.log(err);
      errDict[err] = ['com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning', 'verify'];
    }




    // Conscrypt CertPinManager //
    //////////////////////////////
    try {
      var conscrypt_CertPinManager_Activity = Java.use('com.android.org.conscrypt.CertPinManager');
      conscrypt_CertPinManager_Activity.checkChainPinning.overload('java.lang.String', 'java.util.List').implementation = function(a, b) {
        console.log('[+] Bypassing Conscrypt CertPinManager: ' + a);
        return;
      };
    } catch (err) {
      console.log('[-] Conscrypt CertPinManager pinner not found');
      //console.log(err);
      errDict[err] = ['com.android.org.conscrypt.CertPinManager', 'checkChainPinning'];
    }




    // Conscrypt CertPinManager (Legacy) //
    ///////////////////////////////////////
    try {
      var legacy_conscrypt_CertPinManager_Activity = Java.use('com.android.org.conscrypt.CertPinManager');
      legacy_conscrypt_CertPinManager_Activity.isChainValid.overload('java.lang.String', 'java.util.List').implementation = function(a, b) {
        console.log('[+] Bypassing Conscrypt CertPinManager (Legacy): ' + a);
        return true;
      };
    } catch (err) {
      console.log('[-] Conscrypt CertPinManager (Legacy) pinner not found');
      //console.log(err);
      errDict[err] = ['com.android.org.conscrypt.CertPinManager', 'isChainValid'];
    }




    // CWAC-Netsecurity (unofficial back-port pinner for Android<4.2) CertPinManager //
    ///////////////////////////////////////////////////////////////////////////////////
    try {
      var cwac_CertPinManager_Activity = Java.use('com.commonsware.cwac.netsecurity.conscrypt.CertPinManager');
      cwac_CertPinManager_Activity.isChainValid.overload('java.lang.String', 'java.util.List').implementation = function(a, b) {
        console.log('[+] Bypassing CWAC-Netsecurity CertPinManager: ' + a);
        return true;
      };
    } catch (err) {
      console.log('[-] CWAC-Netsecurity CertPinManager pinner not found');
      //console.log(err);
      errDict[err] = ['com.commonsware.cwac.netsecurity.conscrypt.CertPinManager', 'isChainValid'];
    }




    // Worklight Androidgap WLCertificatePinningPlugin //
    /////////////////////////////////////////////////////
    try {
      var androidgap_WLCertificatePinningPlugin_Activity = Java.use('com.worklight.androidgap.plugin.WLCertificatePinningPlugin');
      androidgap_WLCertificatePinningPlugin_Activity.execute.overload('java.lang.String', 'org.json.JSONArray', 'org.apache.cordova.CallbackContext').implementation = function(a, b, c) {
        console.log('[+] Bypassing Worklight Androidgap WLCertificatePinningPlugin: ' + a);
        return true;
      };
    } catch (err) {
      console.log('[-] Worklight Androidgap WLCertificatePinningPlugin pinner not found');
      //console.log(err);
      errDict[err] = ['com.worklight.androidgap.plugin.WLCertificatePinningPlugin', 'execute'];
    }




    // Netty FingerprintTrustManagerFactory //
    //////////////////////////////////////////
    try {
      var netty_FingerprintTrustManagerFactory = Java.use('io.netty.handler.ssl.util.FingerprintTrustManagerFactory');
      //NOTE: sometimes this below implementation could be useful
      //var netty_FingerprintTrustManagerFactory = Java.use('org.jboss.netty.handler.ssl.util.FingerprintTrustManagerFactory');
      netty_FingerprintTrustManagerFactory.checkTrusted.implementation = function(type, chain) {
        console.log('[+] Bypassing Netty FingerprintTrustManagerFactory');
      };
    } catch (err) {
      console.log('[-] Netty FingerprintTrustManagerFactory pinner not found');
      //console.log(err);
      errDict[err] = ['io.netty.handler.ssl.util.FingerprintTrustManagerFactory', 'checkTrusted'];
    }




    // Squareup CertificatePinner [OkHTTP<v3] (double bypass) //
    ////////////////////////////////////////////////////////////
    try {
      // Bypass Squareup CertificatePinner  {1}
      var Squareup_CertificatePinner_Activity_1 = Java.use('com.squareup.okhttp.CertificatePinner');
      Squareup_CertificatePinner_Activity_1.check.overload('java.lang.String', 'java.security.cert.Certificate').implementation = function(a, b) {
        console.log('[+] Bypassing Squareup CertificatePinner {1}: ' + a);
        return;
      };
    } catch (err) {
      console.log('[-] Squareup CertificatePinner {1} pinner not found');
      //console.log(err);
      errDict[err] = ['com.squareup.okhttp.CertificatePinner', 'check'];
    }
    try {
      // Bypass Squareup CertificatePinner {2}
      var Squareup_CertificatePinner_Activity_2 = Java.use('com.squareup.okhttp.CertificatePinner');
      Squareup_CertificatePinner_Activity_2.check.overload('java.lang.String', 'java.util.List').implementation = function(a, b) {
        console.log('[+] Bypassing Squareup CertificatePinner {2}: ' + a);
        return;
      };
    } catch (err) {
      console.log('[-] Squareup CertificatePinner {2} pinner not found');
      //console.log(err);
      errDict[err] = ['com.squareup.okhttp.CertificatePinner', 'check'];
    }




    // Squareup OkHostnameVerifier [OkHTTP v3] (double bypass) //
    /////////////////////////////////////////////////////////////
    try {
      // Bypass Squareup OkHostnameVerifier {1}
      var Squareup_OkHostnameVerifier_Activity_1 = Java.use('com.squareup.okhttp.internal.tls.OkHostnameVerifier');
      Squareup_OkHostnameVerifier_Activity_1.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function(a, b) {
        console.log('[+] Bypassing Squareup OkHostnameVerifier {1}: ' + a);
        return true;
      };
    } catch (err) {
      console.log('[-] Squareup OkHostnameVerifier check not found');
      //console.log(err);
      errDict[err] = ['com.squareup.okhttp.internal.tls.OkHostnameVerifier', 'verify'];
    }
    try {
      // Bypass Squareup OkHostnameVerifier {2}
      var Squareup_OkHostnameVerifier_Activity_2 = Java.use('com.squareup.okhttp.internal.tls.OkHostnameVerifier');
      Squareup_OkHostnameVerifier_Activity_2.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function(a, b) {
        console.log('[+] Bypassing Squareup OkHostnameVerifier {2}: ' + a);
        return true;
      };
    } catch (err) {
      console.log('[-] Squareup OkHostnameVerifier check not found');
      //console.log(err);
      errDict[err] = ['com.squareup.okhttp.internal.tls.OkHostnameVerifier', 'verify'];
    }




    // Android WebViewClient (quadruple bypass) //
    //////////////////////////////////////////////
    try {
      // Bypass WebViewClient {1} (deprecated from Android 6)
      var AndroidWebViewClient_Activity_1 = Java.use('android.webkit.WebViewClient');
      AndroidWebViewClient_Activity_1.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.SslErrorHandler', 'android.net.http.SslError').implementation = function(obj1, obj2, obj3) {
        console.log('[+] Bypassing Android WebViewClient check {1}');
      };
    } catch (err) {
      console.log('[-] Android WebViewClient {1} check not found');
      //console.log(err)
      errDict[err] = ['android.webkit.WebViewClient', 'onReceivedSslError'];
    }
    // Not working properly temporarily disused
    //try {
    //  // Bypass WebViewClient {2}
    //  var AndroidWebViewClient_Activity_2 = Java.use('android.webkit.WebViewClient');
    //  AndroidWebViewClient_Activity_2.onReceivedHttpError.overload('android.webkit.WebView', 'android.webkit.WebResourceRequest', 'android.webkit.WebResourceResponse').implementation = function(obj1, obj2, obj3) {
    //    console.log('[+] Bypassing Android WebViewClient check {2}');
    //  };
    //} catch (err) {
    //  console.log('[-] Android WebViewClient {2} check not found');
    //  //console.log(err)
    //  errDict[err] = ['android.webkit.WebViewClient', 'onReceivedHttpError'];
    //}
    try {
      // Bypass WebViewClient {3}
      var AndroidWebViewClient_Activity_3 = Java.use('android.webkit.WebViewClient');
      //AndroidWebViewClient_Activity_3.onReceivedError.overload('android.webkit.WebView', 'int', 'java.lang.String', 'java.lang.String').implementation = function(obj1, obj2, obj3, obj4) {
      AndroidWebViewClient_Activity_3.onReceivedError.implementation = function(view, errCode, description, failingUrl) {
        console.log('[+] Bypassing Android WebViewClient check {3}');
      };
    } catch (err) {
      console.log('[-] Android WebViewClient {3} check not found');
      //console.log(err)
      errDict[err] = ['android.webkit.WebViewClient', 'onReceivedError'];
    }
    try {
      // Bypass WebViewClient {4}
      var AndroidWebViewClient_Activity_4 = Java.use('android.webkit.WebViewClient');
      AndroidWebViewClient_Activity_4.onReceivedError.overload('android.webkit.WebView', 'android.webkit.WebResourceRequest', 'android.webkit.WebResourceError').implementation = function(obj1, obj2, obj3) {
        console.log('[+] Bypassing Android WebViewClient check {4}');
      };
    } catch (err) {
      console.log('[-] Android WebViewClient {4} check not found');
      //console.log(err)
      errDict[err] = ['android.webkit.WebViewClient', 'onReceivedError'];
    }




    // Apache Cordova WebViewClient //
    //////////////////////////////////
    try {
      var CordovaWebViewClient_Activity = Java.use('org.apache.cordova.CordovaWebViewClient');
      CordovaWebViewClient_Activity.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.SslErrorHandler', 'android.net.http.SslError').implementation = function(obj1, obj2, obj3) {
        console.log('[+] Bypassing Apache Cordova WebViewClient check');
        obj3.proceed();
      };
    } catch (err) {
      console.log('[-] Apache Cordova WebViewClient check not found');
      //console.log(err);
    }




    // Boye AbstractVerifier //
    ///////////////////////////
    try {
      var boye_AbstractVerifier = Java.use('ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier');
      boye_AbstractVerifier.verify.implementation = function(host, ssl) {
        console.log('[+] Bypassing Boye AbstractVerifier check for: ' + host);
      };
    } catch (err) {
      console.log('[-] Boye AbstractVerifier check not found');
      //console.log(err);
      errDict[err] = ['ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier', 'verify'];
    }



    // Apache AbstractVerifier (quadruple bypass) //
    ////////////////////////////////////////////////
    try {
      var apache_AbstractVerifier_1 = Java.use('org.apache.http.conn.ssl.AbstractVerifier');
      apache_AbstractVerifier_1.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function(a, b) {
        console.log('[+] Bypassing Apache AbstractVerifier {1} check for: ' + a);
        return;
      };
    } catch (err) {
      console.log('[-] Apache AbstractVerifier {1} check not found');
      //console.log(err);
      errDict[err] = ['org.apache.http.conn.ssl.AbstractVerifier', 'verify'];
    }
        try {
      var apache_AbstractVerifier_2 = Java.use('org.apache.http.conn.ssl.AbstractVerifier');
      apache_AbstractVerifier_2.verify.overload('java.lang.String', 'javax.net.ssl.SSLSocket').implementation = function(a, b) {
        console.log('[+] Bypassing Apache AbstractVerifier {2} check for: ' + a);
        return;
      };
    } catch (err) {
      console.log('[-] Apache AbstractVerifier {2} check not found');
      //console.log(err);
      errDict[err] = ['org.apache.http.conn.ssl.AbstractVerifier', 'verify'];
    }
        try {
      var apache_AbstractVerifier_3 = Java.use('org.apache.http.conn.ssl.AbstractVerifier');
      apache_AbstractVerifier_3.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function(a, b) {
        console.log('[+] Bypassing Apache AbstractVerifier {3} check for: ' + a);
        return;
      };
    } catch (err) {
      console.log('[-] Apache AbstractVerifier {3} check not found');
      //console.log(err);
      errDict[err] = ['org.apache.http.conn.ssl.AbstractVerifier', 'verify'];
    }
        try {
      var apache_AbstractVerifier_4 = Java.use('org.apache.http.conn.ssl.AbstractVerifier');
      apache_AbstractVerifier_4.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean').implementation = function(a, b, c, d) {
        console.log('[+] Bypassing Apache AbstractVerifier {4} check for: ' + a);
        return;
      };
    } catch (err) {
      console.log('[-] Apache AbstractVerifier {4} check not found');
      //console.log(err);
      errDict[err] = ['org.apache.http.conn.ssl.AbstractVerifier', 'verify'];
    }

   // Chromium Cronet //
    /////////////////////
    try {
      var CronetEngineBuilderImpl_Activity = Java.use("org.chromium.net.impl.CronetEngineBuilderImpl");
      // Setting argument to TRUE (default is TRUE) to disable Public Key pinning for local trust anchors
      CronetEngine_Activity.enablePublicKeyPinningBypassForLocalTrustAnchors.overload('boolean').implementation = function(a) {
        console.log("[+] Disabling Public Key pinning for local trust anchors in Chromium Cronet");
        var cronet_obj_1 = CronetEngine_Activity.enablePublicKeyPinningBypassForLocalTrustAnchors.call(this, true);
        return cronet_obj_1;
      };
      // Bypassing Chromium Cronet pinner
      CronetEngine_Activity.addPublicKeyPins.overload('java.lang.String', 'java.util.Set', 'boolean', 'java.util.Date').implementation = function(hostName, pinsSha256, includeSubdomains, expirationDate) {
        console.log("[+] Bypassing Chromium Cronet pinner: " + hostName);
        var cronet_obj_2 = CronetEngine_Activity.addPublicKeyPins.call(this, hostName, pinsSha256, includeSubdomains, expirationDate);
        return cronet_obj_2;
      };
    } catch (err) {
      console.log('[-] Chromium Cronet pinner not found')
      //console.log(err);
    }




    // Flutter Pinning packages http_certificate_pinning and ssl_pinning_plugin (double bypass) //
    //////////////////////////////////////////////////////////////////////////////////////////////
    try {
      // Bypass HttpCertificatePinning.check {1}
      var HttpCertificatePinning_Activity = Java.use('diefferson.http_certificate_pinning.HttpCertificatePinning');
      HttpCertificatePinning_Activity.checkConnexion.overload("java.lang.String", "java.util.List", "java.util.Map", "int", "java.lang.String").implementation = function (a, b, c ,d, e) {
        console.log('[+] Bypassing Flutter HttpCertificatePinning : ' + a);
        return true;
      };
    } catch (err) {
      console.log('[-] Flutter HttpCertificatePinning pinner not found');
      //console.log(err);
      errDict[err] = ['diefferson.http_certificate_pinning.HttpCertificatePinning', 'checkConnexion'];
    }
    try {
      // Bypass SslPinningPlugin.check {2}
      var SslPinningPlugin_Activity = Java.use('com.macif.plugin.sslpinningplugin.SslPinningPlugin');
      SslPinningPlugin_Activity.checkConnexion.overload("java.lang.String", "java.util.List", "java.util.Map", "int", "java.lang.String").implementation = function (a, b, c ,d, e) {
        console.log('[+] Bypassing Flutter SslPinningPlugin: ' + a);
        return true;
      };
    } catch (err) {
      console.log('[-] Flutter SslPinningPlugin pinner not found');
      //console.log(err);
      errDict[err] = ['com.macif.plugin.sslpinningplugin.SslPinningPlugin', 'checkConnexion'];
    }




    // Unusual/obfuscated pinners bypass //
    ///////////////////////////////////////
    try {
      // Iterating all caught pinner errors and try to overload them
      for (var key in errDict) {
        var errStr = key;
        var targetClass = errDict[key][0]
        var targetFunc = errDict[key][1]
        var retType = Java.use(targetClass)[targetFunc].returnType.type;
        //console.log("errDict content: "+errStr+" "+targetClass+"."+targetFunc);
        if (String(errStr).includes('.overload')) {
          overloader(errStr, targetClass, targetFunc,retType);
        }
      }
    } catch (err) {
      //console.log('[-] The pinner "'+targetClass+'.'+targetFunc+'" is not unusual/obfuscated, skipping it..');
      //console.log(err);
    }




    // Dynamic SSLPeerUnverifiedException Bypasser                               //
    // An useful technique to bypass SSLPeerUnverifiedException failures raising //
    // when the Android app uses some uncommon SSL Pinning methods or an heavily //
    // code obfuscation. Inspired by an idea of: https://github.com/httptoolkit  //
    ///////////////////////////////////////////////////////////////////////////////
    try {
      var UnverifiedCertError = Java.use('javax.net.ssl.SSLPeerUnverifiedException');
      UnverifiedCertError.init.implementation = function (reason) {
        try {
          var stackTrace = Java.use('java.lang.Thread').currentThread().getStackTrace();
          var exceptionStackIndex = stackTrace.findIndex(stack =>
            stack.getClassName() === "javax.net.ssl.SSLPeerUnverifiedException"
          );
          // Retrieve the method raising the SSLPeerUnverifiedException
          var callingFunctionStack = stackTrace[exceptionStackIndex + 1];
          var className = callingFunctionStack.getClassName();
          var methodName = callingFunctionStack.getMethodName();
          var callingClass = Java.use(className);
          var callingMethod = callingClass[methodName];
          console.log('\x1b[36m[!] Unexpected SSLPeerUnverifiedException occurred related to the method "'+className+'.'+methodName+'"\x1b[0m');
          //console.log("Stacktrace details:\n"+stackTrace);
          // Checking if the SSLPeerUnverifiedException was generated by an usually negligible (not blocking) method
          if (className == 'com.android.org.conscrypt.ActiveSession' || className == 'com.google.android.gms.org.conscrypt.ActiveSession') {
            throw 'Reason: skipped SSLPeerUnverifiedException bypass since the exception was raised from a (usually) non blocking method on the Android app';
          }
          else {
            console.log('\x1b[34m[!] Starting to dynamically circumvent the SSLPeerUnverifiedException for the method "'+className+'.'+methodName+'"...\x1b[0m');
            var retTypeName = callingMethod.returnType.type;
            // Skip it when the calling method was already bypassed with Frida
            if (!(callingMethod.implementation)) {
              // Trying to bypass (via implementation) the SSLPeerUnverifiedException if due to an uncommon SSL Pinning method
              callingMethod.implementation = function() {
                console.log('\x1b[34m[+] Bypassing the unusual/obfuscated pinner "'+className+'.'+methodName+'" via Frida function implementation\x1b[0m');
                returner(retTypeName);
              }
            }
          }
        } catch (err2) {
          // Dynamic circumvention via function implementation does not works, then trying via function overloading
          if (String(err2).includes('.overload')) {
            overloader(err2, className, methodName, retTypeName);
          } else {
            if (String(err2).includes('SSLPeerUnverifiedException')) {
              console.log('\x1b[36m[-] Failed to dynamically circumvent SSLPeerUnverifiedException -> '+err2+'\x1b[0m');
            } else {
              //console.log('\x1b[36m[-] Another kind of exception raised during overloading  -> '+err2+'\x1b[0m');
            }
          }
        }
        //console.log('\x1b[36m[+] SSLPeerUnverifiedException hooked\x1b[0m');
        return this.$init(reason);
      };
    } catch (err1) {
      //console.log('\x1b[36m[-] SSLPeerUnverifiedException not found\x1b[0m');
      //console.log('\x1b[36m'+err1+'\x1b[0m');
    }


  });

}, 0);




function returner(typeName) {
  // This is a improvable rudimentary fix, if not works you can patch it manually
  //console.log("typeName: "+typeName)
  if (typeName === undefined || typeName === 'void') {
    return;
  } else if (typeName === 'boolean') {
    return true;
  } else {
    return null;
  }
}


function overloader(errStr, targetClass, targetFunc, retType) {
  // One ring to overload them all.. ;-)
  var tClass = Java.use(targetClass);
  var tFunc = tClass[targetFunc];
  var params = [];
  var argList = [];
  var overloads = tFunc.overloads;
  var returnTypeName = retType;
  var splittedList = String(errStr).split('.overload');
  for (var n=1; n<splittedList.length; n++) {
    var extractedOverload = splittedList[n].trim().split('(')[1].slice(0,-1).replaceAll("'","");
    // Discarding useless error strings
    if (extractedOverload.includes('<signature>')) {
      continue;
    }
    console.log('\x1b[34m[!] Found the unusual/obfuscated pinner "'+targetClass+'.'+targetFunc+'('+extractedOverload+')"\x1b[0m');
    // Check if extractedOverload is empty
    if (!extractedOverload) {
      // Overloading method withouth arguments
      tFunc.overload().implementation = function() {
        var printStr = printer();
        console.log('\x1b[34m[+] Bypassing the unusual/obfuscated pinner "'+targetClass+'.'+targetFunc+'('+extractedOverload+')"'+printStr+'\x1b[0m');
        returner(returnTypeName);
      }
    } else {
      // Check if extractedOverload has multiple arguments
      if (extractedOverload.includes(',')) {
        argList = extractedOverload.split(', ');
      }
      // Considering max 8 arguments for the method to overload (Note: increase it, if needed)
      if (argList.length == 0) {
        tFunc.overload(extractedOverload).implementation = function(a) {
          var printStr = printer();
          console.log('\x1b[34m[+] Bypassing the unusual/obfuscated pinner "'+targetClass+'.'+targetFunc+'('+extractedOverload+')"'+printStr+'\x1b[0m');
          returner(returnTypeName);
        }
      } else if (argList.length == 2) {
        tFunc.overload(argList[0], argList[1]).implementation = function(a,b) {
          var printStr = printer(a);
          console.log('\x1b[34m[+] Bypassing the unusual/obfuscated pinner "'+targetClass+'.'+targetFunc+'('+extractedOverload+')"'+printStr+'\x1b[0m');
          returner(returnTypeName);
        }
      } else if (argList.length == 3) {
        tFunc.overload(argList[0], argList[1], argList[2]).implementation = function(a,b,c) {
          var printStr = printer(a,b);
          console.log('\x1b[34m[+] Bypassing the unusual/obfuscated pinner "'+targetClass+'.'+targetFunc+'('+extractedOverload+')"'+printStr+'\x1b[0m');
          returner(returnTypeName);
        }
      } else if (argList.length == 4) {
        tFunc.overload(argList[0], argList[1], argList[2], argList[3]).implementation = function(a,b,c,d) {
          var printStr = printer(a,b,c);
          console.log('\x1b[34m[+] Bypassing the unusual/obfuscated pinner "'+targetClass+'.'+targetFunc+'('+extractedOverload+')"'+printStr+'\x1b[0m');
          returner(returnTypeName);
        }
      }  else if (argList.length == 5) {
        tFunc.overload(argList[0], argList[1], argList[2], argList[3], argList[4]).implementation = function(a,b,c,d,e) {
          var printStr = printer(a,b,c,d);
          console.log('\x1b[34m[+] Bypassing the unusual/obfuscated pinner "'+targetClass+'.'+targetFunc+'('+extractedOverload+')"'+printStr+'\x1b[0m');
          returner(returnTypeName);
        }
      }  else if (argList.length == 6) {
        tFunc.overload(argList[0], argList[1], argList[2], argList[3], argList[4], argList[5]).implementation = function(a,b,c,d,e,f) {
          var printStr = printer(a,b,c,d,e);
          console.log('\x1b[34m[+] Bypassing the unusual/obfuscated pinner "'+targetClass+'.'+targetFunc+'('+extractedOverload+')"'+printStr+'\x1b[0m');
          returner(returnTypeName);
        }
      }  else if (argList.length == 7) {
        tFunc.overload(argList[0], argList[1], argList[2], argList[3], argList[4], argList[5], argList[6]).implementation = function(a,b,c,d,e,f,g) {
          var printStr = printer(a,b,c,d,e,f);
          console.log('\x1b[34m[+] Bypassing the unusual/obfuscated pinner "'+targetClass+'.'+targetFunc+'('+extractedOverload+')"'+printStr+'\x1b[0m');
          returner(returnTypeName);
        }
      }  else if (argList.length == 8) {
        tFunc.overload(argList[0], argList[1], argList[2], argList[3], argList[4], argList[5], argList[6], argList[7]).implementation = function(a,b,c,d,e,f,g,h) {
          var printStr = printer(a,b,c,d,e,f,g);
          console.log('\x1b[34m[+] Bypassing the unusual/obfuscated pinner "'+targetClass+'.'+targetFunc+'('+extractedOverload+')"'+printStr+'\x1b[0m');
          returner(returnTypeName);
        }
      }

    }

  }
}


function printer(a,b,c,d,e,f,g,h) {
  // Build the string to print for the overloaded pinner
  var printList = [];
  var printStr = '';
  if (typeof a === 'string') {
    printList.push(a);
  }
  if (typeof b === 'string') {
    printList.push(b);
  }
  if (typeof c === 'string') {
    printList.push(c);
  }
  if (typeof d === 'string') {
    printList.push(d);
  }
  if (typeof e === 'string') {
    printList.push(e);
  }
  if (typeof f === 'string') {
    printList.push(f);
  }
  if (typeof g === 'string') {
    printList.push(g);
  }
  if (typeof h === 'string') {
    printList.push(h);
  }
  if (printList.length !== 0) {
    printStr = ' check for:';
    for (var i=0; i<printList.length; i++) {
      printStr += ' '+printList[i];
    }
  }
  return printStr;
}

hook exit kill异常退出

const STD_STRING_SIZE = 3 * Process.pointerSize;
class StdString {
    constructor() {
        this.handle = Memory.alloc(STD_STRING_SIZE);
    }

    dispose() {
        const [data, isTiny] = this._getData();
        if (!isTiny) {
            Java.api.$delete(data);
        }
    }

    disposeToString() {
        const result = this.toString();
        this.dispose();
        return result;
    }

    toString() {
        const [data] = this._getData();
        return data.readUtf8String();
    }

    _getData() {
        const str = this.handle;
        const isTiny = (str.readU8() & 1) === 0;
        const data = isTiny ? str.add(1) : str.add(2 * Process.pointerSize).readPointer();
        return [data, isTiny];
    }
}

function prettyMethod(method_id, withSignature) {
    const result = new StdString();
    Java.api['art::ArtMethod::PrettyMethod'](result, method_id, withSignature ? 1 : 0);
    return result.disposeToString();
}

function hook_libc_exit() {
    var exit = Module.findExportByName("libc.so", "exit");
    console.log("native:" + exit);

    Interceptor.attach(exit, {
        onEnter: function (args) {
            console.log(Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join("\n"));
        },
        onLeave: function (retval) {
            //send("gifcore so result value: "+retval);
        }
    });
}

function anti_exit() {
    const exit_ptr = Module.findExportByName(null, '_exit');
    DMLog.i('anti_exit', "exit_ptr : " + exit_ptr);
    if (null == exit_ptr) {
        return;
    }
    Interceptor.replace(exit_ptr, new NativeCallback(function (code) {
        if (null == this) {
            return 0;
        }
        var lr = FCCommon.getLR(this.context);
        DMLog.i('exit debug', 'entry, lr: ' + lr);
        return 0;
    }, 'int', ['int', 'int']));
}

function anti_kill() {
    const kill_ptr = Module.findExportByName(null, 'kill');
    DMLog.i('anti_kill', "kill_ptr : " + kill_ptr);

    if (null == kill_ptr) {
        return;
    }
    Interceptor.replace(kill_ptr, new NativeCallback(function (ptid, code) {
        if (null == this) {
            return 0;
        }
        var lr = FCCommon.getLR(this.context);
        DMLog.i('kill debug', 'entry, lr: ' + lr);
        FCAnd.showNativeStacks(this.context);
        return 0;
    }, 'int', ['int', 'int']));
}

常用打印及转换

//工具相关函数
var base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
    base64DecodeChars = new Array((-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), 62, (-1), (-1), (-1), 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, (-1), (-1), (-1), (-1), (-1), (-1), (-1), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, (-1), (-1), (-1), (-1), (-1), (-1), 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, (-1), (-1), (-1), (-1), (-1));

// base64 解码
function stringToBase64(e) {
    var r, a, c, h, o, t;
    for (c = e.length, a = 0, r = ''; a < c;) {
        if (h = 255 & e.charCodeAt(a++), a == c) {
            r += base64EncodeChars.charAt(h >> 2),
                r += base64EncodeChars.charAt((3 & h) << 4),
                r += '==';
            break
        }
        if (o = e.charCodeAt(a++), a == c) {
            r += base64EncodeChars.charAt(h >> 2),
                r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
                r += base64EncodeChars.charAt((15 & o) << 2),
                r += '=';
            break
        }
        t = e.charCodeAt(a++),
            r += base64EncodeChars.charAt(h >> 2),
            r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
            r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6),
            r += base64EncodeChars.charAt(63 & t)
    }
    return r
}

// base64 编码
function base64ToString(e) {
    var r, a, c, h, o, t, d;
    for (t = e.length, o = 0, d = ''; o < t;) {
        do
            r = base64DecodeChars[255 & e.charCodeAt(o++)];
        while (o < t && r == -1);
        if (r == -1)
            break;
        do
            a = base64DecodeChars[255 & e.charCodeAt(o++)];
        while (o < t && a == -1);
        if (a == -1)
            break;
        d += String.fromCharCode(r << 2 | (48 & a) >> 4);
        do {
            if (c = 255 & e.charCodeAt(o++), 61 == c)
                return d;
            c = base64DecodeChars[c]
        } while (o < t && c == -1);
        if (c == -1)
            break;
        d += String.fromCharCode((15 & a) << 4 | (60 & c) >> 2);
        do {
            if (h = 255 & e.charCodeAt(o++), 61 == h)
                return d;
            h = base64DecodeChars[h]
        } while (o < t && h == -1);
        if (h == -1)
            break;
        d += String.fromCharCode((3 & c) << 6 | h)
    }
    return d
}

// hex 字符转 base64
function hexToBase64(str) {
    return base64Encode(String.fromCharCode.apply(null, str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x1 ").replace(/ +/, "").split(" ")));
}

// base64 转 hex
function base64ToHex(str) {
    for (var i = 0, bin = base64Decode(str.replace(/[ \r\n]+$/, "")), hex = []; i < bin.length; ++i) {
        var tmp = bin.charCodeAt(i).toString(16);
        if (tmp.length === 1)
            tmp = "0" + tmp;
        hex[hex.length] = tmp;
    }
    return hex.join("");
}


function hexToBytes(str) {
    var pos = 0;
    var len = str.length;
    if (len % 2 != 0) {
        return null;
    }
    len /= 2;
    var hexA = new Array();
    for (var i = 0; i < len; i++) {
        var s = str.substr(pos, 2);
        var v = parseInt(s, 16);
        hexA.push(v);
        pos += 2;
    }
    return hexA;
}

function bytesToHex(arr) {
    var str = '';
    var k, j;
    for (var i = 0; i < arr.length; i++) {
        k = arr[i];
        j = k;
        if (k < 0) {
            j = k + 256;
        }
        if (j < 16) {
            str += "0";
        }
        str += j.toString(16);
    }
    return str;
}

function stringToHex(str) {
    var val = "";
    for (var i = 0; i < str.length; i++) {
        if (val == "")
            val = str.charCodeAt(i).toString(16);
        else
            val += str.charCodeAt(i).toString(16);
    }
    return val
}

function stringToBytes(str) {
    var ch, st, re = [];
    for (var i = 0; i < str.length; i++) {
        ch = str.charCodeAt(i);
        st = [];
        do {
            st.push(ch & 0xFF);
            ch = ch >> 8;
        }
        while (ch);
        re = re.concat(st.reverse());
    }
    return re;
}

//将byte[]转成String的方法
function bytesToString(arr) {
    var str = '';
    arr = new Uint8Array(arr);
    for (var i in arr) {
        str += String.fromCharCode(arr[i]);
    }
    return str;
}

function bytesToBase64(e) {
    var r, a, c, h, o, t;
    for (c = e.length, a = 0, r = ''; a < c;) {
        if (h = 255 & e[a++], a == c) {
            r += base64EncodeChars.charAt(h >> 2),
                r += base64EncodeChars.charAt((3 & h) << 4),
                r += '==';
            break
        }
        if (o = e[a++], a == c) {
            r += base64EncodeChars.charAt(h >> 2),
                r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
                r += base64EncodeChars.charAt((15 & o) << 2),
                r += '=';
            break
        }
        t = e[a++],
            r += base64EncodeChars.charAt(h >> 2),
            r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
            r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6),
            r += base64EncodeChars.charAt(63 & t)
    }
    return r
}

function base64ToBytes(e) {
    var r, a, c, h, o, t, d;
    for (t = e.length, o = 0, d = []; o < t;) {
        do
            r = base64DecodeChars[255 & e.charCodeAt(o++)];
        while (o < t && r == -1);
        if (r == -1)
            break;
        do
            a = base64DecodeChars[255 & e.charCodeAt(o++)];
        while (o < t && a == -1);
        if (a == -1)
            break;
        d.push(r << 2 | (48 & a) >> 4);
        do {
            if (c = 255 & e.charCodeAt(o++), 61 == c)
                return d;
            c = base64DecodeChars[c]
        } while (o < t && c == -1);
        if (c == -1)
            break;
        d.push((15 & a) << 4 | (60 & c) >> 2);
        do {
            if (h = 255 & e.charCodeAt(o++), 61 == h)
                return d;
            h = base64DecodeChars[h]
        } while (o < t && h == -1);
        if (h == -1)
            break;
        d.push((3 & c) << 6 | h)
    }
    return d
}

点击查看备忘录

分类: app逆向

浙公网安备33011302000604

辽ICP备20003309号