diff --git a/.gitignore b/.gitignore index 69550e6..c4131fc 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ ChOma_host *.dylib RootHelperSample/launchdshim/launchdhook/jitter/jitter Bootstrap/jb +.stamp \ No newline at end of file diff --git a/Bootstrap/jb.zip b/Bootstrap/jb.zip index 6a96739..fe313b7 100644 Binary files a/Bootstrap/jb.zip and b/Bootstrap/jb.zip differ diff --git a/Makefile b/Makefile index 58ed1eb..56f7253 100644 --- a/Makefile +++ b/Makefile @@ -12,8 +12,15 @@ all: Serotonin.tipa shims: echo "[*] Building cfprefsdshim" $(MAKE) -C $(CFPREFSD_SHIM) - $(LDID) -S$(CFPREFSD_SHIM)ent.plist $(CFPREFSD_SHIM).theos/obj/debug/cfprefsdshim - $(CTBYPASS) -i $(CFPREFSD_SHIM).theos/obj/debug/cfprefsdshim -r -o $(CFPREFSD_SHIM)cfprefsdshimsignedinjected + $(LDID) -S$(CFPREFSD_SHIM)ent.plist $(CFPREFSD_SHIM).theos/obj/cfprefsdshim + $(CTBYPASS) -i $(CFPREFSD_SHIM).theos/obj/cfprefsdshim -r -o $(CFPREFSD_SHIM)cfprefsdshimsignedinjected + +makebootstrap: + echo "[*] Making libiosexechook / libts2jailbreakenv" + $(MAKE) -C RootHelperSample/launchdshim/libiosexechook + # $(LDID) -SRootHelperSample/launchdshim/launchdentitlements.plist RootHelperSample/launchdshim/libiosexechook/.theos/obj/libiosexechook.dylib + $(CTBYPASS) -i RootHelperSample/launchdshim/libiosexechook/.theos/obj/libiosexechook.dylib -r -o Bootstrap/jb/usr/lib/libTS2JailbreakEnv.dylib + zip -vr9 -q Bootstrap/jb.zip Bootstrap/jb -x "*.DS_Store" Serotonin.tipa: $(wildcard **/*.c **/*.m **/*.swift **/*.plist **/*.xml) echo "[*] Building ChOma for host" @@ -30,24 +37,28 @@ Serotonin.tipa: $(wildcard **/*.c **/*.m **/*.swift **/*.plist **/*.xml) $(MAKE) -C RootHelperSample/launchdshim/launchdhook echo "[*] Signing launchd hook" - $(CTBYPASS) -i RootHelperSample/launchdshim/launchdhook/.theos/obj/debug/launchdhook.dylib -r -o RootHelperSample/launchdshim/launchdhook/launchdhooksigned.dylib + $(CTBYPASS) -i RootHelperSample/launchdshim/launchdhook/.theos/obj/launchdhook.dylib -r -o RootHelperSample/launchdshim/launchdhook/launchdhooksigned.dylib echo "[*] Building general hook" $(MAKE) -C RootHelperSample/launchdshim/generalhook echo "[*] Signing general hook" - $(CTBYPASS) -i RootHelperSample/launchdshim/generalhook/.theos/obj/debug/generalhook.dylib -r -o RootHelperSample/launchdshim/generalhook/generalhook.dylib + $(CTBYPASS) -i RootHelperSample/launchdshim/generalhook/.theos/obj/generalhook.dylib -r -o RootHelperSample/launchdshim/generalhook/generalhook.dylib echo "[*] Building xpcproxyhook" $(MAKE) -C RootHelperSample/launchdshim/xpcproxyhook echo "[*] Signing xpcproxyhook" - $(LDID) -SRootHelperSample/launchdshim/xpcproxyhook/.theos/obj/debug/xpcproxyhook.dylib - $(CTBYPASS) -i RootHelperSample/launchdshim/xpcproxyhook/.theos/obj/debug/xpcproxyhook.dylib -r -o RootHelperSample/launchdshim/xpcproxyhook/xpcproxyhook.dylib + $(LDID) -SRootHelperSample/launchdshim/xpcproxyhook/.theos/obj/xpcproxyhook.dylib + $(CTBYPASS) -i RootHelperSample/launchdshim/xpcproxyhook/.theos/obj/xpcproxyhook.dylib -r -o RootHelperSample/launchdshim/xpcproxyhook/xpcproxyhook.dylib echo "[*] Building jitter" $(MAKE) -C RootHelperSample/launchdshim/launchdhook/jitter - $(CTBYPASS) -i RootHelperSample/launchdshim/launchdhook/jitter/.theos/obj/debug/jitter -r -o RootHelperSample/launchdshim/launchdhook/jitter/jitter + $(CTBYPASS) -i RootHelperSample/launchdshim/launchdhook/jitter/.theos/obj/jitter -r -o RootHelperSample/launchdshim/launchdhook/jitter/jitter + + echo "[*] Building rootlesshooks" + $(MAKE) -C RootHelperSample/launchdshim/rootlesshooks/ + $(CTBYPASS) -i RootHelperSample/launchdshim/rootlesshooks/.theos/obj/rootlesshooks.dylib -r -o RootHelperSample/launchdshim/rootlesshooks/rootlesshooks.dylib # jank workaround at best, can someone else please fix this weird file dependency? – bomberfish echo "[*] Copying fastPathSign" @@ -63,11 +74,12 @@ Serotonin.tipa: $(wildcard **/*.c **/*.m **/*.swift **/*.plist **/*.xml) mkdir Payload cp -a build/Build/Products/Release-iphoneos/Serotonin.app Payload rm -rf Payload/Serotonin.app/Frameworks - cp RootHelperSample/.theos/obj/debug/arm64/serotoninroothelper Payload/Serotonin.app/serotoninroothelper + cp RootHelperSample/.theos/obj/serotoninroothelper Payload/Serotonin.app/serotoninroothelper install -m755 RootHelperSample/launchdshim/launchdhook/launchdhooksigned.dylib Payload/Serotonin.app/launchdhooksigned.dylib install -m755 RootHelperSample/launchdshim/generalhook/generalhook.dylib Payload/Serotonin.app/generalhooksigned.dylib install -m755 RootHelperSample/launchdshim/xpcproxyhook/xpcproxyhook.dylib Payload/Serotonin.app/xpcproxyhooksigned.dylib install -m755 RootHelperSample/launchdshim/launchdhook/jitter/jitter Payload/Serotonin.app/jitterd + install -m755 RootHelperSample/launchdshim/rootlesshooks/rootlesshooks.dylib Payload/Serotonin.app/rootlesshooks.dylib cp usprebooter/unzip Payload/Serotonin.app/unzip cp Bootstrap/jb.zip Payload/Serotonin.app/jb.zip $(LDID) -S./RootHelperSample/entitlements.plist -Cadhoc Payload/Serotonin.app/{fastPathSign,ldid,serotoninroothelper} @@ -105,6 +117,6 @@ apple-include: gsed -i -E s/'__API_UNAVAILABLE\(.*\)'// apple-include/IOKit/IOKitLib.h clean: - rm -rf Payload build RootHelperSample/.theos RootHelperSample/launchdshim/cfprefsdshim/.theos RootHelperSample/launchdshim/generalhook/.theos RootHelperSample/launchdshim/launchdhook/.theos RootHelperSample/launchdshim/xpcproxyhook/.theos RootHelperSample/build apple-include FUCK.tipa Serotonin.tipa + rm -rf Payload build RootHelperSample/.theos RootHelperSample/launchdshim/cfprefsdshim/.theos RootHelperSample/launchdshim/generalhook/.theos RootHelperSample/launchdshim/launchdhook/.theos RootHelperSample/launchdshim/launchdhook/jitter/.theos RootHelperSample/launchdshim/xpcproxyhook/.theos RootHelperSample/build apple-include FUCK.tipa Serotonin.tipa .PHONY: all clean Serotonin.tipa diff --git a/RootHelperSample/Makefile b/RootHelperSample/Makefile index 3e20a05..82b9188 100644 --- a/RootHelperSample/Makefile +++ b/RootHelperSample/Makefile @@ -1,6 +1,6 @@ TARGET := iphone:clang:16.5:14.0 ARCHS = arm64 - +FINALPACKAGE = 1 include $(THEOS)/makefiles/common.mk TOOL_NAME = serotoninroothelper diff --git a/RootHelperSample/launchdshim/cfprefsdshim/Makefile b/RootHelperSample/launchdshim/cfprefsdshim/Makefile index 87dcdc0..d84ea73 100644 --- a/RootHelperSample/launchdshim/cfprefsdshim/Makefile +++ b/RootHelperSample/launchdshim/cfprefsdshim/Makefile @@ -1,6 +1,6 @@ TARGET := iphone:clang:latest ARCHS = arm64 - +FINALPACKAGE = 1 include $(THEOS)/makefiles/common.mk TOOL_NAME = cfprefsdshim @@ -12,6 +12,6 @@ $(TOOL_NAME)_CODESIGN_FLAGS = -Sent.plist # EDIT substrate.h similarly to libhooker's tbd in vendor/lib!!! to /var/jb/usr/lib/libhooker.dylib # old: //install-name: /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate -after-package:: ct_bypass -i .theos/obj/debug/cfprefsdshim -o cfprefsd +after-package:: ct_bypass -i .theos/obj/cfprefsdshim -o cfprefsd include $(THEOS_MAKE_PATH)/tool.mk diff --git a/RootHelperSample/launchdshim/cfprefsdshim/cfprefsdshimsignedinjected b/RootHelperSample/launchdshim/cfprefsdshim/cfprefsdshimsignedinjected index 596ad9c..ec34959 100755 Binary files a/RootHelperSample/launchdshim/cfprefsdshim/cfprefsdshimsignedinjected and b/RootHelperSample/launchdshim/cfprefsdshim/cfprefsdshimsignedinjected differ diff --git a/RootHelperSample/launchdshim/generalhook/Makefile b/RootHelperSample/launchdshim/generalhook/Makefile index 77dda24..220f127 100644 --- a/RootHelperSample/launchdshim/generalhook/Makefile +++ b/RootHelperSample/launchdshim/generalhook/Makefile @@ -1,6 +1,7 @@ TARGET := iphone:clang:latest:15.0 ARCHS = arm64 THEOS_PACKAGE_SCHEME = rootless +FINALPACKAGE = 1 include $(THEOS)/makefiles/common.mk LIBRARY_NAME = generalhook @@ -12,5 +13,5 @@ $(LIBRARY_NAME)_LDFLAGS = -L./ -lbsm #launchdhook_EXTRA_FRAMEWORKS += IOMobileFramebuffer IOSurface after-package:: echo "[*] Signing general hook" - ct_bypass -i .theos/obj/debug/generalhook.dylib -o generalhooksigned.dylib + ct_bypass -i .theos/obj/generalhook.dylib -o generalhooksigned.dylib include $(THEOS_MAKE_PATH)/library.mk diff --git a/RootHelperSample/launchdshim/generalhook/main.m b/RootHelperSample/launchdshim/generalhook/main.m index dde9d71..341b0bb 100644 --- a/RootHelperSample/launchdshim/generalhook/main.m +++ b/RootHelperSample/launchdshim/generalhook/main.m @@ -44,6 +44,17 @@ - (BOOL)isLoaded { @end +static char gExecutablePath[PATH_MAX]; +static int load_executable_path(void) +{ + char executablePath[PATH_MAX]; + uint32_t bufsize = PATH_MAX; + if (_NSGetExecutablePath(executablePath, &bufsize) == 0) { + if (realpath(executablePath, gExecutablePath) != NULL) return 0; + } + return -1; +} + static void overwriteMainCFBundle() { // Overwrite CFBundleGetMainBundle uint32_t *pc = (uint32_t *)CFBundleGetMainBundle; @@ -212,6 +223,10 @@ void applySandboxExtensions(void) NSLog(@"generalhook - loading tweaks for pid %d", getpid()); const char* oldJBROOT = getenv("JBROOT"); setenv("JBROOT", jbroot("/"), 1); + if (!strcmp(gExecutablePath, "/System/Library/CoreServices/SpringBoard.app/SpringBoard") || + !strcmp(gExecutablePath, "/usr/libexec/lsd")) { + dlopen(jbroot("/rootlesshooks.dylib"), RTLD_NOW); + } dlopen(jbroot("/usr/lib/TweakLoader.dylib"), RTLD_NOW); if(oldJBROOT) setenv("JBROOT", oldJBROOT, 1); else unsetenv("JBROOT"); } diff --git a/RootHelperSample/launchdshim/launchdhook/Makefile b/RootHelperSample/launchdshim/launchdhook/Makefile index bdfaeab..dc0053c 100644 --- a/RootHelperSample/launchdshim/launchdhook/Makefile +++ b/RootHelperSample/launchdshim/launchdhook/Makefile @@ -1,16 +1,16 @@ TARGET := iphone:clang:latest:15.0 +FINALPACKAGE = 1 ARCHS = arm64 THEOS_PACKAGE_SCHEME = rootless include $(THEOS)/makefiles/common.mk LIBRARY_NAME = launchdhook -FINALPACKAGE=1 -launchdhook_FILES = $(wildcard *.m) $(wildcard *.c) $(wildcard verbose/*.m) $(wildcard jbserver/*.c) $(wildcard jbserver/*.m) $(wildcard fun/*.m) $(wildcard fun/kpf/*.c) $(wildcard fun/kpf/*.m) ../../jbroot.m -launchdhook_CFLAGS = -fobjc-arc -isystem "../../../usprebooter/Private Headers I stole from the macOS SDK" -Wno-error -O3 -launchdhook_CODESIGN_FLAGS = -S../launchdentitlements.plist -launchdhook_LDFLAGS = -F./Frameworks -L./ -lbsm -lhooker -framework IOKit -launchdhook_EXTRA_FRAMEWORKS += IOMobileFramebuffer IOSurface +$(LIBRARY_NAME)_FILES = $(wildcard *.m) $(wildcard *.c) $(wildcard verbose/*.m) $(wildcard jbserver/*.c) $(wildcard jbserver/*.m) $(wildcard fun/*.m) $(wildcard fun/kpf/*.c) $(wildcard fun/kpf/*.m) ../../jbroot.m +$(LIBRARY_NAME)_CFLAGS = -fobjc-arc -isystem "../../../usprebooter/Private Headers I stole from the macOS SDK" -Wno-error -O3 +$(LIBRARY_NAME)_CODESIGN_FLAGS = -S../launchdentitlements.plist +$(LIBRARY_NAME)_LDFLAGS = -F./Frameworks -L./ -lbsm -lhooker -framework IOKit +$(LIBRARY_NAME)_EXTRA_FRAMEWORKS += IOMobileFramebuffer IOSurface after-package:: echo "[*] Signing launchd hook" - ct_bypass -i .theos/obj/debug/launchdhook.dylib -o launchdhooksigned.dylib + ct_bypass -i .theos/obj/launchdhook.dylib -o launchdhooksigned.dylib include $(THEOS_MAKE_PATH)/library.mk diff --git a/RootHelperSample/launchdshim/launchdhook/jitter/Makefile b/RootHelperSample/launchdshim/launchdhook/jitter/Makefile index 04e17d8..43c1b26 100644 --- a/RootHelperSample/launchdshim/launchdhook/jitter/Makefile +++ b/RootHelperSample/launchdshim/launchdhook/jitter/Makefile @@ -1,6 +1,6 @@ TARGET := iphone:clang:latest ARCHS = arm64 - +FINALPACKAGE = 1 include $(THEOS)/makefiles/common.mk TOOL_NAME = jitter @@ -10,6 +10,6 @@ jitter_CFLAGS = -fobjc-arc -isystem -Wno-error -O3 jitter_LDFLAGS = -L./ -lbsm jitter_CODESIGN_FLAGS = -Sent.plist -after-package:: ct_bypass -i .theos/obj/debug/jitter -o jitter +after-package:: ct_bypass -i .theos/obj/jitter -o jitter include $(THEOS_MAKE_PATH)/tool.mk diff --git a/RootHelperSample/launchdshim/launchdhook/jitter/jitter.m b/RootHelperSample/launchdshim/launchdhook/jitter/jitter.m index f5e8eb0..6b37d71 100644 --- a/RootHelperSample/launchdshim/launchdhook/jitter/jitter.m +++ b/RootHelperSample/launchdshim/launchdhook/jitter/jitter.m @@ -13,12 +13,12 @@ #include "../fun/memoryControl.h" #include "../jbserver/bsm/audit.h" #include "../jbserver/xpc_private.h" - +#include #define PT_DETACH 11 /* stop tracing a process */ #define PT_ATTACHEXC 14 /* attach to running process with signal exception */ #define MEMORYSTATUS_CMD_SET_JETSAM_HIGH_WATER_MARK 5 #define JBD_MSG_PROC_SET_DEBUGGED 23 - +bool shouldUiCache = false; int ptrace(int request, pid_t pid, caddr_t addr, int data); // void JBLogError(const char *format, ...); // void JBLogDebug(const char *format, ...); @@ -124,6 +124,13 @@ int main(int argc, char* argv[]) dispatch_resume(sourceSystemWide); dispatch_main(); + if (shouldUiCache == false) { + pid_t pid; + extern char **environ; + char *argv[] = {"/var/jb/usr/bin/uicache", "-a", NULL}; + posix_spawn(&pid, argv[0], NULL, NULL, argv, environ); + shouldUiCache = true; + } return 0; } } diff --git a/RootHelperSample/launchdshim/libiosexechook/Makefile b/RootHelperSample/launchdshim/libiosexechook/Makefile new file mode 100644 index 0000000..7554fc2 --- /dev/null +++ b/RootHelperSample/launchdshim/libiosexechook/Makefile @@ -0,0 +1,16 @@ +TARGET := iphone:clang:latest:15.0 +FINALPACKAGE = 1 +ARCHS = arm64 +THEOS_PACKAGE_SCHEME = rootless +include $(THEOS)/makefiles/common.mk + +LIBRARY_NAME = libiosexechook +$(LIBRARY_NAME)_FILES = $(wildcard *.m) $(wildcard *.c) +$(LIBRARY_NAME)_CFLAGS = -fobjc-arc -isystem "../../../usprebooter/Private Headers I stole from the macOS SDK" -Wno-error -Wno-int-conversion -Wno-incompatible-function-pointer-types -O3 +$(LIBRARY_NAME)_LDFLAGS = -F./Frameworks -L./ -lbsm -lhooker +after-package:: + echo "[*] Signing libiosexechook" + ct_bypass -i .theos/obj/libiosexechook.dylib -r -o libiosexechook.dylib +include $(THEOS_MAKE_PATH)/library.mk +# Bootstrap/jb/usr/lib/libTS2JailbreakEnv.dylib +# Use the built dylib here and put it as libts2jailbreakenv.dylib in bootstrap/jb/usr/lib/libTS2JailbreakEnv.dylib \ No newline at end of file diff --git a/RootHelperSample/launchdshim/libiosexechook/fishhook.c b/RootHelperSample/launchdshim/libiosexechook/fishhook.c new file mode 100644 index 0000000..9365d26 --- /dev/null +++ b/RootHelperSample/launchdshim/libiosexechook/fishhook.c @@ -0,0 +1,277 @@ +// Copyright (c) 2013, Facebook, Inc. +// All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name Facebook nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific +// prior written permission. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "fishhook.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if __has_include() +#include +#endif + +#ifdef __LP64__ +typedef struct mach_header_64 mach_header_t; +typedef struct segment_command_64 segment_command_t; +typedef struct section_64 section_t; +typedef struct nlist_64 nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 +#else +typedef struct mach_header mach_header_t; +typedef struct segment_command segment_command_t; +typedef struct section section_t; +typedef struct nlist nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT +#endif + +#ifndef SEG_DATA_CONST +#define SEG_DATA_CONST "__DATA_CONST" +#endif + +struct rebindings_entry { + struct rebinding *rebindings; + size_t rebindings_nel; + struct rebindings_entry *next; +}; + +static struct rebindings_entry *_rebindings_head; + +static int prepend_rebindings(struct rebindings_entry **rebindings_head, + struct rebinding rebindings[], + size_t nel) { + struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry)); + if (!new_entry) { + return -1; + } + new_entry->rebindings = (struct rebinding *) malloc(sizeof(struct rebinding) * nel); + if (!new_entry->rebindings) { + free(new_entry); + return -1; + } + memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel); + new_entry->rebindings_nel = nel; + new_entry->next = *rebindings_head; + *rebindings_head = new_entry; + return 0; +} + +#if 0 +static int get_protection(void *addr, vm_prot_t *prot, vm_prot_t *max_prot) { + mach_port_t task = mach_task_self(); + vm_size_t size = 0; + vm_address_t address = (vm_address_t)addr; + memory_object_name_t object; +#ifdef __LP64__ + mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; + vm_region_basic_info_data_64_t info; + kern_return_t info_ret = vm_region_64( + task, &address, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_64_t)&info, &count, &object); +#else + mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT; + vm_region_basic_info_data_t info; + kern_return_t info_ret = vm_region(task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &count, &object); +#endif + if (info_ret == KERN_SUCCESS) { + if (prot != NULL) + *prot = info.protection; + + if (max_prot != NULL) + *max_prot = info.max_protection; + + return 0; + } + + return -1; +} +#endif + +static void perform_rebinding_with_section(struct rebindings_entry *rebindings, + section_t *section, + intptr_t slide, + nlist_t *symtab, + char *strtab, + uint32_t *indirect_symtab) { + uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; + void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr); + + for (uint i = 0; i < section->size / sizeof(void *); i++) { + uint32_t symtab_index = indirect_symbol_indices[i]; + if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL || + symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { + continue; + } + uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; + char *symbol_name = strtab + strtab_offset; + bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1]; + struct rebindings_entry *cur = rebindings; + while (cur) { + for (uint j = 0; j < cur->rebindings_nel; j++) { + if (symbol_name_longer_than_1 && strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) { + kern_return_t err; + + if (cur->rebindings[j].replaced != NULL && indirect_symbol_bindings[i] != cur->rebindings[j].replacement) + *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i]; + + /** + * 1. Moved the vm protection modifying codes to here to reduce the + * changing scope. + * 2. Adding VM_PROT_WRITE mode unconditionally because vm_region + * API on some iOS/Mac reports mismatch vm protection attributes. + * -- Lianfu Hao Jun 16th, 2021 + **/ + err = vm_protect (mach_task_self (), (uintptr_t)indirect_symbol_bindings, section->size, 0, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY); + if (err == KERN_SUCCESS) { + /** + * Once we failed to change the vm protection, we + * MUST NOT continue the following write actions! + * iOS 15 has corrected the const segments prot. + * -- Lionfore Hao Jun 11th, 2021 + **/ + #if !__has_feature(ptrauth_calls) && 0 // FIXME: build on Linux + indirect_symbol_bindings[i] = cur->rebindings[j].replacement; + #else + void *replacement = cur->rebindings[j].replacement; + if (!strcmp(section->sectname, "__auth_got")) { + void *stripped = ptrauth_strip(replacement, ptrauth_key_process_independent_code); + replacement = ptrauth_sign_unauthenticated(stripped, ptrauth_key_process_independent_code, &indirect_symbol_bindings[i]); + } + indirect_symbol_bindings[i] = replacement; + #endif + } + goto symbol_loop; + } + } + cur = cur->next; + } + symbol_loop:; + } +} + +static void rebind_symbols_for_image(struct rebindings_entry *rebindings, + const struct mach_header *header, + intptr_t slide) { + Dl_info info; + if (dladdr(header, &info) == 0) { + return; + } + + segment_command_t *cur_seg_cmd; + segment_command_t *linkedit_segment = NULL; + struct symtab_command* symtab_cmd = NULL; + struct dysymtab_command* dysymtab_cmd = NULL; + + uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t); + for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { + cur_seg_cmd = (segment_command_t *)cur; + if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { + if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { + linkedit_segment = cur_seg_cmd; + } + } else if (cur_seg_cmd->cmd == LC_SYMTAB) { + symtab_cmd = (struct symtab_command*)cur_seg_cmd; + } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) { + dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd; + } + } + + if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment || + !dysymtab_cmd->nindirectsyms) { + return; + } + + // Find base symbol/string table addresses + uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; + nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff); + char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); + + // Get indirect symbol table (array of uint32_t indices into symbol table) + uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); + + cur = (uintptr_t)header + sizeof(mach_header_t); + for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { + cur_seg_cmd = (segment_command_t *)cur; + if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { + if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 && + strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) { + continue; + } + for (uint j = 0; j < cur_seg_cmd->nsects; j++) { + section_t *sect = + (section_t *)(cur + sizeof(segment_command_t)) + j; + if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { + perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); + } + if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { + perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); + } + } + } + } +} + +static void _rebind_symbols_for_image(const struct mach_header *header, + intptr_t slide) { + rebind_symbols_for_image(_rebindings_head, header, slide); +} + +int rebind_symbols_image(void *header, + intptr_t slide, + struct rebinding rebindings[], + size_t rebindings_nel) { + struct rebindings_entry *rebindings_head = NULL; + int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel); + rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide); + if (rebindings_head) { + free(rebindings_head->rebindings); + } + free(rebindings_head); + return retval; +} + +int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) { + int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel); + if (retval < 0) { + return retval; + } + // If this was the first call, register callback for image additions (which is also invoked for + // existing images, otherwise, just run on existing images + if (!_rebindings_head->next) { + _dyld_register_func_for_add_image(_rebind_symbols_for_image); + } else { + uint32_t c = _dyld_image_count(); + for (uint32_t i = 0; i < c; i++) { + _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i)); + } + } + return retval; +} \ No newline at end of file diff --git a/RootHelperSample/launchdshim/libiosexechook/fishhook.h b/RootHelperSample/launchdshim/libiosexechook/fishhook.h new file mode 100644 index 0000000..9521409 --- /dev/null +++ b/RootHelperSample/launchdshim/libiosexechook/fishhook.h @@ -0,0 +1,75 @@ +// Copyright (c) 2013, Facebook, Inc. +// All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name Facebook nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific +// prior written permission. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef fishhook_h +#define fishhook_h + +#include +#include + +#if !defined(FISHHOOK_EXPORT) +#define FISHHOOK_VISIBILITY __attribute__((visibility("hidden"))) +#else +#define FISHHOOK_VISIBILITY __attribute__((visibility("default"))) +#endif + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + +/* + * A structure representing a particular intended rebinding from a symbol + * name to its replacement + */ +struct rebinding { + const char *name; + void *replacement; + void **replaced; +}; + +/* + * For each rebinding in rebindings, rebinds references to external, indirect + * symbols with the specified name to instead point at replacement for each + * image in the calling process as well as for all future images that are loaded + * by the process. If rebind_functions is called more than once, the symbols to + * rebind are added to the existing list of rebindings, and if a given symbol + * is rebound more than once, the later rebinding will take precedence. + */ +FISHHOOK_VISIBILITY +int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel); + +/* + * Rebinds as above, but only in the specified image. The header should point + * to the mach-o header, the slide should be the slide offset. Others as above. + */ +FISHHOOK_VISIBILITY +int rebind_symbols_image(void *header, + intptr_t slide, + struct rebinding rebindings[], + size_t rebindings_nel); + +#ifdef __cplusplus +} +#endif //__cplusplus + +#endif //fishhook_h diff --git a/RootHelperSample/launchdshim/libiosexechook/libiosexechook.m b/RootHelperSample/launchdshim/libiosexechook/libiosexechook.m new file mode 100644 index 0000000..6264d5c --- /dev/null +++ b/RootHelperSample/launchdshim/libiosexechook/libiosexechook.m @@ -0,0 +1,540 @@ +// fork() and rootless fix for Procursus bootstrap (named libTS2JailbreakEnv.dylib) +// there's lots of stuff not cleaned up, feel free to play around +// Requires fishhook from https://github.com/khanhduytran0/fishhook +// Usage: inject to libiosexec.dylib, ensure all binaries have get-task-allow entitlement + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fishhook.h" +#import +#include +#include +#include +#include + +#define printf(...) // __VA_ARGS__ + +char* mach_error_string(kern_return_t); +kern_return_t mach_vm_allocate(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, int flags); +kern_return_t mach_vm_map(vm_map_t target_task, mach_vm_address_t *address, mach_vm_size_t size, mach_vm_offset_t mask, int flags, mem_entry_name_port_t object, memory_object_offset_t offset, boolean_t copy, vm_prot_t cur_protection, vm_prot_t max_protection, vm_inherit_t inheritance); +kern_return_t mach_vm_protect(mach_port_name_t task, mach_vm_address_t address, mach_vm_size_t size, boolean_t set_max, vm_prot_t new_prot); +kern_return_t mach_vm_copy(vm_map_t target_task, mach_vm_address_t source_address, mach_vm_size_t count, mach_vm_address_t dest_address); + +#define PT_TRACE_ME 0 +#define PT_DETACH 11 +#define PT_ATTACHEXC 14 +int ptrace(int, pid_t, caddr_t, int); + +static uint64_t THE_OFFSET; + +int (*orig_daemon)(int, int); +int (*orig_fork)(void); +int (*orig_vfork)(void); +int (*orig_access)(const char *path, int amode); +int (*orig_execve)(const char* path, char* const argv[], char* const envp[]); +int (*orig_posix_spawn)(pid_t *restrict pid, const char *restrict path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, char *const argv[restrict], + char *const envp[restrict]); +int (*orig_stat)(const char *restrict path, struct stat *restrict buf); +int (*orig_uname)(struct utsname *name); + +// thanks @miticollo +void handle_exception(arm_thread_state64_t *state) { + uint64_t pc = (uint64_t) __darwin_arm_thread_state64_get_pc(*state); + __darwin_arm_thread_state64_set_pc_fptr(*state, (void *) (pc + THE_OFFSET)); + if (*(uint64_t *) pc != *(uint64_t *) __darwin_arm_thread_state64_get_pc(*state)) { + fprintf(stderr, "pc and pc+off instruction doesn't match\n"); + kill(getpid(), SIGKILL); + } + printf("jump: %p -> %p\n", pc, (uint64_t) __darwin_arm_thread_state64_get_pc(*state)); +} + +void handleFaultyTextPage(int signum, struct siginfo_t *siginfo, void *context) { + static int failureCount; + + printf("Got SIGBUS, fixing\n"); + + struct __darwin_ucontext *ucontext = (struct __darwin_ucontext *) context; + struct __darwin_mcontext64 *machineContext = (struct __darwin_mcontext64 *) ucontext->uc_mcontext; + + handle_exception(&machineContext->__ss); + // handle_exception changed register state for continuation +} + +#define CS_DEBUGGED 0x10000000 +int csops(pid_t pid, unsigned int ops, void *useraddr, size_t usersize); +int isJITEnabled() { + int flags; + csops(getpid(), 0, &flags, sizeof(flags)); + return (flags & CS_DEBUGGED) != 0; +} + +const struct segment_command_64 *builtin_getsegbyname(struct mach_header_64 *mhp, char *segname) +{ + struct segment_command_64 *sgp; + uint32_t i; + + sgp = (struct segment_command_64 *) + ((char *)mhp + sizeof(struct mach_header_64)); + for (i = 0; i < mhp->ncmds; i++){ + if(sgp->cmd == LC_SEGMENT_64) + if(strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0) + return(sgp); + sgp = (struct segment_command_64 *)((char *)sgp + sgp->cmdsize); + } + return NULL; +} + +size_t size_of_image(struct mach_header_64 *header) { + struct load_command *lc = (struct load_command *) (header + 1); + for (uint32_t i = 0; i < header->ncmds; i++) { + //printf("cmd %d = %d\n", i, lc->cmd); + if (lc->cmd == LC_CODE_SIGNATURE) { + struct linkedit_data_command *cmd = (struct linkedit_data_command *)lc; + //printf("size %d\n", cmd->dataoff + cmd->datasize); + return header->sizeofcmds + cmd->dataoff + cmd->datasize; + } + lc = (struct load_command *) ((char *) lc + lc->cmdsize); + } + printf("LC_CODE_SIGNATURE is not found\n"); + abort(); + return 0; +} + +static void post_fork(int pid) { + printf("fork pid=%d\n", pid); + if (pid == 0) { + // fix fork by any chance... + kill(getpid(), SIGSTOP); + usleep(2000); + + if (THE_OFFSET) return; + + kern_return_t result; + const struct mach_header_64 *header = _dyld_get_image_header(0); + uint64_t slide = _dyld_get_image_vmaddr_slide(0); + size_t size = size_of_image(header); + + // SIMULATE READ ONLY + //const struct section_64 *thisSect = getsectbyname(SEG_TEXT, SECT_TEXT); + //result = mach_vm_protect(mach_task_self(), thisSect->addr + slide, thisSect->size, TRUE, VM_PROT_READ); + //printf("RO mach_vm_protect: %s\n", mach_error_string(result)); + + // Copy the whole image memory + //mach_vm_address_t remap; + const struct mach_header_64 *remap; + result = mach_vm_map(mach_task_self(), &remap, size, 0, VM_FLAGS_ANYWHERE, NULL, NULL, FALSE, VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE, VM_INHERIT_DEFAULT); + printf("line %d: %p\n", __LINE__, remap); + result = mach_vm_copy(mach_task_self(), header, size, remap); + printf("line %d: %s\n", __LINE__, mach_error_string(result)); + THE_OFFSET = (uint64_t)remap - (uint64_t)header; + printf("offset=%p\n", THE_OFFSET); + + const struct segment_command_64 *seg = builtin_getsegbyname(remap, SEG_TEXT); + mach_vm_address_t text_remap = remap + (seg->vmaddr + slide - (uint64_t)header); + result = mach_vm_protect(mach_task_self(), text_remap, seg->vmsize, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); + printf("mach_vm_protect(%p): %s\n", text_remap, mach_error_string(result)); + + // Unblock signal handler + sigset_t set; + sigemptyset(&set); + sigprocmask(SIG_SETMASK, &set, 0); + + struct sigaction sigAction; + sigAction.sa_sigaction = handleFaultyTextPage; + sigAction.sa_flags = SA_SIGINFO; + sigaction(SIGBUS, &sigAction, NULL); + + if (!isJITEnabled()) { + fprintf(stderr, "forked process couldn't get JIT, killing\n"); + kill(getpid(), SIGKILL); + } + } else if (pid > 0) { + // Enable JIT for the child process + int ret; + ret = ptrace(PT_ATTACHEXC, pid, 0, 0); + if (!ret && !isJITEnabled()) { + fprintf(stderr, "%s: looks like this process does not have get-task-allow entitlement. Forkfix will abort\n", getprogname()); + abort(); + } + if (!ret) { + // Detach process + for (int i = 0; i < 1000; i++) { + usleep(1000); + ret = ptrace(PT_DETACH, pid, 0, 0); + if (!ret) { + break; + } + } + printf("detach=%d\n", ret); + kill(pid, SIGCONT); + } + //assert(!ret); + } +} + +int hooked_fork() { + int pid = orig_fork(); + post_fork(pid); + return pid; +} + +int hooked_vfork() { + int pid = orig_vfork(); + post_fork(pid); + return pid; +} + +int hooked_daemon(nochdir, noclose) + int nochdir, noclose; +{ + struct sigaction osa, sa; + int fd; + pid_t newgrp; + int oerrno; + int osa_ok; + + /* A SIGHUP may be thrown when the parent exits below. */ + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + osa_ok = sigaction(SIGHUP, &sa, &osa); +#ifndef VARIANT_PRE1050 + //move_to_root_bootstrap(); +#endif /* !VARIANT_PRE1050 */ + switch (hooked_fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + newgrp = setsid(); + oerrno = errno; + if (osa_ok != -1) + sigaction(SIGHUP, &osa, NULL); + + if (newgrp == -1) { + errno = oerrno; + return (-1); + } + + if (!nochdir) + (void)chdir("/"); + + if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) { + (void)dup2(fd, STDIN_FILENO); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + if (fd > 2) + (void)close(fd); + } + return (0); +} + +bool g_sign_failed = false; + +void machoEnumerateArchs(FILE* machoFile, bool (^archEnumBlock)(struct mach_header_64* header, uint32_t offset)) +{ + struct mach_header_64 mh={0}; + if(fseek(machoFile,0,SEEK_SET)!=0)return; + if(fread(&mh,sizeof(mh),1,machoFile)!=1)return; + + if(mh.magic==FAT_MAGIC || mh.magic==FAT_CIGAM)//and || mh.magic==FAT_MAGIC_64 || mh.magic==FAT_CIGAM_64? with fat_arch_64 + { + struct fat_header fh={0}; + if(fseek(machoFile,0,SEEK_SET)!=0)return; + if(fread(&fh,sizeof(fh),1,machoFile)!=1)return; + + for(int i = 0; i < OSSwapBigToHostInt32(fh.nfat_arch); i++) + { + uint32_t archMetadataOffset = sizeof(fh) + sizeof(struct fat_arch) * i; + + struct fat_arch fatArch={0}; + if(fseek(machoFile, archMetadataOffset, SEEK_SET)!=0)break; + if(fread(&fatArch, sizeof(fatArch), 1, machoFile)!=1)break; + + if(fseek(machoFile, OSSwapBigToHostInt32(fatArch.offset), SEEK_SET)!=0)break; + if(fread(&mh, sizeof(mh), 1, machoFile)!=1)break; + + if(mh.magic != MH_MAGIC_64 && mh.magic != MH_CIGAM_64) continue; //require Macho64 + + if(!archEnumBlock(&mh, OSSwapBigToHostInt32(fatArch.offset))) + break; + } + } + else if(mh.magic == MH_MAGIC_64 || mh.magic == MH_CIGAM_64) //require Macho64 + { + archEnumBlock(&mh, 0); + } +} + +void machoGetInfo(FILE* candidateFile, bool *isMachoOut, bool *isLibraryOut) +{ + if (!candidateFile) return; + + __block bool isMacho=false; + __block bool isLibrary = false; + + machoEnumerateArchs(candidateFile, ^bool(struct mach_header_64* header, uint32_t offset) { + switch(OSSwapLittleToHostInt32(header->filetype)) { + case MH_DYLIB: + case MH_BUNDLE: + isLibrary = true; + case MH_EXECUTE: + isMacho = true; + return false; + + default: + return true; + } + }); + + if (isMachoOut) *isMachoOut = isMacho; + if (isLibraryOut) *isLibraryOut = isLibrary; +} + + + +int execBinary(const char* path, char** argv) +{ + pid_t pid = 0; + posix_spawn_file_actions_t file_actions; + int ret; + + posix_spawn_file_actions_init(&file_actions); + + int devnull = open("/dev/null", O_WRONLY); + if (devnull == -1) { + return -1; + } + + posix_spawn_file_actions_adddup2(&file_actions, devnull, STDOUT_FILENO); + posix_spawn_file_actions_adddup2(&file_actions, devnull, STDERR_FILENO); + + ret = posix_spawn(&pid, path, &file_actions, NULL, (char* const*)argv, NULL); + + close(devnull); + + posix_spawn_file_actions_destroy(&file_actions); + + if(ret != 0) { + return -1; + } + + int status = 0; + while(waitpid(pid, &status, 0) != -1) + { + if (WIFSIGNALED(status)) { + return 128 + WTERMSIG(status); + } else if (WIFEXITED(status)) { + return WEXITSTATUS(status); + } + }; + + return -1; +} + +typedef struct ProcessedFile { + char path[PATH_MAX]; + struct ProcessedFile* next; +} ProcessedFile; + +ProcessedFile* processed_files_head = NULL; + +bool is_file_processed(const char* path) { + ProcessedFile* current = processed_files_head; + while (current != NULL) { + if (strcmp(current->path, path) == 0) { + return true; + } + current = current->next; + } + return false; +} + +void add_processed_file(const char* path) { + ProcessedFile* new_file = (ProcessedFile*)malloc(sizeof(ProcessedFile)); + strncpy(new_file->path, path, PATH_MAX); + new_file->next = processed_files_head; + processed_files_head = new_file; +} + + +void get_identifier(const char* path, char* identifier) { + int pipefd[2]; + if (pipe(pipefd) == -1) { + perror("pipe failed"); + exit(1); + } + + pid_t pid; + posix_spawn_file_actions_t file_actions; + posix_spawnattr_t attr; + + posix_spawn_file_actions_init(&file_actions); + posix_spawn_file_actions_adddup2(&file_actions, pipefd[1], STDOUT_FILENO); + posix_spawn_file_actions_addclose(&file_actions, pipefd[0]); + + posix_spawnattr_init(&attr); + + char* const argv[] = {"/var/jb/basebins/ldid_dpkg_autosign", "-h", (char*)path, NULL}; + int status = posix_spawn(&pid, "/var/jb/basebins/ldid_dpkg_autosign", &file_actions, &attr, argv, NULL); + if (status != 0) { + perror("posix_spawn failed"); + exit(1); + } + + close(pipefd[1]); + + FILE* fp = fdopen(pipefd[0], "r"); + if (fp == NULL) { + perror("fdopen failed"); + exit(1); + } + + char output[PATH_MAX]; + while (fgets(output, sizeof(output), fp) != NULL) { + if (strstr(output, "Identifier=") != NULL) { + sscanf(output, "Identifier=%s", identifier); + break; + } + } + + fclose(fp); + close(pipefd[0]); + + int status_code; + waitpid(pid, &status_code, 0); + if (WIFEXITED(status_code) && WEXITSTATUS(status_code) != 0) { + fprintf(stderr, "Child process exited with error status\n"); + exit(1); + } +} + +int autosign(char* path) +{ + if (strstr(path, ".dpkg-new") == NULL && strstr(path, "/Library/dpkg/tmp.ci/") == NULL) + return 0; + + if (is_file_processed(path)) { + return 0; + } + + FILE* fp = fopen(path, "rb"); + if(fp) { + bool ismacho=false,islib=false; + machoGetInfo(fp, &ismacho, &islib); + + if(ismacho) + { + if(!islib) + { + char identifier[PATH_MAX]; + get_identifier(path, identifier); + + if (strlen(identifier) == 0) { + fprintf(stderr, "Failed to retrieve identifier\n"); + return 1; + } + + char identifier_arg[PATH_MAX]; + snprintf(identifier_arg, sizeof(identifier_arg), "-I%s", identifier); + + char sent[PATH_MAX]; + snprintf(sent,sizeof(sent),"-S%s", "/var/jb/ents.plist"); + + char* args[] = {"ldid_dpkg_autosign", "-M", sent, path, identifier_arg, NULL}; + int status = execBinary("/var/jb/ldid_dpkg_autosign", args); + if(status != 0) { + g_sign_failed = true; + } + } + + char* args[] = {"ct_bypass_dpkg_autosign", "-i", path, "-o", path, "-r", NULL}; + int status = execBinary("/var/jb/ct_bypass_dpkg_autosign", args); + if(status != 0) { + g_sign_failed = true; + } + + } + + add_processed_file(path); + fclose(fp); + } + + return 0; +} + + +int (*dpkghook_orig_close)(int fd); +int dpkghook_new_close(int fd) +{ + int olderr=errno; + + char path[PATH_MAX]={0}; + int s=fcntl(fd, F_GETPATH, path); + + errno = olderr; + + int ret = dpkghook_orig_close(fd); + + olderr=errno; + + if(s==0 && path[0]) + { + struct stat st={0}; + stat(path, &st); + + + int autosign(char* path); + autosign(path); + } + + errno = olderr; + return ret; +} + +struct rebinding rebindings[4] = { + {"daemon", hooked_daemon, (void *)&orig_daemon}, + {"fork", hooked_fork, (void *)&orig_fork}, + {"vfork", hooked_vfork, (void *)&orig_vfork}, + {"close", dpkghook_new_close, (void**)&dpkghook_orig_close}, +}; + +__attribute__((constructor)) static void init(int argc, char **argv) { + NSLog(@"libiosexechook is running"); + NSProcessInfo *processInfo = [NSProcessInfo processInfo]; + + NSString *currentProcessName = [processInfo processName]; + if ([currentProcessName isEqualToString:@"dpkg"]) { + rebind_symbols(rebindings, 4); + } else { + rebind_symbols(rebindings, 3); + } +} diff --git a/RootHelperSample/launchdshim/rootlesshooks/Makefile b/RootHelperSample/launchdshim/rootlesshooks/Makefile new file mode 100644 index 0000000..27bfdf9 --- /dev/null +++ b/RootHelperSample/launchdshim/rootlesshooks/Makefile @@ -0,0 +1,15 @@ +TARGET := iphone:clang:16.5:15.0 +INSTALL_TARGET_PROCESSES = lsd +FINALPACKAGE = 1 +THEOS_PACKAGE_SCHEME = rootless +ARCHS = arm64 arm64e + +include $(THEOS)/makefiles/common.mk + +TWEAK_NAME = rootlesshooks + +$(TWEAK_NAME)_FILES = $(wildcard *.x) ../../jbroot.m +$(TWEAK_NAME)_CFLAGS = -fobjc-arc -I../.include +$(TWEAK_NAME)_LDFLAGS = -rpath @loader_path/fallback + +include $(THEOS_MAKE_PATH)/tweak.mk diff --git a/RootHelperSample/launchdshim/rootlesshooks/SpringBoard.x b/RootHelperSample/launchdshim/rootlesshooks/SpringBoard.x new file mode 100644 index 0000000..e17d49f --- /dev/null +++ b/RootHelperSample/launchdshim/rootlesshooks/SpringBoard.x @@ -0,0 +1,70 @@ +#import +#import +#import +#import +#import "../../jbroot.h" +bool string_has_prefix(const char *str, const char* prefix) +{ + if (!str || !prefix) { + return false; + } + + size_t str_len = strlen(str); + size_t prefix_len = strlen(prefix); + + if (str_len < prefix_len) { + return false; + } + + return !strncmp(str, prefix, prefix_len); +} + +@interface XBSnapshotContainerIdentity : NSObject +@property (nonatomic, readonly, copy) NSString* bundleIdentifier; +- (NSString*)snapshotContainerPath; +@end + +%hook XBSnapshotContainerIdentity + +- (NSString *)snapshotContainerPath +{ + NSString *path = %orig; + if([path hasPrefix:@"/var/mobile/Library/SplashBoard/Snapshots/"] && ![self.bundleIdentifier hasPrefix:@"com.apple."]) { + return jbrootobjc(path); + } + return path; +} + +%end + +%hookf(int, fcntl, int fildes, int cmd, ...) { + if (cmd == F_SETPROTECTIONCLASS) { + char filePath[PATH_MAX]; + if (fcntl(fildes, F_GETPATH, filePath) != -1) { + // Skip setting protection class on jailbreak apps, this doesn't work and causes snapshots to not be saved correctly + if (string_has_prefix(filePath, jbroot("/var/mobile/Library/SplashBoard/Snapshots"))) { + return 0; + } + } + } + + va_list a; + va_start(a, cmd); + const char *arg1 = va_arg(a, void *); + const void *arg2 = va_arg(a, void *); + const void *arg3 = va_arg(a, void *); + const void *arg4 = va_arg(a, void *); + const void *arg5 = va_arg(a, void *); + const void *arg6 = va_arg(a, void *); + const void *arg7 = va_arg(a, void *); + const void *arg8 = va_arg(a, void *); + const void *arg9 = va_arg(a, void *); + const void *arg10 = va_arg(a, void *); + va_end(a); + return %orig(fildes, cmd, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); +} + +void springboardInit(void) +{ + %init(); +} diff --git a/RootHelperSample/launchdshim/rootlesshooks/lsd.x b/RootHelperSample/launchdshim/rootlesshooks/lsd.x new file mode 100644 index 0000000..d83b9b8 --- /dev/null +++ b/RootHelperSample/launchdshim/rootlesshooks/lsd.x @@ -0,0 +1,104 @@ +#import +// #import +// #import +#import "../../jbroot.h" +#import + +#define POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE 1 +extern int posix_spawnattr_set_persona_np(const posix_spawnattr_t* __restrict, uid_t, uint32_t); +extern int posix_spawnattr_set_persona_uid_np(const posix_spawnattr_t* __restrict, uid_t); +extern int posix_spawnattr_set_persona_gid_np(const posix_spawnattr_t* __restrict, uid_t); +extern char **environ; + +int cmd_wait_for_exit(pid_t pid) +{ + int status = 0; + do { + if (waitpid(pid, &status, 0) == -1) { + return -1; + } + } while (!WIFEXITED(status) && !WIFSIGNALED(status)); + return status; +} + +int __exec_cmd_internal_va(bool suspended, bool root, bool waitForExit, pid_t *pidOut, const char *binary, int argc, va_list va_args) +{ + const char *argv[argc+1]; + argv[0] = binary; + for (int i = 1; i < argc; i++) { + argv[i] = va_arg(va_args, const char *); + } + argv[argc] = NULL; + + posix_spawnattr_t attr = NULL; + posix_spawnattr_init(&attr); + if (suspended) { + posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED); + } + if (root) { + posix_spawnattr_set_persona_np(&attr, 99, POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE); + posix_spawnattr_set_persona_uid_np(&attr, 0); + posix_spawnattr_set_persona_gid_np(&attr, 0); + } + + pid_t spawnedPid = 0; + int spawnError = posix_spawn(&spawnedPid, binary, NULL, &attr, (char *const *)argv, environ); + if (attr) posix_spawnattr_destroy(&attr); + if (spawnError != 0) return spawnError; + + if (waitForExit && !suspended) { + return cmd_wait_for_exit(spawnedPid); + } + else if (pidOut) { + *pidOut = spawnedPid; + } + return 0; +} + +int exec_cmd(const char *binary, ...) +{ + int argc = 1; + va_list args; + va_start(args, binary); + while (va_arg(args, const char *)) argc++; + va_end(args); + + va_start(args, binary); + int r = __exec_cmd_internal_va(false, false, true, NULL, binary, argc, args); + va_end(args); + return r; +} + +%hookf(NSURL *, _LSGetInboxURLForBundleIdentifier, NSString *bundleIdentifier) +{ + NSURL *origURL = %orig; + if (![bundleIdentifier hasPrefix:@"com.apple"] && [origURL.path hasPrefix:@"/var/mobile/Library/Application Support/Containers/"]) { + return [NSURL fileURLWithPath:jbrootobjc(origURL.path)]; + } + return origURL; +} + +%hookf(int, _LSServer_RebuildApplicationDatabases) +{ + int r = %orig; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + // Ensure jailbreak apps are readded to icon cache after the system reloads it + // A bit hacky, but works + const char *uicachePath = jbroot("/usr/bin/uicache"); + if (!access(uicachePath, F_OK)) { + exec_cmd(uicachePath, "-a", NULL); + } + }); + + return r; +} + +void lsdInit(void) +{ + MSImageRef coreServicesImage = MSGetImageByName("/System/Library/Frameworks/CoreServices.framework/CoreServices"); + if (coreServicesImage) { + %init(_LSGetInboxURLForBundleIdentifier = MSFindSymbol(coreServicesImage, "__LSGetInboxURLForBundleIdentifier"), + _LSServer_RebuildApplicationDatabases = MSFindSymbol(coreServicesImage, "__LSServer_RebuildApplicationDatabases")); + } +} \ No newline at end of file diff --git a/RootHelperSample/launchdshim/rootlesshooks/main.x b/RootHelperSample/launchdshim/rootlesshooks/main.x new file mode 100644 index 0000000..665dca6 --- /dev/null +++ b/RootHelperSample/launchdshim/rootlesshooks/main.x @@ -0,0 +1,32 @@ +#import +#import +#import "../../jbroot.h" +NSString* safe_getExecutablePath() +{ + char executablePathC[PATH_MAX]; + uint32_t executablePathCSize = sizeof(executablePathC); + _NSGetExecutablePath(&executablePathC[0], &executablePathCSize); + return [NSString stringWithUTF8String:executablePathC]; +} + +NSString* getProcessName() +{ + return safe_getExecutablePath().lastPathComponent; +} + +%ctor +{ + NSString *processName = getProcessName(); + /*if ([processName isEqualToString:@"installd"]) { + extern void installdInit(void); + installdInit(); + } + else*/ if ([processName isEqualToString:@"SpringBoard"]) { + extern void springboardInit(void); + springboardInit(); + } + else if ([processName isEqualToString:@"lsd"]) { + extern void lsdInit(void); + lsdInit(); + } +} \ No newline at end of file diff --git a/RootHelperSample/launchdshim/xpcproxyhook/Makefile b/RootHelperSample/launchdshim/xpcproxyhook/Makefile index 955ecc8..4907fca 100644 --- a/RootHelperSample/launchdshim/xpcproxyhook/Makefile +++ b/RootHelperSample/launchdshim/xpcproxyhook/Makefile @@ -1,6 +1,7 @@ TARGET := iphone:clang:latest:15.0 ARCHS = arm64 THEOS_PACKAGE_SCHEME = rootless +FINALPACKAGE = 1 include $(THEOS)/makefiles/common.mk LIBRARY_NAME = xpcproxyhook @@ -10,6 +11,6 @@ xpcproxyhook_CFLAGS = -fobjc-arc -Wno-error xpcproxyhook_CODESIGN_FLAGS = -S xpcproxyhook_LDFLAGS = -F./Frameworks xpcproxyhook_EXTRA_FRAMEWORKS += IOKit -after-package:: echo "[*] Signing xpcproxyhook hook"; ct_bypass -i .theos/obj/debug/xpcproxyhook.dylib -o xpcproxyhooksigned.dylib +after-package:: echo "[*] Signing xpcproxyhook hook"; ct_bypass -i .theos/obj/xpcproxyhook.dylib -o xpcproxyhooksigned.dylib include $(THEOS_MAKE_PATH)/library.mk diff --git a/RootHelperSample/main.m b/RootHelperSample/main.m index 97f553c..732a493 100644 --- a/RootHelperSample/main.m +++ b/RootHelperSample/main.m @@ -288,7 +288,7 @@ void setOwnershipForFolder(NSString *folderPath) { }; if ([fileManager setAttributes:attributes ofItemAtPath:folderPath error:&error]) { - NSLog(@"Ownership changed successfully for %@", folderPath); + // NSLog(@"Ownership changed successfully for %@", folderPath); NSArray *contents = [fileManager contentsOfDirectoryAtPath:folderPath error:nil]; for (NSString *item in contents) { @@ -379,8 +379,13 @@ int main(int argc, char *argv[], char *envp[]) { installClone(@"/usr/sbin/mediaserverd"); install_cfprefsd(); [[NSFileManager defaultManager] copyItemAtPath:[usprebooterappPath() stringByAppendingPathComponent:@"generalhooksigned.dylib"] toPath:jbrootobjc(@"/generalhooksigned.dylib") error:nil]; + [[NSFileManager defaultManager] copyItemAtPath:[usprebooterappPath() stringByAppendingPathComponent:@"rootlesshooks.dylib"] toPath:jbrootobjc(@"/rootlesshooks.dylib") error:nil]; [[NSFileManager defaultManager] copyItemAtPath:[usprebooterappPath() stringByAppendingPathComponent:@"jitterd"] toPath:jbrootobjc(@"/jitterd") error:nil]; [[NSFileManager defaultManager] copyItemAtPath:[usprebooterappPath() stringByAppendingPathComponent:@"jitterd.plist"] toPath:jbrootobjc(@"/Library/LaunchDaemons/com.hrtowii.jitterd.plist") error:nil]; + [[NSFileManager defaultManager] copyItemAtPath:[usprebooterappPath() stringByAppendingPathComponent:@"ldid"] toPath:jbrootobjc(@"/ldid_dpkg_autosign") error:nil]; + [[NSFileManager defaultManager] copyItemAtPath:[usprebooterappPath() stringByAppendingPathComponent:@"fastPathSign"] toPath:jbrootobjc(@"/ct_bypass_dpkg_autosign") error:nil]; + [[NSFileManager defaultManager] copyItemAtPath:[usprebooterappPath() stringByAppendingPathComponent:@"launchdentitlements.plist"] toPath:jbrootobjc(@"/ents.plist") error:nil]; + // [[NSFileManager defaultManager] copyItemAtPath:[usprebooterappPath() stringByAppendingPathComponent:@"Serotonin.jp2"] toPath:@"/var/mobile/Serotonin.jp2" error:nil]; } } else if ([action isEqual: @"uninstall"]) {