pspawn_payload.m 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. #include <dlfcn.h>
  2. #include <stdio.h>
  3. #include <mach/mach.h>
  4. #include <mach/error.h>
  5. #include <mach/message.h>
  6. #include <string.h>
  7. #include <unistd.h>
  8. #include <spawn.h>
  9. #include <sys/types.h>
  10. #include <errno.h>
  11. #include <stdlib.h>
  12. #include <sys/sysctl.h>
  13. #include <dlfcn.h>
  14. #include <sys/mman.h>
  15. #include <sys/stat.h>
  16. #include <pthread.h>
  17. #include <Foundation/Foundation.h>
  18. #include "fishhook.h"
  19. #include "mach/jailbreak_daemonUser.h"
  20. int file_exist(const char *filename) {
  21. struct stat buffer;
  22. int r = stat(filename, &buffer);
  23. return (r == 0);
  24. }
  25. #define PSPAWN_PAYLOAD_DEBUG 1
  26. //#ifdef PSPAWN_PAYLOAD_DEBUG
  27. #define LAUNCHD_LOG_PATH "/var/log/pspawn_payload_launchd.log"
  28. // XXX multiple xpcproxies opening same file
  29. // XXX not closing logfile before spawn
  30. #define XPCPROXY_LOG_PATH "/var/log//pspawn_payload_xpcproxy.log"
  31. FILE *log_file;
  32. #define DEBUGLOG(fmt, args...)\
  33. do {\
  34. if (log_file == NULL) {\
  35. log_file = fopen((current_process == PROCESS_LAUNCHD) ? LAUNCHD_LOG_PATH : XPCPROXY_LOG_PATH, "a"); \
  36. if (log_file == NULL) break; \
  37. } \
  38. fprintf(log_file, fmt "\n", ##args); \
  39. fflush(log_file); \
  40. } while(0)
  41. /*
  42. #else
  43. #define DEBUGLOG(fmt, args...)
  44. #endif
  45. */
  46. #define PSPAWN_PAYLOAD_DYLIB "/electra/pspawn_payload.dylib"
  47. #define AMFID_PAYLOAD_DYLIB "/electra/amfid_payload.dylib"
  48. #define SBINJECT_PAYLOAD_DYLIB "/usr/lib/TweakInject.dylib"
  49. // since this dylib should only be loaded into launchd and xpcproxy
  50. // it's safe to assume that we're in xpcproxy if getpid() != 1
  51. enum currentprocess {
  52. PROCESS_LAUNCHD,
  53. PROCESS_XPCPROXY,
  54. };
  55. int current_process = PROCESS_XPCPROXY;
  56. #define JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT 2
  57. #define JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT_FROM_XPCPROXY 3
  58. kern_return_t bootstrap_look_up(mach_port_t port, const char *service, mach_port_t *server_port);
  59. mach_port_t jbd_port;
  60. const char* xpcproxy_blacklist[] = {
  61. "com.apple.diagnosticd", // syslog
  62. "MTLCompilerService", // ?_?
  63. "mapspushd", // stupid Apple Maps
  64. "OTAPKIAssetTool", // h_h
  65. "cfprefsd", // o_o
  66. "jailbreakd", // don't inject into jbd since we'd have to call to it
  67. //"com.nito.nitoTV4",
  68. //"com.nito.nitoTV4.nitoTVTopShelf",
  69. /*
  70. "PineBoard", // for now
  71. "com.apple.PineBoard",
  72. "com.apple.HeadBoard",
  73. "HeadBoard",
  74. "com.firecore.infuse.pro",
  75. "com.apple.accessibility.AccessibilityUIServer",
  76. "com.apple.TVAirPlay",
  77. "com.apple.mediaserverd",
  78. "com.apple.SiriViewService",
  79. "com.apple.syncdefaultsd",
  80. "com.apple.TVWatchList",
  81. "com.apple.TVAppStore",
  82. "com.apple.TVIdleScreen",
  83. "com.apple.TVSettings",
  84. */
  85. NULL
  86. };
  87. typedef int (*pspawn_t)(pid_t * pid, const char* path, const posix_spawn_file_actions_t *file_actions, posix_spawnattr_t *attrp, char const* argv[], const char* envp[]);
  88. pspawn_t old_pspawn, old_pspawnp;
  89. int fake_posix_spawn_common(pid_t * pid, const char* path, const posix_spawn_file_actions_t *file_actions, posix_spawnattr_t *attrp, char const* argv[], const char* envp[], pspawn_t old) {
  90. DEBUGLOG("We got called (fake_posix_spawn)! %s", path);
  91. const char *inject_me = NULL;
  92. if (current_process == PROCESS_LAUNCHD) {
  93. if (strcmp(path, "/usr/libexec/xpcproxy") == 0) {
  94. inject_me = PSPAWN_PAYLOAD_DYLIB;
  95. const char* startd = argv[1];
  96. if (startd != NULL) {
  97. const char **blacklist = xpcproxy_blacklist;
  98. while (*blacklist) {
  99. if (strstr(startd, *blacklist)) {
  100. DEBUGLOG("xpcproxy for '%s' which is in blacklist, not injecting", startd);
  101. inject_me = NULL;
  102. break;
  103. }
  104. ++blacklist;
  105. }
  106. }
  107. }
  108. } else if (current_process == PROCESS_XPCPROXY) {
  109. // XXX inject both SBInject & amfid payload into amfid?
  110. // note: DYLD_INSERT_LIBRARIES=libfoo1.dylib:libfoo2.dylib
  111. if (strcmp(path, "/usr/libexec/amfid") == 0) {
  112. DEBUGLOG("Starting amfid -- special handling");
  113. inject_me = AMFID_PAYLOAD_DYLIB;
  114. } else {
  115. inject_me = SBINJECT_PAYLOAD_DYLIB;
  116. }
  117. }
  118. // XXX log different err on inject_me == NULL and nonexistent inject_me
  119. if (inject_me == NULL || !file_exist(inject_me)) {
  120. DEBUGLOG("Nothing to inject");
  121. return old(pid, path, file_actions, attrp, argv, envp);
  122. }
  123. DEBUGLOG("Injecting %s into %s", inject_me, path);
  124. #ifdef PSPAWN_PAYLOAD_DEBUG
  125. if (argv != NULL){
  126. DEBUGLOG("Args: ");
  127. const char** currentarg = argv;
  128. while (*currentarg != NULL){
  129. DEBUGLOG("\t%s", *currentarg);
  130. currentarg++;
  131. }
  132. }
  133. #endif
  134. int envcount = 0;
  135. if (envp != NULL){
  136. DEBUGLOG("Env: ");
  137. const char** currentenv = envp;
  138. while (*currentenv != NULL){
  139. DEBUGLOG("\t%s", *currentenv);
  140. if (strstr(*currentenv, "DYLD_INSERT_LIBRARIES") == NULL) {
  141. envcount++;
  142. }
  143. currentenv++;
  144. }
  145. }
  146. char const** newenvp = malloc((envcount+2) * sizeof(char **));
  147. int j = 0;
  148. for (int i = 0; i < envcount; i++){
  149. if (strstr(envp[j], "DYLD_INSERT_LIBRARIES") != NULL){
  150. continue;
  151. }
  152. newenvp[i] = envp[j];
  153. j++;
  154. }
  155. char *envp_inject = malloc(strlen("DYLD_INSERT_LIBRARIES=") + strlen(inject_me) + 1);
  156. envp_inject[0] = '\0';
  157. strcat(envp_inject, "DYLD_INSERT_LIBRARIES=");
  158. strcat(envp_inject, inject_me);
  159. newenvp[j] = envp_inject;
  160. newenvp[j+1] = NULL;
  161. #if PSPAWN_PAYLOAD_DEBUG
  162. DEBUGLOG("New Env:");
  163. const char** currentenv = newenvp;
  164. while (*currentenv != NULL){
  165. DEBUGLOG("\t%s", *currentenv);
  166. currentenv++;
  167. }
  168. #endif
  169. posix_spawnattr_t attr;
  170. posix_spawnattr_t *newattrp = &attr;
  171. if (attrp) {
  172. DEBUGLOG("attrp!\n");
  173. newattrp = attrp;
  174. short flags;
  175. posix_spawnattr_getflags(attrp, &flags);
  176. flags |= POSIX_SPAWN_START_SUSPENDED;
  177. posix_spawnattr_setflags(attrp, flags);
  178. } else {
  179. DEBUGLOG("attrp else\n");
  180. posix_spawnattr_init(&attr);
  181. posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED);
  182. }
  183. int origret;
  184. #define FLAG_ATTRIBUTE_XPCPROXY (1 << 17)
  185. if (current_process == PROCESS_XPCPROXY) {
  186. // dont leak logging fd into execd process
  187. DEBUGLOG("dont leak logging fd into execd process\n");
  188. #ifdef PSPAWN_PAYLOAD_DEBUG
  189. if (log_file != NULL) {
  190. fclose(log_file);
  191. log_file = NULL;
  192. }
  193. #endif
  194. jbd_call(jbd_port, JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT_FROM_XPCPROXY, getpid());
  195. // dont leak jbd fd into execd process
  196. origret = old(pid, path, file_actions, newattrp, argv, newenvp);
  197. DEBUGLOG("origret %i for xpcproxy\n", origret);
  198. } else {
  199. int gotpid;
  200. origret = old(&gotpid, path, file_actions, newattrp, argv, newenvp);
  201. DEBUGLOG("origret %i for not xpcproxy\n", origret);
  202. if (origret == 0) {
  203. if (pid != NULL) *pid = gotpid;
  204. DEBUGLOG("we in here\n");
  205. jbd_call(jbd_port, JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT, gotpid);
  206. }
  207. }
  208. return origret;
  209. }
  210. int fake_posix_spawn(pid_t * pid, const char* file, const posix_spawn_file_actions_t *file_actions, posix_spawnattr_t *attrp, const char* argv[], const char* envp[]) {
  211. return fake_posix_spawn_common(pid, file, file_actions, attrp, argv, envp, old_pspawn);
  212. }
  213. int fake_posix_spawnp(pid_t * pid, const char* file, const posix_spawn_file_actions_t *file_actions, posix_spawnattr_t *attrp, const char* argv[], const char* envp[]) {
  214. return fake_posix_spawn_common(pid, file, file_actions, attrp, argv, envp, old_pspawnp);
  215. }
  216. void rebind_pspawns(void) {
  217. struct rebinding rebindings[] = {
  218. {"posix_spawn", (void *)fake_posix_spawn, (void **)&old_pspawn},
  219. {"posix_spawnp", (void *)fake_posix_spawnp, (void **)&old_pspawnp},
  220. };
  221. rebind_symbols(rebindings, 2);
  222. }
  223. void* thd_func(void* arg){
  224. NSLog(@"In a new thread!");
  225. rebind_pspawns();
  226. return NULL;
  227. }
  228. __attribute__ ((constructor))
  229. static void ctor(void) {
  230. if (getpid() == 1) {
  231. if (host_get_special_port(mach_host_self(), HOST_LOCAL_NODE, 15, &jbd_port)) {
  232. DEBUGLOG("Can't get hgsp15 :(");
  233. return;
  234. }
  235. DEBUGLOG("Got jbd port: %llx", jbd_port);
  236. current_process = PROCESS_LAUNCHD;
  237. pthread_t thd;
  238. pthread_create(&thd, NULL, thd_func, NULL);
  239. } else {
  240. if (bootstrap_look_up(bootstrap_port, "org.coolstar.jailbreakd", &jbd_port)) {
  241. DEBUGLOG("Can't get bootstrap port :(");
  242. return;
  243. }
  244. DEBUGLOG("Got jbd port: %llx", jbd_port);
  245. current_process = PROCESS_XPCPROXY;
  246. rebind_pspawns();
  247. }
  248. }