Compare commits

...

2 Commits

Author SHA1 Message Date
Michael Scire
967f14fc7e Implement working prodinfo blanking. 2019-05-23 03:10:32 -07:00
Michael Scire
3bba035b84 Implement blank prodinfo creation.
This is a complete implementation of what PR #532 seeks to do
(thanks @ThatNerdyPikachu). However, it currently blackscreens.

This is because we can't actually mitm settings, because settings
must be able to complete its initialization before the sd card
can be mounted. Thus we end up with a circular dependency and
the console blackscreens. This problem may yet be solvable, but
it's unclear immediately how this dependency might be solved. In any
event, this serves as a reference to use in the event that a solution
arises.
2019-05-10 05:27:30 -07:00
14 changed files with 372 additions and 75 deletions

View File

@@ -282,7 +282,11 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue)
break;
case CONFIGITEM_HAS_RCM_BUG_PATCH:
/* UNOFFICIAL: Gets whether this unit has the RCM bug patched. */
*p_outvalue = (int)(fuse_has_rcm_bug_patch());;
*p_outvalue = (int)(fuse_has_rcm_bug_patch());
break;
case CONFIGITEM_SHOULD_BLANK_PROD_INFO:
/* UNOFFICIAL: Gets whether we should blank out certain parts of PRODINFO. */
*p_outvalue = (int)(exosphere_should_blank_prod_info() != 0);
break;
default:
result = 2;

View File

@@ -45,6 +45,7 @@ typedef enum {
CONFIGITEM_NEEDS_SHUTDOWN = 65002,
CONFIGITEM_EXOSPHERE_VERHASH = 65003,
CONFIGITEM_HAS_RCM_BUG_PATCH = 65004,
CONFIGITEM_SHOULD_BLANK_PROD_INFO = 65005,
} ConfigItem;
#define REBOOT_KIND_NO_REBOOT 0

View File

@@ -83,3 +83,11 @@ unsigned int exosphere_should_disable_usermode_exception_handlers(void) {
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS);
}
unsigned int exosphere_should_blank_prod_info(void) {
if (!g_has_loaded_config) {
generic_panic();
}
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_SHOULD_BLANK_PRODINFO);
}

View File

