// Ripped from https://github.com/Ch0pin/medusa/ and modified to fit Androguard packets

colorLog('[+] LOADING HELPERS/PINNING/SSL.JS',{c: Color.Red});

// Bypass TrustManager pinning
function bypass_trustmanager_pinning() {
    var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
    var SSLContext = Java.use('javax.net.ssl.SSLContext');
  
    // TrustManager (Android < 7)
    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) {
            agSysPacket({information: "trustmanager pinning", keyManager: keyManager, trustManager: trustManager, secureRandom: secureRandom}).send();
            SSLContext_init.call(this, keyManager, TrustManagers, secureRandom);
        };
        agSysPacket({information: "trustmanager hooked installed"}).send();
    } catch (err) {
        agSysPacket({information: "error: TrustManager (Android < 7) pinner not found"}).send();
    }
}

// OkHTTPv3 (4 bypass)
function bypass_okhttp3_pinning() {
    try {
        var okhttp3_Activity = Java.use('okhttp3.CertificatePinner');
    } catch (err) {
        agSysPacket({information: "error: OkHTTPv3 pinner not found"}).send();
    }

    try {
        okhttp3_Activity["check$okhttp"].implementation = function (str) {
            agSysPacket({information: "Bypassing OkHTTPv3: {1} " + str}).send();
            return;
        };
        agSysPacket({information: "OkHTTPv3: {1} hooked installed"}).send();
    } catch (err) {
        agSysPacket({information: "error: OkHTTPv3: {1} not found"}).send();
    }

    try {
        okhttp3_Activity.check.overload('java.lang.String', 'java.util.List').implementation = function (str) {
            agSysPacket({information: "Bypassing OkHTTPv3 {2}: " + str}).send();
            return;
        };
        agSysPacket({information: "OkHTTPv3: {2} hooked installed"}).send();
    } catch (err) {
        agSysPacket({information: "error: OkHTTPv3: {2} not found"}).send();
    }

    try {
        // This method of CertificatePinner.check could be found in some old Android app
        okhttp3_Activity.check.overload('java.lang.String', 'java.security.cert.Certificate').implementation = function (str) {
            agSysPacket({information: "Bypassing OkHTTPv3 {3}: " + str}).send();
            return;
        };
        agSysPacket({information: "OkHTTPv3: {3} hooked installed"}).send();
    } catch (err) {
        agSysPacket({information: "error: OkHTTPv3: {3} not found"}).send();
    }

    try {
        okhttp3_Activity.check$okhttp.overload('java.lang.String', 'kotlin.jvm.functions.Function0').implementation = function(a, b) {		
                agSysPacket({information: "Bypassing OkHTTPv3 {4}: " + a}).send();
                return;
        };
        agSysPacket({information: "OkHTTPv3: {4} hooked installed"}).send();
    } catch(err) {
        agSysPacket({information: "error: OkHTTPv3: {4} not found"}).send();
    }
}

function bypass_trustkit_pinning() {
    try {
        var trustkit_Activity = Java.use('com.datatheorem.android.trustkit.pinning.OkHostnameVerifier');
        trustkit_Activity.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function (str) {
            agSysPacket({information: "Bypassing Trustkit {1}: " + str}).send();
            return true;
        };
        agSysPacket({information: "Trustkit {1} hooked installed"}).send();

        trustkit_Activity.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (str) {
            agSysPacket({information: "Bypassing Trustkit {2}: " + str}).send();
            return true;
        };
        agSysPacket({information: "Trustkit {2} hooked installed"}).send();

        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) {
            agSysPacket({information: "Bypassing Trustkit {3}: " + chain}).send();
        };
        agSysPacket({information: "Trustkit {3} hooked installed"}).send();

    } catch (err) {
        agSysPacket({information: "error: Trustkit pinner not found"}).send();
    }
}

