From 73b6225d2eef26742b19d96ecaa7423504fb70a2 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 23 Apr 2018 20:05:22 -0600 Subject: [PATCH] Loader: Add Nso Header loading, loadset validation in CreateProcess --- stratosphere/loader/source/ldr_nso.cpp | 87 +++++++++++++++++++ stratosphere/loader/source/ldr_nso.hpp | 73 ++++++++++++++++ .../loader/source/ldr_process_creation.cpp | 18 +++- 3 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 stratosphere/loader/source/ldr_nso.cpp create mode 100644 stratosphere/loader/source/ldr_nso.hpp diff --git a/stratosphere/loader/source/ldr_nso.cpp b/stratosphere/loader/source/ldr_nso.cpp new file mode 100644 index 000000000..3fbcfba54 --- /dev/null +++ b/stratosphere/loader/source/ldr_nso.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include "ldr_nso.hpp" + +static NsoUtils::NsoHeader g_nso_headers[NSO_NUM_MAX] = {0}; +static bool g_nso_present[NSO_NUM_MAX] = {0}; + +void NsoUtils::GetNsoCodePath(char *content_path, unsigned int index) { + std::fill(content_path, content_path + FS_MAX_PATH, 0); + snprintf(content_path, FS_MAX_PATH, "code:/%s", NsoUtils::GetNsoFileName(index)); +} + +void NsoUtils::GetNsoSdPath(char *content_path, u64 title_id, unsigned int index) { + std::fill(content_path, content_path + FS_MAX_PATH, 0); + snprintf(content_path, FS_MAX_PATH, "sdmc:/atmosphere/titles/%016lx/exefs/%s", title_id, NsoUtils::GetNsoFileName(index)); +} + +bool NsoUtils::IsNsoPresent(unsigned int index) { + return g_nso_present[index]; +} + +Result NsoUtils::LoadNsoHeaders(u64 title_id) { + char nso_path[FS_MAX_PATH] = {0}; + FILE *f_nso; + + /* Zero out the cache. */ + std::fill(g_nso_present, g_nso_present + NSO_NUM_MAX, false); + std::fill(g_nso_headers, g_nso_headers + NSO_NUM_MAX, (const NsoUtils::NsoHeader &){0}); + + for (unsigned int i = 0; i < NSO_NUM_MAX; i++) { + GetNsoSdPath(nso_path, title_id, i); + if ((f_nso = fopen(nso_path, "rb")) != NULL) { + if (fread(&g_nso_headers[i], sizeof(NsoUtils::NsoHeader), 1, f_nso) != sizeof(NsoUtils::NsoHeader)) { + return 0xA09; + } + g_nso_present[i] = true; + fclose(f_nso); + continue; + } + GetNsoCodePath(nso_path, i); + if ((f_nso = fopen(nso_path, "rb")) != NULL) { + if (fread(&g_nso_headers[i], sizeof(NsoUtils::NsoHeader), 1, f_nso) != sizeof(NsoUtils::NsoHeader)) { + return 0xA09; + } + g_nso_present[i] = true; + fclose(f_nso); + continue; + } + if (1 < i && i < 12) { + /* If we failed to open a subsdk, there are no more subsdks. */ + i = 12; + } + } + + return 0x0; +} + +Result NsoUtils::ValidateNsoLoadSet() { + /* We *must* have a "main" NSO. */ + if (!g_nso_present[1]) { + return 0xA09; + } + + /* Behavior switches depending on whether we have an rtld. */ + if (g_nso_present[0]) { + /* If we have an rtld, dst offset for .text must be 0 for all other NSOs. */ + for (unsigned int i = 0; i < NSO_NUM_MAX; i++) { + if (g_nso_present[i] && g_nso_headers[i].segments[0].dst_offset != 0) { + return 0xA09; + } + } + } else { + /* If we don't have an rtld, we must ONLY have a main. */ + for (unsigned int i = 2; i < NSO_NUM_MAX; i++) { + if (g_nso_present[i]) { + return 0xA09; + } + } + /* That main's .text must be at dst_offset 0. */ + if (g_nso_headers[1].segments[0].dst_offset != 0) { + return 0xA09; + } + } + + return 0x0; +} \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_nso.hpp b/stratosphere/loader/source/ldr_nso.hpp new file mode 100644 index 000000000..19e514cba --- /dev/null +++ b/stratosphere/loader/source/ldr_nso.hpp @@ -0,0 +1,73 @@ +#pragma once +#include + +#define MAGIC_NSO0 0x304F534E +#define NSO_NUM_MAX 13 + +class NsoUtils { + public: + struct NsoSegment { + u32 file_offset; + u32 dst_offset; + u32 decomp_size; + u32 align_or_total_size; + }; + + struct NsoHeader { + u32 magic; + u32 _0x4; + u32 _0x8; + u32 flags; + NsoSegment segments[3]; + u8 build_id[0x20]; + u32 compressed_sizes[3]; + u8 _0x6C[0x24]; + u64 dynstr_extents; + u64 dynsym_extents; + u8 section_hashes[3][0x20]; + }; + + + static_assert(sizeof(NsoHeader) == 0x100, "Incorrectly defined NsoHeader!"); + + static const char *GetNsoFileName(unsigned int index) { + switch (index) { + case 0: + return "rtld"; + case 1: + return "main"; + case 2: + return "subsdk0"; + case 3: + return "subsdk1"; + case 4: + return "subsdk2"; + case 5: + return "subsdk3"; + case 6: + return "subsdk4"; + case 7: + return "subsdk5"; + case 8: + return "subsdk6"; + case 9: + return "subsdk7"; + case 10: + return "subsdk8"; + case 11: + return "subsdk9"; + case 12: + return "sdk"; + default: + /* TODO: Panic. */ + return "?"; + } + } + + static void GetNsoCodePath(char *content_path, unsigned int index); + static void GetNsoSdPath(char *content_path, u64 title_id, unsigned int index); + + static bool IsNsoPresent(unsigned int index); + static Result LoadNsoHeaders(u64 title_id); + static Result ValidateNsoLoadSet(); +}; \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index 7e0138a60..87f3f2c9f 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -5,40 +5,54 @@ #include "ldr_launch_queue.hpp" #include "ldr_content_management.hpp" #include "ldr_npdm.hpp" +#include "ldr_nso.hpp" Result ProcessCreation::CreateProcess(Handle *out_process_h, u64 index, char *nca_path, LaunchQueue::LaunchItem *launch_item, u64 flags, Handle reslimit_h) { NpdmUtils::NpdmInfo info; Registration::Process *target_process; Result rc; + /* Get the process from the registration queue. */ target_process = Registration::get_process(index); if (target_process == NULL) { return 0x1009; } + /* Mount the title's exefs. */ rc = ContentManagement::MountCodeForTidSid(&target_process->tid_sid); if (R_FAILED(rc)) { return rc; } + /* Load the process's NPDM. */ rc = NpdmUtils::LoadNpdmFromCache(target_process->tid_sid.title_id, &info); if (R_FAILED(rc)) { goto CREATE_PROCESS_END; } + /* Validate the title we're loading is what we expect. */ if (info.aci0->title_id < info.acid->title_id_range_min || info.aci0->title_id > info.acid->title_id_range_max) { rc = 0x1209; goto CREATE_PROCESS_END; } + /* Validate that the ACI0 Kernel Capabilities are valid and restricted by the ACID Kernel Capabilities. */ rc = NpdmUtils::ValidateCapabilities((u32 *)info.acid_kac, info.acid->kac_size/sizeof(u32), (u32 *)info.aci0_kac, info.aci0->kac_size/sizeof(u32)); if (R_FAILED(rc)) { goto CREATE_PROCESS_END; } - /* TODO: Read in all NSO headers, see what NSOs are present. */ + /* Read in all NSO headers, see what NSOs are present. */ + rc = NsoUtils::LoadNsoHeaders(info.aci0->title_id); + if (R_FAILED(rc)) { + goto CREATE_PROCESS_END; + } - /* TODO: Validate that the set of NSOs to be loaded is correct. */ + /* Validate that the set of NSOs to be loaded is correct. */ + rc = NsoUtils::ValidateNsoLoadSet(); + if (R_FAILED(rc)) { + goto CREATE_PROCESS_END; + } /* TODO: Create the CreateProcessInfo. */