diff options
author | Ladislav Zezula <ladislav.zezula@avast.com> | 2021-05-17 12:57:54 +0200 |
---|---|---|
committer | Ladislav Zezula <ladislav.zezula@avast.com> | 2021-05-17 12:57:54 +0200 |
commit | 33bdae86c2345db9514addbf9d5a698eada56fb2 (patch) | |
tree | ff40d644d75b5976b051bf6534e459805e9d184c /src | |
parent | a3332c7c9bc36f13aace6543ebc719f833882dfc (diff) |
Added alignment-aware block encryption/decryption
Diffstat (limited to 'src')
-rw-r--r-- | src/SBaseCommon.cpp | 111 | ||||
-rw-r--r-- | src/SFileListFile.cpp | 8 | ||||
-rw-r--r-- | src/SFileReadFile.cpp | 10 | ||||
-rw-r--r-- | src/StormCommon.h | 5 |
4 files changed, 110 insertions, 24 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
|