SBInject.x 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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. //NSLog(@"plist: %@", plist);
  42. NSString *plistPath = [dylibDir stringByAppendingPathComponent:plist];
  43. NSDictionary *filter = [NSDictionary dictionaryWithContentsOfFile:plistPath];
  44. // This boolean indicates whether or not the dylib has already been injected
  45. BOOL isInjected = NO;
  46. // If supported iOS versions are specified within the plist, we check those first
  47. NSArray *supportedVersions = filter[@"CoreFoundationVersion"];
  48. if (supportedVersions) {
  49. if (supportedVersions.count != 1 && supportedVersions.count != 2) {
  50. continue; // Supported versions are in the wrong format, we should skip
  51. }
  52. if (supportedVersions.count == 1 && [supportedVersions[0] doubleValue] > kCFCoreFoundationVersionNumber) {
  53. continue; // Doesn't meet lower bound
  54. }
  55. if (supportedVersions.count == 2 && ([supportedVersions[0] doubleValue] > kCFCoreFoundationVersionNumber || [supportedVersions[1] doubleValue] <= kCFCoreFoundationVersionNumber)) {
  56. continue; // Outside bounds
  57. }
  58. }
  59. // Decide whether or not to load the dylib based on the Bundles values
  60. NSArray *injectBundles = filter[@"Filter"][@"Bundles"];
  61. //NSLog(@"injectBundles: %@ vs %@", injectBundles, bundleIdentifier);
  62. if ([injectBundles containsObject:bundleIdentifier]){
  63. //NSLog(@"inject bundles contains object: %@", bundleIdentifier);
  64. [dylibsToInject addObject:[[plistPath stringByDeletingPathExtension] stringByAppendingString:@".dylib"]];
  65. isInjected = YES;
  66. continue;
  67. }
  68. /*
  69. for (NSString *entry in filter[@"Filter"][@"Bundles"]) {
  70. // Check to see whether or not this bundle is actually loaded in this application or not
  71. if (!CFBundleGetBundleWithIdentifier((CFStringRef)entry)) {
  72. // If not, skip it
  73. continue;
  74. }
  75. [dylibsToInject addObject:[[plistPath stringByDeletingPathExtension] stringByAppendingString:@".dylib"]];
  76. isInjected = YES;
  77. //NSLog(@"break");
  78. continue;
  79. }
  80. */
  81. if (!isInjected) {
  82. // Decide whether or not to load the dylib based on the Executables values
  83. for (NSString *process in filter[@"Filter"][@"Executables"]) {
  84. if ([process isEqualToString:processName]) {
  85. [dylibsToInject addObject:[[plistPath stringByDeletingPathExtension] stringByAppendingString:@".dylib"]];
  86. isInjected = YES;
  87. continue;
  88. }
  89. }
  90. }
  91. if (!isInjected) {
  92. // Decide whether or not to load the dylib based on the Classes values
  93. for (NSString *clazz in filter[@"Filter"][@"Classes"]) {
  94. // Also check if this class is loaded in this application or not
  95. if (!NSClassFromString(clazz)) {
  96. // This class couldn't be loaded, skip
  97. continue;
  98. }
  99. // It's fine to add this dylib at this point
  100. [dylibsToInject addObject:[[plistPath stringByDeletingPathExtension] stringByAppendingString:@".dylib"]];
  101. isInjected = YES;
  102. continue;
  103. }
  104. }
  105. }
  106. [dylibsToInject sortUsingSelector:@selector(caseInsensitiveCompare:)];
  107. return dylibsToInject;
  108. }
  109. int file_exist(char *filename) {
  110. struct stat buffer;
  111. int r = stat(filename, &buffer);
  112. return (r == 0);
  113. }
  114. @interface SpringBoard : NSObject
  115. - (BOOL)launchApplicationWithIdentifier:(NSString *)identifier suspended:(BOOL)suspended;
  116. - (id)sharedApplication;
  117. @end
  118. %group SafeMode
  119. %hook FBApplicationInfo
  120. - (NSDictionary *)environmentVariables {
  121. NSDictionary *originalVariables = %orig;
  122. NSMutableDictionary *newVariables = [originalVariables mutableCopy];
  123. [newVariables setObject:@1 forKey:@"_SafeMode"];
  124. return [newVariables autorelease];
  125. }
  126. %end
  127. /*
  128. %hook SBLockScreenManager
  129. -(BOOL)_finishUIUnlockFromSource:(int)arg1 withOptions:(id)arg2 {
  130. BOOL ret = %orig;
  131. [(SpringBoard *)[%c(UIApplication) sharedApplication] launchApplicationWithIdentifier:@"org.coolstar.SafeMode" suspended:NO];
  132. return ret;
  133. }
  134. // Necessary on iPhone X to show after swipe unlock gesture
  135. -(void)lockScreenViewControllerDidDismiss {
  136. %orig;
  137. [(SpringBoard *)[%c(UIApplication) sharedApplication] launchApplicationWithIdentifier:@"org.coolstar.SafeMode" suspended:NO];
  138. }
  139. %end
  140. */
  141. %end
  142. static BOOL isSpringBoardOrBackboard = NO;
  143. static NSString *processHash = @"";
  144. BOOL safeMode = false;
  145. void SpringBoardSigHandler(int signo, siginfo_t *info, void *uap){
  146. if (isSpringBoardOrBackboard){
  147. FILE *f = fopen("/var/mobile/Library/.sbinjectSafeMode", "w");
  148. fprintf(f, "Hello World\n");
  149. fclose(f);
  150. }
  151. FILE *f = fopen([[NSString stringWithFormat:@"%@/.safeMode-%@", NSTemporaryDirectory(), processHash] UTF8String], "w");
  152. fprintf(f, "Hello World!\n");
  153. fclose(f);
  154. raise(signo);
  155. }
  156. __attribute__ ((constructor))
  157. static void ctor(void) {
  158. @autoreleasepool {
  159. if (NSBundle.mainBundle.bundleIdentifier == nil || ![NSBundle.mainBundle.bundleIdentifier isEqualToString:@"org.coolstar.SafeMode"]){
  160. char pathbuf[PROC_PIDPATHINFO_MAXSIZE] = {0};
  161. int ret = proc_pidpath(getpid(), pathbuf, sizeof(pathbuf));
  162. if (ret > 0){
  163. uint8_t digest[CC_SHA1_DIGEST_LENGTH];
  164. CC_SHA1(pathbuf, ret, digest);
  165. NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
  166. for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
  167. {
  168. [output appendFormat:@"%02x", digest[i]];
  169. }
  170. processHash = [[NSString alloc] initWithString:output];
  171. }
  172. safeMode = false;
  173. NSString *processName = [[NSProcessInfo processInfo] processName];
  174. struct sigaction action;
  175. memset(&action, 0, sizeof(action));
  176. action.sa_sigaction = &SpringBoardSigHandler;
  177. action.sa_flags = SA_SIGINFO | SA_RESETHAND;
  178. sigemptyset(&action.sa_mask);
  179. sigaction(SIGQUIT, &action, NULL);
  180. sigaction(SIGILL, &action, NULL);
  181. sigaction(SIGTRAP, &action, NULL);
  182. sigaction(SIGABRT, &action, NULL);
  183. sigaction(SIGEMT, &action, NULL);
  184. sigaction(SIGFPE, &action, NULL);
  185. sigaction(SIGBUS, &action, NULL);
  186. sigaction(SIGSEGV, &action, NULL);
  187. sigaction(SIGSYS, &action, NULL);
  188. if ([processName isEqualToString:@"backboardd"] || [NSBundle.mainBundle.bundleIdentifier isEqualToString:@"com.apple.springboard"]){
  189. isSpringBoardOrBackboard = YES;
  190. if (file_exist("/var/mobile/Library/.sbinjectSafeMode")){
  191. safeMode = true;
  192. if ([NSBundle.mainBundle.bundleIdentifier isEqualToString:@"com.apple.springboard"]){
  193. unlink("/var/mobile/Library/.sbinjectSafeMode");
  194. NSLog(@"Entering Safe Mode!");
  195. %init(SafeMode);
  196. }
  197. }
  198. }
  199. if ([NSBundle.mainBundle.bundleIdentifier isEqualToString:@"com.apple.springboard"]){
  200. dlopen("/usr/lib/TweakInjectMapsCheck.dylib", RTLD_LAZY | RTLD_GLOBAL);
  201. }
  202. const char *safeModeByProcPath = [[NSString stringWithFormat:@"%@/.safeMode-%@", NSTemporaryDirectory(), processHash] UTF8String];
  203. if (file_exist((char *)safeModeByProcPath)){
  204. safeMode = true;
  205. unlink(safeModeByProcPath);
  206. }
  207. if (getenv("_MSSafeMode")){
  208. if (strcmp(getenv("_MSSafeMode"),"1") == 0){
  209. safeMode = true;
  210. }
  211. }
  212. if (getenv("_SafeMode")){
  213. if (strcmp(getenv("_SafeMode"),"1") == 0){
  214. safeMode = true;
  215. }
  216. }
  217. if (getenv("_SubstituteSafeMode")){
  218. if (strcmp(getenv("_SubstituteSafeMode"),"1") == 0){
  219. safeMode = true;
  220. }
  221. }
  222. if (!safeMode){
  223. //HBLogInfo(@"In bundle: %@", NSBundle.mainBundle.bundleIdentifier);
  224. NSArray *theList = sbinjectGenerateDylibList();
  225. //HBLogInfo(@"theList: %@", theList);
  226. for (NSString *dylib in theList) {
  227. NSLog(@"Injecting %@", dylib);
  228. void *dl = dlopen([dylib UTF8String], RTLD_LAZY | RTLD_GLOBAL);
  229. if (dl == NULL) {
  230. NSLog(@"Injection failed: '%s'", dlerror());
  231. }
  232. }
  233. } else {
  234. NSLog(@"TweakInject: Entering Safe Mode!");
  235. }
  236. }
  237. }
  238. }