[Sticky] Bytecode tutorial
[Sticky] Bytecode tutorial Posted on: 04/30/2009 5:17pm
Quote Post
Quote from: "maximillian"


You will need:
Flasm: http://flasm.sourceforge.net/
sothink: http://isohunt.com/download/69837813/sothink.torrent
(Admin note: US Government shut this place down.)
notepad++: http://notepad-plus.sourceforge.net/uk/site.htm

Like I said follow ROUGHLY what I do, there are differences between games.



Sorry for stealing your post PK but I wanted this on the front page.
Quote from: "ActionScript 1 OpCodes"
No Operation (NOP) = 0x02,
ActionCall = 0x9E,
ActionDefineLocal = 0x3C,
ActionDefineLocal2 = 0x41,
ActionSetMember = 0x4F,
ActionAdd = 0x0A,
ActionAdd2 = 0x47,
ActionAnd = 0x10,
ActionAsciiToChar = 0x33,
ActionBitAnd = 0x60,
ActionBitLShift = 0x63,
ActionBitOR = 0x61,
ActionBitRShift = 0x64,
ActionBitURShift = 0x65,
ActionBitXor = 0x62,
ActionCharToAscii = 0x32,
ActionCloneSprite = 0x24,
ActionConstantPool = 0x88,
ActionDecrement = 0x51,
ActionDelete = 0x3A,
ActionDelete2 = 0x3B,
ActionDivide = 0x0D,
ActionEndDrag = 0x28,
ActionEnumerate = 0x46,
ActionEnumerate2 = 0x55,
ActionEquals = 0x0E,
ActionEquals2 = 0x49,
ActionGetMember = 0x4E,
ActionGetProperty = 0x22,
ActionGetTime = 0x34,
ActionGetVariable = 0x1C,
ActionGotoFrame = 0x81,
ActionGotoFrame2 = 0x9F,
ActionGoToLabel = 0x8C,
ActionGreater = 0x67,
ActionIncrement = 0x50,
ActionInitArray = 0x42,
ActionInitObject = 0x43,
ActionInstanceOf = 0x54,
ActionLess = 0x0F,
ActionLess2 = 0x48,
ActionMBAsciiToChar = 0x37,
ActionMBCharToAscii = 0x36,
ActionMBStringExtract = 0x35,
ActionMBStringLength = 0x31,
ActionModulo = 0x3F,
ActionMultiply = 0x0C,
ActionNextFrame = 0x04,
ActionNone = 0x0,
ActionNot = 0x12,
ActionOr = 0x11,
ActionPlay = 0x06,
ActionPop = 0x17,
ActionPreviousFrame = 0x05,
ActionPush = 0x96,
ActionPushDuplicate = 0x4C,
ActionRandomNumber = 0x30,
ActionRemoveSprite = 0x25,
ActionReturn = 0x3E,
ActionSetProperty = 0x23,
ActionSetTarget = 0x8B,
ActionSetTarget2 = 0x20,
ActionSetVariable = 0x1D,
ActionStackSwap = 0x4D,
ActionStartDrag = 0x27,
ActionStop = 0x07,
ActionStopSounds = 0x09,
ActionStoreRegister = 0x87,
ActionStrictEquals = 0x66,
ActionStringAdd = 0x21,
ActionStringEquals = 0x13,
ActionStringExtract = 0x15,
ActionStringGreater = 0x68,
ActionStringLength = 0x14,
ActionStringLess = 0x29,
ActionSubtract = 0x0B,
ActionTargetPath = 0x45,
ActionToggleQuality = 0x08,
ActionToInteger = 0x18,
ActionToNumber = 0x4A,
ActionToString = 0x4B,
ActionTrace = 0x26,
ActionTypeOf = 0x44,
ActionWaitForFrame = 0x8A,
ActionWaitForFrame2 = x8D,
ActionWith = 0x94,
ActionCallFunction = 0x3D,
ActionCallMethod = 0x52,
ActionNewMethod = 0x53,
ActionNewObject = 0x40,
ActionDefineFunction = 0x9B,
ActionDefineFunction2 = x8E,
ActionIf = 0x9D,
ActionJump = 0x99,
Quote from: "ActionScript 3 opcodes"
final int OP_bkpt = 0x01;
final int OP_nop = 0x02;
final int OP_throw = 0x03;
final int OP_getsuper = 0x04;
final int OP_setsuper = 0x05;
final int OP_dxns = 0x06;
final int OP_dxnslate = 0x07;
final int OP_kill = 0x08;
final int OP_label = 0x09;
final int OP_ifnlt = 0x0C;
final int OP_ifnle = 0x0D;
final int OP_ifngt = 0x0E;
final int OP_ifnge = 0x0F;
final int OP_jump = 0x10;
final int OP_iftrue = 0x11;
final int OP_iffalse = 0x12;
final int OP_ifeq = 0x13;
final int OP_ifne = 0x14;
final int OP_iflt = 0x15;
final int OP_ifle = 0x16;
final int OP_ifgt = 0x17;
final int OP_ifge = 0x18;
final int OP_ifstricteq = 0x19;
final int OP_ifstrictne = 0x1A;
final int OP_lookupswitch = 0x1B;
final int OP_pushwith = 0x1C;
final int OP_popscope = 0x1D;
final int OP_nextname = 0x1E;
final int OP_hasnext = 0x1F;
final int OP_pushnull = 0x20;
final int OP_pushundefined = 0x21;
final int OP_pushintant = 0x22;
final int OP_nextvalue = 0x23;
final int OP_pushbyte = 0x24;
final int OP_pushshort = 0x25;
final int OP_pushtrue = 0x26;
final int OP_pushfalse = 0x27;
final int OP_pushnan = 0x28;
final int OP_pop = 0x29;
final int OP_dup = 0x2A;
final int OP_swap = 0x2B;
final int OP_pushstring = 0x2C;
final int OP_pushint = 0x2D;
final int OP_pushuint = 0x2E;
final int OP_pushdouble = 0x2F;
final int OP_pushscope = 0x30;
final int OP_pushnamespace = 0x31;
final int OP_hasnext2 = 0x32;
final int OP_newfunction = 0x40;
final int OP_call = 0x41;
final int OP_construct = 0x42;
final int OP_callmethod = 0x43;
final int OP_callstatic = 0x44;
final int OP_callsuper = 0x45;
final int OP_callproperty = 0x46;
final int OP_returnvoid = 0x47;
final int OP_returnvalue = 0x48;
final int OP_constructsuper = 0x49;
final int OP_constructprop = 0x4A;
final int OP_callsuperid = 0x4B;
final int OP_callproplex = 0x4C;
final int OP_callinterface = 0x4D;
final int OP_callsupervoid = 0x4E;
final int OP_callpropvoid = 0x4F;
final int OP_newobject = 0x55;
final int OP_newarray = 0x56;
final int OP_newactivation = 0x57;
final int OP_newclass = 0x58;
final int OP_getdescendants = 0x59;
final int OP_newcatch = 0x5A;
final int OP_findpropstrict = 0x5D;
final int OP_findproperty = 0x5E;
final int OP_finddef = 0x5F;
final int OP_getlex = 0x60;
final int OP_setproperty = 0x61;
final int OP_getlocal = 0x62;
final int OP_setlocal = 0x63;
final int OP_getglobalscope = 0x64;
final int OP_getscopeobject = 0x65;
final int OP_getproperty = 0x66;
final int OP_getpropertylate = 0x67;
final int OP_initproperty = 0x68;
final int OP_setpropertylate = 0x69;
final int OP_deleteproperty = 0x6A;
final int OP_deletepropertylate = 0x6B;
final int OP_getslot = 0x6C;
final int OP_setslot = 0x6D;
final int OP_getglobalslot = 0x6E;
final int OP_setglobalslot = 0x6F;
final int OP_convert_s = 0x70;
final int OP_esc_xelem = 0x71;
final int OP_esc_xattr = 0x72;
final int OP_convert_i = 0x73;
final int OP_convert_u = 0x74;
final int OP_convert_d = 0x75;
final int OP_convert_b = 0x76;
final int OP_convert_o = 0x77;
final int OP_coerce = 0x80;
final int OP_coerce_b = 0x81;
final int OP_coerce_a = 0x82;
final int OP_coerce_i = 0x83;
final int OP_coerce_d = 0x84;
final int OP_coerce_s = 0x85;
final int OP_astype = 0x86;
final int OP_astypelate = 0x87;
final int OP_coerce_u = 0x88;
final int OP_coerce_o = 0x89;
final int OP_negate = 0x90;
final int OP_increment = 0x91;
final int OP_inclocal = 0x92;
final int OP_decrement = 0x93;
final int OP_declocal = 0x94;
final int OP_typeof = 0x95;
final int OP_not = 0x96;
final int OP_bitnot = 0x97;
final int OP_concat = 0x9A;
final int OP_add_d = 0x9B;
final int OP_add = 0xA0;
final int OP_subtract = 0xA1;
final int OP_multiply = 0xA2;
final int OP_divide = 0xA3;
final int OP_modulo = 0xA4;
final int OP_lshift = 0xA5;
final int OP_rshift = 0xA6;
final int OP_urshift = 0xA7;
final int OP_bitand = 0xA8;
final int OP_bitor = 0xA9;
final int OP_bitxor = 0xAA;
final int OP_equals = 0xAB;
final int OP_strictequals = 0xAC;
final int OP_lessthan = 0xAD;
final int OP_lessequals = 0xAE;
final int OP_greaterthan = 0xAF;
final int OP_greaterequals = 0xB0;
final int OP_instanceof = 0xB1;
final int OP_istype = 0xB2;
final int OP_istypelate = 0xB3;
final int OP_in = 0xB4;
final int OP_increment_i = 0xC0;
final int OP_decrement_i = 0xC1;
final int OP_inclocal_i = 0xC2;
final int OP_declocal_i = 0xC3;
final int OP_negate_i = 0xC4;
final int OP_add_i = 0xC5;
final int OP_subtract_i = 0xC6;
final int OP_multiply_i = 0xC7;
final int OP_getlocal0 = 0xD0;
final int OP_getlocal1 = 0xD1;
final int OP_getlocal2 = 0xD2;
final int OP_getlocal3 = 0xD3;
final int OP_setlocal0 = 0xD4;
final int OP_setlocal1 = 0xD5;
final int OP_setlocal2 = 0xD6;
final int OP_setlocal3 = 0xD7;
final int OP_debug = 0xEF;
final int OP_debugline = 0xF0;
final int OP_debugfile = 0xF1;
final int OP_bkptline = 0xF2;

