SBInject.x 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. #import <dlfcn.h>
  2. #import <objc/runtime.h>
  3. #import <stdlib.h>
  4. #import <stdio.h>
  5. #import <unistd.h>
  6. #import <pthread.h>
  7. #import <sys/stat.h>
  8. #import <sys/types.h>
  9. #import <CommonCrypto/CommonDigest.h>
  10. #include <syslog.h>
  11. #define PROC_PIDPATHINFO_MAXSIZE (1024)
  12. int proc_pidpath(pid_t pid, void *buffer, uint32_t buffersize);
  13. #define dylibDir @"/Library/MobileSubstrate/DynamicLibraries/"
  14. NSArray *sbinjectGenerateDylibList() {
  15. NSString *processName = [[NSProcessInfo processInfo] processName];
  16. // launchctl, amfid you are special cases
  17. if ([processName isEqualToString:@"launchctl"]) {
  18. HBLogInfo(@"launchctl exit");
  19. return nil;
  20. }
  21. if ([processName isEqualToString:@"amfid"]) {
  22. HBLogInfo(@"amfid exit");
  23. return nil;
  24. }
  25. // Create an array containing all the filenames in dylibDir (/opt/simject)
  26. NSError *e = nil;
  27. NSArray *dylibDirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dylibDir error:&e];
  28. if (e) {
  29. return nil;
  30. }
  31. // Read current bundle identifier
  32. NSString *bundleIdentifier = NSBundle.mainBundle.bundleIdentifier;
  33. NSLog(@"bundleID: %@", bundleIdentifier);
  34. // We're only interested in the plist files
  35. NSArray *plists = [dylibDirContents filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF ENDSWITH %@", @"plist"]];
  36. // Create an empty mutable array that will contain a list of dylib paths to be injected into the target process
  37. NSMutableArray *dylibsToInject = [NSMutableArray array];
  38. // Loop through the list of plists
  39. for (NSString *plist in plists) {
  40. // We'll want to deal with absolute paths, so append the filename to dylibDir
  41. NSString *plistPath = [dylibDir stringByAppendingPathComponent:plist];
  42. NSDictionary *filter = [NSDictionary dictionaryWithContentsOfFile:plistPath];
  43. // This boolean indicates whether or not the dylib has already been injected
  44. BOOL isInjected = NO;
  45. // If supported iOS versions are specified within the plist, we check those first
  46. NSArray *supportedVersions = filter[@"CoreFoundationVersion"];
  47. if (supportedVersions) {
  48. if (supportedVersions.count != 1 && supportedVersions.count != 2) {
  49. continue; // Supported versions are in the wrong format, we should skip
  50. }
  51. if (supportedVersions.count == 1 && [supportedVersions[0] doubleValue] > kCFCoreFoundationVersionNumber) {
  52. continue; // Doesn't meet lower bound
  53. }
  54. if (supportedVersions.count == 2 && ([supportedVersions[0] doubleValue] > kCFCoreFoundationVersionNumber || [supportedVersions[1] doubleValue] <= kCFCoreFoundationVersionNumber)) {
  55. continue; // Outside bounds
  56. }
  57. }
  58. // Decide whether or not to load the dylib based on the Bundles values
  59. NSArray *injectBundles = filter[@"Filter"][@"Bundles"];
  60. //NSLog(@"bundles: %@", injectBundles);
  61. if ([injectBundles containsObject:bundleIdentifier]){
  62. //NSLog(@"inject bundles contains object: %@", bundleIdentifier);
  63. [dylibsToInject addObject:[[plistPath stringByDeletingPathExtension] stringByAppendingString:@".dylib"]];
  64. isInjected = YES;
  65. break;
  66. }
  67. /*
  68. for (NSString *entry in filter[@"Filter"][@"Bundles"]) {
  69. // Check to see whether or not this bundle is actually loaded in this application or not
  70. if (!CFBundleGetBundleWithIdentifier((CFStringRef)entry)) {
  71. // If not, skip it
  72. continue;
  73. }
  74. [dylibsToInject addObject:[[plistPath stringByDeletingPathExtension] stringByAppendingString:@".dylib"]];
  75. isInjected = YES;
  76. break;
  77. }
  78. */
  79. if (!isInjected) {
  80. // Decide whether or not to load the dylib based on the Executables values
  81. for (NSString *process in filter[@"Filter"][@"Executables"]) {
  82. if ([process isEqualToString:processName]) {
  83. [dylibsToInject addObject:[[plistPath stringByDeletingPathExtension] stringByAppendingString:@".dylib"]];
  84. isInjected = YES;
  85. break;
  86. }
  87. }
  88. }
  89. if (!isInjected) {
  90. // Decide whether or not to load the dylib based on the Classes values
  91. for (NSString *clazz in filter[@"Filter"][@"Classes"]) {
  92. // Also check if this class is loaded in this application or not
  93. if (!NSClassFromString(clazz)) {
  94. // This class couldn't be loaded, skip
  95. continue;
  96. }
  97. // It's fine to add this dylib at this point
  98. [dylibsToInject addObject:[[plistPath stringByDeletingPathExtension] stringByAppendingString:@".dylib"]];
  99. isInjected = YES;
  100. break;
  101. }
  102. }
  103. }
  104. [dylibsToInject sortUsingSelector:@selector(caseInsensitiveCompare:)];
  105. return dylibsToInject;
  106. }
  107. int file_exist(char *filename) {
  108. struct stat buffer;
  109. int r = stat(filename, &buffer);
  110. return (r == 0);
  111. }
  112. @interface SpringBoard : NSObject
  113. - (BOOL)launchApplicationWithIdentifier:(NSString *)identifier suspended:(BOOL)suspended;
  114. - (id)sharedApplication;
  115. @end
  116. %group SafeMode
  117. %hook FBApplicationInfo
  118. - (NSDictionary *)environmentVariables {
  119. NSDictionary *originalVariables = %orig;
  120. NSMutableDictionary *newVariables = [originalVariables mutableCopy];
  121. [newVariables setObject:@1 forKey:@"_SafeMode"];
  122. return [newVariables autorelease];
  123. }
  124. %end
  125. /*
  126. %hook SBLockScreenManager
  127. -(BOOL)_finishUIUnlockFromSource:(int)arg1 withOptions:(id)arg2 {
  128. BOOL ret = %orig;
  129. [(SpringBoard *)[%c(UIApplication) sharedApplication] launchApplicationWithIdentifier:@"org.coolstar.SafeMode" suspended:NO];
  130. return ret;
  131. }
  132. // Necessary on iPhone X to show after swipe unlock gesture
  133. -(void)lockScreenViewControllerDidDismiss {
  134. %orig;
  135. [(SpringBoard *)[%c(UIApplication) sharedApplication] launchApplicationWithIdentifier:@"org.coolstar.SafeMode" suspended:NO];
  136. }
  137. %end
  138. */
  139. %end
  140. static BOOL isSpringBoardOrBackboard = NO;
  141. static NSString *processHash = @"";
  142. BOOL safeMode = false;
  143. void SpringBoardSigHandler(int signo, siginfo_t *info, void *uap){
  144. if (isSpringBoardOrBackboard){
  145. FILE *f = fopen("/var/mobile/Library/.sbinjectSafeMode", "w");
  146. fprintf(f, "Hello World\n");
  147. fclose(f);
  148. }
  149. FILE *f = fopen([[NSString stringWithFormat:@"%@/.safeMode-%@", NSTemporaryDirectory(), processHash] UTF8String], "w");
  150. fprintf(f, "Hello World!\n");
  151. fclose(f);
  152. raise(signo);
  153. }
  154. __attribute__ ((constructor))
  155. static void ctor(void) {
  156. @autoreleasepool {
  157. if (NSBundle.mainBundle.bundleIdentifier == nil || ![NSBundle.mainBundle.bundleIdentifier isEqualToString:@"org.coolstar.SafeMode"]){
  158. char pathbuf[PROC_PIDPATHINFO_MAXSIZE] = {0};
  159. int ret = proc_pidpath(getpid(), pathbuf, sizeof(pathbuf));
  160. if (ret > 0){
  161. uint8_t digest[CC_SHA1_DIGEST_LENGTH];
  162. CC_SHA1(pathbuf, ret, digest);
  163. NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
  164. for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
  165. {
  166. [output appendFormat:@"%02x", digest[i]];
  167. }
  168. processHash = [[NSString alloc] initWithString:output];
  169. }
  170. safeMode = false;
  171. NSString *processName = [[NSProcessInfo processInfo] processName];
  172. struct sigaction action;
  173. memset(&action, 0, sizeof(action));
  174. action.sa_sigaction = &SpringBoardSigHandler;
  175. action.sa_flags = SA_SIGINFO | SA_RESETHAND;
  176. sigemptyset(&action.sa_mask);
  177. sigaction(SIGQUIT, &action, NULL);
  178. sigaction(SIGILL, &action, NULL);
  179. sigaction(SIGTRAP, &action, NULL);
  180. sigaction(SIGABRT, &action, NULL);
  181. sigaction(SIGEMT, &action, NULL);
  182. sigaction(SIGFPE, &action, NULL);
  183. sigaction(SIGBUS, &action, NULL);
  184. sigaction(SIGSEGV, &action, NULL);
  185. sigaction(SIGSYS, &action, NULL);
  186. if ([processName isEqualToString:@"backboardd"] || [NSBundle.mainBundle.bundleIdentifier isEqualToString:@"com.apple.springboard"]){
  187. isSpringBoardOrBackboard = YES;
  188. if (file_exist("/var/mobile/Library/.sbinjectSafeMode")){
  189. safeMode = true;
  190. if ([NSBundle.mainBundle.bundleIdentifier isEqualToString:@"com.apple.springboard"]){
  191. unlink("/var/mobile/Library/.sbinjectSafeMode");
  192. NSLog(@"Entering Safe Mode!");
  193. %init(SafeMode);
  194. }
  195. }
  196. }
  197. if ([NSBundle.mainBundle.bundleIdentifier isEqualToString:@"com.apple.springboard"]){
  198. dlopen("/usr/lib/TweakInjectMapsCheck.dylib", RTLD_LAZY | RTLD_GLOBAL);
  199. }
  200. const char *safeModeByProcPath = [[NSString stringWithFormat:@"%@/.safeMode-%@", NSTemporaryDirectory(), processHash] UTF8String];
  201. if (file_exist((char *)safeModeByProcPath)){
  202. safeMode = true;
  203. unlink(safeModeByProcPath);
  204. }
  205. if (getenv("_MSSafeMode")){
  206. if (strcmp(getenv("_MSSafeMode"),"1") == 0){
  207. safeMode = true;
  208. }
  209. }
  210. if (getenv("_SafeMode")){
  211. if (strcmp(getenv("_SafeMode"),"1") == 0){
  212. safeMode = true;
  213. }
  214. }
  215. if (getenv("_SubstituteSafeMode")){
  216. if (strcmp(getenv("_SubstituteSafeMode"),"1") == 0){
  217. safeMode = true;
  218. }
  219. }
  220. if (!safeMode){
  221. //HBLogInfo(@"In bundle: %@", NSBundle.mainBundle.bundleIdentifier);
  222. NSArray *theList = sbinjectGenerateDylibList();
  223. //HBLogInfo(@"theList: %@", theList);
  224. for (NSString *dylib in theList) {
  225. NSLog(@"Injecting %@", dylib);
  226. void *dl = dlopen([dylib UTF8String], RTLD_LAZY | RTLD_GLOBAL);
  227. if (dl == NULL) {
  228. NSLog(@"Injection failed: '%s'", dlerror());
  229. }
  230. }
  231. } else {
  232. NSLog(@"TweakInject: Entering Safe Mode!");
  233. }
  234. }
  235. }
  236. }