一次app抓包引發的Android分析(續)
0x00起因
首先說明下,本文不是正統的《一次app抓包引發的Android分析記錄》的續篇,只是分析的某APP和起因是一樣的,故借題。然而本文所做的分析解決了《一次app抓包引發的Android分析記錄》所留下的問題,故稱為續。
移動應用已不像起初,burpuite代理改遍天下。交叉編譯、傳輸加密、DEX加殼等防護方式開始再慢慢應用,我們已經很難再肆意的抓包改資料了。
文中所分析的APP也是,將加密演算法交叉編譯至so庫中,對傳輸引數進行整體加密。分析arm彙編程式碼是一條路子,但已然令大部分人望而生畏。然而安全總是充滿著各種奇思淫意,就算不走彙編,依舊能找到其它路子。
接下來透過這篇文章向各位分享下分析過程。
0x01整體思路
我們此次分析的目的是為了還原傳輸過程中的request和response,當然更重要的是要能控制request中的引數。為此我們需要了解引數是如何加密的。透過《一次app抓包引發的Android分析記錄》我們知道加密函式是libGoblin.so中的e函式,但似乎透過它要還原出解密函式不是那麼容易。
不過既然知道了引數是何函式進行加密,那我們只要再知道引數是什麼樣的格式,直接呼叫這個函式來加密偽造後的資料,而後抓包替換掉對應的引數不就可以了嗎?
為此我採取的策略是在json資料進入加密函式之前將其截獲,這樣就能知道引數是以什麼樣的格式傳遞。知道了具體的引數格式,便可偽造資料抓包改包。
0x02解析request
首先來看下一個request,如下圖引數都是加密的,本小節目的就是為了搞清楚下面這一串是什麼東西。
萬年不變的第一步,apktool反編譯、dex2jar反編譯。當然程式碼混淆了,不過這不影響我們分析。android傳送網路請求主要採用JDK的HttpURLConnection和Apache的HttpClient,搜尋跟這兩個相關的函式和欄位以及請求的URL。而這些特徵函式和欄位,通常是不會進行混淆的。Andbug動態跟蹤亦可,本次通文采用靜態程式碼分析,故如此!
透過AndroidManifest.xml瞭解app的結構,找到主包名等等。jd-gui檢視java程式碼,request是POST請求,所以搜尋post、HttpPost字眼看看:
利用如上方式,反覆透過搜尋http請求方法及請求URL等特徵縮小範圍,最終定位到AbstractHttpRequest類,該類是抽象類並擁有很多有趣的方法。而CommonTask類擴充套件了AbstractHttpRequest類。透過閱讀程式碼知道該類為關鍵類。《一次app抓包引發的Android分析記錄》文中亦說明了,此處不再贅述!
而c引數被AbstractHttpRequest的chrome()處理,跟進該函式:
str為字串常量“6000lex”和json資料一起進入NetworkParam的convertValue(),跟進:
在convertValue()中最終呼叫了加密函式,因此我們在Goblin.e加密paramString1和paramString2之前將這兩個引數透過log列印出來就知道請求的資料到底是什麼東西了。
用文字編輯器開啟com.xxx.net下的NetworkParam.smali定位到convertValue()函式,在Goblin.e處新增如下程式碼:
透過修改smali程式碼將加密前後的字串列印出來,重打包簽名後安裝,檢視logcat日誌:
如上圖所示,現在我們已經知道了引數傳遞的格式了。然而日誌中還出現了預料之外的資料。照上分析p1應該為常量“6000lex”,而圖中卻出現了時間戳。對比傳輸的資料(下圖),發現上圖第二個request-e是b引數的密文。而b的明文正是登陸資訊。搜尋convertValue發現b也是呼叫該函式加密,故導致此現象。
透過如上分析,最終確定了登陸時c和b格式分別為:
c={“adid":"f854a2765b5dcc3e","cid":"C2487","gid":"5EFD7B7D-A648-2F40-9D53-D42F1BCCC468","ke":"1410276980456","ma":"","mno":"310260","model":"sdk","msg":"","nt":"burp","osVersion":"4.4_19","pid":"10010","sid":"352C4C51-F09F-C929-E03A-B5E311BA2808","t":"p_ucLogin","uid":"000000000000000","un":"","vid":"60001060"}
b={"loginT":1,"paramJson":"","prenum":"","pwd":"xxxx","uname":"xxxx","userSecurity":{"communityCode":"0","imeiCode":"000000000000000","imsiCode":"310260000000000","osType":"14","stationId":"0","terminalType":"02"}}
由於c中的ke和b的加密因子一樣。猜測服務端解密時,先透過“6000lex”解密出c,獲取ke的值,再透過ke解密出b引數。
實際上上面那個猜想是正確的,而《一次app抓包引發的Android分析記錄》中所做的猜想也是正確的。Goblin.d()就是解密函式。
《一次app抓包引發的Android分析記錄》中所記錄的:
利用Goblin.d()解密後為:
至於《一次app抓包引發的Android分析記錄》中為何解密會失敗,下面章節會說明到!
0x03解析response
如下,response響應報文亦是亂七八糟的一堆。這樣就算我們能改包了,也無法判斷結果到底是否正確。因此在開始改包之前,必須先解密response。
透過0x02的分析知道app是用Apache的HttpClient進行post請求。而HttpClient獲取response報文是透過getEntity()函式。故直接搜尋getEntity,有了0x02的分析,輕鬆定位到AbstractHttpRequest類的getResult方法,由於此處dex2jar反編譯錯誤。所以直接分析smali程式碼。
開啟AbstractHttpRequest.smali:
getEntity()結果為v0,v0最終進入到dealWithResponse(),而dealWithResponse返回一個Object而不是String,所以繼續跟進dealWithResponse():
dealWithResponse只有一個引數,故跟蹤p1:
p1最終進行到parseProtoResponse()和buildHttpResultString()中,跟進parseProtoResponse():
parseProtoResponse()中呼叫了Goblin中的函式,可能這個函式就是我們想要的。先放著。我們再看看buildHttpResultString()這個函式,此處直接jd-gui檢視:
buildHttpResultString()是個抽象類,具體程式碼在CommonTask、MultiTask、PollTask中實現。而這幾個類中最終都是呼叫Response類的pareResponse()方法,而pareResponse()也呼叫了Goblin中的解密函式。parseProtoResponse()也是Response類一個方法。所以我們將解密響應報文的函式定位在這兩個函式中。接下來就是驗證下是否正確。
修改Response.smali中的parseProtoResponse()和pareResponse(),將解密後的結果透過日誌列印出來:
在parseProtoResponse()和pareResponse()中標記了幾處,但最終只出現如上結果,故確認解密函式為pareResponse()。
0x04控制request
至此我們已經完全看到了request和response傳輸的明文內容,如下所示,一個完成的請求響應過程:
當然我們目的是為了測試,所以必須要能改request請求才可以。
嘗試一:
既然我們知道了request的加密函式,那麼在自己的APP中呼叫,加密完替換掉burp攔截到得資料即可。
新建一個app引用同樣的Goblin類,進行加解密測試,測試程式碼如下:
加密可以加密,但實際上加密結果和原始的密文不一樣。而解密卻失敗,失敗的原因正和《一次app抓包引發的Android分析記錄》中所做的測試一樣。
發生了JNI WARNING:NewStringUTF input is not valid Modified UTF-8錯誤,而這個錯誤是由於在JNI中,google修改了UTF8的標準,當正常UTF8中包含了不符合這個標準的位元組時,checkJNI函式就會報這個錯,導致應用崩潰。
在google中亦有關於此錯誤的報告:
https://code.google.com/p/android/issues/detail?id=64892
https://code.google.com/p/android/issues/detail?id=25386
然而直接將原始密文拿來解密卻可以正常解碼,所以問題出可能出在加密環節。折騰半天,沒有發現適合我們這邊的處理辦法。但在測試過程中發現,在原始APP應用中透過修改smali程式碼可以正常加解密。難道so中還有檢測環境?當然這個得檢視arm彙編才知道。
既然如此,那直接改造原始app吧。
嘗試二:
改造思路:在原始APP內部中攔截引數—>透過外部修改攔截到的引數—>放行引數,後續按正常流程進行
在原始app內部中攔截引數,只要在讓引數再進入加密之前進入到我們控制的函式中即可。為了方便說明。我們先來看看如何透過外部修改內部攔截的引數。
藉助android的廣播機制,我們能實現實時的跟app進行互動。因此,也能實時的讓外部跟app內部進行資料互動。新建一個myBroadcast類,程式碼如下:
#!java
public class myBroadcast extends BroadcastReceiver{ public static boolean sw = false; public static boolean swc = false; public static boolean swb = false; public static String datac = null; public static String datab = null; public static String kec = "6000lex";
// 接收廣播
public void onReceive(Context context, Intent intent) {
Log.i("broadcast-intent",intent.toString());
String action = intent.getAction();
if(action.equals("com.test.broadcast1")){
sw = intent.getBooleanExtra("sw", true); // 控制是否攔截
swc = intent.getBooleanExtra("swc", false);// 控制攔截c
swb = intent.getBooleanExtra("swb",false); // 控制攔截b
}
else if(action.equals("com.test.broadcast2")){
datac = intent.getStringExtra("datac"); // 接收c
datab = intent.getStringExtra("datab"); // 接收b
}
Log.v("receiver-data:sw:swc:swb|datac:datab",sw+":"+swc+":"+swb+"|"+datac+":"+datab);
}
// 延遲函式
public static void delay(){
try {
Thread.currentThread();
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 修改函式
public static String alter(String ke,String json){
Log.v("json-data-1",ke+":"+json);
while(sw){
delay();
if(ke.equals(kec)){
if(swc){
if(datac!=null){
json = datac;
Log.v("json-data1-2",json);
break;
}
}
else{
break;
}
}
else{
if(swb){
if(datab!=null){
json =datab;
Log.v("json-data2-2",json);
break;
}
}
else{
break;
}
}
}
return json;
}
}
利用onReceive實時接收外部資料,利用alter函式檢測外部是否傳送了攔截指令。當收到攔截的指令時,呼叫delay()函式進行迴圈延時,直到接收到偽造的資料或者關閉攔截。
將myBroadcast類轉化為smali程式碼。把myBroadcast.smali放在NetworkParam.smali同級目錄下。並在AndroidManifest.xml中註冊myBroadcast這個receiver,且將exproted設定為true。
即允許該receiver被外部訪問。
這樣我們就能在NetworkParam.smali中使用這個receiver
接下來,我們來修改NetworkParam.smali,使其接收我們的指令。在Goblin.e前後修改如下:
讓p0進入alter()函式,修改後結果儲存至v3,讓v3替換p0進入到Goblin.e()中。“request-data-1”列印出原來的引數,“json-data-1”列印出進入alter函式中的引數。“request-data-2”列印出修改後的引數,“request-data-e”,列印出加密後的引數。
修改完後,重新打包、簽名、安裝,執行檢視logcat日誌:
1、 預設設定是不攔截,所以logcat日誌中應該能完整得看到
“request-data-1”—>“json-data-1”—>“request-data-2(不變)”—>“request-data-e”—>“response-data”
測試如下:
和預期一樣
2、 設定攔截指令,即sw為true,攔截資料進入迴圈延遲並等待接收偽造的引數。所以logcat日誌中應該只能看到“request-data-1”—>”json-data-1”,測試如下:
am命令傳送廣播:
Logcat日誌,如預期,資料被攔截,應用一直處於載入當中:
3、傳送偽造資料,按照設計。此時logcat日誌中應該能看到
“request-data-1”—>“json-data-1”—>“request-data-2(修改後)”—>“request-data-e”—>“response-data”
測試如下:
傳送廣播指令,攔截b引數,放行c引數:
Logcat日誌顯示如下,c(帶600lex的)被放行,b(帶時間戳的)被攔截:
am命令傳送datab資料,將uname改為test222:
結果如下,receiver接收到偽造的資料後,將原b引數進行修改後放行:
和預期一樣。
0x05結語
最終,我們實現了檢視傳輸明文資訊,並控制了request請求報文。即便它採用so加密。我們依舊可以盡情的測試了~
相關文章
- 一次app抓包引發的Android分析記錄2020-08-19APPAndroid
- Android App 如何防止抓包2022-12-19AndroidAPP
- 記一次抓包和破解App介面2020-07-25APP
- BLE抓包分析2020-12-08
- wireshark抓包分析2015-06-15
- APP抓包神器drony2024-07-10APP
- android開發--防止介面被抓包2021-09-09Android
- APP常用抓包技術2020-11-20APP
- IPSEC隧道抓包分析2019-09-27
- tcpdump抓包分析詳解2013-07-17TCP
- 記一次簡單的wireshark抓包TCP2020-12-20TCP
- Charles抓包—App資源代理2018-10-29APP
- 模擬器上app抓包2024-05-01APP
- Android https 抓包指南2020-12-28AndroidHTTP
- 一次聊天引發的思考--java併發包實戰2019-03-20Java
- 手機抓包+注入黑科技HttpCanary——最強大的Android抓包注入工具2018-12-23HTTPPCAAndroid
- 通過抓包分析 HTTPS2018-10-15HTTP
- UDP協議抓包分析 -- wireshark2020-10-03UDP協議
- Wireshark和TcpDump抓包分析心得2016-06-18TCP
- 詳細Fildder抓包Android教程2017-10-26Android
- wireshark安裝使用與tcpdump的抓包分析2018-05-08TCP
- 使用WireShark抓包分析TCP協議2020-12-05TCP協議
- IP和TCP抓包分析實驗2021-12-02TCP
- tcpdump抓包分析NAT ping不通2024-05-24TCP
- Android平臺HTTPS抓包解決方案及問題分析2019-04-27AndroidHTTP
- 使用mumu模擬器抓包 andriod app2023-12-07APP
- Android 使用Fiddler4抓包2019-04-19Android
- Android抓包 Charles http介面除錯2017-11-13AndroidHTTP除錯
- android開發透過wireshark實現flutter應用抓包2024-08-21AndroidFlutter
- 使用tcpdump+wireshark抓包分析網路資料包2018-12-16TCP
- 在 Android 上利用 tcpdump 進行抓包2017-08-02AndroidTCP
- Android系統手機端抓包方法2014-12-13Android
- 爬蟲分析利器:谷歌Chrome F12抓包分析2018-12-02爬蟲谷歌Chrome
- ? 抓包分析 TCP 建立和斷開連線的流程2019-03-31TCP
- 原來你是這樣的Websocket--抓包分析2018-05-10Web
- HTTP抓包2017-02-08HTTP
- tcpdump抓包2024-04-18TCP
- APP爬蟲-雙向認證抓包的兩種方法2020-03-27APP爬蟲