I started to bytecode hack with CE myself I picked it pretty quickly it's very fun to create these kind of hacks its like editing assembly in native programs to create hacks like I used to in MMORPGS but one problem with editing bytecode in SWF.. my question is how do I NOP.. data in order to keep the size limit the same.

Okay so here is some original data for a example game I want to hack

Code: [Select]

//96 10 00 00 73 63 6f 72 65 00 06 00 00 00 00 00 00 00 00
_push "score" 0
//96 0b 00 00 61 6d 6d 6f 00 07 05 00 00 00
_push "ammo" 5

which means

Code: [Select]

score = 0;
ammo = 5;

of course this is the initializer for start of game.. so if I initialize a higher ammo or score.. I'll hack the game

now the values are 4 bytes aka DWORD but it's saved in reverse order small endian or idk..

but i know in C++ programming its easy to replicate using Lo/Hi's

So changing ammo is easy I just modify these

Code: [Select]

05 00 00 00
now what about score? when I try to edit score.. it changes assembly completely.

Code: [Select]

//96 0c 00 00 73 63 6f 72 65 00 07 [a3 07 64 00]
_push "score" 6555555
//96 0b 00 00 61 6d 6d 6f 00 07 05 00 00 00
_push "ammo" 5

I just changed it to

Code: [Select]

