修改Android源码,解放HTTPS抓包

Posted 袁国正_yy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了修改Android源码,解放HTTPS抓包相关的知识,希望对你有一定的参考价值。

欢迎访问我的个人网站:https://coderyuan.com

文章目录

为什么HTTPS不能抓包了?

Google在android 7.0以后的版本中,添加了“网络安全配置(Network security configuration)”的相关配置项。其旨在增强App的安全性,可以避免TargetSDK版本>=N的App内部网络请求在非测试环境下被恶意抓包。

Network Sercurity Configuration对安全性的保证,主要是通过以下几个途径:是否允许明文HTTP请求(非HTTPS)、HTTPS证书(区分系统、用户)信任设置、域名以及App的Debug或Release配置,只有符合Manifest中配置的NetworkConfig字段内容下的条件,才可以进行正常的HTTP请求,如果需要使用Charles、Fiddler等工具进行抓包,也需要利用NetworkConfig来配置可以信任的证书,否则HTTPS请求在CONNECT阶段,就会返回错误,同时LogCat会打印出类似以下的错误信息:

     Caused by: java.security.cert.CertificateException: xxx.
        at com.android.org.conscrypt.TrustManagerImpl.verifyChain(TrustManagerImpl.java:661)
        at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:539)
        at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:605)
        at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:495)
        at com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:321)
        at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:113)
        at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:87)
        at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:116)
        at com.huawei.secure.android.common.ssl.SecureX509TrustManager.checkServerTrusted(:105)
        at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:212)
        at com.android.org.conscrypt.ConscryptFileDescriptorSocket.verifyCertificateChain(ConscryptFileDescriptorSocket.java:404)
        at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
        at com.android.org.conscrypt.NativeSsl.doHandshake(NativeSsl.java:375)

通常,Android开发者会将NetworkConfig按照以下XML文件进行配置:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <!-- 允许明文HTTP(非HTTPS)请求 -->
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <!-- 其他情况只允许信任系统内置证书 -->
            <certificates src="system"/>
        </trust-anchors>
    </base-config>
    <!-- 仅Debug包允许用户证书 -->
    <debug-overrides>
        <trust-anchors>
            <certificates src="user"/>
        </trust-anchors>
    </debug-overrides>
</network-security-config>

然而对于开发者来说,通常App发版上线会选择使用Release版本的安装包,如果希望对Release包抓包,只能使用Android 6.0及以下的手机,这样一来,有时候出现线上问题,便无法进行HTTPS抓包,并不利于测试和修正

如何解决这一问题?

基本的几个思路

  • 反编译APK,修改NetworkConfig相关的XML文件,让App可以信任用户配置的证书

    这个方案局限性比较大,现在很多App都对反编译技术(主要是apktool)进行了防御,有的App使用apktool解包以后,就无法成功打包了,还有的用上了加固技术,通用的反编译工具无法直接获取真正Apk内部的文件。不过,如果能够使用apktool成功获得解码后的XML文件,且进行修改后能够重新打包成可以运行的Apk,这样做也是可行的

  • 使用Hook

    使用Xposed框架在App请求过程中进行Hook,依赖的基础仍然是反编译,且Xposed框架需要建立在Root后的基础上,成功率比较低,且成本还比较高

  • 修改Android源码

    这个应该是最成功且一劳永逸的办法了,但成本不小,需要有一台性能不错的电脑(至少硬盘空间够大)和一台Google的手机,具体如何实现,下面详细来说

寻求一个适合自己的办法

这里并不是说修改源码就是最好的,举例来讲,能使用6.0手机,是最简单的办法,但随着现在最低SDK版本的提升,在不久的将来,一些App或许就不会再支持6.0了,所以我只是在寻找一个更为靠谱的方案

如何改Android源码

准备工作

首先需要将AOSP的源码准备好,这里就不在赘述了,跟着Google官方的文档下载即可:https://source.android.com/setup/build/downloading

如果没法访问Google,可以使用清华大学的镜像:https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/

