當前位置:
首頁 > 最新 > 網易雲音樂PC客戶端加密API逆向解析

網易雲音樂PC客戶端加密API逆向解析

*本文作者:Tan1993,本文屬 FreeBuf 原創獎勵計劃,未經許可禁止轉載。

1、前言

網上已經有大量的web端介面解析的方法了,但是對客戶端的介面解析基本上找不到什麼資料,本文主要分析網易雲音樂PC客戶端的API介面交互方式。

通過內部的代理設置,使用fiddler作為代理工具,即可查看交互流程:

可以大致看一下交互方式,通過HTTPS POST交互,POST了一串params的內容,內容加密,返回JSON內容,我要做的重點就在於解析params的生成方式,用於模擬這次交互。

(Tan1993:這是後續編寫的內容,截圖很多都是後補的,所以可能會出現使用不同的調試工具,不同的環境,不同的時間等,不影響閱讀。另外本人工作主要是linux網路方向的,像是這次只是我的一點業餘愛好,也很少會去逆向東西,如果出現一些比較業餘的操作或想法時,還望指出)

2、初步了解

下載最新版PC版網易雲安裝(目前是2.3.0.196231版本),分析在程序所在目錄下的文件。

動態鏈接庫與可執行文件

第一個最讓我注意的時libcurl,這個網路庫可以用於HTTP協議交互,如果通過該庫與伺服器交互, od斷點到curl_easy_perform再往回推就可以判斷轉換演算法位置了,然而事實比我想像的複雜多了,這個庫僅在程序剛運行時用於一些無關的網路交互(Tan1993:記不清了,好像是版本還是客戶端信息相關的請求)。

第二個是libcef,這個是個基於C/C++的Web browser控制項,可以簡單理解為就是個瀏覽器的殼子(Tan:為什麼說關鍵API沒用到libcurl庫,因為除了開始時cef框架還沒初始化前網路交互用到那個庫而已,一點cef環境起來了,都是通過JS ajax交互了)。

其他的除了cef依賴的dll外,兩個主程序和cloudmusic.dll都比較值得關注。

資源文件

除了在package下的其他都是cef庫依賴的資源文件。

都是未知的格式,一般看到未知格式的文件,我都會用7z嘗試打開看看,是不是某種歸檔格式文件,這個一下就蒙中了,是zip格式的。

除了幾個通過後綴就能看出來的皮膚文件,還有兩個比較可疑的文件,翻一翻比較大的orpheus.ntpk文件,裡面可以看到都是網頁相關的資源文件,看到那個core.js,就讓我聯想到網頁版API提取時用到的那個core.js文件了,腦海里就想著替換然後對轉換流程動態分析了,事實有點不盡人意,該zip文件加密了

OK,調研階段結束,在不進行逆向解析前,能了解到的也就止步於此了。

3、第一輪嘗試

其實一開始我是把目光放在libcurl上面的,在斷點到curl庫的函數上時發現只有程序剛運行時觸發過幾次,後面所有網路交互都不用這個庫了,就轉戰到cef上。而cef的重點在於內部的JS文件,能提取到該文件才是關鍵的。

0x2712即CURLOPT_URL宏,eax中存放著url的字元串指針,基本上都是無關的url。

第一個任務來了,逆向尋找特徵串,也就是密碼,這裡斷點到系統文件操作API上,斷到CreateFileW,一頓的F9後可以看到載入到default.skin文件了(圖中是native.ntpk,同類型的加密ZIP文件),後續就單步調試下去。

然後看到一個比較特別的內存塊,一看就是PNG格式的文件頭,就可以判斷這一步資源已經解壓縮到內存了。

往上推幾步,斷點,縮小範圍,再跟下來,看看哪裡做了解壓操作,再一步步跟函數。(Tan1993:可能比較業餘,但我也只能一點點縮小範圍在一點點看流程,憑經驗判斷可能會做什麼操作,縮短到比較短的範圍,不然一堆彙編碼真的會受不了,感謝世界上程序員的思想都是接近的吧)。

得知密碼後,就可以解壓出core.js文件了(Tan1993:這裡僅提供思路,不提供便民服務哈)

又是這一堆讓人窒息的混淆,卡得懷疑人生,先解壓縮再看吧。

解壓後,搜幾個關鍵字,比如params,eapi,batch等最上面HTTP交互時的一些特徵

關鍵代碼,像這樣混淆的JS代碼,如果不通過調試器跟蹤,很難看懂,目前能可以看出也只有channel.serialData應該時比較關鍵的轉換函數,但是搜索了整個JS文件都找不到函數定義,不知道是不是混淆到哪個奇怪的地方了。

