From 29901739a2506999955e1fdd5a2f8f7b2140b089 Mon Sep 17 00:00:00 2001 From: Ladislav Zezula Date: Mon, 15 Apr 2024 23:30:36 +0200 Subject: Work in progress: New MPQ protector --- src/SBaseFileTable.cpp | 10 +++++++++- src/SFileOpenArchive.cpp | 39 +++++++++++++++++++++++++++++---------- test/StormTest.cpp | 20 +++++++++++++------- 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp index fa388fc..a222ac5 100644 --- a/src/SBaseFileTable.cpp +++ b/src/SBaseFileTable.cpp @@ -479,8 +479,16 @@ DWORD ConvertMpqHeaderToFormat4( { case MPQ_FORMAT_VERSION_1: - // Check for malformed MPQ header version 1.0 + // Make sure that the MPQ Header is properly swapped BSWAP_TMPQHEADER(pHeader, MPQ_FORMAT_VERSION_1); + + // Check for blatantly wrong MPQ header by the hash table position + if(((ByteOffset + pHeader->dwHashTablePos) & 0xFFFFFFFF) > FileSize) + return ERROR_FAKE_MPQ_HEADER; + if(((ByteOffset + pHeader->dwBlockTablePos) & 0xFFFFFFFF) > FileSize) + return ERROR_FAKE_MPQ_HEADER; + + // Check for malformed MPQ header version 1.0 if(pHeader->wFormatVersion != MPQ_FORMAT_VERSION_1 || pHeader->dwHeaderSize != MPQ_HEADER_SIZE_V1) { pHeader->wFormatVersion = MPQ_FORMAT_VERSION_1; diff --git a/src/SFileOpenArchive.cpp b/src/SFileOpenArchive.cpp index 4b2d3b4..a97ecea 100644 --- a/src/SFileOpenArchive.cpp +++ b/src/SFileOpenArchive.cpp @@ -228,7 +228,7 @@ bool WINAPI SFileOpenArchive( DWORD dwFlags, HANDLE * phMpq) { - TMPQUserData * pUserData; + TMPQUserData * pUserData = NULL; TFileStream * pStream = NULL; // Open file stream TMPQArchive * ha = NULL; // Archive handle TFileEntry * pFileEntry; @@ -354,18 +354,25 @@ bool WINAPI SFileOpenArchive( { if(ha->pUserData == NULL && dwHeaderID == ID_MPQ_USERDATA) { + // Copy the eventual user data to the separate buffer + memcpy(&ha->UserData, ha->HeaderData, sizeof(TMPQUserData)); + // Verify if this looks like a valid user data - pUserData = IsValidMpqUserData(ByteOffset, FileSize, ha->HeaderData); + pUserData = IsValidMpqUserData(ByteOffset, FileSize, &ha->UserData); if(pUserData != NULL) { - // Fill the user data header - ha->UserDataPos = ByteOffset; - ha->pUserData = &ha->UserData; - memcpy(ha->pUserData, pUserData, sizeof(TMPQUserData)); - - // Continue searching from that position - ByteOffset += ha->pUserData->dwHeaderOffs; - break; + // Set the byte offset to the loaded user data + ULONGLONG TempByteOffset = ByteOffset + pUserData->dwHeaderOffs; + + // Read the eventual MPQ header from the position where the user data points + if(!FileStream_Read(ha->pStream, &TempByteOffset, ha->HeaderData, sizeof(ha->HeaderData))) + { + dwErrCode = GetLastError(); + break; + } + + // Re-initialize the header ID + dwHeaderID = BSWAP_INT32_UNSIGNED(ha->HeaderData[0]); } } } @@ -405,12 +412,24 @@ bool WINAPI SFileOpenArchive( // Move the pointers ByteOffset += 0x200; + pUserData = NULL; } } // Did we identify one of the supported headers? if(dwErrCode == ERROR_SUCCESS) { + // If we retrieved the offset from the user data offset, initialize the user data + if(pUserData != NULL) + { + // Fill the user data header + ha->pUserData = &ha->UserData; + ha->UserDataPos = ByteOffset; + + // Set the real byte offset + ByteOffset = ByteOffset + pUserData->dwHeaderOffs; + } + // Set the user data position to the MPQ header, if none if(ha->pUserData == NULL) ha->UserDataPos = ByteOffset; diff --git a/test/StormTest.cpp b/test/StormTest.cpp index 048f715..55700be 100755 --- a/test/StormTest.cpp +++ b/test/StormTest.cpp @@ -1651,7 +1651,7 @@ static DWORD SearchArchive( // Construct the full name of the listfile CreateFullPathName(szListFile, _countof(szListFile), szListFileDir, _T("ListFile_Blizzard.txt")); - // fp = fopen("E:\\mpq-listing.txt", "wt"); + fp = fopen("E:\\mpq-listing.txt", "wt"); // Prepare hashing md5_init(&md5state); @@ -1694,7 +1694,7 @@ static DWORD SearchArchive( if(fp != NULL) { pFileData->dwCrc32 = crc32(0, pFileData->FileData, pFileData->dwFileSize); - fprintf(fp, "%08x: %s \n", pFileData->dwCrc32, sf.cFileName); + fprintf(fp, "%08x:%08x: %s \n", pFileData->dwFileSize, pFileData->dwCrc32, sf.cFileName); } // Free the loaded file data @@ -3986,6 +3986,7 @@ static const TEST_INFO1 TestList_MasterMirror[] = static const TEST_INFO1 Test_OpenMpqs[] = { +/* // Correct or damaged archives {_T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ"), NULL, "554b538541e42170ed41cb236483489e", 2910, &TwoFilesD1}, // Base MPQ from Diablo 1 {_T("MPQ_1997_v1_patch_rt_SC1B.mpq"), NULL, "43fe7d362955be68a708486e399576a7", 10}, // From Starcraft 1 BETA @@ -4046,7 +4047,9 @@ static const TEST_INFO1 Test_OpenMpqs[] = {_T("MPQ_2022_v1_Sniper.scx"), NULL, "2e955271b70b79344ad85b698f6ce9d8", 64}, // Multiple items in hash table for staredit\scenario.chk (locale=0, platform=0) {_T("MPQ_2022_v1_OcOc_Bound_2.scx"), NULL, "25cad16a2fb4e883767a1f512fc1dce7", 16}, {_T("MPQ_2023_v1_Lusin2Rpg1.28.w3x"), NULL, "9c21352f06cf763fcf05e8a2691e6194", 10305, &HashVals}, - +*/ + {_T("MPQ_2024_v1_300TK2.09p.w3x"), NULL, "e126ea5df9028e5ee3d7ad4bcf09bab5", 32588}, // Fake MPQ User data, fake MPQ header at offset 0x200 +/* // ASI plugins {_T("MPQ_2020_v1_HS0.1.asi"), NULL, "50cba7460a6e6d270804fb9776a7ec4f", 6022}, {_T("MPQ_2022_v1_hs0.8.asi"), NULL, "6a40f733428001805bfe6e107ca9aec1", 11352}, // Items in hash table have platform = 0xFF @@ -4093,6 +4096,7 @@ static const TEST_INFO1 Test_OpenMpqs[] = // Check the GetFileInfo operations {_T("MPQ_2002_v1_StrongSignature.w3m"), NULL, "7b725d87e07a2173c42fe2314b95fa6c", 17 | TFLG_GET_FILE_INFO}, {_T("MPQ_2013_v4_SC2_EmptyMap.SC2Map"), NULL, "88e1b9a88d56688c9c24037782b7bb68", 33 | TFLG_GET_FILE_INFO}, +*/ }; static const TEST_INFO1 Test_ReopenMpqs[] = @@ -4164,10 +4168,10 @@ static const LPCSTR Test_CreateMpq_Localized[] = //----------------------------------------------------------------------------- // Main -#define TEST_COMMAND_LINE -#define TEST_LOCAL_LISTFILE -#define TEST_STREAM_OPERATIONS -#define TEST_MASTER_MIRROR +//#define TEST_COMMAND_LINE +//#define TEST_LOCAL_LISTFILE +//#define TEST_STREAM_OPERATIONS +//#define TEST_MASTER_MIRROR #define TEST_OPEN_MPQ #define TEST_REOPEN_MPQ #define TEST_VERIFY_SIGNATURE @@ -4244,6 +4248,8 @@ int _tmain(int argc, TCHAR * argv[]) } #endif // TEST_OPEN_MPQ + ExitProcess(0); + #ifdef TEST_REOPEN_MPQ // Test operations involving reopening the archive if(dwErrCode == ERROR_SUCCESS) { -- cgit v1.2.3