score = 6555555;
ammo = 5;

of course I still managed to change the score.. but I changed the algorithm that generates the score like changing addition to multiplication making my score increase much more rapidly(easy bytecode hacks).

But I want to modify the score to anything I want but you see the assembly is different.. so what must I do?

so i'm changing
This ->
96 10 00 00 73 63 6f 72 65 00 06 00 00 00 00 00 00 00 00
To This ->
96 0c 00 00 73 63 6f 72 65 00 07 a3 07 64 00 1d

I have to nop 5 bytes in order for flash not to mess up the data by considering the extra 00 00 00 00 as another opcode.. and screwing up the flow.

Re: ZargWolf can you teach me? Posted on: 04/30/2009 5:33pm
Quote Post
I think nop is 02.
Re: ZargWolf can you teach me? Posted on: 04/30/2009 5:43pm
Quote Post
AHH.. thanks KongregateHack!

96 10 00 00 73 63 6f 72 65 00 06 00 00 00 00 00 00 00 00
To This ->
96 0c 00 00 73 63 6f 72 65 00 07 a3 07 64 00 1d 02 02 02

Nevermind it worked perfectly!! now i got a score changer for some random game!

Time to program a cheat engine in C++ a very small one for my trainer which finds bytes in memory based on process like iexplore.exe etc.. and changes the score to a value you want
If anyone else would like to dive into this more here are some helpful links I've compiled over time.

Adobe's SWF Specifications
You can download the newest spec PDF which lists all the AVM0 bytecode information.

SWF File Reference
This can sometimes be easier to read than the above PDF for SWF specific information.

AVM2 ByteCode Insturctions (for AS3 games)
A good quick reference for AVM2. I wish something like this existed for AVM0. AVM2 is much easier to read, but the use of the constant pool limits what you can do. AVM0 is much more flexible when hacking.

