OSS で SWF をデコンパイルする(2) - swfassist と abcdump で亜流デコンパイラ

Tamarin の abcdump.as を活用して、SWF をダンプしてみよう。

abcdump を使うためには、まず、SWF の中から ABC(ActionScript ByteCode)を取り出す必要がある。前回取り上げた flash-decompiler を使ってもいいんだけど、せっかくなので、前々から試してみたかった swfassistを使ってみた。

いきなり凝ったものを作るのは大変なので、まずは SWF から ABC を抽出するアプリを AIR で作ってみた。

サンプルデータ

試しにこの簡単な AS をコンパイルして SWF にする。

package{
    import flash.display.Sprite;

    public class Test extends Sprite{
        public function Test()
        {
            trace("test");
        }
    }
}

SWF に対して、今回作った ABC 切り出しツールを適用する。

切り出された ABC を abcdump でダンプする。

magic 2e0010
Cpool numbers size 3 1 %
Cpool strings count 13 size 130 46 %
Cpool namespaces count 5 size 9 3 %
Cpool nssets count 0 size 1 0 %
Cpool names count 9 size 25 8 %
MethodInfo count 3 size 13 4 %
InstanceInfo size 8 2 %
ClassInfo size 2 0%
ScriptInfo size 7 2 %
MethodBodies size 76 27 %
script0

class Test extends flash.display::Sprite
{

  function Test():*     /* disp_id -1*/
  {
    // local_count=1 max_scope=1 max_stack=2 code_len=13
    0       getlocal0           
    1       pushscope           
    2       getlocal0           
    3       constructsuper      (0)
    5       findpropstrict      trace
    7       pushstring          "test"
    9       callpropvoid        trace (1)
    12      returnvoid          
  }


  static function Test$cinit():*        /* disp_id 0*/
  {
    // local_count=1 max_scope=1 max_stack=1 code_len=3
    0       getlocal0           
    1       pushscope           
    2       returnvoid          
  }

}


function script0$init():*       /* disp_id 0*/
{
  // local_count=1 max_scope=7 max_stack=2 code_len=35
  0         getlocal0           
  1         pushscope           
  2         getscopeobject      0
  4         getlex              Object
  6         pushscope           
  7         getlex              flash.events::EventDispatcher
  9         pushscope           
  10        getlex              flash.display::DisplayObject
  12        pushscope           
  13        getlex              flash.display::InteractiveObject
  15        pushscope           
  16        getlex              flash.display::DisplayObjectContainer
  18        pushscope           
  19        getlex              flash.display::Sprite
  21        pushscope           
  22        getlex              flash.display::Sprite
  24        newclass            Test
  26        popscope            
  27        popscope            
  28        popscope            
  29        popscope            
  30        popscope            
  31        popscope            
  32        initproperty        Test
  34        returnvoid          
}

OPCODE  SIZE    % OF 51
getlex          14      27%
pushscope       9       17%
popscope        6       11%
getlocal0       4       7%
returnvoid      3       5%
callpropvoid    3       5%
pushstring      2       3%
constructsuper  2       3%
newclass        2       3%
findpropstrict  2       3%
getscopeobject  2       3%
initproperty    2       3%

それっぽいものが出てきたー!

abcdump.as のビルド方法は AS3 decompiler あたりを参照あれ。

SWF から ABC を切り出す AIR のソースコードは以下に。

Input.swf を読み出して、out*.abc としてファイル出力するだけの簡単なコードです。

Flex3 + AIR 1.0 で出来てるよ。

package{
    import flash.display.*;
    import flash.events.*;
    import flash.net.*;
    import org.libspark.swfassist.io.*;
    import org.libspark.swfassist.swf.io.*;
    import org.libspark.swfassist.swf.structures.SWF;

    public class Test extends Sprite{
        public function Test():void{
            loadSWF();
        }
    
        private function loadSWF():void{
            // Input.swf をロード
            var loader:URLLoader = new URLLoader();
            loader.dataFormat = URLLoaderDataFormat.BINARY;
            loader.addEventListener(Event.COMPLETE, completeHandler);
            loader.load(new URLRequest('Input.swf'));
        }

        private function completeHandler(e:Event):void{
            var loader:URLLoader = URLLoader(e.target);

            // swfassist でダンプ
            var input:DataInput = new ByteArrayInputStream(loader.data);
            var context:ReadingContext = new ReadingContext();
            context.needsABCData = true;
            var reader:SWFReader = new SWFReader();
            var swf:SWF = reader.readSWF(input, context);

            // AbcVisitor で visit
            var abcVisitor:AbcVisitor = new AbcVisitor();
            swf.visit(abcVisitor);

            // 情報を出力
            var t:TextField = new TextField();
            t.text = "frame rate: " + swf.header.frameRate + "\nVersion: " + swf.header.version
                + "\nabc count: " + abcVisitor.count;
            addChild(t);
        }
    }
}

import org.libspark.swfassist.swf.tags.AbstractTagVisitor;
import org.libspark.swfassist.swf.tags.DoABC;
import flash.filesystem.*;

class AbcVisitor extends AbstractTagVisitor{
    public var count:int = 0;
    
    // ABC に反応
    override public function visitDoABC(tag:DoABC):void{
        count++;

        if(!tag.abcData){
            return;
        }

        // フレーム名を取り除く
        // framename 0x00 ABC
        var pos:int = 0;
        while(tag.abcData.readByte() != 0){
        }

        // ファイルに出力
        // C:\Documents and Settings\username\Application Data\AppName\Local Store
        var fs:FileStream = new FileStream();
        var file:File = File.applicationStorageDirectory.resolvePath("out" + count + ".abc");
        trace(file.nativePath);
        fs.open(file, "write");
        fs.writeBytes(tag.abcData, tag.abcData.position);
        fs.close();
    }
}