[Linux] AoB/Bytecode hacking example (flare/flasm/scanmem)
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)
# wget http://chat.kongregate.com/game_files/0000/2632/sproing_kong.swf -O orig.swfGet the actionscript (This creates orig.flr)
# cp orig.swf a.swf
# flare orig.swfDecompress if it's compressed
# flasm -x a.swfDisassemble
# 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.
> 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
6515 | label196:I'm just going to change this so it compiles
6516 | branch -32449 // branch target not found
6517 | label197:
6515 | label196:Bam. Reassemble, it'll be quite different than orig.swf, but that's okay, the parts we're modifying are the same in memory.
6516 | branch label197
6517 | label197:
# flasm -a a.flm 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
> a.flm successfully assembled to a.swf, 682685 bytes
# cp a.swf b.swfOkay, 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:
# flasm -d b.swf > b.flm
1280 | if (bonustype == 'health_bonus') {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
1281 | sHealthBonus.start(0, 1);
1282 | health += 15;
1283 | healthBar._width += 15;
5245 | push 'health_bonus'The part we really care about is the addition of the 15, to health.
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
5254 | push 'health', 'health'Flash is stack based,
5255 | getVariable
5256 | push 15
5257 | add
5258 | setVariable
I'm going to show how each step changes the stack. Let's assume we're at 55 health.
5254 | push 'health', 'health'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.
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
Save, and recompile.
# 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.
# 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.
| 96 05 00 07 0f 00 00 00Which has fields
| 96, 05 00, (07, 0f 00 00 00)
| push, length, (integer, integer_data)
It's little-endian, so all the multibyte fields are backward.
e.g.
| 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
| 08 3b 1c 96 05 00 07 0f 00 00 00 47 1d 96Change the 0x0f (15) to 0x32 (100)
| 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
# scanmem $(pgrep -f flash)Success! Only one match. Now to get the memory address (yours will be different) and write the new value
# option scan_data_type bytearray
# 08 3b 1c 96 05 00 07 0f 00 00 00 47 1d 96
# listWorst 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.
> [ 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
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)