1. 程式人生 > >部分APP無法代理抓包的原因及解決方法

部分APP無法代理抓包的原因及解決方法

引言

HTTP應用層的抓包已經成為日常工作測試與除錯中的重要一環,最近接觸新專案突然之間發現之前的抓包手段都不好使了,頓時模組與模組之間的前端與服務之間的互動都變成了不可見,整個人都好像被矇住了眼睛。 其實自己也很早就發現平時使用的支付寶等APP使用Fiddler 或 Charles這類代理抓包軟體預設情況下就無法抓取請求的,但使用Wireshark這類網絡卡抓包軟體可以看到這些APP的流量,而已這些流量也表明這些APP使用的主要應用層協議仍然是HTTP(https),不過我們的代理抓包工具卻失效了。如今終於在實際工作中遇到了,也不得不解決了,畢竟眼前有東西擋住會讓我渾身不適。    

代理抓包原理

為了弄明白為什麼Fiddler 或 Charles對這些APP無效,我們有必要先了解代理抓包我原理(當然您不想了解也是完全可以的,直接看後面的實際操作就可以完成,原理分析什麼的可以有興趣隨時回來看)   Fiddler 或 Charles 這類使用的代理的抓包軟體與Wireshark是完全不同的(Wireshark 使用的網絡卡資料複製,只要是經過指定網絡卡都會被抓取),其只能對使用代理的應用層網路協議生效,比如常見的HTTP(https),Websocket  。 這裡以HTTP為例簡單說明下

 

