uicache.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /*
  2. * uicache.m
  3. * nitoTV
  4. *
  5. * Based on asu_inject.c in AppSyncUnified
  6. * https://github.com/angelXwind/AppSync
  7. * http://cydia.angelxwind.net/?page/net.angelxwind.appsyncunified
  8. *
  9. * Copyright (c) 2014 Karen Tsai <angelXwind@angelxwind.net>
  10. *
  11. * AppSync Unified is NOT for piracy. Use it legally.
  12. *
  13. * This program is free software: you can redistribute it and/or modify
  14. * it under the terms of the GNU General Public License as published by
  15. * the Free Software Foundation, either version 3 of the License, or
  16. * (at your option) any later version.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU General Public License
  24. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  25. */
  26. /*
  27. this is the main source of the uicache binary, it will inject librespring.dylib
  28. into PineBoard, register for some notifications and then fire off
  29. the distributed notification that gets received by librespring inside PineBoard.app
  30. */
  31. #include <dlfcn.h>
  32. #import <Foundation/Foundation.h>
  33. #include <mach-o/dyld.h>
  34. #import <objc/runtime.h>
  35. #include <sys/cdefs.h>
  36. #include <sys/types.h>
  37. #include <sys/param.h>
  38. #include <mach/mach.h>
  39. #include <mach/boolean.h>
  40. #include <dispatch/dispatch.h>
  41. #include <stdint.h>
  42. #include <stdio.h>
  43. #include <stdlib.h>
  44. #include <spawn.h>
  45. #include <assert.h>
  46. #import <objc/runtime.h>
  47. //#import "rocketbootstrap.h"
  48. #import "AppSupport/CPDistributedMessagingCenter.h"
  49. #import <MobileCoreServices/MobileCoreServices.h>
  50. #import "NSTask.h"
  51. #include <errno.h>
  52. #include <libgen.h>
  53. #include <string.h>
  54. #include <unistd.h>
  55. #include <getopt.h>
  56. @interface PBSSystemService : NSObject
  57. +(id)sharedInstance;
  58. -(void)endpointForProviderType:(id)arg1 withIdentifier:(id)arg2 responseBlock:(/*^block*/id)arg3 ;
  59. -(void)launchKioskApp;
  60. -(void)sleepSystemForReason:(id)arg1 ;
  61. -(void)wakeSystemForReason:(id)arg1 ;
  62. -(void)relaunchBackboardd;
  63. -(void)relaunch;
  64. -(void)reboot;
  65. -(id)infoForProvidersWithType:(id)arg1 ;
  66. -(void)deactivateApplication;
  67. -(void)registerServiceProviderEndpoint:(id)arg1 forProviderType:(id)arg2 ;
  68. -(void)deactivateScreenSaver;
  69. @end
  70. @interface LSApplicationWorkspace : NSObject
  71. + (id)defaultWorkspace;
  72. - (BOOL)_LSPrivateRebuildApplicationDatabasesForSystemApps:(BOOL)arg1 internal:(BOOL)arg2 user:(BOOL)arg3;
  73. - (BOOL)registerApplicationDictionary:(NSDictionary *)applicationDictionary;
  74. - (BOOL)registerBundleWithInfo:(NSDictionary *)bundleInfo options:(NSDictionary *)options type:(unsigned long long)arg3 progress:(id)arg4 ;
  75. - (BOOL)registerApplication:(NSURL *)url;
  76. - (BOOL)registerPlugin:(NSURL *)url;
  77. - (BOOL)unregisterApplication:(NSURL *)url;
  78. - (NSArray *)installedPlugins;
  79. -(void)_LSPrivateSyncWithMobileInstallation;
  80. @end
  81. #define DLog(format, ...) CFShow((__bridge CFStringRef)[NSString stringWithFormat:format, ## __VA_ARGS__]);
  82. extern char*** _NSGetEnviron(void);
  83. extern int proc_listallpids(void*, int);
  84. extern int proc_pidpath(int, void*, uint32_t);
  85. static const char* cynject_path = "/usr/bin/cynject";
  86. static const char* dylib_path = "/usr/lib/librespring.dylib";
  87. static const char* dispatch_queue_name = NULL;
  88. static const char* process_name = "PineBoard";
  89. static int process_buffer_size = 4096;
  90. static pid_t process_pid = -1;
  91. static boolean_t find_process(const char* name, pid_t* ppid_ret) {
  92. pid_t *pid_buffer;
  93. char path_buffer[MAXPATHLEN];
  94. int count, i, ret;
  95. boolean_t res = FALSE;
  96. pid_buffer = (pid_t*)calloc(1, process_buffer_size);
  97. assert(pid_buffer != NULL);
  98. count = proc_listallpids(pid_buffer, process_buffer_size);
  99. if(count) {
  100. for(i = 0; i < count; i++) {
  101. pid_t ppid = pid_buffer[i];
  102. ret = proc_pidpath(ppid, (void*)path_buffer, sizeof(path_buffer));
  103. if(ret < 0) {
  104. printf("(%s:%d) proc_pidinfo() call failed.\n", __FILE__, __LINE__);
  105. continue;
  106. }
  107. if(strstr(path_buffer, name)) {
  108. res = TRUE;
  109. *ppid_ret = ppid;
  110. break;
  111. }
  112. }
  113. }
  114. free(pid_buffer);
  115. return res;
  116. }
  117. /* Set platform binary flag */
  118. #define FLAG_PLATFORMIZE (1 << 1)
  119. void platformize_me() {
  120. void* handle = dlopen("/usr/lib/libjailbreak.dylib", RTLD_LAZY);
  121. if (!handle) return;
  122. // Reset errors
  123. dlerror();
  124. typedef void (*fix_entitle_prt_t)(pid_t pid, uint32_t what);
  125. fix_entitle_prt_t ptr = (fix_entitle_prt_t)dlsym(handle, "jb_oneshot_entitle_now");
  126. const char *dlsym_error = dlerror();
  127. if (dlsym_error) return;
  128. ptr(getpid(), FLAG_PLATFORMIZE);
  129. }
  130. static void inject_dylib(const char* name, pid_t pid, const char* dylib) {
  131. char** argv;
  132. char pid_buf[32];
  133. int res;
  134. pid_t child;
  135. argv = calloc(4, sizeof(char*));
  136. assert(argv != NULL);
  137. snprintf(pid_buf, sizeof(pid_buf), "%d", pid);
  138. argv[0] = (char*)name;
  139. argv[1] = (char*)pid_buf;
  140. argv[2] = (char*)dylib;
  141. argv[3] = NULL;
  142. printf("(%s:%d) calling \"%s %s %s\"\n", __FILE__, __LINE__, argv[0], argv[1], argv[2]);
  143. res = posix_spawn(&child, argv[0], NULL, NULL, argv, (char* const*)_NSGetEnviron());
  144. assert(res == 0);
  145. return;
  146. }
  147. @interface NSDistributedNotificationCenter : NSNotificationCenter
  148. + (id)defaultCenter;
  149. - (void)addObserver:(id)arg1 selector:(SEL)arg2 name:(id)arg3 object:(id)arg4;
  150. - (void)postNotificationName:(id)arg1 object:(id)arg2 userInfo:(id)arg3;
  151. @end
  152. @interface MessageHandler : NSObject
  153. -(void)rebuildCache:(id)a;
  154. @end
  155. @implementation MessageHandler
  156. + (NSString *)returnForProcess:(NSString *)call
  157. {
  158. if (call==nil)
  159. return 0;
  160. char line[200];
  161. FILE* fp = popen([call UTF8String], "r");
  162. NSMutableArray *lines = [[NSMutableArray alloc]init];
  163. if (fp)
  164. {
  165. while (fgets(line, sizeof line, fp))
  166. {
  167. NSString *s = [NSString stringWithCString:line encoding:NSUTF8StringEncoding];
  168. s = [s stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  169. [lines addObject:s];
  170. }
  171. }
  172. pclose(fp);
  173. return [lines componentsJoinedByString:@"\n"];
  174. }
  175. - (BOOL)forceReboot {
  176. NSString *command = @"/usr/bin/dpkg -s com.nito.nitotv4 | grep Version | cut -d \" \" -f2";
  177. int _returnCode = 0;
  178. BOOL _finished = FALSE;
  179. NSMutableArray *lines = [NSMutableArray new];
  180. char line[200];
  181. FILE* fp = popen([command UTF8String], "r");
  182. if (fp)
  183. {
  184. while (fgets(line, sizeof line, fp))
  185. {
  186. NSString *s = [NSString stringWithCString:line encoding:NSUTF8StringEncoding];
  187. s = [s stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  188. [lines addObject:s];
  189. }
  190. }
  191. pclose(fp);
  192. BOOL reboot = FALSE;
  193. NSString *version = [lines lastObject];
  194. //DLog(@"nitoTV version: -%@-", version);
  195. NSString *minimumVersion = @"2.4-103";
  196. //DDLogInfo(@"package: '%@' online version: '%@' installed version: '%@'", objectName, onlineVersion, installedVersion);
  197. NSComparisonResult theResult = [minimumVersion compare:version options:NSNumericSearch];
  198. if ( theResult == NSOrderedDescending )
  199. {
  200. //DLog(@"minimum version %@ is less than installedVersion: %@", minimumVersion, version);
  201. reboot = true;
  202. }
  203. return reboot;
  204. }
  205. //called from PBReloadHelper in tvOS 9 circumstances so we can rebuild the cache with proper entitlements
  206. -(void)rebuildCache:(BOOL)reboot
  207. {
  208. DLog(@"Rebuilding cache...\n");
  209. LSApplicationWorkspace *workspace = [LSApplicationWorkspace defaultWorkspace];
  210. [workspace _LSClearSchemaCaches]; //may or may not be necessary
  211. [workspace _LSPrivateRebuildApplicationDatabasesForSystemApps:YES internal:YES user:NO];
  212. BOOL forceReboot = [self forceReboot];
  213. if (reboot || forceReboot){
  214. DLog(@"Respringing...\n");
  215. /*
  216. dlopen("/System/Library/PrivateFrameworks/PineBoardServices.framework/PineBoardServices", RTLD_NOW);
  217. id systemService = [objc_getClass("PBSSystemService") sharedInstance];
  218. DLog(@"system service: %@", systemService);
  219. [systemService relaunchBackboardd];
  220. */
  221. NSString *killallPath = [MessageHandler returnForProcess:@"/usr/bin/which killall"];
  222. NSTask *installTask = [NSTask launchedTaskWithLaunchPath:killallPath arguments:@[@"-9", @"backboardd"]];
  223. }
  224. }
  225. //called from PBReloadHelper in tvOS 10, just to close things up.
  226. -(void)donewithit:(id)a
  227. {
  228. DLog(@"Finished!\n");
  229. exit(0);
  230. }
  231. @end
  232. #define OPTION_FLAGS "r"
  233. char *progname;
  234. static struct option longopts[] = {
  235. { "reboot", no_argument, NULL, 'r' },
  236. { NULL, 0, NULL, 0 }
  237. };
  238. int main(int argc, char* argv[]) {
  239. platformize_me();
  240. printf("uicache for tvOS slim edition\n");
  241. bool reboot = false;
  242. int flag;
  243. // NSString *value = nil;
  244. while ((flag = getopt_long(argc, argv, OPTION_FLAGS, longopts, NULL)) != -1) {
  245. switch(flag) {
  246. case 'r':
  247. reboot = true;
  248. break;
  249. }
  250. }
  251. MessageHandler *mh = [MessageHandler new];
  252. [mh rebuildCache:reboot];
  253. return 0;
  254. //[[LSApplicationWorkspace defaultWorkspace] _LSPrivateRebuildApplicationDatabasesForSystemApps:YES internal:YES user:NO];
  255. /*
  256. printf("based on asu_inject from AppSyncUnified\n");
  257. printf("Creating queue...\n");
  258. dispatch_queue_t queue = dispatch_queue_create(dispatch_queue_name, 0);
  259. printf("Finding PineBoard PID...\n");
  260. dispatch_async(queue, ^{ while (!find_process(process_name, &process_pid)); });
  261. printf("Waiting for queue to come back...\n");
  262. */
  263. /* wait for queue to come back */
  264. /*
  265. dispatch_sync(queue, ^{});
  266. printf("PineBoard PID is %d\n", process_pid);
  267. printf("Injecting librespring.dylib into PineBoard...\n");
  268. inject_dylib(cynject_path, process_pid, dylib_path);
  269. sleep(1);
  270. */
  271. /*
  272. since we can only inject the library once, need to actually trigger the
  273. respring with some notifications, due to some restrictions with entitlements
  274. we need to start a runloop to receive some notifications back to do the actual
  275. database rebuilding.
  276. */
  277. NSDistributedNotificationCenter* notificationCenter;
  278. notificationCenter = [NSDistributedNotificationCenter defaultCenter];
  279. [notificationCenter addObserver:[MessageHandler new] selector:@selector(rebuildCache:) name:@"kjc.AppEnabler.rebuild_appdb" object:nil];
  280. [notificationCenter addObserver:[MessageHandler new] selector:@selector(donewithit:) name:@"kjc.AppEnabler.done" object:nil];
  281. [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"kjc.AppEnabler.recache" object:nil userInfo:nil];
  282. CFRunLoopRun();
  283. return 0;
  284. }