From cb197bcf404eba1c67e2c63a5e05ac65d3a50d0a Mon Sep 17 00:00:00 2001 From: Jemma Poffinbarger Date: Tue, 9 Apr 2024 08:15:30 -0500 Subject: [PATCH] Account Settings and eShop app support (#17) * Added support for eShop and account settings apps (account settings is currently broken) * Moved account settings patch to run at application start --- .gitignore | 1 + src/main.cpp | 2 + src/patches/account_settings.cpp | 136 +++++++++++++++++++++++++++++++ src/patches/account_settings.h | 20 +++++ src/patches/eshop_applet.cpp | 103 +++++++++++++++++++++++ 5 files changed, 262 insertions(+) create mode 100644 src/patches/account_settings.cpp create mode 100644 src/patches/account_settings.h create mode 100644 src/patches/eshop_applet.cpp diff --git a/.gitignore b/.gitignore index 91fd97e..982b963 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ *.elf *.wps certs/ +*.lst \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index e624029..e5a91df 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -61,6 +61,7 @@ WUPS_USE_WUT_DEVOPTAB(); #include #include #include +#include "patches/account_settings.h" //thanks @Gary#4139 :p static void write_string(uint32_t addr, const char* str) @@ -162,6 +163,7 @@ ON_APPLICATION_START() { setup_olv_libs(); matchmaking_notify_titleswitch(); + patchAccountSettings(); } ON_APPLICATION_ENDS() { diff --git a/src/patches/account_settings.cpp b/src/patches/account_settings.cpp new file mode 100644 index 0000000..6a495e5 --- /dev/null +++ b/src/patches/account_settings.cpp @@ -0,0 +1,136 @@ +/* Copyright 2023 Pretendo Network contributors + Copyright 2023 Jemma Poffinbarger + Copyright 2023 Ash Logan + Copyright 2019 Maschell + + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby + granted, provided that the above copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "config.h" +#include "olv_urls.h" +#include "utils/logger.h" +#include "utils/replace_mem.h" + +#include +#include +#include +#include +#include +#include + +#include "ca_pem.h" // generated at buildtime + +#define ACCOUNT_SETTINGS_TID_J 0x000500101004B000 +#define ACCOUNT_SETTINGS_TID_U 0x000500101004B100 +#define ACCOUNT_SETTINGS_TID_E 0x000500101004B200 + +const char whitelist_original[] = { + 0x68, 0x74, 0x74, 0x70, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x2E, + 0x6E, 0x69, 0x6E, 0x74, 0x65, 0x6E, 0x64, 0x6F, 0x2E, 0x6E, 0x65, 0x74 +}; + +const char whitelist_new[] = { + 0x68, 0x74, 0x74, 0x70, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x2E, + 0x70, 0x72, 0x65, 0x74, 0x65, 0x6E, 0x64, 0x6F, 0x2E, 0x63, 0x63, 0x00 +}; + +const char wave_original[] = "saccount.nintendo.net"; + +const char wave_new[] = "saccount.pretendo.cc"; + +bool isAccountSettingsTitle(); + +static std::optional rootca_pem_handle{}; + +DECL_FUNCTION(int, FSOpenFile_accSettings, FSClient *client, FSCmdBlock *block, char *path, const char *mode, uint32_t *handle, + int error) { + if(!isAccountSettingsTitle()) { + return real_FSOpenFile_accSettings(client, block, path, mode, handle, error); + } + + if (!Config::connect_to_network) { + DEBUG_FUNCTION_LINE("Inkay: account settings patches skipped."); + return real_FSOpenFile_accSettings(client, block, path, mode, handle, error); + } + + // Check for root CA file and take note of its handle + if (strcmp("vol/content/browser/rootca.pem", path) == 0) { + int ret = real_FSOpenFile_accSettings(client, block, path, mode, handle, error); + rootca_pem_handle = *handle; + DEBUG_FUNCTION_LINE("Inkay: Found account settings CA, replacing..."); + return ret; + } + return real_FSOpenFile_accSettings(client, block, path, mode, handle, error); +} + +DECL_FUNCTION(FSStatus, FSReadFile_accSettings, FSClient *client, FSCmdBlock *block, uint8_t *buffer, uint32_t size, uint32_t count, + FSFileHandle handle, uint32_t unk1, uint32_t flags) { + if(!isAccountSettingsTitle()) { + return real_FSReadFile_accSettings(client, block, buffer, size, count, handle, unk1, flags); + } + if (size != 1) { + DEBUG_FUNCTION_LINE("Inkay: account settings CA replacement failed!"); + } + + if (rootca_pem_handle && *rootca_pem_handle == handle) { + strlcpy((char *) buffer, (const char *) ca_pem, size * count); + return (FSStatus) count; + } + return real_FSReadFile_accSettings(client, block, buffer, size, count, handle, unk1, flags); +} + +DECL_FUNCTION(FSStatus, FSCloseFile_accSettings, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask) { + if(!isAccountSettingsTitle()) { + return real_FSCloseFile_accSettings(client, block, handle, errorMask); + } + if (handle == rootca_pem_handle) { + rootca_pem_handle.reset(); + } + return real_FSCloseFile_accSettings(client, block, handle, errorMask); +} + +bool isAccountSettingsTitle() { + return (OSGetTitleID() != 0 && ( + OSGetTitleID() == ACCOUNT_SETTINGS_TID_J || + OSGetTitleID() == ACCOUNT_SETTINGS_TID_U || + OSGetTitleID() == ACCOUNT_SETTINGS_TID_E + )); +} + +bool patchAccountSettings() { + if(!isAccountSettingsTitle()) { + return false; + } + + if (!Config::connect_to_network) { + DEBUG_FUNCTION_LINE("Inkay: account settings patches skipped."); + return false; + } + + DEBUG_FUNCTION_LINE("Inkay: hewwo account settings!\n"); + + if (!replace(0x10000000, 0x10000000, wave_original, sizeof(wave_original), wave_new, sizeof(wave_new))) { + DEBUG_FUNCTION_LINE("Inkay: We didn't find the url /)>~<(\\"); + return false; + } + + if (!replace(0x10000000, 0x10000000, whitelist_original, sizeof(whitelist_original), whitelist_new, sizeof(whitelist_new))) { + DEBUG_FUNCTION_LINE("Inkay: We didn't find the whitelist /)>~<(\\"); + return false; + } + + return true; +} + +WUPS_MUST_REPLACE_FOR_PROCESS(FSOpenFile_accSettings, WUPS_LOADER_LIBRARY_COREINIT, FSOpenFile, WUPS_FP_TARGET_PROCESS_GAME); +WUPS_MUST_REPLACE_FOR_PROCESS(FSReadFile_accSettings, WUPS_LOADER_LIBRARY_COREINIT, FSReadFile, WUPS_FP_TARGET_PROCESS_GAME); +WUPS_MUST_REPLACE_FOR_PROCESS(FSCloseFile_accSettings, WUPS_LOADER_LIBRARY_COREINIT, FSCloseFile, WUPS_FP_TARGET_PROCESS_GAME); diff --git a/src/patches/account_settings.h b/src/patches/account_settings.h new file mode 100644 index 0000000..6d09e68 --- /dev/null +++ b/src/patches/account_settings.h @@ -0,0 +1,20 @@ +/* Copyright 2023 Pretendo Network contributors + Copyright 2023 Jemma Poffinbarger + Copyright 2023 Ash Logan + Copyright 2019 Maschell + + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby + granted, provided that the above copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ + +#pragma once + +bool isAccountSettingsTitle(); + +bool patchAccountSettings(); \ No newline at end of file diff --git a/src/patches/eshop_applet.cpp b/src/patches/eshop_applet.cpp new file mode 100644 index 0000000..3775b0b --- /dev/null +++ b/src/patches/eshop_applet.cpp @@ -0,0 +1,103 @@ +/* Copyright 2023 Pretendo Network contributors + Copyright 2023 Ash Logan + Copyright 2019 Maschell + + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby + granted, provided that the above copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "config.h" +#include "olv_urls.h" +#include "utils/logger.h" +#include "utils/replace_mem.h" + +#include +#include +#include +#include +#include + +#include "ca_pem.h" // generated at buildtime + +const char wave_original[] = "https://ninja.wup.shop.nintendo.net/ninja/wood_index.html?"; +const char wave_new[] = "http://samurai.wup.shop.pretendo.cc/ninja/wood_index.html?"; + +const char whitelist_original[] = { + 0x68, 0x74, 0x74, 0x70, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x75, 0x72, 0x61, 0x69, 0x2E, + 0x77, 0x75, 0x70, 0x2E, 0x73, 0x68, 0x6F, 0x70, 0x2E, 0x6E, 0x69, 0x6E, + 0x74, 0x65, 0x6E, 0x64, 0x6F, 0x2E, 0x6E, 0x65, 0x74 +}; + +const char whitelist_new[] = { + 0x68, 0x74, 0x74, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x75, 0x72, 0x61, 0x69, 0x2E, + 0x77, 0x75, 0x70, 0x2E, 0x73, 0x68, 0x6F, 0x70, 0x2E, 0x70, 0x72, 0x65, + 0x74, 0x65, 0x6E, 0x64, 0x6F, 0x2E, 0x63, 0x63, 0x00 +}; + +static std::optional rootca_pem_handle{}; + +DECL_FUNCTION(int, FSOpenFile_eShop, FSClient *client, FSCmdBlock *block, char *path, const char *mode, uint32_t *handle, + int error) { + const char *initialOma = "vol/content/initial.oma"; + + if (!Config::connect_to_network) { + DEBUG_FUNCTION_LINE("Inkay: eShop patches skipped."); + return real_FSOpenFile_eShop(client, block, path, mode, handle, error); + } + + if (strcmp(initialOma, path) == 0) { + //below is a hacky (yet functional!) way to get Inkay to redirect URLs from the Miiverse applet + //we do it when loading this file since it should only load once, preventing massive lag spikes as it searches all of MEM2 xD + + DEBUG_FUNCTION_LINE("Inkay: hewwo eShop!\n"); + + if (!replace(0x10000000, 0x10000000, wave_original, sizeof(wave_original), wave_new, sizeof(wave_new))) + DEBUG_FUNCTION_LINE("Inkay: We didn't find the url /)>~<(\\"); + + if (!replace(0x10000000, 0x10000000, whitelist_original, sizeof(whitelist_original), whitelist_new, sizeof(whitelist_new))) + DEBUG_FUNCTION_LINE("Inkay: We didn't find the whitelist /)>~<(\\"); + + // Check for root CA file and take note of its handle + } else if (strcmp("vol/content/browser/rootca.pem", path) == 0) { + int ret = real_FSOpenFile_eShop(client, block, path, mode, handle, error); + rootca_pem_handle = *handle; + DEBUG_FUNCTION_LINE("Inkay: Found eShop CA, replacing..."); + return ret; + } + + return real_FSOpenFile_eShop(client, block, path, mode, handle, error); +} + +DECL_FUNCTION(FSStatus, FSReadFile_eShop, FSClient *client, FSCmdBlock *block, uint8_t *buffer, uint32_t size, uint32_t count, + FSFileHandle handle, uint32_t unk1, uint32_t flags) { + if (size != 1) { + DEBUG_FUNCTION_LINE("Inkay: eShop CA replacement failed!"); + } + + if (rootca_pem_handle && *rootca_pem_handle == handle) { + strlcpy((char *) buffer, (const char *) ca_pem, size * count); + return (FSStatus) count; + } + + return real_FSReadFile_eShop(client, block, buffer, size, count, handle, unk1, flags); +} + +DECL_FUNCTION(FSStatus, FSCloseFile_eShop, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask) { + if (handle == rootca_pem_handle) { + rootca_pem_handle.reset(); + } + + return real_FSCloseFile_eShop(client, block, handle, errorMask); +} + +WUPS_MUST_REPLACE_FOR_PROCESS(FSOpenFile_eShop, WUPS_LOADER_LIBRARY_COREINIT, FSOpenFile, WUPS_FP_TARGET_PROCESS_ESHOP); +WUPS_MUST_REPLACE_FOR_PROCESS(FSReadFile_eShop, WUPS_LOADER_LIBRARY_COREINIT, FSReadFile, WUPS_FP_TARGET_PROCESS_ESHOP); +WUPS_MUST_REPLACE_FOR_PROCESS(FSCloseFile_eShop, WUPS_LOADER_LIBRARY_COREINIT, FSCloseFile, WUPS_FP_TARGET_PROCESS_ESHOP);