2020-10-10 16:35:14 +01:00
/*
* nso . c
*
2024-04-12 10:47:36 +01:00
* Copyright ( c ) 2020 - 2024 , DarkMatterCore < pabloacurielz @ gmail . com > .
2020-10-10 16:35:14 +01:00
*
* This file is part of nxdumptool ( https : //github.com/DarkMatterCore/nxdumptool).
*
2021-03-25 19:26:58 +00:00
* nxdumptool is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
2020-10-10 16:35:14 +01:00
*
2021-03-25 19:26:58 +00:00
* nxdumptool is distributed in the hope that 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 .
2020-10-10 16:35:14 +01:00
*
* You should have received a copy of the GNU General Public License
2021-03-25 19:26:58 +00:00
* along with this program . If not , see < https : //www.gnu.org/licenses/>.
2020-10-10 16:35:14 +01:00
*/
2024-04-30 22:01:42 +01:00
# include <core/nxdt_utils.h>
# include <core/nso.h>
2020-10-11 16:22:26 +01:00
2024-08-06 21:57:54 +01:00
/* Type definitions. */
typedef enum {
NsoSegmentType_Text = 0 ,
2024-08-08 11:27:54 +01:00
NsoSegmentType_RoData = 1 ,
2024-08-06 21:57:54 +01:00
NsoSegmentType_Data = 2 ,
NsoSegmentType_Count = 3 ///< Total values supported by this enum.
} NsoSegmentType ;
typedef struct {
u8 type ; ///< NsoSegmentType.
const char * name ; ///< Pointer to a string that holds the segment name.
NsoSegmentInfo info ; ///< Copied from the NSO header.
u8 * data ; ///< Dynamically allocated buffer with the decompressed segment data.
} NsoSegment ;
/* Global variables. */
# if LOG_LEVEL < LOG_LEVEL_NONE
static const char * g_nsoSegmentTypeNames [ NsoSegmentType_Count ] = {
[ NsoSegmentType_Text ] = " .text " ,
2024-08-08 11:27:54 +01:00
[ NsoSegmentType_RoData ] = " .rodata " ,
2024-08-06 21:57:54 +01:00
[ NsoSegmentType_Data ] = " .data " ,
} ;
# endif
2020-10-11 16:22:26 +01:00
/* Function prototypes. */
static bool nsoGetModuleName ( NsoContext * nso_ctx ) ;
2024-08-06 21:57:54 +01:00
static bool nsoGetSegment ( NsoContext * nso_ctx , NsoSegment * out , u8 type ) ;
NX_INLINE void nsoFreeSegment ( NsoSegment * segment ) ;
2024-08-08 11:27:54 +01:00
NX_INLINE bool nsoIsNnSdkVersionWithinSegment ( const NsoModStart * mod_start , const NsoSegment * segment , u32 nnsdk_version_memory_offset ) ;
static bool nsoGetNnSdkVersion ( NsoContext * nso_ctx , const NsoModStart * mod_start , const NsoSegment * segment , u32 nnsdk_version_memory_offset ) ;
2024-08-06 21:57:54 +01:00
2024-08-08 11:27:54 +01:00
static bool nsoGetModulePath ( NsoContext * nso_ctx , const NsoSegment * segment ) ;
2024-08-06 21:57:54 +01:00
2024-08-08 11:27:54 +01:00
static bool nsoGetSectionFromRoDataSegment ( NsoContext * nso_ctx , const NsoSectionInfo * section_info , const NsoSegment * segment , u8 * * out_ptr ) ;
2020-10-11 16:22:26 +01:00
bool nsoInitializeContext ( NsoContext * out , PartitionFileSystemContext * pfs_ctx , PartitionFileSystemEntry * pfs_entry )
{
2024-08-06 21:57:54 +01:00
NsoModStart mod_start = { 0 } ;
NsoSegment segment = { 0 } ;
2024-08-08 11:27:54 +01:00
u32 nnsdk_version_memory_offset = 0 ;
2024-08-06 21:57:54 +01:00
bool success = false , dump_nso_header = false , read_nnsdk_version = false ;
2022-07-05 02:04:28 +01:00
2022-09-12 19:19:10 +01:00
if ( ! out | | ! pfs_ctx | | ! ncaStorageIsValidContext ( & ( pfs_ctx - > storage_ctx ) ) | | ! pfs_ctx - > nca_fs_ctx - > nca_ctx | | \
pfs_ctx - > nca_fs_ctx - > nca_ctx - > content_type ! = NcmContentType_Program | | ! pfs_ctx - > offset | | ! pfs_ctx - > size | | ! pfs_ctx - > is_exefs | | \
2022-07-04 00:36:01 +01:00
pfs_ctx - > header_size < = sizeof ( PartitionFileSystemHeader ) | | ! pfs_ctx - > header | | ! pfs_entry )
2020-10-11 16:22:26 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2020-10-11 16:22:26 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
/* Free output context beforehand. */
nsoFreeContext ( out ) ;
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
/* Update output context. */
out - > pfs_ctx = pfs_ctx ;
out - > pfs_entry = pfs_entry ;
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
/* Get entry filename. */
2020-10-15 01:06:53 +01:00
if ( ! ( out - > nso_filename = pfsGetEntryName ( pfs_ctx , pfs_entry ) ) | | ! * ( out - > nso_filename ) )
2020-10-11 16:22:26 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid Partition FS entry filename! " ) ;
2020-10-11 16:22:26 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
/* Read NSO header. */
if ( ! pfsReadEntryData ( pfs_ctx , pfs_entry , & ( out - > nso_header ) , sizeof ( NsoHeader ) , 0 ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to read NSO \" %s \" header! " , out - > nso_filename ) ; ;
2020-10-11 16:22:26 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
dump_nso_header = true ;
2020-10-11 16:22:26 +01:00
/* Verify NSO header. */
if ( __builtin_bswap32 ( out - > nso_header . magic ) ! = NSO_HEADER_MAGIC )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid NSO \" %s \" header magic word! (0x%08X != 0x%08X). " , out - > nso_filename , __builtin_bswap32 ( out - > nso_header . magic ) , __builtin_bswap32 ( NSO_HEADER_MAGIC ) ) ;
2020-10-11 16:22:26 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
# define NSO_VERIFY_SEGMENT_INFO(name, flag) \
if ( out - > nso_header . name # # _segment_info . file_offset < sizeof ( NsoHeader ) | | ! out - > nso_header . name # # _segment_info . size | | \
( ( out - > nso_header . flags & NsoFlags_ # # flag # # Compress ) & & ( ! out - > nso_header . name # # _file_size | | out - > nso_header . name # # _file_size > out - > nso_header . name # # _segment_info . size ) ) | | \
( ! ( out - > nso_header . flags & NsoFlags_ # # flag # # Compress ) & & out - > nso_header . name # # _file_size ! = out - > nso_header . name # # _segment_info . size ) | | \
( out - > nso_header . name # # _segment_info . file_offset + out - > nso_header . name # # _file_size ) > pfs_entry - > size ) { \
LOG_MSG_ERROR ( " Invalid . " # name " segment offset/size for NSO \" %s \" ! (0x%X, 0x%X, 0x%X). " , out - > nso_filename , out - > nso_header . name # # _segment_info . file_offset , \
out - > nso_header . name # # _file_size , out - > nso_header . name # # _segment_info . size ) ; \
goto end ; \
2020-10-11 16:22:26 +01:00
}
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
# define NSO_VERIFY_RODATA_SECTION_INFO(name) \
if ( out - > nso_header . name # # _section_info . size & & ( out - > nso_header . name # # _section_info . offset + out - > nso_header . name # # _section_info . size ) > out - > nso_header . rodata_segment_info . size ) { \
LOG_MSG_ERROR ( " Invalid . " # name " section offset/size for NSO \" %s \" ! (0x%X, 0x%X). " , out - > nso_filename , out - > nso_header . name # # _section_info . offset , out - > nso_header . name # # _section_info . size ) ; \
goto end ; \
2020-10-11 16:22:26 +01:00
}
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
# define NSO_GET_RODATA_SECTION(name) \
do { \
2024-08-08 11:27:54 +01:00
if ( ! nsoGetSectionFromRoDataSegment ( out , & ( out - > nso_header . name # # _section_info ) , & segment , ( u8 * * ) & ( out - > rodata_ # # name # # _section ) ) ) goto end ; \
2024-08-06 21:57:54 +01:00
out - > rodata_ # # name # # _section_size = out - > nso_header . name # # _section_info . size ; \
} while ( 0 )
/* Verify NSO segment info. */
NSO_VERIFY_SEGMENT_INFO ( text , Text ) ;
NSO_VERIFY_SEGMENT_INFO ( rodata , Ro ) ;
NSO_VERIFY_SEGMENT_INFO ( data , Data ) ;
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
/* Verify NSO module name properties. */
2021-03-07 23:22:49 +00:00
if ( out - > nso_header . module_name_size > 1 & & ( out - > nso_header . module_name_offset < sizeof ( NsoHeader ) | | ( out - > nso_header . module_name_offset + out - > nso_header . module_name_size ) > pfs_entry - > size ) )
2020-10-11 16:22:26 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid module name offset/size for NSO \" %s \" ! (0x%X, 0x%X). " , out - > nso_filename , out - > nso_header . module_name_offset , out - > nso_header . module_name_size ) ;
2021-03-07 23:22:49 +00:00
goto end ;
2020-10-11 16:22:26 +01:00
}
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
/* Verify section info blocks for the .rodata segment. */
NSO_VERIFY_RODATA_SECTION_INFO ( api_info ) ;
NSO_VERIFY_RODATA_SECTION_INFO ( dynstr ) ;
NSO_VERIFY_RODATA_SECTION_INFO ( dynsym ) ;
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
/* Get module name. */
if ( ! nsoGetModuleName ( out ) ) goto end ;
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
/* Get .text segment. */
if ( ! nsoGetSegment ( out , & segment , NsoSegmentType_Text ) ) goto end ;
/* Get NsoModStart block. */
memcpy ( & mod_start , segment . data , sizeof ( NsoModStart ) ) ;
2024-08-08 11:27:54 +01:00
/* Check if a NsoNnSdkVersion block exists within this NRO. */
read_nnsdk_version = ( ( mod_start . version & 1 ) ! = 0 & & mod_start . nnsdk_version_offset > = ( s32 ) sizeof ( NsoModStart ) ) ;
if ( read_nnsdk_version )
{
/* Calculate memory offset for the NsoNnSdkVersion block. */
nnsdk_version_memory_offset = ( segment . info . memory_offset + ( u32 ) mod_start . nnsdk_version_offset ) ;
2024-08-06 21:57:54 +01:00
2024-08-08 11:27:54 +01:00
/* Check if the NsoNnSdkVersion block is located within the .text segment. */
/* If so, we'll retrieve it immediately. */
if ( nsoIsNnSdkVersionWithinSegment ( & mod_start , & segment , nnsdk_version_memory_offset ) & & ! nsoGetNnSdkVersion ( out , & mod_start , & segment , nnsdk_version_memory_offset ) ) goto end ;
}
2024-08-06 21:57:54 +01:00
2020-10-11 16:22:26 +01:00
/* Get .rodata segment. */
2024-08-08 11:27:54 +01:00
if ( ! nsoGetSegment ( out , & segment , NsoSegmentType_RoData ) ) goto end ;
2022-07-05 02:04:28 +01:00
2024-08-08 11:27:54 +01:00
/* Check if we didn't read the NsoNnSdkVersion block from the .text segment. */
2024-08-06 21:57:54 +01:00
if ( read_nnsdk_version & & ! out - > nnsdk_version )
{
2024-08-08 11:27:54 +01:00
/* Check if the NsoNnSdkVersion block is located within the .rodata segment. */
if ( ! nsoIsNnSdkVersionWithinSegment ( & mod_start , & segment , nnsdk_version_memory_offset ) )
2024-08-06 21:57:54 +01:00
{
LOG_MSG_ERROR ( " nnSdk version struct not located within .text or .rodata segments in NSO \" %s \" . " , out - > nso_filename ) ;
goto end ;
}
2022-07-05 02:04:28 +01:00
2024-08-08 11:27:54 +01:00
/* Retrieve NsoNnSdkVersion block from the .rodata segment. */
if ( ! nsoGetNnSdkVersion ( out , & mod_start , & segment , nnsdk_version_memory_offset ) ) goto end ;
2024-08-06 21:57:54 +01:00
}
2022-07-05 02:04:28 +01:00
2024-08-08 11:27:54 +01:00
/* Get module path from the .rodata segment. */
if ( ! nsoGetModulePath ( out , & segment ) ) goto end ;
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
/* Get sections from the .rodata segment. */
NSO_GET_RODATA_SECTION ( api_info ) ;
NSO_GET_RODATA_SECTION ( dynstr ) ;
NSO_GET_RODATA_SECTION ( dynsym ) ;
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
success = true ;
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
# undef NSO_GET_RODATA_SECTION
# undef NSO_VERIFY_RODATA_SECTION_INFO
# undef NSO_VERIFY_SEGMENT_INFO
2020-10-11 16:22:26 +01:00
end :
2024-08-06 21:57:54 +01:00
nsoFreeSegment ( & segment ) ;
2022-07-05 02:04:28 +01:00
2021-03-07 23:22:49 +00:00
if ( ! success )
{
2022-07-12 17:34:49 +01:00
if ( dump_nso_header ) LOG_DATA_DEBUG ( & ( out - > nso_header ) , sizeof ( NsoHeader ) , " NSO header dump: " ) ;
2022-07-05 02:04:28 +01:00
2021-03-07 23:22:49 +00:00
nsoFreeContext ( out ) ;
}
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
return success ;
}
static bool nsoGetModuleName ( NsoContext * nso_ctx )
{
2020-10-12 21:35:47 +01:00
if ( nso_ctx - > nso_header . module_name_offset < sizeof ( NsoHeader ) | | nso_ctx - > nso_header . module_name_size < = 1 ) return true ;
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
/* Allocate memory for the module name. */
2024-08-06 21:57:54 +01:00
nso_ctx - > module_name = calloc ( nso_ctx - > nso_header . module_name_size + 1 , sizeof ( char ) ) ;
2020-10-11 16:22:26 +01:00
if ( ! nso_ctx - > module_name )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to allocate memory for NSO \" %s \" module name! " , nso_ctx - > nso_filename ) ;
2020-10-11 16:22:26 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
/* Read module name string. */
2024-08-06 21:57:54 +01:00
if ( ! pfsReadEntryData ( nso_ctx - > pfs_ctx , nso_ctx - > pfs_entry , nso_ctx - > module_name , nso_ctx - > nso_header . module_name_size , nso_ctx - > nso_header . module_name_offset ) )
2020-10-11 16:22:26 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to read NSO \" %s \" module name string! " , nso_ctx - > nso_filename ) ;
2020-10-11 16:22:26 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
return true ;
}
2024-08-06 21:57:54 +01:00
static bool nsoGetSegment ( NsoContext * nso_ctx , NsoSegment * out , u8 type )
2020-10-11 16:22:26 +01:00
{
2024-08-06 21:57:54 +01:00
if ( ! nso_ctx | | ! out | | type > = NsoSegmentType_Count )
{
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
return false ;
}
const char * segment_name = g_nsoSegmentTypeNames [ type ] ;
const NsoSegmentInfo * segment_info = ( type = = NsoSegmentType_Text ? & ( nso_ctx - > nso_header . text_segment_info ) : \
2024-08-08 11:27:54 +01:00
( type = = NsoSegmentType_RoData ? & ( nso_ctx - > nso_header . rodata_segment_info ) : & ( nso_ctx - > nso_header . data_segment_info ) ) ) ;
2024-08-06 21:57:54 +01:00
u32 segment_file_size = ( type = = NsoSegmentType_Text ? nso_ctx - > nso_header . text_file_size : \
2024-08-08 11:27:54 +01:00
( type = = NsoSegmentType_RoData ? nso_ctx - > nso_header . rodata_file_size : nso_ctx - > nso_header . data_file_size ) ) ;
2024-08-06 21:57:54 +01:00
const u8 * segment_hash = ( type = = NsoSegmentType_Text ? nso_ctx - > nso_header . text_segment_hash : \
2024-08-08 11:27:54 +01:00
( type = = NsoSegmentType_RoData ? nso_ctx - > nso_header . rodata_segment_hash : nso_ctx - > nso_header . data_segment_hash ) ) ;
2024-08-06 21:57:54 +01:00
2020-10-11 18:23:58 +01:00
int lz4_res = 0 ;
2024-08-06 21:57:54 +01:00
bool compressed = ( nso_ctx - > nso_header . flags & BIT ( type ) ) , verify = ( nso_ctx - > nso_header . flags & BIT ( type + 3 ) ) ;
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
u8 * buf = NULL ;
2024-08-08 11:27:54 +01:00
u32 buf_size = ( compressed ? LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE ( segment_info - > size ) : segment_info - > size ) ;
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
u8 * read_ptr = NULL ;
2024-08-08 11:27:54 +01:00
u32 read_size = ( compressed ? segment_file_size : segment_info - > size ) ;
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
u8 hash [ SHA256_HASH_SIZE ] = { 0 } ;
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
bool success = false ;
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
/* Clear output struct. */
nsoFreeSegment ( out ) ;
/* Allocate memory for the segment buffer. */
if ( ! ( buf = calloc ( buf_size , sizeof ( u8 ) ) ) )
2020-10-11 16:22:26 +01:00
{
2024-08-08 11:27:54 +01:00
LOG_MSG_ERROR ( " Failed to allocate 0x%X bytes for the %s segment in NSO \" %s \" ! " , buf_size , segment_name , nso_ctx - > nso_filename ) ;
2020-10-11 16:22:26 +01:00
return NULL ;
}
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
read_ptr = ( compressed ? ( buf + ( buf_size - segment_file_size ) ) : buf ) ;
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
/* Read segment data. */
if ( ! pfsReadEntryData ( nso_ctx - > pfs_ctx , nso_ctx - > pfs_entry , read_ptr , read_size , segment_info - > file_offset ) )
2020-10-11 16:22:26 +01:00
{
2024-08-06 21:57:54 +01:00
LOG_MSG_ERROR ( " Failed to read %s segment in NSO \" %s \" ! " , segment_name , nso_ctx - > nso_filename ) ;
2020-10-11 16:22:26 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
/* Decompress segment data in-place. */
if ( compressed & & ( lz4_res = LZ4_decompress_safe ( ( char * ) read_ptr , ( char * ) buf , ( int ) segment_file_size , ( int ) buf_size ) ) ! = ( int ) segment_info - > size )
2020-10-11 16:22:26 +01:00
{
2024-08-06 21:57:54 +01:00
LOG_MSG_ERROR ( " LZ4 decompression failed for %s segment in NSO \" %s \" ! (%d). " , segment_name , nso_ctx - > nso_filename , lz4_res ) ;
goto end ;
2020-10-11 16:22:26 +01:00
}
2022-07-05 02:04:28 +01:00
2020-10-11 18:23:58 +01:00
if ( verify )
{
2024-08-06 21:57:54 +01:00
/* Verify segment data hash. */
sha256CalculateHash ( hash , buf , segment_info - > size ) ;
if ( memcmp ( hash , segment_hash , SHA256_HASH_SIZE ) ! = 0 )
2020-10-11 18:23:58 +01:00
{
2024-08-06 21:57:54 +01:00
LOG_MSG_ERROR ( " %s segment checksum mismatch for NSO \" %s \" ! " , segment_name , nso_ctx - > nso_filename ) ;
2020-10-11 18:23:58 +01:00
goto end ;
}
}
2022-07-05 02:04:28 +01:00
2024-08-06 21:57:54 +01:00
/* Fill output struct. */
out - > type = type ;
out - > name = segment_name ;
memcpy ( & ( out - > info ) , segment_info , sizeof ( NsoSegmentInfo ) ) ;
out - > data = buf ;
2020-10-11 16:22:26 +01:00
success = true ;
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
end :
2024-08-06 21:57:54 +01:00
if ( ! success & & buf ) free ( buf ) ;
return success ;
}
NX_INLINE void nsoFreeSegment ( NsoSegment * segment )
{
if ( ! segment ) return ;
if ( segment - > data ) free ( segment - > data ) ;
memset ( segment , 0 , sizeof ( NsoSegment ) ) ;
}
2024-08-08 11:27:54 +01:00
NX_INLINE bool nsoIsNnSdkVersionWithinSegment ( const NsoModStart * mod_start , const NsoSegment * segment , u32 nnsdk_version_memory_offset )
2024-08-06 21:57:54 +01:00
{
2024-08-08 11:27:54 +01:00
return ( mod_start & & segment & & nnsdk_version_memory_offset > = segment - > info . memory_offset & & \
( nnsdk_version_memory_offset + sizeof ( NsoNnSdkVersion ) ) < = ( segment - > info . memory_offset + segment - > info . size ) ) ;
2024-08-06 21:57:54 +01:00
}
2024-08-08 11:27:54 +01:00
static bool nsoGetNnSdkVersion ( NsoContext * nso_ctx , const NsoModStart * mod_start , const NsoSegment * segment , u32 nnsdk_version_memory_offset )
2024-08-06 21:57:54 +01:00
{
if ( ! nso_ctx | | ! mod_start | | ! segment | | ! segment - > data )
2020-10-11 16:22:26 +01:00
{
2024-08-06 21:57:54 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
return false ;
}
2024-08-08 11:27:54 +01:00
/* Return immediately if the NsoNnSdkVersion block has already been retrieved. */
if ( nso_ctx - > nnsdk_version ) return true ;
2024-08-06 21:57:54 +01:00
2024-08-08 11:27:54 +01:00
/* Make sure we're targetting the right NSO segment. */
if ( ! nsoIsNnSdkVersionWithinSegment ( mod_start , segment , nnsdk_version_memory_offset ) )
2024-08-06 21:57:54 +01:00
{
LOG_MSG_ERROR ( " nnSdk version struct isn't located within %s segment in NSO \" %s \" ! ([0x%X, 0x%X] not within [0x%X, 0x%X]). " , segment - > name , nso_ctx - > nso_filename , \
2024-08-08 11:27:54 +01:00
nnsdk_version_memory_offset , nnsdk_version_memory_offset + ( u32 ) sizeof ( NsoNnSdkVersion ) , segment - > info . memory_offset , segment - > info . memory_offset + segment - > info . size ) ;
2024-08-06 21:57:54 +01:00
return false ;
2020-10-11 16:22:26 +01:00
}
2022-07-05 02:04:28 +01:00
2024-08-08 11:27:54 +01:00
/* Allocate memory for the NsoNnSdkVersion block. */
2024-08-06 21:57:54 +01:00
nso_ctx - > nnsdk_version = malloc ( sizeof ( NsoNnSdkVersion ) ) ;
if ( ! nso_ctx - > nnsdk_version )
{
LOG_MSG_ERROR ( " Failed to allocate memory for NSO \" %s \" nnSdk version struct! " , nso_ctx - > nso_filename ) ;
return false ;
}
2024-08-08 11:27:54 +01:00
/* Calculate segment-relative offset for the NsoNnSdkVersion block and copy its data. */
u32 nnsdk_version_segment_offset = ( nnsdk_version_memory_offset - segment - > info . memory_offset ) ;
memcpy ( nso_ctx - > nnsdk_version , segment - > data + nnsdk_version_segment_offset , sizeof ( NsoNnSdkVersion ) ) ;
2024-08-06 21:57:54 +01:00
LOG_MSG_DEBUG ( " nnSdk version (NSO \" %s \" , %s segment, virtual offset 0x%X, physical offset 0x%X): %u.%u.%u. " , nso_ctx - > nso_filename , segment - > name , \
2024-08-08 11:27:54 +01:00
nnsdk_version_memory_offset , nnsdk_version_segment_offset , nso_ctx - > nnsdk_version - > major , nso_ctx - > nnsdk_version - > minor , nso_ctx - > nnsdk_version - > micro ) ;
2024-08-06 21:57:54 +01:00
return true ;
2020-10-11 16:22:26 +01:00
}
2024-08-08 11:27:54 +01:00
static bool nsoGetModulePath ( NsoContext * nso_ctx , const NsoSegment * segment )
2020-10-11 16:22:26 +01:00
{
2024-08-08 11:27:54 +01:00
if ( ! nso_ctx | | ! segment | | segment - > type ! = NsoSegmentType_RoData | | ! segment - > data )
2024-08-06 21:57:54 +01:00
{
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
return false ;
}
2024-08-08 11:27:54 +01:00
/* Get data from the start of the .rodata segment. */
const NsoRoDataStart * rodata_start = ( const NsoRoDataStart * ) segment - > data ;
/* Perform sanity checks. */
if ( ( nso_ctx - > nso_header . text_segment_info . memory_offset + rodata_start - > data_segment_offset ) = = nso_ctx - > nso_header . data_segment_info . memory_offset | | \
rodata_start - > module_path . zero ! = 0 | | ! rodata_start - > module_path . path_length | | ! rodata_start - > module_path . path [ 0 ] ) return true ;
2022-07-05 02:04:28 +01:00
2024-08-08 11:27:54 +01:00
/* Allocate memory for the module path string. */
nso_ctx - > module_path = calloc ( rodata_start - > module_path . path_length + 1 , sizeof ( char ) ) ;
if ( ! nso_ctx - > module_path )
2020-10-11 16:22:26 +01:00
{
2024-08-08 11:27:54 +01:00
LOG_MSG_ERROR ( " Failed to allocate memory for NSO \" %s \" module path! " , nso_ctx - > nso_filename ) ;
2020-10-11 16:22:26 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2024-08-08 11:27:54 +01:00
/* Copy module path string. */
sprintf ( nso_ctx - > module_path , " %.*s " , ( int ) rodata_start - > module_path . path_length , rodata_start - > module_path . path ) ;
LOG_MSG_DEBUG ( " Module path (NSO \" %s \" ): \" %s \" . " , nso_ctx - > nso_filename , nso_ctx - > module_path ) ;
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
return true ;
}
2020-10-10 16:35:14 +01:00
2024-08-08 11:27:54 +01:00
static bool nsoGetSectionFromRoDataSegment ( NsoContext * nso_ctx , const NsoSectionInfo * section_info , const NsoSegment * segment , u8 * * out_ptr )
2020-10-11 16:22:26 +01:00
{
2024-08-08 11:27:54 +01:00
if ( ! nso_ctx | | ! section_info | | ! segment | | segment - > type ! = NsoSegmentType_RoData | | ! segment - > data | | ! out_ptr )
2024-08-06 21:57:54 +01:00
{
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
return false ;
}
/* Return immediately if the desired section is not within the .rodata segment. */
if ( ! section_info - > size | | ( section_info - > offset + section_info - > size ) > segment - > info . size ) return true ;
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
/* Allocate memory for the desired .rodata section. */
2024-08-06 21:57:54 +01:00
if ( ! ( * out_ptr = malloc ( section_info - > size ) ) )
2020-10-11 16:22:26 +01:00
{
2024-08-06 21:57:54 +01:00
LOG_MSG_ERROR ( " Failed to allocate 0x%X bytes for section at .rodata segment offset 0x%X in NSO \" %s \" ! " , section_info - > size , section_info - > offset , nso_ctx - > nso_filename ) ;
2020-10-11 16:22:26 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
/* Copy .rodata section data. */
2024-08-06 21:57:54 +01:00
memcpy ( * out_ptr , segment - > data + section_info - > offset , section_info - > size ) ;
2022-07-05 02:04:28 +01:00
2020-10-11 16:22:26 +01:00
return true ;
}