目錄
- 第一章 · 起源
- 第二章 · 嘗試
- 第三章 · 脫獄
- 第四章 · 柳暗花明
- 第五章 · 終結
第一章 · 起源
某日,想做個爬蟲工具,爬某個網站上的資料已做實驗之用,大家都知道爬pc網頁上的資料有幾個常見的問題:首先是資料不規范需要自己決議html,第二現在很多網站是前端動態渲染的,直接爬取的html可能就是個靜態頁面什么也沒有,還需要執行js才能生成最終的頁面,因此就考慮,能否用它App的介面去爬資料,因為一般App呼叫的介面回傳的都是json格式,決議起來比較方便,
第二章 · 嘗試
既然要抓取App的介面,肯定需要抓包,現在介面一般都用的https通信,直接抓二進制的tcp包是無法解密的,想到用Fiddler,它的原理很簡單,就是啟動一個https代理服務器,手機上設定Fidder啟動的代理服務ip和埠,這是第一步,第二步就是證書,Fiddler會自己生成一個證書,把這個證書作為根證書匯入的手機中,這時手機對Fiddler回傳的證書就是可信任的,通信的時候,Fiddler作為客戶端它會和服務端建立https連接,得到解密后的資料,然后自己啟動一個https的服務,把資料回傳給手機端,手機端這個時候實際訪問的是Fillder啟動的https服務,驗證的證書也是Fillder自己頒發的,至于https如何驗證簽名如何加密通訊的這里就不做展開了,有興趣的可以自己查閱,
但是,必須要說的是這樣配置代理用Fiddler抓包的確可以抓到用手機瀏覽器訪問的內容,現在App為了防止代理模式抓https的包,一般都會把證書放到自己的App中,驗證https server回傳的證書和App中的證書是否一致,這個時候server回傳的證書是Fiddler自己頒發的,肯定和App本地的不一樣所以App這時是無法訪問介面的,
為什么Fiddler要自己頒發一個證書呢?這是由于Fiddler需要把和服務端通信的內容解密,所以它作為一個正常的client端先和服務端建立連接,驗證服務端的證書,用服務端的證書上的公鑰加密自己生成的對稱秘鑰,服務端拿到對稱秘鑰后用私鑰解密,雙方都知道對稱秘鑰,之后的通信用對稱秘鑰加解密,同理Fiddler作為App的代理服務器,也是要和app建立https連接的,這時如果直接回傳服務端上的證書,App用服務端上證書的公鑰加密App生成的對稱秘鑰,但是Fiddler沒有證書的私鑰,所以Fiddler需要自己頒發一個證書,有公私鑰,才能和App正常的用https通信,
嘗試用Fiddler代理抓App的包失敗了
第三章 · 脫獄
遇到了問題,肯定要解決問題,現在問題的關鍵就是App驗證本地證書和Fiddler回傳的證書不一致,要做的就是繞開這驗證,或者讓App驗證這個證書永遠通過,不得不說,道高一尺魔高一丈,還真有這樣的東西,根據維基百科的解釋Xposed框架(Xposed framework)是一套開放源代碼的、在Android高權限模式下運行的框架服務,可以在不修改APK檔案的情況下修改程式的運行(修改系統),基于它可以制作出許多功能強大的模塊,且在功能不沖突的情況下同時運作,
安裝Xposed是有風險的,弄不好手機會變成磚,這里我就用模擬器替代手機安裝了,下載MuMu模擬器,安裝Xposed框架

同時還需要安裝一個Xposed的一個插件Just Trust Me

安裝好之后,我們再打開這個App,同時配置模擬器網路的代理,再用Fiddler抓包可以看到內容

