溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

發(fā)布時間:2022-01-18 16:37:24 來源:億速云 閱讀:143 作者:柒染 欄目:數(shù)據(jù)安全

本篇文章為大家展示了如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù),內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

項目

該項目的目標是構(gòu)建一個Spotify客戶端,讓它能夠?qū)W習我的聽曲習慣并跳過一些我通常會跳過的歌曲。不得不承認,這種需求來自于我的懶惰。我不想在當我有心情想要聽某些音樂時,創(chuàng)建或查找播放列表。我希望的是在我的庫中選擇一首歌,然后可以隨機播放其他歌曲,并從隊列中刪除不“flow(節(jié)奏與旋律的流暢)”的歌曲。

為了實現(xiàn)這一點,我需要學習某種能夠執(zhí)行此任務的模型(在未來的帖子中可能更多)。但是為了能夠訓練一個模型,我首先需要數(shù)據(jù)來訓練它。

數(shù)據(jù)

我需要完整的聽歌歷史記錄,包括我跳過的那些歌曲。獲取歷史記錄很簡單。雖然Spotify API僅允許獲取最近50首播放的歌曲,但我們可以設(shè)置一個cron job來重復輪詢該端點。完整代碼已經(jīng)發(fā)布在此處:https://gist.github.com/SamL98/c1200a30cdb19103138308f72de8d198

最困難的部分是跟蹤跳過。Spotify Web API并沒有為此提供任何的端點。之前我使用Spotify AppleScript API創(chuàng)建了一些控制播放的服務(本文的其余部分將涉及到MacOS Spotify客戶端)。我可以使用這些服務來跟蹤跳過的內(nèi)容,但這感覺像是在回避挑戰(zhàn)。我怎么能完成它呢?

Hooking

我最近學習了解了有關(guān)hooking的技術(shù),你可以在其中“攔截”從目標二進制文件生成的函數(shù)調(diào)用。我認為這將是跟蹤跳過的最佳方法。

最常見的鉤子類型是interpose hook。這種類型的鉤子會覆蓋PLT中的重定位,但這究竟意味著什么呢?

PLT或過程鏈接表允許你的代碼引用外部函數(shù)(想想libc)而不知道該函數(shù)在內(nèi)存中的位置,你只需引用PLT中的一個條目。鏈接器在運行時為PLT中的每個函數(shù)或符號執(zhí)行“重定位”。這種方法的一個好處是,如果外部函數(shù)在不同的地址加載,則只需要更改PLT中的重定位,而不是每次對代碼中該函數(shù)的引用。

因此,當我們?yōu)閜rintf創(chuàng)建一個interpose hook時,每當我們hooking的進程調(diào)用printf時,我們將調(diào)用printf的實現(xiàn)而不是libc(我們的自定義庫通常也會調(diào)用標準實現(xiàn))。

在對鉤子有了一些基本的知識背景后,下面我們準備嘗試在Spotify中插入一個鉤子。但首先我們需要弄清楚我們想要hook的是什么。

尋找 hook 的位置

如前所述,只能為外部函數(shù)創(chuàng)建一個interpose hook,因此我們將在libc或Objective-C runtime中查找函數(shù)。

在研究在哪hook時,我認為一個開始hooking的好地方是Spotify處理“media control keys”或我MacBook上的F7-F9。假設(shè)這些鍵的處理程序在spotify應用程序中單擊Next按鈕被調(diào)用時會調(diào)用函數(shù)。我最終在:https://github.com/nevyn/spmediakeytap上找到了SPMediaKeyTap庫。我想我可以試一試,看看Spotify是否復制并粘貼了這個庫中的代碼。在SPMediaKeyTap庫中,有一個方法startWatchingMediaKeys。我在Spotify二進制文件上運行了strings命令,看看他們是否有這個方法,果然:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

Bingo!!如果我們將Spotify二進制文件加載到IDA(當然是免費版本)并搜索此字符串,我們就會找到相應的方法:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

如果我們查看這個函數(shù)對應的源碼,我們會發(fā)現(xiàn)CGEventTapCreate函數(shù)的有趣參數(shù)tapEventCallback:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

如果我們回顧一下反匯編,我們可以看到sub_10010C230子例程作為tapEventCallback參數(shù)傳遞。如果我們查看這個函數(shù)的源碼或反匯編,我們看到只調(diào)用了一個庫函數(shù)CGEventTapEnable:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

讓我們嘗試hook這個函數(shù)。

我們需要做的第一件事是創(chuàng)建一個庫來定義我們的自定義CGEventTapEnable。代碼如下:

