Undocumented Bytecodes
I did some analysis how the bytecodes inDoABC
tag parsed, and compared the result against what I saw in the AVM2 documentation (May 2007). I found that Flash Player can parse certain bytecodes that are not mentioned in the documentation.Bytecode | Note | Bytecode | Note | Bytecode | Note | Bytecode | Note |
0x00 | RESERVED | 0x40 | newfunction | 0x80 | coerce | 0xc0 | increment_i |
0x01 | UNDOCUMENTED | 0x41 | call | 0x81 | UNDOCUMENTED | 0xc1 | decrement_i |
0x02 | nop | 0x42 | construct | 0x82 | coerce_a | 0xc2 | inclocal_i |
0x03 | throw | 0x43 | callmethod | 0x83 | UNDOCUMENTED | 0xc3 | declocal_i |
0x04 | getsuper | 0x44 | callstatic | 0x84 | UNDOCUMENTED | 0xc4 | negate_i |
0x05 | setsuper | 0x45 | callsuper | 0x85 | coerce_s | 0xc5 | add_i |
0x06 | dxns | 0x46 | callproperty | 0x86 | astype | 0xc6 | subtract_i |
0x07 | dxnslate | 0x47 | returnvoid | 0x87 | astypelate | 0xc7 | multiply_i |
0x08 | kill | 0x48 | returnvalue | 0x88 | UNDOCUMENTED | 0xc8 | RESERVED |
0x09 | label | 0x49 | constructsuper | 0x89 | UNDOCUMENTED | 0xc9 | RESERVED |
0x0a | RESERVED | 0x4a | constructprop | 0x8a | RESERVED | 0xca | RESERVED |
0x0b | RESERVED | 0x4b | RESERVED | 0x8b | RESERVED | 0xcb | RESERVED |
0x0c | ifnlt | 0x4c | callproplex | 0x8c | RESERVED | 0xcc | RESERVED |
0x0d | ifnle | 0x4d | RESERVED | 0x8d | RESERVED | 0xcd | RESERVED |
0x0e | ifngt | 0x4e | callsupervoid | 0x8e | RESERVED | 0xce | RESERVED |
0x0f | ifnge | 0x4f | callpropvoid | 0x8f | RESERVED | 0xcf | RESERVED |
0x10 | jump | 0x50 | UNDOCUMENTED | 0x90 | negate | 0xd0 | getlocal_0 |
0x11 | iftrue | 0x51 | UNDOCUMENTED | 0x91 | increment | 0xd1 | getlocal_1 |
0x12 | iffalse | 0x52 | UNDOCUMENTED | 0x92 | inclocal | 0xd2 | getlocal_2 |
0x13 | ifeq | 0x53 | UNDOCUMENTED | 0x93 | decrement | 0xd3 | getlocal_3 |
0x14 | ifne | 0x54 | RESERVED | 0x94 | declocal | 0xd4 | setlocal_0 |
0x15 | iflt | 0x55 | newobject | 0x95 | typeof | 0xd5 | setlocal_1 |
0x16 | ifle | 0x56 | newarray | 0x96 | not | 0xd6 | setlocal_2 |
0x17 | ifgt | 0x57 | newactivation | 0x97 | bitnot | 0xd7 | setlocal_3 |
0x18 | ifge | 0x58 | newclass | 0x98 | RESERVED | 0xd8 | RESERVED |
0x19 | ifstricteq | 0x59 | getdescendants | 0x99 | RESERVED | 0xd9 | RESERVED |
0x1a | ifstrictne | 0x5a | newcatch | 0x9a | RESERVED | 0xda | RESERVED |
0x1b | lookupswitch | 0x5b | RESERVED | 0x9b | RESERVED | 0xdb | RESERVED |
0x1c | pushwith | 0x5c | RESERVED | 0x9c | RESERVED | 0xdc | RESERVED |
0x1d | popscope | 0x5d | findpropstrict | 0x9d | RESERVED | 0xdd | RESERVED |
0x1e | nextname | 0x5e | findproperty | 0x9e | RESERVED | 0xde | RESERVED |
0x1f | hasnext | 0x5f | UNDOCUMENTED | 0x9f | RESERVED | 0xdf | RESERVED |
0x20 | pushnull | 0x60 | getlex | 0xa0 | add | 0xe0 | RESERVED |
0x21 | pushundefined | 0x61 | setproperty | 0xa1 | subtract | 0xe1 | RESERVED |
0x22 | RESERVED | 0x62 | getlocal | 0xa2 | multiply | 0xe2 | RESERVED |
0x23 | nextvalue | 0x63 | setlocal | 0xa3 | divide | 0xe3 | RESERVED |
0x24 | pushbyte | 0x64 | getglobalscope | 0xa4 | modulo | 0xe4 | RESERVED |
0x25 | pushshort | 0x65 | getscopeobject | 0xa5 | lshift | 0xe5 | RESERVED |
0x26 | pushtrue | 0x66 | getproperty | 0xa6 | rshift | 0xe6 | RESERVED |
0x27 | pushfalse | 0x67 | UNDOCUMENTED | 0xa7 | urshift | 0xe7 | RESERVED |
0x28 | pushnan | 0x68 | initproperty | 0xa8 | bitand | 0xe8 | RESERVED |
0x29 | pop | 0x69 | RESERVED | 0xa9 | bitor | 0xe9 | RESERVED |
0x2a | dup | 0x6a | deleteproperty | 0xaa | bitxor | 0xea | RESERVED |
0x2b | swap | 0x6b | RESERVED | 0xab | equals | 0xeb | RESERVED |
0x2c | pushstring | 0x6c | getslot | 0xac | strictequals | 0xec | RESERVED |
0x2d | pushint | 0x6d | setslot | 0xad | lessthan | 0xed | RESERVED |
0x2e | pushuint | 0x6e | getglobalslot | 0xae | lessequals | 0xee | RESERVED |
0x2f | pushdouble | 0x6f | setglobalslot | 0xaf | greaterequals | 0xef | debug |
0x30 | pushscope | 0x70 | convert_s | 0xb0 | UNDOCUMENTED | 0xf0 | debugline |
0x31 | pushnamespace | 0x71 | esc_xelem | 0xb1 | instanceof | 0xf1 | debugfile |
0x32 | hasnext2 | 0x72 | esc_xattr | 0xb2 | istype | 0xf2 | UNDOCUMENTED |
0x33 | RESERVED | 0x73 | convert_i | 0xb3 | istypelate | 0xf3 | RESERVED |
0x34 | RESERVED | 0x74 | convert_u | 0xb4 | in | 0xf4 | RESERVED |
0x35 | UNDOCUMENTED | 0x75 | convert_d | 0xb5 | RESERVED | 0xf5 | RESERVED |
0x36 | UNDOCUMENTED | 0x76 | convert_b | 0xb6 | RESERVED | 0xf6 | RESERVED |
0x37 | UNDOCUMENTED | 0x77 | convert_o | 0xb7 | RESERVED | 0xf7 | RESERVED |
0x38 | UNDOCUMENTED | 0x78 | checkfilter | 0xb8 | RESERVED | 0xf8 | RESERVED |
0x39 | UNDOCUMENTED | 0x79 | RESERVED | 0xb9 | RESERVED | 0xf9 | RESERVED |
0x3a | UNDOCUMENTED | 0x7a | RESERVED | 0xba | RESERVED | 0xfa | RESERVED |
0x3b | UNDOCUMENTED | 0x7b | RESERVED | 0xbb | RESERVED | 0xfb | RESERVED |
0x3c | UNDOCUMENTED | 0x7c | RESERVED | 0xbc | RESERVED | 0xfc | RESERVED |
0x3d | UNDOCUMENTED | 0x7d | RESERVED | 0xbd | RESERVED | 0xfd | RESERVED |
0x3e | UNDOCUMENTED | 0x7e | RESERVED | 0xbe | RESERVED | 0xfe | RESERVED |
0x3f | RESERVED | 0x7f | RESERVED | 0xbf | RESERVED | 0xff | RESERVED |
The loop and the big switch statement parsing
DoABC
bytecode is near 0x6087e9
. Instruction near 0x58f25d
also reads bytecode. The documentation certainly needs an update on Adobe's side so developers can add the currently undocumented bytecodes to their decompiler/disassembler.JIT
After adding new functionalities to my pintool I run it against Flash Player. Here is my observation.When executing a flash file containing
DoAction
tag in Flash Player no memory page allocated with or set to *EXECUTE*
flag. Thus no dynamically generated code was executed with the most common method. Therefore I think DoAction
works with interpreted execution. Meaning every single bytecode run on isolation rather than a set of bytes compiled&run (JIT).When executing a flash file containing
DoABC
tag in Flash Player I observed increased usage of VirtualAlloc
. The page was allocated with PAGE_READWRITE
flag. Later on the execution the page was set to PAGE_EXECUTE_READ
and the execution flow was transferred to the page. When the execution was returned to the caller the page was set back to PAGE_READWRITE
. I knew this was a part of how JIT works. Change of the memory protection flags is the mitigation for DEP.0x5205a6
is a VirtualProtect
call to change the memory protection flags. When it's called with PAGE_READWRITE
it's called via 0x5fc39c
. When it's called with PAGE_EXECUTE_READ
it's called via 0x5fc2e9
.During my experiment I figured out that instruction at
0x5d20ef
calls into the JIT-compiled code. Though this might not be the only address to call JIT-compiled code from. I observed many call backs in the JIT-compiled code. One of the callback might be to give continuous feedback to the caller for example if a long loop is being executed. I observed that constants are encrypted with xor
instructions to make memory spraying more difficult. This is not new but first time for me to see. This is how 0x41414141
looks like when it's encrypted.
03af1f67 b83a7c1959 mov eax,59197C3Ah
03af1f6c 357b3d5818 xor eax,18583D7Bh
All offsets in this post are RVAs, that is relative to Flash Player's image base. Offsets are appropriate in Flash Player 12.0.0.38 (flashplayer_12_sa.exe has a size of 10,339,208).