flashactionscript-3actionscriptbytecodeavm2

How are methods found in AVM2 bytecode?


I've been playing around with ABC bytecode and was hoping someone could clear up a point of confusion for me. I have a simple flash file that places a clip on the stage and has a tiny script to update its position on each frame. The code looks something like:

package
{
     import flash.display.MovieClip;     
     import flash.events.Event;

     public class RedCircle extends MovieClip
     {
          public function RedCircle()
          {
               this.addEventListener(Event.ENTER_FRAME, moveit);
          }

          function moveit(e:Event)
          {
               this.x -=1;
          }
     }
}

Which compiles to something like:

protected package protected RedCircle
{
    class RedCircle extends flash.display.MovieClip
    {
        static () : Void
        {
            getlocal_0();
            pushscope();
            returnvoid();
        }



        RedCircle () : Void
        {
            getlocal_0();
            pushscope();
            getlocal_0();
            constructsuper(0);
            getlocal_0();
            getlex(flash.events.Event);
            getproperty(ENTER_FRAME);
            getlex(internal .moveit);       // ###1
            callpropvoid(addEventListener, 2);
            returnvoid();
        }



        function (anonymous) (flash.events.Event param1) : Void // ###2
        {
            getlocal_0();
            pushscope();
            getlocal_0();
            getlocal_0();
            getproperty(x);
            decrement();
            setproperty(x);
            returnvoid();
        }
    }
}

