From ae4ba287d5e2b56accfaa490616572e44245a14a Mon Sep 17 00:00:00 2001 From: B3n30 Date: Sun, 15 Mar 2020 17:54:13 +0100 Subject: [PATCH 1/6] warn if cia contend is encrypted --- src/citra_qt/game_list_worker.cpp | 8 ++++-- src/core/file_sys/ncch_container.cpp | 33 ++++++++++++++++++++-- src/core/file_sys/ncch_container.h | 6 ++++ src/core/hle/service/am/am.cpp | 41 ++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 4 deletions(-) diff --git a/src/citra_qt/game_list_worker.cpp b/src/citra_qt/game_list_worker.cpp index a20ae4794..de02e79c0 100644 --- a/src/citra_qt/game_list_worker.cpp +++ b/src/citra_qt/game_list_worker.cpp @@ -47,12 +47,16 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign if (!is_dir && HasSupportedFileExtension(physical_name)) { std::unique_ptr loader = Loader::GetLoader(physical_name); if (!loader) + { return true; + } bool executable = false; - loader->IsExecutable(executable); - if (!executable) + auto res = loader->IsExecutable(executable); + if (!executable && res != Loader::ResultStatus::ErrorEncrypted) + { return true; + } u64 program_id = 0; loader->ReadProgramId(program_id); diff --git a/src/core/file_sys/ncch_container.cpp b/src/core/file_sys/ncch_container.cpp index 056f7a901..651b0d307 100644 --- a/src/core/file_sys/ncch_container.cpp +++ b/src/core/file_sys/ncch_container.cpp @@ -133,8 +133,37 @@ Loader::ResultStatus NCCHContainer::OpenFile(const std::string& filepath, u32 nc return Loader::ResultStatus::Success; } +Loader::ResultStatus NCCHContainer::LoadHeader() { + if (has_header) + return Loader::ResultStatus::Success; + if (!file.IsOpen()) { + + return Loader::ResultStatus::Error; + } + + // Reset read pointer in case this file has been read before. + file.Seek(ncch_offset, SEEK_SET); + + if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) + return Loader::ResultStatus::Error; + + // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... + if (Loader::MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) { + LOG_DEBUG(Service_FS, "Only loading the first (bootable) NCCH within the NCSD file!"); + ncch_offset += 0x4000; + file.Seek(ncch_offset, SEEK_SET); + file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); + } + + // Verify we are loading the correct file type... + if (Loader::MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) + return Loader::ResultStatus::ErrorInvalidFormat; + + has_header = true; + return Loader::ResultStatus::Success; +} + Loader::ResultStatus NCCHContainer::Load() { - LOG_INFO(Service_FS, "Loading NCCH from file {}", filepath); if (is_loaded) return Loader::ResultStatus::Success; @@ -697,7 +726,7 @@ Loader::ResultStatus NCCHContainer::ReadOverrideRomFS(std::shared_ptr #include "common/file_util.h" #include "common/logging/log.h" +#include "common/common_paths.h" #include "common/string_util.h" #include "core/core.h" #include "core/file_sys/errors.h" @@ -33,6 +34,16 @@ #include "core/loader/loader.h" #include "core/loader/smdh.h" +namespace { +bool HasSupportedFileExtension(std::string path) { + static const std::array extensions = {{".3ds", ".3dsx", ".elf", ".axf", + ".cci", ".cxi" ".app" + }}; + const auto file_ext = FileUtil::GetExtensionFromFilename(path); + return std::find(extensions.begin(), extensions.end(), file_ext) != extensions.end(); +} +} + namespace Service::AM { constexpr u16 PLATFORM_CTR = 0x0004; @@ -373,6 +384,36 @@ InstallStatus InstallCIA(const std::string& path, installFile.Close(); LOG_INFO(Service_AM, "Installed {} successfully.", path); + + const FileUtil::DirectoryEntryCallable callback = [&callback](u64* num_entries_out, + const std::string& directory, + const std::string& virtual_name) -> bool { + const std::string physical_name = directory + DIR_SEP + virtual_name; + const bool is_dir = FileUtil::IsDirectory(physical_name); + if (!is_dir && HasSupportedFileExtension(physical_name)) { + std::unique_ptr loader = Loader::GetLoader(physical_name); + if (!loader) + { + return true; + } + + bool executable = false; + auto res = loader->IsExecutable(executable); + if (res == Loader::ResultStatus::ErrorEncrypted) + { + return false; + } + return true; + } else { + return FileUtil::ForeachDirectoryEntry(nullptr, physical_name, callback); + } + + }; + if (!FileUtil::ForeachDirectoryEntry(nullptr, path, callback)) + { + LOG_ERROR(Service_AM, "CIA {} contained encrypted files.", path); + return InstallStatus::ErrorEncrypted; + } return InstallStatus::Success; } From 391580c658ad3060a80186f6562f4183c23ebe5b Mon Sep 17 00:00:00 2001 From: B3n30 Date: Sun, 15 Mar 2020 18:42:53 +0100 Subject: [PATCH 2/6] fix clang-format --- src/citra_qt/game_list_worker.cpp | 8 +++----- src/core/hle/service/am/am.cpp | 30 +++++++++++++----------------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/citra_qt/game_list_worker.cpp b/src/citra_qt/game_list_worker.cpp index de02e79c0..c00825b9e 100644 --- a/src/citra_qt/game_list_worker.cpp +++ b/src/citra_qt/game_list_worker.cpp @@ -46,15 +46,13 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign const bool is_dir = FileUtil::IsDirectory(physical_name); if (!is_dir && HasSupportedFileExtension(physical_name)) { std::unique_ptr loader = Loader::GetLoader(physical_name); - if (!loader) - { + if (!loader) { return true; } bool executable = false; - auto res = loader->IsExecutable(executable); - if (!executable && res != Loader::ResultStatus::ErrorEncrypted) - { + auto res = loader->IsExecutable(executable); + if (!executable && res != Loader::ResultStatus::ErrorEncrypted) { return true; } diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 79ae93130..4c1fdc33d 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -9,9 +9,9 @@ #include #include #include +#include "common/common_paths.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "common/common_paths.h" #include "common/string_util.h" #include "core/core.h" #include "core/file_sys/errors.h" @@ -36,13 +36,13 @@ namespace { bool HasSupportedFileExtension(std::string path) { - static const std::array extensions = {{".3ds", ".3dsx", ".elf", ".axf", - ".cci", ".cxi" ".app" - }}; + static const std::array extensions = {{".3ds", ".3dsx", ".elf", ".axf", ".cci", + ".cxi" + ".app"}}; const auto file_ext = FileUtil::GetExtensionFromFilename(path); return std::find(extensions.begin(), extensions.end(), file_ext) != extensions.end(); } -} +} // namespace namespace Service::AM { @@ -385,32 +385,28 @@ InstallStatus InstallCIA(const std::string& path, LOG_INFO(Service_AM, "Installed {} successfully.", path); - const FileUtil::DirectoryEntryCallable callback = [&callback](u64* num_entries_out, - const std::string& directory, - const std::string& virtual_name) -> bool { + const FileUtil::DirectoryEntryCallable callback = + [&callback](u64* num_entries_out, const std::string& directory, + const std::string& virtual_name) -> bool { const std::string physical_name = directory + DIR_SEP + virtual_name; const bool is_dir = FileUtil::IsDirectory(physical_name); if (!is_dir && HasSupportedFileExtension(physical_name)) { std::unique_ptr loader = Loader::GetLoader(physical_name); - if (!loader) - { - return true; + if (!loader) { + return true; } bool executable = false; - auto res = loader->IsExecutable(executable); - if (res == Loader::ResultStatus::ErrorEncrypted) - { + auto res = loader->IsExecutable(executable); + if (res == Loader::ResultStatus::ErrorEncrypted) { return false; } return true; } else { return FileUtil::ForeachDirectoryEntry(nullptr, physical_name, callback); } - }; - if (!FileUtil::ForeachDirectoryEntry(nullptr, path, callback)) - { + if (!FileUtil::ForeachDirectoryEntry(nullptr, path, callback)) { LOG_ERROR(Service_AM, "CIA {} contained encrypted files.", path); return InstallStatus::ErrorEncrypted; } From 2465fd423a405da85dd02235efa90d8f3b96be67 Mon Sep 17 00:00:00 2001 From: B3n30 Date: Sun, 15 Mar 2020 18:57:29 +0100 Subject: [PATCH 3/6] fix review comments --- src/core/file_sys/ncch_container.cpp | 1 - src/core/hle/service/am/am.cpp | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/file_sys/ncch_container.cpp b/src/core/file_sys/ncch_container.cpp index 651b0d307..bb972c431 100644 --- a/src/core/file_sys/ncch_container.cpp +++ b/src/core/file_sys/ncch_container.cpp @@ -137,7 +137,6 @@ Loader::ResultStatus NCCHContainer::LoadHeader() { if (has_header) return Loader::ResultStatus::Success; if (!file.IsOpen()) { - return Loader::ResultStatus::Error; } diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 4c1fdc33d..f3d7ea985 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -36,9 +36,8 @@ namespace { bool HasSupportedFileExtension(std::string path) { - static const std::array extensions = {{".3ds", ".3dsx", ".elf", ".axf", ".cci", - ".cxi" - ".app"}}; + static const std::array extensions = { + {".3ds", ".3dsx", ".elf", ".axf", ".cci", ".cxi", ".app"}}; const auto file_ext = FileUtil::GetExtensionFromFilename(path); return std::find(extensions.begin(), extensions.end(), file_ext) != extensions.end(); } From 1d92343344d1e5696631863f26fd4c98c9a0c92f Mon Sep 17 00:00:00 2001 From: B3n30 Date: Sun, 15 Mar 2020 19:00:26 +0100 Subject: [PATCH 4/6] fix more review comments --- src/citra_qt/game_list_worker.cpp | 2 +- src/core/file_sys/ncch_container.cpp | 9 ++++++--- src/core/hle/service/am/am.cpp | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/citra_qt/game_list_worker.cpp b/src/citra_qt/game_list_worker.cpp index c00825b9e..f03244637 100644 --- a/src/citra_qt/game_list_worker.cpp +++ b/src/citra_qt/game_list_worker.cpp @@ -51,7 +51,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign } bool executable = false; - auto res = loader->IsExecutable(executable); + const auto res = loader->IsExecutable(executable); if (!executable && res != Loader::ResultStatus::ErrorEncrypted) { return true; } diff --git a/src/core/file_sys/ncch_container.cpp b/src/core/file_sys/ncch_container.cpp index bb972c431..cb8a2d513 100644 --- a/src/core/file_sys/ncch_container.cpp +++ b/src/core/file_sys/ncch_container.cpp @@ -134,8 +134,9 @@ Loader::ResultStatus NCCHContainer::OpenFile(const std::string& filepath, u32 nc } Loader::ResultStatus NCCHContainer::LoadHeader() { - if (has_header) + if (has_header) { return Loader::ResultStatus::Success; + } if (!file.IsOpen()) { return Loader::ResultStatus::Error; } @@ -143,8 +144,9 @@ Loader::ResultStatus NCCHContainer::LoadHeader() { // Reset read pointer in case this file has been read before. file.Seek(ncch_offset, SEEK_SET); - if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) + if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) { return Loader::ResultStatus::Error; + } // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... if (Loader::MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) { @@ -155,8 +157,9 @@ Loader::ResultStatus NCCHContainer::LoadHeader() { } // Verify we are loading the correct file type... - if (Loader::MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) + if (Loader::MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) { return Loader::ResultStatus::ErrorInvalidFormat; + } has_header = true; return Loader::ResultStatus::Success; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index f3d7ea985..44bdfc8a2 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -396,7 +396,7 @@ InstallStatus InstallCIA(const std::string& path, } bool executable = false; - auto res = loader->IsExecutable(executable); + const auto res = loader->IsExecutable(executable); if (res == Loader::ResultStatus::ErrorEncrypted) { return false; } From a4457d871c1035975d45c59ade64ba7fbc27c447 Mon Sep 17 00:00:00 2001 From: B3n30 Date: Sun, 15 Mar 2020 20:58:43 +0100 Subject: [PATCH 5/6] Log if common key during Ticket::Load is missing --- src/core/file_sys/ticket.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/file_sys/ticket.cpp b/src/core/file_sys/ticket.cpp index 4a62b297b..2b6926adc 100644 --- a/src/core/file_sys/ticket.cpp +++ b/src/core/file_sys/ticket.cpp @@ -47,6 +47,7 @@ std::optional> Ticket::GetTitleKey() const { std::memcpy(ctr.data(), &ticket_body.title_id, sizeof(u64)); HW::AES::SelectCommonKeyIndex(ticket_body.common_key_index); if (!HW::AES::IsNormalKeyAvailable(HW::AES::KeySlotID::TicketCommonKey)) { + LOG_ERROR(Service_FS, "CommonKey {} missing", ticket_body.common_key_index); return {}; } auto key = HW::AES::GetNormalKey(HW::AES::KeySlotID::TicketCommonKey); From ed51f0609773687007b66c08dd0dc598dc3db1c6 Mon Sep 17 00:00:00 2001 From: B3n30 Date: Tue, 17 Mar 2020 13:40:58 +0100 Subject: [PATCH 6/6] fix path usage in InstallCia --- src/core/hle/service/am/am.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 44bdfc8a2..31df828e3 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -34,15 +34,6 @@ #include "core/loader/loader.h" #include "core/loader/smdh.h" -namespace { -bool HasSupportedFileExtension(std::string path) { - static const std::array extensions = { - {".3ds", ".3dsx", ".elf", ".axf", ".cci", ".cxi", ".app"}}; - const auto file_ext = FileUtil::GetExtensionFromFilename(path); - return std::find(extensions.begin(), extensions.end(), file_ext) != extensions.end(); -} -} // namespace - namespace Service::AM { constexpr u16 PLATFORM_CTR = 0x0004; @@ -389,7 +380,7 @@ InstallStatus InstallCIA(const std::string& path, const std::string& virtual_name) -> bool { const std::string physical_name = directory + DIR_SEP + virtual_name; const bool is_dir = FileUtil::IsDirectory(physical_name); - if (!is_dir && HasSupportedFileExtension(physical_name)) { + if (!is_dir) { std::unique_ptr loader = Loader::GetLoader(physical_name); if (!loader) { return true; @@ -405,7 +396,12 @@ InstallStatus InstallCIA(const std::string& path, return FileUtil::ForeachDirectoryEntry(nullptr, physical_name, callback); } }; - if (!FileUtil::ForeachDirectoryEntry(nullptr, path, callback)) { + if (!FileUtil::ForeachDirectoryEntry( + nullptr, + GetTitlePath( + Service::AM::GetTitleMediaType(container.GetTitleMetadata().GetTitleID()), + container.GetTitleMetadata().GetTitleID()), + callback)) { LOG_ERROR(Service_AM, "CIA {} contained encrypted files.", path); return InstallStatus::ErrorEncrypted; }