/* UIKit Tools - command-line utilities for UIKit * Copyright (C) 2018-2019 Sam Bingner * Copyright (C) 2008-2012 Jay Freeman (saurik) */ /* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* Modified BSD License {{{ */ /* * Redistribution and use in source and binary * forms, with or without modification, are permitted * provided that the following conditions are met: * * 1. Redistributions of source code must retain the * above copyright notice, this list of conditions * and the following disclaimer. * 2. Redistributions in binary form must reproduce the * above copyright notice, this list of conditions * and the following disclaimer in the documentation * and/or other materials provided with the * distribution. * 3. The name of the author may not be used to endorse * or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* }}} */ #include #include #include #include #include /* Set platform binary flag */ #define FLAG_PLATFORMIZE (1 << 1) #include #include #ifndef __IPHONE_10_0 #define __IPHONE_10_0 100000 #endif #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_10_0 #include #endif @interface FBSSystemService +(id)sharedService; -(void)sendActions:(NSSet*)actions withResult:(id)result; @end @interface PBSSystemService +(id)sharedInstance; -(void)relaunch; -(void)relaunchBackboardd; @end typedef enum { None = 0, RestartRenderServer = (1 << 0), // also relaunch backboardd SnapshotTransition = (1 << 1), FadeToBlackTransition = (1 << 2), } SBSRelaunchActionStyle; @interface SBSRelaunchAction +(id)actionWithReason:(id)reason options:(int64_t)options targetURL:(NSURL*)url; @end void platformizeme() { void* handle = dlopen("/usr/lib/libjailbreak.dylib", RTLD_LAZY); if (!handle) return; // Reset errors dlerror(); typedef void (*fix_entitle_prt_t)(pid_t pid, uint32_t what); fix_entitle_prt_t ptr = (fix_entitle_prt_t)dlsym(handle, "jb_oneshot_entitle_now"); const char *dlsym_error = dlerror(); if (dlsym_error) { return; } ptr(getpid(), FLAG_PLATFORMIZE); } launch_data_t CF2launch_data(CFTypeRef cfr); void myCFDictionaryApplyFunction(const void *key, const void *value, void *context) { launch_data_t ik, iw, where = context; ik = CF2launch_data(key); iw = CF2launch_data(value); launch_data_dict_insert(where, iw, launch_data_get_string(ik)); launch_data_free(ik); } launch_data_t CF2launch_data(CFTypeRef cfr) { launch_data_t r; CFTypeID cft = CFGetTypeID(cfr); if (cft == CFStringGetTypeID()) { char buf[4096]; CFStringGetCString(cfr, buf, sizeof(buf), kCFStringEncodingUTF8); r = launch_data_alloc(LAUNCH_DATA_STRING); launch_data_set_string(r, buf); } else if (cft == CFBooleanGetTypeID()) { r = launch_data_alloc(LAUNCH_DATA_BOOL); launch_data_set_bool(r, CFBooleanGetValue(cfr)); } else if (cft == CFArrayGetTypeID()) { CFIndex i, ac = CFArrayGetCount(cfr); r = launch_data_alloc(LAUNCH_DATA_ARRAY); for (i = 0; i < ac; i++) { CFTypeRef v = CFArrayGetValueAtIndex(cfr, i); if (v) { launch_data_t iv = CF2launch_data(v); launch_data_array_set_index(r, iv, i); } } } else if (cft == CFDictionaryGetTypeID()) { r = launch_data_alloc(LAUNCH_DATA_DICTIONARY); CFDictionaryApplyFunction(cfr, myCFDictionaryApplyFunction, r); } else if (cft == CFDataGetTypeID()) { r = launch_data_alloc(LAUNCH_DATA_ARRAY); launch_data_set_opaque(r, CFDataGetBytePtr(cfr), CFDataGetLength(cfr)); } else if (cft == CFNumberGetTypeID()) { long long n; double d; CFNumberType cfnt = CFNumberGetType(cfr); switch (cfnt) { case kCFNumberSInt8Type: case kCFNumberSInt16Type: case kCFNumberSInt32Type: case kCFNumberSInt64Type: case kCFNumberCharType: case kCFNumberShortType: case kCFNumberIntType: case kCFNumberLongType: case kCFNumberLongLongType: CFNumberGetValue(cfr, kCFNumberLongLongType, &n); #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_10_0 if (kCFCoreFoundationVersionNumber >= 1443.00) { r = (launch_data_t)xpc_int64_create(n); } else { #else { #endif // This hangs forever if I call it on new iOS??? r = launch_data_alloc(LAUNCH_DATA_INTEGER); launch_data_set_integer(r, n); } break; case kCFNumberFloat32Type: case kCFNumberFloat64Type: case kCFNumberFloatType: case kCFNumberDoubleType: CFNumberGetValue(cfr, kCFNumberDoubleType, &d); #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_10_0 if (kCFCoreFoundationVersionNumber >= 1443.00) { r = (launch_data_t)xpc_double_create(d); } else { #else { #endif // Not sure if this hangs, but added to be safe r = launch_data_alloc(LAUNCH_DATA_REAL); launch_data_set_real(r, d); } break; default: r = NULL; break; } } else { r = NULL; } return r; } CFPropertyListRef CreateMyPropertyListFromFile(const char *posixfile) { CFPropertyListRef propertyList; CFStringRef errorString; CFDataRef resourceData; SInt32 errorCode; CFURLRef fileURL; fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false); if (!fileURL) { fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile); } if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode)) { fprintf(stderr, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode); } propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListMutableContainers, &errorString); if (!propertyList) { fprintf(stderr, "%s: propertyList is NULL\n", getprogname()); } return propertyList; } #define _assert(test, format, args...) do { \ if (test) break; \ fprintf(stderr, format "\n", ##args); \ return 1; \ } while (false) void stop() { sleep(1); } #define SpringBoard_plist "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist" pid_t launch_get_job_pid(const char * job) { launch_data_t resp; launch_data_t msg; msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); if (msg == NULL) { return -1; } launch_data_dict_insert(msg, launch_data_new_string(job), LAUNCH_KEY_GETJOB); resp = launch_msg(msg); launch_data_free(msg); if (resp == NULL) { return -1; } if (launch_data_get_type(resp) != LAUNCH_DATA_DICTIONARY) return -1; launch_data_t pid_data = launch_data_dict_lookup(resp, "PID"); if (pid_data == NULL || launch_data_get_type(pid_data) != LAUNCH_DATA_INTEGER) return -1; pid_t pid = (pid_t)launch_data_get_integer(pid_data); launch_data_free(resp); return pid; } int main(int argc, const char *argv[]) { platformizeme(); _assert(argc == 1, "usage: sbreload"); if (kCFCoreFoundationVersionNumber >= 1400.00) { dlopen("/System/Library/PrivateFrameworks/PineBoardServices.framework/PineBoardServices", RTLD_LAZY); Class $PBSSystemService = objc_getClass("PBSSystemService"); if ($PBSSystemService) { pid_t pb_pid = launch_get_job_pid("com.apple.PineBoard"); if ($PBSSystemService) { [[$PBSSystemService sharedInstance] relaunchBackboardd]; for (int i=0; i<100; i++) { if (kill(pb_pid, 0)) { return 0; } usleep(1000); } } return -1; } dlopen("/System/Library/PrivateFrameworks/FrontBoardServices.framework/FrontBoardServices", RTLD_LAZY); dlopen("/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices", RTLD_LAZY); Class $SBSRelaunchAction = objc_getClass("SBSRelaunchAction"); Class $FBSSystemService = objc_getClass("FBSSystemService"); pid_t sb_pid = launch_get_job_pid("com.apple.SpringBoard"); if ($SBSRelaunchAction && $FBSSystemService) { @autoreleasepool { id action = [$SBSRelaunchAction actionWithReason:@"respring" options:RestartRenderServer targetURL:nil]; id sharedService = [$FBSSystemService sharedService]; [sharedService sendActions:[NSSet setWithObject:action] withResult:nil]; for (int i=0; i<100; i++) { if (kill(sb_pid, 0)) { return 0; } usleep(1000); } } } // Don't fail back to old behavior on iOS11+ since it can cause hangs system("launchctl stop com.apple.backboardd"); return 0; } CFDictionaryRef plist = CreateMyPropertyListFromFile(SpringBoard_plist); _assert(plist != NULL, "CreateMyPropertyListFromFile() == NULL"); launch_data_t job = CF2launch_data(plist); _assert(job != NULL, "CF2launch_data() == NULL"); launch_data_t data, request, response; data = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LABEL); _assert(data != NULL, "launch_data_dict_lookup(LABEL) == NULL"); const char *label = launch_data_get_string(data); request = launch_data_alloc(LAUNCH_DATA_DICTIONARY); launch_data_dict_insert(request, launch_data_new_string(label), LAUNCH_KEY_GETJOB); response = launch_msg(request); _assert(response != NULL, "launch_msg(GetJob) == NULL"); launch_data_free(request); pid_t pid; if (launch_data_get_type(response) == LAUNCH_DATA_ERRNO) { int error = launch_data_get_errno(response); _assert(error == ESRCH, "GetJob(%s): %s", label, strerror(error)); pid = -1; } else if (launch_data_get_type(response) == LAUNCH_DATA_DICTIONARY) { data = launch_data_dict_lookup(response, LAUNCH_JOBKEY_PID); _assert(data != NULL, "launch_data_dict_lookup(PID) == NULL"); pid = launch_data_get_integer(data); } else _assert(false, "launch_data_get_type() not in (DICTIONARY, ERRNO)"); launch_data_free(response); // 600 is being used to approximate 4.x/5.x boundary if (kCFCoreFoundationVersionNumber < 600) { fprintf(stderr, "notify_post(com.apple.mobile.springboard_teardown)\n"); notify_post("com.apple.mobile.springboard_teardown"); } else { // XXX: this code is preferable to launchctl unoad but it requires libvproc? :( //vproc_err_t *error = _vproc_send_signal_by_label(label, VPROC_MAGIC_UNLOAD_SIGNAL); //_assert(error == NULL, "_vproc_send_signal_by_label(UNLOAD) != NULL"); fprintf(stderr, "launchctl unload SpringBoard.plist\n"); system("launchctl unload " SpringBoard_plist); } if (pid != -1) { fprintf(stderr, "waiting for kill(%u) != 0...\n", pid); while (kill(pid, 0) == 0) stop(); int error = errno; _assert(error == ESRCH, "kill(%u): %s", pid, strerror(error)); } request = launch_data_alloc(LAUNCH_DATA_DICTIONARY); launch_data_dict_insert(request, job, LAUNCH_KEY_SUBMITJOB); for (;;) { response = launch_msg(request); _assert(response != NULL, "launch_msg(SubmitJob) == NULL"); _assert(launch_data_get_type(response) == LAUNCH_DATA_ERRNO, "launch_data_get_type() != ERRNO"); int error = launch_data_get_errno(response); launch_data_free(response); const char *string = strerror(error); if (error == EEXIST) { fprintf(stderr, "SubmitJob(%s): %s, retrying...\n", label, string); stop(); } else { _assert(error == 0, "SubmitJob(%s): %s", label, string); break; } } launch_data_free(request); return 0; }