/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version 1.1 (the * "License"); you may not use this file except in compliance with the License. You may obtain * a copy of the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT * WARRANTY OF ANY KIND, either express or implied. See the License for the specific * language governing rights and limitations under the License. * * The Original Code is [Open Source Virtual Machine.] * * The Initial Developer of the Original Code is Adobe System Incorporated. Portions created * by the Initial Developer are Copyright (C)[ 2004-2006 ] Adobe Systems Incorporated. All Rights * Reserved. * * Contributor(s): Adobe AS3 Team * * Alternatively, the contents of this file may be used under the terms of either the GNU * General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public * License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the * LGPL are applicable instead of those above. If you wish to allow use of your version of this * file only under the terms of either the GPL or the LGPL, and not to allow others to use your * version of this file under the terms of the MPL, indicate your decision by deleting provisions * above and replace them with the notice and other provisions required by the GPL or the * LGPL. If you do not delete the provisions above, a recipient may use your version of this file * under the terms of any one of the MPL, the GPL or the LGPL. * ***** END LICENSE BLOCK ***** */ package abcdump { import flash.utils.ByteArray import avmplus.System const TAB = " " // method flags const NEED_ARGUMENTS:int = 0x01 const NEED_ACTIVATION:int = 0x02 const NEED_REST:int = 0x04 const HAS_OPTIONAL:int = 0x08 const IGNORE_REST:int = 0x10 const NATIVE:int = 0x20 const HAS_ParamNames:int = 0x80 const CONSTANT_Utf8 :int = 0x01 const CONSTANT_Int :int = 0x03 const CONSTANT_UInt :int = 0x04 const CONSTANT_PrivateNs :int = 0x05 // non-shared namespace const CONSTANT_Double :int = 0x06 const CONSTANT_Qname :int = 0x07 // o.ns::name, ct ns, ct name const CONSTANT_Namespace :int = 0x08 const CONSTANT_Multiname :int = 0x09 // o.name, ct nsset, ct name const CONSTANT_False :int = 0x0A const CONSTANT_True :int = 0x0B const CONSTANT_Null :int = 0x0C const CONSTANT_QnameA :int = 0x0D // o.@ns::name, ct ns, ct attr-name const CONSTANT_MultinameA :int = 0x0E // o.@name, ct attr-name const CONSTANT_RTQname :int = 0x0F // o.ns::name, rt ns, ct name const CONSTANT_RTQnameA :int = 0x10 // o.@ns::name, rt ns, ct attr-name const CONSTANT_RTQnameL :int = 0x11 // o.ns::[name], rt ns, rt name const CONSTANT_RTQnameLA :int = 0x12 // o.@ns::[name], rt ns, rt attr-name const CONSTANT_NameL :int = 0x13 // o.[], ns=public implied, rt name const CONSTANT_NameLA :int = 0x14 // o.@[], ns=public implied, rt attr-name const CONSTANT_NamespaceSet :int = 0x15 const CONSTANT_PackageNs :int = 0x16 const CONSTANT_PackageInternalNs :int = 0x17 const CONSTANT_ProtectedNs :int = 0x18 const CONSTANT_StaticProtectedNs :int = 0x19 const CONSTANT_StaticProtectedNs2 :int = 0x1a const CONSTANT_MultinameL :int = 0x1B const CONSTANT_MultinameLA :int = 0x1C const constantKinds:Array = [ "0", "utf8", "2", "int", "uint", "private", "double", "qname", "namespace", "multiname", "false", "true", "null", "@qname", "@multiname", "rtqname", "@rtqname", "[qname]", "@[qname]", "[name]", "@[name]", "nsset" ] const TRAIT_Slot :int = 0x00 const TRAIT_Method :int = 0x01 const TRAIT_Getter :int = 0x02 const TRAIT_Setter :int = 0x03 const TRAIT_Class :int = 0x04 const TRAIT_Function :int = 0x05 const TRAIT_Const :int = 0x06 const traitKinds:Array = [ "var", "function", "function get", "function set", "class", "function", "const" ] const OP_bkpt:int = 0x01 const OP_nop:int = 0x02 const OP_throw:int = 0x03 const OP_getsuper:int = 0x04 const OP_setsuper:int = 0x05 const OP_dxns:int = 0x06 const OP_dxnslate:int = 0x07 const OP_kill:int = 0x08 const OP_label:int = 0x09 const OP_ifnlt:int = 0x0C const OP_ifnle:int = 0x0D const OP_ifngt:int = 0x0E const OP_ifnge:int = 0x0F const OP_jump:int = 0x10 const OP_iftrue:int = 0x11 const OP_iffalse:int = 0x12 const OP_ifeq:int = 0x13 const OP_ifne:int = 0x14 const OP_iflt:int = 0x15 const OP_ifle:int = 0x16 const OP_ifgt:int = 0x17 const OP_ifge:int = 0x18 const OP_ifstricteq:int = 0x19 const OP_ifstrictne:int = 0x1A const OP_lookupswitch:int = 0x1B const OP_pushwith:int = 0x1C const OP_popscope:int = 0x1D const OP_nextname:int = 0x1E const OP_hasnext:int = 0x1F const OP_pushnull:int = 0x20 const OP_pushundefined:int = 0x21 const OP_pushconstant:int = 0x22 const OP_nextvalue:int = 0x23 const OP_pushbyte:int = 0x24 const OP_pushshort:int = 0x25 const OP_pushtrue:int = 0x26 const OP_pushfalse:int = 0x27 const OP_pushnan:int = 0x28 const OP_pop:int = 0x29 const OP_dup:int = 0x2A const OP_swap:int = 0x2B const OP_pushstring:int = 0x2C const OP_pushint:int = 0x2D const OP_pushuint:int = 0x2E const OP_pushdouble:int = 0x2F const OP_pushscope:int = 0x30 const OP_pushnamespace:int = 0x31 const OP_hasnext2:int = 0x32 const OP_newfunction:int = 0x40 const OP_call:int = 0x41 const OP_construct:int = 0x42 const OP_callmethod:int = 0x43 const OP_callstatic:int = 0x44 const OP_callsuper:int = 0x45 const OP_callproperty:int = 0x46 const OP_returnvoid:int = 0x47 const OP_returnvalue:int = 0x48 const OP_constructsuper:int = 0x49 const OP_constructprop:int = 0x4A const OP_callsuperid:int = 0x4B const OP_callproplex:int = 0x4C const OP_callinterface:int = 0x4D const OP_callsupervoid:int = 0x4E const OP_callpropvoid:int = 0x4F const OP_newobject:int = 0x55 const OP_newarray:int = 0x56 const OP_newactivation:int = 0x57 const OP_newclass:int = 0x58 const OP_getdescendants:int = 0x59 const OP_newcatch:int = 0x5A const OP_findpropstrict:int = 0x5D const OP_findproperty:int = 0x5E const OP_finddef:int = 0x5F const OP_getlex:int = 0x60 const OP_setproperty:int = 0x61 const OP_getlocal:int = 0x62 const OP_setlocal:int = 0x63 const OP_getglobalscope:int = 0x64 const OP_getscopeobject:int = 0x65 const OP_getproperty:int = 0x66 const OP_getpropertylate:int = 0x67 const OP_initproperty:int = 0x68 const OP_setpropertylate:int = 0x69 const OP_deleteproperty:int = 0x6A const OP_deletepropertylate:int = 0x6B const OP_getslot:int = 0x6C const OP_setslot:int = 0x6D const OP_getglobalslot:int = 0x6E const OP_setglobalslot:int = 0x6F const OP_convert_s:int = 0x70 const OP_esc_xelem:int = 0x71 const OP_esc_xattr:int = 0x72 const OP_convert_i:int = 0x73 const OP_convert_u:int = 0x74 const OP_convert_d:int = 0x75 const OP_convert_b:int = 0x76 const OP_convert_o:int = 0x77 const OP_coerce:int = 0x80 const OP_coerce_b:int = 0x81 const OP_coerce_a:int = 0x82 const OP_coerce_i:int = 0x83 const OP_coerce_d:int = 0x84 const OP_coerce_s:int = 0x85 const OP_astype:int = 0x86 const OP_astypelate:int = 0x87 const OP_coerce_u:int = 0x88 const OP_coerce_o:int = 0x89 const OP_negate:int = 0x90 const OP_increment:int = 0x91 const OP_inclocal:int = 0x92 const OP_decrement:int = 0x93 const OP_declocal:int = 0x94 const OP_typeof:int = 0x95 const OP_not:int = 0x96 const OP_bitnot:int = 0x97 const OP_concat:int = 0x9A const OP_add_d:int = 0x9B const OP_add:int = 0xA0 const OP_subtract:int = 0xA1 const OP_multiply:int = 0xA2 const OP_divide:int = 0xA3 const OP_modulo:int = 0xA4 const OP_lshift:int = 0xA5 const OP_rshift:int = 0xA6 const OP_urshift:int = 0xA7 const OP_bitand:int = 0xA8 const OP_bitor:int = 0xA9 const OP_bitxor:int = 0xAA const OP_equals:int = 0xAB const OP_strictequals:int = 0xAC const OP_lessthan:int = 0xAD const OP_lessequals:int = 0xAE const OP_greaterthan:int = 0xAF const OP_greaterequals:int = 0xB0 const OP_instanceof:int = 0xB1 const OP_istype:int = 0xB2 const OP_istypelate:int = 0xB3 const OP_in:int = 0xB4 const OP_increment_i:int = 0xC0 const OP_decrement_i:int = 0xC1 const OP_inclocal_i:int = 0xC2 const OP_declocal_i:int = 0xC3 const OP_negate_i:int = 0xC4 const OP_add_i:int = 0xC5 const OP_subtract_i:int = 0xC6 const OP_multiply_i:int = 0xC7 const OP_getlocal0:int = 0xD0 const OP_getlocal1:int = 0xD1 const OP_getlocal2:int = 0xD2 const OP_getlocal3:int = 0xD3 const OP_setlocal0:int = 0xD4 const OP_setlocal1:int = 0xD5 const OP_setlocal2:int = 0xD6 const OP_setlocal3:int = 0xD7 const OP_debug:int = 0xEF const OP_debugline:int = 0xF0 const OP_debugfile:int = 0xF1 const OP_bkptline:int = 0xF2 const opNames = [ "OP_0x00 ", "bkpt ", "nop ", "throw ", "getsuper ", "setsuper ", "dxns ", "dxnslate ", "kill ", "label ", "OP_0x0A ", "OP_0x0B ", "ifnlt ", "ifnle ", "ifngt ", "ifnge ", "jump ", "iftrue ", "iffalse ", "ifeq ", "ifne ", "iflt ", "ifle ", "ifgt ", "ifge ", "ifstricteq ", "ifstrictne ", "lookupswitch ", "pushwith ", "popscope ", "nextname ", "hasnext ", "pushnull ", "pushundefined ", "pushconstant ", "nextvalue ", "pushbyte ", "pushshort ", "pushtrue ", "pushfalse ", "pushnan ", "pop ", "dup ", "swap ", "pushstring ", "pushint ", "pushuint ", "pushdouble ", "pushscope ", "pushnamespace ", "hasnext2 ", "OP_0x33 ", "OP_0x34 ", "OP_0x35 ", "OP_0x36 ", "OP_0x37 ", "OP_0x38 ", "OP_0x39 ", "OP_0x3A ", "OP_0x3B ", "OP_0x3C ", "OP_0x3D ", "OP_0x3E ", "OP_0x3F ", "newfunction ", "call ", "construct ", "callmethod ", "callstatic ", "callsuper ", "callproperty ", "returnvoid ", "returnvalue ", "constructsuper", "constructprop ", "callsuperid ", "callproplex ", "callinterface ", "callsupervoid ", "callpropvoid ", "OP_0x50 ", "OP_0x51 ", "OP_0x52 ", "OP_0x53 ", "OP_0x54 ", "newobject ", "newarray ", "newactivation ", "newclass ", "getdescendants", "newcatch ", "OP_0x5B ", "OP_0x5C ", "findpropstrict", "findproperty ", "finddef ", "getlex ", "setproperty ", "getlocal ", "setlocal ", "getglobalscope", "getscopeobject", "getproperty ", "OP_0x67 ", "initproperty ", "OP_0x69 ", "deleteproperty", "OP_0x6A ", "getslot ", "setslot ", "getglobalslot ", "setglobalslot ", "convert_s ", "esc_xelem ", "esc_xattr ", "convert_i ", "convert_u ", "convert_d ", "convert_b ", "convert_o ", "checkfilter ", "OP_0x79 ", "OP_0x7A ", "OP_0x7B ", "OP_0x7C ", "OP_0x7D ", "OP_0x7E ", "OP_0x7F ", "coerce ", "coerce_b ", "coerce_a ", "coerce_i ", "coerce_d ", "coerce_s ", "astype ", "astypelate ", "coerce_u ", "coerce_o ", "OP_0x8A ", "OP_0x8B ", "OP_0x8C ", "OP_0x8D ", "OP_0x8E ", "OP_0x8F ", "negate ", "increment ", "inclocal ", "decrement ", "declocal ", "typeof ", "not ", "bitnot ", "OP_0x98 ", "OP_0x99 ", "concat ", "add_d ", "OP_0x9C ", "OP_0x9D ", "OP_0x9E ", "OP_0x9F ", "add ", "subtract ", "multiply ", "divide ", "modulo ", "lshift ", "rshift ", "urshift ", "bitand ", "bitor ", "bitxor ", "equals ", "strictequals ", "lessthan ", "lessequals ", "greaterthan ", "greaterequals ", "instanceof ", "istype ", "istypelate ", "in ", "OP_0xB5 ", "OP_0xB6 ", "OP_0xB7 ", "OP_0xB8 ", "OP_0xB9 ", "OP_0xBA ", "OP_0xBB ", "OP_0xBC ", "OP_0xBD ", "OP_0xBE ", "OP_0xBF ", "increment_i ", "decrement_i ", "inclocal_i ", "declocal_i ", "negate_i ", "add_i ", "subtract_i ", "multiply_i ", "OP_0xC8 ", "OP_0xC9 ", "OP_0xCA ", "OP_0xCB ", "OP_0xCC ", "OP_0xCD ", "OP_0xCE ", "OP_0xCF ", "getlocal0 ", "getlocal1 ", "getlocal2 ", "getlocal3 ", "setlocal0 ", "setlocal1 ", "setlocal2 ", "setlocal3 ", "OP_0xD8 ", "OP_0xD9 ", "OP_0xDA ", "OP_0xDB ", "OP_0xDC ", "OP_0xDD ", "OP_0xDE ", "OP_0xDF ", "OP_0xE0 ", "OP_0xE1 ", "OP_0xE2 ", "OP_0xE3 ", "OP_0xE4 ", "OP_0xE5 ", "OP_0xE6 ", "OP_0xE7 ", "OP_0xE8 ", "OP_0xE9 ", "OP_0xEA ", "OP_0xEB ", "OP_0xEC ", "OP_0xED ", "OP_0xEE ", "debug ", "debugline ", "debugfile ", "bkptline ", "timestamp ", "OP_0xF4 ", "verifypass ", "alloc ", "mark ", "wb ", "prologue ", "sendenter ", "doubletoatom ", "sweep ", "codegenop ", "verifyop ", "decode " ]; var totalSize:int var opSizes:Array = new Array(256) class Multiname { var nsset:Array var name:String function Multiname(nsset:Array, name:String) { this.nsset = nsset this.name = name } public function toString() { return /*'{' + nsset + '}::' + */name } } dynamic class MetaData { var name:String public function toString():String { var last:String var s:String = last = '['+name+'(' var n for (n in this) s = (last = s + n + "=" + '"' + this[n] + '"') + ',' return last + ')]' } } class MemberInfo { var id:int var kind:int var name var metadata:Array } dynamic class LabelInfo { var count:int function labelFor (target:int):String { if (target in this) return this[target] return this[target] = "L" + (++count) } } class MethodInfo extends MemberInfo { var flags:int var debugName var paramTypes var optionalValues var returnType var local_count:int var max_scope:int var max_stack:int var code_length:uint var code:ByteArray var activation:Traits var anon:Boolean public function toString():String { return format() } public function format():String { var s:String = "" if (flags & NATIVE) s = "native " return s + traitKinds[kind] + " " + name + "(" + paramTypes + "):" + returnType + "\t/* disp_id " + id + "*/" } function dump(abc:Abc, indent:String, attr:String="") { print("") if (metadata) { for each (var md in metadata) print(indent+md) } print(indent+attr+format()) if (code) { print(indent+"{") var oldindent = indent indent += TAB if (flags & NEED_ACTIVATION) { print(indent+"activation {") activation.dump(abc, indent+TAB, "") print(indent+"}") } print(indent+"// local_count="+local_count+ " max_scope=" + max_scope + " max_stack=" + max_stack + " code_len=" + code.length) code.position = 0 var labels:LabelInfo = new LabelInfo() while (code.bytesAvailable > 0) { var start:int = code.position var s = indent + start while (s.length < 12) s += ' '; var opcode = code.readUnsignedByte() if (opcode == OP_label || ((code.position-1) in labels)) { print(indent) print(indent + labels.labelFor(code.position-1) + ": ") } s += opNames[opcode] s += opNames[opcode].length < 8 ? "\t\t" : "\t" switch(opcode) { case OP_debugfile: case OP_pushstring: s += '"' + abc.strings[readU32()].replace(/\n/g,"\\n").replace(/\t/g,"\\t") + '"' break case OP_pushnamespace: s += abc.namespaces[readU32()] break case OP_pushint: var i:int = abc.ints[readU32()] s += i + "\t// 0x" + i.toString(16) break case OP_pushuint: var u:uint = abc.uints[readU32()] s += u + "\t// 0x" + u.toString(16) break; case OP_pushdouble: s += abc.doubles[readU32()] break; case OP_getsuper: case OP_setsuper: case OP_getproperty: case OP_initproperty: case OP_setproperty: case OP_getlex: case OP_findpropstrict: case OP_findproperty: case OP_finddef: case OP_deleteproperty: case OP_istype: case OP_coerce: case OP_astype: case OP_getdescendants: s += abc.names[readU32()] break; case OP_constructprop: case OP_callproperty: case OP_callproplex: case OP_callsuper: case OP_callsupervoid: case OP_callpropvoid: s += abc.names[readU32()] s += " (" + readU32() + ")" break; case OP_newfunction: { var method_id = readU32() s += abc.methods[method_id] abc.methods[method_id].anon = true break; } case OP_callstatic: s += abc.methods[readU32()] s += " (" + readU32() + ")" break; case OP_newclass: s += abc.instances[readU32()] break; case OP_lookupswitch: var pos = code.position-1; var target = pos + readS24() var maxindex = readU32() s += "default:" + labels.labelFor(target) // target + "("+(target-pos)+")" s += " maxcase:" + maxindex for (var i:int=0; i <= maxindex; i++) { target = pos + readS24(); s += " " + labels.labelFor(target) // target + "("+(target-pos)+")" } break; case OP_jump: case OP_iftrue: case OP_iffalse: case OP_ifeq: case OP_ifne: case OP_ifge: case OP_ifnge: case OP_ifgt: case OP_ifngt: case OP_ifle: case OP_ifnle: case OP_iflt: case OP_ifnlt: case OP_ifstricteq: case OP_ifstrictne: var offset = readS24() var target = code.position+offset //s += target + " ("+offset+")" s += labels.labelFor(target) if (!((code.position) in labels)) s += "\n" break; case OP_inclocal: case OP_declocal: case OP_inclocal_i: case OP_declocal_i: case OP_getlocal: case OP_kill: case OP_setlocal: case OP_debugline: case OP_getglobalslot: case OP_getslot: case OP_setglobalslot: case OP_setslot: case OP_pushshort: case OP_newcatch: s += readU32() break case OP_debug: s += code.readUnsignedByte() s += " " + readU32() s += " " + code.readUnsignedByte() s += " " + readU32() break; case OP_newobject: s += "{" + readU32() + "}" break; case OP_newarray: s += "[" + readU32() + "]" break; case OP_call: case OP_construct: case OP_constructsuper: s += "(" + readU32() + ")" break; case OP_pushbyte: case OP_getscopeobject: s += code.readByte() break; case OP_hasnext2: s += readU32() + " " + readU32() default: /*if (opNames[opcode] == ("0x"+opcode.toString(16).toUpperCase())) s += " UNKNOWN OPCODE"*/ break } var size:int = code.position - start totalSize += size opSizes[opcode] = int(opSizes[opcode]) + size print(s) } print(oldindent+"}\n") } } function readU32():int { var result:int = code.readUnsignedByte(); if (!(result & 0x00000080)) return result; result = result & 0x0000007f | code.readUnsignedByte()<<7; if (!(result & 0x00004000)) return result; result = result & 0x00003fff | code.readUnsignedByte()<<14; if (!(result & 0x00200000)) return result; result = result & 0x001fffff | code.readUnsignedByte()<<21; if (!(result & 0x10000000)) return result; return result & 0x0fffffff | code.readUnsignedByte()<<28; } function readS24():int { var b:int = code.readUnsignedByte() b |= code.readUnsignedByte()<<8 b |= code.readByte()<<16 return b } } class SlotInfo extends MemberInfo { var type var value public function format():String { return traitKinds[kind] + " " + name + ":" + type + (value !== undefined ? (" = " + (value is String ? ('"'+value+'"') : value)) : "") + "\t/* slot_id " + id + " */" } function dump(abc:Abc, indent:String, attr:String="") { if (kind == TRAIT_Const || kind == TRAIT_Slot) { if (metadata) { for each (var md in metadata) print(indent+md) } print(indent+attr+format()) return } // else, class var ct:Traits = value var it:Traits = ct.itraits print('') if (metadata) { for each (var md in metadata) print(indent+md) } var def:String; if (it.flags & CLASS_FLAG_interface) def = "interface" else { def = "class"; if (!(it.flags & CLASS_FLAG_sealed)) def = "dynamic " + def; if (it.flags & CLASS_FLAG_final) def = "final " + def; } print(indent+attr+def+" "+name+" extends "+it.base) var oldindent = indent indent += TAB if (it.interfaces.length > 0) print(indent+"implements "+it.interfaces) print(oldindent+"{") it.init.dump(abc,indent) it.dump(abc,indent) ct.dump(abc,indent,"static ") ct.init.dump(abc,indent,"static ") print(oldindent+"}\n") } } class Traits { var name var init:MethodInfo var itraits:Traits var base var flags:int var protectedNs:Namespace const interfaces:Array = [] const names:Object = {} const slots:Array = [] const methods:Array = [] const members:Array = [] public function toString():String { return String(name) } public function dump(abc:Abc, indent:String, attr:String="") { for each (var m in members) m.dump(abc,indent,attr) } } const ATTR_final :int = 0x01; // 1=final, 0=virtual const ATTR_override :int = 0x02; // 1=override, 0=new const ATTR_metadata :int = 0x04; // 1=has metadata, 0=no metadata const ATTR_public :int = 0x08; // 1=add public namespace const CLASS_FLAG_sealed :int = 0x01; const CLASS_FLAG_final :int = 0x02; const CLASS_FLAG_interface :int = 0x04; class Abc { private var data:ByteArray var major:int var minor:int var ints:Array var uints:Array var doubles:Array var strings:Array var namespaces:Array var nssets:Array var names:Array var defaults:Array = new Array(constantKinds.length) var methods:Array var instances:Array var classes:Array var scripts:Array var publicNs = new Namespace("") var anyNs = new Namespace("*") var magic:int function Abc(data:ByteArray) { data.position = 0 this.data = data magic = data.readInt() print("magic " + magic.toString(16)) if (magic != (46<<16|14) && magic != (46<<16|15) && magic != (46<<16|16)) throw new Error("not an abc file. magic=" + magic.toString(16)) parseCpool() defaults[CONSTANT_Utf8] = strings defaults[CONSTANT_Int] = ints defaults[CONSTANT_UInt] = uints defaults[CONSTANT_Double] = doubles defaults[CONSTANT_Int] = ints defaults[CONSTANT_False] = { 10:false } defaults[CONSTANT_True] = { 11:true } defaults[CONSTANT_Namespace] = namespaces defaults[CONSTANT_PrivateNs] = namespaces defaults[CONSTANT_PackageNs] = namespaces defaults[CONSTANT_PackageInternalNs] = namespaces defaults[CONSTANT_ProtectedNs] = namespaces defaults[CONSTANT_StaticProtectedNs] = namespaces defaults[CONSTANT_StaticProtectedNs2] = namespaces defaults[CONSTANT_Null] = { 12: null } parseMethodInfos() parseMetadataInfos() parseInstanceInfos() parseClassInfos() parseScriptInfos() parseMethodBodies() } function readU32():int { var result:int = data.readUnsignedByte(); if (!(result & 0x00000080)) return result; result = result & 0x0000007f | data.readUnsignedByte()<<7; if (!(result & 0x00004000)) return result; result = result & 0x00003fff | data.readUnsignedByte()<<14; if (!(result & 0x00200000)) return result; result = result & 0x001fffff | data.readUnsignedByte()<<21; if (!(result & 0x10000000)) return result; return result & 0x0fffffff | data.readUnsignedByte()<<28; } function parseCpool() { var i:int, j:int var n:int var kind:int var start:int = data.position // ints n = readU32() ints = [0] for (i=1; i < n; i++) ints[i] = readU32() // uints n = readU32() uints = [0] for (i=1; i < n; i++) uints[i] = uint(readU32()) // doubles n = readU32() doubles = [NaN] for (i=1; i < n; i++) doubles[i] = data.readDouble() print("Cpool numbers size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %") start = data.position // strings n = readU32() strings = [""] for (i=1; i < n; i++) strings[i] = data.readUTFBytes(readU32()) print("Cpool strings count "+ n +" size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %") start = data.position // namespaces n = readU32() namespaces = [publicNs] for (i=1; i < n; i++) switch (data.readByte()) { case CONSTANT_Namespace: case CONSTANT_PackageNs: case CONSTANT_PackageInternalNs: case CONSTANT_ProtectedNs: case CONSTANT_StaticProtectedNs: case CONSTANT_StaticProtectedNs2: { namespaces[i] = new Namespace(strings[readU32()]) // todo mark kind of namespace. break; } case CONSTANT_PrivateNs: readU32(); namespaces[i] = new Namespace(null, "private") break; } print("Cpool namespaces count "+ n +" size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %") start = data.position // namespace sets n = readU32() nssets = [null] for (i=1; i < n; i++) { var count:int = readU32() var nsset = nssets[i] = [] for (j=0; j < count; j++) nsset[j] = namespaces[readU32()] } print("Cpool nssets count "+ n +" size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %") start = data.position // multinames n = readU32() names = [null] namespaces[0] = anyNs strings[0] = "*" // any name for (i=1; i < n; i++) switch (data.readByte()) { case CONSTANT_Qname: case CONSTANT_QnameA: names[i] = new QName(namespaces[readU32()], strings[readU32()]) break; case CONSTANT_RTQname: case CONSTANT_RTQnameA: names[i] = new QName(strings[readU32()]) break; case CONSTANT_RTQnameL: case CONSTANT_RTQnameLA: names[i] = null break; case CONSTANT_NameL: case CONSTANT_NameLA: names[i] = new QName(new Namespace(""), null) break; case CONSTANT_Multiname: case CONSTANT_MultinameA: var name = strings[readU32()] names[i] = new Multiname(nssets[readU32()], name) break; case CONSTANT_MultinameL: case CONSTANT_MultinameLA: names[i] = new Multiname(nssets[readU32()], null) break; default: throw new Error("invalid kind " + data[data.position-1]) } print("Cpool names count "+ n +" size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %") start = data.position namespaces[0] = publicNs strings[0] = "*" } function parseMethodInfos() { var start:int = data.position names[0] = new QName(publicNs,"*") var method_count:int = readU32() methods = [] for (var i:int=0; i < method_count; i++) { var m = methods[i] = new MethodInfo() var param_count:int = readU32() m.returnType = names[readU32()] m.paramTypes = [] for (var j:int=0; j < param_count; j++) m.paramTypes[j] = names[readU32()] m.debugName = strings[readU32()] m.flags = data.readByte() if (m.flags & HAS_OPTIONAL) { // has_optional var optional_count:int = readU32(); m.optionalValues = [] for( var k:int = param_count-optional_count; k < param_count; ++k) { var index = readU32() // optional value index var kind:int = data.readByte() // kind byte for each default value if (index == 0) { // kind is ignored, default value is based on type m.optionalValues[k] = undefined } else { if (!defaults[kind]) print("ERROR kind="+kind+" method_id " + i) else m.optionalValues[k] = defaults[kind][index] } } } if (m.flags & HAS_ParamNames) { // has_paramnames for( var k:int = 0; k < param_count; ++k) { readU32(); } } } print("MethodInfo count " +method_count+ " size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %") } function parseMetadataInfos() { var count:int = readU32() metadata = [] for (var i:int=0; i < count; i++) { // MetadataInfo var m = metadata[i] = new MetaData() m.name = strings[readU32()]; var values_count:int = readU32(); var names:Array = [] for(var q:int = 0; q < values_count; ++q) names[q] = strings[readU32()] // name for(var q:int = 0; q < values_count; ++q) m[names[q]] = strings[readU32()] // value } } function parseInstanceInfos() { var start:int = data.position var count:int = readU32() instances = [] for (var i:int=0; i < count; i++) { var t = instances[i] = new Traits() t.name = names[readU32()] t.base = names[readU32()] t.flags = data.readByte() if (t.flags & 8) t.protectedNs = namespaces[readU32()] var interface_count = readU32() for (var j:int=0; j < interface_count; j++) t.interfaces[i] = names[readU32()] var m = t.init = methods[readU32()] m.name = t.name m.kind = TRAIT_Method m.id = -1 parseTraits(t) } print("InstanceInfo size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %") } function parseTraits(t:Traits) { var namecount = readU32() for (var i:int=0; i < namecount; i++) { var name = names[readU32()] var tag = data.readByte() var kind = tag & 0xf var member switch(kind) { case TRAIT_Slot: case TRAIT_Const: case TRAIT_Class: var slot = member = new SlotInfo() slot.id = readU32() t.slots[slot.id] = slot if (kind==TRAIT_Slot || kind==TRAIT_Const) { slot.type = names[readU32()] var index=readU32() if (index) slot.value = defaults[data.readByte()][index] } else // (kind == TRAIT_Class) { slot.value = classes[readU32()] } break; case TRAIT_Method: case TRAIT_Getter: case TRAIT_Setter: var disp_id = readU32() var method = member = methods[readU32()] t.methods[disp_id] = method method.id = disp_id //print("\t",traitKinds[kind],name,disp_id,method,"// disp_id", disp_id) break; } if (!member) print("error trait kind "+kind) member.kind = kind member.name = name t.names[String(name)] = t.members[i] = member if ( (tag >> 4) & ATTR_metadata ) { member.metadata = [] for(var j:int=0, mdCount:int=readU32(); j < mdCount; ++j) member.metadata[j] = metadata[readU32()] } } } function parseClassInfos() { var start:int = data.position var count:int = instances.length classes = [] for (var i:int=0; i < count; i++) { var t:Traits = classes[i] = new Traits() t.init = methods[readU32()] t.base = "Class" t.itraits = instances[i] t.name = t.itraits.name + "$" t.init.name = t.itraits.name + "$cinit" t.init.kind = TRAIT_Method parseTraits(t) } print("ClassInfo size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+"%") } function parseScriptInfos() { var start:int = data.position var count:int = readU32() scripts = [] for (var i:int=0; i < count; i++) { var t = new Traits() scripts[i] = t t.name = "script" + i t.base = names[0] // Object t.init = methods[readU32()] t.init.name = t.name + "$init" t.init.kind = TRAIT_Method parseTraits(t) } print("ScriptInfo size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %") } function parseMethodBodies() { var start:int = data.position var count:int = readU32() for (var i:int=0; i < count; i++) { var m = methods[readU32()] m.max_stack = readU32() m.local_count = readU32() var initScopeDepth = readU32() var maxScopeDepth = readU32() m.max_scope = maxScopeDepth - initScopeDepth var code_length = readU32() m.code = new ByteArray() m.code.endian = "littleEndian" if (code_length > 0) data.readBytes(m.code, 0, code_length) var ex_count = readU32() for (var j:int = 0; j < ex_count; j++) { var from = readU32() var to = readU32() var target = readU32() var type = names[readU32()] //print("magic " + magic.toString(16)) //if (magic >= (46<<16|16)) var name = names[readU32()]; } parseTraits(m.activation = new Traits) } print("MethodBodies size "+(data.position-start)+" "+int(100*(data.position-start)/data.length)+" %") } function dump(indent:String="") { for each (var t in scripts) { print(indent+t.name) t.dump(this,indent) t.init.dump(this,indent) } for each (var m in methods) { if (m.anon) { m.dump(this,indent) } } print("OPCODE\tSIZE\t% OF "+totalSize) var done = [] for (;;) { var max:int = -1; var maxsize:int = 0; for (var i:int=0; i < 256; i++) { if (opSizes[i] > maxsize && !done[i]) { max = i; maxsize = opSizes[i]; } } if (max == -1) break; done[max] = 1; print(opNames[max]+"\t"+int(opSizes[max])+"\t"+int(100*opSizes[max]/totalSize)+"%") } } } class Rect { var nBits:int var xMin:int, xMax:int var yMin:int, yMax:int public function toString() { return "[Rect "+xMin+" "+yMin+" "+xMax+" "+yMax+"]" } } const stagDoABC :int = 72; // embedded .abc (AVM+) bytecode const stagSymbolClass :int = 76; const stagDoABC2 :int = 82; // revised ABC version with a name var tagNames:Array = [ "End", // 00 "ShowFrame", // 01 "DefineShape", // 02 "FreeCharacter", // 03 "PlaceObject", // 04 "RemoveObject", // 05 "DefineBits", // 06 "DefineButton", // 07 "JPEGTables", // 08 "SetBackgroundColor", // 09 "DefineFont", // 10 "DefineText", // 11 "DoAction", // 12 "DefineFontInfo", // 13 "DefineSound", // 14 "StartSound", // 15 "StopSound", // 16 "DefineButtonSound", // 17 "SoundStreamHead", // 18 "SoundStreamBlock", // 19 "DefineBitsLossless", // 20 "DefineBitsJPEG2", // 21 "DefineShape2", // 22 "DefineButtonCxform", // 23 "Protect", // 24 "PathsArePostScript", // 25 "PlaceObject2", // 26 "27 (invalid)", // 27 "RemoveObject2", // 28 "SyncFrame", // 29 "30 (invalid)", // 30 "FreeAll", // 31 "DefineShape3", // 32 "DefineText2", // 33 "DefineButton2", // 34 "DefineBitsJPEG3", // 35 "DefineBitsLossless2", // 36 "DefineEditText", // 37 "DefineVideo", // 38 "DefineSprite", // 39 "NameCharacter", // 40 "ProductInfo", // 41 "DefineTextFormat", // 42 "FrameLabel", // 43 "DefineBehavior", // 44 "SoundStreamHead2", // 45 "DefineMorphShape", // 46 "FrameTag", // 47 "DefineFont2", // 48 "GenCommand", // 49 "DefineCommandObj", // 50 "CharacterSet", // 51 "FontRef", // 52 "DefineFunction", // 53 "PlaceFunction", // 54 "GenTagObject", // 55 "ExportAssets", // 56 "ImportAssets", // 57 "EnableDebugger", // 58 "DoInitAction", // 59 "DefineVideoStream", // 60 "VideoFrame", // 61 "DefineFontInfo2", // 62 "DebugID", // 63 "EnableDebugger2", // 64 "ScriptLimits", // 65 "SetTabIndex", // 66 "DefineShape4", // 67 "DefineMorphShape2", // 68 "FileAttributes", // 69 "PlaceObject3", // 70 "ImportAssets2", // 71 "DoABC", // 72 "73 (invalid)", // 73 "74 (invalid)", // 74 "75 (invalid)", // 75 "SymbolClass", // 76 "77 (invalid)", // 77 "78 (invalid)", // 78 "79 (invalid)", // 79 "80 (invalid)", // 80 "81 (invalid)", // 81 "DoABC2", // 82 "83 (invalid)" // 83 ] class Swf { private var bitPos:int private var bitBuf:int private var data:ByteArray function Swf(data:ByteArray) { this.data = data print("size "+decodeRect()) print("frame rate "+(data.readUnsignedByte()<<8|data.readUnsignedByte())) print("frame count "+data.readUnsignedShort()) decodeTags() } private function decodeTags() { var type:int, h:int, length:int var offset:int while (data.position < data.length) { type = (h = data.readUnsignedShort()) >> 6; if (((length = h & 0x3F) == 0x3F)) length = data.readInt(); print(tagNames[type]+" "+length+"b "+int(100*length/data.length)+"%") switch (type) { case 0: return case stagDoABC2: var pos1:int = data.position data.readInt() print("\nabc name "+readString()) length -= (data.position-pos1) // fall through case stagDoABC: var data2 = new ByteArray data2.endian = "littleEndian" data.readBytes(data2,0,length) new Abc(data2).dump(" ") print("") break default: data.position += length } } } private function readString():String { var s:String = "" var c:int while (c=data.readUnsignedByte()) s += String.fromCharCode(c) return s } private function syncBits() { bitPos = 0 } private function decodeRect():Rect { syncBits(); var rect:Rect = new Rect(); var nBits:int = readUBits(5) rect.xMin = readSBits(nBits); rect.xMax = readSBits(nBits); rect.yMin = readSBits(nBits); rect.yMax = readSBits(nBits); return rect; } function readSBits(numBits:int):int { if (numBits > 32) throw new Error("Number of bits > 32"); var num:int = readUBits(numBits); var shift:int = 32-numBits; // sign extension num = (num << shift) >> shift; return num; } function readUBits(numBits:int):uint { if (numBits == 0) return 0 var bitsLeft:int = numBits; var result:int = 0; if (bitPos == 0) //no value in the buffer - read a byte { bitBuf = data.readUnsignedByte() bitPos = 8; } while (true) { var shift:int = bitsLeft - bitPos; if (shift > 0) { // Consume the entire buffer result |= bitBuf << shift; bitsLeft -= bitPos; // Get the next byte from the input stream bitBuf = data.readUnsignedByte(); bitPos = 8; } else { // Consume a portion of the buffer result |= bitBuf >> -shift; bitPos -= bitsLeft; bitBuf &= 0xff >> (8 - bitPos); // mask off the consumed bits // if (print) System.out.println(" read"+numBits+" " + result); return result; } } } } // main for each (var file in System.argv) { var data:ByteArray = ByteArray.readFile(file) data.endian = "littleEndian" var version:uint = data.readUnsignedInt() switch (version) { case 46<<16|14: case 46<<16|15: case 46<<16|16: var abc:Abc = new Abc(data) abc.dump() break case 67|87<<8|83<<16|9<<24: // SWC9 case 67|87<<8|83<<16|8<<24: // SWC8 case 67|87<<8|83<<16|7<<24: // SWC7 case 67|87<<8|83<<16|6<<24: // SWC6 var udata:ByteArray = new ByteArray udata.endian = "littleEndian" data.position = 8 data.readBytes(udata,0,data.length-data.position) var csize:int = udata.length udata.uncompress() print("decompressed swf "+csize+" -> "+udata.length) udata.position = 0 /*var swf:Swf =*/ new Swf(udata) break case 70|87<<8|83<<16|9<<24: // SWC9 case 70|87<<8|83<<16|8<<24: // SWC8 case 70|87<<8|83<<16|7<<24: // SWC7 case 70|87<<8|83<<16|6<<24: // SWC6 case 70|87<<8|83<<16|5<<24: // SWC5 case 70|87<<8|83<<16|4<<24: // SWC4 data.position = 8 // skip header and length /*var swf:Swf =*/ new Swf(data) break default: print('unknown format '+version) break } } }