AVM2 Constant Pool Viewer
This is a python script I wrote to view constant pool data in AVM2 SWF files. This can make it a lot easier when trying to manipulate bytecode in AS3 games. You can run this in Windows by downloading Cygwin, and installing Python. I messed up on first attempt at programing a zlib decompressor in python for SWF files, so you need to decompress the SWF files with something like SWFTools first until I have time to update it.

AMV2 u30 Converter
Web: http://bluecork.net/cheats/short.php
Script: http://bluecork.net/cheats/scripts/short.py
This is a script I wrote to convert u30 hex to an integer, and vice-versa. This is needed for the AMV2 pushshort (0x25) command.
Re: [Sticky] Bytecode tutorial Posted on: 05/05/2009 8:26pm
Quote Post
The only time I use wildcards is when the bytecode could potentially change, or you need to find multiple locations that are slighty different. This is usually because of references to the constant pool, but can also be because of if statement offsets, and different values for the same variable. In AVM0 the cpool is just strings, while in AVM2 (AS3) the cpool has been expanded to use strings, ints, uints, doubles, and other stuff not that important for hacking.

Here are some real world examples of what I am talking about.

AVM0 Cpool example for the game Bunny Invasion II. You start out with 1000 money.

Real code:
Code: [Select]
money = 1000;
Peusdo code and Bytecode: (from Sothink)
Code: [Select]
//96 07 00 08 07 07 e8 03 00 00
_push "money" 1000

We can break the push down into its segments.

96 = push
07 00 = Size (7 bytes)
08 = Constant Pool 8 (the next byte is the location in the cpool)
07 = Cpool location
07 = 4-byte Integer (the next four bytes are the value)
e8 03 00 00 = 1000

If we look at the first seven objects in the cpool we see this.

Code: [Select]
//88 = Set Constant Pool
//c4 05 9b = Length/Objects
//00 77 61 74 63 68 69 6e 67 76 69 64 = watchingvid
//00 63 6f 6d 70 6c 65 74 65 64 67 61 6d 65 = completedgame
//00 76 6f 6c 75 6d 65 73 65 74 = volumeset
//00 74 69 6d 65 72 = timer
//00 61 64 64 65 64 74 69 6d 65 = addedtime
//00 73 74 61 72 74 54 69 6d 65 = startTime
//00 77 65 61 70 6e 75 6d 62 65 72 = weapnumber
//00 6d 6f 6e 65 79 = money
_constantPool "watchingvid" "completedgame" "volumeset" "timer" "addedtime" "startTime" "weapnumber" "money"

Notice that money is located in the seventh spot, which coorelates to the '08 07' in the push statement. The reason why this is important is because if you wanted to replace money=1000 with money=100000 you would normally do this.

96 07 00 08 07 07 e8 03 00 00 -> 96 07 00 08 07 07 a0 86 01 00

However if the game was updated, and another variable was added above money=1000, then suddenly money is located in the eight spot in the constant pool instead of seventh. So you would have to change what you're looking for to '96 07 00 08 08 07 e8 03 00 00'. Since we already know that the '08 07' is a reference to the constant pool we can just make the 07 a wildcard, and search for '96 07 00 08 ?? 07 e8 03 00 00'. That way if the game ever gets updated this search will continue to work.

The only time we would have to significantly change our search is if the author added enough variables to break the 255 size limit. Then the push would change from using the 08 Cpool 8 command to the 09 Cpool 16 command, which uses two bytes to reference the cpool location. In that case our new search would look like this '96 08 00 09 ?? ?? 07 e8 03 00 00'.

This is much more important in AVM2 where every other byte can be a reference to the cpool. For example lets look at Hero's Arms. Let's change the code so that when we pick up a 3 gold coin it is worth much more.

Real code:
Code: [Select]
if (param1 is Coin3)

Peusdo code and Bytecode: (this is only for the line 'metaHero.AddGold(3);')
Code: [Select]
//60 81 02
_as3_getlex metaHero
//24 03
_as3_pushbyte 3
//4f c9 05 01
_as3_callpropvoid AddGold(param count:1)

So lets break this all down.

60 = Get a namespace from the cpool
81 02 = location of object in cpool
24 = Push byte onto stack
03 = 3
4f = Call a function with given namespace referenced in the cpool
c9 05 = location of object in cpool
01 = Number of parameters on the stack to send to this function

This all makes little sense unless we can read the cpool. Lucky for us Sothink will do that for us, which is why we can see the names of the objects in the peusdo code. Since we know which bytes are cpool locations we can replace those bytes with wildcards to come up with a search of '60 ?? ?? 24 03 4f ?? ?? 01'.

