1.大綱
主要是從幾個點來了解安卓APP通用的解決辦法:
- wifi代理抓包
- vpn抓包
- 客戶端ssl驗證解決方案
- 服務(wù)器ssl驗證解決方案
- 雙向ssl驗證
- 非http協(xié)議抓包
- 自有的ssl驗證協(xié)議
1.wifi代理抓包
當我們準備抓一個app的時候倒庵,第一件事肯定是抓包褒墨。傳統(tǒng)的抓包解決方案是電腦安裝Fiddler/Charles,手機WIFI配置好代理擎宝。給手機流量代理到我們的抓包軟件郁妈,這樣我們就可以完成對app的抓包:
一般沒有證書效驗的app,或者抓包檢測的app我們都可以通過這種辦法進行抓包绍申。但是這里我們并不推介這種抓包的辦法噩咪。我個人的習(xí)慣一般是使用vpn抓包的方式顾彰。
因為網(wǎng)上很多對wifi配置代理抓包的文章,這里就不一一概述胃碾。主要就是wifi設(shè)置代理涨享,app安裝抓包軟件的證書。
這里簡單的說說我們通過抓包軟件抓到的包的原理仆百,方便后面理解為什么需要過ssl驗證:
這里用猿人學(xué)一張圖簡單來說明厕隧,非常簡單明了。就是客戶端 -> 抓包軟件 -> 服務(wù)端俄周。
1.1 優(yōu)點
wifi代理抓包的優(yōu)點可能就是配置非常非常方便吁讨,導(dǎo)入抓包軟件的證書后也可以抓https的包。
1.2 缺點
相對于vpn抓包方案峦朗,wifi代理抓包最容易被檢測建丧。
1.2.1 wifi代理檢測
缺點就是很容易被檢測是否使用了代理:
System.getProperty("http.proxyHost")
System.getProperty("http.proxyPort")
如何判斷app進行了wifi代理檢測?
配置好代理波势,打開抓包軟件翎朱,可以抓到app的其它請求但是抓不到這個app的數(shù)據(jù),app直接顯示無網(wǎng)絡(luò)尺铣。這時候可能就是wifi代理被檢測拴曲,或者有ssl證書效驗。
wifi代理檢測用的app還是比較多迄埃,建議采用vpn抓包的方式規(guī)避疗韵。
1.2.2 強制代理
或者是某些APP直接強制使用代理:
OkHttpClient client = new OkHttpClient().newBuilder().proxy(Proxy.NO_PROXY).build();
和我們py的requests一樣兑障,安卓也可以設(shè)置代理侄非。設(shè)置請求走默認代理而不走系統(tǒng)代理,防止被抓包流译。
如何判斷app是進行了強制代理逞怨?
設(shè)置了wifi代理后,關(guān)閉抓包軟件福澡,app依舊可以正常請求網(wǎng)絡(luò)叠赦。可能是app強制進行了代理轉(zhuǎn)發(fā)革砸,或者壓根沒走http協(xié)議除秀。
強制代理的情況很少,目前沒有遇見過算利,這時候可以通過tcpdump抓取網(wǎng)絡(luò)報文可以發(fā)現(xiàn)App直接和服務(wù)器建立了連接而沒有和代理建立連接
2 VPN抓包
vpn抓包個人習(xí)慣使用Postern+Charles的組合册踩。這里非常推介,所以寫一下安裝和配置的過程
2.1 環(huán)境
簡單的介紹一下我的環(huán)境:
- Postern-3.1.2
- charles-proxy-4.6.2-win64
- win 10
- 安卓8效拭,面具root
注意電腦的防火墻要關(guān)閉暂吉,否則可能導(dǎo)致流量無法轉(zhuǎn)發(fā)胖秒。之前出現(xiàn)過公司電腦不行,個人筆記本可以的情況慕的。然后關(guān)閉公司電腦后charles才能連接到手機阎肝。害我折騰了半天
2.2 安裝配置
先給我們的手機安裝Postern,電腦安裝charles肮街。然后配置charles
2.2.1 Charles配置
打開charles設(shè)置proxy风题,點擊proxy->proxy-settings,開啟socks然后配置一個端口嫉父,我這里給的是8889俯邓,使用socks代理來轉(zhuǎn)發(fā)流量。
保存后再點擊proxy->ssl proxying后熔号,配置抓https的包
規(guī)則稽鞭,兩個都填*就行了
2.2.2 charles導(dǎo)出證書
從charles導(dǎo)出證書,注意charles導(dǎo)出證書的時候會導(dǎo)出在你選擇的路徑上一級(有點坑)
推送到手機引镊,并安裝該證書
adb push xxx.pem /sdcard
安卓7以后不信用戶目錄的證書朦蕴,所以需要把證書移至系統(tǒng)證書目錄。利用面具的Move Certificates模塊來移動證書弟头。
重啟自動遷移證書吩抓。
2.2.3 沒有面具遷移證書的辦法
有時候手機使用的su去進行的root,沒有使用面具赴恨。所以這么好的插件就無法使用了疹娶,這時候我們可以用命令進行操作:
先安裝charles證書,再獲取root權(quán)限
su
mount -o rw,remount -t auto /system
執(zhí)行后伦连,通過 mount | grep /system 查看雨饺,很明顯有了rw權(quán)限。還有就是mount的時候要加上-t auto惑淳,不然我手機提示設(shè)備繁忙额港。
再移動用戶證書到系統(tǒng)證書:
mv /data/misc/user/0/cacerts-added/* /system/etc/security/cacerts/
執(zhí)行完后考慮到系統(tǒng)安全,最好改回ro權(quán)限:
mount -o ro,remount -t auto /system
驗證一下證書是否在系統(tǒng)目錄:
2.2.4 Postern配置
charles配置好后歧焦,打開手機的Postern添加一個代理
名字隨便取移斩,地址是電腦的局域網(wǎng)ip,端口是之前設(shè)置的socket端口绢馍,然后協(xié)議選擇socks5向瓷,保存。
配置代理規(guī)則舰涌,添加一個規(guī)則猖任。匹配類型是匹配所有地址,動作是通過代理連接舵稠,代理組選擇剛配置好的代理即可超升。
app設(shè)置vpn為charles的代理后入宦,正常情況下會彈出這個提示:
這樣就配置就可以通過vpn抓包的辦法去抓包了。
2.3 優(yōu)點
相較于wifi代理抓包的辦法室琢,vpn檢測相對較少乾闰。而且一次配置終生受益。
2.4 缺點
雖然vpn抓包的方式非常棒盈滴,但是也并不是全能的涯肩,它也是可以被檢測的。
public void isDeviceInVPN() {
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (name.equals("tun0") || name.equals("ppp0")) {
Log.i("TAG", "isDeviceInVPN current device is in VPN.");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
可以通過判斷網(wǎng)絡(luò)接口名字包含 ppp0 或 tun0是否有vpn代理巢钓,當然也有很多其它的辦法進行檢測病苗。但是對app進行vpn檢測的還是很少,除非是安全性要求較高的app症汹。
并且現(xiàn)在的許多app都開始增加了app抓包的防范硫朦,比如說ssl 單向/雙向驗證,socket協(xié)議背镇,某些有實力的大廠自己實現(xiàn)一套協(xié)議咬展。
3 客戶端ssl驗證
在抓https的包的時候我們經(jīng)常涉及到一個證書的問題,再說這個之前簡單的說說http和https:
http是明文傳播瞒斩,易被攔截和修改破婆。
https在HTTP的基礎(chǔ)上加了一安全層(SSL或TSL)
這也就引出了我們抓https包的時候,容易遇見的ssl證書問題胸囱。
https實際上就是http+ssl祷舀。由于http發(fā)送的數(shù)據(jù)直接就是明文。安全性非常差烹笔。https會在數(shù)據(jù)發(fā)送前裳扯,先用ssl進行加密
客戶端ssl驗證簡單來說就是客戶端驗證服務(wù)端返回的證書,從剛才第一點說的wifi代理抓包的原來來說箕宙,如果我們使用了中間人軟件來抓包嚎朽,其實真正請求服務(wù)端和響應(yīng)客戶端的是我們抓包軟件铺纽。這時候客戶端驗證的是抓包軟件的證書柬帕,所以肯定驗證會失敗狡门!
剛好這里有hatch的一個案例陷寝,我們通過這個案例來學(xué)習(xí)如何bypass 客戶端的ssl驗證。
3.1 案例
該app是波蘭某電商app其馏,剛好app客戶端驗證了服務(wù)端的證書凤跑。
3.1.1 vpn抓包
先看看我們vpn抓包的表現(xiàn)效果,打開我們的charles和postern叛复。然后打開app進行抓包:
app顯示:
雖然看不懂波蘭語仔引,但是大概的意思應(yīng)該是沒有網(wǎng)絡(luò)連接了
charles顯示:
抓包都是連接失敗扔仓,請求里面也提示了證書問題:SSL handshake with client failed: An unknown issue occurred processing the certificate (certificate_unknown)
3.1.2 bypass ssl pinning
一般這種情況都是app驗證了服務(wù)端的證書,服務(wù)端驗證app證書的app非常少咖耘,因為太消耗服務(wù)器資源翘簇。除了soul之前是雙向驗證之外(后面新版本好像也取消了雙向驗證)
這里推介使用frida 的一個bypass 腳本,就是hook掉驗證的代碼即可:
https://github.com/WooyunDota/DroidSSLUnpinning/blob/master/ObjectionUnpinningPlus/hooks.js
下載這段通用的hook腳本儿倒,先啟動我們的frida server:
ail@aildeMacBook-Pro ~ % adb shell
hammerhead:/ $ su
hammerhead:/ # cd /data/local/tmp/
hammerhead:/data/local/tmp # ./frs14
使用spwan的方式hook:
ail@aildeMacBook-Pro ObjectionUnpinningPlus % workon firda14
ERROR: Environment 'firda14' does not exist. Create it with 'mkvirtualenv firda14'.
ail@aildeMacBook-Pro ObjectionUnpinningPlus % workon frida14
(frida14) ail@aildeMacBook-Pro ObjectionUnpinningPlus % frida -U -f pl.allegro -l hooks.js --no-pause
____
/ _ | Frida 14.2.18 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://frida.re/docs/home/
Spawned `pl.allegro`. Resuming main thread!
[Nexus 5::pl.allegro]-> message: {'type': 'send', 'payload': 'Custom, Empty TrustManager ready'} data: None
message: {'type': 'send', 'payload': 'com.squareup.okhttp not found'} data: None
message: {'type': 'send', 'payload': 'registerClass from hostnameVerifier >>>>>>>> Missing implementation for: boolean verify(java.lang.String, javax.net.ssl.SSLSession)'} data: None
message: {'type': 'send', 'payload': 'Xutils hooks not Found'} data: None
message: {'type': 'send', 'payload': 'httpclientandroidlib Hooks not found'} data: None
message: {'type': 'send', 'payload': 'OpenSSLSocketImpl pinning'} data: None
message: {'type': 'send', 'payload': 'Trustkit pinner not found'} data: None
[-] Cronet pinner not found
message: {'type': 'send', 'payload': 'OpenSSLSocketImpl.verifyCertificateChain'} data: None
message: {'type': 'send', 'payload': 'Overriding SSLContext.init() with the custom TrustManager'} data: None
message: {'type': 'send', 'payload': 'Overriding SSLContext.init() with the custom TrustManager'} data: None
message: {'type': 'send', 'payload': 'Overriding SSLContext.init() with the custom TrustManager'} data: None
message: {'type': 'send', 'payload': 'Overriding SSLContext.init() with the custom TrustManager'} data: None
message: {'type': 'send', 'payload': 'Overriding SSLContext.init() with the custom TrustManager'} data: None
message: {'type': 'send', 'payload': 'Overriding SSLContext.init() with the custom TrustManager'} data: None
message: {'type': 'send', 'payload': 'Overriding SSLContext.init() with the custom TrustManager'} data: None
message: {'type': 'send', 'payload': 'OpenSSLSocketImpl.verifyCertificateChain'} data: None
message: {'type': 'send', 'payload': 'OpenSSLSocketImpl.verifyCertificateChain'} data: None
message: {'type': 'send', 'payload': 'OpenSSLSocketImpl.verifyCertificateChain'} data: None
message: {'type': 'send', 'payload': 'OpenSSLSocketImpl.verifyCertificateChain'} data: None
message: {'type': 'send', 'payload': 'OpenSSLSocketImpl.verifyCertificateChain'} data: None
抓包成功:
當然解決方案不止這一種版保,我們還可以hook掉加載證書的地方,給證書替換成我們抓包軟件的證書就行
4 服務(wù)端ssl驗證
服務(wù)端ssl驗證就是服務(wù)端驗證ssl證書夫否,這時候就需要我們找到app的證書和密碼彻犁。然后將app中內(nèi)置的證書導(dǎo)入到Charles中去。
一般通用的找證書的辦法就是反編譯apk后凰慈,直接通過grep搜索p12后綴的文件名汞幢。但是一些app會將證書改成jpg或者gif等其它后綴名。這時候我們可以hook app加載證書的地方微谓,輸出證書路徑和密碼急鳄。
這時候我們可以使用肉絲巨巨的r0capture,github地址是:
https://github.com/r0ysue/r0capture
r0capture不止提供了證書導(dǎo)出的功能堰酿,還提供了非常強大的功能疾宏,這里因為我用的比較少后面有機會進行補充。
找到證書后触创,再charles的Proxy→SSL Proxy Settings→Client Certificates→Add添加新的證書即可坎藐。
5 雙向驗證
雙向驗證的app太少了,這里沒有案例哼绑。參考單向的客戶端驗證和服務(wù)端驗證的解決方案岩馍。只是需要同時解決客戶端驗證和服務(wù)端驗證證書的問題。
6 非http協(xié)議抓包
現(xiàn)在一些app不走http協(xié)議抖韩,而選擇一些開源的非http協(xié)議蛀恩。比如某手的quic協(xié)議和某寶的Spdy協(xié)議,默認并不采用傳統(tǒng)的http或者https協(xié)議茂浮。
這種情況下我們?nèi)绾芜M行抓包呢双谆?
6.1 某寶抓包
我們以某寶為例,淘寶采用的是Spdy協(xié)議席揽,如果要直接去抓這個協(xié)議的數(shù)據(jù)估計要去研究該協(xié)議了或者直接hook app構(gòu)造參數(shù)的地方顽馋,輸出參數(shù)內(nèi)容,避免直接抓包需要研究一個協(xié)議帶來的時間成本幌羞。
但是索性的是不論是某寶還是某手寸谜,某團都提供了一個降級成http協(xié)議方案,當他們協(xié)議使用的服務(wù)器無法連接時會采用http協(xié)議去連接其他服務(wù)器属桦。所以這里就提出來了兩個解決方案:
- iptables 直接干掉所有tcp或者udp這種可疑的協(xié)議熊痴,使他們被迫降級成http協(xié)議
- hook掉開關(guān)他爸,直接讓app采用http協(xié)議
這里只說hook的方案:
這里就是它的開關(guān),我們hook后直接返回false就行:
Java.perform(
function(){
var SwitchConfig_2 = Java.use('mtopsdk.mtop.global.e')
SwitchConfig_2.d.implementation = function(){
return false
}
SwitchConfig_2.e.implementation = function(){
return false
}
}
)
hook后app就會采用http協(xié)議了
7 自集成的SSL庫
除了使用tcp協(xié)議外果善,現(xiàn)在還有一些有實力的大廠APP基于開源的SSL庫讲逛,自己集成了一套SSL庫。這樣我們傳統(tǒng)的單向/雙向證書解決的方案又行不通了岭埠。因為它是自己實現(xiàn)的一套SSL庫盏混,和安卓默認的SSL效驗的地方肯定不一樣。
這里我們就以最新版本的抖音為例惜论。