2008年04月01日
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();
}
}