Development: Recover protected application data from non-rooted Android phone

I had a near disaster at work yesterday. Our production server for a networked mobile app we’re building got wiped by mistake, and some of the data were not properly backed up. The good news is that the content of the DB is cached by the Android client. Since my phone is rooted, it was a simple task to recover the cache files (in this case in json format) from my phone.

However, the system runs in both English and French, and I only had the English data cached. We are currently beta testing with a bilingual group, so some of our testers are using it in French. I set about trying to recover the data from a tester’s phone, but none of them had rooted devices, which made the process a bit sticky. My first thought was to try Carbon Backup from Clockworkmod, which managed to do a data backup, but didn’t take the cache files.

I did a bit of Googling, and found a post about reading the /data directory without rooting. This turned me on to the run-as command in adb shell:

(the package names have been censored for obvious reasons)

% adb shell
shell@android:/ $ run-as com.example.myapp ls /data/data/com.example.myapp
run-as: Package 'com.example.myapp' is not debuggable

Unfortunately it turns out run-as only works for applications complied for debugging, not with release builds, and our test group had installed a release form Google Play.

I couldn’t install a debug build over the release package, because Android checks certificate signatures when you try to use adb to install a package. This seemed like a dead end, and I just about gave up.

Then it occurred to me that I could try signing the debug package with the release key before installing it:

% jarsigner -verbose -sigalg Sha1withRSA -digestalg SHA1 -keystore ../myapp.keystore InitialActivity-debug.apk myapp
Enter Passphrase for keystore: ***********
jarsigner: unable to sign jar: java.util.zip.ZipException: invalid entry compressed size (expected 18030 but got 18426 bytes)

Hmm, nope. A little more googling, and I found a pertinent question on stackoverflow. The second reply suggested doing this:

Rename the .apk file to .zip
Unpack the .zip file and remove the META-INF folder
Zip the folder again and rename it to .apk
Sign the apk:

I did this and ran jarsigner again, and it signed the package no problem.

At this point, I was still not too optimistic, but I ran adb install -r package.apk with the phone connected, and lo and behold, it installed. Opened up my adb shell, and gave it another whirl:

shell@android:/ $ run-as com.example.myapp ls /data/data/com.example.myapp
cache
databases
files
lib
shared_prefs

I was pretty amazed to see that it had worked. The rest was easy:

shell@android:/ $ run-as com.example.myapp
shell@android:/data/data/com.example.myapp $ cd cache
shell@android:/data/data/com.example.myapp/cache $ ls *.json
categories.json
deeds.json
events.json
friends.json
groupUsers-28.json
groups.json
requests.json
shell@android:/data/data/com.example.myapp/cache $ chmod 604 *.json

I made the files world-readable so back on my local terminal I could adb pull them:

% adb pull /data/data/com.example.myapp/cache/categories.json
83 KB/s (6700 bytes in 0.078s)

Repeat for the other files I needed, and I had all my data back!

I have to admit, I was pretty satisfied with myself when this worked. Hopefully posting this here will help somebody else out of a bind in the future.

tl;dr With some hackery and android build tools, managed to recover from a major data loss.

comments powered by Disqus