function bypass_trustmanagerimpl_pinning(){
    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) {
            agSysPacket({information: "Bypassing TrustManagerImpl (Android > 7) checkTrustedRecursive check: "+ host}).send();
            return array_list.$new();
        };
        agSysPacket({information: "TrustManagerImpl checkTrustedRecursive hooked installed"}).send();
    } catch (err) {
        agSysPacket({information: "error: TrustManagerImpl (Android > 7) checkTrustedRecursive check not found"}).send();
    } 

    // TrustManagerImpl (Android > 7)
    try {
        var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
        TrustManagerImpl.verifyChain.implementation = function (untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
            agSysPacket({information: "Bypassing TrustManagerImpl verifyChain (Android > 7): ' + host): "+ host}).send();
            return untrustedChain;
        };
        agSysPacket({information: "TrustManagerImpl verifyChain hooked installed"}).send();
    } catch (err) {
        agSysPacket({information: "error: TrustManagerImpl (Android > 7) verifyChain pinner not found"}).send();
    }
}

function bypass_appcelerator_pinning() {
    try {
        var appcelerator_PinningTrustManager = Java.use('appcelerator.https.PinningTrustManager');
        appcelerator_PinningTrustManager.checkServerTrusted.implementation = function () {
            agSysPacket({information: "Bypassing Appcelerator PinningTrustManager"}).send();
            return;
        };
        agSysPacket({information: "Appcelerator hooked installed"}).send();
    } catch (err) {
        agSysPacket({information: "error: Appcelerator PinningTrustManager pinner not found"}).send();
    }
}

function bypass_conscript_pinning() {
    try {
        var OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl');
        OpenSSLSocketImpl.verifyCertificateChain.implementation = function (certRefs, JavaObject, authMethod) {
            agSysPacket({information: "Bypassing OpenSSLSocketImpl Conscrypt"}).send();
        };
        agSysPacket({information: "OpenSSLSocketImpl hooked installed"}).send();
    } catch (err) {
        agSysPacket({information: "error: OpenSSLSocketImpl Conscrypt pinner not found"}).send();
    }


    // OpenSSLEngineSocketImpl Conscrypt
    try {
        var OpenSSLEngineSocketImpl_Activity = Java.use('com.android.org.conscrypt.OpenSSLEngineSocketImpl');
        OpenSSLSocketImpl_Activity.verifyCertificateChain.overload('[Ljava.lang.Long;', 'java.lang.String').implementation = function (str1, str2) {
            agSysPacket({information: "Bypassing OpenSSLEngineSocketImpl Conscrypt: " + str2}).send();
        };
        agSysPacket({information: "OpenSSLEngineSocketImpl hooked installed"}).send();
    } catch (err) {
        agSysPacket({information: "error: OpenSSLEngineSocketImpl Conscrypt pinner not found"}).send();
    }

    try {
        var conscrypt_CertPinManager_Activity = Java.use('com.android.org.conscrypt.CertPinManager');
        conscrypt_CertPinManager_Activity.isChainValid.overload('java.lang.String', 'java.util.List').implementation = function (str) {
            agSysPacket({information: "Bypassing Conscrypt CertPinManager: " + str}).send();
            return true;
        };
        agSysPacket({information: "Conscrypt hooked installed"}).send();
    } catch (err) {
        agSysPacket({information: "error: Conscrypt CertPinManager pinner not found"}).send();
    }

}

function bypass_apacheharmony_pinning() {
    try {
        var OpenSSLSocketImpl_Harmony = Java.use('org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl');
        OpenSSLSocketImpl_Harmony.verifyCertificateChain.implementation = function (asn1DerEncodedCertificateChain, authMethod) {
            agSysPacket({information: "Bypassing OpenSSLSocketImpl Apache Harmony"}).send();
        };
        agSysPacket({information: "OpenSSLSocketImpl Apache Harmony hooked installed"}).send();
    } catch (err) {
        agSysPacket({information: "error: OpenSSLSocketImpl Apache Harmony pinner not found"}).send();
    }
}

function bypass_phonegapp_pinning() {
    // PhoneGap sslCertificateChecker (https://github.com/EddyVerbruggen/SSLCertificateChecker-PhoneGap-Plugin)
    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 (str) {
            agSysPacket({information: "Bypassing PhoneGap sslCertificateChecker: " + str}).send();
            return true;
        };
        agSysPacket({information: "PhoneGap sslCertificateChecker hooked installed"}).send();
    } catch (err) {
        agSysPacket({information: "error: PhoneGap sslCertificateChecker pinner not found"}).send();
    }
}

