Showing posts with label AVM2. Show all posts
Showing posts with label AVM2. Show all posts

Sunday, April 6, 2014

Things and Stuff

What on earth have I been doing for the last 6 months?
That's a pretty damn decent question.

Here's a rough overview:
  - I wrote an AS3-based SWF/AVM2 dis/reassembler that's blazingly fast.
  - I used that reassembler to make a fixed loader and a thing that makes all variables public, and it runs a lot faster and is more stable than the one or two public open source ones.
  - I wrote some fancy new SWF encryption/obfuscation technologies. They're not quite finished. But I found out how to beat all current decrypters, including memory dumpers for the foreseeable future ;)
  - I discovered a new, not-yet-documented hacking method for injecting code into SWFs, think class overriding but without inheritance problems
  - I wrote a magical, fully working program that once installed could potentially allow users access hacks for any flash game in-place on any site with the press of a single button. Need testers/hackers.
  - At some point I wrote an aimbot for an AS3 game using a mixture of bytecode modification, Loader-based variable modification and class overriding. Just thought I'd put it out there for the record. It can definitely be done, and with some of these new techniques I've found, it's not actually that difficult to make hacks with that kind of complexity.

Now, I really want to share these new, beautiful things around, but they're not quite ready for public release. I need some people to do some private testing.

So, if anyone is interested in learning some new things, seeing some awesome stuff and helping a brother out, flick me a message or your email/msn/skype/aim or something.

There's a lot of really awesome, mind-blowing flash hacking tools I want to create. I've been working on what I believe is the ultimate flash hacking platform. It's more awesome then you could possibly know, but I can't really put much more time into it unless I start getting support from people.

Thursday, August 29, 2013

Basic SWF Bytecode modification (AVM2/AS3) tutorial

I decided I would write up a basic SWF modification tutorial, and explain the basics of SWF modification, AoBs, ect.
I'm gonna go into some serious detail in this tutorial, so be prepared.

Before you start doing any extreme SWF hacks, you're gonna need a pretty good understanding of how SWFs work. Luckily, there are a lot of tools out there nowadays that allow you to take apart a SWF and see it's juicy innards.

There are some things you will need to know

