Believe it or not, you actually can see the
differences!
What is data01.bin?
- Stores the index & the code saved within a unsigned integer stored in bytes.
data01.bin is simply copied to the following directory on pressing OK:
- "%userProfile%\Documents\My Games\The Saboteur™\SaveGames"
- This is then followed up by adding a few bytes to the top of the file header on success.
Header Information The one stored in the main installation directory.
header is: 00 00 00 00 00 00 00 00 A8 7F B8 21 3D B9 5D CA
The Unlocked in the SaveGames directory on success
header is: DB 5A 00 00 8C 72 C2 3B A8 7F B8 21 3D B9 5D CA
This means that:
The two differences are the following:
00 00 00 00 00 00 00 00 //Not unlocked / Default
DB 5A 00 00 8C 72 C2 3B //Unlocked and enabled
This also means that:
- Alternatively... One can make a unlocker that simply opens the original data01.bin in the game's directory.
- Edit the first 8 bytes of it to correspond with the above
- Save it to their saves folder removing the whole key code issue.
- Enjoy it in game.
After curiously throwing VideoSetup.exe into ida, I realized this application is a C# app.
Then decompiled it into readable source code with DnSpy
- I also realized my theory about data01.bin was partially correct.
Now deep diving into the decompiled '
VideoSetup" src..
We can start to see a clearer picture of what's going on to put this issue to rest.
Reversing VideoSetup:
There's a form called
UnlockKey.cs while opening and clicking the forms ok button we start to see the code responsible.
The code corresponds to the following and we see the above method may not work.
Can you figure it out?
```
string text = this.KeyField.Text;
if (text.Length < 19)
{
MessageBox.Show(this.TextManager.GetLocalizedString(LocalizedID.Unlock_KeyTooShort));
return;
}
uint num = this.CalcStrCRC(text);
for (int i = 0; i < this.KeyCRCTableLength; i++)
{
if (this.KeyCRCTable[i] == num)
{
MessageBox.Show(this.TextManager.GetLocalizedString(LocalizedID.Unlock_Success));
this.WriteCodeToFile((uint)i, 0x1557F8D2 ^ num); //Remember this for later.
base.Close();
return;
}
}
this.WriteCodeToFile(0, 0);
MessageBox.Show(this.TextManager.GetLocalizedString(LocalizedID.Unlock_Failed));
```
If you answered: uint num = this.CalcStrCRC(text); then you'd be right. This CRC32 table is checked.
Answer: The code shown has a CRC32 check that is written to those 8 bytes.
```
private uint CalcStrCRC(string strIn)
{
char[] array = strIn.ToCharArray();
if (array.Length == 0)
{
return 0;
}
uint num = 0x811C9DC5;
for (int i = 0; i < array.Length; i++)
{
num ^= (uint)(array[i] | ' ');
num *= 0x1000193;
}
return (num ^ 0x2A) * 0x1000193;
}
```
I noticed something odd in this line
```
this.WriteCodeToFile((uint)i, 0x1557F8D2 ^ num); //Remember this for later.
```
This is also referenced here:
```
private const uint NaziKnifeCRC = 358086866U;
```
Converting: 358086866U to Hex is: 0x1557F8D2 where have we seen this before?
Meaning this CRC32 check only ensures it matches that value or it will throw an error.
- This is good cause these bytes never change! We can use another persons file that is already unlocked if we wish!
App Message Responses We start to look a bit deeper and start to have the following messages:
```
// Already have it unlocked
this.m_LocalizedText[23] = "You already entered a valid unlock code.";
//On success
this.m_LocalizedText[24] = "Congratulations! You have unlocked the knife stealth kill. To use it, sneak up behind an enemy, then press and hold <SK1>.";
//Invalid code entered
this.m_LocalizedText[25] = "You entered an invalid code.";
//Code 2 short
this.m_LocalizedText[26] = "Unlock code too short.";
```
In turn it calls:
- If Successful
```
MessageBox.Show(this.TextManager.GetLocalizedString(LocalizedID.Unlock_Success));
```
or
- If Failure
```
MessageBox.Show(this.TextManager.GetLocalizedString(LocalizedID.Unlock_Failed));
MessageBox.Show(this.TextManager.GetLocalizedString(LocalizedID.Unlock_KeyTooShort));
MessageBox.Show(this.TextManager.GetLocalizedString(LocalizedID.Unlock_AlreadyEntered));
```
Finally, we start to see what's actually going on by going into the "
WriteCodeToFile" function.
- Open save folder path
- Setup a BinaryWriter to write to our directory.
- Write data01.bin index and code
- Write CRC32 hash of the file to the 8 byte header.
- Close the file and clean up and you're done
```
private void WriteCodeToFile(uint index, uint code)
{
try
{
if (!Directory.Exists(this.TextManager.GetSaveFolder()))
{
Directory.CreateDirectory(this.TextManager.GetSaveFolder());
}
FileStream fileStream = new FileStream(this.TextManager.GetSaveFolder() + "data01.bin", FileMode.Create);
BinaryWriter binaryWriter = new BinaryWriter(fileStream);
binaryWriter.Write(index);
binaryWriter.Write(code);
for (int i = 0; i < this.KeyCRCTableLength; i++)
{
binaryWriter.Write(this.KeyCRCTable[i]);
}
binaryWriter.Close();
}
catch (IOException ex)
{
MessageBox.Show(ex.Message);
}
}
```
Hope this helps ;)
Cheers!
- BTB