2018-09-07 16:00:13 +01:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018 Atmosphère-NX
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms and conditions of the GNU General Public License,
|
|
|
|
* version 2, as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2018-04-27 03:27:52 +01:00
|
|
|
#include <switch.h>
|
2019-03-17 02:14:51 +00:00
|
|
|
#include <stratosphere.hpp>
|
2018-04-27 03:27:52 +01:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cstdio>
|
2018-06-19 19:07:31 +01:00
|
|
|
#include <functional>
|
2018-04-27 03:27:52 +01:00
|
|
|
#include <cstring>
|
|
|
|
#include "ldr_nro.hpp"
|
2018-04-27 10:17:07 +01:00
|
|
|
#include "ldr_registration.hpp"
|
2018-04-27 03:27:52 +01:00
|
|
|
#include "ldr_map.hpp"
|
|
|
|
#include "ldr_random.hpp"
|
|
|
|
|
|
|
|
Result NroUtils::ValidateNrrHeader(NrrHeader *header, u64 size, u64 title_id_min) {
|
|
|
|
if (header->magic != MAGIC_NRR0) {
|
2019-03-28 22:06:50 +00:00
|
|
|
return ResultLoaderInvalidNrr;
|
2018-04-27 03:27:52 +01:00
|
|
|
}
|
|
|
|
if (header->nrr_size != size) {
|
2019-03-28 22:06:50 +00:00
|
|
|
return ResultLoaderInvalidSize;
|
2018-04-27 03:27:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Check NRR signature. */
|
|
|
|
if (false) {
|
2019-03-28 22:06:50 +00:00
|
|
|
return ResultLoaderInvalidSignature;
|
2018-04-27 03:27:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (header->title_id_min != title_id_min) {
|
2019-03-28 22:06:50 +00:00
|
|
|
return ResultLoaderInvalidNrr;
|
2018-04-27 03:27:52 +01:00
|
|
|
}
|
|
|
|
|
2019-03-29 05:39:39 +00:00
|
|
|
return ResultSuccess;
|
2018-04-27 10:17:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2019-03-17 02:14:51 +00:00
|
|
|
NroHeader nro_hdr = {0};
|
2018-05-02 08:39:24 +01:00
|
|
|
MappedCodeMemory mcm_nro = {0};
|
|
|
|
MappedCodeMemory mcm_bss = {0};
|
2018-04-27 10:17:07 +01:00
|
|
|
unsigned int i;
|
2019-03-29 05:39:39 +00:00
|
|
|
Result rc = ResultSuccess;
|
2018-04-27 10:17:07 +01:00
|
|
|
u8 nro_hash[0x20];
|
2019-03-17 02:14:51 +00:00
|
|
|
|
|
|
|
/* Perform cleanup on failure. */
|
|
|
|
ON_SCOPE_EXIT {
|
|
|
|
if (R_FAILED(rc)) {
|
|
|
|
mcm_nro.Close();
|
|
|
|
mcm_bss.Close();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-04-27 10:17:07 +01:00
|
|
|
/* Ensure there is an available NRO slot. */
|
2018-06-19 19:07:31 +01:00
|
|
|
if (std::all_of(target_proc->nro_infos.begin(), target_proc->nro_infos.end(), std::mem_fn(&Registration::NroInfo::in_use))) {
|
2019-03-28 22:06:50 +00:00
|
|
|
rc = ResultLoaderInsufficientNroRegistrations;
|
2019-03-17 02:14:51 +00:00
|
|
|
return rc;
|
2018-04-27 10:17:07 +01:00
|
|
|
}
|
|
|
|
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))) {
|
2018-05-02 08:39:24 +01:00
|
|
|
if (R_SUCCEEDED(mcm_bss.OpenAtAddress(process_h, bss_heap_address, bss_heap_size, mcm_nro.code_memory_address + nro_heap_size))) {
|
2018-04-27 10:17:07 +01:00
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
mcm_nro.Close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i >= 0x200) {
|
2019-03-28 22:06:50 +00:00
|
|
|
rc = ResultLoaderInsufficientAddressSpace;
|
2019-03-17 02:14:51 +00:00
|
|
|
return rc;
|
2018-04-27 10:17:07 +01:00
|
|
|
}
|
2019-03-17 02:14:51 +00:00
|
|
|
|
|
|
|
/* Map the NRO. */
|
2018-04-27 10:17:07 +01:00
|
|
|
if (R_FAILED((rc = mcm_nro.Map()))) {
|
2019-03-17 02:14:51 +00:00
|
|
|
return rc;
|
2018-04-27 10:17:07 +01:00
|
|
|
}
|
|
|
|
|
2019-03-17 02:14:51 +00:00
|
|
|
/* Read data from NRO while it's mapped. */
|
|
|
|
{
|
|
|
|
nro_hdr = *((NroHeader *)mcm_nro.mapped_address);
|
|
|
|
|
|
|
|
if (nro_hdr.magic != MAGIC_NRO0) {
|
2019-03-28 22:06:50 +00:00
|
|
|
rc = ResultLoaderInvalidNro;
|
2019-03-17 02:14:51 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
if (nro_hdr.nro_size != nro_heap_size || nro_hdr.bss_size != bss_heap_size) {
|
2019-03-28 22:06:50 +00:00
|
|
|
rc = ResultLoaderInvalidNro;
|
2019-03-17 02:14:51 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
if ((nro_hdr.text_size & 0xFFF) || (nro_hdr.ro_size & 0xFFF) || (nro_hdr.rw_size & 0xFFF) || (nro_hdr.bss_size & 0xFFF)) {
|
2019-03-28 22:06:50 +00:00
|
|
|
rc = ResultLoaderInvalidNro;
|
2019-03-17 02:14:51 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
if (nro_hdr.text_offset != 0 || nro_hdr.text_offset + nro_hdr.text_size != nro_hdr.ro_offset || nro_hdr.ro_offset + nro_hdr.ro_size != nro_hdr.rw_offset || nro_hdr.rw_offset + nro_hdr.rw_size != nro_hdr.nro_size) {
|
2019-03-28 22:06:50 +00:00
|
|
|
rc = ResultLoaderInvalidNro;
|
2019-03-17 02:14:51 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-04-04 20:01:37 +01:00
|
|
|
sha256CalculateHash(nro_hash, mcm_nro.mapped_address, nro_hdr.nro_size);
|
2018-04-27 10:17:07 +01:00
|
|
|
}
|
2019-03-17 02:14:51 +00:00
|
|
|
|
|
|
|
/* Unmap the NRO. */
|
|
|
|
if (R_FAILED((rc = mcm_nro.Unmap()))) {
|
|
|
|
return rc;
|
2018-04-27 10:17:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!Registration::IsNroHashPresent(target_proc->index, nro_hash)) {
|
2019-03-28 22:06:50 +00:00
|
|
|
rc = ResultLoaderInvalidSignature;
|
2019-03-17 02:14:51 +00:00
|
|
|
return rc;
|
2018-04-27 10:17:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Registration::IsNroAlreadyLoaded(target_proc->index, nro_hash)) {
|
2019-03-28 22:06:50 +00:00
|
|
|
rc = ResultLoaderNroAlreadyLoaded;
|
2019-03-17 02:14:51 +00:00
|
|
|
return rc;
|
2018-04-27 10:17:07 +01:00
|
|
|
}
|
|
|
|
|
2019-03-17 02:14:51 +00:00
|
|
|
if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address, nro_hdr.text_size, 5)))) {
|
|
|
|
return rc;
|
2018-04-27 10:17:07 +01:00
|
|
|
}
|
|
|
|
|
2019-03-17 02:14:51 +00:00
|
|
|
if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address + nro_hdr.ro_offset, nro_hdr.ro_size, 1)))) {
|
|
|
|
return rc;
|
2018-04-27 10:17:07 +01:00
|
|
|
}
|
|
|
|
|
2019-03-17 02:14:51 +00:00
|
|
|
if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address + nro_hdr.rw_offset, nro_hdr.rw_size + nro_hdr.bss_size, 3)))) {
|
|
|
|
return rc;
|
2018-04-27 10:17:07 +01:00
|
|
|
}
|
|
|
|
|
2019-03-17 02:14:51 +00:00
|
|
|
Registration::AddNroToProcess(target_proc->index, &mcm_nro, &mcm_bss, nro_hdr.text_size, nro_hdr.ro_size, nro_hdr.rw_size, nro_hdr.build_id);
|
2018-04-27 10:33:44 +01:00
|
|
|
*out_address = mcm_nro.code_memory_address;
|
2019-03-29 05:39:39 +00:00
|
|
|
rc = ResultSuccess;
|
2018-04-27 10:17:07 +01:00
|
|
|
|
2019-03-17 02:14:51 +00:00
|
|
|
return rc;
|
2018-06-19 19:07:31 +01:00
|
|
|
}
|