PWAAT - decryptionCtrl

Back to Home

This file, which I'll call DecryptionControl, is responsible for decrypting the assets the game uses like music, sprites, etc. The game uses aes encryption to encrypt the assets, and this file is responsible for decrypting them. The decryption is done using the RijndaelManaged class in C#.


The password that the game uses for encryption is "u8DurGE2" and the salt is "6BBGizHE".


Here is the code the game uses to load and decrypt the assets:


  public byte[] load(string in_path)
  {
    byte[] array = File.ReadAllBytes(in_path);
    RijndaelManaged rijndaelManaged = new RijndaelManaged();
    rijndaelManaged.KeySize = 128;
    rijndaelManaged.BlockSize = 128;
    string text = "u8DurGE2";
    string text2 = "6BBGizHE";
    byte[] bytes = Encoding.UTF8.GetBytes(text2);
    Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(text, bytes);
    rfc2898DeriveBytes.IterationCount = 1000;
    rijndaelManaged.Key = rfc2898DeriveBytes.GetBytes(rijndaelManaged.KeySize / 8);
    rijndaelManaged.IV = rfc2898DeriveBytes.GetBytes(rijndaelManaged.BlockSize / 8);
    ICryptoTransform cryptoTransform = rijndaelManaged.CreateDecryptor();
    byte[] array2 = cryptoTransform.TransformFinalBlock(array, 0, array.Length);
    cryptoTransform.Dispose();
    return array2;
  }
      

Here's a breakdown of the code:


This function, `load`, takes a file path as an argument and returns a decrypted byte array.


  public byte[] load(string in_path)
      
It reads all bytes from the file and stores them in a byte array.

  byte[] array = File.ReadAllBytes(in_path);
      
An instance of `RijndaelManaged` is created for AES encryption, with key and block sizes set to 128 bits.

  RijndaelManaged rijndaelManaged = new RijndaelManaged();
  rijndaelManaged.KeySize = 128;
  rijndaelManaged.BlockSize = 128;
      
The password and salt are defined, and the salt is converted to a byte array.

  string text = "u8DurGE2"; 
  string text2 = "6BBGizHE";
  byte[] bytes = Encoding.UTF8.GetBytes(text2);
      
A `Rfc2898DeriveBytes` instance is created with the password and salt, and the iteration count is set to 1000. This generates the key and initialization vector (IV) for the `RijndaelManaged` instance.

  Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(text, bytes);
  rfc2898DeriveBytes.IterationCount = 1000;
  rijndaelManaged.Key = rfc2898DeriveBytes.GetBytes(rijndaelManaged.KeySize / 8);
  rijndaelManaged.IV = rfc2898DeriveBytes.GetBytes(rijndaelManaged.BlockSize / 8);
      
A decryptor is created, which decrypts the data and stores it in a new byte array. The decryptor is then disposed of.

  ICryptoTransform cryptoTransform = rijndaelManaged.CreateDecryptor();
  byte[] array2 = cryptoTransform.TransformFinalBlock(array, 0, array.Length);
  cryptoTransform.Dispose();
      
Finally, the decrypted data is returned.

  return array2;
      

Now that you know how the game decrypts the assets, you can now decrypt the assets yourself. In my case, I created a python program that decrypts the assets and saves them to a folder. Below, you can see the python code I used to decrypt the assets:


  import os
  import traceback
  from Crypto.Cipher import AES
  from Crypto.Protocol.KDF import PBKDF2
  from Crypto.Util.Padding import unpad
  import base64
  
  def decrypt_file(file_path, password, salt, key_size=128, block_size=128, iterations=1000):
  # Read the encrypted file
  with open(file_path, 'rb') as file:
      encrypted_data = file.read()
  
  # Derive the key and IV
  key_iv = PBKDF2(password, salt.encode(), dkLen=key_size//8 + block_size//8, count=iterations)
  key = key_iv[:key_size//8]
  iv = key_iv[key_size//8:]
  
  # Create the cipher object and decrypt the data
  cipher = AES.new(key, AES.MODE_CBC, iv)
  decrypted_data = unpad(cipher.decrypt(encrypted_data), block_size//8)
  
  return decrypted_data
  
  # Usage
  encrypted_folder = '[REPLACE WITH PATH TO FOLDER]'
  decrypted_folder = '[REPLACE WITH PATH TO FOLDER]'
  password = 'u8DurGE2'  # The password used for encryption
  salt = '6BBGizHE'  # The salt used for encryption
  
  # Lists to keep track of successful and unsuccessful decryptions
  successful_decryptions = []
  unsuccessful_decryptions = []
  
  # Check if the encrypted folder exists
  if not os.path.exists(encrypted_folder):
  print(f"Encrypted folder does not exist: {encrypted_folder}")
  else:
  print(f"Encrypted folder exists: {encrypted_folder}")
  
  # Check if there are any files in the encrypted folder
  if not any(os.scandir(encrypted_folder)):
  print(f"No files found in encrypted folder: {encrypted_folder}")
  else:
  print(f"Files found in encrypted folder: {encrypted_folder}")
  
  # Decrypt all files in the encrypted folder and its subfolders
  for root, dirs, files in os.walk(encrypted_folder):
  for file_name in files:
      file_path = os.path.join(root, file_name)
      
      # Create the same subdirectory structure in the decrypted folder
      relative_path = os.path.relpath(root, encrypted_folder)
      decrypted_root = os.path.join(decrypted_folder, relative_path)
      os.makedirs(decrypted_root, exist_ok=True)
  
      # Save the decrypted data to a file in the decrypted folder
      decrypted_file_path = os.path.join(decrypted_root, file_name)
  
      # Check if the decrypted file already exists
      if os.path.exists(decrypted_file_path):
          print(f"File {decrypted_file_path} already exists. Skipping.")
          continue
  
      try:
          print(f"Decrypting file: {file_path}")  # Print the file being decrypted
          decrypted_data = decrypt_file(file_path, password, salt)
  
          print(f"Saving decrypted file to: {decrypted_file_path}")  # Print the output file path
          with open(decrypted_file_path, 'wb') as file:
              file.write(decrypted_data)
  
          successful_decryptions.append(file_path)
      except Exception as e:
          print(f"An error occurred during decryption of file: {file_path}")
          print(traceback.format_exc())  # Print the full traceback of the exception
          unsuccessful_decryptions.append(file_path)
  
  # Print the successful and unsuccessful decryptions
  print(f"Successful decryptions: {len(successful_decryptions)}")
  for file_path in successful_decryptions:
  print(f"Successfully decrypted: {file_path}")
  
  print(f"Unsuccessful decryptions: {len(unsuccessful_decryptions)}")
  for file_path in unsuccessful_decryptions:
  print(f"Failed to decrypt: {file_path}")
      

This code reads all the files in the encrypted folder and its subfolders, decrypts them using the decrypt_file function, and saves the decrypted data to the decrypted folder. It also prints the successful and unsuccessful decryptions.
You can use this code to decrypt the assets of the game and access the original files. This can be useful for modding the game or extracting the assets for other purposes.
I hope this information helps you understand how the game decrypts its assets and how you can decrypt them yourself.