Extracting ADB backups

Sometimes when developing, reversing, or otherwise working with Android apps, it can be useful to see what's in your (or your target's) data directory. Typically, you either have root or the app's signing keys so it's trivial to see what's going on. However, there are couple situations where you might not be rooted or can't directly read the app's data directory. For example, you might be working on a friend's device or debugging a library that is included in an app for which you don't have the signing keys.

This post addresses one possible solution to this problem, which is to examine a backup of that app's data taken via ADB. ADB backups are meant to make local backups of app data that are compressed and optionally encrypted. In our case, we don't want or need to encrypt the backup since we're planning to extract it for our own reading anyway.

Let's look at the adb backup options:

adb backup [-f <file>] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all] [-system|-nosystem] [<packages...>]
       - write an archive of the device's data to <file>.
       If no -f option is supplied then the data is written
       to "backup.ab" in the current directory.
       (-apk|-noapk enable/disable backup of the .apks themselves
          in the archive; the default is noapk.)
       (-obb|-noobb enable/disable backup of any installed apk expansion
          (aka .obb) files associated with each application; the default
          is noobb.)
       (-shared|-noshared enable/disable backup of the device's
          shared storage / SD card contents; the default is noshared.)
       (-all means to back up all installed applications)
       (-system|-nosystem toggles whether -all automatically includes
          system applications; the default is to include system apps)
       (<packages...> is the list of applications to be backed up.  If
          the -all or -shared flags are passed, then the package
          list is optional.  Applications explicitly given on the
          command line will be included even if -nosystem would
          ordinarily cause them to be omitted.)

Note that if the app developer has allowBackup:false in their manifest, you won't be able to use this method.

So we can create a backup for our app with something like:

adb backup <package_name>

This will open a prompt on the device to confirm a backup.

Once we have this backup file (backup.ab by default) we can extract it to see what's inside. The file format is a modified compressed tar. The modifications are an extra four lines at the beginning of the file that contain metadata about the backup; ANDROID BACKUP "magic number," version, compression, and encryption algorithm respectively. Since the file isn't encrypted, we can easily extract the file by dropping the first four "extra" lines and prepending the gzip header.

First four lines of backup.ab look something like this:

ANDROID BACKUP
3
1
none
...rest of file

To extract:

# drop the first four lines:
tail -n +5 backup.ab > trimmed.dat 

# add the gz header:
printf "\x1f\x8b\x08\x00\x00\x00\x00\x00" | cat - trimmed.dat > backup.gz 

# eXtract, Verbose, compreZZed, File
tar -xvzf backup.gz

# or all together:
(printf "\x1f\x8b\x08\x00\x00\x00\x00\x00" ; tail -n +5 backup.ab) | tar xvzf -

Once the backup is extracted, we can easily browse what data was stored locally by our application.