diff --git a/Makefile b/Makefile index e38f766b9..a380a5d94 100644 --- a/Makefile +++ b/Makefile @@ -50,12 +50,13 @@ dist: all mkdir atmosphere-$(AMSVER)/atmosphere mkdir atmosphere-$(AMSVER)/sept mkdir atmosphere-$(AMSVER)/switch - mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036 - mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034 + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032 + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034 + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036 + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037 mkdir -p atmosphere-$(AMSVER)/atmosphere/fatal_errors cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin - mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/sept/payload.bin cp sept/sept-primary/sept-primary.bin atmosphere-$(AMSVER)/sept/sept-primary.bin @@ -66,13 +67,16 @@ dist: all cp common/defaults/system_settings.ini atmosphere-$(AMSVER)/atmosphere/system_settings.ini cp -r common/defaults/kip_patches atmosphere-$(AMSVER)/atmosphere/kip_patches cp -r common/defaults/hbl_html atmosphere-$(AMSVER)/atmosphere/hbl_html - 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/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D/exefs.nsp cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/exefs.nsp - cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro + cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034/exefs.nsp + cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036/exefs.nsp + cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/exefs.nsp mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags/boot2.flag - cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D/exefs.nsp + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/flags + touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/flags/boot2.flag + cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../; rm -r atmosphere-$(AMSVER) mkdir out diff --git a/stratosphere/Makefile b/stratosphere/Makefile index 7bbb09435..76ce0d97a 100644 --- a/stratosphere/Makefile +++ b/stratosphere/Makefile @@ -1,4 +1,4 @@ -MODULES := loader pm sm boot ams_mitm eclct.stub creport fatal dmnt +MODULES := loader pm sm boot ams_mitm eclct.stub ro creport fatal dmnt SUBFOLDERS := libstratosphere $(MODULES) diff --git a/stratosphere/loader/source/ldr_main.cpp b/stratosphere/loader/source/ldr_main.cpp index 9b248344e..5c7249989 100644 --- a/stratosphere/loader/source/ldr_main.cpp +++ b/stratosphere/loader/source/ldr_main.cpp @@ -26,7 +26,6 @@ #include "ldr_process_manager.hpp" #include "ldr_debug_monitor.hpp" #include "ldr_shell.hpp" -#include "ldr_ro_service.hpp" extern "C" { extern u32 __start__; @@ -111,22 +110,16 @@ int main(int argc, char **argv) { consoleDebugInit(debugDevice_SVC); - auto server_manager = new WaitableManager(1); + static auto s_server_manager = WaitableManager(1); /* Add services to manager. */ - server_manager->AddWaitable(new ServiceServer("ldr:pm", 1)); - server_manager->AddWaitable(new ServiceServer("ldr:shel", 3)); - server_manager->AddWaitable(new ServiceServer("ldr:dmnt", 2)); - if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { - /* On 1.0.0-2.3.0, Loader services ldr:ro instead of ro. */ - server_manager->AddWaitable(new ServiceServer("ldr:ro", 0x20)); - } - + s_server_manager.AddWaitable(new ServiceServer("ldr:pm", 1)); + s_server_manager.AddWaitable(new ServiceServer("ldr:shel", 3)); + s_server_manager.AddWaitable(new ServiceServer("ldr:dmnt", 2)); + /* Loop forever, servicing our services. */ - server_manager->Process(); - - delete server_manager; - + s_server_manager.Process(); + return 0; } diff --git a/stratosphere/loader/source/ldr_map.cpp b/stratosphere/loader/source/ldr_map.cpp index c05157bca..468a57a0b 100644 --- a/stratosphere/loader/source/ldr_map.cpp +++ b/stratosphere/loader/source/ldr_map.cpp @@ -27,15 +27,6 @@ Result MapUtils::LocateSpaceForMap(u64 *out, u64 out_size) { } } - -Result MapUtils::MapCodeMemoryForProcess(Handle process_h, bool is_64_bit_address_space, u64 base_address, u64 size, u64 *out_code_memory_address) { - if (kernelAbove200()) { - return MapCodeMemoryForProcessModern(process_h, base_address, size, out_code_memory_address); - } else { - return MapCodeMemoryForProcessDeprecated(process_h, is_64_bit_address_space, base_address, size, out_code_memory_address); - } -} - Result MapUtils::LocateSpaceForMapModern(u64 *out, u64 out_size) { MemoryInfo mem_info = {}; AddressSpaceInfo address_space = {}; @@ -70,7 +61,7 @@ Result MapUtils::LocateSpaceForMapModern(u64 *out, u64 out_size) { cur_base = address_space.map_end; } else { if (R_FAILED(svcQueryMemory(&mem_info, &page_info, cur_base))) { - /* TODO: panic. */ + std::abort(); } if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { *out = cur_base; @@ -105,7 +96,7 @@ Result MapUtils::LocateSpaceForMapDeprecated(u64 *out, u64 out_size) { rc = ResultKernelOutOfMemory; while (true) { if (mem_info.type == 0x10) { - return rc; + return rc; } if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { *out = cur_base; @@ -126,70 +117,6 @@ Result MapUtils::LocateSpaceForMapDeprecated(u64 *out, u64 out_size) { return rc; } -Result MapUtils::MapCodeMemoryForProcessModern(Handle process_h, u64 base_address, u64 size, u64 *out_code_memory_address) { - AddressSpaceInfo address_space = {}; - Result rc; - - if (R_FAILED((rc = GetAddressSpaceInfo(&address_space, process_h)))) { - return rc; - } - - if (size > address_space.addspace_size) { - return ResultLoaderInsufficientAddressSpace; - } - - u64 try_address; - for (unsigned int i = 0; i < 0x200; i++) { - while (true) { - try_address = address_space.addspace_base + (StratosphereRandomUtils::GetRandomU64((u64)(address_space.addspace_size - size) >> 12) << 12); - if (address_space.heap_size && (address_space.heap_base <= try_address + size - 1 && try_address <= address_space.heap_end - 1)) { - continue; - } - if (address_space.map_size && (address_space.map_base <= try_address + size - 1 && try_address <= address_space.map_end - 1)) { - continue; - } - break; - } - rc = svcMapProcessCodeMemory(process_h, try_address, base_address, size); - if (rc != ResultKernelInvalidMemoryState) { - break; - } - } - if (R_SUCCEEDED(rc)) { - *out_code_memory_address = try_address; - } - return rc; -} - -Result MapUtils::MapCodeMemoryForProcessDeprecated(Handle process_h, bool is_64_bit_address_space, u64 base_address, u64 size, u64 *out_code_memory_address) { - Result rc; - u64 addspace_base, addspace_size; - if (is_64_bit_address_space) { - addspace_base = 0x8000000ULL; - addspace_size = 0x78000000ULL; - } else { - addspace_base = 0x200000ULL; - addspace_size = 0x3FE0000ULL; - } - - if (size > addspace_size) { - return ResultLoaderInsufficientAddressSpace; - } - - u64 try_address; - for (unsigned int i = 0; i < 0x200; i++) { - try_address = addspace_base + (StratosphereRandomUtils::GetRandomU64((u64)(addspace_size - size) >> 12) << 12); - rc = svcMapProcessCodeMemory(process_h, try_address, base_address, size); - if (rc != ResultKernelInvalidMemoryState) { - break; - } - } - if (R_SUCCEEDED(rc)) { - *out_code_memory_address = try_address; - } - return rc; -} - Result MapUtils::GetAddressSpaceInfo(AddressSpaceInfo *out, Handle process_h) { Result rc; if (R_FAILED((rc = svcGetInfo(&out->heap_base, 4, process_h, 0)))) { diff --git a/stratosphere/loader/source/ldr_map.hpp b/stratosphere/loader/source/ldr_map.hpp index 134d89a2f..c9a83b608 100644 --- a/stratosphere/loader/source/ldr_map.hpp +++ b/stratosphere/loader/source/ldr_map.hpp @@ -35,11 +35,6 @@ class MapUtils { static Result LocateSpaceForMapDeprecated(u64 *out, u64 out_size); static Result LocateSpaceForMapModern(u64 *out, u64 out_size); static Result LocateSpaceForMap(u64 *out, u64 out_size); - - - static Result MapCodeMemoryForProcessDeprecated(Handle process_h, bool is_64_bit_address_space, u64 base_address, u64 size, u64 *out_code_memory_address); - static Result MapCodeMemoryForProcessModern(Handle process_h, u64 base_address, u64 size, u64 *out_code_memory_address); - static Result MapCodeMemoryForProcess(Handle process_h, bool is_64_bit_address_space, u64 base_address, u64 size, u64 *out_code_memory_address); }; class AutoCloseMap { @@ -84,92 +79,3 @@ class AutoCloseMap { } } }; - -struct MappedCodeMemory { - Handle process_handle; - u64 base_address; - u64 size; - u64 code_memory_address; - void *mapped_address; - - bool IsActive() { - return this->code_memory_address != 0; - } - - bool IsMapped() { - return this->mapped_address != NULL; - } - - /* Utility functions. */ - Result Open(Handle process_h, bool is_64_bit_address_space, u64 address, u64 size) { - Result rc; - if (this->IsActive()) { - return ResultLoaderInternalError; - } - - this->process_handle = process_h; - this->base_address = address; - this->size = size; - - if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(this->process_handle, is_64_bit_address_space, this->base_address, this->size, &this->code_memory_address)))) { - Close(); - } - return rc; - } - - Result OpenAtAddress(Handle process_h, u64 address, u64 size, u64 target_code_memory_address) { - Result rc; - if (this->IsActive()) { - return ResultLoaderInternalError; - } - this->process_handle = process_h; - this->base_address = address; - this->size = size; - - if (R_SUCCEEDED((rc = svcMapProcessCodeMemory(this->process_handle, target_code_memory_address, this->base_address, this->size)))) { - this->code_memory_address = target_code_memory_address; - } else { - Close(); - } - return rc; - } - - Result Map() { - Result rc; - u64 try_address; - if (this->IsMapped()) { - return ResultLoaderInternalError; - } - if (R_FAILED(rc = MapUtils::LocateSpaceForMap(&try_address, size))) { - return rc; - } - - if (R_FAILED((rc = svcMapProcessMemory((void *)try_address, this->process_handle, this->code_memory_address, size)))) { - return rc; - } - - this->mapped_address = (void *)try_address; - return rc; - } - - Result Unmap() { - Result rc = ResultSuccess; - if (this->IsMapped()) { - if (R_FAILED((rc = svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->code_memory_address, this->size)))) { - /* TODO: panic(). */ - } - } - this->mapped_address = NULL; - return rc; - } - - void Close() { - Unmap(); - if (this->IsActive()) { - if (R_FAILED(svcUnmapProcessCodeMemory(this->process_handle, this->code_memory_address, this->base_address, this->size))) { - /* TODO: panic(). */ - } - } - *this = {}; - } -}; diff --git a/stratosphere/loader/source/ldr_nro.cpp b/stratosphere/loader/source/ldr_nro.cpp deleted file mode 100644 index a28da564a..000000000 --- a/stratosphere/loader/source/ldr_nro.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include "ldr_nro.hpp" -#include "ldr_registration.hpp" -#include "ldr_map.hpp" - -Result NroUtils::ValidateNrrHeader(NrrHeader *header, u64 size, u64 title_id_min) { - if (header->magic != MAGIC_NRR0) { - return ResultLoaderInvalidNrr; - } - if (header->nrr_size != size) { - return ResultLoaderInvalidSize; - } - - /* TODO: Check NRR signature. */ - if (false) { - return ResultLoaderInvalidSignature; - } - - if (header->title_id_min != title_id_min) { - return ResultLoaderInvalidNrr; - } - - return ResultSuccess; -} - -Result NroUtils::LoadNro(Registration::Process *target_proc, Handle process_h, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size, u64 *out_address) { - NroHeader nro_hdr = {}; - MappedCodeMemory mcm_nro = {}; - MappedCodeMemory mcm_bss = {}; - unsigned int i; - Result rc = ResultSuccess; - u8 nro_hash[0x20]; - - /* Perform cleanup on failure. */ - ON_SCOPE_EXIT { - if (R_FAILED(rc)) { - mcm_nro.Close(); - mcm_bss.Close(); - } - }; - - /* Ensure there is an available NRO slot. */ - if (std::all_of(target_proc->nro_infos.begin(), target_proc->nro_infos.end(), std::mem_fn(&Registration::NroInfo::in_use))) { - rc = ResultLoaderInsufficientNroRegistrations; - return rc; - } - for (i = 0; i < 0x200; i++) { - if (R_SUCCEEDED(mcm_nro.Open(process_h, target_proc->is_64_bit_addspace, nro_heap_address, nro_heap_size))) { - if (R_SUCCEEDED(mcm_bss.OpenAtAddress(process_h, bss_heap_address, bss_heap_size, mcm_nro.code_memory_address + nro_heap_size))) { - break; - } else { - mcm_nro.Close(); - } - } - } - if (i >= 0x200) { - rc = ResultLoaderInsufficientAddressSpace; - return rc; - } - - /* Map the NRO. */ - if (R_FAILED((rc = mcm_nro.Map()))) { - return rc; - } - - /* Read data from NRO while it's mapped. */ - { - nro_hdr = *((NroHeader *)mcm_nro.mapped_address); - - if (nro_hdr.magic != MAGIC_NRO0) { - rc = ResultLoaderInvalidNro; - return rc; - } - if (nro_hdr.nro_size != nro_heap_size || nro_hdr.bss_size != bss_heap_size) { - rc = ResultLoaderInvalidNro; - return rc; - } - if ((nro_hdr.text_size & 0xFFF) || (nro_hdr.ro_size & 0xFFF) || (nro_hdr.rw_size & 0xFFF) || (nro_hdr.bss_size & 0xFFF)) { - rc = ResultLoaderInvalidNro; - return rc; - } - if (nro_hdr.text_offset != 0 || nro_hdr.text_offset + nro_hdr.text_size != nro_hdr.ro_offset || nro_hdr.ro_offset + nro_hdr.ro_size != nro_hdr.rw_offset || nro_hdr.rw_offset + nro_hdr.rw_size != nro_hdr.nro_size) { - rc = ResultLoaderInvalidNro; - return rc; - } - - sha256CalculateHash(nro_hash, mcm_nro.mapped_address, nro_hdr.nro_size); - } - - /* Unmap the NRO. */ - if (R_FAILED((rc = mcm_nro.Unmap()))) { - return rc; - } - - if (!Registration::IsNroHashPresent(target_proc->index, nro_hash)) { - rc = ResultLoaderInvalidSignature; - return rc; - } - - if (Registration::IsNroAlreadyLoaded(target_proc->index, nro_hash)) { - rc = ResultLoaderNroAlreadyLoaded; - return rc; - } - - if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address, nro_hdr.text_size, 5)))) { - return rc; - } - - if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address + nro_hdr.ro_offset, nro_hdr.ro_size, 1)))) { - return rc; - } - - if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address + nro_hdr.rw_offset, nro_hdr.rw_size + nro_hdr.bss_size, 3)))) { - return rc; - } - - Registration::AddNroToProcess(target_proc->index, &mcm_nro, &mcm_bss, nro_hdr.text_size, nro_hdr.ro_size, nro_hdr.rw_size, nro_hdr.build_id); - *out_address = mcm_nro.code_memory_address; - rc = ResultSuccess; - - return rc; -} diff --git a/stratosphere/loader/source/ldr_nro.hpp b/stratosphere/loader/source/ldr_nro.hpp deleted file mode 100644 index a2886d141..000000000 --- a/stratosphere/loader/source/ldr_nro.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 "ldr_registration.hpp" -#define MAGIC_NRO0 0x304F524E -#define MAGIC_NRR0 0x3052524E - -class NroUtils { - public: - struct NrrHeader { - u32 magic; - u32 _0x4; - u32 _0x8; - u32 _0xC; - u64 title_id_mask; - u64 title_id_pattern; - u64 _0x20; - u64 _0x28; - u8 modulus[0x100]; - u8 fixed_key_signature[0x100]; - u8 nrr_signature[0x100]; - u64 title_id_min; - u32 nrr_size; - u32 _0x33C; - u32 hash_offset; - u32 num_hashes; - u64 _0x348; - }; - - struct NroHeader { - u32 entrypoint_insn; - u32 mod_offset; - u64 padding; - u32 magic; - u32 _0x14; - u32 nro_size; - u32 _0x1C; - u32 text_offset; - u32 text_size; - u32 ro_offset; - u32 ro_size; - u32 rw_offset; - u32 rw_size; - u32 bss_size; - u32 _0x3C; - unsigned char build_id[0x20]; - u8 _0x60[0x20]; - }; - - - static_assert(sizeof(NrrHeader) == 0x350, "Incorrectly defined NrrHeader!"); - static_assert(sizeof(NroHeader) == 0x80, "Incorrectly defined NroHeader!"); - static Result ValidateNrrHeader(NrrHeader *header, u64 size, u64 title_id_min); - static Result LoadNro(Registration::Process *target_proc, Handle process_h, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size, u64 *out_address); -}; \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_process_manager.cpp b/stratosphere/loader/source/ldr_process_manager.cpp index ddef59a8c..c6a6d11cf 100644 --- a/stratosphere/loader/source/ldr_process_manager.cpp +++ b/stratosphere/loader/source/ldr_process_manager.cpp @@ -27,9 +27,7 @@ Result ProcessManagerService::CreateProcess(Out proc_h, u64 index, Registration::TidSid tid_sid; LaunchQueue::LaunchItem *launch_item; char nca_path[FS_MAX_PATH] = {0}; - - fprintf(stderr, "CreateProcess(%016lx, %08x, %08x);\n", index, flags, reslimit_h.handle); - + ON_SCOPE_EXIT { /* Loader doesn't persist the copied resource limit handle. */ svcCloseHandle(reslimit_h.handle); diff --git a/stratosphere/loader/source/ldr_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index 3d01cad43..44c075255 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -20,7 +20,6 @@ #include #include #include "ldr_registration.hpp" -#include "ldr_nro.hpp" static Registration::List g_registration_list = {}; static u64 g_num_registered = 1; @@ -51,15 +50,6 @@ Registration::Process *Registration::GetProcessByProcessId(u64 pid) { return NULL; } -Registration::Process *Registration::GetProcessByRoService(void *service) { - for (unsigned int i = 0; i < Registration::MaxProcesses; i++) { - if (g_registration_list.processes[i].in_use && g_registration_list.processes[i].owner_ro_service == service) { - return &g_registration_list.processes[i]; - } - } - return NULL; -} - bool Registration::RegisterTidSid(const TidSid *tid_sid, u64 *out_index) { Registration::Process *free_process = GetFreeProcess(); if (free_process == NULL) { @@ -124,140 +114,6 @@ void Registration::AddModuleInfo(u64 index, u64 base_address, u64 size, const un } } -void Registration::CloseRoService(void *service, Handle process_h) { - Registration::Process *target_process = GetProcessByRoService(service); - if (target_process == NULL) { - return; - } - for (unsigned int i = 0; i < Registration::MaxNrrInfos; i++) { - if (target_process->nrr_infos[i].IsActive() && target_process->nrr_infos[i].process_handle == process_h) { - target_process->nrr_infos[i].Close(); - } - } - target_process->owner_ro_service = NULL; -} - -Result Registration::AddNrrInfo(u64 index, MappedCodeMemory *nrr_info) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - /* TODO: std::abort(); */ - return ResultLoaderProcessNotRegistered; - } - - auto nrr_info_it = std::find_if_not(target_process->nrr_infos.begin(), target_process->nrr_infos.end(), std::mem_fn(&MappedCodeMemory::IsActive)); - if (nrr_info_it == target_process->nrr_infos.end()) { - return ResultLoaderInsufficientNrrRegistrations; - } - *nrr_info_it = *nrr_info; - return ResultSuccess; -} - -Result Registration::RemoveNrrInfo(u64 index, u64 base_address) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - /* Despite the fact that this should really be a panic condition, Nintendo returns 0x1009 in this case. */ - return ResultLoaderProcessNotRegistered; - } - - for (unsigned int i = 0; i < Registration::MaxNrrInfos; i++) { - if (target_process->nrr_infos[i].IsActive() && target_process->nrr_infos[i].base_address == base_address) { - target_process->nrr_infos[i].Close(); - return ResultSuccess; - } - } - return ResultLoaderNotRegistered; -} - - -bool Registration::IsNroHashPresent(u64 index, u8 *nro_hash) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - /* TODO: panic */ - return false; - } - - for (unsigned int i = 0; i < Registration::MaxNrrInfos; i++) { - if (target_process->nrr_infos[i].IsActive()) { - NroUtils::NrrHeader *nrr = (NroUtils::NrrHeader *)target_process->nrr_infos[i].mapped_address; - /* Binary search. */ - int low = 0, high = (int)(nrr->num_hashes - 1); - while (low <= high) { - int mid = (low + high) / 2; - u8 *hash_in_nrr = (u8 *)nrr + nrr->hash_offset + 0x20 * mid; - int ret = std::memcmp(hash_in_nrr, nro_hash, 0x20); - if (ret == 0) { - return true; - } else if (ret > 0) { - high = mid - 1; - } else { - low = mid + 1; - } - } - } - } - return false; -} - -bool Registration::IsNroAlreadyLoaded(u64 index, u8 *build_id) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - /* TODO: panic */ - return true; - } - - for (unsigned int i = 0; i < Registration::MaxNroInfos; i++) { - if (target_process->nro_infos[i].in_use && std::equal(build_id, build_id + 0x20, target_process->nro_infos[i].build_id)) { - return true; - } - } - return false; -} - -void Registration::AddNroToProcess(u64 index, MappedCodeMemory *nro, MappedCodeMemory *bss, u32 text_size, u32 ro_size, u32 rw_size, u8 *build_id) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - /* TODO: panic */ - return; - } - - auto nro_info_it = std::find_if_not(target_process->nro_infos.begin(), target_process->nro_infos.end(), std::mem_fn(&Registration::NroInfo::in_use)); - if (nro_info_it != target_process->nro_infos.end()) { - nro_info_it->base_address = nro->code_memory_address; - nro_info_it->nro_heap_address = nro->base_address; - nro_info_it->nro_heap_size = nro->size; - nro_info_it->bss_heap_address = bss->base_address; - nro_info_it->bss_heap_size = bss->size; - nro_info_it->text_size = text_size; - nro_info_it->ro_size = ro_size; - nro_info_it->rw_size = rw_size; - std::copy(build_id, build_id + sizeof(nro_info_it->build_id), nro_info_it->build_id); - nro_info_it->in_use = true; - } -} - -Result Registration::RemoveNroInfo(u64 index, Handle process_h, u64 nro_heap_address) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - return ResultLoaderProcessNotRegistered; - } - - for (unsigned int i = 0; i < Registration::MaxNroInfos; i++) { - if (target_process->nro_infos[i].in_use && target_process->nro_infos[i].nro_heap_address == nro_heap_address) { - NroInfo *info = &target_process->nro_infos[i]; - Result rc = svcUnmapProcessCodeMemory(process_h, info->base_address + info->text_size + info->ro_size + info->rw_size, info->bss_heap_address, info->bss_heap_size); - if (R_SUCCEEDED(rc)) { - rc = svcUnmapProcessCodeMemory(process_h, info->base_address + info->text_size + info->ro_size, nro_heap_address + info->text_size + info->ro_size, info->rw_size); - if (R_SUCCEEDED(rc)) { - rc = svcUnmapProcessCodeMemory(process_h, info->base_address, nro_heap_address, info->text_size + info->ro_size); - } - } - target_process->nro_infos[i] = {}; - return rc; - } - } - return ResultLoaderNotLoaded; -} - Result Registration::GetProcessModuleInfo(LoaderModuleInfo *out, u32 max_out, u64 process_id, u32 *num_written) { Registration::Process *target_process = GetProcessByProcessId(process_id); if (target_process == NULL) { diff --git a/stratosphere/loader/source/ldr_registration.hpp b/stratosphere/loader/source/ldr_registration.hpp index 27f458ee7..6588a8ae1 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -24,28 +24,12 @@ class Registration { public: static constexpr size_t MaxProcesses = 0x40; static constexpr size_t MaxModuleInfos = 0x20; - static constexpr size_t MaxNrrInfos = 0x40; - static constexpr size_t MaxNroInfos = 0x40; public: struct ModuleInfoHolder { bool in_use; LoaderModuleInfo info; }; - - struct NroInfo { - bool in_use; - u64 base_address; - u64 total_mapped_size; - u64 nro_heap_address; - u64 nro_heap_size; - u64 bss_heap_address; - u64 bss_heap_size; - u64 text_size; - u64 ro_size; - u64 rw_size; - unsigned char build_id[0x20]; - }; - + struct TidSid { u64 title_id; FsStorageId storage_id; @@ -59,9 +43,6 @@ class Registration { u64 title_id; Registration::TidSid tid_sid; std::array module_infos; - std::array nro_infos; - std::array nrr_infos; - void *owner_ro_service; }; struct List { @@ -72,19 +53,11 @@ class Registration { static Registration::Process *GetFreeProcess(); static Registration::Process *GetProcess(u64 index); static Registration::Process *GetProcessByProcessId(u64 pid); - static Registration::Process *GetProcessByRoService(void *service); static Result GetRegisteredTidSid(u64 index, Registration::TidSid *out); static bool RegisterTidSid(const TidSid *tid_sid, u64 *out_index); static bool UnregisterIndex(u64 index); static void SetProcessIdTidAndIs64BitAddressSpace(u64 index, u64 process_id, u64 tid, bool is_64_bit_addspace); static void AddModuleInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id); - static void CloseRoService(void *service, Handle process_h); - static Result AddNrrInfo(u64 index, MappedCodeMemory *nrr_info); - static Result RemoveNrrInfo(u64 index, u64 base_address); - static bool IsNroHashPresent(u64 index, u8 *nro_hash); - static bool IsNroAlreadyLoaded(u64 index, u8 *build_id); - static void AddNroToProcess(u64 index, MappedCodeMemory *nro, MappedCodeMemory *bss, u32 text_size, u32 ro_size, u32 rw_size, u8 *build_id); - static Result RemoveNroInfo(u64 index, Handle process_h, u64 base_address); static Result GetProcessModuleInfo(LoaderModuleInfo *out, u32 max_out, u64 process_id, u32 *num_written); /* Atmosphere MitM Extension. */ diff --git a/stratosphere/loader/source/ldr_ro_service.cpp b/stratosphere/loader/source/ldr_ro_service.cpp deleted file mode 100644 index 5348a2edd..000000000 --- a/stratosphere/loader/source/ldr_ro_service.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - * 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 . - */ - -#include -#include -#include -#include - -#include "ldr_ro_service.hpp" -#include "ldr_registration.hpp" -#include "ldr_map.hpp" -#include "ldr_nro.hpp" - -Result RelocatableObjectsService::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { - Registration::Process *target_proc = NULL; - if (!this->has_initialized || this->process_id != pid_desc.pid) { - return ResultLoaderInvalidProcess; - } - if (nro_address & 0xFFF) { - return ResultLoaderInvalidAddress; - } - if (nro_address + nro_size <= nro_address || !nro_size || (nro_size & 0xFFF)) { - return ResultLoaderInvalidSize; - } - if (bss_size && bss_address + bss_size <= bss_address) { - return ResultLoaderInvalidSize; - } - /* Ensure no overflow for combined sizes. */ - if (U64_MAX - nro_size < bss_size) { - return ResultLoaderInvalidSize; - } - target_proc = Registration::GetProcessByProcessId(pid_desc.pid); - if (target_proc == NULL || (target_proc->owner_ro_service != NULL && (RelocatableObjectsService *)(target_proc->owner_ro_service) != this)) { - return ResultLoaderInvalidSession; - } - target_proc->owner_ro_service = this; - - return NroUtils::LoadNro(target_proc, this->process_handle, nro_address, nro_size, bss_address, bss_size, load_address.GetPointer()); -} - -Result RelocatableObjectsService::UnloadNro(PidDescriptor pid_desc, u64 nro_address) { - Registration::Process *target_proc = NULL; - if (!this->has_initialized || this->process_id != pid_desc.pid) { - return ResultLoaderInvalidProcess; - } - if (nro_address & 0xFFF) { - return ResultLoaderInvalidAddress; - } - - target_proc = Registration::GetProcessByProcessId(pid_desc.pid); - if (target_proc == NULL || (target_proc->owner_ro_service != NULL && (RelocatableObjectsService *)(target_proc->owner_ro_service) != this)) { - return ResultLoaderInvalidSession; - } - target_proc->owner_ro_service = this; - - return Registration::RemoveNroInfo(target_proc->index, this->process_handle, nro_address); -} - -Result RelocatableObjectsService::LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) { - Result rc = ResultSuccess; - Registration::Process *target_proc = NULL; - MappedCodeMemory nrr_info = {}; - ON_SCOPE_EXIT { - if (R_FAILED(rc) && nrr_info.IsActive()) { - nrr_info.Close(); - } - }; - - if (!this->has_initialized || this->process_id != pid_desc.pid) { - rc = ResultLoaderInvalidProcess; - return rc; - } - if (nrr_address & 0xFFF) { - rc = ResultLoaderInvalidAddress; - return rc; - } - if (nrr_address + nrr_size <= nrr_address || !nrr_size || (nrr_size & 0xFFF)) { - rc = ResultLoaderInvalidSize; - return rc; - } - - target_proc = Registration::GetProcessByProcessId(pid_desc.pid); - if (target_proc == NULL || (target_proc->owner_ro_service != NULL && (RelocatableObjectsService *)(target_proc->owner_ro_service) != this)) { - rc = ResultLoaderInvalidSession; - return rc; - } - target_proc->owner_ro_service = this; - - if (R_FAILED((rc = nrr_info.Open(this->process_handle, target_proc->is_64_bit_addspace, nrr_address, nrr_size)))) { - return rc; - } - - if (R_FAILED((rc = nrr_info.Map()))) { - return rc; - } - - rc = NroUtils::ValidateNrrHeader((NroUtils::NrrHeader *)nrr_info.mapped_address, nrr_size, target_proc->title_id); - if (R_SUCCEEDED(rc)) { - Registration::AddNrrInfo(target_proc->index, &nrr_info); - } - - return rc; -} - -Result RelocatableObjectsService::UnloadNrr(PidDescriptor pid_desc, u64 nrr_address) { - Registration::Process *target_proc = NULL; - if (!this->has_initialized || this->process_id != pid_desc.pid) { - return ResultLoaderInvalidProcess; - } - if (nrr_address & 0xFFF) { - return ResultLoaderInvalidAddress; - } - - target_proc = Registration::GetProcessByProcessId(pid_desc.pid); - if (target_proc == NULL || (target_proc->owner_ro_service != NULL && (RelocatableObjectsService *)(target_proc->owner_ro_service) != this)) { - return ResultLoaderInvalidSession; - } - target_proc->owner_ro_service = this; - - return Registration::RemoveNrrInfo(target_proc->index, nrr_address); -} - -Result RelocatableObjectsService::Initialize(PidDescriptor pid_desc, CopiedHandle process_h) { - u64 handle_pid; - if (R_SUCCEEDED(svcGetProcessId(&handle_pid, process_h.handle)) && handle_pid == pid_desc.pid) { - if (this->has_initialized) { - svcCloseHandle(this->process_handle); - } - this->process_handle = process_h.handle; - this->process_id = handle_pid; - this->has_initialized = true; - return ResultSuccess; - } - return ResultLoaderInvalidProcess; -} diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp index f380e39cf..5015a6a91 100644 --- a/stratosphere/pm/source/pm_main.cpp +++ b/stratosphere/pm/source/pm_main.cpp @@ -164,26 +164,24 @@ int main(int argc, char **argv) /* Initialize and spawn the Process Tracking thread. */ Registration::InitializeSystemResources(); if (R_FAILED(process_track_thread.Initialize(&ProcessTracking::MainLoop, NULL, 0x4000, 0x15))) { - /* TODO: Panic. */ + std::abort(); } if (R_FAILED(process_track_thread.Start())) { - /* TODO: Panic. */ + std::abort(); } - /* TODO: What's a good timeout value to use here? */ - auto server_manager = new WaitableManager(1); + /* Create Server Manager. */ + static auto s_server_manager = WaitableManager(1); /* TODO: Create services. */ - server_manager->AddWaitable(new ServiceServer("pm:shell", 3)); - server_manager->AddWaitable(new ServiceServer("pm:dmnt", 2)); - server_manager->AddWaitable(new ServiceServer("pm:bm", 6)); - server_manager->AddWaitable(new ServiceServer("pm:info", 1)); - + s_server_manager.AddWaitable(new ServiceServer("pm:shell", 3)); + s_server_manager.AddWaitable(new ServiceServer("pm:dmnt", 2)); + s_server_manager.AddWaitable(new ServiceServer("pm:bm", 6)); + s_server_manager.AddWaitable(new ServiceServer("pm:info", 2)); + /* Loop forever, servicing our services. */ server_manager->Process(); - /* Cleanup. */ - delete server_manager; return 0; } diff --git a/stratosphere/ro/Makefile b/stratosphere/ro/Makefile new file mode 100644 index 000000000..2d3bf2862 --- /dev/null +++ b/stratosphere/ro/Makefile @@ -0,0 +1,166 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITPRO)/libnx/switch_rules + +AMSBRANCH := $(shell git symbolic-ref --short HEAD) +AMSREV := $(AMSBRANCH)-$(shell git rev-parse --short HEAD) + +ifneq (, $(strip $(shell git status --porcelain 2>/dev/null))) + AMSREV := $(AMSREV)-dirty +endif + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm". +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include ../../common/include +EXEFS_SRC := exefs_src + +DEFINES := -DDISABLE_IPC -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" -DATMOSPHERE_GIT_REV=\"$(AMSREV)\" -DINI_MAX_LINE=768 + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE + +CFLAGS := -g -Wall -O2 -ffunction-sections \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) -D__SWITCH__ + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lstratosphere -lnx + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/../libstratosphere + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).nsp + +ifeq ($(strip $(APP_JSON)),) +$(OUTPUT).nsp : $(OUTPUT).nso +else +$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm +endif + +$(OUTPUT).nso : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/stratosphere/ro/ro.json b/stratosphere/ro/ro.json new file mode 100644 index 000000000..57d4d3bf3 --- /dev/null +++ b/stratosphere/ro/ro.json @@ -0,0 +1,94 @@ +{ + "name": "ro", + "title_id": "0x0100000000000037", + "title_id_range_min": "0x0100000000000037", + "title_id_range_max": "0x0100000000000037", + "main_thread_stack_size": "0x00005000", + "main_thread_priority": 49, + "default_cpu_id": 3, + "process_category": 0, + "is_retail": true, + "pool_partition": 2, + "is_64_bit": true, + "address_space_type": 1, + "filesystem_access": { + "permissions": "0xFFFFFFFFFFFFFFFF" + }, + "service_access": ["fatal:u", "spl:", "set:sys", "fsp-srv", "pm:info"], + "service_host": ["ldr:ro", "ro:dmnt", "ro:1"], + "kernel_capabilities": [{ + "type": "kernel_flags", + "value": { + "highest_thread_priority": 59, + "lowest_thread_priority": 28, + "lowest_cpu_id": 3, + "highest_cpu_id": 3 + } + }, { + "type": "syscalls", + "value": { + "svcSetHeapSize": "0x01", + "svcSetMemoryPermission": "0x02", + "svcSetMemoryAttribute": "0x03", + "svcMapMemory": "0x04", + "svcUnmapMemory": "0x05", + "svcQueryMemory": "0x06", + "svcExitProcess": "0x07", + "svcCreateThread": "0x08", + "svcStartThread": "0x09", + "svcExitThread": "0x0a", + "svcSleepThread": "0x0b", + "svcGetThreadPriority": "0x0c", + "svcSetThreadPriority": "0x0d", + "svcGetThreadCoreMask": "0x0e", + "svcSetThreadCoreMask": "0x0f", + "svcGetCurrentProcessorNumber": "0x10", + "svcSignalEvent": "0x11", + "svcClearEvent": "0x12", + "svcMapSharedMemory": "0x13", + "svcUnmapSharedMemory": "0x14", + "svcCreateTransferMemory": "0x15", + "svcCloseHandle": "0x16", + "svcResetSignal": "0x17", + "svcWaitSynchronization": "0x18", + "svcCancelSynchronization": "0x19", + "svcArbitrateLock": "0x1a", + "svcArbitrateUnlock": "0x1b", + "svcWaitProcessWideKeyAtomic": "0x1c", + "svcSignalProcessWideKey": "0x1d", + "svcGetSystemTick": "0x1e", + "svcConnectToNamedPort": "0x1f", + "svcSendSyncRequestLight": "0x20", + "svcSendSyncRequest": "0x21", + "svcSendSyncRequestWithUserBuffer": "0x22", + "svcSendAsyncRequestWithUserBuffer": "0x23", + "svcGetProcessId": "0x24", + "svcGetThreadId": "0x25", + "svcBreak": "0x26", + "svcOutputDebugString": "0x27", + "svcReturnFromException": "0x28", + "svcGetInfo": "0x29", + "svcWaitForAddress": "0x34", + "svcSignalToAddress": "0x35", + "svcCreateSession": "0x40", + "svcAcceptSession": "0x41", + "svcReplyAndReceiveLight": "0x42", + "svcReplyAndReceive": "0x43", + "svcReplyAndReceiveWithUserBuffer": "0x44", + "svcCreateEvent": "0x45", + "svcSetProcessMemoryPermission": "0x73", + "svcMapProcessMemory": "0x74", + "svcUnmapProcessMemory": "0x75", + "svcQueryProcessMemory": "0x76", + "svcMapProcessCodeMemory": "0x77", + "svcUnmapProcessCodeMemory": "0x78", + "svcCallSecureMonitor": "0x7f" + } + }, { + "type": "min_kernel_version", + "value": "0x0030" + }, { + "type": "handle_table_size", + "value": 0 + }] +} \ No newline at end of file diff --git a/stratosphere/ro/source/ro_debug_monitor.cpp b/stratosphere/ro/source/ro_debug_monitor.cpp new file mode 100644 index 000000000..ef6897c60 --- /dev/null +++ b/stratosphere/ro/source/ro_debug_monitor.cpp @@ -0,0 +1,29 @@ +/* + * 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 . + */ + +#include +#include +#include + +#include "ro_debug_monitor.hpp" +#include "ro_registration.hpp" + +Result DebugMonitorService::GetProcessModuleInfo(Out count, OutBuffer out_infos, u64 pid) { + if (out_infos.num_elements > INT_MAX) { + return ResultRoInvalidSize; + } + return Registration::GetProcessModuleInfo(count.GetPointer(), out_infos.buffer, out_infos.num_elements, pid); +} \ No newline at end of file diff --git a/stratosphere/ro/source/ro_debug_monitor.hpp b/stratosphere/ro/source/ro_debug_monitor.hpp new file mode 100644 index 000000000..d8273e2df --- /dev/null +++ b/stratosphere/ro/source/ro_debug_monitor.hpp @@ -0,0 +1,33 @@ +/* + * 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 + +enum DebugMonitorServiceCmd { + Dmnt_Cmd_GetProcessModuleInfo = 0 +}; + +class DebugMonitorService final : public IServiceObject { + private: + /* Actual commands. */ + Result GetProcessModuleInfo(Out count, OutBuffer out_infos, u64 pid); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + }; +}; diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp new file mode 100644 index 000000000..cd5f9e807 --- /dev/null +++ b/stratosphere/ro/source/ro_main.cpp @@ -0,0 +1,138 @@ +/* + * 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 . + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "ro_debug_monitor.hpp" +#include "ro_service.hpp" +#include "ro_registration.hpp" + +extern "C" { + extern u32 __start__; + + u32 __nx_applet_type = AppletType_None; + + #define INNER_HEAP_SIZE 0x30000 + size_t nx_inner_heap_size = INNER_HEAP_SIZE; + char nx_inner_heap[INNER_HEAP_SIZE]; + + void __libnx_initheap(void); + void __appInit(void); + void __appExit(void); + + /* Exception handling. */ + alignas(16) u8 __nx_exception_stack[0x1000]; + u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); + void __libnx_exception_handler(ThreadExceptionDump *ctx); + u64 __stratosphere_title_id = TitleId_Ro; + void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx); +} + +void __libnx_exception_handler(ThreadExceptionDump *ctx) { + StratosphereCrashHandler(ctx); +} + +void __libnx_initheap(void) { + void* addr = nx_inner_heap; + size_t size = nx_inner_heap_size; + + /* Newlib */ + extern char* fake_heap_start; + extern char* fake_heap_end; + + fake_heap_start = (char*)addr; + fake_heap_end = (char*)addr + size; +} + +void __appInit(void) { + Result rc; + + SetFirmwareVersionForLibnx(); + + rc = smInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = setsysInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = fsInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + rc = pminfoInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + } + + rc = fsdevMountSdmc(); + if (R_FAILED(rc)) { + std::abort(); + } + + CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); +} + +void __appExit(void) { + fsdevUnmountAll(); + fsExit(); + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + pminfoExit(); + } + setsysExit(); + smExit(); +} + +/* Helpers to create RO objects. */ +static const auto MakeRoServiceForSelf = []() { return std::make_shared(RoModuleType_ForSelf); }; +static const auto MakeRoServiceForOthers = []() { return std::make_shared(RoModuleType_ForOthers); }; + +int main(int argc, char **argv) +{ + /* Initialize. */ + Registration::Initialize(); + + /* Static server manager. */ + static auto s_server_manager = WaitableManager(1); + + /* Create services. */ + s_server_manager.AddWaitable(new ServiceServer("ro:dmnt", 2)); + /* NOTE: Official code passes 32 for ldr:ro max sessions. We will pass 2, because that's the actual limit. */ + s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 2)); + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { + s_server_manager.AddWaitable(new ServiceServer("ro:1", 2)); + } + + /* Loop forever, servicing our services. */ + s_server_manager.Process(); + + /* Cleanup */ + return 0; +} + diff --git a/stratosphere/ro/source/ro_map.cpp b/stratosphere/ro/source/ro_map.cpp new file mode 100644 index 000000000..69046f9ae --- /dev/null +++ b/stratosphere/ro/source/ro_map.cpp @@ -0,0 +1,257 @@ +/* + * 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 . + */ + +#include +#include + +#include "ro_map.hpp" + +bool MapUtils::CanAddGuardRegions(Handle process_handle, u64 address, u64 size) { + MemoryInfo mem_info; + u32 page_info; + + /* Nintendo doesn't validate SVC return values at all. */ + /* TODO: Should we allow these to fail? */ + if (R_FAILED(svcQueryProcessMemory(&mem_info, &page_info, process_handle, address - 1))) { + std::abort(); + } + if (mem_info.type == MemType_Unmapped && address - GuardRegionSize >= mem_info.addr) { + if (R_FAILED(svcQueryProcessMemory(&mem_info, &page_info, process_handle, address + size))) { + std::abort(); + } + return mem_info.type == MemType_Unmapped && address + size + GuardRegionSize <= mem_info.addr + mem_info.size; + } + + return false; +} + +Result MapUtils::LocateSpaceForMap(u64 *out, u64 out_size) { + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) { + return LocateSpaceForMapModern(out, out_size); + } else { + return LocateSpaceForMapDeprecated(out, out_size); + } +} + + +Result MapUtils::MapCodeMemoryForProcess(MappedCodeMemory &out_mcm, Handle process_handle, bool is_64_bit, u64 base_address, u64 size) { + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) { + return MapCodeMemoryForProcessModern(out_mcm, process_handle, base_address, size); + } else { + return MapCodeMemoryForProcessDeprecated(out_mcm, process_handle, is_64_bit, base_address, size); + } +} + +Result MapUtils::LocateSpaceForMapModern(u64 *out, u64 out_size) { + MemoryInfo mem_info = {}; + AddressSpaceInfo address_space = {}; + u32 page_info = 0; + u64 cur_base = 0, cur_end = 0; + Result rc; + + if (R_FAILED((rc = GetAddressSpaceInfo(&address_space, CUR_PROCESS_HANDLE)))) { + return rc; + } + + cur_base = address_space.addspace_base; + + rc = ResultKernelOutOfMemory; + cur_end = cur_base + out_size; + if (cur_end <= cur_base) { + return rc; + } + + while (true) { + if (address_space.heap_size && (address_space.heap_base <= cur_end - 1 && cur_base <= address_space.heap_end - 1)) { + /* If we overlap the heap region, go to the end of the heap region. */ + if (cur_base == address_space.heap_end) { + return rc; + } + cur_base = address_space.heap_end; + } else if (address_space.map_size && (address_space.map_base <= cur_end - 1 && cur_base <= address_space.map_end - 1)) { + /* If we overlap the map region, go to the end of the map region. */ + if (cur_base == address_space.map_end) { + return rc; + } + cur_base = address_space.map_end; + } else { + if (R_FAILED(svcQueryMemory(&mem_info, &page_info, cur_base))) { + std::abort(); + } + if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { + *out = cur_base; + return ResultSuccess; + } + if (mem_info.addr + mem_info.size <= cur_base) { + return rc; + } + cur_base = mem_info.addr + mem_info.size; + if (cur_base >= address_space.addspace_end) { + return rc; + } + } + cur_end = cur_base + out_size; + if (cur_base + out_size <= cur_base) { + return rc; + } + } +} + + +Result MapUtils::LocateSpaceForMapDeprecated(u64 *out, u64 out_size) { + MemoryInfo mem_info = {}; + u32 page_info = 0; + Result rc; + + u64 cur_base = 0x8000000ULL; + if (R_FAILED((rc = svcQueryMemory(&mem_info, &page_info, cur_base)))) { + return rc; + } + + rc = ResultKernelOutOfMemory; + while (true) { + if (mem_info.type == 0x10) { + return rc; + } + if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { + *out = cur_base; + return ResultSuccess; + } + u64 mem_end = mem_info.addr + mem_info.size; + if (mem_end < cur_base) { + return rc; + } + if (mem_end >> 31) { + break; + } + cur_base = mem_end; + if (R_FAILED((rc = svcQueryMemory(&mem_info, &page_info, cur_base)))) { + return rc; + } + } + return rc; +} + +Result MapUtils::MapCodeMemoryForProcessModern(MappedCodeMemory &out_mcm, Handle process_handle, u64 base_address, u64 size) { + AddressSpaceInfo address_space = {}; + Result rc; + + if (R_FAILED((rc = GetAddressSpaceInfo(&address_space, process_handle)))) { + return rc; + } + + if (size > address_space.addspace_size) { + return ResultRoInsufficientAddressSpace; + } + + u64 try_address; + for (unsigned int i = 0; i < LocateRetryCount; i++) { + while (true) { + try_address = address_space.addspace_base + (StratosphereRandomUtils::GetRandomU64((u64)(address_space.addspace_size - size) >> 12) << 12); + if (address_space.heap_size && (address_space.heap_base <= try_address + size - 1 && try_address <= address_space.heap_end - 1)) { + continue; + } + if (address_space.map_size && (address_space.map_base <= try_address + size - 1 && try_address <= address_space.map_end - 1)) { + continue; + } + break; + } + MappedCodeMemory tmp_mcm(process_handle, try_address, base_address, size); + rc = tmp_mcm.GetResult(); + if (rc == ResultKernelInvalidMemoryState) { + continue; + } + if (R_FAILED(rc)) { + return rc; + } + + if (!CanAddGuardRegions(process_handle, try_address, size)) { + continue; + } + + /* We're done searching. */ + out_mcm = std::move(tmp_mcm); + return ResultSuccess; + } + + return ResultRoInsufficientAddressSpace; +} + +Result MapUtils::MapCodeMemoryForProcessDeprecated(MappedCodeMemory &out_mcm, Handle process_handle, bool is_64_bit, u64 base_address, u64 size) { + Result rc; + u64 addspace_base, addspace_size; + if (is_64_bit) { + addspace_base = 0x8000000ULL; + addspace_size = 0x78000000ULL; + } else { + addspace_base = 0x200000ULL; + addspace_size = 0x3FE0000ULL; + } + + if (size > addspace_size) { + return ResultRoInsufficientAddressSpace; + } + + u64 try_address; + for (unsigned int i = 0; i < LocateRetryCount; i++) { + try_address = addspace_base + (StratosphereRandomUtils::GetRandomU64((u64)(addspace_size - size) >> 12) << 12); + + MappedCodeMemory tmp_mcm(process_handle, try_address, base_address, size); + rc = tmp_mcm.GetResult(); + if (rc == ResultKernelInvalidMemoryState) { + continue; + } + if (R_FAILED(rc)) { + return rc; + } + + if (!CanAddGuardRegions(process_handle, try_address, size)) { + continue; + } + + /* We're done searching. */ + out_mcm = std::move(tmp_mcm); + return ResultSuccess; + } + + return ResultRoInsufficientAddressSpace; +} + +Result MapUtils::GetAddressSpaceInfo(AddressSpaceInfo *out, Handle process_h) { + Result rc; + if (R_FAILED((rc = svcGetInfo(&out->heap_base, 4, process_h, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->heap_size, 5, process_h, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->map_base, 2, process_h, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->map_size, 3, process_h, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->addspace_base, 12, process_h, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->addspace_size, 13, process_h, 0)))) { + return rc; + } + out->heap_end = out->heap_base + out->heap_size; + out->map_end = out->map_base + out->map_size; + out->addspace_end = out->addspace_base + out->addspace_size; + return ResultSuccess; +} \ No newline at end of file diff --git a/stratosphere/ro/source/ro_map.hpp b/stratosphere/ro/source/ro_map.hpp new file mode 100644 index 000000000..bfffee190 --- /dev/null +++ b/stratosphere/ro/source/ro_map.hpp @@ -0,0 +1,136 @@ +/* + * 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 + +class MappedCodeMemory { + private: + Handle process_handle = INVALID_HANDLE; + Result result = ResultRoInternalError; + u64 dst_address = 0; + u64 src_address = 0; + u64 size = 0; + public: + MappedCodeMemory() : process_handle(INVALID_HANDLE), result(ResultRoInternalError), dst_address(0), src_address(0), size(0) { + /* ... */ + } + + MappedCodeMemory(Handle p_h, u64 dst, u64 src, u64 sz) : process_handle(p_h), dst_address(dst), src_address(src), size(sz) { + this->result = svcMapProcessCodeMemory(this->process_handle, this->dst_address, this->src_address, this->size); + } + + ~MappedCodeMemory() { + if (this->process_handle != INVALID_HANDLE && this->size > 0 && R_SUCCEEDED(this->result)) { + if (R_FAILED((this->result = svcUnmapProcessCodeMemory(this->process_handle, this->dst_address, this->src_address, this->size)))) { + std::abort(); + } + } + } + + u64 GetDstAddress() const { + return this->dst_address; + } + + Result GetResult() const { + return this->result; + } + + bool IsSuccess() const { + return R_SUCCEEDED(this->result); + } + + void Invalidate() { + this->process_handle = INVALID_HANDLE; + } + + MappedCodeMemory &operator=(MappedCodeMemory &&o) { + this->process_handle = o.process_handle; + this->result = o.result; + this->dst_address = o.dst_address; + this->src_address = o.src_address; + this->size = o.size; + o.Invalidate(); + return *this; + } +}; + +class AutoCloseMap { + private: + Handle process_handle; + Result result; + void *mapped_address; + u64 base_address; + u64 size; + public: + AutoCloseMap(void *mp, Handle p_h, u64 ba, u64 sz) : process_handle(p_h), mapped_address(mp), base_address(ba), size(sz) { + this->result = svcMapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size); + } + AutoCloseMap(u64 mp, Handle p_h, u64 ba, u64 sz) : process_handle(p_h), mapped_address(reinterpret_cast(mp)), base_address(ba), size(sz) { + this->result = svcMapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size); + } + + ~AutoCloseMap() { + if (this->process_handle != INVALID_HANDLE && R_SUCCEEDED(this->result)) { + if (R_FAILED((this->result = svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size)))) { + std::abort(); + } + } + } + + Result GetResult() const { + return this->result; + } + + bool IsSuccess() const { + return R_SUCCEEDED(this->result); + } + + void Invalidate() { + this->process_handle = INVALID_HANDLE; + } +}; + + +class MapUtils { + public: + static constexpr size_t GuardRegionSize = 0x4000; + static constexpr size_t LocateRetryCount = 0x200; + public: + struct AddressSpaceInfo { + u64 heap_base; + u64 heap_size; + u64 heap_end; + u64 map_base; + u64 map_size; + u64 map_end; + u64 addspace_base; + u64 addspace_size; + u64 addspace_end; + }; + private: + static Result GetAddressSpaceInfo(AddressSpaceInfo *out, Handle process_h); + static Result LocateSpaceForMapDeprecated(u64 *out, u64 out_size); + static Result LocateSpaceForMapModern(u64 *out, u64 out_size); + + static Result MapCodeMemoryForProcessDeprecated(MappedCodeMemory &out_mcm, Handle process_handle, bool is_64_bit, u64 base_address, u64 size); + static Result MapCodeMemoryForProcessModern(MappedCodeMemory &out_mcm, Handle process_handle, u64 base_address, u64 size); + public: + static Result LocateSpaceForMap(u64 *out, u64 out_size); + static Result MapCodeMemoryForProcess(MappedCodeMemory &out_mcm, Handle process_handle, bool is_64_bit, u64 base_address, u64 size); + static bool CanAddGuardRegions(Handle process_handle, u64 address, u64 size); +}; \ No newline at end of file diff --git a/stratosphere/ro/source/ro_nrr.cpp b/stratosphere/ro/source/ro_nrr.cpp new file mode 100644 index 000000000..ee752dc9c --- /dev/null +++ b/stratosphere/ro/source/ro_nrr.cpp @@ -0,0 +1,81 @@ +/* + * 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 . + */ + +#include +#include +#include +#include + +#include "ro_nrr.hpp" +#include "ro_registration.hpp" + +Result NrrUtils::ValidateNrrSignature(const NrrHeader *header) { + /* TODO: Implement RSA-2048 PSS..... */ + + /* TODO: Check PSS fixed-key signature. */ + if (false) { + return ResultRoNotAuthorized; + } + + /* Check TitleID pattern is valid. */ + if ((header->title_id & header->title_id_mask) != header->title_id_pattern) { + return ResultRoNotAuthorized; + } + + /* TODO: Check PSS signature over hashes. */ + if (false) { + return ResultRoNotAuthorized; + } + + return ResultSuccess; +} + +Result NrrUtils::ValidateNrr(const NrrHeader *header, u64 size, u64 title_id, RoModuleType expected_type, bool enforce_type) { + if (header->magic != MagicNrr0) { + return ResultRoInvalidNrr; + } + if (header->nrr_size != size) { + return ResultRoInvalidSize; + } + + bool ease_nro_restriction = Registration::ShouldEaseNroRestriction(); + + /* Check signature. */ + Result rc = ValidateNrrSignature(header); + if (R_FAILED(rc)) { + if (!ease_nro_restriction) { + return rc; + } + } + + /* Check title id. */ + if (title_id != header->title_id) { + if (!ease_nro_restriction) { + return ResultRoInvalidNrr; + } + } + + /* Check type. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { + if (!enforce_type || expected_type != static_cast(header->nrr_type)) { + if (!ease_nro_restriction) { + return ResultRoInvalidNrrType; + } + } + } + + return ResultSuccess; +} \ No newline at end of file diff --git a/stratosphere/ro/source/ro_nrr.hpp b/stratosphere/ro/source/ro_nrr.hpp new file mode 100644 index 000000000..08e2204d9 --- /dev/null +++ b/stratosphere/ro/source/ro_nrr.hpp @@ -0,0 +1,56 @@ +/* + * 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 + +enum RoModuleType : u32 { + RoModuleType_ForSelf = 0, + RoModuleType_ForOthers = 1, +}; + +struct NrrHeader { + u32 magic; + u32 _0x4; + u32 _0x8; + u32 _0xC; + u64 title_id_mask; + u64 title_id_pattern; + u64 _0x20; + u64 _0x28; + u8 modulus[0x100]; + u8 fixed_key_signature[0x100]; + u8 nrr_signature[0x100]; + u64 title_id; + u32 nrr_size; + u8 nrr_type; /* 7.0.0+ */ + u8 _0x33D[3]; + u32 hash_offset; + u32 num_hashes; + u64 _0x348; +}; +static_assert(sizeof(NrrHeader) == 0x350, "NrrHeader definition!"); + +class NrrUtils { + public: + static constexpr u32 MagicNrr0 = 0x3052524E; + private: + static Result ValidateNrrSignature(const NrrHeader *header); + public: + static Result ValidateNrr(const NrrHeader *header, u64 size, u64 title_id, RoModuleType expected_type, bool enforce_type); +}; \ No newline at end of file diff --git a/stratosphere/ro/source/ro_patcher.cpp b/stratosphere/ro/source/ro_patcher.cpp new file mode 100644 index 000000000..355a7d2ce --- /dev/null +++ b/stratosphere/ro/source/ro_patcher.cpp @@ -0,0 +1,188 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "ro_patcher.hpp" +#include "ro_registration.hpp" + +/* IPS Patching adapted from Luma3DS (https://github.com/AuroraWright/Luma3DS/blob/master/sysmodules/loader/source/patcher.c) */ + +#define IPS_MAGIC "PATCH" +#define IPS_TAIL "EOF" + +#define IPS32_MAGIC "IPS32" +#define IPS32_TAIL "EEOF" + +static inline u8 HexNybbleToU8(const char nybble) { + if ('0' <= nybble && nybble <= '9') { + return nybble - '0'; + } else if ('a' <= nybble && nybble <= 'f') { + return nybble - 'a' + 0xa; + } else { + return nybble - 'A' + 0xA; + } +} + +static bool MatchesBuildId(const char *name, size_t name_len, const u8 *build_id) { + /* Validate name is hex build id. */ + for (unsigned int i = 0; i < name_len - 4; i++) { + if (isxdigit(name[i]) == 0) { + return false; + } + } + + /* Read build id from name. */ + u8 build_id_from_name[0x20] = {0}; + for (unsigned int name_ofs = 0, id_ofs = 0; name_ofs < name_len - 4; id_ofs++) { + build_id_from_name[id_ofs] |= HexNybbleToU8(name[name_ofs++]) << 4; + build_id_from_name[id_ofs] |= HexNybbleToU8(name[name_ofs++]); + } + + return memcmp(build_id, build_id_from_name, sizeof(build_id_from_name)) == 0; +} + +static void ApplyIpsPatch(u8 *mapped_nro, size_t mapped_size, bool is_ips32, FILE *f_ips) { + u8 buffer[4]; + while (fread(buffer, is_ips32 ? 4 : 3, 1, f_ips) == 1) { + if (is_ips32 && memcmp(buffer, IPS32_TAIL, 4) == 0) { + break; + } else if (!is_ips32 && memcmp(buffer, IPS_TAIL, 3) == 0) { + break; + } + + /* Offset of patch. */ + u32 patch_offset; + if (is_ips32) { + patch_offset = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; + } else { + patch_offset = (buffer[0] << 16) | (buffer[1] << 8) | (buffer[2]); + } + + /* Size of patch. */ + if (fread(buffer, 2, 1, f_ips) != 1) { + break; + } + u32 patch_size = (buffer[0] << 8) | (buffer[1]); + + /* Check for RLE encoding. */ + if (patch_size == 0) { + /* Size of RLE. */ + if (fread(buffer, 2, 1, f_ips) != 1) { + break; + } + + u32 rle_size = (buffer[0] << 8) | (buffer[1]); + + /* Value for RLE. */ + if (fread(buffer, 1, 1, f_ips) != 1) { + break; + } + + if (patch_offset < sizeof(Registration::NroHeader)) { + if (patch_offset + rle_size > sizeof(Registration::NroHeader)) { + u32 diff = sizeof(Registration::NroHeader) - patch_offset; + patch_offset += diff; + rle_size -= diff; + goto IPS_RLE_PATCH_OFFSET_WITHIN_BOUNDS; + } + } else { + IPS_RLE_PATCH_OFFSET_WITHIN_BOUNDS: + patch_offset -= sizeof(Registration::NroHeader); + if (patch_offset + rle_size > mapped_size) { + rle_size = mapped_size - patch_offset; + } + memset(mapped_nro + patch_offset, buffer[0], rle_size); + } + } else { + if (patch_offset < sizeof(Registration::NroHeader)) { + if (patch_offset + patch_size > sizeof(Registration::NroHeader)) { + u32 diff = sizeof(Registration::NroHeader) - patch_offset; + patch_offset += diff; + patch_size -= diff; + fseek(f_ips, diff, SEEK_CUR); + goto IPS_DATA_PATCH_OFFSET_WITHIN_BOUNDS; + } else { + fseek(f_ips, patch_size, SEEK_CUR); + } + } else { + IPS_DATA_PATCH_OFFSET_WITHIN_BOUNDS: + patch_offset -= sizeof(Registration::NroHeader); + u32 read_size = patch_size; + if (patch_offset + read_size > mapped_size) { + read_size = mapped_size - patch_offset; + } + if (fread(mapped_nro + patch_offset, read_size, 1, f_ips) != 1) { + break; + } + if (patch_size > read_size) { + fseek(f_ips, patch_size - read_size, SEEK_CUR); + } + } + } + } +} + +void PatchUtils::ApplyPatches(const ModuleId *module_id, u8 *mapped_nro, size_t mapped_size) { + /* Inspect all patches from /atmosphere/nro_patches/<*>/<*>.ips */ + char path[FS_MAX_PATH+1] = {0}; + snprintf(path, sizeof(path) - 1, "sdmc:/atmosphere/nro_patches"); + DIR *patches_dir = opendir(path); + struct dirent *pdir_ent; + if (patches_dir != NULL) { + /* Iterate over the patches directory to find patch subdirectories. */ + while ((pdir_ent = readdir(patches_dir)) != NULL) { + if (strcmp(pdir_ent->d_name, ".") == 0 || strcmp(pdir_ent->d_name, "..") == 0) { + continue; + } + snprintf(path, sizeof(path) - 1, "sdmc:/atmosphere/nro_patches/%s", pdir_ent->d_name); + DIR *patch_dir = opendir(path); + struct dirent *ent; + if (patch_dir != NULL) { + /* Iterate over the patch subdirectory to find .ips patches. */ + while ((ent = readdir(patch_dir)) != NULL) { + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { + continue; + } + size_t name_len = strlen(ent->d_name); + if ((4 < name_len && name_len <= 0x44) && ((name_len & 1) == 0) && strcmp(ent->d_name + name_len - 4, ".ips") == 0 && MatchesBuildId(ent->d_name, name_len, module_id->build_id)) { + snprintf(path, sizeof(path) - 1, "sdmc:/atmosphere/nro_patches/%s/%s", pdir_ent->d_name, ent->d_name); + FILE *f_ips = fopen(path, "rb"); + if (f_ips != NULL) { + u8 header[5]; + if (fread(header, 5, 1, f_ips) == 1) { + if (memcmp(header, IPS_MAGIC, 5) == 0) { + ApplyIpsPatch(mapped_nro, mapped_size, false, f_ips); + } else if (memcmp(header, IPS32_MAGIC, 5) == 0) { + ApplyIpsPatch(mapped_nro, mapped_size, true, f_ips); + } + } + fclose(f_ips); + } + } + } + closedir(patch_dir); + } + } + closedir(patches_dir); + } +} \ No newline at end of file diff --git a/stratosphere/ro/source/ro_patcher.hpp b/stratosphere/ro/source/ro_patcher.hpp new file mode 100644 index 000000000..ed88c7223 --- /dev/null +++ b/stratosphere/ro/source/ro_patcher.hpp @@ -0,0 +1,26 @@ +/* + * 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 "ro_types.hpp" + +class PatchUtils { + public: + static void ApplyPatches(const ModuleId *module_id, u8 *mapped_nro, size_t mapped_size); +}; \ No newline at end of file diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp new file mode 100644 index 000000000..49c090030 --- /dev/null +++ b/stratosphere/ro/source/ro_registration.cpp @@ -0,0 +1,518 @@ +/* + * 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 . + */ + +#include +#include +#include +#include + +#include "ro_registration.hpp" +#include "ro_map.hpp" +#include "ro_nrr.hpp" +#include "ro_patcher.hpp" + +/* Declare process contexts as global array. */ +static Registration::RoProcessContext g_process_contexts[Registration::MaxSessions] = {}; + +static bool g_is_development_hardware, g_is_development_function_enabled; + +void Registration::Initialize() { + if (R_FAILED(splInitialize())) { + std::abort(); + } + ON_SCOPE_EXIT { splExit(); }; + + if (R_FAILED(splIsDevelopment(&g_is_development_hardware))) { + std::abort(); + } + + { + u64 out_val = 0; + if (R_FAILED(splGetConfig(SplConfigItem_IsDebugMode, &out_val))) { + std::abort(); + } + g_is_development_function_enabled = out_val != 0; + } +} + +bool Registration::ShouldEaseNroRestriction() { + bool should_ease = false; + + if (R_FAILED(setsysGetSettingsItemValue("ro", "ease_nro_restriction", &should_ease, sizeof(should_ease)))) { + return false; + } + + /* Nintendo only allows easing restriction on dev, we will allow on production, as well. */ + /* should_ease &= g_is_development_function_enabled; */ + return should_ease; +} + +Result Registration::RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id) { + /* Check if a process context already exists. */ + for (size_t i = 0; i < Registration::MaxSessions; i++) { + if (g_process_contexts[i].process_id == process_id) { + return ResultRoInvalidSession; + } + } + + /* Find a free process context. */ + for (size_t i = 0; i < Registration::MaxSessions; i++) { + if (!g_process_contexts[i].in_use) { + std::memset(&g_process_contexts[i], 0, sizeof(g_process_contexts[i])); + g_process_contexts[i].process_id = process_id; + g_process_contexts[i].process_handle = process_handle; + g_process_contexts[i].in_use = true; + *out_context = &g_process_contexts[i]; + return ResultSuccess; + } + } + + /* Failure to find a free context is actually an abort condition. */ + /* TODO: Should this return an unofficial error code? */ + std::abort(); +} + +void Registration::UnregisterProcess(RoProcessContext *context) { + if (context->process_handle != INVALID_HANDLE) { + for (size_t i = 0; i < Registration::MaxNrrInfos; i++) { + if (context->nrr_in_use[i]) { + UnmapNrr(context->process_handle, context->nrr_infos[i].header, context->nrr_infos[i].nrr_heap_address, context->nrr_infos[i].nrr_heap_size, context->nrr_infos[i].mapped_code_address); + } + } + svcCloseHandle(context->process_handle); + } + std::memset(context, 0, sizeof(*context)); +} + +Result Registration::GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id) { + size_t count = 0; + for (size_t sess = 0; sess < Registration::MaxSessions; sess++) { + if (g_process_contexts[sess].process_id == process_id) { + /* For convenience, helper. */ + const RoProcessContext *context = &g_process_contexts[sess]; + + for (size_t i = 0; i < Registration::MaxNroInfos && count < max_out_count; i++) { + if (!context->nro_in_use[i]) { + continue; + } + + /* Just copy out the info. */ + LoaderModuleInfo *out_info = &out_infos[count++]; + memcpy(out_info->build_id, &context->nro_infos[i].module_id, sizeof(context->nro_infos[i].module_id)); + out_info->base_address = context->nro_infos[i].base_address; + out_info->size = context->nro_infos[i].nro_heap_size + context->nro_infos[i].bss_heap_size; + } + + break; + } + } + + *out_count = static_cast(count); + return ResultSuccess; +} + +Result Registration::LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_address, u64 nrr_size, RoModuleType expected_type, bool enforce_type) { + /* Validate address/size. */ + if (nrr_address & 0xFFF) { + return ResultRoInvalidAddress; + } + if (nrr_size == 0 || (nrr_size & 0xFFF) || !(nrr_address < nrr_address + nrr_size)) { + return ResultRoInvalidSize; + } + + /* Check we have space for a new NRR. */ + size_t slot = 0; + for (slot = 0; slot < Registration::MaxNrrInfos; slot++) { + if (!context->nrr_in_use[slot]) { + break; + } + } + if (slot == Registration::MaxNrrInfos) { + return ResultRoTooManyNrr; + } + + NrrInfo *nrr_info = &context->nrr_infos[slot]; + + /* Map. */ + NrrHeader *header = nullptr; + u64 mapped_code_address = 0; + Result rc = MapAndValidateNrr(&header, &mapped_code_address, context->process_handle, title_id, nrr_address, nrr_size, expected_type, enforce_type); + if (R_FAILED(rc)) { + return rc; + } + + /* Set NRR info. */ + nrr_info->header = header; + nrr_info->nrr_heap_address = nrr_address; + nrr_info->nrr_heap_size = nrr_size; + nrr_info->mapped_code_address = mapped_code_address; + context->nrr_in_use[slot] = true; + + /* TODO. */ + return ResultSuccess; +} + +Result Registration::UnloadNrr(RoProcessContext *context, u64 nrr_address) { + /* Validate address. */ + if (nrr_address & 0xFFF) { + return ResultRoInvalidAddress; + } + + /* Check the NRR is loaded. */ + size_t slot = 0; + for (slot = 0; slot < Registration::MaxNrrInfos; slot++) { + if (!context->nrr_in_use[slot]) { + continue; + } + + if (context->nrr_infos[slot].nrr_heap_address == nrr_address) { + break; + } + } + if (slot == Registration::MaxNrrInfos) { + return ResultRoNotRegistered; + } + + /* Unmap. */ + const NrrInfo nrr_info = context->nrr_infos[slot]; + { + /* Nintendo does this unconditionally, whether or not the actual unmap succeeds. */ + context->nrr_in_use[slot] = false; + std::memset(&context->nrr_infos[slot], 0, sizeof(context->nrr_infos[slot])); + } + return UnmapNrr(context->process_handle, nrr_info.header, nrr_info.nrr_heap_address, nrr_info.nrr_heap_size, nrr_info.mapped_code_address); +} + + +Result Registration::LoadNro(u64 *out_address, RoProcessContext *context, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { + /* Validate address/size. */ + if (nro_address & 0xFFF) { + return ResultRoInvalidAddress; + } + if (nro_size == 0 || (nro_size & 0xFFF) || !(nro_address < nro_address + nro_size)) { + return ResultRoInvalidSize; + } + if (bss_address & 0xFFF) { + return ResultRoInvalidAddress; + } + if ((bss_size & 0xFFF) || (bss_size > 0 && !(bss_address < bss_address + bss_size))) { + return ResultRoInvalidSize; + } + + const u64 total_size = nro_size + bss_size; + if (total_size < nro_size || total_size < bss_size) { + return ResultRoInvalidSize; + } + + /* Check we have space for a new NRO. */ + size_t slot = 0; + for (slot = 0; slot < Registration::MaxNroInfos; slot++) { + if (!context->nro_in_use[slot]) { + break; + } + } + if (slot == Registration::MaxNroInfos) { + return ResultRoTooManyNro; + } + + NroInfo *nro_info = &context->nro_infos[slot]; + nro_info->nro_heap_address = nro_address; + nro_info->nro_heap_size = nro_size; + nro_info->bss_heap_address = bss_address; + nro_info->bss_heap_size = bss_size; + + /* Map the NRO. */ + Result rc = MapNro(&nro_info->base_address, context->process_handle, nro_address, nro_size, bss_address, bss_size); + if (R_FAILED(rc)) { + return rc; + } + + /* Validate the NRO (parsing region extents). */ + u64 rx_size, ro_size, rw_size; + if (R_FAILED((rc = ValidateNro(&nro_info->module_id, &rx_size, &ro_size, &rw_size, context, nro_info->base_address, nro_size, bss_size)))) { + UnmapNro(context->process_handle, nro_info->base_address, nro_address, bss_address, bss_size, nro_size, 0); + return rc; + } + + /* Set NRO perms. */ + if (R_FAILED((rc = SetNroPerms(context->process_handle, nro_info->base_address, rx_size, ro_size, rw_size + bss_size)))) { + UnmapNro(context->process_handle, nro_info->base_address, nro_address, bss_address, bss_size, rx_size + ro_size, rw_size); + return rc; + } + + nro_info->code_size = rx_size + ro_size; + nro_info->rw_size = rw_size; + context->nro_in_use[slot] = true; + *out_address = nro_info->base_address; + return ResultSuccess; +} + +bool Registration::IsNroHashPresent(RoProcessContext *context, const Sha256Hash *hash) { + for (size_t i = 0; i < Registration::MaxNrrInfos; i++) { + if (context->nrr_in_use[i]) { + const Sha256Hash *nro_hashes = reinterpret_cast(reinterpret_cast(context->nrr_infos[i].header) + context->nrr_infos[i].header->hash_offset); + if (std::binary_search(nro_hashes, nro_hashes + context->nrr_infos[i].header->num_hashes, *hash)) { + return true; + } + } + } + return false; +} + +Result Registration::ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, RoProcessContext *context, u64 base_address, u64 nro_size, u64 bss_size) { + /* Find space to map the NRO. */ + u64 map_address; + if (R_FAILED(MapUtils::LocateSpaceForMap(&map_address, nro_size))) { + return ResultRoInsufficientAddressSpace; + } + + /* Actually map the NRO. */ + AutoCloseMap nro_map(map_address, context->process_handle, base_address, nro_size); + if (!nro_map.IsSuccess()) { + return nro_map.GetResult(); + } + + /* Validate header. */ + const Registration::NroHeader *header = reinterpret_cast(map_address); + if (header->magic != MagicNro0) { + return ResultRoInvalidNro; + } + if (header->nro_size != nro_size || header->bss_size != bss_size) { + return ResultRoInvalidNro; + } + if ((header->text_size & 0xFFF) || (header->ro_size & 0xFFF) || (header->rw_size & 0xFFF) || (header->bss_size & 0xFFF)) { + return ResultRoInvalidNro; + } + if (header->text_offset > header->ro_offset || header->ro_offset > header->rw_offset) { + return ResultRoInvalidNro; + } + if (header->text_offset != 0 || header->text_offset + header->text_size != header->ro_offset || header->ro_offset + header->ro_size != header->rw_offset || header->rw_offset + header->rw_size != header->nro_size) { + return ResultRoInvalidNro; + } + + /* Verify NRO hash. */ + { + Sha256Hash hash; + sha256CalculateHash(&hash, header, nro_size); + if (!IsNroHashPresent(context, &hash)) { + return ResultRoNotAuthorized; + } + } + + ModuleId module_id; + std::memcpy(&module_id, header->build_id, sizeof(module_id)); + + /* Check if NRO has already been loaded. */ + for (size_t i = 0; i < Registration::MaxNroInfos; i++) { + if (context->nro_in_use[i]) { + if (std::memcmp(&context->nro_infos[i].module_id, &module_id, sizeof(module_id)) == 0) { + return ResultRoAlreadyLoaded; + } + } + } + + /* Apply patches to NRO. */ + PatchUtils::ApplyPatches(&module_id, reinterpret_cast(map_address), nro_size); + + *out_module_id = module_id; + *out_rx_size = header->text_size; + *out_ro_size = header->ro_size; + *out_rw_size = header->rw_size; + return ResultSuccess; +} + +Result Registration::SetNroPerms(Handle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size) { + Result rc; + const u64 rx_offset = 0; + const u64 ro_offset = rx_offset + rx_size; + const u64 rw_offset = ro_offset + ro_size; + + if (R_FAILED((rc = svcSetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, 5)))) { + return rc; + } + if (R_FAILED((rc = svcSetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, 1)))) { + return rc; + } + if (R_FAILED((rc = svcSetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, 3)))) { + return rc; + } + + return ResultSuccess; +} + +Result Registration::UnloadNro(RoProcessContext *context, u64 nro_address) { + /* Validate address. */ + if (nro_address & 0xFFF) { + return ResultRoInvalidAddress; + } + + /* Check the NRO is loaded. */ + size_t slot = 0; + for (slot = 0; slot < Registration::MaxNroInfos; slot++) { + if (!context->nro_in_use[slot]) { + continue; + } + + if (context->nro_infos[slot].base_address == nro_address) { + break; + } + } + if (slot == Registration::MaxNroInfos) { + return ResultRoNotLoaded; + } + + /* Unmap. */ + const NroInfo nro_info = context->nro_infos[slot]; + { + /* Nintendo does this unconditionally, whether or not the actual unmap succeeds. */ + context->nro_in_use[slot] = false; + std::memset(&context->nro_infos[slot], 0, sizeof(context->nro_infos[slot])); + } + return UnmapNro(context->process_handle, nro_info.base_address, nro_info.nro_heap_address, nro_info.bss_heap_address, nro_info.bss_heap_size, nro_info.code_size, nro_info.rw_size); +} + +Result Registration::MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size, RoModuleType expected_type, bool enforce_type) { + Result rc; + MappedCodeMemory nrr_mcm; + + /* First, map the NRR. */ + if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(nrr_mcm, process_handle, true, nrr_heap_address, nrr_heap_size)))) { + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + /* Try mapping as 32-bit, since we might have guessed wrong on < 3.0.0. */ + rc = MapUtils::MapCodeMemoryForProcess(nrr_mcm, process_handle, false, nrr_heap_address, nrr_heap_size); + } + if (R_FAILED(rc)) { + return rc; + } + } + + const u64 code_address = nrr_mcm.GetDstAddress(); + u64 map_address; + if (R_FAILED(MapUtils::LocateSpaceForMap(&map_address, nrr_heap_size))) { + return ResultRoInsufficientAddressSpace; + } + + /* Nintendo...does not check the return value of this map. We will check, instead of aborting if it fails. */ + AutoCloseMap nrr_map(map_address, process_handle, code_address, nrr_heap_size); + if (!nrr_map.IsSuccess()) { + return nrr_map.GetResult(); + } + + NrrHeader *nrr_header = reinterpret_cast(map_address); + if (R_FAILED((rc = NrrUtils::ValidateNrr(nrr_header, nrr_heap_size, title_id, expected_type, enforce_type)))) { + return rc; + } + + /* Invalidation here actually prevents them from unmapping at scope exit. */ + nrr_map.Invalidate(); + nrr_mcm.Invalidate(); + + *out_header = nrr_header; + *out_mapped_code_address = code_address; + return ResultSuccess; +} + +Result Registration::UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address) { + Result rc = svcUnmapProcessMemory((void *)header, process_handle, mapped_code_address, nrr_heap_size); + if (R_FAILED(rc)) { + return rc; + } + + return svcUnmapProcessCodeMemory(process_handle, mapped_code_address, nrr_heap_address, nrr_heap_size); +} + +Result Registration::MapNro(u64 *out_base_address, Handle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) { + Result rc; + MappedCodeMemory nro_mcm; + MappedCodeMemory bss_mcm; + u64 base_address; + + /* Map the NRO, and map the BSS immediately after it. */ + size_t i = 0; + for (i = 0; i < MapUtils::LocateRetryCount; i++) { + MappedCodeMemory tmp_nro_mcm; + bool is_64_bit = true; + if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(tmp_nro_mcm, process_handle, is_64_bit, nro_heap_address, nro_heap_size)))) { + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + /* Try mapping as 32-bit, since we might have guessed wrong on < 3.0.0. */ + is_64_bit = false; + rc = MapUtils::MapCodeMemoryForProcess(tmp_nro_mcm, process_handle, is_64_bit, nro_heap_address, nro_heap_size); + } + if (R_FAILED(rc)) { + return rc; + } + } + base_address = tmp_nro_mcm.GetDstAddress(); + + if (bss_heap_size > 0) { + MappedCodeMemory tmp_bss_mcm(process_handle, base_address + nro_heap_size, bss_heap_address, bss_heap_size); + rc = tmp_bss_mcm.GetResult(); + if (rc == ResultKernelInvalidMemoryState) { + continue; + } + if (R_FAILED(rc)) { + return rc; + } + + if (!MapUtils::CanAddGuardRegions(process_handle, base_address, nro_heap_size + bss_heap_size)) { + continue; + } + + bss_mcm = std::move(tmp_bss_mcm); + } else { + if (!MapUtils::CanAddGuardRegions(process_handle, base_address, nro_heap_size)) { + continue; + } + } + nro_mcm = std::move(tmp_nro_mcm); + break; + } + if (i == MapUtils::LocateRetryCount) { + return ResultRoInsufficientAddressSpace; + } + + /* Invalidation here actually prevents them from unmapping at scope exit. */ + nro_mcm.Invalidate(); + bss_mcm.Invalidate(); + + *out_base_address = base_address; + return ResultSuccess; +} + +Result Registration::UnmapNro(Handle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size) { + Result rc; + + /* First, unmap bss. */ + if (bss_heap_size > 0) { + if (R_FAILED((rc = svcUnmapProcessCodeMemory(process_handle, base_address + code_size + rw_size, bss_heap_address, bss_heap_size)))) { + return rc; + } + } + + /* Next, unmap .rwdata */ + if (rw_size > 0) { + if (R_FAILED((rc = svcUnmapProcessCodeMemory(process_handle, base_address + code_size, nro_heap_address + code_size, rw_size)))) { + return rc; + } + } + + /* Finally, unmap .text + .rodata. */ + if (R_FAILED((rc = svcUnmapProcessCodeMemory(process_handle, base_address, nro_heap_address, code_size)))) { + return rc; + } + + return ResultSuccess; +} diff --git a/stratosphere/ro/source/ro_registration.hpp b/stratosphere/ro/source/ro_registration.hpp new file mode 100644 index 000000000..f416534f1 --- /dev/null +++ b/stratosphere/ro/source/ro_registration.hpp @@ -0,0 +1,102 @@ +/* + * 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 "ro_nrr.hpp" +#include "ro_types.hpp" + +class Registration { + public: + /* NOTE: 2 ldr:ro, 2 ro:1. Nintendo only actually supports 2 total, but we'll be a little more generous. */ + static constexpr size_t MaxSessions = 0x4; + static constexpr size_t MaxNrrInfos = 0x40; + static constexpr size_t MaxNroInfos = 0x40; + + static constexpr u32 MagicNro0 = 0x304F524E; + public: + struct NroHeader { + u32 entrypoint_insn; + u32 mod_offset; + u64 padding; + u32 magic; + u32 _0x14; + u32 nro_size; + u32 _0x1C; + u32 text_offset; + u32 text_size; + u32 ro_offset; + u32 ro_size; + u32 rw_offset; + u32 rw_size; + u32 bss_size; + u32 _0x3C; + unsigned char build_id[0x20]; + u8 _0x60[0x20]; + }; + static_assert(sizeof(NroHeader) == 0x80, "NroHeader definition!"); + + struct NroInfo { + u64 base_address; + u64 nro_heap_address; + u64 nro_heap_size; + u64 bss_heap_address; + u64 bss_heap_size; + u64 code_size; + u64 rw_size; + ModuleId module_id; + }; + struct NrrInfo { + NrrHeader *header; + u64 nrr_heap_address; + u64 nrr_heap_size; + u64 mapped_code_address; + }; + struct RoProcessContext { + bool nro_in_use[MaxNroInfos]; + bool nrr_in_use[MaxNrrInfos]; + NroInfo nro_infos[MaxNroInfos]; + NrrInfo nrr_infos[MaxNrrInfos]; + Handle process_handle; + u64 process_id; + bool in_use; + }; + private: + static Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size, RoModuleType expected_type, bool enforce_type); + static Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address); + static bool IsNroHashPresent(RoProcessContext *context, const Sha256Hash *hash); + + static Result MapNro(u64 *out_base_address, Handle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size); + static Result ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, RoProcessContext *context, u64 base_address, u64 nro_size, u64 bss_size); + static Result SetNroPerms(Handle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size); + static Result UnmapNro(Handle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size); + public: + static void Initialize(); + static bool ShouldEaseNroRestriction(); + + static Result RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id); + static void UnregisterProcess(RoProcessContext *context); + + static Result LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_address, u64 nrr_size, RoModuleType expected_type, bool enforce_type); + static Result UnloadNrr(RoProcessContext *context, u64 nrr_address); + static Result LoadNro(u64 *out_address, RoProcessContext *context, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size); + static Result UnloadNro(RoProcessContext *context, u64 nro_address); + + static Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id); +}; \ No newline at end of file diff --git a/stratosphere/ro/source/ro_service.cpp b/stratosphere/ro/source/ro_service.cpp new file mode 100644 index 000000000..0b0a9e7c6 --- /dev/null +++ b/stratosphere/ro/source/ro_service.cpp @@ -0,0 +1,108 @@ +/* + * 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 . + */ + +#include +#include +#include +#include + +#include "ro_service.hpp" +#include "ro_registration.hpp" + +RelocatableObjectsService::~RelocatableObjectsService() { + if (this->IsInitialized()) { + Registration::UnregisterProcess(this->context); + this->context = nullptr; + } +} + +bool RelocatableObjectsService::IsProcessIdValid(u64 process_id) { + if (!this->IsInitialized()) { + return false; + } + + return this->context->process_id == process_id; +} + +u64 RelocatableObjectsService::GetTitleId(Handle process_handle) { + u64 title_id = 0; + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) { + /* 3.0.0+: Use svcGetInfo. */ + if (R_FAILED(svcGetInfo(&title_id, 18, process_handle, 0))) { + std::abort(); + } + } else { + /* 1.0.0-2.3.0: We're not inside loader, so ask pm. */ + u64 process_id = 0; + if (R_FAILED(svcGetProcessId(&process_id, process_handle))) { + std::abort(); + } + if (R_FAILED(pminfoGetTitleId(&title_id, process_id))) { + std::abort(); + } + } + return title_id; +} + +Result RelocatableObjectsService::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::LoadNro(load_address.GetPointer(), this->context, nro_address, nro_size, bss_address, bss_size); +} + +Result RelocatableObjectsService::UnloadNro(PidDescriptor pid_desc, u64 nro_address) { + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::UnloadNro(this->context, nro_address); +} + +Result RelocatableObjectsService::LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) { + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::LoadNrr(this->context, GetTitleId(this->context->process_handle), nrr_address, nrr_size, RoModuleType_ForSelf, true); +} + +Result RelocatableObjectsService::UnloadNrr(PidDescriptor pid_desc, u64 nrr_address) { + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::UnloadNrr(this->context, nrr_address); +} + +Result RelocatableObjectsService::Initialize(PidDescriptor pid_desc, CopiedHandle process_h) { + /* Validate the input pid/process handle. */ + u64 handle_pid = 0; + if (R_FAILED(svcGetProcessId(&handle_pid, process_h.handle)) || handle_pid != pid_desc.pid) { + return ResultRoInvalidProcess; + } + + return Registration::RegisterProcess(&this->context, process_h.handle, pid_desc.pid); +} + +Result RelocatableObjectsService::LoadNrrEx(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size, CopiedHandle process_h) { + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::LoadNrr(this->context, GetTitleId(process_h.handle), nrr_address, nrr_size, this->type, this->type == RoModuleType_ForOthers); +} \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_ro_service.hpp b/stratosphere/ro/source/ro_service.hpp similarity index 77% rename from stratosphere/loader/source/ldr_ro_service.hpp rename to stratosphere/ro/source/ro_service.hpp index f5cc4b62d..6354d0805 100644 --- a/stratosphere/loader/source/ldr_ro_service.hpp +++ b/stratosphere/ro/source/ro_service.hpp @@ -18,7 +18,8 @@ #include #include -#include "ldr_registration.hpp" + +#include "ro_registration.hpp" enum RoServiceCmd { Ro_Cmd_LoadNro = 0, @@ -30,17 +31,20 @@ enum RoServiceCmd { }; class RelocatableObjectsService final : public IServiceObject { - Handle process_handle = 0; - u64 process_id = U64_MAX; - bool has_initialized = false; + private: + Registration::RoProcessContext *context = nullptr; + RoModuleType type; public: - virtual ~RelocatableObjectsService() override { - Registration::CloseRoService(this, this->process_handle); - if (this->has_initialized) { - svcCloseHandle(this->process_handle); - } + explicit RelocatableObjectsService(RoModuleType t) : type(t) { + /* ... */ } - + virtual ~RelocatableObjectsService() override; + private: + bool IsInitialized() const { + return this->context != nullptr; + } + bool IsProcessIdValid(u64 process_id); + static u64 GetTitleId(Handle process_handle); private: /* Actual commands. */ Result LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size); @@ -56,5 +60,6 @@ class RelocatableObjectsService final : public IServiceObject { MakeServiceCommandMeta(), MakeServiceCommandMeta(), MakeServiceCommandMeta(), + MakeServiceCommandMeta(), }; }; diff --git a/stratosphere/ro/source/ro_types.hpp b/stratosphere/ro/source/ro_types.hpp new file mode 100644 index 000000000..b6473adb1 --- /dev/null +++ b/stratosphere/ro/source/ro_types.hpp @@ -0,0 +1,42 @@ +/* + * 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 + +struct ModuleId { + u8 build_id[0x20]; +}; +static_assert(sizeof(ModuleId) == sizeof(LoaderModuleInfo::build_id), "ModuleId definition!"); + +struct Sha256Hash { + u8 hash[0x20]; + + bool operator==(const Sha256Hash &o) const { + return std::memcmp(this, &o, sizeof(*this)) == 0; + } + bool operator!=(const Sha256Hash &o) const { + return std::memcmp(this, &o, sizeof(*this)) != 0; + } + bool operator<(const Sha256Hash &o) const { + return std::memcmp(this, &o, sizeof(*this)) < 0; + } + bool operator>(const Sha256Hash &o) const { + return std::memcmp(this, &o, sizeof(*this)) > 0; + } +}; +static_assert(sizeof(Sha256Hash) == sizeof(Sha256Hash::hash), "Sha256Hash definition!");