From 2a7df193ce7345f52c0bed2fbd7e633ee5c1632e Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Wed, 8 Mar 2023 16:49:02 +0800 Subject: [PATCH] support checkra1n clones being passed into -i --- include/kerninfo.h | 1 + include/palerain.h | 3 +++ src/Makefile | 1 + src/boyermoore_memmem.c | 59 +++++++++++++++++++++++++++++++++++++++++ src/exec_checkra1n.c | 9 +++++-- src/main.c | 10 ++++++- src/optparse.c | 33 ++++++++++++++++++++++- 7 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 src/boyermoore_memmem.c diff --git a/include/kerninfo.h b/include/kerninfo.h index da8727b..a90d586 100644 --- a/include/kerninfo.h +++ b/include/kerninfo.h @@ -59,6 +59,7 @@ #define palerain_option_setup_rootful (1 << 2) /* create fakefs */ #define palerain_option_setup_rootful_forced (1 << 3) /* create fakefs over an existing one */ #define palerain_option_setup_partial_root (1 << 4) /* fakefs creating should be partial */ +#define palerain_option_checkrain_is_clone (1 << 5) /* supplied checkra1n is checkra1n clone */ #ifdef DEV_BUILD #define palerain_option_test1 (1 << 29) diff --git a/include/palerain.h b/include/palerain.h index f357b7c..f82fe3c 100644 --- a/include/palerain.h +++ b/include/palerain.h @@ -198,6 +198,9 @@ int exitrecv_cmd(const uint64_t ecid); int exec_checkra1n(void); int override_file(override_file_t *finfo, niarelap_file_t** orig, unsigned int *orig_len, char *filename); void* pongo_helper(void* ptr); +const unsigned char * +boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen, + const unsigned char* needle, size_t nlen); extern void* pongo_usb_callback(void* arg); diff --git a/src/Makefile b/src/Makefile index 9d60f94..a11909a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,6 +3,7 @@ all: palera1n OBJECTS = main.o dfuhelper.o devhelper.o lockdown_helper.o OBJECTS += optparse.o override_file.o log.o lock_vars.o credits.o OBJECTS += fake_embedded.o exec_checkra1n.o pongo_helper.o +OBJECTS += boyermoore_memmem.o ifeq (,$(findstring macosx, $(CFLAGS))) ifneq (,$(findstring version-min=, $(CFLAGS))) diff --git a/src/boyermoore_memmem.c b/src/boyermoore_memmem.c new file mode 100644 index 0000000..6a37f26 --- /dev/null +++ b/src/boyermoore_memmem.c @@ -0,0 +1,59 @@ +#include +#include + +const unsigned char * +boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen, + const unsigned char* needle, size_t nlen) +{ + size_t scan = 0; + size_t bad_char_skip[UCHAR_MAX + 1]; /* Officially called: + * bad character shift */ + + /* Sanity checks on the parameters */ + if (nlen <= 0 || !haystack || !needle) + return NULL; + + /* ---- Preprocess ---- */ + /* Initialize the table to default value */ + /* When a character is encountered that does not occur + * in the needle, we can safely skip ahead for the whole + * length of the needle. + */ + for (scan = 0; scan <= UCHAR_MAX; scan = scan + 1) + bad_char_skip[scan] = nlen; + + /* C arrays have the first byte at [0], therefore: + * [nlen - 1] is the last byte of the array. */ + size_t last = nlen - 1; + + /* Then populate it with the analysis of the needle */ + for (scan = 0; scan < last; scan = scan + 1) + bad_char_skip[needle[scan]] = last - scan; + + /* ---- Do the matching ---- */ + + /* Search the haystack, while the needle can still be within it. */ + while (hlen >= nlen) + { + /* scan from the end of the needle */ + for (scan = last; haystack[scan] == needle[scan]; scan = scan - 1) + if (scan == 0) /* If the first byte matches, we've found it. */ + { + return haystack; + } + + /* otherwise, we need to skip some bytes and start again. + Note that here we are getting the skip value based on the last byte + of needle, no matter where we didn't match. So if needle is: "abcd" + then we are skipping based on 'd' and that value will be 4, and + for "abcdd" we again skip on 'd' but the value will be only 1. + The alternative of pretending that the mismatched character was + the last character is slower in the normal case (E.g. finding + "abcd" in "...azcd..." gives 4 by using 'd' but only + 4-2==2 using 'z'. */ + hlen -= bad_char_skip[haystack[last]]; + haystack += bad_char_skip[haystack[last]]; + } + + return NULL; +} diff --git a/src/exec_checkra1n.c b/src/exec_checkra1n.c index 4bc7631..5d36b17 100644 --- a/src/exec_checkra1n.c +++ b/src/exec_checkra1n.c @@ -97,9 +97,14 @@ int exec_checkra1n(void) { } #endif checkra1n_exec: {}; - char args[0x10] = "-pE"; + char args[0x10] = "-E"; if (checkrain_option_enabled(host_flags, host_option_demote)) strncat(args, "d", 0xf); - if (verbose >= 2) strncat(args, "v", 0xf); + if (!checkrain_option_enabled(host_flags, palerain_option_checkrain_is_clone)) { + strncat(args, "p", 0xf); + if (verbose >= 2) strncat(args, "v", 0xf); + } else { + strncat(args, "R", 0xf); + } if (pongo_path != NULL) strncat(args, "k", 0xf); // keep this at last pid_t pid; char* checkra1n_argv[] = { diff --git a/src/main.c b/src/main.c index b00383d..e1d8e9f 100644 --- a/src/main.c +++ b/src/main.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -56,8 +57,8 @@ void thr_cleanup(void* ptr) { } int build_checks(void) { -#if defined(__APPLE__) #ifndef NO_CHECKRAIN +#if defined(__APPLE__) struct mach_header_64* c1_header = (struct mach_header_64*)&checkra1n[0]; if (c1_header->magic != MH_MAGIC_64 && c1_header->magic != MH_CIGAM_64) { LOG(LOG_FATAL, "Broken build: checkra1n is not a thin Mach-O"); @@ -68,6 +69,13 @@ int build_checks(void) { return -1; } #endif + if (checkra1n_len <= (UCHAR_MAX + 1)) { + LOG(LOG_FATAL, "checkra1n too small"); + } + if (boyermoore_horspool_memmem(&checkra1n[0], checkra1n_len, (const unsigned char*)"[ra1npoc15-part] thanks to", strlen("[ra1npoc15-part] thanks to")) != NULL) { + host_flags |= palerain_option_checkrain_is_clone; + LOG(LOG_WARNING, "Do not distribute!"); + } #endif #ifndef NO_KPF struct mach_header_64 *kpf_hdr = (struct mach_header_64 *)checkra1n_kpf_pongo; diff --git a/src/optparse.c b/src/optparse.c index a1092da..4054e79 100644 --- a/src/optparse.c +++ b/src/optparse.c @@ -9,8 +9,10 @@ #include #include #include +#include #include #include +#include checkrain_option_t host_flags = 0; checkrain_option_p host_flags_p = &host_flags; @@ -211,7 +213,36 @@ int optparse(int argc, char* argv[]) { } else if (!(st.st_mode & S_IXUSR) && !(st.st_mode & S_IXGRP) && !(st.st_mode & S_IXOTH)) { LOG(LOG_FATAL, "%s is not executable", optarg); return -1; - }; + } else if (!(st.st_mode & S_IFREG)) { + LOG(LOG_FATAL, "%s is not a regular file", optarg); + return -1; + } + if (st.st_size < (UCHAR_MAX+1)) { + LOG(LOG_FATAL, "%s too small", optarg); + return -1; + } + int checkra1n_fd = open(optarg, O_RDONLY); + if (checkra1n_fd == -1) { + LOG(LOG_FATAL, "Cannot open %s: %d (%s)", optarg, errno, strerror(errno)); + return -1; + } + void* addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, checkra1n_fd, 0); + if (addr == MAP_FAILED) { + LOG(LOG_ERROR, "Failed to map file %s: %d (%s)", optarg, errno, strerror(errno)); + return -1; + } + if (boyermoore_horspool_memmem(addr, st.st_size, (const unsigned char*)"[ra1npoc15-part] thanks to", strlen("[ra1npoc15-part] thanks to")) != NULL) + { + host_flags |= palerain_option_checkrain_is_clone; + LOG(LOG_VERBOSE3, "%s is checkra1n clone", optarg); + } + else + { + host_flags &= ~palerain_option_checkrain_is_clone; + LOG(LOG_VERBOSE3, "%s is checkra1n", optarg); + } + munmap(addr, st.st_size); + close(checkra1n_fd); ext_checkra1n = calloc(1, strlen(optarg) + 1); snprintf(ext_checkra1n, strlen(optarg) + 1, "%s", optarg); break;