From 33bdae86c2345db9514addbf9d5a698eada56fb2 Mon Sep 17 00:00:00 2001 From: Ladislav Zezula Date: Mon, 17 May 2021 12:57:54 +0200 Subject: Added alignment-aware block encryption/decryption --- src/SBaseCommon.cpp | 111 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 90 insertions(+), 21 deletions(-) (limited to 'src/SBaseCommon.cpp') diff --git a/src/SBaseCommon.cpp b/src/SBaseCommon.cpp index 43113a1..b81d492 100644 --- a/src/SBaseCommon.cpp +++ b/src/SBaseCommon.cpp @@ -414,52 +414,121 @@ DWORD GetDefaultSpecialFileFlags(DWORD dwFileSize, USHORT wFormatVersion) //----------------------------------------------------------------------------- // Encrypting/Decrypting MPQ data block +static DWORD EncryptUInt32Unaligned(PDWORD DataPointer, DWORD i, DWORD dwXorKey) +{ + LPBYTE pbDataPointer = (LPBYTE)(DataPointer + i); + LPBYTE pbXorKey = (LPBYTE)(&dwXorKey); + DWORD dwValue32; + + // Retrieve the value + dwValue32 = ((DWORD)pbDataPointer[0] << 0x00) | + ((DWORD)pbDataPointer[1] << 0x08) | + ((DWORD)pbDataPointer[2] << 0x10) | + ((DWORD)pbDataPointer[3] << 0x18); + + // Perform unaligned XOR + pbDataPointer[0] = (pbDataPointer[0] ^ pbXorKey[0]); + pbDataPointer[1] = (pbDataPointer[1] ^ pbXorKey[1]); + pbDataPointer[2] = (pbDataPointer[2] ^ pbXorKey[2]); + pbDataPointer[3] = (pbDataPointer[3] ^ pbXorKey[3]); + return dwValue32; +} + void EncryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey1) { - LPDWORD DataBlock = (LPDWORD)pvDataBlock; + LPDWORD DataPointer = (LPDWORD)pvDataBlock; DWORD dwValue32; DWORD dwKey2 = 0xEEEEEEEE; // Round to DWORDs dwLength >>= 2; - // Encrypt the data block at array of DWORDs - for(DWORD i = 0; i < dwLength; i++) + // We need different approach on non-aligned buffers + if(STORMLIB_DWORD_ALIGNED(DataPointer)) { - // Modify the second key - dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + for(DWORD i = 0; i < dwLength; i++) + { + // Modify the second key + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; - dwValue32 = DataBlock[i]; - DataBlock[i] = DataBlock[i] ^ (dwKey1 + dwKey2); + // We can use 32-bit approach, when the buffer is aligned + DataPointer[i] = (dwValue32 = DataPointer[i]) ^ (dwKey1 + dwKey2); - dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); - dwKey2 = dwValue32 + dwKey2 + (dwKey2 << 5) + 3; + dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); + dwKey2 = dwValue32 + dwKey2 + (dwKey2 << 5) + 3; + } + } + else + { + for(DWORD i = 0; i < dwLength; i++) + { + // Modify the second key + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + + // The data are unaligned. Make sure we don't cause data misalignment error + dwValue32 = EncryptUInt32Unaligned(DataPointer, i, (dwKey1 + dwKey2)); + + dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); + dwKey2 = dwValue32 + dwKey2 + (dwKey2 << 5) + 3; + } } } +static DWORD DecryptUInt32Unaligned(PDWORD DataPointer, DWORD i, DWORD dwXorKey) +{ + LPBYTE pbDataPointer = (LPBYTE)(DataPointer + i); + LPBYTE pbXorKey = (LPBYTE)(&dwXorKey); + + // Perform unaligned XOR + pbDataPointer[0] = (pbDataPointer[0] ^ pbXorKey[0]); + pbDataPointer[1] = (pbDataPointer[1] ^ pbXorKey[1]); + pbDataPointer[2] = (pbDataPointer[2] ^ pbXorKey[2]); + pbDataPointer[3] = (pbDataPointer[3] ^ pbXorKey[3]); + + // Retrieve the value + return ((DWORD)pbDataPointer[0] << 0x00) | + ((DWORD)pbDataPointer[1] << 0x08) | + ((DWORD)pbDataPointer[2] << 0x10) | + ((DWORD)pbDataPointer[3] << 0x18); +} + void DecryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey1) { - LPDWORD DataBlock = (LPDWORD)pvDataBlock; + PDWORD DataPointer = (PDWORD)pvDataBlock; DWORD dwValue32; DWORD dwKey2 = 0xEEEEEEEE; - // Check for alignment - //assert(((size_t)(pvDataBlock) & 0x03) == 0); - // Round to DWORDs dwLength >>= 2; - // Decrypt the data block at array of DWORDs - for(DWORD i = 0; i < dwLength; i++) + // We need different approach on non-aligned buffers + if(STORMLIB_DWORD_ALIGNED(DataPointer)) { - // Modify the second key - dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + for(DWORD i = 0; i < dwLength; i++) + { + // Modify the second key + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + + // We can use 32-bit approach, when the buffer is aligned + DataPointer[i] = dwValue32 = DataPointer[i] ^ (dwKey1 + dwKey2); + + dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); + dwKey2 = dwValue32 + dwKey2 + (dwKey2 << 5) + 3; + } + } + else + { + for(DWORD i = 0; i < dwLength; i++) + { + // Modify the second key + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; - DataBlock[i] = DataBlock[i] ^ (dwKey1 + dwKey2); - dwValue32 = DataBlock[i]; + // The data are unaligned. Make sure we don't cause data misalignment error + dwValue32 = DecryptUInt32Unaligned(DataPointer, i, (dwKey1 + dwKey2)); - dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); - dwKey2 = dwValue32 + dwKey2 + (dwKey2 << 5) + 3; + dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); + dwKey2 = dwValue32 + dwKey2 + (dwKey2 << 5) + 3; + } } } -- cgit v1.2.3