How to circumvent the Android licensing check (and play the games you bought abroad)

NOTE: This howto requires quite a lot of prior knowledge. Don't have that? No problem at all, because there will be a talk on it on April 20th, 2011 at BSidesLondon. That's also why there are many details missing - it would kinda ruin the purpose of the talk. If I find the time, I'll post some detailed instructions after the talk.

DISCLAIMER: I'm not trying to encourage piracy with this article, merely showing people with the necessary technical background how they can circumvent the Android license check in one area where it, pardon my French, sucks donkey balls. Usual disclaimer, if you're my target audience you know that stuff: I assume no legal responsibility, reverse engineering might be illegal (depending on your local laws) in your part of the world, and most importantly, don't get caught.

Introduction

I travel quite a bit, so I'm used to spending a few hours at the airport or sometimes days (especially in countries like Albania or what some like to call "third world countries") without any connection to the Internet. Yeah, there might be some access points at the airport, but they usually bear an unreasonably high price tag ($10 for an hour? From my cold dead hands...).

Now, this isn't much of a problem, since I bought these cool new Android games right before leaving and would really like to try them out when I'm bored. Right? Right. Apart from one thing.
Invalid License. Please contact the APP support team and email your order details
This game is not licensed. Please purchase it from Android Market.\nIf you already purchased, please check your network connection and try back later.
License verification failed, please reinstall APP from Android Market.
This application is not licensed. Please purchase it from Android Market.
And my favourite:
This is an illegal copy of APP. You can get full version of APP by following the link below.

In my eyes, this is borderline illegal. I purchased stuff and can't use it, and in some cases the messages suggest that I just stole things. "Sorry, you can't use what you just bought. Thief."
I do, however, understand the motivation behind it. Still, I'm a paying customer. If you make it less attractive to use legal versions, I (as the customer) am going to give you the one finger salute.

But it's all good. Time for a treatment I like to call enforced customer rights.

How the license check works

I'm just going to give you a very high-level overview here, if you really want to find out more, read the docs (or reverse engineer it...but as usual, legal situation...).

Apps that want to use the license check use the Market API to query whether a certain customer has purchased a license through the Market. If you ever see a package com.android.vending.licensing in an app, you most likely just found one that uses this method.
The App needs to provide a class <T implements com.android.vending.licensing.LicenseCheckerCallback> that implement public void abstract allow(), public abstract void applicationError(com.android.vending.licensing.LicenseCheckerCallback.ApplicationErrorCode) and public abstract void dontAllow(). It's probably rather easy figure out the meaning of each one...if not, find and read the docs ;)
To keep it short, you grab an instance of com.android.vending.licensing.LicenseChecker and call its checkAccess(com.android.vending.licensing.LicenseCheckerCallback) method. The LicenseChecker will then perform the query and call the appropriate method in the callback. One nice feature of that course of action is that the backend can cache the result for a few days, so if you're only going on a two-day trip, you may now stop reading, just start the app right before leaving.

Let's break it!

You need some tool to disassemble your .apk file - personally, I prefer apktool, but if you are really fond of some other tool, go for it. Hell, disassemble it by hand if you like the challenge.
Pull the apk and disassemble it. I'll assume that we're working with smali for the rest of the tutorial, but if you use anything else, you probably know how to port the code below.

Find the LicenseChecker class - the most straightforward way is to use
find <dir> -name 'LicenseChecker.smali'
If you want something a bit more fancy that would still work if the developer (in a misguided attempt to obfuscate things) renamed or moved some classes, try this one instead:
find <dir> -name '*smali' -exec grep -H 'allow()' {} \;
Of course, with the latter version, you should make sure that you find the caller of allow() and not the declaration (which would be in the callback).

Open that file and find the following line:
.method public declared-synchronized checkAccess(Lcom/android/vending/licensing/LicenseCheckerCallback;)V
Then replace the body of that method with something like this:

    .locals 9
    .parameter "callback"

    .prologue
    monitor-enter p0

    :try_start_0
    const-string v1, "LOLicenseChecker"

    const-string v2, "Oops. Hax."

    invoke-static {v1, v2}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I

    invoke-interface {p1}, Lcom/android/vending/licensing/LicenseCheckerCallback;->allow()V
    :try_end_0
    .catchall {:try_start_0 .. :try_end_0} :catchall_0

    goto :goto_0

    :catchall_0
    move-exception v0

    :goto_0
    monitor-exit p0

    return-void

...and repeat the same thing for this method:
.method private declared-synchronized handleServiceConnectionError(Lcom/android/vending/licensing/LicenseValidator;)V

    .locals 3
    .parameter "validator"

    .prologue
    .line 249
    monitor-enter p0

    :try_start_0

    const-string v1, "LOLicenseChecker"

    const-string v2, "Oops. Hax. (no remote either)"

    invoke-static {v1, v2}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I

    invoke-virtual {p1}, Lcom/android/vending/licensing/LicenseValidator;->getCallback()Lcom/android/vending/licensing/LicenseCheckerCallback;

    move-result-object v0

    invoke-interface {v0}, Lcom/android/vending/licensing/LicenseCheckerCallback;->allow()V
    :try_end_0
    .catchall {:try_start_0 .. :try_end_0} :catchall_0

    .line 256
    :goto_0
    monitor-exit p0

    return-void
    :catchall_0
    move-exception v0

    monitor-exit p0

    throw v0

What do these methods do now? Well, much less than before, that's for sure. Writing a short message to the log (you can retrieve that one with logcat) and calling the callback's allow() method, plus a bit of exception "handling".
You can remove the log messages and exception handling, of course, the former is just useful for debugging purposes and the latter in case some app passes around null values.

Now for the last part: Re-package your app, sign it, remove the old installation (PROTIP: adb uninstall -k <appname> will uninstall the app and keep its data and cache intact), and install your new package. You're done, now catch your flight to the Bahamas and go play!

But...but...I can't find LicenseChecker.smali!

That might mean that the app doesn't use the license check or...wait, do all of your packages, classes and methods have single-letter names instead of the human-friendly ones that give away their intended use?

...yes? That a problem?

Congratulations, sonny, you just set your anchor firmly in Oh-go-screw-yourself country. Chances are that the app developer didn't like people snooping around in his source and obfuscated it with ProGuard. Reversing apps like that is pretty annoying, but not impossible - I'm going to go into that in my BSidesLondon talk (see the top of the page for more infos).