SWFバイナリ編集のススメ第0回 (解析編)
こんにちは、クライアント基盤チームのよやです。
昨今、Flash Player の自作が流行りのようですが、デバッグやテストに苦労されているとも聞き及んでいます。
丁度タイミング良く、SWFバイナリ編集のススメでサンプルとして引用している IO_SWF パッケージが SWF 解析ツールとして充実してきました。折角ですので、この場で紹介させて下さい。
野良 Flash Player の開発者であれば自前で SWF 解析ツールを用意出来ると思いますが、何かあった時に verify check として利用頂けると幸いです。
IO_SWF のインストール
php と pear コマンドが入っていれば3つのコマンドでインストール出来ます。
1 2 3 |
% sudo pear channel-discover openpear.org % sudo pear install openpear/IO_Bit % sudo pear install openpear/IO_SWF |
これで、 /usr/share/php/sample/ 辺りに php の解析ツールがインストールされます。
これと別に IO_SWF を Subversion で取ってきて実行すると、必要に応じて PHP のコードを弄る事で、より深い SWF 解析が出来ます。お勧めです。
1 2 3 |
~/svn% svn checkout http://svn.openpear.org/IO_SWF/trunk IO_SWF ~/svn% cd IO_SWF ~/svn/IO_SWF% php sample/swfdump -f hoge.swf |
サンプル SWF
ケルベロス開発チームから頂いた SWF ファイル(CerberusQuest.swf)をサンプルにして説明します。
SWF 全体
swfdump.php
- SWF の生の情報が分かります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
~/svn/IO_SWF% php sample/swfdump.php -f quest.swf Signature: FWS Version: 4 FileLength: 97620 FrameSize: Xmin: 0 Xmax: 240 Ymin: 0 Ymax: 240 FrameRate: 12 FrameCount: 101 Tags: Code: 9(SetBackgroundColor) Length: 3 Color: #000000 Code: 24(Protect) Length: 0 Code: 12(DoAction) Length: 2075 Actions: [0] Push(Code:0x96) (Length:9) (String)NextURL [1] Push(Code:0x96) (Length:27) (String)http://labs.gree.jp/blog/ <略> |
- -h を付けると16進数ダンプの表示がつきます。
1 2 3 4 5 6 7 8 9 10 11 12 |
~/svn/IO_SWF% php sample/swfdump.php -h -f CerberusQuest.swf Signature: FWS Version: 4 FileLength: 96523 FrameSize: Xmin: 0 Xmax: 240 Ymin: 0 Ymax: 240 FrameRate: 12 FrameCount: 101 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef 0x00000000 46 57 53 04 0b 79 01 00 70 00 09 60 00 00 96 00 FWS y p ` 0x00000010 00 0c 65 00 e Tags: <略> |
- DefineMorphShape も解析できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<略> Code: 46(DefineMorphShape) Length: 345 ShapeId: 102 StartBounds: Xmin: -44.95 Xmax: 12.55 Ymin: -20.95 Ymax: 21 EndBounds: Xmin: -44.95 Xmax: 12.55 Ymin: -20.95 Ymax: 21 Offset:183 FillStyles: Bigmap(64): BitmapId: 101 StartBitmapMatrix: | 20.000 0.000 | -36.50 | 0.000 20.000 | -38.75 EndBitmapMatrix: | 20.000 0.000 | -36.50 | 0.000 20.000 | -38.75 LineStyles: Width: 0 => 0 Color: #000000(00) => #000000(00) StartEdge: ChangeStyle: MoveTo: (-1.95, -18.2) FillStyle: 1|0 LineStyle: 1 StraightEdge: MoveTo: (-9.35, -19.45) <略> |
swfheader.php
- SWF のヘッダ情報を表示します。
1 2 3 4 5 6 7 8 9 10 11 |
~/svn/IO_SWF% php sample/swfheader.php CerberusQuest.swf SWF Headers: Signature:FWS Version:4 FileLength:96523 FrameSize.Xmin:0 FrameSize.Xmax:4800 FrameSize.Ymin:0 FrameSize.Ymax:4800 FrameRate:3072 FrameCount:101 |
- FrameRate を書き換える事も出来ます。
FrameRate の 3072 は 0x100(=256) 倍の数が入っていて、12 frames/sec 相当の値です。これを 256 で上書きすると 1 frames/sec になります。
1 |
~/svn/IO_SWF% php sample/swfheader.php CerberusQuest.swf FrameRate=256 > CerberusQuest-1fps.swf |
- FrameSize も書き換えられます。
FrameSize.Xmax:4800 とありますが、内部的に TWIPS という単位で座標値が格納されていて、通常の pixel の 20倍です。ですので、240 相当です。
FrameSize.Xmin を負の値で上書きする事により、Flash が通常表示する枠より左側を表示させてみます。
1 |
~/svn/IO_SWF% php sample/swfheader.php CerberusQuest.swf FrameSize.Xmin=-4800 > CerberusQuest-4800.swf |
Flash によっては表示枠外に何か隠されている事がありますが、このファイルには隠し事はないようです。
swfgreptags.php
- 特定のタグを持つ SWF を探す
例えば、カレントディレクトリで DefineMorphShape タグを持つ SWF ファイルを以下のように探せます。
1 2 3 4 |
~/svn/IO_SWF% php sample/swfgreptags.php DefineMorphShape *.swf CerberusQuest.swf: DefineMorphShape(code:46, length:345) CerberusQuest.swf: DefineMorphShape(code:46, length:119) CerberusQuest.swf: DefineMorphShape(code:46, length:147) |
コンテンツ系
swfextract.php
- SWF に含まれるコンテンツを吸い出します。画像、音声、MovieClip に対応しています。
1 2 3 4 5 |
~/svn/IO_SWF% php sample/swfextract.php -f CerberusQuest.swf -p "t-" t-129.png t-133.png t-135.png <略> |
- 指定した prefix (上の例では "t-" )に CharacterID を付けたファイル名です。
- -m を付けると、MovieClip を SWF 形式で出力します。
1 2 3 4 5 |
~/svn/IO_SWF% php sample/swfextract.php -f CerberusQuest.swf -p "t-" -m t-1.swf t-30.swf t-33.swf <略> |
- 骸骨のキャラです。> t-41.swf (SpriteId:41 の MovieClip)
殆どの部分が上の方(Y < 0)に隠れてしまっています。
0, 0 をベースにデザインされているのが予想出来るので、先程の swfheader.php で FrameSize を弄ります。
1 |
~/svn/IO_SWF% php sample/swfheader.php t-41.swf FrameSize.Xmin=-2400 FrameSize.Ymin=-2400 FrameSize.Xmax=2400 FrameSize.Ymax=2400 > t-41-2400.swf |
表示フレーム枠を左上に半分シフトして (-120, -120)-(120, 120) にする事で、画像の全体が表示されました。
ムービークリップ系
swflistmovieclip.php
- MovieClip の一覧を表示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
~/svn/IO_SWF% php sample/swflistmovieclip.php CerberusQuest.swf SpriteId:1 FrameCount:1 TagCount:2 name:dum0 dum0 SpriteId:30 FrameCount:5 TagCount:14 SpriteId:33 FrameCount:4 TagCount:10 SpriteId:36 FrameCount:18 TagCount:28 SpriteId:131 FrameCount:3 TagCount:7 SpriteId:148 FrameCount:5 TagCount:87 name:b */b (149) */f/b (40,149) */f/b (40,149) pl0/f/b (40,149) pl0/f/b (40,149) */pl0/f/b (50,40,149) */pl0/f/b (50,40,149) p0/pl0/f/b (50,40,149) p0/pl0/f/b (50,40,149) SpriteId:149 FrameCount:33 TagCount:68 name:f */f (40) */f (40) pl0/f (40) |
MovieClip の内部ID(SpriteId), フレーム数、タグ数、名前と、可能性のあるターゲットパス名が表示されます。
アクション系
swflistaction.php
- SWF に含まれる Actionバイトコードの一覧を表示します。
1 2 3 4 5 |
~/svn/IO_SWF% php sample/swflistaction.php CerberusQuest.swf spriteId:0(root) frame:1 => instruction:323 length=1880 spriteId:30 frame:1 => instruction:1 length=2 spriteId:33 frame:1 => instruction:1 length=2 <略> |
- MovieClipの ID(spriteId), フレーム番号、バイトコードの命令数、バイト長の順で1行に表示します。
swfdeleteaction.php
- SWF 中の ActionScript を消します。制御を無くして全フレーム表示させたい場合に便利です。
全消し (-a オプション)
1 2 3 |
~/svn/IO_SWF% php sample/swfdeleteaction.php -a -f CerberusQuest.swf > CerberusQuest-noaction.swf ~/svn/IO_SWF% php sample/swflistaction.php CerberusQuestnoaction.swf ~/svn/IO_SWF% |
- 制御が全部消えるので、各々の MovieClip が無限ループで再生されました。
- 条件を指定すると特定のアクションを削除します。-s でスプライト(MovieClip)指定、-l でバイトコード長指定が出来ます。
1 2 3 4 5 6 7 8 9 10 |
~/svn/IO_SWF% php sample/swflistaction.php CerberusQuest.swf spriteId:0(root) frame:1 => instruction:323 length=1880 spriteId:30 frame:1 => instruction:1 length=2 spriteId:33 frame:1 => instruction:1 length=2 <略> ~/svn/IO_SWF% php sample/swfdeleteaction.php -s 0:1 -f CerberusQuest.swf > CerberusQuest-noaction0-1.swf ~/svn/IO_SWF% php sample/swflistaction.php CerberusQuest-noaction0-1.swf spriteId:30 frame:1 => instruction:1 length=2 spriteId:33 frame:1 => instruction:1 length=2 <略> |
swfdump.php
- swfdump.php で Actionコードを逆アセンブルした結果が表示されます。又、-l を付けると Branch 命令に関連する場所にラベルがつきます。
1 2 3 4 5 6 7 8 9 10 |
~/svn/IO_SWF% php sample/swfdump.php -l -f CerberusQuest.swf <略> [95] StringExtract(Code:0x15) [96] SetVariable(Code:0x1D) [97] Jump(Code:0x99) (Length:2) BranchOffset=-106 (LABEL: 75) (LABEL: 98): [98] Push(Code:0x96) (Length:5) (String)btn [99] Push(Code:0x96) (Length:3) (String)0 [100] SetVariable(Code:0x1D) (LABEL: 101): |
swfdecompile.php
- ActionScript バイトコードを逆コンパイルします。但し、独自言語です。
1 2 3 4 5 6 7 8 9 10 |
~/svn/IO_SWF% php sample/swfdecompile.php -f CerberusQuest.swf Signature: FWS Version: 4 FileLength: 96523 FrameSize: Xmin: 0 Xmax: 240 Ymin: 0 Ymax: 240 FrameRate: 12 FrameCount: 101 Tags: Actions: (in frame 1) 1 NextURL = "http://labs.gree.jp/blog/" |
Push Push SetVariable が = に変換されて表示されたり、他にもバイトコードのニーモニックそのものより見易い形式で表示されます。
SWF 解析が捗りますね!
最後に
この他にも便利なツールがありますので、是非お試し下さい。引数無しで実行するとヘルプが表示されるので、使い方はそれで何となく分かると思います。
特に、
- swfdeformeshape.php (Shapeの線分を減らす)
- swfcountshapeedges.php (Shapeの線分を数える)
- swfwireframe.php (塗りを境界のみ表示する)
の3つは、Shape の構造を探る上で役に立つかもしれません。
尚、これらの解析ツールの半分は beketa 氏 の実装です。この場を借りて感謝します。
以上、Flash Player 作りでデバッグのお役に立てると幸いです。
P.S. ツールが欲しいと思った時に即興で作ったものばかりなので、ファイル指定で -f を付けたり付けなかったり、ファイル名も統一性がありませんが、どうかご容赦を。