#include "ExHeaderProcess.h" #include #include #include #include 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& 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 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< 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 accessible_save_ids; uint64_t ext_savedata_id = 0; std::array 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*)&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 syscall_list; std::vector interrupt_list; std::vector 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 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 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 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; }