How are methods found in AVM2 bytecode?

2019-04-09 05:07发布

问题:

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.

回答1:

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.



回答2:

i'm exploring as3swf and as3abc libs exactly at this current moment) and getlex is mentioned twice in as3abc lib:

package com.codeazur.as3abc.factories
    public function create(code:int):AbstractOperation
        switch (code) {//in real life this switch block is really huge
             case Opcodes.GetLex:    return new MultinameOperation(code);
        }

and:

package com.codeazur.as3abc.data.bytecode
public class Opcodes

public static const GetLex:uint = 0x60;
_opNames[ GetLex ] = "GetLex";

so as i understand it's just a keyword inside AVM2. and more closely about your question: i think here:

getlex(flash.events.Event);
getproperty(ENTER_FRAME);
getlex(internal .moveit);

callpropvoid(addEventListener, 2);

we can see a kind of adding an event listener (or maybe i'm just crazy) and one more thing to mention: your former moveit function is the only one that accepts Event as a parameter so it's not hard to call exactly it. And by the way: how did you get inside AVM2? maybe the function is so internal that it's name is visible only if you are an entity in this class ;)?
and here's a link to some avm2 bytecode from adobe



回答3:

Turns out that the method names are multinames stored in the class traits; a problem compounded by the fact that I was looking for the trait names in the string table, not the multiname table. Oops.

Seems though, that the method name field is redundant in the ABC file.