In this document, I will describe how I managed to get unlimited stars and coins in the Android game "Lily's Garden". In short, here are the steps:
- Use the
adb backup
feature to extract all of the game's data - Extract the Android backup into a tar file
- Modify the file which stores the number of coins and stars
- Re-sign the coins/stars field using a reverse-engineered HMAC key
- Convert the tar file back to an Android backup
- Use the
adb restore
feature to put the new game data on the phone
To backup the game's data, run the following command with your phone plugged in and in developer mode:
$ adb backup dk.tactile.lilysgarden
This creates a file called backup.ab. To extract a tarbal from this, run this command (source):
$ dd if=backup.ab bs=1 skip=24 | python2 -c "import zlib,sys;sys.stdout.write(zlib.decompress(sys.stdin.read()))" >backup.tar
Now you want to edit the file called apps/dk.tactile.lilysgarden/sp/dk.tactile.lilysgarden.v2.playerprefs.xml
inside of backup.tar
.
You can do this with vim, or with some other tool.
I found that untarring->editing->tarring did not work, probably because of permissions.
Inside of dk.tactile.lilysgarden.v2.playerprefs.xml
, look for a line starting with:
<string name="4b79e50f33d58cde3869c24d8dd0102c">
This contains most of the game's state, as far as I can tell. The tag contains a bunch of URL-encoded JSON, followed by a 32-byte HMACSHA256 checksum. Near the end of the JSON should be something like this:
"in":{"it":{"BoosterBomb":0,"BoosterMagic":0,"BoosterRocket":0,"BoosterClearSingle":1,"Life":0,"Coin":590,"star":0,"BoosterClearCross":0,"UnlimitedLives":0}}
Here, you can change Coin
and star
as much as you like. However, you must re-generate the checksum as follows:
checksum = hmac_sha256(key="9e30c2ab-e2d6-47bf-9999-3371f14bdac0", data=<json>+"UserSettingsManagerLocalPrivateUserSettings")
Where <json>
is the new JSON data (not URL-encoded) and +
is string concatenation.
Now that you have modified the backup tarbal, you can create a new backup, backup_new.ab
file as follows:
$ dd if=backup.ab bs=1 count=24 >backup_new.ab
$ cat backup.tar | python2 -c "import zlib,sys;sys.stdout.write(zlib.compress(sys.stdin.read()))" >>backup_new.ab
Finally, upload this backup file to your phone like so:
$ adb restore backup_new.ab
First, I extracted the apk from my phone using adb
, and then I extracted it with apktool.
I quickly noticed that the game uses Unity, and that most of the source code was in a file called armeabi-v7a/libil2cpp.so
.
Upon discovering that libil2cpp.so
had no symbols, I tried using adb backup
to inspect the app's data directly.
Using the data extracted from adb backup
, I grep'd for keywords like "coin" and "star", and eventually found where the game stored these quantities.
However, I could not modify these fields as they were signed. Modifying any values resulted in the game being effectively reset to the starting point.
Since the backup data was signed, I knew I needed to look deeper at the code. I found Il2CppInspector, and used it to get symbols. With symbols, I found a few things of note:
public class TactilePlayerPrefs // TypeDefIndex: 5728
{
...
public static void SetSignedString(string name, string value); // 0x00B2060C
public static void SetSecuredString(string name, string value); // 0x00B209F0
public static string GetString(string name); // 0x00B20A98
public static string GetString(string name, string defaultValue); // 0x00B20B48
public static string GetSignedString(string name, string defaultValue); // 0x00B20C00
public static string GetSecuredString(string name, string defaultValue); // 0x00B20EC8
...
}
public static class TactilePrefsExtensions // TypeDefIndex: 5730
{
// Extension methods
public static string TactilePlayerPrefsHash(this string value, string salt); // 0x00B20724
public static byte[] TactilePlayerPrefsCryptKeyHash(this string value); // 0x00B219DC
}
I used objdump
to dump the assembly for libil2cpp.so
, and found the corresponding blocks of instructions for these methods.
It turned out that SetSecuredString()
simply called SetSignedString
, which md5-hashes the key name and signs the value using a hash from TactilePlayerPrefsHash
.
Looking at the disassembly for TactilePlayerPrefsHash()
(which I include below), I inferred the following original code:
key = <something from a static variable after a lot of indirection>
x = HMACSHA256(Encoding.UTF8.GetBytes(key))
y = x.ComputeHash(Encoding.UTF8.GetBytes(value + salt))
z = StringBuilder()
foreach (byte b in y) {
z.append(b.ToString("x2"))
}
return z.ToString()
The only missing piece was key
.
It seemed that the key was stored somewhere in the .bss
section (for static variables).
This looked bad, since it could be generated at runtime in some complex way.
However, I was hopeful that the key was just sitting inside of the binary somewhere, or in global-metadata.dat
(where libil2cpp.so
stores many of its strings and symbols).
Thus, I wrote a little program (keysearch.cs below) that tried every short substring in the entire binary as the key.
The program worked, and I found the key of 9e30c2ab-e2d6-47bf-9999-3371f14bdac0
!
hello,I have been studying how to modify this game for a long time . but i have any progress
there is several buttons in the top of main view such as facebook button and setting button so on .
now i want to remove one of them or both of them, how to do ? can you give me hints?
i use ida open so but i don't find any symbols of them . and how to remove a components such as label or button in U3d game
thanks.