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 ++++++++++++++++++++++++++++++++++++++++---------- src/SFileListFile.cpp | 8 +++- src/SFileReadFile.cpp | 10 +++++ src/StormCommon.h | 5 ++- test/StormTest.cpp | 11 +---- 5 files changed, 112 insertions(+), 33 deletions(-) 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; + } } } diff --git a/src/SFileListFile.cpp b/src/SFileListFile.cpp index e2b8166..a1e16b0 100644 --- a/src/SFileListFile.cpp +++ b/src/SFileListFile.cpp @@ -90,6 +90,7 @@ static TListFileCache * CreateListFileCache( DWORD dwFlags) { TListFileCache * pCache = NULL; + size_t cchWildCardAligned = 0; size_t cchWildCard = 0; DWORD dwBytesRead = 0; @@ -99,10 +100,13 @@ static TListFileCache * CreateListFileCache( // Append buffer for name mask, if any if(szWildCard != NULL) + { cchWildCard = strlen(szWildCard) + 1; + cchWildCardAligned = (cchWildCard + 3) & 0xFFFFFFFC; + } // Allocate cache for one file block - pCache = (TListFileCache *)STORM_ALLOC(BYTE, sizeof(TListFileCache) + cchWildCard + dwFileSize + 1); + pCache = (TListFileCache *)STORM_ALLOC(BYTE, sizeof(TListFileCache) + cchWildCardAligned + dwFileSize + 1); if(pCache != NULL) { // Clear the entire structure @@ -117,7 +121,7 @@ static TListFileCache * CreateListFileCache( } // Fill-in the rest of the cache pointers - pCache->pBegin = (LPBYTE)(pCache + 1) + cchWildCard; + pCache->pBegin = (LPBYTE)(pCache + 1) + cchWildCardAligned; // Load the entire listfile to the cache PfnLoadFile(pHandle, pCache->pBegin, dwFileSize, &dwBytesRead); diff --git a/src/SFileReadFile.cpp b/src/SFileReadFile.cpp index 34edc06..820f400 100644 --- a/src/SFileReadFile.cpp +++ b/src/SFileReadFile.cpp @@ -227,6 +227,9 @@ static DWORD ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFileP LPBYTE pbRawData; DWORD dwErrCode = ERROR_SUCCESS; + // The buffer needs to be aligned to 4-byte boundary + assert(STORMLIB_DWORD_ALIGNED(pbBuffer)); + // If the file buffer is not allocated yet, do it. if(hf->pbFileSector == NULL) { @@ -360,6 +363,7 @@ static DWORD ReadMpkFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFileP DWORD dwErrCode = ERROR_SUCCESS; // We do not support patch files in MPK archives + assert(STORMLIB_DWORD_ALIGNED(pvBuffer)); assert(hf->pPatchInfo == NULL); // If the file buffer is not allocated yet, do it. @@ -458,6 +462,9 @@ static DWORD ReadMpqFileSectorFile(TMPQFile * hf, void * pvBuffer, DWORD dwFileP DWORD dwBytesRead; // Number of bytes read (temporary variable) DWORD dwErrCode; + // The buffer needs to be aligned to 4-byte boundary + assert(STORMLIB_DWORD_ALIGNED(pbBuffer)); + // If the file position is at or beyond end of file, do nothing if(dwFilePos >= hf->dwDataSize) { @@ -576,6 +583,9 @@ static DWORD ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePo DWORD dwBytesRead = 0; DWORD dwErrCode = ERROR_SUCCESS; + // Make sure that the buffer is properly aligned + assert(STORMLIB_DWORD_ALIGNED(pvBuffer)); + // Make sure that the patch file is loaded completely if(dwErrCode == ERROR_SUCCESS && hf->pbFileData == NULL) { diff --git a/src/StormCommon.h b/src/StormCommon.h index ff14a32..e9187f9 100644 --- a/src/StormCommon.h +++ b/src/StormCommon.h @@ -67,8 +67,11 @@ #define STORMLIB_MAX(a, b) ((a > b) ? a : b) #define STORMLIB_UNUSED(p) ((void)(p)) +// Checks for data pointers aligned to 4-byte boundary +#define STORMLIB_DWORD_ALIGNED(ptr) (((size_t)(ptr) & 0x03) == 0) + // Macro for building 64-bit file offset from two 32-bit -#define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | (ULONGLONG)lo) +#define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | (ULONGLONG)lo) //----------------------------------------------------------------------------- // MTYPE definition - specifies what kind of MPQ is the map type diff --git a/test/StormTest.cpp b/test/StormTest.cpp index 20d606e..ab003e4 100644 --- a/test/StormTest.cpp +++ b/test/StormTest.cpp @@ -4285,23 +4285,16 @@ int _tmain(int argc, TCHAR * argv[]) // // Tests on a local listfile // - /* + if(dwErrCode == ERROR_SUCCESS) { TestOnLocalListFile(_T("FLAT-MAP:ListFile_Blizzard.txt")); dwErrCode = TestOnLocalListFile(_T("ListFile_Blizzard.txt")); } - */ + // // Open all files from the command line // - /* - SFILE_MARKERS Markers = { sizeof(SFILE_MARKERS) }; - Markers.dwSignature = 'XHSC'; - Markers.szHashTableKey = "(cash table)"; - Markers.szBlockTableKey = "(clock table)"; - SFileSetArchiveMarkers(&Markers); - */ for(int i = 1; i < argc; i++) { -- cgit v1.2.3 From b3c4fa29a1b1773f8dba1d7b1b341fa81c659b3d Mon Sep 17 00:00:00 2001 From: Ladislav Zezula Date: Mon, 17 May 2021 13:01:38 +0200 Subject: Removed asserts --- src/SFileReadFile.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/SFileReadFile.cpp b/src/SFileReadFile.cpp index 820f400..34edc06 100644 --- a/src/SFileReadFile.cpp +++ b/src/SFileReadFile.cpp @@ -227,9 +227,6 @@ static DWORD ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFileP LPBYTE pbRawData; DWORD dwErrCode = ERROR_SUCCESS; - // The buffer needs to be aligned to 4-byte boundary - assert(STORMLIB_DWORD_ALIGNED(pbBuffer)); - // If the file buffer is not allocated yet, do it. if(hf->pbFileSector == NULL) { @@ -363,7 +360,6 @@ static DWORD ReadMpkFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFileP DWORD dwErrCode = ERROR_SUCCESS; // We do not support patch files in MPK archives - assert(STORMLIB_DWORD_ALIGNED(pvBuffer)); assert(hf->pPatchInfo == NULL); // If the file buffer is not allocated yet, do it. @@ -462,9 +458,6 @@ static DWORD ReadMpqFileSectorFile(TMPQFile * hf, void * pvBuffer, DWORD dwFileP DWORD dwBytesRead; // Number of bytes read (temporary variable) DWORD dwErrCode; - // The buffer needs to be aligned to 4-byte boundary - assert(STORMLIB_DWORD_ALIGNED(pbBuffer)); - // If the file position is at or beyond end of file, do nothing if(dwFilePos >= hf->dwDataSize) { @@ -583,9 +576,6 @@ static DWORD ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePo DWORD dwBytesRead = 0; DWORD dwErrCode = ERROR_SUCCESS; - // Make sure that the buffer is properly aligned - assert(STORMLIB_DWORD_ALIGNED(pvBuffer)); - // Make sure that the patch file is loaded completely if(dwErrCode == ERROR_SUCCESS && hf->pbFileData == NULL) { -- cgit v1.2.3 From 26b2f0e8b6e711aa05b67bf2466a715a063b2517 Mon Sep 17 00:00:00 2001 From: Ladislav Zezula Date: Mon, 17 May 2021 15:19:36 +0200 Subject: PDWORD -> LPDWORD --- src/SBaseCommon.cpp | 6 +++--- test/StormTest.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/SBaseCommon.cpp b/src/SBaseCommon.cpp index b81d492..a1aa78e 100644 --- a/src/SBaseCommon.cpp +++ b/src/SBaseCommon.cpp @@ -414,7 +414,7 @@ DWORD GetDefaultSpecialFileFlags(DWORD dwFileSize, USHORT wFormatVersion) //----------------------------------------------------------------------------- // Encrypting/Decrypting MPQ data block -static DWORD EncryptUInt32Unaligned(PDWORD DataPointer, DWORD i, DWORD dwXorKey) +static DWORD EncryptUInt32Unaligned(LPDWORD DataPointer, DWORD i, DWORD dwXorKey) { LPBYTE pbDataPointer = (LPBYTE)(DataPointer + i); LPBYTE pbXorKey = (LPBYTE)(&dwXorKey); @@ -474,7 +474,7 @@ void EncryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey1) } } -static DWORD DecryptUInt32Unaligned(PDWORD DataPointer, DWORD i, DWORD dwXorKey) +static DWORD DecryptUInt32Unaligned(LPDWORD DataPointer, DWORD i, DWORD dwXorKey) { LPBYTE pbDataPointer = (LPBYTE)(DataPointer + i); LPBYTE pbXorKey = (LPBYTE)(&dwXorKey); @@ -494,7 +494,7 @@ static DWORD DecryptUInt32Unaligned(PDWORD DataPointer, DWORD i, DWORD dwXorKey) void DecryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey1) { - PDWORD DataPointer = (PDWORD)pvDataBlock; + LPDWORD DataPointer = (LPDWORD)pvDataBlock; DWORD dwValue32; DWORD dwKey2 = 0xEEEEEEEE; diff --git a/test/StormTest.cpp b/test/StormTest.cpp index ab003e4..5b32dc1 100644 --- a/test/StormTest.cpp +++ b/test/StormTest.cpp @@ -4285,13 +4285,13 @@ int _tmain(int argc, TCHAR * argv[]) // // Tests on a local listfile // - + /* if(dwErrCode == ERROR_SUCCESS) { TestOnLocalListFile(_T("FLAT-MAP:ListFile_Blizzard.txt")); dwErrCode = TestOnLocalListFile(_T("ListFile_Blizzard.txt")); } - + */ // // Open all files from the command line // -- cgit v1.2.3