搜尋
首頁運維安全如何逆向分析Spotify.app並hook其功能取得數據

專案

該專案的目標是建立一個Spotify客戶端,讓它能夠學習我的聽曲習慣並跳過一些我通常會跳過的歌曲。不得不承認,這種需求來自於我的懶惰。我不想在當我有心情想要聽某些音樂時,創建或尋找播放清單。我希望的是在我的庫中選擇一首歌,然後可以隨機播放其他歌曲,並從隊列中刪除不“flow(節奏與旋律的流暢度)”的歌曲。

為了實現這一點,我需要學習某種能夠執行此任務的模型(在未來的帖子中可能更多)。但是為了能夠訓練一個模型,我首先需要資料來訓練它。

資料

我需要完整的聽歌歷史記錄,包括我跳過的那些歌曲。取得歷史記錄很簡單。雖然Spotify API僅允許取得最近50首播放的歌曲,但我們可以設定一個cron job來重複輪詢該端點。完整程式碼已經發佈在此處:https://gist.github.com/SamL98/c1200a30cdb19103138308f72de8d198

最困難的部分是追蹤跳過。 Spotify Web API並沒有為此提供任何的端點。之前我使用Spotify AppleScript API創建了一些控製播放的服務(本文的其餘部分將涉及到MacOS Spotify客戶端)。我可以使用這些服務來追蹤跳過的內容,但這感覺像是在迴避挑戰。我怎麼能完成它?

Hooking

我最近學習了解了有關hooking的技術,你可以在其中「攔截」從目標二進位產生的函數呼叫。我認為這將是追蹤跳過的最佳方法。

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

PLT或過程連結表允許你的程式碼引用外部函數(想想libc)而不知道該函數在記憶體中的位置,你只需引用PLT中的一個條目。連結器在運行時為PLT中的每個函數或符號執行「重定位」。這種方法的一個好處是,如果外部函數在不同的位址加載,則只需要更改PLT中的重定位,而不是每次對程式碼中該函數的參考。

因此,當我們為printf建立一個interpose hook時,每當我們hooking的程序呼叫printf時,我們將呼叫printf的實作而不是libc(我們的自訂函式庫通常也會呼叫標準實作) 。

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

尋找 hook 的位置

如前所述,只能為外部函數建立一個interpose hook,因此我們將在libc或Objective-C runtime中尋找函數。

在研究在哪裡hook時,我認為一個開始hooking的好地方是Spotify處理「media control keys」或我MacBook上的F7-F9。假設這些鍵的處理程序在spotify應用程式中按一下Next按鈕被呼叫時會呼叫函數。我最終在:https://github.com/nevyn/spmediakeytap上找到了SPMediaKeyTap庫。我想我可以試一試,看看Spotify是否複製並貼上了這個庫中的程式碼。在SPMediaKeyTap庫中,有一個方法startWatchingMediaKeys。我在Spotify二進位檔案上執行了strings指令,看看他們是否有這個方法,果然:

如何逆向分析Spotify.app並hook其功能取得數據

Bingo!!如果我們將Spotify二進位檔案載入到IDA(當然是免費版本)並蒐索此字串,我們就會找到相應的方法:

如何逆向分析Spotify.app並hook其功能取得數據

##如果我們查看這個函數對應的源碼,我們會發現CGEventTapCreate函數的有趣參數tapEventCallback:

如何逆向分析Spotify.app並hook其功能取得數據

如果我們回顧一下反彙編,我們可以看到sub_10010C230子程式作為tapEventCallback參數傳遞。如果我們查看這個函數的原始碼或反彙編,我們看到只呼叫了一個函式庫函數CGEventTapEnable:

如何逆向分析Spotify.app並hook其功能取得數據

讓我們嘗試hook這個函數。

我們需要做的第一件事是建立一個函式庫來定義我們的自訂CGEventTapEnable。程式碼如下:

