swfからabcの切り出し

swfからabcを切り出してディスアセンブルする方法は、OSS で SWF をデコンパイルする(2) - swfassist と abcdump で亜流デコンパイラ - てっく煮ブログ 跡地あたりに書いてある。ただ、AIRが必要だったり、入力ファイル名が固定だったするあたりが嫌だったので、他の方法を試してみた。

サンプルとして使ったのは、以下のようなアクションスクリプト

package{
    import flash.display.*;
    import flash.text.*;

    public class Hello extends Sprite {
	public function Hello(){
	    trace('Hello');
	    var text:TextField = new TextField();
	    text.text = 'Hello world!!';
	    text.autoSize = TextFieldAutoSize.LEFT;
	    this.addChild(text);
	}
    }
}

SWFTOOLSによるダンプ

まずは、swftoolsに含まれるswfdumpによるダンプ。

$ swfdump  Hello.swf
[HEADER]        File version: 9
[HEADER]        File is zlib compressed. Ratio: 68%
[HEADER]        File size: 971 (Depacked)
[HEADER]        Frame rate: 24.000000
[HEADER]        Frame count: 1
[HEADER]        Movie width: 500.00
[HEADER]        Movie height: 375.00
[045]         4 FILEATTRIBUTES
[04d]       457 METADATA
[041]         4 SCRIPTLIMITS
[009]         3 SETBACKGROUNDCOLOR (86/9c/a7)
[029]        26 SERIALNUMBER
[02b]         6 FRAMELABEL "Hello"
==== Error: Unknown tag:0x052 ====
[052]       412 (null)
==== Error: Unknown tag:0x04c ====
[04c]        10 (null)
[001]         0 SHOWFRAME 1 (00:00:00,000) (label "Hello")
[000]         0 END

全体の構造が分かるのは便利なんだけれども、一部のタグだけを取り出すのは難しい。--hexをつければ、中身をダンプできるけど、それでもやっぱり難しい。

$ swfdump  --hex Hello.swf
[HEADER]        File version: 9
[HEADER]        File is zlib compressed. Ratio: 68%
[HEADER]        File size: 971 (Depacked)
[HEADER]        Frame rate: 24.000000
[HEADER]        Frame count: 1
[HEADER]        Movie width: 500.00
[HEADER]        Movie height: 375.00
...
[02b]         6 FRAMELABEL "Hello"
                -=> 48 65 6c 6c 6f 00                                   Hello.
==== Error: Unknown tag:0x052 ====
[052]       412 (null)
                -=> 01 00 00 00 66 72 61 6d 65 31 00 10 00 2e 00 00     ....frame1......
                -=> 00 00 14 00 05 48 65 6c 6c 6f 0d 66 6c 61 73 68     .....Hello.flash
                -=> 2e 64 69 73 70 6c 61 79 06 53 70 72 69 74 65 05     .display.Sprite.
                -=> 74 72 61 63 65 0a 66 6c 61 73 68 2e 74 65 78 74     trace.flash.text
....

swfmillで切り出す

swfmillを使うとswfをXMLに変換できる。

$ swfmill swf2xml Hello.swf
<?xml version="1.0"?>
<swf version="9" compressed="1">
  <Header framerate="24" frames="1">
    <size>
      <Rectangle left="0" right="10000" top="0" bottom="7500"/>
    </size>
    <tags>
      ...
      <UnknownTag id="0x52">
        <data>AQAAAGZyYW1lMQAQA...</data>
      </UnknownTag>
      ...
    </tags>
  </Header>
</swf>

なので、swfmillでXMLにしたあと、目的のタグを抜くスクリプトRubyで書いた。(XPathはすばらしい)

require 'rexml/document'
require 'base64'

doc = REXML::Document.new ARGF
puts Base64.decode64(doc.elements['//UnknownTag[@id="0x52"]/data'].text)

あとはXMLをこのスクリプトに渡すようにしてやる。

$ swfmill swf2xml Hello.swf | ruby ~/c/code/croquis/extract_abc.rb > a.abc

ただ、このABCにはSWFとしてのヘッダが付加されている。なので、abcdumpに渡す前に、ファイルの先頭から10 00 2E 00よりも前にあるデータを削除しないといけない。