Tweak.x 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. #define LIGHTMESSAGING_USE_ROCKETBOOTSTRAP 0
  2. #define LIGHTMESSAGING_TIMEOUT 300
  3. #import "LightMessaging/LightMessaging.h"
  4. #import "rocketbootstrap_internal.h"
  5. #ifndef __APPLE_API_PRIVATE
  6. #define __APPLE_API_PRIVATE
  7. #include "sandbox.h"
  8. #undef __APPLE_API_PRIVATE
  9. #else
  10. #include "sandbox.h"
  11. #endif
  12. #import <mach/mach.h>
  13. #import <substrate.h>
  14. #import <libkern/OSAtomic.h>
  15. //#import <launch.h>
  16. #import <CoreFoundation/CFUserNotification.h>
  17. #import <libkern/OSCacheControl.h>
  18. #import <sys/sysctl.h>
  19. extern int *_NSGetArgc(void);
  20. extern const char ***_NSGetArgv(void);
  21. #define kUserAppsPath "/var/mobile/Applications/"
  22. #define kSystemAppsPath "/Applications/"
  23. static BOOL isDaemon;
  24. static kern_return_t rocketbootstrap_look_up_with_timeout(mach_port_t bp, const name_t service_name, mach_port_t *sp, mach_msg_timeout_t timeout)
  25. {
  26. if (rocketbootstrap_is_passthrough() || isDaemon) {
  27. if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_5_0) {
  28. int sandbox_result = sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_LOCAL_NAME | SANDBOX_CHECK_NO_REPORT, service_name);
  29. if (sandbox_result) {
  30. return sandbox_result;
  31. }
  32. }
  33. return bootstrap_look_up(bp, service_name, sp);
  34. }
  35. // Compatibility mode for Flex, limits it to only the processes in rbs 1.0.1 and earlier
  36. if (strcmp(service_name, "FLMessagingCenterSpringboard") == 0) {
  37. const char **argv = *_NSGetArgv();
  38. size_t arg0len = strlen(argv[0]);
  39. bool allowed = false;
  40. if ((arg0len > sizeof(kUserAppsPath)) && (memcmp(argv[0], kUserAppsPath, sizeof(kUserAppsPath) - 1) == 0))
  41. allowed = true;
  42. if ((arg0len > sizeof(kSystemAppsPath)) && (memcmp(argv[0], kSystemAppsPath, sizeof(kSystemAppsPath) - 1) == 0))
  43. allowed = true;
  44. if (!allowed)
  45. return 1;
  46. }
  47. // Ask our service running inside of the com.apple.ReportCrash.SimulateCrash job
  48. mach_port_t servicesPort = MACH_PORT_NULL;
  49. kern_return_t err = bootstrap_look_up(bp, "com.apple.ReportCrash.SimulateCrash", &servicesPort);
  50. if (err)
  51. return err;
  52. mach_port_t selfTask = mach_task_self();
  53. // Create a reply port
  54. mach_port_name_t replyPort = MACH_PORT_NULL;
  55. err = mach_port_allocate(selfTask, MACH_PORT_RIGHT_RECEIVE, &replyPort);
  56. if (err) {
  57. mach_port_deallocate(selfTask, servicesPort);
  58. return err;
  59. }
  60. // Send message
  61. size_t service_name_size = strlen(service_name);
  62. size_t size = (sizeof(_rocketbootstrap_lookup_query_t) + service_name_size + 3) & ~3;
  63. if (size < sizeof(_rocketbootstrap_lookup_response_t)) {
  64. size = sizeof(_rocketbootstrap_lookup_response_t);
  65. }
  66. char buffer[size];
  67. _rocketbootstrap_lookup_query_t *message = (_rocketbootstrap_lookup_query_t *)&buffer[0];
  68. memset(message, 0, sizeof(_rocketbootstrap_lookup_response_t));
  69. message->head.msgh_id = ROCKETBOOTSTRAP_LOOKUP_ID;
  70. message->head.msgh_size = size;
  71. message->head.msgh_remote_port = servicesPort;
  72. message->head.msgh_local_port = replyPort;
  73. message->head.msgh_reserved = 0;
  74. message->head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
  75. message->name_length = service_name_size;
  76. memcpy(&message->name[0], service_name, service_name_size);
  77. mach_msg_option_t options = MACH_SEND_MSG | MACH_RCV_MSG;
  78. if (timeout == 0) {
  79. timeout = MACH_MSG_TIMEOUT_NONE;
  80. } else {
  81. options |= MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT;
  82. }
  83. err = mach_msg(&message->head, options, size, size, replyPort, timeout, MACH_PORT_NULL);
  84. // Parse response
  85. if (!err) {
  86. _rocketbootstrap_lookup_response_t *response = (_rocketbootstrap_lookup_response_t *)message;
  87. if (response->body.msgh_descriptor_count)
  88. *sp = response->response_port.name;
  89. else
  90. err = 1;
  91. }
  92. // Cleanup
  93. mach_port_deallocate(selfTask, servicesPort);
  94. mach_port_deallocate(selfTask, replyPort);
  95. return err;
  96. }
  97. kern_return_t rocketbootstrap_look_up(mach_port_t bp, const name_t service_name, mach_port_t *sp)
  98. {
  99. return rocketbootstrap_look_up_with_timeout(bp, service_name, sp, LIGHTMESSAGING_TIMEOUT);
  100. }
  101. static NSMutableSet *allowedNames;
  102. static volatile OSSpinLock namesLock;
  103. static void daemon_restarted_callback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
  104. {
  105. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  106. OSSpinLockLock(&namesLock);
  107. NSSet *allNames = [allowedNames copy];
  108. OSSpinLockUnlock(&namesLock);
  109. for (NSString *name in allNames) {
  110. const char *service_name = [name UTF8String];
  111. LMConnectionSendOneWay(&connection, 0, service_name, strlen(service_name));
  112. }
  113. [allNames release];
  114. [pool drain];
  115. }
  116. kern_return_t rocketbootstrap_unlock(const name_t service_name)
  117. {
  118. if (rocketbootstrap_is_passthrough())
  119. return 0;
  120. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  121. NSString *serviceNameString = [NSString stringWithUTF8String:service_name];
  122. OSSpinLockLock(&namesLock);
  123. BOOL containedName;
  124. if (!allowedNames) {
  125. allowedNames = [[NSMutableSet alloc] init];
  126. [allowedNames addObject:serviceNameString];
  127. CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), &daemon_restarted_callback, daemon_restarted_callback, CFSTR("com.rpetrich.rocketd.started"), NULL, CFNotificationSuspensionBehaviorCoalesce);
  128. containedName = NO;
  129. } else {
  130. containedName = [allowedNames containsObject:serviceNameString];
  131. if (!containedName) {
  132. [allowedNames addObject:serviceNameString];
  133. }
  134. }
  135. OSSpinLockUnlock(&namesLock);
  136. [pool drain];
  137. if (containedName) {
  138. return 0;
  139. }
  140. // Ask rocketd to unlock it for us
  141. int sandbox_result = sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_LOCAL_NAME | SANDBOX_CHECK_NO_REPORT, kRocketBootstrapUnlockService);
  142. if (sandbox_result) {
  143. [pool drain];
  144. return sandbox_result;
  145. }
  146. return LMConnectionSendOneWay(&connection, 0, service_name, strlen(service_name));
  147. }
  148. #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  149. kern_return_t rocketbootstrap_register(mach_port_t bp, name_t service_name, mach_port_t sp)
  150. {
  151. kern_return_t err = rocketbootstrap_unlock(service_name);
  152. if (err)
  153. return err;
  154. return bootstrap_register(bp, service_name, sp);
  155. }
  156. #pragma GCC diagnostic warning "-Wdeprecated-declarations"
  157. mach_msg_return_t mach_msg_server_once(boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), mach_msg_size_t max_size, mach_port_t rcv_name, mach_msg_options_t options);
  158. mach_msg_return_t (*_mach_msg_server_once)(boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), mach_msg_size_t max_size, mach_port_t rcv_name, mach_msg_options_t options);
  159. static volatile OSSpinLock server_once_lock;
  160. static boolean_t (*server_once_demux_orig)(mach_msg_header_t *, mach_msg_header_t *);
  161. static bool continue_server_once;
  162. static boolean_t new_demux(mach_msg_header_t *request, mach_msg_header_t *reply)
  163. {
  164. // Highjack ROCKETBOOTSTRAP_LOOKUP_ID from the com.apple.ReportCrash.SimulateCrash demuxer
  165. if (request->msgh_id == ROCKETBOOTSTRAP_LOOKUP_ID) {
  166. continue_server_once = true;
  167. _rocketbootstrap_lookup_query_t *lookup_message = (_rocketbootstrap_lookup_query_t *)request;
  168. // Extract service name
  169. size_t length = request->msgh_size - offsetof(_rocketbootstrap_lookup_query_t, name);
  170. if (lookup_message->name_length <= length) {
  171. length = lookup_message->name_length;
  172. }
  173. // Ask rocketd if it's unlocked
  174. LMResponseBuffer buffer;
  175. if (LMConnectionSendTwoWay(&connection, 1, &lookup_message->name[0], length, &buffer))
  176. return false;
  177. BOOL nameIsAllowed = LMResponseConsumeInteger(&buffer) != 0;
  178. // Lookup service port
  179. mach_port_t servicePort = MACH_PORT_NULL;
  180. mach_port_t selfTask = mach_task_self();
  181. kern_return_t err;
  182. if (nameIsAllowed) {
  183. mach_port_t bootstrap = MACH_PORT_NULL;
  184. err = task_get_bootstrap_port(selfTask, &bootstrap);
  185. if (!err) {
  186. char *buffer = malloc(length + 1);
  187. if (buffer) {
  188. memcpy(buffer, lookup_message->name, length);
  189. buffer[length] = '\0';
  190. err = bootstrap_look_up(bootstrap, buffer, &servicePort);
  191. free(buffer);
  192. }
  193. }
  194. }
  195. // Generate response
  196. _rocketbootstrap_lookup_response_t response;
  197. response.head.msgh_id = 0;
  198. response.head.msgh_size = (sizeof(_rocketbootstrap_lookup_response_t) + 3) & ~3;
  199. response.head.msgh_remote_port = request->msgh_remote_port;
  200. response.head.msgh_local_port = MACH_PORT_NULL;
  201. response.head.msgh_reserved = 0;
  202. response.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0);
  203. if (servicePort != MACH_PORT_NULL) {
  204. response.head.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
  205. response.body.msgh_descriptor_count = 1;
  206. response.response_port.name = servicePort;
  207. response.response_port.disposition = MACH_MSG_TYPE_COPY_SEND;
  208. response.response_port.type = MACH_MSG_PORT_DESCRIPTOR;
  209. } else {
  210. response.body.msgh_descriptor_count = 0;
  211. }
  212. // Send response
  213. err = mach_msg(&response.head, MACH_SEND_MSG, response.head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  214. if (err) {
  215. if (servicePort != MACH_PORT_NULL)
  216. mach_port_mod_refs(selfTask, servicePort, MACH_PORT_RIGHT_SEND, -1);
  217. mach_port_mod_refs(selfTask, reply->msgh_remote_port, MACH_PORT_RIGHT_SEND_ONCE, -1);
  218. }
  219. return true;
  220. }
  221. return server_once_demux_orig(request, reply);
  222. }
  223. static mach_msg_return_t $mach_msg_server_once(boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), mach_msg_size_t max_size, mach_port_t rcv_name, mach_msg_options_t options)
  224. {
  225. // Highjack com.apple.ReportCrash.SimulateCrash's use of mach_msg_server_once
  226. OSSpinLockLock(&server_once_lock);
  227. if (!server_once_demux_orig) {
  228. server_once_demux_orig = demux;
  229. demux = new_demux;
  230. } else if (server_once_demux_orig == demux) {
  231. demux = new_demux;
  232. } else {
  233. OSSpinLockUnlock(&server_once_lock);
  234. mach_msg_return_t result = _mach_msg_server_once(demux, max_size, rcv_name, options);
  235. return result;
  236. }
  237. OSSpinLockUnlock(&server_once_lock);
  238. mach_msg_return_t result;
  239. do {
  240. continue_server_once = false;
  241. result = _mach_msg_server_once(demux, max_size, rcv_name, options);
  242. } while (continue_server_once);
  243. return result;
  244. }
  245. static pid_t pid_of_process(const char *process_name)
  246. {
  247. int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
  248. size_t miblen = 4;
  249. size_t size;
  250. int st = sysctl(mib, miblen, NULL, &size, NULL, 0);
  251. struct kinfo_proc * process = NULL;
  252. struct kinfo_proc * newprocess = NULL;
  253. do {
  254. size += size / 10;
  255. newprocess = (struct kinfo_proc *)realloc(process, size);
  256. if (!newprocess) {
  257. if (process) {
  258. free(process);
  259. }
  260. return 0;
  261. }
  262. process = newprocess;
  263. st = sysctl(mib, miblen, process, &size, NULL, 0);
  264. } while (st == -1 && errno == ENOMEM);
  265. if (st == 0) {
  266. if (size % sizeof(struct kinfo_proc) == 0) {
  267. int nprocess = size / sizeof(struct kinfo_proc);
  268. if (nprocess) {
  269. for (int i = nprocess - 1; i >= 0; i--) {
  270. if (strcmp(process[i].kp_proc.p_comm, process_name) == 0) {
  271. pid_t result = process[i].kp_proc.p_pid;
  272. free(process);
  273. return result;
  274. }
  275. }
  276. }
  277. }
  278. }
  279. free(process);
  280. return 0;
  281. }
  282. static int daemon_die_queue;
  283. static CFFileDescriptorRef daemon_die_fd;
  284. static CFRunLoopSourceRef daemon_die_source;
  285. static void process_terminate_callback(CFFileDescriptorRef fd, CFOptionFlags callBackTypes, void *info);
  286. static void observe_rocketd(void)
  287. {
  288. // Force the daemon to load
  289. mach_port_t bootstrap = MACH_PORT_NULL;
  290. mach_port_t self = mach_task_self();
  291. task_get_bootstrap_port(self, &bootstrap);
  292. mach_port_t servicesPort = MACH_PORT_NULL;
  293. kern_return_t err = bootstrap_look_up(bootstrap, "com.rpetrich.rocketbootstrapd", &servicesPort);
  294. if (err) {
  295. //NSLog(@"RocketBootstrap: failed to launch rocketd!");
  296. } else {
  297. mach_port_name_t replyPort = MACH_PORT_NULL;
  298. err = mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &replyPort);
  299. if (err == 0) {
  300. LMResponseBuffer buffer;
  301. uint32_t size = LMBufferSizeForLength(0);
  302. memset(&buffer.message, 0, sizeof(LMMessage));
  303. buffer.message.head.msgh_id = 2;
  304. buffer.message.head.msgh_size = size;
  305. buffer.message.head.msgh_local_port = replyPort;
  306. buffer.message.head.msgh_reserved = 0;
  307. buffer.message.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
  308. buffer.message.head.msgh_remote_port = servicesPort;
  309. buffer.message.body.msgh_descriptor_count = 0;
  310. buffer.message.data.in_line.length = 0;
  311. err = mach_msg(&buffer.message.head, MACH_SEND_MSG | MACH_RCV_MSG | _LIGHTMESSAGING_TIMEOUT_FLAGS, size, sizeof(LMResponseBuffer), replyPort, LIGHTMESSAGING_TIMEOUT, MACH_PORT_NULL);
  312. if (err) {
  313. }
  314. // Cleanup
  315. mach_port_mod_refs(self, replyPort, MACH_PORT_RIGHT_RECEIVE, -1);
  316. }
  317. mach_port_mod_refs(self, servicesPort, MACH_PORT_RIGHT_SEND, -1);
  318. }
  319. // Find it
  320. pid_t pid = pid_of_process("rocketd");
  321. if (pid) {
  322. //NSLog(@"RocketBootstrap: rocketd found: %d", pid);
  323. daemon_die_queue = kqueue();
  324. struct kevent changes;
  325. EV_SET(&changes, pid, EVFILT_PROC, EV_ADD | EV_RECEIPT, NOTE_EXIT, 0, NULL);
  326. (void)kevent(daemon_die_queue, &changes, 1, &changes, 1, NULL);
  327. daemon_die_fd = CFFileDescriptorCreate(NULL, daemon_die_queue, true, process_terminate_callback, NULL);
  328. daemon_die_source = CFFileDescriptorCreateRunLoopSource(NULL, daemon_die_fd, 0);
  329. CFRunLoopAddSource(CFRunLoopGetCurrent(), daemon_die_source, kCFRunLoopDefaultMode);
  330. CFFileDescriptorEnableCallBacks(daemon_die_fd, kCFFileDescriptorReadCallBack);
  331. } else {
  332. NSLog(@"RocketBootstrap: unable to find rocketd!");
  333. }
  334. }
  335. static void process_terminate_callback(CFFileDescriptorRef fd, CFOptionFlags callBackTypes, void *info)
  336. {
  337. struct kevent event;
  338. (void)kevent(daemon_die_queue, NULL, 0, &event, 1, NULL);
  339. NSLog(@"RocketBootstrap: rocketd terminated: %d, relaunching", (int)(pid_t)event.ident);
  340. // Cleanup
  341. CFRunLoopRemoveSource(CFRunLoopGetCurrent(), daemon_die_source, kCFRunLoopDefaultMode);
  342. CFRelease(daemon_die_source);
  343. CFRelease(daemon_die_fd);
  344. close(daemon_die_queue);
  345. observe_rocketd();
  346. }
  347. static void SanityCheckNotificationCallback(CFUserNotificationRef userNotification, CFOptionFlags responseFlags)
  348. {
  349. }
  350. %ctor
  351. {
  352. %init();
  353. // Attach rockets when in the com.apple.ReportCrash.SimulateCrash job
  354. // (can't check in using the launchd APIs because it hates more than one checkin; this will do)
  355. const char **argv = *_NSGetArgv();
  356. if (strcmp(argv[0], "/System/Library/CoreServices/ReportCrash") == 0 && argv[1] && strcmp(argv[1], "-f") == 0) {
  357. isDaemon = YES;
  358. MSHookFunction(mach_msg_server_once, $mach_msg_server_once, (void **)&_mach_msg_server_once);
  359. } else if (strcmp(argv[0], "/System/Library/CoreServices/SpringBoard.app/SpringBoard") == 0) {
  360. if (kCFCoreFoundationVersionNumber < 847.20) {
  361. return;
  362. }
  363. // Sanity check on the SimulateCrash service
  364. mach_port_t bootstrap = MACH_PORT_NULL;
  365. mach_port_t self = mach_task_self();
  366. task_get_bootstrap_port(self, &bootstrap);
  367. mach_port_t servicesPort = MACH_PORT_NULL;
  368. kern_return_t err = bootstrap_look_up(bootstrap, "com.apple.ReportCrash.SimulateCrash", &servicesPort);
  369. //bool has_simulate_crash;
  370. if (err) {
  371. //has_simulate_crash = false;
  372. } else {
  373. mach_port_mod_refs(self, servicesPort, MACH_PORT_RIGHT_SEND, -1);
  374. //has_simulate_crash = true;
  375. //servicesPort = MACH_PORT_NULL;
  376. //err = bootstrap_look_up(bootstrap, "com.rpetrich.rocketbootstrapd", &servicesPort);
  377. }
  378. if (err == 0) {
  379. mach_port_mod_refs(self, servicesPort, MACH_PORT_RIGHT_SEND, -1);
  380. observe_rocketd();
  381. } else {
  382. const CFTypeRef keys[] = {
  383. kCFUserNotificationAlertHeaderKey,
  384. kCFUserNotificationAlertMessageKey,
  385. kCFUserNotificationDefaultButtonTitleKey,
  386. };
  387. const CFTypeRef valuesCrash[] = {
  388. CFSTR("System files missing!"),
  389. CFSTR("RocketBootstrap has detected that your SimulateCrash crash reporting daemon is missing or disabled.\nThis daemon is required for proper operation of packages that depend on RocketBootstrap."),
  390. CFSTR("OK"),
  391. };
  392. /*const CFTypeRef valuesRocket[] = {
  393. CFSTR("System files missing!"),
  394. CFSTR("RocketBootstrap has detected that your rocketbootstrap daemon is missing or disabled.\nThis daemon is required for proper operation of packages that depend on RocketBootstrap."),
  395. CFSTR("OK"),
  396. };*/
  397. CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, /*has_simulate_crash ? (const void **)valuesRocket :*/ (const void **)valuesCrash, sizeof(keys) / sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  398. SInt32 err = 0;
  399. CFUserNotificationRef notification = CFUserNotificationCreate(kCFAllocatorDefault, 0.0, kCFUserNotificationPlainAlertLevel, &err, dict);
  400. CFRunLoopSourceRef runLoopSource = CFUserNotificationCreateRunLoopSource(kCFAllocatorDefault, notification, SanityCheckNotificationCallback, 0);
  401. CFRunLoopAddSource(CFRunLoopGetMain(), runLoopSource, kCFRunLoopCommonModes);
  402. CFRelease(dict);
  403. }
  404. }
  405. }