diff options
author | Ladislav Zezula <ladislav.zezula@avg.com> | 2014-03-03 14:56:59 +0100 |
---|---|---|
committer | Ladislav Zezula <ladislav.zezula@avg.com> | 2014-03-03 14:56:59 +0100 |
commit | 2abb68a7c5f894b9fb826dea9887a7d26a22888f (patch) | |
tree | 5b72c822085d018974693827a21423c3dbb2d52f /src/SFileAttributes.cpp | |
parent | 11f1d89cee7d09b8ffa2350cd2ee5a1b929260bf (diff) |
+ Fixed bug in checking size of (attributes) file
Diffstat (limited to 'src/SFileAttributes.cpp')
-rw-r--r-- | src/SFileAttributes.cpp | 150 |
1 files changed, 92 insertions, 58 deletions
diff --git a/src/SFileAttributes.cpp b/src/SFileAttributes.cpp index a74b54f..2589fc3 100644 --- a/src/SFileAttributes.cpp +++ b/src/SFileAttributes.cpp @@ -51,65 +51,111 @@ static DWORD GetSizeOfAttributesFile(DWORD dwAttrFlags, DWORD dwFileTableSize) return cbAttrFile; } -static bool CheckSizeOfAttributesFile(DWORD cbSizeOfAttr, DWORD dwAttrFlags, DWORD dwFileTableSize) +static DWORD CheckSizeOfAttributesFile(DWORD cbAttrFile, DWORD dwAttrFlags, DWORD dwFileTableSize) { - DWORD cbAttrFile = sizeof(MPQ_ATTRIBUTES_HEADER); + DWORD cbHeaderSize = sizeof(MPQ_ATTRIBUTES_HEADER); + DWORD cbChecksumSize1 = 0; + DWORD cbChecksumSize2 = 0; + DWORD cbFileTimeSize1 = 0; + DWORD cbFileTimeSize2 = 0; + DWORD cbFileHashSize1 = 0; + DWORD cbFileHashSize2 = 0; + DWORD cbPatchBitSize1 = 0; + DWORD cbPatchBitSize2 = 0; + DWORD cbPatchBitSize3 = 0; - // Calculate size of the (attributes) file + // + // Various variants with the patch bit + // + // interface.MPQ.part from WoW build 10958 has + // the MPQ_ATTRIBUTE_PATCH_BIT set, but there's an array of DWORDs instead. + // The array is filled with zeros, so we don't know what it should contain + // + // Zenith.SC2MAP has the MPQ_ATTRIBUTE_PATCH_BIT set, but the bit array is missing + // + // Elimination Tournament 2.w3x's (attributes) have one entry less + // + // There may be two variants: Either the (attributes) file has full + // number of entries, or has one entry less + // + + // Get the expected size of CRC32 array if(dwAttrFlags & MPQ_ATTRIBUTE_CRC32) - cbAttrFile += dwFileTableSize * sizeof(DWORD); + { + cbChecksumSize1 += dwFileTableSize * sizeof(DWORD); + cbChecksumSize2 += cbChecksumSize1 - sizeof(DWORD); + } + + // Get the expected size of FILETIME array if(dwAttrFlags & MPQ_ATTRIBUTE_FILETIME) - cbAttrFile += dwFileTableSize * sizeof(ULONGLONG); + { + cbFileTimeSize1 += dwFileTableSize * sizeof(ULONGLONG); + cbFileTimeSize2 += cbFileTimeSize1 - sizeof(ULONGLONG); + } + + // Get the expected size of MD5 array if(dwAttrFlags & MPQ_ATTRIBUTE_MD5) - cbAttrFile += dwFileTableSize * MD5_DIGEST_SIZE; + { + cbFileHashSize1 += dwFileTableSize * MD5_DIGEST_SIZE; + cbFileHashSize2 += cbFileHashSize1 - MD5_DIGEST_SIZE; + } - // Various variants with the patch bit + // Get the expected size of patch bit array if(dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) { - // Check for expected size - if(cbSizeOfAttr == (cbAttrFile + (dwFileTableSize + 6) / 8)) - return true; - - // Zenith.SC2MAP has the MPQ_ATTRIBUTE_PATCH_BIT set, but the bit array is missing - if(cbSizeOfAttr == cbSizeOfAttr) - return false; - - // interface.MPQ.part from WoW build 10958 has - // the MPQ_ATTRIBUTE_PATCH_BIT set, but there's an array of DWORDs instead. - // The array is filled with zeros, so we don't know what it should contain - if(cbSizeOfAttr == (cbAttrFile + dwFileTableSize * sizeof(DWORD))) - return false; - - // Size mismatch - assert(false); - return false; + cbPatchBitSize1 = + cbPatchBitSize2 = ((dwFileTableSize + 6) / 8); + cbPatchBitSize3 = dwFileTableSize * sizeof(DWORD); } - assert(cbSizeOfAttr == cbAttrFile); - return false; + // Check if the (attributes) file entry count is equal to our file table size + if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1 + cbPatchBitSize1)) + return dwFileTableSize; + + // Check if the (attributes) file entry count is equal to our file table size minus one + if(cbAttrFile == (cbHeaderSize + cbChecksumSize2 + cbFileTimeSize2 + cbFileHashSize2 + cbPatchBitSize2)) + return dwFileTableSize - 1; + + // Zenith.SC2MAP has the MPQ_ATTRIBUTE_PATCH_BIT set, but the bit array is missing + if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1)) + return dwFileTableSize; + + // interface.MPQ.part (WoW build 10958) has the MPQ_ATTRIBUTE_PATCH_BIT set + // but there's an array of DWORDs (filled with zeros) instead of array of bits + if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1 + cbPatchBitSize3)) + return dwFileTableSize; + + // Invalid size of the (attributes) file + assert(false); + return 0; } static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrFile) { LPBYTE pbAttrFileEnd = pbAttrFile + cbAttrFile; LPBYTE pbAttrPtr = pbAttrFile; - DWORD dwBlockTableSize = ha->pHeader->dwBlockTableSize; + DWORD dwAttributesEntries = 0; DWORD i; - bool bPatchBitsValid = false; // Load and verify the header if((pbAttrPtr + sizeof(MPQ_ATTRIBUTES_HEADER)) <= pbAttrFileEnd) { PMPQ_ATTRIBUTES_HEADER pAttrHeader = (PMPQ_ATTRIBUTES_HEADER)pbAttrPtr; + // Verify the header version BSWAP_ARRAY32_UNSIGNED(pAttrHeader, sizeof(MPQ_ATTRIBUTES_HEADER)); if(pAttrHeader->dwVersion != MPQ_ATTRIBUTES_V1) return ERROR_BAD_FORMAT; - // Verify the flags and of the file - assert((pAttrHeader->dwFlags & ~MPQ_ATTRIBUTE_ALL) == 0); + // Verify the flags + if(pAttrHeader->dwFlags & ~MPQ_ATTRIBUTE_ALL) + return ERROR_BAD_FORMAT; + + // Verify whether file size of (attributes) is expected + dwAttributesEntries = CheckSizeOfAttributesFile(cbAttrFile, pAttrHeader->dwFlags, ha->pHeader->dwBlockTableSize); + if(dwAttributesEntries == 0) + return ERROR_BAD_FORMAT; - bPatchBitsValid = CheckSizeOfAttributesFile(cbAttrFile, pAttrHeader->dwFlags, dwBlockTableSize); ha->dwAttrFlags = pAttrHeader->dwFlags; pbAttrPtr = (LPBYTE)(pAttrHeader + 1); } @@ -118,14 +164,14 @@ static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrF if(ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32) { LPDWORD ArrayCRC32 = (LPDWORD)pbAttrPtr; - DWORD cbArraySize = dwBlockTableSize * sizeof(DWORD); + DWORD cbArraySize = dwAttributesEntries * sizeof(DWORD); // Verify if there's enough data if((pbAttrPtr + cbArraySize) > pbAttrFileEnd) return ERROR_FILE_CORRUPT; BSWAP_ARRAY32_UNSIGNED(ArrayCRC32, cbCRC32Size); - for(i = 0; i < dwBlockTableSize; i++) + for(i = 0; i < dwAttributesEntries; i++) ha->pFileTable[i].dwCrc32 = ArrayCRC32[i]; pbAttrPtr += cbArraySize; } @@ -134,14 +180,14 @@ static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrF if(ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME) { ULONGLONG * ArrayFileTime = (ULONGLONG *)pbAttrPtr; - DWORD cbArraySize = dwBlockTableSize * sizeof(ULONGLONG); + DWORD cbArraySize = dwAttributesEntries * sizeof(ULONGLONG); // Verify if there's enough data if((pbAttrPtr + cbArraySize) > pbAttrFileEnd) return ERROR_FILE_CORRUPT; BSWAP_ARRAY64_UNSIGNED(ArrayFileTime, cbFileTimeSize); - for(i = 0; i < dwBlockTableSize; i++) + for(i = 0; i < dwAttributesEntries; i++) ha->pFileTable[i].FileTime = ArrayFileTime[i]; pbAttrPtr += cbArraySize; } @@ -150,13 +196,13 @@ static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrF if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5) { LPBYTE ArrayMd5 = pbAttrPtr; - DWORD cbArraySize = dwBlockTableSize * MD5_DIGEST_SIZE; + DWORD cbArraySize = dwAttributesEntries * MD5_DIGEST_SIZE; // Verify if there's enough data if((pbAttrPtr + cbArraySize) > pbAttrFileEnd) return ERROR_FILE_CORRUPT; - for(i = 0; i < dwBlockTableSize; i++) + for(i = 0; i < dwAttributesEntries; i++) { memcpy(ha->pFileTable[i].md5, ArrayMd5, MD5_DIGEST_SIZE); ArrayMd5 += MD5_DIGEST_SIZE; @@ -165,36 +211,25 @@ static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrF } // Read the patch bit for each file (if present) - if((ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) && bPatchBitsValid) + if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) { LPBYTE pbBitArray = pbAttrPtr; - DWORD cbArraySize = (dwBlockTableSize + 7) / 8; + DWORD cbArraySize = (dwAttributesEntries + 7) / 8; DWORD dwByteIndex = 0; DWORD dwBitMask = 0x80; // Verify if there's enough data - if((pbAttrPtr + cbArraySize) > pbAttrFileEnd) - return ERROR_FILE_CORRUPT; - - for(i = 0; i < dwBlockTableSize; i++) + if((pbAttrPtr + cbArraySize) == pbAttrFileEnd) { - ha->pFileTable[i].dwFlags |= (pbBitArray[dwByteIndex] & dwBitMask) ? MPQ_FILE_PATCH_FILE : 0; - dwByteIndex += (dwBitMask & 0x01); - dwBitMask = (dwBitMask << 0x07) | (dwBitMask >> 0x01); + for(i = 0; i < dwAttributesEntries; i++) + { + ha->pFileTable[i].dwFlags |= (pbBitArray[dwByteIndex] & dwBitMask) ? MPQ_FILE_PATCH_FILE : 0; + dwByteIndex += (dwBitMask & 0x01); + dwBitMask = (dwBitMask << 0x07) | (dwBitMask >> 0x01); + } } - - pbAttrPtr += cbArraySize; } - // - // Note: Version 7.00 of StormLib saved the (attributes) incorrectly. - // Sometimes, number of entries in the (attributes) was 1 item less - // than block table size. - // If we encounter such table, we will zero all three arrays - // - - if(pbAttrPtr != pbAttrFileEnd) - ha->dwAttrFlags = 0; return ERROR_SUCCESS; } @@ -333,7 +368,6 @@ int SAttrLoadAttributes(TMPQArchive * ha) return ERROR_FILE_CORRUPT; // Attempt to open the "(attributes)" file. - // If it's not there, then the archive doesn't support attributes if(SFileOpenFileEx((HANDLE)ha, ATTRIBUTES_NAME, SFILE_OPEN_ANY_LOCALE, &hFile)) { // Retrieve and check size of the (attributes) file |