Created
April 4, 2022 22:21
-
-
Save Nenkai/745ab77c10a1a57e38e39f3d1705bcfb to your computer and use it in GitHub Desktop.
Cheat Engine CETRAINER file unpacking
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// LinqPad file | |
// Does not include code initial archive unpacking (CET_Archive.dat) | |
// File has file names + inflated data chunks, just use offzip - https://aluigi.altervista.org/mytoolz.htm#offzip | |
void Main() | |
{ | |
string baseFolder = "folder"; | |
var data = File.ReadAllBytes(baseFolder + "CET_TRAINER.CETRAINER"); | |
Decrypt(data); | |
var magic = Encoding.ASCII.GetString(data.AsSpan(0, 7)); | |
if (magic != "BRICKES") | |
throw new InvalidDataException("Invalid magic"); | |
File.WriteAllBytes(baseFolder + "CET_TRAINER.CETRAINER.dec", data); | |
byte[] decomp = DecompressFile(data.Skip(7).ToArray()); // Skip magic | |
File.WriteAllBytes(baseFolder + "CET_TRAINER.CETRAINER.dec.decomp", decomp); | |
ProcessCTFile(decomp, baseFolder); | |
} | |
void ProcessCTFile(byte[] data, string outputDir) | |
{ | |
XmlDocument doc = new XmlDocument(); | |
using var xmlStream = new MemoryStream(data); | |
doc.Load(xmlStream); | |
var root = doc["CheatTable"]; | |
// Extract forms and files | |
// Forms can be openned with MiTeC DFM Editor - https://www.mitec.cz/dfm.html | |
foreach (XmlElement file in root.SelectNodes("Forms")[0]) | |
{ | |
var textContent = file.InnerText; | |
var fileName = file.Name; | |
byte[] decodedDataBuffer = new byte[(textContent.Length / 5) * 4 + (textContent.Length % 5)]; | |
Base85ToBin(textContent, decodedDataBuffer); | |
byte[] actualFileData = DecompressFile(decodedDataBuffer); | |
File.WriteAllBytes(outputDir + ("FORM_" + fileName), actualFileData); | |
} | |
foreach (XmlElement file in root.SelectNodes("Files")[0]) | |
{ | |
var textContent = file.InnerText; | |
var fileName = file.Name; | |
byte[] decodedDataBuffer = new byte[(textContent.Length / 5) * 4 + (textContent.Length % 5)]; | |
Base85ToBin(textContent, decodedDataBuffer); | |
byte[] actualFileData = DecompressFile(decodedDataBuffer); | |
File.WriteAllBytes(outputDir + fileName, actualFileData); | |
} | |
} | |
// https://github.com/cheat-engine/cheat-engine/blob/dd1f2f83d45d61f7ff692038e322e32bae7fb96e/Cheat%20Engine/OpenSave.pas#L1420 | |
void Decrypt(byte[] data) | |
{ | |
int v11 = 2; | |
if (data.Length >= 2) | |
{ | |
--v11; | |
do | |
{ | |
++v11; | |
data[v11] ^= data[v11 - 2]; | |
} | |
while (data.Length - 1 > v11); | |
} | |
v11 = data.Length - 2; | |
if (v11 >= 0) | |
{ | |
++v11; | |
do | |
{ | |
--v11; | |
data[v11] ^= data[v11 + 1]; | |
} | |
while (v11 > 0); | |
} | |
byte v10 = 0xCD; | |
int v3 = data.Length - 1; | |
v11 = 0; | |
if (v3 >= 0) | |
{ | |
--v11; | |
do | |
{ | |
data[++v11] ^= v10; | |
v10 += 2; | |
} | |
while (v3 > v11); | |
} | |
} | |
byte[] DecompressFile(byte[] data) | |
{ | |
using var ms = new MemoryStream(data.ToArray()); | |
using var output = new MemoryStream(); | |
using (var ds = new DeflateStream(ms, CompressionMode.Decompress)) | |
{ | |
byte[] sizeBuf = new byte[sizeof(int)]; | |
ds.Read(sizeBuf); | |
int uncompSize = BinaryPrimitives.ReadInt32LittleEndian(sizeBuf); | |
byte[] uncompData = new byte[uncompSize]; | |
ds.Read(uncompData); | |
return uncompData; | |
} | |
} | |
// https://github.com/cheat-engine/cheat-engine/blob/dd1f2f83d45d61f7ff692038e322e32bae7fb96e/Cheat%20Engine/custombase85.pas#L97 | |
void Base85ToBin(string inputStringBase85, byte[] outData) | |
{ | |
const string customBase85 = "0123456789" + | |
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + | |
"abcdefghijklmnopqrstuvwxyz" + | |
"!#$%()*+,-./:;=?@[]^_{}"; | |
var size = inputStringBase85.Length; | |
var i = 0; | |
var j = 0; | |
var testt = "Hello world".IndexOf("o"); | |
while (i < size) | |
{ | |
var a = (customBase85.IndexOf(inputStringBase85[i])) * 85 * 85 * 85 * 85; | |
i++; | |
if (i < size) | |
{ | |
a = a + (customBase85.IndexOf(inputStringBase85[i])) * 85 * 85 * 85; | |
i++; | |
} | |
if (i < size) | |
{ | |
a = a + (customBase85.IndexOf(inputStringBase85[i])) * 85 * 85; | |
i++; | |
} | |
if (i < size) | |
{ | |
a = a + (customBase85.IndexOf(inputStringBase85[i])) * 85; | |
i++; | |
} | |
if (i < size) | |
{ | |
a = a + (customBase85.IndexOf(inputStringBase85[i])); | |
i++; | |
outData[j + 0] = (byte)((a >> 24) & 0xFF); | |
outData[j + 1] = (byte)((a >> 16) & 0xFF); | |
outData[j + 2] = (byte)((a >> 8) & 0xFF); | |
outData[j + 3] = (byte)(a & 0xFF); | |
j += 4; | |
} | |
switch (size % 5) | |
{ | |
case 2: | |
a = a + 84 * 85 * 85 + 84 * 85 * 84; | |
outData[j + 0] = (byte)((a >> 24) & 0xFF); | |
break; | |
case 3: | |
a = a + 84 * 85 * 84; | |
outData[j + 0] = (byte)((a >> 24) & 0xFF); | |
outData[j + 1] = (byte)((a >> 16) & 0xFF); | |
break; | |
case 4: | |
a = a + 84; | |
outData[j + 0] = (byte)((a >> 24) & 0xFF); | |
outData[j + 1] = (byte)((a >> 16) & 0xFF); | |
outData[j + 1] = (byte)((a >> 8) & 0xFF); | |
break; | |
} | |
} | |
} | |
void GetBytecode() | |
{ | |
// Encoding from CE + lua's "encodeFunction" | |
// From https://github.com/cheat-engine/cheat-engine/blob/dd1f2f83d45d61f7ff692038e322e32bae7fb96e/Cheat%20Engine/LuaHandler.pas#L13180 | |
string luabytecode = "c-n1]%{T;S5XWcf9FpJ^/fMgq6HkJoM]1(3ip4^r!E4!=RTko:Bx$O+6)sTE$yexuY/u#t95w32fnk2k$A4zJS939j-M39_(qD[t:iwUu;}O(uoDrOpGr%r_2sjM5g[;f,Icp4Oua;i,fv2C@z+yLa74($:L[C1n1hBFUmZXyKRs,@BH0$Rtw!{QFf7yo30T_e!eYkO/?@W%I3Bn-8=XW,{v%6dt#Z:[S:#H^Nl]7LdYj5]7TR=A=ipOy[+9mUjDxJh*n#xF{5{iui94$nJF3AZk)-XuvoB6iWj#55W[mUPkef;F;)D({8E?fVc*PM26]xyLZA63Lj.^"; | |
byte[] decodedDataBuffer = new byte[(luabytecode.Length / 5) * 4 + (luabytecode.Length % 5)]; | |
Base85ToBin(luabytecode, decodedDataBuffer); | |
// Skip 2 bytes and decompress "zlib" to get LuaC bytecode | |
// Use LuaDec (might need to compile to the correct version afterwards or you will get a precompiled header chunk error) | |
// Check 0x05 once decompressed, indicates the version of the lua compiler i.e 0x53 = 5.3 | |
// May also need to compile it as x64 if you get a size_t mismatch error | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment