// // XSHackIos.m // nochange // // Created by mac on 2024/10/15. // #import #import #include #include #include #include #import #include #include #import "XSHelper.h" #import "XSHackIos.h" #import "XSPhoneConfig.h" #import "XSPhoneInfo.h" /** 获取当前应用 */ SBApplication* XSGetFrontMostApplication(void) { __block id app = nil; NSLog(@"XS- getFrontMostApplication"); dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); dispatch_async(dispatch_get_main_queue(), ^{ @try { SpringBoard *springboard = (SpringBoard *)[UIApplication sharedApplication]; if ([springboard respondsToSelector:@selector(_accessibilityFrontMostApplication)]) { app = [springboard _accessibilityFrontMostApplication]; } } @catch (NSException *exception) { NSLog(@"XS- Debug: %@", exception.reason); } dispatch_semaphore_signal(semaphore); }); if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)) != 0) { NSLog(@"XSGetFrontMostApplication timeout"); } return app; } NSString* XSFrontMostAppId(void) { SBApplication* sbapp = XSGetFrontMostApplication(); if (sbapp == nil || [sbapp isEqual:[NSNull null]]) { return @""; } return sbapp.bundleIdentifier; } extern char **environ; //执行系统命令 int XSRuncmd(char *args[]) { printf("XSRuncmd: args "); for (int i = 0; args[i] != NULL; i++) { printf("%s ", args[i]); } printf("\n"); posix_spawnattr_t attr; posix_spawn_file_actions_t fact; pid_t pid; posix_spawnattr_init(&attr); posix_spawn_file_actions_init(&fact); posix_spawn(&pid,args[0], NULL, NULL,args,environ); perror("posix_spawn"); NSLog(@"XS- pid=%d,child pid = %d\n",getpid(),pid); int stat=0; waitpid(pid,&stat,0); NSLog(@"XS- stat is %d\n",stat); return pid; } int unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf){ int rv = remove(fpath); if (rv) perror(fpath); return rv; } void XSSystem(const char *cmd) { int stat = nftw(cmd, unlink_cb, 64, FTW_DEPTH | FTW_PHYS); NSLog(@"nftw res: %d", stat); } NSString* XSGetAppExecutable(NSString *pkgName) { //获取应用程序列表 Class cls = NSClassFromString(@"LSApplicationWorkspace"); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" id s = [(id)cls performSelector:NSSelectorFromString(@"defaultWorkspace")]; NSArray *array = [s performSelector:NSSelectorFromString(@"allApplications")]; #pragma clang diagnostic pop Class LSApplicationProxy_class = NSClassFromString(@"LSApplicationProxy"); for (LSApplicationProxy_class in array) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" NSString *res = [LSApplicationProxy_class performSelector:@selector(applicationIdentifier)]; //NSString *strBundleID = [LSApplicationProxy_class performSelector:@selector(bundleIdentifier)]; #pragma clang diagnostic pop // localizedName if ([res isEqualToString:pkgName]) { NSURL * bundleFullURL = [LSApplicationProxy_class performSelector:@selector(bundleURL)]; NSString * s_bundleURL = [bundleFullURL absoluteString]; NSString *prefix = @"file://"; NSRange needleRange = NSMakeRange(prefix.length, s_bundleURL.length - prefix.length ); NSString *needle = [s_bundleURL substringWithRange:needleRange]; NSBundle *bundle = [NSBundle bundleWithPath:needle]; NSString* executable = [[bundle infoDictionary] valueForKeyPath:@"CFBundleExecutable"]; return executable; } } return nil; } int XSReboot(void) { char *argv[] = { "/usr/sbin/reboot", NULL }; int r = XSRuncmd(argv); return r; } int XSKillApp(NSString *appexe) { if(appexe) { const char* app = [appexe UTF8String]; char *appStr = strdup(app); char *argv[] = { "/usr/bin/killall", "-9", appStr, NULL }; int r = XSRuncmd(argv); free(appStr); return r; } return 0; } int kill2(NSString *appexe) { NSString *cmd = [NSString stringWithFormat:@"/usr/bin/killall -9 %@", appexe]; const char* app = [cmd UTF8String]; char *appStr = strdup(app); int r = system2(appStr, NULL, NULL); free(appStr); return r; } int XSKillAppByName(NSString *pkgName) { NSString *appexe = XSGetAppExecutable(pkgName); if (!appexe) { NSLog(@"App executable not found for package: %@", pkgName); return -1; } // NSString *cmd = [NSString stringWithFormat:@"/usr/bin/killall -9 %@", appexe]; const char *cmdStr = [appexe UTF8String]; // char *argv[] = {"/usr/bin/killall", "-9", (char *)app, NULL}; char *appStr = strdup(cmdStr); // int r = c(appStr, NULL, NULL); char *argv[] = { "/usr/bin/killall", "-9", appStr, NULL }; int r = XSRuncmd(argv); // 这条路径内部有 waitpid,不会留僵尸 free(appStr); return r; } int (*_XSOpenApp)(CFStringRef, Boolean); int XSBringAppForeground(NSString *appIdentifier) { void* sbServices = dlopen("/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices", RTLD_LAZY); CFStringRef appBundleName = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), appIdentifier); //[NSString stringWithFormat:@"%s", eventData]; NSLog(@"XS-: Switch to application: %@", appBundleName); if (!_XSOpenApp) { _XSOpenApp = (int(*)(CFStringRef, Boolean))dlsym(sbServices,"SBSLaunchApplicationWithIdentifier"); } return _XSOpenApp(appBundleName, false); } void bringAppToForeground(NSString *bundleID) { @autoreleasepool { Class FBSSystemServiceClass = NSClassFromString(@"FBSSystemService"); if (FBSSystemServiceClass) { id service = [FBSSystemServiceClass sharedService]; NSDictionary *options = @{ @"BSLaunchOrigin": @"SpringBoard", @"BSSuspended": @NO, }; [service openApplication:bundleID options:options clientPort:0 withResult:^(NSError *error) { if (error) { NSLog(@"Failed to bring app to foreground: %@", error); } }]; } } } void activateApp(NSString *bundleID){ // 这个方法不行 Class SBApplicationControllerClass = NSClassFromString(@"SBApplicationController"); if (SBApplicationControllerClass) { id controller = [SBApplicationControllerClass sharedInstance]; id app = [controller applicationWithBundleIdentifier:bundleID]; if ([app respondsToSelector:@selector(activate)]) { [app activate]; } } } void XSRemoteUnlock(void) { // 获取 SBLockScreenManager 的共享实例 // 获取 SBUIController 实例 @autoreleasepool { Class sbUIControllerClass = objc_getClass("SBUIController"); id sbUIController = [sbUIControllerClass performSelector:@selector(sharedInstance)]; // 检查设备是否在锁屏状态 if ([sbUIController respondsToSelector:@selector(isOnLockScreen)]) { BOOL isLocked = [sbUIController performSelector:@selector(isOnLockScreen)]; if (isLocked) { // 模拟滑动解锁(对于没有密码的设备) CGPoint startPoint = CGPointMake(50, CGRectGetMidY([UIScreen mainScreen].bounds)); CGPoint endPoint = CGPointMake(CGRectGetMaxX([UIScreen mainScreen].bounds) - 50, CGRectGetMidY([UIScreen mainScreen].bounds)); UITouch *touch = [[UITouch alloc] init]; [touch setValue:[NSValue valueWithCGPoint:startPoint] forKey:@"_locationInWindow"]; UIEvent *event = [[UIEvent alloc] init]; [event setValue:@(UIEventTypeTouches) forKey:@"_type"]; [event setValue:[NSSet setWithObject:touch] forKey:@"_touches"]; [[UIApplication sharedApplication] sendEvent:event]; [touch setValue:[NSValue valueWithCGPoint:endPoint] forKey:@"_locationInWindow"]; [[UIApplication sharedApplication] sendEvent:event]; // 对于有密码的设备,还需要模拟输入密码 // 这部分代码会更复杂,需要模拟点击数字键盘 NSLog(@"Simulated unlock gesture"); } else { NSLog(@"Device is not locked"); } } else { NSLog(@"Unable to determine lock state"); } // 模拟按下 Home 键 SpringBoard *springboard = (SpringBoard *)[UIApplication sharedApplication]; if ([springboard respondsToSelector:@selector(_simulateHomeButtonPress)]) { [springboard performSelector:@selector(_simulateHomeButtonPress)]; NSLog(@"Simulated home button press"); } } /* Class lockScreenManagerClass = objc_getClass("SBLockScreenManager"); if (lockScreenManagerClass) { id lockScreenManager = [lockScreenManagerClass performSelector:@selector(sharedInstance)]; // 获取 SBUserAgent 的共享实例 Class userAgentClass = objc_getClass("SBUserAgent"); id userAgent = [userAgentClass performSelector:@selector(sharedUserAgent)]; if (lockScreenManager && userAgent) { // 调用解锁方法 if ([lockScreenManager performSelector:@selector(isUILocked)]) { [lockScreenManager performSelector:@selector(unlockUIFromSource:withOptions:) withObject:@(1) withObject:nil]; // 尝试唤醒屏幕 [userAgent performSelector:@selector(undimScreen)]; // 尝试解锁设备 [userAgent performSelector:@selector(unlockDevice)]; NSLog(@"-ad -log- Unlock command sent"); }else { NSLog(@"-ad log- No lock"); } } else { NSLog(@"Unable to get LockScreenManager"); } } else { NSLog(@"-ad log- NO SBLockScreenManager"); } */ } NSString* getDeviceName(void) { struct utsname systemInfo; uname(&systemInfo); return [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding]; } /* round up number by multiple of another number */ int roundUp(int numToRound, int multiple) { if (multiple == 0) return numToRound; int remainder = numToRound % multiple; if (remainder == 0) return numToRound; return numToRound + multiple - remainder; } CGImageRef createScreenShotCGImageRef(void) { Boolean isiPad8orUp = false; CGFloat scale = [UIScreen mainScreen].scale; CGSize screenSize = [UIScreen mainScreen].bounds.size; int height = (int)(screenSize.height * scale); int width = (int)(screenSize.width * scale); // check whether it is ipad8 or later NSString *searchText = getDeviceName(); NSRange range = [searchText rangeOfString:@"^iPad[8-9]|iPad[1-9][0-9]+" options:NSRegularExpressionSearch]; if (range.location != NSNotFound) { // ipad pro (3rd) or later isiPad8orUp = true; } if (isiPad8orUp) { if (width < height) { int temp = width; width = height; height = temp; } } else { if (width > height) { int temp = width; width = height; height = temp; } } int bytesPerElement = 4; int bytesPerRow = roundUp(bytesPerElement * width, 32); NSNumber *IOSurfaceBytesPerElement = [NSNumber numberWithInteger:bytesPerElement]; NSNumber *IOSurfaceBytesPerRow = [NSNumber numberWithInteger:bytesPerRow]; // don't know why but it should be a multiple of 32 NSNumber *IOSurfaceAllocSize = [NSNumber numberWithInteger:bytesPerRow * height]; NSNumber *nheight = [NSNumber numberWithInteger:height]; NSNumber *nwidth = [NSNumber numberWithInteger:width]; NSNumber *IOSurfacePixelFormat = [NSNumber numberWithInteger:1111970369]; NSNumber *IOSurfaceIsGlobal = [NSNumber numberWithInteger:1]; NSDictionary *properties = [[NSDictionary alloc] initWithObjectsAndKeys:IOSurfaceAllocSize, @"IOSurfaceAllocSize" , IOSurfaceBytesPerElement, @"IOSurfaceBytesPerElement", IOSurfaceBytesPerRow, @"IOSurfaceBytesPerRow", nheight, @"IOSurfaceHeight", IOSurfaceIsGlobal, @"IOSurfaceIsGlobal", IOSurfacePixelFormat, @"IOSurfacePixelFormat", nwidth, @"IOSurfaceWidth", nil]; IOSurfaceRef screenSurface = IOSurfaceCreate((__bridge CFDictionaryRef)(properties)); properties = nil; IOSurfaceLock(screenSurface, 0, NULL); CARenderServerRenderDisplay(0, CFSTR("LCD"), screenSurface, 0, 0); CGImageRef cgImageRef = nil; if (screenSurface) { cgImageRef = UICreateCGImageFromIOSurface(screenSurface); int targetWidth = CGImageGetWidth(cgImageRef); int targetHeight = CGImageGetHeight(cgImageRef); if (isiPad8orUp) // rotate 90 degrees counterclockwise { CGColorSpaceRef colorSpaceInfo = CGImageGetColorSpace(cgImageRef); CGContextRef bitmap; //if (sourceImage.imageOrientation == UIImageOrientationUp || sourceImage.imageOrientation == UIImageOrientationDown) { bitmap = CGBitmapContextCreate(NULL, targetHeight, targetWidth, CGImageGetBitsPerComponent(cgImageRef), CGImageGetBytesPerRow(cgImageRef), colorSpaceInfo, kCGImageAlphaPremultipliedFirst); //} else { //bitmap = CGBitmapContextCreate(NULL, targetHeight, targetWidth, CGImageGetBitsPerComponent(cgImageRef), CGImageGetBytesPerRow(imageRef), colorSpaceInfo, bitmapInfo); //} CGFloat degrees = -90.f; CGFloat radians = degrees * (M_PI / 180.f); CGContextTranslateCTM (bitmap, 0.5*targetHeight, 0.5*targetWidth); CGContextRotateCTM (bitmap, radians); CGContextTranslateCTM (bitmap, -0.5*targetWidth, -0.5*targetHeight); CGContextDrawImage(bitmap, CGRectMake(0, 0, targetWidth, targetHeight), cgImageRef); CGImageRelease(cgImageRef); cgImageRef = CGBitmapContextCreateImage(bitmap); CGColorSpaceRelease(colorSpaceInfo); CGContextRelease(bitmap); } } IOSurfaceUnlock(screenSurface, 0, NULL); CFRelease(screenSurface); screenSurface = nil; return cgImageRef; } UIImage* XSCcaptureScreen2(void) { CGImageRef cgImage = createScreenShotCGImageRef(); UIImage *uiImage = [UIImage imageWithCGImage:cgImage]; NSLog(@"img2:%@", uiImage); CFRelease(cgImage); return uiImage; } UIImage* XSCaptureScreen(void) { NSLog(@"captureScreen"); UIImage* res = _UICreateScreenUIImage(); NSLog(@"img1:%@", res); if (res && res != nil && ![res isEqual:[NSNull null]]) { return res; } return XSCcaptureScreen2(); } UIImage* defaultIconWithSize (CGSize size, NSString *appName) { UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); CGContextRef context = UIGraphicsGetCurrentContext(); // 使用随机颜色填充矩形 CGFloat hue = (CGFloat)arc4random() / UINT32_MAX; UIColor *color = [UIColor colorWithHue:hue saturation:0.5 brightness:0.8 alpha:1.0]; CGContextSetFillColorWithColor(context, color.CGColor); CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height)); // 可选:添加应用名称的首字母 NSString *initial = [[appName substringToIndex:1] uppercaseString]; UIColor *textColor = [UIColor whiteColor]; NSDictionary *attributes = @{ NSFontAttributeName: [UIFont boldSystemFontOfSize:size.width * 0.6], NSForegroundColorAttributeName: textColor }; CGSize textSize = [initial sizeWithAttributes:attributes]; CGPoint textPoint = CGPointMake((size.width - textSize.width) / 2, (size.height - textSize.height) / 2); [initial drawAtPoint:textPoint withAttributes:attributes]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; } NSArray* XSGetApps(void){ NSMutableArray *appInfoArray = [NSMutableArray array]; //获取应用程序列表 Class cls = NSClassFromString(@"LSApplicationWorkspace"); id s = [(id)cls performSelector:NSSelectorFromString(@"defaultWorkspace")]; NSArray *array = [s performSelector:NSSelectorFromString(@"allApplications")]; Class LSApplicationProxy_class = NSClassFromString(@"LSApplicationProxy"); for (LSApplicationProxy_class in array){ NSString * applicationIdentifier = [LSApplicationProxy_class performSelector:@selector(applicationIdentifier)]; NSString *strBundleID = [LSApplicationProxy_class performSelector:@selector(bundleIdentifier)]; NSString *localizedName = [LSApplicationProxy_class performSelector:@selector(localizedName)]; // localizedName //获取应用的相关信息 NSString *strVersion = [LSApplicationProxy_class performSelector:@selector(bundleVersion)]; NSString *strShortVersion = [LSApplicationProxy_class performSelector:@selector(shortVersionString)]; NSURL * bundleFullURL = [LSApplicationProxy_class performSelector:@selector(bundleURL)]; NSString * s_bundleURL = [bundleFullURL absoluteString]; NSURL *strContainerURL = [LSApplicationProxy_class performSelector:@selector(containerURL)]; NSString *strContainerDataPath = [strContainerURL path]; if ([s_bundleURL hasPrefix:@"file:///private/"]){ NSString *prefix = @"file://"; NSRange needleRange = NSMakeRange(prefix.length, s_bundleURL.length - prefix.length ); NSString *needle = [s_bundleURL substringWithRange:needleRange]; NSBundle *bundle = [NSBundle bundleWithPath:needle]; NSString* executable = [[bundle infoDictionary] valueForKeyPath:@"CFBundleExecutable"]; } UIImage *icon = nil; // 方法1: 尝试从bundle中获取图标 NSBundle *appBundle = [NSBundle bundleWithIdentifier:applicationIdentifier]; if (appBundle) { NSString *iconPath = [appBundle pathForResource:@"AppIcon60x60" ofType:@"png"]; if (iconPath) { icon = [UIImage imageWithContentsOfFile:iconPath]; } } // 方法2: 如果方法1失败,尝试使用UIApplication的私有API if (!icon) { UIApplication *application = [UIApplication sharedApplication]; if ([application respondsToSelector:@selector(_iconDataForBundleIdentifier:)]) { NSData *iconData = [application performSelector:@selector(_iconDataForBundleIdentifier:) withObject:applicationIdentifier]; if (iconData) { icon = [UIImage imageWithData:iconData]; } } } // 尝试使用_applicationIconImageForBundleIdentifier:获取图标 if ([UIImage respondsToSelector:@selector(_applicationIconImageForBundleIdentifier:format:scale:)]) { icon = [UIImage _applicationIconImageForBundleIdentifier:applicationIdentifier format:10 scale:[UIScreen mainScreen].scale]; } if (!icon) { // 使用默认图标 icon = defaultIconWithSize(CGSizeMake(60, 60), localizedName); } NSString *logInfo = [NSString stringWithFormat:@"bundleId:%@,name:%@,containerPath:%@,version:%@,shortVersion:%@,icon:%@", strBundleID, localizedName, strContainerDataPath, strVersion, strShortVersion, icon]; NSLog(@"%@", logInfo); NSDictionary *dic = @{ @"bundleId": applicationIdentifier ?: @"", @"name": localizedName ?: @"", @"containerPath": strContainerDataPath ?: @"", @"version": strVersion ?: @"", @"shortVersion": strShortVersion ?: @"", @"icon": icon ?: [NSNull null] }; [appInfoArray addObject:dic]; } return [appInfoArray copy]; } void XSCleanSafariHistory(void) { NSString *historyPath = @"/var/mobile/Library/Safari/History.db"; sqlite3 *database; if (sqlite3_open([historyPath UTF8String], &database) == SQLITE_OK) { char *errMsg; const char *sql = "DELETE FROM history_items; DELETE FROM history_visits; VACUUM;"; if (sqlite3_exec(database, sql, NULL, NULL, &errMsg) != SQLITE_OK) { NSLog(@"Error cleaning Safari history: %s", errMsg); sqlite3_free(errMsg); } else { NSLog(@"Safari history cleaned successfully"); } sqlite3_close(database); } else { NSLog(@"Unable to open database"); } } /** 清楚safari数据 */ void XSCleanSafari(void) { NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error; // 清理 Safari 缓存 NSString *safariCachePath = @"/var/mobile/Library/Caches/com.apple.mobilesafari"; [XSHelper rmFiles:safariCachePath]; NSString *safariSafePath = @"/var/mobile/Library/Caches/com.apple.Safari.SafeBrowsing"; [XSHelper rmFiles:safariSafePath]; // 清理 Safari 的 WebKit 缓存 NSString *webKitCachePath = @"/var/mobile/Library/WebKit/com.apple.mobilesafari"; [XSHelper rmFiles:webKitCachePath]; // 清理 Safari 历史记录 // cleanSafariHistory(); /* NSString *historyPath = @"/var/mobile/Library/Safari/History.db"; [fileManager removeItemAtPath:historyPath error:&error]; if (error) { NSLog(@"Error clearing Safari history: %@", error.localizedDescription); } */ NSString *historyPath = @"/var/mobile/Library/Safari"; [XSHelper rmFiles:historyPath]; // 清理 Safari Cookies NSString *cookiesPath = @"/var/mobile/Library/Cookies/Cookies.binarycookies"; [XSHelper rmFiles:cookiesPath]; NSLog(@"Safari data cleared successfully"); NSFileManager *man = [NSFileManager defaultManager]; // 清理cookie NSString *cookiepath = @"/var/mobile/Library/Cookies"; [XSHelper rmFiles:cookiepath]; cookiepath = @"/private/var/root/Library/Cookies"; [XSHelper rmFiles:cookiepath]; // 获取safari的沙盒路径 NSString* safaricontainer = nil; NSString* installplist = @"/var/mobile/Library/Caches/com.apple.mobile.installation.plist"; if ([man fileExistsAtPath:installplist]) { NSDictionary* plist = [NSDictionary dictionaryWithContentsOfFile:installplist]; id obj = plist[@"User"][@"com.apple.mobilesafari"]; if (obj == nil) { obj = plist[@"System"][@"com.apple.mobilesafari"]; } if (obj != nil) { safaricontainer = obj[@"Container"]; } } else { Class LSApplicationProxy_cls = objc_getClass("LSApplicationProxy"); id obj = [LSApplicationProxy_cls performSelector:@selector(applicationProxyForIdentifier:) withObject:@"com.apple.mobilesafari"]; if (obj != nil && [obj respondsToSelector:@selector(dataContainerURL)]) { safaricontainer = [[obj performSelector:@selector(dataContainerURL)] path]; } } if(safaricontainer) { // 清理library NSString* libpath = [safaricontainer stringByAppendingPathComponent:@"Library"]; NSString* libcachepath = [libpath stringByAppendingPathComponent:@"Caches"]; [XSHelper rmFiles:libcachepath]; } } void XSCleanKeychain(void) { NSFileManager* man = [NSFileManager defaultManager]; if ([man fileExistsAtPath:@"/var/Keychains/keychain-2.db"]) { XSSystem("cp /var/Keychains/keychain-2.db /tmp/"); sqlite3* ppDb = NULL; char cmd[256]; int rc = sqlite3_open("/tmp/keychain-2.db", &ppDb); if (rc == SQLITE_OK && ppDb) { strcpy(cmd, "DELETE FROM cert WHERE agrp<>'apple' and agrp not like '%apple%' and agrp <> 'ichat' and agrp <>'lockdown-identities'"); sqlite3_exec(ppDb, cmd, 0, 0, 0); strcpy(cmd, "DELETE FROM keys WHERE agrp<>'apple' and agrp not like '%apple%' and agrp <> 'ichat' and agrp <>'lockdown-identities'"); sqlite3_exec(ppDb, cmd, 0, 0, 0); strcpy(cmd, "DELETE FROM inet WHERE agrp<>'apple' and agrp not like '%apple%' and agrp <> 'ichat' and agrp <>'lockdown-identities'"); sqlite3_exec(ppDb, cmd, 0, 0, 0); // 关键:关闭数据库,释放 /private/var/tmp/keychain-2.db 句柄 sqlite3_close(ppDb); ppDb = NULL; XSSystem("cp /tmp/keychain-2.* /var/Keychains/"); } else { // 打开失败也要确保关闭 if (ppDb) { sqlite3_close(ppDb); ppDb = NULL; } NSLog(@"XSCleanKeychain: sqlite3_open failed: %d", rc); } } } void XSCleanPastboard(void) { UIPasteboard* pb = [UIPasteboard generalPasteboard]; if (pb != nil) { NSArray* items = [pb items]; if (items != nil) { items = [NSArray array]; } [pb setItems:items]; } NSFileManager* man = [NSFileManager defaultManager]; NSProcessInfo* proc = [NSProcessInfo processInfo]; BOOL isbe8 = FALSE; NSOperatingSystemVersion ver; ver.majorVersion = 8; ver.minorVersion = 0; ver.patchVersion = 0; if ([proc respondsToSelector:@selector(isOperatingSystemAtLeastVersion:)]) { isbe8 = [proc isOperatingSystemAtLeastVersion:ver]; } NSString* pbplist = nil; if ([man fileExistsAtPath:@"/System/Library/LaunchDaemons/com.apple.UIKit.pasteboardd.plist"]) { pbplist = @"/System/Library/LaunchDaemons/com.apple.UIKit.pasteboardd.plist"; //pbbundle = @"com.apple.UIKit.pasteboardd"; } else if ([man fileExistsAtPath:@"/Library/LaunchDaemons/com.apple.UIKit.pasteboardd.plist"]) { pbplist = @"/Library/LaunchDaemons/com.apple.UIKit.pasteboardd.plist"; //pbbundle = @"com.apple.UIKit.pasteboardd"; } else if ([man fileExistsAtPath:@"/System/Library/LaunchDaemons/com.apple.pasteboard.pasted.plist"]) { pbplist = @"/System/Library/LaunchDaemons/com.apple.pasteboard.pasted.plist"; //pbbundle = @"com.apple.pasteboard.pasted"; } // BOOL pbdbexist = [man fileExistsAtPath:@"/var/mobile/Library/Caches/com.apple.UIKit.pboard/pasteboardDB"]; NSString* pbcontainer = nil; if ([man fileExistsAtPath:@"/var/mobile/Library/Caches/com.apple.UIKit.pboard"]) { pbcontainer = @"/var/mobile/Library/Caches/com.apple.UIKit.pboard"; } else if ([man fileExistsAtPath:@"/var/mobile/Library/Caches/com.apple.Pasteboard"]) { pbcontainer = @"/var/mobile/Library/Caches/com.apple.Pasteboard"; } if (!isbe8 && [man fileExistsAtPath:pbplist]) { XSSystem("launchctl unload -w"); } if (pbcontainer != nil && [man fileExistsAtPath:pbcontainer]) { NSString* cmd = [NSString stringWithFormat:@"rm -rf %@/*", pbcontainer]; XSSystem([cmd UTF8String]); } } NSString* getAppExecutable(NSString *pkgName){ //获取应用程序列表 Class cls = NSClassFromString(@"LSApplicationWorkspace"); id s = [(id)cls performSelector:NSSelectorFromString(@"defaultWorkspace")]; NSArray *array = [s performSelector:NSSelectorFromString(@"allApplications")]; Class LSApplicationProxy_class = NSClassFromString(@"LSApplicationProxy"); for (LSApplicationProxy_class in array){ NSString * res = [LSApplicationProxy_class performSelector:@selector(applicationIdentifier)]; NSString *strBundleID = [LSApplicationProxy_class performSelector:@selector(bundleIdentifier)]; // localizedName if ([res isEqualToString:pkgName]) { NSURL * bundleFullURL = [LSApplicationProxy_class performSelector:@selector(bundleURL)]; NSString * s_bundleURL = [bundleFullURL absoluteString]; if ([s_bundleURL hasPrefix:@"file:///private/"]){ NSString *prefix = @"file://"; NSRange needleRange = NSMakeRange(prefix.length, s_bundleURL.length - prefix.length ); NSString *needle = [s_bundleURL substringWithRange:needleRange]; NSBundle *bundle = [NSBundle bundleWithPath:needle]; NSString* executable = [[bundle infoDictionary] valueForKeyPath:@"CFBundleExecutable"]; return executable; }} } return nil; } NSString* getAppSandboxPath(NSString *app){ //获取应用程序列表 Class cls = NSClassFromString(@"LSApplicationWorkspace"); id s = [(id)cls performSelector:NSSelectorFromString(@"defaultWorkspace")]; NSArray *array = [s performSelector:NSSelectorFromString(@"allApplications")]; Class LSApplicationProxy_class = NSClassFromString(@"LSApplicationProxy"); for (LSApplicationProxy_class in array){ NSString *strBundleID = [LSApplicationProxy_class performSelector:@selector(bundleIdentifier)]; //获取应用的相关信息 NSString *strVersion = [LSApplicationProxy_class performSelector:@selector(bundleVersion)]; NSString *strShortVersion = [LSApplicationProxy_class performSelector:@selector(shortVersionString)]; NSURL *strContainerURL = [LSApplicationProxy_class performSelector:@selector(containerURL)]; NSString *strContainerDataPath = [strContainerURL path]; //NSLog(@"bundleID:%@ localizedName: %@", strBundleID, strLocalizedName); if ([strBundleID isEqualToString:app]) { return strContainerDataPath; } } return nil; } NSString* XSGetAppInfoPath(NSString *app) { NSString *appDataPath = getAppSandboxPath(app); if (appDataPath) { //判断目录,只有这两个目录才能清除,如果是其他的目录,比如/var/mobile/Documents/ 千万不能清除, //否则可能需要重新激活或产生其他的问题 if ([appDataPath hasPrefix:@"/private/var/mobile/Containers/Data/Application/"] || [appDataPath hasPrefix:@"/var/mobile/Containers/Data/Application/"]) { NSString *strLibraryPath = [appDataPath stringByAppendingPathComponent:@"Library"]; NSString *strPreferencesPath = [strLibraryPath stringByAppendingPathComponent:@"Preferences"]; NSString *appInfoPlist = [strPreferencesPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", app]]; NSLog(@"app info path:%@", appInfoPlist); return appInfoPlist; } } return nil; } NSDictionary* XSGetAppInfo(NSString *appInfoPlist) { if (appInfoPlist) { NSDictionary *appInfo = [[NSDictionary alloc] initWithContentsOfFile:appInfoPlist]; return appInfo; } return nil; } void XSSaveAppInfo(NSDictionary *dic, NSString *appInfoPath) { NSMutableDictionary *saveDic = [[NSMutableDictionary alloc] init]; if (dic) { id kzzhDeviceInfo = dic[@"kzzhDeviceInfo"]; if (kzzhDeviceInfo) { [saveDic setValue:kzzhDeviceInfo forKey:@"kzzhDeviceInfo"]; } id appInfo = dic[@"appInfo"]; if (appInfo) { [saveDic setValue:appInfo forKey:@"appInfo"]; } id applovinInfo = dic[@"applovinInfo"]; if (applovinInfo) { [saveDic setValue:applovinInfo forKey:@"applovinInfo"]; } id kNilOptions = dic[@"kNilOptions"]; if (kNilOptions) { [saveDic setValue:kNilOptions forKey:@"kNilOptions"]; } id bfaceDictKey = dic[@"bfaceDictKey"]; XSPhoneConfig *config = [XSPhoneConfig sharedInstance]; NSString *deviceId = config.DeviceId; NSString *loclIp = [[XSPhoneInfo sharedInstance] IPAddress]; if (bfaceDictKey) { NSMutableDictionary *tempDic = [NSMutableDictionary dictionaryWithDictionary:bfaceDictKey]; [tempDic setValue:deviceId forKey:@"adbrush_deviceid"]; [tempDic setValue:loclIp forKey:@"adbrush_localip"]; [saveDic setValue:tempDic forKey:@"bfaceDictKey"]; } else { NSDictionary *tempDic = @{@"adbrush_deviceid": deviceId, @"adbrush_localip": loclIp}; [saveDic setValue:tempDic forKey:@"bfaceDictKey"]; } /* NSDictionary *strAttrib = [NSDictionary dictionaryWithObjectsAndKeys: @"mobile",NSFileGroupOwnerAccountName, @"mobile",NSFileOwnerAccountName, nil]; */ [saveDic writeToFile:appInfoPath atomically:YES]; } } void cleanBundleContainer(NSString *appDataPath) { //判断目录,只有这两个目录才能清除,如果是其他的目录,比如/var/mobile/Documents/ 千万不能清除, //否则可能需要重新激活或产生其他的问题 if ([appDataPath hasPrefix:@"/private/var/mobile/Containers/Data/Application/"] || [appDataPath hasPrefix:@"/var/mobile/Containers/Data/Application/"]) { NSFileManager *fm = [NSFileManager defaultManager]; NSString *strDocumentsPath = [appDataPath stringByAppendingPathComponent:@"Documents"]; [fm removeItemAtPath:strDocumentsPath error:nil]; NSString *strLibraryPath = [appDataPath stringByAppendingPathComponent:@"Library"]; NSString *strCachesPath = [strLibraryPath stringByAppendingPathComponent:@"Caches"]; NSString *strPreferencesPath = [strLibraryPath stringByAppendingPathComponent:@"Preferences"]; [fm removeItemAtPath:strLibraryPath error:nil]; NSString *strTmpPath = [appDataPath stringByAppendingPathComponent:@"tmp"]; [fm removeItemAtPath:strTmpPath error:nil]; NSString *strStoreKitPath = [appDataPath stringByAppendingPathComponent:@"StoreKit"]; [fm removeItemAtPath:strStoreKitPath error:nil]; //删除沙盒目录之后,要以mobile身份创建相应的目录,否则可能会因为权限问题使再次安装的应用 //不能写入应用沙盒目录 NSDictionary *strAttrib = [NSDictionary dictionaryWithObjectsAndKeys: @"mobile",NSFileGroupOwnerAccountName, @"mobile",NSFileOwnerAccountName, nil]; [fm createDirectoryAtPath:appDataPath withIntermediateDirectories:NO attributes:strAttrib error:nil]; [fm createDirectoryAtPath:strDocumentsPath withIntermediateDirectories:NO attributes:strAttrib error:nil]; [fm createDirectoryAtPath:strLibraryPath withIntermediateDirectories:NO attributes:strAttrib error:nil]; [fm createDirectoryAtPath:strCachesPath withIntermediateDirectories:NO attributes:strAttrib error:nil]; [fm createDirectoryAtPath:strPreferencesPath withIntermediateDirectories:NO attributes:strAttrib error:nil]; [fm createDirectoryAtPath:strTmpPath withIntermediateDirectories:NO attributes:strAttrib error:nil]; } } void clearAppStringInfo(NSString* appPath) { NSFileManager *fm = [NSFileManager defaultManager]; if (![fm fileExistsAtPath:appPath]) { return; } if ([appPath hasPrefix:@"/private/var/containers/Bundle/Application"] || [appPath hasPrefix:@"/var/containers/Bundle/Application"] || [appPath hasPrefix:@"/private/var/mobile/Containers/Data/Application"] || [appPath hasPrefix:@"/var/mobile/Containers/Data/Application"]) { NSString *itunesPath = [appPath stringByDeletingLastPathComponent]; NSLog(@"itunes path: %@", itunesPath); NSString *file1 = [NSString stringWithFormat:@"%@/iTunesMetadata.plist", itunesPath]; NSString *file2 = [NSString stringWithFormat:@"%@/iTunesArtwork", itunesPath]; NSString *file3 = [NSString stringWithFormat:@"%@/BundleMetadata.plist", itunesPath]; [fm removeItemAtPath:file1 error:nil]; [fm removeItemAtPath:file2 error:nil]; [fm removeItemAtPath:file3 error:nil]; } } void XSClearAppData(NSString *app) { NSString *appPath = getAppSandboxPath(app); if (appPath) { cleanBundleContainer(appPath); clearAppStringInfo(appPath); } } BOOL screenIsLocked(void) { Class sbUIControllerClass = objc_getClass("SBUIController"); id sbUIController = [sbUIControllerClass performSelector:@selector(sharedInstance)]; // 检查设备是否在锁屏状态 if ([sbUIController respondsToSelector:@selector(isOnLockScreen)]) { BOOL isLocked = [sbUIController performSelector:@selector(isOnLockScreen)]; return isLocked; } return YES; } void XSClearAll(NSString *appId) { XSCleanSafari(); XSCleanKeychain(); XSCleanPastboard(); XSClearAppData(appId); } double getCPUTemperature(void) { double temperature = 0.0; io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleSMC")); if (platformExpert) { CFMutableDictionaryRef propertyDict = NULL; kern_return_t result = IORegistryEntryCreateCFProperties(platformExpert, &propertyDict, kCFAllocatorDefault, 0); if (result == kIOReturnSuccess) { CFDataRef data = CFDictionaryGetValue(propertyDict, CFSTR("TC0P")); if (data) { const UInt8 *bytes = CFDataGetBytePtr(data); temperature = (double)bytes[0]; } CFRelease(propertyDict); } IOObjectRelease(platformExpert); } return temperature; } // 注入网络权限 void injectNetworkPermissions(void) { // 创建授权选项 SecTaskRef task = SecTaskCreateFromSelf(NULL); if (!task) return; CFErrorRef error = NULL; CFTypeRef entitled = SecTaskCopyValueForEntitlement(task, CFSTR("com.apple.private.network.client"), &error); if (!entitled) { // 添加网络客户端权限 CFDictionaryRef entitlements = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue((CFMutableDictionaryRef)entitlements, CFSTR("com.apple.private.network.client"), kCFBooleanTrue); static void *security = NULL; static SecTaskSetEntitlementsPtr SetEntitlements = NULL; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ security = dlopen("/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY); if (security) { SetEntitlements = (SecTaskSetEntitlementsPtr)dlsym(security, "SecTaskSetEntitlements"); } }); if (!security || !SetEntitlements) { NSLog(@"XS- no SecTaskSetEntitlements"); return; } // 应用权限 SetEntitlements(task, entitlements); CFRelease(entitlements); } if (entitled) CFRelease(entitled); CFRelease(task); } BOOL injectEntitlementsWithOptions(NSDictionary *options, NSError **error) { static void *security = NULL; static SecTaskSetEntitlementsPtr SetEntitlements = NULL; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ security = dlopen("/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY); if (security) { SetEntitlements = (SecTaskSetEntitlementsPtr)dlsym(security, "SecTaskSetEntitlements"); } }); if (!security || !SetEntitlements) { if (error) { *error = [NSError errorWithDomain:@"EntitlementsInjectorErrorDomain" code:-1 userInfo:@{NSLocalizedDescriptionKey: @"无法加载必要的系统函数"}]; } return NO; } SecTaskRef task = SecTaskCreateFromSelf(NULL); if (!task) { if (error) { *error = [NSError errorWithDomain:@"EntitlementsInjectorErrorDomain" code:-2 userInfo:@{NSLocalizedDescriptionKey: @"无法创建任务引用"}]; } return NO; } // 转换权限字典 NSMutableDictionary *entitlements = [NSMutableDictionary dictionary]; // 基本网络权限 entitlements[@"com.apple.developer.networking.HotspotConfiguration"] = @YES; entitlements[@"com.apple.developer.networking.networkextension"] = @YES; entitlements[@"com.apple.developer.networking.wifi-info"] = @YES; // 添加用户自定义权限 [entitlements addEntriesFromDictionary:options]; // 注入权限 CFErrorRef cfError = NULL; Boolean success = SetEntitlements(task, (__bridge CFDictionaryRef)entitlements); CFRelease(task); if (!success) { if (error) { *error = (__bridge NSError *)cfError; } if (cfError) CFRelease(cfError); return NO; } return YES; } // 验证权限是否注入成功 BOOL verifyEntitlement(NSString *entitlementName) { SecTaskRef task = SecTaskCreateFromSelf(NULL); if (!task) return NO; CFErrorRef error = NULL; CFTypeRef value = SecTaskCopyValueForEntitlement(task, (__bridge CFStringRef)entitlementName, &error); CFRelease(task); if (error) { CFRelease(error); return NO; } BOOL hasEntitlement = (value != NULL); if (value) CFRelease(value); return hasEntitlement; } pid_t system2(const char *command, int *infp, int *outfp) { if (!command || strlen(command) == 0) { NSLog(@"system2: Invalid command"); return -1; } int p_stdin[2]; int p_stdout[2]; pid_t pid; if (pipe(p_stdin) == -1) { NSLog(@"system2: Failed to create stdin pipe: %s", strerror(errno)); return -1; } if (pipe(p_stdout) == -1) { NSLog(@"system2: Failed to create stdout pipe: %s", strerror(errno)); close(p_stdin[0]); close(p_stdin[1]); return -1; } posix_spawn_file_actions_t actions; posix_spawn_file_actions_init(&actions); posix_spawn_file_actions_adddup2(&actions, p_stdin[0], STDIN_FILENO); posix_spawn_file_actions_adddup2(&actions, p_stdout[1], STDOUT_FILENO); // 子进程中关闭不需要的端 posix_spawn_file_actions_addclose(&actions, p_stdin[1]); posix_spawn_file_actions_addclose(&actions, p_stdout[0]); char *argv[] = {"/bin/sh", "-c", (char *)command, NULL}; int status = posix_spawn(&pid, "/bin/sh", &actions, NULL, argv, environ); posix_spawn_file_actions_destroy(&actions); // 父进程关闭不需要的端 close(p_stdin[0]); // 子进程的读端 close(p_stdout[1]); // 子进程的写端 if (status != 0) { NSLog(@"system2: posix_spawn failed with status %d", status); close(p_stdin[1]); close(p_stdout[0]); return -1; } // 返回父进程需要的文件描述符 if (infp) { *infp = p_stdin[1]; // 父进程写入到子进程的stdin } else { close(p_stdin[1]); } if (outfp) { *outfp = p_stdout[0]; // 父进程从子进程的stdout读取 } else { close(p_stdout[0]); } if (pid > 0) { int status = 0; waitpid(pid, &status, 0); } return pid; } void printInfo(id object) { if (!object) { NSLog(@"Object is nil"); return; } // 获取类名 NSLog(@"\nClass: %@", NSStringFromClass([object class])); // 1. 获取所有属性 unsigned int propertyCount; objc_property_t *properties = class_copyPropertyList([object class], &propertyCount); NSLog(@"\n=== Properties ==="); for (unsigned int i = 0; i < propertyCount; i++) { objc_property_t property = properties[i]; // 属性名 const char *propertyName = property_getName(property); // 属性特性 const char *attributes = property_getAttributes(property); NSLog(@"Property: %s, Attributes: %s", propertyName, attributes); // 尝试获取属性值 @try { NSString *name = [NSString stringWithUTF8String:propertyName]; id value = [object valueForKey:name]; NSLog(@"Value: %@", value); } @catch (NSException *exception) { NSLog(@"Cannot access value"); } } free(properties); // 2. 获取所有实例方法 unsigned int methodCount; Method *methods = class_copyMethodList([object class], &methodCount); NSLog(@"\n=== Methods ==="); for (unsigned int i = 0; i < methodCount; i++) { Method method = methods[i]; SEL selector = method_getName(method); NSString *methodName = NSStringFromSelector(selector); // 获取参数个数 int arguments = method_getNumberOfArguments(method); struct objc_method_description *desc = method_getDescription(method); NSLog(@"args:%s", desc->types); // 获取返回值类型 char *returnType = method_copyReturnType(method); NSLog(@"Method: %@, Args: %d, ReturnType: %s", methodName, arguments, returnType); free(returnType); } free(methods); // 3. 获取所有实例变量 unsigned int ivarCount; Ivar *ivars = class_copyIvarList([object class], &ivarCount); NSLog(@"\n=== Instance Variables ==="); for (unsigned int i = 0; i < ivarCount; i++) { Ivar ivar = ivars[i]; const char *ivarName = ivar_getName(ivar); const char *ivarType = ivar_getTypeEncoding(ivar); NSLog(@"Ivar: %s, Type: %s", ivarName, ivarType); } free(ivars); // 4. 获取遵循的协议 unsigned int protocolCount; Protocol * __unsafe_unretained *protocols = class_copyProtocolList([object class], &protocolCount); NSLog(@"\n=== Protocols ==="); for (unsigned int i = 0; i < protocolCount; i++) { Protocol *protocol = protocols[i]; const char *protocolName = protocol_getName(protocol); NSLog(@"Protocol: %s", protocolName); } free(protocols); } // 递归获取父类信息 void printClassHierarchy(Class cls) { NSLog(@"\n=== Class Hierarchy ==="); while (cls) { NSLog(@"Class: %@", NSStringFromClass(cls)); cls = class_getSuperclass(cls); } } // 获取方法的详细信息 void printMethodInfo(Method method) { SEL selector = method_getName(method); NSString *methodName = NSStringFromSelector(selector); // 获取参数类型 unsigned int argCount = method_getNumberOfArguments(method); NSMutableArray *argTypes = [NSMutableArray array]; for (unsigned int i = 0; i < argCount; i++) { char *argType = method_copyArgumentType(method, i); if (argType) { [argTypes addObject:[NSString stringWithUTF8String:argType]]; free(argType); } } // 获取返回值类型 char *returnType = method_copyReturnType(method); NSString *returnTypeString = returnType ? [NSString stringWithUTF8String:returnType] : @"?"; free(returnType); NSLog(@"Method: %@\nReturn Type: %@\nArgument Types: %@", methodName, returnTypeString, argTypes); } // 获取属性的详细信息 void printPropertyInfo(objc_property_t property) { const char *name = property_getName(property); const char *attributes = property_getAttributes(property); unsigned int attrCount; objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount); NSMutableArray *attributeDetails = [NSMutableArray array]; for (unsigned int i = 0; i < attrCount; i++) { NSString *attrName = @(attrs[i].name); NSString *attrValue = attrs[i].value ? @(attrs[i].value) : @""; [attributeDetails addObject:[NSString stringWithFormat:@"%@=%@", attrName, attrValue]]; } free(attrs); NSLog(@"Property: %s\nAttributes: %s\nDetailed Attributes: %@", name, attributes, attributeDetails); } // 属性特性解析工具 NSDictionary* parsePropertyAttributes(const char *attributes) { NSString *attributeString = @(attributes); NSArray *components = [attributeString componentsSeparatedByString:@","]; NSMutableDictionary *result = [NSMutableDictionary dictionary]; for (NSString *component in components) { if ([component hasPrefix:@"T"]) { result[@"type"] = [component substringFromIndex:1]; } else if ([component isEqualToString:@"R"]) { result[@"readonly"] = @YES; } else if ([component isEqualToString:@"C"]) { result[@"copy"] = @YES; } else if ([component isEqualToString:@"&"]) { result[@"strong"] = @YES; } else if ([component isEqualToString:@"W"]) { result[@"weak"] = @YES; } else if ([component isEqualToString:@"N"]) { result[@"nonatomic"] = @YES; } } return result; } // 获取前台应用 void getFrontAppWindows(void) { SpringBoard *springBoard = (SpringBoard *)[UIApplication sharedApplication]; UIApplication *frontApp = [springBoard _accessibilityFrontMostApplication]; printInfo(frontApp); // 获取SBWindowManager类 Class SBWindowManagerClass = NSClassFromString(@"SBWindowManager"); id windowManager = [SBWindowManagerClass sharedInstance]; NSLog(@"SBWindowManager"); printInfo(windowManager); UIWindow *win = [[NSClassFromString(@"SBUIController") sharedInstance] window]; printInfo(win); // 尝试获取windows if ([windowManager respondsToSelector:@selector(windows)]) { NSArray *windows = [windowManager windows]; NSLog(@"windows: %@", windows); } } void findButtonsInView(UIView *view) { NSLog(@"findButtonsInView:%@",view); // 如果是按钮 if ([view isKindOfClass:[UIButton class]]) { UIButton *button = (UIButton *)view; CGRect frameInWindow = [button convertRect:button.bounds toView:nil]; NSString *title = [button titleForState:UIControlStateNormal]; NSLog(@"Button: %@, Position: %@", title, NSStringFromCGRect(frameInWindow)); } // 遍历子视图 for (UIView *subview in view.subviews) { findButtonsInView(subview); } } UIWindow* getKeyWindow(void) { NSLog(@"getKeyWindow"); getFrontAppWindows(); UIWindow *keyWindow = nil; // iOS 13及以上版本 if (@available(iOS 13.0, *)) { NSSet *connectedScenes = [[UIApplication sharedApplication] connectedScenes]; for (UIScene *scene in connectedScenes) { if ([scene isKindOfClass:[UIWindowScene class]]) { UIWindowScene *windowScene = (UIWindowScene *)scene; for (UIWindow *window in windowScene.windows) { NSLog(@"getKeyWindow: %@", window); findButtonsInView(window); if (window.isKeyWindow) { keyWindow = window; //break; } } } } } else { // iOS 13以下版本 keyWindow = [[UIApplication sharedApplication] keyWindow]; } return keyWindow; } void getAllButtons(void) { dispatch_sync(dispatch_get_main_queue(),^{ // 获取当前窗口 //FBScene *scene = getForegroundAppScene(); //findButtonsInView(scene.window); UIWindow *keyWindow = getKeyWindow(); // 递归查找按钮 findButtonsInView(keyWindow); }); }