Add info and script on patching sysmodules manually

This commit is contained in:
KazushiM
2022-03-31 17:06:40 +08:00
parent 3daff1b712
commit 0bb4249a5f
5 changed files with 213 additions and 50 deletions

View File

@@ -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.
<details>
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`)
</details>
## Acknowledgement

View File

@@ -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

View File

@@ -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<uintptr_t>(buf);
std::memcpy(buf, reinterpret_cast<void *>(mapped_nso), nso_size);
Erista::Patch(mapped_exe, nso_size);
std::memcpy(buf, reinterpret_cast<void *>(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);

View File

@@ -17,10 +17,19 @@
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
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 <executable>\n", argv[0], pcv_opt, ptm_opt);
if ((argc != 3 && argc != 4) || exe_opt == UNKNOWN) {
fprintf(stderr, "Usage:\n"\
" %s %s | %s [%s] <exec_path>\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<uintptr_t>(file_buffer);
size_t exe_size = reinterpret_cast<size_t>(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<const char *>(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<uintptr_t>(erista_buf), file_size);
if (save_patched) {
char* exec_path_erista = reinterpret_cast<char *>(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<uintptr_t>(mariko_buf), file_size);
if (save_patched) {
char* exec_path_mariko = reinterpret_cast<char *>(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<uintptr_t>(mariko_buf), file_size);
if (save_patched) {
char* exec_path_mariko = reinterpret_cast<char *>(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;

View File

@@ -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!"