????新版D音由于插入了過多破壞性代碼,已經(jīng)無法由jadx反編譯成功了,太多地方都解析失敗。我嘗試用MT管理器換引擎反編譯,也都是一樣的結(jié)果。這里如果想分析,需要jadx結(jié)合smali代碼硬啃。我對著smali啃了一個參數(shù),有些蛋疼。。于是還是用豌豆莢下了歷史版本,反編譯看java了。
抓包
? ? 首先還是登錄抓包:
? ? ? ?首先是post請求,有query、body、cookie和另外的一些字段。換個手機號登錄再抓一次作對比。參數(shù)太多,不方便看,切換到WebForms選項卡,對比參數(shù),首先是query。第一次抓包:
? ? ? ? 第二次抓包:
? ? 可以看到變化的只有 ts 和 _rticket,很容易看出來,一個是10位時間戳,一個是13位時間戳。那么query方面可能沒有我們太需要關(guān)注的地方。
? ? 然后body:
? ? 手機號與密碼經(jīng)過了加密。再看下其他內(nèi)容:
? ? X-Khronos與X-SS-REQ-TICKET依然是時間戳,這里需要注意的參數(shù)是X-Gorgon、X-SS-STUB和x-tt-trace-id。對比兩次請求,我們發(fā)現(xiàn)x-tt-trace-id里中間有一部分與末尾部分是相同的,猜測與cookie或是一些本地配置參數(shù)有關(guān)。cookie重新打開了幾次,也沒有發(fā)生變化,暫時也不看。這個cookie的生成盲猜要比登錄協(xié)議復(fù)雜得多。
? ? 那么確定下來需要找的參數(shù):body中的password、mobile、header中的X-Gorgon、X-SS-STUB和x-tt-trace-id。
分析
? ? 還是慣例,jadx全局先搜一波url唄。?
? ? 很容易的就找到這里。可以看到在將手機號與密碼put進map之前,執(zhí)行了StringUtils.encryptWithXor函數(shù):
? ? 比較簡單,先轉(zhuǎn)成bytes數(shù)組,逐位與5進行亦或,最后再轉(zhuǎn)成16進制拼接為字符串。驗證一下:
? ? 相同。密碼同樣。
? ? 然后分析header中的字段。搜索X-Gorgon,找到這個類:
? ? 一個匿名類,我們hook一下a方法。這里夜神模擬器的firda又出問題了,換真機測試。。hook腳本與輸入結(jié)果如下:
? ? 第一個參數(shù)str也就是完整的url,第二個參數(shù)map就是所有其他的headers參數(shù),包括cookie、x-ss-req-ticket、user-agent、accept等參數(shù)。同時x-tt-trace-id與X-SS-STUB在此之前就已經(jīng)賦值:
? ? 回到代碼分析,我們找到X-Gorgon的來源:a3,a3是通過調(diào)用上面的函數(shù)得到的。
? ? 大概要找出來的參數(shù)有:i,a2,str4,str5,str6,那個currentTimeMillis是10位int類型的時間戳。
? ? 我們先hook最后的a.a方法,看看a2、str4、str5、str6是什么:
? ? 幾乎沒任何參考價值。。還是一步一步看。
? ? a2:
? ? ? ? 由d.a(b2)得到,b2由tt.d(str)得到,str是url。看看tt.d做了什么:
? ? ? ? 對url進行了切割,獲取?到#的部分,也就是query部分。再看d.a():
? ? ? ? 對query部分進行md5,解決一個。
? ? str4:從Map中取X-SS-STUB,如果有則賦值,否則賦為32個0;
? ? ? str5和str6都在這里:
首先是str5,對cookie進行md5。str6對cookie先調(diào)了tt.e():
? ? 就是從cookie里取sessionid,然后再md5。以上4個參數(shù)如果為空,就賦32個0。最后是a.a:
? ? 各種位運算。懶得看了。接下來分析i:
? ? 如果map中有“META-SHADOWMAZE”則為1,否則為-1。
? ? 然后是一個巨長無比的調(diào)用鏈:
? ? 最外層的a:
? ? 以及發(fā)現(xiàn)驚喜的leviathan,native!驚不驚喜意不意外!不往下看了??!
? ? 上面還漏了兩個大坑:x-tt-trace-id與X-SS-STUB。
? ? 全局搜索X-SS-STUB,定位到下面:
? ? 跟進:
? ? 接口+抽象方法。現(xiàn)在我們掌握的線索有,真實調(diào)用的類一定繼承自TypedOutput,并且實現(xiàn)了md5Stub()方法。全局搜索String md5Stub():
? 這里就不太好看了,嘗試用frida打印調(diào)用棧,只要找到調(diào)用棧,我們不就知道到底走的哪條路線了嗎。但是使用frida啟動時遇到:
? 這是由于activity沒有指定被調(diào)用的Intent,我在Manifest.xml中加入后依然報錯,遇到了知識盲區(qū),暫時無法解決。那么換一個思路,可以選擇去hook每一個方法,或者動態(tài)調(diào)試。對于自己來說目前可能還是hook比較順手,而且這幾個方法也比較簡單,直接去hook就可以,看打印出哪個那就是了。最終確定,這里真實調(diào)用的是com.bytedance.retrofit2.mime.b類中的md5Stub()。
? ? 查找md5Stub的用例卻沒有查找到。觀察b中沒有構(gòu)造方法,其中的主要類變量f31162a是在a方法中初始化:
? ? 追蹤a的調(diào)用,一共有兩處,我們來到了這里:
? ? 繼續(xù)找調(diào)用它的地方。
? ? 有點眼熟啊,貌似是從Map里取了一些參數(shù)。Map很有可能是query、body、或者header中的某些部分。再回去看上面負責初始化f31132a的a方法,就是把傳過來的參數(shù)一一組合,以&和=做連接。
? ? 既然我們已經(jīng)知道b類中的md5Stub一定會被調(diào)用,而a又是初始化md5Stub中參數(shù)f311332a的唯一途徑,那么可以按照如上的思路,編寫hook代碼,嘗試還原最終拼接后的參數(shù):? ??
? ? 這不就是post的請求體嗎?再回去看調(diào)用鏈:
? ? 先對上面的字符串取md5,再對結(jié)果執(zhí)行a方法:
? ? 又是一些位運算,結(jié)果就是最終的X-SS-STUB。
總結(jié)
? ? java層還是屬于皮毛,真正難的地方在so層,D音so層加載Map足以讓人絕望。這次在找x-ss-stub的時候,遲遲定位不到關(guān)鍵代碼,全局搜索不出來,方法剖析和打印調(diào)用棧都GG的情況下,略有小懵。不過能用的方法還是太多了,總有一款適合你。。。
聲明
?????????聲明:本文分析過程僅供學習,并無任何個人以及商業(yè)或其他用途。如有不慎侵權(quán),請聯(lián)系我刪除。