天天看點

Android 各個系統版本下https的抓包方法目錄

目錄

  • 目錄
      • 本文側重點在哪
      • 測試環境
      • https 通信過程和中間人攻擊
      • 我們原來怎麼抓https包的
      • Android 7.0 (api 24 ) 和 targetSdkVersion 對抓包的影響
        • 如何能在Android 7.0 上成功的抓自己開發的app的https的包
        • 如何在Android 7.X上抓第三方app的https包
      • 使用自簽名證書的應用和雙向驗證的應用

本文側重點在哪

  1. https 的用戶端和伺服器端的請求流程,加了Charles之後對請求有什麼影響(中間人攻擊)
  2. 我們原來怎麼抓https包的
  3. Android 7.0 (api 24 ) 和 targetSdkVersion 對抓包的影響

    開發者對自己app的抓包,

    逆向工程師對别人app的抓包

  4. 有 CA 簽名的證書通路https的app和自簽名證書app

    微網誌和我們自己的app

  5. 設定 VirtualApp 為debug模式,并且設定VA 的targetSdkVersion <24

測試環境

  • Android EVA-AL00 7.0 API 24 華為,安裝了Charles根證書,并且設定了代理
  • Android EVA-AL00 6.0 API 23 華為,安裝了Charles根證書,并且設定了代理
  • 微部落格戶端,https 通信過程和中間人攻擊

    下圖是https 用戶端和伺服器端通信的基本流程

    Android 各個系統版本下https的抓包方法目錄
    那麼如何抓包呢,原理其實說起來也很簡單,就是在用戶端給伺服器端發消息的時候,中間人(Charles)截取用戶端發送給伺服器的請求,然後僞裝成用戶端與伺服器進行通信;将伺服器傳回給用戶端的内容發送給用戶端,僞裝成伺服器與用戶端進行通信。
    其實Charles就是這麼做的,當配置了Charles之後,理論上所有的http/https請求資料都被攔截到了。看下面一張簡化的中間人抓包的圖(圖檔來源于網絡):
    Android 各個系統版本下https的抓包方法目錄

    我們原來怎麼抓https包的

    分為三步

    1. 手機上導入Charles根證書,導入方式見Charles官網

    Android 各個系統版本下https的抓包方法目錄
    1. 電腦端Charles設定https抓包配置

      菜單欄 Proxy –>ProxySetting

      Android 各個系統版本下https的抓包方法目錄
    2. 手機端配置代理
      Android 各個系統版本下https的抓包方法目錄

    理論上來說按照上面的配置,配置之後,然後手機上安裝新浪微部落格戶端之後就能抓到微網誌的資料了。我們分别用Android6.0 和 Android7.0 的手機打開微部落格戶端看下效果:

    發現在6.x系統上,微網誌打開正常,并且Charles顯示列出了已經抓到的微網誌的接口api。

    但是在Android 7.x作業系統上,微網誌顯示

    網絡出錯啦,請點選按鈕重新加載

    如下圖:
    Android 各個系統版本下https的抓包方法目錄
    而且Charles上顯示确實抓到了包,但是報錯

    You may need to configure your browser or application to trust the Charles Root Certificate. See SSL Proxying in the Help menu.

    ,Charles說手機端沒有信任Charles的根證書,但是我們手機上已經安裝了Charles根證書了,為什麼會這樣?

    原來在Android 7.0(API 24 ) ,有一個名為“Network Security Configuration”的新安全功能。這個新功能的目标是允許開發人員在不修改應用程式代碼的情況下自定義他們的網絡安全設定。如果應用程式的SDK高于或等于24,則隻有系統證書才會被信任。是以使用者導入的Charles根證書是不被信任的。具體說明看官方文檔在這個官方文檔裡面說了,如何能指定信任使用者安裝的根證書進而可以實作抓包。

    Android 7.0 (api 24 ) 和 targetSdkVersion 對抓包的影響

    這裡要分兩種情況:

    - 抓自己開發的app的網絡包

    - 抓第三方app的網絡包,比如微部落格戶端

    這兩種情況有什麼差別的,第一種app是我們自己開發的,我們手裡有源碼,能夠修改,能夠做到像官方文檔裡面說的一樣進行配置。第二種我們沒有源碼,要想做到像官方文檔裡面配置的話,隻能反編譯後,把配置檔案添加進去然後重新打包,但是重新打包就會遇到很多坑。

    如何能在Android 7.0 上成功的抓自己開發的app的https的包

    目前我常用的有三種方式:
    1. 作業系統,通過apk裡面的配置檔案控制app的證書信任機制

    Android 官方文檔裡的配置方式進行配置,文檔裡面講的很詳細,7.0之後對于自己app可選擇的可信任的證書鍊控制很細。我這裡隻添加一種方式,讓我們自己開發的app能夠信任Android手機上使用者導入的根證書。

    如下圖所示,手機的根證有兩種,一種是系統預裝的,一種是使用者自己導入的:

    Android 各個系統版本下https的抓包方法目錄
    配置方式:
    添加如下檔案 res/xml/network_security_config.xml 到你的代碼裡面:
    
    <network-security-config> 
      <debug-overrides> 
        <trust-anchors> 
          <!-- Trust user added CAs while debuggable only -->
          <certificates src="user" /> 
        </trust-anchors> 
      </debug-overrides> 
    </network-security-config>
    
    然後在你 app的 manifest 檔案中引入上面的檔案, 如下所示:
    
    <?xml version="1.0" encoding="utf-8"?>
    <manifest ... >
        <application android:networkSecurityConfig="@xml/network_security_config" ... >
            ...
        </application>
    </manifest>
               
    然後配置好之後,這樣就可以直接通路,https請求,并且通過Charles抓包了。
    1. 降低 targetSdkVersion 的版本來繞過Android 7.0(api 24)上網絡的安全機制

      如果我們不想像上面一樣配置這麼複雜的東西,可以通過降低targetSdkVersion的方式來達到一樣的效果,在gradle檔案中配置

      targetSdkVersion < 24

      就可以了。
    defaultConfig {
            applicationId "me.febsky.okhttp.test"
            minSdkVersion 
            targetSdkVersion 
            versionCode 
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
               
    網絡請求測試代碼如下,别忘了子啊manifest檔案中申請網絡請求權限:
    package me.febsky.okhttp.test;
    
    import android.support.annotation.NonNull;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    
    import java.io.IOException;
    import java.util.concurrent.TimeUnit;
    
    import okhttp3.Call;
    import okhttp3.Callback;
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.Response;
    
    public class MainActivity extends AppCompatActivity {
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            new Thread() {
                @Override
                public void run() {
                    super.run();
    
                    String url = "https://api.weibo.cn/2/guest/cardlist?" +
                            "networktype=wifi&uicode=10000327&moduleID=708&checktoken=" +
                            "&c=android&i=a52f1e4&s=c1108e87&ua=OPPO-OPPO%20R9m__weibo__6.8.2__android__android5.1" +
                            "&wm=9847_0002&aid=01AilAKZLB81znjKciZxofmqIMYg52EReWuEaQL7hIDXj6IR4." +
                            "&did=b87cc255f19b91ff8e202968adab0eb9fc159a2e&&v_f=2&v_p=33" +
                            "&from=1068295010&gsid=_2AkMg6ZSSf8NhqwJRmP0QzGPgb4l_wgjEieLBAH7sJRM3HRl-3T9jqnUstRUyD-wT6lM3A4HWHM1fFXBWuOYnxg.." +
                            "& +
                            "&sflag=1&containerid=1087030002_2982_2_50&need_head_cards=0";
    
                    OkHttpClient okHttpClient = new OkHttpClient.Builder()
                            .connectTimeout(, TimeUnit.SECONDS)
                            .writeTimeout(, TimeUnit.SECONDS)
                            .readTimeout(, TimeUnit.SECONDS)
                            //.sslSocketFactory()
                            .build();
    
                    Request request = new Request.Builder()
                            .url(url)
                            .build();
    
                    okHttpClient.newCall(request).enqueue(new Callback() {
                        @Override
                        public void onFailure(Call call, IOException e) {
                            Log.e("Q_M", "GET -->" + e.toString());
    
                        }
    
                        @Override
                        public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                            Log.d("Q_M", "GET -->" + response.body().string());
                        }
                    });
                }
            }.start();
        }
    }
               
    1. 修改http請求架構的協定棧,讓架構不驗證證書
    這裡我們用的是okhttp的網絡請求架構,如何添加信任證書或者,不驗證證書,可以參考官網文檔,或者自行搜尋。這會因為你使用的架構不同而不同。

    如何在Android 7.X上抓第三方app的https包

    一般情況下第三方我們都是抓第三方app的包,為了分析别人的資料。但是像上面所說的前兩種方式一般在第三方app上不會存在的,誰也不想讓自己的app被抓包不是。
    1. 我們可以通過重打包的方式強行修改配置,或者強行降低 targetSdkVersion,或者強行修改别人源碼裡面的信任證書的代碼,然後再重打包就好了(分别針對上面1,2,3裡面所說的方法,隻不過通過逆向的方式添加)。
    2. 通過使用Xposed的 JustTrustMe 子產品來信任所有的證書,Xposed不會用的看這裡
    3. 哈哈,使用Android 7.0 以下的系統安裝應用,并抓包

    使用自簽名證書的應用和雙向驗證的應用

    在抓取一些第三方應用的包的時候,他們比較頑固:其一呢,用戶端通過指定的方式隻信任某一個證書;其二,我們一般來說隻有用戶端驗證服務端公鑰證書是不是合法,但是某些app,比如支付寶,采用雙向驗證的方式,在通信過程中,伺服器會驗證app的公鑰證書,這時候,就沒辦法使用Charles(中間人攻擊的方式)抓包了。

    自簽名證書的https和CA簽名的https 差別在哪裡?對https請求的影響又在哪裡?關于這些問題建議了解些證書和CA相關的概念以及證書分發的流程。

    從前面的https 用戶端和伺服器端通信的時序圖中可以看到,在握手過程中用戶端會驗證伺服器的公鑰證書。如果證書驗證不通過會終止請求。那麼問題來了,證書合不合法誰說了算?一般來說,在Android 手機出廠的時候會預裝一些證書,這些證書是CA的根證書,理論上來說預設是可信任的。如上圖:

    手機上的證書分為兩種.png

    裡面的系統清單的證書就是。對于網站來說,這個驗證過程是由浏覽器來做的,浏覽器通過系統裡面預裝的CA的根證書來驗證這個伺服器的證書是不是由系統已經有的這些CA簽發的。如果是那麼就認為這個https的請求中的伺服器證書是可信的。

    但是話說回來,我們在開發app的時候,請求網絡一般用的是網絡架構,這靈活性就比較好了,大多數的網絡請求架構(HttpClient,OkHttp等)都支援修改這個證書驗證過程,就是我不用上面所說的浏覽器驗證證書的過程,修改架構裡面這個驗證過程,(因為CA簽發證書是要花錢的好多公司都是用的自簽名的證書)指定架構在驗證的時候隻會認為和我們公司一樣的證書才認為驗證成功,才能進行接下來的通信。

    其實上面 通過使用Xposed的 JustTrustMe 子產品來信任所有的證書,Xposed不會用的看這裡 就是針對這些網絡請求架構進行

    攻擊的

    通過hook的方式強制讓這些網絡架構信任所有的證書。