Handle VerifyError: Error #1014 when loading swfs

2019-03-22 07:22发布

问题:

We are making a system that has a main swf for the application, and loads separate tools from separate swfs -- there will be versioning issues in the future since the separate swfs are coming from a cms (especially now since we're still developing, but also in the future when other developers might create an incompatible tool). I'm trying hard to prevent them as much as possible but I'd really like to be able to display a message to the user of the system when an incompatible swf is loaded.

This would mean that we need to catch that VerifyError or at least determine the loading failed for some reason - I've got no idea how to handle that at the moment. I suspect it might be possible using 10.1 and the uncaughtError system, but we are currently targeting flash player 10. Does anyone have a good idea? (we are already handling IOErrorEvent.IO_ERROR)

UPDATE: I've built a solution that scans the bytecode before importing, looks like that will work. I'll post the solution later.

回答1:

The best way to do this is by using one of the libraries bhups suggested. I used senocular's for the next example. Also, because the senocular's library provides only basic operations for the parsed SWF you may need the SWF Format Spec (adobe.com/devnet/swf/pdf/swf_file_format_spec_v10.pdf) to get the info you want out of the loaded SWF.

The next example lists all the class names from a loaded SWF:

package swf
{
 import flash.events.Event;
 import flash.net.URLRequest;
 import flash.net.URLStream;
 import flash.utils.ByteArray;
 import flash.utils.Endian;

 import swf.SWFReader;

 public class GetSWFInfo
 {

  private var swfInfo:SWFReader;

  public function GetSWFInfo()
  {
   var urlRequest:URLRequest = new URLRequest("theswf.swf");
   var loader:URLStream = new URLStream();   
   loader.load(urlRequest);
   loader.addEventListener(Event.COMPLETE, onComplete);
  }


  public function onComplete(e:Event):void {
   var recivedByteArray :ByteArray = new ByteArray();
   URLStream(e.currentTarget).readBytes(recivedByteArray);


   //create a new instance of SWFReader
   swfInfo = new SWFReader();
   //readTag it's a callback function that will be called when a tag is read during the SWF parse process.
   //read more on tags in the SWF specification document
   swfInfo.tagCallback =  readTag;
   //start parsing
   swfInfo.parse(recivedByteArray); 
  }



  public function readTag(tag:uint, bytes:ByteArray):void {


   //76 it's the tag type for SymbolClass tag
   //read more in the SWF specification document
   if (76 == tag) {


    var classesArray:Array = new Array();
    var symbolsNumber:uint = 0;
    var currentId:uint = 0;

    bytes.endian = Endian.LITTLE_ENDIAN;

    //read the symbols Number
    //again read more in the SWF specification document
    symbolsNumber = bytes.readShort();

    bytes.position = 4;

    while (true) {

     var i:uint = bytes.position;

     //every string name ends with a null byte
     //again read more in the SWF specification document
     while(bytes[i] != 0) i++;

     var readAmount:uint = i - bytes.position;

     classesArray.push(bytes.readUTFBytes(readAmount));

     //the last ID is always the base class Id, and it's 0
     currentId=bytes.readUnsignedShort();

     bytes.position++;     

     if (currentId==0) {
      break;
     }
    }

    //this two should be equal
    trace(classesArray.length + 1);//the number of elements in the classesArray
    trace(symbolsNumber);//the number of classes retrived from the SWF

    //list the names
    var name:String;
    for each (name in classesArray) {
     trace(name);
    }

    //now you have an array with all the class names that you can use to compare

   }
  }
 }

}



回答2:

I did misunderstand what you are trying to do.

Well, actually, I guess there is no handler for verify error and to detect it, you have to fight with byte-codes.

By the way, I have and idea which is not the very answer for your question but may helps you.

a 3rd party swf is depending on a class that should be in my swf -- if that class is missing I get the VerifyError.

From this point, I can advice that if you link the 'missing class' into your swf and load the 3rd party swf into ApplicationDomain.currentDomain or new ApplicationDomain(ApplicationDomain.currentDomain), you can avoid the 'Verify Error'. (This is because the flash player will find the diffinition of the missing class in the parent swf.)

Here is my sample code which loads a swf with verify error(http://teionclub.com/test/xml/main.swf).

Avoiding VerifyError - wonderfl build flash online


回答3:

I think there is a way to workaround this issue.

  1. load the swf using a URLLoader or URLStream into a ByteArray.
  2. Use any open source library for parsing SWF binary like this or this.
  3. check if it verifies that the whole byte array represents valid SWF file.
  4. If above test succeeds then load this ByteArray into loader using loadBytes method.
  5. Else show user that this is not working.

Disclaimer: A binary file can be a valid SWF still might not be render-able, but with this you can discard all the invalid SWFs or any other formats whose extension are changed to swf.



回答4:

I've worked with that kind of application in the past but I think it would be better to fix the SWF loaded rather than handling VerifyError. VeriyError indicates that the SWF loaded is corrupted or malformed.

And it's natural that the SWF itself is malformed rather than that the SWF is corrupted during the transfer. I guess you are trying to load png or other format named ".swf" or the SWF is generated by some software other than Flex compiler or Flash such as swfmill(In the latter case, there would be a bug in that software).



回答5:

To finally answer my own question, this is the utility class I've been using to detect possible errors. I load the SWF as a bytearray and scan the contents before loading it as an actual MovieClip.

As you can see my code heavily depends on the com.segfaultlabs.swfutils package

Important: I've stopped using this method of preventing errors, opting for the more manual approach of checking the files by actually trying to load them and see if they work. This is because the utility is not complete, and my current knowledge of the ABC format is not good enough to make sure I can develop a check that will always be correct.

Posting my code here as starting point for others who want to take a stab at it :-)

package nl.ijsfontein.utils
{
    import com.segfaultlabs.swfutils.ABC.ABCCPool;
    import com.segfaultlabs.swfutils.ABC.ABCClass;
    import com.segfaultlabs.swfutils.ABC.ABCInstance;
    import com.segfaultlabs.swfutils.ABC.ABCMethodInfo;
    import com.segfaultlabs.swfutils.ABC.ABCMultiname;
    import com.segfaultlabs.swfutils.ABC.ABCParser;
    import com.segfaultlabs.swfutils.ABC.ABCTraitConstSlot;
    import com.segfaultlabs.swfutils.ABC.ABCTraitsInfo;
    import com.segfaultlabs.swfutils.ABC.ABCinfo;
    import com.segfaultlabs.swfutils.SWFDataInput;
    import com.segfaultlabs.swfutils.SWFFile;

    import flash.system.ApplicationDomain;
    import flash.utils.ByteArray;

    /**
     * utility to see which classes a swf uses, but doesn't contain itself 
     * - this can be used to detect possible VerifyErrors before they happen.
     */
    public class SwfDependencyUtil
    {
        public function SwfDependencyUtil()
        {
        }

        // return null if ok, or name of needed class if external depencendy
        private static function resolveSuper(abc:ABCinfo, superClass:String):String
        {
            //if (superClass.indexOf("flash.") == 0 || superClass.indexOf("*") == 0 || superClass.indexOf("Object") == 0)
            if (superClass.indexOf("*") == 0)
            {
                trace('  super: ' + superClass + " (ignore)");

            }
            else
            {
                var superClassClass:ABCClass = null;
                for each ( var c:ABCClass in abc.classes )
                {
                    if (c.name == superClass)
                    {
                        superClassClass = c;
                    }
                }
                if (superClassClass)
                {
                    trace('  super: ' + superClass + " (resolved internally)");
                    return resolveSuper(abc, superClassClass.iref.base);

                }
                else
                {
                    trace('  super: ' + superClass + " (NOTFOUND)");
                    return superClass;
                }
            }

            return null;
        }

        /*
         * checks: classes, superclasses, static variables, member variables
         * TODO: function arguments
         * won't check: method bodies
         *
         * TODO: refactor to multiple methods
         */
        public static function getDependencies(swfBytes:ByteArray):Array /* of String */
        {
            var result:Array = [];
                swfBytes.position = 0;
                var swfr:SWFFile = new SWFFile(swfBytes);
                var arr:Array;
                if ( swfr.compressed )
                {
                    swfr.dataInput = swfr.uncompress();
                    swfr.readHeader();                  
                };
                arr = swfr.parseTags();
                if ( arr[82] != null )
                {
                    var abc:ABCinfo = new ABCinfo();
                    var cpool:ABCCPool = new ABCCPool();
                    var abcparse:ABCParser = new ABCParser();

                    abcparse.readMethodBytes = true;
                    abcparse.readExceptions = false;
                    for ( var j:int = 0; j < arr[82].length; j += 1 )
                    {
                        swfr.dataInstance.position = arr[82][j].position;
                        try
                        {
                            abcparse.parse( swfr.dataInput as SWFDataInput, abc, cpool, new FakeLogger() );
                            for each ( var c:ABCClass in abc.classes )
                            {
                                trace('class:', c.name);
                                var superClass:String = c.iref.base;
                                var dependency:String = resolveSuper(abc, superClass);
                                if (dependency)
                                {
                                    result.push(dependency);
                                }
                                for each (var mn:ABCMultiname in c.iref.interfaces)
                                {
                                    var interfaceName:String = mn.nsset[0] != "" ? mn.nsset[0] + "::" + mn.name : mn.name;
                                    var interfaceDependency:String = resolveSuper(abc, interfaceName);
                                    if (interfaceDependency)
                                    {
                                        result.push(interfaceDependency);
                                    }
                                }
                                for each (var ti:ABCTraitsInfo in c.traits)
                                {
                                    if (ti is ABCTraitConstSlot)
                                    {
                                        var constName:String
                                        if (QName(ABCTraitConstSlot(ti).type).uri)
                                        {
                                            constName = QName(ABCTraitConstSlot(ti).type).uri + "::" + QName(ABCTraitConstSlot(ti).type).localName
                                        }
                                        else
                                        {
                                            constName = QName(ABCTraitConstSlot(ti).type).localName
                                        }
                                        var constDependency:String = resolveSuper(abc, constName);
                                        if (constDependency)
                                        {
                                            result.push(constDependency);
                                        }
                                    }
                                    else if (ti is ABCMethodInfo)
                                    {
                                        trace('method', ABCMethodInfo(ti).name);
                                    } else
                                    {
                                        trace(ti);
                                    }
                                    // trace(ti.type.localName);
                                }

                                // const (static?) members: c.traits
                            }

                            for each ( var i:ABCInstance in abc.instances )
                            {
//                              trace(i);
                                for each (var instanceTi:ABCTraitsInfo in i.traits)
                                {
                                    if (instanceTi is ABCTraitConstSlot)
                                    {
                                        trace('instance:', createClassNameFromQname(ABCTraitConstSlot(instanceTi).type));
                                        var csdep:String = resolveSuper(abc, createClassNameFromQname(ABCTraitConstSlot(instanceTi).type));
                                        if (csdep)
                                        {
                                            result.push(csdep);
                                        }
                                    }
                                    else if (instanceTi is ABCMethodInfo)
                                    {

                                    }
                                    else
                                    {
                                        trace('unexpected trait type');
                                    }
                                }
                            }

                            abc.dispose();
                        } 
                        catch ( e:Error ) 
                        { 
                            trace( "  Error  ",e.getStackTrace() );
                        };                          
                    };
                    cpool.dispose();
                }
                else
                {
                    trace("No DoABC block... ;(");
                }
            return result;
        }

        private static function createClassNameFromQname(qn:QName):String
        {
            var result:String
            if (qn.uri)
            {
                result = qn.uri + "::" + qn.localName
            }
            else
            {
                result = qn.localName
            }
            return result;
        }

        public static function getUnsatisfiedDependencies(swfBytes:ByteArray):Array /* of String */
        {
            var result:Array = [];
            var dependencies:Array = SwfDependencyUtil.getDependencies(swfBytes)
            for each (var dependency:String in dependencies)
            {
                if (ApplicationDomain.currentDomain.hasDefinition(dependency))
                {
                    trace('ok: ', dependency);
                }
                else
                {
                    trace('ERROR: unsatisfied dependency: ', dependency);
                    result.push(dependency);
                }
            }
            return result;
        }
    }
}