diff options
author | Ladislav Zezula <ladislav.zezula@avg.com> | 2014-08-25 12:59:01 +0200 |
---|---|---|
committer | Ladislav Zezula <ladislav.zezula@avg.com> | 2014-08-25 12:59:01 +0200 |
commit | d7044aecaeb2bbb33e0f5cc2080d5b2995bd79d5 (patch) | |
tree | e260ef9d1415bef05b0071af573512d90f93ae3b /src/SBaseFileTable.cpp | |
parent | 2b5b7e977145c16f6ec23933c41678c382d22de3 (diff) |
+ Added support for newest Spazzler protector
Diffstat (limited to 'src/SBaseFileTable.cpp')
-rw-r--r-- | src/SBaseFileTable.cpp | 259 |
1 files changed, 102 insertions, 157 deletions
diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp index a2eb5a8..33e1472 100644 --- a/src/SBaseFileTable.cpp +++ b/src/SBaseFileTable.cpp @@ -56,30 +56,6 @@ static DWORD GetNecessaryBitCount(ULONGLONG MaxValue) return dwBitCount; } -static int CompareFilePositions(const void * p1, const void * p2) -{ - TMPQBlock * pBlock1 = *(TMPQBlock **)p1; - TMPQBlock * pBlock2 = *(TMPQBlock **)p2; - DWORD dwFileEnd1; - DWORD dwFileEnd2; - - // Compare file begins - if(pBlock1->dwFilePos < pBlock2->dwFilePos) - return -1; - if(pBlock1->dwFilePos > pBlock2->dwFilePos) - return +1; - - // If the files begin at the same position, compare their ends - dwFileEnd1 = pBlock1->dwFilePos + pBlock1->dwCSize; - dwFileEnd2 = pBlock2->dwFilePos + pBlock2->dwCSize; - if(dwFileEnd1 < dwFileEnd2) - return -1; - if(dwFileEnd1 > dwFileEnd2) - return +1; - - return 0; -} - //----------------------------------------------------------------------------- // Support functions for BIT_ARRAY @@ -240,27 +216,6 @@ void SetBits( //----------------------------------------------------------------------------- // Support for MPQ header -static DWORD GetMaxFileOffset32(TMPQArchive * ha) -{ - TMPQHeader * pHeader = ha->pHeader; - DWORD dwMaxFileOffset = ha->pHeader->dwArchiveSize; - - // We can call this only for malformed archives v 1.0 - assert(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1); - assert((ha->dwFlags & MPQ_FLAG_MALFORMED) != 0); - - // If the block table is at the end, decrement the limit by the block table - if(pHeader->dwBlockTablePos + (pHeader->dwBlockTableSize * sizeof(TMPQBlock)) >= dwMaxFileOffset) - dwMaxFileOffset = pHeader->dwBlockTablePos; - - // If the hash table is at the end, decrement the limit by the hash table - if(pHeader->dwHashTablePos + (pHeader->dwHashTableSize * sizeof(TMPQBlock)) >= dwMaxFileOffset) - dwMaxFileOffset = pHeader->dwHashTablePos; - - // Return what we found - return dwMaxFileOffset; -} - static ULONGLONG DetermineArchiveSize_V1( TMPQArchive * ha, TMPQHeader * pHeader, @@ -2049,78 +2004,40 @@ void InvalidateInternalFiles(TMPQArchive * ha) //----------------------------------------------------------------------------- // Support for file tables - hash table, block table, hi-block table -int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize) -{ - TMPQHash * pHashTable; - - // Sanity checks - assert((dwHashTableSize & (dwHashTableSize - 1)) == 0); - assert(ha->pHashTable == NULL); - - // If the required hash table size is zero, don't create anything - if(dwHashTableSize == 0) - dwHashTableSize = HASH_TABLE_SIZE_DEFAULT; - - // Create the hash table - pHashTable = STORM_ALLOC(TMPQHash, dwHashTableSize); - if(pHashTable == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - - // Fill it - memset(pHashTable, 0xFF, dwHashTableSize * sizeof(TMPQHash)); - ha->dwMaxFileCount = dwHashTableSize; - ha->pHashTable = pHashTable; - return ERROR_SUCCESS; -} - -TMPQHash * LoadHashTable(TMPQArchive * ha) +// Fixes the case when hash table size is set to arbitrary long value +static void FixHashTableSize(TMPQArchive * ha, ULONGLONG ByteOffset) { - TMPQHeader * pHeader = ha->pHeader; - ULONGLONG ByteOffset; - TMPQHash * pHashTable = NULL; - DWORD dwTableSize; - DWORD dwCmpSize; - - // If the MPQ has no hash table, do nothing - if(pHeader->dwHashTablePos == 0 && pHeader->wHashTablePosHi == 0) - return NULL; + ULONGLONG FileSize = 0; + DWORD dwFixedTableSize; + DWORD dwHashTableSize = ha->pHeader->dwHashTableSize; - // If the hash table size is zero, do nothing - if(pHeader->dwHashTableSize == 0) - return NULL; + // Spazzler protector abuses the fact that hash table size does not matter + // if the hash table is entirely filled with values different than 0xFFFFFFFF. + // It sets the hash table size to 0x00100000, which slows down file searching + // and adding a listfile. - // Load the hash table for MPQ variations - switch(ha->dwSubType) + // Only if the hash table size is correct + if((dwHashTableSize & (dwHashTableSize - 1)) == 0) { - case MPQ_SUBTYPE_MPQ: - - // Get the compressed and uncompressed hash table size - dwTableSize = pHeader->dwHashTableSize * sizeof(TMPQHash); - dwCmpSize = (DWORD)pHeader->HashTableSize64; - - // - // Load the table from the MPQ, with decompression - // - // Note: We will NOT check if the hash table is properly decrypted. - // Some MPQ protectors corrupt the hash table by rewriting part of it. - // Hash table, the way how it works, allows arbitrary values for unused entries. - // + // Retrieve the file size + FileStream_GetSize(ha->pStream, &FileSize); - ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); - pHashTable = (TMPQHash *)LoadMpqTable(ha, ByteOffset, dwCmpSize, dwTableSize, MPQ_KEY_HASH_TABLE); - break; + // Work as long as the size greater than file size + for(;;) + { + // Try the size of one half of the current + dwFixedTableSize = dwHashTableSize >> 1; + if(ByteOffset + (dwFixedTableSize * sizeof(TMPQHash)) <= FileSize) + break; - case MPQ_SUBTYPE_SQP: - pHashTable = LoadSqpHashTable(ha); - break; + // Cut the hash table size to half + dwHashTableSize = dwFixedTableSize; + } - case MPQ_SUBTYPE_MPK: - pHashTable = LoadMpkHashTable(ha); - break; + // Fix the hash table size + ha->pHeader->dwHashTableSize = dwHashTableSize; + ha->pHeader->HashTableSize64 = dwHashTableSize * sizeof(TMPQHash); } - - // Return the hash table - return pHashTable; } // This function fixes the scenario then dwBlockTableSize @@ -2174,64 +2091,90 @@ static void FixBlockTableSize( } } -// This function fixes the scenario then dwBlockTableSize -// is greater and goes into a MPQ file -static void FixCompressedFileSize( - TMPQArchive * ha, - TMPQBlock * pBlockTable) +int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize) { - TMPQHeader * pHeader = ha->pHeader; - TMPQBlock ** SortTable; - TMPQBlock * pBlockTableEnd = pBlockTable + pHeader->dwBlockTableSize; - TMPQBlock * pBlock; - size_t nElements = 0; - size_t nIndex; - DWORD dwMaxFileOffs; + TMPQHash * pHashTable; - // Only perform this check on MPQs version 1.0 - assert(pHeader->dwHeaderSize == MPQ_HEADER_SIZE_V1); + // Sanity checks + assert((dwHashTableSize & (dwHashTableSize - 1)) == 0); + assert(ha->pHashTable == NULL); - // Allocate sort table for all entries - SortTable = STORM_ALLOC(TMPQBlock*, pHeader->dwBlockTableSize); - if(SortTable != NULL) - { - // Calculate the end of the archive - dwMaxFileOffs = GetMaxFileOffset32(ha); + // If the required hash table size is zero, don't create anything + if(dwHashTableSize == 0) + dwHashTableSize = HASH_TABLE_SIZE_DEFAULT; - // Put all blocks to a sort table - for(pBlock = pBlockTable; pBlock < pBlockTableEnd; pBlock++) - { - if(pBlock->dwFlags & MPQ_FILE_EXISTS) - SortTable[nElements++] = pBlock; - } + // Create the hash table + pHashTable = STORM_ALLOC(TMPQHash, dwHashTableSize); + if(pHashTable == NULL) + return ERROR_NOT_ENOUGH_MEMORY; - // Have we found at least one block? - if(nElements > 0) - { - // Sort the table - qsort(SortTable, nElements, sizeof(TMPQBlock *), CompareFilePositions); + // Fill it + memset(pHashTable, 0xFF, dwHashTableSize * sizeof(TMPQHash)); + ha->dwMaxFileCount = dwHashTableSize; + ha->pHashTable = pHashTable; + return ERROR_SUCCESS; +} + +TMPQHash * LoadHashTable(TMPQArchive * ha) +{ + TMPQHeader * pHeader = ha->pHeader; + ULONGLONG ByteOffset; + TMPQHash * pHashTable = NULL; + DWORD dwTableSize; + DWORD dwCmpSize; + + // If the MPQ has no hash table, do nothing + if(pHeader->dwHashTablePos == 0 && pHeader->wHashTablePosHi == 0) + return NULL; + + // If the hash table size is zero, do nothing + if(pHeader->dwHashTableSize == 0) + return NULL; + + // Load the hash table for MPQ variations + switch(ha->dwSubType) + { + case MPQ_SUBTYPE_MPQ: - // Walk the table and set all compressed sizes to they - // match the difference (dwFilePos1 - dwFilePos0) - for(nIndex = 0; nIndex < nElements - 1; nIndex++) + ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); + if((pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) && (ha->dwFlags & MPQ_FLAG_MALFORMED)) { - TMPQBlock * pBlock1 = SortTable[nIndex + 1]; - TMPQBlock * pBlock0 = SortTable[nIndex]; - - pBlock0->dwCSize = (pBlock0->dwFlags & MPQ_FILE_COMPRESS_MASK) ? (pBlock1->dwFilePos - pBlock0->dwFilePos) : pBlock0->dwFSize; + // Calculate the hash table offset as 32-bit value + ByteOffset = (DWORD)ha->MpqPos + pHeader->dwHashTablePos; + + // Defense against map protectors that set the hash table size too big + FixHashTableSize(ha, ByteOffset); } - // Fix the last entry - pBlock = SortTable[nElements - 1]; - pBlock->dwCSize = (pBlock->dwFlags & MPQ_FILE_COMPRESS_MASK) ? (dwMaxFileOffs - pBlock->dwFilePos) : pBlock->dwFSize; - } + // Get the compressed and uncompressed hash table size + dwTableSize = pHeader->dwHashTableSize * sizeof(TMPQHash); + dwCmpSize = (DWORD)pHeader->HashTableSize64; + + // + // Load the table from the MPQ, with decompression + // + // Note: We will NOT check if the hash table is properly decrypted. + // Some MPQ protectors corrupt the hash table by rewriting part of it. + // Hash table, the way how it works, allows arbitrary values for unused entries. + // + + pHashTable = (TMPQHash *)LoadMpqTable(ha, ByteOffset, dwCmpSize, dwTableSize, MPQ_KEY_HASH_TABLE); + break; + + case MPQ_SUBTYPE_SQP: + pHashTable = LoadSqpHashTable(ha); + break; - STORM_FREE(SortTable); + case MPQ_SUBTYPE_MPK: + pHashTable = LoadMpkHashTable(ha); + break; } -} + // Return the hash table + return pHashTable; +} -TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool bDontFixEntries) +TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool /* bDontFixEntries */) { TMPQHeader * pHeader = ha->pHeader; TMPQBlock * pBlockTable = NULL; @@ -2278,9 +2221,11 @@ TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool bDontFixEntries) if(pBlockTable != NULL) FixBlockTableSize(ha, pBlockTable); - // Defense against protectors that set invalid compressed size - if((ha->dwFlags & MPQ_FLAG_MALFORMED) && (bDontFixEntries == false)) - FixCompressedFileSize(ha, pBlockTable); + // + // Note: TMPQBlock::dwCSize file size can be completely invalid + // for compressed files and the file can still be read. + // Abused by some MPQ protectors + // } break; |