Hacking Shared Preferences on non-rooted Android devices

While attempting to analyze applications, I often face the problem that they only allow ARM devices to launch them. You can simply check that by looking at the folder lib when after unzipping an APK. Then you need a rooted device if you want to checkdynamic data about the application.

The goal of this article is to show you how you can easily modify an application to analyse the internal files created.

Looking at internal files

When you install an application, its internal folder is located on an Android device in : data/data/<package_name>/ and can contain assets, libs, databases, shared preferences, etc.

herolte:/data/data/com.example.application $ ls
app_.gpg.classloader app_webview code_cache files shared_prefs 
app_textures         cache       databases  lib   

But if you try to list its files without rooting your phone, you will quickly face a wall, you don’t have the permissions to read the content.

herolte:/ $ ls /data/data/com.example.application
ls: /data/data/com.example.application: Permission denied
1|herolte:/ $ id
uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid) context=u:r:shell:s0

Making possible to look at it without root

For a developper, it’s important for debugging purpose to make the files used available, so you can flag an android application as debuggable to be able to access the filesystem.

In the Manifest.xml :

<manifest ...>
    ...
    <application ... android:debuggable="true">
        ...
    </application>
</manifest>

So here is the trick, we can recompile an application to make it debuggable simply with apktool && sign.jar

$ apktool d test_app.apk
$ # Then you add the debuggable flag to the Manifest.xml
$ apktool b test_app/ -o test_app.modified.apk
$ java -jar sign.jar test_app.modified.apk
$ adb install test_app.modified.s.apk

Here we are, you are now able to launch a shell with the rights to access the application internal’s :

$ adb run-as com.example.application
> ls
cache
code_cache
lib
shared_prefs
> id
id=10170(u0_a170) gid=10170(u0_a170) groups=10170(u0_a170),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid) context=u:r:untrusted_app:s0:c170,c256,c512,c768

Playing with shared preferences

Shared preferences are often used as save file for internal configurations of an application. Vulnerability related to their access exist and can be very dangerous (MODE_WORLD_WRITEABLE, MODE_WORLD_READABLE, etc.) but here my goal will be to use them to demonstrate of a game cheat can work.

However I do not encourage you to cheat in games.

Let’s take an example game named EasySharedGame, after playing few hours, you are frustrated an want to unlock the God Staff of mothafucka destructor but it cost a lot of money and you don’t have such time because you’re a busy person.

After playing for a while you’ve got 9547 pounds. First step is to close the the game and see if such value exists in the shared preferences. So we will grep like a forensic ctf player and here , magic there is a file match:

$ grep -lre 9547 .
./shared_prefs/playerprefs.xml
$ grep 9547 ./shared_prefs/playerprefs.xml
    <int name="money_count" value="9547" />

You can change this value to 99999999 and become the hero you always wanted to be. Now imagine you want your bestfriend to be able to have that much money but he know nothing about Android stuff.

I’ll show you how to make it permanent so you’ll be the hero of you’re friend too.

First keep the apktool decompilation output.

Then in the Manifest.xml you will look for the first activity launched. Here we will take UnityPlayerActivity :

<activity android:label="@string/app_name" android:maxAspectRatio="1.86" android:name="com.unity3d.player.UnityPlayerActivity" android:resizeableActivity="false" android:screenOrientation="sensorLandscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true"/>
</activity>

In the smali folder search for the associated package com/unity3d/player and add the file CheatClass.smali containing the following code ( I simply generated this Samli by compiling a test application and extracted it with apktool) :

.class public Lcom/unity3d/player/CheatClass; # Here change your package name
.super Ljava/lang/Object;
.source "CheatClass.java"

.method public constructor <init>()V
    .locals 0

    .line 6
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    return-void
.end method

.method public static moreCoinsPlz(Landroid/app/Activity;)V
    .locals 4
    .param p0, "context"

    .line 9
    const-string v0, "player_prefs" # Here change the preference file name

    const/4 v1, 0x0

    invoke-virtual {p0, v0, v1}, Landroid/app/Activity;->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences;

    move-result-object v0

    .line 10
    .local v0, "sp":Landroid/content/SharedPreferences;
    invoke-interface {v0}, Landroid/content/SharedPreferences;->edit()Landroid/content/SharedPreferences$Editor;

    move-result-object v1

    .line 11
    .local v1, "editor":Landroid/content/SharedPreferences$Editor;
    const-string v2, "money_count" # Here the key name

    const v3, 0x5f5e0ff # And here the value to change

    invoke-interface {v1, v2, v3}, Landroid/content/SharedPreferences$Editor;->putInt(Ljava/lang/String;I)Landroid/content/SharedPreferences$Editor;

    .line 12
    invoke-interface {v1}, Landroid/content/SharedPreferences$Editor;->apply()V

    .line 13
    return-void
.end method

And in UnityPlayerActivity search the function onCreate and add this to the smali code :

.method protected onCreate(Landroid/os/Bundle;)V

    ...
    
    move-object v0, p0

    check-cast v0, Landroid/app/Activity;

    invoke-static {v0}, Lcom/unity3d/player/CheatClass;->moreCoinsPlz(Landroid/app/Activity;)V # change the package name of CheatClass

    ...
.end method

Recompile and sign it. Tadam your friends have a cheated version too.

(if you forgot : apktool b base/ -o cheat.apk && java -jar sign.jar cheat.apk )

After the first launch the shared_pref will contain the modified value :

herolte: $ cat player_prefs.xml                                                                                             
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <int name="money_count" value="99999999" />
</map>