Commits

Anonymous committed b89a55c

parser/container/swf: handle SWF v9 doABC tag and allow v10 scripts to be parsed

  • Participants
  • Parent commits b94e407

Comments (0)

Files changed (2)

hachoir-parser/hachoir_parser/container/action_script.py

 
  - Alexis' SWF Reference:
    http://www.m2osw.com/swf_alexref.html
+ - Tamarin ABC format:
+   http://www.m2osw.com/abc_format.html
 
-Author: Sebastien Ponce
+Authors: Sebastien Ponce, Robert Xiao
 Creation date: 26 April 2008
 """
 
+from hachoir_parser import Parser
 from hachoir_core.field import (FieldSet, ParserError,
-    Bit, Bits, UInt8, UInt32, Int16, UInt16, Float32, CString,
-    RawBytes)
-#from hachoir_core.field import Field
+    Bit, Bits, UInt8, UInt32, Int16, UInt16, Float32, Float64, CString, Enum,
+    Bytes, RawBytes, NullBits, String, SubFile, Field)
+from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
 from hachoir_core.field.float import FloatExponent
 from struct import unpack
 
+class FlashPackedInteger(Bits):
+    def __init__(self, parent, name, signed=False, nbits=30, description=None):
+        Bits.__init__(self, parent, name, 8, description)
+        stream = self._parent.stream
+        addr = self.absolute_address
+        size = 0
+        value = 0
+        mult = 1
+        while True:
+            byte = stream.readBits(addr+size, 8, LITTLE_ENDIAN)
+            value += mult * (byte & 0x7f)
+            size += 8
+            mult <<= 7
+            if byte < 128:
+                break
+        self._size = size
+        if signed and (1 << (nbits-1)) <= value:
+            value -= (1 << nbits)
+        self.createValue = lambda: value
+
+class FlashU30(FlashPackedInteger):
+    def __init__(self, parent, name, description=None):
+        FlashPackedInteger.__init__(self, parent, name, signed=False, nbits=30, description=description)
+
+class FlashS32(FlashPackedInteger):
+    def __init__(self, parent, name, description=None):
+        FlashPackedInteger.__init__(self, parent, name, signed=True, nbits=32, description=description)
+
+class FlashU32(FlashPackedInteger):
+    def __init__(self, parent, name, description=None):
+        FlashPackedInteger.__init__(self, parent, name, signed=False, nbits=32, description=description)
+
 class FlashFloat64(FieldSet):
     def createFields(self):
-        yield Bits(self, "mantisa_high", 20)
+        yield Bits(self, "mantissa_high", 20)
         yield FloatExponent(self, "exponent", 11)
         yield Bit(self, "negative")
-        yield Bits(self, "mantisa_low", 32)
+        yield Bits(self, "mantissa_low", 32)
 
     def createValue(self):
         # Manual computation:
-        # mantisa = mantisa_high * 2^32 + mantisa_low
-        # float = 2^exponent + (1 + mantisa / 2^52)
+        # mantissa = mantissa_high * 2^32 + mantissa_low
+        # float = 2^exponent + (1 + mantissa / 2^52)
         # (and float is negative if negative=True)
         bytes = self.parent.stream.readBytes(
             self.absolute_address, self.size//8)
     0x05: (UInt8, "Boolean[]"),
     0x06: (FlashFloat64, "Double[]"),
     0x07: (UInt32, "Integer[]"),
-    0x08: (UInt8, "Dictionnary_Lookup_Index[]"),
-    0x09: (UInt16, "Large_Dictionnary_Lookup_Index[]"),
+    0x08: (UInt8, "Dictionary_Lookup_Index[]"),
+    0x09: (UInt16, "Large_Dictionary_Lookup_Index[]"),
 }
 
 def parseBranch(parent, size):
 def parseWaitForFrameDyn(parent, size):
     yield UInt8(parent, "skip")
 
-def parseDeclareDictionnary(parent, size):
+def parseDeclareDictionary(parent, size):
     count = UInt16(parent, "count")
     yield count
     for i in range(count.value):
         # Objects
         0x2B: ("Cast_Object[]", "Cast Object", None),
         0x42: ("Declare_Array[]", "Declare Array", None),
-        0x88: ("Declare_Dictionary[]", "Declare Dictionary", parseDeclareDictionnary),
+        0x88: ("Declare_Dictionary[]", "Declare Dictionary", parseDeclareDictionary),
         0x43: ("Declare_Object[]", "Declare Object", None),
         0x3A: ("Delete[]", "Delete", None),
         0x3B: ("Delete_All[]", "Delete All", None),
 def parseActionScript(parent, size):
     yield ActionScript(parent, "action", size=size*8)
 
+def FindABC(field):
+    while not getattr(field, "isABC", False):
+        field = field.parent
+        if field is None:
+            return None
+    return field
+
+def GetConstant(field, pool, index):
+    if index == 0:
+        return None
+    return FindABC(field)["constant_%s_pool/constant[%i]"%(pool, index)]
+
+def GetMultiname(field, index):
+    fld = GetConstant(field, "multiname", index)
+    if fld is None:
+        return "*"
+    if "name_index" not in fld:
+        return "?"
+    fld2 = GetConstant(fld, "string", fld["name_index"].value)
+    if fld2 is None:
+        return "*"
+    return fld2.value
+
+class ABCStringIndex(FlashU30):
+    def createDisplay(self):
+        fld = GetConstant(self, "string", self.value)
+        if fld is None:
+            return "*"
+        return fld.value
+
+class ABCNSIndex(FlashU30):
+    def createDisplay(self):
+        fld = GetConstant(self, "namespace", self.value)
+        if fld is None:
+            return "*"
+        return fld.display
+
+class ABCMethodIndex(FlashU30):
+    def createDisplay(self):
+        fld = FindABC(self)["method_array/method[%i]"%self.value]
+        if fld is None:
+            return "*"
+        return fld.description
+
+class ABCMultinameIndex(FlashU30):
+    def createDisplay(self):
+        return GetMultiname(self, self.value)
+
+class ABCConstantPool(FieldSet):
+    def __init__(self, parent, name, klass):
+        FieldSet.__init__(self, parent, 'constant_%s_pool'%name)
+        self.klass = klass
+    def createFields(self):
+        ctr = FlashU30(self, "count")
+        yield ctr
+        for i in xrange(ctr.value-1):
+            yield self.klass(self, "constant[%i]"%(i+1))
+
+class ABCObjectArray(FieldSet):
+    def __init__(self, parent, name, klass):
+        self.arrname = name
+        FieldSet.__init__(self, parent, name+'_array')
+        self.klass = klass
+    def createFields(self):
+        ctr = FlashU30(self, "count")
+        yield ctr
+        for i in xrange(ctr.value):
+            yield self.klass(self, self.arrname+"[]")
+
+class ABCClassArray(FieldSet):
+    def __init__(self, parent, name):
+        FieldSet.__init__(self, parent, name+'_array')
+    def createFields(self):
+        ctr = FlashU30(self, "count")
+        yield ctr
+        for i in xrange(ctr.value):
+            yield ABCInstanceInfo(self, "instance[]")
+        for i in xrange(ctr.value):
+            yield ABCClassInfo(self, "class[]")
+
+class ABCConstantString(FieldSet):
+    def createFields(self):
+        yield FlashU30(self, "length")
+        size = self["length"].value
+        if size:
+            yield String(self, "data", size, charset="UTF-8")
+
+    def createDisplay(self):
+        if "data" in self:
+            return self["data"].display
+        else:
+            return "<empty>"
+
+    def createValue(self):
+        if "data" in self:
+            return self["data"].value
+        else:
+            return ""
+
+class ABCConstantNamespace(FieldSet):
+    NAMESPACE_KIND = {8: "Namespace",
+                      5: "PrivateNamespace",
+                      22: "PackageNamespace",
+                      23: "PacakgeInternalNamespace",
+                      24: "ProtectedNamespace",
+                      25: "ExplicitNamespace",
+                      26: "MultinameL"}
+    def createFields(self):
+        yield Enum(UInt8(self, "kind"), self.NAMESPACE_KIND)
+        yield ABCStringIndex(self, "name_index")
+
+    def createDisplay(self):
+        return "%s %s"%(self["kind"].display, self["name_index"].display)
+
+    def createValue(self):
+        return self["name_index"].value
+
+class ABCConstantNamespaceSet(FieldSet):
+    def createFields(self):
+        ctr = FlashU30(self, "namespace_count")
+        yield ctr
+        for i in xrange(ctr.value):
+            yield ABCNSIndex(self, "namespace_index[]")
+
+    def createDescription(self):
+        ret = [fld.display for fld in self.array("namespace_index")]
+        return ', '.join(ret)
+
+class ABCConstantMultiname(FieldSet):
+    MULTINAME_KIND = {7: "Qname",
+                      13: "QnameA",
+                      9: "Multiname",
+                      14: "MultinameA",
+                      15: "RTQname",
+                      16: "RTQnameA",
+                      27: "MultinameL",
+                      17: "RTQnameL",
+                      18: "RTQnameLA"}
+    def createFields(self):
+        yield Enum(UInt8(self, "kind"), self.MULTINAME_KIND)
+        kind = self["kind"].value
+        if kind in (7,13): # Qname
+            yield FlashU30(self, "namespace_index")
+            yield ABCStringIndex(self, "name_index")
+        elif kind in (9,14): # Multiname
+            yield ABCStringIndex(self, "name_index")
+            yield FlashU30(self, "namespace_set_index")
+        elif kind in (15,16): # RTQname
+            yield ABCStringIndex(self, "name_index")
+        elif kind == 27: # MultinameL
+            yield FlashU30(self, "namespace_set_index")
+        elif kind in (17,18): # RTQnameL
+            pass
+
+    def createDisplay(self):
+        kind = self["kind"].display
+        if "name_index" in self:
+            return kind + " " + self["name_index"].display
+        return kind
+
+    def createValue(self):
+        return self["kind"].value
+
+class ABCTrait(FieldSet):
+    TRAIT_KIND = {0: "slot",
+                  1: "method",
+                  2: "getter",
+                  3: "setter",
+                  4: "class",
+                  5: "function",
+                  6: "const",}
+    def createFields(self):
+        yield ABCMultinameIndex(self, "name_index")
+        yield Enum(Bits(self, "kind", 4), self.TRAIT_KIND)
+        yield Enum(Bit(self, "is_final"), {True:'final',False:'virtual'})
+        yield Enum(Bit(self, "is_override"), {True:'override',False:'new'})
+        yield Bit(self, "has_metadata")
+        yield Bits(self, "unused", 1)
+        kind = self["kind"].value
+        if kind in (0,6): # slot, const
+            yield FlashU30(self, "slot_id")
+            yield ABCMultinameIndex(self, "type_index")
+            ### TODO reference appropriate constant pool using value_kind
+            yield FlashU30(self, "value_index")
+            if self['value_index'].value != 0:
+                yield UInt8(self, "value_kind")
+        elif kind in (1,2,3): # method, getter, setter
+            yield FlashU30(self, "disp_id")
+            yield ABCMethodIndex(self, "method_info")
+        elif kind == 4: # class
+            yield FlashU30(self, "disp_id")
+            yield FlashU30(self, "class_info")
+        elif kind == 5: # function
+            yield FlashU30(self, "disp_id")
+            yield ABCMethodIndex(self, "method_info")
+        if self['has_metadata'].value:
+            yield ABCObjectArray(self, "metadata", FlashU30)
+
+class ABCValueKind(FieldSet):
+    def createFields(self):
+        yield FlashU30(self, "value_index")
+        yield UInt8(self, "value_kind")
+
+class ABCMethodInfo(FieldSet):
+    def createFields(self):
+        yield FlashU30(self, "param_count")
+        yield ABCMultinameIndex(self, "ret_type")
+        for i in xrange(self["param_count"].value):
+            yield ABCMultinameIndex(self, "param_type[]")
+        yield ABCStringIndex(self, "name_index")
+        yield Bit(self, "need_arguments")
+        yield Bit(self, "need_activation")
+        yield Bit(self, "need_rest")
+        yield Bit(self, "has_optional")
+        yield Bit(self, "ignore_rest")
+        yield Bit(self, "explicit")
+        yield Bit(self, "setsdxns")
+        yield Bit(self, "has_paramnames")
+        if self["has_optional"].value:
+            yield ABCObjectArray(self, "optional", ABCValueKind)
+        if self["has_paramnames"].value:
+            for i in xrange(self["param_count"].value):
+                yield FlashU30(self, "param_name[]")
+
+    def createDescription(self):
+        ret = GetMultiname(self, self["ret_type"].value)
+        ret += " " + self["name_index"].display
+        ret += "(" + ", ".join(GetMultiname(self, fld.value) for fld in self.array("param_type")) + ")"
+        return ret
+
+class ABCMetadataInfo(FieldSet):
+    def createFields(self):
+        yield ABCStringIndex(self, "name_index")
+        yield FlashU30(self, "values_count")
+        count = self["values_count"].value
+        for i in xrange(count):
+            yield FlashU30(self, "key[]")
+        for i in xrange(count):
+            yield FlashU30(self, "value[]")
+
+class ABCInstanceInfo(FieldSet):
+    def createFields(self):
+        yield ABCMultinameIndex(self, "name_index")
+        yield ABCMultinameIndex(self, "super_index")
+        yield Bit(self, "is_sealed")
+        yield Bit(self, "is_final")
+        yield Bit(self, "is_interface")
+        yield Bit(self, "is_protected")
+        yield Bits(self, "unused", 4)
+        if self['is_protected'].value:
+            yield ABCNSIndex(self, "protectedNS")
+        yield FlashU30(self, "interfaces_count")
+        for i in xrange(self["interfaces_count"].value):
+            yield ABCMultinameIndex(self, "interface[]")
+        yield ABCMethodIndex(self, "iinit_index")
+        yield ABCObjectArray(self, "trait", ABCTrait)
+
+class ABCClassInfo(FieldSet):
+    def createFields(self):
+        yield ABCMethodIndex(self, "cinit_index")
+        yield ABCObjectArray(self, "trait", ABCTrait)
+
+class ABCScriptInfo(FieldSet):
+    def createFields(self):
+        yield ABCMethodIndex(self, "init_index")
+        yield ABCObjectArray(self, "trait", ABCTrait)
+
+class ABCException(FieldSet):
+    def createFields(self):
+        yield FlashU30(self, "start")
+        yield FlashU30(self, "end")
+        yield FlashU30(self, "target")
+        yield FlashU30(self, "type_index")
+        yield FlashU30(self, "name_index")
+
+class ABCMethodBody(FieldSet):
+    def createFields(self):
+        yield ABCMethodIndex(self, "method_info")
+        yield FlashU30(self, "max_stack")
+        yield FlashU30(self, "max_regs")
+        yield FlashU30(self, "scope_depth")
+        yield FlashU30(self, "max_scope")
+        yield FlashU30(self, "code_length")
+        yield RawBytes(self, "code", self['code_length'].value)
+        yield ABCObjectArray(self, "exception", ABCException)
+        yield ABCObjectArray(self, "trait", ABCTrait)
+
+def parseABC(parent, size):
+    code = parent["code"].value
+    if code == parent.TAG_DO_ABC_DEFINE:
+        yield UInt32(parent, "action_flags")
+        yield CString(parent, "action_name")
+    yield UInt16(parent, "minor_version")
+    yield UInt16(parent, "major_version")
+    parent.isABC = True
+
+    yield ABCConstantPool(parent, "int", FlashS32)
+    yield ABCConstantPool(parent, "uint", FlashU32)
+    yield ABCConstantPool(parent, "double", Float64)
+    yield ABCConstantPool(parent, "string", ABCConstantString)
+    yield ABCConstantPool(parent, "namespace", ABCConstantNamespace)
+    yield ABCConstantPool(parent, "namespace_set", ABCConstantNamespaceSet)
+    yield ABCConstantPool(parent, "multiname", ABCConstantMultiname)
+
+    yield ABCObjectArray(parent, "method", ABCMethodInfo)
+    yield ABCObjectArray(parent, "metadata", ABCMetadataInfo)
+    yield ABCClassArray(parent, "class")
+    yield ABCObjectArray(parent, "script", ABCScriptInfo)
+    yield ABCObjectArray(parent, "body", ABCMethodBody)
+

hachoir-parser/hachoir_parser/container/swf.py

 
 from hachoir_parser import Parser
 from hachoir_core.field import (FieldSet, ParserError,
-    Bit, Bits, UInt8, UInt32, UInt16, CString, Enum,
+    Bit, Bits, UInt8, UInt16, Int32, UInt32, Int64, CString, Enum,
     Bytes, RawBytes, NullBits, String, SubFile)
 from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
 from hachoir_core.text_handler import textHandler, filesizeHandler
 from hachoir_parser.image.jpeg import JpegChunk, JpegFile
 from hachoir_core.stream import StringInputStream, ConcatStream
 from hachoir_parser.common.deflate import Deflate, has_deflate
-from hachoir_parser.container.action_script import parseActionScript
+from hachoir_parser.container.action_script import parseActionScript, parseABC
 import math
 
 # Maximum file size (50 MB)
     for index in xrange(parent["count"].value):
         yield Export(parent, "export[]")
 
+def parseProductInfo(parent, size):
+    yield Int32(parent, "product_id")
+    yield Int32(parent, "edition")
+    yield UInt8(parent, "major_version")
+    yield UInt8(parent, "minor_version")
+    yield Int64(parent, "build_number")
+    yield Int64(parent, "compilation_date")
+
+def parseScriptLimits(parent, size):
+    yield UInt16(parent, "max_recursion_limit")
+    yield UInt16(parent, "timeout_seconds", "Seconds of processing until the SWF is considered 'stuck'")
+
+def parseSymbolClass(parent, size):
+    yield UInt16(parent, "count")
+    for index in xrange(parent["count"].value):
+        yield UInt16(parent, "symbol_id[]")
+        yield CString(parent, "symbol_name[]")
+
+def parseBinaryData(parent, size):
+    yield UInt16(parent, "data_id")
+    yield UInt32(parent, "reserved")
+    if size > 6:
+        yield RawBytes(parent, "data", size-6)
+
 class Tag(FieldSet):
     TAG_BITS = 6
     TAG_BITS_JPEG2 = 32
     TAG_BITS_JPEG3 = 35
+    TAG_DO_ABC_DEFINE = 82
     TAG_INFO = {
         # SWF version 1.0
          0: ("end[]", "End", None),
         36: ("def_bits_lossless2[]", "Define bits lossless 2", None),
         39: ("def_sprite[]", "Define sprite", None),
         40: ("name_character[]", "Name character", None),
-        41: ("serial_number", "Serial number", None),
+        41: ("product_info", "Generator product info", parseProductInfo),
         42: ("generator_text[]", "Generator text", None),
         43: ("frame_label[]", "Frame label", None),
         45: ("sound_hdr2[]", "Sound stream header2", parseSoundHeader),
         64: ("enable_debug2", "Enable debugger 2", None),
 
         # SWF version 7.0
-        65: ("script_limits[]", "Script limits", None),
+        65: ("script_limits[]", "Script limits", parseScriptLimits),
         66: ("tab_index[]", "Set tab index", None),
 
         # SWF version 8.0
         78: ("def_scale_grid[]", "Define scaling factors", None),
         83: ("def_shape4[]", "Define shape 4", None),
         84: ("def_morph2[]", "Define a morphing shape 2", None),
+
+        # SWF version 9.0
+        72: ("do_abc[]", "SWF 9 ActionScript container; actions only", parseABC),
+        76: ("symbol_class[]", "Instantiate objects from a set of classes", parseSymbolClass),
+        82: ("do_abc_define[]", "SWF 9 ActionScript container; identifier, name, actions", parseABC),
+        86: ("def_scene_frame[]", "Define raw data for scenes and frames", None),
+        87: ("def_binary_data[]", "Defines a buffer of any size with any binary user data", parseBinaryData),
+        88: ("def_font_name[]", "Define the legal font name and copyright", None),
     }
 
     def __init__(self, *args):
         return "Tag: %s (%s)" % (self["code"].display, self["length"].display)
 
 class SwfFile(Parser):
-    VALID_VERSIONS = set(xrange(1, 9+1))
+    VALID_VERSIONS = set(xrange(1, 10+1))
     PARSER_TAGS = {
         "id": "swf",
         "category": "container",