-
Notifications
You must be signed in to change notification settings - Fork 9
Key Files
Robert Jordan edited this page Mar 9, 2020
·
9 revisions
The CatSystem2 engine looks for 3 different key files in the install folder on launch.
| FileName | Usage |
|---|---|
cs2_debug_key.dat |
Enables debug mode features |
direct.dat |
Required to launch the game without a CD Used when /document/APP/<direct>1</direct>
|
key.dat |
Usage is not yet known Likely used for CD installations |
enum KeyFileType {
Direct = 0, // "direct.dat"
Key = 1, // "key.dat" (requires volume serial number)
Cs2DebugKey = 2, // "cs2_debug_key.dat"
}
class KeyFileGenerator {
// Because "key.dat" requires volume serial number, we can supply our own when not running on the target machine
public static byte[] CreateKeyFile(KeyFileType keyType, [Optional] string vcode1, [Optional] uint? volumeSerialNumber) {
// Steps:
// 1. Create string using V_CODE1:
// 1.a Set string to V_CODE1 value
// 1.b (cs2_debug_key.dat) Append "@@--cs2-debug-key--@@"
// 2. Generate Seed for Mersenne Twister:
// 2.a GenerateTocSeed with new string (1.)
// 2.b (key.dat) Add volume serial number (volume with /Windows)
// 3. Generate Key File data:
// 3.a MersenneTwister.SetSeed with new Seed (2.)
// 3.b GenRand 16 UInt32 values (for key file data)
// 4. Encrypt Key File data:
// 4.a GenRand 16 more UInt32 values (for encryption key)
// 4.b Blowfish.SetKey with new 64-byte key
// 4.c Blowfish.Encrypt with key file data (3.b)
// Encountered with cs2_open.exe Toolset v4.01, not fully understood
// Likely used in the absence of /document/APP/v_code in startup.xml
// V_CODE (1) executable resource is ignore... I think
if (vcode1 == null)
vcode1 = "open_cs2";
// Extra step for "cs2_debug_key.dat": + "@@--cs2-debug-key--@@";
if (keyType == KeyFileType.Cs2DebugKey)
vcode1 += "@@--cs2-debug-key--@@";
// Use a CRC-32-style checksum of vcode for Mersenne Twister seed
uint vcode1Seed = GenerateTocSeed(vcode1String);
// Extra step for "key.dat": + volumeSerialNumber
if (keyType == KeyFileType.Key) {
if (!volumeSerialNumber.HasValue) {
// Get current machine's volume serial number
// For volume where /Windows is present
volumeSerialNumber = GetVolumeSerialNumber();
}
unchecked {
vcode1Seed += volumeSerialNumber.Value;
}
}
// Generate PRNG values for the key file's data and encryption key.
// One of the few times I’ve seen Mersenne Twister used to generate multiple numbers per seed in CatSystem2
MersenneTwister mt = new MersenneTwister();
mt.SetSeed(vcode1Seed);
uint[] tmpUIntBuffer = uint[16];
byte[] keyFileData = new byte[64]; // First 16 MT values
byte[] keyFileBlowfishKey = new byte[64]; // Next 16 MT values
// Generate key file data (mti=0,15)
for (int i = 0; i < 16; i++) {
tmpUIntBuffer[i] = mt.GenRand();
}
Buffer.BlockCopy(tmpUInts, 0, keyFileData, 0, 64);
// Generate key to encrypt key file data (mti=16,31)
for (int i = 0; i < 16; i++) {
tmpUIntBuffer[i] = mt.GenRand();
}
Buffer.BlockCopy(tmpUInts, 0, keyFileBlowfishKey, 0, 64);
// Encrypt key file data
Blowfish bf = new Blowfish();
blowfish.SetKey(keyFileBlowfishKey);
blowfish.Encrypt(keyFileData, 0, 64);
return keyFileData;
}
// Same as used in KIFINT archives
// CRC-32/BZIP2 (but we negate after every byte, instead of at the end)
public static uint GenerateTocSeed(string vcode) {
unchecked {
uint crc = 0xffffffff;
for (int i = 0; i < vcode.Length; i++) {
crc ^= ((uint) vcode[i] << 24);
for (int j = 0; j < 8; j++) {
if ((crc & 0x80000000) != 0)
crc = (crc << 1) ^ 0x04c11db7; // Polynomial
else
crc <<= 1;
}
crc = ~crc;
}
return crc;
}
}
// Get volume serial number on the target machine
// Default volume is where /Windows is present
public static uint GetVolumeSerialNumber([Optional] string rootPathName) {
if (rootPathName == null) {
// Get serial number of volume where /Windows is present
rootPathName = Environment.GetSpecialFolder(Environment.SpecialFolder.Windows);
}
bool result GetVolumeInformation(
Path.GetRoot(windowsRoot),
null, 0,
out uint volumeSerialNumber,
out _,
out _,
null, 0);
volumeSerialNumber = currentSerialNumber;
if (!result)
throw new Win32Exceprion();
return volumeSerialNumber;
}
#region Native Methods
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private extern static bool GetVolumeInformation(
string rootPathName, // only input
StringBuilder volumeNameBuffer,
int volumeNameSize,
out uint volumeSerialNumber,
out uint maximumComponentLength,
out uint fileSystemFlags, // flags that we don’t need
StringBuilder fileSystemNameBuffer,
int nFileSystemNameSize);
#endregion
}