@@ -39,6 +39,7 @@
#define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u)
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
#define EXOSPHERE_FLAG_SHOULD_BLANK_PRODINFO (1 << 4u)
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
typedef struct {
@@ -54,6 +55,7 @@ unsigned int exosphere_should_perform_620_keygen(void);
unsigned int exosphere_should_override_debugmode_priv(void);
unsigned int exosphere_should_override_debugmode_user(void);
unsigned int exosphere_should_disable_usermode_exception_handlers(void);
unsigned int exosphere_should_blank_prod_info(void);
static inline unsigned int exosphere_get_target_firmware_for_init(void) {
const unsigned int magic = MAILBOX_EXOSPHERE_CONFIG_PHYS.magic;

View File

@@ -28,6 +28,7 @@
#define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u)
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
#define EXOSPHERE_FLAG_SHOULD_BLANK_PRODINFO (1 << 4u)
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
typedef struct {

View File

@@ -199,6 +199,14 @@ static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int ke
fatal_error("[NXBOOT]: Failed to parse BCT.ini!\n");
}
{
FILE *flag = fopen("atmosphere/flags/blank_prodinfo.flag", "rb");
if (flag != NULL) {
exo_cfg.flags |= EXOSPHERE_FLAG_SHOULD_BLANK_PRODINFO;
fclose(flag);
}
}
if ((exo_cfg.target_firmware < ATMOSPHERE_TARGET_FIRMWARE_MIN) || (exo_cfg.target_firmware > ATMOSPHERE_TARGET_FIRMWARE_MAX)) {
fatal_error("[NXBOOT]: Invalid Exosphere target firmware!\n");
}

View File

@@ -112,7 +112,6 @@ class IROStorage : public IStorage {
virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0;
};
class ProxyStorage : public IStorage {
private:
FsStorage *base_storage;
@@ -172,4 +171,4 @@ class ROProxyStorage : public IROStorage {
virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
return fsStorageOperateRange(this->base_storage, operation_type, offset, size, out_range_info);
};
};
};

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include <stratosphere.hpp>
#include "fs_shim.h"
#include "../debug.hpp"
#include "fs_istorage.hpp"
class MemoryStorage : public IStorage {
private:
u8 * const buffer;
const u64 size;
public:
MemoryStorage(void *b, u64 sz) : buffer(static_cast<u8 *>(b)), size(sz) {
/* ... */
}
virtual ~MemoryStorage() {
/* ... */
}
public:
virtual Result Read(void *buffer, size_t size, u64 offset) override {
if (size == 0) {
return ResultSuccess;
}
if (buffer == nullptr) {
return ResultFsNullptrArgument;
}
if (!IStorage::IsRangeValid(offset, size, this->size)) {
return ResultFsOutOfRange;
}
std::memcpy(buffer, this->buffer + offset, size);
return ResultSuccess;
}
virtual Result Write(void *buffer, size_t size, u64 offset) override {
if (size == 0) {
return ResultSuccess;
}
if (buffer == nullptr) {
return ResultFsNullptrArgument;
}
if (!IStorage::IsRangeValid(offset, size, this->size)) {
return ResultFsOutOfRange;
}
std::memcpy(this->buffer + offset, buffer, size);
return ResultSuccess;
}
virtual Result Flush() override {
return ResultSuccess;
}
virtual Result GetSize(u64 *out_size) override {
*out_size = this->size;
return ResultSuccess;
}
virtual Result SetSize(u64 size) override {
return ResultFsUnsupportedOperation;
}
virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
switch (operation_type) {
case 2: /* TODO: OperationType_Invalidate */
return ResultSuccess;
case 3: /* TODO: OperationType_Query */
if (out_range_info == nullptr) {
return ResultFsNullptrArgument;
}
/* N checks for size == sizeof(*out_range_info) here, but that's because their wrapper api is bad. */
std::memset(out_range_info, 0, sizeof(*out_range_info));
return ResultSuccess;
default:
return ResultFsUnsupportedOperation;
}
}
};
class ReadOnlyMemoryStorage : public MemoryStorage {
public:
ReadOnlyMemoryStorage(const void *b, u64 sz) : MemoryStorage(const_cast<void *>(b), sz) {
/* ... */
}
virtual ~ReadOnlyMemoryStorage() {
/* ... */
}
public:
virtual Result Write(void *buffer, size_t size, u64 offset) override {
return ResultFsUnsupportedOperation;
}
};

View File