function bypass_ibm_pinning() {
    // IBM MobileFirst pinTrustedCertificatePublicKey (double bypass)
    try {
        var WLClient_Activity = Java.use('com.worklight.wlclient.api.WLClient');
        WLClient_Activity.getInstance().pinTrustedCertificatePublicKey.overload('java.lang.String').implementation = function (cert) {
            agSysPacket({information: "Bypassing IBM MobileFirst pinTrustedCertificatePublicKey {1}: " + cert}).send();
            return;
        };
        agSysPacket({information: "IBM MobileFirst pinTrustedCertificatePublicKey hooked installed"}).send();

        WLClient_Activity.getInstance().pinTrustedCertificatePublicKey.overload('[Ljava.lang.String;').implementation = function (cert) {
            agSysPacket({information: "Bypassing IBM MobileFirst pinTrustedCertificatePublicKey {2}: " + cert}).send();
            return;
        };
        agSysPacket({information: "IBM MobileFirst pinTrustedCertificatePublicKey hooked installed"}).send();

    } catch (err) {
        agSysPacket({information: "error: IBM MobileFirst pinTrustedCertificatePublicKey pinner not found"}).send();
    }
}

function bypass_ibmworklight_pinning() {
    // IBM WorkLight (ancestor of MobileFirst) HostNameVerifierWithCertificatePinning (quadruple bypass)
    try {
        var worklight_Activity = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning');
        worklight_Activity.verify.overload('java.lang.String', 'javax.net.ssl.SSLSocket').implementation = function (str) {
            agSysPacket({information: "Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {1}: " + str}).send();
            return;
        };
        worklight_Activity.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (str) {
            agSysPacket({information: "Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {2}: " + str}).send();
            return;
        };
        
        worklight_Activity.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;').implementation = function (str) {
            agSysPacket({information: "Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {3}: " + str}).send();
            return;
        };
        worklight_Activity.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function (str) {
            agSysPacket({information: "Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {4}: " + str}).send();
            return true;
        };

    } catch (err) {
        agSysPacket({information: "error: IBM WorkLight HostNameVerifierWithCertificatePinning pinner not found"}).send();
    }
}

function bypass_cwac_pinning() {
    // 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 (str) {
            agSysPacket({information: "Bypassing CWAC-Netsecurity CertPinManager: " + str}).send();
            return true;
        };

    } catch (err) {
        agSysPacket({information: "error: CWAC-Netsecurity CertPinManager pinner not found"}).send();
    }
}

function bypass_netty_pinning() {        
    // 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) {
            agSysPacket({information: "Bypassing Netty FingerprintTrustManagerFactory"}).send();
        };

    } catch (err) {
        agSysPacket({information: "error: Netty FingerprintTrustManagerFactory pinner not found"}).send();
    }
}

function bypass_worklight_pinning() {
    // 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 (str) {
            agSysPacket({information: "Bypassing Worklight Androidgap WLCertificatePinningPlugin: " + str}).send();
            return true;
        };

    } catch (err) {
        agSysPacket({information: "error: Worklight Androidgap WLCertificatePinningPlugin pinner not found"}).send();
    }
}

function bypass_squareup_pinning() {
    try {
        var Squareup_CertificatePinner_Activity = Java.use('com.squareup.okhttp.CertificatePinner');
        Squareup_CertificatePinner_Activity.check.overload('java.lang.String', 'java.security.cert.Certificate').implementation = function (str1, str2) {
            agSysPacket({information: "Bypassing Squareup CertificatePinner {1}: " + str1}).send();
            return;
        };

        Squareup_CertificatePinner_Activity.check.overload('java.lang.String', 'java.util.List').implementation = function (str1, str2) {
            agSysPacket({information: "Bypassing Squareup CertificatePinner {2}: " + str1}).send();
            return;
        };
    
    } catch (err) {
        agSysPacket({information: "error: Squareup CertificatePinner pinner not found"}).send();
    }

    try {
        var Squareup_OkHostnameVerifier_Activity = Java.use('com.squareup.okhttp.internal.tls.OkHostnameVerifier');
        Squareup_OkHostnameVerifier_Activity.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (str1, str2) {
            agSysPacket({information: "Bypassing Squareup OkHostnameVerifier {1}: " + str1}).send();
            return true;
        };
        
        Squareup_OkHostnameVerifier_Activity.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function (str1, str2) {
            agSysPacket({information: "Bypassing Squareup OkHostnameVerifier {2}: " + str1}).send();
            return true;
        };
        
    } catch (err) {
        agSysPacket({information: "error: Squareup OkHostnameVerifier pinner not found"}).send();
    }
}

