As discussed in my previous blog post, PBO Hiding in DayZ, the PBO file format is used to store and organize game assets, such as textures, models, and scripts. It is designed to be lightweight and efficient to keep load times and performance overhead to a minimum. However, the ease of accessing PBO files has led to issues with unauthorized modifications and content theft. This has led Bohemia Interactive and some server owners/content creators to utilize an encrypted PBO file format, also known as ebo.
WHAT IS PBO ENCRYPTION?
PBO encryption is primarily used to protect the intellectual property of content creators and server owners. Key advantages of the ebo file format include:
- Increasing the difficulty of cheating by reducing visibility into the addon/game behavior
- Safeguarding custom content/intellectual property from unauthorized access/distribution
PBO encryption involves encrypting the contents (without the header) of a PBO file using the RC4 algorithm.
This renders the file unreadable by typical PBO browsers such as PBO Manager and BankRev.
Bohemia only encrypts one PBO in DayZ: worlds_enoch.ebo
. This EBO contains critical game assets that Bohemia would like to prevent the distribution of. However, the game must decrypt the file in memory at some point in time. So when and how does it decrypt the file?
EXISTING METHODS OF DECRYPTION
The inner workings of PBO encryption are no secret. It has been documented multiple times on public cheating forums. The game and public decryptors perform the following steps when decrypting EBO files:
- Read the encrypted file and its associated metadata
- Generate a temporary key by performing operations on the global decryption key based on the current chunk position
- Iterate through the encrypted buffer in chunks of 1024 bytes
- Decrypt the current chunk using the (alleged) RC4 algorithm
- Store the decrypted chunk in memory
- Continue executing steps 3-5 until all chunks have been decrypted and processed
- Clean up and finalize
There are two key prerequisites to the existing methods of EBO decryption:
- Knowledge of the decryption algorithm
- Analyzing and identifying the specific decryption method used
- Familiarity with the mathematical operations utilized by the algorithm
- Awareness of the procedures required for decryption
- Knowledge of the decryption key
- Access to the key used for decryption
- Ability to determine the key’s format/structure
- Knowledge of how the decryption algorithm utilizes the key
A NEW METHOD
What if we want to future-proof ourselves and alleviate the repetitive tasks of reverse-engineering and reimplementing a volatile key/encryption algorithm? Well, the answer is simple: have the game do the work for us.
CONCEPT
As the game initializes, it reads all PBO and EBO files into its memory. These addons are represented as a QFBank. A QFBank contains relevant information required for the game to understand an EBO/PBO. The QFBank class contains a particularly interesting field: a pointer to a file buffer interface (IFileBuffer). The file buffer exposes several crucial functions, such as reading the file’s data.
No additional operations (such as decryption) are performed on the addon when it is read from disk. This means that EBO files are stored in memory in their encrypted form, and we can’t just locate the data buffer in memory and read pre-decrypted data from it. However, we could call the function the game uses to decrypt the data within the buffer.
IMPLEMENTATION
The implementation is fairly simple but relies on the PBO iteration and identification detailed in my previous post.
- Locate the GFileBanks .data pointer
- I used a signature in PBO Ninja to reduce my future workload when the game updates.
- Find the desired QFBank
- Loop through GFileBanks until the file bank representing the desired EBO is found.
- Get the desired QFBanks file buffer
- Inside the QFBank, there is a pointer to an IFileBuffer or a child class of IFileBuffer.
- Dereferencing this pointer gives us the file buffer which contains the QFBanks file date in memory.
- Call IFileBuffer::Read
- IFileBuffer::Read is the 10th index of IFileBuffers virtual table. The function returns a boolean and takes four parameters: an IFileBuffer pointer, a pointer to an output buffer, a 64-bit integer representing the offset to start reading from, and another 64-bit integer representing the number of bytes to read.
- An output buffer needs to be allocated prior to calling the function.
- By allocating an output buffer that is at least the size of the file on disk, and calling IFIleBuffer::Read with an offset of zero and a length of the size of the file, the files decrypted bytes will be stored in the output buffer.
- Save the decrypted data
- Now that the decrypted bytes have been obtained, they can be stored in any manner. In PBO Ninja, I wrote the decrypted bytes to disk.
CONCLUSION
In this post, we have explored the process EBO encryption/decryption in DayZ, a widely used method utilized to protect game assets and other intellectual property.
Although the existing methods of EBO decryption are functional, they are not efficient. The new approach I have implemented has many advantages over static EBO decryption and leverages the game’s own function to handle the decryption process.
Any information in this post could become outdated at any point in time. The content of this blog post is intended for educational purposes only. By reading this blog post, you agree to use the information solely for educational purposes and to abide by all applicable laws and regulations concerning the protection of intellectual property. The author and publisher of this blog post shall not be held responsible for any damages, direct or indirect, arising from the misuse of the information provided.