Here is where we now run into a limitation with AVM2 hacking. The command '24' only pushes a single singed byte, which has a limited value of 127 (which would be 7F). So we can change the '24 03' to '24 7F', and any coin we pick up with the value of 3 will be worth 127. However the good stuff all costs 5000 gold, and even getting 127 for each coin would still take us too much grinding to get to 5000. So we now have to dip into the cpool to reference a value larger than 127. Instead of using the '24' command we should instead use the '2d' command which pushes a 4 byte int from the cpool. However we have to find out what all the values are in the cpool to know which one to use. Using the handy python script I pasted in an early post we can decode the cpool and see the values for all the ints. I will only paste in the first 16 to save space.

Code: [Select]
int index count = 804
001 -1 002 0 003 2 004 16
005 10 006 1 007 100 008 1800
009 4 00a 1700 00b 1210 00c 20
00d 460 00e 660 00f 860 010 1150

Okay, so we want to make a coin worth 1800 instead of 3. To do this we would change the '24 03' to '2d 08'. In this case the 08 following the 2d is the location of the int in the cpool, so '2d 0b' would make the coins worth 1210.

We can expand our search with another wildcard. Instead of searching for '24 03' we can search for '24 ??' to include all instances of where coins are being added, and not just when a value of 3 is added. This will let us find the bytecode for adding coin values of 10 and 20 as well. The downside of this is that we are now finding 65 results when we only want 3. The reason for this is because we are using so many wildcards that we are finding cases of code unrelated to coin adding. To get around this you can either stop using some wildcards, or expand your searching. The bytecode we are searching for is only a single line of code. If we expand it include the next line that plays a sound we can further refine our search.

Code: [Select]

//60 a5 01
_as3_getlex com.lachhhEngine.games.sfx::Jukebox
//66 c6 2b
_as3_getproperty instance
//60 1b
_as3_getlex com.herosarm.factories::HA_SfxFactory
//66 c6 2b
_as3_getproperty instance
//66 97 03
_as3_getproperty ID_SFX_CASH

The commands 60 and 66 are just reference more cpool objects, and can be wildcarded as well. If we add '60 ?? ??' to the end of our search we get 37 results. Adding '66 ?? ??' gets us down to 8. Adding any more doesn't refine our search any so we can now try adding the code that comes before.

Code: [Select]
if (param1 is Coin3)

//60 aa 15
_as3_getlex com.herosarm.items.dropitems::Coin3
//12 1f 00 00
_as3_iffalse offset: 31

This will probably be more sucessful at refining our search because there are less cpool references. This translates out to '60 ?? ?? b3 12 ?? ?? 00'. Adding that before our search reduces our results to three. The full search is now '60 ?? ?? b3 12 ?? ?? 00 60 ?? ?? 24 ?? 4f ?? ?? 01 60 ?? ??'. You can add all the results, and change the '24 ??' to '2d 08'. Now all 3, 10, and 20 valued coins will be worth 1800. It would be easier to just not use wildcards, but using them allows you to keep using the same search if the game gets updated.

Instead of using wildcards to make crazy searches you can also use them for just minor tweaks. For example these are the bytecodes for adding 3, 10, and 20 coins.

60 AA 15 B3 12 1F 00 00 60 81 02 24 03 4F C9 05 01 60 A5 01 = 3
60 A7 15 B3 12 1F 00 00 60 81 02 24 0A 4F C9 05 01 60 A5 01 = 10
60 A3 15 B3 12 1F 00 00 60 81 02 24 14 4F C9 05 01 60 A5 01 = 20

You'll notice looking at all of these that there are only a couple bytes different. Just replace those instances where the byte is different with a wildcard to find all three at once.

Another problem you may have noticed. Lets say that we don't want our coins to be worth 1800. We want our coins to be worth 5000, but there are no ints with a value of 5000. This is another limitation of AVM2 which is very difficult to get around. One thing you can do is change the value of one of the int objects in the cpool to 5000, and reference it. The problem with doing that is that whatever variable was referencing the value you changed to 5000 will now also be worth 5000, and that can have drastic negitive effects on the game. Every int is also unique, and can be referenced by multiple variables. For example a game has two variables: 'distance=2000' and 'money=2000'. You change the value of the 2000 int to 5000 to start off with more money. The unintended consequence is that distance will now also be set to 5000, and who knows what that will do to the game. Its best just to use an existing cpool object and deal with the fact that it may not be exactly what you want.

