翻转校园app_逆向分析-Frida Hook Java层加解密 SSL检测

介于黄河科技学院开发的app,翻转校园app的逆向分析,Frida hook JAVA层

这个APP用的阿里OSS,腾讯浏览器框架,

  • 先抓包,,我们会发现直接抓包抓不到,这里我用Frida hook该app的ssl
  • Frida Hook ssl代码:
    # -*- coding: UTF-8 -*-
    
    import frida, sys
    
    jscode = """
    
        
        //Hook SLL检测
        Java.perform(function() {
     
    /*
    hook list:
    1.SSLcontext
    2.okhttp
    3.webview
    4.XUtils
    5.httpclientandroidlib
    6.JSSE
    7.network\_security\_config (android 7.0+)
    8.Apache Http client (support partly)
    9.OpenSSLSocketImpl
    10.TrustKit
    11.Cronet
    */
     
        // Attempts to bypass SSL pinning implementations in a number of
        // ways. These include implementing a new TrustManager that will
        // accept any SSL certificate, overriding OkHTTP v3 check()
        // method etc.
        var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
        var HostnameVerifier = Java.use('javax.net.ssl.HostnameVerifier');
        var SSLContext = Java.use('javax.net.ssl.SSLContext');
        var quiet_output =false;
     
        // Helper method to honor the quiet flag.
     
        function quiet_send(data) {
     
            if (quiet_output) {
     
                return;
            }
     
            send(data)
        }
     
     
        // Implement a new TrustManager
        // ref: https://gist.github.com/oleavr/3ca67a173ff7d207c6b8c3b0ca65a9d8
        // Java.registerClass() is only supported on ART for now(201803). 所以android 4.4以下不兼容,4.4要切换成ART使用.
        /*
    06-07 16:15:38.541 27021-27073/mi.sslpinningdemo W/System.err: java.lang.IllegalArgumentException: Required method checkServerTrusted(X509Certificate[], String, String, String) missing
    06-07 16:15:38.542 27021-27073/mi.sslpinningdemo W/System.err:     at android.net.http.X509TrustManagerExtensions.<init>(X509TrustManagerExtensions.java:73)
            at mi.ssl.MiPinningTrustManger.<init>(MiPinningTrustManger.java:61)
    06-07 16:15:38.543 27021-27073/mi.sslpinningdemo W/System.err:     at mi.sslpinningdemo.OkHttpUtil.getSecPinningClient(OkHttpUtil.java:112)
            at mi.sslpinningdemo.OkHttpUtil.get(OkHttpUtil.java:62)
            at mi.sslpinningdemo.MainActivity$1$1.run(MainActivity.java:36)
    */
        var X509Certificate = Java.use("java.security.cert.X509Certificate");
        var TrustManager;
        try {
            TrustManager = Java.registerClass({
                name:'org.wooyun.TrustManager',
                implements: [X509TrustManager],
                methods: {
                    checkClientTrusted:function(chain, authType) {},
                    checkServerTrusted:function(chain, authType) {},
                    getAcceptedIssuers:function() {
                        // var certs = [X509Certificate.$new()];
                        // return certs;
                        return [];
                    }
                }
            });
        }catch (e) {
            quiet_send("registerClass from X509TrustManager >>>>>>>> " + e.message);
        }
     
     
     
     
     
        // Prepare the TrustManagers array to pass to SSLContext.init()
        var TrustManagers = [TrustManager.$new()];
     
        try {
            // Prepare a Empty SSLFactory
            var TLS_SSLContext = SSLContext.getInstance("TLS");
            TLS_SSLContext.init(null, TrustManagers,null);
            var EmptySSLFactory = TLS_SSLContext.getSocketFactory();
        }catch (e) {
            quiet_send(e.message);
        }
     
        send('Custom, Empty TrustManager ready');
     
        // 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');
     
        // Override the init method, specifying our new TrustManager
        SSLContext_init.implementation =function(keyManager, trustManager, secureRandom) {
     
            quiet_send('Overriding SSLContext.init() with the custom TrustManager');
     
            SSLContext_init.call(this,null, TrustManagers,null);
        };
     
        /*** okhttp3.x unpinning ***/
     
     
        // Wrap the logic in a try/catch as not all applications will have
        // okhttp as part of the app.
        try {
     
            var CertificatePinner = Java.use('okhttp3.CertificatePinner');
     
            quiet_send('OkHTTP 3.x Found');
     
            CertificatePinner.check.overload('java.lang.String','java.util.List').implementation =function() {
     
                quiet_send('OkHTTP 3.x check() called. Not throwing an exception.');
            }
     
        }catch (err) {
     
            // If we dont have a ClassNotFoundException exception, raise the
            // problem encountered.
            if (err.message.indexOf('ClassNotFoundException') === 0) {
     
                throw new Error(err);
            }
        }
     
        // Appcelerator Titanium PinningTrustManager
     
        // Wrap the logic in a try/catch as not all applications will have
        // appcelerator as part of the app.
        try {
     
            var PinningTrustManager = Java.use('appcelerator.https.PinningTrustManager');
     
            send('Appcelerator Titanium Found');
     
            PinningTrustManager.checkServerTrusted.implementation =function() {
     
                quiet_send('Appcelerator checkServerTrusted() called. Not throwing an exception.');
            }
     
        }catch (err) {
     
            // If we dont have a ClassNotFoundException exception, raise the
            // problem encountered.
            if (err.message.indexOf('ClassNotFoundException') === 0) {
     
                throw new Error(err);
            }
        }
     
        /*** okhttp unpinning ***/
     
     
        try {
            var OkHttpClient = Java.use("com.squareup.okhttp.OkHttpClient");
            OkHttpClient.setCertificatePinner.implementation =function(certificatePinner) {
                // do nothing
                quiet_send("OkHttpClient.setCertificatePinner Called!");
                return this;
            };
     
            // Invalidate the certificate pinnet checks (if "setCertificatePinner" was called before the previous invalidation)
            var CertificatePinner = Java.use("com.squareup.okhttp.CertificatePinner");
            CertificatePinner.check.overload('java.lang.String','[Ljava.security.cert.Certificate;').implementation =function(p0, p1) {
                // do nothing
                quiet_send("okhttp Called! [Certificate]");
                return;
            };
            CertificatePinner.check.overload('java.lang.String','java.util.List').implementation =function(p0, p1) {
                // do nothing
                quiet_send("okhttp Called! [List]");
                return;
            };
        }catch (e) {
            quiet_send("com.squareup.okhttp not found");
        }
     
        /*** WebView Hooks ***/
     
        /* frameworks/base/core/java/android/webkit/WebViewClient.java */
        /* public void onReceivedSslError(Webview, SslErrorHandler, SslError) */
        var WebViewClient = Java.use("android.webkit.WebViewClient");
     
        WebViewClient.onReceivedSslError.implementation =function(webView, sslErrorHandler, sslError) {
            quiet_send("WebViewClient onReceivedSslError invoke");
            //执行proceed方法
            sslErrorHandler.proceed();
            return;
        };
     
        WebViewClient.onReceivedError.overload('android.webkit.WebView','int','java.lang.String','java.lang.String').implementation =function(a, b, c, d) {
            quiet_send("WebViewClient onReceivedError invoked");
            return;
        };
     
        WebViewClient.onReceivedError.overload('android.webkit.WebView','android.webkit.WebResourceRequest','android.webkit.WebResourceError').implementation =function() {
            quiet_send("WebViewClient onReceivedError invoked");
            return;
        };
     
        /*** JSSE Hooks ***/
     
        /* libcore/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java */
        /* public final TrustManager[] getTrustManager() */
        /* TrustManagerFactory.getTrustManagers maybe cause X509TrustManagerExtensions error  */
        // var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
        // TrustManagerFactory.getTrustManagers.implementation = function(){
        //     quiet_send("TrustManagerFactory getTrustManagers invoked");
        //     return TrustManagers;
        // }
     
        var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection");
        /* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */
        /* public void setDefaultHostnameVerifier(HostnameVerifier) */
        HttpsURLConnection.setDefaultHostnameVerifier.implementation =function(hostnameVerifier) {
            quiet_send("HttpsURLConnection.setDefaultHostnameVerifier invoked");
            return null;
        };
        /* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */
        /* public void setSSLSocketFactory(SSLSocketFactory) */
        HttpsURLConnection.setSSLSocketFactory.implementation =function(SSLSocketFactory) {
            quiet_send("HttpsURLConnection.setSSLSocketFactory invoked");
            return null;
        };
        /* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */
        /* public void setHostnameVerifier(HostnameVerifier) */
        HttpsURLConnection.setHostnameVerifier.implementation =function(hostnameVerifier) {
            quiet_send("HttpsURLConnection.setHostnameVerifier invoked");
            return null;
        };
     
        /*** Xutils3.x hooks ***/
        //Implement a new HostnameVerifier
        var TrustHostnameVerifier;
        try {
            TrustHostnameVerifier = Java.registerClass({
                name:'org.wooyun.TrustHostnameVerifier',
                implements: [HostnameVerifier],
                method: {
                    verify:function(hostname, session) {
                        return true;
                    }
                }
            });
     
        }catch (e) {
            //java.lang.ClassNotFoundException: Didn't find class "org.wooyun.TrustHostnameVerifier"
            quiet_send("registerClass from hostnameVerifier >>>>>>>> " + e.message);
        }
     
        try {
            var RequestParams = Java.use('org.xutils.http.RequestParams');
            RequestParams.setSslSocketFactory.implementation = function(sslSocketFactory) {
                sslSocketFactory = EmptySSLFactory;
                return null;
            }
     
            RequestParams.setHostnameVerifier.implementation = function(hostnameVerifier) {
                hostnameVerifier = TrustHostnameVerifier.$new();
                return null;
            }
     
        } catch (e) {
            quiet_send("Xutils hooks not Found");
        }
     
        /*** httpclientandroidlib Hooks ***/
        try {
            var AbstractVerifier = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier");
            AbstractVerifier.verify.overload('java.lang.String', '[Ljava.lang.String', '[Ljava.lang.String', 'boolean').implementation = function() {
                quiet_send("httpclientandroidlib Hooks");
                return null;
            }
        } catch (e) {
            quiet_send("httpclientandroidlib Hooks not found");
        }
     
        /***
    android 7.0+ network_security_config TrustManagerImpl hook
    apache httpclient partly
    ***/
        var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
        // try {
        //     var Arrays = Java.use("java.util.Arrays");
        //     //apache http client pinning maybe baypass
        //     //https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#471
        //     TrustManagerImpl.checkTrusted.implementation = function (chain, authType, session, parameters, authType) {
        //         quiet_send("TrustManagerImpl checkTrusted called");
        //         //Generics currently result in java.lang.Object
        //         return Arrays.asList(chain);
        //     }
        //
        // } catch (e) {
        //     quiet_send("TrustManagerImpl checkTrusted nout found");
        // }
     
        try {
            // Android 7+ TrustManagerImpl
            TrustManagerImpl.verifyChain.implementation = function(untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
                quiet_send("TrustManagerImpl verifyChain called");
                // Skip all the logic and just return the chain again :P
                //https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2017/november/bypassing-androids-network-security-configuration/
                // https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#L650
                return untrustedChain;
            }
        } catch (e) {
            quiet_send("TrustManagerImpl verifyChain nout found below 7.0");
        }
        // OpenSSLSocketImpl
        try {
            var OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl');
            OpenSSLSocketImpl.verifyCertificateChain.implementation = function(certRefs, authMethod) {
                quiet_send('OpenSSLSocketImpl.verifyCertificateChain');
            }
     
            quiet_send('OpenSSLSocketImpl pinning')
        } catch (err) {
            quiet_send('OpenSSLSocketImpl pinner not found');
        }
        // Trustkit
        try {
            var Activity = Java.use("com.datatheorem.android.trustkit.pinning.OkHostnameVerifier");
            Activity.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function(str) {
                quiet_send('Trustkit.verify1:' + str);
                return true;
            };
            Activity.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function(str) {
                quiet_send('Trustkit.verify2:' + str);
                return true;
            };
     
            quiet_send('Trustkit pinning')
        } catch (err) {
            quiet_send('Trustkit pinner not found')
        }
     
        try {
            //cronet pinner hook
            //weibo don't invoke
     
            var netBuilder = Java.use("org.chromium.net.CronetEngine$Builder");
     
            //https://developer.android.com/guide/topics/connectivity/cronet/reference/org/chromium/net/CronetEngine.Builder.html#enablePublicKeyPinningBypassForLocalTrustAnchors(boolean)
            netBuilder.enablePublicKeyPinningBypassForLocalTrustAnchors.implementation =function(arg) {
     
                //weibo not invoke
                console.log("Enables or disables public key pinning bypass for local trust anchors = " + arg);
     
                //true to enable the bypass, false to disable.
                var ret = netBuilder.enablePublicKeyPinningBypassForLocalTrustAnchors.call(this,true);
                return ret;
            };
     
            netBuilder.addPublicKeyPins.implementation =function(hostName, pinsSha256, includeSubdomains, expirationDate) {
                console.log("cronet addPublicKeyPins hostName = " + hostName);
     
                //var ret = netBuilder.addPublicKeyPins.call(this,hostName, pinsSha256,includeSubdomains, expirationDate);
                //this 是调用 addPublicKeyPins 前的对象吗? Yes,CronetEngine.Builder
                return this;
            };
     
        }catch (err) {
            console.log('[-] Cronet pinner not found')
        }
    });
        
        
    """
    
    def on_message(message, data):
        if message['type'] == 'send':
            print("[*] {0}".format(message['payload']))
        else:
            print(message)
    pass
    
    # 查找USB设备并附加到目标进程
    session = frida.get_usb_device(1000).attach('翻转校园')
    # 在目标进程里创建脚本
    script = session.create_script(jscode)
    # 注册消息回调
    script.on('message', on_message)
    print('[*] Start attach')
    # 加载创建好的javascript脚本
    script.load()
    # 读取系统输入
    sys.stdin.read()

    Hook完ssl后就可以正常抓包了

  • 抓包结果1
  • 抓包结果2
  • 分析参数
    device	        00000000-6fd2-5a75-ffff-ffffca01fdf4
    password	vv7F7Mzn9YGUElFoJCfN4Q==
    
    random	        F0A28BBDE391B7F0A6BEA2F09D999EE5BCA3
    login_type	password
    key	        118342203120011

    发现device应该是本地生成的,password是加密过的,random 和cookie里的PHPSESSID 都是第一个请求连接返回的值 ,key是账号

  • 今天主要目的是解决登录,实现登录功能就行,接下来进入分析app环节

