In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-20 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >
Share
Shulou(Shulou.com)05/31 Report--
This article shows you how to reverse analyze Spotify.app and hook its function to obtain data, the content is concise and easy to understand, absolutely can make your eyes bright, through the detailed introduction of this article, I hope you can get something.
Project
The goal of the project is to build a Spotify client that can learn my listening habits and skip some songs that I usually skip. I have to admit that this demand comes from my laziness. I don't want to create or look up playlists when I'm in the mood to listen to some music. What I want is to select a song in my library, then play other songs at random, and remove songs that are not "flow" from the queue.
To achieve this, I need to learn some kind of model that can perform this task (maybe more in future posts). But in order to be able to train a model, I first need data to train it.
data
I need a complete history of listening to songs, including the songs I skipped. It's easy to get the history. Although Spotify API only allows you to get the last 50 songs played, we can set up a cron job to poll the endpoint repeatedly. The complete code has been released here: https://gist.github.com/SamL98/c1200a30cdb19103138308f72de8d198
The hardest part is tracking skip. Spotify Web API does not provide any endpoints for this. I used Spotify AppleScript API to create some services that control playback (the rest of this article will cover the MacOS Spotify client). I can use these services to track skipped content, but it feels like avoiding a challenge. How can I finish it?
Hooking
I recently learned about hooking techniques where you can "intercept" function calls generated from the target binaries. I think this will be the best way to track skip.
The most common hook type is interpose hook. This type of hook overrides relocation in PLT, but what does that mean?
The PLT or procedure link table allows your code to refer to an external function (think libc) without knowing where the function is in memory. You only need to reference an entry in PLT. The linker performs a "relocation" for each function or symbol in the PLT at run time. One advantage of this approach is that if the external function is loaded at a different address, you only need to change the relocation in the PLT, rather than each reference to the function in the code.
So when we create an interpose hook for printf, whenever our hooking process calls printf, we call the printf implementation instead of libc (our custom libraries usually call the standard implementation, too).
With some basic knowledge of hooks, let's try to insert a hook into Spotify. But first we need to figure out what we want with hook.
Find the location of hook
As mentioned earlier, only one interpose hook can be created for external functions, so we will look for functions in libc or Objective-C runtime.
When studying where to hook, I think a good place to start hooking is for Spotify to deal with "media control keys" or F7-F9 on my MacBook. Assume that the handler of these keys calls the function when the Next button is clicked in the spotify application. I finally found the SPMediaKeyTap library on: https://github.com/nevyn/spmediakeytap. I think I can give it a try and see if Spotify copied and pasted the code in this library. In the SPMediaKeyTap library, there is a method startWatchingMediaKeys. I ran the strings command on the Spotify binary to see if they had this method, and sure enough:
Bingo!! If we load the Spotify binary into IDA (free version, of course) and search for this string, we will find the appropriate method:
If we look at the source code corresponding to this function, we will find the interesting parameter tapEventCallback of the CGEventTapCreate function:
If we review the disassembly, we can see that the sub_ 10010C230 subroutine is passed as a tapEventCallback parameter. If we look at the source code or disassembly of this function, we see that only one library function, CGEventTapEnable, is called:
Let's try the function hook.
The first thing we need to do is to create a library to define our custom CGEventTapEnable. The code is as follows:
# include # include void CGEventTapEnable (CFMachPortRef tap, bool enable) {typeof (CGEventTapEnable) * old_tap_enable; printf ("Ihumm hooked!\ n"); old_tap_enable = dlsym (RTLD_NEXT, "CGEventTapEnable"); (* old_tap_enable) (tap, enable);}
The dlsym function call gets the address of the actual library CGEventTapEnable function. Then we call the old implementation so that we don't accidentally break anything. Let's compile our library (https://ntvalk.blogspot.com/2013/11/hooking-explained-detouring-library.html) like this:
Gcc-fno-common-c. C gcc-dynamiclib-o.
Now, let's try running Spotify:DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES= / Applications/Spotify.app/Contents/MacOS/Spotify when inserting the hook. Click to enter:
Spotify opens normally, but Apple's system Integrity Protection (SIP) does not let us load the unsigned library: (.
Fortunately, I'm a member of Apple's reasonably priced developer project, so I can code the library. The problem can be regarded as solved. Let's sign our library with a $100 certificate, run the previous command, and then.
Failed. This is not surprising. Apple does not allow you to insert libraries signed with any of the old identifiers, only those used when signing the original binaries. Looks like we have to find another way to hook Spotify.
As a supplementary note, careful readers may notice that our hook function CGEventTapEnable is called only when media key event times out. So even if we could insert the hook, we might not see any output. The main purpose of this section is to elaborate on my initial failures (and negligence) and to serve as a learning experience.
HookCase
After some digging, I found a great library, HookCase: https://github.com/steven-michaud/HookCase. HookCase lets us implement a hook type that is more powerful than inserting a patch hook.
By modifying the function you want hook to trigger the interrupt to insert Patch hooks. The kernel can then handle this interrupt and then transfer execution to our personal code. For those who are interested, I strongly recommend that you read the HookCase documentation because it is more detailed.
Patch hooks not only allows us to make hook calls to external functions, but also allows us to hook any function in the target binary (because it does not depend on PLT). HookCase provides us with a framework to insert patch and / or interpose hooks, as well as kernel extensions to handle interrupts generated by patch hooks and to run our custom code.
Looking for sub_100CC2E20
Now that we have a way to hook Spotify any function in the binary, there is only one last problem left. What's the location?
Let's revisit the SPMediaKeyTap source code to see how to handle media control keys. In the callback function, we can see that if we press F7, F8 or F9 (NX_KEYTYPE_PREVIOUS,NX_KEYTYPE_PLAY, etc.), we will execute the handleAndReleaseMediaKeyEvent selector:
Then notify delegate in the selector:
Let's look at the delegate method in repo:
It turns out that it just sets up a template for processing keys. Let's search for the receiveMediaKeyEvent function in IDA and view the graphical view of the corresponding function:
It looks very similar, doesn't it? We can see that a common function sub_10006FE10 is called for each type of key, and only an integer parameter is set to distinguish between them. Let's hook it and see if we can record the keys pressed.
As we can see from the disassembly, sub_10006FE10 takes two parameters: 1) a pointer to the playerDelegate property of the SPTClientAppDelegate singleton, and 2) an integer that specifies what type of event occurred (0 for pause / playback, 3 for next, and 4 for previous).
Take a look at sub_10006FE10 (I won't include it here, but I strongly suggest you check it yourself), we can see that it is actually a wrapper for sub_10006DE40, which contains most of the content:
Wow! It looks complicated. Let's try to break it down.
Judging from the structure of this graph, there is a node pointing to the top with many outgoing edges:
As suggested by IDA, this is the switch statement on esi (the second integer argument described earlier). It seems that Spotify is dealing with more than just Previous,Pause/Play and Next. Let's focus on dealing with Next or 3 block:
Admittedly, it took me some time to do this, but I want you to notice call R12 in the fourth line at the bottom. If you look at other situations, you will find a very similar pattern of calling registers. This seems like a good function, but how do we know where it is?
Let's open a new tool: debugger (debugger). I had a lot of trouble when I first tried to debug Spotify. Now maybe it's because I'm not familiar with debuggers, but I think I've come up with a pretty smart solution.
We first set a hook on sub_10006DE40, and then we trigger a breakpoint in the code. We can do this by executing the assembly instruction int 3 (for example, debugging like GDB and LLDB).
Here's what hook looks like in the HookCase framework:
After adding this to the HookCase template library, you must also add it to the user_hooks array:
We can then compile it using the template provided by Makefile HookCase. You can then insert the library into Spotify:HC_INSERT_LIBRARY= / Applications/Spotify.app/Contents/MacOS/Spotify using the following command.
Then we can run LLDB and attach it to the running Spotify process, as follows:
Try pressing F9 (if Spotify is not the active window, it may open iTunes). The int $3 line in the hook should trigger the debugger.
Now we can get to the sub_10006DE40 entry point. Note that the PC will be located at the location corresponding to the address shown in the IDA (I think this is due to the location where the process is loaded into memory). In my current process, the push R15 instruction is located in 0x10718ee44:
In IDA, the address of the instruction is 0x10006DE44, which gives us an offset 0x7121000. In IDA, the address where the R12 instruction is called is 0x10006E234. Then we can add the offset to the address and set a breakpoint, b-a 0x10718f234, accordingly, and then continue.
When we click on the target instruction, we can print out the contents of register R12:
All we have to do is subtract the offset from this address, and look, we got our nominal address: 0x100CC2E20.
Hooking sub_100CC2E20
Now, let's hook the function:
Add it to the user_hooks array, compile, run, and watch: every time you press F9 or click the next button in the Spotify application, our message is logged.
Now we have hook the skip function
I'll release the rest of the code, but I won't do the rest of the reverse work, because this article is long enough.
In short, I also hook the previous function (which would be a good exercise if you follow suit). Then, of the two hooks, I first check to see if the current song is halfway through. If so, I don't do anything, assuming I'm just tired of the song, not that it's inappropriate. Then in backs (F7), I pop up last skip.
I'd like to say a few words about how to check whether the current song is halfway through. My initial approach was to actually call popen and then run the corresponding AppleScript command, but it didn't feel right.
I ran class-dump on the Spotify binary and found two classes: SPAppleScriptObjectModel and SPAppleScriptTrack. These methods expose the necessary properties for the playback position, duration, and track ID. Then I hook the getter for these properties and call them with next and back hooks (I think Swizzle is more reasonable, but I can't make it work).
I use a file to trace skips, where the first line contains the number of skips, and when we skip, we increment this counter and write the trace ID and timestamp to the file on the counter specified line. In the back button, we just reduce this counter. This way, when we press the back button, we just set the file to write new skips to the backdated file.
The above content is how to reverse analyze Spotify.app and hook its function to obtain data, have you learned the knowledge or skills? If you want to learn more skills or enrich your knowledge reserve, you are welcome to follow the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.