Merge branch 'bis_protect'

This commit is contained in:
Michael Scire 2018-11-29 12:48:20 -08:00
commit 7bc95f35d7
23 changed files with 1012 additions and 49 deletions

View file

@ -48,7 +48,8 @@ dist: all
cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036/exefs.nsp
cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034/exefs.nsp
cp stratosphere/set_mitm/set_mitm.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/exefs.nsp
touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/boot2.flag
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags
touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags/boot2.flag
cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../;
rm -r atmosphere-$(AMSVER)
mkdir out

View file

@ -61,7 +61,6 @@ class IStorageInterface : public IServiceObject {
};
virtual Result Write(InBuffer<u8, BufferType_Type1> buffer, u64 offset, u64 size) final {
return this->base_storage->Write(buffer.buffer, std::min(buffer.num_elements, size), offset);
};
virtual Result Flush() final {
return this->base_storage->Flush();
@ -108,3 +107,65 @@ class IROStorage : public IStorage {
virtual Result GetSize(u64 *out_size) = 0;
virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0;
};
class ProxyStorage : public IStorage {
private:
FsStorage *base_storage;
public:
ProxyStorage(FsStorage *s) : base_storage(s) {
/* ... */
};
ProxyStorage(FsStorage s) {
this->base_storage = new FsStorage(s);
};
virtual ~ProxyStorage() {
fsStorageClose(base_storage);
delete base_storage;
};
public:
virtual Result Read(void *buffer, size_t size, u64 offset) override {
return fsStorageRead(this->base_storage, offset, buffer, size);
};
virtual Result Write(void *buffer, size_t size, u64 offset) override {
return fsStorageWrite(this->base_storage, offset, buffer, size);
};
virtual Result Flush() override {
return fsStorageFlush(this->base_storage);
};
virtual Result GetSize(u64 *out_size) override {
return fsStorageGetSize(this->base_storage, out_size);
};
virtual Result SetSize(u64 size) override {
return fsStorageSetSize(this->base_storage, size);
};
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);
};
};
class ROProxyStorage : public IROStorage {
private:
FsStorage *base_storage;
public:
ROProxyStorage(FsStorage *s) : base_storage(s) {
/* ... */
};
ROProxyStorage(FsStorage s) {
this->base_storage = new FsStorage(s);
};
virtual ~ROProxyStorage() {
fsStorageClose(base_storage);
delete base_storage;
};
public:
virtual Result Read(void *buffer, size_t size, u64 offset) override {
return fsStorageRead(this->base_storage, offset, buffer, size);
};
virtual Result GetSize(u64 *out_size) override {
return fsStorageGetSize(this->base_storage, out_size);
};
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

@ -18,6 +18,44 @@
#include "fs_shim.h"
/* Missing fsp-srv commands. */
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, u32 PartitionId) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u32 PartitionId;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 12;
raw->PartitionId = PartitionId;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreateSubservice(&out->s, s, &r, 0);
}
}
return rc;
}
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) {
IpcCommand c;
ipcInitialize(&c);

View file

@ -17,6 +17,7 @@ typedef struct {
} FsRangeInfo;
/* Missing fsp-srv commands. */
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, u32 PartitionId);
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out);
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out);

View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2018 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 <switch.h>
#include <cstring>
#include <stratosphere.hpp>
#include "fsmitm_boot0storage.hpp"
static HosMutex g_boot0_mutex;
static u8 g_boot0_bct_buffer[Boot0Storage::BctEndOffset];
bool Boot0Storage::CanModifyBctPubks() {
return this->title_id != 0x010000000000001FULL;
}
Result Boot0Storage::Read(void *_buffer, size_t size, u64 offset) {
std::scoped_lock<HosMutex> lk{g_boot0_mutex};
return Base::Read(_buffer, size, offset);
}
Result Boot0Storage::Write(void *_buffer, size_t size, u64 offset) {
std::scoped_lock<HosMutex> lk{g_boot0_mutex};
Result rc = 0;
u8 *buffer = static_cast<u8 *>(_buffer);
/* Protect the keyblob region from writes. */
if (offset <= EksStart) {
if (offset + size < EksStart) {
/* Fall through, no need to do anything here. */
} else {
if (offset + size < EksEnd) {
/* Adjust size to avoid writing end of data. */
size = EksStart - offset;
} else {
/* Perform portion of write falling past end of keyblobs. */
const u64 diff = EksEnd - offset;
if (R_FAILED((rc = Base::Write(buffer + diff, size - diff, EksEnd)))) {
return rc;
}
/* Adjust size to avoid writing end of data. */
size = EksStart - offset;
}
}
} else {
if (offset < EksEnd) {
if (offset + size < EksEnd) {
/* Ignore writes falling strictly within the region. */
return 0;
} else {
/* Only write past the end of the keyblob region. */
buffer = buffer + (EksEnd - offset);
size -= (EksEnd - offset);
offset = EksEnd;
}
} else {
/* Fall through, no need to do anything here. */
}
}
if (size == 0) {
return 0;
}
/* We care about protecting autorcm from NS. */
if (CanModifyBctPubks() || offset >= BctEndOffset || (offset + BctSize >= BctEndOffset && offset % BctSize >= BctPubkEnd)) {
return Base::Write(buffer, size, offset);
}
/* First, let's deal with the data past the end. */
if (offset + size >= BctEndOffset) {
const u64 diff = BctEndOffset - offset;
if (R_FAILED((rc = ProxyStorage::Write(buffer + diff, size - diff, BctEndOffset)))) {
return rc;
}
size -= diff;
}
/* Read in the current BCT region. */
if (R_FAILED((rc = ProxyStorage::Read(g_boot0_bct_buffer, BctEndOffset, 0)))) {
return rc;
}
/* Update the bct buffer. */
for (u64 cur_ofs = offset; cur_ofs < BctEndOffset && cur_ofs < offset + size; cur_ofs++) {
const u64 cur_bct_rel_ofs = cur_ofs % BctSize;
if (cur_bct_rel_ofs < BctPubkStart || BctPubkEnd <= cur_bct_rel_ofs) {
g_boot0_bct_buffer[cur_ofs] = buffer[cur_ofs - offset];
}
}
return ProxyStorage::Write(g_boot0_bct_buffer, BctEndOffset, 0);
}

