mirror of
https://github.com/jakcron/nstool.git
synced 2024-12-22 10:45:28 +00:00
Port NsoProcess and NroProcess to libtoolchain.
This commit is contained in:
parent
948c0e965f
commit
aeb87156b5
|
@ -76,7 +76,7 @@
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\liblz4\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include</AdditionalIncludeDirectories>
|
||||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
|
@ -86,7 +86,7 @@
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\liblz4\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include</AdditionalIncludeDirectories>
|
||||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\liblz4\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include</AdditionalIncludeDirectories>
|
||||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
|
@ -114,7 +114,7 @@
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\liblz4\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include</AdditionalIncludeDirectories>
|
||||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
|
@ -188,6 +188,8 @@
|
||||||
<ClCompile Include="..\..\..\src\KeyBag.cpp" />
|
<ClCompile Include="..\..\..\src\KeyBag.cpp" />
|
||||||
<ClCompile Include="..\..\..\src\main.cpp" />
|
<ClCompile Include="..\..\..\src\main.cpp" />
|
||||||
<ClCompile Include="..\..\..\src\NacpProcess.cpp" />
|
<ClCompile Include="..\..\..\src\NacpProcess.cpp" />
|
||||||
|
<ClCompile Include="..\..\..\src\NroProcess.cpp" />
|
||||||
|
<ClCompile Include="..\..\..\src\NsoProcess.cpp" />
|
||||||
<ClCompile Include="..\..\..\src\PfsProcess.cpp" />
|
<ClCompile Include="..\..\..\src\PfsProcess.cpp" />
|
||||||
<ClCompile Include="..\..\..\src\PkiCertProcess.cpp" />
|
<ClCompile Include="..\..\..\src\PkiCertProcess.cpp" />
|
||||||
<ClCompile Include="..\..\..\src\PkiValidator.cpp" />
|
<ClCompile Include="..\..\..\src\PkiValidator.cpp" />
|
||||||
|
|
|
@ -143,5 +143,11 @@
|
||||||
<ClCompile Include="..\..\..\src\PkiCertProcess.cpp">
|
<ClCompile Include="..\..\..\src\PkiCertProcess.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\..\src\NsoProcess.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\..\src\NroProcess.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
192
src/NroProcess.cpp
Normal file
192
src/NroProcess.cpp
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
#include "NroProcess.h"
|
||||||
|
|
||||||
|
#include <nn/hac/define/nro-hb.h>
|
||||||
|
|
||||||
|
nstool::NroProcess::NroProcess() :
|
||||||
|
mModuleName("nstool::NroProcess"),
|
||||||
|
mFile(),
|
||||||
|
mCliOutputMode(true, false, false, false),
|
||||||
|
mVerify(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NroProcess::process()
|
||||||
|
{
|
||||||
|
importHeader();
|
||||||
|
importCodeSegments();
|
||||||
|
|
||||||
|
if (mCliOutputMode.show_basic_info)
|
||||||
|
displayHeader();
|
||||||
|
|
||||||
|
processRoMeta();
|
||||||
|
|
||||||
|
if (mIsHomebrewNro)
|
||||||
|
mAssetProc.process();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NroProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
|
||||||
|
{
|
||||||
|
mFile = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NroProcess::setCliOutputMode(CliOutputMode type)
|
||||||
|
{
|
||||||
|
mCliOutputMode = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NroProcess::setVerifyMode(bool verify)
|
||||||
|
{
|
||||||
|
mVerify = verify;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NroProcess::setIs64BitInstruction(bool flag)
|
||||||
|
{
|
||||||
|
mRoMeta.setIs64BitInstruction(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NroProcess::setListApi(bool listApi)
|
||||||
|
{
|
||||||
|
mRoMeta.setListApi(listApi);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NroProcess::setListSymbols(bool listSymbols)
|
||||||
|
{
|
||||||
|
mRoMeta.setListSymbols(listSymbols);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NroProcess::setAssetIconExtractPath(const tc::io::Path& path)
|
||||||
|
{
|
||||||
|
mAssetProc.setIconExtractPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NroProcess::setAssetNacpExtractPath(const tc::io::Path& path)
|
||||||
|
{
|
||||||
|
mAssetProc.setNacpExtractPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NroProcess::setAssetRomfsShowFsTree(bool show_fs_tree)
|
||||||
|
{
|
||||||
|
mAssetProc.setRomfsShowFsTree(show_fs_tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NroProcess::setAssetRomfsExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs)
|
||||||
|
{
|
||||||
|
mAssetProc.setRomfsExtractJobs(extract_jobs);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nstool::RoMetadataProcess& nstool::NroProcess::getRoMetadataProcess() const
|
||||||
|
{
|
||||||
|
return mRoMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NroProcess::importHeader()
|
||||||
|
{
|
||||||
|
if (mFile == nullptr)
|
||||||
|
{
|
||||||
|
throw tc::Exception(mModuleName, "No file reader set.");
|
||||||
|
}
|
||||||
|
if (mFile->canRead() == false || mFile->canSeek() == false)
|
||||||
|
{
|
||||||
|
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if file_size is smaller than NRO header size
|
||||||
|
if (tc::io::IOUtil::castInt64ToSize(mFile->length()) < sizeof(nn::hac::sNroHeader))
|
||||||
|
{
|
||||||
|
throw tc::Exception(mModuleName, "Corrupt NRO: file too small.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// read nro
|
||||||
|
tc::ByteData scratch = tc::ByteData(sizeof(nn::hac::sNroHeader));
|
||||||
|
mFile->seek(0, tc::io::SeekOrigin::Begin);
|
||||||
|
mFile->read(scratch.data(), scratch.size());
|
||||||
|
|
||||||
|
// parse nro header
|
||||||
|
mHdr.fromBytes(scratch.data(), scratch.size());
|
||||||
|
|
||||||
|
// setup homebrew extension
|
||||||
|
nn::hac::sNroHeader* raw_hdr = (nn::hac::sNroHeader*)scratch.data();
|
||||||
|
|
||||||
|
int64_t file_size = mFile->length();
|
||||||
|
if (((tc::bn::le64<uint64_t>*)raw_hdr->reserved_0.data())->unwrap() == nn::hac::nro::kNroHomebrewStructMagic && file_size > int64_t(mHdr.getNroSize()))
|
||||||
|
{
|
||||||
|
mIsHomebrewNro = true;
|
||||||
|
mAssetProc.setInputFile(std::make_shared<tc::io::SubStream>(tc::io::SubStream(mFile, int64_t(mHdr.getNroSize()), file_size - int64_t(mHdr.getNroSize()))));
|
||||||
|
mAssetProc.setCliOutputMode(mCliOutputMode);
|
||||||
|
mAssetProc.setVerifyMode(mVerify);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mIsHomebrewNro = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NroProcess::importCodeSegments()
|
||||||
|
{
|
||||||
|
if (mHdr.getTextInfo().size > 0)
|
||||||
|
{
|
||||||
|
mTextBlob = tc::ByteData(mHdr.getTextInfo().size);
|
||||||
|
mFile->seek(mHdr.getTextInfo().memory_offset, tc::io::SeekOrigin::Begin);
|
||||||
|
mFile->read(mTextBlob.data(), mTextBlob.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mHdr.getRoInfo().size > 0)
|
||||||
|
{
|
||||||
|
mRoBlob = tc::ByteData(mHdr.getRoInfo().size);
|
||||||
|
mFile->seek(mHdr.getRoInfo().memory_offset, tc::io::SeekOrigin::Begin);
|
||||||
|
mFile->read(mRoBlob.data(), mRoBlob.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mHdr.getDataInfo().size > 0)
|
||||||
|
{
|
||||||
|
mDataBlob = tc::ByteData(mHdr.getDataInfo().size);
|
||||||
|
mFile->seek(mHdr.getDataInfo().memory_offset, tc::io::SeekOrigin::Begin);
|
||||||
|
mFile->read(mDataBlob.data(), mDataBlob.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NroProcess::displayHeader()
|
||||||
|
{
|
||||||
|
fmt::print("[NRO Header]\n");
|
||||||
|
fmt::print(" RoCrt: \n");
|
||||||
|
fmt::print(" EntryPoint: 0x{:x}\n", mHdr.getRoCrtEntryPoint());
|
||||||
|
fmt::print(" ModOffset: 0x{:x}\n", mHdr.getRoCrtModOffset());
|
||||||
|
fmt::print(" ModuleId: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getModuleId().data(), mHdr.getModuleId().size(), false, ""));
|
||||||
|
fmt::print(" NroSize: 0x{:x}\n", mHdr.getNroSize());
|
||||||
|
fmt::print(" Program Sections:\n");
|
||||||
|
fmt::print(" .text:\n");
|
||||||
|
fmt::print(" Offset: 0x{:x}\n", mHdr.getTextInfo().memory_offset);
|
||||||
|
fmt::print(" Size: 0x{:x}\n", mHdr.getTextInfo().size);
|
||||||
|
fmt::print(" .ro:\n");
|
||||||
|
fmt::print(" Offset: 0x{:x}\n", mHdr.getRoInfo().memory_offset);
|
||||||
|
fmt::print(" Size: 0x{:x}\n", mHdr.getRoInfo().size);
|
||||||
|
if (mCliOutputMode.show_extended_info)
|
||||||
|
{
|
||||||
|
fmt::print(" .api_info:\n");
|
||||||
|
fmt::print(" Offset: 0x{:x}\n", mHdr.getRoEmbeddedInfo().memory_offset);
|
||||||
|
fmt::print(" Size: 0x{:x}\n", mHdr.getRoEmbeddedInfo().size);
|
||||||
|
fmt::print(" .dynstr:\n");
|
||||||
|
fmt::print(" Offset: 0x{:x}\n", mHdr.getRoDynStrInfo().memory_offset);
|
||||||
|
fmt::print(" Size: 0x{:x}\n", mHdr.getRoDynStrInfo().size);
|
||||||
|
fmt::print(" .dynsym:\n");
|
||||||
|
fmt::print(" Offset: 0x{:x}\n", mHdr.getRoDynSymInfo().memory_offset);
|
||||||
|
fmt::print(" Size: 0x{:x}\n", mHdr.getRoDynSymInfo().size);
|
||||||
|
}
|
||||||
|
fmt::print(" .data:\n");
|
||||||
|
fmt::print(" Offset: 0x{:x}\n", mHdr.getDataInfo().memory_offset);
|
||||||
|
fmt::print(" Size: 0x{:x}\n", mHdr.getDataInfo().size);
|
||||||
|
fmt::print(" .bss:\n");
|
||||||
|
fmt::print(" Size: 0x{:x}\n", mHdr.getBssSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NroProcess::processRoMeta()
|
||||||
|
{
|
||||||
|
if (mRoBlob.size())
|
||||||
|
{
|
||||||
|
// setup ro metadata
|
||||||
|
mRoMeta.setApiInfo(mHdr.getRoEmbeddedInfo().memory_offset, mHdr.getRoEmbeddedInfo().size);
|
||||||
|
mRoMeta.setDynSym(mHdr.getRoDynSymInfo().memory_offset, mHdr.getRoDynSymInfo().size);
|
||||||
|
mRoMeta.setDynStr(mHdr.getRoDynStrInfo().memory_offset, mHdr.getRoDynStrInfo().size);
|
||||||
|
mRoMeta.setRoBinary(mRoBlob);
|
||||||
|
mRoMeta.setCliOutputMode(mCliOutputMode);
|
||||||
|
mRoMeta.process();
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,6 @@
|
||||||
#include "RoMetadataProcess.h"
|
#include "RoMetadataProcess.h"
|
||||||
#include "AssetProcess.h"
|
#include "AssetProcess.h"
|
||||||
|
|
||||||
#include <nn/hac/define/meta.h>
|
|
||||||
#include <nn/hac/NroHeader.h>
|
#include <nn/hac/NroHeader.h>
|
||||||
|
|
||||||
namespace nstool {
|
namespace nstool {
|
||||||
|
@ -24,14 +23,14 @@ public:
|
||||||
void setListSymbols(bool listSymbols);
|
void setListSymbols(bool listSymbols);
|
||||||
|
|
||||||
// for homebrew NROs with Asset blobs appended
|
// for homebrew NROs with Asset blobs appended
|
||||||
void setAssetListFs(bool list);
|
|
||||||
void setAssetIconExtractPath(const tc::io::Path& path);
|
void setAssetIconExtractPath(const tc::io::Path& path);
|
||||||
void setAssetNacpExtractPath(const tc::io::Path& path);
|
void setAssetNacpExtractPath(const tc::io::Path& path);
|
||||||
void setAssetRomfsExtractPath(const tc::io::Path& path);
|
void setAssetRomfsShowFsTree(bool show_fs_tree);
|
||||||
|
void setAssetRomfsExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs);
|
||||||
|
|
||||||
const RoMetadataProcess& getRoMetadataProcess() const;
|
const nstool::RoMetadataProcess& getRoMetadataProcess() const;
|
||||||
private:
|
private:
|
||||||
const std::string kModuleName = "NroProcess";
|
std::string mModuleName;
|
||||||
|
|
||||||
std::shared_ptr<tc::io::IStream> mFile;
|
std::shared_ptr<tc::io::IStream> mFile;
|
||||||
CliOutputMode mCliOutputMode;
|
CliOutputMode mCliOutputMode;
|
||||||
|
@ -39,9 +38,9 @@ private:
|
||||||
|
|
||||||
nn::hac::NroHeader mHdr;
|
nn::hac::NroHeader mHdr;
|
||||||
tc::ByteData mTextBlob, mRoBlob, mDataBlob;
|
tc::ByteData mTextBlob, mRoBlob, mDataBlob;
|
||||||
RoMetadataProcess mRoMeta;
|
nstool::RoMetadataProcess mRoMeta;
|
||||||
bool mIsHomebrewNro;
|
bool mIsHomebrewNro;
|
||||||
AssetProcess mAssetProc;
|
nstool::AssetProcess mAssetProc;
|
||||||
|
|
||||||
void importHeader();
|
void importHeader();
|
||||||
void importCodeSegments();
|
void importCodeSegments();
|
||||||
|
|
285
src/NsoProcess.cpp
Normal file
285
src/NsoProcess.cpp
Normal file
|
@ -0,0 +1,285 @@
|
||||||
|
#include "NsoProcess.h"
|
||||||
|
|
||||||
|
#include <lz4.h>
|
||||||
|
|
||||||
|
nstool::NsoProcess::NsoProcess() :
|
||||||
|
mModuleName("nstool::NsoProcess"),
|
||||||
|
mFile(),
|
||||||
|
mCliOutputMode(true, false, false, false),
|
||||||
|
mVerify(false),
|
||||||
|
mIs64BitInstruction(true),
|
||||||
|
mListApi(false),
|
||||||
|
mListSymbols(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NsoProcess::process()
|
||||||
|
{
|
||||||
|
importHeader();
|
||||||
|
importCodeSegments();
|
||||||
|
if (mCliOutputMode.show_basic_info)
|
||||||
|
displayNsoHeader();
|
||||||
|
|
||||||
|
processRoMeta();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NsoProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
|
||||||
|
{
|
||||||
|
mFile = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NsoProcess::setCliOutputMode(CliOutputMode type)
|
||||||
|
{
|
||||||
|
mCliOutputMode = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NsoProcess::setVerifyMode(bool verify)
|
||||||
|
{
|
||||||
|
mVerify = verify;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NsoProcess::setIs64BitInstruction(bool flag)
|
||||||
|
{
|
||||||
|
mRoMeta.setIs64BitInstruction(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NsoProcess::setListApi(bool listApi)
|
||||||
|
{
|
||||||
|
mRoMeta.setListApi(listApi);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NsoProcess::setListSymbols(bool listSymbols)
|
||||||
|
{
|
||||||
|
mRoMeta.setListSymbols(listSymbols);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nstool::RoMetadataProcess& nstool::NsoProcess::getRoMetadataProcess() const
|
||||||
|
{
|
||||||
|
return mRoMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NsoProcess::importHeader()
|
||||||
|
{
|
||||||
|
if (mFile == nullptr)
|
||||||
|
{
|
||||||
|
throw tc::Exception(mModuleName, "No file reader set.");
|
||||||
|
}
|
||||||
|
if (mFile->canRead() == false || mFile->canSeek() == false)
|
||||||
|
{
|
||||||
|
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if file_size is smaller than NSO header size
|
||||||
|
size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length());
|
||||||
|
if (file_size < sizeof(nn::hac::sNsoHeader))
|
||||||
|
{
|
||||||
|
throw tc::Exception(mModuleName, "Corrupt NSO: file too small.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// read nso
|
||||||
|
tc::ByteData scratch = tc::ByteData(sizeof(nn::hac::sNsoHeader));
|
||||||
|
mFile->seek(0, tc::io::SeekOrigin::Begin);
|
||||||
|
mFile->read(scratch.data(), scratch.size());
|
||||||
|
|
||||||
|
// parse nso header
|
||||||
|
mHdr.fromBytes(scratch.data(), scratch.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NsoProcess::importCodeSegments()
|
||||||
|
{
|
||||||
|
tc::ByteData scratch;
|
||||||
|
nn::hac::detail::sha256_hash_t calc_hash;
|
||||||
|
|
||||||
|
// process text segment
|
||||||
|
if (mHdr.getTextSegmentInfo().is_compressed)
|
||||||
|
{
|
||||||
|
// allocate/read compressed text
|
||||||
|
scratch = tc::ByteData(mHdr.getTextSegmentInfo().file_layout.size);
|
||||||
|
mFile->seek(mHdr.getTextSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
|
||||||
|
mFile->read(scratch.data(), scratch.size());
|
||||||
|
|
||||||
|
// allocate for decompressed text segment
|
||||||
|
mTextBlob = tc::ByteData(mHdr.getTextSegmentInfo().memory_layout.size);
|
||||||
|
|
||||||
|
// decompress text segment
|
||||||
|
if (decompressData(scratch.data(), scratch.size(), mTextBlob.data(), mTextBlob.size()) != mTextBlob.size())
|
||||||
|
{
|
||||||
|
throw tc::Exception(mModuleName, "NSO text segment failed to decompress");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// read text segment directly (not compressed)
|
||||||
|
mTextBlob = tc::ByteData(mHdr.getTextSegmentInfo().file_layout.size);
|
||||||
|
mFile->seek(mHdr.getTextSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
|
||||||
|
mFile->read(mTextBlob.data(), mTextBlob.size());
|
||||||
|
}
|
||||||
|
if (mHdr.getTextSegmentInfo().is_hashed)
|
||||||
|
{
|
||||||
|
tc::crypto::GenerateSha256Hash(calc_hash.data(), mTextBlob.data(), mTextBlob.size());
|
||||||
|
if (calc_hash != mHdr.getTextSegmentInfo().hash)
|
||||||
|
{
|
||||||
|
throw tc::Exception(mModuleName, "NSO text segment failed SHA256 verification");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process ro segment
|
||||||
|
if (mHdr.getRoSegmentInfo().is_compressed)
|
||||||
|
{
|
||||||
|
// allocate/read compressed ro segment
|
||||||
|
scratch = tc::ByteData(mHdr.getRoSegmentInfo().file_layout.size);
|
||||||
|
mFile->seek(mHdr.getRoSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
|
||||||
|
mFile->read(scratch.data(), scratch.size());
|
||||||
|
|
||||||
|
// allocate for decompressed ro segment
|
||||||
|
mRoBlob = tc::ByteData(mHdr.getRoSegmentInfo().memory_layout.size);
|
||||||
|
|
||||||
|
// decompress ro segment
|
||||||
|
if (decompressData(scratch.data(), scratch.size(), mRoBlob.data(), mRoBlob.size()) != mRoBlob.size())
|
||||||
|
{
|
||||||
|
throw tc::Exception(mModuleName, "NSO ro segment failed to decompress");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// read ro segment directly (not compressed)
|
||||||
|
mRoBlob = tc::ByteData(mHdr.getRoSegmentInfo().file_layout.size);
|
||||||
|
mFile->seek(mHdr.getRoSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
|
||||||
|
mFile->read(mRoBlob.data(), mRoBlob.size());
|
||||||
|
}
|
||||||
|
if (mHdr.getRoSegmentInfo().is_hashed)
|
||||||
|
{
|
||||||
|
tc::crypto::GenerateSha256Hash(calc_hash.data(), mRoBlob.data(), mRoBlob.size());
|
||||||
|
if (calc_hash != mHdr.getRoSegmentInfo().hash)
|
||||||
|
{
|
||||||
|
throw tc::Exception(mModuleName, "NSO ro segment failed SHA256 verification");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process ro segment
|
||||||
|
if (mHdr.getDataSegmentInfo().is_compressed)
|
||||||
|
{
|
||||||
|
// allocate/read compressed ro segment
|
||||||
|
scratch = tc::ByteData(mHdr.getDataSegmentInfo().file_layout.size);
|
||||||
|
mFile->seek(mHdr.getDataSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
|
||||||
|
mFile->read(scratch.data(), scratch.size());
|
||||||
|
|
||||||
|
// allocate for decompressed ro segment
|
||||||
|
mDataBlob = tc::ByteData(mHdr.getDataSegmentInfo().memory_layout.size);
|
||||||
|
|
||||||
|
// decompress ro segment
|
||||||
|
if (decompressData(scratch.data(), scratch.size(), mDataBlob.data(), mDataBlob.size()) != mDataBlob.size())
|
||||||
|
{
|
||||||
|
throw tc::Exception(mModuleName, "NSO data segment failed to decompress");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// read ro segment directly (not compressed)
|
||||||
|
mDataBlob = tc::ByteData(mHdr.getDataSegmentInfo().file_layout.size);
|
||||||
|
mFile->seek(mHdr.getDataSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
|
||||||
|
mFile->read(mDataBlob.data(), mDataBlob.size());
|
||||||
|
}
|
||||||
|
if (mHdr.getDataSegmentInfo().is_hashed)
|
||||||
|
{
|
||||||
|
tc::crypto::GenerateSha256Hash(calc_hash.data(), mDataBlob.data(), mDataBlob.size());
|
||||||
|
if (calc_hash != mHdr.getDataSegmentInfo().hash)
|
||||||
|
{
|
||||||
|
throw tc::Exception(mModuleName, "NSO data segment failed SHA256 verification");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NsoProcess::displayNsoHeader()
|
||||||
|
{
|
||||||
|
fmt::print("[NSO Header]\n");
|
||||||
|
fmt::print(" ModuleId: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getModuleId().data(), mHdr.getModuleId().size(), false, ""));
|
||||||
|
if (mCliOutputMode.show_layout)
|
||||||
|
{
|
||||||
|
fmt::print(" Program Segments:\n");
|
||||||
|
fmt::print(" .module_name:\n");
|
||||||
|
fmt::print(" FileOffset: 0x{:x}\n", mHdr.getModuleNameInfo().offset);
|
||||||
|
fmt::print(" FileSize: 0x{:x}\n", mHdr.getModuleNameInfo().size);
|
||||||
|
fmt::print(" .text:\n");
|
||||||
|
fmt::print(" FileOffset: 0x{:x}\n", mHdr.getTextSegmentInfo().file_layout.offset);
|
||||||
|
fmt::print(" FileSize: 0x{:x}{:s}\n", mHdr.getTextSegmentInfo().file_layout.size, (mHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : ""));
|
||||||
|
fmt::print(" .ro:\n");
|
||||||
|
fmt::print(" FileOffset: 0x{:x}\n", mHdr.getRoSegmentInfo().file_layout.offset);
|
||||||
|
fmt::print(" FileSize: 0x{:x}{:s}\n", mHdr.getRoSegmentInfo().file_layout.size, (mHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : ""));
|
||||||
|
fmt::print(" .data:\n");
|
||||||
|
fmt::print(" FileOffset: 0x{:x}\n", mHdr.getDataSegmentInfo().file_layout.offset);
|
||||||
|
fmt::print(" FileSize: 0x{:x}{:s}\n", mHdr.getDataSegmentInfo().file_layout.size, (mHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : ""));
|
||||||
|
}
|
||||||
|
fmt::print(" Program Sections:\n");
|
||||||
|
fmt::print(" .text:\n");
|
||||||
|
fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getTextSegmentInfo().memory_layout.offset);
|
||||||
|
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getTextSegmentInfo().memory_layout.size);
|
||||||
|
if (mHdr.getTextSegmentInfo().is_hashed && mCliOutputMode.show_extended_info)
|
||||||
|
{
|
||||||
|
fmt::print(" Hash: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getTextSegmentInfo().hash.data(), mHdr.getTextSegmentInfo().hash.size(), false, ""));
|
||||||
|
}
|
||||||
|
fmt::print(" .ro:\n");
|
||||||
|
fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getRoSegmentInfo().memory_layout.offset);
|
||||||
|
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getRoSegmentInfo().memory_layout.size);
|
||||||
|
if (mHdr.getRoSegmentInfo().is_hashed && mCliOutputMode.show_extended_info)
|
||||||
|
{
|
||||||
|
fmt::print(" Hash: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getRoSegmentInfo().hash.data(), mHdr.getRoSegmentInfo().hash.size(), false, ""));
|
||||||
|
}
|
||||||
|
if (mCliOutputMode.show_extended_info)
|
||||||
|
{
|
||||||
|
fmt::print(" .api_info:\n");
|
||||||
|
fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getRoEmbeddedInfo().offset);
|
||||||
|
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getRoEmbeddedInfo().size);
|
||||||
|
fmt::print(" .dynstr:\n");
|
||||||
|
fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getRoDynStrInfo().offset);
|
||||||
|
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getRoDynStrInfo().size);
|
||||||
|
fmt::print(" .dynsym:\n");
|
||||||
|
fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getRoDynSymInfo().offset);
|
||||||
|
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getRoDynSymInfo().size);
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::print(" .data:\n");
|
||||||
|
fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getDataSegmentInfo().memory_layout.offset);
|
||||||
|
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getDataSegmentInfo().memory_layout.size);
|
||||||
|
if (mHdr.getDataSegmentInfo().is_hashed && mCliOutputMode.show_extended_info)
|
||||||
|
{
|
||||||
|
fmt::print(" Hash: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getDataSegmentInfo().hash.data(), mHdr.getDataSegmentInfo().hash.size(), false, ""));
|
||||||
|
}
|
||||||
|
fmt::print(" .bss:\n");
|
||||||
|
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getBssSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
void nstool::NsoProcess::processRoMeta()
|
||||||
|
{
|
||||||
|
if (mRoBlob.size())
|
||||||
|
{
|
||||||
|
// setup ro metadata
|
||||||
|
mRoMeta.setApiInfo(mHdr.getRoEmbeddedInfo().offset, mHdr.getRoEmbeddedInfo().size);
|
||||||
|
mRoMeta.setDynSym(mHdr.getRoDynSymInfo().offset, mHdr.getRoDynSymInfo().size);
|
||||||
|
mRoMeta.setDynStr(mHdr.getRoDynStrInfo().offset, mHdr.getRoDynStrInfo().size);
|
||||||
|
mRoMeta.setRoBinary(mRoBlob);
|
||||||
|
mRoMeta.setCliOutputMode(mCliOutputMode);
|
||||||
|
mRoMeta.process();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t nstool::NsoProcess::decompressData(const byte_t* src, size_t src_len, byte_t* dst, size_t dst_capacity)
|
||||||
|
{
|
||||||
|
if (src_len >= LZ4_MAX_INPUT_SIZE)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t src_len_input = int32_t(src_len);
|
||||||
|
int32_t dst_capcacity_input = (dst_capacity < LZ4_MAX_INPUT_SIZE) ? int32_t(dst_capacity) : LZ4_MAX_INPUT_SIZE;
|
||||||
|
|
||||||
|
int32_t decomp_size = LZ4_decompress_safe((const char*)src, (char*)dst, src_len_input, dst_capcacity_input);
|
||||||
|
|
||||||
|
if (decomp_size < 0)
|
||||||
|
{
|
||||||
|
memset(dst, 0, dst_capacity);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size_t(decomp_size);
|
||||||
|
}
|
|
@ -22,9 +22,9 @@ public:
|
||||||
void setListApi(bool listApi);
|
void setListApi(bool listApi);
|
||||||
void setListSymbols(bool listSymbols);
|
void setListSymbols(bool listSymbols);
|
||||||
|
|
||||||
const RoMetadataProcess& getRoMetadataProcess() const;
|
const nstool::RoMetadataProcess& getRoMetadataProcess() const;
|
||||||
private:
|
private:
|
||||||
const std::string kModuleName = "NsoProcess";
|
std::string mModuleName;
|
||||||
|
|
||||||
std::shared_ptr<tc::io::IStream> mFile;
|
std::shared_ptr<tc::io::IStream> mFile;
|
||||||
CliOutputMode mCliOutputMode;
|
CliOutputMode mCliOutputMode;
|
||||||
|
@ -35,12 +35,15 @@ private:
|
||||||
|
|
||||||
nn::hac::NsoHeader mHdr;
|
nn::hac::NsoHeader mHdr;
|
||||||
tc::ByteData mTextBlob, mRoBlob, mDataBlob;
|
tc::ByteData mTextBlob, mRoBlob, mDataBlob;
|
||||||
RoMetadataProcess mRoMeta;
|
nstool::RoMetadataProcess mRoMeta;
|
||||||
|
|
||||||
void importHeader();
|
void importHeader();
|
||||||
void importCodeSegments();
|
void importCodeSegments();
|
||||||
void displayNsoHeader();
|
void displayNsoHeader();
|
||||||
void processRoMeta();
|
void processRoMeta();
|
||||||
|
|
||||||
|
size_t decompressData(const byte_t* src, size_t src_len, byte_t* dst, size_t dst_capacity);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
11
src/main.cpp
11
src/main.cpp
|
@ -9,8 +9,8 @@
|
||||||
//#include "NcaProcess.h"
|
//#include "NcaProcess.h"
|
||||||
//#include "MetaProcess.h"
|
//#include "MetaProcess.h"
|
||||||
#include "CnmtProcess.h"
|
#include "CnmtProcess.h"
|
||||||
//#include "NsoProcess.h"
|
#include "NsoProcess.h"
|
||||||
//#include "NroProcess.h"
|
#include "NroProcess.h"
|
||||||
#include "NacpProcess.h"
|
#include "NacpProcess.h"
|
||||||
//#include "IniProcess.h"
|
//#include "IniProcess.h"
|
||||||
//#include "KipProcess.h"
|
//#include "KipProcess.h"
|
||||||
|
@ -115,7 +115,6 @@ int umain(const std::vector<std::string>& args, const std::vector<std::string>&
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NSO)
|
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NSO)
|
||||||
{
|
{
|
||||||
nstool::NsoProcess obj;
|
nstool::NsoProcess obj;
|
||||||
|
@ -147,13 +146,11 @@ int umain(const std::vector<std::string>& args, const std::vector<std::string>&
|
||||||
if (set.aset.nacp_extract_path.isSet())
|
if (set.aset.nacp_extract_path.isSet())
|
||||||
obj.setAssetNacpExtractPath(set.aset.nacp_extract_path.get());
|
obj.setAssetNacpExtractPath(set.aset.nacp_extract_path.get());
|
||||||
|
|
||||||
if (set.fs.extract_path.isSet())
|
obj.setAssetRomfsShowFsTree(set.fs.show_fs_tree);
|
||||||
obj.setAssetRomfsExtractPath(set.fs.extract_path.get());
|
obj.setAssetRomfsExtractJobs(set.fs.extract_jobs);
|
||||||
obj.setAssetListFs(set.fs.show_fs_tree);
|
|
||||||
|
|
||||||
obj.process();
|
obj.process();
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NACP)
|
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NACP)
|
||||||
{
|
{
|
||||||
nstool::NacpProcess obj;
|
nstool::NacpProcess obj;
|
||||||
|
|
|
@ -1,174 +0,0 @@
|
||||||
#include <iostream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <fnd/SimpleTextOutput.h>
|
|
||||||
#include <fnd/OffsetAdjustedIFile.h>
|
|
||||||
#include <fnd/Vec.h>
|
|
||||||
#include <fnd/lz4.h>
|
|
||||||
#include <nn/hac/define/nro-hb.h>
|
|
||||||
#include "NroProcess.h"
|
|
||||||
|
|
||||||
nstool::NroProcess::NroProcess():
|
|
||||||
mFile(),
|
|
||||||
mCliOutputMode(true, false, false, false),
|
|
||||||
mVerify(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NroProcess::process()
|
|
||||||
{
|
|
||||||
importHeader();
|
|
||||||
importCodeSegments();
|
|
||||||
|
|
||||||
if (mCliOutputMode.show_basic_info)
|
|
||||||
displayHeader();
|
|
||||||
|
|
||||||
processRoMeta();
|
|
||||||
|
|
||||||
if (mIsHomebrewNro)
|
|
||||||
mAssetProc.process();
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NroProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
|
|
||||||
{
|
|
||||||
mFile = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NroProcess::setCliOutputMode(CliOutputMode type)
|
|
||||||
{
|
|
||||||
mCliOutputMode = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NroProcess::setVerifyMode(bool verify)
|
|
||||||
{
|
|
||||||
mVerify = verify;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NroProcess::setIs64BitInstruction(bool flag)
|
|
||||||
{
|
|
||||||
mRoMeta.setIs64BitInstruction(flag);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NroProcess::setListApi(bool listApi)
|
|
||||||
{
|
|
||||||
mRoMeta.setListApi(listApi);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NroProcess::setListSymbols(bool listSymbols)
|
|
||||||
{
|
|
||||||
mRoMeta.setListSymbols(listSymbols);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NroProcess::setAssetListFs(bool list)
|
|
||||||
{
|
|
||||||
mAssetProc.setListFs(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NroProcess::setAssetIconExtractPath(const std::string& path)
|
|
||||||
{
|
|
||||||
mAssetProc.setIconExtractPath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NroProcess::setAssetNacpExtractPath(const std::string& path)
|
|
||||||
{
|
|
||||||
mAssetProc.setNacpExtractPath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NroProcess::setAssetRomfsExtractPath(const std::string& path)
|
|
||||||
{
|
|
||||||
mAssetProc.setRomfsExtractPath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
const RoMetadataProcess& nstool::NroProcess::getRoMetadataProcess() const
|
|
||||||
{
|
|
||||||
return mRoMeta;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NroProcess::importHeader()
|
|
||||||
{
|
|
||||||
tc::ByteData scratch;
|
|
||||||
|
|
||||||
if (*mFile == nullptr)
|
|
||||||
{
|
|
||||||
throw tc::Exception(kModuleName, "No file reader set.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((*mFile)->size() < sizeof(nn::hac::sNroHeader))
|
|
||||||
{
|
|
||||||
throw tc::Exception(kModuleName, "Corrupt NRO: file too small");
|
|
||||||
}
|
|
||||||
|
|
||||||
scratch.alloc(sizeof(nn::hac::sNroHeader));
|
|
||||||
(*mFile)->read(scratch.data(), 0, scratch.size());
|
|
||||||
|
|
||||||
mHdr.fromBytes(scratch.data(), scratch.size());
|
|
||||||
|
|
||||||
// setup homebrew extension
|
|
||||||
nn::hac::sNroHeader* raw_hdr = (nn::hac::sNroHeader*)scratch.data();
|
|
||||||
if (((tc::bn::le64<uint64_t>*)raw_hdr->reserved_0)->get() == nn::hac::nro::kNroHomebrewStructMagic && (*mFile)->size() > mHdr.getNroSize())
|
|
||||||
{
|
|
||||||
mIsHomebrewNro = true;
|
|
||||||
mAssetProc.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getNroSize(), (*mFile)->size() - mHdr.getNroSize()));
|
|
||||||
mAssetProc.setCliOutputMode(mCliOutputMode);
|
|
||||||
mAssetProc.setVerifyMode(mVerify);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
mIsHomebrewNro = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NroProcess::importCodeSegments()
|
|
||||||
{
|
|
||||||
mTextBlob.alloc(mHdr.getTextInfo().size);
|
|
||||||
(*mFile)->read(mTextBlob.data(), mHdr.getTextInfo().memory_offset, mTextBlob.size());
|
|
||||||
mRoBlob.alloc(mHdr.getRoInfo().size);
|
|
||||||
(*mFile)->read(mRoBlob.data(), mHdr.getRoInfo().memory_offset, mRoBlob.size());
|
|
||||||
mDataBlob.alloc(mHdr.getDataInfo().size);
|
|
||||||
(*mFile)->read(mDataBlob.data(), mHdr.getDataInfo().memory_offset, mDataBlob.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NroProcess::displayHeader()
|
|
||||||
{
|
|
||||||
std::cout << "[NRO Header]" << std::endl;
|
|
||||||
std::cout << " RoCrt: " << std::endl;
|
|
||||||
std::cout << " EntryPoint: 0x" << std::hex << mHdr.getRoCrtEntryPoint() << std::endl;
|
|
||||||
std::cout << " ModOffset: 0x" << std::hex << mHdr.getRoCrtModOffset() << std::endl;
|
|
||||||
std::cout << " ModuleId: " << fnd::SimpleTextOutput::arrayToString(mHdr.getModuleId().data, nn::hac::nro::kModuleIdSize, false, "") << std::endl;
|
|
||||||
std::cout << " NroSize: 0x" << std::hex << mHdr.getNroSize() << std::endl;
|
|
||||||
std::cout << " Program Sections:" << std::endl;
|
|
||||||
std::cout << " .text:" << std::endl;
|
|
||||||
std::cout << " Offset: 0x" << std::hex << mHdr.getTextInfo().memory_offset << std::endl;
|
|
||||||
std::cout << " Size: 0x" << std::hex << mHdr.getTextInfo().size << std::endl;
|
|
||||||
std::cout << " .ro:" << std::endl;
|
|
||||||
std::cout << " Offset: 0x" << std::hex << mHdr.getRoInfo().memory_offset << std::endl;
|
|
||||||
std::cout << " Size: 0x" << std::hex << mHdr.getRoInfo().size << std::endl;
|
|
||||||
if (mCliOutputMode.show_extended_info)
|
|
||||||
{
|
|
||||||
std::cout << " .api_info:" << std::endl;
|
|
||||||
std::cout << " Offset: 0x" << std::hex << mHdr.getRoEmbeddedInfo().memory_offset << std::endl;
|
|
||||||
std::cout << " Size: 0x" << std::hex << mHdr.getRoEmbeddedInfo().size << std::endl;
|
|
||||||
std::cout << " .dynstr:" << std::endl;
|
|
||||||
std::cout << " Offset: 0x" << std::hex << mHdr.getRoDynStrInfo().memory_offset << std::endl;
|
|
||||||
std::cout << " Size: 0x" << std::hex << mHdr.getRoDynStrInfo().size << std::endl;
|
|
||||||
std::cout << " .dynsym:" << std::endl;
|
|
||||||
std::cout << " Offset: 0x" << std::hex << mHdr.getRoDynSymInfo().memory_offset << std::endl;
|
|
||||||
std::cout << " Size: 0x" << std::hex << mHdr.getRoDynSymInfo().size << std::endl;
|
|
||||||
}
|
|
||||||
std::cout << " .data:" << std::endl;
|
|
||||||
std::cout << " Offset: 0x" << std::hex << mHdr.getDataInfo().memory_offset << std::endl;
|
|
||||||
std::cout << " Size: 0x" << std::hex << mHdr.getDataInfo().size << std::endl;
|
|
||||||
std::cout << " .bss:" << std::endl;
|
|
||||||
std::cout << " Size: 0x" << std::hex << mHdr.getBssSize() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NroProcess::processRoMeta()
|
|
||||||
{
|
|
||||||
if (mRoBlob.size())
|
|
||||||
{
|
|
||||||
// setup ro metadata
|
|
||||||
mRoMeta.setApiInfo(mHdr.getRoEmbeddedInfo().memory_offset, mHdr.getRoEmbeddedInfo().size);
|
|
||||||
mRoMeta.setDynSym(mHdr.getRoDynSymInfo().memory_offset, mHdr.getRoDynSymInfo().size);
|
|
||||||
mRoMeta.setDynStr(mHdr.getRoDynStrInfo().memory_offset, mHdr.getRoDynStrInfo().size);
|
|
||||||
mRoMeta.setRoBinary(mRoBlob);
|
|
||||||
mRoMeta.setCliOutputMode(mCliOutputMode);
|
|
||||||
mRoMeta.process();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,240 +0,0 @@
|
||||||
#include <iostream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <fnd/SimpleTextOutput.h>
|
|
||||||
#include <fnd/OffsetAdjustedIFile.h>
|
|
||||||
#include <fnd/Vec.h>
|
|
||||||
#include <fnd/lz4.h>
|
|
||||||
#include "NsoProcess.h"
|
|
||||||
|
|
||||||
nstool::NsoProcess::NsoProcess():
|
|
||||||
mFile(),
|
|
||||||
mCliOutputMode(true, false, false, false),
|
|
||||||
mVerify(false),
|
|
||||||
mIs64BitInstruction(true),
|
|
||||||
mListApi(false),
|
|
||||||
mListSymbols(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NsoProcess::process()
|
|
||||||
{
|
|
||||||
importHeader();
|
|
||||||
importCodeSegments();
|
|
||||||
if (mCliOutputMode.show_basic_info)
|
|
||||||
displayNsoHeader();
|
|
||||||
|
|
||||||
processRoMeta();
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NsoProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
|
|
||||||
{
|
|
||||||
mFile = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NsoProcess::setCliOutputMode(CliOutputMode type)
|
|
||||||
{
|
|
||||||
mCliOutputMode = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NsoProcess::setVerifyMode(bool verify)
|
|
||||||
{
|
|
||||||
mVerify = verify;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NsoProcess::setIs64BitInstruction(bool flag)
|
|
||||||
{
|
|
||||||
mRoMeta.setIs64BitInstruction(flag);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NsoProcess::setListApi(bool listApi)
|
|
||||||
{
|
|
||||||
mRoMeta.setListApi(listApi);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NsoProcess::setListSymbols(bool listSymbols)
|
|
||||||
{
|
|
||||||
mRoMeta.setListSymbols(listSymbols);
|
|
||||||
}
|
|
||||||
|
|
||||||
const RoMetadataProcess& nstool::NsoProcess::getRoMetadataProcess() const
|
|
||||||
{
|
|
||||||
return mRoMeta;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NsoProcess::importHeader()
|
|
||||||
{
|
|
||||||
tc::ByteData scratch;
|
|
||||||
|
|
||||||
if (*mFile == nullptr)
|
|
||||||
{
|
|
||||||
throw tc::Exception(kModuleName, "No file reader set.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((*mFile)->size() < sizeof(nn::hac::sNsoHeader))
|
|
||||||
{
|
|
||||||
throw tc::Exception(kModuleName, "Corrupt NSO: file too small");
|
|
||||||
}
|
|
||||||
|
|
||||||
scratch.alloc(sizeof(nn::hac::sNsoHeader));
|
|
||||||
(*mFile)->read(scratch.data(), 0, scratch.size());
|
|
||||||
|
|
||||||
mHdr.fromBytes(scratch.data(), scratch.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NsoProcess::importCodeSegments()
|
|
||||||
{
|
|
||||||
tc::ByteData scratch;
|
|
||||||
uint32_t decompressed_len;
|
|
||||||
fnd::sha::sSha256Hash calc_hash;
|
|
||||||
|
|
||||||
// process text segment
|
|
||||||
if (mHdr.getTextSegmentInfo().is_compressed)
|
|
||||||
{
|
|
||||||
scratch.alloc(mHdr.getTextSegmentInfo().file_layout.size);
|
|
||||||
(*mFile)->read(scratch.data(), mHdr.getTextSegmentInfo().file_layout.offset, scratch.size());
|
|
||||||
mTextBlob.alloc(mHdr.getTextSegmentInfo().memory_layout.size);
|
|
||||||
fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mTextBlob.data(), (uint32_t)mTextBlob.size(), decompressed_len);
|
|
||||||
if (decompressed_len != mTextBlob.size())
|
|
||||||
{
|
|
||||||
throw tc::Exception(kModuleName, "NSO text segment failed to decompress");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mTextBlob.alloc(mHdr.getTextSegmentInfo().file_layout.size);
|
|
||||||
(*mFile)->read(mTextBlob.data(), mHdr.getTextSegmentInfo().file_layout.offset, mTextBlob.size());
|
|
||||||
}
|
|
||||||
if (mHdr.getTextSegmentInfo().is_hashed)
|
|
||||||
{
|
|
||||||
fnd::sha::Sha256(mTextBlob.data(), mTextBlob.size(), calc_hash.bytes);
|
|
||||||
if (calc_hash != mHdr.getTextSegmentInfo().hash)
|
|
||||||
{
|
|
||||||
throw tc::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.data(), mHdr.getRoSegmentInfo().file_layout.offset, scratch.size());
|
|
||||||
mRoBlob.alloc(mHdr.getRoSegmentInfo().memory_layout.size);
|
|
||||||
fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mRoBlob.data(), (uint32_t)mRoBlob.size(), decompressed_len);
|
|
||||||
if (decompressed_len != mRoBlob.size())
|
|
||||||
{
|
|
||||||
throw tc::Exception(kModuleName, "NSO ro segment failed to decompress");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mRoBlob.alloc(mHdr.getRoSegmentInfo().file_layout.size);
|
|
||||||
(*mFile)->read(mRoBlob.data(), mHdr.getRoSegmentInfo().file_layout.offset, mRoBlob.size());
|
|
||||||
}
|
|
||||||
if (mHdr.getRoSegmentInfo().is_hashed)
|
|
||||||
{
|
|
||||||
fnd::sha::Sha256(mRoBlob.data(), mRoBlob.size(), calc_hash.bytes);
|
|
||||||
if (calc_hash != mHdr.getRoSegmentInfo().hash)
|
|
||||||
{
|
|
||||||
throw tc::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.data(), mHdr.getDataSegmentInfo().file_layout.offset, scratch.size());
|
|
||||||
mDataBlob.alloc(mHdr.getDataSegmentInfo().memory_layout.size);
|
|
||||||
fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mDataBlob.data(), (uint32_t)mDataBlob.size(), decompressed_len);
|
|
||||||
if (decompressed_len != mDataBlob.size())
|
|
||||||
{
|
|
||||||
throw tc::Exception(kModuleName, "NSO data segment failed to decompress");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mDataBlob.alloc(mHdr.getDataSegmentInfo().file_layout.size);
|
|
||||||
(*mFile)->read(mDataBlob.data(), mHdr.getDataSegmentInfo().file_layout.offset, mDataBlob.size());
|
|
||||||
}
|
|
||||||
if (mHdr.getDataSegmentInfo().is_hashed)
|
|
||||||
{
|
|
||||||
fnd::sha::Sha256(mDataBlob.data(), mDataBlob.size(), calc_hash.bytes);
|
|
||||||
if (calc_hash != mHdr.getDataSegmentInfo().hash)
|
|
||||||
{
|
|
||||||
throw tc::Exception(kModuleName, "NSO data segment failed SHA256 verification");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NsoProcess::displayNsoHeader()
|
|
||||||
{
|
|
||||||
std::cout << "[NSO Header]" << std::endl;
|
|
||||||
std::cout << " ModuleId: " << fnd::SimpleTextOutput::arrayToString(mHdr.getModuleId().data, nn::hac::nso::kModuleIdSize, false, "") << std::endl;
|
|
||||||
if (mCliOutputMode.show_layout)
|
|
||||||
{
|
|
||||||
std::cout << " Program Segments:" << std::endl;
|
|
||||||
std::cout << " .module_name:" << std::endl;
|
|
||||||
std::cout << " FileOffset: 0x" << std::hex << mHdr.getModuleNameInfo().offset << std::endl;
|
|
||||||
std::cout << " FileSize: 0x" << std::hex << mHdr.getModuleNameInfo().size << std::endl;
|
|
||||||
std::cout << " .text:" << std::endl;
|
|
||||||
std::cout << " FileOffset: 0x" << std::hex << mHdr.getTextSegmentInfo().file_layout.offset << std::endl;
|
|
||||||
std::cout << " FileSize: 0x" << std::hex << mHdr.getTextSegmentInfo().file_layout.size << (mHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl;
|
|
||||||
std::cout << " .ro:" << std::endl;
|
|
||||||
std::cout << " FileOffset: 0x" << std::hex << mHdr.getRoSegmentInfo().file_layout.offset << std::endl;
|
|
||||||
std::cout << " FileSize: 0x" << std::hex << mHdr.getRoSegmentInfo().file_layout.size << (mHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl;
|
|
||||||
std::cout << " .data:" << std::endl;
|
|
||||||
std::cout << " FileOffset: 0x" << std::hex << mHdr.getDataSegmentInfo().file_layout.offset << std::endl;
|
|
||||||
std::cout << " FileSize: 0x" << std::hex << mHdr.getDataSegmentInfo().file_layout.size << (mHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl;
|
|
||||||
}
|
|
||||||
std::cout << " Program Sections:" << std::endl;
|
|
||||||
std::cout << " .text:" << std::endl;
|
|
||||||
std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.offset << std::endl;
|
|
||||||
std::cout << " MemorySize: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.size << std::endl;
|
|
||||||
if (mHdr.getTextSegmentInfo().is_hashed && mCliOutputMode.show_extended_info)
|
|
||||||
{
|
|
||||||
std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getTextSegmentInfo().hash.bytes, 32, false, "") << std::endl;
|
|
||||||
}
|
|
||||||
std::cout << " .ro:" << std::endl;
|
|
||||||
std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.offset << std::endl;
|
|
||||||
std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.size << std::endl;
|
|
||||||
if (mHdr.getRoSegmentInfo().is_hashed && mCliOutputMode.show_extended_info)
|
|
||||||
{
|
|
||||||
std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getRoSegmentInfo().hash.bytes, 32, false, "") << std::endl;
|
|
||||||
}
|
|
||||||
if (mCliOutputMode.show_extended_info)
|
|
||||||
{
|
|
||||||
std::cout << " .api_info:" << std::endl;
|
|
||||||
std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoEmbeddedInfo().offset << std::endl;
|
|
||||||
std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoEmbeddedInfo().size << std::endl;
|
|
||||||
std::cout << " .dynstr:" << std::endl;
|
|
||||||
std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoDynStrInfo().offset << std::endl;
|
|
||||||
std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoDynStrInfo().size << std::endl;
|
|
||||||
std::cout << " .dynsym:" << std::endl;
|
|
||||||
std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoDynSymInfo().offset << std::endl;
|
|
||||||
std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoDynSymInfo().size << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << " .data:" << std::endl;
|
|
||||||
std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getDataSegmentInfo().memory_layout.offset << std::endl;
|
|
||||||
std::cout << " MemorySize: 0x" << std::hex << mHdr.getDataSegmentInfo().memory_layout.size << std::endl;
|
|
||||||
if (mHdr.getDataSegmentInfo().is_hashed && mCliOutputMode.show_extended_info)
|
|
||||||
{
|
|
||||||
std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getDataSegmentInfo().hash.bytes, 32, false, "") << std::endl;
|
|
||||||
}
|
|
||||||
std::cout << " .bss:" << std::endl;
|
|
||||||
std::cout << " MemorySize: 0x" << std::hex << mHdr.getBssSize() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nstool::NsoProcess::processRoMeta()
|
|
||||||
{
|
|
||||||
if (mRoBlob.size())
|
|
||||||
{
|
|
||||||
// setup ro metadata
|
|
||||||
mRoMeta.setApiInfo(mHdr.getRoEmbeddedInfo().offset, mHdr.getRoEmbeddedInfo().size);
|
|
||||||
mRoMeta.setDynSym(mHdr.getRoDynSymInfo().offset, mHdr.getRoDynSymInfo().size);
|
|
||||||
mRoMeta.setDynStr(mHdr.getRoDynStrInfo().offset, mHdr.getRoDynStrInfo().size);
|
|
||||||
mRoMeta.setRoBinary(mRoBlob);
|
|
||||||
mRoMeta.setCliOutputMode(mCliOutputMode);
|
|
||||||
mRoMeta.process();
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue