From b8fb98fcc4aa2d1c6ab6ce57b6b2e10a25861a56 Mon Sep 17 00:00:00 2001 From: Ladislav Zezula Date: Fri, 30 Sep 2016 11:23:29 +0200 Subject: + Yet another protector --- src/SBaseCommon.cpp | 2 +- src/SBaseDumpData.cpp | 4 +-- src/SBaseFileTable.cpp | 40 +++++++++++++++--------- src/SBaseSubTypes.cpp | 4 +-- src/StormLib.h | 7 +++-- storm_dll/storm_test.cpp | 12 ++++---- test/StormTest.cpp | 80 ++++++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 119 insertions(+), 30 deletions(-) diff --git a/src/SBaseCommon.cpp b/src/SBaseCommon.cpp index 977664a..aa891df 100644 --- a/src/SBaseCommon.cpp +++ b/src/SBaseCommon.cpp @@ -721,7 +721,7 @@ TMPQHash * AllocateHashEntry( pHash->dwName1 = dwName1; pHash->dwName2 = dwName2; pHash->lcLocale = (USHORT)lcLocale; - pHash->wPlatform = 0; + pHash->Platform = 0; pHash->dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable); } diff --git a/src/SBaseDumpData.cpp b/src/SBaseDumpData.cpp index d156030..334561b 100644 --- a/src/SBaseDumpData.cpp +++ b/src/SBaseDumpData.cpp @@ -51,11 +51,11 @@ void DumpHashTable(TMPQHash * pHashTable, DWORD dwHashTableSize) printf("== Hash Table =================================\n"); for(i = 0; i < dwHashTableSize; i++) { - printf("[%08x] %08X %08X %04X %04X %08X\n", i, + printf("[%08x] %08X %08X %04X %02X %08X\n", i, pHashTable[i].dwName1, pHashTable[i].dwName2, pHashTable[i].lcLocale, - pHashTable[i].wPlatform, + pHashTable[i].Platform, pHashTable[i].dwBlockIndex); } printf("-----------------------------------------------\n\n"); diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp index fe5eb78..c7a26aa 100644 --- a/src/SBaseFileTable.cpp +++ b/src/SBaseFileTable.cpp @@ -612,32 +612,42 @@ static bool IsValidHashEntry1(TMPQArchive * ha, TMPQHash * pHash, TMPQBlock * pB } // Returns a hash table entry in the following order: -// 1) A hash table entry with the preferred locale -// 2) A hash table entry with the neutral locale +// 1) A hash table entry with the preferred locale and platform +// 2) A hash table entry with the neutral|matching locale and neutral|matching platform // 3) NULL -static TMPQHash * GetHashEntryLocale(TMPQArchive * ha, const char * szFileName, LCID lcLocale) +// Storm_2016.dll: 15020940 +static TMPQHash * GetHashEntryLocale(TMPQArchive * ha, const char * szFileName, LCID lcLocale, BYTE Platform) { - TMPQHash * pHashNeutral = NULL; TMPQHash * pFirstHash = GetFirstHashEntry(ha, szFileName); + TMPQHash * pBestEntry = NULL; TMPQHash * pHash = pFirstHash; // Parse the found hashes while(pHash != NULL) { - // If the locales match, return it - if(lcLocale == pHash->lcLocale) + // Storm_2016.dll: 150209CB + // If the hash entry matches both locale and platform, return it immediately + // Note: We only succeed this check if the locale is non-neutral, because + // some Warcraft III maps have several items with neutral locale&platform, which leads + // to wrong item being returned + if((lcLocale || Platform) && pHash->lcLocale == lcLocale && pHash->Platform == Platform) return pHash; - - // If we found neutral hash, remember it - if(pHash->lcLocale == 0) - pHashNeutral = pHash; + + // Storm_2016.dll: 150209D9 + // If (locale matches or is neutral) OR (platform matches or is neutral) + // remember this as the best entry + if(pHash->lcLocale == 0 || pHash->lcLocale == lcLocale) + { + if(pHash->Platform == 0 || pHash->Platform == Platform) + pBestEntry = pHash; + } // Get the next hash entry for that file pHash = GetNextHashEntry(ha, pFirstHash, pHash); } // At the end, return neutral hash (if found), otherwise NULL - return pHashNeutral; + return pBestEntry; } // Returns a hash table entry in the following order: @@ -1803,7 +1813,7 @@ TFileEntry * GetFileEntryLocale2(TMPQArchive * ha, const char * szFileName, LCID // we will need the pointer to hash table entry if(ha->pHashTable != NULL) { - pHash = GetHashEntryLocale(ha, szFileName, lcLocale); + pHash = GetHashEntryLocale(ha, szFileName, lcLocale, 0); if(pHash != NULL && MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize) { if(PtrHashIndex != NULL) @@ -1987,7 +1997,8 @@ int RenameFileEntry( pHashEntry->dwName1 = 0xFFFFFFFF; pHashEntry->dwName2 = 0xFFFFFFFF; pHashEntry->lcLocale = 0xFFFF; - pHashEntry->wPlatform = 0xFFFF; + pHashEntry->Platform = 0xFF; + pHashEntry->Reserved = 0xFF; pHashEntry->dwBlockIndex = HASH_ENTRY_DELETED; } @@ -2027,7 +2038,8 @@ int DeleteFileEntry(TMPQArchive * ha, TMPQFile * hf) pHashEntry->dwName1 = 0xFFFFFFFF; pHashEntry->dwName2 = 0xFFFFFFFF; pHashEntry->lcLocale = 0xFFFF; - pHashEntry->wPlatform = 0xFFFF; + pHashEntry->Platform = 0xFF; + pHashEntry->Reserved = 0xFF; pHashEntry->dwBlockIndex = HASH_ENTRY_DELETED; } diff --git a/src/SBaseSubTypes.cpp b/src/SBaseSubTypes.cpp index 333b881..47c205e 100644 --- a/src/SBaseSubTypes.cpp +++ b/src/SBaseSubTypes.cpp @@ -216,7 +216,7 @@ TMPQHash * LoadSqpHashTable(TMPQArchive * ha) // Store the rest. Note that this must be done last, // because block index corresponds to pMpqHash->dwName2 pMpqHash->dwBlockIndex = MPQ_BLOCK_INDEX(pSqpHash); - pMpqHash->wPlatform = 0; + pMpqHash->Platform = 0; pMpqHash->lcLocale = 0; } } @@ -544,7 +544,7 @@ TMPQHash * LoadMpkHashTable(TMPQArchive * ha) // Copy the MPK hash entry to the hash table pHash->dwBlockIndex = pMpkHash[i].dwBlockIndex; - pHash->wPlatform = 0; + pHash->Platform = 0; pHash->lcLocale = 0; pHash->dwName1 = pMpkHash[i].dwName2; pHash->dwName2 = pMpkHash[i].dwName3; diff --git a/src/StormLib.h b/src/StormLib.h index 3621096..b6df481 100644 --- a/src/StormLib.h +++ b/src/StormLib.h @@ -617,12 +617,13 @@ typedef struct _TMPQHash // The platform the file is used for. 0 indicates the default platform. // No other values have been observed. - // Note: wPlatform is actually just BYTE, but since it has never been used, we don't care. - USHORT wPlatform; + BYTE Platform; + BYTE Reserved; #else - USHORT wPlatform; + BYTE Platform; + BYTE Reserved; USHORT lcLocale; #endif diff --git a/storm_dll/storm_test.cpp b/storm_dll/storm_test.cpp index 5e498b3..2d1e6bc 100644 --- a/storm_dll/storm_test.cpp +++ b/storm_dll/storm_test.cpp @@ -22,22 +22,22 @@ //----------------------------------------------------------------------------- // Main -unsigned char szKoreanFileName[] = {0x77, 0x61, 0x72, 0x33, 0x6D, 0x61, 0x70, 0x49, 0x6D, 0x70, 0x6F, 0x72, 0x74, 0x65, 0x64, 0x5C, 0xBF, 0xD5, 0xB1, 0xB9, 0x2E, 0x6D, 0x70, 0x33, 0x00}; - int main() { - LPCSTR szArchiveName = "e:\\MPQ_2016_v1_KoreanFile.w3m"; + LPCSTR szArchiveName = "e:\\Multimedia\\MPQs\\1995 - Test MPQs\\MPQ_2016_v1_123.w3x"; HANDLE hMpq = NULL; HANDLE hFile = NULL; - char szFileName[MAX_PATH]; + BYTE Buffer[0x100]; + DWORD dwBytesRead = 0; _asm int 3; if(StormOpenArchive(szArchiveName, 0, 0, &hMpq)) { - memcpy(szFileName, szKoreanFileName, _countof(szKoreanFileName)); - if(StormOpenFileEx(hMpq, szFileName, 0, &hFile)) + if(StormOpenFileEx(hMpq, "war3map.j", 0, &hFile)) { + dwBytesRead = StormGetFileSize(hFile, NULL); + StormReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead, NULL); StormCloseFile(hFile); } diff --git a/test/StormTest.cpp b/test/StormTest.cpp index 410dfd4..0feb003 100644 --- a/test/StormTest.cpp +++ b/test/StormTest.cpp @@ -308,6 +308,25 @@ static bool IsMpqExtension(const char * szFileName) return false; } +static void BinaryFromString(const char * szBinary, LPBYTE pbBuffer, DWORD cbBuffer) +{ + LPBYTE pbBufferEnd = pbBuffer + cbBuffer; + char * szTemp; + char szHexaDigit[4]; + + while(szBinary[0] != 0 && pbBuffer < pbBufferEnd) + { + // Get the 2-byte chunk + szHexaDigit[0] = szBinary[0]; + szHexaDigit[1] = szBinary[1]; + szHexaDigit[2] = 0; + + // Convert to integer + *pbBuffer++ = (BYTE)strtoul(szHexaDigit, &szTemp, 16); + szBinary += 2; + } +} + static void AddStringBeforeExtension(char * szBuffer, const char * szFileName, const char * szExtraString) { const char * szExtension; @@ -1675,7 +1694,7 @@ static int SearchArchive( // Increment number of files dwFileCount++; -// if(!_stricmp(sf.cFileName, "Interface\\Glues\\CREDITS\\1024px-Blade3_final2.blp")) +// if(!_stricmp(sf.cFileName, "war3map.j")) // DebugBreak(); if(dwTestFlags & TEST_FLAG_MOST_PATCHED) @@ -2634,6 +2653,60 @@ static int TestOpenArchive_SetPos(const char * szPlainName, const char * szFileN return nError; } +static int TestOpenArchive_ProtectedMap(const char * szPlainName, const char * szListFile = NULL, DWORD dwExpectedFileCount = 0, const char * szExpectedMD5 = NULL) +{ + TLogHelper Logger("ProtectedMapTest", szPlainName); + HANDLE hMpq; + DWORD dwTestFlags = TEST_FLAG_LOAD_FILES | TEST_FLAG_HASH_FILES; + DWORD dwFileCount = 0; + BYTE ExpectedMD5[MD5_DIGEST_SIZE]; + BYTE OverallMD5[MD5_DIGEST_SIZE]; + char szListFileBuff[MAX_PATH]; + int nError; + + // Copy the archive so we won't fuck up the original one + nError = OpenExistingArchiveWithCopy(&Logger, szPlainName, szPlainName, &hMpq); + if(nError == ERROR_SUCCESS) + { + // If the listfile was given, add it to the MPQ + if(szListFile != NULL) + { + Logger.PrintProgress("Adding listfile %s ...", szListFile); + CreateFullPathName(szListFileBuff, szMpqSubDir, szListFile); + nError = SFileAddListFile(hMpq, szListFileBuff); + if(nError != ERROR_SUCCESS) + Logger.PrintMessage("Failed to add the listfile to the MPQ"); + } + + // Search the archive and load every file + nError = SearchArchive(&Logger, hMpq, dwTestFlags, &dwFileCount, OverallMD5); + SFileCloseArchive(hMpq); + } + + // Check the file count and hash, if required + if(nError == ERROR_SUCCESS && dwExpectedFileCount != 0) + { + if(dwFileCount != dwExpectedFileCount) + { + Logger.PrintMessage("File count mismatch(expected: %u, found:%u)", dwExpectedFileCount, dwFileCount); + nError = ERROR_CAN_NOT_COMPLETE; + } + } + + // Check the overall hash, if required + if(nError == ERROR_SUCCESS && szExpectedMD5 != NULL && szExpectedMD5[0] != 0) + { + BinaryFromString(szExpectedMD5, ExpectedMD5, MD5_DIGEST_SIZE); + if(memcmp(ExpectedMD5, OverallMD5, MD5_DIGEST_SIZE)) + { + Logger.PrintMessage("Extracted files MD5 mismatch"); + nError = ERROR_CAN_NOT_COMPLETE; + } + } + + return nError; +} + // Open an empty archive (found in WoW cache - it's just a header) static int TestOpenArchive_WillFail(const char * szPlainName) { @@ -4470,9 +4543,12 @@ int main(int argc, char * argv[]) if(nError == ERROR_SUCCESS) nError = TestOpenArchive("MPQ_2016_v1_ProtectedMap_1.4.w3x"); -*/ + if(nError == ERROR_SUCCESS) nError = TestOpenArchive("MPQ_2016_v1_KoreanFile.w3m"); +*/ + if(nError == ERROR_SUCCESS) + nError = TestOpenArchive_ProtectedMap("MPQ_2016_v1_123.w3x", NULL, 17, "23b09ad3b8d89ec97df8860447abc7eb"); /* // Open the multi-file archive with wrong prefix to see how StormLib deals with it if(nError == ERROR_SUCCESS) -- cgit v1.2.3