1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-26 20:22:17 +00:00
nxdumptool/source/nso.c

439 lines
17 KiB
C
Raw Normal View History

2019-06-28 21:13:54 +01:00
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "nso.h"
#include "lz4.h"
#include "util.h"
#include "ui.h"
/* Extern variables */
extern int breaks;
extern int font_height;
extern char strbuf[NAME_BUF_LEN * 4];
/* Statically allocated variables */
static u8 *nsoBinaryData = NULL;
static u64 nsoBinaryDataSize = 0;
static u64 nsoBinaryTextSectionOffset = 0;
static u64 nsoBinaryTextSectionSize = 0;
static u64 nsoBinaryRodataSectionOffset = 0;
static u64 nsoBinaryRodataSectionSize = 0;
static u64 nsoBinaryDataSectionOffset = 0;
static u64 nsoBinaryDataSectionSize = 0;
void freeNsoBinaryData()
{
if (nsoBinaryData)
{
free(nsoBinaryData);
nsoBinaryData = NULL;
}
nsoBinaryDataSize = 0;
nsoBinaryTextSectionOffset = 0;
nsoBinaryTextSectionSize = 0;
nsoBinaryRodataSectionOffset = 0;
nsoBinaryRodataSectionSize = 0;
nsoBinaryDataSectionOffset = 0;
nsoBinaryDataSectionSize = 0;
}
bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes128CtrContext *aes_ctx, u64 nso_base_offset, nso_header_t *nsoHeader)
{
if (!ncmStorage || !ncaId || !aes_ctx || !nso_base_offset || !nsoHeader)
{
uiDrawString("Error: invalid parameters to load .text, .rodata and .data sections from NSO in Program NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
return false;
}
u8 i;
u8 *nsoTextSection = NULL;
u64 nsoTextSectionSize = 0;
u8 *nsoRodataSection = NULL;
u64 nsoRodataSectionSize = 0;
u8 *nsoDataSection = NULL;
u64 nsoDataSectionSize = 0;
u8 *curCompressedSection;
u64 curCompressedSectionSize;
u64 curCompressedSectionOffset;
u8 curSectionFlag;
u8 *curDecompressedSection;
u64 curDecompressedSectionSize;
bool success = true;
freeNsoBinaryData();
for(i = 0; i < 3; i++)
{
curCompressedSection = NULL;
curCompressedSectionSize = (i == 0 ? (u64)nsoHeader->text_compressed_size : (i == 1 ? (u64)nsoHeader->rodata_compressed_size : (u64)nsoHeader->data_compressed_size));
curCompressedSectionOffset = (nso_base_offset + (i == 0 ? (u64)nsoHeader->text_segment_header.file_offset : (i == 1 ? (u64)nsoHeader->rodata_segment_header.file_offset : (u64)nsoHeader->data_segment_header.file_offset)));
curSectionFlag = (1 << i);
curDecompressedSection = NULL;
curDecompressedSectionSize = (i == 0 ? (u64)nsoHeader->text_segment_header.decompressed_size : (i == 1 ? (u64)nsoHeader->rodata_segment_header.decompressed_size : (u64)nsoHeader->data_segment_header.decompressed_size));
// Load section
curCompressedSection = malloc(curCompressedSectionSize);
if (!curCompressedSection)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to allocate memory for the compressed %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data")));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
success = false;
break;
}
if (!processNcaCtrSectionBlock(ncmStorage, ncaId, aes_ctx, curCompressedSectionOffset, curCompressedSection, curCompressedSectionSize, false))
{
breaks++;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read 0x%016lX bytes %s section from NSO in Program NCA!", curCompressedSectionSize, (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data")));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
free(curCompressedSection);
success = false;
break;
}
if (nsoHeader->flags & curSectionFlag)
{
if (curDecompressedSectionSize <= curCompressedSectionSize)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid decompressed size for %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data")));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
free(curCompressedSection);
success = false;
break;
}
// Uncompress section
curDecompressedSection = malloc(curDecompressedSectionSize);
if (!curDecompressedSection)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to allocate memory for the decompressed %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data")));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
free(curCompressedSection);
success = false;
break;
}
if (LZ4_decompress_safe((const char*)curCompressedSection, (char*)curDecompressedSection, (int)curCompressedSectionSize, (int)curDecompressedSectionSize) != (int)curDecompressedSectionSize)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to allocate memory for the decompressed %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data")));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
free(curDecompressedSection);
free(curCompressedSection);
success = false;
break;
}
free(curCompressedSection);
switch(i)
{
case 0:
nsoTextSection = curDecompressedSection;
nsoTextSectionSize = curDecompressedSectionSize;
break;
case 1:
nsoRodataSection = curDecompressedSection;
nsoRodataSectionSize = curDecompressedSectionSize;
break;
case 2:
nsoDataSection = curDecompressedSection;
nsoDataSectionSize = curDecompressedSectionSize;
break;
default:
break;
}
} else {
switch(i)
{
case 0:
nsoTextSection = curCompressedSection;
nsoTextSectionSize = curCompressedSectionSize;
break;
case 1:
nsoRodataSection = curCompressedSection;
nsoRodataSectionSize = curCompressedSectionSize;
break;
case 2:
nsoDataSection = curCompressedSection;
nsoDataSectionSize = curCompressedSectionSize;
break;
default:
break;
}
}
}
curCompressedSection = curDecompressedSection = NULL;
if (success)
{
// Calculate full binary size
u64 finalTextSectionSize = nsoTextSectionSize;
u64 finalRodataSectionSize = nsoRodataSectionSize;
nsoBinaryDataSize = finalTextSectionSize = nsoTextSectionSize;
if ((u64)nsoHeader->rodata_segment_header.memory_offset > nsoBinaryDataSize)
{
nsoBinaryDataSize += ((u64)nsoHeader->rodata_segment_header.memory_offset - nsoBinaryDataSize);
} else
if ((u64)nsoHeader->rodata_segment_header.memory_offset < nsoBinaryDataSize)
{
finalTextSectionSize -= (nsoBinaryDataSize - (u64)nsoHeader->rodata_segment_header.memory_offset);
nsoBinaryDataSize -= (nsoBinaryDataSize - (u64)nsoHeader->rodata_segment_header.memory_offset);
}
nsoBinaryDataSize += nsoRodataSectionSize;
if ((u64)nsoHeader->data_segment_header.memory_offset > nsoBinaryDataSize)
{
nsoBinaryDataSize += ((u64)nsoHeader->data_segment_header.memory_offset - nsoBinaryDataSize);
} else
if ((u64)nsoHeader->data_segment_header.memory_offset < nsoBinaryDataSize)
{
finalRodataSectionSize -= (nsoBinaryDataSize - (u64)nsoHeader->data_segment_header.memory_offset);
nsoBinaryDataSize -= (nsoBinaryDataSize - (u64)nsoHeader->data_segment_header.memory_offset);
}
nsoBinaryDataSize += nsoDataSectionSize;
nsoBinaryData = calloc(nsoBinaryDataSize, sizeof(u8));
if (nsoBinaryData)
{
memcpy(nsoBinaryData, nsoTextSection, finalTextSectionSize);
memcpy(nsoBinaryData + (u64)nsoHeader->rodata_segment_header.memory_offset, nsoRodataSection, finalRodataSectionSize);
memcpy(nsoBinaryData + (u64)nsoHeader->data_segment_header.memory_offset, nsoDataSection, nsoDataSectionSize);
nsoBinaryTextSectionOffset = 0;
nsoBinaryTextSectionSize = finalTextSectionSize;
nsoBinaryRodataSectionOffset = (u64)nsoHeader->rodata_segment_header.memory_offset;
nsoBinaryRodataSectionSize = finalRodataSectionSize;
nsoBinaryDataSectionOffset = (u64)nsoHeader->data_segment_header.memory_offset;
nsoBinaryDataSectionSize = nsoDataSectionSize;
} else {
uiDrawString("Error: unable to allocate memory for full decompressed NSO in Program NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
nsoBinaryDataSize = 0;
success = false;
}
}
if (nsoTextSection) free(nsoTextSection);
if (nsoRodataSection) free(nsoRodataSection);
if (nsoDataSection) free(nsoDataSection);
return success;
}
bool retrieveMiddlewareListFromNso(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes128CtrContext *aes_ctx, const char *nso_filename, u64 nso_base_offset, nso_header_t *nsoHeader, char *programInfoXml)
{
if (!ncmStorage || !ncaId || !aes_ctx || !nso_filename || !strlen(nso_filename) || !nso_base_offset || !nsoHeader || !programInfoXml)
{
uiDrawString("Error: invalid parameters to retrieve middleware list from NSO in Program NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
return false;
}
u64 i;
char tmp[NAME_BUF_LEN] = {'\0'};
if (!loadNsoBinaryData(ncmStorage, ncaId, aes_ctx, nso_base_offset, nsoHeader)) return false;
for(i = 0; i < nsoBinaryRodataSectionSize; i++)
{
char *curStr = ((char*)nsoBinaryData + nsoBinaryRodataSectionOffset + i);
if (strncmp(curStr, "SDK MW+", 7) != 0) continue;
// Found a match
char *mwDev = (curStr + 7);
char *mwName = (strchr(mwDev, '+') + 1);
// Filter nnSdk entries
if (!strncasecmp(mwName, "NintendoSdk_nnSdk", 17))
{
i += strlen(curStr);
continue;
}
sprintf(tmp, " <Middleware>\n" \
" <ModuleName>%s</ModuleName>\n" \
" <VenderName>%.*s</VenderName>\n" \
" <NsoName>%s</NsoName>\n" \
" </Middleware>\n", \
mwName, \
(int)(mwName - mwDev - 1), mwDev, \
nso_filename);
strcat(programInfoXml, tmp);
// Update counter
i += strlen(curStr);
}
freeNsoBinaryData();
return true;
}
bool retrieveSymbolsListFromNso(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes128CtrContext *aes_ctx, const char *nso_filename, u64 nso_base_offset, nso_header_t *nsoHeader, char *programInfoXml)
{
if (!ncmStorage || !ncaId || !aes_ctx || !nso_filename || !strlen(nso_filename) || !nso_base_offset || !nsoHeader || !programInfoXml)
{
uiDrawString("Error: invalid parameters to retrieve symbols list from NSO in Program NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
return false;
}
u64 i;
bool success = false;
char tmp[NAME_BUF_LEN] = {'\0'};
u32 mod_magic_offset;
u32 mod_magic;
s32 dynamic_section_offset;
bool armv7;
u64 dynamic_block_size;
u64 dynamic_block_cnt;
bool found_strtab = false;
u64 symbol_str_table_offset = 0;
bool found_symtab = false;
u64 symbol_table_offset = 0;
bool found_strsz = false;
u64 symbol_str_table_size = 0;
char *symbol_str_table = NULL;
u64 cur_symbol_table_offset = 0;
if (!loadNsoBinaryData(ncmStorage, ncaId, aes_ctx, nso_base_offset, nsoHeader)) return false;
mod_magic_offset = *((u32*)(&(nsoBinaryData[0x04])));
mod_magic = *((u32*)(&(nsoBinaryData[mod_magic_offset])));
dynamic_section_offset = ((s32)mod_magic_offset + *((s32*)(&(nsoBinaryData[mod_magic_offset + 0x04]))));
if (bswap_32(mod_magic) != MOD_MAGIC)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid MOD0 magic word in decompressed NSO from Program NCA! (0x%08X)", bswap_32(mod_magic));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
}
armv7 = (*((u64*)(&(nsoBinaryData[dynamic_section_offset]))) > (u64)0xFFFFFFFF || *((u64*)(&(nsoBinaryData[dynamic_section_offset + 0x10]))) > (u64)0xFFFFFFFF);
// Read dynamic section
dynamic_block_size = (armv7 ? 0x08 : 0x10);
dynamic_block_cnt = ((nsoBinaryDataSize - dynamic_section_offset) / dynamic_block_size);
for(i = 0; i < dynamic_block_cnt; i++)
{
if ((nsoBinaryDataSize - dynamic_section_offset - (i * dynamic_block_size)) < dynamic_block_size) break;
u64 tag = (armv7 ? (u64)(*((u32*)(&(nsoBinaryData[dynamic_section_offset + (i * dynamic_block_size)])))) : *((u64*)(&(nsoBinaryData[dynamic_section_offset + (i * dynamic_block_size)]))));
u64 val = (armv7 ? (u64)(*((u32*)(&(nsoBinaryData[dynamic_section_offset + (i * dynamic_block_size) + 0x04])))) : *((u64*)(&(nsoBinaryData[dynamic_section_offset + (i * dynamic_block_size) + 0x08]))));
if (!tag) break;
if (tag == DT_STRTAB && !found_strtab)
{
// Retrieve symbol string table offset
symbol_str_table_offset = val;
found_strtab = true;
}
if (tag == DT_SYMTAB && !found_symtab)
{
// Retrieve symbol table offset
symbol_table_offset = val;
found_symtab = true;
}
if (tag == DT_STRSZ && !found_strsz)
{
// Retrieve symbol string table size
symbol_str_table_size = val;
found_strsz = true;
}
if (found_strtab && found_symtab && found_strsz) break;
}
if (!found_strtab || !found_symtab || !found_strsz)
{
// Nothing to do here if we can't find what we need
success = true;
goto out;
}
// Point to the symbol string table
symbol_str_table = ((char*)nsoBinaryData + symbol_str_table_offset);
// Retrieve symbol list
cur_symbol_table_offset = symbol_table_offset;
while(true)
{
if (symbol_table_offset < symbol_str_table_offset && cur_symbol_table_offset >= symbol_str_table_offset) break;
u32 st_name = *((u32*)(&(nsoBinaryData[cur_symbol_table_offset])));
u8 st_info = (armv7 ? nsoBinaryData[cur_symbol_table_offset + 0x0C] : nsoBinaryData[cur_symbol_table_offset + 0x04]);
//u8 st_other = (armv7 ? nsoBinaryData[cur_symbol_table_offset + 0x0D] : nsoBinaryData[cur_symbol_table_offset + 0x05]);
u16 st_shndx = (armv7 ? *((u16*)(&(nsoBinaryData[cur_symbol_table_offset + 0x0E]))) : *((u16*)(&(nsoBinaryData[cur_symbol_table_offset + 0x06]))));
u64 st_value = (armv7 ? (u64)(*((u32*)(&(nsoBinaryData[cur_symbol_table_offset + 0x04])))) : *((u64*)(&(nsoBinaryData[cur_symbol_table_offset + 0x08]))));
//u64 st_size = (armv7 ? (u64)(*((u32*)(&(nsoBinaryData[cur_symbol_table_offset + 0x08])))) : *((u64*)(&(nsoBinaryData[cur_symbol_table_offset + 0x10]))));
//u8 st_vis = (st_other & 0x03);
u8 st_type = (st_info & 0x0F);
//u8 st_bind = (st_info >> 0x04);
if (st_name >= symbol_str_table_size) break;
cur_symbol_table_offset += (armv7 ? 0x10 : 0x18);
// TO-DO: Add more filters?
if (!st_shndx && !st_value && st_type != ST_OBJECT)
{
sprintf(tmp, " <UnresolvedApi>\n" \
" <ApiName>%s</ApiName>\n" \
" <NsoName>%s</NsoName>\n" \
" </UnresolvedApi>\n", \
symbol_str_table + st_name, \
nso_filename);
strcat(programInfoXml, tmp);
}
}
success = true;
out:
freeNsoBinaryData();
return success;
}