客戶端需要完成一次HTTP請求,通常需要先找到伺服器,客戶端會根據http請求中url的主機名(實際會使用host中的主角名)及其埠與目標主機建立tcp連線,建立連線後會將http報文傳送給目標伺服器 (更多細節請參考https://tools.ietf.org/html/rfc7232)   接下來我來看下HTTP代理是如何運作的,我們啟動Fiddler 或 Charles就是啟動了一個HTTP代理伺服器,這類工具會通知作業系統,“現在我在系統上建立了一個HTTP代理,IP為XXXXXX埠為XX。如果您使用的是linux您可以手動通知作業系統(export http_proxy=ip:port export https_proxy=$http_proxy),如果您使用的是手機等移動裝置您可以在當前wifi設定處告訴系統你要使用http代理。 現在我們已經告訴系統我們想要使用代理,這個時候執行在系統上的http客戶端再去傳送請求的時候,他就不會再去進行DNS解析,去連線目標伺服器,而是直接連線系統告訴他代理所在的地址(代理的ip及埠,注意無論是http或https或其他支援代理的協議都會連線同一個埠)。然後代理伺服器會與客戶端建立連線,再然後代理伺服器根據請求資訊再去連線真正的伺服器。

 

這裡還有個細節正常在沒有代理的情況下客戶端向伺服器傳送的請求行裡只包含部分URI(實際上是沒有方案,主機名及埠的)

 

如上圖如果在沒有代理的情況下,對www.baidu.com/index.html的請求的請求行實際上是GET /index.html HTTP/1.1 其實並不是我們常見的完整uri。因為在原始的HTTP設計中沒有考慮中間伺服器(即代理)的情況,客戶端在傳送報文前已經知道伺服器的地址並與之建立了連線,沒有必要再發送方案,主機名及埠。不過代理出現後這種做法就會有問題了,客戶端連線了代理伺服器,而代理伺服器卻沒有辦法連線正確的伺服器。因此客戶端傳送給代理的請求其實稍有不同,客戶端會在請求行裡使用完整的uri,這樣代理伺服器才能解析真實的伺服器的地址。 現在我們的請求實際上都是通過代理伺服器(Fiddler 或 Charles)傳送出去的,所以代理抓包軟體不僅知道http請求及響應的所有報文,甚至還可以隨時修改請求及響應。  

部分應用不能抓包的原因

可以看到代理抓包的關鍵就是需要HTTP客戶端按照要求去連線代理伺服器,一般情況下我們已經在系統層面上設定了代理,通常http客戶端都是按要求去實現的,在進行http請求前會先檢查系統代理,如果有設定代理,客戶端會直接使用完整uri去連線代理伺服器。不同的平臺通常會實現自己的的http客戶端的,雖然他們都按照協議要求實現了代理功能,但是並不一定在預設情況下會直接使用系統代理。 在現實中這種況下這種情況還不少,筆者當前專案使用到的Flutter就是這種情況,預設Flutter不會主動使用系統代理,需要單獨設定。(當然個人認為這種策略也是有理由,雖然給測試及除錯帶來了不便不過也在一程度上提高了些許資料安全) 正是因為HTTP客戶端沒有使用我們設定的系統代理,他們自然也不會連線Fiddler 或 Charles建立的代理伺服器,最終導致我們無法獲取任何請求。  

解決方案

不過既然我們已經知道了Fiddler 和 Charles不能抓包的具體原因,前面也提到了代理抓包的原理,那我們就總有辦法解決。 前面說到了我們APP使用的HTTP客戶端沒有連線到代理伺服器,導致我們的代理抓包軟體無法正常抓包,那我們只要想辦法讓客戶端重新連線到代理伺服器就好了(當然這一切都是以不修改客戶端軟體APP為前提的) 方法1:控制DNS解析,通過修改dns的方式讓客戶端以為我們的代理伺服器就是目標伺服器。             優勢:操作方便,通過修改裝置的hosts可以十分方便的首先             劣勢:需要為每個需要操作的域名提前新增host                       在手機等手持裝置上難以修改hosts(即對移動APP這類應用很難實現)   方法2:在網路裝置上直接做流量轉發,將指定終端裝置上發往80及443埠的資料直接轉發到代理伺服器的    目標埠上            優勢:可以針對連線到網路裝置上的終端裝置進行分別配置,而手機等終端裝置不需要進行任何裝置            劣勢:需要單獨的硬體裝置   方法3:使用VPN將終端裝置的流量轉發到代理伺服器            優勢:使用VPN軟體不用新增其他測試。            劣勢:終端上的VPN預設會直接對所有流量進行轉發,要進行合理的配置可能需要額外的學習成本  

實際操作步驟

筆者這裡直接使用上面提到第3種方法(方法1在對於手機APP很難操作,方法2可能需要其他裝置所以這裡不使用),因為我們的測試物件是手機移動APP,所以我們首先要在手機上安裝一個VPN,這裡使用一個十分方便的VPN軟體drony (介紹在這裡https://github.com/SuppSandroB/sandrop/wiki/Drony-FAQ),drony會在你的手機上建立一個VPN,將手機上的所有流量都重定向到drony自身(不是流向vpn伺服器) ,這樣drony就可以管理所有手機上的網路流量,甚至可以對手機上不同APP的流量進行單獨配置。

1:安裝drony  (這裡手機使用的Android裝置)

     您可以在網路上搜索drony選擇自己想要的版本進行安裝,或者在這裡下載(https://files.cnblogs.com/files/lulianqi/Drony_102.apk),安裝完成後開啟軟體如下圖    

 

   

2:開啟代理抓包軟體(這裡代理抓包軟體使用的是Fiddler)

  Fiddler的使用這裡不再介紹,需要開啟遠端代理,並在手機中安裝Fiddler根證書   這裡筆者開啟的遠端代理的地址是192.168.2.244:8888   關於證書安裝有些細節,Fiddler預設的根證書是cer格式,部分手機可能只能識別pem格式證書   直接使用openssl 轉一下就行了 (openssl x509 -inform der -in FiddlerRoot.cer -out FiddlerRoot.pem)   openssl 在中 windows/mac 一般都安裝有,命令直接使用就行了,使用生成的FiddlerRoot.pem安裝到手機上就行了(Charles預設就是pem證書)  

3:配置drony轉發

 

開啟Drony(處於OFF狀態),滑動到SETING頁,點選選擇Networks Wi-Fi 進入配置  

 

在網路列表中選擇點選當前手機wifi連線的網路 (需要確保該網路與Fiddler代理伺服器網路是聯通的)

 

 

配置要為當前網路使用的代理入口(這裡直接填寫fiddler代理地址就可以),選擇代理模式為手動(Manual)

 

注意Proxy type代理方式要選擇 Plain http proxy

 

Filter default value 選擇 Direct all ,然後點選下面的Rule設定應用規則

 

預設您的規則裡應該是空的,這裡直接點選上面的加號新增一個規則(符合規則要求的才會被轉發) 說明一下後面的操作會以鹹魚或支付寶做演示說明,不過筆者當前測試專案並不是鹹魚或支付寶,也不是其公司的員工,選擇這2個APP做演示是因為這些APP比較常用,且無法抓包的原因與筆者當前專案APP是類似的。  

 

在Network id處 選擇當前wifi的SSID Action 選擇 Local proxy chain Application 選擇需要強制代理的APP Hostname 及 Port 不填 表示所有的都會被強制代理,因為APP可能會使用其他的網路協議不一定都是http,可能不希望把所有流量都引流到http代理伺服器,這個時候就會使用這個配置指定ip及端口才轉發 完成後儲存即可,然後返回到SETTING主頁,滑動到LOG頁,點選下面按鈕,使其處於ON的狀態(表示啟用)

 

    這個時候啟動支付寶或鹹魚,我們就可以在Fiddler上看到正常的流量。不過如果你的運氣與筆者一樣可能只能看到這些Tunnel to (TLS管道建立),如果您使用的是Charles在列表裡看到的可能是一個個紅叉。    

 

當然筆者Fiddler根證書是安裝成功的,Fiddler配置也是正確的(手機上的Chrome https抓包都是正常的) 既然流量已經到Fiddler了,Drony的工作算是完美完成了,之所部分APP以不能解密https報文,還是我們自己證書的問題。首先先簡單描述下證書校驗的過程(如果不想看這些過程可以直接看後面操作步驟)    

證書校驗原理

無論Fiddler 或 Charles都使用中間人攻擊的方式替換tls鏈路證書,解密報文然後再加密傳送給真實伺服器。 您可以參看 HTTPS中間人攻擊實踐(原理·實踐)瞭解中間人攻擊的細節  

 

存在代理的情況下客戶端首先連線的是代理伺服器(即是圖中的攻擊者),實際client是直接與Proxy建立的TLS通道,所以代理當然TLS通道的傳輸金鑰然後解密報文。   不過由於證書的存在,client會校驗證書的合法性,然後決定是否連線伺服器。我們使用Fiddler或Charles抓取https前在裝置中安裝根證書正是為了通過client的證書校驗。  

 

在瀏覽器中任意找一個https的網頁,檢視其證書資訊。   從這裡面我們能看到證書包含以下內容: (1) Validity也即有效期,有效期包含生效時間和失效時間,是一個時間區間; (2) 公鑰資訊Subject Public Key Info,包括公鑰的加密演算法和公鑰內容; (3) 指紋資訊,指紋用於驗證證書的完整性,也是證書校驗的關鍵,他保證書沒有被修改過。 其原理就是在釋出證書時,釋出者根據指紋演算法(此處證書使用了SHA-1和SHA-256演算法 有多個指紋是為了相容老的客戶端)計算整個證書的hash指紋【證書內容hash值使用CA私鑰加密就是指紋】並和證書放在一起,client在開啟證書時,自己也根據指紋演算法計算一下證書的hash值,同時使用自己信任的根證書的公鑰解密hash指紋計算出原始hash,如果hash值不一致,則表明證書內容被篡改過; (4) 證書的簽名Certificate Signature Value和Certificate Signature Algorithm,對證書籤名所使用的Hash演算法和Hash值; (5) 簽發該證書的CA機構Issuer; (6) 該證書是簽發給哪個組織/公司資訊Subject; (7) 證書版本Version、證書序列號Serial Number以及Extensions擴充套件資訊等。      

 

上圖即是證書指紋校驗的過程,可能看到Client校驗證書的核心其實是CA公鑰解密原始指紋,CA公鑰從哪裡來,為了確保安全裝置系統會有一批自己信任的CA公鑰列表(根證書)。這些CA公鑰對應的一般是權威機構或組織,然後由這些權威機構頒發證書時會使用他們自己的私鑰去簽名(為證書生成指紋)。這樣就確保了只有權威機構頒發給各個網站的證書才會被客戶端校驗通過。   Filddler沒有這些證書裡公鑰對應的私鑰(CA只會把為完整頒發的證書對應的私鑰給網站的所有者),所以沒有辦法與客戶端完成TLS握手。Filddler為了完成握手只能自己為不同的站點生成證書, 不過自己的生成的證書肯定是用自己的私鑰簽名的,客戶端在自己信任的CA公鑰列表找不到對應根證書,肯定是不能通過證書校驗的。所以Filddler要求我們安裝他的根證書到裝置,這樣自己簽發的證書就可以通過證書校驗,自己就能解密https報文了。  

不能解密的原因

其實通過上面的描述也很明白了不能正常建立連線解密https報文的原因就是證書校驗失敗,我們的根證書安裝不夠完全。 從Android7.0以後,系統允許每個應用可以定義自己的可信CA集。有部分應用預設只會信任系統預裝的CA證書,而不會信任使用者安裝的CA證書(或者說是應用使用的開發框架預設只信任系統證書,因為開發者通常不關心這些配置,也不會去更改他)。而在Android中使用者安裝的證書都是使用者證書,所以無論是Filddler還是Charles我們都只是把他們的根證書安裝到了使用者證書,這些應用並不使用他們,所以我們的安裝的證書是無效的。

 

 

解決方法及操作方法

既然又知道了原因,那就總還是有辦法去解決的。我們只要把代理軟體的根證書安裝成系統證書就可以了。 實際上將證書安裝到系統區操作還是相對簡單的,將證書用指定的名稱放到指定的位置(/system/etc/security/cacerts/)就可以了 先將我們的根證書名稱改為<Certificate_Hash>.<Number> Certificate_Hash表示證書檔案的hash值,Number是為了防止證書檔案的hash值一致而增加的字尾(用0就行了) 下載自己的根證書FiddlerRoot.cer,使用openssl x509 -subject_hash_old -in <Certificate_File> 計算證書hash ,根據hash將證書重新命名為 269953fb.0 (269953fb是筆者證書的hash,大家的肯定不一樣) 然後將269953fb.0檔案複製到/system/etc/security/cacerts/  

 

完成後我們就可以看到代理軟體的證書出現在系統區了。   這裡還有一點需要單獨說明,/system/etc/security/cacerts/目錄的寫許可權,需要手機root許可權。 也就是說複製證書到該目錄需要您root自己的裝置。 關於Android手機的root,通常手機廠家都會有自己官方的教程,建議大家按官方的操作進行root 以小米手機為例 (http://www.miui.com/thread-12281379-1-1.html) 當前小米手機的root需要手機繫結小米賬號7天以上才能解鎖,解鎖後刷入開發版即可完成root 需要注意的是不是所有小米的手機都有對應的開發版的,所以購買測試裝置時留意下。(http://www.miui.com/download.html 這裡可以看下自己的手機有沒有開發版可以使用)   效果 現在證書也被安裝到了系統區,再回到Fiddler看下效果

 

再次開啟閒魚我們已經可以看到完整的https請求了。   下面我們找個請求修改下請求返回資料 藉助Fiddler外掛FreeHttp修改這個請求的返回資料將二手手機修改為二手馬總並將圖片也替換掉 (FreeHttp的使用請參考藉助FreeHttp任意篡改http報文 (使用·實現))  

 

再次開啟閒魚,可以看到經過代理的資料已經被篡改了(注意測試時清除鹹魚的快取及應用資料,以保證每次開啟APP都會請求firstdata