分析源码

AOSP的源码非常的庞大,如果是初次接触,可能会完全摸不着头脑,当然,我们也不可能完全把它的结构和内容摸索清楚再进行修改,学会用文本查找很重要

这里安利VSCode,我们不需要使用Android Studio这样的工具,因为太慢!

首先去搜索一下NetworkSecurityConfig,就会发现有以下相关的文件

其中NetworkSucurityConfig.javaXmlConfigSource.java就和证书判断逻辑相关

仔细分析一波NetworkSucurityConfig类,可以发现这个类使用了Android中常见的Builder模式来进行创建,其中包括一个返回默认配置Builder的静态方法

public static final Builder getDefaultBuilder(ApplicationInfo info)

在Android 9以前,这个方法的参数为两个int型字段:int targetSdkVersion, int targetSandboxVesrsion

这里列出这个方法的详细代码

    /**
     * Return a @link Builder for the default @code NetworkSecurityConfig.
     *
     * <p>
     * The default configuration has the following properties:
     * <ol>
     * <li>If the application targets API level 27 (Android O MR1) or lower then cleartext traffic
     * is allowed by default.</li>
     * <li>Cleartext traffic is not permitted for ephemeral apps.</li>
     * <li>HSTS is not enforced.</li>
     * <li>No certificate pinning is used.</li>
     * <li>The system certificate store is trusted for connections.</li>
     * <li>If the application targets API level 23 (Android M) or lower then the user certificate
     * store is trusted by default as well for non-privileged applications.</li>
     * <li>Privileged applications do not trust the user certificate store on Android P and higher.
     * </li>
     * </ol>
     *
     * @hide
     */
    public static Builder getDefaultBuilder(ApplicationInfo info) 
        Builder builder = new Builder()
                .setHstsEnforced(DEFAULT_HSTS_ENFORCED)
                // System certificate store, does not bypass static pins.
                .addCertificatesEntryRef(
                        new CertificatesEntryRef(SystemCertificateSource.getInstance(), false));
        final boolean cleartextTrafficPermitted = info.targetSdkVersion < Build.VERSION_CODES.P
                && info.targetSandboxVersion < 2;
        builder.setCleartextTrafficPermitted(cleartextTrafficPermitted);
        // Applications targeting N and above must opt in into trusting the user added certificate
        // store.
        if (info.targetSdkVersion <= Build.VERSION_CODES.M && !info.isPrivilegedApp()) 
            // User certificate store, does not bypass static pins.
            builder.addCertificatesEntryRef(
                    new CertificatesEntryRef(UserCertificateSource.getInstance(), false));
        
        return builder;
    

可以看出以下几个特点:

  1. Android 9(假设Target和系统版本一样)以下默认允许明文传输(非HTTPS)

  2. 默认只信任系统内置的根证书,Android 7以下添加对用户配置的证书信任

  3. HSTS不强制开启

于是再次搜索这个方法的调用

其中,第一个调用的地方是重写了XmlConfigSource实现的ConfigSource类中的方法getDefaultConfig

而第二个地方则是在private void parseNetworkSecurityConfig(XmlResourceParser parser)这个方法当中,根据方法名,就知道这是在解析XML配置中的网络安全配置

那么再分别查看它们的调用

那么,系统读取App网络安全配置的基本流程就可以猜想出来了:

  1. 首先是解析AndroidMainfest.xml文件

  2. 中间会初始化一个XmlConfigSource实例用来解析NetworkConfig

  3. 当需要用到网络配置的具体内容时,会调用XmlConfigSource实例的getDefaultConfig方法,其中ensureInitialized会具体负责解析XML文件中的证书、域名、是否允许明文等配置

  4. parseNetworkSecurityConfig方法会首先利用NetworkSecurityConfig类的getDefaultBuilder方法获取一个通用的Builder,构建一个默认配置,再根据XML中的实际情况进行补充

修改相关源码