#include <CoreFoundation/CoreFoundation.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
void CGEventTapEnable(CFMachPortRef tap, bool enable) 
{
  typeof(CGEventTapEnable) *old_tap_enable;
  printf(“I'm hooked!\n”);
  old_tap_enable = dlsym(RTLD_NEXT, “CGEventTapEnable”);
  (*old_tap_enable)(tap, enable);
}

dlsym函數(shù)調(diào)用獲取實際庫CGEventTapEnable函數(shù)的地址。然后我們調(diào)用舊的實現(xiàn),這樣我們就不會意外地破壞任何東西。讓我們像這樣編譯我們的庫(https://ntvalk.blogspot.com/2013/11/hooking-explained-detouring-library.html):

gcc -fno-common -c <filename>.c 
gcc -dynamiclib -o <library name> <filename>.o

現(xiàn)在,讓我們嘗試在插入鉤子時運行Spotify:DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=<library name> /Applications/Spotify.app/Contents/MacOS/Spotify。點擊進入:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

Spotify打開正常,但Apple的系統(tǒng)完整性保護(SIP)沒有讓我們加載未簽名庫:(。

幸運的是,我是Apple的reasonably priced developer項目的成員,所以我可以對庫進行代碼簽名。這個問題算是得到了解決。讓我們用100美元證書簽名我們的庫,運行上一個命令,然后......

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

失敗。這一點不奇怪,Apple不允許你插入使用任何舊標識簽名的庫,只允許使用簽名原始二進制文件時使用的庫??雌饋砦覀儽仨氁业搅硪环N方法來hook Spotify了。

作為補充說明,細心的讀者可能會注意到我們hook的函數(shù)CGEventTapEnable,只有在media key event超時時才會被調(diào)用。因此,即使我們可以插入鉤子,我們也可能不會看到任何的輸出。本節(jié)的主要目的是詳細說明我最初的失?。ê褪韬觯⒆鳛橐粋€學習經(jīng)驗。

HookCase

經(jīng)過一番挖掘,我發(fā)現(xiàn)了一個非常棒的庫HookCase:https://github.com/steven-michaud/HookCase。HookCase讓我們實現(xiàn)一種比插入鉤子( patch hook)更為強大的鉤子類型。

通過修改你希望hook的函數(shù)觸發(fā)中斷插入Patch hooks。然后,內(nèi)核可以處理此中斷,然后將執(zhí)行轉(zhuǎn)移到我們的個人代碼中。對于那些感興趣的人,我強烈建議你閱讀HookCase文檔,因為它更為詳細。

Patch hooks不僅允許我們對外部函數(shù)的hook調(diào)用,而且允許我們hook目標二進制文件內(nèi)的任何函數(shù)(因為它不依賴于PLT)。HookCase為我們提供了一個框架來插入patch和/或interpose hooks,以及內(nèi)核擴展來處理patch hooks生成的中斷,并運行我們的自定義代碼。

尋找 sub_100CC2E20

既然我們已經(jīng)有辦法hook Spotify二進制文件中的任何函數(shù)了,那么只剩下最后一個問題......就是位置在哪?

讓我們重新訪問SPMediaKeyTap源碼,看看如何處理媒體控制鍵。在回調(diào)函數(shù)中,我們可以看到如果按下F7,F(xiàn)8或F9(NX_KEYTYPE_PREVIOUS,NX_KEYTYPE_PLAY等),我們將執(zhí)行handleAndReleaseMediaKeyEvent選擇器:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

然后在所述選擇器中通知delegate:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

讓我們看看repo中的這個delegate方法:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

事實證明它只是為處理keys設(shè)置了一個模板。讓我們在IDA中搜索receiveMediaKeyEvent函數(shù),并查看相應函數(shù)的圖形視圖:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

看起來非常相似,不是嗎?我們可以看到,對每種類型的鍵都調(diào)用了一個公共函數(shù)sub_10006FE10,只設(shè)置了一個整數(shù)參數(shù)來區(qū)分它們。讓我們hook它,看看我們是否可以記錄按下的鍵。

我們可以從反匯編中看到,sub_10006FE10獲得了兩個參數(shù):1)指向SPTClientAppDelegate單例的playerDelegate屬性的指針,以及2)指定發(fā)生了什么類型事件的整數(shù)(0表示暫停/播放,3表示下一個,4表示上一個)。

看看sub_10006FE10(我不會在這里包含它,但我強烈建議你自己檢查一下),我們可以看到它實際上是sub_10006DE40的包裝器,其中包含了大部分內(nèi)容:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

哇!這看起來很復雜。讓我們試著把它分解一下。

從這個圖的結(jié)構(gòu)來看,有一個指向頂部的節(jié)點有許多outgoing edges:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

正如IDA所建議的那樣,這是esi(前面描述的第二個整數(shù)參數(shù))上的switch語句??雌饋鞸potify的處理的不僅僅是Previous,Pause/Play和Next。讓我們把關(guān)注點集中到處理Next或3 block:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

不可否認,為此我花了一些時間,但我想請你注意底部第四行的call r12。如果你查看其他的一些情況,你會發(fā)現(xiàn)一個非常相似的調(diào)用寄存器的模式。這似乎是一個很好的函數(shù),但我們?nèi)绾沃浪谀哪兀?/p>