雖然cef自帶DevTools,但是已經被屏蔽掉了也無法在程序里調出來,所以我想在JS文件中加上alert調試關鍵參數。然後我修改了core.js文件,按原來的密碼壓縮回去。但程序根本就起不來,為什麼呢,看看原版的.ntpk文件,很明顯還有一些奇怪的東西和zip文件一起合成了這個ntpk文件格式。根據經驗判斷很可能時類似於數字簽名的東西(Tan1993:之前我也會對一些可能被篡改的檔案末尾對整個文件加鹽生成一個hash值用於校驗,但是後續跟完網易雲的數字簽名方式讓我又學習了不少)。

4、第二輪嘗試

為了方便調試,我需要替換掉資源文件中的core.js文件,但是該資源文件不僅僅加密壓縮了,還有一些其他內容存在,所以這次跟代碼就是為了了解除了zip文件本身以外其他部分內容的作用。

還是斷到CreateFileW函數上,其實第一輪跟代碼的時候我就已經發現了部分調用系統加密服務提供程序 (CSP)庫的函數。

一步步跟過來,發現用的是SHA1數字簽名演算法(Tan1993:不是很了解CSP庫,但這個是為Windows系列操作系統制訂的底層加密介面,和我理解的SHA不太一樣,我姑且將程序內部的那部分稱為公鑰,與文件頭部的校驗數據進行校驗)。

文件頭NTPK,文件長度0x0D5C5B,校驗串長度0x100

剛好差了0x110長度,除了0x100用於校驗的數據,還有0x10的頭部。

由於我是無法在不知道私鑰的情況下,再次對該文件進行簽名的,所以我只能把程序內部的用於校驗的公鑰一併替換,再生成一個對應的檢驗數據,從而通過系統驗證,或者直接把驗證部分的代碼跳轉邏輯修改掉(Tan1993:其實可能改分支流程修改會更簡單也說不定,但我一開始選擇的是替換公鑰重新生成校驗數據)。

截了一部分代碼,用於修改cloudmusic.dll中的二進位數據,偏移是根據內存載入地址與基址算的,直接固定偏移修改即可。

到這一步其實我已經可以替換掉core.js文件並且可以alert彈出對話框,顯示一些JS運行時數據了,雖然alert彈框並不是那麼好用。

通過alert我可以看到加密前的內容,也就是具體發了哪些數據,以及加密後是什麼樣子的,很可惜的是當我嘗試alert(channel.serialData)時發現是[native code],按我個人理解應該是系統二進位函數才會顯示這個的吧(對JS並不是非常了解),懷疑是庫函數,但查詢無果,後來想了想會不會是JS調用了C++代碼(憑我對cef粗糙的理解),我嘗試去查了一下,果然是可以的,那麼很有可能這部分加密轉換的代碼還是在主程序中,這就很頭疼了,剛從主程序逆向脫離出來到JS這個自由的世界,又要回到看彙編碼的環境了。

5、第三輪嘗試

這一輪主要目的是找到channel.serialData在主程序的位置,根據我對cef的理解,應該是在程序啟動時,註冊了一部分回調函數,可以從註冊的時候找到回調函數入口,然後等觸發channel.serialData動作時,從回調函數跟代碼跟下來。

根據DLL版本,我找到了對應的cef源碼版本,cef註冊回調時是整個結構體的,必須找到對應的版本避免新版本結構體不一樣導致偏移位置有差異。

在看源碼的過程中發現結構體里有個很有意思的欄位,一個debug埠,調研了一下,這個埠很有用了,可以遠程DevTools,這樣還用什麼alert。

如果要在調用初始化前把結構體改掉,要麼API Hook修改,要麼靜態文件修改,文件修改的話只能捨棄一些無用代碼來改這個結構體了,我選了一個不影響的賦值語句,改成給這個地址賦9222。

對照源碼中結構體計算偏移值

原本修改cloudmusic.dll的代碼中增加個代碼段修改的方法

現在我就可以通過http://127.0.0.1:9222遠程訪問DevTools了。可當我打開網頁時一片空白,這時候又憑藉我對cef粗略的了解,在程序目錄下,並沒有devtools相關的資源,其實只要把資源文件補上就可以了(官網已經沒有這麼老的資源文件檔案了,這個還是我網上找的3.1916版本的devtools資源文件)

這時候所有JS調試命令都可以改成console.log來進行了,方便了好多。

