SBInject.x 10 KB

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