/ 作者簡(jiǎn)介 /
本篇文章轉(zhuǎn)自MegatronKing的博客,分享了Android中https抓包相關(guān)的解決方案,希望對(duì)大家有所幫助!
/ 前言 /
HTTP協(xié)議發(fā)展至今已經(jīng)有二十多年的歷史,整個(gè)發(fā)展的趨勢(shì)主要是兩個(gè)方向:效率和安全。效率方面,從HTTP1.0的一次請(qǐng)求一個(gè)連接,到HTTP1.1的連接復(fù)用,到SPDY/HTTP2的多路復(fù)用,到QUIC/HTTP3的基于UDP傳輸,在效率方面越來(lái)越高效。安全方面,從HTTP的明文,到HTTP2強(qiáng)制使用TLSv1.2,到QUIC/HTTP3強(qiáng)制使用TLSv1.3,越來(lái)越注重?cái)?shù)據(jù)傳輸?shù)陌踩浴?偠灾琀TTP協(xié)議的發(fā)展對(duì)用戶是友好的,但是對(duì)開發(fā)者而言卻不那么友善。
抓包是每個(gè)程序員的必修技能之一,尤其是在接口調(diào)試和程序逆向方面具有廣闊的用途。但是,隨著越來(lái)越多的通信協(xié)議使用加密的HTTPS,而且系統(tǒng)層面也開始強(qiáng)制規(guī)定使用HTTPS,抓包似乎是顯得越來(lái)越難了。
本篇博客,主要詳解Android平臺(tái)下,HTTPS抓包的常見(jiàn)問(wèn)題以及解決辦法。工欲善其事必先利其器,博客中以HttpCanary作為抓包工具進(jìn)行講解。更多HttpCanary的資料,請(qǐng)見(jiàn):
GitHub地址:
https://github.com/MegatronKing/HttpCanary
/ 抓包原理 /
幾乎所有網(wǎng)絡(luò)數(shù)據(jù)的抓包都是采用中間人的方式(MITM),包括大家常用的Fiddler、Charles等知名抓包工具,HttpCanary同樣是使用中間人的方式進(jìn)行抓包。

從上面這個(gè)原理圖,可以看出抓包的核心問(wèn)題主要是兩個(gè):
- MITM Server如何偽裝成真正的Server
- MITM Client如何偽裝成真正的Client
第一個(gè)問(wèn)題,MITM Server要成為真正的Server,必須能夠給指定域名簽發(fā)公鑰證書,且公鑰證書能夠通過(guò)系統(tǒng)的安全校驗(yàn)。比如Client發(fā)送了一條https://www.baidu.com/的網(wǎng)絡(luò)請(qǐng)求,MITM Server要偽裝成百度的Server,必須持有www.baidu.com域名的公鑰證書并發(fā)給Client,同時(shí)還要有與公鑰相匹配的私鑰。
MITM Server的處理方式是從第一個(gè)SSL/TLS握手包Client Hello中提取出域名www.baidu.com,利用應(yīng)用內(nèi)置的CA證書創(chuàng)建www.baidu.com域名的公鑰證書和私鑰。創(chuàng)建的公鑰證書在SSL/TLS握手的過(guò)程中發(fā)給Client,Client收到公鑰證書后會(huì)由系統(tǒng)會(huì)對(duì)此證書進(jìn)行校驗(yàn),判斷是否是百度公司持有的證書,但很明顯這個(gè)證書是抓包工具偽造的。為了能夠讓系統(tǒng)校驗(yàn)公鑰證書時(shí)認(rèn)為證書是真實(shí)有效的,我們需要將抓包應(yīng)用內(nèi)置的CA證書手動(dòng)安裝到系統(tǒng)中,作為真正的證書發(fā)行商(CA),即洗白。這就是為什么,HTTPS抓包一定要先安裝CA證書。
第二個(gè)問(wèn)題,MITM Client偽裝成Client。由于服務(wù)器并不會(huì)校驗(yàn)Client(絕大部分情況),所以這個(gè)問(wèn)題一般不會(huì)存在。比如Server一般不會(huì)關(guān)心Client到底是Chrome瀏覽器還是IE瀏覽器,是Android App還是IOS App。當(dāng)然,Server也是可以校驗(yàn)Client的,這個(gè)后面分析。
/ 安裝CA證書 /
抓包應(yīng)用內(nèi)置的CA證書要洗白,必須安裝到系統(tǒng)中。而Android系統(tǒng)將CA證書又分為兩種:用戶CA證書和系統(tǒng)CA證書。顧明思議,用戶CA證書是由用戶自行安裝的,系統(tǒng)CA證書是由系統(tǒng)內(nèi)置的,很明顯后者更加真實(shí)有效。
系統(tǒng)CA證書存放在/etc/security/cacerts/目錄下,名稱是CA證書subjectDN的Md5值前四位移位取或,后綴名是.0,比如00673b5b.0。考慮到安全原因,系統(tǒng)CA證書需要有Root權(quán)限才能進(jìn)行添加和刪除。
對(duì)于非Root的Android設(shè)備,用戶只能安裝用戶CA證書。
無(wú)論是系統(tǒng)CA證書還是用戶CA證書,都可以在設(shè)置->系統(tǒng)安全->加密與憑據(jù)->信任的憑據(jù)中查看:

