From c027537af607f76a7322927988fa17a7fb98eb77 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 1 Jun 2018 22:14:39 +0800 Subject: [PATCH] [nstool] Implement decompression and hash validation to NsoProcess. --- programs/nstool/source/NsoProcess.cpp | 200 ++++++++++++++++++++++---- programs/nstool/source/NsoProcess.h | 12 ++ 2 files changed, 188 insertions(+), 24 deletions(-) diff --git a/programs/nstool/source/NsoProcess.cpp b/programs/nstool/source/NsoProcess.cpp index 0f003ad..4bd9d94 100644 --- a/programs/nstool/source/NsoProcess.cpp +++ b/programs/nstool/source/NsoProcess.cpp @@ -1,5 +1,7 @@ +#include #include #include +#include #include "OffsetAdjustedIFile.h" #include "NsoProcess.h" @@ -26,22 +28,14 @@ NsoProcess::~NsoProcess() void NsoProcess::process() { - fnd::MemoryBlob scratch; - if (mReader == nullptr) { throw fnd::Exception(kModuleName, "No file reader set."); } - 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()); + importHeader(); + importCodeSegments(); + importApiList(); if (mCliOutputType >= OUTPUT_NORMAL) { @@ -64,6 +58,150 @@ void NsoProcess::setVerifyMode(bool 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& 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() { #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); printf("\n"); printf(" Program Segments:\n"); - printf(" .module_id:\n"); + printf(" .module_name:\n"); printf(" FileOffset: 0x%" PRIx32 "\n", mHdr.getModuleNameInfo().offset); printf(" FileSize: 0x%" PRIx32 "\n", mHdr.getModuleNameInfo().size); printf(" .text:\n"); @@ -89,7 +227,7 @@ void NsoProcess::displayHeader() 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) + if (mHdr.getTextSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE) { printf(" Hash: "); _HEXDUMP_L(mHdr.getTextSegmentInfo().hash.bytes, 32); @@ -98,25 +236,29 @@ void NsoProcess::displayHeader() 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) + if (mHdr.getRoSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE) { printf(" Hash: "); _HEXDUMP_L(mHdr.getRoSegmentInfo().hash.bytes, 32); printf("\n"); } - printf(" .api_info:\n"); - printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.offset + mHdr.getRoEmbeddedInfo().offset); - printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoEmbeddedInfo().size); - printf(" .dynstr:\n"); - printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.offset + mHdr.getRoDynStrInfo().offset); - printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoDynStrInfo().size); - printf(" .dynsym:\n"); - printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.offset + mHdr.getRoDynSymInfo().offset); - printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoDynSymInfo().size); + if (mCliOutputType >= OUTPUT_VERBOSE) + { + printf(" .api_info:\n"); + printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.offset + mHdr.getRoEmbeddedInfo().offset); + printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoEmbeddedInfo().size); + printf(" .dynstr:\n"); + printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.offset + mHdr.getRoDynStrInfo().offset); + printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoDynStrInfo().size); + printf(" .dynsym:\n"); + printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.offset + 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) + if (mHdr.getDataSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE) { printf(" Hash: "); _HEXDUMP_L(mHdr.getDataSegmentInfo().hash.bytes, 32); @@ -124,6 +266,16 @@ void NsoProcess::displayHeader() } printf(" .bss:\n"); 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 } \ No newline at end of file diff --git a/programs/nstool/source/NsoProcess.h b/programs/nstool/source/NsoProcess.h index 5973098..1f94d8a 100644 --- a/programs/nstool/source/NsoProcess.h +++ b/programs/nstool/source/NsoProcess.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -18,6 +19,12 @@ public: void setCliOutputMode(CliOutputType type); 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& getApiList() const; private: const std::string kModuleName = "NsoProcess"; @@ -27,6 +34,11 @@ private: bool mVerify; nx::NsoHeader mHdr; + fnd::MemoryBlob mTextBlob, mRoBlob, mDataBlob; + std::vector mApiList; + void importHeader(); + void importCodeSegments(); + void importApiList(); void displayHeader(); }; \ No newline at end of file