A SWF file always starts with a SWF header, containing compression information, SWF filesize, frame count, frame rate and stage size.
You don't need to know too much about the SWF header, but it is useful to note that all uncompressed SWF files start with "FWS" and all compressed SWF files start with "CWS".
Another thing to note is if the file size in the SWF header is wrong the SWF will most likely not load.
This limitation stops you from being able to increase a SWFs size in memory after it is loaded (however, even if it didn't, forcibly increasing a SWFs size in memory could create a whole lot of problems).
It also means if you increase or decrease the SWFs size when hex editing and don't update the SWF size in the SWF header, the SWF will almost certainly not load.

A SWF is made up of many AVM2 tags. Tags can contain many different things, such as images, shapes, data, actionscript bytecode. If you want to have a look at the tags in a SWF, the tool SWFWire Inspector will disassemble AVM2 tags. Most of the time however, the only tags you will need to care about are the ABC (Actionscript Bytecode) tags.

An ABC tag contains AVM2 ABC bytecode  (sometimes just referred to as AVM2 bytecode or ABC bytecode) which contains class definitions, methods, variables, basically all the compiled code.
There can be more than one ABC tag in a SWF. This generally happens if a SWF uses a code library. The code library is often held in a separate ABC tag from the SWFs main code.

ABC code is stack based. You should be familiar with how stacks work in programming. It's nothing particularly difficult. There's a stack. and ABC instructions access it. That's about all there is to it.

Now to the actual tutorial part.


Part 1: Reverse engineering

I'm going to cover SWF bytecode modification using an AVM2 ABC disassembler, hex editor and decompiler. There are other "better" ways of modifying SWFs, but learning how to hex-edit SWFs is pretty much SWF hacking 101. Everything else follows on from it. Thus, even though there are better ways of doing this, it is an extremely good idea to learn this method before learning the others.

In this tutorial, we'll do a score hack. There's many different ways we could do this. we could increase the score gained from picking up a money item, we could increase the starting score, we could teleport the money object to the player or any number of other ways.

I'll try and cover several different approaches to several different types of score hacks. There are many correct ways of approaching this problem.

For those of you who don't know, unhackable is the name of a simple game engine I use for tutorials, examples and proof-of-concepts. The name "unhackable" comes from the fact I originally made it to test AS2/3 anti-hack code. It's basically the simplest game you could make. I've written about 5 different versions of the unhackable engine, this tutorial will be for the unprotected OOP version of unhackable, which you can find here.

Open up the unhackable SWF in FFDec or another similar decompiler.

We'll start with a little reverse-engineering. SWF reverse engineering almost deserves a tutorial of it's own, but it's the kind of thing that you can only really get good at with practice.
This example SWF isn't very complicated, so there's nothing too deep here. For starters, take a look at the names of the classes:


Obvious classes of interest include Money, Player and ScoreKeeper. The document class is also usually useful (Found by going tools -> go to document class). In this case, the document class is named Unhackable.

So, for starters, take a look at Unhackable.


It's good to note that the player, money and score variables are private. This isn't so important for bytecode modification, but if you were to hack it via loader or overrider, it would be relevant.
Most of this code is useless to us.
Lines of interest for us include:

From the Unhackable constructor:
      this.player=new Player(int(Math.random()*300)+50,int(Math.random()*200)+50);  
      this.money=new Money(int(Math.random()*300)+50,int(Math.random()*200)+50);  

From the gameLoop function:
      this.money.dealWithPlayer(this.player.playerX,this.player.playerY);  

If you take a peek into the Player and Money classes, you'll see they pass their constructor arguments on to their super class Thing, which uses  them to set their position, so in actual fact these lines just create a new Player and Money object with random position. The only thing you could accomplish by changing these two lines is changing the player and money starting position, so it  turns out these two lines are not so helpful.

I'll get to the money.dealWithPlayer line in a minute.
So, for the moment let's move on to the Money class

.

Now, we get to some interesting stuff. But first, it's good to notice Money has a super class Thing. It's usually worth taking a quick look in the super classes as there's usually useful stuff in them. This tutorial is getting pretty long, so I'll skip the Thing class. There's nothing particularly important that isn't self-explanatory in there anyway, but you should definitely take a look at it. I'm not going to cover the Player class either since it's long and doesn't need to be reversed for the score hack, but it also extends Thing.

The dealWithPlayer function takes two ints. If we look up the code in the Unhackable class, we see that those ints are the players x and y position. It then compares the players x and y to it's own x and y boundaries, and if the player is within it's boundaries (meaning the player and the money objects are colliding) the money randomizes it's position and calls the addMoney function.

From this point, we have enough information to perform several score hacks.
We could modify the Money class and remove the if statements checking the players position so the player is always colliding with the money.
We could modify the Money class by making it not randomize it's position when the player touches it, or even make it teleport to and follow the player.
We could modify the Unhackable class so that it passes the Money's  position to the dealWithPlayer function instead of the Players since the money is always "colliding" with itself.

But for now, let's move on to the ScoreKeeper class. There may well be a simpler score hack waiting for us in it.


Look at that juicy addMoney function.
There are two obvious hacks you could do here. You could change the line:
gameScore=gameScore+1;
to something like:
gameScore=gameScore+100;
or you could modify the static initializer.
The static initializer is the function that's called to initialize the static class.
It's not normally shown in the decompiler, but the code that sets gameScore to zero and the code that sets score to a new TextField are both in the static initializer.
We'll get to the static initializer in a second. First, we'll create our first AoB and make a simple score hack that gives you 100x more score via hex editing.

Part 2: A simple hack

Click the "view hex" (button with green on it) in the top-left corner of the bytecode window.
Click somewhere inside the code for the addMoney function, you should see some ABC bytecode and commented out green hex codes in the bytecode window.


Don't get too scared of the bytecode. It's not as nice to read as AS3, but you'll get used to it after a while. Practice makes perfect.
You definitely don't need to understand all those ABC codes. You can find a list of all ABC bytecodes and what they do here. I recommend looking some up just to get the hang of things

Anyway, what we want to do is change the +1 to be +100. This is done by changing the "pushbyte 1" opcode to be "pushbyte 100".
So, first we need to create a "find" AoB. this is simply done by taking the hex codes of a bunch of subsequent instructions around the byte we want to change.
Usually a hex string of 8 bytes or so is enough, but on some larger games longer strings are needed.
Let's take the hex from line 1 to line 15
d0 30 5e 1c 60 1c 24 01 a0  61 1c 60 1d
Now we need to make a hack AoB.
The opcode we want to change is the "pushbyte 1" opcode, which has the hex "24 01"
If you look it up in the AVM2 instruction list in the link above I gave above you will see the pushbyte instruction has the opcode 0x24 and is followed by an argument which is the signed byte to push onto the stack.
If you don't understand half of that above sentence, that's ok. Long story short, by changing "24 01" to "24 64" (0x64 = 100) we will change "pushbyte 1" to "pushbyte 100" and change the line:
gameScore=gameScore+1;
to:
gameScore=gameScore+100;
Therefore, our hack AoB is
d0 30 5e 1c 60 1c 24 64 a0  61 1c 60 1d
(the only difference between the hack AoB and the original is "24 01" being changed to "24 64")

Firstly, the SWF needs to be decompressed. You can get a SWF decompresser here. Drag a SWF onto the decompresser to decompress it.
Once the SWF has been decompressed, open the SWF in your favorite hex editor. I'm using HxD.
You should see the SWF file starts with the text "FWS" (hex: 46 57 53). This is the "magic number" for an uncompressed SWF as I mentioned earlier. All uncompressed SWFs start with these bytes.

Use the find and replace function in your hex editor to find the "find" AoB:
d0 30 5e 1c 24 01 a0  61 1c 60 1d
and replace with the hack AoB:
d0 30 5e 1c 24 64 a0  61 1c 60 1d
Save that and you should see your score goes up by 100 instead of one each time you get the money.

If it works, congratulations! If it doesn't, try again!

For the purposes of this tutorial, I have changed one of the lines in this class to make it easier to hack.
The line:
gameScore=gameScore+1
was originally
gameScore++
This may seem like a pointless modification, but both lines compile into different bytecode, and "++" is harder to hack via hex editing than "+=".

Long story short, it changes the lines:
pushbyte 01
add
to be
increment
since increment is a one-byte opcode, this means you cannot make a simple AoB to modify it to add 100, since you would have to fit 3 bytes of instructions in 1 byte, which is impossible. You would have to update the file size in the header, which is messy and not-so-straightforward to do.
Instead, in this case the easiest hack would be to simply change the starting score.
In order to do this, you will have to modify the static initializer I mentioned earlier.

Part 3: Modifying a static initializer

Go to the ScoreKeeper class in FFDec if you aren't already there.
To access the static initializer, click on "class intitalizer" in the traits window in the bottom left corner.


 

the line we want to change is the one setting gameScore to 0, so we will modify the pushByte 0 to be a pushByte 100 making us to start with 100 score.
Do the same as before, use line 1 to line 13 for the "find" AoB:
d0 30 5e 1c 24 00 61 1c 5e 1d 5d 1e
and change 24 00 to 24 64 like before for the hack AoB
d0 30 5e 1c 24 64 61 1c 5e 1d 5d 1e
Open the decompressed SWF up with your hex editor, find and replace, save and you should start with 100 score.

That concludes the easy part of this tutorial. For the next part, we're gonna do something more complicated. To start with, we're going to modify the dealWithPlayer function on the Money class so that the money thinks it's always touching the player.

Part 4: The NOP instruction

Go to the Money class in FFDec, and make sure you re-enable decompilation.
What we're going to do is replace the code for the if statements with NOP opcodes (0x02).
A NOP or No-OPeration opcode is an opcode that does nothing. It simply fills space and gets skipped over. Replacing a piece of code with NOPs effectively removes the code, and by removing the if statements, the code for gaining money will always be run.


The bytecode in the function should be in the same approximate order as the AS3 code.
If statements in bytecode are done using conditional jumps (iffalse, ifne, ect). These basically check the top item(s) on the stack, and skip to some instruction if conditions are met.
In order to not mess up the stack, we have to remove the conditional jumps and all the code related to them, meaning we need to replace all the ABC code for those lines with NOPs.
Luckily, the code is all in one place at the beginning.
The last conditional marks the end of the if statements. you will find it on line 60 of the commented ABC. The start of the if statement code is line 5 of the commented ABC code.

therefore, we have to NOP out everything from line 5 to line 60.
Here's the before AoB:
d1 60 10 24 0a a0 ad 2a 12 08 00 00 29 d1 60 10 24 0a a1 af 12 4c 00 00 d2 60 11 24 0a a0 ad 2a 12 08 00 00 29 d2 60 11 24 0a a1 af 12 34 00 00
And here's the hack AoB:
02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
Hex edit that into the swf, open and you should see your score going up from doing nothing, and the money rapidly randomizing it's position as you automatically pick it up.

this hack could also be performed by just NOPing out the branches and using POP (0x29) to remove any leftover elements on the stack.
e.g using the hack AoB:
d1 60 10 24 0a a0 ad 2a 02 02 02 29 29 d1 60 10 24 0a a1 af 02 02 02 29 d2 60 11 24 0a a0 ad 2a 02 02 02 29 29 d2 60 11 24 0a a1 af 02 02 02 29
In the above AoB each iffalse (12 XX XX XX) was changed to NOPs followed by a pop (02 02 02 29)
This stops the jump from happening and removes the item on the stack that would have been tested by the if. If you wanted to make the code in the if statement never run (i.e make the jump always occur), you could either NOP the code inside the if statement out or replace the conditional jump with an unconditional jump (For example, changing 12 XX XX XX to 11 XX XX XX). There are more ways to do this, but these techniques will work as well as any other.

That's enough on NOPing. Let's move on to an advanced modification involving writing some actual bytecode.
Don't worry, this is still going to be relatively simple. We're gonna make it so the money teleports to and follows the player instead of randomizing it's position when we pick it up.

Part 5: Intermediate bytecode modification

Open up an unmodified Unhackable SWF in FFDec, and go to the dealWithPlayer function in the Money class.
Take the moment to remember that the arguments passed to the function where the players X and Y position.

Long story short, we want to change the lines
               drawX=int(Math.random()*300)+50;
               drawY=int(Math.random()*200)+50;
to be
               drawX=param1;
               drawY=param2;
The code for these lines starts directly after the last iffalse on line 61 of the commented ABC code.
It ends on line 100 where drawY is set.
If you haven't realized yet, ABC bytecode tends to be in reverse order of the AS3 code. Not always, but a lot of the time. For example, the leftmost (or first) part of the bottom line of AS3 code is setting drawY, where as the last line of ABC bytecode is the line that sets it.
This should make sense if you think about it in terms of the order of operations. If you were to execute this line, the first thing you would do is go inside all the brackets. You would get the random number, multiply it by 200, convert the result to an integer, add 50 then set drawY by the result. This is exactly how the AVM2 runs this bytecode, however due to how the commands work, the objects have to be placed on the stack in a specific order for these to be calculated properly. This is because only the top element of the stack can be accessed.
For example, the first code that's run for this line is:
findproperty m[16]"drawX"
this is because the last line is
initproperty m[16]"drawX"
which takes two items off the stack:
the value
the property
in that order. this means before the calculations can begin, the property has to be added to the stack so that the items are in the right order for the last instruction.

Why is all this important, you ask?
because a lot of the time ABC instructions won't operate the way your intuition tells you they do, and they tend to have to be in obscure orders to operate properly. This is one of the reasons bytecode is so painful to work with. you might have to add an instruction 20 opcodes above the one that needs it in order for it to work properly.

Now that we've got all that out of the way, let's make a "find" AoB for this code from lines 63 to 98 (the first and last code from these lines don't need changing so aren't apart of the AoB):
5d 03 60 20 46 21 00 25 ac 02 a2 46 03 01 24 32 a0 68 10 5e 11 5d 03 60 20 46 21 00 25 c8 01 a2 46 03 01 24 32 a0
What we're gonna do is replace all the code between the first:
findpropstrict m[3]"int"
and the first:
add
(lines 63-78)
with NOPs and getlocal1 (the local register containing the first parameter)
Quick piece of information: local registers act like temporary variables that only exist inside a function
We will also replace the code in the next line the same, changing the lines 83-98 to be NOPs and getlocal2 (same as local1 but with second parameter)
This gives us the hack AoB:
02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 d1 68 10 5e 11 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 d2
We can clean that up and make it look prettier by moving all the NOPs to one end:
d1 68 10 5e 11 d2 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
hex edit that into your SWF, and it should work. Touch the money to make it start following you.

Part 6: AoB memory editing

You may have heard the term AoB before in relation to modifying a SWF while it's running after it has been loaded into memory using a memory editor such as Cheat Engine.

The AoB codes we've created are the same as ones that would be used with software such as cheat engine. However, there's some things you need to understand about AoB modification via memory editing before you can start making hacks using it.

The Actionscript Virtual Machine uses something called a JIT Compiler (Just-In-Time Compiler). The JIT compiler compiles the SWF into machine code to be run on the CPU. The JIT compiler doesn't compile a function until it's first run, but once it's compiled the SWF bytecode for that function never gets accessed again. Therefore, memory-based AoB modifications have to be done BEFORE the JIT compiler is run and therefore BEFORE the function is first called.
Usually this means they have to be applied in the game's menu before starting the game, or in worse cases before the game starts to run.
In our case, all the AoBs we've made are for functions that are run when the game first starts, thus they cannot be used for memory-based AoB hacks.

After note:

Since I had to modify the Unhackable SWF several times while making this tutorial, there is a chance that some of the bytecode and AoBs are wrong. Here's hoping that's not the case, but it's totally possible. If you find something wrong, leave a comment.

If you're wondering what to learn next, I'd recommend  playing around with some proper ABC bytecode editing software, such as Yogda, RABCDASM or even the bytecode editor in FFDec.
For further reading, I'd recommend skimming through some of Adobe's SWF Specification.

More specifically, I'd recommend any beginner learn a bit about the constants table. Yogda is especially useful for that.

Hopefully this tutorial is cohesive. I tried to make it as in-detail as possible since all the other SWF modification tutorials I've seen have been pretty terrible.

Lastly, I'd really appreciate some feedback on this tutorial, such as whether it was useful or not, whether it was detailed enough or too detailed, ect.

Friday, July 5, 2013

An Introduction to SWF Obfuscation

I keep running into people who need a good explanation of obfuscation. Since it's a pretty difficult subject to go into any detail in, I thought I should do a post about it so I don't have to spend half an hour explaining it to people every time someone wants to learn more about it.

So, what is obfuscation in general?
Obfuscation is the art of making code, compiled or otherwise, unreadable. In programming, it's generally used to make code harder to decompile, disassemble and reverse-engineer.

There are two main forms of obfuscation used on SWFs. Name obfuscation, and Bytecode obfuscation. Normally, the two are used together, and SWFs that only use one are quite rare.

Name obfuscation is simple. Take the names of every class, variable and function and change it to some randomly generated junk.
For example, if you had a class named "player", it might be renamed to "d+.{8]R0%9r".
This makes it difficult to easily identify what classes/functions/properties do what.
Name obfuscation is impossible to reverse. The best you can do is change it to something like class1, class2, class3... function1, function2, function3... ect. Or alternatively, you can reverse engineer the classes and manually re-name them as you figure out what they do, but that is incredibly time consuming, impossible to automate and you still won't end up "reversing" the obfuscation as such, just giving useful labels to the obfuscated classes.

Two lines of  AS3 with compiled AVM2 Actionscript Bytecode
Bytecode obfuscation is a much more complicated subject.
There are many forms of bytecode obfuscation. Some add junk code, some add extra code branches, some restructure the code. There's a crapload of different ways to do bytecode obfuscation.
In order to understand bytecode obfuscation, you have to understand the difference between AS3 and AVM2 Actionscript bytecode. A SWF does not contain any AS3 code in it, but rather a compiled lower-level language. Comparing the code in a SWF to AS3 is like comparing Assembly to C++ (google them if you want).
This difference in languages means that you could in theory have AVM2 Bytecode that has no equivalent in AS3. Bytecode obfuscators use principals like this to their advantage.
They can crash decompilers by adding invalid code to the SWF that in practice, will never be run, and the decompiler will be unable to decompile the code, since the obfuscated AVM2 code in the SWF has no AS equivalent.
Over the years, bytecode obfuscation has gotten more and more advanced. Back in the day, you could remove it with a hex editor if you knew what to search and replace for. Nowadays, it takes complex programs purpose-written to take out specific obfuscation algorithms to remove such defenses.

Apart from completely thwarting the use of decompilers, bytecode obfuscation has another use. By adding lots of junk bytecode and restructuring the bytecode, it makes it much harder to reverse-engineer the disassembled code. This, coupled with name obfuscation can make it near impossible for most hackers to make hacks and reverse engineer the SWF in general. As you can see above, two lines of AS came out to be about 15 lines of AVM2 ABC  Bytecode. It is not uncommon for an obfuscated class to contain well over a thousand lines of such code, barely readable even without obfuscation.

However, most if not all bytecode obfuscation is theoretically removable. That being said, it's almost always impractically difficult to do so.

So, how do we actually deal with obfuscated SWFs, you ask?
The Free and open-source decompiler JPEXS FFDec has some very good deobfuscation routines in it. If that doesn't work:
My biggest piece of advice would be to look for useful unobfuscated strings. Name obfuscation does not obfuscate all strings. names and packages of Adobe classes tend to stay unobfuscated. So do events a lot of the time. you can always go through the strings constant pool (using Yogda or another bytecode editor). Finding unobfuscated strings can quite often allow you to figure out what's what, and a lot of the time it's pretty much the only option you have.
Another piece of advice is don't tackle obfuscated SWFs until you are very confidant with working in bytecode. It's not easy. Even the best hackers tend to dread dealing with obfuscated games.

I would also recommend using a variable scanner if you can, however there are not many (if any?) good AS3 var scanners (there's my shitty one, and AS3Watson), since they really only started being publicly released a year ago, and I don't think there's a single tutorial on the internet on AS3 var scanning. I'll probably make one some time, but not for a while.

I could have written more. Maybe I'll do another post, or a series of posts on obfuscation in the future. There's heaps to write about, and I only scratched the surface on most of the things I mentioned.

Hopefully this is readable and makes sense. I'm pretty exhausted right now.

Thursday, April 4, 2013

Manatee SWF Decrypter V3

Funny story, I was gonna release this a week ago, but my laptop charger broke as I was typing this out. I was hoping to get a release out before the end of the month, but nonetheless here's V3 of my decryptor!Those of you who were paying attention will probably be wondering why there's no V2.
Here's a little explanation:
The V1 method simply loaded the SWF and scanned it for loaders.
The V2 method (which I didn't finish because I discovered the superior V3 method while working on it) functioned by overriding the Loader class in a loaded SWF. some of you may have realized by now that Adobe classes cannot normally be overrided, but with a little black magic, you can do the equivalent.
The V3 method works by using the debug version of flash player and editing mm.cfg to load the SWF alongside the target SWF, and a little black magic.
The V3 method is much harder to detect and defend against than the V1 and V2 methods and less buggy since it doesn't utilize the Loader class to load the game.

Installation
Download and install the flash content debug player on this page
Download this SWF (it's also on the downloads page) and move it to:

C:/ManateeUniversalDecryptorV3.swf

open (or create and open if it doesn't exist):

%userprofile%/mm.cfg

in notepad or equivalent and add the line:

 PreloadSwf=C:/ManateeUniversalDecryptorV3.swf  

to your mm.cfg file.
A helpful hint if you start getting annoyed with all the errors is to add the following line to your mm.cfg

 SuppressDebuggerExceptionDialogs=1  

and then use  the "silent" mode (which I will mention later).

Usage
Once Manatee SWF Decrypter has been installed, it will be automatically injected into any game running in the debug player. So, to use it you just open the game in the debug player you installed. (eg, if you install the Firefox debug plugin, open the game in Firefox to inject the decrypter into it)
Manatee SWF Decrypter V3 is controlled entirely with the numpad.
There are two output modes: normal mode and the slightly ironically named silent mode.
Normal mode outputs messages by throwing errors, and silent mode outputs messages by beeping.
Numpad 1 enters silent mode, and numpad 2 enters normal mode.
Numpad 5 is a kind of "hello world" button that in normal mode simply tells you that the decryptor is connected. In silent mode, a single "beep" shows that the decryptor is connected.
Numpad 3 tells you if there's remaining SWFs to be dumped. in normal mode it throws an error telling you exactly how many SWFs are left for dumping. in silent mode, 3 beeps means 1 or more SWFs remaining, and 9 beeps means there are no SWFs remaining.
Numpad 0 dumps remaining SWFs. In normal mode, it tells you how many SWFs are remaining when the SWF finishes saving. In silent mode when the SWF has finished dumping you get 3 beeps if one or more SWF is remaining to be dumped or 9 beeps if no SWFs remain to be dumped.

Rule of thumb for silent mode:
1 beep = the decryptor is connected
3 beeps = 1+ SWFs remaining
9 beeps = 0 SWFs remaining

Currently, the only major bug is sometimes the AVM2 garbage collector destroys the ByteArray the SWF is held in before you get a chance to dump it.
It doesn't usually happen to "important" SWFs though, so fixing that bug is pretty low priority for me.

Thursday, December 20, 2012

Flash AS3 Class overriding template and tutorial

The biggest problem with class overriding I have found so far is that large classes can take a lot of work to "fix" to work with class overriding. So, I have created a template for class overriding that can make this process a vast amount easier.
It certainly has it's limitations, but where it works, it works well.
To be able to use it properly, you need to have a pretty good understanding of how it works.
I'll start with a quick overview of how it works, don't worry too much if you don't understand it properly yet:

A custom loader dynamically generates a SWF containing a single "nullified" class that has no methods or properties and uses class overriding to nullify the document class in an instance of the game SWF (this instance is used solely for accessing the original, unmodified classes in the game).


Template classes use the proxy extends hack to pretend to "extend" the original class in the game. e.g. if you were overriding the class "Money", the template does this by using the proxy hack to pretend to extend the original "Money" class held in the nullified instance of the SWF loaded by the custom loader.

Classes are overridden by extending the BasicOverrider class that is included in the template. This class and the custom loader do all the hard work for you. Any public methods and variables can now be modified, without having to "fix" any code you're not working with.

Here's a nice little visual explanation for the loader:



With any luck, you now have a decent understanding of how the loader works.
so, onto how the template extender works:



If you need more information, you can take a peek at it's source.

Now, hopefully you understand it's limitations. It cannot access private/protected vars and requires a little work to fix type casting problems (Note: a lot of the time this isn't a problem) due to the nature of how it functions.

Now, here's the mini-tutorial and template download link:
You'll need this SWF and the OverriderLoader template for this tutorial.
I will assume you know everything covered in this tutorial.

Setting up the template is really easy.
The SWF name is "Unhackable.swf" and if you open it up with a decompiler, it'll tell you the document class  is "Unhackable".
open up the Overrider class and change:

 loader.load("SWF NAME HERE.swf","DocumentClassNameHere");  

to:

 loader.load("unhackable.swf","Unhackable");  

Next, we need to fix the stage access problem. Unfortunately, template overriding doesn't work on the document class (because it gets nullified so you can load the other classes without running code), so we'll need to use the old method. We'll use the same code we wrote up in the last tutorial. Chuck this code in a new class called "Unhackable":

 package   
 {  
      import flash.utils.*;  
      import flash.display.*;  
      import flash.events.*;  
      public class Unhackable extends MovieClip  
      {  
           private var Player:Class = getDefinitionByName("UnhackableEngine.Player") as Class;  
           private var Money:Class = getDefinitionByName("UnhackableEngine.Money") as Class;  
           private var ScoreKeeper:Class = getDefinitionByName("UnhackableEngine.ScoreKeeper") as Class;  
           private var player:Object;  
           private var money:Object;  
           private var score:Object;  
           public function Unhackable()  
           {  
                this.addEventListener(Event.ADDED_TO_STAGE,addToStage);  
                return;  
           }  
           private function addToStage(e:Event)  
           {  
                this.player = new Player(int(Math.random() * 300) + 50,int(Math.random() * 200) + 50);  
                this.money = new Money(int(Math.random() * 300) + 50,int(Math.random() * 200) + 50);  
                this.score = new ScoreKeeper();  
                addChild(this.score as DisplayObject);  
                addChild(this.money as DisplayObject);  
                addChild(this.player as DisplayObject);  
                stage.addEventListener(Event.ENTER_FRAME, this.gameLoop);  
           }// end function  
           private function gameLoop(event:Event)  
           {  
                this.money.dealWithPlayer(this.player.playerX, this.player.playerY);  
                this.player.doMove();  
                return;  
           }// end function  
      }  
 }  

Now that that problem's solved, we can get on to using the template for something useful.

We'll make each money pickup give you 100 score.
Now, if you remember, the actual score variable is private, which means that template overriding can't touch it. however, the static "addMoney" method is public and can thus be edited.

So, let's start with a basic overrider template class, then.
Create a new class in the package "UnhackableEngine" called "ScoreKeeper" and give it this code:

 package UnhackableEngine {  
      import OverriderLoaderUtils.BasicOverrider;  
      import flash.utils.*;  
      public class ScoreKeeper extends BasicOverrider {  
           public function ScoreKeeper() {  
                super(getQualifiedClassName(this));  
                origObject = new origClass();  
           }  
           static function addMoney()  
           {  
                origClass.addMoney();  
           }  
      }  
 }  

This is just a basic overrider template class with a wrapper function for the addMoney static method (as all static methods need wrapper functions when using the template). Our goal right now is to get it to compile with this.

So, lets add the line

 var scoreKeeper:ScoreKeeper;  

to our Overrider class to make it all compile together and see what errors we get.
You'll get an error telling you it doesn't want to be added as a child. We've been here before, haven't we? last tutorial?
Change:

 addChild(this.score as DisplayObject);  

to:

 addChild(this.score.origObject as DisplayObject);  

in your Overrider class to make it quit it's bitching. Compile, and it should be happy.

Now all we need to do to make money add 100 score is go to our ScoreKeeper class and change:

           static function addMoney()  
           {  
                origClass.addMoney();  
           }  

to:

           static function addMoney()  
           {  
                for(var i:int = 0;i<100;i++){  
                     origClass.addMoney();  
                }  
           }  

Compile, and you're done!

Here's the finished source code for the tutorial.


Oh, and if you're wondering how to fix static variables, you do something like this:

           static function get varName():Object  
           {  
                return origClass.varName();  
           }  
           static function set varName(value:Object):Void  
           {  
                origClass.varName = value;  
           }  

Where varName is the name of the variable.

The pros of using the template:
- no need to fix any non-static functions
- static functions only need to be redirected, so fixing is much simpler compared to the old method

The cons of using the template
- doesn't work on the document class
- doesn't work on private/protected vars/methods
- type casting is still a problem

Monday, December 3, 2012

Flash AS3 Class injection via Loader

I discovered a new technique for hacking flash games a month or so ago. I've been referring to it as class overriding (simply because class injection could be used to refer to injecting bytecode into a SWF).
It's a fairly simple technique that allows you to have extreme control over a loaded SWF.
I was going to do this tutorial on an actual game, but unfortunately there weren't any games that were good examples, so instead I modified my Unhackable Engine base (designed to be the simplest game possible for testing new security measures/ect in a semi-real environment and easy to port to other languages, the original AS3 version is under 90 lines of code, and the AS2 version is less than 60).
I'm going to assume you have some experience with AS3 and flash. If you don't, the source for this tutorial will be available.

Be warned, this SWF was botched together as fast as possible, and modified to function in weird ways specifically for the tutorial. Don't judge me on the quality of it's code.

Download the "game" used in this tutorial here.

 Anyways, to the tutorial:

The first thing we do is we start with a basic loader template that will load the SWF.
Create a new document class (I called mine Overrider), and put this code in it:
 package {  
      import flash.display.MovieClip;  
      import flash.display.Loader;  
      import flash.net.URLRequest;  
      import flash.system.ApplicationDomain;  
      import flash.system.LoaderContext;  
      public class Overrider extends MovieClip {  
           private var ldr:Loader = new Loader();  
           public function Overrider() {  
                this.addChild(ldr);  
                var urlReq:URLRequest = new URLRequest("unhackable.swf");  
                ldr.load(urlReq,new LoaderContext(false,ApplicationDomain.currentDomain));  
           }  
      }  
 }  

This will load the SWF into our SWFs ApplicationDomain, meaning that classes will be loaded into the same space, and when flash player tries to load a class that already exists in the current ApplicationDomain, the new class is dropped, and the old one remains (which is how this hack works).
Now, compile and run that, and you will get an error.

 TypeError: Error #1009: Cannot access a property or method of a null object reference.  
      at Unhackable()  

let's open unhackable.swf with a decompiler, and see what the problem is.
We know from the error that the error is within Unhackable(), so let's look at the code:

 public class Unhackable extends MovieClip  
 {  
   ...  
   public function Unhackable()  
   {  
     this.player = new Player(int(Math.random() * 300) + 50, int(Math.random() * 200) + 50);  
     this.money = new Money(int(Math.random() * 300) + 50, int(Math.random() * 200) + 50);  
     this.score = new ScoreKeeper();  
     addChild(this.score);  
     addChild(this.money);  
     addChild(this.player);  
     stage.addEventListener(Event.ENTER_FRAME, this.gameLoop);  
     return;  
   }  
   ...  
 }  

Ah, there's our problem. Let me explain for those of us who are unfamiliar with the bugs in the Loader class...
When A SWF is loaded normally (without the Loader class), the document class's constructor is called after it has been added to the stage and therefore has the stage property set. When a SWF is loaded with the Loader class, the document class's constructor is called before it has had it's stage property set, so that refference to the stage:

 stage.addEventListener(Event.ENTER_FRAME, this.gameLoop);   

will throw an error, because the stage variable is still a null object reference (it's null), and as the error states, you cannot access a method of a null object reference.

Luckily, we can fix this quite easily with class overriding.

Create a new class in your default package called Unhackable and copy and paste the code the decompiler generated for that class over.

Now, when you compile that, you'll get about a half-dozen errors. this is ok, and completely normal in class overriding. You see, that class imports other classes (Player, Money, ScoreKeeper) that we don't have in our project. The first solution you probably think if would be to decompile and add every class in the SWF, but that would be a pain in the ass and counterproductive, so we're gonna hack our way through this. the question we need to answer is: How do we access a class we can't import? The solution is simple: using the getDefinitionByName function in flash.utils.*.
so, replace:

 import UnhackableEngine.*;  

with:

 import flash.utils.*;  

getDefintionByName will dynamically find the class for us whenever we need it. so, with a little modification we can fix this class.
replace:

     private var player:Player;  
     private var money:Money;  
     private var score:ScoreKeeper;  

with:

           private var Player:Class = getDefinitionByName("UnhackableEngine.Player") as Class;  
           private var Money:Class = getDefinitionByName("UnhackableEngine.Money") as Class;  
           private var ScoreKeeper:Class = getDefinitionByName("UnhackableEngine.ScoreKeeper") as Class;  
           private var player:Object;  
           private var money:Object;  
           private var score:Object;  

Also, since there are no references to  this new class you've added, by default it won't compile into your SWF, so go back to your document class, and under the class variable definitions, add:

 private var unhackable:Unhackable;  

When you try and compile, you'll get some more errors now, but that's OK, cause we're gonna have to replace the function they're in anyway.
So, onto replacing the function, fixing the stage reference problems and adding the needed type casting.
First, we're gonna move all the code in the constructor to another function, and make that function be called when the object has been added to the stage (using the ADDED_TO_STAGE listener).
replace:

           public function Unhackable()  
           {  
                this.player = new Player(int(Math.random() * 300) + 50,int(Math.random() * 200) + 50);  
                this.money = new Money(int(Math.random() * 300) + 50,int(Math.random() * 200) + 50);  
                this.score = new ScoreKeeper();  
                addChild(this.score as DisplayObject);  
                addChild(this.money as DisplayObject);  
                addChild(this.player as DisplayObject);  
                stage.addEventListener(Event.ENTER_FRAME, this.gameLoop);  
                return;  
           }  

with:

           public function Unhackable()  
           {  
                this.addEventListener(Event.ADDED_TO_STAGE,addToStage);  
                return;  
           }  
           private function addToStage(e:Event)  
           {  
                this.player = new Player(int(Math.random() * 300) + 50,int(Math.random() * 200) + 50);  
                this.money = new Money(int(Math.random() * 300) + 50,int(Math.random() * 200) + 50);  
                this.score = new ScoreKeeper();  
                addChild(this.score);  
                addChild(this.money);  
                addChild(this.player);  
                stage.addEventListener(Event.ENTER_FRAME, this.gameLoop);  
           }  

And now we add some type casting to fix the last 3 errors.
replace:

                addChild(this.score);  
                addChild(this.money);  
                addChild(this.player);  

with:

                addChild(this.score as DisplayObject);  
                addChild(this.money as DisplayObject);  
                addChild(this.player as DisplayObject);  

and compile. with any luck, you have no errors, it compiles, loads the SWF and runs perfectly!
If not, try download the source of the finished code and try and figure out what went wrong.

Congratulations! you've successfully overrided your first class!

Now, onto doing something useful.

We'll double player speed and make it so that each "money" pickup gives us 1,000 points.
First, we'll start with the money hack, because the player speed hack is a little more complicated (If you're feeling confident/bored, you can skip to the player speed hack, as it teaches more important stuff).

This one is simple and straightforward.
you'll need a new package (class locations need to be mirrored) called UnhackableEngine, and in there a new class called ScoreKeeper.

Copy and paste the ScoreKeeper code from the decompiler, add this line of code to your document classes variable definitions to make sure it gets compiled in:

 private var scoreKeeper:ScoreKeeper;  

and compile.
You'll get an error.
Don't worry, it's easy to fix.

Our problem is that variables are not being casted from int/whatever to string, so the compiler is getting shitty with us.

replace the two occurrences of:

 score.text = gameScore;  

with:

 score.text = String(gameScore);  

and compile.
It should compile and work without errors.

Now, let's take a look at the addMoney function:

     static function addMoney()  
     {  
       var _loc_2:* = gameScore + 1;  
       gameScore = _loc_2;  
       score.text = String(gameScore);  
       return;  
     }  

seems easy enough to modify. Let's chuck some 0s in there

     static function addMoney()  
     {  
       var _loc_2:* = gameScore + 1000;  
       gameScore = _loc_2;  
       score.text = String(gameScore);  
       return;  
     }  

and boom, money pickups now give us 1000 score.

Onto player movement, and advanced class overriding!
I'm sure you know the drill by now.
Create a new class in the UnhackableEngine package named Player.
C+P the code from the decompiler, add a reference to the class in your document class, try to compile and see what errors you get.

Ooh, we get a new, nasty one.

The definition of base class Thing was not found.

We have two options here, overriding the Thing class will solve this problem, but imagine if Thing extended another custom class, which extended another custom class, which ended up being a class chain 20  classes long? it would be impractical to override them all.

But how can you possibly get around it? you can't just dynamically extend classes. That goes directly against AS3s standards. Luckily, in AS3 there's this nasty little thing called the Proxy class that goes against all of AS3s conventions, and makes this possible.

Although we cannot actually dynamically extend classes, we can pretend we do using the Proxy class to dynamically pretend we have properties of classes which we actually don't.

I'm going to warn you now, everything's about to get messy. Which is why I'm stalling.

I guess I'll explain the Proxy class a little, seems like it's the best way to teach this.

The Proxy class allows you to dynamically change the behavior of an object. When set up, you can make it so that if a property is accessed and doesn't exist in an object, a proxy function is called that can deal with it. we use this functionality to make an object act as if it has the properties of another object by making any attempt to access a property that doesn't exist on the object go to the object it is pretending to extend.

Hopefully, the code will help explain what I mean for those who are still confused...

First thing we need to do is make the Player class extend the Proxy class.
replace:

 public class Player extends Thing  

with:

 public class Player extends Proxy  

now, add these variables to the Player classes variable definitions:

           public var extendedClass:Class = getDefinitionByName("UnhackableEngine.Thing") as Class;  
           public var extended:Object  

add these functions to the class:

  override flash_proxy function getProperty(name:*):*
  {
   return extended[name];
  }
  
  override flash_proxy function setProperty(name:*,value:*):void
  {
   extended[name] = value;
  }

  override flash_proxy function callProperty(name:*, ...rest):*
  {
   extended[name].apply(rest);
  }

these functions are the backbone of our Proxy hack. We don't actually need most (or any) of them. in most situations, you'll need them, though. There are more Proxy functions you may need in other circumstances, but these are the main ones.

We're gonna have to do quite a bit of modifying to make this class work with us.
Any internal references in the player class to the class it extends need to be changed.

replace:

     public function Player(param1, param2)  
     {  
       fillColour = 11141120;  
       this.playerX = param1;  
       this.playerY = param2;  
       this.addEventListener(Event.ADDED_TO_STAGE, this.addListeners);  
       super(param1, param2);  
       return;  
     }  

with:

           public function Player(param1, param2)  
           {  
                extended = new extendedClass(param1, param2);  
                extended.fillColour = 11141120;  
                extended.drawStuff();  
                this.playerX = param1;  
                this.playerY = param2;  
                extended.addEventListener(Event.ADDED_TO_STAGE, this.addListeners);  
                return;  
           }  

and

     public function doMove()  
     {  
       this.playerX = this.playerX + this.xVel * 5;  
       this.playerY = this.playerY + this.yVel * 5;  
       drawX = this.playerX;  
       drawY = this.playerY;  
       drawStuff();  
       return;  
     }  

with:

           public function doMove()  
           {  
                this.playerX = this.playerX + this.xVel * 5;  
                this.playerY = this.playerY + this.yVel * 5;  
                extended.drawX = this.playerX;  
                extended.drawY = this.playerY;  
                extended.drawStuff();  
                return;  
           }  

and

     private function addListeners(event:Event) : void  
     {  
       stage.addEventListener(KeyboardEvent.KEY_DOWN, this.keyDownListener);  
       stage.addEventListener(KeyboardEvent.KEY_UP, this.keyUpListener);  
       return;  
     }  

with:

           private function addListeners(event:Event):void  
           {  
                extended.stage.addEventListener(KeyboardEvent.KEY_DOWN, this.keyDownListener);  
                extended.stage.addEventListener(KeyboardEvent.KEY_UP, this.keyUpListener);  
                return;  
           }  

As you can tell, the Proxy hack can be a bit messy. sometimes, it's better to just override the class it extends as well. cause now where up to the next problem: type casting.
A Proxy object can't be casted to anything the object it's pretending to extend can. And the addChild function which the player object is given as a parameter to expects a DisplayObject. luckily, we're already overriding the class that calls the function, so go back to the Unhackable class and
replace:

 addChild(this.player as DisplayObject);  

with:

 addChild(this.player.extended as DisplayObject);  

compile that and it should work error-free.

The Player class is FINALLY set up for modification (again, the proxy hack isn't really made for this situation, but it's useful to know how to do it).

One quick modification and we'll  have double player speed.

change

                this.playerX = this.playerX + this.xVel * 5;  
                this.playerY = this.playerY + this.yVel * 5;  

to

                this.playerX = this.playerX + this.xVel * 10;  
                this.playerY = this.playerY + this.yVel * 10;  

And we're done!

Download the source for this tutorial here.

Monday, May 14, 2012

Sitelocked SWF Loader


I just finished my SWF sitelock tool for loading sitelocked swfs offline.
This project is the precursor to the packet editor I'm going to make (vast quantities of this projects code can be recycled into my packet editor).
This loader should be able to load ~95% of sitelocked games. it has one or two limitations though:

It's currently windows only (I might make a linux option in the future. wouldn't be too hard. mac would be possible too).
Generally speaking, it can only load games held in a single .swf file. and it tends to break multiplayer games. Both of those are fixable, but it's probably not worth the effort.

Anyways, here's how you use it:

if for example, you wanted to load a sitelocked swf  offline from www.example.com/folder/test.swf you would set "Server" to "www.example.com"  and "Path" to "/folder/test.swf"
after you've set that up, just drag-and-drop the swf onto the loader to load it.

Note: most games only need to be loaded from the correct server and you can usually leave Path blank.

Here's a picture of me loading an unmodified, sitelocked Stick RPG Complete while not connected to the internet:

Note that Path doesn't actually have to be set to anything, I just did that to show it worked.

Download my SWF Sitelock Tool here:
Download

Also, it's an AIR application, so you need AIR installed to run it.

Tuesday, April 10, 2012

Universal SWF Decrypter

I made a Universal SWF Decrypter based on the method/code I posted the other day.
you can find the windows binary here or on my downloads page.
It should work on all swf encryptions that use an AS3 loader. which means it should work on ALL mochicrypt games. and probably on all other SWF encryptions, too.

*edit*
Go Here for the new Universal SWF Decryptor V3

Thursday, April 5, 2012

Decrypting AS3 SWF's

Today, I found a massive flaw in Mochicrypt and probably all AS3 encryption methods. I'm sure others already know about it, but I'll post it anyway.
Firstly, let me start by explaining how these encryptions work:
The swf is encrypted and stored (generally in the decryptor/loader).
The encrypted swf is decrypted and turned into a byteArray
The decrypted byteArray is loaded using the Loader.loadBytes() method.
The Loader is added to the stage.

and, as it turns out, getting the unencrypted swf from the loader is incredibly easy.
there's this usefull as3 class called "LoaderInfo" that will solve all our problems.
in fact, Loader.contentLoaderInfo.bytes contains the byteArray for the loaded swf.
so, what we can do is load the decrypter, scan the stage's children for Loader objects, find the one for the decrypted swf and dump it's data.

Here's a working example I made for Bloons TD 5:
 package   
 {  
     import flash.display.*;  
     import flash.net.*;  
     import flash.events.*;  
     public class BTD5Decryptor extends MovieClip  
     {  
         private var ldr:Loader = new Loader();  
         private var swf:Object;  
         public function BTD5Decryptor()  
         {  
             ldr.load(new URLRequest("btd5.swf"));  
             ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);  
             addChild(ldr);  
             stage.addEventListener(KeyboardEvent.KEY_DOWN, doKeys);  
         }  
         private function onComplete(evt:Event):void  
         {  
             var ldrInfo:LoaderInfo = evt.target as LoaderInfo;  
             swf = ldrInfo.content;  
         }  
         private function doKeys(e:KeyboardEvent)  
         {  
             if (e.keyCode == 96)  
             {  
                 var file:FileReference = new FileReference();  
                 file.save((swf.getChildAt(1) as Loader).contentLoaderInfo.bytes,"unencrypted.swf");  
             }  
         }  
     }  
 }  

To use it, place the Bloons TD 5 swf in it's folder (make sure it's called "btd5.swf"), open the decrypter, wait for the game to load and then press numpad 0 to save the decrypted swf.

Here's a link to the compiled version of the decrypter (same as the code above):
http://www.mediafire.com/?lfk94ox5ge9i85k

Also note that the above code and decrypter may work on more/all mochicrypt swfs/loaders. I've only tested it on Bloons TD 5's mochicrypt loader as that's what it's made for, but it may very well work on all mochicrypt loaders.
*edit*
it only works on some, not all. I'll release a universal SWF decrypter I've made later today.

Friday, March 23, 2012

Getting around SecurityError #2070

So, today I started playing with some SWF loading code in Flash Builder, and I ran into a common sandbox problem.
You get Error #2070 when a loaded swf in another directory tries to access the stage.
I couldn't get around it by using Security.allowDomain("*") either, because it was an AIR application.
So, to get around this problem I decided instead of directly loading the file using Loader.load() I'd load it by loading the file into a byteArray and useing Loader.loadBytes() instead. and guess what? it works.
It completely gets around the sandbox issue.
Have some code:

 private function loadSwfFromUrl(url:String):void{  
      var context:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);  
      var ldr:Loader = new Loader();  
      var swf:ByteArray = new ByteArray();  
      var fs:FileStream = new FileStream();  
      fs.open(new File(url), FileMode.READ);  
      fs.readBytes(swf);  
      fs.close();  
      context.allowCodeImport = true;  
      ldr.loadBytes(swf,context);  
      stage.addChild(ldr);  
 }  

There's probably a better, simpler way to get around this sandbox issue, but at least this works.

Saturday, March 17, 2012

Universal Flash Variable Scanner

So, I had an idea for a universal swf variable scanner.
one that works on AVM1 (AS1/AS2) and AVM2 (AS3) swf's.

The main problem you face when attempting to create such a thing is that AVM1 swf's and AVM2 swf's have separate sandboxes.
AVM2 loaders CAN get and set variables in loaded AVM2 swf's.
AVM2 loaders CANNOT get and set variables in loaded AVM1 swf's.
However, there are ways to get around this.

You could theoretically make a universal scanner made up of an AVM2 loader and scanner and an AVM1 loader and scanner.

for scanning AVM2 swf's there is no problem:
AVM2 Master swf loads AVM2 movie and scans it.

But for AVM1 swf's, you have to get around sandbox issues.
My idea works like so:
AVM2 Master swf loads AVM1 Scanner.
AVM1 Scanner loads AVM1 movie.
AVM1 Scanner communicates with AVM2 Master swf using LocalConnection.
AVM1 Scanner scans AVM1 movie.


If you were smart, you could probably remove the need for communication between the AVM1 and AVM2 scanner parts and have them functioning pretty much completely independantly.