目次
この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。
こんにちは。エンジニアの はる です。
今回はちょっとだけマニアックにswfのバイナリ解析方法について書いてみたいと思います。
バイナリって何となく苦手意識のある方も多いかと思いますが、swfのバイナリ解析は数パターンの計算方法だけ抑えてしまえば意外と素直に読み解けてしまいます。
文系大学出身&英語が読めない私でも大丈夫だったので、興味のある方は是非!
用意するもの
バイナリエディタ
まずはバイナリファイルを閲覧・編集できる環境を用意します。
バイナリエディタは様々なものがありますが、自分の環境や好みに合わせて選んでみてください。代表的なものを下記します。
1. バイナリモードでswfファイルを開く
vi -b hoge.swf
2. テキスト形式から16進数ダンプ形式に変換
:%!xxd
3. 16進数ダンプ形式からテキスト形式に変換
:%!xxd -r
解析用SWF
解析するためにはSWFファイルが必要です。適当にSWFをパブリッシュして用意しておきましょう。
※今回の記事では、非圧縮のSWFをもとに解説させていただきます。解析用SWFは、「ファイル」→「パブリッシュ設定」→「SWF設定:ムービーの圧縮」のチェックをはずしたものをパブリッシュしましょう。
基数変換の基礎知識
解析中に2進数・10進数・16進数の相互変換が沢山あります。このあたりの計算方法は事前に覚えておくか、基数変換を簡単に行えるWEBサイトやソフトが沢山ありますので、用意しておきましょう。
以上の用意ができたら、解析用SWFをお好きなバイナリエディタで開けば、準備完了です。
SWFフォーマット
SWFのバイナリは下記のような形式で構成されています。
ヘッダ(header、header_movie)に基本情報が格納されており、コンテンツ(tag block)にシェイプやスクリプト等のコンテンツ情報が順々に格納されています。
SWF | |||||||||
---|---|---|---|---|---|---|---|---|---|
header(8バイト) | header_movie(可変長) | tag block(可変長) | tag block(可変長) | .. | end tag | ||||
圧縮有無(3バイト) | SWFのバージョン(1バイト) | ファイルサイズ(4バイト) | ステージの幅・高さ(可変長) | フレームレート(2バイト) | _rootのフレーム数(2バイト) | TLC構造(可変長) | TLC構造(可変長) | … | 2バイト |
ヘッダ
まずはヘッダから読み解いていきましょう。ヘッダから読み取れる情報は以下の6種類です。
ヘッダから読み取れる情報
- 圧縮されているかどうか
- Flash Player のバージョン
- ファイルサイズ
- ステージの幅・高さ
- fps(frame per second)
- _rootのフレーム数
上記の情報が格納されているヘッダの構成は以下のようになっています。
ヘッダのバイナリフォーマット
ヘッダ | |||||
---|---|---|---|---|---|
header(8バイト) | header_movie(可変長) | ||||
圧縮有無(3バイト) | SWFのバージョン(1バイト) | ファイルサイズ(4バイト) | ステージの幅・高さ(可変長) | フレームレート(2バイト) | _rootのフレーム数(2バイト) |
それでは実際にバイナリを読み解いていきましょう。1バイト目から順に解説していきます。
バイナリ詳解
1. 1〜3バイト(長さ:3バイト)
SWFが圧縮されたものがどうかを判別します。
16進数 | 説明 | 備考 |
---|---|---|
46 57 53 | 圧縮されていないSWF | FWS |
43 57 53 | 圧縮されたSWF(※SWF6以降のみ) | CWS |
CWSの場合、8バイト以降がZLIB圧縮されていることを示しています。この場合、8バイト以降をZLIB展開する必要があります。
※以降の解説はFWSの前提ですすめさせていただきます。
2. 4バイト目(長さ:1バイト)
SWFのバージョンが格納されています。以下の例の場合、SWF7形式であることが分かります。
16進数 | 説明 | 備考 |
---|---|---|
07 | SWFのバージョン |
3. 5〜8バイト(長さ:4バイト)
swfファイルのファイルサイズが格納されています。ただし、数値はリトルエンディアン形式で格納されているため、注意が必要です。
16進数 | 説明 | 備考 |
---|---|---|
ec 52 01 00 | ファイルサイズ | リトルエンディアン形式 |
<計算方法>1. バイトスワップ
ec 52 01 00 | |
逆順 | 00 01 52 ec |
2. 10進数に変換
1.の結果(16進数) | 000152ec |
---|---|
10進数 | 86764 |
3. ファイルサイズ = 86764 byte
4. 9バイト〜(長さ:可変長)
ステージの幅・高さが格納されています。ただし、数値はRECT構造体で表現されているのに加え、単位はTWIPSのため、注意が必要です。
16進数 | 説明 | 備考 |
---|---|---|
70 00 09 60 00 00 96 00 00 … | ステージの幅・高さ | RECT構造体で表現。単位はTWIPS(トゥイップ) |
<計算方法>1. 2進数に変換
16進数 | 70 00 09 60 00 00 96 00 |
---|---|
2進数 | 01110000 00000000 00001001 01100000 00000000 00000000 10010110 00000000 00000000 |
2. 最初の5ビットを10進数に変換
2進数 | 01110 |
---|---|
10進数 | 14 |
3. 2.の結果毎に6ビット目以降を区切る(余りのビットは捨てられます)
最初の5ビット | 14ビット | 14ビット | 14ビット | 14ビット | 余りのビット |
---|---|---|---|---|---|
01110 | 000 00000000 000 | 01001 01100000 0 | 0000000 0000000 | 0 10010110 00000 | 000 |
4. 3.の結果を10進数に変換
X座標の最小値 | X座標の最大値 | Y座標の最小値 | Y座標の最大値 | 余りのビット | |
---|---|---|---|---|---|
16進数 | 00000000000000 | 01001011000000 | 00000000000000 | 01001011000000 | 000 |
10進数 | 0 | 4800 | 0 | 4800 | – |
5. 4.の結果がtwipのため、ptに変換(1pt=20twips)
X座標の最小値 | X座標の最大値 | Y座標の最小値 | Y座標の最大値 | |
---|---|---|---|---|
twips | 0 | 4800 | 0 | 4800 |
pt | 0 | 240 | 0 | 240 |
6. 縦幅 = 240px, 横幅 = 240px
5. 長さ:2バイト
フレームレートが格納されています。
16進数 | 説明 | 備考 |
---|---|---|
00 08 | フレームレート | 逆順にした結果の1ビット目が整数部、2ビット目が小数部 |
<計算方法>1. バイトスワップ
00 08 | |
逆順 | 08 00 |
2. 整数部、小数部を10進数に変換
1.の結果(16進数) | 08.00 |
---|---|
10進数 | 8.0 |
3. フレームレート = 8 fps
6. 長さ:2バイト
_root(メインタイムライン)のフレーム数が格納されています。
16進数 | 説明 | 備考 |
---|---|---|
14 00 | _rootのフレーム数 | リトルエンディアン形式 |
<計算方法>1. バイトスワップ
14 00 | |
逆順 | 00 14 |
2. 10進数に変換
1.の結果(16進数) | 00 14 |
---|---|
10進数 | 20 |
3. _rootのフレーム数 = 20
ヘッダの解析はこれで終了です。SWF解析の肝は次バイトからのコンテンツ(tag block)ですが、これはヘッダ解析で行った計算方法の応用です。
ここから先は、tagの種類毎にtag blockの構造が異なるため、Adobe社が配布している『SWF file format specification』を片手にtagの種類を特定し、それに合った計算方法で解析していくことになります。
今回は一例として、ヘッダに続くtab block(SetBackgroundColor)の解説を行います。
コンテンツ
コンテンツのバイナリフォーマット
- コンテンツは tag block毎に続き、tag blockは[ tag | length | contents ]の構造(以下、TLC構造)となる。
- tag block の構造は2種類存在し、”「length < 3f」の場合”と”「length >= 3f」または特殊形式の場合”で異なる。
1. タグ形式1(※「length < 3f」)の場合
コンテンツ | |||
---|---|---|---|
tag block(可変長) | … | ||
record header(2バイト) | contents(lengthバイト) | … | |
tag(10ビット) | length(6ビット) | contents(lengthバイト) | … |
2. タグ形式2(※「length >= 3f」または特殊形式)の場合
コンテンツ | ||||
---|---|---|---|---|
tag block(可変長) | … | |||
record header(6バイト) | contents(lengthバイト) | … | ||
tag(10ビット) | 3f(6ビット) | length(4バイト) | contents(lengthバイト) | … |
バイナリ詳解
16進数 | 43 02 ff ff ff ff 0a 05 00 00 00 69 6e 69 74 00 3f 03 3a 03 00 00 88 06 01 1e 00 00 … |
---|---|
2進数 | 01000011 00000010 11111111 11111111 11111111 11111111 … |
1〜2バイト(長さ:2バイト)
16進数 | 説明 | 備考 |
---|---|---|
43 02 | tag(10ビット)+length(6ビット) | リトルエンディアン形式 |
<解析方法>1. バイトスワップ
43 02 | |
逆順 | 02 43 |
2. 2進数に変換
16進数 | 02 43 |
---|---|
2進数 | 00000010 01000011 |
3. 10ビット、6ビットに分割
tag | length |
---|---|
0000001001 | 000011 |
4. 『SWF file format specification』のp.271〜p.273をもとにtagの種類とlengthを算出する
2進数 | 10進数 | 結果 | |
---|---|---|---|
タグ種別 | 0000001001 | 9 | SetBackgroundColor |
length | 000011 | 3 | 3 |
5. タグ形式2(※「length >= 3f」または特殊形式)の場合、続く4ビットを取得し、lengthを算出する6. 続くlengthバイトを取得し、『SWF file format specification』の各タグ処理の記載に沿って解析
※SetBackgroundColorの場合
RGB | ff ff ff |
---|
※setBackgroundColorは、必ずコンテンツの先頭に位置し、「長さ:5バイト」となる
setBackgroundColor(5バイト) | ||
---|---|---|
tag(1バイト) | length(1バイト) | RGB(3バイト) |
あとはtagの種類毎に決められたフォーマットで解析していき、順々にコンテンツ(tag block)を解析していきます。
このswfのバイナリ構造を抑えておくと、例えば、Flash単体では通常取得することが困難な情報をバイナリレベルでswf内に埋め込んでから出力するアプリケーション開発したり、swfmillで出力されるxmlフォーマットもバイナリ構造と近いので理解しやすくなったりします。 皆さんもちょっとアブノーマルなバイナリの世界にいかがでしょうか?