Thumbs upDecrypting and Encrypting CA:Classic Attributes Files

Posts 1–6 of 6 · Page 1 of 1
Decrypting and Encrypting CA:Classic Attributes Files
I did not write a single line of this code. Welcome, to the era of using ChatGPT to exploit a video game. This took almost 4 hours of debugging with ChatGPT to get right, but I got it. This is the source code for Decrypting and Encrypting CA: Classic's Attributes txt files.

What's needed:
- Combat Arms: classic (obviously)
- Visual Studio 2022 with C# WinForms
- .NET 6.0 installed
- A few text boxes and buttons (see image below of how I set it up)
- RezXtract and RezInject and Rez Bypass if you'd like to use a modded rez file (in the rez section)
- BouncyCastle (get this with NuGet Manager in your project)

Shout out to @SoNoS and @qqshutup for their shared info on the Text Decrypter thread. I wanted to make a free to use version, and big shout out to ChatGPT on this.

Video explaining it and showing it working

Attributes TwoFish Key (hex) and IV
Code:
Key: 620C724A2FF22C975B5A2B9C21430820227B3D2800193AAA4C  F3128803AC3ABD
IV: 56B83E3F68B60F0F29357BED335E5642
My WinForm

Code:
Needed
Code:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Crypto.Parameters;
Decryption
Code:
public static void DecryptFile(string encryptedFilePath, string decryptedFilePath, string keyHex, string ivHex)
        {
            byte[] encryptedBytes = File.ReadAllBytes(encryptedFilePath);
            byte[] key = ConvertHexStringToByteArray(keyHex);
            byte[] iv = ConvertHexStringToByteArray(ivHex);
            byte[] decryptedBytes;
            Decrypt(encryptedBytes, out decryptedBytes, key, iv);
            string decryptedText = Encoding.UTF8.GetString(decryptedBytes);
            SaveDecryptedTextToFile(decryptedText, decryptedFilePath);
        }

        public static void Decrypt(byte[] cipherText, out byte[] plainText, byte[] key, byte[] iv)
        {
            var engine = new TwofishEngine();
            var cipher = new CbcBlockCipher(engine);
            var parameters = new ParametersWithIV(new KeyParameter(key), iv);
            cipher.Init(false, parameters);

            int blockSize = cipher.GetBlockSize();
            int numChunks = cipherText.Length / blockSize;

            var plainTextList = new List<byte>(cipherText.Length);

            for (int i = 0; i < numChunks; i++)
            {
                byte[] inputBlock = new byte[blockSize];
                byte[] outputBlock = new byte[blockSize]; 

                Array.Copy(cipherText, i * blockSize, inputBlock, 0, blockSize);
                int processedBytes = cipher.ProcessBlock(inputBlock, 0, outputBlock, 0);

                plainTextList.AddRange(outputBlock);
            }

            plainText = plainTextList.ToArray();
        }
Encrypt
Code:
public static void EncryptFile(string filePath, string encryptedFilePath, string keyHex, string ivHex)
        {
            byte[] plainText = File.ReadAllBytes(filePath);
            byte[] key = ConvertHexStringToByteArray(keyHex);
            byte[] iv = ConvertHexStringToByteArray(ivHex);
            byte[] encryptedBytes;
            Encrypt(plainText, out encryptedBytes, key, iv);
            File.WriteAllBytes(encryptedFilePath, encryptedBytes);
            MessageBox.Show("File encrypted and saved successfully.");
        }

        public static void Encrypt(byte[] plainText, out byte[] encryptedBytes, byte[] key, byte[] iv)
        {
            var engine = new TwofishEngine();
            var cipher = new CbcBlockCipher(engine);
            var parameters = new ParametersWithIV(new KeyParameter(key), iv);
            cipher.Init(true, parameters);

            int blockSize = cipher.GetBlockSize();
            int numChunks = (int)Math.Ceiling((double)plainText.Length / blockSize);

            var encryptedData = new List<byte>(plainText.Length);

            for (int i = 0; i < numChunks; i++)
            {
                byte[] inputBlock = new byte[blockSize];
                byte[] outputBlock = new byte[blockSize];

                int length = Math.Min(blockSize, plainText.Length - i * blockSize);
                Array.Copy(plainText, i * blockSize, inputBlock, 0, length);

                int processedBytes = cipher.ProcessBlock(inputBlock, 0, outputBlock, 0);

                encryptedData.AddRange(outputBlock);
            }

            encryptedBytes = encryptedData.ToArray();
        }