分析APP:

  • 先查壳,将App拖入到PKiD
  • 查壳显示结果
  • 查询发现是 普通的360壳
  • 我用的是Youpk脱壳,如果手下没Google pixel 的话 可以用xposed的模块脱壳,或者其他第三方工具脱壳(我已将脱完壳未修复放到蓝奏云里,需要可下载)
  • 将脱壳后的app拖入到Jadx里面,搜索Url关键词,或者是Url的请求名称,
  •   这里我搜索”sso.schoopia.com/login” 搜索关键字符串 ,,进入这个登录代码
  • ResponseLogin responseLogin = (ResponseLogin) JsonProvider.parseJson(HttpProvider.postMessageWithSendStatus("https://sso.schoopia.com/login", requestLogin.requestLoginArgs(), getSessionValue()), ResponseLogin.class);
    

    请求的参数在这里 requestLoginArgs(),按着Ctrl鼠标点击跟踪到定义处插图005  这里面是将本地的password传到map里面然后返回map的,所以我们要看看本地的password是在哪被赋值的

  • JAVA层追值1  鼠标右键查看用例,发现只有一处调用,跟过去看看
  • JAVA层追值2 发现关键词encrypt加密方法,传的值是咱们输入的密码和第一处请求的时候返回的Random, 跟进去看看encrypt的定义
  • JAVA层追值3 发现是个修改过的md5 外加AES加密,,
  • package edu.hhstu.hhstutalent.provider;
    
    import android.util.Base64;
    import android.util.Log;
    import java.io.UnsupportedEncodingException;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import javax.crypto.Cipher;
    import javax.crypto.spec.SecretKeySpec;
    
    public class AesEncryption {
        private static AesEncryption sInstance = null;
        private static String value = ".youcai.diot";
    
        public static AesEncryption getInstance() {
            if (sInstance == null) {
                sInstance = new AesEncryption();
            }
            return sInstance;
        }
    
        public String encrypt(String str, String str2) {
            String stringToMD5 = stringToMD5(str2 + value);
            Log.i("MD5 ", stringToMD5);
            try {
                Cipher instance = Cipher.getInstance("AES/ECB/PKCS7Padding");
                instance.init(1, new SecretKeySpec(stringToMD5.getBytes(), "AES"));
                return Base64.encodeToString(instance.doFinal(str.getBytes()), 0);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        public String decrypt(String str, String str2) {
            String stringToMD5 = stringToMD5(str2 + value);
            Log.i("MD5 ", stringToMD5);
            try {
                byte[] decode = Base64.decode(str.getBytes(), 0);
                Cipher instance = Cipher.getInstance("AES/ECB/PKCS7Padding");
                instance.init(2, new SecretKeySpec(stringToMD5.getBytes(), "AES"));
                return new String(instance.doFinal(decode), "UTF-8");
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        public String stringToMD5(String str) {
            try {
                byte[] digest = MessageDigest.getInstance("MD5").digest(str.getBytes("UTF-8"));
                StringBuilder sb = new StringBuilder(digest.length * 2);
                for (byte b : digest) {
                    int i = b & 255;
                    if (i < 16) {
                        sb.append("0");
                    }
                    sb.append(Integer.toHexString(i));
                }
                return sb.toString().substring(8, 24);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
                return null;
            } catch (UnsupportedEncodingException e2) {
                e2.printStackTrace();
                return null;
            }
        }
    
        public String stringToMD5WithKey(String str) {
            try {
                byte[] digest = MessageDigest.getInstance("MD5").digest((str + value).getBytes("UTF-8"));
                StringBuilder sb = new StringBuilder(digest.length * 2);
                for (byte b : digest) {
                    int i = b & 255;
                    if (i < 16) {
                        sb.append("0");
                    }
                    sb.append(Integer.toHexString(i));
                }
                return sb.toString().substring(8, 24);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
                return null;
            } catch (UnsupportedEncodingException e2) {
                e2.printStackTrace();
                return null;
            }
        }
    }

    stringToMD5(String str)  本身是个md5方法,但是return的时候 裁剪了一下 return sb.toString().substring(8, 24); ,取了 8-24位置之间的字符串

  • 用Frida hook一下 看看是不是这个位置,
  • 该处Frida 代码
  • Java.perform(function () {
        var AesEncryption = Java.use('edu.hhstu.hhstutalent.provider.AesEncryption');
        AesEncryption.encrypt.implementation = function (arg1, arg2) {
            console.log("AES密码加密");
            send("arg1 "+arg1);
            send("arg2 "+arg2);
            var a=this.encrypt(arg1, arg2);
            send("返回值 "+a);
            return a;
        }
        AesEncryption.decrypt.implementation = function (arg1, arg2) {
            console.log("AES 解密");
            send("arg1 "+arg1);
            send("arg2 "+arg2);
            var a=this.decrypt(arg1, arg2);
            send("返回值 "+a);
            return a;
        }
        AesEncryption.stringToMD5.implementation = function (arg1) {
            console.log("md5加密 stringToMD5");
            send("arg1 "+arg1);
            var a=this.stringToMD5(arg1);
            send("返回值 "+a);
            return a;
        }
        AesEncryption.stringToMD5WithKey.implementation = function (arg1) {
            console.log("md5加密 stringToMD5WithKey");
            send("arg1 "+arg1);
            var a=this.stringToMD5WithKey(arg1);
            send("返回值 "+a);
            return a;
        }
    });

    重启软件,attach Frida,

  • 点击登录 我输入的账号密码是
    118342203129999

    lzonel.cn

  • Frida Hook结果展示 frida hook到了,,位置确实是这里,,先是md5加密,,然后又AES加密
  • md5加密  Frida Hook结果展示md5加密
  • E8B5B3F0A49D90E282AEEC8A9EF0A5B89AE0AC9A.youcai.diot    Random+App里面的key
  • md5(E8B5B3F0A49D90E282AEEC8A9EF0A5B89AE0AC9A.youcai.diot)  返回 2621dce3a543cd85cb0433bb9659c420
  • (2621dce3a543cd85cb0433bb9659c420).substring(8, 24)  取第8位至24位之间字符串 返回 a543cd85cb0433bb

AES加密 需要md5加密后的值当做key ECB模式, PKCS7Padding填充  加密也就这了,纯Java层,

Device Java层生成方法与找密码加密方法相同

 

public static String getSerial(Context context) {
    return new UUID((long) ("LT" + Build.BOARD + Build.BRAND + Build.DEVICE + Build.MANUFACTURER + Build.PRODUCT).hashCode(), (long) "serial".hashCode()).toString();
}

 

 

总体Frida Hook代码

 

# -*- coding: UTF-8 -*-

import frida, sys

jscode = """


Java.perform(function () {
    var AesEncryption = Java.use('edu.hhstu.hhstutalent.provider.AesEncryption');
    AesEncryption.encrypt.implementation = function (arg1, arg2) {
        console.log("AES密码加密");
        send("arg1 "+arg1);
        send("arg2 "+arg2);
        var a=this.encrypt(arg1, arg2);
        send("返回值 "+a);
        return a;
    }
    AesEncryption.decrypt.implementation = function (arg1, arg2) {
        console.log("AES 解密");
        send("arg1 "+arg1);
        send("arg2 "+arg2);
        var a=this.decrypt(arg1, arg2);
        send("返回值 "+a);
        return a;
    }
    AesEncryption.stringToMD5.implementation = function (arg1) {
        console.log("md5加密 stringToMD5");
        send("arg1 "+arg1);
        var a=this.stringToMD5(arg1);
        send("返回值 "+a);
        return a;
    }
    AesEncryption.stringToMD5WithKey.implementation = function (arg1) {
        console.log("md5加密 stringToMD5WithKey");
        send("arg1 "+arg1);
        var a=this.stringToMD5WithKey(arg1);
        send("返回值 "+a);
        return a;
    }
});
    
    
    //Hook SLL检测
    Java.perform(function() {
 
/*
hook list:
1.SSLcontext
2.okhttp
3.webview
4.XUtils
5.httpclientandroidlib
6.JSSE
7.network\_security\_config (android 7.0+)
8.Apache Http client (support partly)
9.OpenSSLSocketImpl
10.TrustKit
11.Cronet
*/
 
    // Attempts to bypass SSL pinning implementations in a number of
    // ways. These include implementing a new TrustManager that will
    // accept any SSL certificate, overriding OkHTTP v3 check()
    // method etc.
    var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
    var HostnameVerifier = Java.use('javax.net.ssl.HostnameVerifier');
    var SSLContext = Java.use('javax.net.ssl.SSLContext');
    var quiet_output =false;
 
    // Helper method to honor the quiet flag.
 
    function quiet_send(data) {
 
        if (quiet_output) {
 
            return;
        }
 
        send(data)
    }
 
 
    // Implement a new TrustManager
    // ref: https://gist.github.com/oleavr/3ca67a173ff7d207c6b8c3b0ca65a9d8
    // Java.registerClass() is only supported on ART for now(201803). 所以android 4.4以下不兼容,4.4要切换成ART使用.
    /*
06-07 16:15:38.541 27021-27073/mi.sslpinningdemo W/System.err: java.lang.IllegalArgumentException: Required method checkServerTrusted(X509Certificate[], String, String, String) missing
06-07 16:15:38.542 27021-27073/mi.sslpinningdemo W/System.err:     at android.net.http.X509TrustManagerExtensions.<init>(X509TrustManagerExtensions.java:73)
        at mi.ssl.MiPinningTrustManger.<init>(MiPinningTrustManger.java:61)
06-07 16:15:38.543 27021-27073/mi.sslpinningdemo W/System.err:     at mi.sslpinningdemo.OkHttpUtil.getSecPinningClient(OkHttpUtil.java:112)
        at mi.sslpinningdemo.OkHttpUtil.get(OkHttpUtil.java:62)
        at mi.sslpinningdemo.MainActivity$1$1.run(MainActivity.java:36)
*/
    var X509Certificate = Java.use("java.security.cert.X509Certificate");
    var TrustManager;
    try {
        TrustManager = Java.registerClass({
            name:'org.wooyun.TrustManager',
            implements: [X509TrustManager],
            methods: {
                checkClientTrusted:function(chain, authType) {},
                checkServerTrusted:function(chain, authType) {},
                getAcceptedIssuers:function() {
                    // var certs = [X509Certificate.$new()];
                    // return certs;
                    return [];
                }
            }
        });
    }catch (e) {
        quiet_send("registerClass from X509TrustManager >>>>>>>> " + e.message);
    }
 
 
 
 
 
    // Prepare the TrustManagers array to pass to SSLContext.init()
    var TrustManagers = [TrustManager.$new()];
 
    try {
        // Prepare a Empty SSLFactory
        var TLS_SSLContext = SSLContext.getInstance("TLS");
        TLS_SSLContext.init(null, TrustManagers,null);
        var EmptySSLFactory = TLS_SSLContext.getSocketFactory();
    }catch (e) {
        quiet_send(e.message);
    }
 
    send('Custom, Empty TrustManager ready');
 
    // 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');
 
    // Override the init method, specifying our new TrustManager
    SSLContext_init.implementation =function(keyManager, trustManager, secureRandom) {
 
        quiet_send('Overriding SSLContext.init() with the custom TrustManager');
 
        SSLContext_init.call(this,null, TrustManagers,null);
    };
 
    /*** okhttp3.x unpinning ***/
 
 
    // Wrap the logic in a try/catch as not all applications will have
    // okhttp as part of the app.
    try {
 
        var CertificatePinner = Java.use('okhttp3.CertificatePinner');
 
        quiet_send('OkHTTP 3.x Found');
 
        CertificatePinner.check.overload('java.lang.String','java.util.List').implementation =function() {
 
            quiet_send('OkHTTP 3.x check() called. Not throwing an exception.');
        }
 
    }catch (err) {
 
        // If we dont have a ClassNotFoundException exception, raise the
        // problem encountered.
        if (err.message.indexOf('ClassNotFoundException') === 0) {
 
            throw new Error(err);
        }
    }
 
    // Appcelerator Titanium PinningTrustManager
 
    // Wrap the logic in a try/catch as not all applications will have
    // appcelerator as part of the app.
    try {
 
        var PinningTrustManager = Java.use('appcelerator.https.PinningTrustManager');
 
        send('Appcelerator Titanium Found');
 
        PinningTrustManager.checkServerTrusted.implementation =function() {
 
            quiet_send('Appcelerator checkServerTrusted() called. Not throwing an exception.');
        }
 
    }catch (err) {
 
        // If we dont have a ClassNotFoundException exception, raise the
        // problem encountered.
        if (err.message.indexOf('ClassNotFoundException') === 0) {
 
            throw new Error(err);
        }
    }
 
    /*** okhttp unpinning ***/
 
 
    try {
        var OkHttpClient = Java.use("com.squareup.okhttp.OkHttpClient");
        OkHttpClient.setCertificatePinner.implementation =function(certificatePinner) {
            // do nothing
            quiet_send("OkHttpClient.setCertificatePinner Called!");
            return this;
        };
 
        // Invalidate the certificate pinnet checks (if "setCertificatePinner" was called before the previous invalidation)
        var CertificatePinner = Java.use("com.squareup.okhttp.CertificatePinner");
        CertificatePinner.check.overload('java.lang.String','[Ljava.security.cert.Certificate;').implementation =function(p0, p1) {
            // do nothing
            quiet_send("okhttp Called! [Certificate]");
            return;
        };
        CertificatePinner.check.overload('java.lang.String','java.util.List').implementation =function(p0, p1) {
            // do nothing
            quiet_send("okhttp Called! [List]");
            return;
        };
    }catch (e) {
        quiet_send("com.squareup.okhttp not found");
    }
 
    /*** WebView Hooks ***/
 
    /* frameworks/base/core/java/android/webkit/WebViewClient.java */
    /* public void onReceivedSslError(Webview, SslErrorHandler, SslError) */
    var WebViewClient = Java.use("android.webkit.WebViewClient");
 
    WebViewClient.onReceivedSslError.implementation =function(webView, sslErrorHandler, sslError) {
        quiet_send("WebViewClient onReceivedSslError invoke");
        //执行proceed方法
        sslErrorHandler.proceed();
        return;
    };
 
    WebViewClient.onReceivedError.overload('android.webkit.WebView','int','java.lang.String','java.lang.String').implementation =function(a, b, c, d) {
        quiet_send("WebViewClient onReceivedError invoked");
        return;
    };
 
    WebViewClient.onReceivedError.overload('android.webkit.WebView','android.webkit.WebResourceRequest','android.webkit.WebResourceError').implementation =function() {
        quiet_send("WebViewClient onReceivedError invoked");
        return;
    };
 
    /*** JSSE Hooks ***/
 
    /* libcore/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java */
    /* public final TrustManager[] getTrustManager() */
    /* TrustManagerFactory.getTrustManagers maybe cause X509TrustManagerExtensions error  */
    // var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
    // TrustManagerFactory.getTrustManagers.implementation = function(){
    //     quiet_send("TrustManagerFactory getTrustManagers invoked");
    //     return TrustManagers;
    // }
 
    var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection");
    /* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */
    /* public void setDefaultHostnameVerifier(HostnameVerifier) */
    HttpsURLConnection.setDefaultHostnameVerifier.implementation =function(hostnameVerifier) {
        quiet_send("HttpsURLConnection.setDefaultHostnameVerifier invoked");
        return null;
    };
    /* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */
    /* public void setSSLSocketFactory(SSLSocketFactory) */
    HttpsURLConnection.setSSLSocketFactory.implementation =function(SSLSocketFactory) {
        quiet_send("HttpsURLConnection.setSSLSocketFactory invoked");
        return null;
    };
    /* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */
    /* public void setHostnameVerifier(HostnameVerifier) */
    HttpsURLConnection.setHostnameVerifier.implementation =function(hostnameVerifier) {
        quiet_send("HttpsURLConnection.setHostnameVerifier invoked");
        return null;
    };
 
    /*** Xutils3.x hooks ***/
    //Implement a new HostnameVerifier
    var TrustHostnameVerifier;
    try {
        TrustHostnameVerifier = Java.registerClass({
            name:'org.wooyun.TrustHostnameVerifier',
            implements: [HostnameVerifier],
            method: {
                verify:function(hostname, session) {
                    return true;
                }
            }
        });
 
    }catch (e) {
        //java.lang.ClassNotFoundException: Didn't find class "org.wooyun.TrustHostnameVerifier"
        quiet_send("registerClass from hostnameVerifier >>>>>>>> " + e.message);
    }
 
    try {
        var RequestParams = Java.use('org.xutils.http.RequestParams');
        RequestParams.setSslSocketFactory.implementation = function(sslSocketFactory) {
            sslSocketFactory = EmptySSLFactory;
            return null;
        }
 
        RequestParams.setHostnameVerifier.implementation = function(hostnameVerifier) {
            hostnameVerifier = TrustHostnameVerifier.$new();
            return null;
        }
 
    } catch (e) {
        quiet_send("Xutils hooks not Found");
    }
 
    /*** httpclientandroidlib Hooks ***/
    try {
        var AbstractVerifier = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier");
        AbstractVerifier.verify.overload('java.lang.String', '[Ljava.lang.String', '[Ljava.lang.String', 'boolean').implementation = function() {
            quiet_send("httpclientandroidlib Hooks");
            return null;
        }
    } catch (e) {
        quiet_send("httpclientandroidlib Hooks not found");
    }
 
    /***
android 7.0+ network_security_config TrustManagerImpl hook
apache httpclient partly
***/
    var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
    // try {
    //     var Arrays = Java.use("java.util.Arrays");
    //     //apache http client pinning maybe baypass
    //     //https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#471
    //     TrustManagerImpl.checkTrusted.implementation = function (chain, authType, session, parameters, authType) {
    //         quiet_send("TrustManagerImpl checkTrusted called");
    //         //Generics currently result in java.lang.Object
    //         return Arrays.asList(chain);
    //     }
    //
    // } catch (e) {
    //     quiet_send("TrustManagerImpl checkTrusted nout found");
    // }
 
    try {
        // Android 7+ TrustManagerImpl
        TrustManagerImpl.verifyChain.implementation = function(untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
            quiet_send("TrustManagerImpl verifyChain called");
            // Skip all the logic and just return the chain again :P
            //https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2017/november/bypassing-androids-network-security-configuration/
            // https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#L650
            return untrustedChain;
        }
    } catch (e) {
        quiet_send("TrustManagerImpl verifyChain nout found below 7.0");
    }
    // OpenSSLSocketImpl
    try {
        var OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl');
        OpenSSLSocketImpl.verifyCertificateChain.implementation = function(certRefs, authMethod) {
            quiet_send('OpenSSLSocketImpl.verifyCertificateChain');
        }
 
        quiet_send('OpenSSLSocketImpl pinning')
    } catch (err) {
        quiet_send('OpenSSLSocketImpl pinner not found');
    }
    // Trustkit
    try {
        var Activity = Java.use("com.datatheorem.android.trustkit.pinning.OkHostnameVerifier");
        Activity.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function(str) {
            quiet_send('Trustkit.verify1:' + str);
            return true;
        };
        Activity.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function(str) {
            quiet_send('Trustkit.verify2:' + str);
            return true;
        };
 
        quiet_send('Trustkit pinning')
    } catch (err) {
        quiet_send('Trustkit pinner not found')
    }
 
    try {
        //cronet pinner hook
        //weibo don't invoke
 
        var netBuilder = Java.use("org.chromium.net.CronetEngine$Builder");
 
        //https://developer.android.com/guide/topics/connectivity/cronet/reference/org/chromium/net/CronetEngine.Builder.html#enablePublicKeyPinningBypassForLocalTrustAnchors(boolean)
        netBuilder.enablePublicKeyPinningBypassForLocalTrustAnchors.implementation =function(arg) {
 
            //weibo not invoke
            console.log("Enables or disables public key pinning bypass for local trust anchors = " + arg);
 
            //true to enable the bypass, false to disable.
            var ret = netBuilder.enablePublicKeyPinningBypassForLocalTrustAnchors.call(this,true);
            return ret;
        };
 
        netBuilder.addPublicKeyPins.implementation =function(hostName, pinsSha256, includeSubdomains, expirationDate) {
            console.log("cronet addPublicKeyPins hostName = " + hostName);
 
            //var ret = netBuilder.addPublicKeyPins.call(this,hostName, pinsSha256,includeSubdomains, expirationDate);
            //this 是调用 addPublicKeyPins 前的对象吗? Yes,CronetEngine.Builder
            return this;
        };
 
    }catch (err) {
        console.log('[-] Cronet pinner not found')
    }
});
    
    
"""

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)
pass

# 查找USB设备并附加到目标进程
session = frida.get_usb_device(1000).attach('翻转校园')
# 在目标进程里创建脚本
script = session.create_script(jscode)
# 注册消息回调
script.on('message', on_message)
print('[*] Start attach')
# 加载创建好的javascript脚本
script.load()
# 读取系统输入
sys.stdin.read()

 

 

未经允许不得转载:晗雅|星空 » 翻转校园app_逆向分析-Frida Hook Java层加解密 SSL检测
分享到:

请选择你看完该文章的感受:

13瞧一瞧 0扯淡 5学到了 4不懂 6正能量 3无聊

评论抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址 (选填)