mirror of
https://github.com/crskycode/GARbro.git
synced 2024-12-23 19:34:15 +08:00
(XP3): added optional AES decryption.
This commit is contained in:
parent
99ab258678
commit
b713a01031
@ -354,6 +354,10 @@ NextEntry:
|
||||
// assume no scripts are compressed using LZ4, return decompressed stream right away
|
||||
return DecompressLz4 (xp3_entry, header, input);
|
||||
}
|
||||
else if (0xA590D7FD == header.ToUInt32 (0)) // cZLIB magic
|
||||
{
|
||||
return DecompressCz (xp3_entry, header, input);
|
||||
}
|
||||
|
||||
if (0xFE == header[0] && 0xFE == header[1] && header[2] < 3 && 0xFF == header[3] && 0xFE == header[4])
|
||||
return DecryptScript (header[2], input, xp3_entry.UnpackedSize);
|
||||
@ -389,6 +393,84 @@ NextEntry:
|
||||
return new Lz4Stream (input, info);
|
||||
}
|
||||
|
||||
Stream DecompressCz (Xp3Entry entry, byte[] src_header, Stream input)
|
||||
{
|
||||
var header = new byte[15];
|
||||
Buffer.BlockCopy (src_header, 0, header, 0, Math.Min (header.Length, src_header.Length));
|
||||
if (header.Length > src_header.Length)
|
||||
input.Read (header, src_header.Length, header.Length - src_header.Length);
|
||||
header[4] ^= 0x11;
|
||||
header[5] ^= 0x7F;
|
||||
header[6] ^= 0x9A;
|
||||
byte key = header[4];
|
||||
int unpacked_size = CzDecryptInt (header, 7, key);
|
||||
int packed_size = CzDecryptInt (header, 11, key);
|
||||
var data = new byte[packed_size];
|
||||
input.Read (data, 0, packed_size);
|
||||
input.Dispose();
|
||||
data = CzDecryptData (data);
|
||||
input = new BinMemoryStream (data);
|
||||
if ('C' == header[4])
|
||||
input = new ZLibStream (input, CompressionMode.Decompress);
|
||||
return input;
|
||||
}
|
||||
|
||||
static int CzDecryptInt (byte[] data, int offset, byte key)
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
data[offset+i] ^= (byte)(key ^ CzHeaderKey[i]);
|
||||
}
|
||||
return data.ToInt32 (offset);
|
||||
}
|
||||
|
||||
static byte[] CzDecryptData (byte[] data)
|
||||
{
|
||||
int padded_size = data.Length - 5;
|
||||
int original_size = padded_size - (data[padded_size+1] ^ data[padded_size]);
|
||||
uint iv_seed = data.ToUInt32 (padded_size+1) ^ 0xBFBFBFBFu;
|
||||
using (var aes = Aes.Create())
|
||||
{
|
||||
aes.Mode = CipherMode.CBC;
|
||||
aes.Padding = PaddingMode.Zeros;
|
||||
aes.Key = CzDefaultKey;
|
||||
aes.IV = CzCreateIV (iv_seed);
|
||||
using (var enc = new MemoryStream (data, 0, padded_size))
|
||||
using (var dec = new InputCryptoStream (enc, aes.CreateDecryptor()))
|
||||
{
|
||||
var original = new byte[original_size];
|
||||
dec.Read (original, 0, original_size);
|
||||
return original;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] CzCreateIV (uint seed)
|
||||
{
|
||||
var state = new uint[4];
|
||||
state[0] = 123456789; // field_0
|
||||
state[1] = 972436830; // field_4
|
||||
state[2] = 524018621; // field_8
|
||||
state[3] = seed; // field_C
|
||||
var iv = new byte[16];
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
uint a = state[3];
|
||||
uint b = state[0] ^ (state[0] << 11);
|
||||
state[0] = state[1];
|
||||
state[1] = state[2];
|
||||
state[2] = a;
|
||||
state[3] = b ^ a ^ ((b ^ (a >> 11)) >> 8);
|
||||
iv[i] = (byte)state[3];
|
||||
}
|
||||
return iv;
|
||||
}
|
||||
|
||||
static readonly byte[] CzHeaderKey = { 0x9D, 0x1D, 0x9A, 0xF2 };
|
||||
static readonly byte[] CzDefaultKey = {
|
||||
0x91, 0x10, 0xFC, 0x75, 0x45, 0x8F, 0xB5, 0xE6, 0xFE, 0xAC, 0xBA, 0x44, 0x76, 0x58, 0xC2, 0x1A
|
||||
};
|
||||
|
||||
Stream DecryptScript (int enc_type, Stream input, uint unpacked_size)
|
||||
{
|
||||
using (var reader = new BinaryReader (input, Encoding.Unicode, true))
|
||||
|
Loading…
x
Reference in New Issue
Block a user