Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 43 additions & 16 deletions .github/workflows/build-jobs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,51 @@ name: Build jobs

on:
workflow_call:
workflow_dispatch:
push:

jobs:
build:
name: build
runs-on: ubuntu-latest
container: ghcr.io/pgalonza/devkita64-atmosphere:latest
#or you can use a generic one
#container: devkitpro/devkita64:latest
container: devkitpro/devkita64

steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Build sys-patch
run: |
make dist -j $(nproc)
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: sys-patch-artifacts
path: ./sys-patch.zip
- name: Checkout
uses: actions/checkout@v4
with:
fetch-tags: true
path: sys-patch
submodules: recursive

- name: Build sys-patch
run: |
make -C sys-patch -j$(nproc) dist && \
VERSION=$(grep 'export VERSION := ' sys-patch/Makefile | cut -c 19-)
TAGVERSION=$(curl -s https://api.github.com/repos/$GITHUB_REPOSITORY/releases/latest | grep "tag_name" | head -1 | cut -d '"' -f 4)
echo "VERSION=${VERSION}" >> $GITHUB_ENV
echo "TAGVERSION=${TAGVERSION}" >> $GITHUB_ENV

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
include-hidden-files: true
overwrite: true
name: sys-patch-${{ env.VERSION }}
path: sys-patch/out/

- name: Fetch git cli and upload release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [ ${{ env.TAGVERSION }} = v${{ env.VERSION }} ];
then echo "Tag version and makefile version are same, don't publish release, only artifact uploaded."
else
wget -q $(curl -s https://api.github.com/repos/cli/cli/releases/latest | grep "browser_download_url" | grep "linux_amd64.tar.gz" | head -1 | cut -d '"' -f 4) && \
tar -xzf gh*.tar.gz && \
chmod +x gh*/bin/gh && \
chmod +x gh*/bin/gh && \
cp gh*/bin/gh /bin/gh && \
rm gh*.tar.gz && \
rm -rf gh*
gh release create v${{ env.VERSION }} sys-patch/sys-patch.zip --title "Sys-patch version ${{ env.VERSION }}" --repo github.com/$GITHUB_REPOSITORY
fi
14 changes: 0 additions & 14 deletions .github/workflows/build.yml

This file was deleted.

38 changes: 0 additions & 38 deletions .github/workflows/release.yml

This file was deleted.

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ MAKEFILES := sysmod overlay
TARGETS := $(foreach dir,$(MAKEFILES),$(CURDIR)/$(dir))

# the below was taken from atmosphere + switch-examples makefile
export VERSION := 1.5.4
export VERSION := 1.5.5

ifneq ($(strip $(shell git symbolic-ref --short HEAD 2>/dev/null)),)
export GIT_BRANCH := $(shell git symbolic-ref --short HEAD)
Expand Down
14 changes: 5 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# sys-patch

A script-like system module that patches **fs**, **es**, **ldr** and **nifm** on boot.
A script-like system module that patches **fs**, **es**, **ldr**, **nifm** and **nim** on boot.

---

Expand Down Expand Up @@ -57,14 +57,10 @@ To activate the sys-module, reboot your switch, or, use [sysmodules overlay](htt

Here's a quick run down of what's being patched:

- **fs**
- **es**
- **ldr**
- **nifm**

**fs** and **es** need new patches after every new firmware version.
**ldr** needs new patches after every new [Atmosphere](https://github.com/Atmosphere-NX/Atmosphere/) release.
**nifm** ctest patch allows the device to connect to a network without needing to make a connection to a server.
- **fs** and **es** need new patches after every new firmware version.
- **ldr** needs new patches after every new [Atmosphere](https://github.com/Atmosphere-NX/Atmosphere/) release.
- **nifm** ctest patch allows the device to connect to a network without needing to make a connection to a server
- **nim** patches to the ssl function call within nim that queries "https://api.hac.%.ctest.srv.nintendo.net/v1/time", and crashes the console if console ssl certificate is not intact. This patch instead makes the console not crash.

The patches are applied on boot. Once done, the sys-module stops running.
The memory footprint *(16kib)* and the binary size *(~50kib)* are both very small.
Expand Down
10 changes: 8 additions & 2 deletions overlay/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ class GuiToggle final : public tsl::Gui {
list->addItem(config_noacidsigchk2.create_list_item("noacidsigchk2"));
list->addItem(config_noncasigchk_old.create_list_item("noncasigchk_old"));
list->addItem(config_noncasigchk_new.create_list_item("noncasigchk_new"));
list->addItem(config_noncasigchk_new2.create_list_item("noncasigchk_new2"));
list->addItem(config_nocntchk.create_list_item("nocntchk"));
list->addItem(config_nocntchk2.create_list_item("nocntchk2"));

Expand All @@ -121,6 +120,11 @@ class GuiToggle final : public tsl::Gui {
list->addItem(new tsl::elm::CategoryHeader("NIM - 0100000000000025"));
list->addItem(config_nim.create_list_item("nim"));

list->addItem(new tsl::elm::CategoryHeader("Disable CA Verification - apply all"));
list->addItem(config_ssl1.create_list_item("disablecaverification1"));
list->addItem(config_ssl2.create_list_item("disablecaverification2"));
list->addItem(config_ssl3.create_list_item("disablecaverification3"));

frame->setContent(list);
return frame;
}
Expand All @@ -129,7 +133,6 @@ class GuiToggle final : public tsl::Gui {
ConfigEntry config_noacidsigchk2{"fs", "noacidsigchk2", true};
ConfigEntry config_noncasigchk_old{"fs", "noncasigchk_old", true};
ConfigEntry config_noncasigchk_new{"fs", "noncasigchk_new", true};
ConfigEntry config_noncasigchk_new2{"fs", "noncasigchk_new2", true};
ConfigEntry config_nocntchk{"fs", "nocntchk", true};
ConfigEntry config_nocntchk2{"fs", "nocntchk2", true};
ConfigEntry config_noacidsigchk{"ldr", "noacidsigchk", true};
Expand All @@ -138,6 +141,9 @@ class GuiToggle final : public tsl::Gui {
ConfigEntry config_es3{"es", "es3", true};
ConfigEntry config_ctest{"nifm", "ctest", true};
ConfigEntry config_nim{"nim", "nim", true};
ConfigEntry config_ssl1{"ssl", "disablecaverification1", false};
ConfigEntry config_ssl2{"ssl", "disablecaverification2", false};
ConfigEntry config_ssl3{"ssl", "disablecaverification3", false};
};

class GuiLog final : public tsl::Gui {
Expand Down
72 changes: 57 additions & 15 deletions sysmod/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ constexpr auto subr_cond(u32 inst) -> bool {
}

constexpr auto bl_cond(u32 inst) -> bool {
return ((inst >> 26) & 0x3F) == 0x25;
const auto type = inst >> 24;
return type == 0x25 || type == 0x94;
}

constexpr auto tbz_cond(u32 inst) -> bool {
Expand Down Expand Up @@ -169,6 +170,10 @@ constexpr auto mov2_cond(u32 inst) -> bool {
}
}

constexpr auto mov3_cond(u32 inst) -> bool {
return (inst >> 24) == 0xD2; // mov x10, #0x3
}

constexpr auto and_cond(u32 inst) -> bool {
return ((inst >> 24) & 0x1F) == 0x0A;
}
Expand All @@ -183,6 +188,14 @@ constexpr auto bne_cond(u32 inst) -> bool {
return type == 0x54 || cond == 0x0;
}

constexpr auto beq_cond(u32 inst) -> bool {
return (inst >> 24) == 0x54; // beq, 0x710011c94c
}

constexpr auto str_cond(u32 inst) -> bool {
return (inst >> 24) == 0xB9; // str, w8,[x19, #0x15c]
}

constexpr auto ctest_cond(u32 inst) -> bool {
return std::byteswap(0xF50301AA) == inst; // mov x21, x1
}
Expand All @@ -195,6 +208,8 @@ constexpr PatchData nop_patch_data{ "0x1F2003D5" };
constexpr PatchData mov0_patch_data{ "0xE0031FAA" };
//mov x2, xzr
constexpr PatchData mov2_patch_data{ "0xE2031FAA" };
constexpr PatchData ssl1_patch_data{ "0x0A" };
constexpr PatchData ssl2_patch_data{ "0x08008052" };
constexpr PatchData ctest_patch_data{ "0x00309AD2001EA1F2610100D4E0031FAAC0035FD6" };

constexpr auto ret0_patch(u32 inst) -> PatchData { return ret0_patch_data; }
Expand All @@ -203,6 +218,8 @@ constexpr auto nop_patch(u32 inst) -> PatchData { return nop_patch_data; }
constexpr auto subs_patch(u32 inst) -> PatchData { return subi_cond(inst) ? (u8)0x1 : (u8)0x0; }
constexpr auto mov0_patch(u32 inst) -> PatchData { return mov0_patch_data; }
constexpr auto mov2_patch(u32 inst) -> PatchData { return mov2_patch_data; }
constexpr auto ssl1_patch(u32 inst) -> PatchData { return ssl1_patch_data; }
constexpr auto ssl2_patch(u32 inst) -> PatchData { return ssl2_patch_data; }
constexpr auto ctest_patch(u32 inst) -> PatchData { return ctest_patch_data; }

constexpr auto b_patch(u32 inst) -> PatchData {
Expand Down Expand Up @@ -243,22 +260,29 @@ constexpr auto mov2_applied(const u8* data, u32 inst) -> bool {
return mov2_patch(inst).cmp(data);
}

constexpr auto ssl1_applied(const u8* data, u32 inst) -> bool {
return ssl1_patch(inst).cmp(data);
}

constexpr auto ssl2_applied(const u8* data, u32 inst) -> bool {
return ssl2_patch(inst).cmp(data);
}

constexpr auto ctest_applied(const u8* data, u32 inst) -> bool {
return ctest_patch(inst).cmp(data);
}

constinit Patterns fs_patterns[] = {
{ "noacidsigchk1", "0xC8FE4739", -24, 0, bl_cond, ret0_patch, ret0_applied, true, FW_VER_ANY, MAKEHOSVERSION(9,2,0) },
{ "noacidsigchk2", "0x0210911F000072", -5, 0, bl_cond, ret0_patch, ret0_applied, true, FW_VER_ANY, MAKEHOSVERSION(9,2,0) },
{ "noncasigchk_old", "0x1E42B9", -5, 0, tbz_cond, nop_patch, nop_applied, true, MAKEHOSVERSION(10,0,0), MAKEHOSVERSION(14,2,1) },
{ "noncasigchk_new", "0x3E4479", -5, 0, tbz_cond, nop_patch, nop_applied, true, MAKEHOSVERSION(15,0,0), MAKEHOSVERSION(16,1,0) },
{ "noncasigchk_new2", "0x258052", -5, 0, tbz_cond, nop_patch, nop_applied, true, MAKEHOSVERSION(17,0,0), FW_VER_ANY },
{ "nocntchk", "0x081C00121F050071..0054", -4, 0, bl_cond, ret0_patch, ret0_applied, true, MAKEHOSVERSION(10,0,0), MAKEHOSVERSION(18,1,0) },
{ "nocntchk2", "0x091C00123F05007161010054", -8, 0, bl_cond, ret0_patch, ret0_applied, true, MAKEHOSVERSION(19,0,0), FW_VER_ANY },
{ "noncasigchk_old", "0x0036.......71..0054..4839", -2, 0, tbz_cond, nop_patch, nop_applied, true, MAKEHOSVERSION(10,0,0), MAKEHOSVERSION(16,1,0) },
{ "noncasigchk_new", "0x.94..0036.258052", 2, 0, tbz_cond, nop_patch, nop_applied, true, MAKEHOSVERSION(17,0,0), FW_VER_ANY }, // 17.0.0 - 19.0.0+
{ "nocntchk", "0x40f9...9408.0012.050071", 2, 0, bl_cond, ret0_patch, ret0_applied, true, MAKEHOSVERSION(10,0,0), MAKEHOSVERSION(18,1,0) },
{ "nocntchk2", "0x40f9...94..40b9..0012", 2, 0, bl_cond, ret0_patch, ret0_applied, true, MAKEHOSVERSION(19,0,0), FW_VER_ANY },
};

constinit Patterns ldr_patterns[] = {
{ "noacidsigchk", "0xFD7B.A8C0035FD6", 16, 2, subs_cond, subs_patch, subs_applied, true, FW_VER_ANY },
{ "noacidsigchk", "0xFD7B.A8C0035FD6", 16, 2, subs_cond, subs_patch, subs_applied, true, FW_VER_ANY },
};

constinit Patterns es_patterns[] = {
Expand All @@ -273,7 +297,12 @@ constinit Patterns nifm_patterns[] = {

constinit Patterns nim_patterns[] = {
{ "nim", "0x.0F00351F2003D5", 8, 0, adr_cond, mov2_patch, mov2_applied, true, MAKEHOSVERSION(17,0,0), FW_VER_ANY },
// { "nim2", "0x600F00351F2003D5", 8, 0, adr_cond, mov2_patch, mov2_applied, true, MAKEHOSVERSION(19,0,0), FW_VER_ANY },
};

constinit Patterns ssl_patterns[] = {
{ "disablecaverification1", "0x6A0080D2", 0, 0, mov3_cond, ssl1_patch, ssl1_applied, false, FW_VER_ANY },
{ "disablecaverification2", "0x2409437AA0000054", 4, 0, beq_cond, ret1_patch, ret1_applied, false, FW_VER_ANY },
{ "disablecaverification3", "0x88160012", 4, 0, str_cond, ssl2_patch, ssl2_applied, false, FW_VER_ANY },
};

// NOTE: add system titles that you want to be patched to this table.
Expand All @@ -286,6 +315,7 @@ constinit PatchEntry patches[] = {
{ "es", 0x0100000000000033, es_patterns, MAKEHOSVERSION(2,0,0) },
{ "nifm", 0x010000000000000F, nifm_patterns },
{ "nim", 0x0100000000000025, nim_patterns },
{ "ssl", 0x0100000000000024, ssl_patterns },
};

struct EmummcPaths {
Expand Down Expand Up @@ -380,7 +410,8 @@ auto apply_patch(PatchEntry& patch) -> bool {

u64 pids[0x50]{};
s32 process_count{};
static u8 buffer[READ_BUFFER_SIZE];
constexpr u64 overlap_size = 0x4f;
static u8 buffer[READ_BUFFER_SIZE + overlap_size];

// skip if version isn't valid
if (VERSION_SKIP &&
Expand Down Expand Up @@ -419,16 +450,27 @@ auto apply_patch(PatchEntry& patch) -> bool {
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!
// u32 overlap_size = 0;
// for (const auto& pattern : patch.patterns) {
// overlap_size = std::max(overlap_size, static_cast<u32>(pattern.byte_pattern.size));
// }
// u8* buffer = (u8*)aligned_alloc(alignof(u8*), READ_BUFFER_SIZE + overlap_size);
// if (!buffer) {
// svcCloseHandle(handle);
// return false;
// }
for (u64 sz = 0; sz < mem_info.size; sz += READ_BUFFER_SIZE - overlap_size) {
const auto actual_size = std::min(READ_BUFFER_SIZE, mem_info.size - sz);
if (R_FAILED(svcReadDebugProcessMemory(buffer + overlap_size, handle, mem_info.addr + sz, actual_size))) {
break;
} else {
patcher(handle, std::span{buffer, actual_size}, mem_info.addr + sz, patch.patterns);
patcher(handle, std::span{buffer, actual_size + overlap_size}, mem_info.addr + sz - overlap_size, patch.patterns);
if (actual_size >= overlap_size) {
memcpy(buffer, buffer + actual_size, overlap_size);
}
}
}
// free(buffer);
}
svcCloseHandle(handle);
return true;
Expand Down