mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-04-22 20:44:49 +00:00
ams.mitm: Implement revamped blanking/write policy
This commit is contained in:
parent
e59dd9c8f0
commit
61f9e0dfb9
10 changed files with 347 additions and 82 deletions
|
@ -58,6 +58,9 @@ namespace ams::mitm {
|
|||
os::ThreadType g_initialize_thread;
|
||||
alignas(os::ThreadStackAlignment) u8 g_initialize_thread_stack[InitializeThreadStackSize];
|
||||
|
||||
os::Mutex g_prodinfo_init_lock(false);
|
||||
bool g_initialized_prodinfo;
|
||||
|
||||
/* Console-unique data backup and protection. */
|
||||
FsFile g_bis_key_file;
|
||||
|
||||
|
@ -90,7 +93,7 @@ namespace ams::mitm {
|
|||
/* Initialize PRODINFO and get a reference for the device. */
|
||||
char device_reference[0x40] = {};
|
||||
ON_SCOPE_EXIT { std::memset(device_reference, 0, sizeof(device_reference)); };
|
||||
mitm::InitializeProdInfoManagement(device_reference, sizeof(device_reference));
|
||||
mitm::SaveProdInfoBackupsAndWipeMemory(device_reference, sizeof(device_reference));
|
||||
|
||||
/* Backup BIS keys. */
|
||||
{
|
||||
|
@ -172,10 +175,17 @@ namespace ams::mitm {
|
|||
|
||||
}
|
||||
|
||||
void StartInitialize() {
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_initialize_thread), InitializeThreadFunc, nullptr, g_initialize_thread_stack, sizeof(g_initialize_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm, InitializeThread)));
|
||||
os::SetThreadNamePointer(std::addressof(g_initialize_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm, InitializeThread));
|
||||
os::StartThread(std::addressof(g_initialize_thread));
|
||||
void EnsureProdInfoInitializedAndKickOffInit() {
|
||||
std::scoped_lock lk(g_prodinfo_init_lock);
|
||||
if (!g_initialized_prodinfo) {
|
||||
mitm::InitializeProdInfoManagement();
|
||||
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_initialize_thread), InitializeThreadFunc, nullptr, g_initialize_thread_stack, sizeof(g_initialize_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm, InitializeThread)));
|
||||
os::SetThreadNamePointer(std::addressof(g_initialize_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm, InitializeThread));
|
||||
os::StartThread(std::addressof(g_initialize_thread));
|
||||
|
||||
g_initialized_prodinfo = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsInitialized() {
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
|
||||
namespace ams::mitm {
|
||||
|
||||
void StartInitialize();
|
||||
void EnsureProdInfoInitializedAndKickOffInit();
|
||||
|
||||
bool IsInitialized();
|
||||
void WaitInitialized();
|
||||
|
||||
|
|
|
@ -95,9 +95,6 @@ void __appExit(void) {
|
|||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
/* Start initialization (sd card init, automatic backups, etc) */
|
||||
mitm::StartInitialize();
|
||||
|
||||
/* Launch all mitm modules in sequence. */
|
||||
mitm::LaunchAllModules();
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ namespace ams::mitm {
|
|||
};
|
||||
static_assert(sizeof(CalibrationInfoHeader) == 0x40);
|
||||
|
||||
constexpr inline size_t CalibrationInfoBodySizeMax = 0x8000 - sizeof(CalibrationInfoHeader);
|
||||
constexpr inline size_t CalibrationInfoBodySizeMax = CalibrationBinarySize - sizeof(CalibrationInfoHeader);
|
||||
|
||||
struct CalibrationInfo {
|
||||
CalibrationInfoHeader header;
|
||||
|
@ -101,14 +101,14 @@ namespace ams::mitm {
|
|||
return *static_cast<const Block *>(static_cast<const void *>(std::addressof(this->body[Block::Offset - sizeof(this->header)])));
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(CalibrationInfo) == 0x8000);
|
||||
static_assert(sizeof(CalibrationInfo) == CalibrationBinarySize);
|
||||
|
||||
struct SecureCalibrationInfoBackup {
|
||||
CalibrationInfo info;
|
||||
Sha256Hash hash;
|
||||
u8 pad[0xC000 - sizeof(info) - sizeof(hash)];
|
||||
u8 pad[SecureCalibrationBinaryBackupSize - sizeof(info) - sizeof(hash)];
|
||||
};
|
||||
static_assert(sizeof(SecureCalibrationInfoBackup) == 0xC000);
|
||||
static_assert(sizeof(SecureCalibrationInfoBackup) == SecureCalibrationBinaryBackupSize);
|
||||
|
||||
bool IsValidSha256Hash(const Sha256Hash &hash, const void *data, size_t data_size) {
|
||||
Sha256Hash calc_hash;
|
||||
|
@ -175,7 +175,7 @@ namespace ams::mitm {
|
|||
std::memcpy(block.serial_number.str, BlankSerialNumberString, sizeof(BlankSerialNumberString));
|
||||
block.crc = GetCrc16(std::addressof(block), Block::Size - sizeof(block.crc));
|
||||
} else if constexpr (std::is_same<Block, SslCertificateBlock>::value) {
|
||||
std::memset(std::addressof(block), 0, Block::Size);
|
||||
std::memset(std::addressof(block), 0, sizeof(block.ssl_certificate));
|
||||
} else if constexpr (Block::IsCrcBlock) {
|
||||
std::memset(std::addressof(block), 0, Block::Size - sizeof(block.crc));
|
||||
block.crc = GetCrc16(std::addressof(block), Block::Size - sizeof(block.crc));
|
||||
|
@ -186,27 +186,6 @@ namespace ams::mitm {
|
|||
}
|
||||
}
|
||||
|
||||
void Blank(CalibrationInfo &info) {
|
||||
/* Set header. */
|
||||
info.header.magic = CalibrationMagic;
|
||||
info.header.body_size = sizeof(info.body);
|
||||
info.header.crc = GetCrc16(std::addressof(info.header), OFFSETOF(CalibrationInfoHeader, crc));
|
||||
|
||||
/* Set blocks. */
|
||||
Blank(info.GetBlock<SerialNumberBlock>());
|
||||
Blank(info.GetBlock<SslKeyBlock>());
|
||||
Blank(info.GetBlock<SslCertificateSizeBlock>());
|
||||
Blank(info.GetBlock<SslCertificateBlock>());
|
||||
Blank(info.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>());
|
||||
Blank(info.GetBlock<EcqvBlsAmiiboRootCertificateBlock>());
|
||||
Blank(info.GetBlock<ExtendedSslKeyBlock>());
|
||||
Blank(info.GetBlock<Rsa2048DeviceKeyBlock>());
|
||||
Blank(info.GetBlock<Rsa2048DeviceCertificateBlock>());
|
||||
|
||||
/* Set header hash. */
|
||||
crypto::GenerateSha256Hash(std::addressof(info.header.body_hash), sizeof(info.header.body_hash), std::addressof(info.body), sizeof(info.body));
|
||||
}
|
||||
|
||||
template<typename Block>
|
||||
bool IsBlank(const Block &block) {
|
||||
if constexpr (std::is_same<Block, SerialNumberBlock>::value) {
|
||||
|
@ -229,6 +208,27 @@ namespace ams::mitm {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void Blank(CalibrationInfo &info) {
|
||||
/* Set header. */
|
||||
info.header.magic = CalibrationMagic;
|
||||
info.header.body_size = sizeof(info.body);
|
||||
info.header.crc = GetCrc16(std::addressof(info.header), OFFSETOF(CalibrationInfoHeader, crc));
|
||||
|
||||
/* Set blocks. */
|
||||
Blank(info.GetBlock<SerialNumberBlock>());
|
||||
Blank(info.GetBlock<SslCertificateSizeBlock>());
|
||||
Blank(info.GetBlock<SslCertificateBlock>());
|
||||
Blank(info.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>());
|
||||
Blank(info.GetBlock<EcqvBlsAmiiboRootCertificateBlock>());
|
||||
Blank(info.GetBlock<ExtendedSslKeyBlock>());
|
||||
if (IsValid(info.GetBlock<Rsa2048DeviceKeyBlock>()) && !IsBlank(info.GetBlock<Rsa2048DeviceKeyBlock>())) Blank(info.GetBlock<Rsa2048DeviceKeyBlock>());
|
||||
if (IsValid(info.GetBlock<Rsa2048DeviceCertificateBlock>()) && !IsBlank(info.GetBlock<Rsa2048DeviceCertificateBlock>())) Blank(info.GetBlock<Rsa2048DeviceCertificateBlock>());
|
||||
|
||||
/* Set header hash. */
|
||||
crypto::GenerateSha256Hash(std::addressof(info.header.body_hash), sizeof(info.header.body_hash), std::addressof(info.body), sizeof(info.body));
|
||||
}
|
||||
|
||||
bool IsValidHeader(const CalibrationInfo &cal) {
|
||||
return IsValid(cal.header) && cal.header.body_size <= CalibrationInfoBodySizeMax && IsValid(cal.header, cal.body);
|
||||
}
|
||||
|
@ -256,25 +256,28 @@ namespace ams::mitm {
|
|||
}
|
||||
|
||||
bool IsValid(const CalibrationInfo &cal) {
|
||||
return IsValidHeader(cal) &&
|
||||
IsValid(cal.GetBlock<SerialNumberBlock>()) &&
|
||||
IsValid(cal.GetBlock<EccB233DeviceCertificateBlock>()) &&
|
||||
IsValid(cal.GetBlock<SslKeyBlock>()) &&
|
||||
IsValid(cal.GetBlock<SslCertificateSizeBlock>()) &&
|
||||
IsValid(cal.GetBlock<SslCertificateBlock>()) &&
|
||||
IsValid(cal.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>()) &&
|
||||
IsValid(cal.GetBlock<EcqvBlsAmiiboRootCertificateBlock>()) &&
|
||||
IsValid(cal.GetBlock<ExtendedSslKeyBlock>()) &&
|
||||
return IsValidHeader(cal) &&
|
||||
IsValid(cal.GetBlock<SerialNumberBlock>()) &&
|
||||
IsValid(cal.GetBlock<EccB233DeviceCertificateBlock>()) &&
|
||||
IsValid(cal.GetBlock<SslKeyBlock>()) &&
|
||||
IsValid(cal.GetBlock<SslCertificateSizeBlock>()) &&
|
||||
cal.GetBlock<SslCertificateSizeBlock>().ssl_certificate_size <= sizeof(cal.GetBlock<SslCertificateBlock>().ssl_certificate) &&
|
||||
IsValid(cal.GetBlock<SslCertificateBlock>(), cal.GetBlock<SslCertificateSizeBlock>().ssl_certificate_size) &&
|
||||
IsValid(cal.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>()) &&
|
||||
IsValid(cal.GetBlock<EcqvBlsAmiiboRootCertificateBlock>()) &&
|
||||
IsValid(cal.GetBlock<ExtendedSslKeyBlock>()) &&
|
||||
IsValidSerialNumber(cal);
|
||||
}
|
||||
|
||||
bool ContainsCorrectDeviceId(const EccB233DeviceCertificateBlock &block, u64 device_id) {
|
||||
static constexpr size_t DeviceIdOffset = 0xB4;
|
||||
char expected_device_id[sizeof("NX0011223344556677")] = {};
|
||||
ON_SCOPE_EXIT { std::memset(expected_device_id, 0, sizeof(expected_device_id)); };
|
||||
static constexpr size_t DeviceIdOffset = 0xC6;
|
||||
char found_device_id_str[sizeof("0011223344556677")] = {};
|
||||
ON_SCOPE_EXIT { std::memset(found_device_id_str, 0, sizeof(found_device_id_str)); };
|
||||
std::memcpy(found_device_id_str, std::addressof(block.device_certificate.data[DeviceIdOffset]), sizeof(found_device_id_str) - 1);
|
||||
|
||||
std::snprintf(expected_device_id, sizeof(expected_device_id), "NX%016lX", device_id);
|
||||
return std::memcmp(expected_device_id, std::addressof(block.device_certificate.data[DeviceIdOffset]), sizeof(expected_device_id) - 1) == 0;
|
||||
static constexpr u64 DeviceIdLowMask = 0x00FFFFFFFFFFFFFFul;
|
||||
|
||||
return (std::strtoul(found_device_id_str, nullptr, 16) & DeviceIdLowMask) == (device_id & DeviceIdLowMask);
|
||||
}
|
||||
|
||||
bool ContainsCorrectDeviceId(const CalibrationInfo &cal) {
|
||||
|
@ -287,8 +290,6 @@ namespace ams::mitm {
|
|||
|
||||
bool IsBlank(const CalibrationInfo &cal) {
|
||||
return IsBlank(cal.GetBlock<SerialNumberBlock>()) ||
|
||||
IsBlank(cal.GetBlock<EccB233DeviceCertificateBlock>()) ||
|
||||
IsBlank(cal.GetBlock<SslKeyBlock>()) ||
|
||||
IsBlank(cal.GetBlock<SslCertificateSizeBlock>()) ||
|
||||
IsBlank(cal.GetBlock<SslCertificateBlock>()) ||
|
||||
IsBlank(cal.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>()) ||
|
||||
|
@ -304,8 +305,6 @@ namespace ams::mitm {
|
|||
R_ABORT_UNLESS(fsStorageRead(&calibration_binary_storage, 0, out, sizeof(*out)));
|
||||
}
|
||||
|
||||
constexpr inline s64 SecureCalibrationInfoBackupOffset = 3_MB;
|
||||
|
||||
constexpr inline const u8 SecureCalibrationBinaryBackupIv[crypto::Aes128CtrDecryptor::IvSize] = {};
|
||||
|
||||
void ReadStorageEncryptedSecureCalibrationBinaryBackupUnsafe(SecureCalibrationInfoBackup *out) {
|
||||
|
@ -505,26 +504,32 @@ namespace ams::mitm {
|
|||
}
|
||||
|
||||
alignas(os::MemoryPageSize) CalibrationInfo g_calibration_info = {};
|
||||
alignas(os::MemoryPageSize) CalibrationInfo g_blank_calibration_info = {};
|
||||
alignas(os::MemoryPageSize) SecureCalibrationInfoBackup g_secure_calibration_info_backup = {};
|
||||
|
||||
std::optional<ams::fs::FileStorage> g_blank_prodinfo_file;
|
||||
std::optional<ams::fs::FileStorage> g_prodinfo_backup_file;
|
||||
std::optional<ams::fs::MemoryStorage> g_blank_prodinfo_storage;
|
||||
std::optional<ams::fs::MemoryStorage> g_fake_secure_backup_storage;
|
||||
|
||||
bool g_allow_writes = false;
|
||||
bool g_has_secure_backup = false;
|
||||
|
||||
os::Mutex g_prodinfo_management_lock(false);
|
||||
|
||||
}
|
||||
|
||||
void InitializeProdInfoManagement(char *out_name, size_t out_name_size) {
|
||||
void InitializeProdInfoManagement() {
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
|
||||
/* First, get our options. */
|
||||
const bool should_blank = exosphere::ShouldBlankProdInfo();
|
||||
bool allow_writes = exosphere::ShouldAllowWritesToProdInfo();
|
||||
|
||||
/* Next, read our prodinfo. */
|
||||
ReadStorageCalibrationBinary(std::addressof(g_calibration_info));
|
||||
ON_SCOPE_EXIT { FillWithGarbage(std::addressof(g_calibration_info), sizeof(g_calibration_info)); };
|
||||
|
||||
/* Next, check if we have a secure backup. */
|
||||
bool has_secure_backup = ReadStorageSecureCalibrationBinaryBackup(std::addressof(g_secure_calibration_info_backup));
|
||||
ON_SCOPE_EXIT { FillWithGarbage(std::addressof(g_secure_calibration_info_backup), sizeof(g_secure_calibration_info_backup)); };
|
||||
|
||||
/* Only allow writes if we have a secure backup. */
|
||||
if (allow_writes && !has_secure_backup) {
|
||||
|
@ -543,8 +548,31 @@ namespace ams::mitm {
|
|||
/* Ensure our preconditions are met. */
|
||||
AMS_ABORT_UNLESS(!allow_writes || has_secure_backup);
|
||||
|
||||
/* Set globals. */
|
||||
g_allow_writes = allow_writes;
|
||||
g_has_secure_backup = has_secure_backup;
|
||||
|
||||
/* If we should blank, do so. */
|
||||
if (should_blank) {
|
||||
g_blank_calibration_info = g_calibration_info;
|
||||
Blank(g_blank_calibration_info);
|
||||
g_blank_prodinfo_storage.emplace(std::addressof(g_blank_calibration_info), sizeof(g_blank_calibration_info));
|
||||
}
|
||||
|
||||
/* Ensure that we have a blank file only if we need one. */
|
||||
AMS_ABORT_UNLESS(should_blank == static_cast<bool>(g_blank_prodinfo_storage));
|
||||
}
|
||||
|
||||
void SaveProdInfoBackupsAndWipeMemory(char *out_name, size_t out_name_size) {
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
FillWithGarbage(std::addressof(g_calibration_info), sizeof(g_calibration_info));
|
||||
FillWithGarbage(std::addressof(g_secure_calibration_info_backup), sizeof(g_secure_calibration_info_backup));
|
||||
};
|
||||
|
||||
/* Save our backup. We always prefer to save a secure copy of data over a non-secure one. */
|
||||
if (has_secure_backup) {
|
||||
if (g_has_secure_backup) {
|
||||
GetSerialNumber(out_name, g_secure_calibration_info_backup.info);
|
||||
SaveProdInfoBackup(std::addressof(g_prodinfo_backup_file), g_secure_calibration_info_backup.info);
|
||||
} else {
|
||||
|
@ -563,17 +591,49 @@ namespace ams::mitm {
|
|||
/* Ensure we made our backup. */
|
||||
AMS_ABORT_UNLESS(g_prodinfo_backup_file);
|
||||
|
||||
/* If we should blank our prodinfo, do so. */
|
||||
if (should_blank) {
|
||||
Blank(g_calibration_info);
|
||||
SaveProdInfoBackup(std::addressof(g_blank_prodinfo_file), g_calibration_info);
|
||||
}
|
||||
|
||||
/* Ensure that we have a blank file if we need one. */
|
||||
AMS_ABORT_UNLESS(!should_blank || g_blank_prodinfo_file);
|
||||
|
||||
/* Setup our memory storage. */
|
||||
g_fake_secure_backup_storage.emplace(std::addressof(g_secure_calibration_info_backup), sizeof(g_secure_calibration_info_backup));
|
||||
|
||||
/* Ensure that we have a fake storage. */
|
||||
AMS_ABORT_UNLESS(static_cast<bool>(g_fake_secure_backup_storage));
|
||||
}
|
||||
|
||||
bool ShouldReadBlankCalibrationBinary() {
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
return static_cast<bool>(g_blank_prodinfo_storage);
|
||||
}
|
||||
|
||||
bool IsWriteToCalibrationBinaryAllowed() {
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
return g_allow_writes;
|
||||
}
|
||||
|
||||
void ReadFromBlankCalibrationBinary(s64 offset, void *dst, size_t size) {
|
||||
AMS_ABORT_UNLESS(ShouldReadBlankCalibrationBinary());
|
||||
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
R_ABORT_UNLESS(g_blank_prodinfo_storage->Read(offset, dst, size));
|
||||
}
|
||||
|
||||
void WriteToBlankCalibrationBinary(s64 offset, const void *src, size_t size) {
|
||||
AMS_ABORT_UNLESS(ShouldReadBlankCalibrationBinary());
|
||||
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
R_ABORT_UNLESS(g_blank_prodinfo_storage->Write(offset, src, size));
|
||||
}
|
||||
|
||||
void ReadFromFakeSecureBackupStorage(s64 offset, void *dst, size_t size) {
|
||||
AMS_ABORT_UNLESS(IsWriteToCalibrationBinaryAllowed());
|
||||
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
R_ABORT_UNLESS(g_fake_secure_backup_storage->Read(offset, dst, size));
|
||||
}
|
||||
|
||||
void WriteToFakeSecureBackupStorage(s64 offset, const void *src, size_t size) {
|
||||
AMS_ABORT_UNLESS(IsWriteToCalibrationBinaryAllowed());
|
||||
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
R_ABORT_UNLESS(g_fake_secure_backup_storage->Write(offset, src, size));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,8 +18,22 @@
|
|||
|
||||
namespace ams::mitm {
|
||||
|
||||
void InitializeProdInfoManagement(char *out_name, size_t out_name_size);
|
||||
constexpr inline size_t CalibrationBinarySize = 0x8000;
|
||||
|
||||
constexpr inline s64 SecureCalibrationInfoBackupOffset = 3_MB;
|
||||
constexpr inline size_t SecureCalibrationBinaryBackupSize = 0xC000;
|
||||
|
||||
void InitializeProdInfoManagement();
|
||||
|
||||
void SaveProdInfoBackupsAndWipeMemory(char *out_name, size_t out_name_size);
|
||||
|
||||
bool ShouldReadBlankCalibrationBinary();
|
||||
bool IsWriteToCalibrationBinaryAllowed();
|
||||
|
||||
void ReadFromBlankCalibrationBinary(s64 offset, void *dst, size_t size);
|
||||
void WriteToBlankCalibrationBinary(s64 offset, const void *src, size_t size);
|
||||
|
||||
void ReadFromFakeSecureBackupStorage(s64 offset, void *dst, size_t size);
|
||||
void WriteToFakeSecureBackupStorage(s64 offset, const void *src, size_t size);
|
||||
|
||||
}
|
||||
|
|
|
@ -14,9 +14,11 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "fs_shim.h"
|
||||
#include "fs_mitm_service.hpp"
|
||||
#include "fsmitm_boot0storage.hpp"
|
||||
#include "fsmitm_calibration_binary_storage.hpp"
|
||||
#include "fsmitm_layered_romfs_storage.hpp"
|
||||
#include "fsmitm_save_utils.hpp"
|
||||
#include "fsmitm_readonly_layered_filesystem.hpp"
|
||||
|
@ -252,7 +254,6 @@ namespace ams::mitm::fs {
|
|||
const bool is_sysmodule = ncm::IsSystemProgramId(this->client_info.program_id);
|
||||
const bool is_hbl = this->client_info.override_status.IsHbl();
|
||||
const bool can_write_bis = is_sysmodule || (is_hbl && GetSettingsItemBooleanValue("atmosphere", "enable_hbl_bis_write"));
|
||||
const bool can_read_cal = is_sysmodule || (is_hbl && GetSettingsItemBooleanValue("atmosphere", "enable_hbl_cal_read"));
|
||||
|
||||
/* Allow HBL to write to boot1 (safe firm) + package2. */
|
||||
/* This is needed to not break compatibility with ChoiDujourNX, which does not check for write access before beginning an update. */
|
||||
|
@ -265,15 +266,8 @@ namespace ams::mitm::fs {
|
|||
if (bis_partition_id == FsBisPartitionId_BootPartition1Root) {
|
||||
out.SetValue(std::make_shared<IStorageInterface>(new Boot0Storage(bis_storage, this->client_info)), target_object_id);
|
||||
} else if (bis_partition_id == FsBisPartitionId_CalibrationBinary) {
|
||||
/* PRODINFO should *never* be writable. */
|
||||
/* If we have permissions, create a read only storage. */
|
||||
if (can_read_cal) {
|
||||
out.SetValue(std::make_shared<IStorageInterface>(new ReadOnlyStorageAdapter(new RemoteStorage(bis_storage))), target_object_id);
|
||||
} else {
|
||||
/* If we can't read cal, return permission denied. */
|
||||
fsStorageClose(&bis_storage);
|
||||
return fs::ResultPermissionDenied();
|
||||
}
|
||||
mitm::EnsureProdInfoInitializedAndKickOffInit();
|
||||
out.SetValue(std::make_shared<IStorageInterface>(new CalibrationBinaryStorage(bis_storage, this->client_info)), target_object_id);
|
||||
} else {
|
||||
if (can_write_bis || can_write_bis_for_choi_support) {
|
||||
/* We can write, so create a writable storage. */
|
||||
|
|
|
@ -54,6 +54,11 @@ namespace ams::mitm::fs {
|
|||
return true;
|
||||
}
|
||||
|
||||
/* We want to mitm settings, to intercept CAL0. */
|
||||
if (program_id == ncm::SystemProgramId::Settings) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We want to mitm sdb, to support sd-romfs redirection of common system archives (like system font, etc). */
|
||||
if (program_id == ncm::SystemProgramId::Sdb) {
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_calibration_binary_storage.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
namespace {
|
||||
|
||||
os::Mutex g_cal0_access_mutex(false);
|
||||
|
||||
}
|
||||
Result CalibrationBinaryStorage::Read(s64 offset, void *_buffer, size_t size) {
|
||||
/* Acquire exclusive calibration binary access. */
|
||||
std::scoped_lock lk(g_cal0_access_mutex);
|
||||
|
||||
/* Get u8 buffer. */
|
||||
u8 *buffer = static_cast<u8 *>(_buffer);
|
||||
|
||||
/* Succeed on zero-size. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle the blank region. */
|
||||
if (this->read_blank) {
|
||||
if (BlankStartOffset <= offset && offset < BlankEndOffset) {
|
||||
const size_t blank_size = std::min(size, static_cast<size_t>(BlankEndOffset - offset));
|
||||
mitm::ReadFromBlankCalibrationBinary(offset, buffer, blank_size);
|
||||
size -= blank_size;
|
||||
buffer += blank_size;
|
||||
offset += blank_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle any in-between data. */
|
||||
if (BlankEndOffset <= offset && offset < FakeSecureStartOffset) {
|
||||
const size_t mid_size = std::min(size, static_cast<size_t>(FakeSecureStartOffset - offset));
|
||||
R_TRY(Base::Read(offset, buffer, mid_size));
|
||||
size -= mid_size;
|
||||
buffer += mid_size;
|
||||
offset += mid_size;
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle the secure region. */
|
||||
if (FakeSecureStartOffset <= offset && offset < FakeSecureEndOffset) {
|
||||
const size_t fake_size = std::min(size, static_cast<size_t>(FakeSecureEndOffset - offset));
|
||||
mitm::ReadFromFakeSecureBackupStorage(offset, buffer, fake_size);
|
||||
size -= fake_size;
|
||||
buffer += fake_size;
|
||||
offset += fake_size;
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle any remaining data. */
|
||||
return Base::Read(offset, buffer, size);
|
||||
}
|
||||
|
||||
Result CalibrationBinaryStorage::Write(s64 offset, const void *_buffer, size_t size) {
|
||||
/* Acquire exclusive calibration binary access. */
|
||||
std::scoped_lock lk(g_cal0_access_mutex);
|
||||
|
||||
/* Get const u8 buffer. */
|
||||
const u8 *buffer = static_cast<const u8 *>(_buffer);
|
||||
|
||||
/* Succeed on zero-size. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Only allow writes if we should. */
|
||||
R_UNLESS(this->allow_writes, fs::ResultUnsupportedOperation());
|
||||
|
||||
/* Handle the blank region. */
|
||||
if (this->read_blank) {
|
||||
if (BlankStartOffset <= offset && offset < BlankEndOffset) {
|
||||
const size_t blank_size = std::min(size, static_cast<size_t>(BlankEndOffset - offset));
|
||||
mitm::WriteToBlankCalibrationBinary(offset, buffer, blank_size);
|
||||
size -= blank_size;
|
||||
buffer += blank_size;
|
||||
offset += blank_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle any in-between data. */
|
||||
if (BlankEndOffset <= offset && offset < FakeSecureStartOffset) {
|
||||
const size_t mid_size = std::min(size, static_cast<size_t>(FakeSecureStartOffset - offset));
|
||||
R_TRY(Base::Write(offset, buffer, mid_size));
|
||||
size -= mid_size;
|
||||
buffer += mid_size;
|
||||
offset += mid_size;
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle the secure region. */
|
||||
if (FakeSecureStartOffset <= offset && offset < FakeSecureEndOffset) {
|
||||
const size_t fake_size = std::min(size, static_cast<size_t>(FakeSecureEndOffset - offset));
|
||||
mitm::WriteToFakeSecureBackupStorage(offset, buffer, fake_size);
|
||||
size -= fake_size;
|
||||
buffer += fake_size;
|
||||
offset += fake_size;
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle any remaining data. */
|
||||
return Base::Write(offset, buffer, size);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_boot0storage.hpp"
|
||||
#include "../amsmitm_prodinfo_utils.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
/* Represents a protected calibration binary partition. */
|
||||
class CalibrationBinaryStorage : public SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200> {
|
||||
public:
|
||||
using Base = SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200>;
|
||||
|
||||
static constexpr s64 BlankStartOffset = 0x0;
|
||||
static constexpr s64 BlankSize = static_cast<s64>(CalibrationBinarySize);
|
||||
static constexpr s64 BlankEndOffset = BlankStartOffset + BlankSize;
|
||||
|
||||
static constexpr s64 FakeSecureStartOffset = SecureCalibrationInfoBackupOffset;
|
||||
static constexpr s64 FakeSecureSize = static_cast<s64>(SecureCalibrationBinaryBackupSize);
|
||||
static constexpr s64 FakeSecureEndOffset = FakeSecureStartOffset + FakeSecureSize;
|
||||
private:
|
||||
sm::MitmProcessInfo client_info;
|
||||
bool read_blank;
|
||||
bool allow_writes;
|
||||
public:
|
||||
CalibrationBinaryStorage(FsStorage &s, const sm::MitmProcessInfo &c)
|
||||
: Base(s), client_info(c),
|
||||
read_blank(mitm::ShouldReadBlankCalibrationBinary()),
|
||||
allow_writes(mitm::IsWriteToCalibrationBinaryAllowed())
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *_buffer, size_t size) override;
|
||||
virtual Result Write(s64 offset, const void *_buffer, size_t size) override;
|
||||
};
|
||||
|
||||
}
|
|
@ -328,10 +328,6 @@ namespace ams::settings::fwdbg {
|
|||
/* This is probably undesirable for normal usage. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_hbl_bis_write", "u8!0x0"));
|
||||
|
||||
/* Enable HBL to read the CAL0 partition. */
|
||||
/* This is probably undesirable for normal usage. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_hbl_cal_read", "u8!0x0"));
|
||||
|
||||
/* Controls whether dmnt cheats should be toggled on or off by */
|
||||
/* default. 1 = toggled on by default, 0 = toggled off by default. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "dmnt_cheats_enabled_by_default", "u8!0x1"));
|
||||
|
|
Loading…
Add table
Reference in a new issue