pspawn_hook.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. #include <dlfcn.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <spawn.h>
  6. #include <sys/types.h>
  7. #include <errno.h>
  8. #include <stdlib.h>
  9. #include <sys/sysctl.h>
  10. #include <dlfcn.h>
  11. #include <sys/mman.h>
  12. #include <sys/stat.h>
  13. #include <mach/mach.h>
  14. #include <pthread.h>
  15. #include <Foundation/Foundation.h>
  16. #include "fishhook.h"
  17. #include "mach/jailbreak_daemonUser.h"
  18. #define LAUNCHD_LOG_PATH "/var/log/pspawn_hook_launchd.log"
  19. #define XPCPROXY_LOG_PATH "/var/log/pspawn_hook_xpcproxy.log"
  20. #define OTHER_LOG_PATH "/var/log/pspawn_hook_other.log"
  21. FILE *log_file;
  22. #define DEBUGLOG(fmt, args...) \
  23. do { \
  24. if (log_file == NULL) { \
  25. const char *log_path; \
  26. if (current_process == PROCESS_LAUNCHD) { \
  27. log_path = LAUNCHD_LOG_PATH; \
  28. } else if (current_process == PROCESS_XPCPROXY) { \
  29. log_path = XPCPROXY_LOG_PATH; \
  30. } else if (current_process == PROCESS_OTHER) { \
  31. log_path = OTHER_LOG_PATH; \
  32. } \
  33. log_file = fopen(log_path, "a"); \
  34. if (log_file == NULL) break; \
  35. } \
  36. time_t seconds = time(NULL); \
  37. char *time = ctime(&seconds); \
  38. fprintf(log_file, "[%.*s] ", (int)strlen(time) - 1, time); \
  39. fprintf(log_file, fmt "\n", ##args); \
  40. fflush(log_file); \
  41. } while(0);
  42. #define PROC_PIDPATHINFO_MAXSIZE (4 * MAXPATHLEN)
  43. int proc_pidpath(pid_t pid, void *buffer, uint32_t buffersize);
  44. #define JAILBREAKD_COMMAND_ENTITLE 1
  45. #define JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT 2
  46. #define JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT_FROM_XPCPROXY 3
  47. #define JAILBREAKD_COMMAND_FIXUP_SETUID 4
  48. #define FLAG_PLATFORMIZE (1 << 1)
  49. enum CurrentProcess {
  50. PROCESS_LAUNCHD,
  51. PROCESS_XPCPROXY,
  52. PROCESS_OTHER
  53. };
  54. int current_process = PROCESS_OTHER;
  55. kern_return_t bootstrap_look_up(mach_port_t port, const char *service, mach_port_t *server_port);
  56. mach_port_t jbd_port;
  57. char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
  58. #define PSPAWN_HOOK_DYLIB "/usr/lib/pspawn_hook.dylib"
  59. #define TWEAKLOADER_DYLIB "/usr/lib/TweakLoader.dylib"
  60. #define AMFID_PAYLOAD_DYLIB "/meridian/amfid_payload.dylib"
  61. #define LIBJAILBREAK_DYLIB "/usr/lib/libjailbreak.dylib"
  62. const char* xpcproxy_blacklist[] = {
  63. "com.apple.diagnosticd", // syslog
  64. "com.apple.WebKit", // O_o
  65. "MTLCompilerService", // ?_?
  66. "OTAPKIAssetTool", // h_h
  67. "cfprefsd", // o_o
  68. "FileProvider", // seems to crash from oosb r/w etc
  69. "jailbreakd", // don't inject into jbd since we'd have to call to it
  70. NULL
  71. };
  72. bool is_blacklisted(const char* proc) {
  73. const char **blacklist = xpcproxy_blacklist;
  74. while (*blacklist) {
  75. if (strstr(proc, *blacklist)) {
  76. return true;
  77. }
  78. ++blacklist;
  79. }
  80. return false;
  81. }
  82. typedef int (*pspawn_t)(pid_t *pid, const char *path, const posix_spawn_file_actions_t *file_actions, posix_spawnattr_t *attrp, const char *argv[], const char *envp[]);
  83. pspawn_t old_pspawn, old_pspawnp;
  84. int fake_posix_spawn_common(pid_t *pid, const char *path, const posix_spawn_file_actions_t *file_actions, posix_spawnattr_t *attrp, const char *argv[], const char *envp[], pspawn_t old) {
  85. DEBUGLOG("fake_posix_spawn_common: %s", path);
  86. const char *inject_me = NULL;
  87. // is the process that's being called xpcproxy?
  88. // cus we wanna inject into that bitch
  89. if (current_process == PROCESS_LAUNCHD &&
  90. strcmp(path, "/usr/libexec/xpcproxy") == 0) {
  91. inject_me = PSPAWN_HOOK_DYLIB; /* inject pspawn into xpcproxy */
  92. // let's check the blacklist, we don't wanna be
  93. // injecting into certain procs, yano
  94. const char* called_bin = argv[1];
  95. if (called_bin != NULL && is_blacklisted(called_bin)) {
  96. inject_me = NULL;
  97. DEBUGLOG("xpcproxy for '%s' which is in blacklist, not injecting", called_bin);
  98. }
  99. } else if (current_process == PROCESS_XPCPROXY) {
  100. if (strcmp(path, "/usr/libexec/amfid") == 0) { /* patch amfid */
  101. inject_me = AMFID_PAYLOAD_DYLIB;
  102. } else if (access(TWEAKLOADER_DYLIB, F_OK) == 0) { /* if twkldr is installed, load it */
  103. inject_me = TWEAKLOADER_DYLIB;
  104. }
  105. }
  106. if (inject_me == NULL) {
  107. DEBUGLOG("Nothing to inject.");
  108. return old(pid, path, file_actions, attrp, argv, envp);
  109. }
  110. DEBUGLOG("Injecting %s", inject_me);
  111. int envcount = 0;
  112. // This prints out the Env vars to the log, and grabs the position of
  113. // DYLD_INSERT_LIBRARIES, if it already exists
  114. // If not, the position will be equal to the end of the env args
  115. if (envp != NULL) {
  116. DEBUGLOG("Env: ");
  117. const char** currentenv = envp;
  118. while (*currentenv != NULL) {
  119. DEBUGLOG("\t%s", *currentenv);
  120. if (strstr(*currentenv, "DYLD_INSERT_LIBRARIES") == NULL) {
  121. envcount++;
  122. }
  123. currentenv++;
  124. }
  125. }
  126. char const** newenvp = (char const **)malloc((envcount + 2) * sizeof(char **));
  127. int j = 0;
  128. for (int i = 0; i < envcount; i++) {
  129. if (strstr(envp[j], "DYLD_INSERT_LIBRARIES") != NULL) {
  130. continue;
  131. }
  132. newenvp[i] = envp[j];
  133. j++;
  134. }
  135. char *envp_inject = (char *)malloc(strlen("DYLD_INSERT_LIBRARIES=") + strlen(inject_me) + 1);
  136. envp_inject[0] = '\0';
  137. strcat(envp_inject, "DYLD_INSERT_LIBRARIES=");
  138. strcat(envp_inject, inject_me);
  139. newenvp[j] = envp_inject;
  140. newenvp[j + 1] = NULL;
  141. DEBUGLOG("New Env:");
  142. const char** currentenv = newenvp;
  143. while (*currentenv != NULL) {
  144. DEBUGLOG("\t%s", *currentenv);
  145. currentenv++;
  146. }
  147. posix_spawnattr_t attr;
  148. posix_spawnattr_t *newattrp = &attr;
  149. if (attrp) { /* add to existing attribs */
  150. newattrp = attrp;
  151. short flags;
  152. posix_spawnattr_getflags(attrp, &flags);
  153. flags |= POSIX_SPAWN_START_SUSPENDED;
  154. posix_spawnattr_setflags(attrp, flags);
  155. } else { /* set new attribs */
  156. posix_spawnattr_init(&attr);
  157. posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED);
  158. }
  159. int origret;
  160. if (current_process == PROCESS_LAUNCHD) {
  161. int gotpid;
  162. origret = old(&gotpid, path, file_actions, newattrp, argv, newenvp);
  163. if (origret == 0) {
  164. if (pid != NULL) *pid = gotpid;
  165. kern_return_t ret = jbd_call(jbd_port, JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT, gotpid);
  166. if (ret != KERN_SUCCESS) {
  167. DEBUGLOG("err: got %x from jbd_call(sigcont, %d)", ret, gotpid);
  168. }
  169. }
  170. } else if (current_process == PROCESS_XPCPROXY) {
  171. kern_return_t ret = jbd_call(jbd_port, JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT_FROM_XPCPROXY, getpid());
  172. if (ret != KERN_SUCCESS) {
  173. DEBUGLOG("err: got %x from jbd_call(xpproxy, %d)", ret, getpid());
  174. }
  175. origret = old(pid, path, file_actions, newattrp, argv, newenvp);
  176. }
  177. return origret;
  178. }
  179. 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[]) {
  180. return fake_posix_spawn_common(pid, file, file_actions, attrp, argv, envp, old_pspawn);
  181. }
  182. 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[]) {
  183. return fake_posix_spawn_common(pid, file, file_actions, attrp, argv, envp, old_pspawnp);
  184. }
  185. void rebind_pspawns(void) {
  186. struct rebinding rebindings[] = {
  187. { "posix_spawn", (void *)fake_posix_spawn, (void **)&old_pspawn },
  188. { "posix_spawnp", (void *)fake_posix_spawnp, (void **)&old_pspawnp }
  189. };
  190. rebind_symbols(rebindings, 2);
  191. }
  192. void *thd_func(void *arg) {
  193. DEBUGLOG("in a new thread!");
  194. rebind_pspawns();
  195. return NULL;
  196. }
  197. __attribute__ ((constructor))
  198. static void ctor(void) {
  199. bzero(pathbuf, sizeof(pathbuf));
  200. proc_pidpath(getpid(), pathbuf, sizeof(pathbuf));
  201. if (getpid() == 1) {
  202. current_process = PROCESS_LAUNCHD;
  203. } else if (strcmp(pathbuf, "/usr/libexec/xpcproxy") == 0) {
  204. current_process = PROCESS_XPCPROXY;
  205. } else {
  206. current_process = PROCESS_OTHER;
  207. }
  208. DEBUGLOG("========================");
  209. DEBUGLOG("hello from pid %d", getpid());
  210. DEBUGLOG("my path: %s", pathbuf);
  211. if (current_process == PROCESS_LAUNCHD) {
  212. if (host_get_special_port(mach_host_self(), HOST_LOCAL_NODE, 15, &jbd_port)) {
  213. DEBUGLOG("Can't get hsp15 :(");
  214. return;
  215. }
  216. DEBUGLOG("got jbd port: %x", jbd_port);
  217. pthread_t thd;
  218. pthread_create(&thd, NULL, thd_func, NULL);
  219. return;
  220. }
  221. if (bootstrap_look_up(bootstrap_port, "zone.sparkes.jailbreakd", &jbd_port)) {
  222. DEBUGLOG("Can't get bootstrap port :(");
  223. return;
  224. }
  225. DEBUGLOG("got jbd port: %x", jbd_port);
  226. // pspawn is usually only ever injected into either launchd,
  227. // or xpcproxy. this is here in case you want to manually inject it into
  228. // another process, in order to have it call to jbd. consider this
  229. // testing-only, and should not be used in production code.
  230. // example (in shell): "> DYLD_INSERT_LIBRARIES=/usr/lib/libjailbreak.dylib ./cydo"
  231. // this will have cydo call to jbd in order to platformize, used in testing
  232. if (current_process == PROCESS_OTHER) {
  233. if (access(LIBJAILBREAK_DYLIB, F_OK) == 0) {
  234. void *handle = dlopen(LIBJAILBREAK_DYLIB, RTLD_LAZY);
  235. if (handle) {
  236. typedef int (*fix_ent)(pid_t pid, uint32_t flags);
  237. fix_ent fixentptr = (fix_ent)dlsym(handle, "jb_oneshot_entitle_now");
  238. fixentptr(getpid(), FLAG_PLATFORMIZE);
  239. }
  240. }
  241. }
  242. rebind_pspawns();
  243. }