View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2018 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 <cstring>
#include <stratosphere.hpp>
#include "fs_istorage.hpp"
/* Represents a sectored storage. */
template<u64 SectorSize>
class SectoredProxyStorage : public ProxyStorage {
private:
u64 cur_seek = 0;
u64 cur_sector = 0;
u64 cur_sector_ofs = 0;
u8 sector_buf[SectorSize];
private:
void Seek(u64 offset) {
this->cur_sector_ofs = offset % SectorSize;
this->cur_seek = offset - this->cur_sector_ofs;
}
public:
SectoredProxyStorage(FsStorage *s) : ProxyStorage(s) { }
SectoredProxyStorage(FsStorage s) : ProxyStorage(s) { }
public:
virtual Result Read(void *_buffer, size_t size, u64 offset) override {
Result rc = 0;
u8 *buffer = static_cast<u8 *>(_buffer);
this->Seek(offset);
if (this->cur_sector_ofs == 0 && size % SectorSize == 0) {
/* Fast case. */
return ProxyStorage::Read(buffer, size, offset);
}
if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, this->cur_seek)))) {
return rc;
}
if (size + this->cur_sector_ofs <= SectorSize) {
memcpy(buffer, sector_buf + this->cur_sector_ofs, size);
} else {
/* Leaving the sector... */
size_t ofs = SectorSize - this->cur_sector_ofs;
memcpy(buffer, sector_buf + this->cur_sector_ofs, ofs);
size -= ofs;
/* We're guaranteed alignment, here. */
const size_t aligned_remaining_size = size - (size % SectorSize);
if (aligned_remaining_size) {
if (R_FAILED((rc = ProxyStorage::Read(buffer + ofs, aligned_remaining_size, offset + ofs)))) {
return rc;
}
ofs += aligned_remaining_size;
size -= aligned_remaining_size;
}
/* Read any leftover data. */
if (size) {
if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, offset + ofs)))) {
return rc;
}
memcpy(buffer + ofs, sector_buf, size);
}
}
return rc;
};
virtual Result Write(void *_buffer, size_t size, u64 offset) override {
Result rc = 0;
u8 *buffer = static_cast<u8 *>(_buffer);
this->Seek(offset);
if (this->cur_sector_ofs == 0 && size % SectorSize == 0) {
/* Fast case. */
return ProxyStorage::Write(buffer, size, offset);
}
if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, this->cur_seek)))) {
return rc;
}
if (size + this->cur_sector_ofs <= SectorSize) {
memcpy(this->sector_buf + this->cur_sector_ofs, buffer, size);
rc = ProxyStorage::Write(this->sector_buf, SectorSize, this->cur_seek);
} else {
/* Leaving the sector... */
size_t ofs = SectorSize - this->cur_sector_ofs;
memcpy(this->sector_buf + this->cur_sector_ofs, buffer, ofs);
if (R_FAILED((rc = ProxyStorage::Write(this->sector_buf, ofs, this->cur_seek)))) {
return rc;
}
size -= ofs;
/* We're guaranteed alignment, here. */
const size_t aligned_remaining_size = size - (size % SectorSize);
if (aligned_remaining_size) {
if (R_FAILED((rc = ProxyStorage::Write(buffer + ofs, aligned_remaining_size, offset + ofs)))) {
return rc;
}
ofs += aligned_remaining_size;
size -= aligned_remaining_size;
}
/* Write any leftover data. */
if (size) {
if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, offset + ofs)))) {
return rc;
}
memcpy(this->sector_buf, buffer + ofs, size);
rc = ProxyStorage::Write(this->sector_buf, SectorSize, this->cur_seek);
}
}
return rc;
};
};
/* Represents an RCM-preserving BOOT0 partition. */
class Boot0Storage : public SectoredProxyStorage<0x200> {
using Base = SectoredProxyStorage<0x200>;
public:
static constexpr u64 BctEndOffset = 0xFC000;
static constexpr u64 BctSize = 0x4000;
static constexpr u64 BctPubkStart = 0x210;
static constexpr u64 BctPubkSize = 0x100;
static constexpr u64 BctPubkEnd = BctPubkStart + BctPubkSize;
static constexpr u64 EksStart = 0x180000;
static constexpr u64 EksSize = 0x4000;
static constexpr u64 EksEnd = EksStart + EksSize;
private:
u64 title_id;
private:
bool CanModifyBctPubks();
public:
Boot0Storage(FsStorage *s, u64 t) : Base(s), title_id(t) { }
Boot0Storage(FsStorage s, u64 t) : Base(s), title_id(t) { }
public:
virtual Result Read(void *_buffer, size_t size, u64 offset) override;
virtual Result Write(void *_buffer, size_t size, u64 offset) override;
};

