Merge branch 'master' into patch-1

This commit is contained in:
Holbrad 2018-05-01 17:20:31 +01:00 committed by GitHub
commit be45a244ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 947 additions and 227 deletions

View File

@ -1,4 +1,5 @@
Atmosphère-NX
![Banner](img/banner.png?raw=true)
===== =====
![License](https://img.shields.io/badge/License-GPLv2-blue.svg) ![License](https://img.shields.io/badge/License-GPLv2-blue.svg)
@ -30,3 +31,17 @@ Atmosphère will want to run a customized Secure Monitor.
### Fusée (First-stage Loader) - In Progress ### Fusée (First-stage Loader) - In Progress
Atmosphère will need a loader -- this will be responsible for loading and validating stage 2 (custom TrustZone) plus package2 (Kernel/FIRM sysmodules), and patching them as needed. (This should implement all functionality normally in Package1loader/NX Bootloader). Atmosphère will need a loader -- this will be responsible for loading and validating stage 2 (custom TrustZone) plus package2 (Kernel/FIRM sysmodules), and patching them as needed. (This should implement all functionality normally in Package1loader/NX Bootloader).
=======
Credits
=====
Atmosphère is currently being developed and maintained by __SciresM__, __TuxSH__, __ktemkin__ and __hexkyz__.<br>
In no particular order, we credit the following for their invaluable contributions:
* __switchbrew__ for the [libnx](https://github.com/switchbrew/libnx) project and the extensive [documentation, research and tool development](http://switchbrew.org) pertaining to the Nintendo Switch.
* __devkitPro__ for the [devkitA64](https://devkitpro.org/) toolchain and libnx support.
* __ReSwitched Team__ for additional [documentation, research and tool development](https://reswitched.tech/) pertaining to the Nintendo Switch.
* __naehrwert__ for the [hekate](https://github.com/nwert/hekate) project and its hwinit code base.
* __hedgeberg__ for research and hardware testing.
* __lioncash__ for code cleanup and general improvements.
* _All those who actively contribute to the Atmosphère repository.

BIN
img/banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
img/banner_light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -40,14 +40,8 @@ void WaitableManager::process() {
handles.resize(signalables.size()); handles.resize(signalables.size());
std::transform(signalables.begin(), signalables.end(), handles.begin(), [](IWaitable *w) { return w->get_handle(); }); std::transform(signalables.begin(), signalables.end(), handles.begin(), [](IWaitable *w) { return w->get_handle(); });
unsigned int num_not_deferred = 0;
for (auto & signalable : signalables) {
if (!signalable->get_deferred()) {
num_not_deferred++;
}
}
rc = svcWaitSynchronization(&handle_index, handles.data(), num_not_deferred, this->timeout); rc = svcWaitSynchronization(&handle_index, handles.data(), signalables.size(), this->timeout);
if (R_SUCCEEDED(rc)) { if (R_SUCCEEDED(rc)) {
/* Handle a signaled waitable. */ /* Handle a signaled waitable. */
/* TODO: What timeout should be passed here? */ /* TODO: What timeout should be passed here? */
@ -82,12 +76,19 @@ void WaitableManager::process() {
delete signalables[handle_index]; delete signalables[handle_index];
} }
/* If relevant, remove from signalables. */
signalables.erase(std::remove(signalables.begin(), signalables.end(), signalables[handle_index]), signalables.end());
for (int i = 0; i < handle_index; i++) { for (int i = 0; i < handle_index; i++) {
signalables[i]->update_priority(); signalables[i]->update_priority();
} }
} }
/* Do deferred callback for each waitable. */ /* Do deferred callback for each waitable. */
std::for_each(signalables.begin() + num_not_deferred, signalables.end(), [](IWaitable *w) { w->handle_deferred(); }); for (auto & waitable : signalables) {
if (waitable->get_deferred()) {
waitable->handle_deferred();
}
}
} }
} }

View File

@ -41,7 +41,7 @@ std::tuple<Result, u32> DebugMonitorService::get_nso_info(u64 pid, OutPointerWit
std::fill(out.pointer, out.pointer + out.num_elements, (const Registration::NsoInfo){0}); std::fill(out.pointer, out.pointer + out.num_elements, (const Registration::NsoInfo){0});
Result rc = Registration::get_nso_infos_for_process_id(out.pointer, out.num_elements, pid, &out_num_nsos); Result rc = Registration::GetNsoInfosForProcessId(out.pointer, out.num_elements, pid, &out_num_nsos);
return std::make_tuple(rc, out_num_nsos); return std::make_tuple(rc, out_num_nsos);
} }

View File

