From 0411dbfddae9b5fe5fdce3bf871b14586e692dc7 Mon Sep 17 00:00:00 2001 From: hexkyz Date: Sun, 20 Sep 2026 23:23:56 +0100 Subject: [PATCH] ro: start implementing 23.0.0 changes --- .../stratosphere/ro/impl/ro_ro_interface.hpp | 14 +- .../include/stratosphere/ro/ro_types.hpp | 117 +++++++--- .../include/stratosphere/rocrt/rocrt.hpp | 9 +- .../include/vapours/results/ro_results.hpp | 8 +- .../ro/source/impl/ro_service_impl.cpp | 206 +++++++++++++++--- stratosphere/ro/source/ro_ro_service.cpp | 2 +- stratosphere/ro/source/ro_ro_service.hpp | 2 +- 7 files changed, 280 insertions(+), 78 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/ro/impl/ro_ro_interface.hpp b/libraries/libstratosphere/include/stratosphere/ro/impl/ro_ro_interface.hpp index 8a993f65b..e84a6fcf2 100644 --- a/libraries/libstratosphere/include/stratosphere/ro/impl/ro_ro_interface.hpp +++ b/libraries/libstratosphere/include/stratosphere/ro/impl/ro_ro_interface.hpp @@ -19,12 +19,12 @@ #include #include -#define AMS_RO_I_RO_INTERFACE_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 0, Result, MapManualLoadModuleMemory, (sf::Out out_load_address, const sf::ClientProcessId &client_pid, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size), (out_load_address, client_pid, nro_address, nro_size, bss_address, bss_size)) \ - AMS_SF_METHOD_INFO(C, H, 1, Result, UnmapManualLoadModuleMemory, (const sf::ClientProcessId &client_pid, u64 nro_address), (client_pid, nro_address)) \ - AMS_SF_METHOD_INFO(C, H, 2, Result, RegisterModuleInfo, (const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size), (client_pid, nrr_address, nrr_size)) \ - AMS_SF_METHOD_INFO(C, H, 3, Result, UnregisterModuleInfo, (const sf::ClientProcessId &client_pid, u64 nrr_address), (client_pid, nrr_address)) \ - AMS_SF_METHOD_INFO(C, H, 4, Result, RegisterProcessHandle, (const sf::ClientProcessId &client_pid, sf::CopyHandle &&process_h), (client_pid, std::move(process_h))) \ - AMS_SF_METHOD_INFO(C, H, 10, Result, RegisterProcessModuleInfo, (const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size, sf::CopyHandle &&process_h), (client_pid, nrr_address, nrr_size, std::move(process_h)), hos::Version_7_0_0) +#define AMS_RO_I_RO_INTERFACE_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, MapManualLoadModuleMemory, (sf::Out out_load_address, const sf::ClientProcessId &client_pid, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size), (out_load_address, client_pid, nro_address, nro_size, bss_address, bss_size)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, UnmapManualLoadModuleMemory, (const sf::ClientProcessId &client_pid, u64 nro_address), (client_pid, nro_address)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, RegisterModuleInfo, (const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size), (client_pid, nrr_address, nrr_size)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, UnregisterModuleInfo, (const sf::ClientProcessId &client_pid, u64 nrr_address), (client_pid, nrr_address)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, RegisterProcessHandle, (const sf::ClientProcessId &client_pid, sf::CopyHandle &&process_h), (client_pid, std::move(process_h))) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, RegisterModuleInfoWithUserProcessHandle, (const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size, sf::CopyHandle &&process_h), (client_pid, nrr_address, nrr_size, std::move(process_h)), hos::Version_7_0_0) AMS_SF_DEFINE_INTERFACE(ams::ro::impl, IRoInterface, AMS_RO_I_RO_INTERFACE_INTERFACE_INFO, 0xA52C55A9) diff --git a/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp b/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp index 4a1ce2684..86c57d0fc 100644 --- a/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp @@ -17,6 +17,7 @@ #pragma once #include #include +#include namespace ams::ro { @@ -40,30 +41,30 @@ namespace ams::ro { u64 program_id_mask; u64 program_id_pattern; u8 reserved_10[0x10]; - u8 modulus[RsaKeySize]; - u8 signature[RsaKeySize]; + u8 public_key[RsaKeySize]; + u8 sign[RsaKeySize]; }; static_assert(sizeof(NrrCertification) == NrrCertification::RsaKeySize + NrrCertification::SignedSize); class NrrHeader { public: - static constexpr u32 Magic = util::FourCC<'N','R','R','0'>::Code; + static constexpr u32 Signature = util::FourCC<'N','R','R','0'>::Code; private: - u32 m_magic; + u32 m_signature; u32 m_key_generation; u8 m_reserved_08[0x08]; NrrCertification m_certification; - u8 m_signature[0x100]; + u8 m_sign[0x100]; ncm::ProgramId m_program_id; u32 m_size; u8 m_nrr_kind; /* 7.0.0+ */ u8 m_reserved_33D[3]; - u32 m_hashes_offset; - u32 m_num_hashes; + u32 m_hash_list_offset_address; + u32 m_num_hash; u8 m_reserved_348[8]; public: bool IsMagicValid() const { - return m_magic == Magic; + return m_signature == Signature; } bool IsProgramIdValid() const { @@ -85,11 +86,11 @@ namespace ams::ro { } u32 GetNumHashes() const { - return m_num_hashes; + return m_num_hash; } size_t GetHashesOffset() const { - return m_hashes_offset; + return m_hash_list_offset_address; } uintptr_t GetHashes() const { @@ -101,7 +102,7 @@ namespace ams::ro { } const u8 *GetCertificationSignature() const { - return m_certification.signature; + return m_certification.sign; } const u8 *GetCertificationSignedArea() const { @@ -109,11 +110,11 @@ namespace ams::ro { } const u8 *GetCertificationModulus() const { - return m_certification.modulus; + return m_certification.public_key; } const u8 *GetSignature() const { - return m_signature; + return m_sign; } const u8 *GetSignedArea() const { @@ -131,32 +132,45 @@ namespace ams::ro { constexpr size_t NrrHeader::GetSignedAreaOffset() { return AMS_OFFSETOF(NrrHeader, m_program_id); } + + static constexpr size_t RocrtHeaderSize = 0x10; + struct RocrtHeader { + rocrt::ModuleHeaderLocation module_header_location; + u32 reserved; + }; + static_assert(sizeof(RocrtHeader) == RocrtHeaderSize); class NroHeader { public: - static constexpr u32 Magic = util::FourCC<'N','R','O','0'>::Code; + static constexpr u32 Signature = util::FourCC<'N','R','O','0'>::Code; static constexpr u32 FlagAlignedHeader = 1; + static constexpr u32 FlagCompress = 2; private: - u32 m_entrypoint_insn; - u32 m_mod_offset; - u8 m_reserved_08[0x8]; - u32 m_magic; - u8 m_version; + RocrtHeader m_rocrt; + u32 m_signature; + u32 m_version; u32 m_size; u32 m_flags; - u32 m_text_offset; + u32 m_text_memory_offset; u32 m_text_size; - u32 m_ro_offset; + u32 m_ro_memory_offset; u32 m_ro_size; - u32 m_rw_offset; - u32 m_rw_size; + u32 m_data_memory_offset; + u32 m_data_size; u32 m_bss_size; - u8 m_reserved_3C[0x4]; + u32 m_additional_header_offset; /* 23.0.0+ */ ModuleId m_module_id; - u8 m_reserved_60[0x20]; + u32 m_dso_handle_offset; + u32 m_reserved_64; + u32 m_embedded_offset; + u32 m_embedded_size; + u32 m_dyn_str_offset; + u32 m_dyn_str_size; + u32 m_dyn_sym_offset; + u32 m_dyn_sym_size; public: bool IsMagicValid() const { - return m_magic == Magic; + return m_signature == Signature; } u32 GetVersion() const { @@ -174,9 +188,13 @@ namespace ams::ro { bool IsAlignedHeader() const { return m_flags & FlagAlignedHeader; } + + bool IsCompress() const { + return m_flags & FlagCompress; + } u32 GetTextOffset() const { - return m_text_offset; + return m_text_memory_offset; } u32 GetTextSize() const { @@ -184,7 +202,7 @@ namespace ams::ro { } u32 GetRoOffset() const { - return m_ro_offset; + return m_ro_memory_offset; } u32 GetRoSize() const { @@ -192,21 +210,60 @@ namespace ams::ro { } u32 GetRwOffset() const { - return m_rw_offset; + return m_data_memory_offset; } u32 GetRwSize() const { - return m_rw_size; + return m_data_size; } u32 GetBssSize() const { return m_bss_size; } + + u32 GetAdditionalHeaderOffset() const { + return m_additional_header_offset; + } const ModuleId *GetModuleId() const { return std::addressof(m_module_id); } }; static_assert(sizeof(NroHeader) == 0x80, "NroHeader definition!"); + + class AdditionalNroHeader { + public: + static constexpr u32 Signature = util::FourCC<'N','R','A','0'>::Code; + private: + u32 m_signature; + u8 m_reserved_04[0xC]; + u32 m_compressed_size; + u32 m_reserved_14; + u32 m_hash_size; + u32 m_hash_offset; + u8 m_reserved_20[0x40]; + u8 m_hash[0x20]; + public: + bool IsMagicValid() const { + return m_signature == Signature; + } + + u32 GetCompressedSize() const { + return m_compressed_size; + } + + u32 GetHashSize() const { + return m_hash_size; + } + + u32 GetHashOffset() const { + return m_hash_offset; + } + + const u8 *GetHash() const { + return m_hash; + } + }; + static_assert(sizeof(AdditionalNroHeader) == 0x80, "AdditionalNroHeader definition!"); } diff --git a/libraries/libstratosphere/include/stratosphere/rocrt/rocrt.hpp b/libraries/libstratosphere/include/stratosphere/rocrt/rocrt.hpp index 6a4c0ce82..0758c9648 100644 --- a/libraries/libstratosphere/include/stratosphere/rocrt/rocrt.hpp +++ b/libraries/libstratosphere/include/stratosphere/rocrt/rocrt.hpp @@ -29,11 +29,18 @@ namespace ams::rocrt { u32 exception_info_start_offset; u32 exception_info_end_offset; u32 module_offset; + u32 relro_start_offset; /* 19.0.0+ */ + u32 full_relro_end_offset; /* 19.0.0+ */ + u32 nx_debug_link_start_offset; /* 19.0.0+ */ + u32 nx_debug_link_end_offset; /* 19.0.0+ */ + u32 note_gnu_build_id_start_offset; /* 19.0.0+ */ + u32 note_gnu_build_id_end_offset; /* 19.0.0+ */ }; struct ModuleHeaderLocation { - u32 pad; + u32 version; u32 header_offset; + u32 version_offset; }; constexpr inline u32 CheckModuleHeaderSignature(const ModuleHeader *header) { diff --git a/libraries/libvapours/include/vapours/results/ro_results.hpp b/libraries/libvapours/include/vapours/results/ro_results.hpp index c187749dc..e3ad6b53f 100644 --- a/libraries/libvapours/include/vapours/results/ro_results.hpp +++ b/libraries/libvapours/include/vapours/results/ro_results.hpp @@ -25,18 +25,20 @@ namespace ams::ro { R_DEFINE_ERROR_RESULT(OutOfAddressSpace, 2); R_DEFINE_ERROR_RESULT(AlreadyLoaded, 3); R_DEFINE_ERROR_RESULT(InvalidNro, 4); - + R_DEFINE_ERROR_RESULT(NotFound, 5); R_DEFINE_ERROR_RESULT(InvalidNrr, 6); R_DEFINE_ERROR_RESULT(TooManyNro, 7); R_DEFINE_ERROR_RESULT(TooManyNrr, 8); R_DEFINE_ERROR_RESULT(NotAuthorized, 9); R_DEFINE_ERROR_RESULT(InvalidNrrKind, 10); - + R_DEFINE_ERROR_RESULT(InvalidBssSize, 11); + + R_DEFINE_ERROR_RESULT(NotImplemented, 1022); R_DEFINE_ERROR_RESULT(InternalError, 1023); R_DEFINE_ERROR_RESULT(InvalidAddress, 1025); R_DEFINE_ERROR_RESULT(InvalidSize, 1026); - + R_DEFINE_ERROR_RESULT(InvalidCurrentMemory, 1027); R_DEFINE_ERROR_RESULT(NotLoaded, 1028); R_DEFINE_ERROR_RESULT(NotRegistered, 1029); R_DEFINE_ERROR_RESULT(InvalidSession, 1030); diff --git a/stratosphere/ro/source/impl/ro_service_impl.cpp b/stratosphere/ro/source/impl/ro_service_impl.cpp index 0eee275eb..6beff3c8f 100644 --- a/stratosphere/ro/source/impl/ro_service_impl.cpp +++ b/stratosphere/ro/source/impl/ro_service_impl.cpp @@ -47,7 +47,7 @@ namespace ams::ro::impl { } }; static_assert(sizeof(Sha256Hash) == sizeof(Sha256Hash::hash)); - + struct NroInfo { u64 base_address; u64 nro_heap_address; @@ -55,8 +55,10 @@ namespace ams::ro::impl { u64 bss_heap_address; u64 bss_heap_size; u64 code_size; + u64 ro_size; /* 23.0.0+ */ u64 rw_size; ModuleId module_id; + bool is_compress; /* 23.0.0+ */ }; struct NrrInfo { @@ -72,7 +74,17 @@ namespace ams::ro::impl { u8 cached_signed_area[sizeof(NrrHeader) - NrrHeader::GetSignedAreaOffset()]; Sha256Hash signed_area_hash; }; - + + struct AdditionalNroHeaderInfo { + u32 signature; + u8 reserved_04[0xC]; + u32 compressed_size; + u32 reserved_14; + u32 hash_size; + u32 hash_offset; + u8 reserved_20[0x40]; + }; + struct ProcessContext { private: bool m_nro_in_use[MaxNroInfos]{}; @@ -246,21 +258,12 @@ namespace ams::ro::impl { R_THROW(ro::ResultNotAuthorized()); } - - 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. */ - 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_CONVERT(os::ResultOutOfAddressSpace, ro::ResultOutOfAddressSpace()) - } R_END_TRY_CATCH; - - /* When we're done, unmap the memory. */ - ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, m_process_handle, base_address, expected_nro_size); }; - + + Result ValidateNro(NroHeader **out_header, AdditionalNroHeaderInfo **out_additional_header_info, void *out_additional_header_hash, u8 *mapped_memory, u64 expected_nro_size, u64 expected_bss_size) { /* Validate header. */ - const NroHeader *header = static_cast(mapped_memory); + NroHeader *header = reinterpret_cast(mapped_memory); R_UNLESS(header->IsMagicValid(), ro::ResultInvalidNro()); - + /* Read sizes from header. */ const u64 nro_size = header->GetSize(); const u64 text_ofs = header->GetTextOffset(); @@ -270,17 +273,28 @@ namespace ams::ro::impl { const u64 rw_ofs = header->GetRwOffset(); const u64 rw_size = header->GetRwSize(); const u64 bss_size = header->GetBssSize(); - - /* Validate sizes meet expected. */ - R_UNLESS(nro_size == expected_nro_size, ro::ResultInvalidNro()); - R_UNLESS(bss_size == expected_bss_size, ro::ResultInvalidNro()); - + /* Validate all sizes are aligned. */ R_UNLESS(util::IsAligned(text_size, os::MemoryPageSize), ro::ResultInvalidNro()); R_UNLESS(util::IsAligned(ro_size, os::MemoryPageSize), ro::ResultInvalidNro()); R_UNLESS(util::IsAligned(rw_size, os::MemoryPageSize), ro::ResultInvalidNro()); R_UNLESS(util::IsAligned(bss_size, os::MemoryPageSize), ro::ResultInvalidNro()); + /* Validate sizes meet expected. */ + R_UNLESS(nro_size == expected_nro_size, ro::ResultInvalidNro()); + + /* Account for compression when checking bss size. */ + if (header->IsCompress()) { + R_UNLESS((bss_size - expected_nro_size + (rw_ofs + rw_size)) == expected_bss_size, ro::ResultInvalidBssSize()); + } else { + /* 23.0.0+ uses the new error code here too. */ + if (hos::GetVersion() >= hos::Version_23_0_0) { + R_UNLESS(bss_size == expected_bss_size, ro::ResultInvalidBssSize()); + } else { + R_UNLESS(bss_size == expected_bss_size, ro::ResultInvalidNro()); + } + } + /* Validate sections are in order. */ R_UNLESS(text_ofs <= ro_ofs, ro::ResultInvalidNro()); R_UNLESS(ro_ofs <= rw_ofs, ro::ResultInvalidNro()); @@ -289,24 +303,119 @@ namespace ams::ro::impl { R_UNLESS(text_ofs == 0, ro::ResultInvalidNro()); R_UNLESS(text_ofs + text_size == ro_ofs, ro::ResultInvalidNro()); R_UNLESS(ro_ofs + ro_size == rw_ofs, ro::ResultInvalidNro()); - R_UNLESS(rw_ofs + rw_size == nro_size, ro::ResultInvalidNro()); - + + /* Only check data section if not using compression. */ + if (!header->IsCompress()) { + R_UNLESS(rw_ofs + rw_size == nro_size, ro::ResultInvalidNro()); + } + /* Verify NRO hash. */ R_TRY(this->ValidateHasNroHash(header)); /* Check if NRO has already been loaded. */ const ModuleId *module_id = header->GetModuleId(); R_UNLESS(R_FAILED(this->GetNroInfoByModuleId(nullptr, module_id)), ro::ResultAlreadyLoaded()); + + /* Validate compression related fields. */ + if (header->IsCompress()) { + const u64 additional_header_offset = header->GetAdditionalHeaderOffset(); + + /* Compression must have aligned header as well. */ + R_UNLESS(header->IsAlignedHeader(), ro::ResultInvalidNro()); + + /* Validate additional header offset. */ + R_UNLESS(additional_header_offset == 0x80, ro::ResultInvalidNro()); + + /* Validate additional header. */ + AdditionalNroHeader *additional_header = reinterpret_cast(mapped_memory + additional_header_offset); + R_UNLESS(additional_header->IsMagicValid(), ro::ResultInvalidNro()); + + /* Read sizes from header. */ + const u64 hash_size = additional_header->GetHashSize(); + const u64 hash_offset = additional_header->GetHashOffset(); + const u64 compressed_size = additional_header->GetCompressedSize(); + + /* Validate additional header hash size and offset. */ + R_UNLESS(hash_size == 0x20, ro::ResultInvalidNro()); + R_UNLESS(hash_offset == 0xE0, ro::ResultInvalidNro()); + + /* Copy the additional header hash to output. */ + std::memcpy(out_additional_header_hash, additional_header->GetHash(), hash_size); + + /* Validate compressed size. */ + R_UNLESS(rw_ofs + rw_size > nro_size, ro::ResultInvalidNro()); + R_UNLESS(compressed_size >= 0x1000, ro::ResultInvalidNro()); + R_UNLESS(util::AlignUp(compressed_size, os::MemoryPageSize) == expected_nro_size, ro::ResultInvalidNro()); + + /* Copy the additional header info to output. This is AdditionalNroHeader minus the hash. */ + std::memcpy(out_additional_header_info, additional_header, sizeof(*additional_header) - hash_size); + } else { + /* Explicitly clear additional header hash and info. */ + std::memset(out_additional_header_hash, 0, crypto::Sha256Generator::HashSize); + std::memset(out_additional_header_info, 0, sizeof(AdditionalNroHeaderInfo)); + } + + /* Copy the header to output. */ + *out_header = header; + R_SUCCEED(); + } + + Result DecompressNro(u8 *mapped_nro, u64 expected_nro_size, const NroHeader *nro_header, u32 compressed_size) { + /* TODO */ + AMS_UNUSED(mapped_nro, expected_nro_size, nro_header, compressed_size); + R_SUCCEED(); + } + + Result CheckAdditionalHeaderHash(const u8 *mapped_nro, const void *additional_nro_header_hash, size_t additional_nro_header_hash_size, u64 rw_ofs, u64 rw_size) { + /* Calculate hash. */ + Sha256Hash nro_rw_hash; + crypto::GenerateSha256(std::addressof(nro_rw_hash), additional_nro_header_hash_size, mapped_nro + 0x1000, (rw_ofs + rw_size) - 0x1000); + + /* Compare the hashes. */ + const Sha256Hash *expected_hash = reinterpret_cast(additional_nro_header_hash); + if (*expected_hash != nro_rw_hash) { + R_THROW(ro::ResultInvalidNro()); + } + R_SUCCEED(); + } + Result ParseNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, bool *out_aligned_header, bool *out_is_compress, u64 base_address, u64 expected_nro_size, u64 expected_bss_size) { + /* Map the NRO. */ + 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_CONVERT(os::ResultOutOfAddressSpace, ro::ResultOutOfAddressSpace()) + } R_END_TRY_CATCH; + + /* When we're done, unmap the memory. */ + ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, m_process_handle, base_address, expected_nro_size); }; + + /* ValidateNro */ + NroHeader *nro_header = nullptr; + AdditionalNroHeaderInfo *additional_nro_header_info = nullptr; + u8 additional_nro_header_hash[crypto::Sha256Generator::HashSize]; + R_TRY(this->ValidateNro(std::addressof(nro_header), std::addressof(additional_nro_header_info), std::addressof(additional_nro_header_hash), static_cast(mapped_memory), expected_nro_size, expected_bss_size)); + + /* DecompressNro */ + if (nro_header->IsCompress()) { + R_TRY(this->DecompressNro(static_cast(mapped_memory), expected_nro_size, nro_header, additional_nro_header_info->compressed_size)); + + /* CheckAdditionalHeaderHash */ + if (nro_header->GetAdditionalHeaderOffset() != 0) { + R_TRY(this->CheckAdditionalHeaderHash(static_cast(mapped_memory), std::addressof(additional_nro_header_hash), sizeof(additional_nro_header_hash), nro_header->GetRwOffset(), nro_header->GetRwSize())); + } + } + /* Apply patches to NRO. */ - LocateAndApplyIpsPatchesToModule(module_id, static_cast(mapped_memory), nro_size); + const ModuleId *module_id = nro_header->GetModuleId(); + LocateAndApplyIpsPatchesToModule(module_id, static_cast(mapped_memory), nro_header->GetSize()); /* Copy to output. */ *out_module_id = *module_id; - *out_rx_size = text_size; - *out_ro_size = ro_size; - *out_rw_size = rw_size; - *out_aligned_header = header->IsAlignedHeader(); + *out_rx_size = nro_header->GetTextSize(); + *out_ro_size = nro_header->GetRoSize(); + *out_rw_size = nro_header->GetRwSize(); + *out_aligned_header = nro_header->IsAlignedHeader(); + *out_is_compress = nro_header->IsCompress(); R_SUCCEED(); } @@ -547,26 +656,34 @@ namespace ams::ro::impl { /* Check we have space for a new NRO. */ NroInfo *nro_info = nullptr; R_TRY(context->GetFreeNroInfo(std::addressof(nro_info))); - nro_info->nro_heap_address = nro_address; - nro_info->nro_heap_size = nro_size; - nro_info->bss_heap_address = bss_address; - nro_info->bss_heap_size = bss_size; - + /* Map the NRO. */ R_TRY(MapNro(std::addressof(nro_info->base_address), context->GetProcessHandle(), nro_address, nro_size, bss_address, bss_size)); ON_RESULT_FAILURE { UnmapNro(context->GetProcessHandle(), nro_info->base_address, nro_address, nro_size, bss_address, bss_size); }; - /* Validate the NRO (parsing region extents). */ + /* Parse the NRO. */ u64 rx_size = 0, ro_size = 0, rw_size = 0; 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)); + bool is_compress = false; + R_TRY(context->ParseNro(std::addressof(nro_info->module_id), std::addressof(rx_size), std::addressof(ro_size), std::addressof(rw_size), std::addressof(aligned_header), std::addressof(is_compress), nro_info->base_address, nro_size, bss_size)); /* Set NRO perms. */ R_TRY(SetNroPerms(context->GetProcessHandle(), nro_info->base_address, rx_size, ro_size, rw_size + bss_size, aligned_header)); context->SetNroInfoInUse(nro_info, true); - nro_info->code_size = rx_size + ro_size; + nro_info->nro_heap_address = nro_address; + nro_info->nro_heap_size = nro_size; + nro_info->bss_heap_address = bss_address; + nro_info->bss_heap_size = bss_size; + if (hos::GetVersion() >= hos::Version_23_0_0) { + nro_info->code_size = rx_size; + nro_info->ro_size = ro_size; + } else { + nro_info->code_size = rx_size + ro_size; + nro_info->ro_size = 0; + } nro_info->rw_size = rw_size; + nro_info->is_compress = is_compress; *out_address = nro_info->base_address; R_SUCCEED(); } @@ -582,6 +699,25 @@ namespace ams::ro::impl { /* Check the NRO is loaded. */ NroInfo *nro_info = nullptr; R_TRY(context->GetNroInfoByAddress(std::addressof(nro_info), nro_address)); + + /* Handle compression related mappings. */ + if (nro_info->is_compress) { + const u64 code_address = nro_info->base_address + 0x1000; + const u64 code_size = nro_info->code_size - 0x1000; + + /* Remove code region permissions. */ + R_TRY(os::SetProcessMemoryPermission(context->GetProcessHandle(), code_address, code_size, os::MemoryPermission_None)); + + /* Map the code region. */ + void *mapped_memory = nullptr; + R_TRY(os::MapProcessMemory(std::addressof(mapped_memory), context->GetProcessHandle(), code_address, code_size, ro::impl::GenerateSecureRandom)); + + /* Clear the memory. */ + std::memset(mapped_memory, 0, code_size); + + /* Unmap the code region. */ + os::UnmapProcessMemory(mapped_memory, context->GetProcessHandle(), code_address, code_size); + } /* Unmap. */ const NroInfo nro_backup = *nro_info; diff --git a/stratosphere/ro/source/ro_ro_service.cpp b/stratosphere/ro/source/ro_ro_service.cpp index ec309fb63..e25c326da 100644 --- a/stratosphere/ro/source/ro_ro_service.cpp +++ b/stratosphere/ro/source/ro_ro_service.cpp @@ -60,7 +60,7 @@ namespace ams::ro { R_RETURN(impl::RegisterProcess(std::addressof(m_context_id), std::move(process_h), client_pid.GetValue())); } - Result RoService::RegisterProcessModuleInfo(const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size, sf::CopyHandle &&process_h) { + Result RoService::RegisterModuleInfoWithUserProcessHandle(const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size, sf::CopyHandle &&process_h) { /* Validate the process. */ R_TRY(impl::ValidateProcess(m_context_id, client_pid.GetValue())); diff --git a/stratosphere/ro/source/ro_ro_service.hpp b/stratosphere/ro/source/ro_ro_service.hpp index a97a9fe15..db5d2f24b 100644 --- a/stratosphere/ro/source/ro_ro_service.hpp +++ b/stratosphere/ro/source/ro_ro_service.hpp @@ -38,7 +38,7 @@ namespace ams::ro { Result RegisterModuleInfo(const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size); Result UnregisterModuleInfo(const sf::ClientProcessId &client_pid, u64 nrr_address); Result RegisterProcessHandle(const sf::ClientProcessId &client_pid, sf::CopyHandle &&process_h); - Result RegisterProcessModuleInfo(const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size, sf::CopyHandle &&process_h); + Result RegisterModuleInfoWithUserProcessHandle(const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size, sf::CopyHandle &&process_h); }; static_assert(ro::impl::IsIRoInterface);