mirror of
https://github.com/jakcron/nstool
synced 2024-11-15 10:16:42 +00:00
commit
4210c029b2
15 changed files with 1194 additions and 386 deletions
|
@ -6,7 +6,7 @@ Tools & Libraries for NX (Nintendo Switch).
|
||||||
|
|
||||||
# Tools
|
# Tools
|
||||||
|
|
||||||
* __nstool__ - read *.npdm, read/extract PartitionFS (PFS0|HFS0) blobs (including *.nsp), read/extract *.xci, read/extract *.nca, read *.cnmt, read *.nso
|
* __nstool__ - read *.npdm, read/extract PartitionFS (PFS0|HFS0) blobs (including *.nsp), read/extract RomFS blobs, read/extract *.xci, read/extract *.nca, read *.cnmt, read *.nso, read *.nro
|
||||||
|
|
||||||
# Libraries
|
# Libraries
|
||||||
|
|
||||||
|
|
147
lib/libnx/include/nx/NroHeader.h
Normal file
147
lib/libnx/include/nx/NroHeader.h
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
#pragma once
|
||||||
|
#include <nx/nro.h>
|
||||||
|
#include <fnd/MemoryBlob.h>
|
||||||
|
#include <fnd/List.h>
|
||||||
|
#include <fnd/ISerialiseableBinary.h>
|
||||||
|
|
||||||
|
namespace nx
|
||||||
|
{
|
||||||
|
class NroHeader :
|
||||||
|
public fnd::ISerialiseableBinary
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct sRoCrt
|
||||||
|
{
|
||||||
|
byte_t data[nro::kRoCrtSize];
|
||||||
|
|
||||||
|
void operator=(const sRoCrt& other)
|
||||||
|
{
|
||||||
|
memcpy(data, other.data, nro::kRoCrtSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const sRoCrt& other) const
|
||||||
|
{
|
||||||
|
return memcmp(data, other.data, nro::kRoCrtSize) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const sRoCrt& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sModuleId
|
||||||
|
{
|
||||||
|
byte_t data[nro::kModuleIdSize];
|
||||||
|
|
||||||
|
void operator=(const sModuleId& other)
|
||||||
|
{
|
||||||
|
memcpy(data, other.data, nro::kModuleIdSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const sModuleId& other) const
|
||||||
|
{
|
||||||
|
return memcmp(data, other.data, nro::kModuleIdSize) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const sModuleId& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sSection
|
||||||
|
{
|
||||||
|
uint32_t memory_offset;
|
||||||
|
uint32_t size;
|
||||||
|
|
||||||
|
void operator=(const sSection& other)
|
||||||
|
{
|
||||||
|
memory_offset = other.memory_offset;
|
||||||
|
size = other.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const sSection& other) const
|
||||||
|
{
|
||||||
|
return (memory_offset == other.memory_offset) \
|
||||||
|
&& (size == other.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const sSection& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
NroHeader();
|
||||||
|
NroHeader(const NroHeader& other);
|
||||||
|
NroHeader(const byte_t* bytes, size_t len);
|
||||||
|
|
||||||
|
bool operator==(const NroHeader& other) const;
|
||||||
|
bool operator!=(const NroHeader& other) const;
|
||||||
|
void operator=(const NroHeader& other);
|
||||||
|
|
||||||
|
// to be used after export
|
||||||
|
const byte_t* getBytes() const;
|
||||||
|
size_t getSize() const;
|
||||||
|
|
||||||
|
// export/import binary
|
||||||
|
void exportBinary();
|
||||||
|
void importBinary(const byte_t* bytes, size_t len);
|
||||||
|
|
||||||
|
// variables
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
const sRoCrt& getRoCrt() const;
|
||||||
|
void setRoCrt(const sRoCrt& ro_crt);
|
||||||
|
|
||||||
|
uint32_t getNroSize() const;
|
||||||
|
void setNroSize(uint32_t size);
|
||||||
|
|
||||||
|
const sSection& getTextInfo() const;
|
||||||
|
void setTextInfo(const sSection& info);
|
||||||
|
|
||||||
|
const sSection& getRoInfo() const;
|
||||||
|
void setRoInfo(const sSection& info);
|
||||||
|
|
||||||
|
const sSection& getDataInfo() const;
|
||||||
|
void setDataInfo(const sSection& info);
|
||||||
|
|
||||||
|
uint32_t getBssSize() const;
|
||||||
|
void setBssSize(uint32_t size);
|
||||||
|
|
||||||
|
const sModuleId& getModuleId() const;
|
||||||
|
void setModuleId(const sModuleId& id);
|
||||||
|
|
||||||
|
const sSection& getRoEmbeddedInfo() const;
|
||||||
|
void setRoEmbeddedInfo(const sSection& info);
|
||||||
|
|
||||||
|
const sSection& getRoDynStrInfo() const;
|
||||||
|
void setRoDynStrInfo(const sSection& info);
|
||||||
|
|
||||||
|
const sSection& getRoDynSymInfo() const;
|
||||||
|
void setRoDynSymInfo(const sSection& info);
|
||||||
|
private:
|
||||||
|
const std::string kModuleName = "NRO_HEADER";
|
||||||
|
|
||||||
|
// binary
|
||||||
|
fnd::MemoryBlob mBinaryBlob;
|
||||||
|
|
||||||
|
// data
|
||||||
|
sRoCrt mRoCrt;
|
||||||
|
uint32_t mNroSize;
|
||||||
|
sSection mTextInfo;
|
||||||
|
sSection mRoInfo;
|
||||||
|
sSection mDataInfo;
|
||||||
|
uint32_t mBssSize;
|
||||||
|
sModuleId mModuleId;
|
||||||
|
sSection mRoEmbeddedInfo;
|
||||||
|
sSection mRoDynStrInfo;
|
||||||
|
sSection mRoDynSymInfo;
|
||||||
|
|
||||||
|
// helpers
|
||||||
|
bool isEqual(const NroHeader& other) const;
|
||||||
|
void copyFrom(const NroHeader& other);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -8,12 +8,13 @@ namespace nx
|
||||||
enum SpecialSectionIndex
|
enum SpecialSectionIndex
|
||||||
{
|
{
|
||||||
SHN_UNDEF,
|
SHN_UNDEF,
|
||||||
SHN_LORESERVE = 65280,
|
SHN_EXPORT = 1,
|
||||||
SHN_LOPROC = 65280,
|
SHN_LORESERVE = 0xFF00,
|
||||||
SHN_HIPROC = 65311,
|
SHN_LOPROC = 0xFF00,
|
||||||
|
SHN_HIPROC = 0xFF1F,
|
||||||
SHN_LOOS,
|
SHN_LOOS,
|
||||||
SHN_HIOS = 65343,
|
SHN_HIOS = 0xFF3F,
|
||||||
SHN_ABS = 65521,
|
SHN_ABS = 0xFFF1,
|
||||||
SHN_COMMON,
|
SHN_COMMON,
|
||||||
SHN_HIRESERVE = 0xFFFF
|
SHN_HIRESERVE = 0xFFFF
|
||||||
};
|
};
|
||||||
|
|
248
lib/libnx/source/NroHeader.cpp
Normal file
248
lib/libnx/source/NroHeader.cpp
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
#include <nx/NroHeader.h>
|
||||||
|
|
||||||
|
nx::NroHeader::NroHeader()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
nx::NroHeader::NroHeader(const NroHeader& other)
|
||||||
|
{
|
||||||
|
copyFrom(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
nx::NroHeader::NroHeader(const byte_t* bytes, size_t len)
|
||||||
|
{
|
||||||
|
importBinary(bytes, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nx::NroHeader::operator==(const NroHeader& other) const
|
||||||
|
{
|
||||||
|
return isEqual(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nx::NroHeader::operator!=(const NroHeader& other) const
|
||||||
|
{
|
||||||
|
return !(*this != other);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NroHeader::operator=(const NroHeader& other)
|
||||||
|
{
|
||||||
|
copyFrom(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
const byte_t* nx::NroHeader::getBytes() const
|
||||||
|
{
|
||||||
|
return mBinaryBlob.getBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t nx::NroHeader::getSize() const
|
||||||
|
{
|
||||||
|
return mBinaryBlob.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NroHeader::exportBinary()
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "exportBinary() unsupported");
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NroHeader::importBinary(const byte_t* bytes, size_t len)
|
||||||
|
{
|
||||||
|
// check input data size
|
||||||
|
if (len < sizeof(sNroHeader))
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "NRO header size is too small");
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear internal members
|
||||||
|
clear();
|
||||||
|
|
||||||
|
// allocate internal local binary copy
|
||||||
|
mBinaryBlob.alloc(sizeof(sNroHeader));
|
||||||
|
memcpy(mBinaryBlob.getBytes(), bytes, mBinaryBlob.getSize());
|
||||||
|
|
||||||
|
// get sNroHeader ptr
|
||||||
|
const nx::sNroHeader* hdr = (const nx::sNroHeader*)mBinaryBlob.getBytes();
|
||||||
|
|
||||||
|
// check NRO signature
|
||||||
|
if (hdr->signature.get() != nro::kNroSig)
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "NRO header corrupt (unrecognised header signature)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// check NRO format version
|
||||||
|
if (hdr->format_version.get() != nro::kDefaultFormatVersion)
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "NRO header corrupt (unsupported format version)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// check NRO flags
|
||||||
|
if (hdr->flags.get() != 0)
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "NRO header corrupt (unsupported flag)");
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(mRoCrt.data, hdr->ro_crt, nro::kRoCrtSize);
|
||||||
|
mNroSize = hdr->size.get();
|
||||||
|
mTextInfo.memory_offset = hdr->text.memory_offset.get();
|
||||||
|
mTextInfo.size = hdr->text.size.get();
|
||||||
|
mRoInfo.memory_offset = hdr->ro.memory_offset.get();
|
||||||
|
mRoInfo.size = hdr->ro.size.get();
|
||||||
|
mDataInfo.memory_offset = hdr->data.memory_offset.get();
|
||||||
|
mDataInfo.size = hdr->data.size.get();
|
||||||
|
mBssSize = hdr->bss_size.get();
|
||||||
|
memcpy(mModuleId.data, hdr->module_id, nro::kModuleIdSize);
|
||||||
|
|
||||||
|
mRoEmbeddedInfo.memory_offset = hdr->embedded.memory_offset.get();
|
||||||
|
mRoEmbeddedInfo.size = hdr->embedded.size.get();
|
||||||
|
|
||||||
|
mRoDynStrInfo.memory_offset = hdr->dyn_str.memory_offset.get();
|
||||||
|
mRoDynStrInfo.size = hdr->dyn_str.size.get();
|
||||||
|
|
||||||
|
mRoDynSymInfo.memory_offset = hdr->dyn_sym.memory_offset.get();
|
||||||
|
mRoDynSymInfo.size = hdr->dyn_sym.size.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NroHeader::clear()
|
||||||
|
{
|
||||||
|
mBinaryBlob.clear();
|
||||||
|
memset(&mRoCrt, 0, sizeof(mRoCrt));
|
||||||
|
memset(&mTextInfo, 0, sizeof(mTextInfo));
|
||||||
|
memset(&mRoInfo, 0, sizeof(mRoInfo));
|
||||||
|
memset(&mDataInfo, 0, sizeof(mDataInfo));
|
||||||
|
mBssSize = 0;
|
||||||
|
memset(&mModuleId, 0, sizeof(mModuleId));
|
||||||
|
memset(&mRoEmbeddedInfo, 0, sizeof(mRoEmbeddedInfo));
|
||||||
|
memset(&mRoDynStrInfo, 0, sizeof(mRoDynStrInfo));
|
||||||
|
memset(&mRoDynSymInfo, 0, sizeof(mRoDynSymInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
const nx::NroHeader::sRoCrt& nx::NroHeader::getRoCrt() const
|
||||||
|
{
|
||||||
|
return mRoCrt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NroHeader::setRoCrt(const sRoCrt& ro_crt)
|
||||||
|
{
|
||||||
|
mRoCrt = ro_crt;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t nx::NroHeader::getNroSize() const
|
||||||
|
{
|
||||||
|
return mNroSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NroHeader::setNroSize(uint32_t size)
|
||||||
|
{
|
||||||
|
mNroSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nx::NroHeader::sSection& nx::NroHeader::getTextInfo() const
|
||||||
|
{
|
||||||
|
return mTextInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NroHeader::setTextInfo(const sSection& info)
|
||||||
|
{
|
||||||
|
mTextInfo = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nx::NroHeader::sSection& nx::NroHeader::getRoInfo() const
|
||||||
|
{
|
||||||
|
return mRoInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NroHeader::setRoInfo(const sSection& info)
|
||||||
|
{
|
||||||
|
mRoInfo = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nx::NroHeader::sSection& nx::NroHeader::getDataInfo() const
|
||||||
|
{
|
||||||
|
return mDataInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NroHeader::setDataInfo(const sSection& info)
|
||||||
|
{
|
||||||
|
mDataInfo = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t nx::NroHeader::getBssSize() const
|
||||||
|
{
|
||||||
|
return mBssSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NroHeader::setBssSize(uint32_t size)
|
||||||
|
{
|
||||||
|
mBssSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nx::NroHeader::sModuleId& nx::NroHeader::getModuleId() const
|
||||||
|
{
|
||||||
|
return mModuleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NroHeader::setModuleId(const sModuleId& id)
|
||||||
|
{
|
||||||
|
mModuleId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nx::NroHeader::sSection& nx::NroHeader::getRoEmbeddedInfo() const
|
||||||
|
{
|
||||||
|
return mRoEmbeddedInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NroHeader::setRoEmbeddedInfo(const sSection& info)
|
||||||
|
{
|
||||||
|
mRoEmbeddedInfo = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nx::NroHeader::sSection& nx::NroHeader::getRoDynStrInfo() const
|
||||||
|
{
|
||||||
|
return mRoDynStrInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NroHeader::setRoDynStrInfo(const sSection& info)
|
||||||
|
{
|
||||||
|
mRoDynStrInfo = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nx::NroHeader::sSection& nx::NroHeader::getRoDynSymInfo() const
|
||||||
|
{
|
||||||
|
return mRoDynSymInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NroHeader::setRoDynSymInfo(const sSection& info)
|
||||||
|
{
|
||||||
|
mRoDynSymInfo = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nx::NroHeader::isEqual(const NroHeader& other) const
|
||||||
|
{
|
||||||
|
return (mRoCrt == other.mRoCrt) \
|
||||||
|
&& (mNroSize == other.mNroSize) \
|
||||||
|
&& (mTextInfo == other.mTextInfo) \
|
||||||
|
&& (mTextInfo == other.mTextInfo) \
|
||||||
|
&& (mRoInfo == other.mRoInfo) \
|
||||||
|
&& (mDataInfo == other.mDataInfo) \
|
||||||
|
&& (mBssSize == other.mBssSize) \
|
||||||
|
&& (mModuleId == other.mModuleId) \
|
||||||
|
&& (mRoEmbeddedInfo == other.mRoEmbeddedInfo) \
|
||||||
|
&& (mRoDynStrInfo == other.mRoDynStrInfo) \
|
||||||
|
&& (mRoDynSymInfo == other.mRoDynSymInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NroHeader::copyFrom(const NroHeader& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
mRoCrt = other.mRoCrt;
|
||||||
|
mNroSize = other.mNroSize;
|
||||||
|
mTextInfo = other.mTextInfo;
|
||||||
|
mTextInfo = other.mTextInfo;
|
||||||
|
mRoInfo = other.mRoInfo;
|
||||||
|
mDataInfo = other.mDataInfo;
|
||||||
|
mBssSize = other.mBssSize;
|
||||||
|
mModuleId = other.mModuleId;
|
||||||
|
mRoEmbeddedInfo = other.mRoEmbeddedInfo;
|
||||||
|
mRoDynStrInfo = other.mRoDynStrInfo;
|
||||||
|
mRoDynSymInfo = other.mRoDynSymInfo;
|
||||||
|
}
|
|
@ -311,6 +311,7 @@ bool nx::NsoHeader::isEqual(const NsoHeader& other) const
|
||||||
}
|
}
|
||||||
void nx::NsoHeader::copyFrom(const NsoHeader& other)
|
void nx::NsoHeader::copyFrom(const NsoHeader& other)
|
||||||
{
|
{
|
||||||
|
clear();
|
||||||
mModuleId = other.mModuleId;
|
mModuleId = other.mModuleId;
|
||||||
mBssSize = other.mBssSize;
|
mBssSize = other.mBssSize;
|
||||||
mTextSegmentInfo = other.mTextSegmentInfo;
|
mTextSegmentInfo = other.mTextSegmentInfo;
|
||||||
|
|
518
programs/nstool/source/CodeObjectProcess.cpp
Normal file
518
programs/nstool/source/CodeObjectProcess.cpp
Normal file
|
@ -0,0 +1,518 @@
|
||||||
|
#include <sstream>
|
||||||
|
#include <fnd/SimpleTextOutput.h>
|
||||||
|
#include <fnd/MemoryBlob.h>
|
||||||
|
#include <compress/lz4.h>
|
||||||
|
#include "OffsetAdjustedIFile.h"
|
||||||
|
#include "CodeObjectProcess.h"
|
||||||
|
|
||||||
|
CodeObjectProcess::CodeObjectProcess():
|
||||||
|
mFile(nullptr),
|
||||||
|
mOwnIFile(false),
|
||||||
|
mCliOutputType(OUTPUT_NORMAL),
|
||||||
|
mVerify(false),
|
||||||
|
mObjType(OBJ_INVALID),
|
||||||
|
mInstructionType(nx::npdm::INSTR_64BIT),
|
||||||
|
mListApi(false),
|
||||||
|
mListSymbols(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeObjectProcess::~CodeObjectProcess()
|
||||||
|
{
|
||||||
|
if (mOwnIFile)
|
||||||
|
{
|
||||||
|
delete mFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeObjectProcess::process()
|
||||||
|
{
|
||||||
|
if (mFile == nullptr)
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "No file reader set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mObjType == OBJ_INVALID)
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "Object type undefined.");
|
||||||
|
}
|
||||||
|
|
||||||
|
importHeader();
|
||||||
|
importCodeSegments();
|
||||||
|
importApiList();
|
||||||
|
|
||||||
|
if (mCliOutputType >= OUTPUT_NORMAL)
|
||||||
|
{
|
||||||
|
if (mObjType == OBJ_NSO)
|
||||||
|
displayNsoHeader();
|
||||||
|
else if (mObjType == OBJ_NRO)
|
||||||
|
displayNroHeader();
|
||||||
|
}
|
||||||
|
displayRoMetaData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeObjectProcess::setInputFile(fnd::IFile* file, bool ownIFile)
|
||||||
|
{
|
||||||
|
mFile = file;
|
||||||
|
mOwnIFile = ownIFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeObjectProcess::setCliOutputMode(CliOutputType type)
|
||||||
|
{
|
||||||
|
mCliOutputType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeObjectProcess::setVerifyMode(bool verify)
|
||||||
|
{
|
||||||
|
mVerify = verify;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeObjectProcess::setCodeObjectType(CodeObjectType type)
|
||||||
|
{
|
||||||
|
mObjType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeObjectProcess::setInstructionType(nx::npdm::InstructionType type)
|
||||||
|
{
|
||||||
|
mInstructionType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeObjectProcess::setListApi(bool listApi)
|
||||||
|
{
|
||||||
|
mListApi = listApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeObjectProcess::setListSymbols(bool listSymbols)
|
||||||
|
{
|
||||||
|
mListSymbols = listSymbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nx::NsoHeader& CodeObjectProcess::getNsoHeader() const
|
||||||
|
{
|
||||||
|
return mNsoHdr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fnd::MemoryBlob& CodeObjectProcess::getTextBlob() const
|
||||||
|
{
|
||||||
|
return mTextBlob;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fnd::MemoryBlob& CodeObjectProcess::getRoBlob() const
|
||||||
|
{
|
||||||
|
return mRoBlob;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fnd::MemoryBlob& CodeObjectProcess::getDataBlob() const
|
||||||
|
{
|
||||||
|
return mDataBlob;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<SdkApiString>& CodeObjectProcess::getApiList() const
|
||||||
|
{
|
||||||
|
return mApiList;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeObjectProcess::importHeader()
|
||||||
|
{
|
||||||
|
if (mObjType == OBJ_NSO)
|
||||||
|
{
|
||||||
|
fnd::MemoryBlob scratch;
|
||||||
|
if (mFile->size() < sizeof(nx::sNsoHeader))
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "Corrupt NSO file too small");
|
||||||
|
}
|
||||||
|
|
||||||
|
scratch.alloc(sizeof(nx::sNsoHeader));
|
||||||
|
mFile->read(scratch.getBytes(), 0, scratch.getSize());
|
||||||
|
|
||||||
|
mNsoHdr.importBinary(scratch.getBytes(), scratch.getSize());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fnd::MemoryBlob scratch;
|
||||||
|
if (mFile->size() < sizeof(nx::sNroHeader))
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "Corrupt NSO file too small");
|
||||||
|
}
|
||||||
|
|
||||||
|
scratch.alloc(sizeof(nx::sNroHeader));
|
||||||
|
mFile->read(scratch.getBytes(), 0, scratch.getSize());
|
||||||
|
|
||||||
|
mNroHdr.importBinary(scratch.getBytes(), scratch.getSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeObjectProcess::importCodeSegments()
|
||||||
|
{
|
||||||
|
if (mObjType == OBJ_NSO)
|
||||||
|
{
|
||||||
|
fnd::MemoryBlob scratch;
|
||||||
|
uint32_t decompressed_len;
|
||||||
|
crypto::sha::sSha256Hash calc_hash;
|
||||||
|
|
||||||
|
// process text segment
|
||||||
|
if (mNsoHdr.getTextSegmentInfo().is_compressed)
|
||||||
|
{
|
||||||
|
scratch.alloc(mNsoHdr.getTextSegmentInfo().file_layout.size);
|
||||||
|
mFile->read(scratch.getBytes(), mNsoHdr.getTextSegmentInfo().file_layout.offset, scratch.getSize());
|
||||||
|
mTextBlob.alloc(mNsoHdr.getTextSegmentInfo().memory_layout.size);
|
||||||
|
compress::lz4::decompressData(scratch.getBytes(), scratch.getSize(), mTextBlob.getBytes(), mTextBlob.getSize(), decompressed_len);
|
||||||
|
if (decompressed_len != mTextBlob.getSize())
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "NSO text segment failed to decompress");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mTextBlob.alloc(mNsoHdr.getTextSegmentInfo().file_layout.size);
|
||||||
|
mFile->read(mTextBlob.getBytes(), mNsoHdr.getTextSegmentInfo().file_layout.offset, mTextBlob.getSize());
|
||||||
|
}
|
||||||
|
if (mNsoHdr.getTextSegmentInfo().is_hashed)
|
||||||
|
{
|
||||||
|
crypto::sha::Sha256(mTextBlob.getBytes(), mTextBlob.getSize(), calc_hash.bytes);
|
||||||
|
if (calc_hash != mNsoHdr.getTextSegmentInfo().hash)
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "NSO text segment failed SHA256 verification");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process ro segment
|
||||||
|
if (mNsoHdr.getRoSegmentInfo().is_compressed)
|
||||||
|
{
|
||||||
|
scratch.alloc(mNsoHdr.getRoSegmentInfo().file_layout.size);
|
||||||
|
mFile->read(scratch.getBytes(), mNsoHdr.getRoSegmentInfo().file_layout.offset, scratch.getSize());
|
||||||
|
mRoBlob.alloc(mNsoHdr.getRoSegmentInfo().memory_layout.size);
|
||||||
|
compress::lz4::decompressData(scratch.getBytes(), scratch.getSize(), mRoBlob.getBytes(), mRoBlob.getSize(), decompressed_len);
|
||||||
|
if (decompressed_len != mRoBlob.getSize())
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "NSO ro segment failed to decompress");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mRoBlob.alloc(mNsoHdr.getRoSegmentInfo().file_layout.size);
|
||||||
|
mFile->read(mRoBlob.getBytes(), mNsoHdr.getRoSegmentInfo().file_layout.offset, mRoBlob.getSize());
|
||||||
|
}
|
||||||
|
if (mNsoHdr.getRoSegmentInfo().is_hashed)
|
||||||
|
{
|
||||||
|
crypto::sha::Sha256(mRoBlob.getBytes(), mRoBlob.getSize(), calc_hash.bytes);
|
||||||
|
if (calc_hash != mNsoHdr.getRoSegmentInfo().hash)
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "NSO ro segment failed SHA256 verification");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process data segment
|
||||||
|
if (mNsoHdr.getDataSegmentInfo().is_compressed)
|
||||||
|
{
|
||||||
|
scratch.alloc(mNsoHdr.getDataSegmentInfo().file_layout.size);
|
||||||
|
mFile->read(scratch.getBytes(), mNsoHdr.getDataSegmentInfo().file_layout.offset, scratch.getSize());
|
||||||
|
mDataBlob.alloc(mNsoHdr.getDataSegmentInfo().memory_layout.size);
|
||||||
|
compress::lz4::decompressData(scratch.getBytes(), scratch.getSize(), mDataBlob.getBytes(), mDataBlob.getSize(), decompressed_len);
|
||||||
|
if (decompressed_len != mDataBlob.getSize())
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "NSO data segment failed to decompress");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mDataBlob.alloc(mNsoHdr.getDataSegmentInfo().file_layout.size);
|
||||||
|
mFile->read(mDataBlob.getBytes(), mNsoHdr.getDataSegmentInfo().file_layout.offset, mDataBlob.getSize());
|
||||||
|
}
|
||||||
|
if (mNsoHdr.getDataSegmentInfo().is_hashed)
|
||||||
|
{
|
||||||
|
crypto::sha::Sha256(mDataBlob.getBytes(), mDataBlob.getSize(), calc_hash.bytes);
|
||||||
|
if (calc_hash != mNsoHdr.getDataSegmentInfo().hash)
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "NSO data segment failed SHA256 verification");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mObjType == OBJ_NRO)
|
||||||
|
{
|
||||||
|
mTextBlob.alloc(mNroHdr.getTextInfo().size);
|
||||||
|
mFile->read(mTextBlob.getBytes(), mNroHdr.getTextInfo().memory_offset, mTextBlob.getSize());
|
||||||
|
mRoBlob.alloc(mNroHdr.getRoInfo().size);
|
||||||
|
mFile->read(mRoBlob.getBytes(), mNroHdr.getRoInfo().memory_offset, mRoBlob.getSize());
|
||||||
|
mDataBlob.alloc(mNroHdr.getDataInfo().size);
|
||||||
|
mFile->read(mDataBlob.getBytes(), mNroHdr.getDataInfo().memory_offset, mDataBlob.getSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeObjectProcess::importApiList()
|
||||||
|
{
|
||||||
|
struct sLayout { size_t offset; size_t size; } api_info, dyn_str, dyn_sym;
|
||||||
|
|
||||||
|
if (mObjType == OBJ_NSO)
|
||||||
|
{
|
||||||
|
api_info.offset = mNsoHdr.getRoEmbeddedInfo().offset;
|
||||||
|
api_info.size = mNsoHdr.getRoEmbeddedInfo().size;
|
||||||
|
dyn_str.offset = mNsoHdr.getRoDynStrInfo().offset;
|
||||||
|
dyn_str.size = mNsoHdr.getRoDynStrInfo().size;
|
||||||
|
dyn_sym.offset = mNsoHdr.getRoDynSymInfo().offset;
|
||||||
|
dyn_sym.size = mNsoHdr.getRoDynSymInfo().size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
api_info.offset = mNroHdr.getRoEmbeddedInfo().memory_offset;
|
||||||
|
api_info.size = mNroHdr.getRoEmbeddedInfo().size;
|
||||||
|
dyn_str.offset = mNroHdr.getRoDynStrInfo().memory_offset;
|
||||||
|
dyn_str.size = mNroHdr.getRoDynStrInfo().size;
|
||||||
|
dyn_sym.offset = mNroHdr.getRoDynSymInfo().memory_offset;
|
||||||
|
dyn_sym.size = mNroHdr.getRoDynSymInfo().size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (api_info.size > 0)
|
||||||
|
{
|
||||||
|
std::stringstream list_stream(std::string((char*)mRoBlob.getBytes() + api_info.offset, api_info.size));
|
||||||
|
std::string api;
|
||||||
|
|
||||||
|
while(std::getline(list_stream, api, (char)0x00))
|
||||||
|
{
|
||||||
|
mApiList.push_back(api);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mApiList.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dyn_sym.size > 0)
|
||||||
|
{
|
||||||
|
mDynSymbolList.parseData(mRoBlob.getBytes() + dyn_sym.offset, dyn_sym.size, mRoBlob.getBytes() + dyn_str.offset, dyn_str.size, mInstructionType == nx::npdm::INSTR_64BIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeObjectProcess::displayNsoHeader()
|
||||||
|
{
|
||||||
|
#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0)
|
||||||
|
|
||||||
|
printf("[NSO Header]\n");
|
||||||
|
printf(" ModuleId: ");
|
||||||
|
_HEXDUMP_L(mNsoHdr.getModuleId().data, nx::nso::kModuleIdSize);
|
||||||
|
printf("\n");
|
||||||
|
printf(" Program Segments:\n");
|
||||||
|
printf(" .module_name:\n");
|
||||||
|
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getModuleNameInfo().offset);
|
||||||
|
printf(" FileSize: 0x%" PRIx32 "\n", mNsoHdr.getModuleNameInfo().size);
|
||||||
|
printf(" .text:\n");
|
||||||
|
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getTextSegmentInfo().file_layout.offset);
|
||||||
|
printf(" FileSize: 0x%" PRIx32 "%s\n", mNsoHdr.getTextSegmentInfo().file_layout.size, mNsoHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : "");
|
||||||
|
printf(" .ro:\n");
|
||||||
|
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoSegmentInfo().file_layout.offset);
|
||||||
|
printf(" FileSize: 0x%" PRIx32 "%s\n", mNsoHdr.getRoSegmentInfo().file_layout.size, mNsoHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : "");
|
||||||
|
printf(" .data:\n");
|
||||||
|
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getDataSegmentInfo().file_layout.offset);
|
||||||
|
printf(" FileSize: 0x%" PRIx32 "%s\n", mNsoHdr.getDataSegmentInfo().file_layout.size, mNsoHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : "");
|
||||||
|
printf(" Program Sections:\n");
|
||||||
|
printf(" .text:\n");
|
||||||
|
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getTextSegmentInfo().memory_layout.offset);
|
||||||
|
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getTextSegmentInfo().memory_layout.size);
|
||||||
|
if (mNsoHdr.getTextSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE)
|
||||||
|
{
|
||||||
|
printf(" Hash: ");
|
||||||
|
_HEXDUMP_L(mNsoHdr.getTextSegmentInfo().hash.bytes, 32);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
printf(" .ro:\n");
|
||||||
|
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoSegmentInfo().memory_layout.offset);
|
||||||
|
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoSegmentInfo().memory_layout.size);
|
||||||
|
if (mNsoHdr.getRoSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE)
|
||||||
|
{
|
||||||
|
printf(" Hash: ");
|
||||||
|
_HEXDUMP_L(mNsoHdr.getRoSegmentInfo().hash.bytes, 32);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
if (mCliOutputType >= OUTPUT_VERBOSE)
|
||||||
|
{
|
||||||
|
printf(" .api_info:\n");
|
||||||
|
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoEmbeddedInfo().offset);
|
||||||
|
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoEmbeddedInfo().size);
|
||||||
|
printf(" .dynstr:\n");
|
||||||
|
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoDynStrInfo().offset);
|
||||||
|
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoDynStrInfo().size);
|
||||||
|
printf(" .dynsym:\n");
|
||||||
|
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoDynSymInfo().offset);
|
||||||
|
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoDynSymInfo().size);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(" .data:\n");
|
||||||
|
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getDataSegmentInfo().memory_layout.offset);
|
||||||
|
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getDataSegmentInfo().memory_layout.size);
|
||||||
|
if (mNsoHdr.getDataSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE)
|
||||||
|
{
|
||||||
|
printf(" Hash: ");
|
||||||
|
_HEXDUMP_L(mNsoHdr.getDataSegmentInfo().hash.bytes, 32);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
printf(" .bss:\n");
|
||||||
|
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getBssSize());
|
||||||
|
|
||||||
|
#undef _HEXDUMP_L
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeObjectProcess::displayNroHeader()
|
||||||
|
{
|
||||||
|
#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0)
|
||||||
|
|
||||||
|
printf("[NRO Header]\n");
|
||||||
|
printf(" RoCrt: ");
|
||||||
|
_HEXDUMP_L(mNroHdr.getRoCrt().data, nx::nro::kRoCrtSize);
|
||||||
|
printf("\n");
|
||||||
|
printf(" ModuleId: ");
|
||||||
|
_HEXDUMP_L(mNroHdr.getModuleId().data, nx::nro::kModuleIdSize);
|
||||||
|
printf("\n");
|
||||||
|
printf(" NroSize: 0x%" PRIx32 "\n", mNroHdr.getNroSize());
|
||||||
|
printf(" Program Sections:\n");
|
||||||
|
printf(" .text:\n");
|
||||||
|
printf(" Offset: 0x%" PRIx32 "\n", mNroHdr.getTextInfo().memory_offset);
|
||||||
|
printf(" Size: 0x%" PRIx32 "\n", mNroHdr.getTextInfo().size);
|
||||||
|
printf(" .ro:\n");
|
||||||
|
printf(" Offset: 0x%" PRIx32 "\n", mNroHdr.getRoInfo().memory_offset);
|
||||||
|
printf(" Size: 0x%" PRIx32 "\n", mNroHdr.getRoInfo().size);
|
||||||
|
if (mCliOutputType >= OUTPUT_VERBOSE)
|
||||||
|
{
|
||||||
|
printf(" .api_info:\n");
|
||||||
|
printf(" Offset: 0x%" PRIx32 "\n", mNroHdr.getRoEmbeddedInfo().memory_offset);
|
||||||
|
printf(" Size: 0x%" PRIx32 "\n", mNroHdr.getRoEmbeddedInfo().size);
|
||||||
|
printf(" .dynstr:\n");
|
||||||
|
printf(" Offset: 0x%" PRIx32 "\n", mNroHdr.getRoDynStrInfo().memory_offset);
|
||||||
|
printf(" Size: 0x%" PRIx32 "\n", mNroHdr.getRoDynStrInfo().size);
|
||||||
|
printf(" .dynsym:\n");
|
||||||
|
printf(" Offset: 0x%" PRIx32 "\n", mNroHdr.getRoDynSymInfo().memory_offset);
|
||||||
|
printf(" Size: 0x%" PRIx32 "\n", mNroHdr.getRoDynSymInfo().size);
|
||||||
|
}
|
||||||
|
printf(" .data:\n");
|
||||||
|
printf(" Offset: 0x%" PRIx32 "\n", mNroHdr.getDataInfo().memory_offset);
|
||||||
|
printf(" Size: 0x%" PRIx32 "\n", mNroHdr.getDataInfo().size);
|
||||||
|
printf(" .bss:\n");
|
||||||
|
printf(" Size: 0x%" PRIx32 "\n", mNroHdr.getBssSize());
|
||||||
|
|
||||||
|
#undef _HEXDUMP_L
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeObjectProcess::displayRoMetaData()
|
||||||
|
{
|
||||||
|
if (mApiList.size() > 0 && (mListApi || mCliOutputType > OUTPUT_NORMAL))
|
||||||
|
{
|
||||||
|
printf("[SDK API List]\n");
|
||||||
|
for (size_t i = 0; i < mApiList.size(); i++)
|
||||||
|
{
|
||||||
|
printf(" API %d:\n", (int)i);
|
||||||
|
printf(" Type: %s\n", getApiTypeStr(mApiList[i].getApiType()));
|
||||||
|
printf(" Vender: %s\n", mApiList[i].getVenderName().c_str());
|
||||||
|
printf(" Module: %s\n", mApiList[i].getModuleName().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mDynSymbolList.getDynamicSymbolList().getSize() > 0 && (mListSymbols || mCliOutputType > OUTPUT_NORMAL))
|
||||||
|
{
|
||||||
|
printf("[Symbol List]\n");
|
||||||
|
for (size_t i = 0; i < mDynSymbolList.getDynamicSymbolList().getSize(); i++)
|
||||||
|
{
|
||||||
|
const DynamicSymbolParser::sDynSymbol& symbol = mDynSymbolList.getDynamicSymbolList()[i];
|
||||||
|
printf(" %s [SHN=%s (%04x)][STT=%s]\n", symbol.name.c_str(), getSectionIndexStr(symbol.shn_index), symbol.shn_index, getSymbolTypeStr(symbol.symbol_type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* CodeObjectProcess::getApiTypeStr(SdkApiString::ApiType type) const
|
||||||
|
{
|
||||||
|
const char* str;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case (SdkApiString::API_MIDDLEWARE):
|
||||||
|
str = "Middleware";
|
||||||
|
break;
|
||||||
|
case (SdkApiString::API_DEBUG):
|
||||||
|
str = "Debug";
|
||||||
|
break;
|
||||||
|
case (SdkApiString::API_PRIVATE):
|
||||||
|
str = "Private";
|
||||||
|
break;
|
||||||
|
case (SdkApiString::API_SDK_VERSION):
|
||||||
|
str = "SDK Version";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str = "UNKNOWN";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* CodeObjectProcess::getSectionIndexStr(nx::dynsym::SpecialSectionIndex shn_index) const
|
||||||
|
{
|
||||||
|
const char* str;
|
||||||
|
switch (shn_index)
|
||||||
|
{
|
||||||
|
case (nx::dynsym::SHN_UNDEF):
|
||||||
|
str = "UNDEF";
|
||||||
|
break;
|
||||||
|
case (nx::dynsym::SHN_EXPORT):
|
||||||
|
str = "EXPORT";
|
||||||
|
break;
|
||||||
|
case (nx::dynsym::SHN_LOPROC):
|
||||||
|
str = "LOPROC";
|
||||||
|
break;
|
||||||
|
case (nx::dynsym::SHN_HIPROC):
|
||||||
|
str = "HIPROC";
|
||||||
|
break;
|
||||||
|
case (nx::dynsym::SHN_LOOS):
|
||||||
|
str = "LOOS";
|
||||||
|
break;
|
||||||
|
case (nx::dynsym::SHN_HIOS):
|
||||||
|
str = "HIOS";
|
||||||
|
break;
|
||||||
|
case (nx::dynsym::SHN_ABS):
|
||||||
|
str = "ABS";
|
||||||
|
break;
|
||||||
|
case (nx::dynsym::SHN_COMMON):
|
||||||
|
str = "COMMON";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str = "UNKNOWN";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* CodeObjectProcess::getSymbolTypeStr(nx::dynsym::SymbolType symbol_type) const
|
||||||
|
{
|
||||||
|
const char* str;
|
||||||
|
switch (symbol_type)
|
||||||
|
{
|
||||||
|
case (nx::dynsym::STT_NOTYPE):
|
||||||
|
str = "NOTYPE";
|
||||||
|
break;
|
||||||
|
case (nx::dynsym::STT_OBJECT):
|
||||||
|
str = "OBJECT";
|
||||||
|
break;
|
||||||
|
case (nx::dynsym::STT_FUNC):
|
||||||
|
str = "FUNC";
|
||||||
|
break;
|
||||||
|
case (nx::dynsym::STT_SECTION):
|
||||||
|
str = "SECTION";
|
||||||
|
break;
|
||||||
|
case (nx::dynsym::STT_FILE):
|
||||||
|
str = "FILE";
|
||||||
|
break;
|
||||||
|
case (nx::dynsym::STT_LOOS):
|
||||||
|
str = "LOOS";
|
||||||
|
break;
|
||||||
|
case (nx::dynsym::STT_HIOS):
|
||||||
|
str = "HIOS";
|
||||||
|
break;
|
||||||
|
case (nx::dynsym::STT_LOPROC):
|
||||||
|
str = "LOPROC";
|
||||||
|
break;
|
||||||
|
case (nx::dynsym::STT_HIPROC):
|
||||||
|
str = "HIPROC";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str = "UNKNOWN";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
74
programs/nstool/source/CodeObjectProcess.h
Normal file
74
programs/nstool/source/CodeObjectProcess.h
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#pragma once
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <fnd/types.h>
|
||||||
|
#include <fnd/IFile.h>
|
||||||
|
#include <nx/npdm.h>
|
||||||
|
#include <nx/NsoHeader.h>
|
||||||
|
#include <nx/NroHeader.h>
|
||||||
|
|
||||||
|
#include "nstool.h"
|
||||||
|
#include "SdkApiString.h"
|
||||||
|
#include "DynamicSymbolParser.h"
|
||||||
|
|
||||||
|
class CodeObjectProcess
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum CodeObjectType
|
||||||
|
{
|
||||||
|
OBJ_NSO,
|
||||||
|
OBJ_NRO,
|
||||||
|
OBJ_INVALID
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeObjectProcess();
|
||||||
|
~CodeObjectProcess();
|
||||||
|
|
||||||
|
void process();
|
||||||
|
|
||||||
|
void setInputFile(fnd::IFile* file, bool ownIFile);
|
||||||
|
void setCliOutputMode(CliOutputType type);
|
||||||
|
void setVerifyMode(bool verify);
|
||||||
|
|
||||||
|
void setCodeObjectType(CodeObjectType type);
|
||||||
|
void setInstructionType(nx::npdm::InstructionType type);
|
||||||
|
void setListApi(bool listApi);
|
||||||
|
void setListSymbols(bool listSymbols);
|
||||||
|
|
||||||
|
// processed data
|
||||||
|
const nx::NsoHeader& getNsoHeader() const;
|
||||||
|
const fnd::MemoryBlob& getTextBlob() const;
|
||||||
|
const fnd::MemoryBlob& getRoBlob() const;
|
||||||
|
const fnd::MemoryBlob& getDataBlob() const;
|
||||||
|
const std::vector<SdkApiString>& getApiList() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string kModuleName = "CodeObjectProcess";
|
||||||
|
|
||||||
|
fnd::IFile* mFile;
|
||||||
|
bool mOwnIFile;
|
||||||
|
|
||||||
|
CliOutputType mCliOutputType;
|
||||||
|
bool mVerify;
|
||||||
|
CodeObjectType mObjType;
|
||||||
|
nx::npdm::InstructionType mInstructionType;
|
||||||
|
bool mListApi;
|
||||||
|
bool mListSymbols;
|
||||||
|
|
||||||
|
nx::NsoHeader mNsoHdr;
|
||||||
|
nx::NroHeader mNroHdr;
|
||||||
|
fnd::MemoryBlob mTextBlob, mRoBlob, mDataBlob;
|
||||||
|
std::vector<SdkApiString> mApiList;
|
||||||
|
DynamicSymbolParser mDynSymbolList;
|
||||||
|
|
||||||
|
void importHeader();
|
||||||
|
void importCodeSegments();
|
||||||
|
void importApiList();
|
||||||
|
void displayNsoHeader();
|
||||||
|
void displayNroHeader();
|
||||||
|
void displayRoMetaData();
|
||||||
|
|
||||||
|
const char* getApiTypeStr(SdkApiString::ApiType type) const;
|
||||||
|
const char* getSectionIndexStr(nx::dynsym::SpecialSectionIndex shn_index) const;
|
||||||
|
const char* getSymbolTypeStr(nx::dynsym::SymbolType symbol_type) const;
|
||||||
|
};
|
|
@ -1,303 +0,0 @@
|
||||||
#include <sstream>
|
|
||||||
#include <fnd/SimpleTextOutput.h>
|
|
||||||
#include <fnd/MemoryBlob.h>
|
|
||||||
#include <compress/lz4.h>
|
|
||||||
#include "OffsetAdjustedIFile.h"
|
|
||||||
#include "NsoProcess.h"
|
|
||||||
|
|
||||||
NsoProcess::NsoProcess():
|
|
||||||
mFile(nullptr),
|
|
||||||
mOwnIFile(false),
|
|
||||||
mCliOutputType(OUTPUT_NORMAL),
|
|
||||||
mVerify(false)
|
|
||||||
{
|
|
||||||
mArchType.isSet = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
NsoProcess::~NsoProcess()
|
|
||||||
{
|
|
||||||
if (mOwnIFile)
|
|
||||||
{
|
|
||||||
delete mFile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NsoProcess::process()
|
|
||||||
{
|
|
||||||
if (mFile == nullptr)
|
|
||||||
{
|
|
||||||
throw fnd::Exception(kModuleName, "No file reader set.");
|
|
||||||
}
|
|
||||||
|
|
||||||
importHeader();
|
|
||||||
importCodeSegments();
|
|
||||||
importApiList();
|
|
||||||
|
|
||||||
if (mCliOutputType >= OUTPUT_NORMAL)
|
|
||||||
{
|
|
||||||
displayHeader();
|
|
||||||
displayRoApiList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NsoProcess::setInputFile(fnd::IFile* file, bool ownIFile)
|
|
||||||
{
|
|
||||||
mFile = file;
|
|
||||||
mOwnIFile = ownIFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NsoProcess::setCliOutputMode(CliOutputType type)
|
|
||||||
{
|
|
||||||
mCliOutputType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NsoProcess::setVerifyMode(bool verify)
|
|
||||||
{
|
|
||||||
mVerify = verify;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NsoProcess::setArchType(nx::npdm::InstructionType type)
|
|
||||||
{
|
|
||||||
mArchType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
const nx::NsoHeader& NsoProcess::getNsoHeader() const
|
|
||||||
{
|
|
||||||
return mHdr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fnd::MemoryBlob& NsoProcess::getTextBlob() const
|
|
||||||
{
|
|
||||||
return mTextBlob;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fnd::MemoryBlob& NsoProcess::getRoBlob() const
|
|
||||||
{
|
|
||||||
return mRoBlob;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fnd::MemoryBlob& NsoProcess::getDataBlob() const
|
|
||||||
{
|
|
||||||
return mDataBlob;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<std::string>& NsoProcess::getApiList() const
|
|
||||||
{
|
|
||||||
return mApiList;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NsoProcess::importHeader()
|
|
||||||
{
|
|
||||||
fnd::MemoryBlob scratch;
|
|
||||||
if (mFile->size() < sizeof(nx::sNsoHeader))
|
|
||||||
{
|
|
||||||
throw fnd::Exception(kModuleName, "Corrupt NSO file too small");
|
|
||||||
}
|
|
||||||
|
|
||||||
scratch.alloc(sizeof(nx::sNsoHeader));
|
|
||||||
mFile->read(scratch.getBytes(), 0, scratch.getSize());
|
|
||||||
|
|
||||||
mHdr.importBinary(scratch.getBytes(), scratch.getSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
void NsoProcess::importCodeSegments()
|
|
||||||
{
|
|
||||||
fnd::MemoryBlob scratch;
|
|
||||||
uint32_t decompressed_len;
|
|
||||||
crypto::sha::sSha256Hash calc_hash;
|
|
||||||
|
|
||||||
// process text segment
|
|
||||||
if (mHdr.getTextSegmentInfo().is_compressed)
|
|
||||||
{
|
|
||||||
scratch.alloc(mHdr.getTextSegmentInfo().file_layout.size);
|
|
||||||
mFile->read(scratch.getBytes(), mHdr.getTextSegmentInfo().file_layout.offset, scratch.getSize());
|
|
||||||
mTextBlob.alloc(mHdr.getTextSegmentInfo().memory_layout.size);
|
|
||||||
compress::lz4::decompressData(scratch.getBytes(), scratch.getSize(), mTextBlob.getBytes(), mTextBlob.getSize(), decompressed_len);
|
|
||||||
if (decompressed_len != mTextBlob.getSize())
|
|
||||||
{
|
|
||||||
throw fnd::Exception(kModuleName, "NSO text segment failed to decompress");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mTextBlob.alloc(mHdr.getTextSegmentInfo().file_layout.size);
|
|
||||||
mFile->read(mTextBlob.getBytes(), mHdr.getTextSegmentInfo().file_layout.offset, mTextBlob.getSize());
|
|
||||||
}
|
|
||||||
if (mHdr.getTextSegmentInfo().is_hashed)
|
|
||||||
{
|
|
||||||
crypto::sha::Sha256(mTextBlob.getBytes(), mTextBlob.getSize(), calc_hash.bytes);
|
|
||||||
if (calc_hash != mHdr.getTextSegmentInfo().hash)
|
|
||||||
{
|
|
||||||
throw fnd::Exception(kModuleName, "NSO text segment failed SHA256 verification");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// process ro segment
|
|
||||||
if (mHdr.getRoSegmentInfo().is_compressed)
|
|
||||||
{
|
|
||||||
scratch.alloc(mHdr.getRoSegmentInfo().file_layout.size);
|
|
||||||
mFile->read(scratch.getBytes(), mHdr.getRoSegmentInfo().file_layout.offset, scratch.getSize());
|
|
||||||
mRoBlob.alloc(mHdr.getRoSegmentInfo().memory_layout.size);
|
|
||||||
compress::lz4::decompressData(scratch.getBytes(), scratch.getSize(), mRoBlob.getBytes(), mRoBlob.getSize(), decompressed_len);
|
|
||||||
if (decompressed_len != mRoBlob.getSize())
|
|
||||||
{
|
|
||||||
throw fnd::Exception(kModuleName, "NSO ro segment failed to decompress");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mRoBlob.alloc(mHdr.getRoSegmentInfo().file_layout.size);
|
|
||||||
mFile->read(mRoBlob.getBytes(), mHdr.getRoSegmentInfo().file_layout.offset, mRoBlob.getSize());
|
|
||||||
}
|
|
||||||
if (mHdr.getRoSegmentInfo().is_hashed)
|
|
||||||
{
|
|
||||||
crypto::sha::Sha256(mRoBlob.getBytes(), mRoBlob.getSize(), calc_hash.bytes);
|
|
||||||
if (calc_hash != mHdr.getRoSegmentInfo().hash)
|
|
||||||
{
|
|
||||||
throw fnd::Exception(kModuleName, "NSO ro segment failed SHA256 verification");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// process data segment
|
|
||||||
if (mHdr.getDataSegmentInfo().is_compressed)
|
|
||||||
{
|
|
||||||
scratch.alloc(mHdr.getDataSegmentInfo().file_layout.size);
|
|
||||||
mFile->read(scratch.getBytes(), mHdr.getDataSegmentInfo().file_layout.offset, scratch.getSize());
|
|
||||||
mDataBlob.alloc(mHdr.getDataSegmentInfo().memory_layout.size);
|
|
||||||
compress::lz4::decompressData(scratch.getBytes(), scratch.getSize(), mDataBlob.getBytes(), mDataBlob.getSize(), decompressed_len);
|
|
||||||
if (decompressed_len != mDataBlob.getSize())
|
|
||||||
{
|
|
||||||
throw fnd::Exception(kModuleName, "NSO data segment failed to decompress");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mDataBlob.alloc(mHdr.getDataSegmentInfo().file_layout.size);
|
|
||||||
mFile->read(mDataBlob.getBytes(), mHdr.getDataSegmentInfo().file_layout.offset, mDataBlob.getSize());
|
|
||||||
}
|
|
||||||
if (mHdr.getDataSegmentInfo().is_hashed)
|
|
||||||
{
|
|
||||||
crypto::sha::Sha256(mDataBlob.getBytes(), mDataBlob.getSize(), calc_hash.bytes);
|
|
||||||
if (calc_hash != mHdr.getDataSegmentInfo().hash)
|
|
||||||
{
|
|
||||||
throw fnd::Exception(kModuleName, "NSO data segment failed SHA256 verification");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void NsoProcess::importApiList()
|
|
||||||
{
|
|
||||||
if (mHdr.getRoEmbeddedInfo().size > 0)
|
|
||||||
{
|
|
||||||
std::stringstream list_stream(std::string((char*)mRoBlob.getBytes() + mHdr.getRoEmbeddedInfo().offset, mHdr.getRoEmbeddedInfo().size));
|
|
||||||
std::string api;
|
|
||||||
|
|
||||||
while(std::getline(list_stream, api, (char)0x00))
|
|
||||||
{
|
|
||||||
mApiList.push_back(api);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mApiList.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mHdr.getRoDynSymInfo().size > 0 && mArchType.isSet == true)
|
|
||||||
{
|
|
||||||
mDynSymbolList.parseData(mRoBlob.getBytes() + mHdr.getRoDynSymInfo().offset, mHdr.getRoDynSymInfo().size, mRoBlob.getBytes() + mHdr.getRoDynStrInfo().offset, mHdr.getRoDynStrInfo().size, *mArchType == nx::npdm::INSTR_64BIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NsoProcess::displayHeader()
|
|
||||||
{
|
|
||||||
#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0)
|
|
||||||
|
|
||||||
printf("[NSO Header]\n");
|
|
||||||
printf(" ModuleId: ");
|
|
||||||
_HEXDUMP_L(mHdr.getModuleId().data, nx::nso::kModuleIdSize);
|
|
||||||
printf("\n");
|
|
||||||
printf(" Program Segments:\n");
|
|
||||||
printf(" .module_name:\n");
|
|
||||||
printf(" FileOffset: 0x%" PRIx32 "\n", mHdr.getModuleNameInfo().offset);
|
|
||||||
printf(" FileSize: 0x%" PRIx32 "\n", mHdr.getModuleNameInfo().size);
|
|
||||||
printf(" .text:\n");
|
|
||||||
printf(" FileOffset: 0x%" PRIx32 "\n", mHdr.getTextSegmentInfo().file_layout.offset);
|
|
||||||
printf(" FileSize: 0x%" PRIx32 "%s\n", mHdr.getTextSegmentInfo().file_layout.size, mHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : "");
|
|
||||||
printf(" .ro:\n");
|
|
||||||
printf(" FileOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().file_layout.offset);
|
|
||||||
printf(" FileSize: 0x%" PRIx32 "%s\n", mHdr.getRoSegmentInfo().file_layout.size, mHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : "");
|
|
||||||
printf(" .data:\n");
|
|
||||||
printf(" FileOffset: 0x%" PRIx32 "\n", mHdr.getDataSegmentInfo().file_layout.offset);
|
|
||||||
printf(" FileSize: 0x%" PRIx32 "%s\n", mHdr.getDataSegmentInfo().file_layout.size, mHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : "");
|
|
||||||
printf(" Program Sections:\n");
|
|
||||||
printf(" .text:\n");
|
|
||||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getTextSegmentInfo().memory_layout.offset);
|
|
||||||
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getTextSegmentInfo().memory_layout.size);
|
|
||||||
if (mHdr.getTextSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE)
|
|
||||||
{
|
|
||||||
printf(" Hash: ");
|
|
||||||
_HEXDUMP_L(mHdr.getTextSegmentInfo().hash.bytes, 32);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
printf(" .ro:\n");
|
|
||||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.offset);
|
|
||||||
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.size);
|
|
||||||
if (mHdr.getRoSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE)
|
|
||||||
{
|
|
||||||
printf(" Hash: ");
|
|
||||||
_HEXDUMP_L(mHdr.getRoSegmentInfo().hash.bytes, 32);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
if (mCliOutputType >= OUTPUT_VERBOSE)
|
|
||||||
{
|
|
||||||
printf(" .api_info:\n");
|
|
||||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoEmbeddedInfo().offset);
|
|
||||||
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoEmbeddedInfo().size);
|
|
||||||
printf(" .dynstr:\n");
|
|
||||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoDynStrInfo().offset);
|
|
||||||
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoDynStrInfo().size);
|
|
||||||
printf(" .dynsym:\n");
|
|
||||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoDynSymInfo().offset);
|
|
||||||
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoDynSymInfo().size);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf(" .data:\n");
|
|
||||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getDataSegmentInfo().memory_layout.offset);
|
|
||||||
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getDataSegmentInfo().memory_layout.size);
|
|
||||||
if (mHdr.getDataSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE)
|
|
||||||
{
|
|
||||||
printf(" Hash: ");
|
|
||||||
_HEXDUMP_L(mHdr.getDataSegmentInfo().hash.bytes, 32);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
printf(" .bss:\n");
|
|
||||||
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getBssSize());
|
|
||||||
|
|
||||||
#undef _HEXDUMP_L
|
|
||||||
}
|
|
||||||
|
|
||||||
void NsoProcess::displayRoApiList()
|
|
||||||
{
|
|
||||||
if (mApiList.size() > 0 || mDynSymbolList.getDynamicSymbolList().getSize() > 0)
|
|
||||||
{
|
|
||||||
printf("[NSO RO Segment]\n");
|
|
||||||
if (mApiList.size() > 0)
|
|
||||||
{
|
|
||||||
printf(" API List:\n");
|
|
||||||
for (size_t i = 0; i < mApiList.size(); i++)
|
|
||||||
{
|
|
||||||
printf(" %s\n", mApiList[i].c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mDynSymbolList.getDynamicSymbolList().getSize() > 0)
|
|
||||||
{
|
|
||||||
printf(" Undefined Symbol List:\n");
|
|
||||||
for (size_t i = 0; i < mDynSymbolList.getDynamicSymbolList().getSize(); i++)
|
|
||||||
{
|
|
||||||
if (mDynSymbolList.getDynamicSymbolList()[i].shn_index == nx::dynsym::SHN_UNDEF && (mDynSymbolList.getDynamicSymbolList()[i].symbol_type == nx::dynsym::STT_FUNC || mDynSymbolList.getDynamicSymbolList()[i].symbol_type == nx::dynsym::STT_NOTYPE))
|
|
||||||
printf(" %s\n", mDynSymbolList.getDynamicSymbolList()[i].name.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <fnd/types.h>
|
|
||||||
#include <fnd/IFile.h>
|
|
||||||
#include <nx/npdm.h>
|
|
||||||
#include <nx/NsoHeader.h>
|
|
||||||
|
|
||||||
#include "nstool.h"
|
|
||||||
#include "DynamicSymbolParser.h"
|
|
||||||
|
|
||||||
class NsoProcess
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NsoProcess();
|
|
||||||
~NsoProcess();
|
|
||||||
|
|
||||||
void process();
|
|
||||||
|
|
||||||
void setInputFile(fnd::IFile* file, bool ownIFile);
|
|
||||||
void setCliOutputMode(CliOutputType type);
|
|
||||||
void setVerifyMode(bool verify);
|
|
||||||
|
|
||||||
void setArchType(nx::npdm::InstructionType type);
|
|
||||||
|
|
||||||
// processed data
|
|
||||||
const nx::NsoHeader& getNsoHeader() const;
|
|
||||||
const fnd::MemoryBlob& getTextBlob() const;
|
|
||||||
const fnd::MemoryBlob& getRoBlob() const;
|
|
||||||
const fnd::MemoryBlob& getDataBlob() const;
|
|
||||||
const std::vector<std::string>& getApiList() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const std::string kModuleName = "NsoProcess";
|
|
||||||
|
|
||||||
fnd::IFile* mFile;
|
|
||||||
bool mOwnIFile;
|
|
||||||
|
|
||||||
CliOutputType mCliOutputType;
|
|
||||||
bool mVerify;
|
|
||||||
sOptional<nx::npdm::InstructionType> mArchType;
|
|
||||||
|
|
||||||
nx::NsoHeader mHdr;
|
|
||||||
fnd::MemoryBlob mTextBlob, mRoBlob, mDataBlob;
|
|
||||||
std::vector<std::string> mApiList;
|
|
||||||
DynamicSymbolParser mDynSymbolList;
|
|
||||||
|
|
||||||
void importHeader();
|
|
||||||
void importCodeSegments();
|
|
||||||
void importApiList();
|
|
||||||
void displayHeader();
|
|
||||||
void displayRoApiList();
|
|
||||||
};
|
|
87
programs/nstool/source/SdkApiString.cpp
Normal file
87
programs/nstool/source/SdkApiString.cpp
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
#include <sstream>
|
||||||
|
#include "SdkApiString.h"
|
||||||
|
|
||||||
|
SdkApiString::SdkApiString(const std::string& full_str) :
|
||||||
|
SdkApiString(API_MIDDLEWARE, "", "")
|
||||||
|
{
|
||||||
|
resolveApiString(full_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
SdkApiString::SdkApiString(ApiType type, const std::string& vender_name, const std::string& module_name) :
|
||||||
|
mApiType(type),
|
||||||
|
mVenderName(vender_name),
|
||||||
|
mModuleName(module_name)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdkApiString::operator=(const SdkApiString& other)
|
||||||
|
{
|
||||||
|
mApiType = other.mApiType;
|
||||||
|
mVenderName = other.mVenderName;
|
||||||
|
mModuleName = other.mModuleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
SdkApiString::ApiType SdkApiString::getApiType() const
|
||||||
|
{
|
||||||
|
return mApiType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdkApiString::setApiType(ApiType type)
|
||||||
|
{
|
||||||
|
mApiType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& SdkApiString::getVenderName() const
|
||||||
|
{
|
||||||
|
return mVenderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdkApiString::setVenderName(const std::string& name)
|
||||||
|
{
|
||||||
|
mVenderName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& SdkApiString::getModuleName() const
|
||||||
|
{
|
||||||
|
return mModuleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdkApiString::setModuleName(const std::string& name)
|
||||||
|
{
|
||||||
|
mModuleName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdkApiString::resolveApiString(const std::string& full_str)
|
||||||
|
{
|
||||||
|
std::stringstream list_stream(full_str);
|
||||||
|
std::string api_type, vender, module;
|
||||||
|
|
||||||
|
std::getline(list_stream, api_type, kSplitChar);
|
||||||
|
std::getline(list_stream, vender, kSplitChar);
|
||||||
|
std::getline(list_stream, module);
|
||||||
|
|
||||||
|
|
||||||
|
if (api_type == kSdkMiddleWareApiString)
|
||||||
|
{
|
||||||
|
if (vender == kVenderNintendo && module.find(kSdkVersionString) != std::string::npos)
|
||||||
|
{
|
||||||
|
mApiType = API_SDK_VERSION;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mApiType = API_MIDDLEWARE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (api_type == kSdkDebugApiString)
|
||||||
|
{
|
||||||
|
mApiType = API_DEBUG;
|
||||||
|
}
|
||||||
|
else if (api_type == kSdkPrivateApiString)
|
||||||
|
{
|
||||||
|
mApiType = API_PRIVATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
mVenderName = vender;
|
||||||
|
mModuleName = module;
|
||||||
|
}
|
43
programs/nstool/source/SdkApiString.h
Normal file
43
programs/nstool/source/SdkApiString.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class SdkApiString
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum ApiType
|
||||||
|
{
|
||||||
|
API_MIDDLEWARE,
|
||||||
|
API_DEBUG,
|
||||||
|
API_PRIVATE,
|
||||||
|
API_SDK_VERSION
|
||||||
|
};
|
||||||
|
|
||||||
|
SdkApiString(const std::string& full_str);
|
||||||
|
SdkApiString(ApiType type, const std::string& vender_name, const std::string& module_name);
|
||||||
|
|
||||||
|
void operator=(const SdkApiString& other);
|
||||||
|
|
||||||
|
ApiType getApiType() const;
|
||||||
|
void setApiType(ApiType type);
|
||||||
|
|
||||||
|
const std::string& getVenderName() const;
|
||||||
|
void setVenderName(const std::string& name);
|
||||||
|
|
||||||
|
const std::string& getModuleName() const;
|
||||||
|
void setModuleName(const std::string& name);
|
||||||
|
private:
|
||||||
|
const std::string kModuleName = "SdkApiString";
|
||||||
|
|
||||||
|
const char kSplitChar = '+';
|
||||||
|
const std::string kSdkMiddleWareApiString = "SDK MW";
|
||||||
|
const std::string kSdkDebugApiString = "SDK Debug";
|
||||||
|
const std::string kSdkPrivateApiString = "SDK Private";
|
||||||
|
const std::string kVenderNintendo = "Nintendo";
|
||||||
|
const std::string kSdkVersionString = "NintendoSdk_nnSdk-";
|
||||||
|
|
||||||
|
ApiType mApiType;
|
||||||
|
std::string mVenderName;
|
||||||
|
std::string mModuleName;
|
||||||
|
|
||||||
|
void resolveApiString(const std::string& full_str);
|
||||||
|
};
|
|
@ -18,6 +18,7 @@
|
||||||
#include <nx/npdm.h>
|
#include <nx/npdm.h>
|
||||||
#include <nx/romfs.h>
|
#include <nx/romfs.h>
|
||||||
#include <nx/nso.h>
|
#include <nx/nso.h>
|
||||||
|
#include <nx/nro.h>
|
||||||
|
|
||||||
UserSettings::UserSettings()
|
UserSettings::UserSettings()
|
||||||
{}
|
{}
|
||||||
|
@ -39,7 +40,7 @@ void UserSettings::showHelp()
|
||||||
printf("\n General Options:\n");
|
printf("\n General Options:\n");
|
||||||
printf(" -d, --dev Use devkit keyset\n");
|
printf(" -d, --dev Use devkit keyset\n");
|
||||||
printf(" -k, --keyset Specify keyset file\n");
|
printf(" -k, --keyset Specify keyset file\n");
|
||||||
printf(" -t, --type Specify input file type [xci, pfs, romfs, nca, npdm, cnmt, nso]\n");
|
printf(" -t, --type Specify input file type [xci, pfs, romfs, nca, npdm, cnmt, nso, nro]\n");
|
||||||
printf(" -y, --verify Verify file\n");
|
printf(" -y, --verify Verify file\n");
|
||||||
printf(" -v, --verbose Verbose output\n");
|
printf(" -v, --verbose Verbose output\n");
|
||||||
printf(" -q, --quiet Minimal output\n");
|
printf(" -q, --quiet Minimal output\n");
|
||||||
|
@ -63,9 +64,11 @@ void UserSettings::showHelp()
|
||||||
printf(" --part1 Extract \"partition 1\" to directory \n");
|
printf(" --part1 Extract \"partition 1\" to directory \n");
|
||||||
printf(" --part2 Extract \"partition 2\" to directory \n");
|
printf(" --part2 Extract \"partition 2\" to directory \n");
|
||||||
printf(" --part3 Extract \"partition 3\" to directory \n");
|
printf(" --part3 Extract \"partition 3\" to directory \n");
|
||||||
printf("\n NSO (Nintendo Software Object)\n");
|
printf("\n NSO (Nintendo Software Object), NRO (Nintendo Relocatable Object)\n");
|
||||||
printf(" nstool [--arch <architecture>] <.nso>\n");
|
printf(" nstool [--listapi --listsym] [--insttype <inst. type>] <file>\n");
|
||||||
printf(" --arch Specify code architecture [32bit, 64bit]\n");
|
printf(" --listapi Print SDK API List.\n");
|
||||||
|
printf(" --listsym Print Dynamic Symbols.\n");
|
||||||
|
printf(" --insttype Specify instruction type [64bit|32bit] (64bit is assumed).\n");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,9 +102,18 @@ bool UserSettings::isListFs() const
|
||||||
return mListFs;
|
return mListFs;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sOptional<nx::npdm::InstructionType>& UserSettings::getArchType() const
|
bool UserSettings::isListApi() const
|
||||||
{
|
{
|
||||||
return mArchType;
|
return mListApi;
|
||||||
|
}
|
||||||
|
bool UserSettings::isListSymbols() const
|
||||||
|
{
|
||||||
|
return mListSymbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
nx::npdm::InstructionType UserSettings::getInstType() const
|
||||||
|
{
|
||||||
|
return mInstructionType;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sOptional<std::string>& UserSettings::getXciUpdatePath() const
|
const sOptional<std::string>& UserSettings::getXciUpdatePath() const
|
||||||
|
@ -289,10 +301,22 @@ void UserSettings::populateCmdArgs(int argc, char** argv, sCmdArgs& cmd_args)
|
||||||
cmd_args.part3_path = args[i+1];
|
cmd_args.part3_path = args[i+1];
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (args[i] == "--arch")
|
else if (args[i] == "--listapi")
|
||||||
|
{
|
||||||
|
if (hasParamter) throw fnd::Exception(kModuleName, args[i] + " does not take a parameter.");
|
||||||
|
cmd_args.list_api = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (args[i] == "--listsym")
|
||||||
|
{
|
||||||
|
if (hasParamter) throw fnd::Exception(kModuleName, args[i] + " does not take a parameter.");
|
||||||
|
cmd_args.list_sym = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (args[i] == "--insttype")
|
||||||
{
|
{
|
||||||
if (!hasParamter) throw fnd::Exception(kModuleName, args[i] + " requries a parameter.");
|
if (!hasParamter) throw fnd::Exception(kModuleName, args[i] + " requries a parameter.");
|
||||||
cmd_args.arch_type = args[i + 1];
|
cmd_args.inst_type = args[i + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
|
@ -552,9 +576,14 @@ void UserSettings::populateUserSettings(sCmdArgs& args)
|
||||||
mNcaPart2Path = args.part2_path;
|
mNcaPart2Path = args.part2_path;
|
||||||
mNcaPart3Path = args.part3_path;
|
mNcaPart3Path = args.part3_path;
|
||||||
|
|
||||||
// determine the architecture type for NSO
|
// determine the architecture type for NSO/NRO
|
||||||
if (args.arch_type.isSet)
|
if (args.inst_type.isSet)
|
||||||
mArchType = getInstructionTypeFromString(*args.arch_type);
|
mInstructionType = getInstructionTypeFromString(*args.inst_type);
|
||||||
|
else
|
||||||
|
mInstructionType = nx::npdm::INSTR_64BIT; // default 64bit
|
||||||
|
|
||||||
|
mListApi = args.list_api.isSet;
|
||||||
|
mListSymbols = args.list_sym.isSet;
|
||||||
|
|
||||||
// determine output path
|
// determine output path
|
||||||
if (args.verbose_output.isSet)
|
if (args.verbose_output.isSet)
|
||||||
|
@ -614,6 +643,8 @@ FileType UserSettings::getFileTypeFromString(const std::string& type_str)
|
||||||
type = FILE_CNMT;
|
type = FILE_CNMT;
|
||||||
else if (str == "nso")
|
else if (str == "nso")
|
||||||
type = FILE_NSO;
|
type = FILE_NSO;
|
||||||
|
else if (str == "nro")
|
||||||
|
type = FILE_NRO;
|
||||||
else
|
else
|
||||||
type = FILE_INVALID;
|
type = FILE_INVALID;
|
||||||
|
|
||||||
|
@ -636,6 +667,8 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
|
||||||
// close file
|
// close file
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
|
fnd::SimpleTextOutput::hxdStyleDump(scratch.getBytes(), scratch.getSize());
|
||||||
|
|
||||||
// prepare decrypted NCA data
|
// prepare decrypted NCA data
|
||||||
byte_t nca_raw[nx::nca::kHeaderSize];
|
byte_t nca_raw[nx::nca::kHeaderSize];
|
||||||
nx::sNcaHeader* nca_header = (nx::sNcaHeader*)(nca_raw + nx::NcaUtils::sectorToOffset(1));
|
nx::sNcaHeader* nca_header = (nx::sNcaHeader*)(nca_raw + nx::NcaUtils::sectorToOffset(1));
|
||||||
|
@ -673,6 +706,9 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
|
||||||
// test nso
|
// test nso
|
||||||
else if (_ASSERT_SIZE(sizeof(nx::sNsoHeader)) && _QUICK_CAST(nx::sNsoHeader, 0)->signature.get() == nx::nso::kNsoSig)
|
else if (_ASSERT_SIZE(sizeof(nx::sNsoHeader)) && _QUICK_CAST(nx::sNsoHeader, 0)->signature.get() == nx::nso::kNsoSig)
|
||||||
file_type = FILE_NSO;
|
file_type = FILE_NSO;
|
||||||
|
// test nso
|
||||||
|
else if (_ASSERT_SIZE(sizeof(nx::sNroHeader)) && _QUICK_CAST(nx::sNroHeader, 0)->signature.get() == nx::nro::kNroSig)
|
||||||
|
file_type = FILE_NRO;
|
||||||
// else unrecognised
|
// else unrecognised
|
||||||
else
|
else
|
||||||
file_type = FILE_INVALID;
|
file_type = FILE_INVALID;
|
||||||
|
@ -694,7 +730,7 @@ nx::npdm::InstructionType UserSettings::getInstructionTypeFromString(const std::
|
||||||
else if (str == "64bit")
|
else if (str == "64bit")
|
||||||
type = nx::npdm::INSTR_64BIT;
|
type = nx::npdm::INSTR_64BIT;
|
||||||
else
|
else
|
||||||
throw fnd::Exception(kModuleName, "Unsupported architecture type: " + str);
|
throw fnd::Exception(kModuleName, "Unsupported instruction type: " + str);
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,9 @@ public:
|
||||||
|
|
||||||
// specialised toggles
|
// specialised toggles
|
||||||
bool isListFs() const;
|
bool isListFs() const;
|
||||||
const sOptional<nx::npdm::InstructionType>& getArchType() const;
|
bool isListApi() const;
|
||||||
|
bool isListSymbols() const;
|
||||||
|
nx::npdm::InstructionType getInstType() const;
|
||||||
|
|
||||||
// specialised paths
|
// specialised paths
|
||||||
const sOptional<std::string>& getXciUpdatePath() const;
|
const sOptional<std::string>& getXciUpdatePath() const;
|
||||||
|
@ -59,7 +61,9 @@ private:
|
||||||
sOptional<std::string> part1_path;
|
sOptional<std::string> part1_path;
|
||||||
sOptional<std::string> part2_path;
|
sOptional<std::string> part2_path;
|
||||||
sOptional<std::string> part3_path;
|
sOptional<std::string> part3_path;
|
||||||
sOptional<std::string> arch_type;
|
sOptional<bool> list_api;
|
||||||
|
sOptional<bool> list_sym;
|
||||||
|
sOptional<std::string> inst_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string mInputPath;
|
std::string mInputPath;
|
||||||
|
@ -80,7 +84,9 @@ private:
|
||||||
sOptional<std::string> mNcaPart2Path;
|
sOptional<std::string> mNcaPart2Path;
|
||||||
sOptional<std::string> mNcaPart3Path;
|
sOptional<std::string> mNcaPart3Path;
|
||||||
|
|
||||||
sOptional<nx::npdm::InstructionType> mArchType;
|
bool mListApi;
|
||||||
|
bool mListSymbols;
|
||||||
|
nx::npdm::InstructionType mInstructionType;
|
||||||
|
|
||||||
void populateCmdArgs(int argc, char** argv, sCmdArgs& cmd_args);
|
void populateCmdArgs(int argc, char** argv, sCmdArgs& cmd_args);
|
||||||
void populateKeyset(sCmdArgs& args);
|
void populateKeyset(sCmdArgs& args);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include "NcaProcess.h"
|
#include "NcaProcess.h"
|
||||||
#include "NpdmProcess.h"
|
#include "NpdmProcess.h"
|
||||||
#include "CnmtProcess.h"
|
#include "CnmtProcess.h"
|
||||||
#include "NsoProcess.h"
|
#include "CodeObjectProcess.h"
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
|
@ -108,18 +108,20 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
cnmt.process();
|
cnmt.process();
|
||||||
}
|
}
|
||||||
else if (user_set.getFileType() == FILE_NSO)
|
else if (user_set.getFileType() == FILE_NSO || user_set.getFileType() == FILE_NRO)
|
||||||
{
|
{
|
||||||
NsoProcess nso;
|
CodeObjectProcess obj;
|
||||||
|
|
||||||
nso.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE);
|
obj.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE);
|
||||||
nso.setCliOutputMode(user_set.getCliOutputType());
|
obj.setCliOutputMode(user_set.getCliOutputType());
|
||||||
nso.setVerifyMode(user_set.isVerifyFile());
|
obj.setVerifyMode(user_set.isVerifyFile());
|
||||||
|
|
||||||
if (user_set.getArchType().isSet)
|
obj.setCodeObjectType(user_set.getFileType() == FILE_NSO ? obj.OBJ_NSO : obj.OBJ_NRO);
|
||||||
nso.setArchType(user_set.getArchType().var);
|
obj.setInstructionType(user_set.getInstType());
|
||||||
|
obj.setListApi(user_set.isListApi());
|
||||||
|
obj.setListSymbols(user_set.isListSymbols());
|
||||||
|
|
||||||
nso.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const fnd::Exception& e) {
|
catch (const fnd::Exception& e) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ enum FileType
|
||||||
FILE_NPDM,
|
FILE_NPDM,
|
||||||
FILE_CNMT,
|
FILE_CNMT,
|
||||||
FILE_NSO,
|
FILE_NSO,
|
||||||
|
FILE_NRO,
|
||||||
FILE_INVALID = -1,
|
FILE_INVALID = -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue