Loader: Add ldr:ro->LoadNro()

This commit is contained in:
Michael Scire 2018-04-27 03:17:07 -06:00
parent 10171313df
commit 772e41971d
6 changed files with 288 additions and 25 deletions

View file

@ -77,13 +77,16 @@ struct MappedCodeMemory {
void *mapped_address; void *mapped_address;
bool IsActive() { bool IsActive() {
return this->code_memory_address != 0;
}
bool IsMapped() {
return this->mapped_address != NULL; return this->mapped_address != NULL;
} }
/* Utility functions. */ /* Utility functions. */
Result Open(Handle process_h, bool is_64_bit_address_space, u64 address, u64 size) { Result Open(Handle process_h, bool is_64_bit_address_space, u64 address, u64 size) {
Result rc; Result rc;
u64 try_address;
if (this->IsActive()) { if (this->IsActive()) {
return 0x19009; return 0x19009;
} }
@ -92,39 +95,63 @@ struct MappedCodeMemory {
this->base_address = address; this->base_address = address;
this->size = size; this->size = size;
if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(process_h, is_64_bit_address_space, address, size, &this->code_memory_address)))) { if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(this->process_handle, is_64_bit_address_space, this->base_address, this->size, &this->code_memory_address)))) {
goto CODE_MEMORY_OPEN_END; Close();
}
if (R_FAILED(rc = MapUtils::LocateSpaceForMap(&try_address, size))) {
goto CODE_MEMORY_OPEN_END;
}
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};
} }
return rc; return rc;
} }
void Close() { Result OpenAtAddress(Handle process_h, u64 address, u64 size, u64 target_code_memory_address) {
Result rc;
if (this->IsActive()) { 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))) { if (R_FAILED(svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size))) {
/* TODO: panic(). */ /* 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))) { if (R_FAILED(svcUnmapProcessCodeMemory(this->process_handle, this->code_memory_address, this->base_address, this->size))) {
/* TODO: panic(). */ /* TODO: panic(). */
} }
}
*this = (const MappedCodeMemory){0}; *this = (const MappedCodeMemory){0};
} }
}
}; };

View file

@ -4,6 +4,7 @@
#include <cstring> #include <cstring>
#include <picosha2.hpp> #include <picosha2.hpp>
#include "ldr_nro.hpp" #include "ldr_nro.hpp"
#include "ldr_registration.hpp"
#include "ldr_map.hpp" #include "ldr_map.hpp"
#include "ldr_random.hpp" #include "ldr_random.hpp"
@ -26,3 +27,90 @@ Result NroUtils::ValidateNrrHeader(NrrHeader *header, u64 size, u64 title_id_min
return 0x0; 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;
}

View file

@ -2,6 +2,7 @@
#include <switch.h> #include <switch.h>
#include <cstdio> #include <cstdio>
#include "ldr_registration.hpp"
#define MAGIC_NRO0 0x304F524E #define MAGIC_NRO0 0x304F524E
#define MAGIC_NRR0 0x3052524E #define MAGIC_NRR0 0x3052524E
@ -27,7 +28,29 @@ class NroUtils {
u64 _0x348; 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(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 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

@ -1,7 +1,9 @@
#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;
@ -153,6 +155,74 @@ Result Registration::RemoveNrrInfo(u64 index, u64 base_address) {
return 0xAA09; 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) { Result Registration::GetNsoInfosForProcessId(Registration::NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written) {
Registration::Process *target_process = GetProcessByProcessId(process_id); Registration::Process *target_process = GetProcessByProcessId(process_id);
if (target_process == NULL) { if (target_process == NULL) {

View file

@ -7,6 +7,7 @@
#define NSO_INFO_MAX (0x20) #define NSO_INFO_MAX (0x20)
#define NRR_INFO_MAX (0x40) #define NRR_INFO_MAX (0x40)
#define NRO_INFO_MAX (0x40)
class Registration { class Registration {
public: public:
@ -21,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;
@ -34,6 +49,7 @@ class Registration {
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];
Registration::NroInfo nro_infos[NRO_INFO_MAX];
MappedCodeMemory nrr_infos[NRR_INFO_MAX]; MappedCodeMemory nrr_infos[NRR_INFO_MAX];
void *owner_ro_service; void *owner_ro_service;
}; };
@ -55,5 +71,8 @@ class Registration {
static void CloseRoService(void *service, Handle process_h); static void CloseRoService(void *service, Handle process_h);
static Result AddNrrInfo(u64 index, MappedCodeMemory *nrr_info); static Result AddNrrInfo(u64 index, MappedCodeMemory *nrr_info);
static Result RemoveNrrInfo(u64 index, u64 base_address); 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); static Result GetNsoInfosForProcessId(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written);
}; };

View file

@ -34,8 +34,40 @@ Result RelocatableObjectsService::dispatch(IpcParsedCommand &r, IpcCommand &out_
std::tuple<Result, u64> RelocatableObjectsService::load_nro(PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { std::tuple<Result, u64> RelocatableObjectsService::load_nro(PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
/* TODO */ Result rc;
return std::make_tuple(0xF601, 0); 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) { std::tuple<Result> RelocatableObjectsService::unload_nro(PidDescriptor pid_desc, u64 nro_address) {
@ -71,6 +103,10 @@ std::tuple<Result> RelocatableObjectsService::load_nrr(PidDescriptor pid_desc, u
goto LOAD_NRR_END; 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); rc = NroUtils::ValidateNrrHeader((NroUtils::NrrHeader *)nrr_info.mapped_address, nrr_size, target_proc->title_id_min);
if (R_SUCCEEDED(rc)) { if (R_SUCCEEDED(rc)) {
Registration::AddNrrInfo(target_proc->index, &nrr_info); Registration::AddNrrInfo(target_proc->index, &nrr_info);