mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-05 19:51:45 +00:00
Loader: Add ldr:ro->LoadNro()
This commit is contained in:
parent
10171313df
commit
772e41971d
6 changed files with 288 additions and 25 deletions
|
@ -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};
|
||||
}
|
||||
};
|
|
@ -4,6 +4,7 @@
|
|||
#include <cstring>
|
||||
#include <picosha2.hpp>
|
||||
#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;
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
#include <switch.h>
|
||||
#include <cstdio>
|
||||
|
||||
#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);
|
||||
};
|
|
@ -1,7 +1,9 @@
|
|||
#include <switch.h>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#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) {
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
/* 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<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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
Loading…
Reference in a new issue