Files
Project_CTR/ctrtool/src/ExHeaderProcess.cpp
T

1053 lines
37 KiB
C++

#include "ExHeaderProcess.h"
#include <tc/io.h>
#include <tc/cli.h>
#include <tc/crypto.h>
#include <tc/ArgumentNullException.h>
ctrtool::ExHeaderProcess::ExHeaderProcess() :
mModuleLabel("ctrtool::ExHeaderProcess"),
mInputStream(),
mKeyBag(),
mShowInfo(false),
mVerbose(false),
mVerify(false),
mShowSyscallNames(false),
mValidSignature(ValidState::Unchecked),
mValidLocalCaps()
{
memset((byte_t*)&mHeader, 0, sizeof(ntd::n3ds::ExtendedHeader));
memset((byte_t*)&mDesc, 0, sizeof(ntd::n3ds::AccessDescriptor));
}
void ctrtool::ExHeaderProcess::setInputStream(const std::shared_ptr<tc::io::IStream>& input_stream)
{
mInputStream = input_stream;
}
void ctrtool::ExHeaderProcess::setKeyBag(const ctrtool::KeyBag& key_bag)
{
mKeyBag = key_bag;
}
void ctrtool::ExHeaderProcess::setCliOutputMode(bool show_info)
{
mShowInfo = show_info;
}
void ctrtool::ExHeaderProcess::setVerboseMode(bool verbose)
{
mVerbose = verbose;
}
void ctrtool::ExHeaderProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
void ctrtool::ExHeaderProcess::setShowSyscallName(bool show_name)
{
mShowSyscallNames = show_name;
}
void ctrtool::ExHeaderProcess::process()
{
// begin processing
importExHeader();
if (mVerify)
verifyExHeader();
if (mShowInfo)
printExHeader();
}
void ctrtool::ExHeaderProcess::importExHeader()
{
// validate input stream
if (mInputStream == nullptr)
{
throw tc::ArgumentNullException(mModuleLabel, "Input stream was null.");
}
if (mInputStream->canRead() == false || mInputStream->canSeek() == false)
{
throw tc::InvalidOperationException(mModuleLabel, "Input stream requires read/seek permissions.");
}
// import header
if (mInputStream->length() < (sizeof(ntd::n3ds::ExtendedHeader) + sizeof(ntd::n3ds::AccessDescriptor)))
{
throw tc::InvalidOperationException(mModuleLabel, "Input stream too small.");
}
mInputStream->seek(0, tc::io::SeekOrigin::Begin);
mInputStream->read((byte_t*)&mHeader, sizeof(ntd::n3ds::ExtendedHeader));
mInputStream->read((byte_t*)&mDesc, sizeof(ntd::n3ds::AccessDescriptor));
}
void ctrtool::ExHeaderProcess::verifyExHeader()
{
std::array<byte_t, tc::crypto::Sha256Generator::kHashSize> desc_hash;
tc::crypto::GenerateSha256Hash(desc_hash.data(), (byte_t*)&mDesc.ncch_rsa_modulus, sizeof(mDesc) - sizeof(mDesc.signature));
if (mKeyBag.rsa_key.find(mKeyBag.RSAKEY_ACCESSDESC) != mKeyBag.rsa_key.end())
{
tc::crypto::RsaKey pubkey = mKeyBag.rsa_key[mKeyBag.RSAKEY_ACCESSDESC];
mValidSignature = tc::crypto::VerifyRsa2048Pkcs1Sha256(mDesc.signature.data(), desc_hash.data(), pubkey) ? ValidState::Good : ValidState::Fail;
}
else
{
fmt::print(stderr, "[{} ERROR] Could not load AccessDescriptor RSA2048 public key.\n", mModuleLabel);
mValidSignature = ValidState::Fail;
}
if (mValidSignature != ValidState::Good)
{
fmt::print(stderr, "[{} ERROR] Signature for AccessDescriptor was invalid.\n", mModuleLabel);
}
mValidLocalCaps.system_save_id[0] = ValidState::Good;
mValidLocalCaps.system_save_id[1] = ValidState::Good;
mValidLocalCaps.fs_access = ValidState::Good;
mValidLocalCaps.core_version = ValidState::Good;
mValidLocalCaps.program_id = ValidState::Good;
mValidLocalCaps.priority = ValidState::Good;
mValidLocalCaps.affinity_mask = ValidState::Good;
mValidLocalCaps.ideal_processor = ValidState::Good;
mValidLocalCaps.old3ds_system_mode = ValidState::Good;
mValidLocalCaps.new3ds_system_mode = ValidState::Good;
mValidLocalCaps.enable_l2_cache = ValidState::Good;
mValidLocalCaps.new3ds_cpu_speed = ValidState::Good;
mValidLocalCaps.service_control = ValidState::Good;
byte_t* exhdr_program_id = (byte_t*)&mHeader.access_control_info.program_id;
byte_t* desc_program_id = (byte_t*)&mDesc.access_control_info.program_id;
for (size_t i = 0; i < sizeof(uint64_t); i++)
{
if (exhdr_program_id[i] == desc_program_id[i] || desc_program_id[i] == 0xFF)
continue;
mValidLocalCaps.program_id = ValidState::Fail;
break;
}
/*
// this does not appear to be correct given working examples: SystemUpdater exhdr: 0x1, desc: 0x2
auto exhdr_core_version = mHeader.access_control_info.core_version.unwrap();
auto desc_core_version = mDesc.access_control_info.core_version.unwrap();
if (exhdr_core_version != desc_core_version)
mValidLocalCaps.core_version = ValidState::Fail;
*/
auto exhdr_thread_priority = mHeader.access_control_info.flags.unwrap().thread_priority;
auto desc_thread_priority = mDesc.access_control_info.flags.unwrap().thread_priority;
if (exhdr_thread_priority < desc_thread_priority)
mValidLocalCaps.priority = ValidState::Fail;
auto exhdr_ideal_processor = mHeader.access_control_info.flags.unwrap().ideal_processor;
auto desc_ideal_processor = mDesc.access_control_info.flags.unwrap().ideal_processor;
if((1<<exhdr_ideal_processor & desc_ideal_processor) == 0)
mValidLocalCaps.ideal_processor = ValidState::Fail;
auto exhdr_affinity_mask = mHeader.access_control_info.flags.unwrap().affinity_mask;
auto desc_affinity_mask = mDesc.access_control_info.flags.unwrap().affinity_mask;
if (exhdr_affinity_mask & ~desc_affinity_mask)
mValidLocalCaps.affinity_mask = ValidState::Fail;
auto exhdr_system_mode = mHeader.access_control_info.flags.unwrap().system_mode;
auto desc_system_mode = mDesc.access_control_info.flags.unwrap().system_mode;
if (exhdr_system_mode > desc_system_mode)
mValidLocalCaps.old3ds_system_mode = ValidState::Fail;
auto exhdr_system_mode_ext = mHeader.access_control_info.flags.unwrap().system_mode_ext;
auto desc_system_mode_ext = mDesc.access_control_info.flags.unwrap().system_mode_ext;
if (exhdr_system_mode_ext > desc_system_mode_ext)
mValidLocalCaps.new3ds_system_mode = ValidState::Fail;
auto exhdr_enable_l2_cache = mHeader.access_control_info.flags.unwrap().enable_l2_cache;
auto desc_enable_l2_cache = mDesc.access_control_info.flags.unwrap().enable_l2_cache;
if (exhdr_enable_l2_cache != desc_enable_l2_cache)
mValidLocalCaps.enable_l2_cache = ValidState::Fail;
auto exhdr_cpu_speed = mHeader.access_control_info.flags.unwrap().cpu_speed;
auto desc_cpu_speed = mDesc.access_control_info.flags.unwrap().cpu_speed;
if (exhdr_cpu_speed != desc_cpu_speed)
mValidLocalCaps.new3ds_cpu_speed = ValidState::Fail;
// Storage Info Verify
auto exhdr_system_savedata_id = mHeader.access_control_info.system_savedata_id;
auto desc_system_savedata_id = mDesc.access_control_info.system_savedata_id;
if(exhdr_system_savedata_id[0].unwrap() & ~desc_system_savedata_id[0].unwrap())
mValidLocalCaps.system_save_id[0] = ValidState::Fail;
if(exhdr_system_savedata_id[1].unwrap() & ~desc_system_savedata_id[1].unwrap())
mValidLocalCaps.system_save_id[1] = ValidState::Fail;
auto exhdr_fs_access = mHeader.access_control_info.fs_access;
auto desc_fs_access = mDesc.access_control_info.fs_access;
for (size_t fs_bit = 0; fs_bit < exhdr_fs_access.bit_size(); fs_bit++)
{
if (exhdr_fs_access.test(fs_bit) == true && desc_fs_access.test(fs_bit) == false)
{
mValidLocalCaps.fs_access = ValidState::Fail;
if (mVerbose)
{
fmt::print(stderr, "[{} ERROR] FsAccess Bit {:d} was not permitted\n", mModuleLabel, fs_bit);
}
}
}
// Service Access Control
auto exhdr_service_access_control = mHeader.access_control_info.service_access_control;
auto desc_service_access_control = mDesc.access_control_info.service_access_control;
bool found_service;
for (size_t i = 0, j; i < exhdr_service_access_control.size(); i++) {
// skip if empty string
if (exhdr_service_access_control[i].decode().empty())
break;
found_service = false;
// locate entry in desc
for (j = 0; j < desc_service_access_control.size(); j++) {
if (exhdr_service_access_control[i].decode() == desc_service_access_control[j].decode())
found_service = true;
}
if (found_service == false)
{
mValidLocalCaps.service_control = Fail;
if (mVerbose)
{
fmt::print(stderr, "[{} ERROR] Service \"{}\" was not permitted\n", mModuleLabel, exhdr_service_access_control[i].decode());
}
}
}
if (mValidLocalCaps.system_save_id[0] != ValidState::Good)
{
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "SystemSaveId1");
}
if (mValidLocalCaps.system_save_id[1] != ValidState::Good)
{
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "SystemSaveId2");
}
if (mValidLocalCaps.fs_access != ValidState::Good)
{
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "FsAccess");
}
/*
if (mValidLocalCaps.core_version != ValidState::Good)
{
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "CoreVersion");
}
*/
if (mValidLocalCaps.program_id != ValidState::Good)
{
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "ProgramId");
}
if (mValidLocalCaps.priority != ValidState::Good)
{
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "ThreadPriority");
}
if (mValidLocalCaps.affinity_mask != ValidState::Good)
{
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "AffinityMask");
}
if (mValidLocalCaps.ideal_processor != ValidState::Good)
{
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "IdealProcessor");
}
if (mValidLocalCaps.old3ds_system_mode != ValidState::Good)
{
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "SystemMode (Old3DS)");
}
if (mValidLocalCaps.new3ds_system_mode != ValidState::Good)
{
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "SystemMode (New3DS)");
}
if (mValidLocalCaps.enable_l2_cache != ValidState::Good)
{
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "EnableL2Cache");
}
if (mValidLocalCaps.new3ds_cpu_speed != ValidState::Good)
{
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "CpuSpeed");
}
if (mValidLocalCaps.service_control != ValidState::Good)
{
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "ServiceAccess");
}
}
void ctrtool::ExHeaderProcess::printExHeader()
{
fmt::print("\n");
fmt::print("Extended header:\n");
fmt::print("Signature: {:6} {}", getValidString(mValidSignature), tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(mDesc.signature.data(), mDesc.signature.size(), true, "", 0x20, 24, false));
fmt::print("NCCH Hdr RSA Modulus: {}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(mDesc.ncch_rsa_modulus.data(), mDesc.ncch_rsa_modulus.size(), true, "", 0x20, 24, false));
printSystemControlInfo(mHeader.system_control_info);
printARM11SystemLocalCapabilities(mHeader.access_control_info, mValidLocalCaps);
printARM11KernelCapabilities(mHeader.access_control_info);
printARM9AccessControlInfo(mHeader.access_control_info);
}
void ctrtool::ExHeaderProcess::printSystemControlInfo(const ntd::n3ds::SystemControlInfo& info)
{
//fmt::print("[SystemControlInfo]\n");
// basic info
fmt::print("Name: {}\n", info.name.decode());
fmt::print("Flags: {:02X}", (uint32_t)info.flags.raw);
if (info.flags.bitarray.test(info.Flags_CompressExefsPartition0))
fmt::print(" [compressed]");
if (info.flags.bitarray.test(info.Flags_SdmcApplication))
fmt::print(" [sd app]");
fmt::print("\n");
fmt::print("Remaster version: {:04x}\n", (uint32_t)info.remaster_version.unwrap());
// code set info
fmt::print("Code text address: 0x{:08X}\n", info.text.address.unwrap());
fmt::print("Code text size: 0x{:08X}\n", info.text.code_size.unwrap());
fmt::print("Code text max pages: 0x{:08X} (0x{:08X})\n", info.text.num_max_pages.unwrap(), info.text.num_max_pages.unwrap() * 0x1000);
fmt::print("Code ro address: 0x{:08X}\n", info.rodata.address.unwrap());
fmt::print("Code ro size: 0x{:08X}\n", info.rodata.code_size.unwrap());
fmt::print("Code ro max pages: 0x{:08X} (0x{:08X})\n", info.rodata.num_max_pages.unwrap(), info.rodata.num_max_pages.unwrap() * 0x1000);
fmt::print("Code data address: 0x{:08X}\n", info.data.address.unwrap());
fmt::print("Code data size: 0x{:08X}\n", info.data.code_size.unwrap());
fmt::print("Code data max pages: 0x{:08X} (0x{:08X})\n", info.data.num_max_pages.unwrap(), info.data.num_max_pages.unwrap() * 0x1000);
fmt::print("Code bss size: 0x{:08X}\n", info.bss_size.unwrap());
fmt::print("Code stack size: 0x{:08X}\n", info.stack_size.unwrap());
// Dependency
for (size_t i = 0; i < info.dependency_list.size(); i++)
{
if (info.dependency_list[i].unwrap() != 0)
{
fmt::print("Dependency: {:016x}\n", info.dependency_list[i].unwrap());
}
}
// savedata size
fmt::print("Savedata size: ");
uint64_t savedata_size = info.savedata_size.unwrap();
if (savedata_size < (1024)) // < KB
{
fmt::print("0x{:x}", savedata_size);
}
else if (savedata_size < (1024 * 1024)) // < MB
{
fmt::print("{:d}K", (savedata_size >> 10));
}
else
{
fmt::print("{:d}M", (savedata_size >> 20));
}
fmt::print("\n");
fmt::print("Jump id: {:016x}\n", info.jump_id.unwrap());
}
void ctrtool::ExHeaderProcess::printARM11SystemLocalCapabilities(const ntd::n3ds::AccessControlInfo& info, const ValidARM11SystemLocalCapabilities& valid)
{
//fmt::print("[ARM11SystemLocalCapabilities]\n");
fmt::print("Program id: {:016x} {}\n", info.program_id.unwrap(), getValidString(valid.program_id));
fmt::print("Core version: 0x{:08x}\n", info.core_version.unwrap());
fmt::print("System mode: {} (AppMemory: {}) {}\n", getSystemModeString(info.flags.unwrap().system_mode), getSystemModeAppMemorySizeString(info.flags.unwrap().system_mode), getValidString(valid.old3ds_system_mode));
fmt::print("System mode (New3DS): {} (AppMemory: {}) {}\n", getSystemModeExtString(info.flags.unwrap().system_mode_ext, info.flags.unwrap().system_mode), getSystemModeExtAppMemorySizeString(info.flags.unwrap().system_mode_ext, info.flags.unwrap().system_mode), getValidString(valid.new3ds_system_mode));
fmt::print("CPU Speed (New3DS): {} {}\n", (info.flags.unwrap().cpu_speed ? "804MHz" : "268MHz"), getValidString(valid.new3ds_cpu_speed));
fmt::print("Enable L2 Cache: {} {}\n", (info.flags.unwrap().enable_l2_cache ? "YES" : "NO"), getValidString(valid.enable_l2_cache));
fmt::print("Ideal processor: {:d} {}\n", (uint32_t)info.flags.unwrap().ideal_processor, getValidString(valid.ideal_processor));
fmt::print("Affinity mask: {:d} {}\n", (uint32_t)info.flags.unwrap().affinity_mask, getValidString(valid.affinity_mask));
fmt::print("Main thread priority: {:d} {}\n", (uint32_t)info.flags.unwrap().thread_priority, getValidString(valid.priority));
fmt::print("MaxCpu: {:d}\n", info.resource_limit_descriptor[info.ResourceLimitDescriptorIndex_MaxCpu].unwrap());
std::vector<uint32_t> accessible_save_ids;
uint64_t ext_savedata_id = 0;
std::array<uint32_t, 3> other_user_save_ids = {0, 0, 0};
bool use_other_variation_savedata = false;
if (info.other_attributes.test(info.OtherAttribute_UseExtendedSavedataAccessControl))
{
uint32_t id;
if (0 != (id = info.accessible_unique_ids_0.unwrap().save_id0))
accessible_save_ids.push_back(id);
if (0 != (id = info.accessible_unique_ids_0.unwrap().save_id1))
accessible_save_ids.push_back(id);
if (0 != (id = info.accessible_unique_ids_0.unwrap().save_id2))
accessible_save_ids.push_back(id);
if (0 != (id = info.accessible_unique_ids_1.unwrap().save_id0))
accessible_save_ids.push_back(id);
if (0 != (id = info.accessible_unique_ids_1.unwrap().save_id1))
accessible_save_ids.push_back(id);
if (0 != (id = info.accessible_unique_ids_1.unwrap().save_id2))
accessible_save_ids.push_back(id);
}
else
{
ext_savedata_id = info.ext_savedata_id.unwrap();
other_user_save_ids[0] = info.accessible_unique_ids_0.unwrap().save_id0;
other_user_save_ids[1] = info.accessible_unique_ids_0.unwrap().save_id1;
other_user_save_ids[2] = info.accessible_unique_ids_0.unwrap().save_id2;
}
use_other_variation_savedata = info.accessible_unique_ids_0.unwrap().flag;
fmt::print("Ext savedata id: 0x{:016x}\n", ext_savedata_id);
for (size_t i = 0; i < info.system_savedata_id.size(); i++)
{
fmt::print("System savedata id {:d}: 0x{:08x} {}\n", i+1, info.system_savedata_id[i].unwrap(), getValidString(valid.system_save_id[i]));
}
for (size_t i = 0; i < other_user_save_ids.size(); i++)
{
fmt::print("OtherUserSaveDataId{:d}: 0x{:05x}\n", i+1, other_user_save_ids[i]);
}
fmt::print("Accessible Savedata Ids: {}\n", (accessible_save_ids.size() == 0 ? "None" : ""));
for (size_t i = 0; i < accessible_save_ids.size(); i++)
{
fmt::print(" > 0x{:05x}\n", accessible_save_ids[i]);
}
fmt::print("Other Variation Saves: {}\n", (use_other_variation_savedata ? "Accessible" : "Inaccessible"));
uint64_t fs_access_raw = ((((tc::bn::le64<uint64_t>*)&info.fs_access)->unwrap() << 8) >> 8); // clearing the upper 8 bits since fs_access is 56 bits
fmt::print("FS access: {:6} 0x{:014x}\n", getValidString(valid.fs_access), fs_access_raw);
for (size_t i = 0; i < info.fs_access.bit_size(); i++)
{
if (info.fs_access.test(i))
{
fmt::print(" > {}\n", getFsAccessBitString(i));
}
}
fmt::print("Service access: {}\n", getValidString(mValidLocalCaps.service_control));
auto& service_access = info.service_access_control;
for (size_t i = 0; i < service_access.size(); i++)
{
if (service_access[i].decode().empty())
break;
fmt::print(" > {}\n", service_access[i].decode());
}
fmt::print("Reslimit category: {:02X}\n", (uint32_t)info.resource_limit_category);
}
void ctrtool::ExHeaderProcess::printARM11KernelCapabilities(const ntd::n3ds::AccessControlInfo& info)
{
size_t i, j;
std::vector<byte_t> syscall_list;
std::vector<byte_t> interrupt_list;
std::vector<uint32_t> unknown_desc_list;
union KernDesc
{
uint32_t raw;
tc::bn::bitarray<4> bits;
ntd::n3ds::AccessControlInfo::InterruptDescriptor interrupt;
ntd::n3ds::AccessControlInfo::SystemCallDescriptor syscall;
ntd::n3ds::AccessControlInfo::ReleaseKernelVersionDescriptor kernel_ver;
ntd::n3ds::AccessControlInfo::HandleTableSizeDescriptor handle_table;
ntd::n3ds::AccessControlInfo::OtherCapabilitiesDescriptor other_cap;
ntd::n3ds::AccessControlInfo::MappingStaticDescriptor mapping_static;
ntd::n3ds::AccessControlInfo::MappingIODescriptor mapping_io;
};
KernDesc prev_desc;
prev_desc.raw = 0;
for (i = 0; i < info.kernel_descriptors.size(); i++)
{
KernDesc desc;
desc.raw = info.kernel_descriptors[i].unwrap();
uint32_t prefix_bits;
for (prefix_bits = 0; prefix_bits < 32; prefix_bits++)
{
if (desc.bits.test(31 - prefix_bits) == false)
{
break;
}
}
if (prefix_bits == ntd::n3ds::AccessControlInfo::DescriptorPrefix_InterruptNumList)
{
if (desc.interrupt.interrupt_0 != 0)
interrupt_list.push_back(desc.interrupt.interrupt_0);
if (desc.interrupt.interrupt_1 != 0)
interrupt_list.push_back(desc.interrupt.interrupt_1);
if (desc.interrupt.interrupt_2 != 0)
interrupt_list.push_back(desc.interrupt.interrupt_2);
if (desc.interrupt.interrupt_3 != 0)
interrupt_list.push_back(desc.interrupt.interrupt_3);
}
else if (prefix_bits == ntd::n3ds::AccessControlInfo::DescriptorPrefix_SysCallList)
{
for (j = 0; j < 24; j++)
{
if ((desc.syscall.systemcall_lower_bitarray >> j) & 1)
syscall_list.push_back(byte_t(desc.syscall.systemcall_upper * 24) + byte_t(j));
}
}
else if (prefix_bits == ntd::n3ds::AccessControlInfo::DescriptorPrefix_ReleaseKernelVersion)
{
fmt::print("Kernel release version: {:d}.{:d}\n", ((desc.kernel_ver.version >> 8) & 0xFF), ((desc.kernel_ver.version >> 0) & 0xFF));
}
else if (prefix_bits == ntd::n3ds::AccessControlInfo::DescriptorPrefix_HandleTableSize)
{
fmt::print("Handle table size: 0x{:X}\n", (uint32_t)desc.handle_table.size);
}
else if (prefix_bits == ntd::n3ds::AccessControlInfo::DescriptorPrefix_OtherCapabilities)
{
fmt::print("Kernel flags: \n");
fmt::print(" > Allow debug: {}\n", (desc.other_cap.permit_debug ? "YES" : "NO"));
fmt::print(" > Force debug: {}\n", (desc.other_cap.force_debug ? "YES" : "NO"));
fmt::print(" > Allow non-alphanum: {}\n", (desc.other_cap.can_use_non_alphabet_and_number ? "YES" : "NO"));
fmt::print(" > Shared page writing: {}\n", (desc.other_cap.can_write_shared_page ? "YES" : "NO"));
fmt::print(" > Privilege priority: {}\n", (desc.other_cap.can_use_privileged_priority ? "YES" : "NO"));
fmt::print(" > Allow main() args: {}\n", (desc.other_cap.permit_main_function_argument ? "YES" : "NO"));
fmt::print(" > Shared device mem: {}\n", (desc.other_cap.can_share_device_memory ? "YES" : "NO"));
fmt::print(" > Memory Type: {}\n", getMemoryTypeString(desc.other_cap.memory_type));
fmt::print(" > Runnable on sleep: {}\n", (desc.other_cap.runnable_on_sleep ? "YES" : "NO"));
fmt::print(" > Special memory: {}\n", (desc.other_cap.special_memory_layout ? "YES" : "NO"));
fmt::print(" > Access Core 2: {}\n", (desc.other_cap.can_access_core2 ? "YES" : "NO"));
}
else if (prefix_bits == ntd::n3ds::AccessControlInfo::DescriptorPrefix_MappingStatic)
{
if (prev_desc.raw == 0)
{
prev_desc.raw = desc.raw;
}
else
{
fmt::print("{:24}0x{:X}-0x{:X}{}\n",
(desc.mapping_static.flag ? "StaticMapping:" : "IoMapping:"),
(prev_desc.mapping_static.page << 12),
(desc.mapping_static.page << 12)-1,
(prev_desc.mapping_static.flag ? ":r" : "")
);
prev_desc.raw = 0;
}
}
else if (prefix_bits == ntd::n3ds::AccessControlInfo::DescriptorPrefix_MappingIo)
{
fmt::print("IoMapping: 0x{:X}\n", (desc.mapping_io.page << 12));
}
else if (prefix_bits == 32)
{
continue;
}
else
{
unknown_desc_list.push_back(desc.raw);
}
}
fmt::print("Allowed systemcalls: ");
if (syscall_list.size() > 0)
{
if (!mShowSyscallNames)
{
std::vector<std::string> string_list;
for (size_t i = 0; i < syscall_list.size(); i++)
{
string_list.push_back(getByteHexString(syscall_list[i]));
}
fmt::print("{}", tc::cli::FormatUtil::formatListWithLineLimit(string_list, 46, 24, false));
}
else
{
fmt::print("\n");
for (size_t i = 0; i < syscall_list.size(); i++)
{
fmt::print(" > {} {}\n", getByteHexString(syscall_list[i]), getSysCallName(syscall_list[i]));
}
}
}
else
{
fmt::print("none\n");
}
fmt::print("Allowed interrupts: ");
if (interrupt_list.size() > 0)
{
std::vector<std::string> string_list;
for (size_t i = 0; i < interrupt_list.size(); i++)
{
string_list.push_back(getByteHexString(interrupt_list[i]));
}
fmt::print("{}", tc::cli::FormatUtil::formatListWithLineLimit(string_list, 46, 24, false));
}
else
{
fmt::print("none\n");
}
for (i = 0; i < unknown_desc_list.size(); i++)
{
fmt::print("Unknown descriptor: {:08X}\n", unknown_desc_list[i]);
}
}
void ctrtool::ExHeaderProcess::printARM9AccessControlInfo(const ntd::n3ds::AccessControlInfo& info)
{
//fmt::print("[ARM9AccessControlInfo]\n");
// collect arm9 caps as a vector of strings
std::vector<std::string> arm9_caps_str;
for (size_t i = 0; i < info.arm9_access_control.bit_size(); i++)
{
if (info.arm9_access_control.test(i))
{
arm9_caps_str.push_back(getArm9CapabilityBitString(i));
}
}
// print arm9 caps
fmt::print("Arm9Capability: {}\n", (arm9_caps_str.size() == 0 ? "none" : ""));
for (size_t i = 0; i < arm9_caps_str.size(); i++)
{
fmt::print(" > {}\n", arm9_caps_str[i]);
}
// print desc version
fmt::print("Desc Version: 0x{:x}\n", (uint32_t)info.desc_version);
}
std::string ctrtool::ExHeaderProcess::getValidString(byte_t validstate)
{
std::string ret_str;
switch (validstate)
{
case ValidState::Unchecked:
ret_str = "";
break;
case ValidState::Good:
ret_str = "(GOOD)";
break;
case ValidState::Fail:
default:
ret_str = "(FAIL)";
break;
}
return ret_str;
}
std::string ctrtool::ExHeaderProcess::getSystemModeString(byte_t system_mode)
{
std::string str;
switch (system_mode)
{
case ntd::n3ds::AccessControlInfo::SystemMode_PROD :
str = "prod";
break;
case ntd::n3ds::AccessControlInfo::SystemMode_DEV1 :
str = "dev1";
break;
case ntd::n3ds::AccessControlInfo::SystemMode_DEV2 :
str = "dev2";
break;
case ntd::n3ds::AccessControlInfo::SystemMode_DEV3 :
str = "dev3";
break;
case ntd::n3ds::AccessControlInfo::SystemMode_DEV4 :
str = "dev4";
break;
default:
str = fmt::format("Unknown (0x{:x})", (uint32_t)system_mode);
break;
}
return str;
}
std::string ctrtool::ExHeaderProcess::getSystemModeExtString(byte_t system_mode_ext, byte_t system_mode)
{
std::string str;
switch (system_mode_ext)
{
case ntd::n3ds::AccessControlInfo::SystemModeExt_LEGACY :
//str = "Legacy";
str = fmt::format("ctr {}", getSystemModeString(system_mode));
break;
case ntd::n3ds::AccessControlInfo::SystemModeExt_PROD :
str = "snake prod";
break;
case ntd::n3ds::AccessControlInfo::SystemModeExt_DEV1 :
str = "snake dev1";
break;
default:
str = fmt::format("Unknown (0x{:x})", (uint32_t)system_mode_ext);
break;
}
return str;
}
std::string ctrtool::ExHeaderProcess::getSystemModeAppMemorySizeString(byte_t system_mode)
{
std::string str;
switch (system_mode)
{
case ntd::n3ds::AccessControlInfo::SystemMode_PROD :
str = "64MB";
break;
case ntd::n3ds::AccessControlInfo::SystemMode_DEV1 :
str = "96MB";
break;
case ntd::n3ds::AccessControlInfo::SystemMode_DEV2 :
str = "80MB";
break;
case ntd::n3ds::AccessControlInfo::SystemMode_DEV3 :
str = "72MB";
break;
case ntd::n3ds::AccessControlInfo::SystemMode_DEV4 :
str = "32MB";
break;
default:
str = "Unknown";
break;
}
return str;
}
std::string ctrtool::ExHeaderProcess::getSystemModeExtAppMemorySizeString(byte_t system_mode_ext, byte_t system_mode)
{
std::string str;
switch (system_mode_ext)
{
case ntd::n3ds::AccessControlInfo::SystemModeExt_LEGACY :
str = getSystemModeAppMemorySizeString(system_mode);
break;
case ntd::n3ds::AccessControlInfo::SystemModeExt_PROD :
str = "124MB";
break;
case ntd::n3ds::AccessControlInfo::SystemModeExt_DEV1 :
str = "178MB";
break;
default:
str = "Unknown";
break;
}
return str;
}
std::string ctrtool::ExHeaderProcess::getFsAccessBitString(size_t bit)
{
std::string str;
switch(bit)
{
case ntd::n3ds::AccessControlInfo::FileSystemAccess_CategorySystemApplication :
str = "Category System Application";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_CategoryHardwareCheck :
str = "Category Hardware Check";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_CategoryFileSystemTool :
str = "Category File System Tool";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_Debug :
str = "Debug";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_TwlCardBackup :
str = "TWL Card Backup";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_TwlNandData :
str = "TWL Nand Data";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_Boss :
str = "BOSS";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_DirectSdmc :
str = "Direct SDMC";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_Core :
str = "Core";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_CtrNandRo :
str = "CTR NAND RO";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_CtrNandRw :
str = "CTR NAND RW";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_CtrNandRoWrite :
str = "CTR NAND RO (Write Access)";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_CategorySystemSettings :
str = "Category System Settings";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_CardBoard :
str = "CARD BOARD";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_ExportImportIvs :
str = "Export Import IVS";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_DirectSdmcWrite :
str = "Direct SDMC (Write Only)";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_SwitchCleanup :
str = "Switch Cleanup";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_SaveDataMove :
str = "Save Data Move";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_Shop :
str = "Shop";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_Shell :
str = "Shell";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_CategoryHomeMenu :
str = "Category HomeMenu";
break;
case ntd::n3ds::AccessControlInfo::FileSystemAccess_ExternalSeed :
str = "External Seed (Seed DB)";
break;
default :
str = fmt::format("Bit {:d} (unknown)", bit);
break;
}
return str;
}
std::string ctrtool::ExHeaderProcess::getMemoryTypeString(byte_t memory_type)
{
std::string str;
switch(memory_type)
{
case ntd::n3ds::AccessControlInfo::MemoryType_Application :
str = "APPLICATION";
break;
case ntd::n3ds::AccessControlInfo::MemoryType_System :
str = "SYSTEM";
break;
case ntd::n3ds::AccessControlInfo::MemoryType_Base :
str = "BASE";
break;
default :
str = fmt::format("Unknown ({:d})", memory_type);
break;
}
return str;
}
std::string ctrtool::ExHeaderProcess::getByteHexString(byte_t byte)
{
return fmt::format("{:02X}", byte);
}
std::string ctrtool::ExHeaderProcess::getSysCallName(byte_t syscall)
{
// List of 3DS system calls. NULL indicates unknown.
static const size_t kSysCallNum = 128;
static const char *const kSysCallList[kSysCallNum] =
{
NULL, // 00
"ControlMemory", // 01
"QueryMemory", // 02
"ExitProcess", // 03
"GetProcessAffinityMask", // 04
"SetProcessAffinityMask", // 05
"GetProcessIdealProcessor", // 06
"SetProcessIdealProcessor", // 07
"CreateThread", // 08
"ExitThread", // 09
"SleepThread", // 0A
"GetThreadPriority", // 0B
"SetThreadPriority", // 0C
"GetThreadAffinityMask", // 0D
"SetThreadAffinityMask", // 0E
"GetThreadIdealProcessor", // 0F
"SetThreadIdealProcessor", // 10
"GetCurrentProcessorNumber", // 11
"Run", // 12
"CreateMutex", // 13
"ReleaseMutex", // 14
"CreateSemaphore", // 15
"ReleaseSemaphore", // 16
"CreateEvent", // 17
"SignalEvent", // 18
"ClearEvent", // 19
"CreateTimer", // 1A
"SetTimer", // 1B
"CancelTimer", // 1C
"ClearTimer", // 1D
"CreateMemoryBlock", // 1E
"MapMemoryBlock", // 1F
"UnmapMemoryBlock", // 20
"CreateAddressArbiter", // 21
"ArbitrateAddress", // 22
"CloseHandle", // 23
"WaitSynchronization1", // 24
"WaitSynchronizationN", // 25
"SignalAndWait", // 26
"DuplicateHandle", // 27
"GetSystemTick", // 28
"GetHandleInfo", // 29
"GetSystemInfo", // 2A
"GetProcessInfo", // 2B
"GetThreadInfo", // 2C
"ConnectToPort", // 2D
"SendSyncRequest1", // 2E
"SendSyncRequest2", // 2F
"SendSyncRequest3", // 30
"SendSyncRequest4", // 31
"SendSyncRequest", // 32
"OpenProcess", // 33
"OpenThread", // 34
"GetProcessId", // 35
"GetProcessIdOfThread", // 36
"GetThreadId", // 37
"GetResourceLimit", // 38
"GetResourceLimitLimitValues", // 39
"GetResourceLimitCurrentValues", // 3A
"GetThreadContext", // 3B
"Break", // 3C
"OutputDebugString", // 3D
"ControlPerformanceCounter", // 3E
NULL, // 3F
NULL, // 40
NULL, // 41
NULL, // 42
NULL, // 43
NULL, // 44
NULL, // 45
NULL, // 46
"CreatePort", // 47
"CreateSessionToPort", // 48
"CreateSession", // 49
"AcceptSession", // 4A
"ReplyAndReceive1", // 4B
"ReplyAndReceive2", // 4C
"ReplyAndReceive3", // 4D
"ReplyAndReceive4", // 4E
"ReplyAndReceive", // 4F
"BindInterrupt", // 50
"UnbindInterrupt", // 51
"InvalidateProcessDataCache", // 52
"StoreProcessDataCache", // 53
"FlushProcessDataCache", // 54
"StartInterProcessDma", // 55
"StopDma", // 56
"GetDmaState", // 57
"RestartDma", // 58
"SetGpuProt", // 59
"SetWifiEnabled", // 5A
NULL, // 5B
NULL, // 5C
NULL, // 5D
NULL, // 5E
NULL, // 5F
"DebugActiveProcess", // 60
"BreakDebugProcess", // 61
"TerminateDebugProcess", // 62
"GetProcessDebugEvent", // 63
"ContinueDebugEvent", // 64
"GetProcessList", // 65
"GetThreadList", // 66
"GetDebugThreadContext", // 67
"SetDebugThreadContext", // 68
"QueryDebugProcessMemory", // 69
"ReadProcessMemory", // 6A
"WriteProcessMemory", // 6B
"SetHardwareBreakPoint", // 6C
"GetDebugThreadParam", // 6D
NULL, // 6E
NULL, // 6F
"ControlProcessMemory", // 70
"MapProcessMemory", // 71
"UnmapProcessMemory", // 72
"CreateCodeSet", // 73
NULL, // 74
"CreateProcess", // 75
"TerminateProcess", // 76
"SetProcessResourceLimits", // 77
"CreateResourceLimit", // 78
"SetResourceLimitValues", // 79
"AddCodeSegment", // 7A
"Backdoor", // 7B
"KernelSetState", // 7C
"QueryProcessMemory", // 7D
NULL, // 7E
NULL, // 7F
};
std::string str;
if (syscall >= kSysCallNum)
return std::string();
if (kSysCallList[syscall] != nullptr)
{
str = kSysCallList[syscall];
}
else
{
str = fmt::format("Unknown {:02X}", syscall);;
}
return str;
}
std::string ctrtool::ExHeaderProcess::getArm9CapabilityBitString(size_t bit)
{
std::string str;
switch(bit)
{
case ntd::n3ds::AccessControlInfo::Arm9Capability_FsMountNand :
str = "FsMountNand";
break;
case ntd::n3ds::AccessControlInfo::Arm9Capability_FsMountNandRoWrite :
str = "FsMountNandRoWrite";
break;
case ntd::n3ds::AccessControlInfo::Arm9Capability_FsMountTwln :
str = "FsMountTwln";
break;
case ntd::n3ds::AccessControlInfo::Arm9Capability_FsMountWnand :
str = "FsMountWnand";
break;
case ntd::n3ds::AccessControlInfo::Arm9Capability_FsMountCardSpi :
str = "FsMountCardSpi";
break;
case ntd::n3ds::AccessControlInfo::Arm9Capability_UseSdif3 :
str = "UseSdif3";
break;
case ntd::n3ds::AccessControlInfo::Arm9Capability_CreateSeed :
str = "CreateSeed";
break;
case ntd::n3ds::AccessControlInfo::Arm9Capability_UseCardSpi :
str = "UseCardSpi";
break;
case ntd::n3ds::AccessControlInfo::Arm9Capability_SdApplication :
str = "SdApplication";
break;
case ntd::n3ds::AccessControlInfo::Arm9Capability_UseDirectSdmc :
str = "UseDirectSdmc";
break;
default :
str = fmt::format("Bit {:d} (unknown)", bit);
break;
}
return str;
}