[Linux] AoB/Bytecode hacking example (flare/flasm/scanmem)
-migrated-
Required tools: flasm, flare, scanmem, dhex
Required skills: Basic command line, locating the game swf
Useful reference: Super happy unofficial Flash 5 actionscript bytecode speccy fun

Let's work with a relatively simple game. http://www.kongregate.com/games/squidsquid/sproing

Download the swf (and make a copy so we don't have to redownload it if we mess something up)
Code: [Select]
# wget http://chat.kongregate.com/game_files/0000/2632/sproing_kong.swf -O orig.swf
# cp orig.swf a.swf
Get the actionscript (This creates orig.flr)
Code: [Select]
# flare orig.swfDecompress if it's compressed
Code: [Select]
# flasm -x a.swfDisassemble
Code: [Select]
# flasm -d a.swf > a.flmIt might return warnings, some of which we can ignore, but the one that comes up with the game I chose happens to break the reassembly.
Code: [Select]
> branch target not found: -32449So, let's find the line and fix it so it can recompile. Since we're not actually going to run it, it doesn't matter if this breaks the game logic. A quick search brings us
Code: [Select]
6515 | label196:
6516 |  branch -32449 // branch target not found
6517 | label197:
I'm just going to change this so it compiles
Code: [Select]
6515 | label196:
6516 |  branch label197
6517 | label197:
Bam. Reassemble, it'll be quite different than orig.swf, but that's okay, the parts we're modifying are the same in memory.
Code: [Select]
# flasm -a a.flm
> a.flm successfully assembled to a.swf, 682685 bytes
Create a copy, we'll be changing this and then comparing it to a.swf. Thus when we change b.flm, we'll be able to find the bytes that changed in b.swf
Code: [Select]
# cp a.swf b.swf
# flasm -d b.swf > b.flm
Okay, let's open up an editor or two. I'm using vim with a vertical split, orig.flr on one side, b.flm on the other. We're going to change the amount of health the health bonus circle restores, so let's search around for "health" in orig.flr. Eventually you'll find:
Code: [Select]
1280 | if (bonustype == 'health_bonus') {
1281 |  sHealthBonus.start(0, 1);
1282 |  health += 15;
1283 |  healthBar._width += 15;
Now we must find it in the assembly (b.flm). I'm going to search for health_bonus. After poking around, here's the relevant section. You can tell by context because "sHealthBonus.start" and "healthBar._width" are also in this same section
Code: [Select]
5245 | push 'health_bonus'
5246 | equals
5247 | not
5248 | branchIfTrue label162
5249 | push 1, 0.0, 2, 'sHealthBonus'
5250 | getVariable
5251 | push 'start'
5252 | callMethod
5253 | pop
5254 | push 'health', 'health'
5255 | getVariable
5256 | push 15
5257 | add
5258 | setVariable
5259 | push 'healthBar'
5260 | getVariable
5261 | push '_width', 'healthBar'
5262 | getVariable
5263 | push '_width'
5264 | getMember
5265 | push 15
5266 | add
5267 | setMember
The part we really care about is the addition of the 15, to health.
Code: [Select]
5254 | push 'health', 'health'
5255 | getVariable
5256 | push 15
5257 | add
5258 | setVariable
Flash is stack based,
I'm going to show how each step changes the stack. Let's assume we're at 55 health.
Code: [Select]
5254 | push 'health', 'health'
Stack: 'health', 'health'
health: 55

5255 | getVariable
Stack: 'health', 55
health: 55

5256 | push 15
Stack: 'health', 55, 15
health: 55

5257 | add
Stack: 'health', 70
health: 55

5258 | setVariable
Stack:
health: 70
So, it's simple enough to increase the amount it heals. I'm going to replace "push 15" with "push 50". Note, we can't add commands because that would change how everything lines up later in the program. We can remove commands by replacing them with 02 (no-op, nop), but we don't need to do any of that with this.

Save, and recompile.
Code: [Select]
# flasm -a b.flmNow we have a.swf and b.swf, which differ solely by the change of the 15 to 50

I use dhex, which is a diff hex editor, which makes it super easy to find the differences.
Code: [Select]
# dhex a.swf b.swfHitting 'n' brings us to the first (and only) difference. We see that 0x0f (15) changed to 0x32 (50).

The command starts at the 96 right before it.
Code: [Select]
| 96 05 00 07 0f 00 00 00Which has fields
Code: [Select]
| 96, 05 00, (07, 0f 00 00 00)
Code: [Select]
| push, length, (integer, integer_data)
It's little-endian, so all the multibyte fields are backward.

e.g.
Code: [Select]
| 0f 00 00 00 is 0x0000000f (15)

Back to the point, our array of bytes needs to get enough of the surroundings so that there aren't other
matches when we use scanmem. Let's grab some bytes from before and after the push command
Code: [Select]
| 08 3b 1c 96 05 00 07 0f 00 00 00 47 1d 96Change the 0x0f (15) to 0x32 (100)
Code: [Select]
| 08 3b 1c 96 05 00 07 32 00 00 00 47 1d 96And we're done! We have our bytes. Now to test it.

Start scanmem (following works if you only have one instance of flash running), change the scan data type to bytearray, and search for the original bytes
Code: [Select]
# scanmem $(pgrep -f flash)
# option scan_data_type bytearray
# 08 3b 1c 96 05 00 07 0f 00 00 00 47 1d 96
Success! Only one match. Now to get the memory address (yours will be different) and write the new value
Code: [Select]
# list
> [ 0]       0x7f43644be646, 08 3b 1c 96 05 00 07 0f 00 00 00 47 1d 96, [bytearray]
# write bytearray 0x7f43644be646 08 3b 1c 96 05 00 07 32 00 00 00 47 1d 96
Worst that will happen is you crash/freeze flash. It's easy enough to restart. I use `pkill -f flash` and then with a refresh of the page and it should reload. You'll have to restart scanmem too.

Anyway.

Success!

You might now notice that sometimes the health bar width doesn't line up with your actual health If you look up, you'll see that we only changed the health addition to 50, not the healthBar width. If you really care that much, I'm sure you can fix it yourself now

(broken image removed)