首页>软件资讯>常见问题

常见问题

Fiddler的APP抓包大全

发布时间:2022-12-22 16:23:30人气:757

最近工作中遇到一些比较奇葩的App,一边测试一边搜集整理出了比较全的姿势。

1.不走代理的APP

如何判断:连接Fiddler代理-->抓不到包-->关闭Fiddler后正常通信。


解决方法:PC端模拟器+如下全局代理抓包工具,筛选出模拟器进程无需配置可以直接解密https流量


(1) HTTPAnalyserv7


1.png


(2) HTTPDebuggerpro


2.png


2.常规APP抓包

(1)普通代理 Fiddler


PS:fiddler 抓app仍是http代理抓包,容易被检测限制


可以在FiddlerScript 中的handle下配置 抓取websocket (多是明文)


static function OnWebSocketMessage(oMsg: WebSocketMessage) {

     // Log Message to the LOG tab

     FiddlerApplication.Log.LogString(oMsg.ToString());

}  

3.png


(2)Charles + postern 可以新建一个VPN配置socks5代理

 从而绕过更多抓包限制,Charles处理https包更优秀,配置如下简图,也可自行百度高级用法。

4.png

5.png


去掉本地windows代理,只抓取移动端的流量

6.png

7.png


手机端Postern左侧配置代理—添加代理服务器输入上图设置好的socks代理8889端口,代理类型选择socks5,再返回配置规则删除其他规则,添加匹配所有通过刚刚设置好的代理保存。如图:


8.png

9.png

开启postern,允许连接vpn,浏览器输入chls.pro这个网址下载安装证书。


Charles点击图中的第二步解密按钮,可解密https流量(8.0以上系统可以将证书放到系统证书目录下)


(3)VPN手机端使用Httpcanary抓包


使用VPN,流量会强制走VPN通道,可以抓到更多的包。


配置安装证书,有root权限可以把证书安装到系统证书下(7.1及以下系统默认信任用户证书)


10.png


点击右下的小飞机即可抓包,右上角有过滤选项可以只抓http tcp等


左边设置目标应用可以指定进程,方便只抓取想要的数据包


PS:安卓7.1及以上,抓取https流量,需要root后把fiddler、burp、charles等工具的的证书安装到系统根证书下


openssl x509 -inform PEM -subject_hash_old -in Desktop.pem |head -1  #获取hash值

用hash值.0重命名证书

adb push 重命名后证书 /sdcard/

mount -o rw,remount /      #挂载为可读写

mv /sdcard/证书 /etc/security/cacerts/      #系统证书路径

chmod 644 /etc/security/cacerts/证书  #修改权限644

# /data/misc/user/0/cacerts-added   #用户证书路径



3.代理证书校验绕过

如何判断:0x02抓不到的有可能就是代理检测,更直观的判断就是App可以正常使用,打开httpcanary抓不到或者网络连接失败


说到代理检测:先简单介绍下数字证书常见的检测机制


一般来说主要检测证书中{证书链 签发关系 公钥 指纹}等这些内容,所以我们绕过代理检测也要从这些方面入手。


所以实际抓包测试中hook掉系统自带的检测api和常见框架中的检测api即可。然后再利用0x02中的抓包姿势就O了。


下介绍两种方式:


(1)Xposed框架+JustTrustMe (0.3)

11.png

12.png


安装xp框架后直接安装justtrustme的apk,在模块里勾选中开启,然后一定!!重启模拟器/手机,打开关闭xp框架和模块一定要重启手机才能生效。


JustTrustMe的源码<可自行编译>(文末有编译好的apk等本文工具打包下载)


项目地址:https://github.com/Fuzion24/JustTrustMe



从项目代码中可以看到作者hook了很多常见的系统函数、常见框架(okhttp等)HttpsURLConnection 下的API X509TrustManager、HostnameVerifier(域名验证),setSSLSocketFactory()中的sslcontext里的checksevercertificate(),setHostnameVerifier() 方法;okhttp/okhttp3框架中证书Pinner,certificatePinner 下的 check 方法, 设置通信组件中的setSSLSocketFactory() 方法等。


(2)Frida + Hook.js


Frida请自行安装,可以参考https://www.jianshu.com/p/c349471bdef7


Frida:hook中常用的两个命令:


frida -UF -l .hook.js   #注入最前端的进程(当前的app)

frida -U --no-pause -f com.xxx.xxx(包名) -l .Hook.js  #启动前注入


下面的JS代码类似于frida下的增强版的justtrustme ,Hook了下述的一些系统api和框架


 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


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')

    }

});



4

总结

常见的App抓包姿势差不多就这些了,基本可以抓到未加固的或者debug版的App的数据包。代码混淆,入口加固,资源加固等App,混淆类的需要通过匹配函数的参数类型找到修改后的函数名自己重写方法、加固类的需要jadx-guif反编译后分析加固的的逻辑找到App的函数入口让其初始化解密后再重写相关函数等。后期再找案例分析。



上一条:Fiddler Everywhere 4.0 为调试提供了新的筛选体验

下一条:Quest:Toad Data Modeler