@@ -27,11 +27,14 @@
#include "fsmitm_boot0storage.hpp"
#include "fsmitm_romstorage.hpp"
#include "fsmitm_layeredrom.hpp"
#include "fsmitm_utils.hpp"
#include "fs_dir_utils.hpp"
#include "fs_save_utils.hpp"
#include "fs_subdirectory_filesystem.hpp"
#include "fs_directory_savedata_filesystem.hpp"
#include "fs_file_storage.hpp"
#include "fs_memory_storage.hpp"
#include "../debug.hpp"
@@ -261,11 +264,14 @@ Result FsMitmService::OpenBisStorage(Out<std::shared_ptr<IStorageInterface>> out
const bool is_sysmodule = TitleIdIsSystem(this->title_id);
const bool has_bis_write_flag = Utils::HasFlag(this->title_id, "bis_write");
const bool has_cal0_read_flag = Utils::HasFlag(this->title_id, "cal_read");
const bool has_blank_cal0_flag = ShouldBlankProdInfo();
if (bis_partition_id == BisStorageId_Boot0) {
storage = std::make_shared<IStorageInterface>(new Boot0Storage(bis_storage, this->title_id));
} else if (bis_partition_id == BisStorageId_Prodinfo) {
/* PRODINFO should *never* be writable. */
if (is_sysmodule || has_cal0_read_flag) {
if (has_blank_cal0_flag) {
storage = std::make_shared<IStorageInterface>(new MitmProxyStorage(std::make_unique<ReadOnlyMemoryStorage>(Utils::GetBlankProdInfoBuffer(), ProdInfoSize), bis_storage.s));
} else if (is_sysmodule || has_cal0_read_flag) {
storage = std::make_shared<IStorageInterface>(new ROProxyStorage(bis_storage));
} else {
/* Do not allow non-sysmodules to read *or* write CAL0. */

View File

@@ -44,7 +44,7 @@ class FsMitmService : public IMitmServiceObject {
bool should_override_contents;
public:
FsMitmService(std::shared_ptr<Service> s, u64 pid) : IMitmServiceObject(s, pid) {
if (Utils::HasSdDisableMitMFlag(this->title_id)) {
if (this->title_id == TitleId_Settings || Utils::HasSdDisableMitMFlag(this->title_id)) {
this->should_override_contents = false;
} else {
this->should_override_contents = (this->title_id >= TitleId_ApplicationStart || Utils::HasSdMitMFlag(this->title_id)) && Utils::HasOverrideButton(this->title_id);
@@ -65,7 +65,7 @@ class FsMitmService : public IMitmServiceObject {
has_launched_qlaunch = true;
}
return has_launched_qlaunch || tid == TitleId_Ns || tid >= TitleId_ApplicationStart || Utils::HasSdMitMFlag(tid);
return has_launched_qlaunch || tid == TitleId_Settings || tid == TitleId_Ns || tid >= TitleId_ApplicationStart || Utils::HasSdMitMFlag(tid);
}
static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx);

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "fs_istorage.hpp"
class MitmProxyStorage : public IStorage {
private:
Service srv_holder;
std::unique_ptr<IStorage> fwd_storage;
public:
MitmProxyStorage(std::unique_ptr<IStorage> st, Service sr = {}) : srv_holder(sr), fwd_storage(std::move(st)) {
/* ... */
}
virtual ~MitmProxyStorage() {
if (serviceIsActive(&srv_holder)) {
serviceClose(&srv_holder);
}
}
public:
virtual Result Read(void *buffer, size_t size, u64 offset) override {
return this->fwd_storage->Read(buffer, size, offset);
}
virtual Result Write(void *buffer, size_t size, u64 offset) override {
return this->fwd_storage->Write(buffer, size, offset);
}
virtual Result Flush() override {
return this->fwd_storage->Flush();
}
virtual Result GetSize(u64 *out_size) override {
return this->fwd_storage->GetSize(out_size);
}
virtual Result SetSize(u64 size) override {
return this->fwd_storage->SetSize(size);
}
virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
return this->fwd_storage->OperateRange(operation_type, offset, size, out_range_info);
}
};

View File

@@ -13,7 +13,7 @@
* 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 <switch.h>
#include <stratosphere.hpp>
#include <atomic>
@@ -60,10 +60,13 @@ static HblOverrideConfig g_hbl_override_config = {
static char g_config_ini_data[0x800];
/* Backup file for CAL0 partition. */
static constexpr size_t ProdinfoSize = 0x8000;
static FsFile g_cal0_file = {0};
static u8 g_cal0_storage_backup[ProdinfoSize];
static u8 g_cal0_backup[ProdinfoSize];
static u8 g_cal0_storage_backup[ProdInfoSize];
static u8 g_cal0_backup[ProdInfoSize];
static HosMutex g_blank_cal0_lock;
static bool g_initialized_blank_cal0 = false;
static u8 g_blank_cal0[ProdInfoSize];
static bool IsHexadecimal(const char *str) {
while (*str) {
@@ -85,37 +88,37 @@ void Utils::InitializeThreadFunc(void *args) {
if (R_FAILED(smGetServiceOriginal(&tmp_hnd, smEncodeName(required_active_services[i])))) {
std::abort();
} else {
svcCloseHandle(tmp_hnd);
svcCloseHandle(tmp_hnd);
}
}
});
/* Mount SD. */
while (R_FAILED(fsMountSdcard(&g_sd_filesystem))) {
svcSleepThread(1000000ULL);
}
/* Back up CAL0, if it's not backed up already. */
fsFsCreateDirectory(&g_sd_filesystem, "/atmosphere/automatic_backups");
{
FsStorage cal0_storage;
if (R_FAILED(fsOpenBisStorage(&cal0_storage, BisStorageId_Prodinfo)) || R_FAILED(fsStorageRead(&cal0_storage, 0, g_cal0_storage_backup, ProdinfoSize))) {
if (R_FAILED(fsOpenBisStorage(&cal0_storage, BisStorageId_Prodinfo)) || R_FAILED(fsStorageRead(&cal0_storage, 0, g_cal0_storage_backup, ProdInfoSize))) {
std::abort();
}
fsStorageClose(&cal0_storage);
char serial_number[0x40] = {0};
memcpy(serial_number, g_cal0_storage_backup + 0x250, 0x18);
char prodinfo_backup_path[FS_MAX_PATH] = {0};
if (strlen(serial_number) > 0) {
snprintf(prodinfo_backup_path, sizeof(prodinfo_backup_path) - 1, "/atmosphere/automatic_backups/%s_PRODINFO.bin", serial_number);
} else {
snprintf(prodinfo_backup_path, sizeof(prodinfo_backup_path) - 1, "/atmosphere/automatic_backups/PRODINFO.bin");
}
fsFsCreateFile(&g_sd_filesystem, prodinfo_backup_path, ProdinfoSize, 0);
fsFsCreateFile(&g_sd_filesystem, prodinfo_backup_path, ProdInfoSize, 0);
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, prodinfo_backup_path, FS_OPEN_READ | FS_OPEN_WRITE, &g_cal0_file))) {
bool has_auto_backup = false;
size_t read = 0;
@@ -124,7 +127,7 @@ void Utils::InitializeThreadFunc(void *args) {
is_cal0_valid &= memcmp(g_cal0_backup, "CAL0", 4) == 0;
is_cal0_valid &= memcmp(g_cal0_backup + 0x250, serial_number, 0x18) == 0;
u32 cal0_size = ((u32 *)g_cal0_backup)[2];
is_cal0_valid &= cal0_size + 0x40 <= ProdinfoSize;
is_cal0_valid &= cal0_size + 0x40 <= ProdInfoSize;
if (is_cal0_valid) {
u8 calc_hash[0x20];
sha256CalculateHash(calc_hash, g_cal0_backup + 0x40, cal0_size);
@@ -132,19 +135,19 @@ void Utils::InitializeThreadFunc(void *args) {
}
has_auto_backup = is_cal0_valid;
}
if (!has_auto_backup) {
fsFileSetSize(&g_cal0_file, ProdinfoSize);
fsFileWrite(&g_cal0_file, 0, g_cal0_storage_backup, ProdinfoSize);
fsFileSetSize(&g_cal0_file, ProdInfoSize);
fsFileWrite(&g_cal0_file, 0, g_cal0_storage_backup, ProdInfoSize);
fsFileFlush(&g_cal0_file);
}
/* NOTE: g_cal0_file is intentionally not closed here. This prevents any other process from opening it. */
memset(g_cal0_storage_backup, 0, sizeof(g_cal0_storage_backup));
memset(g_cal0_backup, 0, sizeof(g_cal0_backup));
std::memset(g_cal0_storage_backup, 0, sizeof(g_cal0_storage_backup));
std::memset(g_cal0_backup, 0, sizeof(g_cal0_backup));
}
}
/* Check for MitM flags. */
FsDir titles_dir;
if (R_SUCCEEDED(fsFsOpenDirectory(&g_sd_filesystem, "/atmosphere/titles", FS_DIROPEN_DIRECTORY, &titles_dir))) {
@@ -172,7 +175,7 @@ void Utils::InitializeThreadFunc(void *args) {
fsFileClose(&f);
}
}
memset(title_path, 0, sizeof(title_path));
strcpy(title_path, "/atmosphere/titles/");
strcat(title_path, dir_entry.name);
@@ -195,25 +198,25 @@ void Utils::InitializeThreadFunc(void *args) {
}
fsDirClose(&titles_dir);
}
Utils::RefreshConfiguration();
/* Initialize set:sys. */
DoWithSmSession([&]() {
if (R_FAILED(setsysInitialize())) {
std::abort();
}
});
/* Signal SD is initialized. */
g_has_initialized = true;
/* Load custom settings configuration. */
SettingsItemManager::LoadConfiguration();
/* Signal to waiters that we are ready. */
g_sd_signal.Signal();
/* Initialize HID. */
while (!g_has_hid_session) {
DoWithSmSession([&]() {
@@ -243,7 +246,7 @@ Result Utils::OpenSdFile(const char *fn, int flags, FsFile *out) {
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
return fsFsOpenFile(&g_sd_filesystem, fn, flags, out);
}
@@ -251,7 +254,7 @@ Result Utils::OpenSdFileForAtmosphere(u64 title_id, const char *fn, int flags, F
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
char path[FS_MAX_PATH];
if (*fn == '/') {
snprintf(path, sizeof(path), "/atmosphere/titles/%016lx%s", title_id, fn);
@@ -265,7 +268,7 @@ Result Utils::OpenRomFSSdFile(u64 title_id, const char *fn, int flags, FsFile *o
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
return OpenRomFSFile(&g_sd_filesystem, title_id, fn, flags, out);
}
@@ -273,7 +276,7 @@ Result Utils::OpenSdDir(const char *path, FsDir *out) {
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
return fsFsOpenDirectory(&g_sd_filesystem, path, FS_DIROPEN_DIRECTORY | FS_DIROPEN_FILE, out);
}
@@ -281,7 +284,7 @@ Result Utils::OpenSdDirForAtmosphere(u64 title_id, const char *path, FsDir *out)
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
char safe_path[FS_MAX_PATH];
if (*path == '/') {
snprintf(safe_path, sizeof(safe_path), "/atmosphere/titles/%016lx%s", title_id, path);
@@ -295,7 +298,7 @@ Result Utils::OpenRomFSSdDir(u64 title_id, const char *path, FsDir *out) {
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
return OpenRomFSDir(&g_sd_filesystem, title_id, path, out);
}
@@ -327,7 +330,7 @@ bool Utils::HasSdRomfsContent(u64 title_id) {
fsFileClose(&data_file);
return true;
}
/* Check for romfs folder with non-zero content. */
FsDir dir;
if (R_FAILED(Utils::OpenRomFSSdDir(title_id, "", &dir))) {
@@ -336,7 +339,7 @@ bool Utils::HasSdRomfsContent(u64 title_id) {
ON_SCOPE_EXIT {
fsDirClose(&dir);
};
FsDirectoryEntry dir_entry;
u64 read_entries;
return R_SUCCEEDED(fsDirRead(&dir, 0, &read_entries, 1, &dir_entry)) && read_entries == 1;
@@ -346,40 +349,40 @@ Result Utils::SaveSdFileForAtmosphere(u64 title_id, const char *fn, void *data,
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
Result rc = ResultSuccess;
char path[FS_MAX_PATH];
if (*fn == '/') {
snprintf(path, sizeof(path), "/atmosphere/titles/%016lx%s", title_id, fn);
} else {
snprintf(path, sizeof(path), "/atmosphere/titles/%016lx/%s", title_id, fn);
}
/* Unconditionally create. */
FsFile f;
fsFsCreateFile(&g_sd_filesystem, path, size, 0);
/* Try to open. */
rc = fsFsOpenFile(&g_sd_filesystem, path, FS_OPEN_READ | FS_OPEN_WRITE, &f);
if (R_FAILED(rc)) {
return rc;
}
/* Always close, if we opened. */
ON_SCOPE_EXIT {
fsFileClose(&f);
};
/* Try to make it big enough. */
rc = fsFileSetSize(&f, size);
if (R_FAILED(rc)) {
return rc;
}
/* Try to write the data. */
rc = fsFileWrite(&f, 0, data, size);
return rc;
}
@@ -395,14 +398,14 @@ bool Utils::HasTitleFlag(u64 tid, const char *flag) {
if (IsSdInitialized()) {
FsFile f;
char flag_path[FS_MAX_PATH];
memset(flag_path, 0, sizeof(flag_path));
snprintf(flag_path, sizeof(flag_path) - 1, "flags/%s.flag", flag);
if (R_SUCCEEDED(OpenSdFileForAtmosphere(tid, flag_path, FS_OPEN_READ, &f))) {
fsFileClose(&f);
return true;
}
/* TODO: Deprecate. */
snprintf(flag_path, sizeof(flag_path) - 1, "%s.flag", flag);
if (R_SUCCEEDED(OpenSdFileForAtmosphere(tid, flag_path, FS_OPEN_READ, &f))) {
@@ -440,7 +443,7 @@ bool Utils::HasSdMitMFlag(u64 tid) {
if (IsHblTid(tid)) {
return true;
}
if (IsSdInitialized()) {
return std::find(g_mitm_flagged_tids.begin(), g_mitm_flagged_tids.end(), tid) != g_mitm_flagged_tids.end();
}
@@ -458,10 +461,10 @@ Result Utils::GetKeysHeld(u64 *keys) {
if (!Utils::IsHidAvailable()) {
return MAKERESULT(Module_Libnx, LibnxError_InitFail_HID);
}
hidScanInput();
*keys = hidKeysHeld(CONTROLLER_P1_AUTO);
return ResultSuccess;
}
@@ -477,21 +480,21 @@ bool Utils::HasOverrideButton(u64 tid) {
/* Disable button override disable for non-applications. */
return true;
}
/* Unconditionally refresh loader.ini contents. */
RefreshConfiguration();
if (IsHblTid(tid) && HasOverrideKey(&g_hbl_override_config.override_key)) {
return true;
}
OverrideKey title_cfg = GetTitleOverrideKey(tid);
return HasOverrideKey(&title_cfg);
}
static OverrideKey ParseOverrideKey(const char *value) {
OverrideKey cfg;
/* Parse on by default. */
if (value[0] == '!') {
cfg.override_by_default = true;
@@ -499,7 +502,7 @@ static OverrideKey ParseOverrideKey(const char *value) {
} else {
cfg.override_by_default = false;
}
/* Parse key combination. */
if (strcasecmp(value, "A") == 0) {
cfg.key_combination = KEY_A;
@@ -540,7 +543,7 @@ static OverrideKey ParseOverrideKey(const char *value) {
} else {
cfg.key_combination = 0;
}
return cfg;
}
@@ -582,7 +585,7 @@ static int FsMitmIniHandler(void *user, const char *section, const char *name, c
static int FsMitmTitleSpecificIniHandler(void *user, const char *section, const char *name, const char *value) {
/* We'll output an override key when relevant. */
OverrideKey *user_cfg = reinterpret_cast<OverrideKey *>(user);
if (strcasecmp(section, "override_config") == 0) {
if (strcasecmp(name, "override_key") == 0) {
*user_cfg = ParseOverrideKey(value);
@@ -596,27 +599,27 @@ static int FsMitmTitleSpecificIniHandler(void *user, const char *section, const
OverrideKey Utils::GetTitleOverrideKey(u64 tid) {
OverrideKey cfg = g_default_override_key;
char path[FS_MAX_PATH+1] = {0};
snprintf(path, FS_MAX_PATH, "/atmosphere/titles/%016lx/config.ini", tid);
snprintf(path, FS_MAX_PATH, "/atmosphere/titles/%016lx/config.ini", tid);
FsFile cfg_file;
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, path, FS_OPEN_READ, &cfg_file))) {
ON_SCOPE_EXIT { fsFileClose(&cfg_file); };
size_t config_file_size = 0x20000;
fsFileGetSize(&cfg_file, &config_file_size);
char *config_buf = reinterpret_cast<char *>(calloc(1, config_file_size + 1));
if (config_buf != NULL) {
ON_SCOPE_EXIT { free(config_buf); };
/* Read title ini contents. */
fsFileRead(&cfg_file, 0, config_buf, config_file_size, &config_file_size);
/* Parse title ini. */
ini_parse_string(config_buf, FsMitmTitleSpecificIniHandler, &cfg);
}
}
return cfg;
}
@@ -625,20 +628,20 @@ void Utils::RefreshConfiguration() {
if (R_FAILED(fsFsOpenFile(&g_sd_filesystem, "/atmosphere/loader.ini", FS_OPEN_READ, &config_file))) {
return;
}
u64 size;
if (R_FAILED(fsFileGetSize(&config_file, &size))) {
return;
}
size = std::min(size, (decltype(size))0x7FF);
/* Read in string. */
std::fill(g_config_ini_data, g_config_ini_data + 0x800, 0);
size_t r_s;
fsFileRead(&config_file, 0, g_config_ini_data, size, &r_s);
fsFileClose(&config_file);
ini_parse_string(g_config_ini_data, FsMitmIniHandler, NULL);
}
@@ -665,3 +668,86 @@ Result Utils::GetSettingsItemBooleanValue(const char *name, const char *key, boo
void Utils::RebootToFatalError(AtmosphereFatalErrorContext *ctx) {
BpcRebootManager::RebootForFatalError(ctx);
}
void Utils::EnsureBlankProdInfo() {
std::scoped_lock<HosMutex> lk(g_blank_cal0_lock);
if (g_initialized_blank_cal0) {
return;
}
/* Read CAL0 in from NAND. */
{
FsStorage cal0_storage;
if (R_FAILED(fsOpenBisStorage(&cal0_storage, BisStorageId_Prodinfo)) || R_FAILED(fsStorageRead(&cal0_storage, 0, g_blank_cal0, ProdInfoSize))) {
std::abort();
}
fsStorageClose(&cal0_storage);
}
if (!IsCal0Valid(g_blank_cal0)) {
std::abort();
}
const char blank_serial[] = "XAW00000000000";
std::memcpy(&g_blank_cal0[0x250], blank_serial, sizeof(blank_serial) - 1);
static constexpr size_t NumErase = 7;
for (size_t i = 0; i < NumErase; i++) {
static constexpr size_t s_erase_offsets[NumErase] = {0xAE0, 0x3AE0, 0x35E1, 0x36E1, 0x2B0, 0x3D70, 0x3FC0};
static constexpr size_t s_erase_sizes[NumErase] = {0x800, 0x130, 0x6, 0x6, 0x180, 0x240, 0x240};
std::memset(&g_blank_cal0[s_erase_offsets[i]], 0, s_erase_sizes[i]);
}
static constexpr size_t NumHashes = 2;
{
static constexpr size_t s_hash_offsets[NumHashes] = {0x12E0, 0x20};
static constexpr size_t s_data_offsets[NumHashes] = {0xAE0, 0x40};
const size_t data_sizes[NumHashes] = {*reinterpret_cast<u32 *>(&g_blank_cal0[0xAD0]), *reinterpret_cast<u32 *>(&g_blank_cal0[0x8])};
for (size_t i = 0; i < NumHashes; i++) {
u8 hash[SHA256_HASH_SIZE];
sha256CalculateHash(hash, &g_blank_cal0[s_data_offsets[i]], data_sizes[i]);
std::memcpy(&g_blank_cal0[s_hash_offsets[i]], hash, sizeof(hash));
}
}
g_initialized_blank_cal0 = true;
}
bool Utils::IsCal0Valid(const u8 *cal0) {
bool is_cal0_valid = true;
is_cal0_valid &= std::memcmp(cal0, "CAL0", 4) == 0;
u32 cal0_size = ((u32 *)cal0)[2];
is_cal0_valid &= cal0_size + 0x40 <= ProdInfoSize;
if (is_cal0_valid) {
u8 calc_hash[0x20];
sha256CalculateHash(calc_hash, cal0 + 0x40, cal0_size);
is_cal0_valid &= std::memcmp(calc_hash, cal0 + 0x20, sizeof(calc_hash)) == 0;
}
return is_cal0_valid;
}
u16 Utils::GetCrc16(const void *data, size_t size) {
static constexpr u16 s_crc_table[0x10] = {
0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401,
0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
};
if (data == nullptr) {
std::abort();
}
u16 crc16 = 0x55AA;
const u8 *data_u8 = reinterpret_cast<const u8 *>(data);
for (size_t i = 0; i < size; i++) {
crc16 = (crc16 >> 4) ^ (s_crc_table[crc16 & 0xF]) ^ (s_crc_table[data_u8[i] & 0xF]);
crc16 = (crc16 >> 4) ^ (s_crc_table[crc16 & 0xF]) ^ (s_crc_table[(data_u8[i] >> 4) & 0xF]);
}
return crc16;
}
const void *Utils::GetBlankProdInfoBuffer() {
EnsureBlankProdInfo();
return g_blank_cal0;
}

View File

@@ -37,6 +37,8 @@ enum BisStorageId : u32 {
BisStorageId_SystemProperPartition = 33,
};
static constexpr size_t ProdInfoSize = 0x8000;
struct OverrideKey {
u64 key_combination;
bool override_by_default;
@@ -61,6 +63,8 @@ class Utils {
static bool HasSdRomfsContent(u64 title_id);
static const void *GetBlankProdInfoBuffer();
/* Delayed Initialization + MitM detection. */
static void InitializeThreadFunc(void *args);
@@ -87,9 +91,15 @@ class Utils {
static Result GetSettingsItemValue(const char *name, const char *key, void *out, size_t max_size, u64 *out_size);
static Result GetSettingsItemBooleanValue(const char *name, const char *key, bool *out);
/* CRC util. */
static u16 GetCrc16(const void *data, size_t size);
/* Error occurred. */
static void RebootToFatalError(AtmosphereFatalErrorContext *ctx);
private:
static void RefreshConfiguration();
static void EnsureBlankProdInfo();
static bool IsCal0Valid(const u8 *cal0);
};