diff options
author | Ladislav Zezula <zezula-at-volny-dot-cz> | 2016-06-18 20:32:15 +0200 |
---|---|---|
committer | Ladislav Zezula <zezula-at-volny-dot-cz> | 2016-06-18 20:32:15 +0200 |
commit | 303631f9d7f753dacffb1e038dd273dc204e1ea9 (patch) | |
tree | 5397acd7c5b8efe6e1a721f0177b534e38d9acd6 | |
parent | 47b6b6eb4addd82a89d6bcd8fa92f9c0a09a5781 (diff) |
+ Updated for protectors fiddling with too big block table index
-rw-r--r-- | src/SBaseCommon.cpp | 4 | ||||
-rw-r--r-- | src/SBaseFileTable.cpp | 46 | ||||
-rw-r--r-- | src/SBaseSubTypes.cpp | 8 | ||||
-rw-r--r-- | src/SFileFindFile.cpp | 2 | ||||
-rw-r--r-- | src/SFileListFile.cpp | 2 | ||||
-rw-r--r-- | src/SFileOpenArchive.cpp | 4 | ||||
-rw-r--r-- | src/StormLib.h | 7 | ||||
-rw-r--r-- | test/StormTest.cpp | 6 |
8 files changed, 36 insertions, 43 deletions
diff --git a/src/SBaseCommon.cpp b/src/SBaseCommon.cpp index 54fc6d0..95bdeb4 100644 --- a/src/SBaseCommon.cpp +++ b/src/SBaseCommon.cpp @@ -628,7 +628,7 @@ TMPQHash * GetFirstHashEntry(TMPQArchive * ha, const char * szFileName) TMPQHash * pHash = ha->pHashTable + dwIndex;
// If the entry matches, we found it.
- if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && pHash->dwBlockIndex < ha->dwFileTableSize)
+ if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize)
return pHash;
// If that hash entry is a free entry, it means we haven't found the file
@@ -663,7 +663,7 @@ TMPQHash * GetNextHashEntry(TMPQArchive * ha, TMPQHash * pFirstHash, TMPQHash * pHash = ha->pHashTable + dwIndex;
// If the entry matches, we found it.
- if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && pHash->dwBlockIndex < ha->dwFileTableSize)
+ if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize)
return pHash;
// If that hash entry is a free entry, it means we haven't found the file
diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp index 9d37ff1..981328b 100644 --- a/src/SBaseFileTable.cpp +++ b/src/SBaseFileTable.cpp @@ -581,11 +581,9 @@ int ConvertMpqHeaderToFormat4( // Hash entry verification when the file table does not exist yet
bool IsValidHashEntry(TMPQArchive * ha, TMPQHash * pHash)
{
- TFileEntry * pFileEntry = ha->pFileTable + MPQ_BLOCK_INDEX(pHash->dwBlockIndex);
- DWORD dwBlockIndex = MPQ_BLOCK_INDEX(pHash->dwBlockIndex);
+ TFileEntry * pFileEntry = ha->pFileTable + MPQ_BLOCK_INDEX(pHash);
- pFileEntry = ha->pFileTable + dwBlockIndex;
- return ((dwBlockIndex < ha->dwFileTableSize) && (pFileEntry->dwFlags & MPQ_FILE_EXISTS)) ? true : false;
+ return ((MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize) && (pFileEntry->dwFlags & MPQ_FILE_EXISTS)) ? true : false;
}
// Hash entry verification when the file table does not exist yet
@@ -593,20 +591,12 @@ static bool IsValidHashEntry1(TMPQArchive * ha, TMPQHash * pHash, TMPQBlock * pB {
ULONGLONG ByteOffset;
TMPQBlock * pBlock;
- DWORD dwBlockIndex;
-
- // We need to mask out the upper 4 bits of the block table index.
- // This is because it gets shifted out when calculating block table offset
- // BlockTableOffset = pHash->dwBlockIndex * 0x10
- // Malformed MPQ maps may contain invalid entries
- // Note that Storm.dll does not perfom this check
- dwBlockIndex = MPQ_BLOCK_INDEX(pHash->dwBlockIndex);
// The block index is considered valid if it's less than block table size
- if(dwBlockIndex < ha->pHeader->dwBlockTableSize)
+ if(MPQ_BLOCK_INDEX(pHash) < ha->pHeader->dwBlockTableSize)
{
// Calculate the block table position
- pBlock = pBlockTable + dwBlockIndex;
+ pBlock = pBlockTable + MPQ_BLOCK_INDEX(pHash);
// Check whether this is an existing file
// Also we do not allow to be file size greater than 2GB
@@ -787,28 +777,28 @@ static int BuildFileTableFromBlockTable( // - Multiple hash entries (different file name) point to the same block entry
//
// Ignore all hash table entries where:
- // - dwBlockIndex >= BlockTableSize
+ // - Block Index >= BlockTableSize
// - Flags of the appropriate block table entry
//
if(IsValidHashEntry1(ha, pHash, pBlockTable))
{
- DWORD dwBlockIndex = MPQ_BLOCK_INDEX(pHash->dwBlockIndex);
- DWORD dwNewIndex = MPQ_BLOCK_INDEX(pHash->dwBlockIndex);
+ DWORD dwOldIndex = MPQ_BLOCK_INDEX(pHash);
+ DWORD dwNewIndex = MPQ_BLOCK_INDEX(pHash);
// Determine the new block index
if(DefragmentTable != NULL)
{
// Need to handle case when multiple hash
// entries point to the same block entry
- if(DefragmentTable[dwBlockIndex] == HASH_ENTRY_FREE)
+ if(DefragmentTable[dwOldIndex] == HASH_ENTRY_FREE)
{
- DefragmentTable[dwBlockIndex] = dwItemCount;
+ DefragmentTable[dwOldIndex] = dwItemCount;
dwNewIndex = dwItemCount++;
}
else
{
- dwNewIndex = DefragmentTable[dwBlockIndex];
+ dwNewIndex = DefragmentTable[dwOldIndex];
}
// Fix the pointer in the hash entry
@@ -820,7 +810,7 @@ static int BuildFileTableFromBlockTable( // Get the pointer to the file entry and the block entry
pFileEntry = ha->pFileTable + dwNewIndex;
- pBlock = pBlockTable + dwBlockIndex;
+ pBlock = pBlockTable + dwOldIndex;
// ByteOffset is only valid if file size is not zero
pFileEntry->ByteOffset = pBlock->dwFilePos;
@@ -1814,11 +1804,11 @@ TFileEntry * GetFileEntryLocale2(TMPQArchive * ha, const char * szFileName, LCID if(ha->pHashTable != NULL)
{
pHash = GetHashEntryLocale(ha, szFileName, lcLocale);
- if(pHash != NULL && pHash->dwBlockIndex < ha->dwFileTableSize)
+ if(pHash != NULL && MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize)
{
if(PtrHashIndex != NULL)
PtrHashIndex[0] = (DWORD)(pHash - ha->pHashTable);
- return ha->pFileTable + pHash->dwBlockIndex;
+ return ha->pFileTable + MPQ_BLOCK_INDEX(pHash);
}
}
@@ -1848,11 +1838,11 @@ TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID l if(ha->pHashTable != NULL)
{
pHash = GetHashEntryExact(ha, szFileName, lcLocale);
- if(pHash != NULL && pHash->dwBlockIndex < ha->dwFileTableSize)
+ if(pHash != NULL && MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize)
{
if(PtrHashIndex != NULL)
PtrHashIndex[0] = (DWORD)(pHash - ha->pHashTable);
- return ha->pFileTable + pHash->dwBlockIndex;
+ return ha->pFileTable + MPQ_BLOCK_INDEX(pHash);
}
}
@@ -2640,11 +2630,11 @@ int DefragmentFileTable(TMPQArchive * ha) for(pHash = ha->pHashTable; pHash < pHashTableEnd; pHash++)
{
- if(pHash->dwBlockIndex < ha->dwFileTableSize)
+ if(MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize)
{
// If that block entry is there, set it to the hash entry
// If not, set it as DELETED
- dwNewBlockIndex = DefragmentTable[pHash->dwBlockIndex];
+ dwNewBlockIndex = DefragmentTable[MPQ_BLOCK_INDEX(pHash)];
pHash->dwBlockIndex = (dwNewBlockIndex != HASH_ENTRY_FREE) ? dwNewBlockIndex : HASH_ENTRY_DELETED;
}
}
@@ -2753,7 +2743,7 @@ int RebuildFileTable(TMPQArchive * ha, DWORD dwNewHashTableSize) {
if(IsValidHashEntry(ha, pHash))
{
- pFileEntry = ha->pFileTable + pHash->dwBlockIndex;
+ pFileEntry = ha->pFileTable + MPQ_BLOCK_INDEX(pHash);
AllocateHashEntry(ha, pFileEntry, pHash->lcLocale);
}
}
diff --git a/src/SBaseSubTypes.cpp b/src/SBaseSubTypes.cpp index 9807701..1f72c17 100644 --- a/src/SBaseSubTypes.cpp +++ b/src/SBaseSubTypes.cpp @@ -187,7 +187,6 @@ TMPQHash * LoadSqpHashTable(TMPQArchive * ha) TSQPHash * pSqpHashEnd; TSQPHash * pSqpHash; TMPQHash * pMpqHash; - DWORD dwBlockIndex; int nError = ERROR_SUCCESS; // Load the hash table @@ -203,8 +202,7 @@ TMPQHash * LoadSqpHashTable(TMPQArchive * ha) if(pSqpHash->dwBlockIndex != HASH_ENTRY_FREE) { // Check block index against the size of the block table - dwBlockIndex = pSqpHash->dwBlockIndex; - if(pHeader->dwBlockTableSize <= dwBlockIndex && dwBlockIndex < HASH_ENTRY_DELETED) + if(pHeader->dwBlockTableSize <= MPQ_BLOCK_INDEX(pSqpHash) && MPQ_BLOCK_INDEX(pSqpHash) < HASH_ENTRY_DELETED) nError = ERROR_FILE_CORRUPT; // We do not support nonzero locale and platform ID @@ -216,8 +214,8 @@ TMPQHash * LoadSqpHashTable(TMPQArchive * ha) pMpqHash->dwName2 = pSqpHash->dwName2; // Store the rest. Note that this must be done last, - // because pSqpHash->dwBlockIndex corresponds to pMpqHash->dwName2 - pMpqHash->dwBlockIndex = dwBlockIndex; + // because block index corresponds to pMpqHash->dwName2 + pMpqHash->dwBlockIndex = MPQ_BLOCK_INDEX(pSqpHash); pMpqHash->wPlatform = 0; pMpqHash->lcLocale = 0; } diff --git a/src/SFileFindFile.cpp b/src/SFileFindFile.cpp index 41f31e1..18bf4d5 100644 --- a/src/SFileFindFile.cpp +++ b/src/SFileFindFile.cpp @@ -299,7 +299,7 @@ static int DoMPQSearch_HashTable(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileDa if(IsValidHashEntry(ha, pHash))
{
// Check if this file entry should be included in the search result
- if(DoMPQSearch_FileEntry(hs, lpFindFileData, ha, pHash, ha->pFileTable + pHash->dwBlockIndex))
+ if(DoMPQSearch_FileEntry(hs, lpFindFileData, ha, pHash, ha->pFileTable + MPQ_BLOCK_INDEX(pHash)))
return ERROR_SUCCESS;
}
}
diff --git a/src/SFileListFile.cpp b/src/SFileListFile.cpp index 62b8f11..3918bd9 100644 --- a/src/SFileListFile.cpp +++ b/src/SFileListFile.cpp @@ -350,7 +350,7 @@ static int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFil while(pHash != NULL)
{
// Allocate file name for the file entry
- AllocateFileName(ha, ha->pFileTable + pHash->dwBlockIndex, szFileName);
+ AllocateFileName(ha, ha->pFileTable + MPQ_BLOCK_INDEX(pHash), szFileName);
// Now find the next language version of the file
pHash = GetNextHashEntry(ha, pFirstHash, pHash);
diff --git a/src/SFileOpenArchive.cpp b/src/SFileOpenArchive.cpp index 2cb6698..62effe7 100644 --- a/src/SFileOpenArchive.cpp +++ b/src/SFileOpenArchive.cpp @@ -369,8 +369,8 @@ bool WINAPI SFileOpenArchive( // higher than 0x10000000, it would overflow in 32-bit version // Observed in the malformed Warcraft III maps // Example map: MPQ_2016_v1_ProtectedMap_TableSizeOverflow.w3x - ha->pHeader->dwBlockTableSize = MPQ_BLOCK_INDEX(ha->pHeader->dwBlockTableSize); - ha->pHeader->dwHashTableSize = MPQ_BLOCK_INDEX(ha->pHeader->dwHashTableSize); + ha->pHeader->dwBlockTableSize = (ha->pHeader->dwBlockTableSize & BLOCK_INDEX_MASK); + ha->pHeader->dwHashTableSize = (ha->pHeader->dwHashTableSize & BLOCK_INDEX_MASK); // Both MPQ_OPEN_NO_LISTFILE or MPQ_OPEN_NO_ATTRIBUTES trigger read only mode if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES)) diff --git a/src/StormLib.h b/src/StormLib.h index c25a1a8..3621096 100644 --- a/src/StormLib.h +++ b/src/StormLib.h @@ -230,7 +230,12 @@ extern "C" { MPQ_FILE_SIGNATURE | \ MPQ_FILE_EXISTS) -#define MPQ_BLOCK_INDEX(bi) ((bi) & 0x0FFFFFFF) // Index mask for block table index +// We need to mask out the upper 4 bits of the block table index. +// This is because it gets shifted out when calculating block table offset +// BlockTableOffset = pHash->dwBlockIndex << 0x04 +// Malformed MPQ maps may contain block indexes like 0x40000001 or 0xF0000023 +#define BLOCK_INDEX_MASK 0x0FFFFFFF +#define MPQ_BLOCK_INDEX(pHash) (pHash->dwBlockIndex & BLOCK_INDEX_MASK) // Compression types for multiple compressions #define MPQ_COMPRESSION_HUFFMANN 0x01 // Huffmann compression (used on WAVE files only) diff --git a/test/StormTest.cpp b/test/StormTest.cpp index 2d73bd9..e19d7c3 100644 --- a/test/StormTest.cpp +++ b/test/StormTest.cpp @@ -4405,7 +4405,7 @@ int main(int argc, char * argv[]) // Open a partial MPQ with compressed hash table
if(nError == ERROR_SUCCESS)
nError = TestOpenArchive("part-file://MPQ_2010_v2_HashTableCompressed.MPQ.part");
-
+*/
if(nError == ERROR_SUCCESS)
nError = TestOpenArchive("MPQ_2002_v1_ProtectedMap_HashTable_FakeValid.w3x");
@@ -4464,9 +4464,9 @@ int main(int argc, char * argv[]) // Protector from China (2016-05-27)
if(nError == ERROR_SUCCESS)
nError = TestOpenArchive("MPQ_2016_v1_WME4_4.w3x");
-*/
+
if(nError == ERROR_SUCCESS)
- nError = TestOpenArchive("MPQ_2016_v1_AnotherProtectedMap.w3x");
+ nError = TestOpenArchive("MPQ_2016_v1_SP_(4)Adrenaline.w3x");
/*
// Open the multi-file archive with wrong prefix to see how StormLib deals with it
if(nError == ERROR_SUCCESS)
|