My question is how does the 'getlex' operation work (I've marked it with ###1). It is passed a multiname which references the 'moveit' method of the class. Unfortunately, the 'name' field in method info seems never ever to get used by the compiler. All methods have the empty string as its name (Shown above as the unnamed function at ###2).

How does the flash player link the multiname to the unnamed method? There appears to be no provision for this in the AVM2 spec.

I know it's possible because commercial decompilers like sothink manage to determine the method name. I'm just not sure how they do it, or how the code could ever work.


Solution

  • I'm not sure why your decompiler shows the method as (anonymous).

    Here is a dump of the abcData:

    abcFile{
    minor_version (17): 16
    major_version (19): 46
    constant_pool{
        int_count (21): 0
            [0]: zero (not included in abcFile)
        uint_count (22): 0
            [0]: zero (not included in abcFile)
        double_count (23): 0
            [0]: NaN (not included in abcFile)
        string_count (24): 17
            string_info[0]{
                name: * (not included in abcFile)
            }
            string_info[1]{
                size (25): 12
                name (26): "flash.events"
            }
            string_info[2]{
                size (38): 5
                name (39): "Event"
            }
            string_info[3]{
                size (44): 0
                name (45): ""
            }
            string_info[4]{
                size (45): 9
                name (46): "RedCircle"
            }
            string_info[5]{
                size (55): 13
                name (56): "flash.display"
            }
            string_info[6]{
                size (69): 9
                name (70): "MovieClip"
            }
            string_info[7]{
                size (79): 6
                name (80): "moveit"
            }
            string_info[8]{
                size (86): 11
                name (87): "ENTER_FRAME"
            }
            string_info[9]{
                size (98): 16
                name (99): "addEventListener"
            }
            string_info[10]{
                size (115): 1
                name (116): "x"
            }
            string_info[11]{
                size (117): 6
                name (118): "Object"
            }
            string_info[12]{
                size (124): 15
                name (125): "EventDispatcher"
            }
            string_info[13]{
                size (140): 13
                name (141): "DisplayObject"
            }
            string_info[14]{
                size (154): 17
                name (155): "InteractiveObject"
            }
            string_info[15]{
                size (172): 22
                name (173): "DisplayObjectContainer"
            }
            string_info[16]{
                size (195): 6
                name (196): "Sprite"
            }
            namespace_count (202): 6
                namespace_info[0]{
                    kind: * (not included in abcFile)
                }
                namespace_info[1]{
                    kind (203): CONSTANT_PackageNamespace
                    name (204): 1
                }
                namespace_info[2]{
                    kind (205): CONSTANT_PackageNamespace
                    name (206): 3
                }
                namespace_info[3]{
                    kind (207): CONSTANT_PackageNamespace
                    name (208): 5
                }
                namespace_info[4]{
                    kind (209): CONSTANT_ProtectedNamespace
                    name (210): 4
                }
                namespace_info[5]{
                    kind (211): CONSTANT_PackageInternalNs
                    name (212): 3
                }
            ns_set_count (213): 0
                ns_set_info[0]{
                    ns: 0 (not included in abcFile)
                }
            multiname_count (214): 14
                multiname_info[0]{
                    kind: 0 (not included in abcFile)
                }
                multiname_info[1]{
                    kind (216): CONSTANT_QName
                    multiname_kind_QNAME{
                        ns (216): 1
                        name (217): 2 ("Event")
                    }
                }
                multiname_info[2]{
                    kind (219): CONSTANT_QName
                    multiname_kind_QNAME{
                        ns (219): 2
                        name (220): 4 ("RedCircle")
                    }
                }
                multiname_info[3]{
                    kind (222): CONSTANT_QName
                    multiname_kind_QNAME{
                        ns (222): 3
                        name (223): 6 ("MovieClip")
                    }
                }
                multiname_info[4]{
                    kind (225): CONSTANT_QName
                    multiname_kind_QNAME{
                        ns (225): 5
                        name (226): 7 ("moveit")
                    }
                }
                multiname_info[5]{
                    kind (228): CONSTANT_QName
                    multiname_kind_QNAME{
                        ns (228): 2
                        name (229): 8 ("ENTER_FRAME")
                    }
                }
                multiname_info[6]{
                    kind (231): CONSTANT_QName
                    multiname_kind_QNAME{
                        ns (231): 2
                        name (232): 9 ("addEventListener")
                    }
                }
                multiname_info[7]{
                    kind (234): CONSTANT_QName
                    multiname_kind_QNAME{
                        ns (234): 2
                        name (235): 10 ("x")
                    }
                }
                multiname_info[8]{
                    kind (237): CONSTANT_QName
                    multiname_kind_QNAME{
                        ns (237): 2
                        name (238): 11 ("Object")
                    }
                }
                multiname_info[9]{
                    kind (240): CONSTANT_QName
                    multiname_kind_QNAME{
                        ns (240): 1
                        name (241): 12 ("EventDispatcher")
                    }
                }
                multiname_info[10]{
                    kind (243): CONSTANT_QName
                    multiname_kind_QNAME{
                        ns (243): 3
                        name (244): 13 ("DisplayObject")
                    }
                }
                multiname_info[11]{
                    kind (246): CONSTANT_QName
                    multiname_kind_QNAME{
                        ns (246): 3
                        name (247): 14 ("InteractiveObject")
                    }
                }
                multiname_info[12]{
                    kind (249): CONSTANT_QName
                    multiname_kind_QNAME{
                        ns (249): 3
                        name (250): 15 ("DisplayObjectContainer")
                    }
                }
                multiname_info[13]{
                    kind (252): CONSTANT_QName
                    multiname_kind_QNAME{
                        ns (252): 3
                        name (253): 16 ("Sprite")
                    }
                }
        }
        method_count (254): 4
            method_info[0]{
                param_count (255): 0
                return_type (256): 0
                name (257): 0
                flags (258): 0
                    NEED_ARGUMENTS (0x01): false
                    NEED_ACTIVATION (0x02): false
                    NEED_REST (0x04): false
                    HAS_OPTIONAL (0x08): false
                    SET_DXNS (0x40): false
                    HAS_PARAM_NAMES (0x80): false
            }
            method_info[1]{
                param_count (259): 0
                return_type (260): 0
                name (261): 0
                flags (262): 0
                    NEED_ARGUMENTS (0x01): false
                    NEED_ACTIVATION (0x02): false
                    NEED_REST (0x04): false
                    HAS_OPTIONAL (0x08): false
                    SET_DXNS (0x40): false
                    HAS_PARAM_NAMES (0x80): false
            }
            method_info[2]{
                param_count (263): 1
                return_type (264): 0
                param_type[0] (265): 1
                name (266): 0
                flags (267): 0
                    NEED_ARGUMENTS (0x01): false
                    NEED_ACTIVATION (0x02): false
                    NEED_REST (0x04): false
                    HAS_OPTIONAL (0x08): false
                    SET_DXNS (0x40): false
                    HAS_PARAM_NAMES (0x80): false
            }
            method_info[3]{
                param_count (268): 0
                return_type (269): 0
                name (270): 0
                flags (271): 0
                    NEED_ARGUMENTS (0x01): false
                    NEED_ACTIVATION (0x02): false
                    NEED_REST (0x04): false
                    HAS_OPTIONAL (0x08): false
                    SET_DXNS (0x40): false
                    HAS_PARAM_NAMES (0x80): false
            }
        metadata_count (272): 0
        class_count (273): 1
            instance_info[0]{
                name (274): 2 (RedCircle)
                super_name (275): 3 (MovieClip)
                flags (276): 9
                    CONSTANT_ClassSealed (0x01): true
                    CONSTANT_ClassFinal (0x02): false
                    CONSTANT_ClassInterface (0x04): false
                    CONSTANT_ClassProtectedNs (0x08): true
                protectedNs (277): 4
                intrf_count (278): 0
                iinit (279): 1
                trait_count (280): 1
                    traits_info[0]{
                        name (281): 4 (moveit)
                        kind (282): Trait_Method
                        ATTR_Final (0x1): false
                        ATTR_Override (0x2): false
                        ATTR_Metadata (0x4): false
                        trait_method{
                            disp_id (283): 0
                            method (284): 2
                        }
                    }
            }
            class_info[0]{
                cinit (285): 0
                trait_count (286): 0
            }
        script_count (287): 1
            init (288): 3
                trait_count (289): 1
                    traits_info[0]{
                        name (290): 2 (RedCircle)
                        kind (291): Trait_Class
                        ATTR_Metadata (0x4): false
                        trait_class{
                            slot_id (292): 1
                            classi (293): 0
                        }
                    }
        method_body_count (294): 4
            method_body_info[0]{
                method (295): 0
                max_stack (296): 1
                local_count (297): 1
                init_scope_depth (298): 9
                max_scope_depth (299): 10
                code_length (300): 3
                    208 0xD0 (301) getlocal_0
                    48 0x30 (302) pushscope
                    71 0x47 (303) returnvoid
    
                exception_count (304): 0
                trait_count (305): 0
            }
            method_body_info[1]{
                method (306): 1
                max_stack (307): 3
                local_count (308): 1
                init_scope_depth (309): 10
                max_scope_depth (310): 11
                code_length (311): 17
                    208 0xD0 (312) getlocal_0
                    48 0x30 (313) pushscope
                    208 0xD0 (314) getlocal_0
                    73 0x49 (315) constructsuper
                        arg_count: 0
                    208 0xD0 (317) getlocal_0
                    96 0x60 (318) getlex
                        index: 1 (Event)
                    102 0x66 (320) getproperty
                        index: 5 (ENTER_FRAME)
                    208 0xD0 (322) getlocal_0
                    102 0x66 (323) getproperty
                        index: 4 (moveit)
                    79 0x4F (325) callpropvoid
                        index: 6 (addEventListener)
                        arg_count: 2
                    71 0x47 (328) returnvoid
    
                exception_count (329): 0
                trait_count (330): 0
            }
            method_body_info[2]{
                method (331): 2
                max_stack (332): 3
                local_count (333): 2
                init_scope_depth (334): 10
                max_scope_depth (335): 11
                code_length (336): 10
                    208 0xD0 (337) getlocal_0
                    48 0x30 (338) pushscope
                    208 0xD0 (339) getlocal_0
                    208 0xD0 (340) getlocal_0
                    102 0x66 (341) getproperty
                        index: 7
                    147 0x93 (343) decrement
                    97 0x61 (344) setproperty
                        index: 7
                    71 0x47 (346) returnvoid
    
                exception_count (347): 0
                trait_count (348): 0
            }
            method_body_info[3]{
                method (349): 3
                max_stack (350): 2
                local_count (351): 1
                init_scope_depth (352): 1
                max_scope_depth (353): 9
                code_length (354): 39
                    208 0xD0 (355) getlocal_0
                    48 0x30 (356) pushscope
                    101 0x65 (357) getscopeobject
                        index: 0
                    96 0x60 (359) getlex
                        index: 8
                    48 0x30 (361) pushscope
                    96 0x60 (362) getlex
                        index: 9
                    48 0x30 (364) pushscope
                    96 0x60 (365) getlex
                        index: 10
                    48 0x30 (367) pushscope
                    96 0x60 (368) getlex
                        index: 11
                    48 0x30 (370) pushscope
                    96 0x60 (371) getlex
                        index: 12
                    48 0x30 (373) pushscope
                    96 0x60 (374) getlex
                        index: 13
                    48 0x30 (376) pushscope
                    96 0x60 (377) getlex
                        index: 3
                    48 0x30 (379) pushscope
                    96 0x60 (380) getlex
                        index: 3
                    88 0x58 (382) newclass
                        index: 0
                    29 0x1D (384) popscope
                    29 0x1D (385) popscope
                    29 0x1D (386) popscope
                    29 0x1D (387) popscope
                    29 0x1D (388) popscope
                    29 0x1D (389) popscope
                    29 0x1D (390) popscope
                    104 0x68 (391) initproperty
                        index: 2
                    71 0x47 (393) returnvoid
    
                exception_count (394): 0
                trait_count (395): 0
            }
     }
    

    What you're interested in here is instance_info[0]. This is the definition of a run-time instance of a class, which would be RedCircle here. Instances have an array of Traits of various types. RedCircle has one trait (moveit) of kind Trait_Method which means the trait has a reference to a method (2).

    So if you skip to method_body_info[1] (the constructor of RedCircle) you can see at byte 323 that getProperty is called with an index of 4.

    102 0x66 (323) getproperty
                       index: 4 (moveit)
    

    Which is a reference to the multiname constant pool.

    multiname_info[4]{
        kind (225): CONSTANT_QName
        multiname_kind_QNAME{
            ns (225): 5
            name (226): 7 ("moveit")
        }
    }
    

    When it comes to calling the method it looks up the name index in the traits for the instance.

    traits_info[0]{
        name (281): 4 (moveit)
        kind (282): Trait_Method
        ATTR_Final (0x1): false
        ATTR_Override (0x2): false
        ATTR_Metadata (0x4): false
        trait_method{
            disp_id (283): 0
            method (284): 2
        }
    }
    

    Then calls the relevant method.

    method_info[2]{
        param_count (263): 1
        return_type (264): 0
        param_type[0] (265): 1
        name (266): 0
        flags (267): 0
            NEED_ARGUMENTS (0x01): false
            NEED_ACTIVATION (0x02): false
            NEED_REST (0x04): false
            HAS_OPTIONAL (0x08): false
            SET_DXNS (0x40): false
            HAS_PARAM_NAMES (0x80): false
    }
    
    method_body_info[2]{
        method (331): 2
        max_stack (332): 3
        local_count (333): 2
        init_scope_depth (334): 10
        max_scope_depth (335): 11
        code_length (336): 10
            208 0xD0 (337) getlocal_0
            48 0x30 (338) pushscope
            208 0xD0 (339) getlocal_0
            208 0xD0 (340) getlocal_0
            102 0x66 (341) getproperty
                               index: 7 (x)
            147 0x93 (343) decrement
            97 0x61 (344) setproperty
                               index: 7 (x)
            71 0x47 (346) returnvoid
        exception_count (347): 0
        trait_count (348): 0
    }
    

    A somewhat simplified answer, but I hope it clears up a few issues.