diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 6f318288ed..349ea0893c 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -225,14 +225,12 @@ bool CBoot::BootUp() DVDInterface::SetDiscInside(VolumeHandler::IsValid()); - u32 _TMDsz = 0x208; - u8* _pTMD = new u8[_TMDsz]; - pVolume->GetTMD(_pTMD, &_TMDsz); - if (_TMDsz) + u32 tmd_size; + std::unique_ptr tmd_buf = pVolume->GetTMD(&tmd_size); + if (tmd_size) { - WII_IPC_HLE_Interface::ES_DIVerify(_pTMD, _TMDsz); + WII_IPC_HLE_Interface::ES_DIVerify(tmd_buf.get(), tmd_size); } - delete []_pTMD; _StartupPara.bWii = VolumeHandler::IsWii(); diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp index 0d38bba31f..aef2190d1a 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include "Common/CommonTypes.h" #include "Common/Logging/LogManager.h" @@ -107,19 +108,16 @@ bool CWII_IPC_HLE_Device_di::IOCtlV(u32 _CommandAddress) _dbg_assert_msg_(WII_IPC_DVD, CommandBuffer.InBuffer[1].m_Address == 0, "DVDLowOpenPartition with ticket"); _dbg_assert_msg_(WII_IPC_DVD, CommandBuffer.InBuffer[2].m_Address == 0, "DVDLowOpenPartition with cert chain"); - // Get TMD offset for requested partition... - u64 const TMDOffset = ((u64)Memory::Read_U32(CommandBuffer.InBuffer[0].m_Address + 4) << 2 ) + 0x2c0; + u64 const partition_offset = ((u64)Memory::Read_U32(CommandBuffer.InBuffer[0].m_Address + 4) << 2); + VolumeHandler::GetVolume()->ChangePartition(partition_offset); - INFO_LOG(WII_IPC_DVD, "DVDLowOpenPartition: TMDOffset 0x%016" PRIx64, TMDOffset); - - static u32 const TMDsz = 0x208; //CommandBuffer.PayloadBuffer[0].m_Size; - u8 pTMD[TMDsz]; + INFO_LOG(WII_IPC_DVD, "DVDLowOpenPartition: partition_offset 0x%016" PRIx64, partition_offset); // Read TMD to the buffer - VolumeHandler::RAWReadToPtr(pTMD, TMDOffset, TMDsz); - - Memory::CopyToEmu(CommandBuffer.PayloadBuffer[0].m_Address, pTMD, TMDsz); - WII_IPC_HLE_Interface::ES_DIVerify(pTMD, TMDsz); + u32 tmd_size; + std::unique_ptr tmd_buf = VolumeHandler::GetVolume()->GetTMD(&tmd_size); + Memory::CopyToEmu(CommandBuffer.PayloadBuffer[0].m_Address, tmd_buf.get(), tmd_size); + WII_IPC_HLE_Interface::ES_DIVerify(tmd_buf.get(), tmd_size); ReturnValue = 1; } diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index c15be880ea..6162e0db8f 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -20,7 +21,11 @@ public: virtual bool Read(u64 _Offset, u64 _Length, u8* _pBuffer) const = 0; virtual bool RAWRead(u64 _Offset, u64 _Length, u8* _pBuffer) const = 0; virtual bool GetTitleID(u8*) const { return false; } - virtual void GetTMD(u8*, u32 *_sz) const { *_sz=0; } + virtual std::unique_ptr GetTMD(u32 *_sz) const + { + *_sz = 0; + return std::unique_ptr(); + } virtual std::string GetUniqueID() const = 0; virtual std::string GetRevisionSpecificUniqueID() const { return ""; } virtual std::string GetMakerID() const = 0; @@ -34,6 +39,8 @@ public: virtual bool CheckIntegrity() const { return false; } virtual bool IsDiscTwo() const { return false; } + virtual bool ChangePartition(u64 offset) { return false; } + // Increment CACHE_REVISION if values are changed (ISOFile.cpp) enum ECountry { diff --git a/Source/Core/DiscIO/VolumeCreator.cpp b/Source/Core/DiscIO/VolumeCreator.cpp index 4e740554f6..24fa75909e 100644 --- a/Source/Core/DiscIO/VolumeCreator.cpp +++ b/Source/Core/DiscIO/VolumeCreator.cpp @@ -69,7 +69,7 @@ static const unsigned char s_master_key_korean[16] = { 0x13,0xf2,0xfe,0xfb,0xba,0x4c,0x9b,0x7e }; -static IVolume* CreateVolumeFromCryptedWiiImage(IBlobReader& _rReader, u32 _PartitionGroup, u32 _VolumeType, u32 _VolumeNum, bool Korean); +static IVolume* CreateVolumeFromCryptedWiiImage(IBlobReader& _rReader, u32 _PartitionGroup, u32 _VolumeType, u32 _VolumeNum); EDiscType GetDiscType(IBlobReader& _rReader); IVolume* CreateVolumeFromFilename(const std::string& _rFilename, u32 _PartitionGroup, u32 _VolumeNum) @@ -89,10 +89,7 @@ IVolume* CreateVolumeFromFilename(const std::string& _rFilename, u32 _PartitionG case DISC_TYPE_WII_CONTAINER: { - u8 region; - pReader->Read(0x3,1,®ion); - - IVolume* pVolume = CreateVolumeFromCryptedWiiImage(*pReader, _PartitionGroup, 0, _VolumeNum, region == 'K'); + IVolume* pVolume = CreateVolumeFromCryptedWiiImage(*pReader, _PartitionGroup, 0, _VolumeNum); if (pVolume == nullptr) { @@ -140,7 +137,32 @@ bool IsVolumeWadFile(const IVolume *_rVolume) return (Common::swap32(MagicWord) == 0x00204973 || Common::swap32(MagicWord) == 0x00206962); } -static IVolume* CreateVolumeFromCryptedWiiImage(IBlobReader& _rReader, u32 _PartitionGroup, u32 _VolumeType, u32 _VolumeNum, bool Korean) +void VolumeKeyForParition(IBlobReader& _rReader, u64 offset, u8* VolumeKey) +{ + CBlobBigEndianReader Reader(_rReader); + + u8 SubKey[16]; + _rReader.Read(offset + 0x1bf, 16, SubKey); + + u8 IV[16]; + memset(IV, 0, 16); + _rReader.Read(offset + 0x44c, 8, IV); + + bool usingKoreanKey = false; + // Issue: 6813 + // Magic value is at partition's offset + 0x1f1 (1byte) + // If encrypted with the Korean key, the magic value would be 1 + // Otherwise it is zero + if (Reader.Read8(0x3) == 'K' && Reader.Read8(offset + 0x1f1) == 1) + usingKoreanKey = true; + + aes_context AES_ctx; + aes_setkey_dec(&AES_ctx, (usingKoreanKey ? s_master_key_korean : s_master_key), 128); + + aes_crypt_cbc(&AES_ctx, AES_DECRYPT, 16, IV, SubKey, VolumeKey); +} + +static IVolume* CreateVolumeFromCryptedWiiImage(IBlobReader& _rReader, u32 _PartitionGroup, u32 _VolumeType, u32 _VolumeNum) { CBlobBigEndianReader Reader(_rReader); @@ -184,32 +206,11 @@ static IVolume* CreateVolumeFromCryptedWiiImage(IBlobReader& _rReader, u32 _Part { const SPartition& rPartition = PartitionGroup[_PartitionGroup].PartitionsVec.at(i); - if (rPartition.Type == _VolumeType || i == _VolumeNum) + if ((rPartition.Type == _VolumeType && (int)_VolumeNum == -1) || i == _VolumeNum) { - u8 SubKey[16]; - _rReader.Read(rPartition.Offset + 0x1bf, 16, SubKey); - - u8 IV[16]; - memset(IV, 0, 16); - _rReader.Read(rPartition.Offset + 0x44c, 8, IV); - - bool usingKoreanKey = false; - // Issue: 6813 - // Magic value is at partition's offset + 0x1f1 (1byte) - // If encrypted with the Korean key, the magic value would be 1 - // Otherwise it is zero - if (Korean && Reader.Read8(rPartition.Offset + 0x1f1) == 1) - usingKoreanKey = true; - - aes_context AES_ctx; - aes_setkey_dec(&AES_ctx, (usingKoreanKey ? s_master_key_korean : s_master_key), 128); - u8 VolumeKey[16]; - aes_crypt_cbc(&AES_ctx, AES_DECRYPT, 16, IV, SubKey, VolumeKey); - - // -1 means the caller just wanted the partition with matching type - if ((int)_VolumeNum == -1 || i == _VolumeNum) - return new CVolumeWiiCrypted(&_rReader, rPartition.Offset, VolumeKey); + VolumeKeyForParition(_rReader, rPartition.Offset, VolumeKey); + return new CVolumeWiiCrypted(&_rReader, rPartition.Offset, VolumeKey); } } diff --git a/Source/Core/DiscIO/VolumeCreator.h b/Source/Core/DiscIO/VolumeCreator.h index e37db0b07b..b12983ff5f 100644 --- a/Source/Core/DiscIO/VolumeCreator.h +++ b/Source/Core/DiscIO/VolumeCreator.h @@ -12,10 +12,12 @@ namespace DiscIO { class IVolume; +class IBlobReader; IVolume* CreateVolumeFromFilename(const std::string& _rFilename, u32 _PartitionGroup = 0, u32 _VolumeNum = -1); IVolume* CreateVolumeFromDirectory(const std::string& _rDirectory, bool _bIsWii, const std::string& _rApploader = "", const std::string& _rDOL = ""); bool IsVolumeWiiDisc(const IVolume *_rVolume); bool IsVolumeWadFile(const IVolume *_rVolume); +void VolumeKeyForParition(IBlobReader& _rReader, u64 offset, u8* VolumeKey); } // namespace diff --git a/Source/Core/DiscIO/VolumeWiiCrypted.cpp b/Source/Core/DiscIO/VolumeWiiCrypted.cpp index 1406fc9c69..e769a1cae8 100644 --- a/Source/Core/DiscIO/VolumeWiiCrypted.cpp +++ b/Source/Core/DiscIO/VolumeWiiCrypted.cpp @@ -11,9 +11,11 @@ #include "Common/CommonFuncs.h" #include "Common/CommonTypes.h" +#include "Common/MsgHandler.h" #include "Common/Logging/Log.h" #include "DiscIO/Blob.h" #include "DiscIO/Volume.h" +#include "DiscIO/VolumeCreator.h" #include "DiscIO/VolumeGC.h" #include "DiscIO/VolumeWiiCrypted.h" @@ -33,6 +35,17 @@ CVolumeWiiCrypted::CVolumeWiiCrypted(IBlobReader* _pReader, u64 _VolumeOffset, m_pBuffer = new u8[0x8000]; } +bool CVolumeWiiCrypted::ChangePartition(u64 offset) +{ + m_VolumeOffset = offset; + m_LastDecryptedBlockOffset = -1; + + u8 volume_key[16]; + DiscIO::VolumeKeyForParition(*m_pReader, offset, volume_key); + aes_setkey_dec(m_AES_ctx.get(), volume_key, 128); + return true; +} + CVolumeWiiCrypted::~CVolumeWiiCrypted() { @@ -96,18 +109,32 @@ bool CVolumeWiiCrypted::GetTitleID(u8* _pBuffer) const // TitleID offset in tik is 0x1DC return RAWRead(m_VolumeOffset + 0x1DC, 8, _pBuffer); } -void CVolumeWiiCrypted::GetTMD(u8* _pBuffer, u32 * _sz) const -{ - *_sz = 0; - u32 tmdSz, - tmdAddr; - RAWRead(m_VolumeOffset + 0x2a4, sizeof(u32), (u8*)&tmdSz); - RAWRead(m_VolumeOffset + 0x2a8, sizeof(u32), (u8*)&tmdAddr); - tmdSz = Common::swap32(tmdSz); - tmdAddr = Common::swap32(tmdAddr) << 2; - RAWRead(m_VolumeOffset + tmdAddr, tmdSz, _pBuffer); - *_sz = tmdSz; +std::unique_ptr CVolumeWiiCrypted::GetTMD(u32 *size) const +{ + *size = 0; + u32 tmd_size; + u32 tmd_address; + + RAWRead(m_VolumeOffset + 0x2a4, sizeof(u32), (u8*)&tmd_size); + RAWRead(m_VolumeOffset + 0x2a8, sizeof(u32), (u8*)&tmd_address); + tmd_size = Common::swap32(tmd_size); + tmd_address = Common::swap32(tmd_address) << 2; + + if (tmd_size > 1024 * 1024 * 4) + { + // The size is checked so that a malicious or corrupt ISO + // can't force Dolphin to allocate up to 4 GiB of memory. + // 4 MiB should be much bigger than the size of TMDs and much smaller + // than the amount of RAM in a computer that can run Dolphin. + PanicAlert("TMD > 4 MiB"); + tmd_size = 1024 * 1024 * 4; + } + + std::unique_ptr buf{ new u8[tmd_size] }; + RAWRead(m_VolumeOffset + tmd_address, tmd_size, buf.get()); + *size = tmd_size; + return buf; } std::string CVolumeWiiCrypted::GetUniqueID() const diff --git a/Source/Core/DiscIO/VolumeWiiCrypted.h b/Source/Core/DiscIO/VolumeWiiCrypted.h index 3dd09a08f5..cd4fb5599d 100644 --- a/Source/Core/DiscIO/VolumeWiiCrypted.h +++ b/Source/Core/DiscIO/VolumeWiiCrypted.h @@ -27,7 +27,7 @@ public: bool Read(u64 _Offset, u64 _Length, u8* _pBuffer) const override; bool RAWRead(u64 _Offset, u64 _Length, u8* _pBuffer) const override; bool GetTitleID(u8* _pBuffer) const override; - void GetTMD(u8* _pBuffer, u32* _sz) const override; + virtual std::unique_ptr GetTMD(u32 *_sz) const override; std::string GetUniqueID() const override; std::string GetMakerID() const override; std::vector GetNames() const override; @@ -41,6 +41,8 @@ public: bool SupportsIntegrityCheck() const override { return true; } bool CheckIntegrity() const override; + bool ChangePartition(u64 offset) override; + private: std::unique_ptr m_pReader; std::unique_ptr m_AES_ctx; diff --git a/Source/Core/DolphinWX/ISOProperties.cpp b/Source/Core/DolphinWX/ISOProperties.cpp index 691c0a8e5e..85c101c0f0 100644 --- a/Source/Core/DolphinWX/ISOProperties.cpp +++ b/Source/Core/DolphinWX/ISOProperties.cpp @@ -130,20 +130,23 @@ CISOProperties::CISOProperties(const std::string fileName, wxWindow* parent, wxW bool IsWiiDisc = DiscIO::IsVolumeWiiDisc(OpenISO); if (IsWiiDisc) { - for (u32 i = 0; i < 0xFFFFFFFF; i++) // yes, technically there can be OVER NINE THOUSAND partitions... + for (int group = 0; group < 4; group++) { - WiiPartition temp; - if ((temp.Partition = DiscIO::CreateVolumeFromFilename(fileName, 0, i)) != nullptr) + for (u32 i = 0; i < 0xFFFFFFFF; i++) // yes, technically there can be OVER NINE THOUSAND partitions... { - if ((temp.FileSystem = DiscIO::CreateFileSystem(temp.Partition)) != nullptr) + WiiPartition temp; + if ((temp.Partition = DiscIO::CreateVolumeFromFilename(fileName, group, i)) != nullptr) { - temp.FileSystem->GetFileList(temp.Files); - WiiDisc.push_back(temp); + if ((temp.FileSystem = DiscIO::CreateFileSystem(temp.Partition)) != nullptr) + { + temp.FileSystem->GetFileList(temp.Files); + WiiDisc.push_back(temp); + } + } + else + { + break; } - } - else - { - break; } } }