So, you might have noticed I've disappeared under a rock for the time being.
I'm working on a few big, exciting new projects right now that I can't say a huge amount about.
I guess you could say the last year or so of my flash hacking has kind of been leading up to these projects, and they should blow some minds when they get released.
It's gonna be a few months before I can start to shed light on then, and until I can I probably won't be making much in the way of new posts here.
So basically, I'm probably going to be inactive for a couple months.
I'll release more info when I can.
Saturday, November 30, 2013
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:
From the gameLoop function:
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.
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.
Packet editor progress #8
Work on BPE has been going slow lately, as usual.
Adding UDP support creates a whole lot of problems.
The only "good" way of doing it is binding to the UDP port on each IP that's been hooked, which is messy and generally a pain to implement.
It seems like that's the only option, though since the AIR DatagramSocket doesn't allow you to get the destination IP address of an incoming packet if you bind 0.0.0.0 (their address for binding to all incoming addresses)
The good news is that gives me a reason to redesign BPE a little bit. For a while now, I've been thinking it would be good to manage hooked ports on a per-server basis, instead of having to hook ports on all servers. The bad news is making that work will require some pretty huge modifications, which will take some time.
I'm thinking for now I might just make it you can only hook one UDP server, since I can't think of a clean solution for multiple UDP hooks right now. Plus, the only use I've found for hooking UDP is DNS requests which would only require one server hook anyway.
So, it's highly likely 1.3 will only have partial UDP support.
I'm also modifying the socket open hook so that plugins can redirect connections. This is useful for games that use more than one server. You could for example use it to write a plugin that allows you to choose from one of many private servers.
I'll probably release an example plugin using it which works as a quickstart server/game chooser for Stick Arena and/or BBH.
I haven't started work on the server/port adding plugin hooks, but they should be simple enough to add. I still plan on putting them in the next release, since those hooks and UDP support would be a powerful combination allowing me to automatically hook servers, as I have mentioned before.
While I'm at it I might as well mention I'm currently working on a new tutorial on SWF bytecode modification. Just the basics.
It's going to be really detailed, since I haven't seen any really in-depth tutorials out there on the subject.
Be warned, though. I'm not even half way done and it's already stupidly long.
Adding UDP support creates a whole lot of problems.
The only "good" way of doing it is binding to the UDP port on each IP that's been hooked, which is messy and generally a pain to implement.
It seems like that's the only option, though since the AIR DatagramSocket doesn't allow you to get the destination IP address of an incoming packet if you bind 0.0.0.0 (their address for binding to all incoming addresses)
The good news is that gives me a reason to redesign BPE a little bit. For a while now, I've been thinking it would be good to manage hooked ports on a per-server basis, instead of having to hook ports on all servers. The bad news is making that work will require some pretty huge modifications, which will take some time.
I'm thinking for now I might just make it you can only hook one UDP server, since I can't think of a clean solution for multiple UDP hooks right now. Plus, the only use I've found for hooking UDP is DNS requests which would only require one server hook anyway.
So, it's highly likely 1.3 will only have partial UDP support.
I'm also modifying the socket open hook so that plugins can redirect connections. This is useful for games that use more than one server. You could for example use it to write a plugin that allows you to choose from one of many private servers.
I'll probably release an example plugin using it which works as a quickstart server/game chooser for Stick Arena and/or BBH.
I haven't started work on the server/port adding plugin hooks, but they should be simple enough to add. I still plan on putting them in the next release, since those hooks and UDP support would be a powerful combination allowing me to automatically hook servers, as I have mentioned before.
While I'm at it I might as well mention I'm currently working on a new tutorial on SWF bytecode modification. Just the basics.
It's going to be really detailed, since I haven't seen any really in-depth tutorials out there on the subject.
Be warned, though. I'm not even half way done and it's already stupidly long.
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.
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.
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 |
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.
Labels:
actionscript,
adobe,
AS3,
AVM2,
cheat,
class,
flash,
hack,
hacking,
obfuscation,
programming,
swf
Wednesday, June 5, 2013
Server-sided values
Someone (Jazy) mentioned server-sided values in a comment today-ish, so I've decided I'm going to do a long-ass post explaining in detail just what server-sided means in hacking.
server-sided is a term used too often and too generally, usually by people who don't understand it.
The concept of server sidedness is relatively simple.
data that is stored and processed by the client is said to be client sided, and data that is stored and processed by a server, or any other computer you don't have direct access to is said to be server sided.
However, these phrases are a bit ambiguous, and the more hacking you do, the more you will find that there is in fact a fair bit of overlap between them.
In reality, the vast majority of the time when people say something is "server sided", it means that no one has discovered a way of hacking it, as opposed to it being unhackable.
in theory, the only way of changing fully server-sided values, values which the client has absolutely no influence on, is by gaining access to the server the values are stored on, which is not only usually incredibly difficult but also highly illegal. Like, jail time illegal.
However, since the majority of the time people use the term "server-sided" much more loosely, a lot of the time it is still possible to hack things people have deemed "server-sided". It usually requires doing some pretty low-level hacking (hacking with very little abstraction), though.
Packet editors are wonderful for this, as packet editors give you raw access to the communication channel between the client and the server. As a general rule, if a value on a server is hackable then a good packet editor can hack it.
A good example someone told me of how server sided values work:
Imagine you are playing a game with a friend. They pick a random number, and you have to guess it.
Your friend picks the number 5.
You guess the number 7.
He tells you you are wrong.
In this situation, your friend is the server and the number he picked is the server-sided value.
You have no control over the number he picks. The only thing you can change is the number you guess, which is a client-sided value.
Trying to hack a server-sided value is like trying to tell your friend that he cannot have chosen 5 because you said he chose 7, which makes no sense.
An example of a "server-side" value hack would be my boxhead bounty hunter money hack.
In boxhead bounty hunter, money is server sided. Every purchase you make with the in-game currency is processed by the server, and there is no way to directly set your money. You can't even tell the server you gained X money. I did however manage to indirectly hack the value by sending a packet to the server whenever a new money pickup was dropped saying "I picked up money drop #X", which meant I would gain money so fast it was effectively the same as being able to change my money directly.
In summary, there are two ways people use the term "server-sided":
1) A value stored/processed/ect by the server
2) A value stored/processed/ect by the server that the client has no influence on
Personally, I find it's best practice to only use the second use of the word.
And a tip to everyone out there: use the terms "server-sided" and "impossible" as sparingly as you can, cause when it comes to hacking almost anything is possible if you know how.
Also, for the record: most hackers just throw the term "server-sided" around to scare newbies. It's usually their way of politely saying "stop spamming me asking me to hack this goddam game" or "hacking this game is probably wayyy above your skill level".
server-sided is a term used too often and too generally, usually by people who don't understand it.
The concept of server sidedness is relatively simple.
data that is stored and processed by the client is said to be client sided, and data that is stored and processed by a server, or any other computer you don't have direct access to is said to be server sided.
However, these phrases are a bit ambiguous, and the more hacking you do, the more you will find that there is in fact a fair bit of overlap between them.
In reality, the vast majority of the time when people say something is "server sided", it means that no one has discovered a way of hacking it, as opposed to it being unhackable.
in theory, the only way of changing fully server-sided values, values which the client has absolutely no influence on, is by gaining access to the server the values are stored on, which is not only usually incredibly difficult but also highly illegal. Like, jail time illegal.
However, since the majority of the time people use the term "server-sided" much more loosely, a lot of the time it is still possible to hack things people have deemed "server-sided". It usually requires doing some pretty low-level hacking (hacking with very little abstraction), though.
Packet editors are wonderful for this, as packet editors give you raw access to the communication channel between the client and the server. As a general rule, if a value on a server is hackable then a good packet editor can hack it.
A good example someone told me of how server sided values work:
Imagine you are playing a game with a friend. They pick a random number, and you have to guess it.
Your friend picks the number 5.
You guess the number 7.
He tells you you are wrong.
In this situation, your friend is the server and the number he picked is the server-sided value.
You have no control over the number he picks. The only thing you can change is the number you guess, which is a client-sided value.
Trying to hack a server-sided value is like trying to tell your friend that he cannot have chosen 5 because you said he chose 7, which makes no sense.
An example of a "server-side" value hack would be my boxhead bounty hunter money hack.
In boxhead bounty hunter, money is server sided. Every purchase you make with the in-game currency is processed by the server, and there is no way to directly set your money. You can't even tell the server you gained X money. I did however manage to indirectly hack the value by sending a packet to the server whenever a new money pickup was dropped saying "I picked up money drop #X", which meant I would gain money so fast it was effectively the same as being able to change my money directly.
In summary, there are two ways people use the term "server-sided":
1) A value stored/processed/ect by the server
2) A value stored/processed/ect by the server that the client has no influence on
Personally, I find it's best practice to only use the second use of the word.
And a tip to everyone out there: use the terms "server-sided" and "impossible" as sparingly as you can, cause when it comes to hacking almost anything is possible if you know how.
Also, for the record: most hackers just throw the term "server-sided" around to scare newbies. It's usually their way of politely saying "stop spamming me asking me to hack this goddam game" or "hacking this game is probably wayyy above your skill level".
Labels:
editor,
flash,
hack,
hacking,
multiplayer,
packet,
server,
server-sided,
sided
Friday, April 12, 2013
The Future #3
So, I've been thinking about BPE a bit.
I'm thinking if I add UDP compatibility and add a few new hooks, I can make it automatically hook all new servers that are being connected to using some black magic. This could work as a partial solution for games that don't have static servers (think any game that has users host their own servers).
I'm going to need to make new plugin hooks that allow plugins to add server (and maybe port?) hooks for this to work, so I might add some functionality in for having different plugin "permissions".
Realistically, it would be incredibly easy to hack your way around any restrictions I put on the plugins, but it's worth trying out anyway. I might just add it as a programming exercise.
I don't have much spare time around uni, so it could take a month or two to add these features and write the plugin needed, but I think it's a pretty sound next step to take, that solves one of BPEs biggest drawbacks in some situations.
Progress is going to be slow for a while. Which is unfortunate, since this would be a pretty big update to add.
I'm thinking if I add UDP compatibility and add a few new hooks, I can make it automatically hook all new servers that are being connected to using some black magic. This could work as a partial solution for games that don't have static servers (think any game that has users host their own servers).
I'm going to need to make new plugin hooks that allow plugins to add server (and maybe port?) hooks for this to work, so I might add some functionality in for having different plugin "permissions".
Realistically, it would be incredibly easy to hack your way around any restrictions I put on the plugins, but it's worth trying out anyway. I might just add it as a programming exercise.
I don't have much spare time around uni, so it could take a month or two to add these features and write the plugin needed, but I think it's a pretty sound next step to take, that solves one of BPEs biggest drawbacks in some situations.
Progress is going to be slow for a while. Which is unfortunate, since this would be a pretty big update to add.
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:
open (or create and open if it doesn't exist):
in notepad or equivalent and add the line:
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
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.
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.
Labels:
actionscript,
adobe,
AS3,
AVM2,
decrypter,
decryption,
encryption,
manatee,
mochicrypt,
programming,
swf,
V3
Thursday, February 21, 2013
The Future #2
I've been working on a few things recently. Had some brilliant and questionable ideas.
Here's my itinerary for the near-ish future:
I'm working on a new, far simpler than the alternatives fix for the stage access loader bug. I have a theory that seems 100% sound that should fix it. The same method I'll use in it can also theoretically fix addChild bugs for any and all proxy-overriden classes.
My plan is this:
Override the DisplayObjectContainer class (or stage class, but DisplayObjectContainer could also have the added benefit of fixing all addChild problems for proxy-overriden classes).
Modify the addChild method to fix any problems.
It wouldn't be too difficult, and if I make it work, I'll release a new overrider loader template base.
I'm working on a new, better Universal Decrypter. The old one has too many flaws. I'd estimate it currently works on about 80% of encrypted SWFs out there. I've found a better method, which should work on somewhere between 95-99% of encrypted SWFs currently in the wild.
The new method is slightly harder to detect, but much harder to block. It leaves a much smaller footprint, and is generally better, however it is also much more complicated.
I'm also planning on using the above fix to make it more compatible.
I've also got another project I've been working on, that I'm being a little secretive about. Not sure if anything will come of it, since even if it does work, it'll require custom-built "broken" SWFs, since the normal compiler won't let me do the hacks I need. I've been experimenting with circular extension, and doing a lot of crazy bytecode hacks to try and make this thing work.
I'm also looking into writing a Windows network driver for BPE, since IP hooks are easy in Linux/Mac but require a driver in Windows.
I start university soon, so I might get less productive. Be prepared.
Here's my itinerary for the near-ish future:
I'm working on a new, far simpler than the alternatives fix for the stage access loader bug. I have a theory that seems 100% sound that should fix it. The same method I'll use in it can also theoretically fix addChild bugs for any and all proxy-overriden classes.
My plan is this:
Override the DisplayObjectContainer class (or stage class, but DisplayObjectContainer could also have the added benefit of fixing all addChild problems for proxy-overriden classes).
Modify the addChild method to fix any problems.
It wouldn't be too difficult, and if I make it work, I'll release a new overrider loader template base.
I'm working on a new, better Universal Decrypter. The old one has too many flaws. I'd estimate it currently works on about 80% of encrypted SWFs out there. I've found a better method, which should work on somewhere between 95-99% of encrypted SWFs currently in the wild.
The new method is slightly harder to detect, but much harder to block. It leaves a much smaller footprint, and is generally better, however it is also much more complicated.
I'm also planning on using the above fix to make it more compatible.
I've also got another project I've been working on, that I'm being a little secretive about. Not sure if anything will come of it, since even if it does work, it'll require custom-built "broken" SWFs, since the normal compiler won't let me do the hacks I need. I've been experimenting with circular extension, and doing a lot of crazy bytecode hacks to try and make this thing work.
I'm also looking into writing a Windows network driver for BPE, since IP hooks are easy in Linux/Mac but require a driver in Windows.
I start university soon, so I might get less productive. Be prepared.
Monday, February 4, 2013
Retro-style Raycasting #1
I've decided to start a series of almost tutorial-like posts with information on retro-style raycasting.
More specifically, this series of posts will cover my previous, current and future work with writing Wolfenstein-3D-like 2D array based raycasting engines with various optimizations.
There have been little to no major optimizations in the area of 2D array based raycasting, presumably because all modern hardware is too powerful to need it.
I've spent the last couple of years on and off building and improving raycasting engines.
Most of my early work was in AS2.
Here's a brief overview of my history with the pseudo-3D:
Ye Olde AS2 engines:
I originally started off by porting the code from this tutorial to AS2.
My initial port had graphical errors and a lot of bugs, but worked as a proof-of-concept, which I did about two years ago.
This was my V1 engine.
The V2 engine started off as a complete re-port of the code.
The V2 engine was bug-free, and started off as a texture-less raycaster.
The first improvement I made to the V2 engine was the addition of "fog" which was used as a basic way to implement basic pseudo-lighting.
The second improvement was to draw each wall face as a gradient-filled square instead of a crap-ton of vertical lines, which greatly decreased the time it took to draw the frame.
The V2 engine was also the first engine to support textures, for which I made two drawing algorithms, neither of which were particularly effective.
V3 was my first attempt at implementing my "extra trig" raycasting engine. I'll go into more detail on extra trig raycasting in another post.
The V3 engine, although not a complete failure, required an incredibly buggy piece of code to run: my "edge fix". to put it simply, the edge fix works by using the height of the previous raycasted wall to work out how high the current wall-face edge height is. the edge fix is effectively an interpolation algorithm for the raycaster, it gives a better looking image for many less rays, however, it was unfinished and had many bugs.
The V3 engine was discontinued very early on.
With the partial failure of the V3 engine, I started development on the V3-2 engine, which was built off the V2 engine because the V3 engine was deemed too unstable and bugged.
The V3-2 engine got to be quite powerful after several optimizations.
The V3-2 engine was the first engine to support sprites and have working z-sorting (z-sorting was done per-wall instead of per-pixel, which later gave me the idea of my Left-Right z-sorting algorithm which I may cover in a later post)
Also included in V3-2 was an incredibly snazzy minimap and basic multiplayer support.
V3-2 did not support textures, but was as bug-free and stable as V2.
V4 was a second, more advanced attempt at implementing my "extra-trig" raycasting method, built off of V3-2.
It was filled with bugs and hack-y fixes, but was beginning to show promise before I stopped development.
I stopped development on V4 a little over a year ago.
This was my last iteration of my AS2 raycaster.
About three or four months after the discontinuation of V4, I wrote a full, working AS3 Maze War prototype with a full server running on Adobe Air.
After that, I wrote a quick-and-dirty pseudo-3d engines that used vector-based maps and worked by calculating line intersections and trigonometry, it worked to an extent however there were visual bugs with it.
I dabbled with some experiments and wrote my own basic pseudo-3D engine in C++ that used vector-based maps, A similar "rasterization" technique to my "extra trig" raycasting method and was drawn using SDL late last year in preparation for writing an implementation of my Flood-Fill Raycaster-Like engine, which hasn't been started yet.
So far, I've come up with 3 Wolfenstein-like algorithms for raycasting that are faster than the conventional methods:
Extra-Trig Raycasting:
typically would use less than 50 rays to calculate any scene likely to exist in a game, resolution of display has no effect on rays used or calculation time, however amount of walls on the screen does. uses significantly more maths than regular raycasting but many major operations can be done using precalculation and look-up tables.
Estimated speed VS traditional method:
Depends on resolution, at 640x480, estimated 2-10 times faster.
Binary Raycaster:
Hard to estimate how many rays it would use - however it would definitely use significantly less. at a screen width of 640, maybe around 50-200 rays would be likely. More processing would have to be done per ray, but most likely not much. increased resolution means more calculation, more walls on screen means more calculation. significantly easier to implement than extra-trig
Estimated speed VS traditional method:
estimated 2-10 times faster
Flood-Fill Raycaster
Technically not a raycaster as it doesn't use rays. FFRC is one step on from Extra-Trig. Each tile in view is only checked once, trigonometry is used to do almost everything. heavy use of precalc and look-up tables can greatly increase efficiency. Calculation needed to do is proportional to tiles in view, both empty and filled. Screen resolution has no effect on processing needed.
Estimated speed VS traditional method:
estimated 5-100 times faster.
I have also been working on another perspective-based rasterizer which I believe would be limited to maze-war-like maps, but would allow players to look in all directions and possibly have free movement while still being more efficient than any other method above.
And that concludes a rough overview of my work on pseudo-3D engines. at least all the interesting stuff.
More specifically, this series of posts will cover my previous, current and future work with writing Wolfenstein-3D-like 2D array based raycasting engines with various optimizations.
There have been little to no major optimizations in the area of 2D array based raycasting, presumably because all modern hardware is too powerful to need it.
I've spent the last couple of years on and off building and improving raycasting engines.
Most of my early work was in AS2.
Here's a brief overview of my history with the pseudo-3D:
Ye Olde AS2 engines:
I originally started off by porting the code from this tutorial to AS2.
My initial port had graphical errors and a lot of bugs, but worked as a proof-of-concept, which I did about two years ago.
This was my V1 engine.
The V2 engine started off as a complete re-port of the code.
The V2 engine was bug-free, and started off as a texture-less raycaster.
The first improvement I made to the V2 engine was the addition of "fog" which was used as a basic way to implement basic pseudo-lighting.
The second improvement was to draw each wall face as a gradient-filled square instead of a crap-ton of vertical lines, which greatly decreased the time it took to draw the frame.
The V2 engine was also the first engine to support textures, for which I made two drawing algorithms, neither of which were particularly effective.
V3 was my first attempt at implementing my "extra trig" raycasting engine. I'll go into more detail on extra trig raycasting in another post.
The V3 engine, although not a complete failure, required an incredibly buggy piece of code to run: my "edge fix". to put it simply, the edge fix works by using the height of the previous raycasted wall to work out how high the current wall-face edge height is. the edge fix is effectively an interpolation algorithm for the raycaster, it gives a better looking image for many less rays, however, it was unfinished and had many bugs.
The V3 engine was discontinued very early on.
With the partial failure of the V3 engine, I started development on the V3-2 engine, which was built off the V2 engine because the V3 engine was deemed too unstable and bugged.
The V3-2 engine got to be quite powerful after several optimizations.
The V3-2 engine was the first engine to support sprites and have working z-sorting (z-sorting was done per-wall instead of per-pixel, which later gave me the idea of my Left-Right z-sorting algorithm which I may cover in a later post)
Also included in V3-2 was an incredibly snazzy minimap and basic multiplayer support.
V3-2 did not support textures, but was as bug-free and stable as V2.
V4 was a second, more advanced attempt at implementing my "extra-trig" raycasting method, built off of V3-2.
It was filled with bugs and hack-y fixes, but was beginning to show promise before I stopped development.
I stopped development on V4 a little over a year ago.
This was my last iteration of my AS2 raycaster.
About three or four months after the discontinuation of V4, I wrote a full, working AS3 Maze War prototype with a full server running on Adobe Air.
After that, I wrote a quick-and-dirty pseudo-3d engines that used vector-based maps and worked by calculating line intersections and trigonometry, it worked to an extent however there were visual bugs with it.
I dabbled with some experiments and wrote my own basic pseudo-3D engine in C++ that used vector-based maps, A similar "rasterization" technique to my "extra trig" raycasting method and was drawn using SDL late last year in preparation for writing an implementation of my Flood-Fill Raycaster-Like engine, which hasn't been started yet.
So far, I've come up with 3 Wolfenstein-like algorithms for raycasting that are faster than the conventional methods:
Extra-Trig Raycasting:
typically would use less than 50 rays to calculate any scene likely to exist in a game, resolution of display has no effect on rays used or calculation time, however amount of walls on the screen does. uses significantly more maths than regular raycasting but many major operations can be done using precalculation and look-up tables.
Estimated speed VS traditional method:
Depends on resolution, at 640x480, estimated 2-10 times faster.
Binary Raycaster:
Hard to estimate how many rays it would use - however it would definitely use significantly less. at a screen width of 640, maybe around 50-200 rays would be likely. More processing would have to be done per ray, but most likely not much. increased resolution means more calculation, more walls on screen means more calculation. significantly easier to implement than extra-trig
Estimated speed VS traditional method:
estimated 2-10 times faster
Flood-Fill Raycaster
Technically not a raycaster as it doesn't use rays. FFRC is one step on from Extra-Trig. Each tile in view is only checked once, trigonometry is used to do almost everything. heavy use of precalc and look-up tables can greatly increase efficiency. Calculation needed to do is proportional to tiles in view, both empty and filled. Screen resolution has no effect on processing needed.
Estimated speed VS traditional method:
estimated 5-100 times faster.
I have also been working on another perspective-based rasterizer which I believe would be limited to maze-war-like maps, but would allow players to look in all directions and possibly have free movement while still being more efficient than any other method above.
And that concludes a rough overview of my work on pseudo-3D engines. at least all the interesting stuff.
Labels:
3D,
actionscript,
as2,
AS3,
c++,
casting,
flash,
optimization,
pseudo,
rasterizer,
ray,
raycasting,
SDL,
wolfenstein
Tuesday, January 29, 2013
BPE Pre-Alpha V1.2
With this release comes good news and bad news.
The bad news is my prehook system doesn't work, so I'm going to have to find a new way of adding IP hooks. I've looked at several alternatives and found that I could quite easily make a cross-platform hook for Linux/Mac, but Windows doesn't like letting anyone do any low-ish level network hacks.
The good news is that PEW support has been added, which is a pretty big thing.
I've actually fixed the multiple server hook bug this time - connections now go to where they're meant to go.
I've also done a little more work on the GUI, adding the ability to actually remove port hooks and plugins.
It also no longer saves which hooks you were using. Now that Workspaces are implemented, it no longer needs to.
I'll update the Wiki with some documentation on Workspace files since to load plugins you have to edit the Workspace file via text editor.
I'd also like to point out that now Workspace files have been implemented, BPE is much more friendly towards having complete game hacks released on it since all an end-user needs to do to use a properly set up plugin-based hack is double click a Workspace file.
Feel free to spread this around.
People should use this.
DOWNLOAD
Changelog:
*BPE Pre-Alpha V1.2 (PEPAPI V1.1) (PEWVer 1.0)
- added PEW/Workspace support
- added "resetWorkspace", "hookServer", "hookPort" and "loadPlugin" Workspace commands
- added save & load workspace buttons
- save function saves server hooks and plugin hooks but not plugin configuration
- GUI improvements
- added ability to unload plugins via "Plugin Options" menu
- added ability to unhook ports via "Port Options" menu
- fixed bug where BPE would sometimes not close properly and hang in memory
- BPE now removes server hooks on close, use workspaces to save them
- made BPE the default file opener for .pew files (secretly done in V1.1)
- fixed bug where if more than one server was hooked all connections were routed to the first server in the list
- cleaned up shit tons of code
The bad news is my prehook system doesn't work, so I'm going to have to find a new way of adding IP hooks. I've looked at several alternatives and found that I could quite easily make a cross-platform hook for Linux/Mac, but Windows doesn't like letting anyone do any low-ish level network hacks.
The good news is that PEW support has been added, which is a pretty big thing.
I've actually fixed the multiple server hook bug this time - connections now go to where they're meant to go.
I've also done a little more work on the GUI, adding the ability to actually remove port hooks and plugins.
It also no longer saves which hooks you were using. Now that Workspaces are implemented, it no longer needs to.
I'll update the Wiki with some documentation on Workspace files since to load plugins you have to edit the Workspace file via text editor.
I'd also like to point out that now Workspace files have been implemented, BPE is much more friendly towards having complete game hacks released on it since all an end-user needs to do to use a properly set up plugin-based hack is double click a Workspace file.
Feel free to spread this around.
People should use this.
DOWNLOAD
Changelog:
*BPE Pre-Alpha V1.2 (PEPAPI V1.1) (PEWVer 1.0)
- added PEW/Workspace support
- added "resetWorkspace", "hookServer", "hookPort" and "loadPlugin" Workspace commands
- added save & load workspace buttons
- save function saves server hooks and plugin hooks but not plugin configuration
- GUI improvements
- added ability to unload plugins via "Plugin Options" menu
- added ability to unhook ports via "Port Options" menu
- fixed bug where BPE would sometimes not close properly and hang in memory
- BPE now removes server hooks on close, use workspaces to save them
- made BPE the default file opener for .pew files (secretly done in V1.1)
- fixed bug where if more than one server was hooked all connections were routed to the first server in the list
- cleaned up shit tons of code
Monday, January 28, 2013
Packet Editor Progress #7
It's been a while since I gave a progress update on BPE.
IP hooks (in the form of PreHook) are being added next release. unfortunately, for both hooks to work simultaneously, I have to re-design the entire hook system, which is why this release has been taking quite some time.
Since BPE was originally designed to only have one hook system, it makes things difficult because the server hooks and the port hooks were effectively the same body of code, and now need to be completely separated and mostly re-written (I'm splitting one class into about five to make it work).
There will be a "Server Manager" class that acts as an interface for the different hooks (including plugin interaction, which is controlled by the "Plugin Manager"), and a "Master Server" class that will organize the serversockets and pass connections through the different hooks as they come.
So, next release should fix the two most significant limitations of BPE: the multiple server hook bugs and not being able to hook IPs.
PEW files will probably start being implemented in the release after this next one. I'm focusing on fixing major bugs/limitations before adding new features.
And yes, I still need a Mac tester.
I'd also appreciate a Linux tester.
IP hooks (in the form of PreHook) are being added next release. unfortunately, for both hooks to work simultaneously, I have to re-design the entire hook system, which is why this release has been taking quite some time.
Since BPE was originally designed to only have one hook system, it makes things difficult because the server hooks and the port hooks were effectively the same body of code, and now need to be completely separated and mostly re-written (I'm splitting one class into about five to make it work).
There will be a "Server Manager" class that acts as an interface for the different hooks (including plugin interaction, which is controlled by the "Plugin Manager"), and a "Master Server" class that will organize the serversockets and pass connections through the different hooks as they come.
So, next release should fix the two most significant limitations of BPE: the multiple server hook bugs and not being able to hook IPs.
PEW files will probably start being implemented in the release after this next one. I'm focusing on fixing major bugs/limitations before adding new features.
And yes, I still need a Mac tester.
I'd also appreciate a Linux tester.
Subscribe to:
Posts (Atom)