If you can subtract bytes from somewhere else in the game you can also use pushshort (0x25) instead of pushbyte (0x24) and pushint (0x2D). Pushshort lets you push an unsigned 30 bit value instead of a signed 8 bit value. So where pushbyte was limited to 24 7F for a max value of 127, pushshort can do 25 FF FF FF FF 03 for a max value of 1073741823.

The problem with pushshort is that its using Flash's special u30 variable length 30 bit integer. This makes it difficult to properly calculate what you need to use to get the right value without a good understanding of bit calculation.
Re: [Sticky] Bytecode tutorial Posted on: 05/06/2009 10:56pm
Quote Post
I wanted to make some comments on the things pkedpker has said now that I've actually had time to read them. He did a really good job opening this discussion up.

Quote from: "pkedpker"
so i'm changing
This ->
96 10 00 00 73 63 6f 72 65 00 06 00 00 00 00 00 00 00 00
To This ->
96 0c 00 00 73 63 6f 72 65 00 07 a3 07 64 00 1d
in reality they are both the same exact thing but why so different at
06 and 07.. again use your logical thinking maybe 06 means just set it to 0.
and 07 means give it a value from greater then 0 to make value of 4 bytes aka (-1)

This is a case where the SWF spec PDF mentioned in an earlier post comes in very handy. You can break down the push into its parts to better figure out what it is doing and how to alter it.

96 = Push
10 00 = Length of 16 bytes
00 = String follows
73 63 6f 72 65 = 'score'
00 = End of string
06 = Double (the next eight bytes are the value)
00 00 00 00 00 00 00 00 = 0

What you did is change this from a double to a 4 byte integer by changing the 06 to a 07. It would have been easier to just add a new double value in there. Using your example of 6555555 the new bytecode would be this.

96 10 00 00 73 63 6f 72 65 00 06 e8 01 59 41 00 00 00 c0

There are more benefits to this than just making it easier to edit. By changing score from a double to an integer you could potentially break the game if it tries to add points to your score that include a decimal. The game will figure out its trying to add a double into an integer and stop working because of a data type mismatch. The reason why score is a double is probably just to raise the max value, and has nothing to do with decimals. You likely wouldn't run into a problem until the game tried to increase the score past the 4 byte limit of 4,294,967,295.

Quote from: "pkedpker"
//1d = _setVariable

what that means could be logically guessed to either the ; semicolon symbol or complete the process..
like I set the score to the value 0.. but its floating around.. but when I send the 0x1d opcode it actually makes it visible/sets it

This has more to do with assembly and memory assignment. The 0x96 push command pushes (where it gets its name) the objects in its statement onto the stack. The 0x1d command will then assign whatever is on the stack.

0x96 -> push the string 'score' and the double '0' onto the stack
0x1d -> take the first object on the stack, and assign it as a variable for the second object on the stack


Quote from: "SWF Spec"
ActionSetVariable does the following:
1. Pops the value off the stack.
2. Pops the name off the stack, a string which names the variable to set.
3. Sets the variable name in the current execution context to value.

For example the push command can be used to push more than two objects onto the stack. You could do this.

96 14 00 07 01 00 00 00 07 02 00 00 00 07 03 00 00 00 07 04 00 00 00

That will push the four integers 1, 2, 3, and 4 onto the stack. It is then the responsibity of other commands to pop those values off the stack to do something with them.

AVM2 does away with a push command, and each command will do a push, pop, or whatever on its own. The same command for score=0 as a double in AVM2 would be this.

5e 01 2f 01 68 01

5e = Find property
01 = Location of string in the constant pool (assuming 'score' is located at the first spot)
2f = Push double onto stack
01 = Location of double in the constant pool (assuming '0' is located at the first spot)
68 = Initalize propety
01 = Location of string in the constant pool (this is identical to the 5e 01 location)

This is why being able to read the constant pool is so important in AVM2. They've reduced the size of the code to make a flash game take less space and memory, and made it harder to read and alter.

For this reason I actually think its easier to use bytecode hacking to change the way the game works, and not to change what certain values are set to. You can easily change values with CE, and don't have to resort to bytecode hacking. The use of encrypted secure variables is causing normal CE hacks to no longer work though. In those cases it is possible to reprogram the game to bypass security checks so that your normal CE hacks start working again.
Re: [Sticky] Bytecode tutorial Posted on: 11/21/2009 12:45am
Quote Post
I noticed that there are "public functions" and "protected functions." What's the difference? Is it not possible to change the code in protected functions?

