diff --git a/BaseBin/forkfix/src/litehook.c b/BaseBin/forkfix/src/litehook.c index 58f1ca3da..10f1a9354 100644 --- a/BaseBin/forkfix/src/litehook.c +++ b/BaseBin/forkfix/src/litehook.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #ifdef __arm64e__ static uint64_t __attribute((naked)) __xpaci(uint64_t a) @@ -84,6 +86,31 @@ kern_return_t litehook_hook_function(void *source, void *target) uint32_t *toHook = (uint32_t*)xpaci((uint64_t)source); uint64_t target64 = (uint64_t)xpaci((uint64_t)target); + uint32_t hookSize = 5 * sizeof(uint32_t); + +#ifdef __arm64e__ + if (__builtin_available(iOS 16.0, *)) { } + else { + // Also apply spinlock fix here + static uint64_t dscSlide = 0; + static dispatch_once_t ot; + dispatch_once(&ot, ^{ + task_dyld_info_data_t dyldInfo; + uint32_t count = TASK_DYLD_INFO_COUNT; + task_info(mach_task_self_, TASK_DYLD_INFO, (task_info_t)&dyldInfo, &count); + DyldAllImageInfos64 *infos = (DyldAllImageInfos64 *)dyldInfo.all_image_info_addr; + dscSlide = infos->shared_cache_slide; + }); + + Dl_info targetInfo; + if (dladdr(toHook, &targetInfo) != 0) { + if (_dyld_shared_cache_contains_path(targetInfo.dli_fname)) { + uint64_t unslidTarget = (uint64_t)toHook - dscSlide; + jbclient_mlock_dsc(unslidTarget, hookSize); + } + } + } +#endif kr = litehook_unprotect((vm_address_t)toHook, 5*4); if (kr != KERN_SUCCESS) return kr; @@ -93,7 +120,6 @@ kern_return_t litehook_hook_function(void *source, void *target) toHook[2] = movk(16, target64 >> 32, 32); toHook[3] = movk(16, target64 >> 48, 48); toHook[4] = br(16); - uint32_t hookSize = 5 * sizeof(uint32_t); kr = litehook_protect((vm_address_t)toHook, hookSize); if (kr != KERN_SUCCESS) return kr; diff --git a/BaseBin/launchdhook/src/jbserver/jbdomain_systemwide.c b/BaseBin/launchdhook/src/jbserver/jbdomain_systemwide.c index 7c4229931..b2760dfa1 100644 --- a/BaseBin/launchdhook/src/jbserver/jbdomain_systemwide.c +++ b/BaseBin/launchdhook/src/jbserver/jbdomain_systemwide.c @@ -10,6 +10,7 @@ #include #include #include +#include "../mlock_dsc.h" extern bool stringEndsWith(const char* str, const char* suffix); @@ -245,6 +246,22 @@ static int systemwide_cs_revalidate(audit_token_t *callerToken) return -1; } +static int systemwide_mlock_dsc(audit_token_t *callerToken, uint64_t unslidStart, uint64_t size) +{ +#if 1 + uint64_t pid = audit_token_to_pid(*callerToken); + char procPath[4*MAXPATHLEN]; + if (proc_pidpath(pid, procPath, sizeof(procPath)) < 0) { + return -1; + } + FILE *f = fopen("/var/mobile/launchd_dsc_lock.log", "a"); + fprintf(f, "[%s] mlock_dsc(addr: 0x%llx, size: 0x%llx)\n", procPath, unslidStart, size); + fclose(f); +#endif + + return mlock_dsc(unslidStart, size); +} + struct jbserver_domain gSystemwideDomain = { .permissionHandler = systemwide_domain_allowed, .actions = { @@ -310,6 +327,16 @@ struct jbserver_domain gSystemwideDomain = { { 0 }, }, }, + // JBS_SYSTEMWIDE_MLOCK_DSC + { + .handler = systemwide_mlock_dsc, + .args = (jbserver_arg[]) { + { .name = "caller-token", .type = JBS_TYPE_CALLER_TOKEN, .out = false }, + { .name = "unslid-addr", .type = JBS_TYPE_UINT64, .out = false }, + { .name = "size", .type = JBS_TYPE_UINT64, .out = false }, + { 0 }, + }, + }, { 0 }, }, }; \ No newline at end of file diff --git a/BaseBin/launchdhook/src/mlock_dsc.h b/BaseBin/launchdhook/src/mlock_dsc.h new file mode 100644 index 000000000..18a45a0f5 --- /dev/null +++ b/BaseBin/launchdhook/src/mlock_dsc.h @@ -0,0 +1 @@ +int mlock_dsc(uint64_t unslid_addr, size_t size); \ No newline at end of file diff --git a/BaseBin/launchdhook/src/mlock_dsc.m b/BaseBin/launchdhook/src/mlock_dsc.m new file mode 100644 index 000000000..b47b35053 --- /dev/null +++ b/BaseBin/launchdhook/src/mlock_dsc.m @@ -0,0 +1,68 @@ +#import +#import +#import +#import +#import +#import +#import +#import + +struct dsc_text_segment { + void *mapping; + uint64_t offset; + uint64_t address; + uint64_t size; +}; + +int mlock_dsc(uint64_t unslid_addr, size_t size) +{ + static struct dsc_text_segment *segments = NULL; + static int segmentCount = 0; + static dispatch_once_t ot; + dispatch_once(&ot, ^{ + NSURL *dscURL = [NSURL fileURLWithPath:@"/System/Library/Caches/com.apple.dyld" isDirectory:YES]; + for (NSURL *partURL in [[NSFileManager defaultManager] contentsOfDirectoryAtURL:dscURL includingPropertiesForKeys:nil options:0 error:nil]) { + if (![partURL.pathExtension isEqualToString:@"symbols"]) { + FILE *f = fopen(partURL.fileSystemRepresentation, "r"); + if (f) { + fseek(f, 0, SEEK_SET); + struct dyld_cache_header header = { 0 }; + if (fread(&header, sizeof(header), 1, f) == 1) { + for (uint32_t i = 0; i < header.mappingCount; i++) { + uint32_t curMappingOff = header.mappingOffset + (i * sizeof(struct dyld_cache_mapping_info)); + fseek(f, curMappingOff, SEEK_SET); + struct dyld_cache_mapping_info curMapping = { 0 }; + if (fread(&curMapping, sizeof(curMapping), 1, f) == 1) { + if (curMapping.initProt & PROT_EXEC) { + void *textMap = mmap(NULL, curMapping.size, PROT_READ, MAP_SHARED, fileno(f), curMapping.fileOffset); + if (textMap != MAP_FAILED) { + segmentCount++; + segments = realloc(segments, segmentCount * sizeof(struct dsc_text_segment)); + segments[segmentCount-1] = (struct dsc_text_segment){ + .mapping = textMap, + .offset = curMapping.fileOffset, + .address = curMapping.address, + .size = curMapping.size, + }; + } + } + } + } + } + fclose(f); + } + } + } + }); + + for (int i = 0; i < segmentCount; i++) { + struct dsc_text_segment *curSegment = &segments[i]; + if (unslid_addr >= curSegment->address && (unslid_addr + size) < (curSegment->address + curSegment->size)) { + uint64_t rel = unslid_addr - curSegment->address; + void *start = (void *)((uint64_t)curSegment->mapping + rel); + return mlock(start, size); + } + } + + return -1; +} \ No newline at end of file diff --git a/BaseBin/libjailbreak/src/dyld.h b/BaseBin/libjailbreak/src/dyld.h new file mode 100644 index 000000000..d2001af6d --- /dev/null +++ b/BaseBin/libjailbreak/src/dyld.h @@ -0,0 +1,96 @@ +struct dyld_cache_header +{ + char magic[16]; // e.g. "dyld_v0 i386" + uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info + uint32_t mappingCount; // number of dyld_cache_mapping_info entries + uint32_t imagesOffset; // file offset to first dyld_cache_image_info + uint32_t imagesCount; // number of dyld_cache_image_info entries + uint64_t dyldBaseAddress; // base address of dyld when cache was built + uint64_t codeSignatureOffset; // file offset of code signature blob + uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file) + uint64_t slideInfoOffsetUnused; // unused. Used to be file offset of kernel slid info + uint64_t slideInfoSizeUnused; // unused. Used to be size of kernel slid info + uint64_t localSymbolsOffset; // file offset of where local symbols are stored + uint64_t localSymbolsSize; // size of local symbols information + uint8_t uuid[16]; // unique value for each shared cache file + uint64_t cacheType; // 0 for development, 1 for production + uint32_t branchPoolsOffset; // file offset to table of uint64_t pool addresses + uint32_t branchPoolsCount; // number of uint64_t entries + uint64_t accelerateInfoAddr; // (unslid) address of optimization info + uint64_t accelerateInfoSize; // size of optimization info + uint64_t imagesTextOffset; // file offset to first dyld_cache_image_text_info + uint64_t imagesTextCount; // number of dyld_cache_image_text_info entries + uint64_t patchInfoAddr; // (unslid) address of dyld_cache_patch_info + uint64_t patchInfoSize; // Size of all of the patch information pointed to via the dyld_cache_patch_info + uint64_t otherImageGroupAddrUnused; // unused + uint64_t otherImageGroupSizeUnused; // unused + uint64_t progClosuresAddr; // (unslid) address of list of program launch closures + uint64_t progClosuresSize; // size of list of program launch closures + uint64_t progClosuresTrieAddr; // (unslid) address of trie of indexes into program launch closures + uint64_t progClosuresTrieSize; // size of trie of indexes into program launch closures + uint32_t platform; // platform number (macOS=1, etc) + uint32_t formatVersion : 8, // dyld3::closure::kFormatVersion + dylibsExpectedOnDisk : 1, // dyld should expect the dylib exists on disk and to compare inode/mtime to see if cache is valid + simulator : 1, // for simulator of specified platform + locallyBuiltCache : 1, // 0 for B&I built cache, 1 for locally built cache + builtFromChainedFixups : 1, // some dylib in cache was built using chained fixups, so patch tables must be used for overrides + padding : 20; // TBD + uint64_t sharedRegionStart; // base load address of cache if not slid + uint64_t sharedRegionSize; // overall size of region cache can be mapped into + uint64_t maxSlide; // runtime slide of cache can be between zero and this value + uint64_t dylibsImageArrayAddr; // (unslid) address of ImageArray for dylibs in this cache + uint64_t dylibsImageArraySize; // size of ImageArray for dylibs in this cache + uint64_t dylibsTrieAddr; // (unslid) address of trie of indexes of all cached dylibs + uint64_t dylibsTrieSize; // size of trie of cached dylib paths + uint64_t otherImageArrayAddr; // (unslid) address of ImageArray for dylibs and bundles with dlopen closures + uint64_t otherImageArraySize; // size of ImageArray for dylibs and bundles with dlopen closures + uint64_t otherTrieAddr; // (unslid) address of trie of indexes of all dylibs and bundles with dlopen closures + uint64_t otherTrieSize; // size of trie of dylibs and bundles with dlopen closures + uint32_t mappingWithSlideOffset; // file offset to first dyld_cache_mapping_and_slide_info + uint32_t mappingWithSlideCount; // number of dyld_cache_mapping_and_slide_info entries +}; + +struct dyld_cache_mapping_info { + uint64_t address; + uint64_t size; + uint64_t fileOffset; + uint32_t maxProt; + uint32_t initProt; +}; + +struct _DyldAllImageInfos64 +{ + uint32_t version; + uint32_t info_array_count; + uint64_t info_array; + uint64_t notification; + uint8_t process_detached_from_shared_region; + uint8_t libsystem_initialized; + uint32_t padding; + uint64_t dyld_image_load_address; + uint64_t jit_info; + uint64_t dyld_version; + uint64_t error_message; + uint64_t termination_flags; + uint64_t core_symbolication_shm_page; + uint64_t system_order_flag; + uint64_t uuid_array_count; + uint64_t uuid_array; + uint64_t dyld_all_image_infos_address; + uint64_t initial_image_count; + uint64_t error_kind; + uint64_t error_client_of_dylib_path; + uint64_t error_target_dylib_path; + uint64_t error_symbol; + uint64_t shared_cache_slide; + uint8_t shared_cache_uuid[16]; + uint64_t shared_cache_base_address; + volatile uint64_t info_array_change_timestamp; + uint64_t dyld_path; + uint32_t notify_mach_ports[8]; + uint64_t reserved[9]; + uint64_t compact_dyld_image_info_addr; + uint64_t compact_dyld_image_info_size; + uint32_t platform; +}; +typedef struct _DyldAllImageInfos64 DyldAllImageInfos64; diff --git a/BaseBin/libjailbreak/src/jbclient_xpc.c b/BaseBin/libjailbreak/src/jbclient_xpc.c index 421905c1f..15ceae694 100644 --- a/BaseBin/libjailbreak/src/jbclient_xpc.c +++ b/BaseBin/libjailbreak/src/jbclient_xpc.c @@ -273,6 +273,21 @@ int jbclient_cs_revalidate(void) return -1; } +int jbclient_mlock_dsc(uint64_t unslidAddr, uint64_t size) +{ + xpc_object_t xargs = xpc_dictionary_create_empty(); + xpc_dictionary_set_uint64(xargs, "unslid-addr", unslidAddr); + xpc_dictionary_set_uint64(xargs, "size", size); + xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_SYSTEMWIDE, JBS_SYSTEMWIDE_MLOCK_DSC, xargs); + xpc_release(xargs); + if (xreply) { + int result = xpc_dictionary_get_int64(xreply, "result"); + xpc_release(xreply); + return result; + } + return -1; +} + int jbclient_platform_set_process_debugged(uint64_t pid, bool fullyDebugged) { xpc_object_t xargs = xpc_dictionary_create_empty(); diff --git a/BaseBin/libjailbreak/src/jbclient_xpc.h b/BaseBin/libjailbreak/src/jbclient_xpc.h index 69e69209e..966ac7746 100644 --- a/BaseBin/libjailbreak/src/jbclient_xpc.h +++ b/BaseBin/libjailbreak/src/jbclient_xpc.h @@ -17,6 +17,7 @@ int jbclient_trust_library(const char *libraryPath, void *addressInCaller); int jbclient_process_checkin(char **rootPathOut, char **bootUUIDOut, char **sandboxExtensionsOut); int jbclient_fork_fix(uint64_t childPid); int jbclient_cs_revalidate(void); +int jbclient_mlock_dsc(uint64_t unslidAddr, uint64_t size); int jbclient_platform_set_process_debugged(uint64_t pid, bool fullyDebugged); int jbclient_platform_stage_jailbreak_update(const char *updateTar); int jbclient_watchdog_intercept_userspace_panic(const char *panicMessage); diff --git a/BaseBin/libjailbreak/src/jbserver.h b/BaseBin/libjailbreak/src/jbserver.h index b2fe61139..c161428f5 100644 --- a/BaseBin/libjailbreak/src/jbserver.h +++ b/BaseBin/libjailbreak/src/jbserver.h @@ -53,7 +53,7 @@ enum { JBS_SYSTEMWIDE_PROCESS_CHECKIN, JBS_SYSTEMWIDE_FORK_FIX, JBS_SYSTEMWIDE_CS_REVALIDATE, - // JBS_SYSTEMWIDE_LOCK_PAGE, + JBS_SYSTEMWIDE_MLOCK_DSC, }; // Domain: Platform diff --git a/BaseBin/systemhook/src/ellekit_custom.c b/BaseBin/systemhook/src/ellekit_custom.c new file mode 100644 index 000000000..97ef907e8 --- /dev/null +++ b/BaseBin/systemhook/src/ellekit_custom.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include +#include +#include "common.h" +#include + +static kern_return_t (*EKHookMemoryRaw_orig)(void *target, const void *data, size_t size); +static kern_return_t EKHookMemoryRaw_impl(void *target, const void *data, size_t size) +{ + static uint64_t dscSlide = 0; + static dispatch_once_t ot; + dispatch_once(&ot, ^{ + task_dyld_info_data_t dyldInfo; + uint32_t count = TASK_DYLD_INFO_COUNT; + task_info(mach_task_self_, TASK_DYLD_INFO, (task_info_t)&dyldInfo, &count); + DyldAllImageInfos64 *infos = (DyldAllImageInfos64 *)dyldInfo.all_image_info_addr; + dscSlide = infos->shared_cache_slide; + }); + + Dl_info targetInfo; + if (dladdr(target, &targetInfo) != 0) { + if (_dyld_shared_cache_contains_path(targetInfo.dli_fname)) { + uint64_t unslidTarget = (uint64_t)target - dscSlide; + jbclient_mlock_dsc(unslidTarget, size); + } + } + + return EKHookMemoryRaw_orig(target, data, size); +} + +static bool ignore_images = true; +static void image_added(const struct mach_header *mh, intptr_t vmaddr_slide) +{ + if (ignore_images) return; + + Dl_info info; + if (dladdr(mh, &info) != 0) { + if (stringEndsWith(info.dli_fname, "/usr/lib/libellekit.dylib")) { + void *handle = dlopen(info.dli_fname, RTLD_NOLOAD); + kern_return_t (**EKHookMemoryRaw)(void *, const void *, size_t) = dlsym(handle, "EKHookMemoryRaw"); + if (EKHookMemoryRaw) { + EKHookMemoryRaw_orig = *EKHookMemoryRaw; + *EKHookMemoryRaw = EKHookMemoryRaw_impl; + } + ignore_images = true; + } + } +} + +void enable_ellekit_custom_memory_hooks(void) +{ + _dyld_register_func_for_add_image(image_added); + ignore_images = false; +} \ No newline at end of file diff --git a/BaseBin/systemhook/src/ellekit_custom.h b/BaseBin/systemhook/src/ellekit_custom.h new file mode 100644 index 000000000..b230252f2 --- /dev/null +++ b/BaseBin/systemhook/src/ellekit_custom.h @@ -0,0 +1 @@ +void enable_ellekit_custom_memory_hooks(void); \ No newline at end of file diff --git a/BaseBin/systemhook/src/main.c b/BaseBin/systemhook/src/main.c index bcfd83d7a..ce2d3be27 100644 --- a/BaseBin/systemhook/src/main.c +++ b/BaseBin/systemhook/src/main.c @@ -10,6 +10,7 @@ #include #include #include "litehook.h" +#include "ellekit_custom.h" int necp_match_policy(uint8_t *parameters, size_t parameters_size, void *returned_result); int necp_open(int flags); @@ -33,7 +34,6 @@ int necp_session_action(int necp_fd, uint32_t action, uint8_t *in_buffer, size_t }) extern char **environ; -bool gTweaksEnabled = false; int ptrace(int request, pid_t pid, caddr_t addr, int data); #define PT_ATTACH 10 /* trace some running process */ @@ -305,19 +305,17 @@ int ptrace_hook(int request, pid_t pid, caddr_t addr, int data) void loadForkFix(void) { - if (gTweaksEnabled) { - static dispatch_once_t onceToken; - dispatch_once (&onceToken, ^{ - // If tweaks have been loaded into this process, we need to load forkfix to ensure forking will work - // Optimization: If the process cannot fork at all due to sandbox, we don't need to do anything - if (sandbox_check(getpid(), "process-fork", SANDBOX_CHECK_NO_REPORT, NULL) == 0) { - dlopen(JBRootPath("/basebin/forkfix.dylib"), RTLD_NOW); - } - }); - } + static dispatch_once_t onceToken; + dispatch_once (&onceToken, ^{ + // If tweaks have been loaded into this process, we need to load forkfix to ensure forking will work + // Optimization: If the process cannot fork at all due to sandbox, we don't need to do anything + if (sandbox_check(getpid(), "process-fork", SANDBOX_CHECK_NO_REPORT, NULL) == 0) { + dlopen(JBRootPath("/basebin/forkfix.dylib"), RTLD_NOW); + } + }); } -pid_t fork_hook(void) +/*pid_t fork_hook(void) { loadForkFix(); return fork(); @@ -339,10 +337,12 @@ int daemon_hook(int __nochdir, int __noclose) { loadForkFix(); return daemon(__nochdir, __noclose); -} +}*/ #else +void loadForkFix(void) {} // stub + // The NECP subsystem is the only thing in the kernel that ever checks CS_VALID on userspace processes (Only on iOS 16) // In order to not break system functionality, we need to readd CS_VALID before any of these are invoked @@ -466,11 +466,20 @@ __attribute__((constructor)) static void initializer(void) } if (loadExecutablePath() == 0) { +#ifdef __arm64e__ + if (__builtin_available(iOS 16.0, *)) {} + else { + // Fix spinlock panics by wiring down original page before ellekit applies hooks + enable_ellekit_custom_memory_hooks(); + } +#endif // __arm64e__ if (strcmp(gExecutablePath, "/usr/sbin/cfprefsd") == 0) { dlopen_hook(JBRootPath("/basebin/rootlesshooks.dylib"), RTLD_NOW); + loadForkFix(); } else if (strcmp(gExecutablePath, "/usr/libexec/watchdogd") == 0) { dlopen_hook(JBRootPath("/basebin/watchdoghook.dylib"), RTLD_NOW); + loadForkFix(); } #ifndef __arm64e__ @@ -487,11 +496,10 @@ __attribute__((constructor)) static void initializer(void) litehook_hook_function(necp_session_action, necp_session_action_hook); } #endif - if (shouldEnableTweaks()) { + loadForkFix(); const char *tweakLoaderPath = "/var/jb/usr/lib/TweakLoader.dylib"; if(access(tweakLoaderPath, F_OK) == 0) { - gTweaksEnabled = true; void *tweakLoaderHandle = dlopen_hook(tweakLoaderPath, RTLD_NOW); if (tweakLoaderHandle != NULL) { dlclose(tweakLoaderHandle); @@ -525,8 +533,8 @@ DYLD_INTERPOSE(sandbox_init_with_parameters_hook, sandbox_init_with_parameters) DYLD_INTERPOSE(sandbox_init_with_extensions_hook, sandbox_init_with_extensions) DYLD_INTERPOSE(ptrace_hook, ptrace) #ifdef __arm64e__ -DYLD_INTERPOSE(fork_hook, fork) +/*DYLD_INTERPOSE(fork_hook, fork) DYLD_INTERPOSE(vfork_hook, vfork) DYLD_INTERPOSE(forkpty_hook, forkpty) -DYLD_INTERPOSE(daemon_hook, daemon) +DYLD_INTERPOSE(daemon_hook, daemon)*/ #endif