diff options
Diffstat (limited to 'src/SBaseCommon.cpp')
-rw-r--r-- | src/SBaseCommon.cpp | 255 |
1 files changed, 142 insertions, 113 deletions
diff --git a/src/SBaseCommon.cpp b/src/SBaseCommon.cpp index ef753bd..be7a74d 100644 --- a/src/SBaseCommon.cpp +++ b/src/SBaseCommon.cpp @@ -266,175 +266,191 @@ DWORD GetDefaultSpecialFileFlags(DWORD dwFileSize, USHORT wFormatVersion) //----------------------------------------------------------------------------- -// Encrypting and decrypting MPQ file data +// Encrypting/Decrypting MPQ data block -void EncryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwSeed1) +void EncryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey1) { - LPDWORD block = (LPDWORD)pvFileBlock; - DWORD dwSeed2 = 0xEEEEEEEE; - DWORD ch; + LPDWORD DataBlock = (LPDWORD)pvDataBlock; + DWORD dwValue32; + DWORD dwKey2 = 0xEEEEEEEE; // Round to DWORDs dwLength >>= 2; - while(dwLength-- > 0) + // Encrypt the data block at array of DWORDs + for(DWORD i = 0; i < dwLength; i++) { - dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)]; - ch = *block; - *block++ = ch ^ (dwSeed1 + dwSeed2); + // Modify the second key + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; - dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B); - dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3; + dwValue32 = DataBlock[i]; + DataBlock[i] = DataBlock[i] ^ (dwKey1 + dwKey2); + + dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); + dwKey2 = dwValue32 + dwKey2 + (dwKey2 << 5) + 3; } } -void DecryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwSeed1) +void DecryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey1) { - LPDWORD block = (LPDWORD)pvFileBlock; - DWORD dwSeed2 = 0xEEEEEEEE; - DWORD ch; + LPDWORD DataBlock = (LPDWORD)pvDataBlock; + DWORD dwValue32; + DWORD dwKey2 = 0xEEEEEEEE; // Round to DWORDs dwLength >>= 2; - while(dwLength-- > 0) + // Decrypt the data block at array of DWORDs + for(DWORD i = 0; i < dwLength; i++) { - dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)]; - ch = *block ^ (dwSeed1 + dwSeed2); - - dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B); - dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3; - *block++ = ch; + // Modify the second key + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + + DataBlock[i] = DataBlock[i] ^ (dwKey1 + dwKey2); + dwValue32 = DataBlock[i]; + + dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); + dwKey2 = dwValue32 + dwKey2 + (dwKey2 << 5) + 3; } } /** - * Functions tries to get file decryption key. The trick comes from sector - * positions which are stored at the begin of each compressed file. We know the - * file size, that means we know number of sectors that means we know the first - * DWORD value in sector position. And if we know encrypted and decrypted value, - * we can find the decryption key !!! + * Functions tries to get file decryption key. This comes from these facts + * + * - We know the decrypted value of the first DWORD in the encrypted data + * - We know the decrypted value of the second DWORD (at least aproximately) + * - There is only 256 variants of how the second key is modified + * + * The first iteration of dwKey1 and dwKey2 is this: + * + * dwKey2 = 0xEEEEEEEE + StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)] + * dwDecrypted0 = DataBlock[0] ^ (dwKey1 + dwKey2); + * + * This means: + * + * (dwKey1 + dwKey2) = DataBlock[0] ^ dwDecrypted0; * - * hf - MPQ file handle - * SectorOffsets - DWORD array of sector positions - * ch - Decrypted value of the first sector pos */ -DWORD DetectFileKeyBySectorSize(LPDWORD SectorOffsets, DWORD decrypted) +DWORD DetectFileKeyBySectorSize(LPDWORD EncryptedData, DWORD dwSectorSize, DWORD dwDecrypted0) { - DWORD saveKey1; - DWORD temp = *SectorOffsets ^ decrypted; // temp = seed1 + seed2 - temp -= 0xEEEEEEEE; // temp = seed1 + StormBuffer[0x400 + (seed1 & 0xFF)] + DWORD dwDecrypted1Max = dwSectorSize + dwDecrypted0; + DWORD dwKey1PlusKey2; + DWORD DataBlock[2]; - for(int i = 0; i < 0x100; i++) // Try all 255 possibilities - { - DWORD seed1; - DWORD seed2 = 0xEEEEEEEE; - DWORD ch; + // We must have at least 2 DWORDs there to be able to decrypt something + if(dwSectorSize < 0x08) + return 0; + + // Get the value of the combined encryption key + dwKey1PlusKey2 = (EncryptedData[0] ^ dwDecrypted0) - 0xEEEEEEEE; - // Try the first DWORD (We exactly know the value) - seed1 = temp - StormBuffer[0x400 + i]; - seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; - ch = SectorOffsets[0] ^ (seed1 + seed2); + // Try all 256 combinations of dwKey1 + for(DWORD i = 0; i < 0x100; i++) + { + DWORD dwSaveKey1; + DWORD dwKey1 = dwKey1PlusKey2 - StormBuffer[MPQ_HASH_KEY2_MIX + i]; + DWORD dwKey2 = 0xEEEEEEEE; - if(ch != decrypted) - continue; + // Modify the second key and decrypt the first DWORD + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + DataBlock[0] = EncryptedData[0] ^ (dwKey1 + dwKey2); - // Add 1 because we are decrypting sector positions - saveKey1 = seed1 + 1; + // Did we obtain the same value like dwDecrypted0? + if(DataBlock[0] == dwDecrypted0) + { + // Save this key value. Increment by one because + // we are decrypting sector offset table + dwSaveKey1 = dwKey1 + 1; - // If OK, continue and test the second value. We don't know exactly the value, - // but we know that the second one has lower 16 bits set to zero - // (no compressed sector is larger than 0xFFFF bytes) - seed1 = ((~seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B); - seed2 = ch + seed2 + (seed2 << 5) + 3; + // Rotate both keys + dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); + dwKey2 = DataBlock[0] + dwKey2 + (dwKey2 << 5) + 3; - seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; - ch = SectorOffsets[1] ^ (seed1 + seed2); + // Modify the second key again and decrypt the second DWORD + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + DataBlock[1] = EncryptedData[1] ^ (dwKey1 + dwKey2); - if((ch & 0xFFFF0000) == 0) - return saveKey1; + // Now compare the results + if(DataBlock[1] <= dwDecrypted1Max) + return dwSaveKey1; + } } + + // Key not found return 0; } -// Function tries to detect file encryption key. It expectes at least two uncompressed bytes -DWORD DetectFileKeyByKnownContent(void * pvFileContent, DWORD nDwords, ...) +// Function tries to detect file encryption key based on expected file content +// It is the same function like before, except that we know the value of the second DWORD +DWORD DetectFileKeyByKnownContent(void * pvEncryptedData, DWORD dwDecrypted0, DWORD dwDecrypted1) { - LPDWORD pdwContent = (LPDWORD)pvFileContent; - va_list argList; - DWORD dwDecrypted[0x10]; - DWORD saveKey1; - DWORD dwTemp; - DWORD i, j; - - // We need at least two DWORDS to detect the file key - if(nDwords < 0x02 || nDwords > 0x10) - return 0; - memset(dwDecrypted, 0, sizeof(dwDecrypted)); - - va_start(argList, nDwords); - for(i = 0; i < nDwords; i++) - dwDecrypted[i] = va_arg(argList, DWORD); - va_end(argList); - - dwTemp = (*pdwContent ^ dwDecrypted[0]) - 0xEEEEEEEE; - for(i = 0; i < 0x100; i++) // Try all 256 possibilities - { - DWORD seed1; - DWORD seed2 = 0xEEEEEEEE; - DWORD ch; + LPDWORD EncryptedData = (LPDWORD)pvEncryptedData; + DWORD dwKey1PlusKey2; + DWORD DataBlock[2]; - // Try the first DWORD - seed1 = dwTemp - StormBuffer[0x400 + i]; - seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; - ch = pdwContent[0] ^ (seed1 + seed2); + // Get the value of the combined encryption key + dwKey1PlusKey2 = (EncryptedData[0] ^ dwDecrypted0) - 0xEEEEEEEE; - if(ch != dwDecrypted[0]) - continue; + // Try all 256 combinations of dwKey1 + for(DWORD i = 0; i < 0x100; i++) + { + DWORD dwSaveKey1; + DWORD dwKey1 = dwKey1PlusKey2 - StormBuffer[MPQ_HASH_KEY2_MIX + i]; + DWORD dwKey2 = 0xEEEEEEEE; - saveKey1 = seed1; + // Modify the second key and decrypt the first DWORD + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + DataBlock[0] = EncryptedData[0] ^ (dwKey1 + dwKey2); - // If OK, continue and test all bytes. - for(j = 1; j < nDwords; j++) + // Did we obtain the same value like dwDecrypted0? + if(DataBlock[0] == dwDecrypted0) { - seed1 = ((~seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B); - seed2 = ch + seed2 + (seed2 << 5) + 3; + // Save this key value + dwSaveKey1 = dwKey1; - seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; - ch = pdwContent[j] ^ (seed1 + seed2); + // Rotate both keys + dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); + dwKey2 = DataBlock[0] + dwKey2 + (dwKey2 << 5) + 3; - if(ch == dwDecrypted[j] && j == nDwords - 1) - return saveKey1; + // Modify the second key again and decrypt the second DWORD + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + DataBlock[1] = EncryptedData[1] ^ (dwKey1 + dwKey2); + + // Now compare the results + if(DataBlock[1] == dwDecrypted1) + return dwSaveKey1; } } + + // Key not found return 0; } -DWORD DetectFileKeyByContent(void * pvFileContent, DWORD dwFileSize) +DWORD DetectFileKeyByContent(void * pvEncryptedData, DWORD dwSectorSize, DWORD dwFileSize) { DWORD dwFileKey; // Try to break the file encryption key as if it was a WAVE file - if(dwFileSize >= 0x0C) + if(dwSectorSize >= 0x0C) { - dwFileKey = DetectFileKeyByKnownContent(pvFileContent, 3, 0x46464952, dwFileSize - 8, 0x45564157); + dwFileKey = DetectFileKeyByKnownContent(pvEncryptedData, 0x46464952, dwFileSize - 8); if(dwFileKey != 0) return dwFileKey; } // Try to break the encryption key as if it was an EXE file - if(dwFileSize > 0x40) + if(dwSectorSize > 0x40) { - dwFileKey = DetectFileKeyByKnownContent(pvFileContent, 2, 0x00905A4D, 0x00000003); + dwFileKey = DetectFileKeyByKnownContent(pvEncryptedData, 0x00905A4D, 0x00000003); if(dwFileKey != 0) return dwFileKey; } // Try to break the encryption key as if it was a XML file - if(dwFileSize > 0x04) + if(dwSectorSize > 0x04) { - dwFileKey = DetectFileKeyByKnownContent(pvFileContent, 2, 0x6D783F3C, 0x6576206C); + dwFileKey = DetectFileKeyByKnownContent(pvEncryptedData, 0x6D783F3C, 0x6576206C); if(dwFileKey != 0) return dwFileKey; } @@ -673,7 +689,7 @@ ULONGLONG FindFreeMpqSpace(TMPQArchive * ha) //----------------------------------------------------------------------------- // Common functions - MPQ File -TMPQFile * CreateMpqFile(TMPQArchive * ha) +TMPQFile * CreateFileHandle(TMPQArchive * ha, TFileEntry * pFileEntry) { TMPQFile * hf; @@ -683,9 +699,21 @@ TMPQFile * CreateMpqFile(TMPQArchive * ha) { // Fill the file structure memset(hf, 0, sizeof(TMPQFile)); - hf->ha = ha; - hf->pStream = NULL; hf->dwMagic = ID_MPQ_FILE; + hf->pStream = NULL; + hf->ha = ha; + + // If the called entered a file entry, we also copy informations from the file entry + if(ha != NULL && pFileEntry != NULL) + { + // Set the raw position and MPQ position + hf->RawFilePos = ha->MpqPos + pFileEntry->ByteOffset; + hf->MpqFilePos = pFileEntry->ByteOffset; + + // Set the data size + hf->dwDataSize = pFileEntry->dwFileSize; + hf->pFileEntry = pFileEntry; + } } return hf; @@ -863,7 +891,7 @@ __AllocateAndLoadPatchInfo: // Allocate space for patch header. Start with default size, // and if its size if bigger, then we reload them - hf->pPatchInfo = (TPatchInfo *)STORM_ALLOC(BYTE, dwLength); + hf->pPatchInfo = STORM_ALLOC(TPatchInfo, 1); if(hf->pPatchInfo == NULL) return ERROR_NOT_ENOUGH_MEMORY; @@ -983,7 +1011,7 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) // If we don't know the file key, try to find it. if(hf->dwFileKey == 0) { - hf->dwFileKey = DetectFileKeyBySectorSize(hf->SectorOffsets, dwSectorOffsLen); + hf->dwFileKey = DetectFileKeyBySectorSize(hf->SectorOffsets, ha->dwSectorSize, dwSectorOffsLen); if(hf->dwFileKey == 0) { STORM_FREE(hf->SectorOffsets); @@ -1356,13 +1384,13 @@ int WriteMpqDataMD5( } // Frees the structure for MPQ file -void FreeMPQFile(TMPQFile *& hf) +void FreeFileHandle(TMPQFile *& hf) { if(hf != NULL) { // If we have patch file attached to this one, free it first if(hf->hfPatch != NULL) - FreeMPQFile(hf->hfPatch); + FreeFileHandle(hf->hfPatch); // Then free all buffers allocated in the file structure if(hf->pPatchHeader != NULL) @@ -1377,20 +1405,21 @@ void FreeMPQFile(TMPQFile *& hf) STORM_FREE(hf->SectorChksums); if(hf->pbFileSector != NULL) STORM_FREE(hf->pbFileSector); - FileStream_Close(hf->pStream); + if(hf->pStream != NULL) + FileStream_Close(hf->pStream); STORM_FREE(hf); hf = NULL; } } // Frees the MPQ archive -void FreeMPQArchive(TMPQArchive *& ha) +void FreeArchiveHandle(TMPQArchive *& ha) { if(ha != NULL) { // First of all, free the patch archive, if any if(ha->haPatch != NULL) - FreeMPQArchive(ha->haPatch); + FreeArchiveHandle(ha->haPatch); // Close the file stream FileStream_Close(ha->pStream); |