mirror of
https://github.com/Dark98/threeSD.git
synced 2026-07-06 00:38:55 +00:00
ncch: Fix some issues with DecryptToFile
Now have byte-for-byte accuracy against Godmode9 Now uses a file instead of a path Preliminary work to support non-executable files
This commit is contained in:
@@ -479,7 +479,14 @@ bool SDMCImporter::DumpCXI(const ContentSpecifier& specifier, const std::string&
|
|||||||
fmt::format("{}{:08x}.app", content_path, tmd.GetBootContentID());
|
fmt::format("{}{:08x}.app", content_path, tmd.GetBootContentID());
|
||||||
dump_cxi_ncch = std::make_unique<NCCHContainer>(
|
dump_cxi_ncch = std::make_unique<NCCHContainer>(
|
||||||
std::make_shared<SDMCFile>(config.sdmc_path, boot_content_path, "rb"));
|
std::make_shared<SDMCFile>(config.sdmc_path, boot_content_path, "rb"));
|
||||||
return dump_cxi_ncch->DecryptToFile(destination, callback) == ResultStatus::Success;
|
|
||||||
|
if (!FileUtil::CreateFullPath(destination)) {
|
||||||
|
LOG_ERROR(Core, "Failed to create path {}", destination);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dump_cxi_ncch->DecryptToFile(std::make_shared<FileUtil::IOFile>(destination, "wb"),
|
||||||
|
callback) == ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDMCImporter::AbortDumpCXI() {
|
void SDMCImporter::AbortDumpCXI() {
|
||||||
|
|||||||
@@ -427,22 +427,17 @@ ResultStatus NCCHContainer::ReadSeedCrypto(bool& used) {
|
|||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultStatus NCCHContainer::DecryptToFile(const std::string& destination,
|
ResultStatus NCCHContainer::DecryptToFile(std::shared_ptr<FileUtil::IOFile> dest_file,
|
||||||
const ProgressCallback& callback) {
|
const ProgressCallback& callback) {
|
||||||
ResultStatus result = Load();
|
ResultStatus result = Load();
|
||||||
if (result != ResultStatus::Success)
|
if (result != ResultStatus::Success)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
if (!has_header || !has_exheader)
|
if (!has_header)
|
||||||
return ResultStatus::ErrorNotUsed;
|
return ResultStatus::ErrorNotUsed;
|
||||||
|
|
||||||
if (!FileUtil::CreateFullPath(destination)) {
|
|
||||||
LOG_ERROR(Core, "Could not create path {}", destination);
|
|
||||||
return ResultStatus::Error;
|
|
||||||
}
|
|
||||||
auto dest_file = std::make_shared<FileUtil::IOFile>(destination, "wb");
|
|
||||||
if (!*dest_file) {
|
if (!*dest_file) {
|
||||||
LOG_ERROR(Core, "Could not open file {}", destination);
|
LOG_ERROR(Core, "File is not open");
|
||||||
return ResultStatus::Error;
|
return ResultStatus::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,29 +452,39 @@ ResultStatus NCCHContainer::DecryptToFile(const std::string& destination,
|
|||||||
return ret ? ResultStatus::Success : ResultStatus::Error;
|
return ret ? ResultStatus::Success : ResultStatus::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto total_size = file->GetSize() - sizeof(NCCH_Header);
|
||||||
|
// These are written directly instead of using the decryptor
|
||||||
|
if (has_exheader) {
|
||||||
|
total_size -= sizeof(ExHeader_Header);
|
||||||
|
}
|
||||||
|
if (has_exefs) {
|
||||||
|
total_size -= sizeof(ExeFs_Header);
|
||||||
|
}
|
||||||
|
decryptor.Reset(total_size);
|
||||||
|
|
||||||
|
std::size_t written{};
|
||||||
|
|
||||||
// Write NCCH header
|
// Write NCCH header
|
||||||
NCCH_Header modified_header = ncch_header;
|
NCCH_Header modified_header = ncch_header;
|
||||||
modified_header.no_crypto.Assign(1);
|
modified_header.raw_crypto_flags = 0x4; // No crypto
|
||||||
modified_header.secondary_key_slot = 0;
|
modified_header.secondary_key_slot = 0;
|
||||||
if (dest_file->WriteBytes(&modified_header, sizeof(modified_header)) !=
|
if (dest_file->WriteBytes(&modified_header, sizeof(modified_header)) !=
|
||||||
sizeof(modified_header)) {
|
sizeof(modified_header)) {
|
||||||
LOG_ERROR(Core, "Could not write NCCH header to file {}", destination);
|
LOG_ERROR(Core, "Could not write NCCH header to file");
|
||||||
return ResultStatus::Error;
|
return ResultStatus::Error;
|
||||||
}
|
}
|
||||||
|
written += sizeof(NCCH_Header);
|
||||||
|
|
||||||
// Write Exheader
|
// Write Exheader
|
||||||
if (dest_file->WriteBytes(&exheader_header, sizeof(exheader_header)) !=
|
if (has_exheader) {
|
||||||
sizeof(exheader_header)) {
|
if (dest_file->WriteBytes(&exheader_header, sizeof(exheader_header)) !=
|
||||||
LOG_ERROR(Core, "Could not write Exheader to file {}", destination);
|
sizeof(exheader_header)) {
|
||||||
return ResultStatus::Error;
|
LOG_ERROR(Core, "Could not write Exheader to file");
|
||||||
|
return ResultStatus::Error;
|
||||||
|
}
|
||||||
|
written += sizeof(ExHeader_Header);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto total_size =
|
|
||||||
file->GetSize() - sizeof(NCCH_Header) - sizeof(ExHeader_Header) - sizeof(ExeFs_Header);
|
|
||||||
decryptor.Reset(total_size);
|
|
||||||
|
|
||||||
std::size_t written = sizeof(NCCH_Header) + sizeof(ExHeader_Header);
|
|
||||||
|
|
||||||
const auto Write = [&](std::string_view name, std::size_t offset, std::size_t size,
|
const auto Write = [&](std::string_view name, std::size_t offset, std::size_t size,
|
||||||
bool decrypt = false, const Key::AESKey& key = {},
|
bool decrypt = false, const Key::AESKey& key = {},
|
||||||
const Key::AESKey& ctr = {}, std::size_t aes_seek_pos = 0) {
|
const Key::AESKey& ctr = {}, std::size_t aes_seek_pos = 0) {
|
||||||
@@ -490,59 +495,75 @@ ResultStatus NCCHContainer::DecryptToFile(const std::string& destination,
|
|||||||
if (aborted.exchange(false)) {
|
if (aborted.exchange(false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
file->Seek(written, SEEK_SET);
|
|
||||||
ASSERT_MSG(written <= offset, "Offsets are not in increasing order");
|
ASSERT_MSG(written <= offset, "Offsets are not in increasing order");
|
||||||
if (!decryptor.DecryptAndWriteFile(file, offset - written, dest_file, callback)) {
|
|
||||||
LOG_ERROR(Core, "Could not write data before {} to {}", name, destination);
|
// Zero out the gap
|
||||||
return false;
|
const std::array<u8, 1024> zeroes{};
|
||||||
|
std::size_t zeroes_left = offset - written;
|
||||||
|
while (zeroes_left > 0) {
|
||||||
|
const auto to_write = std::min(zeroes.size(), zeroes_left);
|
||||||
|
if (dest_file->WriteBytes(zeroes.data(), to_write) != to_write) {
|
||||||
|
LOG_ERROR(Core, "Could not write zeroes before {}", name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
zeroes_left -= to_write;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file->Seek(offset, SEEK_SET);
|
||||||
|
|
||||||
if (aborted.exchange(false)) {
|
if (aborted.exchange(false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!decryptor.DecryptAndWriteFile(file, size, dest_file, callback, decrypt, key, ctr,
|
if (!decryptor.DecryptAndWriteFile(file, size, dest_file, callback, decrypt, key, ctr,
|
||||||
aes_seek_pos)) {
|
aes_seek_pos)) {
|
||||||
LOG_ERROR(Core, "Could not write {} to {}", name, destination);
|
LOG_ERROR(Core, "Could not write {}", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
written = offset + size;
|
written = offset + size;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!Write("logo", ncch_header.logo_region_offset * 0x200,
|
||||||
|
ncch_header.logo_region_size * 0x200)) {
|
||||||
|
return ResultStatus::Error;
|
||||||
|
}
|
||||||
|
|
||||||
if (!Write("plain region", ncch_header.plain_region_offset * 0x200,
|
if (!Write("plain region", ncch_header.plain_region_offset * 0x200,
|
||||||
ncch_header.plain_region_size * 0x200)) {
|
ncch_header.plain_region_size * 0x200)) {
|
||||||
return ResultStatus::Error;
|
return ResultStatus::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write ExeFS header
|
// Write ExeFS header
|
||||||
if (dest_file->WriteBytes(&exefs_header, sizeof(exefs_header)) != sizeof(exefs_header)) {
|
if (has_exefs) {
|
||||||
LOG_ERROR(Core, "Could not write ExeFS header to file {}", destination);
|
if (dest_file->WriteBytes(&exefs_header, sizeof(exefs_header)) != sizeof(exefs_header)) {
|
||||||
return ResultStatus::Error;
|
LOG_ERROR(Core, "Could not write ExeFS header to file");
|
||||||
}
|
|
||||||
written += sizeof(ExeFs_Header);
|
|
||||||
|
|
||||||
for (unsigned section_number = 0; section_number < kMaxSections; section_number++) {
|
|
||||||
const auto& section = exefs_header.section[section_number];
|
|
||||||
if (section.offset == 0 && section.size == 0) { // not used
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Key::AESKey key;
|
|
||||||
if (strcmp(section.name, "icon") == 0 || strcmp(section.name, "banner") == 0) {
|
|
||||||
key = primary_key;
|
|
||||||
} else {
|
|
||||||
key = secondary_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plus 1 for the ExeFS header
|
|
||||||
if (!Write(section.name, section.offset + (ncch_header.exefs_offset + 1) * 0x200,
|
|
||||||
section.size, true, key, exefs_ctr, section.offset + sizeof(exefs_header))) {
|
|
||||||
return ResultStatus::Error;
|
return ResultStatus::Error;
|
||||||
}
|
}
|
||||||
|
written += sizeof(ExeFs_Header);
|
||||||
|
|
||||||
|
for (unsigned section_number = 0; section_number < kMaxSections; section_number++) {
|
||||||
|
const auto& section = exefs_header.section[section_number];
|
||||||
|
if (section.offset == 0 && section.size == 0) { // not used
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Key::AESKey key;
|
||||||
|
if (strcmp(section.name, "icon") == 0 || strcmp(section.name, "banner") == 0) {
|
||||||
|
key = primary_key;
|
||||||
|
} else {
|
||||||
|
key = secondary_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plus 1 for the ExeFS header
|
||||||
|
if (!Write(section.name, section.offset + (ncch_header.exefs_offset + 1) * 0x200,
|
||||||
|
section.size, true, key, exefs_ctr, section.offset + sizeof(exefs_header))) {
|
||||||
|
return ResultStatus::Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Write("romfs", ncch_header.romfs_offset * 0x200, ncch_header.romfs_size * 0x200, true,
|
if (has_romfs && !Write("romfs", ncch_header.romfs_offset * 0x200,
|
||||||
secondary_key, romfs_ctr)) {
|
ncch_header.romfs_size * 0x200, true, secondary_key, romfs_ctr)) {
|
||||||
return ResultStatus::Error;
|
return ResultStatus::Error;
|
||||||
}
|
}
|
||||||
if (written < file->GetSize()) {
|
if (written < file->GetSize()) {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ struct NCCH_Header {
|
|||||||
BitField<1, 1, u8> no_romfs;
|
BitField<1, 1, u8> no_romfs;
|
||||||
BitField<2, 1, u8> no_crypto;
|
BitField<2, 1, u8> no_crypto;
|
||||||
BitField<5, 1, u8> seed_crypto;
|
BitField<5, 1, u8> seed_crypto;
|
||||||
|
u8 raw_crypto_flags;
|
||||||
};
|
};
|
||||||
u32_le plain_region_offset;
|
u32_le plain_region_offset;
|
||||||
u32_le plain_region_size;
|
u32_le plain_region_size;
|
||||||
@@ -271,7 +272,7 @@ public:
|
|||||||
* Decrypts this NCCH and write to the destination file.
|
* Decrypts this NCCH and write to the destination file.
|
||||||
* @return ResultStatus result of function.
|
* @return ResultStatus result of function.
|
||||||
*/
|
*/
|
||||||
ResultStatus DecryptToFile(const std::string& destination,
|
ResultStatus DecryptToFile(std::shared_ptr<FileUtil::IOFile> dest_file,
|
||||||
const ProgressCallback& callback = [](std::size_t, std::size_t) {});
|
const ProgressCallback& callback = [](std::size_t, std::size_t) {});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user