第四章 · 柳暗花明
看到上面的截圖,的確是找打了請求的地址,但是發現發出的請求內容是
{
"pagesize": 10,
"page": 1,
"platid": "0",
"langid": "0",
"typeid": "0",
"saletime": "全部",
"time": 1595672934792,
"sign": "c2522179dd87522f0d2997e28d48289e"
}
沒錯,有一個簽名,這個簽名一看就知道是md5做的,但是我不知道規則,而且生成的簽名肯定是這些請求的引數加上一個字串,最后md5一下,服務端還要驗證簽名,才能正常回傳結果,這時仿佛又回到了原點,不解決這個簽名,我們是無法正常請求服務的,要解決這個簽名唯一的辦法就是反編譯包,從代碼中發現蛛絲馬跡,
apktool是一個開源的用于反編譯apk包的工具,用它試試看能不能找到對應的md5加密的原始碼,用它反編譯apk后,代碼非常龐大,在里面搜索介面的名稱djscreen竟然找到代碼了
invoke-static {}, Ljava/lang/System;->currentTimeMillis()J
move-result-wide v8
.line 493
.local v8, "time":J
new-instance v10, Ljava/lang/StringBuilder;
invoke-direct {v10}, Ljava/lang/StringBuilder;-><init>()V
const-string v11, "10"
invoke-virtual {v10, v11}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v10
iget v11, p0, Lcom/ws3dm/app/activity/GameCategoryActivity;->mPage:I
invoke-virtual {v10, v11}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
move-result-object v10
iget-object v11, p0, Lcom/ws3dm/app/activity/GameCategoryActivity;->platid:Ljava/lang/String;
invoke-virtual {v10, v11}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v10
iget-object v11, p0, Lcom/ws3dm/app/activity/GameCategoryActivity;->langid:Ljava/lang/String;
invoke-virtual {v10, v11}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v10
iget-object v11, p0, Lcom/ws3dm/app/activity/GameCategoryActivity;->typeid:Ljava/lang/String;
invoke-virtual {v10, v11}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v10
iget-object v11, p0, Lcom/ws3dm/app/activity/GameCategoryActivity;->saletime:Ljava/lang/String;
invoke-virtual {v10, v11}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v10
invoke-virtual {v10, v8, v9}, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder;
move-result-object v10
invoke-virtual {v10}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v7
.line 494
.local v7, "validate":Ljava/lang/String;
invoke-static {v7}, Lcom/ws3dm/app/util/StringUtil;->MD5(Ljava/lang/String;)Ljava/lang/String;
根據代碼可以看到MD5的規則就是10+mPage+platid+langid+typeid+saletime+time然后送達工具com.ws3dm.app.util.StringUtil執行MD5方法,進入這個類看看怎么MD5的
.method public static MD5(Ljava/lang/String;)Ljava/lang/String;
.locals 9
.param p0, "string" # Ljava/lang/String;
.prologue
.line 77
const-string v4, "e8S8Ho0N25z78u6qn4kHyN"
.line 78
.local v4, "key":Ljava/lang/String;
new-instance v5, Ljava/lang/StringBuilder;
invoke-direct {v5}, Ljava/lang/StringBuilder;-><init>()V
invoke-virtual {v5, p0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v5
invoke-virtual {v5, v4}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v5
invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object p0
驚喜的發現傳入的字串+e8S8Ho0N25z78u6qn4kHyN然后再MD5就是結果
試試101000全部1595672934792e8S8Ho0N25z78u6qn4kHyN正好是c2522179dd87522f0d2997e28d48289e和抓包中的引數一致,搞定,到此基本上是破解結束了,后續還發現基本上所有的介面都是采取這種模式進行md5然后請求的,
第五章 · 終結
至此整個的抓包加破解的流程就結束了,整個程序其實是做二件事情,解決https驗證證書,找到md5簽名的規則,整個App相對而且是比較好破解的,整個MD5的程序都是Java寫的,比較好反編譯,這也是和后端通信,常見的一種處理方式,但是這種方式,的確不是很安全,沒有很好的保護Api不被外部破解,比較安全的做法是把簽名方法放到so中,或者對資料進行加密解密,同時也不要忘記混淆代碼,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/73497.html
標籤:其他
上一篇:xss原理繞過防御個人總結