#include <corefoundation>
#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);
}</stdio.h></stdlib.h></dlfcn.h></corefoundation>
dlsym函式呼叫取得實際函式庫CGEventTapEnable函數的位址。然後我們呼叫舊的實現,這樣我們就不會意外地破壞任何東西。讓我們像這樣編譯我們的函式庫(https://ntvalk.blogspot.com/2013/11/hooking-explained-detouring-library.html):

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

现在,让我们尝试在插入钩子时运行Spotify:DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES= /Applications/Spotify.app/Contents/MacOS/Spotify。点击进入:

如何逆向分析Spotify.app並hook其功能取得數據

Spotify打开正常,但Apple的系统完整性保护(SIP)没有让我们加载未签名库:(。

幸运的是,我是Apple的reasonably priced developer项目的成员,所以我可以对库进行代码签名。这个问题算是得到了解决。让我们用100美元证书签名我们的库,运行上一个命令,然后......

如何逆向分析Spotify.app並hook其功能取得數據

失败。这一点不奇怪,Apple不允许你插入使用任何旧标识签名的库,只允许使用签名原始二进制文件时使用的库。看起来我们必须要找到另一种方法来hook Spotify了。

作为补充说明,细心的读者可能会注意到我们hook的函数CGEventTapEnable,只有在media key event超时时才会被调用。因此,即使我们可以插入钩子,我们也可能不会看到任何的输出。本节的主要目的是详细说明我最初的失败(和疏忽),并作为一个学习经验。

HookCase

经过一番挖掘,我发现了一个非常棒的库HookCase:https://github.com/steven-michaud/HookCase。HookCase让我们实现一种比插入钩子( patch hook)更为强大的钩子类型。

通过修改你希望hook的函数触发中断插入Patch hooks。然后,内核可以处理此中断,然后将执行转移到我们的个人代码中。对于那些感兴趣的人,我强烈建议你阅读HookCase文档,因为它更为详细。

Patch hooks不仅允许我们对外部函数的hook调用,而且允许我们hook目标二进制文件内的任何函数(因为它不依赖于PLT)。HookCase为我们提供了一个框架来插入patch和/或interpose hooks,以及内核扩展来处理patch hooks生成的中断,并运行我们的自定义代码。

寻找 sub_100CC2E20

既然我们已经有办法hook Spotify二进制文件中的任何函数了,那么只剩下最后一个问题......就是位置在哪?

让我们重新访问SPMediaKeyTap源码,看看如何处理媒体控制键。在回调函数中,我们可以看到如果按下F7,F8或F9(NX_KEYTYPE_PREVIOUS,NX_KEYTYPE_PLAY等),我们将执行handleAndReleaseMediaKeyEvent选择器:

如何逆向分析Spotify.app並hook其功能取得數據

然后在所述选择器中通知delegate:

如何逆向分析Spotify.app並hook其功能取得數據

让我们看看repo中的这个delegate方法:

如何逆向分析Spotify.app並hook其功能取得數據

事实证明它只是为处理keys设置了一个模板。让我们在IDA中搜索receiveMediaKeyEvent函数,并查看相应函数的图形视图:

如何逆向分析Spotify.app並hook其功能取得數據

看起来非常相似,不是吗?我们可以看到,对每种类型的键都调用了一个公共函数sub_10006FE10,只设置了一个整数参数来区分它们。让我们hook它,看看我们是否可以记录按下的键。

我们可以从反汇编中看到,sub_10006FE10获得了两个参数:1)指向SPTClientAppDelegate单例的playerDelegate属性的指针,以及2)指定发生了什么类型事件的整数(0表示暂停/播放,3表示下一个,4表示上一个)。

看看sub_10006FE10(我不会在这里包含它,但我强烈建议你自己检查一下),我们可以看到它实际上是sub_10006DE40的包装器,其中包含了大部分内容:

如何逆向分析Spotify.app並hook其功能取得數據

哇!这看起来很复杂。让我们试着把它分解一下。

从这个图的结构来看,有一个指向顶部的节点有许多outgoing edges:

如何逆向分析Spotify.app並hook其功能取得數據

正如IDA所建议的那样,这是esi(前面描述的第二个整数参数)上的switch语句。看起来Spotify的处理的不仅仅是Previous,Pause/Play和Next。让我们把关注点集中到处理Next或3 block:

如何逆向分析Spotify.app並hook其功能取得數據

不可否認,為此我花了一些時間,但我想請你注意底部第四行的call r12。如果你查看其他的一些情況,你會發現一個非常相似的呼叫暫存器的模式。這似乎是一個很好的函數,但我們如何知道它在哪裡?

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

我們先在sub_10006DE40上設定一個hook,然後我們在程式碼中觸發一個斷點。我們可以透過執行彙編指令int 3來做到這一點(例如像GDB和LLDB之類的偵錯)。

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

如何逆向分析Spotify.app並hook其功能取得數據

將此加入HookCase範本庫後,你也必須將其加入user_hooks陣列:

如何逆向分析Spotify.app並hook其功能取得數據

然後我們可以使用Makefile HookCase提供的模板來編譯它。然後可以使用以下指令將庫插入Spotify:HC_INSERT_LIBRARY= /Applications/Spotify.app/Contents/MacOS/Spotify。

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

如何逆向分析Spotify.app並hook其功能取得數據

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

現在我們可以進入到sub_10006DE40入口點這一步。請注意,PC將位於與IDA中顯示的位址相對應的位置(我認為這是由於進程載入到記憶體的位置所導致的)。在我目前的進程中,push r15指令位於0x10718ee44:

如何逆向分析Spotify.app並hook其功能取得數據

在IDA中,該指令的位址為0x10006DE44,它給了我們一個偏移量0x7121000。在IDA中,呼叫r12指令的位址為0x10006E234。然後我們可以將偏移量加到該位址,並相應地設定一個斷點,b -a 0x10718f234,然後繼續。

當我們點擊目標指令時,我們可以列印出暫存器r12的內容:

如何逆向分析Spotify.app並hook其功能取得數據

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

Hooking sub_100CC2E20

現在,讓我們來hook這個函數:

如何逆向分析Spotify.app並hook其功能取得數據

將其加入到user_hooks數組,編譯,運行,並觀察:每次按F9或點選Spotify應用程式中的next按鈕,都會記錄我們的訊息。

現在我們已經hook了skip功能,

如何逆向分析Spotify.app並hook其功能取得數據

我將發布剩餘的程式碼,但我不會完成其餘部分的逆向工作,因為這篇文章已經夠長的了。

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

針對如何檢查當前歌曲是否已經過了一半的方法我想說幾句。我最初的方法是實際呼叫popen,然後運行相應的AppleScript命令,但感覺這不太對。

我在Spotify二進位檔案上運行了class-dump,發現了兩個類別:SPAppleScriptObjectModel和SPAppleScriptTrack。這些方法公開了播放位置,持續時間和曲目ID所需的必要屬性。然後,我為這些屬性hook了getter,並使用next和back hooks調用它們(我認為Swizzle更合理,但我無法讓它正常工作)。

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

以上是如何逆向分析Spotify.app並hook其功能取得數據的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:亿速云。如有侵權,請聯絡admin@php.cn刪除

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具