Frustrating the Android Hackers
Skyler Slade
Unlike the Grooveshark web application, which was always free to use, Grooveshark’s native mobile apps on Android, iOS, BlackBerry, webOS, and Symbian, required a paid Grooveshark Anywhere subscription to access most of their features. Playing songs on demand, creating playlists, adding songs to your library, and so on, required a paid account1. To encourage subscriptions, the Android and iOS apps offered free, 14-day trials. The Android application’s security features—minimal as they were—were standard for the era but an easy target for hackers looking to use the Grooveshark mobile service for free.
A subscription was required to play songs on-demand.
Grooveshark Android app version 2.7, released June 25, 2012
Trials were limited to once per device, and each user was allowed up to three devices. Limiting trials by device of course meant that the Grooveshark Android app needed to be able to uniquely identify the device on which it was running. If you could hack the app to change how it identified itself, you’d have an easy way to create perpetual free trials and use the service without a subscription.
The Grooveshark Android app’s primary method of identifying itself was by using the device’s cellular network identifier (IMEI, MEID, or ESN). But cellular network identifiers weren’t available on WiFi-only tablets. In this case, the app would attempt to use ANDROID_ID, which was a unique identifier exposed by the operating system. But ANDROID_ID was not always unique: a bug caused some popular devices running Android versions prior to 2.2 to always report 9774d56d682e549c as their ANDROID_ID2. On some other devices, like the Barnes & Noble NOOK—in whose app store Grooveshark was a featured partner3—ANDROID_ID always reported null. If neither a network identifier nor ANDROID_ID was available, the app would fall back to using a UUID that it generated itself, which we made persistent across reinstalls as best we could.
If there was a more tamper-proof way of uniquely identifying an Android device circa 2010-2012, we weren’t aware of it4. Limiting trials by user instead of device was an option, but our signup APIs were easily exploitable since we didn’t do any validation of new accounts; the now well-familiar pattern of clicking a link to validate an email address was uncommon for the era, and in any case, we didn’t do it.
In 2012, Google would introduce a proper subscriptions API with support for free trials for apps published in the Android Market5 but we would never have access to it: In April 2011, Google removed Grooveshark from the Android Market for unspecified terms of service violations6. Thereafter, anyone wanting the app had to download it directly from us and sideload it, and we were prevented from using any official Google Market subscription APIs.
Android applications were easy to decompile and redistribute. Hacked versions of the Grooveshark Android app would appear for download on popular forums within hours or days of an official release, and there wasn’t anything we could do to prevent it. We obfuscated the app’s bytecode after compilation, as was standard practice, but this wasn’t an effective countermeasure to hackers. Device integrity APIs didn’t exist yet. Our only option was to make hacked apps frustrating to use from the server side, which was the only part of the application under our control.
Jay, the lead developer of Grooveshark Lite, had the idea to use heuristics in the mobile API to detect when someone was using a hacked app and, once detected, return random errors and incorrect responses. The idea being that if hacked versions of the Grooveshark app were buggy and frustrating to use, maybe people would give up trying.
A normal, unmodified release of the Grooveshark Android app made API calls in a certain order, and certain API methods were only called a certain number of times per device, or per user, or per logged-in session, etc. We understood the behavior of the official app and in the mobile API, we noted important deviations. Each deviation was a signal, and enough signals indicated to us that a user was using a hacked app. Signals accumulated over time, so a user might be able to use a hacked app for several hours or days before we identified them, which had the added benefit of temporarily giving the impression that a hacker’s modifications had escaped detection.
Some early detection rules. We referred to users of hacked apps as "attackers."
Work notebook entry from late 2010
Once the mobile API identified a hacked app, its primary response was to return the wrong audio stream whenever a user attempted to play a song. A user might choose to play Gotye’s Somebody That I Used to Know—a very popular song of the era—but a hacked app would play a song from a local Gainesville punk rock band instead.
Fortunately for us, at least one popular Android app hacking community published their releases on a public forum. Soon after a release of a hacked Grooveshark app, the mobile API would collect enough signals to engage its countermeasures and users would reply that the app was buggy and was anyone else experiencing issues with the wrong song playing? Whether this strategy was actually successful in stopping anyone from using hacked versions of the app, I don’t know. From watching the forums, it certainly seemed like it did. But if nothing else, the cat and mouse game was a great source of entertainment for the team.
-
Without a subscription, users could access a limited number of tag- and genre- based autoplay stations. ↩︎
-
https://web.archive.org/web/20111110073305/https://goodereader.com/blog/electronic-readers/barnes-and-noble-unveils-the-nook-tablet ↩︎
-
Our strategy was actually a documented best practice. Whether we followed this guide or stumbled upon the strategy ourselves, I have long since forgotten: https://web.archive.org/web/20170305052727/https://android-developers.googleblog.com/2011/03/identifying-app-installations.html ↩︎
-
Later renamed to the Google Play Store. ↩︎
-
https://web.archive.org/web/20110409114702/http://news.cnet.com/8301-31001_3-20051156-261.html ↩︎