了解了网络安全配置加载的流程,那么从理论上来说,我们可以在任一环境进行修改,即可去掉HTTPS的抓包限制,其中,修改的几个点如下:

  1. 首先修改NetworkSecurityConfig的默认Builder,让它能够和Android 6.0以下的配置一样,默认添加对用户配置的证书信任,具体修改如下:

        // NetworkSecurityConfig.java
        
        // 修改前
        public static Builder getDefaultBuilder(ApplicationInfo info) 
            Builder builder = new Builder()
                    .setHstsEnforced(DEFAULT_HSTS_ENFORCED)
                    // System certificate store, does not bypass static pins.
                    .addCertificatesEntryRef(
                            new CertificatesEntryRef(SystemCertificateSource.getInstance(), false));
            final boolean cleartextTrafficPermitted = info.targetSdkVersion < Build.VERSION_CODES.P
                    && info.targetSandboxVersion < 2;
            builder.setCleartextTrafficPermitted(cleartextTrafficPermitted);
            // Applications targeting N and above must opt in into trusting the user added certificate
            // store.
            if (info.targetSdkVersion <= Build.VERSION_CODES.M && !info.isPrivilegedApp()) 
                // User certificate store, does not bypass static pins.
                builder.addCertificatesEntryRef(
                        new CertificatesEntryRef(UserCertificateSource.getInstance(), false));
            
            return builder;
        
    
        // NetworkSecurityConfig.java
        
        // 修改后
        public static final Builder getDefaultBuilder(ApplicationInfo info) 
            Builder builder = new Builder()
                    .setHstsEnforced(DEFAULT_HSTS_ENFORCED)
                    // System certificate store, does not bypass static pins.
                    .addCertificatesEntryRef(
                            new CertificatesEntryRef(SystemCertificateSource.getInstance(), false));
            final boolean cleartextTrafficPermitted = info.targetSdkVersion < Build.VERSION_CODES.P
                    && info.targetSandboxVersion < 2;
            builder.setCleartextTrafficPermitted(true);
            // 修改点1:忽略targetSdkVersion的判断,直接添加UserCertificateSource
            builder.addCertificatesEntryRef(new CertificatesEntryRef(UserCertificateSource.getInstance(), false));
            return builder;
        
    
  2. 其次我们需要忽略App内置的配置,直接忽略XmlConfigSource中的解析结果,getDefaultConfig方法直接返回我们改过的DefaultConfig:

        // XmlConfigSource.java
        // 修改前
        public NetworkSecurityConfig getDefaultConfig() 
            ensureInitialized();
            return mDefaultConfig;
        
    
        // XmlConfigSource.java
        // 修改后
        public NetworkSecurityConfig getDefaultConfig() 
            // 防止出现其他异常,其他初始化操作不进行修改(保留ensureInitialized方法的调用)
            ensureInitialized();
            // 修改点2:不使用解析后的mDefaultConfig,重新获取一个DefaultConfig
            NetworkSecurityConfig.Builder builder = NetworkSecurityConfig.getDefaultBuilder(null);
            return builder.build();
        
    

刷机验证

确认没有基本的Java语法问题以后,lunch、make一波走起!!

如果还不会编译AOSP,戳这里:https://source.android.com/setup/build/building

稍等片刻,刷机试验……

注意:刷完机后,先不要着急连上WiFi挂上代理抓包,很多手机刷完以后时间会还原,所以先去设置中把时间调成正确的,否则无论如何证书都是过期的……

调整好时间后,连上Charles或者Fiddler的代理,装上它们的证书,即可对任意的App进行HTTPS抓包了!

终于不是满屏红叉了!!

以上是关于修改Android源码,解放HTTPS抓包的主要内容,如果未能解决你的问题,请参考以下文章

android 7.0以上charles https抓包

android下使用tcpdump抓包

安卓手机mitmproxy抓包

你真的会用Fiddler抓包工具吗—Android程序员抓包必备

Android逆向之http/https网络抓包

抓包整理外篇——————https 抓包 [ 五]