SBInject.x 10 KB

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