View file

@ -78,7 +78,7 @@ void __appExit(void) {
struct FsMitmManagerOptions {
static const size_t PointerBufferSize = 0x800;
static const size_t MaxDomains = 0x10;
static const size_t MaxDomains = 0x40;
static const size_t MaxDomainObjects = 0x4000;
};
using FsMitmManager = WaitableManager<FsMitmManagerOptions>;

View file

@ -31,7 +31,7 @@ class RomFileStorage : public IROStorage {
RomFileStorage(FsFile f) {
this->base_file = new FsFile(f);
};
~RomFileStorage() {
virtual ~RomFileStorage() {
fsFileClose(base_file);
delete base_file;
};
@ -54,30 +54,4 @@ class RomFileStorage : public IROStorage {
};
/* Represents a RomFS accessed via some IStorage. */
class RomInterfaceStorage : public IROStorage {
private:
FsStorage *base_storage;
public:
RomInterfaceStorage(FsStorage *s) : base_storage(s) {
/* ... */
};
RomInterfaceStorage(FsStorage s) {
this->base_storage = new FsStorage(s);
};
~RomInterfaceStorage() {
fsStorageClose(base_storage);
delete base_storage;
};
public:
Result Read(void *buffer, size_t size, u64 offset) override {
return fsStorageRead(this->base_storage, offset, buffer, size);
};
Result GetSize(u64 *out_size) override {
/* TODO: Merge into libnx? */
return fsStorageGetSize(this->base_storage, out_size);
};
Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
/* TODO: Merge into libnx? */
return fsStorageOperateRange(this->base_storage, operation_type, offset, size, out_range_info);
};
};
using RomInterfaceStorage = ROProxyStorage;

View file

@ -24,6 +24,7 @@
#include "fs_shim.h"
#include "fsmitm_utils.hpp"
#include "fsmitm_boot0storage.hpp"
#include "fsmitm_romstorage.hpp"
#include "fsmitm_layeredrom.hpp"
@ -79,12 +80,67 @@ void FsMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx
}
}
/* Gate access to the BIS partitions. */
Result FsMitmService::OpenBisStorage(Out<std::shared_ptr<IStorageInterface>> out_storage, u32 bis_partition_id) {
std::shared_ptr<IStorageInterface> storage = nullptr;
u32 out_domain_id = 0;
Result rc = 0;
ON_SCOPE_EXIT {
if (R_SUCCEEDED(rc)) {
out_storage.SetValue(std::move(storage));
if (out_storage.IsDomain()) {
out_storage.ChangeObjectId(out_domain_id);
}
}
};
{
FsStorage bis_storage;
rc = fsOpenBisStorageFwd(this->forward_service.get(), &bis_storage, bis_partition_id);
if (R_SUCCEEDED(rc)) {
const bool is_sysmodule = this->title_id < 0x0100000000001000;
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");
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) {
storage = std::make_shared<IStorageInterface>(new ROProxyStorage(bis_storage));
} else {
/* Do not allow non-sysmodules to read *or* write CAL0. */
fsStorageClose(&bis_storage);
return 0x320002;
}
} else {
if (is_sysmodule || has_bis_write_flag) {
/* Sysmodules should still be allowed to read and write. */
storage = std::make_shared<IStorageInterface>(new ProxyStorage(bis_storage));
} else {
/* Non-sysmodules should be allowed to read. */
storage = std::make_shared<IStorageInterface>(new ROProxyStorage(bis_storage));
}
}
if (out_storage.IsDomain()) {
out_domain_id = bis_storage.s.object_id;
}
}
}
return rc;
}
/* Add redirection for RomFS to the SD card. */
Result FsMitmService::OpenDataStorageByCurrentProcess(Out<std::shared_ptr<IStorageInterface>> out_storage) {
std::shared_ptr<IStorageInterface> storage = nullptr;
u32 out_domain_id = 0;
Result rc = 0;
if (!this->should_override_contents) {
return RESULT_FORWARD_TO_SESSION;
}
bool has_cache = StorageCacheGetEntry(this->title_id, &storage);
ON_SCOPE_EXIT {
@ -149,6 +205,10 @@ Result FsMitmService::OpenDataStorageByDataId(Out<std::shared_ptr<IStorageInterf
FsStorage data_storage;
FsFile data_file;
if (!this->should_override_contents) {
return RESULT_FORWARD_TO_SESSION;
}
std::shared_ptr<IStorageInterface> storage = nullptr;
u32 out_domain_id = 0;
Result rc = 0;

View file

@ -22,6 +22,7 @@
enum FspSrvCmd : u32 {
FspSrvCmd_SetCurrentProcess = 1,
FspSrvCmd_OpenBisStorage = 12,
FspSrvCmd_OpenDataStorageByCurrentProcess = 200,
FspSrvCmd_OpenDataStorageByDataId = 202,
};
@ -29,26 +30,43 @@ enum FspSrvCmd : u32 {
class FsMitmService : public IMitmServiceObject {
private:
bool has_initialized = false;
bool should_override_contents;
public:
FsMitmService(std::shared_ptr<Service> s) : IMitmServiceObject(s) {
/* ... */
FsMitmService(std::shared_ptr<Service> s, u64 pid) : IMitmServiceObject(s, pid) {
if (Utils::HasSdDisableMitMFlag(this->title_id)) {
this->should_override_contents = false;
} else {
this->should_override_contents = (this->title_id >= 0x0100000000010000ULL || Utils::HasSdMitMFlag(this->title_id)) && Utils::HasOverrideButton(this->title_id);
}
}
static bool ShouldMitm(u64 pid, u64 tid) {
if (Utils::HasSdDisableMitMFlag(tid)) {
/* Don't Mitm KIPs */
if (pid < 0x50) {
return false;
}
return (tid >= 0x0100000000010000ULL || Utils::HasSdMitMFlag(tid)) && Utils::HasOverrideButton(tid);
static std::atomic_bool has_launched_qlaunch = false;
/* TODO: intercepting everything seems to cause issues with sleep mode, for some reason. */
/* Figure out why, and address it. */
if (tid == 0x0100000000001000ULL) {
has_launched_qlaunch = true;
}
return has_launched_qlaunch || tid == 0x010000000000001FULL || tid >= 0x0100000000010000ULL || Utils::HasSdMitMFlag(tid);
}
static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx);
protected:
/* Overridden commands. */
Result OpenBisStorage(Out<std::shared_ptr<IStorageInterface>> out, u32 bis_partition_id);
Result OpenDataStorageByCurrentProcess(Out<std::shared_ptr<IStorageInterface>> out);
Result OpenDataStorageByDataId(Out<std::shared_ptr<IStorageInterface>> out, u64 data_id, u8 storage_id);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MakeServiceCommandMeta<FspSrvCmd_OpenBisStorage, &FsMitmService::OpenBisStorage>(),
MakeServiceCommandMeta<FspSrvCmd_OpenDataStorageByCurrentProcess, &FsMitmService::OpenDataStorageByCurrentProcess>(),
MakeServiceCommandMeta<FspSrvCmd_OpenDataStorageByDataId, &FsMitmService::OpenDataStorageByDataId>(),
};

View file

@ -23,6 +23,7 @@
#include "debug.hpp"
#include "fsmitm_utils.hpp"
#include "ini.h"
#include "sha256.h"
static FsFileSystem g_sd_filesystem = {0};
static std::vector<u64> g_mitm_flagged_tids;
@ -31,11 +32,18 @@ static std::atomic_bool g_has_initialized = false;
static std::atomic_bool g_has_hid_session = false;
static u64 g_override_key_combination = KEY_R;
static u64 g_override_hbl_tid = 0x010000000000100DULL;
static bool g_override_by_default = true;
/* Static buffer for loader.ini contents at runtime. */
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 bool IsHexadecimal(const char *str) {
while (*str) {
if (isxdigit(*str)) {
@ -64,6 +72,60 @@ void Utils::InitializeSdThreadFunc(void *args) {
svcSleepThread(1000ULL);
}
/* 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))) {
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);
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;
if (R_SUCCEEDED(fsFileRead(&g_cal0_file, 0, g_cal0_backup, sizeof(g_cal0_backup), &read)) && read == sizeof(g_cal0_backup)) {
bool is_cal0_valid = true;
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;
if (is_cal0_valid) {
struct sha256_state sha_ctx;
u8 calc_hash[0x20];
sha256_init(&sha_ctx);
sha256_update(&sha_ctx, g_cal0_backup + 0x40, cal0_size);
sha256_finalize(&sha_ctx);
sha256_finish(&sha_ctx, calc_hash);
is_cal0_valid &= memcmp(calc_hash, g_cal0_backup + 0x20, sizeof(calc_hash)) == 0;
}
has_auto_backup = is_cal0_valid;
}
if (!has_auto_backup) {
fsFileSetSize(&g_cal0_file, ProdinfoSize);
fsFileWrite(&g_cal0_file, 0, g_cal0_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));
}
}
/* Check for MitM flags. */
FsDir titles_dir;
if (R_SUCCEEDED(fsFsOpenDirectory(&g_sd_filesystem, "/atmosphere/titles", FS_DIROPEN_DIRECTORY, &titles_dir))) {
@ -76,12 +138,31 @@ void Utils::InitializeSdThreadFunc(void *args) {
char title_path[FS_MAX_PATH] = {0};
strcpy(title_path, "/atmosphere/titles/");
strcat(title_path, dir_entry.name);
strcat(title_path, "/flags/fsmitm.flag");
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) {
g_mitm_flagged_tids.push_back(title_id);
fsFileClose(&f);
} else {
/* TODO: Deprecate. */
memset(title_path, 0, sizeof(title_path));
strcpy(title_path, "/atmosphere/titles/");
strcat(title_path, dir_entry.name);
strcat(title_path, "/fsmitm.flag");
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) {
g_mitm_flagged_tids.push_back(title_id);
fsFileClose(&f);
}
}
memset(title_path, 0, sizeof(title_path));
strcpy(title_path, "/atmosphere/titles/");
strcat(title_path, dir_entry.name);
strcat(title_path, "/flags/fsmitm_disable.flag");
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) {
g_disable_mitm_flagged_tids.push_back(title_id);
fsFileClose(&f);
} else {
/* TODO: Deprecate. */
memset(title_path, 0, sizeof(title_path));
strcpy(title_path, "/atmosphere/titles/");
strcat(title_path, dir_entry.name);
@ -92,6 +173,7 @@ void Utils::InitializeSdThreadFunc(void *args) {
}
}
}
}
fsDirClose(&titles_dir);
}
@ -264,7 +346,56 @@ Result Utils::SaveSdFileForAtmosphere(u64 title_id, const char *fn, void *data,
return rc;
}
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 (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 (OpenSdFileForAtmosphere(tid, flag_path, FS_OPEN_READ, &f)) {
fsFileClose(&f);
return true;
}
}
return false;
}
bool Utils::HasGlobalFlag(const char *flag) {
if (IsSdInitialized()) {
FsFile f;
char flag_path[FS_MAX_PATH] = {0};
snprintf(flag_path, sizeof(flag_path), "/atmosphere/flags/%s.flag", flag);
if (fsFsOpenFile(&g_sd_filesystem, flag_path, FS_OPEN_READ, &f)) {
fsFileClose(&f);
return true;
}
}
return false;
}
bool Utils::HasHblFlag(const char *flag) {
char hbl_flag[FS_MAX_PATH] = {0};
snprintf(hbl_flag, sizeof(hbl_flag), "hbl_%s", flag);
return HasGlobalFlag(hbl_flag);
}
bool Utils::HasFlag(u64 tid, const char *flag) {
return HasTitleFlag(tid, flag) || (tid == g_override_hbl_tid && HasHblFlag(flag));
}
bool Utils::HasSdMitMFlag(u64 tid) {
if (tid == g_override_hbl_tid) {
return true;
}
if (IsSdInitialized()) {
return std::find(g_mitm_flagged_tids.begin(), g_mitm_flagged_tids.end(), tid) != g_mitm_flagged_tids.end();
}
@ -306,7 +437,12 @@ bool Utils::HasOverrideButton(u64 tid) {
static int FsMitMIniHandler(void *user, const char *section, const char *name, const char *value) {
/* Taken and modified, with love, from Rajkosto's implementation. */
if (strcasecmp(section, "config") == 0) {
if (strcasecmp(name, "override_key") == 0) {
if (strcasecmp(name, "hbl_tid") == 0) {
u64 override_tid = strtoul(value, NULL, 16);
if (override_tid != 0) {
g_override_hbl_tid = override_tid;
}
} else if (strcasecmp(name, "override_key") == 0) {
if (value[0] == '!') {
g_override_by_default = true;
value++;

View file

@ -18,6 +18,25 @@
#include <switch.h>
#include <stratosphere.hpp>
enum BisStorageId : u32 {
BisStorageId_Boot0 = 0,
BisStorageId_Boot1 = 10,
BisStorageId_RawNand = 20,
BisStorageId_BcPkg2_1 = 21,
BisStorageId_BcPkg2_2 = 22,
BisStorageId_BcPkg2_3 = 23,
BisStorageId_BcPkg2_4 = 24,
BisStorageId_BcPkg2_5 = 25,
BisStorageId_BcPkg2_6 = 26,
BisStorageId_Prodinfo = 27,
BisStorageId_ProdinfoF = 28,
BisStorageId_Safe = 29,
BisStorageId_User = 30,
BisStorageId_System = 31,
BisStorageId_SystemProperEncryption = 32,
BisStorageId_SystemProperPartition = 33,
};
class Utils {
public:
static bool IsSdInitialized();
@ -38,6 +57,12 @@ class Utils {
/* SD card Initialization + MitM detection. */
static void InitializeSdThreadFunc(void *args);
static bool HasTitleFlag(u64 tid, const char *flag);
static bool HasHblFlag(const char *flag);
static bool HasGlobalFlag(const char *flag);
static bool HasFlag(u64 tid, const char *flag);
static bool HasSdMitMFlag(u64 tid);
static bool HasSdDisableMitMFlag(u64 tid);

View file

@ -0,0 +1,113 @@
/* Based on linux source code */
/*
* sha256_base.h - core logic for SHA-256 implementations
*
* Copyright (C) 2015 Linaro Ltd <ard.biesheuvel@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include "sha256.h"
#define unlikely(x) __builtin_expect(!!(x), 0)
void sha256_block_data_order (uint32_t *ctx, const void *in, size_t num);
int sha256_init(struct sha256_state *sctx)
{
sctx->state[0] = SHA256_H0;
sctx->state[1] = SHA256_H1;
sctx->state[2] = SHA256_H2;
sctx->state[3] = SHA256_H3;
sctx->state[4] = SHA256_H4;
sctx->state[5] = SHA256_H5;
sctx->state[6] = SHA256_H6;
sctx->state[7] = SHA256_H7;
sctx->count = 0;
return 0;
}
int sha256_update(struct sha256_state *sctx,
const void *data,
size_t len)
{
const u8 *data8 = (const u8 *)data;
unsigned int len32 = (unsigned int)len;
unsigned int partial = sctx->count % SHA256_BLOCK_SIZE;
sctx->count += len32;
if (unlikely((partial + len32) >= SHA256_BLOCK_SIZE)) {
int blocks;
if (partial) {
int p = SHA256_BLOCK_SIZE - partial;
memcpy(sctx->buf + partial, data8, p);
data8 += p;
len32 -= p;
sha256_block_data_order(sctx->state, sctx->buf, 1);
}
blocks = len32 / SHA256_BLOCK_SIZE;
len32 %= SHA256_BLOCK_SIZE;
if (blocks) {
sha256_block_data_order(sctx->state, data8, blocks);
data8 += blocks * SHA256_BLOCK_SIZE;
}
partial = 0;
}
if (len32)
memcpy(sctx->buf + partial, data8, len32);
return 0;
}
int sha256_finalize(struct sha256_state *sctx)
{
const int bit_offset = SHA256_BLOCK_SIZE - sizeof(u64);
u64 *bits = (u64 *)(sctx->buf + bit_offset);
unsigned int partial = sctx->count % SHA256_BLOCK_SIZE;
sctx->buf[partial++] = 0x80;
if (partial > bit_offset) {
memset(sctx->buf + partial, 0x0, SHA256_BLOCK_SIZE - partial);
partial = 0;
sha256_block_data_order(sctx->state, sctx->buf, 1);
}
memset(sctx->buf + partial, 0x0, bit_offset - partial);
*bits = __builtin_bswap64(sctx->count << 3);
sha256_block_data_order(sctx->state, sctx->buf, 1);
return 0;
}
int sha256_finish(struct sha256_state *sctx, void *out)
{
unsigned int digest_size = 32;
u32 *digest = (u32 *)out;
int i;
// Switch: misalignment shouldn't be a problem here...
for (i = 0; digest_size > 0; i++, digest_size -= sizeof(u32))
*digest++ = __builtin_bswap32(sctx->state[i]);
*sctx = (struct sha256_state){};
return 0;
}
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,36 @@
#pragma once
/* Based on linux source code */
#ifdef __cplusplus
extern "C" {
#endif
#include <switch/types.h>
#define SHA256_DIGEST_SIZE 32
#define SHA256_BLOCK_SIZE 64
#define SHA256_H0 0x6a09e667UL
#define SHA256_H1 0xbb67ae85UL
#define SHA256_H2 0x3c6ef372UL
#define SHA256_H3 0xa54ff53aUL
#define SHA256_H4 0x510e527fUL
#define SHA256_H5 0x9b05688cUL
#define SHA256_H6 0x1f83d9abUL
#define SHA256_H7 0x5be0cd19UL
struct sha256_state {
u32 state[SHA256_DIGEST_SIZE / 4];
u64 count;
u8 buf[SHA256_BLOCK_SIZE];
};
int sha256_init(struct sha256_state *sctx);
int sha256_update(struct sha256_state *sctx, const void *data, size_t len);
int sha256_finalize(struct sha256_state *sctx);
int sha256_finish(struct sha256_state *sctx, void *out);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,163 @@
.section .text.sha256_armv8, "ax", %progbits
.align 5
.arch armv8-a+crypto
# SHA256 assembly implementation for ARMv8 AArch64 (based on linux source code)
.global sha256_block_data_order
.type sha256_block_data_order,%function
sha256_block_data_order:
.Lsha256prolog:
stp x29, x30, [sp,#-64]!
mov x29, sp
adr x3, .LKConstant256
str q8, [sp, #16]
ld1 {v16.4s-v19.4s}, [x3], #64
ld1 {v0.4s}, [x0], #16
ld1 {v20.4s-v23.4s}, [x3], #64
add x2, x1, x2, lsl #6
ld1 {v1.4s}, [x0]
ld1 {v24.4s-v27.4s}, [x3], #64
sub x0, x0, #16
str q9, [sp, #32]
str q10, [sp, #48]
ld1 {v28.4s-v31.4s}, [x3], #64
.Lsha256loop:
ld1 {v5.16b-v8.16b}, [x1], #64
mov v2.16b, v0.16b
mov v3.16b, v1.16b
rev32 v5.16b, v5.16b
rev32 v6.16b, v6.16b
add v9.4s, v5.4s, v16.4s
rev32 v7.16b, v7.16b
add v10.4s, v6.4s, v17.4s
mov v4.16b, v2.16b
sha256h q2, q3, v9.4s
sha256h2 q3, q4, v9.4s
sha256su0 v5.4s, v6.4s
rev32 v8.16b, v8.16b
add v9.4s, v7.4s, v18.4s
mov v4.16b, v2.16b
sha256h q2, q3, v10.4s
sha256h2 q3, q4, v10.4s
sha256su0 v6.4s, v7.4s
sha256su1 v5.4s, v7.4s, v8.4s
add v10.4s, v8.4s, v19.4s
mov v4.16b, v2.16b
sha256h q2, q3, v9.4s
sha256h2 q3, q4, v9.4s
sha256su0 v7.4s, v8.4s
sha256su1 v6.4s, v8.4s, v5.4s
add v9.4s, v5.4s, v20.4s
mov v4.16b, v2.16b
sha256h q2, q3, v10.4s
sha256h2 q3, q4, v10.4s
sha256su0 v8.4s, v5.4s
sha256su1 v7.4s, v5.4s, v6.4s
add v10.4s, v6.4s, v21.4s
mov v4.16b, v2.16b
sha256h q2, q3, v9.4s
sha256h2 q3, q4, v9.4s
sha256su0 v5.4s, v6.4s
sha256su1 v8.4s, v6.4s, v7.4s
add v9.4s, v7.4s, v22.4s
mov v4.16b, v2.16b
sha256h q2, q3, v10.4s
sha256h2 q3, q4, v10.4s
sha256su0 v6.4s, v7.4s
sha256su1 v5.4s, v7.4s, v8.4s
add v10.4s, v8.4s, v23.4s
mov v4.16b, v2.16b
sha256h q2, q3, v9.4s
sha256h2 q3, q4, v9.4s
sha256su0 v7.4s, v8.4s
sha256su1 v6.4s, v8.4s, v5.4s
add v9.4s, v5.4s, v24.4s
mov v4.16b, v2.16b
sha256h q2, q3, v10.4s
sha256h2 q3, q4, v10.4s
sha256su0 v8.4s, v5.4s
sha256su1 v7.4s, v5.4s, v6.4s
add v10.4s, v6.4s, v25.4s
mov v4.16b, v2.16b
sha256h q2, q3, v9.4s
sha256h2 q3, q4, v9.4s
sha256su0 v5.4s, v6.4s
sha256su1 v8.4s, v6.4s, v7.4s
add v9.4s, v7.4s, v26.4s
mov v4.16b, v2.16b
sha256h q2, q3, v10.4s
sha256h2 q3, q4, v10.4s
sha256su0 v6.4s, v7.4s
sha256su1 v5.4s, v7.4s, v8.4s
add v10.4s, v8.4s, v27.4s
mov v4.16b, v2.16b
sha256h q2, q3, v9.4s
sha256h2 q3, q4, v9.4s
sha256su0 v7.4s, v8.4s
sha256su1 v6.4s, v8.4s, v5.4s
add v9.4s, v5.4s, v28.4s
mov v4.16b, v2.16b
sha256h q2, q3, v10.4s
sha256h2 q3, q4, v10.4s
sha256su0 v8.4s, v5.4s
sha256su1 v7.4s, v5.4s, v6.4s
add v10.4s, v6.4s, v29.4s
mov v4.16b, v2.16b
sha256h q2, q3, v9.4s
sha256h2 q3, q4, v9.4s
sha256su1 v8.4s, v6.4s, v7.4s
add v9.4s, v7.4s, v30.4s
mov v4.16b, v2.16b
sha256h q2, q3, v10.4s
sha256h2 q3, q4, v10.4s
add v10.4s, v8.4s, v31.4s
mov v4.16b, v2.16b
sha256h q2, q3, v9.4s
sha256h2 q3, q4, v9.4s
mov v4.16b, v2.16b
sha256h q2, q3, v10.4s
sha256h2 q3, q4, v10.4s
cmp x1, x2
add v1.4s, v1.4s, v3.4s
add v0.4s, v0.4s, v2.4s
b.ne .Lsha256loop
.Lsha256epilog:
st1 {v0.4s,v1.4s}, [x0]
ldr q10, [sp, #48]
ldr q9, [sp, #32]
ldr q8, [sp, #16]
ldr x29, [sp], #64
ret
.align 5
.LKConstant256:
.word 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
.word 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
.word 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
.word 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
.word 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
.word 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
.word 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
.word 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
.word 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
.word 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
.word 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
.word 0xd192e819,0xd6990624,0xf40e3585,0x106aa070
.word 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
.word 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
.word 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
.word 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
.size sha256_block_data_order,.-sha256_block_data_order
.align 2

@ -1 +1 @@
Subproject commit 0fb33e9c094bffde737c7a73cd5ccce4d7cbae33
Subproject commit 0bec72ca36084e9780a8c28abd4a0b24c03c18af

View file

@ -163,11 +163,22 @@ void EmbeddedBoot2::Main() {
char title_path[FS_MAX_PATH] = {0};
strcpy(title_path, "sdmc:/atmosphere/titles/");
strcat(title_path, ent->d_name);
strcat(title_path, "/boot2.flag");
strcat(title_path, "/flags/boot2.flag");
FILE *f_flag = fopen(title_path, "rb");
if (f_flag != NULL) {
fclose(f_flag);
LaunchTitle((Boot2KnownTitleId)title_id, FsStorageId_None, 0, NULL);
} else {
/* TODO: Deprecate this in the future. */
memset(title_path, 0, FS_MAX_PATH);
strcpy(title_path, "sdmc:/atmosphere/titles/");
strcat(title_path, ent->d_name);
strcat(title_path, "/boot2.flag");
f_flag = fopen(title_path, "rb");
if (f_flag != NULL) {
fclose(f_flag);
LaunchTitle((Boot2KnownTitleId)title_id, FsStorageId_None, 0, NULL);
}
}
}
}

View file

@ -25,7 +25,7 @@ enum SetSysCmd : u32 {
class SetSysMitmService : public IMitmServiceObject {
public:
SetSysMitmService(std::shared_ptr<Service> s) : IMitmServiceObject(s) {
SetSysMitmService(std::shared_ptr<Service> s, u64 pid) : IMitmServiceObject(s, pid) {
/* ... */
}

View file

@ -41,7 +41,7 @@ ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__ -DSM_ENABLE_SMHAX -DSM_ENABLE_MITM -DSM_ENABLE_INIT_DEFERS -DSM_MINIMUM_SESSION_LIMIT=8
CFLAGS += $(INCLUDE) -D__SWITCH__ -DSM_ENABLE_MITM -DSM_ENABLE_INIT_DEFERS -DSM_MINIMUM_SESSION_LIMIT=8
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17

View file

@ -221,13 +221,11 @@ bool Registration::HasService(u64 service) {
Result Registration::GetServiceHandle(u64 pid, u64 service, Handle *out) {
Registration::Service *target_service = GetService(service);
if (target_service == NULL || ShouldInitDefer(service)) {
if (target_service == NULL || ShouldInitDefer(service) || target_service->mitm_waiting_ack) {
/* Note: This defers the result until later. */
return RESULT_DEFER_SESSION;
}
/* */
*out = 0;
Result rc;
if (target_service->mitm_pid == 0 || target_service->mitm_pid == pid) {
@ -255,7 +253,17 @@ Result Registration::GetServiceHandle(u64 pid, u64 service, Handle *out) {
rc = resp->result;
if (R_SUCCEEDED(rc)) {
if (resp->should_mitm) {
rc = svcConnectToPort(&target_service->mitm_fwd_sess_h, target_service->port_h);
if (R_SUCCEEDED(rc)) {
rc = svcConnectToPort(out, target_service->mitm_port_h);
if (R_SUCCEEDED(rc)) {
target_service->mitm_waiting_ack_pid = pid;
target_service->mitm_waiting_ack = true;
} else {
svcCloseHandle(target_service->mitm_fwd_sess_h);
target_service->mitm_fwd_sess_h = 0;
}
}
} else {
rc = svcConnectToPort(out, target_service->port_h);
}
@ -497,6 +505,35 @@ Result Registration::UninstallMitmForPid(u64 pid, u64 service) {
return 0;
}
Result Registration::AcknowledgeMitmSessionForPid(u64 pid, u64 service, Handle *out, u64 *out_pid) {
if (!service) {
return 0xC15;
}
u64 service_name_len = GetServiceNameLength(service);
/* If the service has bytes after a null terminator, that's no good. */
if (service_name_len != 8 && (service >> (8 * service_name_len))) {
return 0xC15;
}
Registration::Service *target_service = GetService(service);
if (target_service == NULL) {
return 0xE15;
}
if ((!IsInitialProcess(pid) && target_service->mitm_pid != pid) || !target_service->mitm_waiting_ack) {
return 0x1015;
}
*out = target_service->mitm_fwd_sess_h;
*out_pid = target_service->mitm_waiting_ack_pid;
target_service->mitm_fwd_sess_h = 0;
target_service->mitm_waiting_ack_pid = 0;
target_service->mitm_waiting_ack = false;
return 0;
}
Result Registration::AssociatePidTidForMitm(u64 pid, u64 tid) {
for (auto &service : g_service_list) {
if (service.mitm_pid) {

View file

@ -43,6 +43,10 @@ class Registration {
u64 mitm_pid;
Handle mitm_port_h;
Handle mitm_query_h;
bool mitm_waiting_ack;
u64 mitm_waiting_ack_pid;
Handle mitm_fwd_sess_h;
};
/* Utilities. */
@ -74,5 +78,6 @@ class Registration {
/* Extension. */
static Result InstallMitmForPid(u64 pid, u64 service, Handle *out, Handle *query_out);
static Result UninstallMitmForPid(u64 pid, u64 service);
static Result AcknowledgeMitmSessionForPid(u64 pid, u64 service, Handle *out, u64 *out_pid);
static Result AssociatePidTidForMitm(u64 pid, u64 tid);
};

View file

@ -98,6 +98,20 @@ Result UserService::AtmosphereUninstallMitm(SmServiceName service) {
return rc;
}
Result UserService::AtmosphereAcknowledgeMitmSession(Out<u64> client_pid, Out<MovedHandle> fwd_h, SmServiceName service) {
Result rc = 0x415;
Handle out_fwd_h = 0;
if (this->has_initialized) {
rc = Registration::AcknowledgeMitmSessionForPid(this->pid, smEncodeName(service.name), &out_fwd_h, client_pid.GetPointer());
}
if (R_SUCCEEDED(rc)) {
fwd_h.SetValue(out_fwd_h);
}
return rc;
}
Result UserService::AtmosphereAssociatePidTidForMitm(u64 pid, u64 tid) {
Result rc = 0x415;
if (this->has_initialized) {

View file

@ -27,7 +27,8 @@ enum UserServiceCmd {
User_Cmd_AtmosphereInstallMitm = 65000,
User_Cmd_AtmosphereUninstallMitm = 65001,
User_Cmd_AtmosphereAssociatePidTidForMitm = 65002
User_Cmd_AtmosphereAssociatePidTidForMitm = 65002,
User_Cmd_AtmosphereAcknowledgeMitmSession = 65003,
};
class UserService final : public IServiceObject {
@ -45,6 +46,7 @@ class UserService final : public IServiceObject {
virtual Result AtmosphereInstallMitm(Out<MovedHandle> srv_h, Out<MovedHandle> qry_h, SmServiceName service);
virtual Result AtmosphereUninstallMitm(SmServiceName service);
virtual Result AtmosphereAssociatePidTidForMitm(u64 pid, u64 tid);
virtual Result AtmosphereAcknowledgeMitmSession(Out<u64> client_pid, Out<MovedHandle> fwd_h, SmServiceName service);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MakeServiceCommandMeta<User_Cmd_Initialize, &UserService::Initialize>(),
@ -56,6 +58,7 @@ class UserService final : public IServiceObject {
MakeServiceCommandMeta<User_Cmd_AtmosphereInstallMitm, &UserService::AtmosphereInstallMitm>(),
MakeServiceCommandMeta<User_Cmd_AtmosphereUninstallMitm, &UserService::AtmosphereUninstallMitm>(),
MakeServiceCommandMeta<User_Cmd_AtmosphereAssociatePidTidForMitm, &UserService::AtmosphereAssociatePidTidForMitm>(),
MakeServiceCommandMeta<User_Cmd_AtmosphereAcknowledgeMitmSession, &UserService::AtmosphereAcknowledgeMitmSession>(),
#endif
};
};