function bypass_webview_pinning() {
    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) {
            agSysPacket({information: "Bypassing Android WebViewClient check {1}"}).send();
        };
    } catch (err) {
        agSysPacket({information: "error: Android WebViewClient {1} check not found"}).send();
    }

    try {
        // Bypass WebViewClient {2}
        var AndroidWebViewClient_Activity_2 = Java.use('android.webkit.WebViewClient');
        AndroidWebViewClient_Activity_2.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.WebResourceRequest', 'android.webkit.WebResourceError').implementation = function(obj1, obj2, obj3) {
            agSysPacket({information: "Bypassing Android WebViewClient check {2}"}).send();
        };
    } catch (err) {
        agSysPacket({information: "error: Android WebViewClient {2} check not found"}).send();
    }

    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) {
            agSysPacket({information: "Bypassing Android WebViewClient check {3}"}).send();
        };
    } catch (err) {
        agSysPacket({information: "error: Android WebViewClient {3} check not found"}).send();
    }

    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) {
            agSysPacket({information: "Bypassing Android WebViewClient check {4}"}).send();
        };
    } catch (err) {
        agSysPacket({information: "error: Android WebViewClient {4} check not found"}).send();
    }
}

function bypass_cordova_pinning() {
    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) {
            agSysPacket({information: "Bypassing Apache Cordova WebViewClient"}).send();
            obj3.proceed();
        };
        
    } catch (err) {
        agSysPacket({information: "error: Apache Cordova WebViewClient pinner not found"}).send();
    }
}

function bypass_boye_pinning() {
    try {
        var boye_AbstractVerifier = Java.use('ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier');
        boye_AbstractVerifier.verify.implementation = function (host, ssl) {
            agSysPacket({information: "Bypassing Boye AbstractVerifier: " + host}).send();
        };
        
    } catch (err) {
        agSysPacket({information: "error: Boye AbstractVerifier pinner not found"}).send();
    }
}

function bypass_apache_pinning() {
    try {
        var apache_AbstractVerifier = Java.use('org.apache.http.conn.ssl.AbstractVerifier');
        apache_AbstractVerifier.verify.implementation = function(a, b, c, d) {
            agSysPacket({information: "Bypassing Apache AbstractVerifier check: " + a}).send();
            return;
        };
    } catch (err) {
        agSysPacket({information: "error: Apache AbstractVerifier check not found"}).send();
    }
}

function bypass_chromecronet_pinning() {
	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) {
            agSysPacket({information: "Bypassing Chromium Cronet {1}"}).send();

            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) {
            agSysPacket({information: "Bypassing Chromium Cronet {2}"}).send();
            var cronet_obj_2 = CronetEngine_Activity.addPublicKeyPins.call(this, hostName, pinsSha256, includeSubdomains, expirationDate);
            return cronet_obj_2;
        };
    } catch (err) {
        agSysPacket({information: "error: Chromium Cronet pinner not found"}).send();
    }
}

function bypass_flutter_pinning() {
    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) {
            agSysPacket({information: "Bypassing Flutter HttpCertificatePinning :" + a}).send();
            return true;
        };
    } catch (err) {
        agSysPacket({information: "error: Flutter HttpCertificatePinning pinner not found"}).send();
    }

    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) {
            agSysPacket({information: "Bypassing Flutter SslPinningPlugin :" + a}).send();
            return true;
        };
    } catch (err) {
        agSysPacket({information: "error: Flutter SslPinningPlugin pinner not found"}).send();
    }
}


function rudimentaryFix(typeName) {
    // This is a improvable rudimentary fix, if not works you can patch it manually
    if (typeName === undefined){
        return;
    } else if (typeName === 'boolean') {
        return true;
    } else {
        return null;
    }
}

