From 772e41971da571bd66e756f2debebbcd3d5b099f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 27 Apr 2018 03:17:07 -0600 Subject: [PATCH] Loader: Add ldr:ro->LoadNro() --- stratosphere/loader/source/ldr_map.hpp | 73 ++++++++++----- stratosphere/loader/source/ldr_nro.cpp | 88 +++++++++++++++++++ stratosphere/loader/source/ldr_nro.hpp | 23 +++++ .../loader/source/ldr_registration.cpp | 70 +++++++++++++++ .../loader/source/ldr_registration.hpp | 19 ++++ stratosphere/loader/source/ldr_ro_service.cpp | 40 ++++++++- 6 files changed, 288 insertions(+), 25 deletions(-) diff --git a/stratosphere/loader/source/ldr_map.hpp b/stratosphere/loader/source/ldr_map.hpp index 3a0c3885f..fb8fcc2fc 100644 --- a/stratosphere/loader/source/ldr_map.hpp +++ b/stratosphere/loader/source/ldr_map.hpp @@ -77,13 +77,16 @@ struct MappedCodeMemory { 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; - u64 try_address; if (this->IsActive()) { return 0x19009; } @@ -91,40 +94,64 @@ struct MappedCodeMemory { this->process_handle = process_h; this->base_address = address; this->size = size; - - if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(process_h, is_64_bit_address_space, address, size, &this->code_memory_address)))) { - goto CODE_MEMORY_OPEN_END; - } - - if (R_FAILED(rc = MapUtils::LocateSpaceForMap(&try_address, size))) { - goto CODE_MEMORY_OPEN_END; + + 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 0x19009; } + this->process_handle = process_h; + this->base_address = address; + this->size = size; - if (R_FAILED((rc = svcMapProcessMemory((void *)try_address, process_h, try_address, size)))) { - goto CODE_MEMORY_OPEN_END; - } - - this->mapped_address = (void *)try_address; - - CODE_MEMORY_OPEN_END: - if (R_FAILED(rc)) { - if (this->code_memory_address && R_FAILED(svcUnmapProcessCodeMemory(this->process_handle, this->code_memory_address, this->base_address, this->size))) { - /* TODO: panic(). */ - } - *this = (const MappedCodeMemory){0}; + 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; } - void Close() { - if (this->IsActive()) { + Result Map() { + Result rc; + u64 try_address; + if (this->IsMapped()) { + return 0x19009; + } + if (R_FAILED(rc = MapUtils::LocateSpaceForMap(&try_address, size))) { + return rc; + } + + if (R_FAILED((rc = svcMapProcessMemory((void *)try_address, this->process_handle, try_address, size)))) { + return rc; + } + + this->mapped_address = (void *)try_address; + return rc; + } + + void Unmap() { + if (this->IsMapped()) { if (R_FAILED(svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size))) { /* TODO: panic(). */ } + } + this->mapped_address = NULL; + } + + 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 = (const MappedCodeMemory){0}; } + *this = (const MappedCodeMemory){0}; } }; \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_nro.cpp b/stratosphere/loader/source/ldr_nro.cpp index 611223b16..3f95c7a50 100644 --- a/stratosphere/loader/source/ldr_nro.cpp +++ b/stratosphere/loader/source/ldr_nro.cpp @@ -4,6 +4,7 @@ #include #include #include "ldr_nro.hpp" +#include "ldr_registration.hpp" #include "ldr_map.hpp" #include "ldr_random.hpp" @@ -24,5 +25,92 @@ Result NroUtils::ValidateNrrHeader(NrrHeader *header, u64 size, u64 title_id_min return 0x6A09; } + return 0x0; +} + +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; + MappedCodeMemory mcm_nro; + MappedCodeMemory mcm_bss; + unsigned int i; + Result rc; + u8 nro_hash[0x20]; + /* Ensure there is an available NRO slot. */ + for (i = 0; i < NRO_INFO_MAX; i++) { + if (!target_proc->nro_infos[i].in_use) { + break; + } + } + if (i >= NRO_INFO_MAX) { + return 0x6E09; + } + 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, nro_heap_address + nro_heap_size))) { + break; + } else { + mcm_nro.Close(); + } + } + } + if (i >= 0x200) { + return 0x6609; + } + if (R_FAILED((rc = mcm_nro.Map()))) { + goto LOAD_NRO_END; + } + + nro = (NroHeader *)mcm_nro.mapped_address; + if (nro->magic != MAGIC_NRO0) { + rc = 0x6809; + goto LOAD_NRO_END; + } + if (nro->nro_size != nro_heap_size || nro->bss_size != bss_heap_size) { + rc = 0x6809; + goto LOAD_NRO_END; + } + if ((nro->text_size & 0xFFF) || (nro->ro_size & 0xFFF) || (nro->rw_size & 0xFFF) || (nro->bss_size & 0xFFF)) { + rc = 0x6809; + goto LOAD_NRO_END; + } + if (nro->text_offset != 0 || nro->text_offset + nro->text_size != nro->ro_offset || nro->ro_offset + nro->ro_size != nro->rw_offset || nro->rw_offset + nro->rw_size != nro->nro_size) { + rc = 0x6809; + goto LOAD_NRO_END; + } + + picosha2::hash256((u8 *)nro, (u8 *)nro + nro->nro_size, nro_hash, nro_hash + sizeof(nro_hash)); + + if (!Registration::IsNroHashPresent(target_proc->index, nro_hash)) { + rc = 0x6C09; + goto LOAD_NRO_END; + } + + if (Registration::IsNroAlreadyLoaded(target_proc->index, nro_hash)) { + rc = 0x7209; + goto LOAD_NRO_END; + } + + if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address, nro->text_size, 5)))) { + goto LOAD_NRO_END; + } + + if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address + nro->ro_offset, nro->ro_size, 1)))) { + goto LOAD_NRO_END; + } + + if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address + nro->rw_offset, nro->rw_size + nro->bss_size, 3)))) { + goto LOAD_NRO_END; + } + + Registration::AddNroToProcess(target_proc->index, &mcm_nro, &mcm_bss, nro->text_size, nro->ro_size, nro->rw_size, nro->build_id); + mcm_nro.Unmap(); + mcm_bss.Unmap(); + rc = 0x0; + +LOAD_NRO_END: + if (R_FAILED(rc)) { + mcm_nro.Close(); + mcm_bss.Close(); + } return 0x0; } \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_nro.hpp b/stratosphere/loader/source/ldr_nro.hpp index ef39d41ae..497278fad 100644 --- a/stratosphere/loader/source/ldr_nro.hpp +++ b/stratosphere/loader/source/ldr_nro.hpp @@ -2,6 +2,7 @@ #include #include +#include "ldr_registration.hpp" #define MAGIC_NRO0 0x304F524E #define MAGIC_NRR0 0x3052524E @@ -27,7 +28,29 @@ class NroUtils { 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_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index 900499ef2..2f9578fb9 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -1,7 +1,9 @@ #include #include #include +#include #include "ldr_registration.hpp" +#include "ldr_nro.hpp" static Registration::List g_registration_list = {0}; static u64 g_num_registered = 1; @@ -153,6 +155,74 @@ Result Registration::RemoveNrrInfo(u64 index, u64 base_address) { return 0xAA09; } + +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 < NRR_INFO_MAX; 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 < NRO_INFO_MAX; i++) { + if (target_process->nro_infos[i].in_use && std::memcmp(target_process->nro_infos[i].build_id, build_id, 0x20) == 0) { + 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; + } + + for (unsigned int i = 0; i < NRO_INFO_MAX; i++) { + if (!target_process->nro_infos[i].in_use) { + target_process->nro_infos[i].base_address = nro->code_memory_address; + target_process->nro_infos[i].nro_heap_address = nro->base_address; + target_process->nro_infos[i].nro_heap_size = nro->size; + target_process->nro_infos[i].bss_heap_address = bss->base_address; + target_process->nro_infos[i].bss_heap_size = bss->size; + target_process->nro_infos[i].text_size = text_size; + target_process->nro_infos[i].ro_size = ro_size; + target_process->nro_infos[i].rw_size = rw_size; + std::copy(build_id, build_id + sizeof(target_process->nro_infos[i].build_id), target_process->nro_infos[i].build_id); + target_process->nro_infos[i].in_use = true; + } + } +} + Result Registration::GetNsoInfosForProcessId(Registration::NsoInfo *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 f29d9e7b3..dc79995ec 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -7,6 +7,7 @@ #define NSO_INFO_MAX (0x20) #define NRR_INFO_MAX (0x40) +#define NRO_INFO_MAX (0x40) class Registration { public: @@ -21,6 +22,20 @@ class Registration { NsoInfo 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; @@ -34,6 +49,7 @@ class Registration { u64 title_id_min; Registration::TidSid tid_sid; Registration::NsoInfoHolder nso_infos[NSO_INFO_MAX]; + Registration::NroInfo nro_infos[NRO_INFO_MAX]; MappedCodeMemory nrr_infos[NRR_INFO_MAX]; void *owner_ro_service; }; @@ -55,5 +71,8 @@ class Registration { 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 GetNsoInfosForProcessId(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written); }; diff --git a/stratosphere/loader/source/ldr_ro_service.cpp b/stratosphere/loader/source/ldr_ro_service.cpp index d32c102d0..1e6b23206 100644 --- a/stratosphere/loader/source/ldr_ro_service.cpp +++ b/stratosphere/loader/source/ldr_ro_service.cpp @@ -34,8 +34,40 @@ Result RelocatableObjectsService::dispatch(IpcParsedCommand &r, IpcCommand &out_ std::tuple RelocatableObjectsService::load_nro(PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { - /* TODO */ - return std::make_tuple(0xF601, 0); + Result rc; + u64 out_address = 0; + Registration::Process *target_proc = NULL; + if (!this->has_initialized || this->process_id != pid_desc.pid) { + rc = 0xAE09; + goto LOAD_NRO_END; + } + if (nro_address & 0xFFF) { + rc = 0xA209; + goto LOAD_NRO_END; + } + if (nro_address + nro_size <= nro_address || !nro_size || (nro_size & 0xFFF)) { + rc = 0xA409; + goto LOAD_NRO_END; + } + if (bss_size && bss_address + bss_size <= bss_address) { + rc = 0xA409; + goto LOAD_NRO_END; + } + /* Ensure no overflow for combined sizes. */ + if (U64_MAX - nro_size < bss_size) { + rc = 0xA409; + goto LOAD_NRO_END; + } + 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 = 0xAC09; + goto LOAD_NRO_END; + } + target_proc->owner_ro_service = this; + + rc = NroUtils::LoadNro(target_proc, this->process_handle, nro_address, nro_size, bss_address, bss_size, &out_address); +LOAD_NRO_END: + return std::make_tuple(rc, out_address); } std::tuple RelocatableObjectsService::unload_nro(PidDescriptor pid_desc, u64 nro_address) { @@ -71,6 +103,10 @@ std::tuple RelocatableObjectsService::load_nrr(PidDescriptor pid_desc, u goto LOAD_NRR_END; } + if (R_FAILED((rc = nrr_info.Map()))) { + goto LOAD_NRR_END; + } + rc = NroUtils::ValidateNrrHeader((NroUtils::NrrHeader *)nrr_info.mapped_address, nrr_size, target_proc->title_id_min); if (R_SUCCEEDED(rc)) { Registration::AddNrrInfo(target_proc->index, &nrr_info);