/ Android7.0的用戶CA限制 /
Android從7.0開始系統(tǒng)不再信任用戶CA證書(應(yīng)用targetSdkVersion >= 24時(shí)生效,如果targetSdkVersion < 24即使系統(tǒng)是7.0+依然會(huì)信任)。也就是說(shuō)即使安裝了用戶CA證書,在Android 7.0+的機(jī)器上,targetSdkVersion >= 24的應(yīng)用的HTTPS包就抓不到了。
比如上面的例子,抓包工具用內(nèi)置的CA證書,創(chuàng)建了www.baidu.com域名的公鑰證書發(fā)給Client,系統(tǒng)校驗(yàn)此證書時(shí)發(fā)現(xiàn)是用戶CA證書簽發(fā)的,sorry。。。那么,我們?nèi)绻@過(guò)這種限制呢?已知有以下四種方式(低于7.0的系統(tǒng)請(qǐng)忽略)。
配置networkSecurityConfig
如果我們想抓自己的App,只需要在AndroidManifest中配置networkSecurityConfig即可:
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application android:networkSecurityConfig="@xml/network_security_config"
... >
...
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
這樣即表示,App信任用戶CA證書,讓系統(tǒng)對(duì)用戶CA證書的校驗(yàn)給予通過(guò)。更多相關(guān)信息,詳見(jiàn)
Network security configuration:
https://developer.android.com/training/articles/security-config
調(diào)低targetSdkVersion < 24
如果想抓一個(gè)App的包,可以找個(gè)歷史版本,只需要其targetSdkVersion < 24即可。然而,隨著googlePlay開始限制targetSdkVersion,現(xiàn)在要求其必須>=26,2019年8月1日后必須>=28,國(guó)內(nèi)應(yīng)用市場(chǎng)也開始逐步響應(yīng)這種限制。絕大多數(shù)App的targetSdkVersion都將大于24了,也就意味著抓HTTPS的包越來(lái)越難操作了。
平行空間抓包
如果我們希望抓targetSdkVersion >= 24的應(yīng)用的包,那又該怎么辦呢?我們可以使用平行空間或者VirtualApp來(lái)曲線救國(guó)。平行空間和VirtualApp這種多開應(yīng)用可以作為宿主系統(tǒng)來(lái)運(yùn)行其它應(yīng)用,如果平行空間和VirtualApp的targetSdkVersion < 24,那么問(wèn)題也就解決了。
在此,我推薦使用平行空間,相比部分開源的VirtualApp,平行空間運(yùn)行得更加穩(wěn)定。但必須注意平行空間的版本4.0.8625以下才是targetSdkVersion < 24,別安裝錯(cuò)了。當(dāng)然,HttpCanary的設(shè)置中是可以直接安裝平行空間的。
安裝到系統(tǒng)CA證書目錄
對(duì)于Root的機(jī)器,這是最完美最佳的解決方案。如果把CA證書安裝到系統(tǒng)CA證書目錄中,那這個(gè)假CA證書就是真正洗白了,不是真的也是真的了。由于系統(tǒng)CA證書格式都是特殊的.0格式,我們必須將抓包工具內(nèi)置的CA證書以這種格式導(dǎo)出,HttpCanary直接提供了這種導(dǎo)出選項(xiàng)。
操作路徑:設(shè)置 -> SSL證書設(shè)置 -> 導(dǎo)出HttpCanary根證書 -> System Trusted(.0)。

