mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-24 07:40:26 +01:00
sm: Add sm:dmnt query extension
This commit is contained in:
parent
deb124138b
commit
b6684ff845
@ -20,6 +20,18 @@ interface nn::sm::detail::IUserInterface is sm: {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Additionally, an interface `sm:dmnt` has been created to allow a debug monitor to query sm's state.
|
||||||
|
|
||||||
|
Its SwIPC definition follows.
|
||||||
|
```
|
||||||
|
interface nn::sm::detail::IDebugMonitorInterface is sm:dmnt {
|
||||||
|
[65000] AtmosphereGetServiceRecord(ServiceName name) -> SmServiceRecord;
|
||||||
|
[65001] AtmosphereListServiceRecords(u64 offset) -> buffer<SmServiceRecord, 6>, u64 count;
|
||||||
|
[65002] AtmosphereGetServiceRecordSize() -> u64 record_size;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
#### AtmosphereInstallMitm
|
#### AtmosphereInstallMitm
|
||||||
|
|
||||||
This command alters the registration for the named service, in order to allow services to intercept communication between client processes and their intended services. It is used by [fs_mitm](fs_mitm.md).
|
This command alters the registration for the named service, in order to allow services to intercept communication between client processes and their intended services. It is used by [fs_mitm](fs_mitm.md).
|
||||||
@ -58,6 +70,50 @@ This command is used internally by the Stratosphere implementation of the [loade
|
|||||||
|
|
||||||
If the given process ID refers to a kernel internal process, error code 0x1015 is returned. This command requires that the session be initialized, returning error code 0x415 if it is not.
|
If the given process ID refers to a kernel internal process, error code 0x1015 is returned. This command requires that the session be initialized, returning error code 0x415 if it is not.
|
||||||
|
|
||||||
|
#### AtmosphereGetServiceRecordSize
|
||||||
|
|
||||||
|
Retrieves `sizeof(SmServiceRecord)` for a service. The current format of `SmServiceRecord` structure follows.
|
||||||
|
|
||||||
|
```
|
||||||
|
struct SmServiceRecord {
|
||||||
|
uint64_t service_name;
|
||||||
|
uint64_t owner_pid;
|
||||||
|
uint64_t max_sessions;
|
||||||
|
uint64_t mitm_pid;
|
||||||
|
uint64_t mitm_waiting_ack_pid;
|
||||||
|
bool is_light;
|
||||||
|
bool mitm_waiting_ack;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### AtmosphereGetServiceRecord
|
||||||
|
|
||||||
|
Retrieves a service registration record for a service.
|
||||||
|
|
||||||
|
#### AtmosphereListServiceRecords
|
||||||
|
|
||||||
|
Provides a list of service registrations records.
|
||||||
|
|
||||||
|
The command will return an array of `SmServiceRecord`s, skipping `offset` records. The number of records returned is indicated by `count`.
|
||||||
|
If `count` is less than the size of the buffer divided by `sizeof(SmServiceRecord)` (the buffer was not completely filled), the end of the service registration list has been reached. Otherwise, client code
|
||||||
|
should increment `offset` by `count` and call again. Client code should retrieve a record size using `AtmosphereGetServiceRecordSize`, and either make sure that the size of a record matches what it expects,
|
||||||
|
or should make sure to use the correct size as the stride while iterating over the array of returned records. Example pseudocode is shown below.
|
||||||
|
|
||||||
|
```
|
||||||
|
offset = 0;
|
||||||
|
record_size = AtmosphereGetServiceRecordSize();
|
||||||
|
do {
|
||||||
|
SmServiceRecord records[16];
|
||||||
|
count = AtmosphereListServiceRecords(offset, buffer(records));
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
SmServiceRecord record = {0};
|
||||||
|
memcpy(&record, &records[i], min(record_size, sizeof(SmServiceRecord));
|
||||||
|
/* process record */
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
} while(count == sizeof(records) / record_size);
|
||||||
|
```
|
||||||
|
|
||||||
### Minimum Session Limit
|
### Minimum Session Limit
|
||||||
|
|
||||||
When a service is registered, the sysmodule registering it must specify a limit on the number of sessions that are allowed to be active for that service at a time. This is used to ensure that services like `fs-pr`, `fs-ldr`, and `ldr:pm` can only be connected to once, adding an additional layer of safety over the regular service verification to ensure that those services are only connected to by the highly priveleged process they are intended to be used by.
|
When a service is registered, the sysmodule registering it must specify a limit on the number of sessions that are allowed to be active for that service at a time. This is used to ensure that services like `fs-pr`, `fs-ldr`, and `ldr:pm` can only be connected to once, adding an additional layer of safety over the regular service verification to ensure that those services are only connected to by the highly priveleged process they are intended to be used by.
|
||||||
|
33
stratosphere/sm/source/sm_dmnt_service.cpp
Normal file
33
stratosphere/sm/source/sm_dmnt_service.cpp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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 <stratosphere.hpp>
|
||||||
|
#include "sm_dmnt_service.hpp"
|
||||||
|
#include "sm_registration.hpp"
|
||||||
|
|
||||||
|
Result DmntService::AtmosphereGetRecord(Out<SmServiceRecord> record, SmServiceName service) {
|
||||||
|
return Registration::GetServiceRecord(smEncodeName(service.name), record.GetPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DmntService::AtmosphereListRecords(OutBuffer<SmServiceRecord> records, Out<u64> out_count, u64 offset) {
|
||||||
|
Registration::ListServiceRecords(offset, records.num_elements, records.buffer, out_count.GetPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DmntService::AtmosphereGetRecordSize(Out<u64> record_size) {
|
||||||
|
record_size.SetValue(sizeof(SmServiceRecord));
|
||||||
|
}
|
||||||
|
|
40
stratosphere/sm/source/sm_dmnt_service.hpp
Normal file
40
stratosphere/sm/source/sm_dmnt_service.hpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 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 <stratosphere.hpp>
|
||||||
|
#include "sm_types.hpp"
|
||||||
|
|
||||||
|
enum DmntServiceCmd {
|
||||||
|
Dmnt_Cmd_AtmosphereGetRecord = 65000,
|
||||||
|
Dmnt_Cmd_AtmosphereListRecords = 65001,
|
||||||
|
Dmnt_Cmd_AtmosphereGetRecordSize = 65002,
|
||||||
|
};
|
||||||
|
|
||||||
|
class DmntService final : public IServiceObject {
|
||||||
|
private:
|
||||||
|
/* Actual commands. */
|
||||||
|
virtual Result AtmosphereGetRecord(Out<SmServiceRecord> record, SmServiceName service);
|
||||||
|
virtual void AtmosphereListRecords(OutBuffer<SmServiceRecord> records, Out<u64> out_count, u64 offset);
|
||||||
|
virtual void AtmosphereGetRecordSize(Out<u64> record_size);
|
||||||
|
public:
|
||||||
|
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||||
|
MakeServiceCommandMeta<Dmnt_Cmd_AtmosphereGetRecord, &DmntService::AtmosphereGetRecord>(),
|
||||||
|
MakeServiceCommandMeta<Dmnt_Cmd_AtmosphereListRecords, &DmntService::AtmosphereListRecords>(),
|
||||||
|
MakeServiceCommandMeta<Dmnt_Cmd_AtmosphereGetRecordSize, &DmntService::AtmosphereGetRecordSize>(),
|
||||||
|
};
|
||||||
|
};
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include "sm_manager_service.hpp"
|
#include "sm_manager_service.hpp"
|
||||||
#include "sm_user_service.hpp"
|
#include "sm_user_service.hpp"
|
||||||
|
#include "sm_dmnt_service.hpp"
|
||||||
#include "sm_registration.hpp"
|
#include "sm_registration.hpp"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -83,6 +84,17 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
server_manager->AddWaitable(new ExistingPortServer<ManagerService>(smm_h, 1));
|
server_manager->AddWaitable(new ExistingPortServer<ManagerService>(smm_h, 1));
|
||||||
|
|
||||||
|
/*===== ATMOSPHERE EXTENSION =====*/
|
||||||
|
/* Create sm:dmnt manually. */
|
||||||
|
Handle smdmnt_h;
|
||||||
|
if (R_FAILED(Registration::RegisterServiceForSelf(smEncodeName("sm:dmnt"), 1, false, &smdmnt_h))) {
|
||||||
|
/* TODO: Panic. */
|
||||||
|
while (1) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
server_manager->AddWaitable(new ExistingPortServer<DmntService>(smm_h, 1));;
|
||||||
|
/*================================*/
|
||||||
|
|
||||||
/* Loop forever, servicing our services. */
|
/* Loop forever, servicing our services. */
|
||||||
server_manager->Process();
|
server_manager->Process();
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ enum ManagerServiceCmd {
|
|||||||
Manager_Cmd_RegisterProcess = 0,
|
Manager_Cmd_RegisterProcess = 0,
|
||||||
Manager_Cmd_UnregisterProcess = 1,
|
Manager_Cmd_UnregisterProcess = 1,
|
||||||
|
|
||||||
|
|
||||||
Manager_Cmd_AtmosphereEndInitDefers = 65000,
|
Manager_Cmd_AtmosphereEndInitDefers = 65000,
|
||||||
Manager_Cmd_AtmosphereHasMitm = 65001,
|
Manager_Cmd_AtmosphereHasMitm = 65001,
|
||||||
};
|
};
|
||||||
|
@ -562,3 +562,51 @@ Result Registration::AssociatePidTidForMitm(u64 pid, u64 tid) {
|
|||||||
}
|
}
|
||||||
return 0x0;
|
return 0x0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Registration::ConvertServiceToRecord(Registration::Service *service, SmServiceRecord *record) {
|
||||||
|
record->service_name = service->service_name;
|
||||||
|
record->owner_pid = service->owner_pid;
|
||||||
|
record->max_sessions = service->max_sessions;
|
||||||
|
record->mitm_pid = service->mitm_pid;
|
||||||
|
record->mitm_waiting_ack_pid = service->mitm_waiting_ack_pid;
|
||||||
|
record->is_light = service->is_light;
|
||||||
|
record->mitm_waiting_ack = service->mitm_waiting_ack;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Registration::GetServiceRecord(u64 service, SmServiceRecord *out) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConvertServiceToRecord(target_service, out);
|
||||||
|
return 0x0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Registration::ListServiceRecords(u64 offset, u64 max_out, SmServiceRecord *out, u64 *out_count) {
|
||||||
|
u64 count = 0;
|
||||||
|
|
||||||
|
for (auto it = g_service_list.begin(); it != g_service_list.end() && count < max_out; it++) {
|
||||||
|
if (it->service_name != 0) {
|
||||||
|
if (offset > 0) {
|
||||||
|
offset--;
|
||||||
|
} else {
|
||||||
|
ConvertServiceToRecord(it, out++);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_count = count;
|
||||||
|
}
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
|
||||||
|
#include "sm_types.hpp"
|
||||||
|
|
||||||
#define REGISTRATION_LIST_MAX_PROCESS (0x40)
|
#define REGISTRATION_LIST_MAX_PROCESS (0x40)
|
||||||
#define REGISTRATION_LIST_MAX_SERVICE (0x100)
|
#define REGISTRATION_LIST_MAX_SERVICE (0x100)
|
||||||
#define REGISTRATION_MAX_SAC_SIZE (0x200)
|
#define REGISTRATION_MAX_SAC_SIZE (0x200)
|
||||||
@ -81,4 +83,8 @@ class Registration {
|
|||||||
static Result UninstallMitmForPid(u64 pid, u64 service);
|
static Result UninstallMitmForPid(u64 pid, u64 service);
|
||||||
static Result AcknowledgeMitmSessionForPid(u64 pid, u64 service, Handle *out, u64 *out_pid);
|
static Result AcknowledgeMitmSessionForPid(u64 pid, u64 service, Handle *out, u64 *out_pid);
|
||||||
static Result AssociatePidTidForMitm(u64 pid, u64 tid);
|
static Result AssociatePidTidForMitm(u64 pid, u64 tid);
|
||||||
|
|
||||||
|
static void ConvertServiceToRecord(Registration::Service *service, SmServiceRecord *record);
|
||||||
|
static Result GetServiceRecord(u64 service, SmServiceRecord *out);
|
||||||
|
static void ListServiceRecords(u64 offset, u64 max_out, SmServiceRecord *out, u64 *out_count);
|
||||||
};
|
};
|
||||||
|
@ -20,3 +20,16 @@ struct SmServiceName {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static_assert(__alignof__(SmServiceName) == 1, "SmServiceName definition!");
|
static_assert(__alignof__(SmServiceName) == 1, "SmServiceName definition!");
|
||||||
|
|
||||||
|
/* For Debug Monitor extensions. */
|
||||||
|
struct SmServiceRecord {
|
||||||
|
u64 service_name;
|
||||||
|
u64 owner_pid;
|
||||||
|
u64 max_sessions;
|
||||||
|
u64 mitm_pid;
|
||||||
|
u64 mitm_waiting_ack_pid;
|
||||||
|
bool is_light;
|
||||||
|
bool mitm_waiting_ack;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(SmServiceRecord) == 0x30, "SmServiceRecord definition!");
|
Loading…
Reference in New Issue
Block a user