@ -9,6 +9,7 @@
#include "ldr_process_manager.hpp" #include "ldr_process_manager.hpp"
#include "ldr_debug_monitor.hpp" #include "ldr_debug_monitor.hpp"
#include "ldr_shell.hpp" #include "ldr_shell.hpp"
#include "ldr_ro_service.hpp"
extern "C" { extern "C" {
extern u32 __start__; extern u32 __start__;
@ -83,6 +84,10 @@ int main(int argc, char **argv)
server_manager->add_waitable(new ServiceServer<ProcessManagerService>("dbg:pm", 1)); server_manager->add_waitable(new ServiceServer<ProcessManagerService>("dbg:pm", 1));
server_manager->add_waitable(new ServiceServer<ShellService>("dbg:shel", 3)); server_manager->add_waitable(new ServiceServer<ShellService>("dbg:shel", 3));
server_manager->add_waitable(new ServiceServer<DebugMonitorService>("dbg:dmnt", 2)); server_manager->add_waitable(new ServiceServer<DebugMonitorService>("dbg:dmnt", 2));
if (!kernelAbove300()) {
/* On 1.0.0-2.3.0, Loader services ldr:ro instead of ro. */
server_manager->add_waitable(new ServiceServer<RelocatableObjectsService>("dbg:ro", 0x20));
}
/* Loop forever, servicing our services. */ /* Loop forever, servicing our services. */
server_manager->process(); server_manager->process();

View File

@ -1,6 +1,7 @@
#include <switch.h> #include <switch.h>
#include "ldr_map.hpp" #include "ldr_map.hpp"
#include "ldr_random.hpp"
Result MapUtils::LocateSpaceForMap(u64 *out, u64 out_size) { Result MapUtils::LocateSpaceForMap(u64 *out, u64 out_size) {
if (kernelAbove200()) { if (kernelAbove200()) {
@ -11,38 +12,26 @@ 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) { Result MapUtils::LocateSpaceForMapModern(u64 *out, u64 out_size) {
MemoryInfo mem_info = {0}; MemoryInfo mem_info = {0};
AddressSpaceInfo address_space = {0};
u32 page_info = 0; u32 page_info = 0;
u64 heap_base = 0, heap_size = 0, heap_end = 0;
u64 map_base = 0, map_size = 0, map_end = 0;
u64 addspace_base = 0, addspace_size = 0, addspace_end = 0;
u64 cur_base = 0, cur_end = 0; u64 cur_base = 0, cur_end = 0;
Result rc; Result rc;
if (R_FAILED((rc = svcGetInfo(&heap_base, 4, CUR_PROCESS_HANDLE, 0)))) { if (R_FAILED((rc = GetAddressSpaceInfo(&address_space, CUR_PROCESS_HANDLE)))) {
return rc; return rc;
} }
if (R_FAILED((rc = svcGetInfo(&heap_size, 5, CUR_PROCESS_HANDLE, 0)))) {
return rc;
}
if (R_FAILED((rc = svcGetInfo(&map_base, 2, CUR_PROCESS_HANDLE, 0)))) {
return rc;
}
if (R_FAILED((rc = svcGetInfo(&map_size, 3, CUR_PROCESS_HANDLE, 0)))) {
return rc;
}
if (R_FAILED((rc = svcGetInfo(&addspace_base, 12, CUR_PROCESS_HANDLE, 0)))) {
return rc;
}
if (R_FAILED((rc = svcGetInfo(&addspace_size, 13, CUR_PROCESS_HANDLE, 0)))) {
return rc;
}
heap_end = heap_base + heap_size;
map_end = map_base + map_size;
addspace_end = addspace_base + addspace_size;
cur_base = addspace_base; cur_base = address_space.addspace_base;
rc = 0xD001; rc = 0xD001;
cur_end = cur_base + out_size; cur_end = cur_base + out_size;
@ -50,11 +39,20 @@ Result MapUtils::LocateSpaceForMapModern(u64 *out, u64 out_size) {
return rc; return rc;
} }
if (heap_size) {
if (map_size) {
while (true) { while (true) {
if (cur_end - 1 < heap_base || heap_end - 1 < cur_base) { if (address_space.heap_size && (address_space.heap_base <= cur_end - 1 && cur_base <= address_space.heap_end - 1)) {
if (cur_end - 1 < map_base || map_end - 1 < cur_base) { /* 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))) { if (R_FAILED(svcQueryMemory(&mem_info, &page_info, cur_base))) {
/* TODO: panic. */ /* TODO: panic. */
} }
@ -66,82 +64,15 @@ Result MapUtils::LocateSpaceForMapModern(u64 *out, u64 out_size) {
return rc; return rc;
} }
cur_base = mem_info.addr + mem_info.size; cur_base = mem_info.addr + mem_info.size;
if (cur_base >= addspace_end) { if (cur_base >= address_space.addspace_end) {
return rc; return rc;
} }
} else {
if (map_end == cur_base) {
return rc;
}
cur_base = map_end;
}
} else {
if (heap_end == cur_base) {
return rc;
}
cur_base = heap_end;
} }
cur_end = cur_base + out_size; cur_end = cur_base + out_size;
if (cur_base + out_size <= cur_base) { if (cur_base + out_size <= cur_base) {
return rc; return rc;
} }
} }
} else {
while (true) {
if (cur_end - 1 < heap_base || heap_end - 1 < cur_base) {
if (R_FAILED(svcQueryMemory(&mem_info, &page_info, cur_base))) {
/* TODO: panic. */
}
if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) {
*out = cur_base;
return 0x0;
}
if (mem_info.addr + mem_info.size <= cur_base) {
return rc;
}
cur_base = mem_info.addr + mem_info.size;
if (cur_base >= addspace_end) {
return rc;
}
} else {
if (heap_end == cur_base) {
return rc;
}
cur_base = heap_end;
}
cur_end = cur_base + out_size;
if (cur_base + out_size <= cur_base) {
return rc;
}
}
}
} else {
while (cur_end > cur_base) {
if (map_size && cur_end - 1 >= map_base && map_end - 1 >= cur_base) {
if (cur_base == map_end) {
return rc;
}
cur_base = map_end;
} else {
if (R_FAILED(svcQueryMemory(&mem_info, &page_info, cur_base))) {
/* TODO: panic. */
}
if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) {
*out = cur_base;
return 0x0;
}
if (mem_info.addr + mem_info.size <= cur_base) {
return rc;
}
cur_base = mem_info.addr + mem_info.size;
if (cur_base >= addspace_end) {
return rc;
}
}
cur_end = cur_base + out_size;
}
}
return rc;
} }
@ -178,3 +109,93 @@ Result MapUtils::LocateSpaceForMapDeprecated(u64 *out, u64 out_size) {
} }
return rc; return rc;
} }
Result MapUtils::MapCodeMemoryForProcessModern(Handle process_h, u64 base_address, u64 size, u64 *out_code_memory_address) {
AddressSpaceInfo address_space = {0};
Result rc;
if (R_FAILED((rc = GetAddressSpaceInfo(&address_space, CUR_PROCESS_HANDLE)))) {
return rc;
}
if (size > address_space.addspace_size) {
return 0x6609;
}
u64 try_address;
for (unsigned int i = 0; i < 0x200; i++) {
while (true) {
try_address = address_space.addspace_base + (RandomUtils::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 != 0xD401) {
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 0x6609;
}
u64 try_address;
for (unsigned int i = 0; i < 0x200; i++) {
try_address = addspace_base + (RandomUtils::GetRandomU64((u64)(addspace_size - size) >> 12) << 12);
rc = svcMapProcessCodeMemory(process_h, try_address, base_address, size);
if (rc != 0xD401) {
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, CUR_PROCESS_HANDLE, 0)))) {
return rc;
}
if (R_FAILED((rc = svcGetInfo(&out->heap_size, 5, CUR_PROCESS_HANDLE, 0)))) {
return rc;
}
if (R_FAILED((rc = svcGetInfo(&out->map_base, 2, CUR_PROCESS_HANDLE, 0)))) {
return rc;
}
if (R_FAILED((rc = svcGetInfo(&out->map_size, 3, CUR_PROCESS_HANDLE, 0)))) {
return rc;
}
if (R_FAILED((rc = svcGetInfo(&out->addspace_base, 12, CUR_PROCESS_HANDLE, 0)))) {
return rc;
}
if (R_FAILED((rc = svcGetInfo(&out->addspace_size, 13, CUR_PROCESS_HANDLE, 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 0;
}

View File

@ -3,7 +3,155 @@
class MapUtils { class MapUtils {
public: 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;
};
static Result GetAddressSpaceInfo(AddressSpaceInfo *out, Handle process_h);
static Result LocateSpaceForMapDeprecated(u64 *out, u64 out_size); static Result LocateSpaceForMapDeprecated(u64 *out, u64 out_size);
static Result LocateSpaceForMapModern(u64 *out, u64 out_size); static Result LocateSpaceForMapModern(u64 *out, u64 out_size);
static Result LocateSpaceForMap(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 {
private:
void *mapped_address;
u64 base_address;
u64 size;
Handle process_handle;
public:
AutoCloseMap() : mapped_address(0), base_address(0), size(0), process_handle(0) { };
~AutoCloseMap() {
Close();
}
void *GetMappedAddress() {
return this->mapped_address;
}
Result Open(Handle process_h, u64 address, u64 size) {
Result rc;
u64 try_address;
if (R_FAILED(rc = MapUtils::LocateSpaceForMap(&try_address, size))) {
return rc;
}
if (R_FAILED((rc = svcMapProcessMemory((void *)try_address, process_h, try_address, size)))) {
return rc;
}
this->mapped_address = (void *)try_address;
this->process_handle = process_h;
this->base_address = address;
this->size = size;
return 0;
}
void Close() {
if (this->mapped_address) {
if (R_FAILED(svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size))) {
/* TODO: panic(). */
}
this->mapped_address = NULL;
}
}
};
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 0x19009;
}
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 0x19009;
}
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 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};
}
}; };