PS. 很不幸的HttpCanary v2.8.0前導(dǎo)出的證書名稱可能不正確,建議升級(jí)到v2.8.0以上版本操作。
導(dǎo)出.0格式的證書后,可以使用MT管理器將.0文件復(fù)制到/etc/security/cacerts/目錄下,或者通過(guò)adb remount然后push也可(這里稍微提一下,別在sdcard里找這個(gè)目錄)。
/ Firefox證書安裝 /
火狐瀏覽器Firefox自行搞了一套CA證書管理,無(wú)論是系統(tǒng)CA證書還是用戶CA證書,F(xiàn)irefox通通都不認(rèn)可。這種情況,我們需要將CA證書通過(guò)特殊方式導(dǎo)入到Firefox中,否則Firefox瀏覽網(wǎng)頁(yè)就無(wú)法工作了。
HttpCanary v2.8.0版本提供了Firefox證書導(dǎo)入選項(xiàng)。在設(shè)置 -> SSL證書設(shè)置 -> 添加HttpCanary根證書至Firefox 中:

點(diǎn)擊右上角復(fù)制按鈕將url復(fù)制到粘貼板,然后保持此頁(yè)面不動(dòng),打開Firefox粘貼輸入復(fù)制的url。

出現(xiàn)下載證書彈框后,一定要手動(dòng)勾上:信任用來(lái)標(biāo)志網(wǎng)站和信任用來(lái)標(biāo)志電子郵件用戶。然后確定即可。
/ 公鑰證書固定 /
證書固定(Certificate Pinning)是指Client端內(nèi)置Server端真正的公鑰證書。在HTTPS請(qǐng)求時(shí),Server端發(fā)給客戶端的公鑰證書必須與Client端內(nèi)置的公鑰證書一致,請(qǐng)求才會(huì)成功。
在這種情況下,由于MITM Server創(chuàng)建的公鑰證書和Client端內(nèi)置的公鑰證書不一致,MITM Server就無(wú)法偽裝成真正的Server了。這時(shí),抓包就表現(xiàn)為App網(wǎng)絡(luò)錯(cuò)誤。已知的知名應(yīng)用,比如餓了么,就采用了證書固定。
另外,有些服務(wù)器采用的自簽證書(證書不是由真正CA發(fā)行商簽發(fā)的),這種情況App請(qǐng)求時(shí)必須使用證書固定。
證書固定的一般做法是,將公鑰證書(.crt或者.cer等格式)內(nèi)置到App中,然后創(chuàng)建TrustManager時(shí)將公鑰證書加進(jìn)去。很多應(yīng)用還會(huì)將內(nèi)置的公鑰證書偽裝起來(lái)或者加密,防止逆向提取,比如餓了么就偽裝成了png,當(dāng)然對(duì)公鑰證書偽裝或者加密沒(méi)什么太大必要,純粹自欺欺人罷了。
證書固定對(duì)抓包是個(gè)非常麻煩的阻礙,不過(guò)我們總是有辦法繞過(guò)的,就是麻煩了點(diǎn)。
JustTrustMe破解證書固定
Xposed和Magisk都有相應(yīng)的模塊,用來(lái)破解證書固定,實(shí)現(xiàn)正常抓包。破解的原理大致是,Hook創(chuàng)建SSLContext等涉及TrustManager相關(guān)的方法,將固定的證書移除。
基于VirtualApp的Hook機(jī)制破解證書固定
Xposed和Magisk需要刷機(jī)等特殊處理,但是如果不想刷機(jī)折騰,我們還可以在VirtualApp中加入Hook代碼,然后利用VirtualApp打開目標(biāo)應(yīng)用進(jìn)行抓包。當(dāng)然,有開發(fā)者已經(jīng)實(shí)現(xiàn)了相關(guān)的功能。詳見(jiàn):
案例1:
https://github.com/PAGalaxyLab/VirtualHook
案例2:
https://github.com/rk700/CertUnpinning
不過(guò),這里CertUnpinning插件的代碼有點(diǎn)問(wèn)題,要改改。
導(dǎo)入真正的公鑰證書和私鑰
如果Client固定了公鑰證書,那么MITM Server必須持有真正的公鑰證書和匹配的私鑰。如果開發(fā)者具有真正服務(wù)端的公鑰證書和私鑰,(比如百度的公鑰證書和私鑰百度的后端開發(fā)肯定有),如果真有的話,可以將其導(dǎo)入HttpCanary中,也可以完成正常抓包。
在設(shè)置 -> SSL證書設(shè)置 -> 管理SSL導(dǎo)入證書 中,切換到服務(wù)端,然后導(dǎo)入公鑰證書+私鑰,支持.p12和.bks格式文件。
/ 雙向認(rèn)證 /
SSL/TLS協(xié)議提供了雙向認(rèn)證的功能,即除了Client需要校驗(yàn)Server的真實(shí)性,Server也需要校驗(yàn)Client的真實(shí)性。這種情況,一般比較少,但是還是有部分應(yīng)用是開啟了雙向認(rèn)證的。比如匿名社交應(yīng)用Soul部分接口就使用了雙向認(rèn)證。使用了雙向認(rèn)證的HTTPS請(qǐng)求,同樣無(wú)法直接抓包。
關(guān)于雙向認(rèn)證的原理
首先,雙向認(rèn)證需要Server支持,Client必須內(nèi)置一套公鑰證書 + 私鑰。在SSL/TLS握手過(guò)程中,Server端會(huì)向Client端請(qǐng)求證書,Client端必須將內(nèi)置的公鑰證書發(fā)給Server,Server驗(yàn)證公鑰證書的真實(shí)性。
注意,這里的內(nèi)置的公鑰證書有區(qū)別于前面第5點(diǎn)的公鑰證書固定,雙向認(rèn)證內(nèi)置的公鑰證書+私鑰是額外的一套,不同于證書固定內(nèi)置的公鑰證書。
如果一個(gè)Client既使用證書固定,又使用雙向認(rèn)證,那么Client端應(yīng)該內(nèi)置一套公鑰證書 + 一套公鑰證書和私鑰。第一套與Server端的公鑰證書相同,用于Client端系統(tǒng)校驗(yàn)與Server發(fā)來(lái)的證書是否相同,即證書固定;第二套SSL/TLS握手時(shí)公鑰證書發(fā)給Server端,Server端進(jìn)行簽名校驗(yàn),即雙向認(rèn)證。
用于雙向認(rèn)證的公鑰證書和私鑰代表了Client端身份,所以其是隱秘的,一般都是用.p12或者.bks文件+密鑰進(jìn)行存放。由于是內(nèi)置在Client中,存儲(chǔ)的密鑰一般也是寫死在Client代碼中,有些App為了防反編譯會(huì)將密鑰寫到so庫(kù)中,比如S匿名社交App,但是只要存在于Client端中都是有辦法提取出來(lái)的。
雙向認(rèn)證抓包
這里以S匿名社交App為例,講解下如何抓取使用了雙向認(rèn)證的App的HTTPS包。如果服務(wù)器使用了Nginx且開啟了雙向認(rèn)證,抓包時(shí)會(huì)出現(xiàn)400 Bad Request的錯(cuò)誤,如下:


