ro: support NROs with read-only first page

This commit is contained in:
Michael Scire 2024-10-10 01:40:28 -07:00
parent 4baf0e8cce
commit 5c6362c56d
4 changed files with 27 additions and 8 deletions

View file

@ -135,14 +135,15 @@ namespace ams::ro {
class NroHeader { class NroHeader {
public: public:
static constexpr u32 Magic = util::FourCC<'N','R','O','0'>::Code; static constexpr u32 Magic = util::FourCC<'N','R','O','0'>::Code;
static constexpr u32 FlagAlignedHeader = 1;
private: private:
u32 m_entrypoint_insn; u32 m_entrypoint_insn;
u32 m_mod_offset; u32 m_mod_offset;
u8 m_reserved_08[0x8]; u8 m_reserved_08[0x8];
u32 m_magic; u32 m_magic;
u8 m_reserved_14[0x4]; u8 m_version;
u32 m_size; u32 m_size;
u8 m_reserved_1C[0x4]; u32 m_flags;
u32 m_text_offset; u32 m_text_offset;
u32 m_text_size; u32 m_text_size;
u32 m_ro_offset; u32 m_ro_offset;
@ -158,10 +159,22 @@ namespace ams::ro {
return m_magic == Magic; return m_magic == Magic;
} }
u32 GetVersion() const {
return m_version;
}
u32 GetSize() const { u32 GetSize() const {
return m_size; return m_size;
} }
u32 GetFlags() const {
return m_flags;
}
bool IsAlignedHeader() const {
return m_flags & FlagAlignedHeader;
}
u32 GetTextOffset() const { u32 GetTextOffset() const {
return m_text_offset; return m_text_offset;
} }

View file

@ -51,11 +51,15 @@ namespace ams::ro::impl {
R_SUCCEED(); R_SUCCEED();
} }
Result SetNroPerms(os::NativeHandle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size) { Result SetNroPerms(os::NativeHandle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size, bool is_aligned_header) {
const u64 rx_offset = 0; const u64 rx_offset = is_aligned_header ? os::MemoryPageSize : 0;
const u64 ro_offset = rx_offset + rx_size; const u64 ro_offset = rx_offset + rx_size;
const u64 rw_offset = ro_offset + ro_size; const u64 rw_offset = ro_offset + ro_size;
if (is_aligned_header) {
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address, os::MemoryPageSize, os::MemoryPermission_ReadOnly));
}
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, os::MemoryPermission_ReadExecute)); R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, os::MemoryPermission_ReadExecute));
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, os::MemoryPermission_ReadOnly)); R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, os::MemoryPermission_ReadOnly));
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, os::MemoryPermission_ReadWrite)); R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, os::MemoryPermission_ReadWrite));

View file

@ -21,7 +21,7 @@ namespace ams::ro::impl {
/* Utilities for working with NROs. */ /* Utilities for working with NROs. */
Result MapNro(u64 *out_base_address, os::NativeHandle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size); Result MapNro(u64 *out_base_address, os::NativeHandle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size);
Result SetNroPerms(os::NativeHandle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size); Result SetNroPerms(os::NativeHandle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size, bool is_aligned_header);
Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size); Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size);
} }

View file

@ -247,7 +247,7 @@ namespace ams::ro::impl {
R_THROW(ro::ResultNotAuthorized()); R_THROW(ro::ResultNotAuthorized());
} }
Result ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, u64 base_address, u64 expected_nro_size, u64 expected_bss_size) { Result ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, bool *out_aligned_header, u64 base_address, u64 expected_nro_size, u64 expected_bss_size) {
/* Map the NRO. */ /* Map the NRO. */
void *mapped_memory = nullptr; void *mapped_memory = nullptr;
R_TRY_CATCH(os::MapProcessMemory(std::addressof(mapped_memory), m_process_handle, base_address, expected_nro_size, ro::impl::GenerateSecureRandom)) { R_TRY_CATCH(os::MapProcessMemory(std::addressof(mapped_memory), m_process_handle, base_address, expected_nro_size, ro::impl::GenerateSecureRandom)) {
@ -306,6 +306,7 @@ namespace ams::ro::impl {
*out_rx_size = text_size; *out_rx_size = text_size;
*out_ro_size = ro_size; *out_ro_size = ro_size;
*out_rw_size = rw_size; *out_rw_size = rw_size;
*out_aligned_header = header->IsAlignedHeader();
R_SUCCEED(); R_SUCCEED();
} }
@ -557,10 +558,11 @@ namespace ams::ro::impl {
/* Validate the NRO (parsing region extents). */ /* Validate the NRO (parsing region extents). */
u64 rx_size = 0, ro_size = 0, rw_size = 0; u64 rx_size = 0, ro_size = 0, rw_size = 0;
R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size), std::addressof(ro_size), std::addressof(rw_size), nro_info->base_address, nro_size, bss_size)); bool aligned_header = false;
R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size), std::addressof(ro_size), std::addressof(rw_size), std::addressof(aligned_header), nro_info->base_address, nro_size, bss_size));
/* Set NRO perms. */ /* Set NRO perms. */
R_TRY(SetNroPerms(context->GetProcessHandle(), nro_info->base_address, rx_size, ro_size, rw_size + bss_size)); R_TRY(SetNroPerms(context->GetProcessHandle(), nro_info->base_address, rx_size, ro_size, rw_size + bss_size, aligned_header));
context->SetNroInfoInUse(nro_info, true); context->SetNroInfoInUse(nro_info, true);
nro_info->code_size = rx_size + ro_size; nro_info->code_size = rx_size + ro_size;