mirror of
https://github.com/jakcron/nstool.git
synced 2024-12-22 18:55:29 +00:00
[nstool] Implement decompression and hash validation to NsoProcess.
This commit is contained in:
parent
915bd80f6f
commit
c027537af6
|
@ -1,5 +1,7 @@
|
||||||
|
#include <sstream>
|
||||||
#include <fnd/SimpleTextOutput.h>
|
#include <fnd/SimpleTextOutput.h>
|
||||||
#include <fnd/MemoryBlob.h>
|
#include <fnd/MemoryBlob.h>
|
||||||
|
#include <compress/lz4.h>
|
||||||
#include "OffsetAdjustedIFile.h"
|
#include "OffsetAdjustedIFile.h"
|
||||||
#include "NsoProcess.h"
|
#include "NsoProcess.h"
|
||||||
|
|
||||||
|
@ -26,22 +28,14 @@ NsoProcess::~NsoProcess()
|
||||||
|
|
||||||
void NsoProcess::process()
|
void NsoProcess::process()
|
||||||
{
|
{
|
||||||
fnd::MemoryBlob scratch;
|
|
||||||
|
|
||||||
if (mReader == nullptr)
|
if (mReader == nullptr)
|
||||||
{
|
{
|
||||||
throw fnd::Exception(kModuleName, "No file reader set.");
|
throw fnd::Exception(kModuleName, "No file reader set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mReader->size() < sizeof(nx::sNsoHeader))
|
importHeader();
|
||||||
{
|
importCodeSegments();
|
||||||
throw fnd::Exception(kModuleName, "Corrupt NSO file too small");
|
importApiList();
|
||||||
}
|
|
||||||
|
|
||||||
scratch.alloc(sizeof(nx::sNsoHeader));
|
|
||||||
mReader->read(scratch.getBytes(), 0, scratch.getSize());
|
|
||||||
|
|
||||||
mHdr.importBinary(scratch.getBytes(), scratch.getSize());
|
|
||||||
|
|
||||||
if (mCliOutputType >= OUTPUT_NORMAL)
|
if (mCliOutputType >= OUTPUT_NORMAL)
|
||||||
{
|
{
|
||||||
|
@ -64,6 +58,150 @@ void NsoProcess::setVerifyMode(bool verify)
|
||||||
mVerify = verify;
|
mVerify = verify;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (mReader->size() < sizeof(nx::sNsoHeader))
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "Corrupt NSO file too small");
|
||||||
|
}
|
||||||
|
|
||||||
|
scratch.alloc(sizeof(nx::sNsoHeader));
|
||||||
|
mReader->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);
|
||||||
|
mReader->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);
|
||||||
|
mReader->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);
|
||||||
|
mReader->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);
|
||||||
|
mReader->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);
|
||||||
|
mReader->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);
|
||||||
|
mReader->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, '+'))
|
||||||
|
{
|
||||||
|
mApiList.push_back(api);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mApiList.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NsoProcess::displayHeader()
|
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)
|
#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)
|
||||||
|
@ -73,7 +211,7 @@ void NsoProcess::displayHeader()
|
||||||
_HEXDUMP_L(mHdr.getModuleId().data, nx::nso::kModuleIdLen);
|
_HEXDUMP_L(mHdr.getModuleId().data, nx::nso::kModuleIdLen);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf(" Program Segments:\n");
|
printf(" Program Segments:\n");
|
||||||
printf(" .module_id:\n");
|
printf(" .module_name:\n");
|
||||||
printf(" FileOffset: 0x%" PRIx32 "\n", mHdr.getModuleNameInfo().offset);
|
printf(" FileOffset: 0x%" PRIx32 "\n", mHdr.getModuleNameInfo().offset);
|
||||||
printf(" FileSize: 0x%" PRIx32 "\n", mHdr.getModuleNameInfo().size);
|
printf(" FileSize: 0x%" PRIx32 "\n", mHdr.getModuleNameInfo().size);
|
||||||
printf(" .text:\n");
|
printf(" .text:\n");
|
||||||
|
@ -89,7 +227,7 @@ void NsoProcess::displayHeader()
|
||||||
printf(" .text:\n");
|
printf(" .text:\n");
|
||||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getTextSegmentInfo().memory_layout.offset);
|
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getTextSegmentInfo().memory_layout.offset);
|
||||||
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getTextSegmentInfo().memory_layout.size);
|
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getTextSegmentInfo().memory_layout.size);
|
||||||
if (mHdr.getTextSegmentInfo().is_hashed)
|
if (mHdr.getTextSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE)
|
||||||
{
|
{
|
||||||
printf(" Hash: ");
|
printf(" Hash: ");
|
||||||
_HEXDUMP_L(mHdr.getTextSegmentInfo().hash.bytes, 32);
|
_HEXDUMP_L(mHdr.getTextSegmentInfo().hash.bytes, 32);
|
||||||
|
@ -98,12 +236,14 @@ void NsoProcess::displayHeader()
|
||||||
printf(" .ro:\n");
|
printf(" .ro:\n");
|
||||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.offset);
|
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.offset);
|
||||||
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.size);
|
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.size);
|
||||||
if (mHdr.getRoSegmentInfo().is_hashed)
|
if (mHdr.getRoSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE)
|
||||||
{
|
{
|
||||||
printf(" Hash: ");
|
printf(" Hash: ");
|
||||||
_HEXDUMP_L(mHdr.getRoSegmentInfo().hash.bytes, 32);
|
_HEXDUMP_L(mHdr.getRoSegmentInfo().hash.bytes, 32);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
if (mCliOutputType >= OUTPUT_VERBOSE)
|
||||||
|
{
|
||||||
printf(" .api_info:\n");
|
printf(" .api_info:\n");
|
||||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.offset + mHdr.getRoEmbeddedInfo().offset);
|
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.offset + mHdr.getRoEmbeddedInfo().offset);
|
||||||
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoEmbeddedInfo().size);
|
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoEmbeddedInfo().size);
|
||||||
|
@ -113,10 +253,12 @@ void NsoProcess::displayHeader()
|
||||||
printf(" .dynsym:\n");
|
printf(" .dynsym:\n");
|
||||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.offset + mHdr.getRoDynSymInfo().offset);
|
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.offset + mHdr.getRoDynSymInfo().offset);
|
||||||
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoDynSymInfo().size);
|
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoDynSymInfo().size);
|
||||||
|
}
|
||||||
|
|
||||||
printf(" .data:\n");
|
printf(" .data:\n");
|
||||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getDataSegmentInfo().memory_layout.offset);
|
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getDataSegmentInfo().memory_layout.offset);
|
||||||
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getDataSegmentInfo().memory_layout.size);
|
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getDataSegmentInfo().memory_layout.size);
|
||||||
if (mHdr.getDataSegmentInfo().is_hashed)
|
if (mHdr.getDataSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE)
|
||||||
{
|
{
|
||||||
printf(" Hash: ");
|
printf(" Hash: ");
|
||||||
_HEXDUMP_L(mHdr.getDataSegmentInfo().hash.bytes, 32);
|
_HEXDUMP_L(mHdr.getDataSegmentInfo().hash.bytes, 32);
|
||||||
|
@ -124,6 +266,16 @@ void NsoProcess::displayHeader()
|
||||||
}
|
}
|
||||||
printf(" .bss:\n");
|
printf(" .bss:\n");
|
||||||
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getBssSize());
|
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getBssSize());
|
||||||
|
if (mApiList.size() > 0)
|
||||||
|
{
|
||||||
|
printf(" API List:\n");
|
||||||
|
for (size_t i = 0; i < mApiList.size(); i++)
|
||||||
|
{
|
||||||
|
printf(" %s\n", mApiList[i].c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#undef _HEXDUMP_L
|
#undef _HEXDUMP_L
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <fnd/types.h>
|
#include <fnd/types.h>
|
||||||
#include <fnd/IFile.h>
|
#include <fnd/IFile.h>
|
||||||
|
@ -18,6 +19,12 @@ public:
|
||||||
void setCliOutputMode(CliOutputType type);
|
void setCliOutputMode(CliOutputType type);
|
||||||
void setVerifyMode(bool verify);
|
void setVerifyMode(bool verify);
|
||||||
|
|
||||||
|
// 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:
|
private:
|
||||||
const std::string kModuleName = "NsoProcess";
|
const std::string kModuleName = "NsoProcess";
|
||||||
|
@ -27,6 +34,11 @@ private:
|
||||||
bool mVerify;
|
bool mVerify;
|
||||||
|
|
||||||
nx::NsoHeader mHdr;
|
nx::NsoHeader mHdr;
|
||||||
|
fnd::MemoryBlob mTextBlob, mRoBlob, mDataBlob;
|
||||||
|
std::vector<std::string> mApiList;
|
||||||
|
|
||||||
|
void importHeader();
|
||||||
|
void importCodeSegments();
|
||||||
|
void importApiList();
|
||||||
void displayHeader();
|
void displayHeader();
|
||||||
};
|
};
|
Loading…
Reference in a new issue