讓我們打開一個新工具:debugger(調(diào)試器)。我最初嘗試調(diào)試Spotify時遇到了很多麻煩?,F(xiàn)在可能是因為我對調(diào)試器不太熟悉的原因,但我認為我想出了一個相當聰明的解決方案。

我們首先在sub_10006DE40上設(shè)置一個hook,然后我們在代碼中觸發(fā)一個斷點。我們可以通過執(zhí)行匯編指令int 3來做到這一點(例如像GDB和LLDB之類的調(diào)試)。

以下是在HookCase框架中hook的樣子:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

將此添加到HookCase模板庫后,你還必須將其添加到user_hooks數(shù)組:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

然后我們可以使用Makefile HookCase提供的模板來編譯它。然后可以使用以下命令將庫插入Spotify:HC_INSERT_LIBRARY=<full path to hook dylib> /Applications/Spotify.app/Contents/MacOS/Spotify。

然后我們可以運行LLDB并將其attach到正在運行的Spotify進程,如下所示:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

嘗試按F9(如果Spotify不是活動窗口,它可能會打開iTunes)。鉤子中的int $3行應該觸發(fā)了調(diào)試器。

現(xiàn)在我們可以進入到sub_10006DE40入口點這步。請注意,PC將位于與IDA中顯示的地址相對應的位置(我認為這是由于進程加載到內(nèi)存的位置所導致的)。在我當前的進程中,push r15指令位于0x10718ee44:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

在IDA中,該指令的地址為0x10006DE44,它給了我們一個偏移量0x7121000。在IDA中,調(diào)用r12指令的地址為0x10006E234。然后我們可以將偏移量添加到該地址,并相應地設(shè)置一個斷點,b -a 0x10718f234,然后繼續(xù)。

當我們點擊目標指令時,我們可以打印出寄存器r12的內(nèi)容:

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

我們要做的就是從這個地址減去偏移量,看,我們獲取到了我們名義上的地址:0x100CC2E20。

Hooking sub_100CC2E20

現(xiàn)在,讓我們來hook這個函數(shù):

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

將其添加到user_hooks數(shù)組,編譯,運行,并觀察:每次按F9或單擊Spotify應用程序中的next按鈕,都會記錄我們的消息。

現(xiàn)在我們已經(jīng)hook了skip功能,

如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù)

我將發(fā)布剩余的代碼,但我不會完成其余部分的逆向工作,因為這篇文章已經(jīng)夠長的了。

簡而言之,我也hook了previous功能(如果你照著做的話,這會是一個很好的練習)。然后,在這兩個鉤子中,我首先檢查當前的歌曲是否已經(jīng)過了一半。如果是的話,我什么都不做,假設(shè)我只是對這首歌感到厭倦,而不是覺得它不合適。然后在backs (F7),我彈出last skip。

針對如何檢查當前歌曲是否已經(jīng)過了一半的方法我想說幾句。我最初的方法是實際調(diào)用popen,然后運行相應的AppleScript命令,但感覺這不太對。

我在Spotify二進制文件上運行了class-dump,發(fā)現(xiàn)了兩個類:SPAppleScriptObjectModel和SPAppleScriptTrack。這些方法公開了播放位置,持續(xù)時間和曲目ID所需的必要屬性。然后,我為這些屬性hook了getter,并使用next和back hooks調(diào)用它們(我認為Swizzle更合理,但我無法讓它正常工作)。

我使用一個文件來跟蹤skips,其中第一行包含跳過次數(shù),在跳過時我們增加這個計數(shù)器,并將跟蹤ID和時間戳寫入計數(shù)器指定行上的文件。在back按鈕,我們只是減少這個計數(shù)器。這樣,當我們按下back按鈕時,我們只是將文件設(shè)置為對已回溯文件寫入new skips。

上述內(nèi)容就是如何逆向分析Spotify.app并hook其功能獲取數(shù)據(jù),你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI