PicasaのDBを何とか解読したい (サムネイルファイル編)

はじめに

以下ではWindows用のツールを数多く利用しています。というのも個人的にLinux用のよいバイナリエディタなどを知らないからです。なのでWindows上のバイナリエディタでデータを眺めながらLinux上でHaskellのコードを書くというような面倒なことをしていました。だれかUbuntuで簡単に手に入り高機能なバイナリエディタ知ってたら教えてください。

欲しい物が多すぎて困る

前回id:xxxxxeeeee:20121214:1355454193でPMPファイルを解析することに成功したわけだが、我々が手に入れたデータは実のところ完全なるメタデータのみであった。平均色・幅・高さ・顔認識結果…しかしそんなものが欲しいのではない。我々が欲しいのは「あるファイルに対応するサムネイル」これにつきる。さて、これをどうやって探したものか?
まずファイルを色々眺めてみるとpmpファイル(と_0ファイル)を除くとPicasaには以下のファイルがあることが分かる。

  • bigthumbs_0.db
  • bigthumbs_index.db
  • facetemplatesV2_0.db
  • facetemplatesV2_index.db
  • previews_0.db
  • previews_index.db
  • thumbindex.db
  • thumbs2_0.db
  • thumbs2_index.db
  • thumbs_0.db
  • thumbs_index.db

ここで仲間はずれを見つけてくださいと問われたら、誰もがthumbindex.dbを挙げるだろう。他のファイルは_0と_indexがペアになっているからだ。しかし、当初私はこの「いかにも怪しいファイル」を覗いてみることなくまずファイルサイズに注目した。実際のファイルサイズはさておき、***_0.dbのファイルが大きい。100Mとか1Gとかある。なのでこいつがJPEGのサムネイルてんこ盛りファイルだという気がしてくる。そこで、http://homepage3.nifty.com/kamisaka/JpegAnalyzer/というソフトを使ってみよう。このソフトはJPEGの内部を(マーカーとかセグメントとか)そのまま見せてくれるのみならず、一部をコピーアンドペーストできたりとJPEGのいかがわしい操作には必携である。さて、見てみただろうか?そうすると、なんとこの_0.dbファイルたちは最初4バイト以外はSOIというJPEGの開始コードから始まりEOI、同じく終了コードで終わる、要はJPEGのバイナリを延々と列挙したものであることが分かる。やった、これをSOIからEOIに切り出せばいい!終  了
しかしよく考えて欲しい。その切り出したJPEGは素のExifさえないJPEGである。見れば何が入っているかは分かっても、見ないと何かはわからない。10枚程度なら一枚一枚調べてもいい、しかし「私の画像ファイルは4万8千です」という状況ではなすすべもない。そしてようやく先程の怪しいファイルを調べることを思いついた。

thumbindex.dbの構造

ではthumbindex.dbを調べることにする。まず、バイナリエディタとしてBinEditの詳細情報 : Vector ソフトを探す!Moontailの詳細情報 : Vector ソフトを探す!を推奨しておく。後者のほうが新しく機能もたくさんあるのだが、なぜか私の環境では検索ができないという致命的な問題があるので前者を検索用に利用している。さて、Moontailが優れている理由は色々あるが、まずはUTF-8をちゃんと表示できる点である。thumbindex.dbには結論からいうとUTF-8でパス名が埋め込まれている。これは「どうせGoogleだからUTF-8だろう」とか決めつけつつUTF-8表示をすれば一目瞭然に分かることだが、UTF-8表示できないエディタだと相当苦労することになるだろう。
閑話休題UTF-8のパス名はどうやらNULL終端の文字列のようだ。そしてNULL終端の文字列の間には30バイトのデータが入っていた。最後まで通してみてみると、どうやら全体の構造としては66 66 46 40と言う謎のヘッダ、次に4バイトのリトルエンディアン(今後特記なければというか全部リトルエンディアン)でデータ長、そのあとはNULL終端文字列と30バイトのデータが延々と繰り返されているらしい、ということにたどり着いた。
そこで、このパス名と30バイトのデータをtsv(tab separated value)に吐いて表計算ソフトに食わせてじっとにらめっこする。すると、色々あって

  • 最後の4バイトは連続して同じ値が出ているようだ。
  • 前の16バイトはものによっては全く同じ8バイトが繰り返されている。
    • 直観で考えるとこれはファイルの作成日時と更新日時が格納されていると考えるのが妥当である。

と言う事がわかる。なのでこれらをそれぞれ普通の整数値として特別扱いしてみることにする。前の16バイトは8バイトunsigned二つとして、後の4バイトはsigned一つとして、もう一度表計算ソフトで眺める。更に少しずるをして元のファイルの作成日時と更新日時を調べる。すると、もう少しちゃんとしたことが分かる。

  • 後ろの4バイトに非負の値が入っている場合、この値はそのファイルがどのディレクトリに属しているかをthumbindex.db内のインデックスで表しているらしい。
  • 日時の差分を取って調べたところ作成日時と更新日時はどうやらある基準日時からの100 nano sec.オーダーでの経過時間が入っているらしい。
    • そして、それからさらに基準日時を調べると、基準日時は1601年1月1日0時0分(UTC)らしい。
    • そしてこのフォーマットはWindowsのHigh Resolution Timerとかいうものらしい。

更に30-16-4=残りの10バイトも興味深く、初めの4バイトは不明、5バイト目はファイルタイプを表現しているらしいというところまで突き止めた。しかし、このファイル名+extraの羅列とJPEGバイナリの単純結合の間にどのような写像が存在しているかは未だ謎になっている。

いよいよthumbs_index.dbを調べよう


今回はとても本能的な調査方法に頼った。右の画像はMoontailでthumbs_index.dbを開いたところである。なにか感じないだろうか?そう、ファイルが三パートにわかれているのである。そしてまず最後のパートの先頭に飛んでみた。すると、JPEGのSOIの位置のような値が書いてある気がする。しかし、どうにもずれている。
も一個ついでにこのパートの長さを調べてみると"ほぼ"thumbindex.dbのエントリの個数*4バイトであった。ほぼ、となると「厳密に」それだけの長さを測って逆算してやる。するとどっこい、その直前にthumbindex.dbのエントリの個数がそのものズバリ含まれているではないか!後はそのエントリを延々ととるだけである。結論からいうと、頭にCD CC CC 3F 00 00 00 00というヘッダがついた後に4バイトのエントリ個数+4バイトの数値(エントリ個数と一致)と言う望ましいステップが始まる。このうち最後のブロックはSOIの位置のようなものが入っていたが、最初のものは4ずれていて、次以降はてんであたってない。しかし、なんと真ん中のブロックも同様にSOIの番地が書いてあるではないか!しかもコチラは寸分違いもせず、更に2番目、3番目の値も至当なものである。となると、最後のブロックには実はサムネイル1つずつのファイルサイズが書かれているということになる。

終局

というわけでいろんな解析をかけた結果「パス名」と「サムネイルのオフセットと長さ」が分かったわけでもういよいよ後はサムネイルの抽出……をする暇がないんだよなぁ。