I'm reverse engineering a game's .pak file format and trying to reconstruct its decryption process in C or Python.
Here’s what I extracted from IDA pseudocode. The function DecryptPak() uses a custom 16-byte key and two main routines: dec_data() to expand the key, and dec_block2() to decrypt 16-byte blocks.
This is the full decompiled code:
Keys3 DCD 0x46970E9C, 0x4BC0685E, 0x59056186, 0xBCA2491E
dword_106D1CB50 DCD 0xEB92B, 0x3A0AE783, 0x9E3B5C67, 0xADDBDABF, 0x7B7484CB
__const:0000000106D1CB50 ; DATA XREF: dec_data+7C↑o
__const:0000000106D1CB50 DCD 0x49156C63, 0xC79AB5E7, 0x79EC9CFF, 0x1725BEAB, 0x2FB89CA3
__const:0000000106D1CB50 DCD 0x24808AD7, 0xDDD28B1F, 0x4740DA4B, 0xBBC3EA73, 0x247B30E7
__const:0000000106D1CB50 DCD 0x91BE385F, 0x401248B, 0x45FCD3A3, 0x530B4CE7, 0xC68DD35F
__const:0000000106D1CB50 DCD 0xE3D16C2B, 0x4F698C13, 0x6B92C747, 0x769EFB1F, 0x4C73BE9B
__const:0000000106D1CB50 DCD 0xC942B193, 0xAD80D827, 0x372FB33F, 0x13CB6AAB, 0x2BDC0AA3
__const:0000000106D1CB50 DCD 0x17A4A247, 0xD5E96CAF
keys1 DCB 0x34, 0x66, 0x25, 0x74, 0x89, 0x78, 0xE4, 0xA9, 0x5A
__const:0000000106D1CA3E ; DATA XREF: decrypt_uint32_t+8↑o
__const:0000000106D1CA3E ; decrypt_uint32_t2+8↑o
__const:0000000106D1CA3E DCB 0x41, 0xBC, 0x7A, 0xD6, 0x16, 0x21, 0x23, 0x4D, 0x61
__const:0000000106D1CA3E DCB 0xDA, 0x94, 0x9B, 0xDF, 0x13, 0x3C, 0x69, 0x3A, 0x31
__const:0000000106D1CA3E DCB 0xA, 0x5F, 0xD7, 0x99, 0x95, 0xF1, 0xAE, 0x72, 0x3D
__const:0000000106D1CA3E DCB 7, 0x60, 0x24, 0xB6, 0x98, 0xEE, 0xC4, 0xA2, 0x2D
__const:0000000106D1CA3E DCB 0x88, 0xDD, 0x8D, 4, 0xEA, 0xBB, 0x11, 0xCA, 0x3E
__const:0000000106D1CA3E DCB 0x5D, 0xA1, 0xF6, 0x3F, 0xB0, 0x97, 0x80, 0x47, 0x2B
__const:0000000106D1CA3E DCB 0xA6, 0xE6, 0xF7, 0xD9, 0xB1, 0x59, 0xC0, 0x7C, 0xBE
__const:0000000106D1CA3E DCB 0x54, 0x28, 0xB7, 0x7E, 0x4F, 0xF8, 0x43, 0x6E, 0xA0
__const:0000000106D1CA3E DCB 0x50, 0xE, 0xF5, 0x90, 0xB8, 0xFB, 0xA3, 0x7B, 0x62
__const:0000000106D1CA3E DCB 0x19, 0x46, 3, 0x2A, 0xB9, 0x8F, 0x9F, 0x77, 0xB4
__const:0000000106D1CA3E DCB 0x5B, 0x83, 0x87, 8, 0xEB, 0xE2, 0x1E, 0x42, 0xF0
__const:0000000106D1CA3E DCB 0xF, 0xE8, 0x71, 0x6A, 0x75, 0xAD, 0x55, 0x1F, 0xB5
__const:0000000106D1CA3E DCB 0xAB, 0x33, 0xFA, 0x7F, 0x15, 0xBD, 0x85, 0xD8, 6
__const:0000000106D1CA3E DCB 0x68, 0xB3, 0x52, 0x30, 0x48, 0xB, 0, 0xED, 0xEF, 0xB2
__const:0000000106D1CA3E DCB 0x57, 0x8E, 0xE7, 0x6C, 0xD5, 0xE5, 0x2E, 0x53, 0x82
__const:0000000106D1CA3E DCB 5, 0xF9, 0x81, 0xF4, 0x56, 0xBF, 0x8C, 0x4B, 0xE3
__const:0000000106D1CA3E DCB 0xDB, 0x4A, 0x91, 0x4C, 0x2C, 0xD3, 0x40, 0x29, 0x4E
__const:0000000106D1CA3E DCB 0x20, 0x14, 0x36, 0x79, 9, 0x6F, 0xD1, 0x37, 0xE0
__const:0000000106D1CA3E DCB 0x39, 0xC, 0x8A, 0x92, 0x38, 0x12, 0x35, 0x6D, 0xE1
__const:0000000106D1CA3E DCB 0xFD, 0x93, 0x9A, 0x17, 0xD4, 0xC9, 0x9C, 0x6B, 0x84
__const:0000000106D1CA3E DCB 0x26, 0x9D, 0xAF, 0x76, 0xC1, 0x9E, 0xD0, 0x96, 0xC5
__const:0000000106D1CA3E DCB 0xCB, 0xE9, 0x73, 0x49, 0xD2, 0xCD, 0x64, 0xC3, 0xC7
__const:0000000106D1CA3E DCB 1, 0x7D, 0xF3, 0xAC, 0xFC, 0xDE, 0xA4, 0x44, 0x32
__const:0000000106D1CA3E DCB 0x1B, 0xC2, 0xBA, 0x1C, 2, 0xC6, 0x27, 0x45, 0x8B
__const:0000000106D1CA3E DCB 0xF2, 0x18, 0xA7, 0x10, 0x51, 0x1D, 0xC8, 0xCF, 0x63
__const:0000000106D1CA3E DCB 0xFF, 0x2F, 0xD, 0x58, 0xCE, 0x65, 0xA5, 0xDC, 0x1A
__const:0000000106D1CA3E DCB 0x3B, 0x86, 0xFE, 0x22, 0x5C, 0xA8, 0x5E, 0x67, 0xAA
__const:0000000106D1CA3E DCB 0xEC, 0x70, 0xCC, 0, 0
__int64 __fastcall dec_data(__int64 result, __int64 a2)
{
__int64 v3; // x20
__int64 i; // x21
__int64 v5; // x21
__int64 v6; // x20
__int64 *v7; // x21
int v8; // w9
__int64 v9[2]; // [xsp+8h] [xbp-48h] BYREF
v9[0] = 0LL;
v9[1] = 0LL;
if ( result && a2 )
{
v3 = result;
for ( i = 0LL; i != 4; i = v5 + 1 )
{
result = swap_end(v3, i);
*((_DWORD *)v9 + v5) = Keys3[v5] ^ result;
}
v6 = 0LL;
v7 = v9;
while ( v6 != 32 )
{
result = decrypt_uint32_t2(*((_DWORD *)v7 + (((_BYTE)v6 + 2) & 3)) ^ *((_DWORD *)v7 + ((v6 + 1) & 3)) ^ (unsigned int)(*((_DWORD *)v7 + (((_BYTE)v6 - 1) & 3)) ^ dword_106D1CB50[v6]));
v8 = *((_DWORD *)v7 + (v6 & 3)) ^ result;
*((_DWORD *)v7 + (v6 & 3)) = v8;
*(_DWORD *)(a2 + 4 * v6++) = v8;
}
}
return result;
}
__int64 __fastcall decrypt_uint32_t2(unsigned int a1)
{
int v1; // w9
int v2; // w8
unsigned __int64 v3; // t2
v1 = 0;
v2 = 0;
while ( v1 != 32 )
{
v2 |= keys1[(unsigned __int8)(a1 >> v1)] << v1;
v1 += 8;
}
HIDWORD(v3) = v2;
LODWORD(v3) = v2;
return (unsigned int)(v3 >> 9) ^ __ROR4__(v2, 19) ^ v2;
}
__int64 __fastcall dec_block2(__int64 result, __int64 a2, __int64 a3)
{
__int64 v5; // x21
__int64 i; // x22
__int64 v7; // x22
unsigned int *v8; // x20
__int64 *v9; // x21
unsigned int v10; // t1
__int64 v11; // x8
__int64 j; // x9
__int64 v13[2]; // [xsp+8h] [xbp-48h] BYREF
v13[0] = 0LL;
v13[1] = 0LL;
if ( result && a2 && a3 )
{
v5 = result;
for ( i = 0LL; i != 4; ++i )
{
result = swap_end(v5, i);
*((_DWORD *)v13 + i) = result;
}
v7 = 0LL;
v8 = (unsigned int *)(a2 + 124);
v9 = v13;
while ( v7 != 32 )
{
v10 = *v8--;
result = decrypt_uint32_t(*((_DWORD *)v9 + (((_BYTE)v7 + 2) & 3)) ^ *((_DWORD *)v9 + ((v7 + 1) & 3)) ^ *((_DWORD *)v9 + (((_BYTE)v7 - 1) & 3)) ^ v10);
*((_DWORD *)v9 + (v7++ & 3)) ^= result;
}
v11 = a3 + 1;
for ( j = 12LL; j != -4; j -= 4LL )
{
*(_DWORD *)(v11 - 1) = bswap32(*(_DWORD *)((char *)v13 + j));
v11 += 4LL;
}
}
return result;
}
__int64 __fastcall decrypt_with_key(unsigned int a1)
{
int v1; // w9
int v2; // w8
unsigned __int64 v3; // t2
v1 = 0;
v2 = 0;
while ( v1 != 32 )
{
v2 |= keys1[(unsigned __int8)(a1 >> v1)] << v1;
v1 += 8;
}
HIDWORD(v3) = v2;
LODWORD(v3) = v2;
return (unsigned int)(v3 >> 22) ^ __ROR4__(v2, 30) ^ __ROR4__(v2, 14) ^ __ROR4__(v2, 8) ^ v2;
}
__int64 __fastcall DecryptPak(__int64 a1, unsigned int a2, __int64 a3)
{
__int64 result; // x0
unsigned int i; // w21
int v7; // w21
__int128 v8[8]; // [xsp+0h] [xbp-B0h] BYREF
memset(v8, 0, sizeof(v8));
result = dec_data(a3, (__int64)v8);
for ( i = 0; i < a2; i = v7 + 16 )
result = dec_block2(a1 + i, (__int64)v8, a1 + i);
return result;
}
My goal: How should I implement this decryption chain in clean C or Python? Does the logic of key expansion and the round loop look similar to known lightweight ciphers, or is it a custom algorithm?