In this tutorial you will learn how to use checksums to prevent people from manually changing your map's bank files. We will look at this example map provided with this tutorial, although you don't really have to, it might be really helpfull. The example map uses the following library: Hash Lib
to generate SHA-256 hash codes. You will need this library too in your map too generate hash codes.
Click here if you are new to hash functions and want to know/learn about them.
Be sure to read ithe library's usage instructions here: http://www.sc2mapster.com/assets/md5-hash/#w-usage.
In some maps, especially in RPG's, people might want to save their character/unit data so that they can go on next time they play that map again. But when you save your data to a bank, players can manually change this information. You can encrypt the data, but you can also use a checksum when you don't need to hide the data anyway, that is probably easier. In both occasions, when players really want to tweak your bank files, they will try and open your map and learn from your triggers anyway. I think that's as safe as you can get at this moment.
So in this tutorial we are going to save a unit with a checksum and load it back again while checking if the data is not modified.
Design
This map will start black.
Once you type "-start" you start of with a new unit.
If you type "-load" you start of with your saved unit. You can type "-save" during the game to save your unit.
With your unit you can make kills and those kills will be saved with your unit's health. That information then can be used to load your unit again. Your kill score will be the information that should not me modified manually by players and is the reason why we will use a checksum with out bank data, the kill score in this map may be comparable to a hero level in RPG maps.
The Trick
This is what the example map does when you save and load:
Save
When you save, it saves the kills and life of your unit to a bank file. It also generates a hash code (our checksum) of your unit's life and kills and then also saves it to that bank file.
Load
When you load, it reads the bank file to get the unit's life and kills again. Then, it generates a hash codes from the loaded life and kills values. Then it checks if the generated hash code is the same as the checksum in the bank file. If they match, then the unit's life and kills should be the same as they were when they were saved, and that means that we can assume it's integrity.
Looking at the Triggers
When we look at the triggers of the example map, we can see 4 triggers (Start, Save, Load and Defeat), 1 function (Generate Checksum) and 1 global variable (Character Unit). We will look at them in a minute, but first I'll explain what the triggers do.
Start - This trigger gets fired when you type "-start", this basically creates a starting unit for you to play with.
Defeat - This trigger gets fired when you die, this basically ends your game in defeat.
Save - This trigger gets fired when you type "-save", this trigger saves your unit (safe).
Load - This trigger gets fired when you type "-load", this trigger loads your unit while checking for integrity.
The Start and Defeat triggers are not that interesting for this tutorial, so is the Character Unit variable, we want to know how the saving and loading goes!
But first, we need to know what the function Generate Checksum does, so lets look at it.
Generate Checksum Function
The function has the following parameters:
Unit Life <Real>
Kills <Integer>
It takes those parameters and calculates a hash value of it with the following actions:
Initialize Hash Input
Add Real Unit Life To Hash Input
Add Integer Kills To Hash Input
Add String "Salt" To Hash Input
General - Return (Generate SHA-256 Hash Code)
As you can see it initializes the hash input, then adds the unit's life and kills to it, then adds the string "Salt" to it and then returns the generated hash value (as a string). Notice that the SHA-256 hash algorithm is used.
The reason why it also adds "Salt" to it is because it would be probably too hard for a player to try and figure out how the checksum was built.
So this function calculates the checksum for us.
Save
When we look at the Save trigger, we see a few actions. Lets go trough them:
Variable - Set Unit Life = (Character Unit Life (Current))
Variable - Set Kill Score = (Character Unit kills (Current))
This basically stores the life and kills of your unit in variables.
Variable - Set Checksum = (Generate Checksum(Unit Life, Kill Score))
This passes those variables to the Generate Checksum function to get our checksum.
Bank - Open bank "checksumexample" for player 1
Bank - Store real Unit Life as "life" of section "unit" in bank (Last opened bank)
Bank - Store integer Kill Score as "kills" of section "unit" in bank (Last opened bank)
Bank - Store string Checksum as "checksum" of section "unit" in bank (Last opened bank)
Bank - Save bank (Last opened bank)
This stores your unit's life and score and the just calculated checksum in a bank.
Load
When we look at the Load trigger, we see this:
Bank - Open bank "checksumexample" for player 1
Variable - Set Unit Life = (Load "life" of section "unit" from bank (Last opened bank) as real value)
Variable - Set Kill Score = (Load "kills" of section "unit" from bank (Last opened bank) as integer value)
Variable - Set Loaded Checksum = (Load key "checksum" of section "unit" from bank (Last opened bank) as a String)
This opens the bank and loads the bank values to variables, the Loaded Checksum variables now holds the checksum value saved in the bank.
This generates a new checksum from the life and kills values from the bank, so if nothing is modified in the bank file, this should be the same as the loaded checksum. That's why there is an if-statement like this following the last action:
General - If (Conditions) then do (Actions) else do (Actions)
If
Loaded Checksum == Generated Checksum
Then
Here are some actions to create our loaded unit from the loaded life and kills values.
Else
Here it lets you know that your unit data is corrupt.
Testing
If you start the map, you will see that the map starts black.
If you type "-start", you can see that your unit spawns and that you are ready to play.
Then, when you attack some units, you can see you lost some health and made some kills.
So now, if you save, you safe the data of your current unit to a bank file, and as you will see, the checksum will be pronounced to you.
If we now restart the game, and then type "-load", you will see that the checksum loaded from the bank and the checksum generated from the unit's loaded life and kills values match, because we didn't change anything in the bank file. So you unit gets loaded.
But what if we change the bank file now, even a little bit? Lets do that.
Lets go to the bank file (which is usually located at <your documents folder>/Starcraft II/Banks), it's filename is "checksumexample.SC2Bank". You can see this when you open it:
And now you start the map again to load your saved (and modified) unit.
You can see that the map finds out that the unit data is not correct anymore. Your data should be safe now for players trying to edit your bank files (until they hack your map ofcourse).
Feedback
Please post any question or feedback relating to this tutorial and let me know what you think!
Lets just hope Blizzard decides to start locking maps properly soon, because bored people are desperate people, this will only slightly slow them down.
But nicely done, this will work perfectly to prevent everyday people from "hacking" your bank files.
If someone actually uses this, all you need to do to break the scheme is run your test map on the new bank and see what the new checksum is supposed to be then change the checksum.
Without proper locking(which I suspect is impossible under the current architecture), nothing can be made even close to secure. You can't encrypt because you can't keep the keys private. You can't hash because as soon as they know the hashing algorithm, it's easy for them to generate a correct checksum. You can't sign because that's encrypting a hash... The only thing you can do is slow down lazy people.
Introduction
In this tutorial you will learn how to use checksums to prevent people from manually changing your map's bank files. We will look at this example map provided with this tutorial, although you don't really have to, it might be really helpfull. The example map uses the following library: Hash Lib
to generate SHA-256 hash codes. You will need this library too in your map too generate hash codes.
Click here if you are new to hash functions and want to know/learn about them.
Be sure to read ithe library's usage instructions here: http://www.sc2mapster.com/assets/md5-hash/#w-usage.
In some maps, especially in RPG's, people might want to save their character/unit data so that they can go on next time they play that map again. But when you save your data to a bank, players can manually change this information. You can encrypt the data, but you can also use a checksum when you don't need to hide the data anyway, that is probably easier. In both occasions, when players really want to tweak your bank files, they will try and open your map and learn from your triggers anyway. I think that's as safe as you can get at this moment.
So in this tutorial we are going to save a unit with a checksum and load it back again while checking if the data is not modified.
Design
This map will start black.
Once you type "-start" you start of with a new unit.
If you type "-load" you start of with your saved unit. You can type "-save" during the game to save your unit.
With your unit you can make kills and those kills will be saved with your unit's health. That information then can be used to load your unit again. Your kill score will be the information that should not me modified manually by players and is the reason why we will use a checksum with out bank data, the kill score in this map may be comparable to a hero level in RPG maps.
The Trick
This is what the example map does when you save and load:
Save
When you save, it saves the kills and life of your unit to a bank file. It also generates a hash code (our checksum) of your unit's life and kills and then also saves it to that bank file.
Load
When you load, it reads the bank file to get the unit's life and kills again. Then, it generates a hash codes from the loaded life and kills values. Then it checks if the generated hash code is the same as the checksum in the bank file. If they match, then the unit's life and kills should be the same as they were when they were saved, and that means that we can assume it's integrity.
Looking at the Triggers
When we look at the triggers of the example map, we can see 4 triggers (Start, Save, Load and Defeat), 1 function (Generate Checksum) and 1 global variable (Character Unit). We will look at them in a minute, but first I'll explain what the triggers do.
The Start and Defeat triggers are not that interesting for this tutorial, so is the Character Unit variable, we want to know how the saving and loading goes!
But first, we need to know what the function Generate Checksum does, so lets look at it.
Generate Checksum Function
The function has the following parameters:
It takes those parameters and calculates a hash value of it with the following actions:
As you can see it initializes the hash input, then adds the unit's life and kills to it, then adds the string "Salt" to it and then returns the generated hash value (as a string). Notice that the SHA-256 hash algorithm is used.
The reason why it also adds "Salt" to it is because it would be probably too hard for a player to try and figure out how the checksum was built.
So this function calculates the checksum for us.
Save
When we look at the Save trigger, we see a few actions. Lets go trough them:
This basically stores the life and kills of your unit in variables.
This passes those variables to the Generate Checksum function to get our checksum.
This stores your unit's life and score and the just calculated checksum in a bank.
Load
When we look at the Load trigger, we see this:
This opens the bank and loads the bank values to variables, the Loaded Checksum variables now holds the checksum value saved in the bank.
This generates a new checksum from the life and kills values from the bank, so if nothing is modified in the bank file, this should be the same as the loaded checksum. That's why there is an if-statement like this following the last action:
If
Then
Else
Testing
If you start the map, you will see that the map starts black.
If you type "-start", you can see that your unit spawns and that you are ready to play.
Then, when you attack some units, you can see you lost some health and made some kills.
So now, if you save, you safe the data of your current unit to a bank file, and as you will see, the checksum will be pronounced to you.
If we now restart the game, and then type "-load", you will see that the checksum loaded from the bank and the checksum generated from the unit's loaded life and kills values match, because we didn't change anything in the bank file. So you unit gets loaded.
But what if we change the bank file now, even a little bit? Lets do that.
Lets go to the bank file (which is usually located at <your documents folder>/Starcraft II/Banks), it's filename is "checksumexample.SC2Bank". You can see this when you open it:
So lets set your kills from 9 to 10. Like this:
And now you start the map again to load your saved (and modified) unit.
You can see that the map finds out that the unit data is not correct anymore. Your data should be safe now for players trying to edit your bank files (until they hack your map ofcourse).
Feedback
Please post any question or feedback relating to this tutorial and let me know what you think!
@magnificence7: Go
Look forward to this.
The tutorial is done now, but may change of course. :P
Lets just hope Blizzard decides to start locking maps properly soon, because bored people are desperate people, this will only slightly slow them down.
But nicely done, this will work perfectly to prevent everyday people from "hacking" your bank files.
If someone actually uses this, all you need to do to break the scheme is run your test map on the new bank and see what the new checksum is supposed to be then change the checksum.
Without proper locking(which I suspect is impossible under the current architecture), nothing can be made even close to secure. You can't encrypt because you can't keep the keys private. You can't hash because as soon as they know the hashing algorithm, it's easy for them to generate a correct checksum. You can't sign because that's encrypting a hash... The only thing you can do is slow down lazy people.
@Catalisk: Go
Well, yes, but it's not just slowing down lazy people, it is also reducing the number of people that try to do that.
EDIT: But I think maps are never gonna have a really good protection.
Nice work.
I saw some sha256 values in Starbattle. I think the creator uses your lib. :D
SHA512 could be fun. :D
Most people will use sha256 because it is secure and don't cost too many space,
which is limited by blizzard.
Avoid array == write all functions involving array into the main.
I like it, because it is simple.
thx for your lib. Could you change the license to MIT or BSD ?
Or write All Rights Reserved into the source code.