---------
Most of my other tutorials deal with registration schemes, and manual ad removal. There's other reasons to reverse software. Let's take for example a game called Plague Inc. I bought this game, and enjoyed playing it while the novelty lasted. One aspect of the game is that they have a lot of unlockable perks, and buyable game types. For shits and giggles, I decided it'd be fun to figure out to see if I could unlock all unlockables and get all game types.
Tools Needed:
-------------
* An android device or emulator with a functional adb connection (ADB setup beyond scope of this tutorial)
* UNIX or unix-like environment with apktool (Use deprecated version 1.4.1)
(Note: apktool no longer works with baksmali, so you have to use an older version.)
* JDK 1.6
(Note: 1.7 can be used, but there's an issue with a change of algorithm with the jarsigner, which causes some unneccessary difficulties, namely a "NO_CERTIFICATE_EXISTS" error upon APK installation)
Java 1.7:
---------
Key Creation:
keytool -genkey -v -keystore testing.keystore -alias testing -keyalg RSA -keysize 2048 -validity 10000
APK Signing:
jarsigner -verbose -keystore /home/ben/testing.keystore -digestalg SHA1 -sigalg MD5withRSA test.apk testing
Retrieval of APK From Device:
-----------------------------
You first need to establish your adb bridge, make sure your device shows up when you type:
adb devices
The name of the apk is:
com.miniclip.plagueinc-1.apk
I'm not sure if that hyphen naming notation is specific to Cyanogenmod or what, but it might be called:
com.miniclip.plagueinc.apk
With most setups, you should be able to type the following to get the APK:
adb pull /data/app/com.miniclip.plagueinc-1.apk
So now you have com.miniclip.plagueinc-1.apk in your current directory.
Unbundling:
-----------
First off type:
apktool
If this gives you command not found, go look up how to do install apktool.
In the directory that contains the target APK file, type the following:
apktool d com.miniclip.plagueinc-1.apk work
This unbundles an APK into its own directory called 'work'
Go into the 'work' directory, and check out the following tree:
res/ - This directory is where all resources (images, layouts, etc) live.
smali/ - This directory is where disassembled java files go.
Unlockable Discovery:
---------------------
Plague Inc's source files were unobfuscated, and pretty easy to follow. If you look at the application's main activity:
smali/com/miniclip/plagueinc/PlagueIncActivity.smali
You'll see a lot of references to com/miniclip/nativeJNI, when you look at this directory, you'll see a ton of cocojava classes, with a lot of dollar signs and numbers afterwards. This is a telltale sign of a singleton design. Opening up the cocojava.smali, you start to see some interesting items:
.field public static mTEST_INAPPS:Z
.field protected static mUSE_ADS:Z
I like seeing 'test' in things because often times there's development code left over in applications that allow you to do things most would not allow you to do. I grepped for the use of mTEST_INAPPS, and found a couple of instances:
ben@localdev:~/plagueinc/work/com.miniclip.plagueinc-1/smali/com/miniclip$ grep -R "mTEST_INAPPS" *
nativeJNI/cocojava.smali:.field public static mTEST_INAPPS:Z
nativeJNI/cocojava.smali: sput-boolean v2, Lcom/miniclip/nativeJNI/cocojava;->mTEST_INAPPS:Z
nativeJNI/cocojava$30.smali: sget-boolean v0, Lcom/miniclip/nativeJNI/cocojava;->mTEST_INAPPS:Z
nativeJNI/cocojava$29.smali: sget-boolean v0, Lcom/miniclip/nativeJNI/cocojava;->mTEST_INAPPS:Z
plagueinc/PlagueIncActivity.smali: sput-boolean v12, Lcom/miniclip/plagueinc/PlagueIncActivity;->mTEST_INAPPS:Z
Ok, so checking out nativeJNI/cocojava$30.smali, I see the following code structure:
.line 2016
:cond_0
sget-boolean v0, Lcom/miniclip/nativeJNI/cocojava;->mTEST_INAPPS:Z
if-eqz v0, :cond_1
.line 2017
sget-object v0, Lcom/miniclip/nativeJNI/cocojava;->mContext:Landroid/content/Context;
check-cast v0, Lcom/miniclip/nativeJNI/InAppActivity;
const-string v1, "android.test.purchased"
invoke-virtual {v0, v1}, Lcom/miniclip/nativeJNI/InAppActivity;->requestPurchaseAct(Ljava/lang/String;)V
goto :goto_0
.line 2019
:cond_1
sget-object v0, Lcom/miniclip/nativeJNI/cocojava;->mContext:Landroid/content/Context;
Basically, it's taking the value of this mTEST_INAPPS, if it's 0, it skips a section of code that calls a purchase with a string argument of "android.test.purchased". I want to do this always, so I'm just going to remove the if-eqz which triggers the jump.
Now on to the nativeJNI/cocojava$29.smali:
.line 1991
:cond_0
sget-boolean v0, Lcom/miniclip/nativeJNI/cocojava;->mTEST_INAPPS:Z
if-eqz v0, :cond_1
.line 1992
sget-object v0, Lcom/miniclip/nativeJNI/cocojava;->mContext:Landroid/content/Context;
check-cast v0, Lcom/miniclip/nativeJNI/InAppActivity;
const-string v1, "android.test.purchased"
invoke-virtual {v0, v1}, Lcom/miniclip/nativeJNI/InAppActivity;->requestPurchaseActManaged(Ljava/lang/String;)V
goto :goto_0
.line 1994
:cond_1
Same exact structure, let's go ahead and remove that if-eqz as well. Your job is now done, all doors are now open. A better way to do this would have been to modify the value in the cocojava object.
Rebundling:
You can rebundle the APK by using apktool against the extracted directory
For instance if you have this directory tree:
com.miniclip.plagueinc-1
|_ smali
|_ res
|_ assets
your directory is com.miniclip.plagueinc-1
You then run the following:
apktool b com.miniclip.plagueinc-1 hacked.apk
This packages the APK back up. You need to sign it with jarsigner (see signing section) before you can install it by typing:
adb install hacked.apk