回到正題,從註冊來跟代碼實在是太痛苦了。一個是註冊的內容比較多,一層疊一層的,而且程序用的是C++ warp的C語言版本的cef庫,和源碼對照跟的時候還是有點差別的。這時候我想到一個非常好的方法,那就是製造一個死循環。

6、第四輪嘗試

上面就提到了,我放棄了從註冊一步步跟蹤回調函數的麻煩方案,而是在JS中知道一個死循環,不停的調用channel.serialData函數,等程序單核滿載時,只需要將調試器附加程序,點一點暫停,基本上就是這個函數相關業務流程的代碼了(JS到機器碼代碼按我理解應該在堆上,而加密的代碼應該在程序代碼段上,所以我定位的時候可以忽略掉很多JS的代碼,找到真正相關的代碼位置)

實際上,channel.serialData的彙編碼也非常多,流程也分了好多部分,這部分工作量實在是降不下來,但是很多可能是為了防止靜態分析的代碼,部分特徵串是運行時生成的,但是因為這部分特徵串都是固定的,所以是可以不用去仔細琢磨的(然而我花了一兩天來看那一堆彙編碼來算出特徵串,非常鬱悶,早知道就逆推就好,但說實話,光逆推也會很難,主要是要有一定理解)

簡單說明一下轉換流程

1、 輸入url(請求部分)和data(提交的json數據)

2、 拼成」nobody」 + url + 「use」 + data + 「md5forencrypt」字元串

3、 對字元串計算MD5

4、 二次拼接url + 「-36cd479b6b5-」 + data + 「-36cd479b6b5-」 + md5

5、 0x10對齊,缺少的部分會以缺少的位數來填充

6、 私有轉換方法(也許是我不知道的一種加密方式?)

附上一部分分析的圖

待加密數據,0x10位元組對齊,每次處理0x10位元組的數據

輔助加密數據(動態生成,但是是固定的,我還傻傻去復現了一遍生成流程)

開始對0x10進行轉換

一堆異或和位移計算,這個還是很好復現到C的代碼中的,這個比較長就不全粘貼了。

循環轉換完後再按照」%02X」格式snprintf到字元串即可。我沒有過多去理解這個加密演算法究竟是什麼原理,只是直譯彙編碼。

後來嘗試反過來解析,看了一早上沒看出來,簡單描述一下為什麼難以逆轉的問題。

內存塊mem

然後在得知後面的eax,ebx,ecx,edx逆推原來的,感覺不太可能,但是mem並不是沒有規律的一個內存塊,而且數組索引時也做了些巧妙的偏移,事實上內存塊確實有不少規律(比如a1是偶數時b1是a1的一半,c1是a1 ^ b1),而且和索引時的偏移可能會相得益彰,如果能看出竅門說不定還是能解的,有興趣的小夥伴也可以研究一下(Tan1993:個人沒學過加密學,只略懂一部分概念)

7、匯總

其實到這一步,我可以通過遠程devtools來看發送前未加密的內容以及結構,同時我也可以通過已經復現的加密方法,對不同業務數據加密發送出去。我發現有一部分請求數據返回內容也是加密的,但這個是可以在客戶端控制e_r的值來控制是否需要返回加密內容的。

寫個模擬客戶端下載歌曲的小Demo,本來發送和接收都是加密的數據的下載介面,就可以通過伺服器驗證實現下載了,解析到此告一段落,雖然過程中還有很多內容值得研究,如果有機會以後會繼續挖掘。

8、總結

由於並沒有找到任何的參考資料,斷斷續續也研究了一周時間。除了實現了目標以外,還是有不少收穫的,比如比較有趣的加密演算法,數字簽名方法,cef庫,還有一些逆向的思路。

比較遺憾的是沒有把解密的演算法也解析出來,同時在客戶端控制e_r的值來控制返回數據是否加密顯然不是好方法,官方只需要忽略這個參數強制對部分API返回加密數據,正常的客戶端也沒有任何影響(難道有平台相關性所以才把這個參數放到客戶端的嗎?)。

(Tan1993:視情況考慮是否在github提供源碼)

9、彩蛋

將一件有趣的事,當時我嘗試在一台國外IP的伺服器上調用web的api介面時發現不能適用,獲取不到數據,然後我又跟了一便JS代碼發現邏輯不一樣,其中發現了一個很有意思的特徵串(在你們看不到的地方,總有調皮的程序員):

*本文作者:Tan1993,本文屬 FreeBuf 原創獎勵計劃,未經許可禁止轉載。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 FreeBuf 的精彩文章:

Rapid勒索病毒分析與檢測
看我如何利用兩個漏洞實現雅虎郵箱通訊錄信息獲取

TAG:FreeBuf |