diff --git a/ctrtool/makefile b/ctrtool/makefile index b563934..98003e3 100644 --- a/ctrtool/makefile +++ b/ctrtool/makefile @@ -165,7 +165,7 @@ shared_lib: $(SRC_OBJ) create_binary_dir # Build Program program: $(SRC_OBJ) create_binary_dir @echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_NAME) - @$(CXX) $(ARCHFLAGS) $(SRC_OBJ) $(LIB) -o "$(PROJECT_BIN_PATH)/$(PROJECT_NAME)" + @$(CXX) $(LDFLAGS) $(ARCHFLAGS) $(SRC_OBJ) $(LIB) -o "$(PROJECT_BIN_PATH)/$(PROJECT_NAME)" # Build Test Program test_program: $(TESTSRC_OBJ) $(SRC_OBJ) create_binary_dir diff --git a/ctrtool/src/CciProcess.cpp b/ctrtool/src/CciProcess.cpp index 01c0510..d9acf8e 100644 --- a/ctrtool/src/CciProcess.cpp +++ b/ctrtool/src/CciProcess.cpp @@ -25,7 +25,8 @@ ctrtool::CciProcess::CciProcess() : mValidCryptoType(ValidState::Unchecked), mDecryptedTitleKey(), mNcchProcess(), - mFsReader() + mFsReader(), + mDecryptNcch(false) { memset(&mHeader, 0, sizeof(mHeader)); } @@ -384,6 +385,21 @@ void ctrtool::CciProcess::extractFs() // begin export mFsReader->openFile(*itr, tc::io::FileMode::Open, tc::io::FileAccess::Read, in_stream); + // decrypt ncch + if (mDecryptNcch) + { + if (mVerbose) + { + fmt::print(stderr, "[{} LOG] Decrypting NCCH partition before saving {}...\n", mModuleLabel, out_path.to_string()); + } + + NcchProcess ncchProcess; + ncchProcess.setInputStream(in_stream); + ncchProcess.setVerboseMode(mVerbose); + ncchProcess.setKeyBag(mKeyBag); + ncchProcess.decryptNcchStream(in_stream); + } + local_fs.openFile(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write, out_stream); in_stream->seek(0, tc::io::SeekOrigin::Begin); @@ -586,4 +602,9 @@ std::string ctrtool::CciProcess::getCryptoTypeString(byte_t crypto_type) std::string ctrtool::CciProcess::getTitleVersionString(uint16_t version) { return fmt::format("{major:d}.{minor:d}.{build:d}", fmt::arg("major", (uint32_t)((version >> 10) & 0x3F)), fmt::arg("minor", (uint32_t)((version >> 4) & 0x3F)), fmt::arg("build", (uint32_t)(version & 0xF))); +} + +void ctrtool::CciProcess::setDecryptNcch(bool decryptNcch) +{ + mDecryptNcch = decryptNcch; } \ No newline at end of file diff --git a/ctrtool/src/CciProcess.h b/ctrtool/src/CciProcess.h index e6005ae..8fb1b67 100644 --- a/ctrtool/src/CciProcess.h +++ b/ctrtool/src/CciProcess.h @@ -20,12 +20,13 @@ class CciProcess void setVerifyMode(bool verify); void setExtractPath(const tc::io::Path& extract_path); void setContentIndex(size_t index); - + // ncch settings passed on void setRawMode(bool raw); void setPlainMode(bool plain); void setShowSyscallName(bool show_name); void setNcchRegionProcessOutputMode(NcchProcess::NcchRegion region, bool show_info, bool show_fs, const tc::Optional& bin_extract_path, const tc::Optional& fs_extract_path); + void setDecryptNcch(bool decryptNcch); void process(); private: @@ -38,6 +39,7 @@ class CciProcess bool mVerify; tc::Optional mExtractPath; size_t mContentIndex; + bool mDecryptNcch; int64_t mBlockSize; int64_t mUsedImageSize; diff --git a/ctrtool/src/CiaProcess.cpp b/ctrtool/src/CiaProcess.cpp index 237f3e3..39f216b 100644 --- a/ctrtool/src/CiaProcess.cpp +++ b/ctrtool/src/CiaProcess.cpp @@ -775,6 +775,20 @@ void ctrtool::CiaProcess::extractCia() { fmt::print(stderr, "[{} LOG] Saving content {:04x} to {}...\n", mModuleLabel, itr->second.cindex, out_path.to_string()); } + + if (mDecryptNcch) + { + if (mVerbose) + { + fmt::print(stderr, "[{} LOG] Decrypting NCCH partition before saving {}...\n", mModuleLabel, out_path.to_string()); + } + + NcchProcess ncchProcess; + ncchProcess.setInputStream(in_stream); + ncchProcess.setVerboseMode(mVerbose); + ncchProcess.setKeyBag(mKeyBag); + ncchProcess.decryptNcchStream(in_stream); + } writeStreamToFile(in_stream, out_path, cache); } @@ -955,4 +969,9 @@ std::string ctrtool::CiaProcess::getCertificatePublicKeyTypeString(brd::es::ESCe std::string ctrtool::CiaProcess::getTitleVersionString(uint16_t version) { return fmt::format("{major:d}.{minor:d}.{build:d}", fmt::arg("major", (uint32_t)((version >> 10) & 0x3F)), fmt::arg("minor", (uint32_t)((version >> 4) & 0x3F)), fmt::arg("build", (uint32_t)(version & 0xF))); +} + +void ctrtool::CiaProcess::setDecryptNcch(bool decryptNcch) +{ + mDecryptNcch = decryptNcch; } \ No newline at end of file diff --git a/ctrtool/src/CiaProcess.h b/ctrtool/src/CiaProcess.h index c708936..55e0511 100644 --- a/ctrtool/src/CiaProcess.h +++ b/ctrtool/src/CiaProcess.h @@ -35,6 +35,7 @@ class CiaProcess void setPlainMode(bool plain); void setShowSyscallName(bool show_name); void setNcchRegionProcessOutputMode(NcchProcess::NcchRegion region, bool show_info, bool show_fs, const tc::Optional& bin_extract_path, const tc::Optional& fs_extract_path); + void setDecryptNcch(bool decryptNcch); void process(); private: @@ -53,6 +54,7 @@ class CiaProcess tc::Optional mContentExtractPath; tc::Optional mFooterExtractPath; size_t mContentIndex; + bool mDecryptNcch; // process variables ntd::n3ds::CiaHeader mHeader; diff --git a/ctrtool/src/NcchProcess.cpp b/ctrtool/src/NcchProcess.cpp index 95b9b69..8e5bf58 100644 --- a/ctrtool/src/NcchProcess.cpp +++ b/ctrtool/src/NcchProcess.cpp @@ -1010,3 +1010,43 @@ void ctrtool::NcchProcess::getAesCounter(byte_t* counter, byte_t ncch_region) tmp->begin_offset.wrap(mRegionInfo[ncch_region].offset); } } + +void ctrtool::NcchProcess::decryptNcchStream(std::shared_ptr& outStream) +{ + importHeader(); + determineRegionLayout(); + determineRegionEncryption(); + + // merge mInputStream with decrypted NcchRegion_ExHeader, NcchRegion_ExeFs, NcchRegion_RomFs + std::vector> streamSlices; + size_t inputStreamOffset = 0; + for (size_t i = 0; i < NcchRegionNum; i++) + { + if (i == NcchRegion_PlainRegion || i == NcchRegion_Logo) continue; + + if (mRegionInfo[i].size) + { + streamSlices.push_back(std::make_shared(tc::io::SubStream(mInputStream, inputStreamOffset, mRegionInfo[i].offset - inputStreamOffset))); + + // update ncchflag[7] to 0x4 in NCCH header + // TODO: find a way to compute correct RSA-2048 signature in NCCH header + if (i == NcchRegion_Header) + { + size_t ncchflagOffset = 0x188; + + streamSlices.push_back(std::make_shared(tc::io::SubStream(mRegionInfo[i].ready_stream, 0, ncchflagOffset + 7))); + streamSlices.push_back(std::make_shared(tc::io::MemoryStream(tc::ByteData({ 0x4 })))); + streamSlices.push_back(std::make_shared(tc::io::SubStream(mRegionInfo[i].ready_stream, ncchflagOffset + 8, mRegionInfo[i].size - (ncchflagOffset + 8)))); + } + else + { + streamSlices.push_back(mRegionInfo[i].ready_stream); + } + + inputStreamOffset = mRegionInfo[i].offset + mRegionInfo[i].size; + } + } + streamSlices.push_back(std::make_shared(tc::io::SubStream(mInputStream, inputStreamOffset, mInputStream->length() - inputStreamOffset))); + + outStream = std::make_shared(tc::io::ConcatenatedStream(streamSlices)); +} \ No newline at end of file diff --git a/ctrtool/src/NcchProcess.h b/ctrtool/src/NcchProcess.h index 319379b..ecb1cce 100644 --- a/ctrtool/src/NcchProcess.h +++ b/ctrtool/src/NcchProcess.h @@ -32,6 +32,9 @@ class NcchProcess void setRegionProcessOutputMode(NcchRegion region, bool show_info, bool show_fs, const tc::Optional& bin_extract_path, const tc::Optional& fs_extract_path); void process(); + + // decrypt NCCH stream + void decryptNcchStream(std::shared_ptr& outStream); private: std::string mModuleLabel; diff --git a/ctrtool/src/Settings.cpp b/ctrtool/src/Settings.cpp index 52f5dff..839a78e 100644 --- a/ctrtool/src/Settings.cpp +++ b/ctrtool/src/Settings.cpp @@ -448,6 +448,7 @@ void ctrtool::SettingsInitializer::parse_args(const std::vector& ar // rom options opts.registerOptionHandler(std::shared_ptr(new SingleParamSizetOptionHandler(rom.content_process_index, {"-n", "--ncch", "--cidx"}))); opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(rom.content_extract_path, {"--contents"}))); + opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(rom.decrypt_ncch, { "--decrypt" }))); // ncch options @@ -631,6 +632,7 @@ void ctrtool::SettingsInitializer::usage_text() "CCI options:\n" " -n, --ncch=index Specify NCCH partition index.\n" " --contents=dir Specify Contents directory path.\n" + " --decrypt Decrypt NCCH partition.\n" //" --initdata=file Specify Initial Data file path.\n" "CIA options:\n" " -n, --ncch=index Specify NCCH partition index.\n" @@ -639,6 +641,7 @@ void ctrtool::SettingsInitializer::usage_text() " --tik=file Specify Ticket file path.\n" " --tmd=file Specify TMD file path.\n" " --footer=file Specify Footer file path.\n" + " --decrypt Decrypt NCCH partition.\n" "NCCH options:\n" " --exheader=file Specify Extended Header file path.\n" " --logo=file Specify Logo file path.\n" diff --git a/ctrtool/src/Settings.h b/ctrtool/src/Settings.h index e3301fb..0d552cd 100644 --- a/ctrtool/src/Settings.h +++ b/ctrtool/src/Settings.h @@ -91,6 +91,7 @@ struct Settings { size_t content_process_index; tc::Optional content_extract_path; + bool decrypt_ncch; } rom; // CIA options @@ -142,6 +143,7 @@ struct Settings rom.content_process_index = 0; rom.content_extract_path = tc::Optional(); + rom.decrypt_ncch = false; cia.certs_path = tc::Optional(); cia.tik_path = tc::Optional(); diff --git a/ctrtool/src/main.cpp b/ctrtool/src/main.cpp index 5a9f94a..77b87dc 100644 --- a/ctrtool/src/main.cpp +++ b/ctrtool/src/main.cpp @@ -113,6 +113,7 @@ int umain(const std::vector& args, const std::vector& proc.setRawMode(set.opt.raw); proc.setPlainMode(set.opt.plain); proc.setShowSyscallName(set.exheader.show_syscalls_as_names); + proc.setDecryptNcch(set.rom.decrypt_ncch); proc.setNcchRegionProcessOutputMode(ctrtool::NcchProcess::NcchRegion_Header, set.opt.info, false, tc::Optional(), tc::Optional()); proc.setNcchRegionProcessOutputMode(ctrtool::NcchProcess::NcchRegion_ExHeader, set.opt.info, false, set.ncch.exheader_path, tc::Optional()); proc.setNcchRegionProcessOutputMode(ctrtool::NcchProcess::NcchRegion_PlainRegion, false, false, set.ncch.plainregion_path, tc::Optional()); @@ -143,6 +144,7 @@ int umain(const std::vector& args, const std::vector& proc.setRawMode(set.opt.raw); proc.setPlainMode(set.opt.plain); proc.setShowSyscallName(set.exheader.show_syscalls_as_names); + proc.setDecryptNcch(set.rom.decrypt_ncch); proc.setNcchRegionProcessOutputMode(ctrtool::NcchProcess::NcchRegion_Header, set.opt.info, false, tc::Optional(), tc::Optional()); proc.setNcchRegionProcessOutputMode(ctrtool::NcchProcess::NcchRegion_ExHeader, set.opt.info, false, set.ncch.exheader_path, tc::Optional()); proc.setNcchRegionProcessOutputMode(ctrtool::NcchProcess::NcchRegion_PlainRegion, false, false, set.ncch.plainregion_path, tc::Optional()); diff --git a/makerom/makefile b/makerom/makefile index 546ff97..705a3cb 100644 --- a/makerom/makefile +++ b/makerom/makefile @@ -156,7 +156,7 @@ static_lib: $(SRC_OBJ) create_binary_dir # Build Program program: $(SRC_OBJ) create_binary_dir @echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_NAME) - @$(CXX) $(ARCHFLAGS) $(SRC_OBJ) $(LIB) -o "$(PROJECT_BIN_PATH)/$(PROJECT_NAME)" + @$(CXX) $(LDFLAGS) $(ARCHFLAGS) $(SRC_OBJ) $(LIB) -o "$(PROJECT_BIN_PATH)/$(PROJECT_NAME)" # Build Test Program test_program: $(TESTSRC_OBJ) $(SRC_OBJ) create_binary_dir