From 0bb4249a5f534ba87886a93c6a37cca5f62b6e5c Mon Sep 17 00:00:00 2001 From: KazushiM <85604869+KazushiMe@users.noreply.github.com> Date: Thu, 31 Mar 2022 17:06:40 +0800 Subject: [PATCH] Add info and script on patching sysmodules manually --- README.md | 23 +++ .../stratosphere/loader/source/oc/Makefile | 2 +- .../loader/source/oc/ldr_oc_suite.cpp | 11 +- .../stratosphere/loader/source/oc/test.cpp | 145 ++++++++++++++---- .../stratosphere/loader/source/oc/test.sh | 82 +++++++++- 5 files changed, 213 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index a3864240..00594540 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,29 @@ If you are to install nro forwarders, remove `R_TRY(ValidateAcidSignature(std::a Uncompress the kip to make it work with config editor: `hactool -t kip1 Atmosphere/stratosphere/loader/loader.kip --uncompress=Atmosphere/stratosphere/loader/loader.kip` +### [DEPRECATED] Patching sysmodules manually + +This method is NOT RECOMMENDED and NOT SUPPORTED ANY MORE, only served as reference. + +
+ + Tools: + - Lockpick_RCM + - TegraExplorer + - [hactool](https://github.com/SciresM/hactool) + - [nx2elf](https://github.com/shuffle2/nx2elf) + - elf2nso from [switch-tools](https://github.com/switchbrew/switch-tools/) + - [hacpack](https://github.com/The-4n/hacPack) + + 1. Dump `prod.keys` with Lockpick_RCM + 2. Dump HOS firmware with TegraExplorer + 3. Configure and run `test.sh` in `/Source/Atmosphere/stratosphere/loader/source/oc/` to generate patched pcv & ptm + 4. Replace nca in `SYSTEM:/Contents/registered/` with TegraExplorer + 5. `ValidateAcidSignature()` should be stubbed to allow unsigned sysmodules to load (a.k.a. `loader_patch`) + +
+ + ## Acknowledgement diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/Makefile b/Source/Atmosphere/stratosphere/loader/source/oc/Makefile index 798740d2..754342a6 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/Makefile +++ b/Source/Atmosphere/stratosphere/loader/source/oc/Makefile @@ -3,7 +3,7 @@ export CC := g++-11 all: test test: - $(CC) ldr_oc_suite.cpp test.cpp -o ./test -O2 -std=c++20 + $(CC) ldr_oc_suite.cpp test.cpp -o ./test -O2 -std=c++20 -Wall clean: rm ./test diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite.cpp index 8864947d..396059a6 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite.cpp @@ -1122,17 +1122,8 @@ namespace ams::ldr::oc { } void Patch(uintptr_t mapped_nso, size_t nso_size) { + #ifdef ATMOSPHERE_IS_STRATOSPHERE SafetyCheck(); - - #ifndef ATMOSPHERE_IS_STRATOSPHERE - void* buf = malloc(nso_size); - uintptr_t mapped_exe = reinterpret_cast(buf); - std::memcpy(buf, reinterpret_cast(mapped_nso), nso_size); - Erista::Patch(mapped_exe, nso_size); - std::memcpy(buf, reinterpret_cast(mapped_nso), nso_size); - Mariko::Patch(mapped_exe, nso_size); - free(buf); - #else bool isMariko = (spl::GetSocType() == spl::SocType_Mariko); if (isMariko) Mariko::Patch(mapped_nso, nso_size); diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/test.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/test.cpp index 3c97a951..354243b2 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/test.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/test.cpp @@ -17,10 +17,19 @@ #include #include #include +#include namespace ams::ldr::oc { namespace pcv { - void Patch(uintptr_t mapped_nso, size_t nso_size); + namespace Erista { + void Patch(uintptr_t mapped_exe, size_t nso_size); + } + + namespace Mariko { + void Patch(uintptr_t mapped_exe, size_t nso_size); + } + + void SafetyCheck(); } namespace ptm { @@ -28,25 +37,24 @@ namespace ams::ldr::oc { } } -static void* ReadFile(const char* file_loc, long* out_size) { - FILE* fp; - void* buf; - long size; - - fp = fopen(file_loc, "r"); +void* loadExec(const char* file_loc, size_t* out_size) { + FILE* fp = fopen(file_loc, "rb"); if (!fp) { - fprintf(stderr, "Cannot open file: %s\n", file_loc); + fprintf(stderr, "Cannot open file: \"%s\"\n", file_loc); exit(-1); } fseek(fp, 0, SEEK_END); - size = ftell(fp); + size_t size = ftell(fp); + fseek(fp, 0, SEEK_SET); - buf = malloc((size + 1) * sizeof(char)); - fread(buf, sizeof(char), size, fp); + void* buf = malloc(size); + + fread(buf, size, 1, fp); fclose(fp); + if (size < 8192) { - fprintf(stderr, "File is too small to process: %u Bytes\n", size); + fprintf(stderr, "File is too small to process: \"%s\" (%lu B)\n", file_loc, size); exit(-1); } @@ -54,41 +62,110 @@ static void* ReadFile(const char* file_loc, long* out_size) { return buf; } +void saveExec(const char* file_loc, const void* buf, size_t size) { + FILE* fp = fopen(file_loc, "wb"); + if (!fp) { + fprintf(stderr, "Cannot write to \"%s\"\n", file_loc); + exit(-1); + } + printf("Saving to \"%s\"...\n", file_loc); + fwrite(buf, size, 1, fp); + fclose(fp); +} + int main(int argc, char** argv) { - const char* pcv_opt = "pcv"; - const char* ptm_opt = "ptm"; - enum { + const char* pcv_opt = "pcv"; + const char* ptm_opt = "ptm"; + const char* save_opt = "-s"; + const char* mariko_ext = ".mariko"; + const char* erista_ext = ".erista"; + enum EXE_OPTION { EXE_PCV, EXE_PTM, UNKNOWN }; - int option = UNKNOWN; - if (argc == 3) { + EXE_OPTION exe_opt = UNKNOWN; + if (argc > 2) { if (!strcmp(argv[1], pcv_opt)) - option = EXE_PCV; + exe_opt = EXE_PCV; if (!strcmp(argv[1], ptm_opt)) - option = EXE_PTM; + exe_opt = EXE_PTM; } - if (option == UNKNOWN) { - fprintf(stderr, "Usage: %s %s/%s \n", argv[0], pcv_opt, ptm_opt); + if ((argc != 3 && argc != 4) || exe_opt == UNKNOWN) { + fprintf(stderr, "Usage:\n"\ + " %s %s | %s [%s] \n\n"\ + " %s : Save patched executable with extension \"%s\" / \"%s\"\n" + , argv[0], pcv_opt, ptm_opt, save_opt + , save_opt, mariko_ext, erista_ext); return -1; } - long file_size; - void* file_buffer = ReadFile(argv[2], &file_size); - uintptr_t mapped_exe = reinterpret_cast(file_buffer); - size_t exe_size = reinterpret_cast(file_size * sizeof(char)); - switch (option) { - case EXE_PCV: - printf("Patching %s...\n", pcv_opt); - ams::ldr::oc::pcv::Patch(mapped_exe, exe_size); - break; - case EXE_PTM: - printf("Patching %s...\n", ptm_opt); - ams::ldr::oc::ptm::Patch(mapped_exe, exe_size); - break; + bool save_patched = false; + char* exec_path = argv[2]; + if (argc == 4 && !strcmp(argv[2], save_opt)) { + save_patched = true; + exec_path = argv[3]; } + + size_t file_size; + void* file_buffer = loadExec(exec_path, &file_size); + + size_t exec_path_len = strlen(reinterpret_cast(exec_path)); + size_t exec_path_patched_len = exec_path_len + std::max(strlen(mariko_ext), strlen(erista_ext)) + 1; + + if (exe_opt == EXE_PCV) { + ams::ldr::oc::pcv::SafetyCheck(); + + { + void* erista_buf = malloc(file_size); + std::memcpy(erista_buf, file_buffer, file_size); + + printf("Patching %s for Erista...\n", pcv_opt); + ams::ldr::oc::pcv::Erista::Patch(reinterpret_cast(erista_buf), file_size); + if (save_patched) { + char* exec_path_erista = reinterpret_cast(malloc(exec_path_patched_len)); + strlcpy(exec_path_erista, exec_path, exec_path_patched_len); + strlcat(exec_path_erista, erista_ext, exec_path_patched_len); + saveExec(exec_path_erista, erista_buf, file_size); + free(exec_path_erista); + } + free(erista_buf); + } + + { + void* mariko_buf = malloc(file_size); + std::memcpy(mariko_buf, file_buffer, file_size); + + printf("Patching %s for Mariko...\n", pcv_opt); + ams::ldr::oc::pcv::Mariko::Patch(reinterpret_cast(mariko_buf), file_size); + if (save_patched) { + char* exec_path_mariko = reinterpret_cast(malloc(exec_path_patched_len)); + strlcpy(exec_path_mariko, exec_path, exec_path_patched_len); + strlcat(exec_path_mariko, mariko_ext, exec_path_patched_len); + saveExec(exec_path_mariko, mariko_buf, file_size); + free(exec_path_mariko); + } + free(mariko_buf); + } + } + + if (exe_opt == EXE_PTM) { + void* mariko_buf = malloc(file_size); + std::memcpy(mariko_buf, file_buffer, file_size); + + printf("Patching %s (Mariko Only)...", ptm_opt); + ams::ldr::oc::ptm::Patch(reinterpret_cast(mariko_buf), file_size); + if (save_patched) { + char* exec_path_mariko = reinterpret_cast(malloc(exec_path_patched_len)); + strlcpy(exec_path_mariko, exec_path, exec_path_patched_len); + strlcat(exec_path_mariko, mariko_ext, exec_path_patched_len); + saveExec(exec_path_mariko, mariko_buf, file_size); + free(exec_path_mariko); + } + free(mariko_buf); + } + free(file_buffer); printf("Passed!\n\n"); return 0; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/test.sh b/Source/Atmosphere/stratosphere/loader/source/oc/test.sh index 908b4672..b9fc7c4a 100755 --- a/Source/Atmosphere/stratosphere/loader/source/oc/test.sh +++ b/Source/Atmosphere/stratosphere/loader/source/oc/test.sh @@ -2,15 +2,36 @@ fw_dir="/Volumes/RAM/NX-14.0.0/" tmp_dir="/Volumes/RAM/" +repack_out_dir="/Volumes/RAM/out/" oc_test_dir="$HOME/Source/Switch-OC-Suite/Source/Atmosphere/stratosphere/loader/source/oc" prodkeys="$HOME/.switch/prod.keys" hactool_exe="$HOME/Source/hactool/hactool" nx2elf_exe="$HOME/Source/nx2elf/nx2elf" +elf2nso_exe="$HOME/Source/switch-tools/elf2nso" +hacpack_exe="$HOME/Source/hacPack/hacpack" + +should_remove_tmp="Y" +should_save_repack="Y" +option_Mariko_Erista="M" echo -e "\nExtracting nca..." out_pcv="${tmp_dir}pcv/" out_ptm="${tmp_dir}ptm/" +mkdir -p "${out_pcv}" +mkdir -p "${out_ptm}" cd $fw_dir + +pcv_nca_name="" +ptm_nca_name="" +for file_00 in ./*.nca/00 +do + if [ -e "${file_00}" ]; then + echo "Processing \"*.nca/00\" files..." + find "${fw_dir}" -type f -name "00" -exec sh -c 'DIR=$(dirname "{}"); FW_DIR=$(dirname "${DIR}"); mv "{}" "${FW_DIR}/00"; rm -r "${DIR}"; mv "${FW_DIR}/00" "${DIR}"' \; + fi + break +done + for nca_file in ./*.nca do file_size=`wc -c "$nca_file" | awk '{print $1}'` @@ -21,12 +42,14 @@ do titleid=`$hactool_exe -k $prodkeys --disablekeywarns -t nca $nca_file | grep 'Title ID'` if [[ $titleid == *"010000000000001a"* ]]; then - echo "$nca_file (pcv) -> $out_pcv" + pcv_nca_name="$(basename $nca_file)" + echo "$pcv_nca_name (pcv) -> $out_pcv" $hactool_exe -k $prodkeys --disablekeywarns -t nca $nca_file --exefsdir "$out_pcv" 1> /dev/null fi if [[ $titleid == *"0100000000000010"* ]]; then - echo "$nca_file (ptm) -> $out_ptm" + ptm_nca_name="$(basename $nca_file)" + echo "$ptm_nca_name (ptm) -> $out_ptm" $hactool_exe -k $prodkeys --disablekeywarns -t nca $nca_file --exefsdir "$out_ptm" 1> /dev/null fi done @@ -35,12 +58,61 @@ echo -e "\nConverting nca to elf..." $nx2elf_exe "${out_pcv}main" 1> /dev/null $nx2elf_exe "${out_ptm}main" 1> /dev/null -echo -e "\nBuilding and testing..." +echo -e "\nBuilding..." cd $oc_test_dir make test 1> /dev/null -./test pcv "${out_pcv}main.elf" -./test ptm "${out_ptm}main.elf" + +echo -e "\nPatching..." + +[ -z "$should_save_repack" ] && read -p "Save and repack to nca (y/N)? " should_save_repack +SAVE_OPT=" " +case $should_save_repack in + Y|y ) SAVE_OPT="-s ";; +esac + +./test pcv $SAVE_OPT "${out_pcv}main.elf" +./test ptm $SAVE_OPT "${out_ptm}main.elf" make clean 1> /dev/null +if [ ! -z $SAVE_OPT ]; then + case $should_save_repack in + Y|y ) + patched_ext=".mariko" + [ -z "$option_Mariko_Erista" ] && read -p "[M]ariko (Default) | [E]rista ? " option_Mariko_Erista + case $option_Mariko_Erista in + E|e ) patched_ext=".erista";; + esac + mkdir -p "${repack_out_dir}" + cd "${tmp_dir}" + echo -e "\nRepacking pcv to ${repack_out_dir}${pcv_nca_name}..." + TMP="${out_pcv}temp/" + mkdir -p "${TMP}" + $elf2nso_exe "${out_pcv}main.elf${patched_ext}" "${TMP}main" 1> /dev/null + cp "${out_pcv}main.npdm" "${TMP}main.npdm" + $hacpack_exe -k $prodkeys -o "${TMP}nca" --type nca --ncatype program --titleid 010000000000001A --exefsdir "${TMP}" 1> /dev/null + find "${TMP}nca" -name "*.nca" -exec mv {} "${repack_out_dir}${pcv_nca_name}" \; + + if [[ $patched_ext == ".mariko" ]]; then + echo -e "\nRepacking ptm (Mariko Only) to ${repack_out_dir}${ptm_nca_name}..." + TMP="${out_ptm}temp/" + mkdir -p "${TMP}" + $elf2nso_exe "${out_ptm}main.elf${patched_ext}" "${TMP}main" 1> /dev/null + cp "${out_ptm}main.npdm" "${TMP}main.npdm" + $hacpack_exe -k $prodkeys -o "${TMP}nca" --type nca --ncatype program --titleid 0100000000000010 --exefsdir "${TMP}" 1> /dev/null + find "${TMP}nca" -name "*.nca" -exec mv {} "${repack_out_dir}${ptm_nca_name}" \; + fi + ;; + esac +fi + +[ -z "$should_remove_tmp" ] && read -p "Remove temp files (Y/n)? " should_remove_tmp +case $should_remove_tmp in + N|n ) + exit;; +esac + rm -fr $out_pcv rm -fr $out_ptm +rm -fr "${tmp_dir}hacpack_backup" + +echo -e "\nDone!"