From 967f14fc7e32e26e2b066fbeb8af99b6e69702ca Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 23 May 2019 03:10:32 -0700 Subject: [PATCH] Implement working prodinfo blanking. --- exosphere/src/configitem.c | 6 +- exosphere/src/configitem.h | 1 + exosphere/src/exocfg.c | 8 ++ exosphere/src/exocfg.h | 2 + fusee/fusee-secondary/src/exocfg.h | 1 + fusee/fusee-secondary/src/nxboot.c | 8 ++ .../ams_mitm/source/fs_mitm/fs_istorage.hpp | 3 +- .../source/fs_mitm/fs_memory_storage.hpp | 113 ++++++++++++++++++ .../source/fs_mitm/fsmitm_service.cpp | 16 +-- .../ams_mitm/source/fs_mitm/fsmitm_utils.hpp | 59 +++++++++ stratosphere/ams_mitm/source/utils.cpp | 96 +++++---------- stratosphere/ams_mitm/source/utils.hpp | 6 +- stratosphere/libstratosphere | 2 +- 13 files changed, 239 insertions(+), 82 deletions(-) create mode 100644 stratosphere/ams_mitm/source/fs_mitm/fs_memory_storage.hpp create mode 100644 stratosphere/ams_mitm/source/fs_mitm/fsmitm_utils.hpp diff --git a/exosphere/src/configitem.c b/exosphere/src/configitem.c index b53421d6f..233e2c772 100644 --- a/exosphere/src/configitem.c +++ b/exosphere/src/configitem.c @@ -282,7 +282,11 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue) break; case CONFIGITEM_HAS_RCM_BUG_PATCH: /* UNOFFICIAL: Gets whether this unit has the RCM bug patched. */ - *p_outvalue = (int)(fuse_has_rcm_bug_patch());; + *p_outvalue = (int)(fuse_has_rcm_bug_patch()); + break; + case CONFIGITEM_SHOULD_BLANK_PROD_INFO: + /* UNOFFICIAL: Gets whether we should blank out certain parts of PRODINFO. */ + *p_outvalue = (int)(exosphere_should_blank_prod_info() != 0); break; default: result = 2; diff --git a/exosphere/src/configitem.h b/exosphere/src/configitem.h index b9600c833..6000ac44e 100644 --- a/exosphere/src/configitem.h +++ b/exosphere/src/configitem.h @@ -45,6 +45,7 @@ typedef enum { CONFIGITEM_NEEDS_SHUTDOWN = 65002, CONFIGITEM_EXOSPHERE_VERHASH = 65003, CONFIGITEM_HAS_RCM_BUG_PATCH = 65004, + CONFIGITEM_SHOULD_BLANK_PROD_INFO = 65005, } ConfigItem; #define REBOOT_KIND_NO_REBOOT 0 diff --git a/exosphere/src/exocfg.c b/exosphere/src/exocfg.c index 533de8beb..9705098f8 100644 --- a/exosphere/src/exocfg.c +++ b/exosphere/src/exocfg.c @@ -83,3 +83,11 @@ unsigned int exosphere_should_disable_usermode_exception_handlers(void) { return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS); } + +unsigned int exosphere_should_blank_prod_info(void) { + if (!g_has_loaded_config) { + generic_panic(); + } + + return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_SHOULD_BLANK_PRODINFO); +} diff --git a/exosphere/src/exocfg.h b/exosphere/src/exocfg.h index ba53be403..b1dc2f927 100644 --- a/exosphere/src/exocfg.h +++ b/exosphere/src/exocfg.h @@ -39,6 +39,7 @@ #define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u) #define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u) #define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u) +#define EXOSPHERE_FLAG_SHOULD_BLANK_PRODINFO (1 << 4u) #define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV) typedef struct { @@ -54,6 +55,7 @@ unsigned int exosphere_should_perform_620_keygen(void); unsigned int exosphere_should_override_debugmode_priv(void); unsigned int exosphere_should_override_debugmode_user(void); unsigned int exosphere_should_disable_usermode_exception_handlers(void); +unsigned int exosphere_should_blank_prod_info(void); static inline unsigned int exosphere_get_target_firmware_for_init(void) { const unsigned int magic = MAILBOX_EXOSPHERE_CONFIG_PHYS.magic; diff --git a/fusee/fusee-secondary/src/exocfg.h b/fusee/fusee-secondary/src/exocfg.h index e8054ec9d..219509785 100644 --- a/fusee/fusee-secondary/src/exocfg.h +++ b/fusee/fusee-secondary/src/exocfg.h @@ -28,6 +28,7 @@ #define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u) #define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u) #define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u) +#define EXOSPHERE_FLAG_SHOULD_BLANK_PRODINFO (1 << 4u) #define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV) typedef struct { diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index f7badf580..bc161b4ba 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -199,6 +199,14 @@ static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int ke fatal_error("[NXBOOT]: Failed to parse BCT.ini!\n"); } + { + FILE *flag = fopen("atmosphere/flags/blank_prodinfo.flag", "rb"); + if (flag != NULL) { + exo_cfg.flags |= EXOSPHERE_FLAG_SHOULD_BLANK_PRODINFO; + fclose(flag); + } + } + if ((exo_cfg.target_firmware < ATMOSPHERE_TARGET_FIRMWARE_MIN) || (exo_cfg.target_firmware > ATMOSPHERE_TARGET_FIRMWARE_MAX)) { fatal_error("[NXBOOT]: Invalid Exosphere target firmware!\n"); } diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp index 34ace2eb9..655b356c8 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp @@ -112,7 +112,6 @@ class IROStorage : public IStorage { virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0; }; - class ProxyStorage : public IStorage { private: FsStorage *base_storage; @@ -172,4 +171,4 @@ class ROProxyStorage : public IROStorage { virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override { return fsStorageOperateRange(this->base_storage, operation_type, offset, size, out_range_info); }; -}; \ No newline at end of file +}; diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_memory_storage.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_memory_storage.hpp new file mode 100644 index 000000000..a195f6dd2 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_memory_storage.hpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include "fs_shim.h" + +#include "../debug.hpp" +#include "fs_istorage.hpp" + +class MemoryStorage : public IStorage { + private: + u8 * const buffer; + const u64 size; + public: + MemoryStorage(void *b, u64 sz) : buffer(static_cast(b)), size(sz) { + /* ... */ + } + + virtual ~MemoryStorage() { + /* ... */ + } + + public: + virtual Result Read(void *buffer, size_t size, u64 offset) override { + if (size == 0) { + return ResultSuccess; + } + if (buffer == nullptr) { + return ResultFsNullptrArgument; + } + if (!IStorage::IsRangeValid(offset, size, this->size)) { + return ResultFsOutOfRange; + } + + std::memcpy(buffer, this->buffer + offset, size); + return ResultSuccess; + } + + virtual Result Write(void *buffer, size_t size, u64 offset) override { + if (size == 0) { + return ResultSuccess; + } + if (buffer == nullptr) { + return ResultFsNullptrArgument; + } + if (!IStorage::IsRangeValid(offset, size, this->size)) { + return ResultFsOutOfRange; + } + + std::memcpy(this->buffer + offset, buffer, size); + return ResultSuccess; + } + + virtual Result Flush() override { + return ResultSuccess; + } + + virtual Result GetSize(u64 *out_size) override { + *out_size = this->size; + return ResultSuccess; + } + + virtual Result SetSize(u64 size) override { + return ResultFsUnsupportedOperation; + } + + virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override { + switch (operation_type) { + case 2: /* TODO: OperationType_Invalidate */ + return ResultSuccess; + case 3: /* TODO: OperationType_Query */ + if (out_range_info == nullptr) { + return ResultFsNullptrArgument; + } + /* N checks for size == sizeof(*out_range_info) here, but that's because their wrapper api is bad. */ + std::memset(out_range_info, 0, sizeof(*out_range_info)); + return ResultSuccess; + default: + return ResultFsUnsupportedOperation; + } + } +}; + +class ReadOnlyMemoryStorage : public MemoryStorage { + public: + ReadOnlyMemoryStorage(const void *b, u64 sz) : MemoryStorage(const_cast(b), sz) { + /* ... */ + } + + virtual ~ReadOnlyMemoryStorage() { + /* ... */ + } + + public: + virtual Result Write(void *buffer, size_t size, u64 offset) override { + return ResultFsUnsupportedOperation; + } +}; diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp index aa4f719b1..aaa40cbda 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp @@ -27,12 +27,14 @@ #include "fsmitm_boot0storage.hpp" #include "fsmitm_romstorage.hpp" #include "fsmitm_layeredrom.hpp" +#include "fsmitm_utils.hpp" #include "fs_dir_utils.hpp" #include "fs_save_utils.hpp" #include "fs_subdirectory_filesystem.hpp" #include "fs_directory_savedata_filesystem.hpp" #include "fs_file_storage.hpp" +#include "fs_memory_storage.hpp" #include "../debug.hpp" @@ -262,23 +264,13 @@ Result FsMitmService::OpenBisStorage(Out> out const bool is_sysmodule = TitleIdIsSystem(this->title_id); const bool has_bis_write_flag = Utils::HasFlag(this->title_id, "bis_write"); const bool has_cal0_read_flag = Utils::HasFlag(this->title_id, "cal_read"); - const bool has_blank_cal0_flag = Utils::HasGlobalFlag("blank_prodinfo"); + const bool has_blank_cal0_flag = ShouldBlankProdInfo(); if (bis_partition_id == BisStorageId_Boot0) { storage = std::make_shared(new Boot0Storage(bis_storage, this->title_id)); } else if (bis_partition_id == BisStorageId_Prodinfo) { /* PRODINFO should *never* be writable. */ if (has_blank_cal0_flag) { - FsFile file; - - if (R_FAILED((rc = Utils::OpenBlankProdInfoFile(&file)))) { - return rc; - } - - storage = std::make_shared(new FileStorage(new ProxyFile(&file))); - if (out_storage.IsDomain()) { - out_domain_id = file.s.object_id; - } - return rc; + storage = std::make_shared(new MitmProxyStorage(std::make_unique(Utils::GetBlankProdInfoBuffer(), ProdInfoSize), bis_storage.s)); } else if (is_sysmodule || has_cal0_read_flag) { storage = std::make_shared(new ROProxyStorage(bis_storage)); } else { diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_utils.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_utils.hpp new file mode 100644 index 000000000..abca5702a --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_utils.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +#include "fs_istorage.hpp" + +class MitmProxyStorage : public IStorage { + private: + Service srv_holder; + std::unique_ptr fwd_storage; + public: + MitmProxyStorage(std::unique_ptr st, Service sr = {}) : srv_holder(sr), fwd_storage(std::move(st)) { + /* ... */ + } + virtual ~MitmProxyStorage() { + if (serviceIsActive(&srv_holder)) { + serviceClose(&srv_holder); + } + } + public: + virtual Result Read(void *buffer, size_t size, u64 offset) override { + return this->fwd_storage->Read(buffer, size, offset); + } + + virtual Result Write(void *buffer, size_t size, u64 offset) override { + return this->fwd_storage->Write(buffer, size, offset); + } + + virtual Result Flush() override { + return this->fwd_storage->Flush(); + } + + virtual Result GetSize(u64 *out_size) override { + return this->fwd_storage->GetSize(out_size); + } + + virtual Result SetSize(u64 size) override { + return this->fwd_storage->SetSize(size); + } + + virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override { + return this->fwd_storage->OperateRange(operation_type, offset, size, out_range_info); + } +}; diff --git a/stratosphere/ams_mitm/source/utils.cpp b/stratosphere/ams_mitm/source/utils.cpp index 5a0f6f928..e19e4b260 100644 --- a/stratosphere/ams_mitm/source/utils.cpp +++ b/stratosphere/ams_mitm/source/utils.cpp @@ -60,13 +60,13 @@ static HblOverrideConfig g_hbl_override_config = { static char g_config_ini_data[0x800]; /* Backup file for CAL0 partition. */ -static constexpr size_t ProdInfoSize = 0x8000; -static constexpr const char *BlankProdInfoPath = "/atmosphere/automatic_backups/BLANK_PRODINFO.bin"; static FsFile g_cal0_file = {0}; static u8 g_cal0_storage_backup[ProdInfoSize]; static u8 g_cal0_backup[ProdInfoSize]; -static FsFile g_blank_prodinfo_file = {0}; +static HosMutex g_blank_cal0_lock; +static bool g_initialized_blank_cal0 = false; +static u8 g_blank_cal0[ProdInfoSize]; static bool IsHexadecimal(const char *str) { while (*str) { @@ -142,9 +142,6 @@ void Utils::InitializeThreadFunc(void *args) { fsFileFlush(&g_cal0_file); } - /* Use one of the storages to make a blank prodinfo file. */ - Utils::CreateBlankProdInfo(); - /* NOTE: g_cal0_file is intentionally not closed here. This prevents any other process from opening it. */ std::memset(g_cal0_storage_backup, 0, sizeof(g_cal0_storage_backup)); std::memset(g_cal0_backup, 0, sizeof(g_cal0_backup)); @@ -672,81 +669,60 @@ void Utils::RebootToFatalError(AtmosphereFatalErrorContext *ctx) { BpcRebootManager::RebootForFatalError(ctx); } -void Utils::CreateBlankProdInfo() { - Result rc; +void Utils::EnsureBlankProdInfo() { + std::scoped_lock lk(g_blank_cal0_lock); + if (g_initialized_blank_cal0) { + return; + } - u8 *cal0; - if (IsCal0Valid(g_cal0_backup)) { - cal0 = g_cal0_backup; - } else if (IsCal0Valid(g_cal0_storage_backup)) { - cal0 = g_cal0_storage_backup; - } else { + /* Read CAL0 in from NAND. */ + { + FsStorage cal0_storage; + if (R_FAILED(fsOpenBisStorage(&cal0_storage, BisStorageId_Prodinfo)) || R_FAILED(fsStorageRead(&cal0_storage, 0, g_blank_cal0, ProdInfoSize))) { + std::abort(); + } + fsStorageClose(&cal0_storage); + } + + if (!IsCal0Valid(g_blank_cal0)) { std::abort(); } const char blank_serial[] = "XAW00000000000"; - std::memcpy(&cal0[0x250], blank_serial, sizeof(blank_serial) - 1); + std::memcpy(&g_blank_cal0[0x250], blank_serial, sizeof(blank_serial) - 1); static constexpr size_t NumErase = 7; for (size_t i = 0; i < NumErase; i++) { static constexpr size_t s_erase_offsets[NumErase] = {0xAE0, 0x3AE0, 0x35E1, 0x36E1, 0x2B0, 0x3D70, 0x3FC0}; static constexpr size_t s_erase_sizes[NumErase] = {0x800, 0x130, 0x6, 0x6, 0x180, 0x240, 0x240}; - std::memset(&cal0[s_erase_offsets[i]], 0, s_erase_sizes[i]); + std::memset(&g_blank_cal0[s_erase_offsets[i]], 0, s_erase_sizes[i]); } static constexpr size_t NumHashes = 2; { static constexpr size_t s_hash_offsets[NumHashes] = {0x12E0, 0x20}; static constexpr size_t s_data_offsets[NumHashes] = {0xAE0, 0x40}; - const size_t data_sizes[NumHashes] = {*reinterpret_cast(&cal0[0xAD0]), *reinterpret_cast(&cal0[0x8])}; + const size_t data_sizes[NumHashes] = {*reinterpret_cast(&g_blank_cal0[0xAD0]), *reinterpret_cast(&g_blank_cal0[0x8])}; for (size_t i = 0; i < NumHashes; i++) { u8 hash[SHA256_HASH_SIZE]; - sha256CalculateHash(hash, &cal0[s_data_offsets[i]], data_sizes[i]); - std::memcpy(&cal0[s_hash_offsets[i]], hash, sizeof(hash)); + sha256CalculateHash(hash, &g_blank_cal0[s_data_offsets[i]], data_sizes[i]); + std::memcpy(&g_blank_cal0[s_hash_offsets[i]], hash, sizeof(hash)); } } - /* File creation is allowed to fail, because it may already exist. */ - fsFsCreateFile(&g_sd_filesystem, BlankProdInfoPath, ProdInfoSize, 0); - - FsFile f; - if (R_FAILED((rc = fsFsOpenFile(&g_sd_filesystem, BlankProdInfoPath, FS_OPEN_READ | FS_OPEN_WRITE, &f)))) { - std::abort(); - } - - if (R_FAILED((rc = fsFileSetSize(&f, ProdInfoSize)))) { - std::abort(); - } - - if (R_FAILED((rc = fsFileWrite(&f, 0, cal0, ProdInfoSize)))) { - std::abort(); - } - - if (R_FAILED((rc = fsFileFlush(&f)))) { - std::abort(); - } - - fsFileClose(&f); - - if (R_FAILED((rc = fsFsOpenFile(&g_sd_filesystem, BlankProdInfoPath, FS_OPEN_READ, &g_blank_prodinfo_file)))) { - std::abort(); - } + g_initialized_blank_cal0 = true; } bool Utils::IsCal0Valid(const u8 *cal0) { - char serial_number[0x40] = {0}; - std::memcpy(serial_number, cal0 + 0x250, 0x18); - bool is_cal0_valid = true; - is_cal0_valid &= std::memcmp(g_cal0_backup, "CAL0", 4) == 0; - is_cal0_valid &= std::memcmp(g_cal0_backup + 0x250, serial_number, 0x18) == 0; - u32 cal0_size = ((u32 *)g_cal0_backup)[2]; + is_cal0_valid &= std::memcmp(cal0, "CAL0", 4) == 0; + u32 cal0_size = ((u32 *)cal0)[2]; is_cal0_valid &= cal0_size + 0x40 <= ProdInfoSize; if (is_cal0_valid) { u8 calc_hash[0x20]; - sha256CalculateHash(calc_hash, g_cal0_backup + 0x40, cal0_size); - is_cal0_valid &= std::memcmp(calc_hash, g_cal0_backup + 0x20, sizeof(calc_hash)) == 0; + sha256CalculateHash(calc_hash, cal0 + 0x40, cal0_size); + is_cal0_valid &= std::memcmp(calc_hash, cal0 + 0x20, sizeof(calc_hash)) == 0; } return is_cal0_valid; @@ -771,15 +747,7 @@ u16 Utils::GetCrc16(const void *data, size_t size) { return crc16; } -Result Utils::OpenBlankProdInfoFile(FsFile *out) { - if (!IsSdInitialized()) { - std::abort(); - return ResultFsSdCardNotPresent; - } - - Result rc = OpenSdFile(BlankProdInfoPath, FS_OPEN_READ, out); - if (R_FAILED((rc))) { - std::abort(); - } - return rc; -} \ No newline at end of file +const void *Utils::GetBlankProdInfoBuffer() { + EnsureBlankProdInfo(); + return g_blank_cal0; +} diff --git a/stratosphere/ams_mitm/source/utils.hpp b/stratosphere/ams_mitm/source/utils.hpp index 51f8cfe84..b4a0c7a1c 100644 --- a/stratosphere/ams_mitm/source/utils.hpp +++ b/stratosphere/ams_mitm/source/utils.hpp @@ -37,6 +37,8 @@ enum BisStorageId : u32 { BisStorageId_SystemProperPartition = 33, }; +static constexpr size_t ProdInfoSize = 0x8000; + struct OverrideKey { u64 key_combination; bool override_by_default; @@ -61,7 +63,7 @@ class Utils { static bool HasSdRomfsContent(u64 title_id); - static Result OpenBlankProdInfoFile(FsFile *out); + static const void *GetBlankProdInfoBuffer(); /* Delayed Initialization + MitM detection. */ static void InitializeThreadFunc(void *args); @@ -98,6 +100,6 @@ class Utils { private: static void RefreshConfiguration(); - static void CreateBlankProdInfo(); + static void EnsureBlankProdInfo(); static bool IsCal0Valid(const u8 *cal0); }; \ No newline at end of file diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index 8ec43f0d6..5fe1dacee 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit 8ec43f0d69463784c00ddb19c5f3b043e713548e +Subproject commit 5fe1dacee28265af5ed8272a3c9921904d6f50fe