Save File and ConvertoHex
Code:
private static byte[] ConvertHexStringToByteArray(string hexString)
        {
            float numberChars = hexString.Length;
            MessageBox.Show(hexString.Length.ToString());
            byte[] bytes = new byte[Convert.ToInt16(numberChars / 2)];
            for (long i = 0; i < numberChars; i += 2)
            {
                bytes[i / 2] = Convert.ToByte(hexString.Substring((int)i, 2), 16);
            }
            return bytes;
        }

        private static void SaveDecryptedTextToFile(string decryptedText, string decryptedFilePath)
        {
            File.WriteAllText(decryptedFilePath, decryptedText);
        }
Do not expect this to work perfectly, there's ONE ISSUE ChatGPT couldn't solve and I'm too stupid
No matter what you Decrypt, the decryption stops when the new decrypted text file matches the same file size as the encrypted text file.

For Example: If Encrypted.txt is 19,242bytes in file size, decrypted.txt will never be larger than that, when it should be.

I figured this is still worth posting because the only public way to do this is with a tool that requires a 300 dollar license key (not blaming someone else for using this). If someone fixes the code to do the full decryption of a file, I'll edit the post and make a few videos on what you can do with Attributes editing.
@Flengo @killingspree888 @meme ... welcome to the new age of hacking CA lol
Get GPT-4 to fix the code so it decrypts the full file

Edit: I did it for you, try it out not sure if it works @supercarz1991

The problem you're encountering likely comes from the way you're handling padding. In your current implementation, you're decrypting in blocks, but not accounting for the padding that might have been added during the encryption process. If the plaintext was not an exact multiple of the block size, padding would be added. When decrypting, this padding needs to be removed.

To fix this, you can use the PaddedBufferedBlockCipher which will handle the padding for you. It should be initialized with both the block cipher (in this case, CbcBlockCipher) and a padding scheme. PKCS7Padding is a commonly used padding scheme, so I'll use that in this example.

Here's your updated Decrypt method:

Code:
public static void Decrypt(byte[] cipherText, out byte[] plainText, byte[] key, byte[] iv)
{
    var engine = new TwofishEngine();
    var cipher = new CbcBlockCipher(engine);
    var blockCipher = new PaddedBufferedBlockCipher(cipher, new Pkcs7Padding());
    var parameters = new ParametersWithIV(new KeyParameter(key), iv);

    blockCipher.Init(false, parameters); // false for decryption

    plainText = new byte[blockCipher.GetOutputSize(cipherText.Length)];
    int length = blockCipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0);
    length += blockCipher.DoFinal(plainText, length);

    // Trim the output array to the actual size
    var result = new byte[length];
    Array.Copy(plainText, 0, result, 0, length);
    plainText = result;
}
This updated Decrypt method first creates a PaddedBufferedBlockCipher that uses a CbcBlockCipher with TwofishEngine and Pkcs7Padding to handle padding. Then, it initializes the cipher with the key and IV for decryption.

The ProcessBytes method is used to process the cipherText, and the result is written to the plainText array. The length of the output is stored in the length variable. The DoFinal method is used to finish the decryption process and handle any remaining bytes. It also returns the length of the final output, which is added to the length variable.

Finally, a new array of the correct size is created, and the decrypted data is copied into it. This ensures that the plainText array does not contain any unnecessary trailing zeros.