有些服務(wù)器可能不會(huì)返回404,直接請(qǐng)求失敗。接下來(lái)看,如何使用HttpCanary配置雙向認(rèn)證抓包。
首先,解壓APK,提取出.p12或者.bks文件,二進(jìn)制的文件一般存放都在raw或者assets目錄。

將client.p12文件導(dǎo)入手機(jī),然后在HttpCanary的設(shè)置 -> SSL證書設(shè)置 -> 管理SSL導(dǎo)入證書中,切換到客戶端(因?yàn)樾枰浣oMITM Client),然后導(dǎo)入.p12文件。
由于雙向認(rèn)證的公鑰證書和私鑰是受密鑰保護(hù)的,所以需要輸入密碼:

一般通過(guò)逆向可以從APK中提取出密鑰,具體操作這里略過(guò)。輸入密鑰后,需要輸入映射域名,這里使用通配符*映射所有相關(guān)域名:

導(dǎo)入完成后如下:

可以點(diǎn)進(jìn)證書詳情查看細(xì)節(jié),這個(gè)client.p12文件包含公鑰證書和私鑰,是用于雙向認(rèn)證的。

配置完成后,重新進(jìn)行抓包,看看效果。

可以看到,之前400 Bad Request的兩個(gè)要求雙向認(rèn)證的請(qǐng)求成功了!
/ SSL重協(xié)商 /
有些服務(wù)器可能會(huì)開啟SSL重協(xié)商,即SSL/TLS握手成功后發(fā)送請(qǐng)求時(shí)服務(wù)器會(huì)要求重新握手。這種情況一般比較少,但是也不排除,已知的應(yīng)用比如 10000社區(qū) 就使用了SSL重協(xié)商。
由于Android系統(tǒng)對(duì)SSL重協(xié)商是有限支持,所以部分系統(tǒng)版本抓包會(huì)失敗,表現(xiàn)為網(wǎng)絡(luò)異常。在Android 8.1以下,SslSocket是完全支持SSL重協(xié)商的,但是SSLEngine卻是不支持SSL重協(xié)商的,而HttpCanary解析SSL/TLS使用的是SSLEngine。在Android 8.1及以上,SSLEngine和SslSocket統(tǒng)一了實(shí)現(xiàn),故是支持SSL重協(xié)商的。
所以,如果確認(rèn)服務(wù)器使用了SSL重協(xié)商,請(qǐng)使用8.1及以上版本系統(tǒng)進(jìn)行抓包。
/ 非Http協(xié)議抓包 /
如果確認(rèn)了以上幾點(diǎn),HttpCanary仍然抓包失敗,那么極有可能使用的并非是HTTP協(xié)議。比如像微信聊天,視頻直播等,使用的就不是HTTP協(xié)議,這種情況需要使用其它的抓包工具,比如Packet Capture這種直接解析TCP/UDP協(xié)議的,但是往往非HTTP協(xié)議的數(shù)據(jù)包即使抓到了也無(wú)法解析出來(lái),因?yàn)榇蟾怕识际嵌M(jìn)制而非文本格式的。
/ 總結(jié) /
抓包是個(gè)技術(shù)活兒,需要對(duì)網(wǎng)絡(luò)協(xié)議有大致的了解,對(duì)抓包感興趣的同學(xué)可以多查閱TCP、UDP、SSL/TLS、HTTP等相關(guān)資料。
HttpCanary是專業(yè)的HTTP協(xié)議抓包工具,專注HTTP協(xié)議三十年(吹過(guò)頭了),不過(guò)目前還不支持QUIC/HTTP3這種新協(xié)議,等QUIC/HTTP3正式應(yīng)用起來(lái)再說(shuō)吧。