🍉

bplistのバイナリを読む

2023/04/25に公開

新規メモ

bplistはapple開発した、xmlを独自フォーマットのバイナリにしたもの。
なので当然テキストエディタで編集することはできません。
xmlよりもファイルサイズを小さくすることができます。
ここではbplistの構造と、バイナリの読み方について説明しています。

altテキスト

xmlに変換するとこう
altテキスト

バイナリエディタで見てみる
altテキスト

全体

まずは全体の構成を理解する必要があります。
大きく4つのセクションに分けて考えることができます。

緑: Header

これはbplistであるという宣言。マジックナンバーと言うやつ。
先頭から8バイト固定で、値も常に同じです。
テキストにするとbplist00となります。00はフォーマットのバージョンなのかな?

青: Object

データ本体。
xmlでいう、キーと値、型が全てがここに保存されている。
しかし、xmlで見た時とは異なる順番でデータは並んでいる。
キー、値、型は混在しており、何バイト目にどの値が保存されてるのか、それはOffset Tableを参照する必要がある。詳しい説明はもう少し後で。

バイト数はデータの量に合わせて変化します。ヘッダーのように固定ではありません。

赤: Offset Table

上で少し話したように、何バイト目にどの値が記録されているのか、場所を記録している。

黄色: Trailer

全体を読むために必要な情報が記録されている。
プログラムはまずここを読んでデータの読み込みを始める。
また、Trailerはファイル末尾から32バイト固定です。
具体的にどのようなデータがここに記録されているのかは次のセクションで。

詳細に

Trailer
altテキスト
前のセクションで、ファイル末尾から32バイト固定だと話しました。具体的に何が記録されているのか。

1-5バイト: 未使用
6バイト目: ソートバージョン
7バイト目: Offset Tableの個々のサイズ (0埋めあり)(ファイルサイズが大きくなると必要)
8バイト目: オブジェクト参照サイズ (知らない)
9-16バイト: Objectの個数
17-24バイト: 最上位オブジェクトであるオフセットテーブルの要素番号 (分からない)
25-32バイト: ファイル先頭から数えてOffset Tableの開始位置のバイト数

読む

まず、25-32バイトのOffset Tableの開始位置のバイト数を見ます。
例では7Fです。(08があるところ)
ここからOffset Tableが始まっていると読み取れます。
altテキスト

次にOffset Tableを読む必要があります。
Offset Tableは何バイト目にどの値が記録されているのかを記録している部分。(Objectの)
1バイトずつそれぞれがplist内の要素です。
しかし、それは値そのものではなく、ポインタ。(実際の値のある位置を指しているもの)
まだ理解できてなくていいです。読めば分かります
Offset Tableは1バイトずつ分けて考えてください。1つずつ見ていきます。
まずは08。これはファイル先頭から数えた値の位置を指しています。
08バイト目を見てください。(厳密には最初のバイトを0と数えるので、08バイト目は09バイト目)
altテキスト
D3であることがわかります。
「D3」は「3つの要素が含まれたDictionary」です。わかりやすい。
D3の右の3バイトを見ます。01, 02, 03 
これらは要素のキー文字列です。しかし、これ自体はポインタであり、キーの文字列そのものではありません。
さらにその右3バイトには値があります。04, 05, 0F

こういう感じかな

D3 01 02 03 04 05 0F

D3
{
  01: 04
  02: 05
  03: 0F
}

それっぽくなりましたが、これらは全てポインタなので実際の値を探す必要があります。

1バイト目を見てください。
「01」
これは実はOffset Tableのインデックス番号です。Offset Tableの2バイト目を指しています。
「0F」
であることがわかります。
先ほど、Offset Tableを読んだ時のように、ファイル先頭から数えた値の位置のことです。
0Fを見ると「57」であることがわかります。
「57」は「7文字のString」です。5はASCII Stringのことです。決まっています。
ここでついでに話しておくと
「17」は「7桁のInt」、
「27」は「7桁のReal」、
「D7」は「7つの要素が含まれたDictionary」(さっき出てきた)
「A7」は「7つの要素が含まれたArray」

「08」は「Boolのfalse」
「09」は「true」
「33」は「日付 (Date)」
「00」は「null」、
を表します。最後の3つはnはありません。決まっています。常に同じ。
altテキスト

話を戻します。
「57」が「7文字のString」であることがわかりました。
では、57の右7バイトを見ます。

56, 65, 72, 73, 69, 6F, 6E
V, e, r, s, i, o, n
これが実際のキーの文字列となります。

02、03も同様の手順で探すことができます。(すべてキー文字列)

では次は値を探してみます。
D3の右の3バイト、01, 02, 03 はすべてキー文字列でした。
値はその次の3バイトにあります。
04, 05, 0F
これらが値のポインタとなります。値の探し方はキーを探した時と同じ。

まとめるとこうなる。

D3
{
01: 04
02: 05
03: 0F
}
01 = 56, 65, 72, 73, 69, 6F, 6E (Version)

D3
{
  "Version": 04
  02: 05
  03: 0F
}

-

「9-16バイトのObjectの個数」と
「7バイト目のOffset Tableの個々のサイズ (0埋めあり)」は
はtrailerの長さを計算するためのもの。
Objectの個数に個々のサイズを掛ける。

分からないところあるので、知ってたら教えてね
https://medium.com/@karaiskc/understanding-apples-binary-property-list-format-281e6da00dbd

Discussion