Also, how do I code for an integer? Or more specifically (if it's any difference), how do I return an integer?
Or better yet, is there a guide somewhere for pushing all types of numbers such as integers, double, and floats?
Re: [Sticky] Bytecode tutorial Posted on: 11/21/2009 11:04am
Quote Post
Quote from: "pythag12"
I noticed that there are "public functions" and "protected functions." What's the difference? Is it not possible to change the code in protected functions?

This has to do with function "visibility" between objects, in object-oriented programming.
Look there: http://www.javacoffeebreak.com/faq/faq0002.html
It works almost the same way in all object-oriented languages. And you can modify the bytecode of these protected functions.

Quote from: "pythag12"
Also, how do I code for an integer? Or more specifically (if it's any difference), how do I return an integer?
In AVM2, you can use the pushbyte opcode (24), which allows signed integers from 0 to 127, then the returnvalue opcode (48).
24 64 48 : pushbyte 100, return value

If you need more than 127 and have enough room, you can do something like this, said you need to return 20.000:
24 64 24 64 c7 24 02 c7 48 (pushbyte 100, pushbyte 100, multiply_i, pushbyte 2, multiply_i, returnvalue)

use this instructions reference: http://www.anotherbigidea.com/javaswf/a ... tions.html

You can also use the constant pool and the pushint (2d) opcode. Locate in the code a pushint with the value you're looking for, and copy/paste it, or use ignored constant pool reader.

There's also a pushshort instruction that uses weird 30 bits integers, that are encoded with a variable number of bytes to save place. Have a look at the U30 definition (begining at line 006) here: http://www.m2osw.com/abc_format.html

Quote from: "pythag12"
Or better yet, is there a guide somewhere for pushing all types of numbers such as integers, double, and floats?

For doubles, in AVM2 I think you will need to use the constant pool and the pushdouble (2f) instruction, and in AVM0, there's a more flexible "_push" (96) statement that allows to push IEEE 754 floating point numbers without using the constant pool, like this:
//96 09 00 06 e1 7a 94 3f 7b 14 ae 47
_push 0.02

"e1 7a 94 3f 7b 14 ae 47" means 0.02
you can use this link for converstions between decimal floating point and IEEE 754 representations: http://babbage.cs.qc.cuny.edu/IEEE-754/Decimal.html

Links tells me 0.02 is 3F 94 7A E1 47 AE 14 7B in hex. Then I need to reverse the bytes by groups of four, because of the little endian byte order, which results in E1 7A 94 3F 7B 14 AE 47. Same stuff as the above _push statement, see ?

There's also this convert_d (75) AVM2 instruction that may allow to push an int on the stack, then convert it to a double. Like 24 0e 75 (pushbyte 15, convert_d) should give you the floating point number 15.0 on the stack. I haven't tested this, however.

AFAIK, there's no float data type, use double.

Also a double is 64 bits data. So pushing a double on the stack will use 2 stack segments, where an integer uses only one stack segment. So if your function returns an integer, and you push a double before the return, this will mess the stack because the return instruction will only pop the first 32 bits of your 64 bits double, and the program will crash.
So make sure to push doubles before calls that will actually need to pop a double from the stack, like before Math.round() or Math.ceil() calls, or wherever the original program uses doubles.

About a guide, I think you can find howto's on this forum, but maybe it would be useful to make a summary post about that. Ignored (and other people I'm forgetting here, sorry) made some really good posts earlier on this thread.
Re: [Sticky] Bytecode tutorial Posted on: 11/22/2009 11:28am
Quote Post
here's how to use pushshort (read easy guide at end of post for the quick method):

let's take one instruction from tookie as example:
//25 d0 0f
_as3_pushshort 2000

and a reminder on how do U30 integers work:
U30 - This is a 30 bit integer value encoded with a variable number of bytes to save space.
All U30's are encoded as 1-5 bytes depending on the value (larger values need more space).
The encoding method is if the hi bit in the current byte is set, then the next byte is also
part of the value. Each bit in a byte contributes 7 bits to the value, with the hi bit telling
us whether to use the next byte, or if this is the last byte for the value. This enables us to
use 30 bit numbers for everything, but still not take up enormous amounts of space.

now let's see how it works with your instruction pushshort 2000, I will use this http://www.mathsisfun.com/binary-decima ... erter.html hex-dec-binary converter

25 is pushshort opcode, so d0 0f means 2000
Code: [Select]
d0 0f
11010000 00001111
Now, the U30 definition says: "Each bit in a byte contributes 7 bits to the value, with the hi bit telling
us whether to use the next byte, or if this is the last byte for the value."

So I remove each first bit of my two 8-bit blocks:
(1)1010000 (0)0001111

Then, I reverse them because of the little-endian byte order, and convert them back to decimal
0001111 1010000
= 2000 I find back the 2000 of pushshort 2000 instruction.

Now, we don't want to replace this with an integer that will take more than 2 bytes to represent, as these U30 integers are variable length.
So, let's see what is the biggest value I can push with pushshort and 2 encoding bytes for my U30.

This will be: 1 everywhere, except the first bit of the second byte, because remember, the hi-bit only tells us "whether to use the next byte, or if this is the last byte for the value." So, the first bit of my second byte has to be 0, because it's the last byte for the value:
11111111 01111111
that translates to:

which is in decimal:
11111111 01111111 (FF 7F)
1111111 1111111 (removed first bits)
1111111 1111111 (inverted for little-endian byte order, yeah it looks same)
16383 (decimal)

So maximum pushshort with only 2 bytes for the U30 integer is
//25 ff 7f
_as3_pushshort 16383

easy guide to pushshort instruction:

Use a converter like this: http://www.mathsisfun.com/binary-decima ... erter.html
I want to pushshort 32571:

  • convert it to binary:
  • cut it down into 7-bits groups:
    1 1111110 0111011
  • if necessary, add padding bits to the first byte so it has 7 bits like the others:
    0000001 1111110 0111011
  • reverse your 7-bits groups:
    0111011 1111110 0000001
  • add a one-bit before each byte, and a zero-bit before the last one:
    10111011 11111110 00000001
  • convert the result to hexadecimal:
    BB FE 01
  • final pushshort instruction is:
    25 BB FE 01

Integer range reminders according to number of encoding U30 bytes.
Knowing the value you want to push, this tells you how many bytes you will need to encode your integer.
1 byte: 0 -> 127
2 bytes: 128 -> 16.383
3 bytes: 16.384 -> 2.097.151
4 bytes: 2.097.152 -> 268.435.455
5 bytes: 268.435.456 -> 1.073.741.823
Re: [Sticky] Bytecode tutorial Posted on: 02/09/2010 2:18am
Quote Post
I finally got around to converting my cpool dumper python script into a generic u30 converter, and turned it into a PHP/Python web page that anyone can use. If you notice something wrong with it send me a message, as I don't follow this thread much.


This is useful if you want to do something like modifying the following code to divide your time by 2000 instead of 1000. You would just change the 25 e8 07 to 25 d0 0f. You'll note that normally the hex for 1000 and 2000 are e8 03 and d0 07 which is why a new converter is needed for these pushshort functions.

Code: [Select]
//46 99 06 00
_as3_callproperty http://adobe.com/AS3/2006/builtin::getTime(param count:0)
//25 e8 07
_as3_pushshort 1000
Re: [Sticky] Bytecode tutorial Posted on: 06/16/2010 3:01am
Quote Post
[hr:1x22rnds][/hr:1x22rnds]Changing strings in a swf's constant pool[hr:1x22rnds][/hr:1x22rnds]

Maybe I should post this in the "introduction to bytecode hacking" thread. :O Click the numbers at the left for images.

1. I used this download link for the program. You need java to be able to open the .jar file.
2. In the program, open a swf file. I chose Colony (it needs to be downloaded to your computer).
3. Click "read tags" and "read doABC".
4. At the top, go to Edit > doABC > cpool_info.
5. Click "names" in the window that pops up
6. Click "string_info", choose your string to modify, type the new string in the textbox and click "replace".
7. Save the modified file.

Perhaps a major problem here is that it's sort of difficult to find the desired string in the really long list. Maybe an option is not to find the old string and replace it, instead add a new one to the constant pool, but after it's added I'm not sure how to push that new string lol. Maybe someone could help here?
Re: [Sticky] Bytecode tutorial Posted on: 11/13/2010 12:43am
Quote Post
I was looking for an AS3 disassembler and ended up stumbling up on this AVM2 Disassembler. I think it might be useful in association with AS3 bytecode hacking, but I haven't tested it yet (it's downloaded though (broken image removed)).

P.S: It's not only a disassembler. All the features are written on the very same page I provided.

P.S.S: It can only disassemble AS3, which covers the void space that flasm doesn't cover (broken image removed)
Re: [Sticky] Bytecode tutorial Posted on: 04/22/2011 8:35pm
Quote Post
How do I look at the constant pool in Sothink?
Re: [Sticky] Bytecode tutorial Posted on: 04/22/2011 9:23pm
Quote Post
In AS2, the constant pool is presented to you in the Bytecode. You'll know it when you see it because...the P-code has the words constant pool in it. And it's gigantic.
In AS3, SoThink isn't enough.
Re: [Sticky] Bytecode tutorial Posted on: 04/23/2011 2:55am
Quote Post
What about AS3?