diff -urN uikittools/sbreload.c uikittools+uicache/sbreload.c --- uikittools/sbreload.c 2018-10-04 15:58:42.000000000 -1000 +++ uikittools+uicache/sbreload.c 1969-12-31 14:00:00.000000000 -1000 @@ -1,270 +0,0 @@ -/* UIKit Tools - command-line utilities for UIKit - * Copyright (C) 2008-2012 Jay Freeman (saurik) -*/ - -/* 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 - -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); - 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); - 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" - -int main(int argc, const char *argv[]) { - platformizeme(); - _assert(argc == 1, "usage: sbreload"); - - 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; -} diff -urN uikittools/sbreload.m uikittools+uicache/sbreload.m --- uikittools/sbreload.m 1969-12-31 14:00:00.000000000 -1000 +++ uikittools+uicache/sbreload.m 2019-06-07 22:37:29.000000000 -1000 @@ -0,0 +1,336 @@ +/* UIKit Tools - command-line utilities for UIKit + * Copyright (C) 2008-2012 Jay Freeman (saurik) +*/ + +/* 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 + +@interface FBSSystemService ++(id)sharedService; +-(void)sendActions:(NSSet*)actions withResult:(id)result; +@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); + 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); + 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 (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 >= 1443.00) { + 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); + } + } + } + } + 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; +} diff -urN uikittools/sbreload.xml uikittools+uicache/sbreload.xml --- uikittools/sbreload.xml 1969-12-31 14:00:00.000000000 -1000 +++ uikittools+uicache/sbreload.xml 2019-06-07 22:37:29.000000000 -1000 @@ -0,0 +1,13 @@ + + + + com.apple.frontboard.launchapplications + + com.apple.frontboard.shutdown + + platform-application + + com.apple.private.skip-library-validation + + + diff -urN uikittools/uicache.mm uikittools+uicache/uicache.mm --- uikittools/uicache.mm 2018-10-04 15:58:19.000000000 -1000 +++ uikittools+uicache/uicache.mm 2019-06-07 22:37:39.000000000 -1000 @@ -1,7 +1,17 @@ /* UIKit Tools - command-line utilities for UIKit * Copyright (C) 2008-2012 Jay Freeman (saurik) + * Portions Copyright (C) 2019 Sam Bingner */ +/* uicache.mm + * + * Licensed under GPL v2.0 license as available at: + * https://www.gnu.org/licenses/gpl2.txt + * + * In adition, usage must meet the following + * Modified BSD terms: + */ + /* Modified BSD License {{{ */ /* * Redistribution and use in source and binary @@ -38,13 +48,16 @@ /* }}} */ #import - -#include -#include -#include -#include - -#include +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import #include "csstore.hpp" @@ -81,6 +94,12 @@ @end +@interface LSApplicationProxy : NSObject +- (NSString*) applicationIdentifier; +- (NSURL*) resourcesDirectoryURL; +- (NSDate*) registeredDate; +@end + @interface LSApplicationWorkspace : NSObject + (id) defaultWorkspace; - (BOOL) registerApplication:(id)application; @@ -89,16 +108,188 @@ - (BOOL) registerApplicationDictionary:(id)application; - (BOOL) installApplication:(id)application withOptions:(id)options; - (BOOL) _LSPrivateRebuildApplicationDatabasesForSystemApps:(BOOL)system internal:(BOOL)internal user:(BOOL)user; +- (NSArray*) allApplications; +@end + +@interface MCMAppDataContainer ++(id)containerWithIdentifier:(NSString*)identifier createIfNecessary:(bool)create existed:(bool*)existed error:(NSError*)error; +-(NSURL*)url; +@end + +@interface FBSSystemService ++(id)sharedService; +-(void)sendActions:(NSSet*)actions withResult:(id)result; @end -int main(int argc, const char *argv[]) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; +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 + +static int verbose=0; +static int standard_uicache(void); +static Class $MCMPluginKitPluginDataContainer; +static Class $MCMAppDataContainer; +static Class $LSApplicationWorkspace; +LSApplicationWorkspace *workspace=nil; + +NSString *getAppPath(NSString *path) +{ + path = [path stringByResolvingSymlinksInPath]; + if (![path hasPrefix:@"/Applications/"]) { + fprintf(stderr, "Error: Path must be within /Applications/\n"); + return nil; + } + return [NSString pathWithComponents:[[path pathComponents] subarrayWithRange:NSMakeRange(0, 3)]]; +} - Class $LSApplicationWorkspace(objc_getClass("LSApplicationWorkspace")); - LSApplicationWorkspace *workspace($LSApplicationWorkspace == nil ? nil : [$LSApplicationWorkspace defaultWorkspace]); +bool appIsRegistered(NSString *path) +{ + @autoreleasepool { + path = getAppPath(path); + if (!path) return false; + for (LSApplicationProxy *app in [workspace allApplications]) { + if ([path isEqualToString:[[app resourcesDirectoryURL] path]]) return true; + } + return false; + } +} - if (kCFCoreFoundationVersionNumber > 1000) // this API is on iOS 7 but invaliding the icon cache is harder there - if ([workspace respondsToSelector:@selector(_LSPrivateRebuildApplicationDatabasesForSystemApps:internal:user:)]) { +bool unregisterPath(NSString *path) +{ + @autoreleasepool { + if (verbose) fprintf(stderr, "Unregistering %s\n", path.lastPathComponent.UTF8String); + path = getAppPath(path); + if (!path) return false; + if (appIsRegistered(path) && ![workspace unregisterApplication:[NSURL fileURLWithPath:path]]) { + fprintf(stderr, "Error: unregisterApplication failed for %s\n", path.lastPathComponent.UTF8String); + return false; + } + } + return true; +} + +// Credit to coolstar for finding how to do this and not sharing with the community thereby forcing me to figure out how to do the same thing. +bool registerPath(NSString *path) +{ + if (!path) { + if (verbose) fprintf(stderr, "registerPath called with no path\n"); + return false; + } + NSString *realPath = getAppPath(path); + if (!realPath) { + if (verbose) fprintf(stderr, "unable to determine path for %s\n", path.UTF8String); + return false; + } + NSDictionary *infoDictionary = [NSDictionary dictionaryWithContentsOfFile: + [realPath stringByAppendingPathComponent:@"Info.plist"]]; + NSString *bundleID = [infoDictionary objectForKey:@"CFBundleIdentifier"]; + + if (bundleID) { + NSFileManager *fm = [NSFileManager defaultManager]; + + if ([infoDictionary objectForKey:@"CFBundleExecutable"]) { + NSString *executable = [realPath stringByAppendingPathComponent:[infoDictionary objectForKey:@"CFBundleExecutable"]]; + if (![fm fileExistsAtPath:executable]) { + fprintf(stderr, "Error: CFBundleExecutable defined but missing for %s - this is a fatal error. Aborting.\n", realPath.lastPathComponent.UTF8String); + return false; + } + } + + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"System", @"ApplicationType", + @YES, @"BundleNameIsLocalized", + bundleID, @"CFBundleIdentifier", + @NO, @"CompatibilityState", + @NO, @"IsDeletable", + realPath, @"Path", + [NSMutableDictionary dictionary], @"_LSBundlePlugins", + nil]; + + id appContainer = [$MCMAppDataContainer containerWithIdentifier:bundleID + createIfNecessary:YES existed:NULL error:nil]; + + NSString *appContainerPath = [[appContainer url] path]; + if (appContainerPath) { + dict[@"Container"] = appContainerPath; + } + NSString *pluginsPath = [realPath stringByAppendingPathComponent:@"PlugIns"]; + for (NSString *plugin in [fm contentsOfDirectoryAtPath:pluginsPath error:nil]) { + NSString *pluginPath = [pluginsPath stringByAppendingPathComponent:plugin]; + NSString *pluginInfoPlistPath = [pluginPath stringByAppendingPathComponent:@"Info.plist"]; + NSString *pluginBundleIdentifier = [[NSDictionary dictionaryWithContentsOfFile:pluginInfoPlistPath] objectForKey:@"CFBundleIdentifier"]; + if (pluginBundleIdentifier) { + id pluginContainer = [$MCMPluginKitPluginDataContainer containerWithIdentifier:pluginBundleIdentifier + createIfNecessary:YES existed:NULL error:nil]; + NSURL *pluginContainerURL = [pluginContainer url]; + NSString *pluginContainerPath = [pluginContainerURL path]; + dict[@"_LSBundlePlugins"][pluginBundleIdentifier] = @{ + @"ApplicationType": @"PluginKitPlugin", + @"BundleNameIsLocalized": @YES, + @"CFBundleIdentifier": pluginBundleIdentifier, + @"CompatibilityState": @NO, + @"Container": pluginContainerPath, + @"Path": pluginPath, + @"PluginOwnerBundleID": bundleID + }; + } + } + if (![[$LSApplicationWorkspace defaultWorkspace] registerApplicationDictionary:dict]) { + fprintf(stderr, "Error: registerApplicationDictionary failed for %s\n", path.lastPathComponent.UTF8String); + return false; + } + } else { + return unregisterPath(realPath); + } + return true; +} + +void usage(void) +{ + fprintf(stderr, "Usage: %s [-hrv] [[-p | -u] /Applications/App.app]]\n", getprogname()); + exit(EXIT_FAILURE); +} + +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 (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 standard_uicache(void) +{ + @autoreleasepool { + if (kCFCoreFoundationVersionNumber > 1000 && // this API is on iOS 7 but invaliding the icon cache is harder there + [workspace respondsToSelector:@selector(_LSPrivateRebuildApplicationDatabasesForSystemApps:internal:user:)]) { if (![workspace _LSPrivateRebuildApplicationDatabasesForSystemApps:YES internal:YES user:NO]) fprintf(stderr, "failed to rebuild application databases"); return 0; @@ -226,12 +417,247 @@ system("killall -SIGCONT SpringBoard"); } - if (respring) - system("launchctl stop com.apple.SpringBoard"); - else - notify_post("com.apple.mobile.application_installed"); - - [pool release]; + notify_post("com.apple.mobile.application_installed"); return 0; + } +} + +pid_t pidOfCydia(void) { + launch_data_t request = launch_data_new_string(LAUNCH_KEY_GETJOBS); + launch_data_t response = launch_msg(request); + launch_data_free(request); + __block pid_t pid=-1; + + if (response == NULL || launch_data_get_type(response) != LAUNCH_DATA_DICTIONARY) return -1; + + xpc_dictionary_apply((xpc_object_t)response, ^bool(const char *key, xpc_object_t value) { + if (xpc_get_type(value) == XPC_TYPE_DICTIONARY) { + const char *program = xpc_dictionary_get_string(value, "Program"); + if (program && strcmp(program, "/Applications/Cydia.app/Cydia") == 0) { + pid = (pid_t)xpc_dictionary_get_int64(value, "PID"); + if (verbose) fprintf(stderr, "Found Cydia running with PID: %d\n", pid); + return false; + } + } + return true; + }); + + if (pid>0) { + return pid; + } + return -1; +} + +int optimized_uicache(void) { + __block int rv=0; + NSMutableDictionary *registered = [NSMutableDictionary new]; + static void (^readHandler)(NSFileHandle*) = ^(NSFileHandle *fh) { + NSData *output = [fh readDataToEndOfFile]; + if (output.length==0) return; + const char *found_path = (const char *)[output bytes]; + NSArray *found = [@(found_path) pathComponents]; + if (found.count >= 3) { + NSString *appPath = [@"/Applications" stringByAppendingPathComponent:found[2]]; + @synchronized (registered) { + if (registered[appPath]) return; + if (verbose) fprintf(stderr, "Updating %s\n", appPath.lastPathComponent.UTF8String); + registered[appPath] = @YES; + } + pid_t cydia_pid; + if ([found[2] isEqualToString:@"Cydia.app"] && + (cydia_pid = pidOfCydia()) > 0) { + // We are in cydia and trying to refresh it - this will kill it. Let's schedule it for later. + if (verbose) fprintf(stderr, "Waiting to refresh Cydia...\n"); + pid_t pid = fork(); + if (pid == 0) { + setpgrp(); + signal(SIGHUP, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + fclose(stdin); + freopen("/dev/null", "a", stderr); + freopen("/dev/null", "a", stdout); + pid = fork(); + if (pid == 0) { + while (kill(cydia_pid, 0)==0) { + sleep(1); + } + const char *uicache = (*_NSGetArgv())[0]; + execl(uicache, uicache, "-vvvvvvv", NULL); + fprintf(stderr, "Unable to exec\n"); + fflush(stderr); + exit(-1); + } + exit(0); + } else if (pid > 0) { + int stat; + waitpid(pid, &stat, 0); + return; + } else { + fprintf(stderr, "Unable to fork\n"); + } + } + if (!registerPath(appPath)) rv++; + } + }; + NSFileManager *fm = [NSFileManager defaultManager]; + NSMutableDictionary *apps = [NSMutableDictionary new]; + NSMutableArray *cleanup = [NSMutableArray new]; + NSMutableArray *finds = [NSMutableArray new]; + if (verbose>1) fprintf(stderr, "Enumerating apps\n"); + for (LSApplicationProxy *app in [workspace allApplications]) { + NSString *path = [[app resourcesDirectoryURL] path]; + if (![path hasPrefix:@"/Applications/"]) continue; + if (verbose>1) fprintf(stderr, "Checking %s\n", path.lastPathComponent.UTF8String); + + NSDate *lastRegistered = [app registeredDate]; + + if ([fm fileExistsAtPath:path]) { + // Check for updated components + NSTask *find = [NSTask new]; + [find setLaunchPath:@"/usr/bin/find"]; + [find setStandardOutput:[NSPipe pipe]]; + NSString *stampPath = [NSString stringWithFormat:@"/var/tmp/uicache.stamp.%@", app.applicationIdentifier]; + [fm createFileAtPath:stampPath contents:nil attributes:@{NSFileModificationDate: lastRegistered}]; + [cleanup addObject:stampPath]; + [find setArguments:@[ path, @"-newer", stampPath, @"-print0", @"-quit"]]; + [finds addObject:find]; + [find launch]; + apps[path.lastPathComponent] = app; + } else { + if (verbose) fprintf(stderr, "De-registering removed app: %s\n", path.lastPathComponent.UTF8String); + @synchronized (registered) { + if (registered[path]) continue; + registered[path] = @YES; + } + if (!unregisterPath(path)) rv++; + } + } + + for (NSString* existing in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"/Applications" error:nil]) { + NSString *path = [@"/Applications" stringByAppendingPathComponent:existing]; + if (apps[existing] || registered[path] || ![existing hasSuffix:@".app"]) continue; + if (verbose) fprintf(stderr, "Registering new app: %s\n", existing.UTF8String); + @synchronized (registered) { + registered[path] = @YES; + } + if (!registerPath(path)) rv++; + } + for (NSTask *find in finds) { + if (verbose>2) fprintf(stderr, "waiting for find %s\n", [find.arguments componentsJoinedByString:@" "].UTF8String); + readHandler([find.standardOutput fileHandleForReading]); + [find waitUntilExit]; + } + for (NSString *path in cleanup) { + [fm removeItemAtPath:path error:nil]; + } + return rv; +} + +int main(int argc, const char *argv[]) +{ + if (getuid() == 0) { + // Be mobile + if (setuid(501)) { + fprintf(stderr, "Error: unable to become mobile"); + return -1; + } + } + dlopen("/System/Library/PrivateFrameworks/MobileContainerManager.framework/MobileContainerManager", RTLD_LAZY); + 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"); + $MCMPluginKitPluginDataContainer = objc_getClass("MCMPluginKitPluginDataContainer"); + $MCMAppDataContainer = objc_getClass("MCMAppDataContainer"); + $LSApplicationWorkspace = objc_getClass("LSApplicationWorkspace"); + workspace = [$LSApplicationWorkspace defaultWorkspace]; + + static int rv=0; + @autoreleasepool { + bool respring=false, do_all=false; + void (*jb_oneshot_entitle_now)(pid_t a, uint64_t b); + void *libjb = dlopen("/usr/lib/libjailbreak.dylib", RTLD_LAZY); + + if (libjb) { + dlerror(); + jb_oneshot_entitle_now = (void (*)(pid_t, uint64_t))dlsym(libjb, "jb_oneshot_entitle_now"); + if (!dlerror()) jb_oneshot_entitle_now(getpid(), 2); + } + + NSMutableDictionary *paths = [NSMutableDictionary new]; + NSMutableDictionary *unregister_paths = [NSMutableDictionary new]; + static struct option long_options[] = + { + {"all", no_argument, 0, 'a'}, + {"help", no_argument, 0, 'h'}, + {"path", required_argument, 0, 'p'}, + {"unregister", required_argument, 0, 'u'}, + {"respring", no_argument, 0, 'r'}, + {"verbose", no_argument, 0, 'v'}, + {0, 0, 0, 0} + }; + int option_index = 0; + char ch; + bool have_path = false; + while ((ch = getopt_long(argc, (char *const *)argv, "ap:ru:vh?", long_options, &option_index)) != -1) { + switch (ch) + { + case 'a': + do_all = true; + break; + case 'h': + usage(); + break; + case 'p': + paths[@(optarg)] = @YES; + have_path = true; + break; + case 'r': + respring = true; + break; + case 'u': + unregister_paths[@(optarg)] = @YES; + have_path = true; + break; + case 'v': + verbose++; + break; + default: + break; + } + } + if (do_all || !$MCMPluginKitPluginDataContainer || !$MCMAppDataContainer || !$LSApplicationWorkspace) { + rv = standard_uicache(); + } else if (have_path) { + for (NSString *path in [paths allKeys]) { + if (verbose) fprintf(stderr, "Refreshing %s\n", path.UTF8String); + if (!registerPath(path)) rv++; + } + for (NSString *path in [unregister_paths allKeys]) { + if (!unregisterPath(path)) rv++; + } + } else { + rv += optimized_uicache(); + } + if ( respring ) + { + pid_t sb_pid = launch_get_job_pid("com.apple.SpringBoard"); + if ($SBSRelaunchAction && $FBSSystemService) { + 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)) { + break; + } + usleep(1000); + } + } else { + system("launchctl stop com.apple.SpringBoard"); + system("launchctl stop com.apple.backboardd"); + } + } + } // @autoreleasepool + return rv; } diff -urN uikittools/uicache.xml uikittools+uicache/uicache.xml --- uikittools/uicache.xml 2018-10-04 15:58:37.000000000 -1000 +++ uikittools+uicache/uicache.xml 2019-06-07 22:37:29.000000000 -1000 @@ -4,23 +4,22 @@ com.apple.private.mobileinstall.allowedSPI InstallForLaunchServices + UninstallForLaunchServices - com.apple.lsapplicationworkspace.rebuildappdatabases - com.apple.private.MobileContainerManager.allowed - com.apple.private.kernel.override-cpumon - com.apple.vpn.installer_events - + com.apple.frontboard.launchapplications + + com.apple.frontboard.shutdown + platform-application - com.apple.private.skip-library-validation