fs.mitm: Implement basic passthrough framework for input commands.
This commit is contained in:
17
stratosphere/fs_mitm/source/debug.cpp
Normal file
17
stratosphere/fs_mitm/source/debug.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <switch.h>
|
||||
#include <cstring>
|
||||
#include "debug.hpp"
|
||||
|
||||
static u64 g_num_logged = 0;
|
||||
|
||||
#define MAX_LOGS U64_MAX
|
||||
|
||||
void Reboot() {
|
||||
while (1) {
|
||||
/* ... */
|
||||
}
|
||||
}
|
||||
|
||||
void Log(const void *data, int size) {
|
||||
/* ... */
|
||||
}
|
||||
4
stratosphere/fs_mitm/source/debug.hpp
Normal file
4
stratosphere/fs_mitm/source/debug.hpp
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
void Reboot();
|
||||
void Log(const void *data, int size);
|
||||
104
stratosphere/fs_mitm/source/fsmitm_main.cpp
Normal file
104
stratosphere/fs_mitm/source/fsmitm_main.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "sm_mitm.h"
|
||||
|
||||
#include "mitm_server.hpp"
|
||||
#include "fsmitm_service.hpp"
|
||||
|
||||
extern "C" {
|
||||
extern u32 __start__;
|
||||
|
||||
u32 __nx_applet_type = AppletType_None;
|
||||
|
||||
#define INNER_HEAP_SIZE 0x1000000
|
||||
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
|
||||
char nx_inner_heap[INNER_HEAP_SIZE];
|
||||
|
||||
void __libnx_initheap(void);
|
||||
void __appInit(void);
|
||||
void __appExit(void);
|
||||
}
|
||||
|
||||
|
||||
void __libnx_initheap(void) {
|
||||
void* addr = nx_inner_heap;
|
||||
size_t size = nx_inner_heap_size;
|
||||
|
||||
/* Newlib */
|
||||
extern char* fake_heap_start;
|
||||
extern char* fake_heap_end;
|
||||
|
||||
fake_heap_start = (char*)addr;
|
||||
fake_heap_end = (char*)addr + size;
|
||||
}
|
||||
|
||||
void __appInit(void) {
|
||||
Result rc;
|
||||
|
||||
rc = smInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM));
|
||||
}
|
||||
|
||||
rc = smMitMInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM));
|
||||
}
|
||||
|
||||
rc = fsInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS));
|
||||
}
|
||||
|
||||
rc = splInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
fatalSimple(0xCAFE << 4 | 3);
|
||||
}
|
||||
|
||||
/* Check for exosphere API compatibility. */
|
||||
u64 exosphere_cfg;
|
||||
if (R_SUCCEEDED(splGetConfig((SplConfigItem)65000, &exosphere_cfg))) {
|
||||
/* MitM requires Atmosphere API 0.1. */
|
||||
u16 api_version = (exosphere_cfg >> 16) & 0xFFFF;
|
||||
if (api_version < 0x0001) {
|
||||
fatalSimple(0xCAFE << 4 | 0xFE);
|
||||
}
|
||||
} else {
|
||||
fatalSimple(0xCAFE << 4 | 0xFF);
|
||||
}
|
||||
|
||||
splExit();
|
||||
}
|
||||
|
||||
void __appExit(void) {
|
||||
/* Cleanup services. */
|
||||
fsExit();
|
||||
smMitMExit();
|
||||
smExit();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
consoleDebugInit(debugDevice_SVC);
|
||||
|
||||
|
||||
/* TODO: What's a good timeout value to use here? */
|
||||
WaitableManager *server_manager = new WaitableManager(U64_MAX);
|
||||
|
||||
/* Create fsp-srv mitm. */
|
||||
server_manager->add_waitable(new MitMServer<FsMitMService>("fsp-srv", 61));
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
server_manager->process();
|
||||
|
||||
/* Cleanup. */
|
||||
delete server_manager;
|
||||
return 0;
|
||||
}
|
||||
|
||||
12
stratosphere/fs_mitm/source/fsmitm_service.cpp
Normal file
12
stratosphere/fs_mitm/source/fsmitm_service.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include <switch.h>
|
||||
#include "fsmitm_service.hpp"
|
||||
|
||||
Result FsMitMService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) {
|
||||
Result rc = 0xF601;
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result FsMitMService::handle_deferred() {
|
||||
/* This service is never deferrable. */
|
||||
return 0;
|
||||
}
|
||||
9
stratosphere/fs_mitm/source/fsmitm_service.hpp
Normal file
9
stratosphere/fs_mitm/source/fsmitm_service.hpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere/iserviceobject.hpp>
|
||||
|
||||
class FsMitMService : IServiceObject {
|
||||
public:
|
||||
virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size);
|
||||
virtual Result handle_deferred();
|
||||
};
|
||||
127
stratosphere/fs_mitm/source/mitm_server.hpp
Normal file
127
stratosphere/fs_mitm/source/mitm_server.hpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "sm_mitm.h"
|
||||
#include "mitm_session.hpp"
|
||||
|
||||
#include "debug.hpp"
|
||||
|
||||
template <typename T>
|
||||
class MitMSession;
|
||||
|
||||
template <typename T>
|
||||
class MitMServer final : public IWaitable {
|
||||
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
|
||||
protected:
|
||||
Handle port_handle;
|
||||
unsigned int max_sessions;
|
||||
unsigned int num_sessions;
|
||||
MitMSession<T> **sessions;
|
||||
char mitm_name[9];
|
||||
|
||||
public:
|
||||
MitMServer(const char *service_name, unsigned int max_s) : max_sessions(max_s) {
|
||||
this->sessions = new MitMSession<T> *[this->max_sessions];
|
||||
for (unsigned int i = 0; i < this->max_sessions; i++) {
|
||||
this->sessions[i] = NULL;
|
||||
}
|
||||
this->num_sessions = 0;
|
||||
strncpy(mitm_name, service_name, 8);
|
||||
mitm_name[8] = '\x00';
|
||||
Result rc;
|
||||
if (R_FAILED((rc = smMitMInstall(&this->port_handle, mitm_name)))) {
|
||||
/* TODO: Panic. */
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~MitMServer() {
|
||||
for (unsigned int i = 0; i < this->max_sessions; i++) {
|
||||
if (this->sessions[i]) {
|
||||
delete this->sessions[i];
|
||||
}
|
||||
|
||||
delete this->sessions;
|
||||
}
|
||||
|
||||
if (port_handle) {
|
||||
if (R_FAILED(smMitMUninstall(mitm_name))) {
|
||||
/* TODO: Panic. */
|
||||
}
|
||||
svcCloseHandle(port_handle);
|
||||
}
|
||||
}
|
||||
|
||||
/* IWaitable */
|
||||
virtual unsigned int get_num_waitables() {
|
||||
unsigned int n = 1;
|
||||
for (unsigned int i = 0; i < this->max_sessions; i++) {
|
||||
if (this->sessions[i]) {
|
||||
n += this->sessions[i]->get_num_waitables();
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
virtual void get_waitables(IWaitable **dst) {
|
||||
dst[0] = this;
|
||||
unsigned int n = 0;
|
||||
for (unsigned int i = 0; i < this->max_sessions; i++) {
|
||||
if (this->sessions[i]) {
|
||||
this->sessions[i]->get_waitables(&dst[1 + n]);
|
||||
n += this->sessions[i]->get_num_waitables();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void delete_child(IWaitable *child) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < this->max_sessions; i++) {
|
||||
if (this->sessions[i] == child) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == this->max_sessions) {
|
||||
/* TODO: Panic, because this isn't our child. */
|
||||
} else {
|
||||
delete this->sessions[i];
|
||||
this->sessions[i] = NULL;
|
||||
this->num_sessions--;
|
||||
}
|
||||
}
|
||||
|
||||
virtual Handle get_handle() {
|
||||
return this->port_handle;
|
||||
}
|
||||
|
||||
|
||||
virtual void handle_deferred() {
|
||||
/* TODO: Panic, because we can never defer a server. */
|
||||
}
|
||||
|
||||
virtual Result handle_signaled(u64 timeout) {
|
||||
/* If this server's port was signaled, accept a new session. */
|
||||
Handle session_h;
|
||||
svcAcceptSession(&session_h, this->port_handle);
|
||||
|
||||
if (this->num_sessions >= this->max_sessions) {
|
||||
svcCloseHandle(session_h);
|
||||
return 0x10601;
|
||||
}
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < this->max_sessions; i++) {
|
||||
if (this->sessions[i] == NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->sessions[i] = new MitMSession<T>(this, session_h, 0, mitm_name);
|
||||
this->sessions[i]->set_parent(this);
|
||||
this->num_sessions++;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
153
stratosphere/fs_mitm/source/mitm_session.hpp
Normal file
153
stratosphere/fs_mitm/source/mitm_session.hpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "mitm_server.hpp"
|
||||
|
||||
#include "debug.hpp"
|
||||
|
||||
|
||||
template <typename T>
|
||||
class MitMServer;
|
||||
|
||||
template <typename T>
|
||||
class MitMSession final : public IWaitable {
|
||||
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
|
||||
|
||||
T *service_object;
|
||||
MitMServer<T> *server;
|
||||
Handle server_handle;
|
||||
Handle client_handle;
|
||||
/* This will be for the actual session. */
|
||||
Service forward_service;
|
||||
|
||||
char *pointer_buffer;
|
||||
size_t pointer_buffer_size;
|
||||
|
||||
static_assert(sizeof(pointer_buffer) <= POINTER_BUFFER_SIZE_MAX, "Incorrect Size for PointerBuffer!");
|
||||
public:
|
||||
MitMSession<T>(MitMServer<T> *s, Handle s_h, Handle c_h, const char *srv) : server(s), server_handle(s_h), client_handle(c_h) {
|
||||
this->service_object = new T();
|
||||
if (R_FAILED(smMitMGetService(&forward_service, srv))) {
|
||||
/* TODO: Panic. */
|
||||
}
|
||||
if (R_FAILED(ipcQueryPointerBufferSize(forward_service.handle, &pointer_buffer_size))) {
|
||||
/* TODO: Panic. */
|
||||
}
|
||||
this->pointer_buffer = new char[pointer_buffer_size];
|
||||
}
|
||||
|
||||
~MitMSession() override {
|
||||
delete this->service_object;
|
||||
serviceClose(&forward_service);
|
||||
if (server_handle) {
|
||||
svcCloseHandle(server_handle);
|
||||
}
|
||||
if (client_handle) {
|
||||
svcCloseHandle(client_handle);
|
||||
}
|
||||
}
|
||||
|
||||
T *get_service_object() { return this->service_object; }
|
||||
Handle get_server_handle() { return this->server_handle; }
|
||||
Handle get_client_handle() { return this->client_handle; }
|
||||
|
||||
/* IWaitable */
|
||||
unsigned int get_num_waitables() override {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void get_waitables(IWaitable **dst) override {
|
||||
dst[0] = this;
|
||||
}
|
||||
|
||||
void delete_child(IWaitable *child) override {
|
||||
/* TODO: Panic, because we can never have any children. */
|
||||
}
|
||||
|
||||
Handle get_handle() override {
|
||||
return this->server_handle;
|
||||
}
|
||||
|
||||
void handle_deferred() override {
|
||||
/* TODO: Panic, because we can never be deferred. */
|
||||
}
|
||||
|
||||
Result handle_signaled(u64 timeout) override {
|
||||
Result rc;
|
||||
int handle_index;
|
||||
|
||||
/* Prepare pointer buffer... */
|
||||
IpcCommand c_for_reply;
|
||||
ipcInitialize(&c_for_reply);
|
||||
ipcAddRecvStatic(&c_for_reply, this->pointer_buffer, this->pointer_buffer_size, 0);
|
||||
u32 *cmdbuf = (u32 *)armGetTls();
|
||||
ipcPrepareHeader(&c_for_reply, 0);
|
||||
|
||||
|
||||
if (R_SUCCEEDED(rc = svcReplyAndReceive(&handle_index, &this->server_handle, 1, 0, timeout))) {
|
||||
if (handle_index != 0) {
|
||||
/* TODO: Panic? */
|
||||
}
|
||||
Log(armGetTls(), 0x100);
|
||||
Result retval = 0;
|
||||
u32 *rawdata_start = cmdbuf;
|
||||
|
||||
IpcParsedCommand r;
|
||||
IpcCommand c;
|
||||
IpcParsedCommand out_r;
|
||||
out_r.NumHandles = 0;
|
||||
|
||||
ipcInitialize(&c);
|
||||
|
||||
retval = ipcParse(&r);
|
||||
|
||||
/* TODO: Close input copy handles that we don't need. */
|
||||
|
||||
if (R_SUCCEEDED(retval)) {
|
||||
rawdata_start = (u32 *)r.Raw;
|
||||
retval = 0xF601;
|
||||
if (r.CommandType == IpcCommandType_Request || r.CommandType == IpcCommandType_RequestWithContext) {
|
||||
retval = this->service_object->dispatch(r, c, rawdata_start[2], (u8 *)this->pointer_buffer, sizeof(this->pointer_buffer));
|
||||
}
|
||||
|
||||
/* 0xF601 --> Dispatch onwards. */
|
||||
if (retval == 0xF601) {
|
||||
/* Patch PID Descriptor, if relevant. */
|
||||
if (r.HasPid) {
|
||||
/* [ctrl 0] [ctrl 1] [handle desc 0] [pid low] [pid high] */
|
||||
cmdbuf[4] = 0xFFFE0000UL | (cmdbuf[4] & 0xFFFFUL);
|
||||
}
|
||||
Log(armGetTls(), 0x100);
|
||||
retval = serviceIpcDispatch(&forward_service);
|
||||
if (R_SUCCEEDED(retval)) {
|
||||
ipcParse(&out_r);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *resp = (decltype(resp))out_r.Raw;
|
||||
|
||||
retval = resp->result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (retval == 0xF601) {
|
||||
/* Session close. */
|
||||
rc = retval;
|
||||
} else {
|
||||
Log(armGetTls(), 0x100);
|
||||
rc = svcReplyAndReceive(&handle_index, &this->server_handle, 0, this->server_handle, 0);
|
||||
/* Clean up copy handles. */
|
||||
for (unsigned int i = 0; i < out_r.NumHandles; i++) {
|
||||
if (out_r.WasHandleCopied[i]) {
|
||||
svcCloseHandle(out_r.Handles[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
};
|
||||
173
stratosphere/fs_mitm/source/sm_mitm.c
Normal file
173
stratosphere/fs_mitm/source/sm_mitm.c
Normal file
@@ -0,0 +1,173 @@
|
||||
#include <switch.h>
|
||||
#include <switch/arm/atomics.h>
|
||||
#include "sm_mitm.h"
|
||||
|
||||
static Handle g_smMitmHandle = INVALID_HANDLE;
|
||||
static u64 g_refCnt;
|
||||
|
||||
Result smMitMInitialize(void) {
|
||||
atomicIncrement64(&g_refCnt);
|
||||
|
||||
if (g_smMitmHandle != INVALID_HANDLE)
|
||||
return 0;
|
||||
|
||||
Result rc = svcConnectToNamedPort(&g_smMitmHandle, "sm:");
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcSendPid(&c);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u64 zero;
|
||||
u64 reserved[2];
|
||||
} *raw;
|
||||
|
||||
raw = ipcPrepareHeader(&c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 0;
|
||||
raw->zero = 0;
|
||||
|
||||
rc = ipcDispatch(g_smMitmHandle);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
ipcParse(&r);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
}
|
||||
}
|
||||
|
||||
if (R_FAILED(rc))
|
||||
smExit();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void smMitMExit(void) {
|
||||
if (atomicDecrement64(&g_refCnt) == 0) {
|
||||
svcCloseHandle(g_smMitmHandle);
|
||||
g_smMitmHandle = INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
Result smMitMGetService(Service* service_out, const char *name_str)
|
||||
{
|
||||
u64 name = smEncodeName(name_str);
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u64 service_name;
|
||||
u64 reserved[2];
|
||||
} *raw;
|
||||
|
||||
raw = ipcPrepareHeader(&c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 1;
|
||||
raw->service_name = name;
|
||||
|
||||
Result rc = ipcDispatch(g_smMitmHandle);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
ipcParse(&r);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
service_out->type = ServiceType_Normal;
|
||||
service_out->handle = r.Handles[0];
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
Result smMitMInstall(Handle *handle_out, const char *name) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u64 service_name;
|
||||
} *raw;
|
||||
|
||||
raw = ipcPrepareHeader(&c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 65000;
|
||||
raw->service_name = smEncodeName(name);
|
||||
|
||||
Result rc = ipcDispatch(g_smMitmHandle);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
ipcParse(&r);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
*handle_out = r.Handles[0];
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result smMitMUninstall(const char *name) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u64 service_name;
|
||||
u64 reserved;
|
||||
} *raw;
|
||||
|
||||
raw = ipcPrepareHeader(&c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 65001;
|
||||
raw->service_name = smEncodeName(name);
|
||||
|
||||
Result rc = ipcDispatch(g_smMitmHandle);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
ipcParse(&r);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
22
stratosphere/fs_mitm/source/sm_mitm.h
Normal file
22
stratosphere/fs_mitm/source/sm_mitm.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @file sm_mitm.h
|
||||
* @brief Service manager (sm) IPC wrapper for Atmosphere extensions.
|
||||
* @author SciresM
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
Result smMitMInitialize(void);
|
||||
void smMitMExit(void);
|
||||
Result smMitMGetService(Service* service_out, const char *name);
|
||||
Result smMitMInstall(Handle *handle_out, const char *name);
|
||||
Result smMitMUninstall(const char *name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user