Android のバックアップファイルを展開する

Android 4.0 以降?でサポートされた adb backup で生成されるファイルを展開してみた。

AOSP とか読まずにネットで拾った情報からだが,

  • 先頭にヘッダが入る
  • ヘッダ以降は,データ群を tar 形式でまとめたものを zlib 形式で圧縮 (アルゴリズム: RFC 1951 Deflate, フォーマット: RFC 1950) したもの

という構造らしい。

# backup2tar.py
import sys, zlib

buflen = 4096

f = open(sys.argv[1], 'rb')
try:
  if f.readline() != "ANDROID BACKUP\x0a":
    raise IOError, "Invalid backup file"
  if f.readline() != "1\x0a":
    raise IOError, "Invalid version number"
  if f.readline() != "1\x0a":
    raise IOError, "Invalid version number"
  if f.readline() != "none\x0a":
    raise IOError, "Encrypted backup file is not supported"

  z = zlib.decompressobj()

  while True:
    buf = f.read(buflen)
    if len(buf) <= 0:
      break
    decompressed = z.decompress(buf)
    sys.stdout.write(decompressed)

  decompressed = z.flush()
  sys.stdout.write(decompressed)
  sys.stdout.flush()
finally:
  f.close()

tar による展開まではやっていないので,

$ python backup2tar.py backup_file | tar xvf -

のように tar コマンドと組み合わせて使う。

tar アーカイブの中身は,基本的に対象パッケージの /data/data ディレクトリの内容物をまとめたものであるようだ。ただし _manifest ファイルの構造はわからなかった。

Pythontarfile モジュールを使えば tar 部分までやってやれなくはないのだが,uid / gid 等の情報*1を失うのがイヤなので,とりあえず tar 形式に変換する部分まで作った。

逆に tar アーカイブからバックアップ形式に変換するスクリプトはこちら。

# tar2backup.py
import sys, zlib

buflen = 4096

f = open(sys.argv[1], 'rb')
try:
  sys.stdout.write("ANDROID BACKUP\x0a1\x0a1\x0anone\x0a")

  z = zlib.compressobj(9)

  while True:
    buf = f.read(buflen)
    if len(buf) <= 0:
      break
    compressed = z.compress(buf)
    sys.stdout.write(compressed)

  compressed = z.flush()
  sys.stdout.write(compressed)
  sys.stdout.flush()
finally:
  f.close()

*1:ご承知のとおり,各 apk ごとに (厳密には shared とかあるので違うけど) ユニークな uid / gid が割り振られる。でも当然ながら違う端末・インストール状況では同じアプリケーションが違う uid / gid となる。なので,ひょっとすると uid / gid 情報を失っても問題ないのかな?知っている人は教えてください。