View File

@ -0,0 +1,117 @@
#include <switch.h>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <picosha2.hpp>
#include "ldr_nro.hpp"
#include "ldr_registration.hpp"
#include "ldr_map.hpp"
#include "ldr_random.hpp"
Result NroUtils::ValidateNrrHeader(NrrHeader *header, u64 size, u64 title_id_min) {
if (header->magic != MAGIC_NRR0) {
return 0x6A09;
}
if (header->nrr_size != size) {
return 0xA409;
}
/* TODO: Check NRR signature. */
if (false) {
return 0x6C09;
}
if (header->title_id_min != 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);
*out_address = mcm_nro.code_memory_address;
mcm_nro.Unmap();
mcm_bss.Unmap();
rc = 0x0;
LOAD_NRO_END:
if (R_FAILED(rc)) {
mcm_nro.Close();
mcm_bss.Close();
}
return 0x0;
}

View File

@ -0,0 +1,56 @@
#pragma once
#include <switch.h>
#include <cstdio>
#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);
};

View File

@ -224,16 +224,12 @@ Result NsoUtils::LoadNsosIntoProcessMemory(Handle process_h, u64 title_id, NsoLo
Result rc = 0xA09; Result rc = 0xA09;
for (unsigned int i = 0; i < NSO_NUM_MAX; i++) { for (unsigned int i = 0; i < NSO_NUM_MAX; i++) {
if (g_nso_present[i]) { if (g_nso_present[i]) {
u64 map_addr = 0; AutoCloseMap nso_map;
if (R_FAILED((rc = MapUtils::LocateSpaceForMap(&map_addr, extents->nso_sizes[i])))) { if (R_FAILED((rc = nso_map.Open(process_h, extents->nso_addresses[i], extents->nso_sizes[i])))) {
return rc; return rc;
} }
u8 *map_base = (u8 *)map_addr; u8 *map_base = (u8 *)nso_map.GetMappedAddress();
if (R_FAILED((rc = svcMapProcessMemory(map_base, process_h, extents->nso_addresses[i], extents->nso_sizes[i])))) {
return rc;
}
FILE *f_nso = OpenNso(i, title_id); FILE *f_nso = OpenNso(i, title_id);
if (f_nso == NULL) { if (f_nso == NULL) {
@ -243,7 +239,6 @@ Result NsoUtils::LoadNsosIntoProcessMemory(Handle process_h, u64 title_id, NsoLo
for (unsigned int seg = 0; seg < 3; seg++) { for (unsigned int seg = 0; seg < 3; seg++) {
if (R_FAILED((rc = LoadNsoSegment(i, seg, f_nso, map_base, map_base + extents->nso_sizes[i])))) { if (R_FAILED((rc = LoadNsoSegment(i, seg, f_nso, map_base, map_base + extents->nso_sizes[i])))) {
fclose(f_nso); fclose(f_nso);
svcUnmapProcessMemory(map_base, process_h, extents->nso_addresses[i], extents->nso_sizes[i]);
return rc; return rc;
} }
} }
@ -262,9 +257,7 @@ Result NsoUtils::LoadNsosIntoProcessMemory(Handle process_h, u64 title_id, NsoLo
u64 bss_base = rw_base + g_nso_headers[i].segments[2].decomp_size, bss_size = g_nso_headers[i].segments[2].align_or_total_size; u64 bss_base = rw_base + g_nso_headers[i].segments[2].decomp_size, bss_size = g_nso_headers[i].segments[2].align_or_total_size;
std::fill(map_base + bss_base, map_base + bss_base + bss_size, 0); std::fill(map_base + bss_base, map_base + bss_base + bss_size, 0);
if (R_FAILED((rc = svcUnmapProcessMemory(map_base, process_h, extents->nso_addresses[i], extents->nso_sizes[i])))) { nso_map.Close();
return rc;
}
for (unsigned int seg = 0; seg < 3; seg++) { for (unsigned int seg = 0; seg < 3; seg++) {
u64 size = g_nso_headers[i].segments[seg].decomp_size; u64 size = g_nso_headers[i].segments[seg].decomp_size;
@ -283,25 +276,19 @@ Result NsoUtils::LoadNsosIntoProcessMemory(Handle process_h, u64 title_id, NsoLo
/* Map in arguments. */ /* Map in arguments. */
if (args != NULL && args_size) { if (args != NULL && args_size) {
u64 arg_map_addr = 0; AutoCloseMap args_map;
if (R_FAILED((rc = MapUtils::LocateSpaceForMap(&arg_map_addr, extents->args_size)))) { if (R_FAILED((rc = args_map.Open(process_h, extents->args_address, extents->args_size)))) {
return rc; return rc;
} }
NsoArgument *arg_map_base = (NsoArgument *)arg_map_addr; NsoArgument *arg_map_base = (NsoArgument *)args_map.GetMappedAddress();
if (R_FAILED((rc = svcMapProcessMemory(arg_map_base, process_h, extents->args_address, extents->args_size)))) {
return rc;
}
arg_map_base->allocated_space = extents->args_size; arg_map_base->allocated_space = extents->args_size;
arg_map_base->args_size = args_size; arg_map_base->args_size = args_size;
std::fill(arg_map_base->_0x8, arg_map_base->_0x8 + sizeof(arg_map_base->_0x8), 0); std::fill(arg_map_base->_0x8, arg_map_base->_0x8 + sizeof(arg_map_base->_0x8), 0);
std::copy(args, args + args_size, arg_map_base->arguments); std::copy(args, args + args_size, arg_map_base->arguments);
if (R_FAILED((rc = svcUnmapProcessMemory(arg_map_base, process_h, extents->args_address, extents->args_size)))) { args_map.Close();
return rc;
}
if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, extents->args_address, extents->args_size, 3)))) { if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, extents->args_address, extents->args_size, 3)))) {
return rc; return rc;

View File

@ -96,7 +96,7 @@ Result ProcessCreation::CreateProcess(Handle *out_process_h, u64 index, char *nc
Result rc; Result rc;
/* Get the process from the registration queue. */ /* Get the process from the registration queue. */
target_process = Registration::get_process(index); target_process = Registration::GetProcess(index);
if (target_process == NULL) { if (target_process == NULL) {
return 0x1009; return 0x1009;
} }
@ -174,10 +174,16 @@ Result ProcessCreation::CreateProcess(Handle *out_process_h, u64 index, char *nc
/* Update the list of registered processes with the new process. */ /* Update the list of registered processes with the new process. */
svcGetProcessId(&process_id, process_h); svcGetProcessId(&process_id, process_h);
Registration::set_process_id_and_tid_min(index, process_id, npdm_info.aci0->title_id); bool is_64_bit_addspace;
if (kernelAbove200()) {
is_64_bit_addspace = (((npdm_info.header->mmu_flags >> 1) & 5) | 2) == 3;
} else {
is_64_bit_addspace = (npdm_info.header->mmu_flags & 0xE) == 0x2;
}
Registration::SetProcessIdTidMinAndIs64BitAddressSpace(index, process_id, npdm_info.aci0->title_id, is_64_bit_addspace);
for (unsigned int i = 0; i < NSO_NUM_MAX; i++) { for (unsigned int i = 0; i < NSO_NUM_MAX; i++) {
if (NsoUtils::IsNsoPresent(i)) { if (NsoUtils::IsNsoPresent(i)) {
Registration::add_nso_info(index, nso_extents.nso_addresses[i], nso_extents.nso_sizes[i], NsoUtils::GetNsoBuildId(i)); Registration::AddNsoInfo(index, nso_extents.nso_addresses[i], nso_extents.nso_sizes[i], NsoUtils::GetNsoBuildId(i));
} }
} }

View File

@ -37,7 +37,7 @@ std::tuple<Result, MovedHandle> ProcessManagerService::create_process(u64 flags,
fprintf(stderr, "CreateProcess(%016lx, %016lx, %08x);\n", flags, index, reslimit_h.handle); fprintf(stderr, "CreateProcess(%016lx, %016lx, %08x);\n", flags, index, reslimit_h.handle);
rc = Registration::get_registered_tid_sid(index, &tid_sid); rc = Registration::GetRegisteredTidSid(index, &tid_sid);
if (R_FAILED(rc)) { if (R_FAILED(rc)) {
std::make_tuple(rc, MovedHandle{process_h}); std::make_tuple(rc, MovedHandle{process_h});
} }
@ -85,7 +85,7 @@ std::tuple<Result> ProcessManagerService::get_program_info(Registration::TidSid
std::tuple<Result, u64> ProcessManagerService::register_title(Registration::TidSid tid_sid) { std::tuple<Result, u64> ProcessManagerService::register_title(Registration::TidSid tid_sid) {
u64 out_index = 0; u64 out_index = 0;
if (Registration::register_tid_sid(&tid_sid, &out_index)) { if (Registration::RegisterTidSid(&tid_sid, &out_index)) {
return std::make_tuple(0, out_index); return std::make_tuple(0, out_index);
} else { } else {
return std::make_tuple(0xE09, out_index); return std::make_tuple(0xE09, out_index);
@ -93,7 +93,7 @@ std::tuple<Result, u64> ProcessManagerService::register_title(Registration::TidS
} }
std::tuple<Result> ProcessManagerService::unregister_title(u64 index) { std::tuple<Result> ProcessManagerService::unregister_title(u64 index) {
if (Registration::unregister_index(index)) { if (Registration::UnregisterIndex(index)) {
return std::make_tuple(0); return std::make_tuple(0);
} else { } else {
return std::make_tuple(0x1009); return std::make_tuple(0x1009);

View File

@ -1,14 +1,15 @@
#include <switch.h> #include <switch.h>
#include <algorithm> #include <algorithm>
#include <cstdio> #include <cstdio>
#include <cstring>
#include "ldr_registration.hpp" #include "ldr_registration.hpp"
#include "ldr_nro.hpp"
static Registration::List g_registration_list = {0}; static Registration::List g_registration_list = {0};
static u64 g_num_registered = 1; static u64 g_num_registered = 1;
Registration::Process *Registration::get_free_process() { Registration::Process *Registration::GetFreeProcess() {
unsigned int i; for (unsigned int i = 0; i < REGISTRATION_LIST_MAX; i++) {
for (i = 0; i < REGISTRATION_LIST_MAX; i++) {
if (!g_registration_list.processes[i].in_use) { if (!g_registration_list.processes[i].in_use) {
return &g_registration_list.processes[i]; return &g_registration_list.processes[i];
} }
@ -16,29 +17,35 @@ Registration::Process *Registration::get_free_process() {
return NULL; return NULL;
} }
Registration::Process *Registration::get_process(u64 index) { Registration::Process *Registration::GetProcess(u64 index) {
unsigned int i; for (unsigned int i = 0; i < REGISTRATION_LIST_MAX; i++) {
for (i = 0; i < REGISTRATION_LIST_MAX && (!g_registration_list.processes[i].in_use || g_registration_list.processes[i].index != index); i++) { if (g_registration_list.processes[i].in_use && g_registration_list.processes[i].index == index) {
}
if (i >= REGISTRATION_LIST_MAX) {
return NULL;
}
return &g_registration_list.processes[i]; return &g_registration_list.processes[i];
}
}
return NULL;
} }
Registration::Process *Registration::get_process_by_process_id(u64 pid) { Registration::Process *Registration::GetProcessByProcessId(u64 pid) {
unsigned int i; for (unsigned int i = 0; i < REGISTRATION_LIST_MAX; i++) {
for (i = 0; i < REGISTRATION_LIST_MAX && (!g_registration_list.processes[i].in_use || g_registration_list.processes[i].process_id != pid); i++) { if (g_registration_list.processes[i].in_use && g_registration_list.processes[i].process_id == pid) {
}
if (i >= REGISTRATION_LIST_MAX) {
return NULL;
}
return &g_registration_list.processes[i]; return &g_registration_list.processes[i];
}
}
return NULL;
} }
bool Registration::register_tid_sid(const TidSid *tid_sid, u64 *out_index) { Registration::Process *Registration::GetProcessByRoService(void *service) {
Registration::Process *free_process = get_free_process(); for (unsigned int i = 0; i < REGISTRATION_LIST_MAX; 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) { if (free_process == NULL) {
return false; return false;
} }
@ -52,8 +59,8 @@ bool Registration::register_tid_sid(const TidSid *tid_sid, u64 *out_index) {
return true; return true;
} }
bool Registration::unregister_index(u64 index) { bool Registration::UnregisterIndex(u64 index) {
Registration::Process *target_process = get_process(index); Registration::Process *target_process = GetProcess(index);
if (target_process == NULL) { if (target_process == NULL) {
return false; return false;
} }
@ -64,8 +71,8 @@ bool Registration::unregister_index(u64 index) {
} }
Result Registration::get_registered_tid_sid(u64 index, Registration::TidSid *out) { Result Registration::GetRegisteredTidSid(u64 index, Registration::TidSid *out) {
Registration::Process *target_process = get_process(index); Registration::Process *target_process = GetProcess(index);
if (target_process == NULL) { if (target_process == NULL) {
return 0x1009; return 0x1009;
} }
@ -75,18 +82,19 @@ Result Registration::get_registered_tid_sid(u64 index, Registration::TidSid *out
return 0; return 0;
} }
void Registration::set_process_id_and_tid_min(u64 index, u64 process_id, u64 tid_min) { void Registration::SetProcessIdTidMinAndIs64BitAddressSpace(u64 index, u64 process_id, u64 tid_min, bool is_64_bit_addspace) {
Registration::Process *target_process = get_process(index); Registration::Process *target_process = GetProcess(index);
if (target_process == NULL) { if (target_process == NULL) {
return; return;
} }
target_process->process_id = process_id; target_process->process_id = process_id;
target_process->title_id_min = tid_min; target_process->title_id_min = tid_min;
target_process->is_64_bit_addspace = is_64_bit_addspace;
} }
void Registration::add_nso_info(u64 index, u64 base_address, u64 size, const unsigned char *build_id) { void Registration::AddNsoInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id) {
Registration::Process *target_process = get_process(index); Registration::Process *target_process = GetProcess(index);
if (target_process == NULL) { if (target_process == NULL) {
return; return;
} }
@ -102,9 +110,144 @@ void Registration::add_nso_info(u64 index, u64 base_address, u64 size, const uns
} }
} }
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 < NRR_INFO_MAX; 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::get_nso_infos_for_process_id(Registration::NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written) { Result Registration::AddNrrInfo(u64 index, MappedCodeMemory *nrr_info) {
Registration::Process *target_process = get_process_by_process_id(process_id); Registration::Process *target_process = GetProcess(index);
if (target_process == NULL) {
/* TODO: panic() */
return 0x7009;
}
for (unsigned int i = 0; i < NRR_INFO_MAX; i++) {
if (!target_process->nrr_infos[i].IsActive()) {
target_process->nrr_infos[i] = *nrr_info;
return 0;
}
}
return 0x7009;
}
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 0x1009;
}
for (unsigned int i = 0; i < NRR_INFO_MAX; i++) {
if (target_process->nrr_infos[i].IsActive() && target_process->nrr_infos[i].base_address == base_address) {
target_process->nrr_infos[i].Close();
return 0;
}
}
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::RemoveNroInfo(u64 index, Handle process_h, u64 nro_heap_address) {
Registration::Process *target_process = GetProcess(index);
if (target_process == NULL) {
return 0xA809;
}
for (unsigned int i = 0; i < NRR_INFO_MAX; 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] = (const NroInfo ){0};
return rc;
}
}
return 0xA809;
}
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) { if (target_process == NULL) {
return 0x1009; return 0x1009;
} }

View File

@ -1,9 +1,13 @@
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include "ldr_map.hpp"
#define REGISTRATION_LIST_MAX (0x40) #define REGISTRATION_LIST_MAX (0x40)
#define NSO_INFO_MAX (0x20) #define NSO_INFO_MAX (0x20)
#define NRR_INFO_MAX (0x40)
#define NRO_INFO_MAX (0x40)
class Registration { class Registration {
public: public:
@ -18,6 +22,20 @@ class Registration {
NsoInfo info; 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 { struct TidSid {
u64 title_id; u64 title_id;
FsStorageId storage_id; FsStorageId storage_id;
@ -25,12 +43,15 @@ class Registration {
struct Process { struct Process {
bool in_use; bool in_use;
bool is_64_bit_addspace;
u64 index; u64 index;
u64 process_id; u64 process_id;
u64 title_id_min; u64 title_id_min;
Registration::TidSid tid_sid; Registration::TidSid tid_sid;
Registration::NsoInfoHolder nso_infos[NSO_INFO_MAX]; Registration::NsoInfoHolder nso_infos[NSO_INFO_MAX];
u64 _0x730; Registration::NroInfo nro_infos[NRO_INFO_MAX];
MappedCodeMemory nrr_infos[NRR_INFO_MAX];
void *owner_ro_service;
}; };
struct List { struct List {
@ -38,13 +59,21 @@ class Registration {
u64 num_processes; u64 num_processes;
}; };
static Registration::Process *get_free_process(); static Registration::Process *GetFreeProcess();
static Registration::Process *get_process(u64 index); static Registration::Process *GetProcess(u64 index);
static Registration::Process *get_process_by_process_id(u64 pid); static Registration::Process *GetProcessByProcessId(u64 pid);
static Result get_registered_tid_sid(u64 index, Registration::TidSid *out); static Registration::Process *GetProcessByRoService(void *service);
static bool register_tid_sid(const TidSid *tid_sid, u64 *out_index); static Result GetRegisteredTidSid(u64 index, Registration::TidSid *out);
static bool unregister_index(u64 index); static bool RegisterTidSid(const TidSid *tid_sid, u64 *out_index);
static void set_process_id_and_tid_min(u64 index, u64 process_id, u64 tid_min); static bool UnregisterIndex(u64 index);
static void add_nso_info(u64 index, u64 base_address, u64 size, const unsigned char *build_id); static void SetProcessIdTidMinAndIs64BitAddressSpace(u64 index, u64 process_id, u64 tid_min, bool is_64_bit_addspace);
static Result get_nso_infos_for_process_id(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written); static void AddNsoInfo(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 GetNsoInfosForProcessId(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written);
}; };

View File

@ -0,0 +1,168 @@
#include <switch.h>
#include <cstdio>
#include <algorithm>
#include "ldr_ro_service.hpp"
#include "ldr_registration.hpp"
#include "ldr_map.hpp"
#include "ldr_nro.hpp"
Result RelocatableObjectsService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) {
Result rc = 0xF601;
switch ((RoServiceCmd)cmd_id) {
case Ro_Cmd_LoadNro:
rc = WrapIpcCommandImpl<&RelocatableObjectsService::load_nro>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Ro_Cmd_UnloadNro:
rc = WrapIpcCommandImpl<&RelocatableObjectsService::unload_nro>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Ro_Cmd_LoadNrr:
rc = WrapIpcCommandImpl<&RelocatableObjectsService::load_nrr>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Ro_Cmd_UnloadNrr:
rc = WrapIpcCommandImpl<&RelocatableObjectsService::unload_nrr>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Ro_Cmd_Initialize:
rc = WrapIpcCommandImpl<&RelocatableObjectsService::initialize>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
default:
break;
}
return rc;
}
std::tuple<Result, u64> RelocatableObjectsService::load_nro(PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
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<Result> RelocatableObjectsService::unload_nro(PidDescriptor pid_desc, u64 nro_address) {
Registration::Process *target_proc = NULL;
if (!this->has_initialized || this->process_id != pid_desc.pid) {
return 0xAE09;
}
if (nro_address & 0xFFF) {
return 0xA209;
}
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 0xAC09;
}
target_proc->owner_ro_service = this;
return Registration::RemoveNroInfo(target_proc->index, this->process_handle, nro_address);
}
std::tuple<Result> RelocatableObjectsService::load_nrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) {
Result rc;
Registration::Process *target_proc = NULL;
MappedCodeMemory nrr_info = {0};
if (!this->has_initialized || this->process_id != pid_desc.pid) {
rc = 0xAE09;
goto LOAD_NRR_END;
}
if (nrr_address & 0xFFF) {
rc = 0xA209;
goto LOAD_NRR_END;
}
if (nrr_address + nrr_size <= nrr_address || !nrr_size || (nrr_size & 0xFFF)) {
rc = 0xA409;
goto LOAD_NRR_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_NRR_END;
}
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)))) {
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);
}
LOAD_NRR_END:
if (R_FAILED(rc)) {
if (nrr_info.IsActive()) {
nrr_info.Close();
}
}
return std::make_tuple(rc);
}
std::tuple<Result> RelocatableObjectsService::unload_nrr(PidDescriptor pid_desc, u64 nrr_address) {
Registration::Process *target_proc = NULL;
if (!this->has_initialized || this->process_id != pid_desc.pid) {
return 0xAE09;
}
if (nrr_address & 0xFFF) {
return 0xA209;
}
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 0xAC09;
}
target_proc->owner_ro_service = this;
return Registration::RemoveNrrInfo(target_proc->index, nrr_address);
}
std::tuple<Result> RelocatableObjectsService::initialize(PidDescriptor pid_desc, CopiedHandle process_h) {
u64 handle_pid;
Result rc = 0xAE09;
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;
rc = 0;
}
return std::make_tuple(rc);
}

View File

@ -0,0 +1,40 @@
#pragma once
#include <switch.h>
#include <stratosphere/iserviceobject.hpp>
#include "ldr_registration.hpp"
enum RoServiceCmd {
Ro_Cmd_LoadNro = 0,
Ro_Cmd_UnloadNro = 1,
Ro_Cmd_LoadNrr = 2,
Ro_Cmd_UnloadNrr = 3,
Ro_Cmd_Initialize = 4,
};
class RelocatableObjectsService : IServiceObject {
Handle process_handle;
u64 process_id;
bool has_initialized;
public:
RelocatableObjectsService() : process_handle(0), process_id(U64_MAX), has_initialized(false) { }
~RelocatableObjectsService() {
Registration::CloseRoService(this, this->process_handle);
if (this->has_initialized) {
svcCloseHandle(this->process_handle);
}
}
virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size);
virtual Result handle_deferred() {
/* This service will never defer. */
return 0;
}
private:
/* Actual commands. */
std::tuple<Result, u64> load_nro(PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size);
std::tuple<Result> unload_nro(PidDescriptor pid_desc, u64 nro_address);
std::tuple<Result> load_nrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size);
std::tuple<Result> unload_nrr(PidDescriptor pid_desc, u64 nrr_address);
std::tuple<Result> initialize(PidDescriptor pid_desc, CopiedHandle process_h);
};

View File

@ -4,7 +4,6 @@
Result ManagerService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) { Result ManagerService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) {
Result rc = 0xF601; Result rc = 0xF601;
switch ((ManagerServiceCmd)cmd_id) { switch ((ManagerServiceCmd)cmd_id) {
case Manager_Cmd_RegisterProcess: case Manager_Cmd_RegisterProcess:
rc = WrapIpcCommandImpl<&ManagerService::register_process>(this, r, out_c, pointer_buffer, pointer_buffer_size); rc = WrapIpcCommandImpl<&ManagerService::register_process>(this, r, out_c, pointer_buffer, pointer_buffer_size);

View File

@ -6,6 +6,15 @@
static Registration::Process g_process_list[REGISTRATION_LIST_MAX_PROCESS] = {0}; static Registration::Process g_process_list[REGISTRATION_LIST_MAX_PROCESS] = {0};
static Registration::Service g_service_list[REGISTRATION_LIST_MAX_SERVICE] = {0}; static Registration::Service g_service_list[REGISTRATION_LIST_MAX_SERVICE] = {0};
u64 GetServiceNameLength(u64 service) {
u64 service_name_len = 0;
while (service & 0xFF) {
service_name_len++;
service >>= 8;
}
return service_name_len;
}
/* Utilities. */ /* Utilities. */
Registration::Process *Registration::GetProcessForPid(u64 pid) { Registration::Process *Registration::GetProcessForPid(u64 pid) {
for (unsigned int i = 0; i < REGISTRATION_LIST_MAX_PROCESS; i++) { for (unsigned int i = 0; i < REGISTRATION_LIST_MAX_PROCESS; i++) {
@ -162,13 +171,10 @@ Result Registration::GetServiceForPid(u64 pid, u64 service, Handle *out) {
return 0xC15; return 0xC15;
} }
u64 service_name_len = 0; u64 service_name_len = GetServiceNameLength(service);
while ((service >> (8 * service_name_len)) & 0xFF) {
service_name_len++;
}
/* If the service has bytes after a null terminator, that's no good. */ /* If the service has bytes after a null terminator, that's no good. */
if ((service >> (8 * service_name_len))) { if (service_name_len != 8 && (service >> (8 * service_name_len))) {
return 0xC15; return 0xC15;
} }
@ -191,13 +197,10 @@ Result Registration::RegisterServiceForPid(u64 pid, u64 service, u64 max_session
return 0xC15; return 0xC15;
} }
u64 service_name_len = 0; u64 service_name_len = GetServiceNameLength(service);
while ((service >> (8 * service_name_len)) & 0xFF) {
service_name_len++;
}
/* If the service has bytes after a null terminator, that's no good. */ /* If the service has bytes after a null terminator, that's no good. */
if ((service >> (8 * service_name_len))) { if (service_name_len != 8 && (service >> (8 * service_name_len))) {
return 0xC15; return 0xC15;
} }
@ -240,13 +243,10 @@ Result Registration::RegisterServiceForSelf(u64 service, u64 max_sessions, bool
return rc; return rc;
} }
u64 service_name_len = 0; u64 service_name_len = GetServiceNameLength(service);
while ((service >> (8 * service_name_len)) & 0xFF) {
service_name_len++;
}
/* If the service has bytes after a null terminator, that's no good. */ /* If the service has bytes after a null terminator, that's no good. */
if ((service >> (8 * service_name_len))) { if (service_name_len != 8 && (service >> (8 * service_name_len))) {
return 0xC15; return 0xC15;
} }
@ -276,27 +276,13 @@ Result Registration::UnregisterServiceForPid(u64 pid, u64 service) {
return 0xC15; return 0xC15;
} }
u64 service_name_len = 0; u64 service_name_len = GetServiceNameLength(service);
while ((service >> (8 * service_name_len)) & 0xFF) {
service_name_len++;
}
/* If the service has bytes after a null terminator, that's no good. */ /* If the service has bytes after a null terminator, that's no good. */
if ((service >> (8 * service_name_len))) { if (service_name_len != 8 && (service >> (8 * service_name_len))) {
return 0xC15; return 0xC15;
} }
if (pid >= REGISTRATION_PID_BUILTIN_MAX) {
Registration::Process *proc = GetProcessForPid(pid);
if (proc == NULL) {
return 0x415;
}
if (!IsValidForSac(proc->sac, proc->sac_size, service, true)) {
return 0x1015;
}
}
Registration::Service *target_service = GetService(service); Registration::Service *target_service = GetService(service);
if (target_service == NULL) { if (target_service == NULL) {
return 0xE15; return 0xE15;

View File

@ -4,7 +4,7 @@
#define REGISTRATION_LIST_MAX_PROCESS (0x40) #define REGISTRATION_LIST_MAX_PROCESS (0x40)
#define REGISTRATION_LIST_MAX_SERVICE (0x100) #define REGISTRATION_LIST_MAX_SERVICE (0x100)
#define REGISTRATION_MAX_SAC_SIZE (0x200) #define REGISTRATION_MAX_SAC_SIZE (0x200)
#define REGISTRATION_PID_BUILTIN_MAX 0x7 #define REGISTRATION_PID_BUILTIN_MAX 0x50
class Registration { class Registration {
public: public:

View File

@ -5,7 +5,6 @@
Result UserService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) { Result UserService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) {
Result rc = 0xF601; Result rc = 0xF601;
switch ((UserServiceCmd)cmd_id) { switch ((UserServiceCmd)cmd_id) {
case User_Cmd_Initialize: case User_Cmd_Initialize:
rc = WrapIpcCommandImpl<&UserService::initialize>(this, r, out_c, pointer_buffer, pointer_buffer_size); rc = WrapIpcCommandImpl<&UserService::initialize>(this, r, out_c, pointer_buffer, pointer_buffer_size);
@ -27,7 +26,7 @@ Result UserService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id,
Result UserService::handle_deferred() { Result UserService::handle_deferred() {
/* If we're deferred, GetService failed. */ /* If we're deferred, GetService failed. */
return WrapDeferredIpcCommandImpl<&UserService::deferred_get_service>(this, this->deferred_service); return WrapDeferredIpcCommandImpl<&UserService::deferred_get_service>(this, this->deferred_service);;
} }

View File

@ -1,6 +1,6 @@
Thermoshère Thermosphère
===== =====
![License](https://img.shields.io/badge/License-GPLv2-blue.svg) ![License](https://img.shields.io/badge/License-GPLv2-blue.svg)
Thermoshère is a hypervisor for the Nintendo Switch. Thermosphère is a hypervisor for the Nintendo Switch.