diff options
author | Sam Bingner <sam@bingner.com> | 2019-05-18 22:15:28 -1000 |
---|---|---|
committer | Sam Bingner <sam@bingner.com> | 2019-05-18 22:15:28 -1000 |
commit | 8cc4cb5a360131fff8a1ff9da70f9893dca1559c (patch) | |
tree | c81253f67edce9bf08d758dd4fd44e409da4d48f | |
parent | b3d0160fb6bcf2b9429907cd6273578f624182ea (diff) |
Update uikittools
l--------- | data/uikittools/_metadata/findutils.dep | 1 | ||||
-rw-r--r-- | data/uikittools/ldrestart-jbd.diff | 6 | ||||
-rw-r--r-- | data/uikittools/uicache.diff | 1109 | ||||
-rw-r--r-- | data/uikittools/world.diff | 702 |
4 files changed, 1816 insertions, 2 deletions
diff --git a/data/uikittools/_metadata/findutils.dep b/data/uikittools/_metadata/findutils.dep new file mode 120000 index 000000000..0cf65ba21 --- /dev/null +++ b/data/uikittools/_metadata/findutils.dep @@ -0,0 +1 @@ +../../findutils
\ No newline at end of file diff --git a/data/uikittools/ldrestart-jbd.diff b/data/uikittools/ldrestart-jbd.diff index 49b86d4b8..43f66a693 100644 --- a/data/uikittools/ldrestart-jbd.diff +++ b/data/uikittools/ldrestart-jbd.diff @@ -2,7 +2,7 @@ diff --git a/ldrestart.cpp b/ldrestart.cpp index a6d6cb6..c9a3b33 100644 --- a/ldrestart.cpp +++ b/ldrestart.cpp -@@ -34,6 +34,28 @@ +@@ -34,6 +34,30 @@ #define FLAG_PLATFORMIZE (1 << 1) #include <dlfcn.h> @@ -20,10 +20,12 @@ index a6d6cb6..c9a3b33 100644 + +const char *skip[] = { + "jailbreakd", ++ "com.saurik.substrated", + "com.apple.MobileFileIntegrity", + "com.openssh.sshd.", + "com.apple.SpringBoard", -+ // "com.apple.logd", ++ "com.apple.securityd", ++ "com.apple.trustd", + "com.apple.diagnosticd", + NULL +}; diff --git a/data/uikittools/uicache.diff b/data/uikittools/uicache.diff new file mode 100644 index 000000000..02828f4ed --- /dev/null +++ b/data/uikittools/uicache.diff @@ -0,0 +1,1109 @@ +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 <launch.h> +-#include <notify.h> +- +-#include <stdio.h> +-#include <unistd.h> +- +-#include <CoreFoundation/CoreFoundation.h> +- +-/* Set platform binary flag */ +-#define FLAG_PLATFORMIZE (1 << 1) +-#include <dlfcn.h> +- +-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 <launch.h> ++#include <notify.h> ++ ++#include <stdio.h> ++#include <unistd.h> ++ ++#include <Foundation/Foundation.h> ++ ++/* Set platform binary flag */ ++#define FLAG_PLATFORMIZE (1 << 1) ++#include <dlfcn.h> ++#include <objc/runtime.h> ++ ++@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 @@ ++<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> ++<plist version="1.0"> ++<dict> ++ <key>com.apple.frontboard.launchapplications</key> ++ <true/> ++ <key>com.apple.frontboard.shutdown</key> ++ <true/> ++ <key>platform-application</key> ++ <true/> ++ <key>com.apple.private.skip-library-validation</key> ++ <true/> ++</dict> ++</plist> +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,14 @@ + /* }}} */ + + #import <Foundation/Foundation.h> +- +-#include <notify.h> +-#include <sys/types.h> +-#include <sys/stat.h> +-#include <unistd.h> +- +-#include <objc/runtime.h> ++#import <dlfcn.h> ++#import <notify.h> ++#import <sys/types.h> ++#import <sys/stat.h> ++#import <unistd.h> ++#import <getopt.h> ++#import <launch.h> ++#import <objc/runtime.h> + + #include "csstore.hpp" + +@@ -81,6 +92,12 @@ + + @end + ++@interface LSApplicationProxy : NSObject ++- (NSString*) applicationIdentifier; ++- (NSURL*) resourcesDirectoryURL; ++- (NSDate*) registeredDate; ++@end ++ + @interface LSApplicationWorkspace : NSObject + + (id) defaultWorkspace; + - (BOOL) registerApplication:(id)application; +@@ -89,16 +106,188 @@ + - (BOOL) registerApplicationDictionary:(id)application; + - (BOOL) installApplication:(id)application withOptions:(id)options; + - (BOOL) _LSPrivateRebuildApplicationDatabasesForSystemApps:(BOOL)system internal:(BOOL)internal user:(BOOL)user; ++- (NSArray<LSApplicationProxy*>*) allApplications; ++@end ++ ++@interface MCMAppDataContainer +++(id)containerWithIdentifier:(NSString*)identifier createIfNecessary:(bool)create existed:(bool*)existed error:(NSError*)error; ++-(NSURL*)url; + @end + +-int main(int argc, const char *argv[]) { +- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; ++@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 ++ ++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)]]; ++} ++ ++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; ++ } ++} ++ ++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 (![workspace 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); ++} + +- Class $LSApplicationWorkspace(objc_getClass("LSApplicationWorkspace")); +- LSApplicationWorkspace *workspace($LSApplicationWorkspace == nil ? nil : [$LSApplicationWorkspace defaultWorkspace]); ++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; ++} + +- if (kCFCoreFoundationVersionNumber > 1000) // this API is on iOS 7 but invaliding the icon cache is harder there +- if ([workspace respondsToSelector:@selector(_LSPrivateRebuildApplicationDatabasesForSystemApps:internal:user:)]) { ++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 +415,188 @@ + 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; ++ } ++} ++ ++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; ++ } ++ 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 @@ + <key>com.apple.private.mobileinstall.allowedSPI</key> + <array> + <string>InstallForLaunchServices</string> ++ <string>UninstallForLaunchServices</string> + </array> +- + <key>com.apple.lsapplicationworkspace.rebuildappdatabases</key> + <true/> +- + <key>com.apple.private.MobileContainerManager.allowed</key> + <true/> +- + <key>com.apple.private.kernel.override-cpumon</key> + <true/> +- + <key>com.apple.vpn.installer_events</key> + <true/> +- ++ <key>com.apple.frontboard.launchapplications</key> ++ <true/> ++ <key>com.apple.frontboard.shutdown</key> ++ <true/> + <key>platform-application</key> + <true/> +- + <key>com.apple.private.skip-library-validation</key> + <true/> + </dict> diff --git a/data/uikittools/world.diff b/data/uikittools/world.diff new file mode 100644 index 000000000..6b0811b1b --- /dev/null +++ b/data/uikittools/world.diff @@ -0,0 +1,702 @@ +diff -ur uikittools/gssc.mm uikittools+subtype/gssc.mm +--- uikittools/gssc.mm 2018-10-04 15:58:19.000000000 -1000 ++++ uikittools+subtype/gssc.mm 2019-05-10 13:56:03.000000000 -1000 +@@ -98,133 +98,280 @@ + if (CFTypeRef (*$MGCopyAnswer)(CFStringRef) = reinterpret_cast<CFTypeRef (*)(CFStringRef)>(dlsym(libMobileGestalt, "MGCopyAnswer"))) { + NSMutableDictionary *answers([NSMutableDictionary dictionary]); + for (NSString *name in [NSArray arrayWithObjects: +- @"HasSEP", +- @"HasThinBezel", +- @"apple-internal-install", +- @"cameraRestriction", +- @"data-plan", +- @"multitasking-gestures", +- @"rear-facing-camera", +- @"wapi", +- @"watch-companion", +- +- @"AirDropCapability", +- @"CarrierInstallCapability", +- @"CellularTelephonyCapability", +- @"UIParallaxCapability", +- @"ambient-light-sensor", +- @"personal-hotspot", +- @"shoebox", +- @"hall-effect-sensor", ++ @"1080p", ++ @"3GProximityCapability", + @"3Gvenice", +- ++ @"3d-imagery", ++ @"3d-maps", ++ @"64-bit", ++ @"720p", ++ @"ASTC", ++ @"AWDID", ++ @"AWDLCapability", ++ @"ActivationProtocol", + @"ActiveWirelessTechnology", +- //@"AirplaneMode", ++ @"AggregateDevicePhotoZoomFactor", ++ @"AggregateDeviceVideoZoomFactor", ++ @"AirDropCapability", ++ @"AirplayMirroringCapability", + @"AllDeviceCapabilities", ++ @"Allow32BitApps", ++ @"AllowOnlyATVCPSDKApps", + @"AllowYouTube", + @"AllowYouTubePlugin", +- //@"ApNonce", +- //@"AppleInternalInstallCapability", +- @"assistant", +- //@"BasebandBoardSnum", +- //@"BasebandCertId", +- //@"BasebandChipId", +- //@"BasebandFirmwareManifestData", ++ @"AppCapacityTVOS", ++ @"AppStore", ++ @"AudioPlaybackCapability", ++ @"AutoFocusCameraCapability", ++ @"BacklightCapability", ++ @"BasebandChipId", ++ @"BasebandChipset", ++ @"BasebandClass", ++ @"BasebandFirmwareManifestData", + @"BasebandFirmwareVersion", +- //@"BasebandKeyHashInformation", +- //@"BasebandRegionSKU", +- //@"BasebandSerialNumber", ++ @"BasebandPostponementStatus", ++ @"BasebandStatus", + @"BatteryCurrentCapacity", + @"BatteryIsCharging", + @"BatteryIsFullyCharged", +- //@"BluetoothAddress", ++ @"BlueLightReductionSupported", + @"BoardId", ++ @"BridgeBuild", ++ @"BridgeRestoreVersion", ++ @"BuddyLanguagesAnimationRequiresOptimization", ++ @"BuildID", + @"BuildVersion", + @"CPUArchitecture", +- //@"CarrierBundleInfoArray", ++ @"CPUSubType", ++ @"CPUType", ++ @"CameraFlashCapability", ++ @"CameraFrontFlashCapability", ++ @"CameraHDR2Capability", ++ @"CameraLiveEffectsCapability", ++ @"CameraMaxBurstLength", + @"CarrierInstallCapability", +- @"cellular-data", ++ @"CellularTelephonyCapability", + @"ChipID", +- //@"CompassCalibration", +- //@"CompassCalibrationDictionary", +- //@"ComputerName", +- @"contains-cellular-radio", ++ @"CloudPhotoLibraryCapability", ++ @"CoastlineGlowRenderingCapability", ++ @"CompassType", ++ @"ContinuityCapability", ++ @"CoreRoutineCapability", ++ @"DMin", ++ @"DebugBoardRevision", ++ @"DesenseBuild", ++ @"DeviceBackGlassMaterial", ++ @"DeviceBackingColor", ++ @"DeviceBrand", + @"DeviceClass", + @"DeviceClassNumber", + @"DeviceColor", ++ @"DeviceColorMapPolicy", ++ @"DeviceCornerRadius", ++ @"DeviceCoverGlassColor", ++ @"DeviceCoverGlassMaterial", ++ @"DeviceCoverMaterial", + @"DeviceEnclosureColor", +- //@"DeviceName", ++ @"DeviceEnclosureMaterial", ++ @"DeviceEnclosureRGBColor", ++ @"DeviceHasAggregateCamera", ++ @"DeviceHousingColor", ++ @"DeviceLaunchTimeLimitScale", ++ @"DeviceName", ++ @"DevicePrefers3DBuildingStrokes", ++ @"DevicePrefersBuildingStrokes", ++ @"DevicePrefersCheapTrafficShaders", ++ @"DevicePrefersProceduralAntiAliasing", ++ @"DevicePrefersTrafficAlpha", ++ @"DeviceRGBColor", ++ @"DeviceRequiresPetalOptimization", ++ @"DeviceRequiresProximityAmeliorations", ++ @"DeviceRequiresSoftwareBrightnessCalculations", ++ @"DeviceSceneUpdateTimeLimitScale", ++ @"DeviceSubBrand", + @"DeviceSupports1080p", + @"DeviceSupports3DImagery", + @"DeviceSupports3DMaps", + @"DeviceSupports4G", ++ @"DeviceSupports4k", + @"DeviceSupports720p", + @"DeviceSupports9Pin", ++ @"DeviceSupportsAOP", ++ @"DeviceSupportsASTC", ++ @"DeviceSupportsAdaptiveMapsUI", ++ @"DeviceSupportsAlwaysListening", ++ @"DeviceSupportsAlwaysOnCompass", ++ @"DeviceSupportsApplePencil", ++ @"DeviceSupportsAutoLowLightVideo", ++ @"DeviceSupportsBatteryModuleAuthentication", ++ @"DeviceSupportsBerkelium2", ++ @"DeviceSupportsCCK", ++ @"DeviceSupportsCameraCaptureOnTouchDown", ++ @"DeviceSupportsCameraHaptics", ++ @"DeviceSupportsCarIntegration", ++ @"DeviceSupportsCinnamon", ++ @"DeviceSupportsClosedLoopHaptics", ++ @"DeviceSupportsCrudeProx", ++ @"DeviceSupportsDClr", ++ @"DeviceSupportsDoNotDisturbWhileDriving", ++ @"DeviceSupportsELabel", ++ @"DeviceSupportsEnhancedAC3", + @"DeviceSupportsFaceTime", ++ @"DeviceSupportsFloorCounting", ++ @"DeviceSupportsHaptics", ++ @"DeviceSupportsHeartHealthAlerts", ++ @"DeviceSupportsHeartRateVariability", ++ @"DeviceSupportsHiResBuildings", + @"DeviceSupportsLineIn", ++ @"DeviceSupportsLiquidDetection_CorrosionMitigation", ++ @"DeviceSupportsLivePhotoAuto", ++ @"DeviceSupportsMapsBlurredUI", + @"DeviceSupportsNavigation", ++ @"DeviceSupportsNewton", ++ @"DeviceSupportsPeriodicALSUpdates", ++ @"DeviceSupportsPortraitLightEffectFilters", ++ @"DeviceSupportsRGB10", ++ @"DeviceSupportsRaiseToSpeak", ++ @"DeviceSupportsSiDP", + @"DeviceSupportsSimplisticRoadMesh", ++ @"DeviceSupportsSingleCameraPortrait", ++ @"DeviceSupportsSiriSpeaks", ++ @"DeviceSupportsStereoAudioRecording", ++ @"DeviceSupportsStudioLightPortraitPreview", ++ @"DeviceSupportsSwimmingWorkouts", ++ @"DeviceSupportsTapToWake", + @"DeviceSupportsTethering", ++ @"DeviceSupportsToneMapping", ++ @"DeviceSupportsUSBTypeC", ++ @"DeviceSupportsWebkit", ++ @"DeviceSupportsYCbCr10", + @"DeviceVariant", +- //@"DiagData", +- @"dictation", +- //@"DieId", +- //@"DiskUsage", +- @"encrypted-data-partition", +- //@"EthernetMacAddress", ++ @"DeviceVariantGuess", ++ @"DisplayMirroringCapability", + @"ExternalChargeCapability", + @"ExternalPowerSourceConnected", +- //@"FaceTimeBitRate2G", +- //@"FaceTimeBitRate3G", +- //@"FaceTimeBitRateLTE", +- //@"FaceTimeBitRateWiFi", +- //@"FaceTimeDecodings", +- //@"FaceTimeEncodings", +- //@"FaceTimePreferredDecoding", +- //@"FaceTimePreferredEncoding", +- //@"FirmwareNonce", +- //@"FirmwarePreflightInfo", ++ @"FDRSealingStatus", ++ @"FMFAllowed", ++ @"FaceTimeBackCameraTemporalNoiseReductionMode", ++ @"FaceTimeCameraRequiresFastSwitchOptions", ++ @"FaceTimeCameraSupportsHardwareFaceDetection", ++ @"FaceTimeFrontCameraTemporalNoiseReductionMode", ++ @"FaceTimePhotosOptIn", + @"FirmwareVersion", ++ @"FirstPartyLaunchTimeLimitScale", + @"ForwardCameraCapability", +- @"gps", +- @"green-tea", ++ @"FrontCameraOffsetFromDisplayCenter", ++ @"FrontFacingCameraAutoHDRCapability", ++ @"FrontFacingCameraBurstCapability", ++ @"FrontFacingCameraHDRCapability", ++ @"FrontFacingCameraHDROnCapability", ++ @"FrontFacingCameraHFRCapability", ++ @"FrontFacingCameraMaxVideoZoomFactor", ++ @"FrontFacingCameraStillDurationForBurst", ++ @"FrontFacingCameraVideoCapture1080pMaxFPS", ++ @"FrontFacingCameraVideoCapture4kMaxFPS", ++ @"FrontFacingCameraVideoCapture720pMaxFPS", ++ @"GPSCapability", ++ @"GSDeviceName", ++ @"H264EncoderCapability", ++ @"HEVCDecoder10bitSupported", ++ @"HEVCDecoder12bitSupported", ++ @"HEVCDecoder8bitSupported", ++ @"HEVCEncodingCapability", + @"HWModelStr", ++ @"HallEffectSensorCapability", + @"HardwarePlatform", +- //@"HasAllFeaturesCapability", ++ @"HasAppleNeuralEngine", + @"HasBaseband", ++ @"HasBattery", ++ @"HasDaliMode", ++ @"HasExtendedColorDisplay", ++ @"HasIcefall", + @"HasInternalSettingsBundle", ++ @"HasMesa", ++ @"HasPKA", ++ @"HasSEP", + @"HasSpringBoard", +- //@"IntegratedCircuitCardIdentifier", +- //@"InternalBuild", +- //@"InternationalMobileEquipmentIdentity", +- //@"InverseDeviceID", +- //@"IsSimulator", +- //@"IsThereEnoughBatteryLevelForSoftwareUpdate", +- //@"IsUIBuild", +- //@"MLBSerialNumber", +- @"main-screen-class", +- @"main-screen-height", +- @"main-screen-orientation", +- @"main-screen-pitch", +- @"main-screen-scale", +- @"main-screen-width", ++ @"HasThinBezel", ++ @"HearingAidAudioEqualizationCapability", ++ @"HearingAidLowEnergyAudioCapability", ++ @"HearingAidPowerReductionCapability", ++ @"HighestSupportedVideoMode", ++ @"HomeButtonType", ++ @"IDAMCapability", ++ @"IcefallInRestrictedMode", ++ @"Image4CryptoHashMethod", ++ @"Image4Supported", ++ @"IsEmulatedDevice", ++ @"IsLargeFormatPhone", ++ @"IsPwrOpposedVol", ++ @"IsSimulator", ++ @"IsUIBuild", ++ @"LaunchTimeLimitScaleSupported", ++ @"LisaCapability", ++ @"LocationRemindersCapability", ++ @"LynxPublicKey", ++ @"MainDisplayRotation", ++ @"MarketingProductName", ++ @"MarketingVersion", ++ @"MaxH264PlaybackLevel", ++ @"MaximumScreenScale", ++ @"MedusaFloatingLiveAppCapability", ++ @"MedusaOverlayAppCapability", ++ @"MedusaPIPCapability", ++ @"MedusaPinnedAppCapability", ++ @"MicrophoneCount", + @"MinimumSupportediTunesVersion", +- //@"MobileEquipmentIdentifier", +- //@"MobileSubscriberCountryCode", +- //@"MobileSubscriberNetworkCode", +- @"wi-fi", ++ @"MixAndMatchPrevention", ++ @"MobileDeviceMinimumVersion", + @"ModelNumber", +- @"not-green-tea", ++ @"MonarchLowEndHardware", ++ @"MusicStore", ++ @"N78aHack", ++ @"NFCRadioCalibrationDataPresent", ++ @"NavajoFusingState", ++ @"NikeIpodCapability", ++ @"OLEDDisplay", ++ @"OfflineDictationCapability", ++ @"OpenGLESVersion", ++ @"PTPLargeFilesCapability", + @"PanoramaCameraCapability", + @"PartitionType", ++ @"PearlIDCapability", ++ @"PeekUICapability", ++ @"PeekUIWidth", ++ @"PersonalHotspotCapability", ++ @"PhoneNumber", ++ @"PhosphorusCapability", ++ @"PhotoCapability", ++ @"PhotoSharingCapability", ++ @"PhotosPostEffectsCapability", ++ @"PipelinedStillImageProcessingCapability", ++ @"PlatinumCapability", + @"ProductName", + @"ProductType", + @"ProductVersion", +- //@"ProximitySensorCalibration", ++ @"RF-exposure-separation-distance", ++ @"RFExposureSeparationDistance", + @"RearCameraCapability", ++ @"RearCameraOffsetFromDisplayCenter", ++ @"RearFacingCamera60fpsVideoCaptureCapability", ++ @"RearFacingCameraAutoHDRCapability", ++ @"RearFacingCameraBurstCapability", ++ @"RearFacingCameraHDRCapability", ++ @"RearFacingCameraHDROnCapability", ++ @"RearFacingCameraHFRCapability", ++ @"RearFacingCameraHFRVideoCapture1080pMaxFPS", ++ @"RearFacingCameraHFRVideoCapture720pMaxFPS", ++ @"RearFacingCameraMaxVideoZoomFactor", ++ @"RearFacingCameraStillDurationForBurst", ++ @"RearFacingCameraVideoCapture1080pMaxFPS", ++ @"RearFacingCameraVideoCapture4kMaxFPS", ++ @"RearFacingCameraVideoCapture720pMaxFPS", ++ @"RearFacingCameraVideoCaptureFPS", ++ @"RearFacingTelephotoCameraCapability", + @"RegionCode", + @"RegionInfo", +- //@"RegionalBehaviorAll", + @"RegionalBehaviorChinaBrick", + @"RegionalBehaviorEUVolumeLimit", + @"RegionalBehaviorGB18030", +@@ -234,109 +381,316 @@ + @"RegionalBehaviorNoVOIP", + @"RegionalBehaviorNoWiFi", + @"RegionalBehaviorShutterClick", ++ @"RegionalBehaviorValid", + @"RegionalBehaviorVolumeLimit", + @"RegulatoryIdentifiers", +- //@"ReleaseType", ++ @"RegulatoryModelNumber", ++ @"ReleaseType", ++ @"RenderWideGamutImagesAtDisplayTime", ++ @"RendersLetterPressSlowly", + @"RequiredBatteryLevelForSoftwareUpdate", ++ @"RestoreOSBuild", ++ @"RestrictedCountryCodes", ++ @"RingerSwitchCapability", ++ @"RotateToWakeStatus", + @"SBAllowSensitiveUI", + @"SBCanForceDebuggingInfo", + @"SDIOManufacturerTuple", + @"SDIOProductInfo", +- //@"SIMTrayStatus", +- //@"ScreenDimensions", +- //@"screen-dimensions", +- //@"SerialNumber", ++ @"SavageUID", ++ @"ScreenRecorderCapability", ++ @"SecureElement", + @"ShouldHactivate", ++ @"SiKACapability", + @"SigningFuse", +- //@"SoftwareBehavior", +- //@"SoftwareBundleVersion", ++ @"SiliconBringupBoard", ++ @"SiriGestureCapability", ++ @"SiriOfflineCapability", ++ @"Skey", ++ @"SoftwareBundleVersion", ++ @"SoftwareDimmingAlpha", ++ @"SphereCapability", ++ @"StarkCapability", ++ @"StrictWakeKeyboardCases", + @"SupportedDeviceFamilies", +- //@"SupportedKeyboards", +- //@"SysCfg", +- //@"UniqueChipID", +- //@"UniqueDeviceID", +- //@"UniqueDeviceIDData", +- //@"UserAssignedDeviceName", +- //@"WifiAddress", +- //@"WifiAddressData", +- //@"WifiVendor", +- //@"WirelessBoardSnum", +- @"iTunesFamilyID", +- +- @"720p", +- @"1080p", ++ @"SupportsBurninMitigation", ++ @"SupportsEDUMU", ++ @"SupportsForceTouch", ++ @"SupportsIrisCapture", ++ @"SupportsLowPowerMode", ++ @"SupportsPerseus", ++ @"SupportsRotateToWake", ++ @"SupportsSOS", ++ @"SupportsSSHBButtonType", ++ @"SupportsTouchRemote", ++ @"TimeSyncCapability", ++ @"TouchDelivery120Hz", ++ @"UIBackgroundQuality", ++ @"UIParallaxCapability", ++ @"UIProceduralWallpaperCapability", ++ @"UIReachability", ++ @"UserIntentPhysicalButtonCGRect", ++ @"UserIntentPhysicalButtonCGRectString", ++ @"UserIntentPhysicalButtonNormalizedCGRect", ++ @"VibratorCapability", ++ @"VideoStillsCapability", ++ @"WAGraphicQuality", ++ @"WLANBkgScanCache", ++ @"WSKU", ++ @"WiFiCallingCapability", ++ @"WifiAntennaSKUVersion", ++ @"WifiCallingSecondaryDeviceCapability", ++ @"WifiChipset", ++ @"WifiFirmwareVersion", ++ @"WifiVendor", ++ @"WirelessChargingCapability", ++ @"YonkersUID", + @"accelerometer", + @"accessibility", + @"additional-text-tones", ++ @"aggregate-cam-photo-zoom", ++ @"aggregate-cam-video-zoom", ++ @"airDropRestriction", ++ @"airplay-mirroring", ++ @"airplay-no-mirroring", + @"all-features", ++ @"allow-32bit-apps", ++ @"ambient-light-sensor", ++ @"ane", + @"any-telephony", ++ @"apn", + @"app-store", ++ @"apple-internal-install", + @"application-installation", ++ @"applicationInstallation", ++ @"arkit", ++ @"arm64", + @"armv6", + @"armv7", ++ @"armv7s", + @"assistant", ++ @"auto-focus", + @"auto-focus-camera", ++ @"baseband-chipset", + @"bluetooth", + @"bluetooth-le", ++ @"builtin-mics", ++ @"c2k-device", ++ @"call-forwarding", ++ @"call-waiting", ++ @"caller-id", + @"camera-flash", ++ @"camera-front", ++ @"camera-front-flash", ++ @"camera-rear", ++ @"cameraRestriction", ++ @"car-integration", ++ @"cell-broadcast", + @"cellular-data", ++ @"class", ++ @"closed-loop", + @"contains-cellular-radio", ++ @"crypto-hash-method", ++ @"data-plan", ++ @"debug-board-revision", ++ @"delay-sleep-for-headset-click", ++ @"device-color-policy", ++ @"device-colors", ++ @"device-name", ++ @"device-name-localized", + @"dictation", + @"display-mirroring", ++ @"display-rotation", + @"displayport", ++ @"does-not-support-gamekit", ++ @"enc-top-type", + @"encode-aac", + @"encrypted-data-partition", ++ @"enforce-googlemail", ++ @"enforce-shutter-click", ++ @"explicitContentRestriction", ++ @"face-detection-support", ++ @"fast-switch-options", + @"fcc-logos-via-software", ++ @"fcm-type", ++ @"firmware-version", ++ @"flash", ++ @"front-auto-hdr", ++ @"front-burst", ++ @"front-burst-image-duration", + @"front-facing-camera", ++ @"front-flash-capability", ++ @"front-hdr", ++ @"front-hdr-on", ++ @"front-max-video-fps-1080p", ++ @"front-max-video-fps-4k", ++ @"front-max-video-fps-720p", ++ @"front-max-video-zoom", ++ @"front-slowmo", ++ @"full-6", ++ @"function-button_halleffect", ++ @"function-button_ringerab", + @"gamekit", + @"gas-gauge-battery", + @"gps", ++ @"gps-capable", ++ @"green-tea", + @"gyroscope", + @"h264-encoder", ++ @"hall-effect-sensor", ++ @"haptics", + @"hardware-keyboard", ++ @"has-sphere", + @"hd-video-capture", + @"hdr-image-capture", ++ @"healthkit", ++ @"hearingaid-audio-equalization", ++ @"hearingaid-low-energy-audio", ++ @"hearingaid-power-reduction", + @"hiccough-interval", ++ @"hide-non-default-apps", + @"hidpi", ++ @"home-button-type", + @"homescreen-wallpaper", + @"hw-encode-snapshots", ++ @"hw-snapshots-need-purplegfx", ++ @"iAP2Capability", ++ @"iTunesFamilyID", ++ @"iap2-protocol-supported", ++ @"image4-supported", + @"international-settings", + @"io-surface-backed-images", ++ @"ipad", ++ @"kConferenceCallType", ++ @"kSimultaneousCallAndDataCurrentlySupported", ++ @"kSimultaneousCallAndDataSupported", ++ @"large-format-phone", ++ @"live-effects", ++ @"live-photo-capture", + @"load-thumbnails-while-scrolling", ++ @"location-reminders", + @"location-services", ++ @"lte-device", + @"magnetometer", ++ @"main-screen-class", ++ @"main-screen-height", ++ @"main-screen-orientation", ++ @"main-screen-pitch", ++ @"main-screen-scale", ++ @"main-screen-width", ++ @"marketing-name", ++ @"mesa", ++ @"metal", + @"microphone", ++ @"mix-n-match-prevention-status", + @"mms", ++ @"modelIdentifier", ++ @"multi-touch", + @"multitasking", ++ @"multitasking-gestures", + @"music-store", ++ @"n78a-mode", ++ @"name", ++ @"navigation", ++ @"nfc", ++ @"nfcWithRadio", + @"nike-ipod", ++ @"nike-support", ++ @"no-coreroutine", ++ @"no-hi-res-buildings", ++ @"no-simplistic-road-mesh", + @"not-green-tea", ++ @"offline-dictation", ++ @"opal", + @"opengles-1", + @"opengles-2", ++ @"opengles-3", ++ @"opposed-power-vol-buttons", ++ @"ota-activation", ++ @"panorama", ++ @"peek-ui-width", + @"peer-peer", ++ @"personal-hotspot", + @"photo-adjustments", + @"photo-stream", ++ @"piezo-clicker", ++ @"pipelined-stillimage-capability", ++ @"platinum", ++ @"post-effects", ++ @"pressure", ++ @"prox-sensor", + @"proximity-sensor", + @"ptp-large-files", ++ @"public-key-accelerator", ++ @"rear-auto-hdr", ++ @"rear-burst", ++ @"rear-burst-image-duration", ++ @"rear-cam-telephoto-capability", ++ @"rear-facing-camera", ++ @"rear-hdr", ++ @"rear-hdr-on", ++ @"rear-max-slomo-video-fps-1080p", ++ @"rear-max-slomo-video-fps-720p", ++ @"rear-max-video-fps-1080p", ++ @"rear-max-video-fps-4k", ++ @"rear-max-video-fps-720p", ++ @"rear-max-video-frame_rate", ++ @"rear-max-video-zoom", ++ @"rear-slowmo", ++ @"regulatory-model-number", + @"ringer-switch", ++ @"role", ++ @"sandman-support", ++ @"screen-dimensions", ++ @"sensitive-ui", ++ @"shoebox", ++ @"sika-support", ++ @"sim", ++ @"sim-phonebook", ++ @"siri-gesture", ++ @"slow-letterpress-rendering", + @"sms", ++ @"software-bundle-version", ++ @"software-dimming-alpha", + @"stand-alone-contacts", + @"still-camera", ++ @"stockholm", ++ @"supports-always-listening", + @"telephony", + @"telephony-maximum-generation", ++ @"thin-bezel", ++ @"tnr-mode-back", ++ @"tnr-mode-front", ++ @"touch-id", + @"tv-out-crossfade", + @"tv-out-settings", ++ @"ui-background-quality", ++ @"ui-no-parallax", ++ @"ui-no-procedural-wallpaper", ++ @"ui-pip", ++ @"ui-reachability", ++ @"ui-traffic-cheap-shaders", ++ @"ui-weather-quality", ++ @"umts-device", + @"unified-ipod", + @"venice", + @"video-camera", ++ @"video-cap", ++ @"video-stills", + @"voice-control", + @"voip", + @"volume-buttons", ++ @"wapi", ++ @"watch-companion", ++ @"wi-fi", + @"wifi", ++ @"wifi-chipset", ++ @"wifi-module-sn", ++ @"wildcat", ++ @"wlan", + @"youtube", + @"youtube-plugin", +- @"ipad", +- @"wildcat", ++ @"youtubePlugin", + nil]) + if (CFTypeRef answer = $MGCopyAnswer(reinterpret_cast<CFStringRef>(name))) { + [answers setObject:(id)answer forKey:name]; |