diff --git a/LightMessaging b/LightMessaging index e6edada..fb5cc4c 160000 --- a/LightMessaging +++ b/LightMessaging @@ -1 +1 @@ -Subproject commit e6edada93c2ac45936785dc80535908455890889 +Subproject commit fb5cc4c1c32cff35956274f3d07943837b7424d9 diff --git a/Makefile b/Makefile index 45f7da7..073de67 100644 --- a/Makefile +++ b/Makefile @@ -2,23 +2,57 @@ LIBRARY_NAME = librocketbootstrap librocketbootstrap_FILES = Tweak.x Shims.x librocketbootstrap_LIBRARIES = substrate librocketbootstrap_FRAMEWORKS = Foundation +librocketbootstrap_USE_MODULES = 0 -TOOL_NAME = rocketd +TOOL_NAME = rocketd _rocketd_reenable rocketd_FILES = rocketd.c +rocketd_CFLAGS = -fblocks rocketd_FRAMEWORKS = CoreFoundation rocketd_INSTALL_PATH = /usr/libexec -rocketd_IPHONE_ARCHS = armv6 arm64 +rocketd_USE_MODULES = 0 +rocketd_CODESIGN_FLAGS = -Sentitlements.xml -ADDITIONAL_CFLAGS = -std=c99 +_rocketd_reenable_FILES = rocketd_reenable.c +_rocketd_reenable_INSTALL_PATH = /usr/libexec +_rocketd_reenable_USE_MODULES = 0 +_rocketd_reenable_CODESIGN_FLAGS = -Sentitlements.xml -IPHONE_ARCHS = armv6 armv7 armv7s arm64 +ADDITIONAL_CFLAGS = -std=c99 -Ioverlayheaders -SDKVERSION_armv6 = 5.1 -INCLUDE_SDKVERSION_armv6 = 7.0 -TARGET_IPHONEOS_DEPLOYMENT_VERSION = 4.0 +# Support targeting 3.0 in packaged builds, but allow testing packages/builds to be missing support for old iOS versions +XCODE4_PATH ?= /Applications/Xcode_Legacy.app +XCODE6_PATH ?= /Volumes/Xcode/Xcode.app +XCODE9_PATH ?= /Volumes/Xcode_9.4.1/Xcode.app +ifeq ($(wildcard $(XCODE4_PATH)/.*),) +ADDITIONAL_CFLAGS += -Idefaultheaders +IPHONE_ARCHS = armv7 armv7s arm64 arm64e +TARGET_IPHONEOS_DEPLOYMENT_VERSION = 8.4 +ifeq ($(FINALPACKAGE),1) +$(error Building final package requires a legacy Xcode install!) +endif +else +armv6_CFLAGS += -Ifullheaders +armv7_CFLAGS += -Ifullheaders +armv7s_CFLAGS += -Ifullheaders +arm64_CFLAGS += -Idefaultheaders +arm64e_CFLAGS += -Idefaultheaders +rocketd_IPHONE_ARCHS = armv6 arm64 +_rocketd_reenable_IPHONE_ARCHS = armv6 arm64 +IPHONE_ARCHS = armv6 armv7 armv7s arm64 arm64e +SDKVERSION_armv6 = 5.1 +INCLUDE_SDKVERSION_armv6 = 8.4 TARGET_IPHONEOS_DEPLOYMENT_VERSION_armv6 = 3.0 -THEOS_PLATFORM_SDK_ROOT_armv6 = /Applications/Xcode_Legacy.app/Contents/Developer +TARGET_IPHONEOS_DEPLOYMENT_VERSION_armv7 = 4.0 +TARGET_IPHONEOS_DEPLOYMENT_VERSION_armv7s = 6.0 +TARGET_IPHONEOS_DEPLOYMENT_VERSION_arm64 = 7.0 +TARGET_IPHONEOS_DEPLOYMENT_VERSION_arm64e = 12.0 +TARGET_IPHONEOS_DEPLOYMENT_VERSION = 9.0 +THEOS_PLATFORM_SDK_ROOT_armv6 = $(XCODE4_PATH)/Contents/Developer +THEOS_PLATFORM_SDK_ROOT_armv7 = $(XCODE6_PATH)/Contents/Developer +THEOS_PLATFORM_SDK_ROOT_armv7s = $(XCODE6_PATH)/Contents/Developer +THEOS_PLATFORM_SDK_ROOT_arm64 = $(XCODE9_PATH)/Contents/Developer +endif include framework/makefiles/common.mk include framework/makefiles/library.mk @@ -28,3 +62,4 @@ stage:: mkdir -p "$(THEOS_STAGING_DIR)/usr/include" cp -a rocketbootstrap.h rocketbootstrap_dynamic.h "$(THEOS_STAGING_DIR)/usr/include" plutil -convert binary1 "$(THEOS_STAGING_DIR)/Library/MobileSubstrate/DynamicLibraries/RocketBootstrap.plist" + plutil -convert binary1 "$(THEOS_STAGING_DIR)/Library/LaunchDaemons/com.rpetrich.rocketbootstrapd.plist" diff --git a/Shims.x b/Shims.x index 7aeaf39..f153859 100644 --- a/Shims.x +++ b/Shims.x @@ -1,15 +1,17 @@ +#import "log.h" +#import "unfair_lock.h" #import "rocketbootstrap_internal.h" #import #import #import -static OSSpinLock spin_lock; +static unfair_lock shim_lock; kern_return_t bootstrap_look_up3(mach_port_t bp, const name_t service_name, mach_port_t *sp, pid_t target_pid, const uuid_t instance_id, uint64_t flags) __attribute__((weak_import)); -kern_return_t (*_bootstrap_look_up3)(mach_port_t bp, const name_t service_name, mach_port_t *sp, pid_t target_pid, const uuid_t instance_id, uint64_t flags); +static kern_return_t (*_bootstrap_look_up3)(mach_port_t bp, const name_t service_name, mach_port_t *sp, pid_t target_pid, const uuid_t instance_id, uint64_t flags); -kern_return_t $bootstrap_look_up3(mach_port_t bp, const name_t service_name, mach_port_t *sp, pid_t target_pid, const uuid_t instance_id, uint64_t flags) +static kern_return_t $bootstrap_look_up3(mach_port_t bp, const name_t service_name, mach_port_t *sp, pid_t target_pid, const uuid_t instance_id, uint64_t flags) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSMutableDictionary *threadDictionary = [NSThread currentThread].threadDictionary; @@ -26,12 +28,12 @@ kern_return_t $bootstrap_look_up3(mach_port_t bp, const name_t service_name, mac static void hook_bootstrap_lookup(void) { static bool hooked_bootstrap_look_up; - OSSpinLockLock(&spin_lock); + unfair_lock_lock(&shim_lock); if (!hooked_bootstrap_look_up) { MSHookFunction(bootstrap_look_up3, $bootstrap_look_up3, (void **)&_bootstrap_look_up3); hooked_bootstrap_look_up = true; } - OSSpinLockUnlock(&spin_lock); + unfair_lock_unlock(&shim_lock); } CFMessagePortRef rocketbootstrap_cfmessageportcreateremote(CFAllocatorRef allocator, CFStringRef name) @@ -98,13 +100,13 @@ static bool has_hooked_messaging_center; - (void)runServerOnCurrentThreadProtectedByEntitlement:(id)entitlement { + %orig(); if (objc_getAssociatedObject(self, &has_hooked_messaging_center)) { NSString **_centerName = CHIvarRef(self, _centerName, NSString *); if (_centerName && *_centerName) { rocketbootstrap_unlock([*_centerName UTF8String]); } } - %orig(); } %end @@ -115,11 +117,116 @@ void rocketbootstrap_distributedmessagingcenter_apply(CPDistributedMessagingCent { if (rocketbootstrap_is_passthrough()) return; - OSSpinLockLock(&spin_lock); + unfair_lock_lock(&shim_lock); if (!has_hooked_messaging_center) { has_hooked_messaging_center = true; %init(messaging_center); } - OSSpinLockUnlock(&spin_lock); + unfair_lock_unlock(&shim_lock); objc_setAssociatedObject(messaging_center, &has_hooked_messaging_center, (id)kCFBooleanTrue, OBJC_ASSOCIATION_ASSIGN); } + +#ifdef __clang__ + +#ifndef __IPHONE_9_0 +#define __IPHONE_9_0 90000 +#define __AVAILABILITY_INTERNAL__IPHONE_9_0 +#endif + +#include + +static xpc_endpoint_t _xpc_endpoint_create(mach_port_t port) +{ + static xpc_endpoint_t(*__xpc_endpoint_create)(mach_port_t); + if (!__xpc_endpoint_create) { + MSImageRef libxpc = MSGetImageByName("/usr/lib/system/libxpc.dylib"); + if (!libxpc) { + return NULL; + } + __xpc_endpoint_create = MSFindSymbol(libxpc, "__xpc_endpoint_create"); + if (!__xpc_endpoint_create) { + return NULL; + } + } + return __xpc_endpoint_create(port); +} + +static mach_port_t _xpc_connection_copy_listener_port(xpc_connection_t connection) +{ + static mach_port_t(*__xpc_connection_copy_listener_port)(xpc_connection_t); + if (!__xpc_connection_copy_listener_port) { + MSImageRef libxpc = MSGetImageByName("/usr/lib/system/libxpc.dylib"); + if (!libxpc) { + return MACH_PORT_NULL; + } + __xpc_connection_copy_listener_port = MSFindSymbol(libxpc, "__xpc_connection_copy_listener_port"); + if (!__xpc_connection_copy_listener_port) { + return MACH_PORT_NULL; + } + } + return __xpc_connection_copy_listener_port(connection); +} + +xpc_object_t xpc_connection_copy_entitlement_value(xpc_connection_t, const char* entitlement); + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +xpc_connection_t rocketbootstrap_xpc_connection_create(const char *name, dispatch_queue_t targetq, uint64_t flags) +{ + mach_port_t bootstrap = MACH_PORT_NULL; + if (task_get_bootstrap_port(mach_task_self(), &bootstrap) != 0) { + return NULL; + } + if (flags & XPC_CONNECTION_MACH_SERVICE_LISTENER) { + xpc_connection_t result = xpc_connection_create(NULL, targetq); + mach_port_t port = _xpc_connection_copy_listener_port(result); + if (rocketbootstrap_register(bootstrap, (char *)name, port) != 0) { + xpc_release(result); + return NULL; + } + return result; + } + mach_port_t send_port = MACH_PORT_NULL; + if (rocketbootstrap_look_up(bootstrap, name, &send_port) != 0) { + return NULL; + } + xpc_endpoint_t endpoint = _xpc_endpoint_create(send_port); + xpc_connection_t result = xpc_connection_create_from_endpoint(endpoint); + xpc_release(endpoint); + if (targetq != NULL) { + xpc_connection_set_target_queue(result, targetq); + } + return result; +} + +xpc_object_t rocketbootstrap_xpc_connection_copy_application_identifier(xpc_connection_t connection) +{ + xpc_object_t application_id = xpc_connection_copy_entitlement_value(connection, "application-identifier"); + if (!application_id) { + return NULL; + } + if (xpc_get_type(application_id) != XPC_TYPE_STRING) { + xpc_release(application_id); + return NULL; + } + xpc_object_t team_id = xpc_connection_copy_entitlement_value(connection, "com.apple.developer.team-identifier"); + if (!team_id) { + return application_id; + } + if (xpc_get_type(team_id) != XPC_TYPE_STRING) { + xpc_release(team_id); + return application_id; + } + const char *application_id_str = xpc_string_get_string_ptr(application_id); + const char *team_id_str = xpc_string_get_string_ptr(team_id); + size_t team_id_length = xpc_string_get_length(team_id); + if (memcmp(application_id_str, team_id_str, team_id_length) != 0 || application_id_str[team_id_length] != '.') { + xpc_release(team_id); + return application_id; + } + xpc_object_t trimmed_id = xpc_string_create(application_id_str + team_id_length + 1); + xpc_release(team_id); + xpc_release(application_id); + return trimmed_id; +} + +#endif diff --git a/Tweak.x b/Tweak.x index a0816d9..5fb5942 100644 --- a/Tweak.x +++ b/Tweak.x @@ -1,5 +1,14 @@ +#include +#undef __IOS_PROHIBITED +#define __IOS_PROHIBITED + +#include + #define LIGHTMESSAGING_USE_ROCKETBOOTSTRAP 0 +#define LIGHTMESSAGING_TIMEOUT 300 #import "LightMessaging/LightMessaging.h" +#import "log.h" +#import "unfair_lock.h" #import "rocketbootstrap_internal.h" @@ -16,6 +25,12 @@ #import //#import #import +#import +#import +#import +#define __DISPATCH_INDIRECT__ +#define DISPATCH_MACH_SPI 1 +#import extern int *_NSGetArgc(void); extern const char ***_NSGetArgv(void); @@ -25,8 +40,31 @@ extern const char ***_NSGetArgv(void); static BOOL isDaemon; -kern_return_t rocketbootstrap_look_up(mach_port_t bp, const name_t service_name, mach_port_t *sp) +static BOOL fill_redirected_name(char new_name[BOOTSTRAP_MAX_NAME_LEN], const name_t old_name) { + size_t length = strlen(old_name); + if (length > 128 - 8) { + return NO; + } + memcpy(new_name, "cy:rbs:", 7); + memcpy(new_name + 7, old_name, length + 1); + return YES; +} + +static kern_return_t rocketbootstrap_look_up_with_timeout(mach_port_t bp, const name_t service_name, mach_port_t *sp, mach_msg_timeout_t timeout) { +#ifdef DEBUG + NSLog(@"RocketBootstrap: rocketbootstrap_look_up(%llu, %s, %p)", (unsigned long long)bp, service_name, sp); +#endif + if (rocketbootstrap_uses_name_redirection()) { + char redirected_name[BOOTSTRAP_MAX_NAME_LEN]; + if (!fill_redirected_name(redirected_name, service_name)) { + return 1; + } + kern_return_t result = bootstrap_look_up(bp, redirected_name, sp); + if (result == 0) { + return 0; + } + } if (rocketbootstrap_is_passthrough() || isDaemon) { if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_5_0) { int sandbox_result = sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_LOCAL_NAME | SANDBOX_CHECK_NO_REPORT, service_name); @@ -51,13 +89,20 @@ kern_return_t rocketbootstrap_look_up(mach_port_t bp, const name_t service_name, // Ask our service running inside of the com.apple.ReportCrash.SimulateCrash job mach_port_t servicesPort = MACH_PORT_NULL; kern_return_t err = bootstrap_look_up(bp, "com.apple.ReportCrash.SimulateCrash", &servicesPort); - if (err) + if (err) { +#ifdef DEBUG + NSLog(@"RocketBootstrap: = %lld (failed to lookup com.apple.ReportCrash.SimulateCrash)", (unsigned long long)err); +#endif return err; + } mach_port_t selfTask = mach_task_self(); // Create a reply port mach_port_name_t replyPort = MACH_PORT_NULL; err = mach_port_allocate(selfTask, MACH_PORT_RIGHT_RECEIVE, &replyPort); if (err) { +#ifdef DEBUG + NSLog(@"RocketBootstrap: = %lld (failed to allocate port)", (unsigned long long)err); +#endif mach_port_deallocate(selfTask, servicesPort); return err; } @@ -78,14 +123,27 @@ kern_return_t rocketbootstrap_look_up(mach_port_t bp, const name_t service_name, message->head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); message->name_length = service_name_size; memcpy(&message->name[0], service_name, service_name_size); - err = mach_msg(&message->head, MACH_SEND_MSG | MACH_RCV_MSG, size, size, replyPort, 0, MACH_PORT_NULL); + mach_msg_option_t options = MACH_SEND_MSG | MACH_RCV_MSG; + if (timeout == 0) { + timeout = MACH_MSG_TIMEOUT_NONE; + } else { + options |= MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT; + } + err = mach_msg(&message->head, options, size, size, replyPort, timeout, MACH_PORT_NULL); // Parse response if (!err) { _rocketbootstrap_lookup_response_t *response = (_rocketbootstrap_lookup_response_t *)message; - if (response->body.msgh_descriptor_count) + if (response->body.msgh_descriptor_count) { *sp = response->response_port.name; - else +#ifdef DEBUG + NSLog(@"RocketBootstrap: = 0 (success)"); +#endif + } else { err = 1; +#ifdef DEBUG + NSLog(@"RocketBootstrap: = 1 (disallowed)"); +#endif + } } // Cleanup mach_port_deallocate(selfTask, servicesPort); @@ -93,15 +151,20 @@ kern_return_t rocketbootstrap_look_up(mach_port_t bp, const name_t service_name, return err; } +kern_return_t rocketbootstrap_look_up(mach_port_t bp, const name_t service_name, mach_port_t *sp) +{ + return rocketbootstrap_look_up_with_timeout(bp, service_name, sp, LIGHTMESSAGING_TIMEOUT); +} + static NSMutableSet *allowedNames; -static volatile OSSpinLock namesLock; +static unfair_lock namesLock; static void daemon_restarted_callback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - OSSpinLockLock(&namesLock); + unfair_lock_lock(&namesLock); NSSet *allNames = [allowedNames copy]; - OSSpinLockUnlock(&namesLock); + unfair_lock_unlock(&namesLock); for (NSString *name in allNames) { const char *service_name = [name UTF8String]; LMConnectionSendOneWay(&connection, 0, service_name, strlen(service_name)); @@ -112,40 +175,91 @@ static void daemon_restarted_callback(CFNotificationCenterRef center, void *obse kern_return_t rocketbootstrap_unlock(const name_t service_name) { +#ifdef DEBUG + NSLog(@"RocketBootstrap: rocketbootstrap_unlock(%s)", service_name); +#endif if (rocketbootstrap_is_passthrough()) return 0; + if (rocketbootstrap_uses_name_redirection()) { + char redirected_name[BOOTSTRAP_MAX_NAME_LEN]; + if (!fill_redirected_name(redirected_name, service_name)) { + return 1; + } + mach_port_t bootstrap = MACH_PORT_NULL; + task_get_bootstrap_port(mach_task_self(), &bootstrap); + mach_port_t service; + kern_return_t err = bootstrap_look_up(bootstrap, service_name, &service); + if (err != 0) { + // If the current process is permitted to register for this port, assume it's about to + int sandbox_result = sandbox_check(getpid(), "mach-register", SANDBOX_FILTER_LOCAL_NAME | SANDBOX_CHECK_NO_REPORT, service_name); + if (sandbox_result) { + return sandbox_result; + } + char *copied_service_name = strdup(service_name); + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); + CFRunLoopPerformBlock(runLoop, kCFRunLoopCommonModes, ^{ + mach_port_t bootstrap = MACH_PORT_NULL; + task_get_bootstrap_port(mach_task_self(), &bootstrap); + mach_port_t service; + kern_return_t err = bootstrap_look_up(bootstrap, copied_service_name, &service); + if (err == 0) { + char redirected_name[BOOTSTRAP_MAX_NAME_LEN]; + fill_redirected_name(redirected_name, copied_service_name); + err = bootstrap_register(bootstrap, redirected_name, service); + if (err != 0) { + mach_port_deallocate(mach_task_self(), service); + } + } + free(copied_service_name); + }); + CFRunLoopWakeUp(runLoop); + return 0; + } else { + err = bootstrap_register(bootstrap, redirected_name, service); + if (err != 0) { + mach_port_deallocate(mach_task_self(), service); + return err; + } + } + } NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSString *serviceNameString = [NSString stringWithUTF8String:service_name]; - OSSpinLockLock(&namesLock); - BOOL containedName = [allowedNames containsObject:serviceNameString]; - OSSpinLockUnlock(&namesLock); + unfair_lock_lock(&namesLock); + BOOL containedName; + if (!allowedNames) { + allowedNames = [[NSMutableSet alloc] init]; + [allowedNames addObject:serviceNameString]; + CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), &daemon_restarted_callback, daemon_restarted_callback, CFSTR("com.rpetrich.rocketd.started"), NULL, CFNotificationSuspensionBehaviorCoalesce); + containedName = NO; + } else { + containedName = [allowedNames containsObject:serviceNameString]; + if (!containedName) { + [allowedNames addObject:serviceNameString]; + } + } + unfair_lock_unlock(&namesLock); + [pool drain]; if (containedName) { - [pool drain]; return 0; } // Ask rocketd to unlock it for us int sandbox_result = sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_LOCAL_NAME | SANDBOX_CHECK_NO_REPORT, kRocketBootstrapUnlockService); if (sandbox_result) { - [pool drain]; return sandbox_result; } - kern_return_t result = LMConnectionSendOneWay(&connection, 0, service_name, strlen(service_name)); - if (!result) { - OSSpinLockLock(&namesLock); - if (!allowedNames) { - allowedNames = [[NSMutableSet alloc] init]; - CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), &daemon_restarted_callback, daemon_restarted_callback, CFSTR("com.rpetrich.rocketd.started"), NULL, CFNotificationSuspensionBehaviorCoalesce); - } - [allowedNames addObject:serviceNameString]; - OSSpinLockUnlock(&namesLock); - } - [pool drain]; - return result; + return LMConnectionSendOneWay(&connection, 0, service_name, strlen(service_name)); } #pragma GCC diagnostic ignored "-Wdeprecated-declarations" kern_return_t rocketbootstrap_register(mach_port_t bp, name_t service_name, mach_port_t sp) { + if (rocketbootstrap_uses_name_redirection()) { + char redirected_name[BOOTSTRAP_MAX_NAME_LEN]; + if (!fill_redirected_name(redirected_name, service_name)) { + return 1; + } + return bootstrap_register(bp, redirected_name, sp); + } kern_return_t err = rocketbootstrap_unlock(service_name); if (err) return err; @@ -153,68 +267,85 @@ kern_return_t rocketbootstrap_register(mach_port_t bp, name_t service_name, mach } #pragma GCC diagnostic warning "-Wdeprecated-declarations" +static kern_return_t handle_bootstrap_lookup_msg(mach_msg_header_t *request) +{ + _rocketbootstrap_lookup_query_t *lookup_message = (_rocketbootstrap_lookup_query_t *)request; + // Extract service name + size_t length = request->msgh_size - offsetof(_rocketbootstrap_lookup_query_t, name); + if (lookup_message->name_length <= length) { + length = lookup_message->name_length; + } +#ifdef DEBUG + NSLog(@"RocketBootstrap: handle_bootstrap_lookup_msg(%.*s)", (int)length, &lookup_message->name[0]); +#endif + // Ask rocketd if it's unlocked + LMResponseBuffer buffer; + if (LMConnectionSendTwoWay(&connection, 1, &lookup_message->name[0], length, &buffer)) + return 1; + BOOL nameIsAllowed = LMResponseConsumeInteger(&buffer) != 0; + // Lookup service port + mach_port_t servicePort = MACH_PORT_NULL; + mach_port_t selfTask = mach_task_self(); + kern_return_t err; + if (nameIsAllowed) { + mach_port_t bootstrap = MACH_PORT_NULL; + err = task_get_bootstrap_port(selfTask, &bootstrap); + if (!err) { + char *buffer = malloc(length + 1); + if (buffer) { + memcpy(buffer, lookup_message->name, length); + buffer[length] = '\0'; + err = bootstrap_look_up(bootstrap, buffer, &servicePort); + free(buffer); + } + } + } + // Generate response + _rocketbootstrap_lookup_response_t response; + response.head.msgh_id = 0; + response.head.msgh_size = (sizeof(_rocketbootstrap_lookup_response_t) + 3) & ~3; + response.head.msgh_remote_port = request->msgh_remote_port; + response.head.msgh_local_port = MACH_PORT_NULL; + response.head.msgh_reserved = 0; + response.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0); + if (servicePort != MACH_PORT_NULL) { + response.head.msgh_bits |= MACH_MSGH_BITS_COMPLEX; + response.body.msgh_descriptor_count = 1; + response.response_port.name = servicePort; + response.response_port.disposition = MACH_MSG_TYPE_COPY_SEND; + response.response_port.type = MACH_MSG_PORT_DESCRIPTOR; + } else { + response.body.msgh_descriptor_count = 0; + } + // Send response + err = mach_msg(&response.head, MACH_SEND_MSG, response.head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + if (err) { + if (servicePort != MACH_PORT_NULL) { + mach_port_mod_refs(selfTask, servicePort, MACH_PORT_RIGHT_SEND, -1); + } + } + return err; +} + mach_msg_return_t mach_msg_server_once(boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), mach_msg_size_t max_size, mach_port_t rcv_name, mach_msg_options_t options); -mach_msg_return_t (*_mach_msg_server_once)(boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), mach_msg_size_t max_size, mach_port_t rcv_name, mach_msg_options_t options); +static mach_msg_return_t (*_mach_msg_server_once)(boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), mach_msg_size_t max_size, mach_port_t rcv_name, mach_msg_options_t options); -static volatile OSSpinLock server_once_lock; +static unfair_lock server_once_lock; static boolean_t (*server_once_demux_orig)(mach_msg_header_t *, mach_msg_header_t *); static bool continue_server_once; static boolean_t new_demux(mach_msg_header_t *request, mach_msg_header_t *reply) { +#ifdef DEBUG + NSLog(@"RocketBootstrap: new_demux(%lld)", (long long)request->msgh_id); +#endif // Highjack ROCKETBOOTSTRAP_LOOKUP_ID from the com.apple.ReportCrash.SimulateCrash demuxer if (request->msgh_id == ROCKETBOOTSTRAP_LOOKUP_ID) { continue_server_once = true; - _rocketbootstrap_lookup_query_t *lookup_message = (_rocketbootstrap_lookup_query_t *)request; - // Extract service name - size_t length = request->msgh_size - offsetof(_rocketbootstrap_lookup_query_t, name); - if (lookup_message->name_length <= length) { - length = lookup_message->name_length; - } - // Ask rocketd if it's unlocked - LMResponseBuffer buffer; - if (LMConnectionSendTwoWay(&connection, 1, &lookup_message->name[0], length, &buffer)) - return false; - if (!LMResponseConsumeInteger(&buffer)) - return false; - // Lookup service port - mach_port_t servicePort = MACH_PORT_NULL; - mach_port_t selfTask = mach_task_self(); - BOOL nameIsAllowed = YES; - kern_return_t err; - if (nameIsAllowed) { - mach_port_t bootstrap = MACH_PORT_NULL; - err = task_get_bootstrap_port(selfTask, &bootstrap); - if (!err) { - NSString *name = [[NSString alloc] initWithBytes:&lookup_message->name[0] length:length encoding:NSUTF8StringEncoding]; - bootstrap_look_up(bootstrap, [name UTF8String], &servicePort); - [name release]; - } - } - // Generate response - _rocketbootstrap_lookup_response_t response; - response.head.msgh_id = 0; - response.head.msgh_size = (sizeof(_rocketbootstrap_lookup_response_t) + 3) & ~3; - response.head.msgh_remote_port = request->msgh_remote_port; - response.head.msgh_local_port = MACH_PORT_NULL; - response.head.msgh_reserved = 0; - response.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0); - if (servicePort != MACH_PORT_NULL) { - response.head.msgh_bits |= MACH_MSGH_BITS_COMPLEX; - response.body.msgh_descriptor_count = 1; - response.response_port.name = servicePort; - response.response_port.disposition = MACH_MSG_TYPE_COPY_SEND; - response.response_port.type = MACH_MSG_PORT_DESCRIPTOR; - } else { - response.body.msgh_descriptor_count = 0; - } - // Send response - err = mach_msg(&response.head, MACH_SEND_MSG, response.head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + kern_return_t err = handle_bootstrap_lookup_msg(request); if (err) { - if (servicePort != MACH_PORT_NULL) - mach_port_mod_refs(selfTask, servicePort, MACH_PORT_RIGHT_SEND, -1); - mach_port_mod_refs(selfTask, reply->msgh_remote_port, MACH_PORT_RIGHT_SEND_ONCE, -1); + mach_port_mod_refs(mach_task_self(), reply->msgh_remote_port, MACH_PORT_RIGHT_SEND_ONCE, -1); } return true; } @@ -223,19 +354,22 @@ static boolean_t new_demux(mach_msg_header_t *request, mach_msg_header_t *reply) static mach_msg_return_t $mach_msg_server_once(boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), mach_msg_size_t max_size, mach_port_t rcv_name, mach_msg_options_t options) { +#ifdef DEBUG + NSLog(@"RocketBootstrap: mach_msg_server_once(%p, %llu, %llu, 0x%llx)", demux, (unsigned long long)max_size, (unsigned long long)rcv_name, (long long)options); +#endif // Highjack com.apple.ReportCrash.SimulateCrash's use of mach_msg_server_once - OSSpinLockLock(&server_once_lock); + unfair_lock_lock(&server_once_lock); if (!server_once_demux_orig) { server_once_demux_orig = demux; demux = new_demux; } else if (server_once_demux_orig == demux) { demux = new_demux; } else { - OSSpinLockUnlock(&server_once_lock); + unfair_lock_unlock(&server_once_lock); mach_msg_return_t result = _mach_msg_server_once(demux, max_size, rcv_name, options); return result; } - OSSpinLockUnlock(&server_once_lock); + unfair_lock_unlock(&server_once_lock); mach_msg_return_t result; do { continue_server_once = false; @@ -244,6 +378,182 @@ static mach_msg_return_t $mach_msg_server_once(boolean_t (*demux)(mach_msg_heade return result; } +#ifdef __clang__ + +static void *interceptedConnection; + +static void *(*_xpc_connection_create_mach_service)(const char *name, dispatch_queue_t targetq, uint64_t flags); +static void *$xpc_connection_create_mach_service(const char *name, dispatch_queue_t targetq, uint64_t flags) +{ +#ifdef DEBUG + NSLog(@"RocketBootstrap: xpc_connection_create_mach_service(%s, %p, %lld)", name, targetq, (unsigned long long)flags); +#endif + if (name && strcmp(name, "com.apple.ReportCrash.SimulateCrash") == 0) { + void *result = _xpc_connection_create_mach_service(name, targetq, flags); + interceptedConnection = result; + return result; + } + return _xpc_connection_create_mach_service(name, targetq, flags); +} + +static void (*__xpc_connection_mach_event)(void *context, dispatch_mach_reason_t reason, dispatch_mach_msg_t message, mach_error_t error); +static void $_xpc_connection_mach_event(void *context, dispatch_mach_reason_t reason, dispatch_mach_msg_t message, mach_error_t error) +{ +#ifdef DEBUG + NSLog(@"RocketBootstrap: _xpc_connection_mach_event(%p, %lu, %p, 0x%x)", context, reason, message, error); +#endif + // Highjack ROCKETBOOTSTRAP_LOOKUP_ID from the com.apple.ReportCrash.SimulateCrash XPC service + if ((reason == DISPATCH_MACH_MESSAGE_RECEIVED) && (context == interceptedConnection)) { + size_t size; + mach_msg_header_t *header = dispatch_mach_msg_get_msg(message, &size); + if (header->msgh_id == ROCKETBOOTSTRAP_LOOKUP_ID) { + handle_bootstrap_lookup_msg(header); + return; + } + } + return __xpc_connection_mach_event(context, reason, message, error); +} + +#endif + +static pid_t pid_of_process(const char *process_name) +{ + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; + size_t miblen = 4; + + size_t size; + int st = sysctl(mib, miblen, NULL, &size, NULL, 0); + + struct kinfo_proc * process = NULL; + struct kinfo_proc * newprocess = NULL; + + do { + if (size == 0){ + st = sysctl(mib, miblen, NULL, &size, NULL, 0); + } + + size += size / 10; + newprocess = (struct kinfo_proc *)realloc(process, size); + + if (!newprocess) { + if (process) { + free(process); + } + return 0; + } + + process = newprocess; + st = sysctl(mib, miblen, process, &size, NULL, 0); + + } while (st == -1 && errno == ENOMEM); + + if (st == 0) { + if (size % sizeof(struct kinfo_proc) == 0) { + int nprocess = size / sizeof(struct kinfo_proc); + if (nprocess) { + for (int i = nprocess - 1; i >= 0; i--) { + if (strcmp(process[i].kp_proc.p_comm, process_name) == 0) { + pid_t result = process[i].kp_proc.p_pid; + free(process); + return result; + } + } + } + } + } + + free(process); + return 0; +} + +static int daemon_die_queue; +static CFFileDescriptorRef daemon_die_fd; +static CFRunLoopSourceRef daemon_die_source; + +static void process_terminate_callback(CFFileDescriptorRef fd, CFOptionFlags callBackTypes, void *info); + +static void observe_rocketd(void) +{ + // Force the daemon to load + mach_port_t bootstrap = MACH_PORT_NULL; + mach_port_t self = mach_task_self(); + task_get_bootstrap_port(self, &bootstrap); + mach_port_t servicesPort = MACH_PORT_NULL; + kern_return_t err = bootstrap_look_up(bootstrap, "com.rpetrich.rocketbootstrapd", &servicesPort); + if (err) { +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wavailability" +#endif + pid_t pid; + char *const argv[] = { "/usr/libexec/_rocketd_reenable", NULL }; + if (posix_spawn(&pid, "/usr/libexec/_rocketd_reenable", NULL, NULL, argv, NULL) == 0) { + waitpid(pid, NULL, 0); + } +#if __clang__ +#pragma clang diagnostic pop +#endif + err = bootstrap_look_up(bootstrap, "com.rpetrich.rocketbootstrapd", &servicesPort); + } + if (err) { +#ifdef DEBUG + NSLog(@"RocketBootstrap: failed to launch rocketd!"); +#endif + } else { + mach_port_name_t replyPort = MACH_PORT_NULL; + err = mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &replyPort); + if (err == 0) { + LMResponseBuffer buffer; + uint32_t size = LMBufferSizeForLength(0); + memset(&buffer.message, 0, sizeof(LMMessage)); + buffer.message.head.msgh_id = 2; + buffer.message.head.msgh_size = size; + buffer.message.head.msgh_local_port = replyPort; + buffer.message.head.msgh_reserved = 0; + buffer.message.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); + buffer.message.head.msgh_remote_port = servicesPort; + buffer.message.body.msgh_descriptor_count = 0; + buffer.message.data.in_line.length = 0; + err = mach_msg(&buffer.message.head, MACH_SEND_MSG | MACH_RCV_MSG | _LIGHTMESSAGING_TIMEOUT_FLAGS, size, sizeof(LMResponseBuffer), replyPort, LIGHTMESSAGING_TIMEOUT, MACH_PORT_NULL); + if (err) { + } + // Cleanup + mach_port_mod_refs(self, replyPort, MACH_PORT_RIGHT_RECEIVE, -1); + } + mach_port_mod_refs(self, servicesPort, MACH_PORT_RIGHT_SEND, -1); + } + // Find it + pid_t pid = pid_of_process("rocketd"); + if (pid) { +#ifdef DEBUG + NSLog(@"RocketBootstrap: rocketd found: %d", pid); +#endif + daemon_die_queue = kqueue(); + struct kevent changes; + EV_SET(&changes, pid, EVFILT_PROC, EV_ADD | EV_RECEIPT, NOTE_EXIT, 0, NULL); + (void)kevent(daemon_die_queue, &changes, 1, &changes, 1, NULL); + daemon_die_fd = CFFileDescriptorCreate(NULL, daemon_die_queue, true, process_terminate_callback, NULL); + daemon_die_source = CFFileDescriptorCreateRunLoopSource(NULL, daemon_die_fd, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), daemon_die_source, kCFRunLoopDefaultMode); + CFFileDescriptorEnableCallBacks(daemon_die_fd, kCFFileDescriptorReadCallBack); + } else { + NSLog(@"RocketBootstrap: unable to find rocketd!"); + } +} + +static void process_terminate_callback(CFFileDescriptorRef fd, CFOptionFlags callBackTypes, void *info) +{ + struct kevent event; + (void)kevent(daemon_die_queue, NULL, 0, &event, 1, NULL); + NSLog(@"RocketBootstrap: rocketd terminated: %d, relaunching", (int)(pid_t)event.ident); + // Cleanup + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), daemon_die_source, kCFRunLoopDefaultMode); + CFRelease(daemon_die_source); + CFRelease(daemon_die_fd); + close(daemon_die_queue); + observe_rocketd(); +} + static void SanityCheckNotificationCallback(CFUserNotificationRef userNotification, CFOptionFlags responseFlags) { } @@ -253,28 +563,87 @@ static void SanityCheckNotificationCallback(CFUserNotificationRef userNotificati %init(); // Attach rockets when in the com.apple.ReportCrash.SimulateCrash job // (can't check in using the launchd APIs because it hates more than one checkin; this will do) - const char **argv = *_NSGetArgv(); - if (strcmp(argv[0], "/System/Library/CoreServices/ReportCrash") == 0 && argv[1] && strcmp(argv[1], "-f") == 0) { - isDaemon = YES; - MSHookFunction(mach_msg_server_once, $mach_msg_server_once, (void **)&_mach_msg_server_once); + const char **_argv = *_NSGetArgv(); + if (strcmp(_argv[0], "/System/Library/CoreServices/ReportCrash") == 0 && _argv[1]) { + if (strcmp(_argv[1], "-f") == 0) { + isDaemon = YES; +#ifdef DEBUG + NSLog(@"RocketBootstrap: Initializing ReportCrash using mach_msg_server"); +#endif + MSHookFunction(mach_msg_server_once, $mach_msg_server_once, (void **)&_mach_msg_server_once); +#ifdef __clang__ + } else if (strcmp(_argv[1], "com.apple.ReportCrash.SimulateCrash") == 0) { + isDaemon = YES; +#ifdef DEBUG + NSLog(@"RocketBootstrap: Initializing ReportCrash using XPC"); +#endif + MSImageRef libxpc = MSGetImageByName("/usr/lib/system/libxpc.dylib"); + if (libxpc) { + void *xpc_connection_create_mach_service = MSFindSymbol(libxpc, "_xpc_connection_create_mach_service"); + if (xpc_connection_create_mach_service) { + MSHookFunction(xpc_connection_create_mach_service, $xpc_connection_create_mach_service, (void **)&_xpc_connection_create_mach_service); + } else { +#ifdef DEBUG + NSLog(@"RocketBootstrap: Could not find xpc_connection_create_mach_service symbol!"); +#endif + } + void *_xpc_connection_mach_event = MSFindSymbol(libxpc, "__xpc_connection_mach_event"); + if (_xpc_connection_mach_event) { + MSHookFunction(_xpc_connection_mach_event, $_xpc_connection_mach_event, (void **)&__xpc_connection_mach_event); + } else { +#ifdef DEBUG + NSLog(@"RocketBootstrap: Could not find _xpc_connection_mach_event symbol!"); +#endif + } + } else { +#ifdef DEBUG + NSLog(@"RocketBootstrap: Could not find libxpc.dylib image!"); +#endif + } +#endif + } } else if (strcmp(argv[0], "/System/Library/CoreServices/SpringBoard.app/SpringBoard") == 0) { +#ifdef DEBUG + NSLog(@"RocketBootstrap: Initializing SpringBoard"); +#endif + if (kCFCoreFoundationVersionNumber < 847.20) { + return; + } // Sanity check on the SimulateCrash service mach_port_t bootstrap = MACH_PORT_NULL; - task_get_bootstrap_port(mach_task_self(), &bootstrap); + mach_port_t self = mach_task_self(); + task_get_bootstrap_port(self, &bootstrap); mach_port_t servicesPort = MACH_PORT_NULL; kern_return_t err = bootstrap_look_up(bootstrap, "com.apple.ReportCrash.SimulateCrash", &servicesPort); + //bool has_simulate_crash; if (err) { + //has_simulate_crash = false; + } else { + mach_port_mod_refs(self, servicesPort, MACH_PORT_RIGHT_SEND, -1); + //has_simulate_crash = true; + //servicesPort = MACH_PORT_NULL; + //err = bootstrap_look_up(bootstrap, "com.rpetrich.rocketbootstrapd", &servicesPort); + } + if (err == 0) { + mach_port_mod_refs(self, servicesPort, MACH_PORT_RIGHT_SEND, -1); + observe_rocketd(); + } else { const CFTypeRef keys[] = { kCFUserNotificationAlertHeaderKey, kCFUserNotificationAlertMessageKey, kCFUserNotificationDefaultButtonTitleKey, }; - const CFTypeRef values[] = { + const CFTypeRef valuesCrash[] = { CFSTR("System files missing!"), CFSTR("RocketBootstrap has detected that your SimulateCrash crash reporting daemon is missing or disabled.\nThis daemon is required for proper operation of packages that depend on RocketBootstrap."), CFSTR("OK"), }; - CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, sizeof(keys) / sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + /*const CFTypeRef valuesRocket[] = { + CFSTR("System files missing!"), + CFSTR("RocketBootstrap has detected that your rocketbootstrap daemon is missing or disabled.\nThis daemon is required for proper operation of packages that depend on RocketBootstrap."), + CFSTR("OK"), + };*/ + CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, /*has_simulate_crash ? (const void **)valuesRocket :*/ (const void **)valuesCrash, sizeof(keys) / sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); SInt32 err = 0; CFUserNotificationRef notification = CFUserNotificationCreate(kCFAllocatorDefault, 0.0, kCFUserNotificationPlainAlertLevel, &err, dict); CFRunLoopSourceRef runLoopSource = CFUserNotificationCreateRunLoopSource(kCFAllocatorDefault, notification, SanityCheckNotificationCallback, 0); diff --git a/defaultheaders/launch.h b/defaultheaders/launch.h new file mode 120000 index 0000000..678b734 --- /dev/null +++ b/defaultheaders/launch.h @@ -0,0 +1 @@ +/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/launch.h \ No newline at end of file diff --git a/defaultheaders/xpc b/defaultheaders/xpc new file mode 120000 index 0000000..097dec4 --- /dev/null +++ b/defaultheaders/xpc @@ -0,0 +1 @@ +/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/xpc/ \ No newline at end of file diff --git a/dispatch/mach_private.h b/dispatch/mach_private.h new file mode 100644 index 0000000..2146fea --- /dev/null +++ b/dispatch/mach_private.h @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2012-2013 Apple Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_MACH_PRIVATE__ +#define __DISPATCH_MACH_PRIVATE__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +__BEGIN_DECLS + +#if DISPATCH_MACH_SPI + +#include + +/*! + * @functiongroup Dispatch Mach Channel SPI + * + * IMPORTANT: This is Libsystem-internal SPI not intended for general use and + * is subject to change at any time without warning. + */ + +/*! + * @typedef dispatch_mach_t + * A dispatch mach channel asynchronously recevives and sends mach messages. + */ +DISPATCH_DECL(dispatch_mach); + +/*! + * @typedef dispatch_mach_reason_t + * Reasons for a mach channel handler to be invoked. + * + * @const DISPATCH_MACH_CONNECTED + * The channel has been connected. The first handler invocation on a channel + * after calling dispatch_mach_connect() will have this reason. + * + * @const DISPATCH_MACH_MESSAGE_RECEIVED + * A message was received, it is passed in the message parameter. + * + * @const DISPATCH_MACH_MESSAGE_SENT + * A message was sent, it is passed in the message parameter (so that associated + * resources can be disposed of). + * + * @const DISPATCH_MACH_MESSAGE_SEND_FAILED + * A message failed to be sent, it is passed in the message parameter (so that + * associated resources can be disposed of), along with the error code from + * mach_msg(). + * + * @const DISPATCH_MACH_MESSAGE_NOT_SENT + * A message was not sent due to the channel being canceled or reconnected, it + * is passed in the message parameter (so that associated resources can be + * disposed of). + * + * @const DISPATCH_MACH_BARRIER_COMPLETED + * A barrier block has finished executing. + * + * @const DISPATCH_MACH_DISCONNECTED + * The channel has been disconnected by a call to dispatch_mach_reconnect() or + * dispatch_mach_cancel(), an empty message is passed in the message parameter + * (so that associated port rights can be disposed of). + * The message header will contain either a remote port with a previously + * connected send right, or a local port with a previously connected receive + * right (if the channel was canceled), or a local port with a receive right + * that was being monitored for a direct reply to a message previously sent to + * the channel (if no reply was received). + * + * @const DISPATCH_MACH_CANCELED + * The channel has been canceled. + */ +DISPATCH_ENUM(dispatch_mach_reason, unsigned long, + DISPATCH_MACH_CONNECTED = 1, + DISPATCH_MACH_MESSAGE_RECEIVED, + DISPATCH_MACH_MESSAGE_SENT, + DISPATCH_MACH_MESSAGE_SEND_FAILED, + DISPATCH_MACH_MESSAGE_NOT_SENT, + DISPATCH_MACH_BARRIER_COMPLETED, + DISPATCH_MACH_DISCONNECTED, + DISPATCH_MACH_CANCELED, + DISPATCH_MACH_REASON_LAST, /* unused */ +); + +/*! + * @typedef dispatch_mach_trailer_t + * Trailer type of mach message received by dispatch mach channels + */ + +typedef mach_msg_context_trailer_t dispatch_mach_trailer_t; + +/*! + * @constant DISPATCH_MACH_RECEIVE_MAX_INLINE_MESSAGE_SIZE + * Maximum size of a message that can be received inline by a dispatch mach + * channel, reception of larger messages requires an extra roundtrip through + * the kernel. + */ + +#define DISPATCH_MACH_RECEIVE_MAX_INLINE_MESSAGE_SIZE \ + (0x4000 - sizeof(dispatch_mach_trailer_t)) + +/*! + * @typedef dispatch_mach_msg_t + * A dispatch mach message encapsulates messages received or sent with dispatch + * mach channels. + */ +DISPATCH_DECL(dispatch_mach_msg); + +/*! + * @typedef dispatch_mach_msg_destructor_t + * Dispatch mach message object destructors. + * + * @const DISPATCH_MACH_MSG_DESTRUCTOR_DEFAULT + * Message buffer storage is internal to the object, if a buffer is supplied + * during object creation, its contents are copied. + * + * @const DISPATCH_MACH_MSG_DESTRUCTOR_FREE + * Message buffer will be deallocated with free(3). + * + * @const DISPATCH_MACH_MSG_DESTRUCTOR_FREE + * Message buffer will be deallocated with vm_deallocate. + */ +DISPATCH_ENUM(dispatch_mach_msg_destructor, unsigned int, + DISPATCH_MACH_MSG_DESTRUCTOR_DEFAULT = 0, + DISPATCH_MACH_MSG_DESTRUCTOR_FREE, + DISPATCH_MACH_MSG_DESTRUCTOR_VM_DEALLOCATE, +); + +/*! + * @function dispatch_mach_msg_create + * Creates a dispatch mach message object, either with a newly allocated message + * buffer of given size, or from an existing message buffer that will be + * deallocated with the specified destructor when the object is released. + * + * If a non-NULL reference to a pointer is provided in 'msg_ptr', it is filled + * with the location of the (possibly newly allocated) message buffer. + * + * It is the responsibility of the application to ensure that it does not modify + * the underlying message buffer once the dispatch mach message object is passed + * to other dispatch mach API. + * + * @param msg The message buffer to create the message object from. + * If 'destructor' is DISPATCH_MACH_MSG_DESTRUCTOR_DEFAULT, + * this argument may be NULL to leave the newly allocated + * message buffer zero-initialized. + * @param size The size of the message buffer. + * Must be >= sizeof(mach_msg_header_t) + * @param destructor The destructor to use to deallocate the message buffer + * when the object is released. + * @param msg_ptr A pointer to a pointer variable to be filled with the + * location of the (possibly newly allocated) message + * buffer, or NULL. + * @result A newly created dispatch mach message object. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_7_0) +DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT +DISPATCH_NOTHROW +dispatch_mach_msg_t +dispatch_mach_msg_create(mach_msg_header_t *msg, size_t size, + dispatch_mach_msg_destructor_t destructor, mach_msg_header_t **msg_ptr); + +/*! + * @function dispatch_mach_msg_get_msg + * Returns the message buffer underlying a dispatch mach message object. + * + * @param message The dispatch mach message object to query. + * @param size_ptr A pointer to a size_t variable to be filled with the + * size of the message buffer, or NULL. + * @result Pointer to message buffer underlying the object. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_7_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NOTHROW +mach_msg_header_t* +dispatch_mach_msg_get_msg(dispatch_mach_msg_t message, size_t *size_ptr); + +#ifdef __BLOCKS__ +/*! + * @typedef dispatch_mach_handler_t + * Prototype of dispatch mach channel handler blocks. + * + * @param reason Reason the handler was invoked. + * @param message Message object that was sent or received. + * @param error Mach error code for the send operation. + */ +typedef void (^dispatch_mach_handler_t)(dispatch_mach_reason_t reason, + dispatch_mach_msg_t message, mach_error_t error); + +/*! + * @function dispatch_mach_create + * Create a dispatch mach channel to asynchronously receive and send mach + * messages. + * + * The specified handler will be called with the corresponding reason parameter + * for each message received and for each message that was successfully sent, + * that failed to be sent, or was not sent; as well as when a barrier block + * has completed, or when channel connection, reconnection or cancellation has + * taken effect. + * + * Dispatch mach channels are created in a disconnected state, they must be + * connected via dispatch_mach_connect() to begin receiving and sending + * messages. + * + * @param label + * An optional string label to attach to the channel. The string is not copied, + * if it is non-NULL it must point to storage that remains valid for the + * lifetime of the channel object. May be NULL. + * + * @param queue + * The target queue of the channel, where the handler and barrier blocks will + * be submitted. + * + * @param handler + * The handler block to submit when a message has been sent or received. + * + * @result + * The newly created dispatch mach channel. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_6_0) +DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT +DISPATCH_NONNULL3 DISPATCH_NOTHROW +dispatch_mach_t +dispatch_mach_create(const char *label, dispatch_queue_t queue, + dispatch_mach_handler_t handler); +#endif + +/*! + * @typedef dispatch_mach_handler_function_t + * Prototype of dispatch mach channel handler functions. + * + * @param context Application-defined context parameter. + * @param reason Reason the handler was invoked. + * @param message Message object that was sent or received. + * @param error Mach error code for the send operation. + */ +typedef void (*dispatch_mach_handler_function_t)(void *context, + dispatch_mach_reason_t reason, dispatch_mach_msg_t message, + mach_error_t error); + +/*! + * @function dispatch_mach_create_f + * Create a dispatch mach channel to asynchronously receive and send mach + * messages. + * + * The specified handler will be called with the corresponding reason parameter + * for each message received and for each message that was successfully sent, + * that failed to be sent, or was not sent; as well as when a barrier block + * has completed, or when channel connection, reconnection or cancellation has + * taken effect. + * + * Dispatch mach channels are created in a disconnected state, they must be + * connected via dispatch_mach_connect() to begin receiving and sending + * messages. + * + * @param label + * An optional string label to attach to the channel. The string is not copied, + * if it is non-NULL it must point to storage that remains valid for the + * lifetime of the channel object. May be NULL. + * + * @param queue + * The target queue of the channel, where the handler and barrier blocks will + * be submitted. + * + * @param context + * The application-defined context to pass to the handler. + * + * @param handler + * The handler function to submit when a message has been sent or received. + * + * @result + * The newly created dispatch mach channel. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_6_0) +DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT +DISPATCH_NONNULL4 DISPATCH_NOTHROW +dispatch_mach_t +dispatch_mach_create_f(const char *label, dispatch_queue_t queue, void *context, + dispatch_mach_handler_function_t handler); + +/*! + * @function dispatch_mach_connect + * Connect a mach channel to the specified receive and send rights. + * + * This function must only be called once during the lifetime of a channel, it + * will initiate message reception and perform any already submitted message + * sends or barrier operations. + * + * @param channel + * The mach channel to connect. + * + * @param receive + * The receive right to associate with the channel. May be MACH_PORT_NULL. + * + * @param send + * The send right to associate with the channel. May be MACH_PORT_NULL. + * + * @param checkin + * An optional message object encapsulating the initial check-in message to send + * upon channel connection. The check-in message is sent immediately before the + * first message submitted via dispatch_mach_send(). The message object will be + * retained until the initial send operation is complete (or not peformed due + * to channel cancellation or reconnection) and the channel handler has + * returned. May be NULL. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_6_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_mach_connect(dispatch_mach_t channel, mach_port_t receive, + mach_port_t send, dispatch_mach_msg_t checkin); + +/*! + * @function dispatch_mach_reconnect + * Reconnect a mach channel to the specified send right. + * + * Disconnects the channel from the current send right, interrupts any pending + * message sends (and returns the messages as unsent), and reconnects the + * channel to a new send right. + * + * The application must wait for the channel handler to be invoked with + * DISPATCH_MACH_DISCONNECTED before releasing the previous send right. + * + * @param channel + * The mach channel to reconnect. + * + * @param send + * The new send right to associate with the channel. May be MACH_PORT_NULL. + * + * @param checkin + * An optional message object encapsulating the initial check-in message to send + * upon channel reconnection. The check-in message is sent immediately before + * the first message submitted via dispatch_mach_send() after this function + * returns. The message object will be retained until the initial send operation + * is complete (or not peformed due to channel cancellation or reconnection) + * and the channel handler has returned. May be NULL. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_6_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_mach_reconnect(dispatch_mach_t channel, mach_port_t send, + dispatch_mach_msg_t checkin); + +/*! + * @function dispatch_mach_cancel + * Cancel a mach channel, preventing any further messages from being sent or + * received. + * + * The application must wait for the channel handler to be invoked with + * DISPATCH_MACH_DISCONNECTED before releasing the underlying send and receive + * rights. + * + * Note: explicit cancellation of mach channels is required, no implicit + * cancellation takes place on release of the last application reference + * to the channel object. Failure to cancel will cause the channel and + * its associated resources to be leaked. + * + * @param channel + * The mach channel to cancel. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_6_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_mach_cancel(dispatch_mach_t channel); + +/*! + * @function dispatch_mach_send + * Asynchronously send a message encapsulated in a dispatch mach message object + * to the specified mach channel. + * + * Unless the message is being sent to a send-once right (as determined by the + * presence of MACH_MSG_TYPE_MOVE_SEND_ONCE in the message header remote bits), + * the message header remote port is set to the channel send right before the + * send operation is performed. + * + * If the message expects a direct reply (as determined by the presence of + * MACH_MSG_TYPE_MAKE_SEND_ONCE in the message header local bits) the receive + * right specified in the message header local port will be monitored until a + * reply message (or a send-once notification) is received, or the channel is + * canceled. Hence the application must wait for the channel handler to be + * invoked with a DISPATCH_MACH_DISCONNECTED message before releasing that + * receive right. + * + * If the message send operation is attempted but the channel is canceled + * before the send operation succesfully completes, the message returned to the + * channel handler with DISPATCH_MACH_MESSAGE_NOT_SENT may be the result of a + * pseudo-receive operation. If the message expected a direct reply, the + * receive right originally specified in the message header local port will + * returned in a DISPATCH_MACH_DISCONNECTED message. + * + * @param channel + * The mach channel to which to send the message. + * + * @param message + * The message object encapsulating the message to send. The object will be + * retained until the send operation is complete and the channel handler has + * returned. The storage underlying the message object may be modified by the + * send operation. + * + * @param options + * Additional send options to pass to mach_msg() when performing the send + * operation. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_6_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL2 DISPATCH_NOTHROW +void +dispatch_mach_send(dispatch_mach_t channel, dispatch_mach_msg_t message, + mach_msg_option_t options); + +#ifdef __BLOCKS__ +/*! + * @function dispatch_mach_send_barrier + * Submit a send barrier to the specified mach channel. Messages submitted to + * the channel before the barrier will be sent before the barrier block is + * executed, and messages submitted to the channel after the barrier will only + * be sent once the barrier block has completed and the channel handler + * invocation for the barrier has returned. + * + * @param channel + * The mach channel to which to submit the barrier. + * + * @param barrier + * The barrier block to submit to the channel target queue. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_6_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_mach_send_barrier(dispatch_mach_t channel, dispatch_block_t barrier); +#endif + +/*! + * @function dispatch_mach_send_barrier_f + * Submit a send barrier to the specified mach channel. Messages submitted to + * the channel before the barrier will be sent before the barrier block is + * executed, and messages submitted to the channel after the barrier will only + * be sent once the barrier block has completed and the channel handler + * invocation for the barrier has returned. + * + * @param channel + * The mach channel to which to submit the barrier. + * + * @param context + * The application-defined context parameter to pass to the function. + * + * @param barrier + * The barrier function to submit to the channel target queue. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_6_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +void +dispatch_mach_send_barrier_f(dispatch_mach_t channel, void *context, + dispatch_function_t barrier); + +#ifdef __BLOCKS__ +/*! + * @function dispatch_mach_receive_barrier + * Submit a receive barrier to the specified mach channel. Channel handlers for + * messages received by the channel after the receive barrier has been + * submitted will only be invoked once the barrier block has completed and the + * channel handler invocation for the barrier has returned. + * + * @param channel + * The mach channel to which to submit the receive barrier. + * + * @param barrier + * The barrier block to submit to the channel target queue. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_6_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_mach_receive_barrier(dispatch_mach_t channel, + dispatch_block_t barrier); +#endif + +/*! + * @function dispatch_mach_receive_barrier_f + * Submit a receive barrier to the specified mach channel. Channel handlers for + * messages received by the channel after the receive barrier has been + * submitted will only be invoked once the barrier block has completed and the + * channel handler invocation for the barrier has returned. + * + * @param channel + * The mach channel to which to submit the receive barrier. + * + * @param context + * The application-defined context parameter to pass to the function. + * + * @param barrier + * The barrier function to submit to the channel target queue. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_6_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +void +dispatch_mach_receive_barrier_f(dispatch_mach_t channel, void *context, + dispatch_function_t barrier); + +/*! + * @function dispatch_mach_get_checkin_port + * Returns the port specified in the message header remote port of the check-in + * message passed to the most recent invocation of dispatch_mach_connect() or + * dispatch_mach_reconnect() for the provided mach channel (irrespective of the + * completion of the (re)connect or check-in operations in question). + * + * Returns MACH_PORT_NULL if dispatch_mach_connect() has not yet been called or + * if the most recently specified check-in message was NULL, and MACH_PORT_DEAD + * if the channel has been canceled. + * + * It is the responsibility of the application to ensure that the port + * specified in a check-in message remains valid at the time this function is + * called. + * + * @param channel + * The mach channel to query. + * + * @result + * The most recently specified check-in port for the channel. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_6_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +mach_port_t +dispatch_mach_get_checkin_port(dispatch_mach_t channel); + +#endif // DISPATCH_MACH_SPI + +__END_DECLS + +#endif \ No newline at end of file diff --git a/entitlements.xml b/entitlements.xml new file mode 100644 index 0000000..1acd00b --- /dev/null +++ b/entitlements.xml @@ -0,0 +1,9 @@ + + + + platform-application + + com.apple.private.skip-library-validation + + + diff --git a/fullheaders/launch.h b/fullheaders/launch.h new file mode 120000 index 0000000..845362d --- /dev/null +++ b/fullheaders/launch.h @@ -0,0 +1 @@ +/Volumes/Xcode/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include/launch.h \ No newline at end of file diff --git a/fullheaders/xpc b/fullheaders/xpc new file mode 120000 index 0000000..73e85a6 --- /dev/null +++ b/fullheaders/xpc @@ -0,0 +1 @@ +/Volumes/Xcode/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include/xpc/ \ No newline at end of file diff --git a/layout/DEBIAN/control b/layout/DEBIAN/control index 5b34585..2bce6bd 100644 --- a/layout/DEBIAN/control +++ b/layout/DEBIAN/control @@ -1,11 +1,11 @@ Package: com.rpetrich.rocketbootstrap Depends: mobilesubstrate (>= 0.9.5000), firmware (>= 3.0) Name: RocketBootstrap -Version: 1.0.2 +Version: 1.0.9 Architecture: iphoneos-arm -Description: mach lookup support library for iOS 7 -Depiction: http://rpetri.ch/cydia/rocketbootstrap/ -Homepage: http://rpetri.ch/cydia/rocketbootstrap/ +Description: Support library allowing tweaks to communicate with sandboxed processes +Depiction: https://www.rpetrich.com/cydia/rocketbootstrap/beta/ +Homepage: https://www.rpetrich.com/cydia/rocketbootstrap/beta/ Maintainer: BigBoss Author: Ryan Petrich Sponsor: thebigboss.org diff --git a/layout/DEBIAN/postinst b/layout/DEBIAN/postinst index e707090..3b51ee5 100755 --- a/layout/DEBIAN/postinst +++ b/layout/DEBIAN/postinst @@ -1,5 +1,8 @@ #!/bin/sh +chown root:wheel /Library/LaunchDaemons/com.rpetrich.rocketbootstrapd.plist launchctl load /Library/LaunchDaemons/com.rpetrich.rocketbootstrapd.plist || true launchctl stop com.apple.ReportCrash.SimulateCrash || true mv /System/Library/LaunchDaemons{BAK,}/com.apple.ReportCrash.SimulateCrash.plist 2> /dev/null || true launchctl load /System/Library/LaunchDaemons/com.apple.ReportCrash.SimulateCrash.plist 2> /dev/null || true +chown root:wheel /usr/libexec/_rocketd_reenable +chmod +s /usr/libexec/_rocketd_reenable diff --git a/libjailbreak_xpc.h b/libjailbreak_xpc.h new file mode 100644 index 0000000..9c9618e --- /dev/null +++ b/libjailbreak_xpc.h @@ -0,0 +1,36 @@ +#include +#include + +/* Flags for entp command. Any combination or none can be specified. */ +/* Wait for xpcproxy to exec before continuing */ +#define FLAG_WAIT_EXEC (1 << 5) +/* Wait for 0.5 sec after acting */ +#define FLAG_DELAY (1 << 4) +/* Send SIGCONT after acting */ +#define FLAG_SIGCONT (1 << 3) +/* Set sandbox exception */ +#define FLAG_SANDBOX (1 << 2) +/* Set platform binary flag */ +#define FLAG_PLATFORMIZE (1 << 1) +/* Set basic entitlements */ +#define FLAG_ENTITLE (1) + +typedef void *jb_connection_t; + +#if __BLOCKS__ +typedef void (^jb_callback_t)(int result); + +/* These ones run asynchronously. Callbacks take 1 on success, 0 on failure. + The queue which they run on is undefined. */ +extern void jb_entitle(jb_connection_t connection, pid_t pid, uint32_t what, jb_callback_t done); +extern void jb_fix_setuid(jb_connection_t connection, pid_t pid, jb_callback_t done); +#endif + +extern jb_connection_t jb_connect(void); +extern void jb_disconnect(jb_connection_t connection); + +extern int jb_entitle_now(jb_connection_t connection, pid_t pid, uint32_t what); +extern int jb_fix_setuid_now(jb_connection_t connection, pid_t pid); + +extern void jb_oneshot_entitle_now(pid_t pid, uint32_t what); +extern void jb_oneshot_fix_setuid_now(pid_t pid); diff --git a/log.h b/log.h new file mode 100644 index 0000000..d6d87a4 --- /dev/null +++ b/log.h @@ -0,0 +1,17 @@ +#import + +// Tiny shim to convert NSLog to public os_log statements on iOS 10 +#ifdef __clang__ +#if __has_include() +#include +#define NSLog(...) do { \ + if (kCFCoreFoundationVersionNumber > 1299.0) { \ + @autoreleasepool { \ + os_log(OS_LOG_DEFAULT, "%{public}@", [NSString stringWithFormat:__VA_ARGS__]); \ + } \ + } else { \ + NSLog(__VA_ARGS__); \ + } \ +} while(0) +#endif +#endif diff --git a/overlayheaders b/overlayheaders new file mode 120000 index 0000000..80cf74d --- /dev/null +++ b/overlayheaders @@ -0,0 +1 @@ +../overlayheaders/ \ No newline at end of file diff --git a/rocketbootstrap.h b/rocketbootstrap.h index ca5b890..cb7ee03 100644 --- a/rocketbootstrap.h +++ b/rocketbootstrap.h @@ -1,3 +1,6 @@ +#ifndef ROCKETBOOTSTRAP_H +#define ROCKETBOOTSTRAP_H + #include #include #include "bootstrap.h" @@ -5,23 +8,54 @@ __BEGIN_DECLS #ifndef ROCKETBOOTSTRAP_LOAD_DYNAMIC -kern_return_t rocketbootstrap_look_up(mach_port_t bp, const name_t service_name, mach_port_t *sp); +// Look up a port by service name +kern_return_t rocketbootstrap_look_up(mach_port_t bootstrap_port, const name_t service_name, mach_port_t *out_service_port); + +// Grant system-wide access to a particular service name +// Note: Will return an error if called from within a sandboxed process +kern_return_t rocketbootstrap_unlock(const name_t service_name); +// Register a port with and grant system-wide access to a particular service name +// Note: Will return an error if called from within a sandboxed process +kern_return_t rocketbootstrap_register(mach_port_t bootstrap_port, name_t service_name, mach_port_t service_port); -kern_return_t rocketbootstrap_unlock(const name_t service_name); // Errors if not in a privileged process such as SpringBoard or backboardd -kern_return_t rocketbootstrap_register(mach_port_t bp, name_t service_name, mach_port_t sp); // Errors if not in a privileged process such as SpringBoard or backboardd +// CFMessagePort helpers #ifdef __COREFOUNDATION_CFMESSAGEPORT__ +// Acquire access to a system-wide CFMessagePort service CFMessagePortRef rocketbootstrap_cfmessageportcreateremote(CFAllocatorRef allocator, CFStringRef name); +// Expose access to a CFMessagePort service +// Note: Will return an error if called from within a sandboxed process kern_return_t rocketbootstrap_cfmessageportexposelocal(CFMessagePortRef messagePort); #endif + +// CPDistributedMessagingCenter helpers + #ifdef __OBJC__ +// Unlock access to a system-wide CPDistributedMessagingCenter service +// Note: Server processes may only run inside privileged processes @class CPDistributedMessagingCenter; void rocketbootstrap_distributedmessagingcenter_apply(CPDistributedMessagingCenter *messaging_center); #endif + +// XPC helpers + +#ifdef __XPC_CONNECTION_H__ +// Create an XPC connection representing a system-wide service +// flags should be XPC_CONNECTION_MACH_SERVICE_LISTENER for a listener connection, and 0 for a client client connection +// XPC_CONNECTION_MACH_SERVICE_LISTENER will return an error if called from within a sandboxed process +// 0 will return an error if the service name hasn't been registered +xpc_connection_t rocketbootstrap_xpc_connection_create(const char *name, dispatch_queue_t targetq, uint64_t flags); +// Copy the application bundle identifier of the peer connection +xpc_object_t rocketbootstrap_xpc_connection_copy_application_identifier(xpc_connection_t connection); +#endif + + #else #include "rocketbootstrap_dynamic.h" #endif __END_DECLS + +#endif diff --git a/rocketbootstrap_dynamic.h b/rocketbootstrap_dynamic.h index 45585d1..e5fe5c9 100644 --- a/rocketbootstrap_dynamic.h +++ b/rocketbootstrap_dynamic.h @@ -2,16 +2,43 @@ // Set ROCKETBOOTSTRAP_LOAD_DYNAMIC and then include rocketbootstrap.h #include +#if __has_include() +#include + +__attribute__((unused)) +static void *_rocketbootstrap_dlsym_func(const char *name) { + void *handle = dlopen("/usr/lib/librocketbootstrap.dylib", RTLD_LAZY); + if (handle) { + void *result = dlsym(handle, name); + if (result) { + return ptrauth_sign_unauthenticated(ptrauth_strip(result, ptrauth_key_function_pointer), ptrauth_key_function_pointer, 0); + } + } + return NULL; +} + +#else + +__attribute__((unused)) +static void *_rocketbootstrap_dlsym_func(const char *name) { + void *handle = dlopen("/usr/lib/librocketbootstrap.dylib", RTLD_LAZY); + if (handle) { + return dlsym(handle, name); + } + return NULL; +} + +#endif + __attribute__((unused)) static kern_return_t rocketbootstrap_look_up(mach_port_t bp, const name_t service_name, mach_port_t *sp) { static kern_return_t (*impl)(mach_port_t bp, const name_t service_name, mach_port_t *sp); if (!impl) { - void *handle = dlopen("/usr/lib/librocketbootstrap.dylib", RTLD_LAZY); - if (handle) - impl = dlsym(handle, "rocketbootstrap_look_up"); - if (!impl) + impl = _rocketbootstrap_dlsym_func("rocketbootstrap_look_up"); + if (!impl) { impl = bootstrap_look_up; + } } return impl(bp, service_name, sp); } @@ -21,11 +48,10 @@ static kern_return_t rocketbootstrap_unlock(const name_t service_name) { static kern_return_t (*impl)(const name_t service_name); if (!impl) { - void *handle = dlopen("/usr/lib/librocketbootstrap.dylib", RTLD_LAZY); - if (handle) - impl = dlsym(handle, "rocketbootstrap_unlock"); - if (!impl) + impl = _rocketbootstrap_dlsym_func("rocketbootstrap_unlock"); + if (!impl) { return -1; + } } return impl(service_name); } @@ -36,11 +62,10 @@ static kern_return_t rocketbootstrap_register(mach_port_t bp, name_t service_nam { static kern_return_t (*impl)(mach_port_t bp, name_t service_name, mach_port_t sp); if (!impl) { - void *handle = dlopen("/usr/lib/librocketbootstrap.dylib", RTLD_LAZY); - if (handle) - impl = dlsym(handle, "rocketbootstrap_register"); - if (!impl) + impl = _rocketbootstrap_dlsym_func("rocketbootstrap_register"); + if (!impl) { impl = bootstrap_register; + } } return impl(bp, service_name, sp); } @@ -52,11 +77,10 @@ static CFMessagePortRef rocketbootstrap_cfmessageportcreateremote(CFAllocatorRef { static CFMessagePortRef (*impl)(CFAllocatorRef allocator, CFStringRef name); if (!impl) { - void *handle = dlopen("/usr/lib/librocketbootstrap.dylib", RTLD_LAZY); - if (handle) - impl = dlsym(handle, "rocketbootstrap_cfmessageportcreateremote"); - if (!impl) + impl = _rocketbootstrap_dlsym_func("rocketbootstrap_cfmessageportcreateremote"); + if (!impl) { impl = CFMessagePortCreateRemote; + } } return impl(allocator, name); } @@ -65,11 +89,10 @@ static kern_return_t rocketbootstrap_cfmessageportexposelocal(CFMessagePortRef m { static kern_return_t (*impl)(CFMessagePortRef messagePort); if (!impl) { - void *handle = dlopen("/usr/lib/librocketbootstrap.dylib", RTLD_LAZY); - if (handle) - impl = dlsym(handle, "rocketbootstrap_cfmessageportexposelocal"); - if (!impl) + impl = _rocketbootstrap_dlsym_func("rocketbootstrap_cfmessageportexposelocal"); + if (!impl) { return -1; + } } return impl(messagePort); } @@ -82,12 +105,36 @@ static void rocketbootstrap_distributedmessagingcenter_apply(CPDistributedMessag { static void (*impl)(CPDistributedMessagingCenter *messagingCenter); if (!impl) { - void *handle = dlopen("/usr/lib/librocketbootstrap.dylib", RTLD_LAZY); - if (handle) - impl = dlsym(handle, "rocketbootstrap_distributedmessagingcenter_apply"); + impl = _rocketbootstrap_dlsym_func("rocketbootstrap_distributedmessagingcenter_apply"); if (!impl) return; } impl(messaging_center); } #endif + +#ifdef __XPC_CONNECTION_H__ +__attribute__((unused)) +static xpc_connection_t rocketbootstrap_xpc_connection_create(const char *name, dispatch_queue_t targetq, uint64_t flags) +{ + static xpc_connection_t (*impl)(const char *name, dispatch_queue_t targetq, uint64_t flags); + if (!impl) { + impl = _rocketbootstrap_dlsym_func("rocketbootstrap_xpc_connection_create"); + if (!impl) + return NULL; + } + return impl(name, targetq, flags); +} + +__attribute__((unused)) +static xpc_object_t rocketbootstrap_xpc_connection_copy_application_identifier(xpc_connection_t connection) +{ + static xpc_object_t (*impl)(xpc_connection_t connection); + if (!impl) { + impl = _rocketbootstrap_dlsym_func("rocketbootstrap_xpc_connection_copy_application_identifier"); + if (!impl) + return NULL; + } + return impl(connection); +} +#endif diff --git a/rocketbootstrap_internal.h b/rocketbootstrap_internal.h index 6244b2b..afb6ac3 100644 --- a/rocketbootstrap_internal.h +++ b/rocketbootstrap_internal.h @@ -1,4 +1,5 @@ #import +#import #import "rocketbootstrap.h" @@ -33,4 +34,19 @@ static inline bool rocketbootstrap_is_passthrough(void) return kCFCoreFoundationVersionNumber < 800.0; } +__attribute__((unused)) +static inline bool rocketbootstrap_uses_name_redirection(void) +{ + if (kCFCoreFoundationVersionNumber >= 1556.00) { + static int state; + int currentState = state; + if (currentState == 0) { + int check = sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_LOCAL_NAME | SANDBOX_CHECK_NO_REPORT, "cy:rbs"); + currentState = check == 0 ? 1 : 2; + } + return currentState - 1; + } + return false; +} + kern_return_t _rocketbootstrap_is_unlocked(const name_t service_name); // Errors if not in a privileged process such as SpringBoard or backboardd diff --git a/rocketd.c b/rocketd.c index c97a0d5..01f73ac 100644 --- a/rocketd.c +++ b/rocketd.c @@ -44,6 +44,11 @@ static void machPortCallback(CFMachPortRef port, void *bytes, CFIndex size, void #endif } break; + case 2: + // Good morning, still awake + reply_data = &one; + reply_length = sizeof one; + break; } CFRelease(name); } @@ -54,6 +59,9 @@ static void machPortCallback(CFMachPortRef port, void *bytes, CFIndex size, void int main(int argc, char *argv[]) { + if (rocketbootstrap_is_passthrough()) { + return 0; + } allowedNames = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks); LMCheckInService(connection.serverName, CFRunLoopGetCurrent(), machPortCallback, NULL); notify_post("com.rpetrich.rocketd.started"); diff --git a/rocketd_reenable.c b/rocketd_reenable.c new file mode 100644 index 0000000..9204bde --- /dev/null +++ b/rocketd_reenable.c @@ -0,0 +1,58 @@ +#import +#import +#import + +#ifdef __LP64__ +#if __has_include() +#include +#define sign_function(ptr) ({ \ + __typeof__(ptr) _ptr = ptr; \ + ptr ? ptrauth_sign_unauthenticated(_ptr, ptrauth_key_function_pointer, 0) : _ptr; \ +}) +#else +#define sign_function(ptr) ptr +#endif +#else +#define sign_function(ptr) ptr +#endif + +#ifdef __arm64__ +#import "libjailbreak_xpc.h" +static int fix_setuid(void) +{ + void *libjailbreak = dlopen("/usr/lib/libjailbreak.dylib", RTLD_LAZY); + if (libjailbreak) { + jb_connection_t (*jb_connect)(void) = sign_function(dlsym(libjailbreak, "jb_connect")); + int (*jb_fix_setuid_now)(jb_connection_t connection, pid_t pid) = sign_function(dlsym(libjailbreak, "jb_fix_setuid_now")); + void (*jb_disconnect)(jb_connection_t connection) = sign_function(dlsym(libjailbreak, "jb_disconnect")); + if (jb_connect && jb_fix_setuid_now && jb_disconnect) { + jb_connection_t connection = jb_connect(); + if (connection) { + int result = jb_fix_setuid_now(connection, getpid()); + jb_disconnect(connection); + return result; + } + } + } + return 1; +} +#else +static int fix_setuid(void) +{ + // Does not apply to older iOS versions + return 1; +} +#endif + +int main(int argc, char *argv[]) +{ + close(0); + close(1); + close(2); + fix_setuid(); + setuid(0); + setgid(0); + seteuid(0); + setegid(0); + return execlp("launchctl", "launchctl", "load", "/Library/LaunchDaemons/com.rpetrich.rocketbootstrapd.plist", NULL); +} diff --git a/tests/Makefile b/tests/Makefile index 2443219..21a8f60 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -8,7 +8,7 @@ ADDITIONAL_CFLAGS = -std=c99 -I../ IPHONE_ARCHS = armv6 arm64 SDKVERSION_armv6 = 5.1 -INCLUDE_SDKVERSION_armv6 = 7.0 +INCLUDE_SDKVERSION_armv6 = 8.4 TARGET_IPHONEOS_DEPLOYMENT_VERSION = 4.0 TARGET_IPHONEOS_DEPLOYMENT_VERSION_armv6 = 3.0 diff --git a/tests/tests.m b/tests/tests.m index f117a48..cd6c8da 100644 --- a/tests/tests.m +++ b/tests/tests.m @@ -1,4 +1,5 @@ #import +#include "log.h" #import "rocketbootstrap.h" diff --git a/unfair_lock.h b/unfair_lock.h new file mode 100644 index 0000000..24e953f --- /dev/null +++ b/unfair_lock.h @@ -0,0 +1,45 @@ +#if __IPHONE_OS_VERSION_MAX_ALLOWED > 100000 +// New SDK +#include +#define unfair_lock os_unfair_lock +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000 +// Only targeting new iOS, always use unfair locks +#define unfair_lock_lock os_unfair_lock_lock +#define unfair_lock_trylock os_unfair_lock_trylock +#define unfair_lock_unlock os_unfair_lock_unlock +#else +// Support both at runtime +#import +static inline void unfair_lock_lock(unfair_lock *lock) +{ + if (&os_unfair_lock_lock != NULL) { + os_unfair_lock_lock(lock); + } else { + OSSpinLockLock((volatile OSSpinLock *)lock); + } +} +static inline bool unfair_lock_trylock(unfair_lock *lock) +{ + if (&os_unfair_lock_trylock != NULL) { + return os_unfair_lock_trylock(lock); + } else { + return OSSpinLockTry((volatile OSSpinLock *)lock); + } +} +static inline void unfair_lock_unlock(unfair_lock *lock) +{ + if (&os_unfair_lock_unlock != NULL) { + os_unfair_lock_unlock(lock); + } else { + OSSpinLockUnlock((volatile OSSpinLock *)lock); + } +} +#endif +#else +// Old SDK, fallback to using regular old spinlocks +#import +#define unfair_lock volatile OSSpinLock +#define unfair_lock_lock OSSpinLockLock +#define unfair_lock_trylock OSSpinLockTry +#define unfair_lock_unlock OSSpinLockUnlock +#endif \ No newline at end of file