mirror of
https://github.com/ladislav-zezula/StormLib.git
synced 2026-01-24 16:26:24 +01:00
Added alignment-aware block encryption/decryption
This commit is contained in:
@@ -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)];
|
||||
|
||||
DataBlock[i] = DataBlock[i] ^ (dwKey1 + dwKey2);
|
||||
dwValue32 = DataBlock[i];
|
||||
// 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 = DecryptUInt32Unaligned(DataPointer, i, (dwKey1 + dwKey2));
|
||||
|
||||
dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B);
|
||||
dwKey2 = dwValue32 + dwKey2 + (dwKey2 << 5) + 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user