initial commit

This commit is contained in:
TotalJustice 2023-05-20 20:52:47 +01:00
commit 0a6db1cdfa
7 changed files with 916 additions and 0 deletions

19
.github/workflows/build_depoly.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
container: devkitpro/devkita64:latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@master
- name: Build
run: make dist -j2
- uses: actions/upload-artifact@master
with:
name: sys-patch
path: sys-patch.zip

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
.vscode
build
Firmware
420000000000000B
*.txt
*.elf
*.npdm
*.nso
*.nsp
*.zip
out
ignoreme

233
Makefile Normal file
View File

@ -0,0 +1,233 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
#
# NO_ICON: if set to anything, do not use icon.
# NO_NACP: if set to anything, no .nacp file is generated.
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
# ICON is the filename of the icon (.jpg), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := sys-patch
BUILD := build
SOURCES := src
DATA := data
INCLUDES := include
#ROMFS := romfs
# sys-patch
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -std=c++23 -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
else
ifneq (,$(findstring icon.jpg,$(icons)))
export APP_ICON := $(TOPDIR)/icon.jpg
endif
endif
else
export APP_ICON := $(TOPDIR)/$(ICON)
endif
ifeq ($(strip $(NO_ICON)),)
export NROFLAGS += --icon=$(APP_ICON)
endif
ifeq ($(strip $(NO_NACP)),)
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
endif
ifneq ($(APP_TITLEID),)
export NACPFLAGS += --titleid=$(APP_TITLEID)
endif
ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
@rm -rf out/
@mkdir -p out/atmosphere/contents/420000000000000B/flags
@touch out/atmosphere/contents/420000000000000B/flags/boot2.flag
@cp $(CURDIR)/toolbox.json out/atmosphere/contents/420000000000000B/toolbox.json
@cp $(CURDIR)/$(TARGET).nsp out/atmosphere/contents/420000000000000B/exefs.nsp
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
dist: all
@echo making dist ...
@rm sys-patch.zip
@cd out; zip -r ../sys-patch.zip ./*; cd ../
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

85
README.md Normal file
View File

@ -0,0 +1,85 @@
# sys-patch
A script-like system module that patches fs, es and ldr on boot.
---
## Building
### prerequisites
- install devkitpro
```sh
git clone https://github.com/ITotalJustice/sys-patch.git
cd sys-patch
make
```
---
## What is being patched?
Here's a quick run down of what's being patched
- fs
- es
- ldr
fs and es need new patches after every new fw version.
ldr on the other hand needs new patches after every new atmosphere release. this is due to ldr service being reimplemented by atmosphere. in fw 10.0.0, a new check was added to ofw which we needed to patch out. As atmosphere closely follows what ofw does, it also added this check. This is why a new patch is needed per atmosphere update.
---
## How does it work?
it uses a collection of patterns to find the piece of code to patch. alternatively, it could just use offsets, however this would mean this tool would have to be updated after every new fw update, that's not ideal.
the patches are applied at boot, then, the sysmod stops running. the memory footpint of the sysmod is very very small, only using 16kib in total. the size of the binary itself is only 14kib! this doesnt really mean much, but im pretty proud of it :)
---
## Does this mean i should stop downloading / using sigpatches?
No, i would personally recommend continuing to use sigpatches. Reason being is that should this tool ever break, i likely wont be quick to fix it.
---
## If i am using sigpatches already, is there any point in using this as well?
Yes, in 2 niche cases.
1. A new ldr patch needs to be created after every atosphere update. Sometimes, a new silent atmosphere update is released. This tool will always patch ldr without having to update patches.
2. Building atmosphere from src will require you to generate a new ldr patch for that custom built atmosphere. This is easy enough due to the public scripts / tools that exist out there, however this will always be able to
Also, if you forget to update your patches when you update fw / atmosphere, this sysmod should be able to patch everything just fine! so it's nice to have as a fallback.
---
## My request
This repo is mainly a proof of concept. I would love for someone to build upon this, make it into something bigger / better.
here are a few ideas that i have:
- option to only apply patches when in emunand
- option to load new patterns from file
- make this into a service / overlay
- make homebrew frontend that can update this sysmod, apply patches, all without having to reboot
---
## Credits / Thanks
software is built on the shoulders of giants. this tool wouldn't be possible wthout these people:
- DarkMatterCore
- MrDude
- BornToHonk (farni)
- TeJay
- ArchBox
- Shchmue (lockpick)
- Jakibaki (sys-netcheat)
- SciresM (Atmosphere, hactool, etc)
- Switchbrew (libnx, switch-examples)
- DevkitPro (toolchain)

390
src/main.cpp Normal file
View File

@ -0,0 +1,390 @@
#include <cstring>
#include <span>
#include <algorithm> // for min
#include <bit> // for byteswap
#include <switch.h>
namespace {
constexpr u64 INNER_HEAP_SIZE = 0x4000; // Size of the inner heap (adjust as necessary).
constexpr u64 READ_BUFFER_SIZE = 0x1000; // size of buffer which memory is read into
constexpr u32 FW_VER_ANY = 0x0;
constexpr u16 REGEX_SKIP = 0x100;
u32 FW_VERSION{}; // set on startup
u32 AMS_VERSION{}; // set on startup
struct DebugEventInfo {
u32 event_type;
u32 flags;
u64 thread_id;
u64 title_id;
u64 process_id;
char process_name[12];
u32 mmu_flags;
u8 _0x30[0x10];
};
struct PatternData {
constexpr PatternData(const char* s) {
// skip leading 0x (if any)
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
s += 2;
}
// invalid string will cause a compile-time error due to no return
constexpr auto hexstr_2_nibble = [](char c) -> u8 {
if (c >= 'A' && c <= 'F') { return c - 'A' + 10; }
if (c >= 'a' && c <= 'f') { return c - 'a' + 10; }
if (c >= '0' && c <= '9') { return c - '0'; }
};
// parse and convert string
while (*s != '\0') {
if (*s == '.') {
data[size] = REGEX_SKIP;
s++;
} else {
data[size] |= hexstr_2_nibble(*s++) << 4;
data[size] |= hexstr_2_nibble(*s++) << 0;
}
size++;
}
}
// 32 is a reasonable max length for a byte pattern
// will compile-time error is size is too small
u16 data[32]{};
u8 size{};
};
struct PatchData {
template<typename T>
constexpr PatchData(T _data) {
data = _data;
size = sizeof(T);
}
u64 data;
u8 size;
};
struct Patterns {
const char* patch_name; // name of patch
PatternData byte_pattern; // the pattern to search
s32 inst_offset; // instruction offset relative to byte pattern
s32 patch_offset; // patch offset relative to inst_offset
bool (*cond)(u32 inst); // check condtion of the instruction
PatchData (*patch)(u32 inst); // the patch data to be applied
u32 min_fw_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
u32 max_fw_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
u32 min_ams_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
u32 max_ams_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
};
struct PatchEntry {
const char* name; // name of the system title
u64 title_id; // title id of the system title
std::span<const Patterns> patterns; // list of patterns to find
u32 min_fw_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
u32 max_fw_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
};
constexpr auto subi_cond(u32 inst) -> bool {
// # Used on Atmosphère-NX 0.11.0 - 0.12.0.
const auto type = (inst >> 24) & 0xFF;
const auto imm = (inst >> 10) & 0xFFF;
return (type == 0x71) && (imm == 0x0A);
}
constexpr auto subr_cond(u32 inst) -> bool {
// # Used on Atmosphère-NX 0.13.0 and later.
const auto type = (inst >> 21) & 0x7F9;
const auto reg = (inst >> 16) & 0x1F;
return (type == 0x358) && (reg == 0x01);
}
constexpr auto bl_cond(u32 inst) -> bool {
return ((inst >> 26) & 0x3F) == 0x25;
}
constexpr auto tbz_cond(u32 inst) -> bool {
return ((inst >> 24) & 0x7F) == 0x36;
}
constexpr auto subs_cond(u32 inst) -> bool {
return subi_cond(inst) || subr_cond(inst);
}
constexpr auto cbz_cond(u32 inst) -> bool {
const auto type = inst >> 24;
return type == 0x34 || type == 0xB4;
};
constexpr auto mov_cond(u32 inst) -> bool {
return ((inst >> 24) & 0x7F) == 0x52;
};
constexpr auto mov2_cond(u32 inst) -> bool {
return (inst >> 24) == 0x2A;
};
constexpr auto bne_cond(u32 inst) -> bool {
const auto type = inst >> 24;
const auto cond = inst & 0x10;
return type == 0x54 || cond == 0x0;
}
// mov w0, wzr (w0 = 0)
constexpr auto ret0_patch(u32 inst) -> PatchData {
return std::byteswap(0xE0031F2A);
}
// nop
constexpr auto nop_patch(u32 inst) -> PatchData {
return std::byteswap(0x1F2003D5);
}
constexpr auto subs_patch(u32 inst) -> PatchData {
return subi_cond(inst) ? (u8)0x1 : (u8)0x0;
}
// b offset
constexpr auto b_patch(u32 inst) -> PatchData {
const auto opcode = 0x14;
const auto offset = (inst >> 5) & 0x7FFFF;
return opcode | offset;
}
// mov x0, xzr (x0 = 0)
constexpr auto mov0_patch(u32 inst) -> PatchData {
return std::byteswap(0xE0031FAA);
}
constexpr Patterns fs_patterns[] = {
{ "noacidsigchk1", "0xC8FE4739", -24, 0, bl_cond, ret0_patch },
{ "noacidsigchk2", "0x0210911F000072", -5, 0, bl_cond, ret0_patch },
{ "noncasigchk_old", "0x1E42B9", -5, 0, tbz_cond, nop_patch },
{ "noncasigchk_new", "0x3E4479", -5, 0, tbz_cond, nop_patch },
{ "nocntchk_old", "0x081C00121F05007181000054", -4, 0, bl_cond, ret0_patch },
{ "nocntchk_new", "0x081C00121F05007141010054", -4, 0, bl_cond, ret0_patch },
};
constexpr Patterns ldr_patterns[] = {
{ "noacidsigchk", "0xFD7BC6A8C0035FD6", 16, 2, subs_cond, subs_patch },
};
// todo: make patch for fw 14.0.0 - 14.1.2
constexpr Patterns es_patterns[] = {
{ "es", "0x1F90013128928052", -4, 0, cbz_cond, b_patch, FW_VER_ANY, MAKEHOSVERSION(13,2,1) },
{ "es", "0xC07240F9E1930091", -4, 0, tbz_cond, nop_patch, FW_VER_ANY, MAKEHOSVERSION(10,2,0) },
{ "es", "0xF3031FAA02000014", -4, 0, bne_cond, nop_patch, FW_VER_ANY, MAKEHOSVERSION(10,2,0) },
{ "es", "0xC0FDFF35A8C35838", -4, 0, mov_cond, nop_patch, MAKEHOSVERSION(11,0,0), MAKEHOSVERSION(13,2,1) },
{ "es", "0xE023009145EEFF97", -4, 0, cbz_cond, b_patch, MAKEHOSVERSION(11,0,0), MAKEHOSVERSION(13,2,1) },
{ "es", "0x.6300...0094A0..D1..FF97", 16, 0, mov2_cond, mov0_patch, MAKEHOSVERSION(15,0,0) },
};
// NOTE: add system titles that you want to be patched to this table.
// a list of system titles can be found here https://switchbrew.org/wiki/Title_list
constexpr PatchEntry patches[] = {
{ "fs", 0x0100000000000000, fs_patterns },
// ldr needs to be patched in fw 10+
{ "ldr", 0x0100000000000001, ldr_patterns, MAKEHOSVERSION(10,0,0) },
// es was added in fw 2
{ "es", 0x0100000000000033, es_patterns, MAKEHOSVERSION(2,0,0) },
};
auto patcher(Handle handle, std::span<const u8> data, u64 addr, std::span<const Patterns> patterns) -> bool {
for (auto& p : patterns) {
// skip if version isn't valid
if ((p.min_fw_ver && p.min_fw_ver > FW_VERSION) ||
(p.max_fw_ver && p.max_fw_ver < FW_VERSION) ||
(p.min_ams_ver && p.min_ams_ver > AMS_VERSION) ||
(p.max_ams_ver && p.max_ams_ver < AMS_VERSION)) {
continue;
}
if (p.byte_pattern.size >= data.size()) {
continue;
}
for (u32 i = 0; i < data.size(); i++) {
if (i + p.byte_pattern.size >= data.size()) {
break;
}
// loop through every byte of the pattern data to find a match
// skipping over any bytes if the value is REGEX_SKIP
u32 count{};
while (count < p.byte_pattern.size) {
if (p.byte_pattern.data[count] != data[i + count] && p.byte_pattern.data[count] != REGEX_SKIP) {
break;
}
count++;
}
// if we have found a matching pattern
if (count == p.byte_pattern.size) {
// fetch the instruction
u32 inst{};
const auto inst_offset = i + p.inst_offset;
std::memcpy(&inst, data.data() + inst_offset, sizeof(inst));
// check if the instruction is the one that we want
if (p.cond(inst)) {
const auto [patch_data, patch_size] = p.patch(inst);
const auto patch_offset = addr + inst_offset + p.patch_offset;
// todo: log failed writes, although this should in theory never fail
if (R_FAILED(svcWriteDebugProcessMemory(handle, &patch_data, patch_offset, patch_size))) {
} else {
// todo: log that this was successful
}
break; // move onto next pattern
}
}
}
}
return false;
}
auto apply_patch(const PatchEntry& patch) -> bool {
Handle handle{};
DebugEventInfo event_info{};
u64 pids[0x50]{};
s32 process_count{};
static u8 buffer[READ_BUFFER_SIZE];
// skip if version isn't valid
if ((patch.min_fw_ver && patch.min_fw_ver > FW_VERSION) ||
(patch.max_fw_ver && patch.max_fw_ver < FW_VERSION)) {
return true;
}
if (R_FAILED(svcGetProcessList(&process_count, pids, 0x50))) {
return false;
}
for (s32 i = 0; i < (process_count - 1); i++) {
if (R_SUCCEEDED(svcDebugActiveProcess(&handle, pids[i])) &&
R_SUCCEEDED(svcGetDebugEvent(&event_info, handle)) &&
patch.title_id == event_info.title_id) {
MemoryInfo mem_info{};
u64 addr{};
u32 page_info{};
for (;;) {
if (R_FAILED(svcQueryDebugProcessMemory(&mem_info, &page_info, handle, addr))) {
break;
}
addr = mem_info.addr + mem_info.size;
// if addr=0 then we hit the reserved memory section
if (!addr) {
break;
}
// skip memory that we don't want
if (!mem_info.size || (mem_info.perm & Perm_Rx) != Perm_Rx || ((mem_info.type & 0xFF) != MemType_CodeStatic)) {
continue;
}
// todo: the byte pattern can in between 2 READ_BUFFER_SIZE boundries!
for (u64 sz = 0; sz < mem_info.size; sz += READ_BUFFER_SIZE) {
const auto actual_size = std::min(READ_BUFFER_SIZE, mem_info.size);
if (R_FAILED(svcReadDebugProcessMemory(buffer, handle, mem_info.addr + sz, actual_size))) {
// todo: log failed reads!
continue;
} else {
patcher(handle, std::span{buffer, actual_size}, mem_info.addr + sz, patch.patterns);
}
}
}
svcCloseHandle(handle);
return true;
} else if (handle) {
svcCloseHandle(handle);
handle = 0;
}
}
return false;
}
} // namespace
int main(int argc, char* argv[]) {
for (auto& patch : patches) {
apply_patch(patch);
}
// note: sysmod exits here.
// to keep it running, add a for (;;) loop (remember to sleep!)
return 0;
}
// libnx stuff goes below
extern "C" {
// Sysmodules should not use applet*.
u32 __nx_applet_type = AppletType_None;
// Sysmodules will normally only want to use one FS session.
u32 __nx_fs_num_sessions = 1;
// Newlib heap configuration function (makes malloc/free work).
void __libnx_initheap(void) {
static char inner_heap[INNER_HEAP_SIZE];
extern char* fake_heap_start;
extern char* fake_heap_end;
// Configure the newlib heap.
fake_heap_start = inner_heap;
fake_heap_end = inner_heap + sizeof(inner_heap);
}
// Service initialization.
void __appInit(void) {
Result rc{};
// Open a service manager session.
if (R_FAILED(rc = smInitialize()))
fatalThrow(rc);
// Retrieve the current version of Horizon OS.
if (R_SUCCEEDED(rc = setsysInitialize())) {
SetSysFirmwareVersion fw{};
if (R_SUCCEEDED(rc = setsysGetFirmwareVersion(&fw))) {
FW_VERSION = MAKEHOSVERSION(fw.major, fw.minor, fw.micro);
hosversionSet(FW_VERSION);
}
setsysExit();
}
// get ams version
if (R_SUCCEEDED(rc = splInitialize())) {
u64 v{};
if (R_SUCCEEDED(rc = splGetConfig((SplConfigItem)65000, &v))) {
AMS_VERSION = (v >> 16) & 0xFFFFFF;
}
splExit();
}
// Add other services you want to use here.
if (R_FAILED(rc = pmdmntInitialize()))
fatalThrow(rc);
// Close the service manager session.
smExit();
}
// Service deinitialization.
void __appExit(void) {
pmdmntExit();
}
} // extern "C"

172
sys-patch.json Normal file
View File

@ -0,0 +1,172 @@
{
"name": "sys-patch",
"title_id": "0x420000000000000B",
"title_id_range_min": "0x420000000000000B",
"title_id_range_max": "0x420000000000000B",
"main_thread_stack_size": "0x1000",
"main_thread_priority": 49,
"default_cpu_id": 3,
"process_category": 1,
"is_retail": true,
"pool_partition": 2,
"is_64_bit": true,
"address_space_type": 1,
"filesystem_access": {
"permissions": "0xffffffffffffffff"
},
"service_access": ["*"],
"service_host": ["patch"],
"kernel_capabilities": [{
"type": "kernel_flags",
"value": {
"highest_thread_priority": 63,
"lowest_thread_priority": 24,
"lowest_cpu_id": 3,
"highest_cpu_id": 3
}
}, {
"type": "syscalls",
"value": {
"svcUnknown": "0x00",
"svcSetHeapSize": "0x01",
"svcSetMemoryPermission": "0x02",
"svcSetMemoryAttribute": "0x03",
"svcMapMemory": "0x04",
"svcUnmapMemory": "0x05",
"svcQueryMemory": "0x06",
"svcExitProcess": "0x07",
"svcCreateThread": "0x08",
"svcStartThread": "0x09",
"svcExitThread": "0x0a",
"svcSleepThread": "0x0b",
"svcGetThreadPriority": "0x0c",
"svcSetThreadPriority": "0x0d",
"svcGetThreadCoreMask": "0x0e",
"svcSetThreadCoreMask": "0x0f",
"svcGetCurrentProcessorNumber": "0x10",
"svcSignalEvent": "0x11",
"svcClearEvent": "0x12",
"svcMapSharedMemory": "0x13",
"svcUnmapSharedMemory": "0x14",
"svcCreateTransferMemory": "0x15",
"svcCloseHandle": "0x16",
"svcResetSignal": "0x17",
"svcWaitSynchronization": "0x18",
"svcCancelSynchronization": "0x19",
"svcArbitrateLock": "0x1a",
"svcArbitrateUnlock": "0x1b",
"svcWaitProcessWideKeyAtomic": "0x1c",
"svcSignalProcessWideKey": "0x1d",
"svcGetSystemTick": "0x1e",
"svcConnectToNamedPort": "0x1f",
"svcSendSyncRequestLight": "0x20",
"svcSendSyncRequest": "0x21",
"svcSendSyncRequestWithUserBuffer": "0x22",
"svcSendAsyncRequestWithUserBuffer": "0x23",
"svcGetProcessId": "0x24",
"svcGetThreadId": "0x25",
"svcBreak": "0x26",
"svcOutputDebugString": "0x27",
"svcReturnFromException": "0x28",
"svcGetInfo": "0x29",
"svcFlushEntireDataCache": "0x2a",
"svcFlushDataCache": "0x2b",
"svcMapPhysicalMemory": "0x2c",
"svcUnmapPhysicalMemory": "0x2d",
"svcGetFutureThreadInfo": "0x2e",
"svcGetLastThreadInfo": "0x2f",
"svcGetResourceLimitLimitValue": "0x30",
"svcGetResourceLimitCurrentValue": "0x31",
"svcSetThreadActivity": "0x32",
"svcGetThreadContext3": "0x33",
"svcWaitForAddress": "0x34",
"svcSignalToAddress": "0x35",
"svcUnknown": "0x36",
"svcUnknown": "0x37",
"svcUnknown": "0x38",
"svcUnknown": "0x39",
"svcUnknown": "0x3a",
"svcUnknown": "0x3b",
"svcDumpInfo": "0x3c",
"svcDumpInfoNew": "0x3d",
"svcUnknown": "0x3e",
"svcUnknown": "0x3f",
"svcCreateSession": "0x40",
"svcAcceptSession": "0x41",
"svcReplyAndReceiveLight": "0x42",
"svcReplyAndReceive": "0x43",
"svcReplyAndReceiveWithUserBuffer": "0x44",
"svcCreateEvent": "0x45",
"svcUnknown": "0x46",
"svcUnknown": "0x47",
"svcMapPhysicalMemoryUnsafe": "0x48",
"svcUnmapPhysicalMemoryUnsafe": "0x49",
"svcSetUnsafeLimit": "0x4a",
"svcCreateCodeMemory": "0x4b",
"svcControlCodeMemory": "0x4c",
"svcSleepSystem": "0x4d",
"svcReadWriteRegister": "0x4e",
"svcSetProcessActivity": "0x4f",
"svcCreateSharedMemory": "0x50",
"svcMapTransferMemory": "0x51",
"svcUnmapTransferMemory": "0x52",
"svcCreateInterruptEvent": "0x53",
"svcQueryPhysicalAddress": "0x54",
"svcQueryIoMapping": "0x55",
"svcCreateDeviceAddressSpace": "0x56",
"svcAttachDeviceAddressSpace": "0x57",
"svcDetachDeviceAddressSpace": "0x58",
"svcMapDeviceAddressSpaceByForce": "0x59",
"svcMapDeviceAddressSpaceAligned": "0x5a",
"svcMapDeviceAddressSpace": "0x5b",
"svcUnmapDeviceAddressSpace": "0x5c",
"svcInvalidateProcessDataCache": "0x5d",
"svcStoreProcessDataCache": "0x5e",
"svcFlushProcessDataCache": "0x5f",
"svcDebugActiveProcess": "0x60",
"svcBreakDebugProcess": "0x61",
"svcTerminateDebugProcess": "0x62",
"svcGetDebugEvent": "0x63",
"svcContinueDebugEvent": "0x64",
"svcGetProcessList": "0x65",
"svcGetThreadList": "0x66",
"svcGetDebugThreadContext": "0x67",
"svcSetDebugThreadContext": "0x68",
"svcQueryDebugProcessMemory": "0x69",
"svcReadDebugProcessMemory": "0x6a",
"svcWriteDebugProcessMemory": "0x6b",
"svcSetHardwareBreakPoint": "0x6c",
"svcGetDebugThreadParam": "0x6d",
"svcUnknown": "0x6e",
"svcGetSystemInfo": "0x6f",
"svcCreatePort": "0x70",
"svcManageNamedPort": "0x71",
"svcConnectToPort": "0x72",
"svcSetProcessMemoryPermission": "0x73",
"svcMapProcessMemory": "0x74",
"svcUnmapProcessMemory": "0x75",
"svcQueryProcessMemory": "0x76",
"svcMapProcessCodeMemory": "0x77",
"svcUnmapProcessCodeMemory": "0x78",
"svcCreateProcess": "0x79",
"svcStartProcess": "0x7a",
"svcTerminateProcess": "0x7b",
"svcGetProcessInfo": "0x7c",
"svcCreateResourceLimit": "0x7d",
"svcSetResourceLimitLimitValue": "0x7e",
"svcCallSecureMonitor": "0x7f"
}
}, {
"type": "min_kernel_version",
"value": "0x0030"
}, {
"type": "handle_table_size",
"value": 64
}, {
"type": "debug_flags",
"value": {
"allow_debug": false,
"force_debug": true
}
}]
}

5
toolbox.json Normal file
View File

@ -0,0 +1,5 @@
{
"name" : "sys-patch",
"tid" : "420000000000000B",
"requires_reboot": false
}