diff --git a/Makefile b/Makefile index 18410a3af..6bcecf67f 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,8 @@ dist: all cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036/exefs.nsp cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034/exefs.nsp cp stratosphere/set_mitm/set_mitm.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/exefs.nsp - touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/boot2.flag + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags + touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags/boot2.flag cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../; rm -r atmosphere-$(AMSVER) mkdir out diff --git a/stratosphere/fs_mitm/source/fs_istorage.hpp b/stratosphere/fs_mitm/source/fs_istorage.hpp index 2f21b6b7e..82a75619e 100644 --- a/stratosphere/fs_mitm/source/fs_istorage.hpp +++ b/stratosphere/fs_mitm/source/fs_istorage.hpp @@ -61,7 +61,6 @@ class IStorageInterface : public IServiceObject { }; virtual Result Write(InBuffer buffer, u64 offset, u64 size) final { return this->base_storage->Write(buffer.buffer, std::min(buffer.num_elements, size), offset); - }; virtual Result Flush() final { return this->base_storage->Flush(); @@ -108,3 +107,65 @@ class IROStorage : public IStorage { virtual Result GetSize(u64 *out_size) = 0; virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0; }; + + +class ProxyStorage : public IStorage { + private: + FsStorage *base_storage; + public: + ProxyStorage(FsStorage *s) : base_storage(s) { + /* ... */ + }; + ProxyStorage(FsStorage s) { + this->base_storage = new FsStorage(s); + }; + virtual ~ProxyStorage() { + fsStorageClose(base_storage); + delete base_storage; + }; + public: + virtual Result Read(void *buffer, size_t size, u64 offset) override { + return fsStorageRead(this->base_storage, offset, buffer, size); + }; + virtual Result Write(void *buffer, size_t size, u64 offset) override { + return fsStorageWrite(this->base_storage, offset, buffer, size); + }; + virtual Result Flush() override { + return fsStorageFlush(this->base_storage); + }; + virtual Result GetSize(u64 *out_size) override { + return fsStorageGetSize(this->base_storage, out_size); + }; + virtual Result SetSize(u64 size) override { + return fsStorageSetSize(this->base_storage, size); + }; + 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); + }; +}; + +class ROProxyStorage : public IROStorage { + private: + FsStorage *base_storage; + public: + ROProxyStorage(FsStorage *s) : base_storage(s) { + /* ... */ + }; + ROProxyStorage(FsStorage s) { + this->base_storage = new FsStorage(s); + }; + virtual ~ROProxyStorage() { + fsStorageClose(base_storage); + delete base_storage; + }; + public: + virtual Result Read(void *buffer, size_t size, u64 offset) override { + return fsStorageRead(this->base_storage, offset, buffer, size); + }; + virtual Result GetSize(u64 *out_size) override { + return fsStorageGetSize(this->base_storage, out_size); + }; + 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/fs_mitm/source/fs_shim.c b/stratosphere/fs_mitm/source/fs_shim.c index 134d83498..915366bb8 100644 --- a/stratosphere/fs_mitm/source/fs_shim.c +++ b/stratosphere/fs_mitm/source/fs_shim.c @@ -18,6 +18,44 @@ #include "fs_shim.h" /* Missing fsp-srv commands. */ +Result fsOpenBisStorageFwd(Service* s, FsStorage* out, u32 PartitionId) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 PartitionId; + } *raw; + + raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 12; + raw->PartitionId = PartitionId; + + Result rc = serviceIpcDispatch(s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreateSubservice(&out->s, s, &r, 0); + } + } + + return rc; +} + Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) { IpcCommand c; ipcInitialize(&c); diff --git a/stratosphere/fs_mitm/source/fs_shim.h b/stratosphere/fs_mitm/source/fs_shim.h index d73c2595b..730a2bfb5 100644 --- a/stratosphere/fs_mitm/source/fs_shim.h +++ b/stratosphere/fs_mitm/source/fs_shim.h @@ -17,6 +17,7 @@ typedef struct { } FsRangeInfo; /* Missing fsp-srv commands. */ +Result fsOpenBisStorageFwd(Service* s, FsStorage* out, u32 PartitionId); Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out); Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out); diff --git a/stratosphere/fs_mitm/source/fsmitm_boot0storage.cpp b/stratosphere/fs_mitm/source/fsmitm_boot0storage.cpp new file mode 100644 index 000000000..1554496bd --- /dev/null +++ b/stratosphere/fs_mitm/source/fsmitm_boot0storage.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018 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 . + */ + +#include +#include +#include + +#include "fsmitm_boot0storage.hpp" + +static HosMutex g_boot0_mutex; +static u8 g_boot0_bct_buffer[Boot0Storage::BctEndOffset]; + +bool Boot0Storage::CanModifyBctPubks() { + return this->title_id != 0x010000000000001FULL; +} + +Result Boot0Storage::Read(void *_buffer, size_t size, u64 offset) { + std::scoped_lock lk{g_boot0_mutex}; + + return Base::Read(_buffer, size, offset); +} + +Result Boot0Storage::Write(void *_buffer, size_t size, u64 offset) { + std::scoped_lock lk{g_boot0_mutex}; + + Result rc = 0; + u8 *buffer = static_cast(_buffer); + + /* Protect the keyblob region from writes. */ + if (offset <= EksStart) { + if (offset + size < EksStart) { + /* Fall through, no need to do anything here. */ + } else { + if (offset + size < EksEnd) { + /* Adjust size to avoid writing end of data. */ + size = EksStart - offset; + } else { + /* Perform portion of write falling past end of keyblobs. */ + const u64 diff = EksEnd - offset; + if (R_FAILED((rc = Base::Write(buffer + diff, size - diff, EksEnd)))) { + return rc; + } + /* Adjust size to avoid writing end of data. */ + size = EksStart - offset; + } + } + } else { + if (offset < EksEnd) { + if (offset + size < EksEnd) { + /* Ignore writes falling strictly within the region. */ + return 0; + } else { + /* Only write past the end of the keyblob region. */ + buffer = buffer + (EksEnd - offset); + size -= (EksEnd - offset); + offset = EksEnd; + } + } else { + /* Fall through, no need to do anything here. */ + } + } + + if (size == 0) { + return 0; + } + + /* We care about protecting autorcm from NS. */ + if (CanModifyBctPubks() || offset >= BctEndOffset || (offset + BctSize >= BctEndOffset && offset % BctSize >= BctPubkEnd)) { + return Base::Write(buffer, size, offset); + } + + /* First, let's deal with the data past the end. */ + if (offset + size >= BctEndOffset) { + const u64 diff = BctEndOffset - offset; + if (R_FAILED((rc = ProxyStorage::Write(buffer + diff, size - diff, BctEndOffset)))) { + return rc; + } + size -= diff; + } + + /* Read in the current BCT region. */ + if (R_FAILED((rc = ProxyStorage::Read(g_boot0_bct_buffer, BctEndOffset, 0)))) { + return rc; + } + + /* Update the bct buffer. */ + for (u64 cur_ofs = offset; cur_ofs < BctEndOffset && cur_ofs < offset + size; cur_ofs++) { + const u64 cur_bct_rel_ofs = cur_ofs % BctSize; + if (cur_bct_rel_ofs < BctPubkStart || BctPubkEnd <= cur_bct_rel_ofs) { + g_boot0_bct_buffer[cur_ofs] = buffer[cur_ofs - offset]; + } + } + + return ProxyStorage::Write(g_boot0_bct_buffer, BctEndOffset, 0); +} diff --git a/stratosphere/fs_mitm/source/fsmitm_boot0storage.hpp b/stratosphere/fs_mitm/source/fsmitm_boot0storage.hpp new file mode 100644 index 000000000..b86791ff8 --- /dev/null +++ b/stratosphere/fs_mitm/source/fsmitm_boot0storage.hpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2018 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 + +#include "fs_istorage.hpp" + +/* Represents a sectored storage. */ +template +class SectoredProxyStorage : public ProxyStorage { + private: + u64 cur_seek = 0; + u64 cur_sector = 0; + u64 cur_sector_ofs = 0; + u8 sector_buf[SectorSize]; + private: + void Seek(u64 offset) { + this->cur_sector_ofs = offset % SectorSize; + this->cur_seek = offset - this->cur_sector_ofs; + } + public: + SectoredProxyStorage(FsStorage *s) : ProxyStorage(s) { } + SectoredProxyStorage(FsStorage s) : ProxyStorage(s) { } + public: + virtual Result Read(void *_buffer, size_t size, u64 offset) override { + Result rc = 0; + u8 *buffer = static_cast(_buffer); + this->Seek(offset); + + if (this->cur_sector_ofs == 0 && size % SectorSize == 0) { + /* Fast case. */ + return ProxyStorage::Read(buffer, size, offset); + } + + if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, this->cur_seek)))) { + return rc; + } + + if (size + this->cur_sector_ofs <= SectorSize) { + memcpy(buffer, sector_buf + this->cur_sector_ofs, size); + } else { + /* Leaving the sector... */ + size_t ofs = SectorSize - this->cur_sector_ofs; + memcpy(buffer, sector_buf + this->cur_sector_ofs, ofs); + size -= ofs; + + /* We're guaranteed alignment, here. */ + const size_t aligned_remaining_size = size - (size % SectorSize); + if (aligned_remaining_size) { + if (R_FAILED((rc = ProxyStorage::Read(buffer + ofs, aligned_remaining_size, offset + ofs)))) { + return rc; + } + ofs += aligned_remaining_size; + size -= aligned_remaining_size; + } + + /* Read any leftover data. */ + if (size) { + if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, offset + ofs)))) { + return rc; + } + memcpy(buffer + ofs, sector_buf, size); + } + } + + return rc; + }; + virtual Result Write(void *_buffer, size_t size, u64 offset) override { + Result rc = 0; + u8 *buffer = static_cast(_buffer); + this->Seek(offset); + + if (this->cur_sector_ofs == 0 && size % SectorSize == 0) { + /* Fast case. */ + return ProxyStorage::Write(buffer, size, offset); + } + + if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, this->cur_seek)))) { + return rc; + } + + + if (size + this->cur_sector_ofs <= SectorSize) { + memcpy(this->sector_buf + this->cur_sector_ofs, buffer, size); + rc = ProxyStorage::Write(this->sector_buf, SectorSize, this->cur_seek); + } else { + /* Leaving the sector... */ + size_t ofs = SectorSize - this->cur_sector_ofs; + memcpy(this->sector_buf + this->cur_sector_ofs, buffer, ofs); + if (R_FAILED((rc = ProxyStorage::Write(this->sector_buf, ofs, this->cur_seek)))) { + return rc; + } + size -= ofs; + + /* We're guaranteed alignment, here. */ + const size_t aligned_remaining_size = size - (size % SectorSize); + if (aligned_remaining_size) { + if (R_FAILED((rc = ProxyStorage::Write(buffer + ofs, aligned_remaining_size, offset + ofs)))) { + return rc; + } + ofs += aligned_remaining_size; + size -= aligned_remaining_size; + } + + /* Write any leftover data. */ + if (size) { + if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, offset + ofs)))) { + return rc; + } + memcpy(this->sector_buf, buffer + ofs, size); + rc = ProxyStorage::Write(this->sector_buf, SectorSize, this->cur_seek); + } + } + + return rc; + }; +}; + +/* Represents an RCM-preserving BOOT0 partition. */ +class Boot0Storage : public SectoredProxyStorage<0x200> { + using Base = SectoredProxyStorage<0x200>; + + public: + static constexpr u64 BctEndOffset = 0xFC000; + static constexpr u64 BctSize = 0x4000; + static constexpr u64 BctPubkStart = 0x210; + static constexpr u64 BctPubkSize = 0x100; + static constexpr u64 BctPubkEnd = BctPubkStart + BctPubkSize; + + static constexpr u64 EksStart = 0x180000; + static constexpr u64 EksSize = 0x4000; + static constexpr u64 EksEnd = EksStart + EksSize; + private: + u64 title_id; + private: + bool CanModifyBctPubks(); + public: + Boot0Storage(FsStorage *s, u64 t) : Base(s), title_id(t) { } + Boot0Storage(FsStorage s, u64 t) : Base(s), title_id(t) { } + public: + virtual Result Read(void *_buffer, size_t size, u64 offset) override; + virtual Result Write(void *_buffer, size_t size, u64 offset) override; +}; \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/fsmitm_main.cpp b/stratosphere/fs_mitm/source/fsmitm_main.cpp index 95f627d7a..9d477261d 100644 --- a/stratosphere/fs_mitm/source/fsmitm_main.cpp +++ b/stratosphere/fs_mitm/source/fsmitm_main.cpp @@ -78,7 +78,7 @@ void __appExit(void) { struct FsMitmManagerOptions { static const size_t PointerBufferSize = 0x800; - static const size_t MaxDomains = 0x10; + static const size_t MaxDomains = 0x40; static const size_t MaxDomainObjects = 0x4000; }; using FsMitmManager = WaitableManager; @@ -88,7 +88,7 @@ int main(int argc, char **argv) Thread sd_initializer_thread = {0}; Thread hid_initializer_thread = {0}; consoleDebugInit(debugDevice_SVC); - + if (R_FAILED(threadCreate(&sd_initializer_thread, &Utils::InitializeSdThreadFunc, NULL, 0x4000, 0x15, 0))) { /* TODO: Panic. */ } diff --git a/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp b/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp index c421a2147..1a186ec4c 100644 --- a/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp +++ b/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp @@ -31,7 +31,7 @@ class RomFileStorage : public IROStorage { RomFileStorage(FsFile f) { this->base_file = new FsFile(f); }; - ~RomFileStorage() { + virtual ~RomFileStorage() { fsFileClose(base_file); delete base_file; }; @@ -54,30 +54,4 @@ class RomFileStorage : public IROStorage { }; /* Represents a RomFS accessed via some IStorage. */ -class RomInterfaceStorage : public IROStorage { - private: - FsStorage *base_storage; - public: - RomInterfaceStorage(FsStorage *s) : base_storage(s) { - /* ... */ - }; - RomInterfaceStorage(FsStorage s) { - this->base_storage = new FsStorage(s); - }; - ~RomInterfaceStorage() { - fsStorageClose(base_storage); - delete base_storage; - }; - public: - Result Read(void *buffer, size_t size, u64 offset) override { - return fsStorageRead(this->base_storage, offset, buffer, size); - }; - Result GetSize(u64 *out_size) override { - /* TODO: Merge into libnx? */ - return fsStorageGetSize(this->base_storage, out_size); - }; - Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override { - /* TODO: Merge into libnx? */ - return fsStorageOperateRange(this->base_storage, operation_type, offset, size, out_range_info); - }; -}; +using RomInterfaceStorage = ROProxyStorage; diff --git a/stratosphere/fs_mitm/source/fsmitm_service.cpp b/stratosphere/fs_mitm/source/fsmitm_service.cpp index 91364ae19..0b1e1cf18 100644 --- a/stratosphere/fs_mitm/source/fsmitm_service.cpp +++ b/stratosphere/fs_mitm/source/fsmitm_service.cpp @@ -24,6 +24,7 @@ #include "fs_shim.h" #include "fsmitm_utils.hpp" +#include "fsmitm_boot0storage.hpp" #include "fsmitm_romstorage.hpp" #include "fsmitm_layeredrom.hpp" @@ -79,12 +80,67 @@ void FsMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx } } +/* Gate access to the BIS partitions. */ +Result FsMitmService::OpenBisStorage(Out> out_storage, u32 bis_partition_id) { + std::shared_ptr storage = nullptr; + u32 out_domain_id = 0; + Result rc = 0; + + ON_SCOPE_EXIT { + if (R_SUCCEEDED(rc)) { + out_storage.SetValue(std::move(storage)); + if (out_storage.IsDomain()) { + out_storage.ChangeObjectId(out_domain_id); + } + } + }; + + { + FsStorage bis_storage; + rc = fsOpenBisStorageFwd(this->forward_service.get(), &bis_storage, bis_partition_id); + if (R_SUCCEEDED(rc)) { + const bool is_sysmodule = this->title_id < 0x0100000000001000; + 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"); + 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 (is_sysmodule || has_cal0_read_flag) { + storage = std::make_shared(new ROProxyStorage(bis_storage)); + } else { + /* Do not allow non-sysmodules to read *or* write CAL0. */ + fsStorageClose(&bis_storage); + return 0x320002; + } + } else { + if (is_sysmodule || has_bis_write_flag) { + /* Sysmodules should still be allowed to read and write. */ + storage = std::make_shared(new ProxyStorage(bis_storage)); + } else { + /* Non-sysmodules should be allowed to read. */ + storage = std::make_shared(new ROProxyStorage(bis_storage)); + } + } + if (out_storage.IsDomain()) { + out_domain_id = bis_storage.s.object_id; + } + } + } + + return rc; +} + /* Add redirection for RomFS to the SD card. */ Result FsMitmService::OpenDataStorageByCurrentProcess(Out> out_storage) { std::shared_ptr storage = nullptr; u32 out_domain_id = 0; Result rc = 0; + if (!this->should_override_contents) { + return RESULT_FORWARD_TO_SESSION; + } + bool has_cache = StorageCacheGetEntry(this->title_id, &storage); ON_SCOPE_EXIT { @@ -148,6 +204,10 @@ Result FsMitmService::OpenDataStorageByDataId(Outshould_override_contents) { + return RESULT_FORWARD_TO_SESSION; + } std::shared_ptr storage = nullptr; u32 out_domain_id = 0; diff --git a/stratosphere/fs_mitm/source/fsmitm_service.hpp b/stratosphere/fs_mitm/source/fsmitm_service.hpp index 2fffacfa9..e7d2ad571 100644 --- a/stratosphere/fs_mitm/source/fsmitm_service.hpp +++ b/stratosphere/fs_mitm/source/fsmitm_service.hpp @@ -22,33 +22,51 @@ enum FspSrvCmd : u32 { FspSrvCmd_SetCurrentProcess = 1, + FspSrvCmd_OpenBisStorage = 12, FspSrvCmd_OpenDataStorageByCurrentProcess = 200, FspSrvCmd_OpenDataStorageByDataId = 202, }; -class FsMitmService : public IMitmServiceObject { +class FsMitmService : public IMitmServiceObject { private: bool has_initialized = false; + bool should_override_contents; public: - FsMitmService(std::shared_ptr s) : IMitmServiceObject(s) { - /* ... */ + FsMitmService(std::shared_ptr s, u64 pid) : IMitmServiceObject(s, pid) { + if (Utils::HasSdDisableMitMFlag(this->title_id)) { + this->should_override_contents = false; + } else { + this->should_override_contents = (this->title_id >= 0x0100000000010000ULL || Utils::HasSdMitMFlag(this->title_id)) && Utils::HasOverrideButton(this->title_id); + } } static bool ShouldMitm(u64 pid, u64 tid) { - if (Utils::HasSdDisableMitMFlag(tid)) { + /* Don't Mitm KIPs */ + if (pid < 0x50) { return false; } - return (tid >= 0x0100000000010000ULL || Utils::HasSdMitMFlag(tid)) && Utils::HasOverrideButton(tid); + + static std::atomic_bool has_launched_qlaunch = false; + + /* TODO: intercepting everything seems to cause issues with sleep mode, for some reason. */ + /* Figure out why, and address it. */ + if (tid == 0x0100000000001000ULL) { + has_launched_qlaunch = true; + } + + return has_launched_qlaunch || tid == 0x010000000000001FULL || tid >= 0x0100000000010000ULL || Utils::HasSdMitMFlag(tid); } static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx); protected: /* Overridden commands. */ + Result OpenBisStorage(Out> out, u32 bis_partition_id); Result OpenDataStorageByCurrentProcess(Out> out); Result OpenDataStorageByDataId(Out> out, u64 data_id, u8 storage_id); public: DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), MakeServiceCommandMeta(), MakeServiceCommandMeta(), }; diff --git a/stratosphere/fs_mitm/source/fsmitm_utils.cpp b/stratosphere/fs_mitm/source/fsmitm_utils.cpp index 32e37fad6..c1683eda9 100644 --- a/stratosphere/fs_mitm/source/fsmitm_utils.cpp +++ b/stratosphere/fs_mitm/source/fsmitm_utils.cpp @@ -23,6 +23,7 @@ #include "debug.hpp" #include "fsmitm_utils.hpp" #include "ini.h" +#include "sha256.h" static FsFileSystem g_sd_filesystem = {0}; static std::vector g_mitm_flagged_tids; @@ -31,11 +32,18 @@ static std::atomic_bool g_has_initialized = false; static std::atomic_bool g_has_hid_session = false; static u64 g_override_key_combination = KEY_R; +static u64 g_override_hbl_tid = 0x010000000000100DULL; static bool g_override_by_default = true; /* Static buffer for loader.ini contents at runtime. */ static char g_config_ini_data[0x800]; +/* Backup file for CAL0 partition. */ +static constexpr size_t ProdinfoSize = 0x8000; +static FsFile g_cal0_file = {0}; +static u8 g_cal0_storage_backup[ProdinfoSize]; +static u8 g_cal0_backup[ProdinfoSize]; + static bool IsHexadecimal(const char *str) { while (*str) { if (isxdigit(*str)) { @@ -64,6 +72,60 @@ void Utils::InitializeSdThreadFunc(void *args) { svcSleepThread(1000ULL); } + /* Back up CAL0, if it's not backed up already. */ + fsFsCreateDirectory(&g_sd_filesystem, "/atmosphere/automatic_backups"); + { + FsStorage cal0_storage; + if (R_FAILED(fsOpenBisStorage(&cal0_storage, BisStorageId_Prodinfo)) || R_FAILED(fsStorageRead(&cal0_storage, 0, g_cal0_storage_backup, ProdinfoSize))) { + std::abort(); + } + fsStorageClose(&cal0_storage); + + char serial_number[0x40] = {0}; + memcpy(serial_number, g_cal0_storage_backup + 0x250, 0x18); + + + char prodinfo_backup_path[FS_MAX_PATH] = {0}; + if (strlen(serial_number) > 0) { + snprintf(prodinfo_backup_path, sizeof(prodinfo_backup_path) - 1, "/atmosphere/automatic_backups/%s_PRODINFO.bin", serial_number); + } else { + snprintf(prodinfo_backup_path, sizeof(prodinfo_backup_path) - 1, "/atmosphere/automatic_backups/PRODINFO.bin"); + } + + fsFsCreateFile(&g_sd_filesystem, prodinfo_backup_path, ProdinfoSize, 0); + if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, prodinfo_backup_path, FS_OPEN_READ | FS_OPEN_WRITE, &g_cal0_file))) { + bool has_auto_backup = false; + size_t read = 0; + if (R_SUCCEEDED(fsFileRead(&g_cal0_file, 0, g_cal0_backup, sizeof(g_cal0_backup), &read)) && read == sizeof(g_cal0_backup)) { + bool is_cal0_valid = true; + is_cal0_valid &= memcmp(g_cal0_backup, "CAL0", 4) == 0; + is_cal0_valid &= memcmp(g_cal0_backup + 0x250, serial_number, 0x18) == 0; + u32 cal0_size = ((u32 *)g_cal0_backup)[2]; + is_cal0_valid &= cal0_size + 0x40 <= ProdinfoSize; + if (is_cal0_valid) { + struct sha256_state sha_ctx; + u8 calc_hash[0x20]; + sha256_init(&sha_ctx); + sha256_update(&sha_ctx, g_cal0_backup + 0x40, cal0_size); + sha256_finalize(&sha_ctx); + sha256_finish(&sha_ctx, calc_hash); + is_cal0_valid &= memcmp(calc_hash, g_cal0_backup + 0x20, sizeof(calc_hash)) == 0; + } + has_auto_backup = is_cal0_valid; + } + + if (!has_auto_backup) { + fsFileSetSize(&g_cal0_file, ProdinfoSize); + fsFileWrite(&g_cal0_file, 0, g_cal0_backup, ProdinfoSize); + fsFileFlush(&g_cal0_file); + } + + /* NOTE: g_cal0_file is intentionally not closed here. This prevents any other process from opening it. */ + memset(g_cal0_storage_backup, 0, sizeof(g_cal0_storage_backup)); + memset(g_cal0_backup, 0, sizeof(g_cal0_backup)); + } + } + /* Check for MitM flags. */ FsDir titles_dir; if (R_SUCCEEDED(fsFsOpenDirectory(&g_sd_filesystem, "/atmosphere/titles", FS_DIROPEN_DIRECTORY, &titles_dir))) { @@ -76,19 +138,39 @@ void Utils::InitializeSdThreadFunc(void *args) { char title_path[FS_MAX_PATH] = {0}; strcpy(title_path, "/atmosphere/titles/"); strcat(title_path, dir_entry.name); - strcat(title_path, "/fsmitm.flag"); + strcat(title_path, "/flags/fsmitm.flag"); if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) { g_mitm_flagged_tids.push_back(title_id); fsFileClose(&f); + } else { + /* TODO: Deprecate. */ + memset(title_path, 0, sizeof(title_path)); + strcpy(title_path, "/atmosphere/titles/"); + strcat(title_path, dir_entry.name); + strcat(title_path, "/fsmitm.flag"); + if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) { + g_mitm_flagged_tids.push_back(title_id); + fsFileClose(&f); + } } memset(title_path, 0, sizeof(title_path)); strcpy(title_path, "/atmosphere/titles/"); strcat(title_path, dir_entry.name); - strcat(title_path, "/fsmitm_disable.flag"); + strcat(title_path, "/flags/fsmitm_disable.flag"); if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) { g_disable_mitm_flagged_tids.push_back(title_id); fsFileClose(&f); + } else { + /* TODO: Deprecate. */ + memset(title_path, 0, sizeof(title_path)); + strcpy(title_path, "/atmosphere/titles/"); + strcat(title_path, dir_entry.name); + strcat(title_path, "/fsmitm_disable.flag"); + if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) { + g_disable_mitm_flagged_tids.push_back(title_id); + fsFileClose(&f); + } } } } @@ -264,7 +346,56 @@ Result Utils::SaveSdFileForAtmosphere(u64 title_id, const char *fn, void *data, return rc; } +bool Utils::HasTitleFlag(u64 tid, const char *flag) { + if (IsSdInitialized()) { + FsFile f; + char flag_path[FS_MAX_PATH]; + + memset(flag_path, 0, sizeof(flag_path)); + snprintf(flag_path, sizeof(flag_path) - 1, "flags/%s.flag", flag); + if (OpenSdFileForAtmosphere(tid, flag_path, FS_OPEN_READ, &f)) { + fsFileClose(&f); + return true; + } + + /* TODO: Deprecate. */ + snprintf(flag_path, sizeof(flag_path) - 1, "%s.flag", flag); + if (OpenSdFileForAtmosphere(tid, flag_path, FS_OPEN_READ, &f)) { + fsFileClose(&f); + return true; + } + } + return false; +} + +bool Utils::HasGlobalFlag(const char *flag) { + if (IsSdInitialized()) { + FsFile f; + char flag_path[FS_MAX_PATH] = {0}; + snprintf(flag_path, sizeof(flag_path), "/atmosphere/flags/%s.flag", flag); + if (fsFsOpenFile(&g_sd_filesystem, flag_path, FS_OPEN_READ, &f)) { + fsFileClose(&f); + return true; + } + } + return false; +} + +bool Utils::HasHblFlag(const char *flag) { + char hbl_flag[FS_MAX_PATH] = {0}; + snprintf(hbl_flag, sizeof(hbl_flag), "hbl_%s", flag); + return HasGlobalFlag(hbl_flag); +} + +bool Utils::HasFlag(u64 tid, const char *flag) { + return HasTitleFlag(tid, flag) || (tid == g_override_hbl_tid && HasHblFlag(flag)); +} + bool Utils::HasSdMitMFlag(u64 tid) { + if (tid == g_override_hbl_tid) { + return true; + } + if (IsSdInitialized()) { return std::find(g_mitm_flagged_tids.begin(), g_mitm_flagged_tids.end(), tid) != g_mitm_flagged_tids.end(); } @@ -306,7 +437,12 @@ bool Utils::HasOverrideButton(u64 tid) { static int FsMitMIniHandler(void *user, const char *section, const char *name, const char *value) { /* Taken and modified, with love, from Rajkosto's implementation. */ if (strcasecmp(section, "config") == 0) { - if (strcasecmp(name, "override_key") == 0) { + if (strcasecmp(name, "hbl_tid") == 0) { + u64 override_tid = strtoul(value, NULL, 16); + if (override_tid != 0) { + g_override_hbl_tid = override_tid; + } + } else if (strcasecmp(name, "override_key") == 0) { if (value[0] == '!') { g_override_by_default = true; value++; diff --git a/stratosphere/fs_mitm/source/fsmitm_utils.hpp b/stratosphere/fs_mitm/source/fsmitm_utils.hpp index ad8d176b1..0811c1f7e 100644 --- a/stratosphere/fs_mitm/source/fsmitm_utils.hpp +++ b/stratosphere/fs_mitm/source/fsmitm_utils.hpp @@ -18,6 +18,25 @@ #include #include +enum BisStorageId : u32 { + BisStorageId_Boot0 = 0, + BisStorageId_Boot1 = 10, + BisStorageId_RawNand = 20, + BisStorageId_BcPkg2_1 = 21, + BisStorageId_BcPkg2_2 = 22, + BisStorageId_BcPkg2_3 = 23, + BisStorageId_BcPkg2_4 = 24, + BisStorageId_BcPkg2_5 = 25, + BisStorageId_BcPkg2_6 = 26, + BisStorageId_Prodinfo = 27, + BisStorageId_ProdinfoF = 28, + BisStorageId_Safe = 29, + BisStorageId_User = 30, + BisStorageId_System = 31, + BisStorageId_SystemProperEncryption = 32, + BisStorageId_SystemProperPartition = 33, +}; + class Utils { public: static bool IsSdInitialized(); @@ -38,6 +57,12 @@ class Utils { /* SD card Initialization + MitM detection. */ static void InitializeSdThreadFunc(void *args); + + static bool HasTitleFlag(u64 tid, const char *flag); + static bool HasHblFlag(const char *flag); + static bool HasGlobalFlag(const char *flag); + static bool HasFlag(u64 tid, const char *flag); + static bool HasSdMitMFlag(u64 tid); static bool HasSdDisableMitMFlag(u64 tid); diff --git a/stratosphere/fs_mitm/source/sha256.c b/stratosphere/fs_mitm/source/sha256.c new file mode 100644 index 000000000..02d40a758 --- /dev/null +++ b/stratosphere/fs_mitm/source/sha256.c @@ -0,0 +1,113 @@ +/* Based on linux source code */ +/* + * sha256_base.h - core logic for SHA-256 implementations + * + * Copyright (C) 2015 Linaro Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "sha256.h" + +#define unlikely(x) __builtin_expect(!!(x), 0) + +void sha256_block_data_order (uint32_t *ctx, const void *in, size_t num); + +int sha256_init(struct sha256_state *sctx) +{ + sctx->state[0] = SHA256_H0; + sctx->state[1] = SHA256_H1; + sctx->state[2] = SHA256_H2; + sctx->state[3] = SHA256_H3; + sctx->state[4] = SHA256_H4; + sctx->state[5] = SHA256_H5; + sctx->state[6] = SHA256_H6; + sctx->state[7] = SHA256_H7; + sctx->count = 0; + + return 0; +} + +int sha256_update(struct sha256_state *sctx, + const void *data, + size_t len) +{ + const u8 *data8 = (const u8 *)data; + unsigned int len32 = (unsigned int)len; + unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; + + sctx->count += len32; + + if (unlikely((partial + len32) >= SHA256_BLOCK_SIZE)) { + int blocks; + + if (partial) { + int p = SHA256_BLOCK_SIZE - partial; + + memcpy(sctx->buf + partial, data8, p); + data8 += p; + len32 -= p; + + sha256_block_data_order(sctx->state, sctx->buf, 1); + } + + blocks = len32 / SHA256_BLOCK_SIZE; + len32 %= SHA256_BLOCK_SIZE; + + if (blocks) { + sha256_block_data_order(sctx->state, data8, blocks); + data8 += blocks * SHA256_BLOCK_SIZE; + } + partial = 0; + } + if (len32) + memcpy(sctx->buf + partial, data8, len32); + + return 0; +} + +int sha256_finalize(struct sha256_state *sctx) +{ + const int bit_offset = SHA256_BLOCK_SIZE - sizeof(u64); + u64 *bits = (u64 *)(sctx->buf + bit_offset); + unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; + + sctx->buf[partial++] = 0x80; + if (partial > bit_offset) { + memset(sctx->buf + partial, 0x0, SHA256_BLOCK_SIZE - partial); + partial = 0; + + sha256_block_data_order(sctx->state, sctx->buf, 1); + } + + memset(sctx->buf + partial, 0x0, bit_offset - partial); + *bits = __builtin_bswap64(sctx->count << 3); + sha256_block_data_order(sctx->state, sctx->buf, 1); + + return 0; +} + +int sha256_finish(struct sha256_state *sctx, void *out) +{ + unsigned int digest_size = 32; + u32 *digest = (u32 *)out; + int i; + + // Switch: misalignment shouldn't be a problem here... + for (i = 0; digest_size > 0; i++, digest_size -= sizeof(u32)) + *digest++ = __builtin_bswap32(sctx->state[i]); + + *sctx = (struct sha256_state){}; + return 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/stratosphere/fs_mitm/source/sha256.h b/stratosphere/fs_mitm/source/sha256.h new file mode 100644 index 000000000..cfb09f89c --- /dev/null +++ b/stratosphere/fs_mitm/source/sha256.h @@ -0,0 +1,36 @@ +#pragma once + +/* Based on linux source code */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define SHA256_DIGEST_SIZE 32 +#define SHA256_BLOCK_SIZE 64 + +#define SHA256_H0 0x6a09e667UL +#define SHA256_H1 0xbb67ae85UL +#define SHA256_H2 0x3c6ef372UL +#define SHA256_H3 0xa54ff53aUL +#define SHA256_H4 0x510e527fUL +#define SHA256_H5 0x9b05688cUL +#define SHA256_H6 0x1f83d9abUL +#define SHA256_H7 0x5be0cd19UL + +struct sha256_state { + u32 state[SHA256_DIGEST_SIZE / 4]; + u64 count; + u8 buf[SHA256_BLOCK_SIZE]; +}; + +int sha256_init(struct sha256_state *sctx); +int sha256_update(struct sha256_state *sctx, const void *data, size_t len); +int sha256_finalize(struct sha256_state *sctx); +int sha256_finish(struct sha256_state *sctx, void *out); + +#ifdef __cplusplus +} +#endif diff --git a/stratosphere/fs_mitm/source/sha256_armv8.s b/stratosphere/fs_mitm/source/sha256_armv8.s new file mode 100644 index 000000000..0420d38be --- /dev/null +++ b/stratosphere/fs_mitm/source/sha256_armv8.s @@ -0,0 +1,163 @@ +.section .text.sha256_armv8, "ax", %progbits +.align 5 +.arch armv8-a+crypto + +# SHA256 assembly implementation for ARMv8 AArch64 (based on linux source code) + +.global sha256_block_data_order +.type sha256_block_data_order,%function +sha256_block_data_order: + +.Lsha256prolog: + + stp x29, x30, [sp,#-64]! + mov x29, sp + adr x3, .LKConstant256 + str q8, [sp, #16] + ld1 {v16.4s-v19.4s}, [x3], #64 + ld1 {v0.4s}, [x0], #16 + ld1 {v20.4s-v23.4s}, [x3], #64 + add x2, x1, x2, lsl #6 + ld1 {v1.4s}, [x0] + ld1 {v24.4s-v27.4s}, [x3], #64 + sub x0, x0, #16 + str q9, [sp, #32] + str q10, [sp, #48] + ld1 {v28.4s-v31.4s}, [x3], #64 + +.Lsha256loop: + + ld1 {v5.16b-v8.16b}, [x1], #64 + mov v2.16b, v0.16b + mov v3.16b, v1.16b + + rev32 v5.16b, v5.16b + rev32 v6.16b, v6.16b + add v9.4s, v5.4s, v16.4s + rev32 v7.16b, v7.16b + add v10.4s, v6.4s, v17.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + sha256su0 v5.4s, v6.4s + rev32 v8.16b, v8.16b + add v9.4s, v7.4s, v18.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + sha256su0 v6.4s, v7.4s + sha256su1 v5.4s, v7.4s, v8.4s + add v10.4s, v8.4s, v19.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + sha256su0 v7.4s, v8.4s + sha256su1 v6.4s, v8.4s, v5.4s + add v9.4s, v5.4s, v20.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + sha256su0 v8.4s, v5.4s + sha256su1 v7.4s, v5.4s, v6.4s + add v10.4s, v6.4s, v21.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + sha256su0 v5.4s, v6.4s + sha256su1 v8.4s, v6.4s, v7.4s + add v9.4s, v7.4s, v22.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + sha256su0 v6.4s, v7.4s + sha256su1 v5.4s, v7.4s, v8.4s + add v10.4s, v8.4s, v23.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + sha256su0 v7.4s, v8.4s + sha256su1 v6.4s, v8.4s, v5.4s + add v9.4s, v5.4s, v24.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + sha256su0 v8.4s, v5.4s + sha256su1 v7.4s, v5.4s, v6.4s + add v10.4s, v6.4s, v25.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + sha256su0 v5.4s, v6.4s + sha256su1 v8.4s, v6.4s, v7.4s + add v9.4s, v7.4s, v26.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + sha256su0 v6.4s, v7.4s + sha256su1 v5.4s, v7.4s, v8.4s + add v10.4s, v8.4s, v27.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + sha256su0 v7.4s, v8.4s + sha256su1 v6.4s, v8.4s, v5.4s + add v9.4s, v5.4s, v28.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + sha256su0 v8.4s, v5.4s + sha256su1 v7.4s, v5.4s, v6.4s + add v10.4s, v6.4s, v29.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + sha256su1 v8.4s, v6.4s, v7.4s + add v9.4s, v7.4s, v30.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + add v10.4s, v8.4s, v31.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + cmp x1, x2 + add v1.4s, v1.4s, v3.4s + add v0.4s, v0.4s, v2.4s + b.ne .Lsha256loop + +.Lsha256epilog: + + st1 {v0.4s,v1.4s}, [x0] + ldr q10, [sp, #48] + ldr q9, [sp, #32] + ldr q8, [sp, #16] + ldr x29, [sp], #64 + ret + +.align 5 +.LKConstant256: +.word 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5 +.word 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5 +.word 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3 +.word 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174 +.word 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc +.word 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da +.word 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7 +.word 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967 +.word 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13 +.word 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85 +.word 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3 +.word 0xd192e819,0xd6990624,0xf40e3585,0x106aa070 +.word 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5 +.word 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3 +.word 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208 +.word 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 + +.size sha256_block_data_order,.-sha256_block_data_order +.align 2 + + + diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index 0fb33e9c0..0bec72ca3 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit 0fb33e9c094bffde737c7a73cd5ccce4d7cbae33 +Subproject commit 0bec72ca36084e9780a8c28abd4a0b24c03c18af diff --git a/stratosphere/pm/source/pm_boot2.cpp b/stratosphere/pm/source/pm_boot2.cpp index 3a6844621..78eddd963 100644 --- a/stratosphere/pm/source/pm_boot2.cpp +++ b/stratosphere/pm/source/pm_boot2.cpp @@ -163,11 +163,22 @@ void EmbeddedBoot2::Main() { char title_path[FS_MAX_PATH] = {0}; strcpy(title_path, "sdmc:/atmosphere/titles/"); strcat(title_path, ent->d_name); - strcat(title_path, "/boot2.flag"); + strcat(title_path, "/flags/boot2.flag"); FILE *f_flag = fopen(title_path, "rb"); if (f_flag != NULL) { fclose(f_flag); LaunchTitle((Boot2KnownTitleId)title_id, FsStorageId_None, 0, NULL); + } else { + /* TODO: Deprecate this in the future. */ + memset(title_path, 0, FS_MAX_PATH); + strcpy(title_path, "sdmc:/atmosphere/titles/"); + strcat(title_path, ent->d_name); + strcat(title_path, "/boot2.flag"); + f_flag = fopen(title_path, "rb"); + if (f_flag != NULL) { + fclose(f_flag); + LaunchTitle((Boot2KnownTitleId)title_id, FsStorageId_None, 0, NULL); + } } } } diff --git a/stratosphere/set_mitm/source/setsys_mitm_service.hpp b/stratosphere/set_mitm/source/setsys_mitm_service.hpp index 2e3da205f..65a172175 100644 --- a/stratosphere/set_mitm/source/setsys_mitm_service.hpp +++ b/stratosphere/set_mitm/source/setsys_mitm_service.hpp @@ -25,7 +25,7 @@ enum SetSysCmd : u32 { class SetSysMitmService : public IMitmServiceObject { public: - SetSysMitmService(std::shared_ptr s) : IMitmServiceObject(s) { + SetSysMitmService(std::shared_ptr s, u64 pid) : IMitmServiceObject(s, pid) { /* ... */ } diff --git a/stratosphere/sm/Makefile b/stratosphere/sm/Makefile index a54a5a0d6..b73b78a53 100644 --- a/stratosphere/sm/Makefile +++ b/stratosphere/sm/Makefile @@ -41,7 +41,7 @@ ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE CFLAGS := -g -Wall -O2 -ffunction-sections \ $(ARCH) $(DEFINES) -CFLAGS += $(INCLUDE) -D__SWITCH__ -DSM_ENABLE_SMHAX -DSM_ENABLE_MITM -DSM_ENABLE_INIT_DEFERS -DSM_MINIMUM_SESSION_LIMIT=8 +CFLAGS += $(INCLUDE) -D__SWITCH__ -DSM_ENABLE_MITM -DSM_ENABLE_INIT_DEFERS -DSM_MINIMUM_SESSION_LIMIT=8 CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17 diff --git a/stratosphere/sm/source/sm_registration.cpp b/stratosphere/sm/source/sm_registration.cpp index 0932db1f7..e5fc77c12 100644 --- a/stratosphere/sm/source/sm_registration.cpp +++ b/stratosphere/sm/source/sm_registration.cpp @@ -221,13 +221,11 @@ bool Registration::HasService(u64 service) { Result Registration::GetServiceHandle(u64 pid, u64 service, Handle *out) { Registration::Service *target_service = GetService(service); - if (target_service == NULL || ShouldInitDefer(service)) { + if (target_service == NULL || ShouldInitDefer(service) || target_service->mitm_waiting_ack) { /* Note: This defers the result until later. */ return RESULT_DEFER_SESSION; } - /* */ - *out = 0; Result rc; if (target_service->mitm_pid == 0 || target_service->mitm_pid == pid) { @@ -255,7 +253,17 @@ Result Registration::GetServiceHandle(u64 pid, u64 service, Handle *out) { rc = resp->result; if (R_SUCCEEDED(rc)) { if (resp->should_mitm) { - rc = svcConnectToPort(out, target_service->mitm_port_h); + rc = svcConnectToPort(&target_service->mitm_fwd_sess_h, target_service->port_h); + if (R_SUCCEEDED(rc)) { + rc = svcConnectToPort(out, target_service->mitm_port_h); + if (R_SUCCEEDED(rc)) { + target_service->mitm_waiting_ack_pid = pid; + target_service->mitm_waiting_ack = true; + } else { + svcCloseHandle(target_service->mitm_fwd_sess_h); + target_service->mitm_fwd_sess_h = 0; + } + } } else { rc = svcConnectToPort(out, target_service->port_h); } @@ -497,6 +505,35 @@ Result Registration::UninstallMitmForPid(u64 pid, u64 service) { return 0; } +Result Registration::AcknowledgeMitmSessionForPid(u64 pid, u64 service, Handle *out, u64 *out_pid) { + if (!service) { + return 0xC15; + } + + u64 service_name_len = GetServiceNameLength(service); + + /* If the service has bytes after a null terminator, that's no good. */ + if (service_name_len != 8 && (service >> (8 * service_name_len))) { + return 0xC15; + } + + Registration::Service *target_service = GetService(service); + if (target_service == NULL) { + return 0xE15; + } + + if ((!IsInitialProcess(pid) && target_service->mitm_pid != pid) || !target_service->mitm_waiting_ack) { + return 0x1015; + } + + *out = target_service->mitm_fwd_sess_h; + *out_pid = target_service->mitm_waiting_ack_pid; + target_service->mitm_fwd_sess_h = 0; + target_service->mitm_waiting_ack_pid = 0; + target_service->mitm_waiting_ack = false; + return 0; +} + Result Registration::AssociatePidTidForMitm(u64 pid, u64 tid) { for (auto &service : g_service_list) { if (service.mitm_pid) { diff --git a/stratosphere/sm/source/sm_registration.hpp b/stratosphere/sm/source/sm_registration.hpp index 9a30652bc..657483822 100644 --- a/stratosphere/sm/source/sm_registration.hpp +++ b/stratosphere/sm/source/sm_registration.hpp @@ -43,6 +43,10 @@ class Registration { u64 mitm_pid; Handle mitm_port_h; Handle mitm_query_h; + + bool mitm_waiting_ack; + u64 mitm_waiting_ack_pid; + Handle mitm_fwd_sess_h; }; /* Utilities. */ @@ -74,5 +78,6 @@ class Registration { /* Extension. */ static Result InstallMitmForPid(u64 pid, u64 service, Handle *out, Handle *query_out); static Result UninstallMitmForPid(u64 pid, u64 service); + static Result AcknowledgeMitmSessionForPid(u64 pid, u64 service, Handle *out, u64 *out_pid); static Result AssociatePidTidForMitm(u64 pid, u64 tid); }; diff --git a/stratosphere/sm/source/sm_user_service.cpp b/stratosphere/sm/source/sm_user_service.cpp index 138ceac8c..02c3b8a61 100644 --- a/stratosphere/sm/source/sm_user_service.cpp +++ b/stratosphere/sm/source/sm_user_service.cpp @@ -98,6 +98,20 @@ Result UserService::AtmosphereUninstallMitm(SmServiceName service) { return rc; } +Result UserService::AtmosphereAcknowledgeMitmSession(Out client_pid, Out fwd_h, SmServiceName service) { + Result rc = 0x415; + Handle out_fwd_h = 0; + if (this->has_initialized) { + rc = Registration::AcknowledgeMitmSessionForPid(this->pid, smEncodeName(service.name), &out_fwd_h, client_pid.GetPointer()); + } + + if (R_SUCCEEDED(rc)) { + fwd_h.SetValue(out_fwd_h); + } + + return rc; +} + Result UserService::AtmosphereAssociatePidTidForMitm(u64 pid, u64 tid) { Result rc = 0x415; if (this->has_initialized) { diff --git a/stratosphere/sm/source/sm_user_service.hpp b/stratosphere/sm/source/sm_user_service.hpp index f99f65bc2..3fedda58c 100644 --- a/stratosphere/sm/source/sm_user_service.hpp +++ b/stratosphere/sm/source/sm_user_service.hpp @@ -27,7 +27,8 @@ enum UserServiceCmd { User_Cmd_AtmosphereInstallMitm = 65000, User_Cmd_AtmosphereUninstallMitm = 65001, - User_Cmd_AtmosphereAssociatePidTidForMitm = 65002 + User_Cmd_AtmosphereAssociatePidTidForMitm = 65002, + User_Cmd_AtmosphereAcknowledgeMitmSession = 65003, }; class UserService final : public IServiceObject { @@ -45,6 +46,7 @@ class UserService final : public IServiceObject { virtual Result AtmosphereInstallMitm(Out srv_h, Out qry_h, SmServiceName service); virtual Result AtmosphereUninstallMitm(SmServiceName service); virtual Result AtmosphereAssociatePidTidForMitm(u64 pid, u64 tid); + virtual Result AtmosphereAcknowledgeMitmSession(Out client_pid, Out fwd_h, SmServiceName service); public: DEFINE_SERVICE_DISPATCH_TABLE { MakeServiceCommandMeta(), @@ -56,6 +58,7 @@ class UserService final : public IServiceObject { MakeServiceCommandMeta(), MakeServiceCommandMeta(), MakeServiceCommandMeta(), + MakeServiceCommandMeta(), #endif }; };