Make sure to update your references to use Org.BouncyCastle.Crypto.Paddings.Pkcs7Padding and Org.BouncyCastle.Crypto.Paddings.PaddedBufferedBlo ckCipher.

Also, note that Encoding.UTF8.GetString(decryptedBytes); may not always produce the expected result, because it assumes that the decrypted file is a UTF8-encoded text file. If the file contains binary data or text encoded in a different character set, this could produce incorrect results or cause an error. It may be better to simply write the decrypted bytes directly to a file.

Quote Originally Posted by Flengo View Post
Get GPT-4 to fix the code so it decrypts the full file

Edit: I did it for you, try it out not sure if it works @supercarz1991

The problem you're encountering likely comes from the way you're handling padding. In your current implementation, you're decrypting in blocks, but not accounting for the padding that might have been added during the encryption process. If the plaintext was not an exact multiple of the block size, padding would be added. When decrypting, this padding needs to be removed.

To fix this, you can use the PaddedBufferedBlockCipher which will handle the padding for you. It should be initialized with both the block cipher (in this case, CbcBlockCipher) and a padding scheme. PKCS7Padding is a commonly used padding scheme, so I'll use that in this example.

Here's your updated Decrypt method:

Code:
public static void Decrypt(byte[] cipherText, out byte[] plainText, byte[] key, byte[] iv)
{
    var engine = new TwofishEngine();
    var cipher = new CbcBlockCipher(engine);
    var blockCipher = new PaddedBufferedBlockCipher(cipher, new Pkcs7Padding());
    var parameters = new ParametersWithIV(new KeyParameter(key), iv);

    blockCipher.Init(false, parameters); // false for decryption

    plainText = new byte[blockCipher.GetOutputSize(cipherText.Length)];
    int length = blockCipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0);
    length += blockCipher.DoFinal(plainText, length);

    // Trim the output array to the actual size
    var result = new byte[length];
    Array.Copy(plainText, 0, result, 0, length);
    plainText = result;
}
This updated Decrypt method first creates a PaddedBufferedBlockCipher that uses a CbcBlockCipher with TwofishEngine and Pkcs7Padding to handle padding. Then, it initializes the cipher with the key and IV for decryption.

The ProcessBytes method is used to process the cipherText, and the result is written to the plainText array. The length of the output is stored in the length variable. The DoFinal method is used to finish the decryption process and handle any remaining bytes. It also returns the length of the final output, which is added to the length variable.

Finally, a new array of the correct size is created, and the decrypted data is copied into it. This ensures that the plainText array does not contain any unnecessary trailing zeros.

Make sure to update your references to use Org.BouncyCastle.Crypto.Paddings.Pkcs7Padding and Org.BouncyCastle.Crypto.Paddings.PaddedBufferedBlo ckCipher.

Also, note that Encoding.UTF8.GetString(decryptedBytes); may not always produce the expected result, because it assumes that the decrypted file is a UTF8-encoded text file. If the file contains binary data or text encoded in a different character set, this could produce incorrect results or cause an error. It may be better to simply write the decrypted bytes directly to a file.

at this line
Code:
length += blockCipher.DoFinal(plainText, length);
it errors out with
Code:
Org.BouncyCastle.Crypto.DataLengthException: 'Last block incomplete in decryption'
same error I ran into with gpt3 and 4
ladies and gentlemen the future is here
Quote Originally Posted by supercarz1991 View Post
at this line
Code:
length += blockCipher.DoFinal(plainText, length);
it errors out with
Code:
Org.BouncyCastle.Crypto.DataLengthException: 'Last block incomplete in decryption'
same error I ran into with gpt3 and 4
Hmm, it might just be a limitation with the library or a bug. I would post an issue about it on the repository if there is one to confirm.
Wow that looks interesting...
Posts 1–6 of 6 · Page 1 of 1

Post a Reply

Similar Threads

Tags for this Thread

None

Need help?