// Dynamic SSLPeerUnverifiedException Patcher                                //
// 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  //
///////////////////////////////////////////////////////////////////////////////
function bypass_sslpeerunverifiedexception_pinning() {
    try {
        var UnverifiedCertError = Java.use('javax.net.ssl.SSLPeerUnverifiedException');
        UnverifiedCertError.$init.implementation = function (str) {
            console.log('Unexpected SSLPeerUnverifiedException occurred, trying to patch it dynamically..');
            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('Attempting to bypass uncommon SSL Pinning method on: '+className+'.'+methodName + '->' + callingClass + ' ' + callingMethod);

                // Skip it when already patched by Frida
                if (callingMethod.implementation) {
                    console.log("Already patched");
                    return; 
                }
                // Trying to patch the uncommon SSL Pinning method via implementation
                var returnTypeName = callingMethod.returnType.type;
                callingMethod.implementation = function() {
                    console.log("Automatic patch");
                    rudimentaryFix(returnTypeName);
                };
            } catch (e) {
                // Dynamic patching via implementation does not works, then trying via function overloading
                //console.log('[!] The uncommon SSL Pinning method has more than one overload); 
                if (String(e).includes(".overload")) {
                    var splittedList = String(e).split(".overload");
                    for (let i=2; i<splittedList.length; i++) {
                        var extractedOverload = splittedList[i].trim().split("(")[1].slice(0,-1).replaceAll("'","");
                        // Check if extractedOverload has multiple arguments
                        if (extractedOverload.includes(",")) {
                            // Go here if overloaded method has multiple arguments (NOTE: max 6 args are covered here)
                            var argList = extractedOverload.split(", ");
                            console.log('Attempting overload of '+className+'.'+methodName+' with arguments: '+extractedOverload + ' ' + argList.length);
                            if (argList.length == 2) {
                                callingMethod.overload(argList[0], argList[1]).implementation = function(a,b) {
                                    rudimentaryFix(returnTypeName);
                                }
                            } else if (argNum == 3) {
                                callingMethod.overload(argList[0], argList[1], argList[2]).implementation = function(a,b,c) {
                                    rudimentaryFix(returnTypeName);
                                }
                            }  else if (argNum == 4) {
                                callingMethod.overload(argList[0], argList[1], argList[2], argList[3]).implementation = function(a,b,c,d) {
                                    rudimentaryFix(returnTypeName);
                                }
                            }  else if (argNum == 5) {
                                callingMethod.overload(argList[0], argList[1], argList[2], argList[3], argList[4]).implementation = function(a,b,c,d,e) {
                                    rudimentaryFix(returnTypeName);
                                }
                            }  else if (argNum == 6) {
                                callingMethod.overload(argList[0], argList[1], argList[2], argList[3], argList[4], argList[5]).implementation = function(a,b,c,d,e,f) {
                                    rudimentaryFix(returnTypeName);
                                }
                            } 
                        // Go here if overloaded method has a single argument
                        } else {
                            callingMethod.overload(extractedOverload).implementation = function(a) {
                                rudimentaryFix(returnTypeName);
                            }
                        }
                    }
                } else {
                    console.log('[-] Failed to dynamically patch SSLPeerUnverifiedException '+e);
                }
            }
            console.log('SSLPeerUnverifiedException hooked');
            return this.$init(str);
        };
    } catch (err) {
        console.log('SSLPeerUnverifiedException not found');
        console.log(err);
    }
}

// Trustmanager pinning
bypass_trustmanager_pinning();

// OkHTTP3 pinning
bypass_okhttp3_pinning();
    
// Trustkit pinning (triple bypass)
bypass_trustkit_pinning();

// Trustmanager Impl pinning
bypass_trustmanagerimpl_pinning();

// Appcelerator Titanium pinning
bypass_appcelerator_pinning();

// OpenSSLSocketImpl Conscrypt pinning
bypass_conscript_pinning();

// OpenSSLSocketImpl Apache Harmony pinning
bypass_apacheharmony_pinning();

// Xservice phonegapp pinning
bypass_phonegapp_pinning();

// IBM MobileFirst pinning
bypass_ibm_pinning();

// IBM worklight pinning
bypass_ibmworklight_pinning();

// CWAC pinning
bypass_cwac_pinning();      

// Worklight pinning
bypass_worklight_pinning();

// Netty pinning
bypass_netty_pinning();

// Squareup pinning
bypass_squareup_pinning();

// Webview pinning
bypass_webview_pinning();

// Cordova pinning
bypass_cordova_pinning();

// Boye pinning
bypass_boye_pinning();

// Apache pinning
bypass_apache_pinning();

// Chrome cronet pinning
bypass_chromecronet_pinning();

// Flutter pinning
bypass_flutter_pinning();

// SSLPeerUnverifiedException patcher
bypass_sslpeerunverifiedexception_pinning();