ホーム DoRuby SWFバイナリ解析

SWFバイナリ解析

この記事はアピリッツの技術ブログ「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種類です。

ヘッダから読み取れる情報

  1. 圧縮されているかどうか
  2. Flash Player のバージョン
  3. ファイルサイズ
  4. ステージの幅・高さ
  5. fps(frame per second)
  6. _rootのフレーム数

上記の情報が格納されているヘッダの構成は以下のようになっています。

ヘッダのバイナリフォーマット

ヘッダ
header(8バイト)header_movie(可変長)
圧縮有無(3バイト)SWFのバージョン(1バイト)ファイルサイズ(4バイト)ステージの幅・高さ(可変長)フレームレート(2バイト)_rootのフレーム数(2バイト)

それでは実際にバイナリを読み解いていきましょう。1バイト目から順に解説していきます。

バイナリ詳解

1. 1〜3バイト(長さ:3バイト)

SWFが圧縮されたものがどうかを判別します。

16進数説明備考
46 57 53圧縮されていないSWFFWS
43 57 53圧縮されたSWF(※SWF6以降のみ)CWS

CWSの場合、8バイト以降がZLIB圧縮されていることを示しています。この場合、8バイト以降をZLIB展開する必要があります。
※以降の解説はFWSの前提ですすめさせていただきます。

2. 4バイト目(長さ:1バイト)

SWFのバージョンが格納されています。以下の例の場合、SWF7形式であることが分かります。

16進数説明備考
07SWFのバージョン

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ビット余りのビット
01110000 00000000 00001001 01100000 00000000 00000000 10010110 00000000

4. 3.の結果を10進数に変換

X座標の最小値X座標の最大値Y座標の最小値Y座標の最大値余りのビット
16進数00000000000000010010110000000000000000000001001011000000000
10進数0480004800

5. 4.の結果がtwipのため、ptに変換(1pt=20twips)

X座標の最小値X座標の最大値Y座標の最小値Y座標の最大値
twips0480004800
pt02400240

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 02tag(10ビット)+length(6ビット)リトルエンディアン形式

<解析方法>1. バイトスワップ

43 02
逆順02 43

2. 2進数に変換

16進数02 43
2進数00000010 01000011

3. 10ビット、6ビットに分割

taglength
0000001001000011

4. 『SWF file format specification』のp.271〜p.273をもとにtagの種類とlengthを算出する

2進数10進数結果
タグ種別00000010019SetBackgroundColor
length00001133

5. タグ形式2(※「length >= 3f」または特殊形式)の場合、続く4ビットを取得し、lengthを算出する6. 続くlengthバイトを取得し、『SWF file format specification』の各タグ処理の記載に沿って解析
※SetBackgroundColorの場合

RGBff ff ff

※setBackgroundColorは、必ずコンテンツの先頭に位置し、「長さ:5バイト」となる

setBackgroundColor(5バイト)
tag(1バイト)length(1バイト)RGB(3バイト)

あとはtagの種類毎に決められたフォーマットで解析していき、順々にコンテンツ(tag block)を解析していきます。
このswfのバイナリ構造を抑えておくと、例えば、Flash単体では通常取得することが困難な情報をバイナリレベルでswf内に埋め込んでから出力するアプリケーション開発したり、swfmillで出力されるxmlフォーマットもバイナリ構造と近いので理解しやすくなったりします。 皆さんもちょっとアブノーマルなバイナリの世界にいかがでしょうか?

記事を共有

最近人気な記事