├── TrollHelper ├── Resources │ ├── LaunchImage.png │ ├── LaunchImage@2x.png │ ├── LaunchImage-700-568h@2x.png │ ├── LaunchImage-800-667h@2x.png │ ├── LaunchImage-700-Landscape~ipad.png │ ├── LaunchImage-700-Portrait@2x~ipad.png │ ├── LaunchImage-700-Portrait~ipad.png │ ├── LaunchImage-800-Portrait-736h@3x.png │ ├── LaunchImage-700-Landscape@2x~ipad.png │ ├── LaunchImage-800-Landscape-736h@3x.png │ ├── AppIcon29x29.png │ ├── AppIcon40x40.png │ ├── AppIcon50x50.png │ ├── AppIcon57x57.png │ ├── AppIcon60x60.png │ ├── AppIcon72x72.png │ ├── AppIcon76x76.png │ ├── AppIcon29x29@2x.png │ ├── AppIcon29x29@3x.png │ ├── AppIcon40x40@2x.png │ ├── AppIcon40x40@3x.png │ ├── AppIcon50x50@2x.png │ ├── AppIcon57x57@2x.png │ ├── AppIcon57x57@3x.png │ ├── AppIcon60x60@2x.png │ ├── AppIcon60x60@3x.png │ ├── AppIcon72x72@2x.png │ ├── AppIcon76x76@2x.png │ └── Info.plist ├── TSHAppDelegateWithScene.h ├── TSHRootViewController.h ├── TSHSceneDelegate.h ├── control ├── TSHAppDelegateNoScene.h ├── TSHAppDelegateNoScene.m ├── Makefile ├── TSHAppDelegateWithScene.m ├── entitlements.plist ├── TSHSceneDelegate.m ├── main.m └── TSHRootViewController.m ├── cert.p12 ├── RootHelper ├── uicache.h ├── unarchive.h ├── control ├── Makefile ├── entitlements.plist ├── unarchive.m ├── uicache.m └── main.m-system ├── Victim ├── victim.p12 ├── README.md └── make_cert.sh ├── TrollStore ├── Resources │ ├── AppIcon29x29.png │ ├── AppIcon40x40.png │ ├── AppIcon50x50.png │ ├── AppIcon57x57.png │ ├── AppIcon60x60.png │ ├── AppIcon72x72.png │ ├── AppIcon76x76.png │ ├── AppIcon29x29@2x.png │ ├── AppIcon29x29@3x.png │ ├── AppIcon40x40@2x.png │ ├── AppIcon40x40@3x.png │ ├── AppIcon50x50@2x.png │ ├── AppIcon57x57@2x.png │ ├── AppIcon57x57@3x.png │ ├── AppIcon60x60@2x.png │ ├── AppIcon60x60@3x.png │ ├── AppIcon72x72@2x.png │ ├── AppIcon76x76@2x.png │ ├── Base.lproj │ │ └── LaunchScreen.storyboardc │ │ │ ├── Info.plist │ │ │ ├── Kx4-55-vNS-view-9BB-B5-Vbi.nib │ │ │ ├── X3T-Aa-nEE-view-vAu-RC-m7d.nib │ │ │ └── UITabBarController-9el-pn-lH0.nib │ ├── fallback.entitlements │ └── Info.plist ├── TSRootViewController.h ├── TSAppDelegate.h ├── TSSettingsAdvancedListController.h ├── control ├── TSSceneDelegate.h ├── TSSettingsListController.h ├── TSAppTableViewController.h ├── main.m ├── Makefile ├── TSInstallationController.h ├── TSApplicationsManager.h ├── TSAppDelegate.m ├── TSRootViewController.m ├── TSCommonTCCServiceNames.h ├── TSAppInfo.h ├── entitlements.plist ├── TSSceneDelegate.m ├── TSSettingsAdvancedListController.m ├── TSApplicationsManager.m ├── TSInstallationController.m └── TSAppTableViewController.m ├── .gitignore ├── Pwnify ├── Makefile └── main.m ├── Shared ├── TSPresentationDelegate.h ├── TSListControllerShared.h ├── TSPresentationDelegate.m ├── TSUtil.h ├── CoreServices.h ├── TSListControllerShared.m └── TSUtil.m ├── install_trollhelperota_ios15.md ├── install_trollhelperota_arm64e.md ├── install_trollhelper.md ├── install_sshrd.md ├── .github └── ISSUE_TEMPLATE │ └── bug-report-issue-template.yml ├── Makefile ├── LICENSE └── README.md /TrollHelper/Resources/LaunchImage.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /TrollHelper/Resources/LaunchImage@2x.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /TrollHelper/Resources/LaunchImage-700-568h@2x.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /TrollHelper/Resources/LaunchImage-800-667h@2x.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /TrollHelper/Resources/LaunchImage-700-Landscape~ipad.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /TrollHelper/Resources/LaunchImage-700-Portrait@2x~ipad.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /TrollHelper/Resources/LaunchImage-700-Portrait~ipad.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /TrollHelper/Resources/LaunchImage-800-Portrait-736h@3x.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /TrollHelper/Resources/LaunchImage-700-Landscape@2x~ipad.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /TrollHelper/Resources/LaunchImage-800-Landscape-736h@3x.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cert.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/cert.p12 -------------------------------------------------------------------------------- /RootHelper/uicache.h: -------------------------------------------------------------------------------- 1 | extern void registerPath(NSString* path, BOOL unregister, BOOL system); -------------------------------------------------------------------------------- /Victim/victim.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/Victim/victim.p12 -------------------------------------------------------------------------------- /RootHelper/unarchive.h: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | 3 | extern int extract(NSString* fileToExtract, NSString* extractionPath); -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon29x29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon29x29.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon40x40.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon50x50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon50x50.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon57x57.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon60x60.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon72x72.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon76x76.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | .DS_Store 3 | .theos/ 4 | packages/ 5 | xcuserdata 6 | .vscode 7 | Pwnify/pwnify 8 | InstallerVictim.ipa 9 | _build -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon29x29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon29x29.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon40x40.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon50x50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon50x50.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon57x57.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon60x60.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon72x72.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon76x76.png -------------------------------------------------------------------------------- /TrollStore/TSRootViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface TSRootViewController : UITabBarController 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon29x29@2x.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon29x29@3x.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon40x40@2x.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon40x40@3x.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon50x50@2x.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon57x57@2x.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon57x57@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon57x57@3x.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon60x60@2x.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon60x60@3x.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon72x72@2x.png -------------------------------------------------------------------------------- /TrollHelper/Resources/AppIcon76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollHelper/Resources/AppIcon76x76@2x.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon29x29@2x.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon29x29@3x.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon40x40@2x.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon40x40@3x.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon50x50@2x.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon57x57@2x.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon57x57@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon57x57@3x.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon60x60@2x.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon60x60@3x.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon72x72@2x.png -------------------------------------------------------------------------------- /TrollStore/Resources/AppIcon76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/AppIcon76x76@2x.png -------------------------------------------------------------------------------- /TrollStore/TSAppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface TSAppDelegate : UIResponder 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /TrollHelper/TSHAppDelegateWithScene.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | @interface TSHAppDelegateWithScene : UIResponder 5 | 6 | @end -------------------------------------------------------------------------------- /TrollStore/TSSettingsAdvancedListController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface TSSettingsAdvancedListController : PSListController 4 | 5 | @end -------------------------------------------------------------------------------- /TrollStore/Resources/Base.lproj/LaunchScreen.storyboardc/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/Base.lproj/LaunchScreen.storyboardc/Info.plist -------------------------------------------------------------------------------- /TrollHelper/TSHRootViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface TSHRootViewController : TSListControllerShared 4 | { 5 | NSString* _newerVersion; 6 | } 7 | @end 8 | -------------------------------------------------------------------------------- /TrollStore/Resources/Base.lproj/LaunchScreen.storyboardc/Kx4-55-vNS-view-9BB-B5-Vbi.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/Base.lproj/LaunchScreen.storyboardc/Kx4-55-vNS-view-9BB-B5-Vbi.nib -------------------------------------------------------------------------------- /TrollStore/Resources/Base.lproj/LaunchScreen.storyboardc/X3T-Aa-nEE-view-vAu-RC-m7d.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/Base.lproj/LaunchScreen.storyboardc/X3T-Aa-nEE-view-vAu-RC-m7d.nib -------------------------------------------------------------------------------- /TrollStore/control: -------------------------------------------------------------------------------- 1 | Package: com.opa334.trollstore 2 | Name: TrollStore 3 | Version: 1.5.1 4 | Architecture: iphoneos-arm 5 | Description: An awesome application! 6 | Maintainer: opa334 7 | Author: opa334 8 | Section: Utilities 9 | -------------------------------------------------------------------------------- /TrollStore/Resources/Base.lproj/LaunchScreen.storyboardc/UITabBarController-9el-pn-lH0.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmingyang113/opa334/HEAD/TrollStore/Resources/Base.lproj/LaunchScreen.storyboardc/UITabBarController-9el-pn-lH0.nib -------------------------------------------------------------------------------- /Victim/README.md: -------------------------------------------------------------------------------- 1 | # Victim IPA and Cert 2 | 3 | In order to compile a pwned TrollHelperOTA arm64 IPA, you need to provide a dev cert with the same team ID as your victim app in this directory. 4 | 5 | ```bash 6 | ./make_cert.sh 7 | ``` 8 | -------------------------------------------------------------------------------- /TrollHelper/TSHSceneDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface TSHSceneDelegate : UIResponder 4 | @property (strong, nonatomic) UIWindow * window; 5 | @property (nonatomic, strong) UINavigationController *rootViewController; 6 | @end -------------------------------------------------------------------------------- /TrollStore/TSSceneDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface TSSceneDelegate : UIResponder 4 | @property (strong, nonatomic) UIWindow * window; 5 | @property (nonatomic, strong) UITabBarController *rootViewController; 6 | @end 7 | -------------------------------------------------------------------------------- /TrollHelper/control: -------------------------------------------------------------------------------- 1 | Package: com.opa334.trollstorehelper 2 | Name: TrollStore Helper 3 | Version: 1.5.1 4 | Architecture: iphoneos-arm 5 | Description: Helper utility to install and manage TrollStore! 6 | Maintainer: opa334 7 | Author: opa334 8 | Section: Applications 9 | -------------------------------------------------------------------------------- /TrollHelper/TSHAppDelegateNoScene.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface TSHAppDelegateNoScene : UIResponder 4 | @property (nonatomic, strong) UIWindow *window; 5 | @property (nonatomic, strong) UINavigationController *rootViewController; 6 | @end -------------------------------------------------------------------------------- /Pwnify/Makefile: -------------------------------------------------------------------------------- 1 | pwnify: 2 | @clang main.m -fobjc-arc -fmodules -mmacosx-version-min=11.0 -o pwnify 3 | 4 | install: pwnify 5 | -@sudo rm /usr/local/bin/pwnify 2>/dev/null || true 6 | @sudo cp ./pwnify /usr/local/bin/pwnify 7 | 8 | clean: 9 | @rm ./pwnify 2>/dev/null || true -------------------------------------------------------------------------------- /RootHelper/control: -------------------------------------------------------------------------------- 1 | Package: com.opa334.trollstoreroothelper 2 | Name: trollstoreroothelper 3 | Version: 1.5.1 4 | Architecture: iphoneos-arm 5 | Description: An awesome tool of some sort!! 6 | Maintainer: opa334 7 | Author: opa334 8 | Section: System 9 | Tag: role::hacker 10 | -------------------------------------------------------------------------------- /TrollStore/TSSettingsListController.h: -------------------------------------------------------------------------------- 1 | #import "TSListControllerShared.h" 2 | 3 | @interface TSSettingsListController : TSListControllerShared 4 | { 5 | PSSpecifier* _installPersistenceHelperSpecifier; 6 | NSString* _newerVersion; 7 | NSString* _newerLdidVersion; 8 | } 9 | @end -------------------------------------------------------------------------------- /TrollStore/TSAppTableViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "TSAppInfo.h" 3 | #import 4 | 5 | @interface TSAppTableViewController : UITableViewController 6 | { 7 | UIImage* _placeholderIcon; 8 | NSArray* _cachedAppInfos; 9 | NSMutableDictionary* _cachedIcons; 10 | UISearchController* _searchController; 11 | NSString* _searchKey; 12 | } 13 | 14 | @end -------------------------------------------------------------------------------- /TrollStore/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "TSAppDelegate.h" 3 | #import "TSUtil.h" 4 | 5 | NSUserDefaults* trollStoreUserDefaults(void) 6 | { 7 | return [[NSUserDefaults alloc] initWithSuiteName:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Preferences/com.opa334.TrollStore.plist"]]; 8 | } 9 | 10 | int main(int argc, char *argv[]) { 11 | @autoreleasepool { 12 | chineseWifiFixup(); 13 | return UIApplicationMain(argc, argv, nil, NSStringFromClass(TSAppDelegate.class)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Shared/TSPresentationDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface TSPresentationDelegate : NSObject 4 | @property (class) UIViewController* presentationViewController; 5 | @property (class) UIAlertController* activityController; 6 | + (void)startActivity:(NSString*)activity withCancelHandler:(void (^)(void))cancelHandler; 7 | + (void)startActivity:(NSString*)activity; 8 | + (void)stopActivityWithCompletion:(void (^)(void))completion; 9 | + (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion; 10 | @end -------------------------------------------------------------------------------- /TrollStore/Makefile: -------------------------------------------------------------------------------- 1 | TARGET := iphone:clang:14.5:14.0 2 | INSTALL_TARGET_PROCESSES = TrollStore 3 | 4 | include $(THEOS)/makefiles/common.mk 5 | 6 | APPLICATION_NAME = TrollStore 7 | 8 | TrollStore_FILES = $(wildcard *.m) $(wildcard ../Shared/*.m) 9 | TrollStore_FRAMEWORKS = UIKit CoreGraphics CoreServices 10 | TrollStore_PRIVATE_FRAMEWORKS = Preferences MobileIcons MobileContainerManager 11 | TrollStore_LIBRARIES = archive 12 | TrollStore_CFLAGS = -fobjc-arc -I../Shared 13 | TrollStore_CODESIGN_FLAGS = -Sentitlements.plist -K../cert.p12 14 | 15 | include $(THEOS_MAKE_PATH)/application.mk 16 | -------------------------------------------------------------------------------- /TrollStore/Resources/fallback.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | application-identifier 6 | TROLLTROLL.* 7 | com.apple.developer.team-identifier 8 | TROLLTROLL 9 | get-task-allow 10 | 11 | keychain-access-groups 12 | 13 | TROLLTROLL.* 14 | com.apple.token 15 | 16 | 17 | -------------------------------------------------------------------------------- /RootHelper/Makefile: -------------------------------------------------------------------------------- 1 | TARGET := iphone:clang:14.5:14.0 2 | ARCHS = arm64 3 | 4 | include $(THEOS)/makefiles/common.mk 5 | 6 | TOOL_NAME = trollstorehelper 7 | 8 | trollstorehelper_FILES = $(wildcard *.m) $(wildcard ../Shared/*.m) 9 | trollstorehelper_CFLAGS = -fobjc-arc -I../Shared 10 | trollstorehelper_CODESIGN_FLAGS = -Sentitlements.plist -K../cert.p12 11 | trollstorehelper_INSTALL_PATH = /usr/local/bin 12 | trollstorehelper_LIBRARIES = archive 13 | trollstorehelper_PRIVATE_FRAMEWORKS = SpringBoardServices BackBoardServices MobileContainerManager 14 | 15 | include $(THEOS_MAKE_PATH)/tool.mk 16 | -------------------------------------------------------------------------------- /TrollHelper/TSHAppDelegateNoScene.m: -------------------------------------------------------------------------------- 1 | #import "TSHAppDelegateNoScene.h" 2 | #import "TSHRootViewController.h" 3 | 4 | @implementation TSHAppDelegateNoScene 5 | 6 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 7 | _window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 8 | _rootViewController = [[UINavigationController alloc] initWithRootViewController:[[TSHRootViewController alloc] init]]; 9 | _window.rootViewController = _rootViewController; 10 | [_window makeKeyAndVisible]; 11 | return YES; 12 | } 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /TrollStore/TSInstallationController.h: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | 3 | @interface TSInstallationController : NSObject 4 | 5 | + (void)presentInstallationAlertIfEnabledForFile:(NSString*)pathToIPA isRemoteInstall:(BOOL)remoteInstall completion:(void (^)(BOOL, NSError*))completionBlock; 6 | 7 | + (void)handleAppInstallFromFile:(NSString*)pathToIPA forceInstall:(BOOL)force completion:(void (^)(BOOL, NSError*))completion; 8 | + (void)handleAppInstallFromFile:(NSString*)pathToIPA completion:(void (^)(BOOL, NSError*))completion; 9 | 10 | + (void)handleAppInstallFromRemoteURL:(NSURL*)remoteURL completion:(void (^)(BOOL, NSError*))completion; 11 | 12 | + (void)installLdid; 13 | 14 | @end -------------------------------------------------------------------------------- /Shared/TSListControllerShared.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | @interface TSListControllerShared : PSListController 6 | - (BOOL)isTrollStore; 7 | - (NSString*)getTrollStoreVersion; 8 | - (void)downloadTrollStoreAndDo:(void (^)(NSString* localTrollStoreTarPath))doHandler; 9 | - (void)installTrollStorePressed; 10 | - (void)updateTrollStorePressed; 11 | - (void)rebuildIconCachePressed; 12 | - (void)refreshAppRegistrationsPressed; 13 | - (void)uninstallPersistenceHelperPressed; 14 | - (void)handleUninstallation; 15 | - (NSMutableArray*)argsForUninstallingTrollStore; 16 | - (void)uninstallTrollStorePressed; 17 | @end -------------------------------------------------------------------------------- /TrollStore/TSApplicationsManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #define TROLLSTORE_ROOT_PATH @"/var/containers/Bundle/TrollStore" 4 | #define TROLLSTORE_MAIN_PATH [TROLLSTORE_ROOT_PATH stringByAppendingPathComponent:@"Main"] 5 | #define TROLLSTORE_APPLICATIONS_PATH [TROLLSTORE_ROOT_PATH stringByAppendingPathComponent:@"Applications"] 6 | 7 | @interface TSApplicationsManager : NSObject 8 | 9 | + (instancetype)sharedInstance; 10 | 11 | - (NSArray*)installedAppPaths; 12 | 13 | - (NSError*)errorForCode:(int)code; 14 | - (int)installIpa:(NSString*)pathToIpa force:(BOOL)force log:(NSString**)logOut; 15 | - (int)installIpa:(NSString*)pathToIpa; 16 | - (int)uninstallApp:(NSString*)appId; 17 | - (int)uninstallAppByPath:(NSString*)path; 18 | - (BOOL)openApplicationWithBundleID:(NSString *)appID; 19 | - (int)changeAppRegistration:(NSString*)appPath toState:(NSString*)newState; 20 | 21 | @end -------------------------------------------------------------------------------- /TrollHelper/Makefile: -------------------------------------------------------------------------------- 1 | export EMBEDDED_ROOT_HELPER ?= 0 2 | 3 | TARGET := iphone:clang:14.5:14.0 4 | INSTALL_TARGET_PROCESSES = TrollStorePersistenceHelper 5 | 6 | include $(THEOS)/makefiles/common.mk 7 | 8 | APPLICATION_NAME = TrollStorePersistenceHelper 9 | 10 | TrollStorePersistenceHelper_FILES = $(wildcard *.m) $(wildcard ../Shared/*.m) 11 | TrollStorePersistenceHelper_FRAMEWORKS = UIKit CoreGraphics CoreServices 12 | TrollStorePersistenceHelper_PRIVATE_FRAMEWORKS = Preferences MobileContainerManager 13 | TrollStorePersistenceHelper_CFLAGS = -fobjc-arc -I../Shared 14 | TrollStorePersistenceHelper_CODESIGN_FLAGS = -Sentitlements.plist -K../cert.p12 15 | 16 | ifeq ($(EMBEDDED_ROOT_HELPER),1) 17 | TrollStorePersistenceHelper_CFLAGS += -DEMBEDDED_ROOT_HELPER=1 18 | TrollStorePersistenceHelper_FILES += $(wildcard ../RootHelper/*.m) 19 | TrollStorePersistenceHelper_LIBRARIES += archive 20 | TrollStorePersistenceHelper_PRIVATE_FRAMEWORKS += SpringBoardServices BackBoardServices 21 | endif 22 | 23 | include $(THEOS_MAKE_PATH)/application.mk -------------------------------------------------------------------------------- /TrollHelper/TSHAppDelegateWithScene.m: -------------------------------------------------------------------------------- 1 | #import "TSHAppDelegateWithScene.h" 2 | 3 | @implementation TSHAppDelegateWithScene 4 | 5 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 6 | return YES; 7 | } 8 | 9 | - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { 10 | // Called when a new scene session is being created. 11 | // Use this method to select a configuration to create the new scene with. 12 | return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; 13 | } 14 | 15 | 16 | - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { 17 | // Called when the user discards a scene session. 18 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 19 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 20 | } 21 | 22 | @end -------------------------------------------------------------------------------- /TrollStore/TSAppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "TSAppDelegate.h" 2 | #import "TSRootViewController.h" 3 | 4 | @implementation TSAppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 7 | return YES; 8 | } 9 | 10 | - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { 11 | // Called when a new scene session is being created. 12 | // Use this method to select a configuration to create the new scene with. 13 | return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; 14 | } 15 | 16 | 17 | - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { 18 | // Called when the user discards a scene session. 19 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 20 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /install_trollhelperota_ios15.md: -------------------------------------------------------------------------------- 1 | # TrollHelperOTA (iOS 15) 2 | 3 | **Supported Devices:** All devices 4 | 5 | **Supported Versions:** iOS 15.0 - 15.5b4 6 | 7 | ## Guide 8 | 9 | 1. On your device, go to the following link: https://api.jailbreaks.app/troll 10 | 11 | 2. An alert should appear, tap "Install" 12 | 13 | 3. When the installation is finished, you will find a "GTA Car Tracker" application on your device. 14 | 15 | 4. If this app has not appeared, that's a stock iOS bug, reboot your device and the app will appear. 16 | 17 | 5. Launch the app, tap "Install TrollStore" 18 | 19 | 6. Wait a few seconds, your device should respring and TrollStore will be installed. 20 | 21 | 7. You can now either delete the "GTA Car Tracker" app, or register it as the persistence helper by opening it and tapping the option at the bottom. If you do this, don't delete the app. 22 | 23 | 8. Open the TrollStore app and press "Install ldid" in the Settings tab, then read the information under "Persistence", and install the Persistence Helper into a system app if you want persistence (not needed if you registered the GTA Car Tracker app as the persistence helper in step 7). 24 | 25 | 9. Done, you can now share IPA files with TrollStore and they will be permanently installed on your device. 26 | -------------------------------------------------------------------------------- /TrollStore/TSRootViewController.m: -------------------------------------------------------------------------------- 1 | #import "TSRootViewController.h" 2 | #import "TSAppTableViewController.h" 3 | #import "TSSettingsListController.h" 4 | #import 5 | 6 | @implementation TSRootViewController 7 | 8 | - (void)loadView { 9 | [super loadView]; 10 | 11 | TSAppTableViewController* appTableVC = [[TSAppTableViewController alloc] init]; 12 | appTableVC.title = @"Apps"; 13 | 14 | TSSettingsListController* settingsListVC = [[TSSettingsListController alloc] init]; 15 | settingsListVC.title = @"Settings"; 16 | 17 | UINavigationController* appNavigationController = [[UINavigationController alloc] initWithRootViewController:appTableVC]; 18 | UINavigationController* settingsNavigationController = [[UINavigationController alloc] initWithRootViewController:settingsListVC]; 19 | 20 | appNavigationController.tabBarItem.image = [UIImage systemImageNamed:@"square.stack.3d.up.fill"]; 21 | settingsNavigationController.tabBarItem.image = [UIImage systemImageNamed:@"gear"]; 22 | 23 | self.title = @"Root View Controller"; 24 | self.viewControllers = @[appNavigationController, settingsNavigationController]; 25 | } 26 | 27 | - (void)viewDidLoad 28 | { 29 | [super viewDidLoad]; 30 | 31 | TSPresentationDelegate.presentationViewController = self; 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /install_trollhelperota_arm64e.md: -------------------------------------------------------------------------------- 1 | # TrollHelperOTA (arm64e) 2 | 3 | **Supported Devices:** All arm64e (A12 - A15) devices 4 | 5 | **Supported Versions:** iOS 14.0 - 15.5b4, 15.6b1 - 15.6b5 6 | 7 | ## Guide 8 | 9 | 1. On your device, go to the following link: https://api.jailbreaks.app/troll64e 10 | 11 | 2. An alert should appear, tap "Install" 12 | 13 | 3. When the installation is finished, you will find a "GTA Car Tracker" application on your device. 14 | 15 | 4. If this app has not appeared, that's a stock iOS bug, reboot your device and the app will appear. 16 | 17 | 5. Launch the app, tap "Install TrollStore" 18 | 19 | 6. Wait a few seconds, your device should respring and TrollStore will be installed. 20 | 21 | 7. You can now either delete the "GTA Car Tracker" app, or register it as the persistence helper by opening it and tapping the option at the bottom. If you do this, don't delete the app. 22 | 23 | 8. Open the TrollStore app and press "Install ldid" in the Settings tab, then read the information under "Persistence", and install the Persistence Helper into a system app if you want persistence (not needed if you registered the GTA Car Tracker app as the persistence helper in step 7). 24 | 25 | 9. Done, you can now share IPA files with TrollStore and they will be permanently installed on your device. 26 | -------------------------------------------------------------------------------- /TrollStore/TSCommonTCCServiceNames.h: -------------------------------------------------------------------------------- 1 | // 2 | // TSCommonTCCServiceNames.h 3 | // IPAInfo 4 | // 5 | // Created by Luke Noble on 30.10.22. 6 | // 7 | 8 | #import 9 | 10 | static NSDictionary* const commonTCCServices = @{ 11 | @"kTCCServicePhotos": @"Photo Library", 12 | @"kTCCServicePhotosAdd": @"Photo Library (Add)", 13 | @"kTCCServiceCamera": @"Camera", 14 | @"kTCCServiceMicrophone": @"Microphone", 15 | @"kTCCServiceAddressBook": @"Contacts", 16 | @"kTCCServiceCalendar": @"Calendars", 17 | @"kTCCServiceReminders": @"Reminders", 18 | @"kTCCServiceWillow": @"HomeKit", 19 | @"kTCCServiceGameCenterFriends": @"Game Center Friends", 20 | @"kTCCServiceExposureNotification": @"Exposure Notifications", 21 | @"kTCCServiceFocusStatus": @"Focus Status", 22 | @"kTCCServiceUserTracking": @"User Tracking", 23 | @"kTCCServiceFaceID": @"Face ID", 24 | @"kTCCServiceMediaLibrary": @"Apple Media Library", 25 | @"kTCCServiceMotion": @"Motion Sensors", 26 | @"kTCCServiceNearbyInteraction": @"Nearby Device Interaction", 27 | @"kTCCServiceBluetoothAlways": @"Bluetooth (Always)", 28 | @"kTCCServiceBluetoothWhileInUse": @"Bluetooth (While In Use)", 29 | @"kTCCServiceBluetoothPeripheral": @"Bluetooth (Peripherals)", 30 | @"kTCCServiceLocation": @"Location" 31 | }; 32 | -------------------------------------------------------------------------------- /install_trollhelper.md: -------------------------------------------------------------------------------- 1 | # TrollHelper 2 | 3 | **Supported Devices:** All jailbroken devices 4 | 5 | **Supported Versions:** iOS 14.0 - 15.5b4, 15.6b1 - 15.6b5 6 | 7 | ## Guide 8 | 9 | 1. Open your package manager, and make sure [Havoc repo](https://havoc.app) is added under Sources, then search for "TrollStore Helper" and install it. 10 | 11 | 2. After the installation, respring and the "TrollHelper" app should have appeared on your home screen. 12 | 13 | 3. Launch the app, tap "Install TrollStore" 14 | 15 | 4. Wait a few seconds, your device should respring and TrollStore will be installed. 16 | 17 | 5. Open the TrollStore app and press "Install ldid" in the Settings tab, then read the information under "Persistence", the TrollHelper app on the home screen will be your persistence helper. 18 | 19 | 6. Done, you can now share IPA files with TrollStore and they will be permanently installed on your device. 20 | 21 | ## Unjailbreaking while retaining TrollStore 22 | 23 | Some people might prefer to use TrollStore in an unjailbroken environment, if that applies to you, follow this guide. 24 | 25 | 1. Uninstall TrollHelper from your package manager 26 | 27 | 2. Now when you launch TrollStore, it will have an option to install the persistence helper into a System app like on iOS 15, do so. 28 | 29 | 3. Now restore rootFS through your jailbreak app, afterwards use the System app to refresh app registrations. 30 | 31 | 4. Done, your device will be jailed, but TrollStore will still work. 32 | -------------------------------------------------------------------------------- /install_sshrd.md: -------------------------------------------------------------------------------- 1 | # SSH Ramdisk 2 | 3 | **Supported Devices:** All checkm8 / arm64 devices 4 | 5 | **Supported Versions:** iOS 14.0 - 15.5b4, 15.6b1 - 15.6b5 6 | 7 | **Additional requirements:** Linux / macOS Computer 8 | 9 | ## Guide 10 | 11 | Video tutorial: https://youtu.be/B0MueVvJSK4 12 | 13 | 1. Run `git clone https://github.com/verygenericname/SSHRD_Script --recursive && cd SSHRD_Script` 14 | 15 | 2. Put your device into DFU mode. Instructions for this can be found [here](https://www.theiphonewiki.com/wiki/DFU_Mode#iPhone.2C_iPad.2C_iPod_touch). 16 | - If you are on an A11 device, enter recovery mode first by pressing and quickly releasing the volume up and volume down button, one at a time. Then, press and hold the side button until you see the recovery mode screen. Finally, put your device into DFU mode as said above. 17 | 18 | 3. Run `./sshrd.sh TrollStore ` 19 | - Make sure to **not** include the `<>` 20 | - The uninstallable system app should be an app you don't need to use (e.g. Tips) 21 | - i.e. `./sshrd.sh 15.0 TrollStore Tips` 22 | 23 | 4. Run `./sshrd.sh boot` the device should start verbosing and show a TrollFace in ascii, then reboot eventually 24 | 25 | 5. Open up the app you replaced (Tips in this example), it should be TrollStore Helper now. 26 | 27 | 6. Make sure you're connected to the internet, and press "Install TrollStore." 28 | 29 | 7. Done, your device will respring and TrollStore should appear on your home screen. 30 | -------------------------------------------------------------------------------- /TrollStore/TSAppInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // TSIPAInfo.h 3 | // IPAInfo 4 | // 5 | // Created by Lars Fröder on 22.10.22. 6 | // 7 | 8 | #import 9 | #import 10 | #import 11 | @import UIKit; 12 | 13 | @interface TSAppInfo : NSObject 14 | { 15 | NSString* _path; 16 | BOOL _isArchive; 17 | struct archive* _archive; 18 | 19 | NSString* _cachedAppBundleName; 20 | NSString* _cachedRegistrationState; 21 | NSDictionary* _cachedInfoDictionary; 22 | NSDictionary* _cachedInfoDictionariesByPluginSubpaths; 23 | NSDictionary* _cachedEntitlementsByBinarySubpaths; 24 | UIImage* _cachedPreviewIcon; 25 | int64_t _cachedSize; 26 | } 27 | 28 | - (instancetype)initWithIPAPath:(NSString*)ipaPath; 29 | - (instancetype)initWithAppBundlePath:(NSString*)bundlePath; 30 | - (NSError*)determineAppBundleName; 31 | - (NSError*)loadInfoDictionary; 32 | - (NSError*)loadEntitlements; 33 | - (NSError*)loadPreviewIcon; 34 | 35 | - (NSError*)sync_loadBasicInfo; 36 | - (NSError*)sync_loadInfo; 37 | 38 | - (void)loadBasicInfoWithCompletion:(void (^)(NSError*))completionHandler; 39 | - (void)loadInfoWithCompletion:(void (^)(NSError*))completionHandler; 40 | 41 | - (NSString*)displayName; 42 | - (NSString*)bundleIdentifier; 43 | - (NSString*)versionString; 44 | - (NSString*)sizeString; 45 | - (NSString*)bundlePath; 46 | - (NSString*)registrationState; 47 | 48 | - (UIImage*)iconForSize:(CGSize)size; 49 | 50 | - (NSAttributedString*)detailedInfoTitle; 51 | - (NSAttributedString*)detailedInfoDescription; 52 | //- (UIImage*)image; 53 | - (void)log; 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /TrollStore/entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | application-identifier 6 | com.opa334.TrollStore 7 | platform-application 8 | 9 | com.apple.security.exception.files.absolute-path.read-write 10 | 11 | / 12 | 13 | com.apple.security.exception.iokit-user-client-class 14 | 15 | AGXDeviceUserClient 16 | IOSurfaceRootUserClient 17 | 18 | com.apple.private.security.no-sandbox 19 | 20 | com.apple.private.persona-mgmt 21 | 22 | com.apple.private.security.container-manager 23 | 24 | com.apple.private.coreservices.canmaplsdatabase 25 | 26 | com.apple.lsapplicationworkspace.rebuildappdatabases 27 | 28 | com.apple.private.MobileContainerManager.allowed 29 | 30 | com.apple.private.MobileInstallationHelperService.InstallDaemonOpsEnabled 31 | 32 | com.apple.private.MobileInstallationHelperService.allowed 33 | 34 | com.apple.private.uninstall.deletion 35 | 36 | com.apple.private.security.storage.MobileDocuments 37 | 38 | com.apple.CommCenter.fine-grained 39 | 40 | cellular-plan 41 | data-usage 42 | data-allowed-write 43 | preferences-write 44 | 45 | com.apple.springboard.opensensitiveurl 46 | 47 | 48 | -------------------------------------------------------------------------------- /RootHelper/entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | platform-application 6 | 7 | com.apple.private.security.container-required 8 | 9 | com.apple.private.security.no-sandbox 10 | 11 | com.apple.private.security.container-manager 12 | 13 | com.apple.private.MobileContainerManager.allowed 14 | 15 | com.apple.private.coreservices.canmaplsdatabase 16 | 17 | com.apple.lsapplicationworkspace.rebuildappdatabases 18 | 19 | com.apple.private.security.storage.AppBundles 20 | 21 | com.apple.private.security.storage.MobileDocuments 22 | 23 | com.apple.private.security.storage-exempt.heritable 24 | 25 | com.apple.private.MobileInstallationHelperService.InstallDaemonOpsEnabled 26 | 27 | com.apple.private.MobileInstallationHelperService.allowed 28 | 29 | com.apple.private.uninstall.deletion 30 | 31 | com.apple.springboard.launchapplications 32 | 33 | com.apple.backboardd.launchapplications 34 | 35 | com.apple.frontboard.launchapplications 36 | 37 | com.apple.multitasking.termination 38 | 39 | com.apple.private.mobileinstall.allowedSPI 40 | 41 | InstallForLaunchServices 42 | Install 43 | UninstallForLaunchServices 44 | Uninstall 45 | UpdatePlaceholderMetadata 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Victim/make_cert.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | export PATH="/opt/homebrew/Cellar/openssl@3/3.0.5/bin:$PATH" 3 | 4 | true && openssl req -newkey rsa:2048 -nodes -keyout root_key.pem -x509 -days 3650 -out root_certificate.pem \ 5 | -subj "/C=CA/O=TrollStore/OU=$1/CN=TrollStore iPhone Root CA" \ 6 | -addext "1.2.840.113635.100.6.2.18=DER:0500" \ 7 | -addext "basicConstraints=critical, CA:true" -addext "keyUsage=critical, digitalSignature, keyCertSign, cRLSign" 8 | true && openssl req -newkey rsa:2048 -nodes -keyout codeca_key.pem -out codeca_certificate.csr \ 9 | -subj "/C=CA/O=TrollStore/OU=$1/CN=TrollStore iPhone Certification Authority" \ 10 | -addext "1.2.840.113635.100.6.2.18=DER:0500" \ 11 | -addext "basicConstraints=critical, CA:true" -addext "keyUsage=critical, keyCertSign, cRLSign" 12 | true && openssl x509 -req -CAkey root_key.pem -CA root_certificate.pem -days 3650 \ 13 | -in codeca_certificate.csr -out codeca_certificate.pem -CAcreateserial -copy_extensions copyall 14 | true && openssl req -newkey rsa:2048 -nodes -keyout dev_key.pem -out dev_certificate.csr \ 15 | -subj "/C=CA/O=TrollStore/OU=$1/CN=TrollStore iPhone OS Application Signing" \ 16 | -addext "basicConstraints=critical, CA:false" \ 17 | -addext "keyUsage = critical, digitalSignature" -addext "extendedKeyUsage = codeSigning" \ 18 | -addext "1.2.840.113635.100.6.1.3=DER:0500" 19 | true && openssl x509 -req -CAkey codeca_key.pem -CA codeca_certificate.pem -days 3650 \ 20 | -in dev_certificate.csr -out dev_certificate.pem -CAcreateserial -copy_extensions copyall 21 | true && cat codeca_certificate.pem root_certificate.pem >certificate_chain.pem 22 | true && /usr/bin/openssl pkcs12 -export -in dev_certificate.pem -inkey dev_key.pem -certfile certificate_chain.pem \ 23 | -keypbe NONE -certpbe NONE -passout pass: \ 24 | -out victim.p12 -name "TrollStore iPhone OS Application Signing" 25 | 26 | rm certificate_chain.pem 27 | rm codeca_certificate.csr 28 | rm codeca_certificate.pem 29 | rm codeca_key.pem 30 | rm dev_certificate.csr 31 | rm dev_certificate.pem 32 | rm dev_key.pem 33 | rm root_certificate.pem 34 | rm root_key.pem -------------------------------------------------------------------------------- /TrollHelper/entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | application-identifier 6 | com.opa334.trollstorepersistencehelper 7 | com.apple.CommCenter.fine-grained 8 | 9 | cellular-plan 10 | data-usage 11 | data-allowed-write 12 | preferences-write 13 | 14 | com.apple.private.persona-mgmt 15 | 16 | 17 | 18 | platform-application 19 | 20 | com.apple.private.security.no-sandbox 21 | 22 | com.apple.private.security.container-manager 23 | 24 | com.apple.private.MobileContainerManager.allowed 25 | 26 | com.apple.private.coreservices.canmaplsdatabase 27 | 28 | com.apple.lsapplicationworkspace.rebuildappdatabases 29 | 30 | com.apple.private.security.storage.AppBundles 31 | 32 | com.apple.private.security.storage.MobileDocuments 33 | 34 | com.apple.private.security.storage-exempt.heritable 35 | 36 | com.apple.private.MobileInstallationHelperService.InstallDaemonOpsEnabled 37 | 38 | com.apple.private.MobileInstallationHelperService.allowed 39 | 40 | com.apple.private.uninstall.deletion 41 | 42 | com.apple.springboard.launchapplications 43 | 44 | com.apple.backboardd.launchapplications 45 | 46 | com.apple.frontboard.launchapplications 47 | 48 | com.apple.multitasking.termination 49 | 50 | com.apple.private.mobileinstall.allowedSPI 51 | 52 | InstallForLaunchServices 53 | Install 54 | UninstallForLaunchServices 55 | Uninstall 56 | UpdatePlaceholderMetadata 57 | 58 | 59 | -------------------------------------------------------------------------------- /Shared/TSPresentationDelegate.m: -------------------------------------------------------------------------------- 1 | #import "TSPresentationDelegate.h" 2 | 3 | @implementation TSPresentationDelegate 4 | 5 | static UIViewController* g_presentationViewController; 6 | static UIAlertController* g_activityController; 7 | 8 | + (UIViewController*)presentationViewController 9 | { 10 | return g_presentationViewController; 11 | } 12 | 13 | + (void)setPresentationViewController:(UIViewController*)vc 14 | { 15 | g_presentationViewController = vc; 16 | } 17 | 18 | + (UIAlertController*)activityController 19 | { 20 | return g_activityController; 21 | } 22 | 23 | + (void)setActivityController:(UIAlertController*)ac 24 | { 25 | g_activityController = ac; 26 | } 27 | 28 | + (void)startActivity:(NSString*)activity withCancelHandler:(void (^)(void))cancelHandler 29 | { 30 | if(self.activityController) 31 | { 32 | self.activityController.title = activity; 33 | } 34 | else 35 | { 36 | self.activityController = [UIAlertController alertControllerWithTitle:activity message:@"" preferredStyle:UIAlertControllerStyleAlert]; 37 | UIActivityIndicatorView* activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(5,5,50,50)]; 38 | activityIndicator.hidesWhenStopped = YES; 39 | activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium; 40 | [activityIndicator startAnimating]; 41 | [self.activityController.view addSubview:activityIndicator]; 42 | 43 | if(cancelHandler) 44 | { 45 | UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action) 46 | { 47 | self.activityController = nil; 48 | cancelHandler(); 49 | }]; 50 | [self.activityController addAction:cancelAction]; 51 | } 52 | 53 | [self presentViewController:self.activityController animated:YES completion:nil]; 54 | } 55 | } 56 | 57 | + (void)startActivity:(NSString*)activity 58 | { 59 | [self startActivity:activity withCancelHandler:nil]; 60 | } 61 | 62 | + (void)stopActivityWithCompletion:(void (^)(void))completionBlock 63 | { 64 | if(!self.activityController) return; 65 | 66 | [self.activityController dismissViewControllerAnimated:YES completion:^ 67 | { 68 | self.activityController = nil; 69 | if(completionBlock) completionBlock(); 70 | }]; 71 | } 72 | 73 | + (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completionBlock 74 | { 75 | [self.presentationViewController presentViewController:viewControllerToPresent animated:flag completion:completionBlock]; 76 | } 77 | 78 | @end -------------------------------------------------------------------------------- /TrollHelper/TSHSceneDelegate.m: -------------------------------------------------------------------------------- 1 | #import "TSHSceneDelegate.h" 2 | #import "TSHRootViewController.h" 3 | 4 | @implementation TSHSceneDelegate 5 | 6 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { 7 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 8 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 9 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 10 | 11 | UIWindowScene* windowScene = (UIWindowScene*)scene; 12 | _window = [[UIWindow alloc] initWithWindowScene:windowScene]; 13 | _rootViewController = [[UINavigationController alloc] initWithRootViewController:[[TSHRootViewController alloc] init]]; 14 | _window.rootViewController = _rootViewController; 15 | [_window makeKeyAndVisible]; 16 | } 17 | 18 | - (void)sceneDidDisconnect:(UIScene *)scene { 19 | // Called as the scene is being released by the system. 20 | // This occurs shortly after the scene enters the background, or when its session is discarded. 21 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 22 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 23 | } 24 | 25 | 26 | - (void)sceneDidBecomeActive:(UIScene *)scene { 27 | // Called when the scene has moved from an inactive state to an active state. 28 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 29 | } 30 | 31 | 32 | - (void)sceneWillResignActive:(UIScene *)scene { 33 | // Called when the scene will move from an active state to an inactive state. 34 | // This may occur due to temporary interruptions (ex. an incoming phone call). 35 | } 36 | 37 | 38 | - (void)sceneWillEnterForeground:(UIScene *)scene { 39 | // Called as the scene transitions from the background to the foreground. 40 | // Use this method to undo the changes made on entering the background. 41 | } 42 | 43 | 44 | - (void)sceneDidEnterBackground:(UIScene *)scene { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | - (void)scene:(UIScene *)scene openURLContexts:(NSSet *)URLContexts 51 | { 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report-issue-template.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Bug Report (No Duplicates Issue Here!) 3 | body: 4 | - type: markdown 5 | attributes: 6 | value: | 7 | # Before post issue please read me 8 | Your issue may already be reported! 9 | Please search on the [issue tracker](https://github.com/opa334/TrollStore/issues) before creating one. 10 | 11 | - type: checkboxes 12 | id: no-duplicates-issue 13 | attributes: 14 | label: No Duplicates Issue 15 | options: 16 | - label: I'm sure I've searched on the issue tracker before creating one. 17 | required: true 18 | 19 | - type: textarea 20 | id: expected-behavior 21 | attributes: 22 | label: Expected Behavior? 23 | description: | 24 | Describing a bug, tell us what should happen 25 | placeholder: Tell us what you **Expected**! 26 | validations: 27 | required: true 28 | 29 | - type: textarea 30 | id: current-behavior 31 | attributes: 32 | label: Current Behavior? 33 | description: | 34 | Describing a bug, tell us what happens instead of the expected behavior 35 | placeholder: Tell us what you **Happened**! 36 | validations: 37 | required: true 38 | 39 | - type: textarea 40 | id: possible-solution 41 | attributes: 42 | label: Possible Solution? 43 | description: | 44 | Not obligatory, but suggest a fix/reason for the bug, 45 | or ideas how to implement the addition or change 46 | placeholder: If not sure leave blank. 47 | value: "-" 48 | 49 | - type: textarea 50 | id: steps-to-reproduce 51 | attributes: 52 | label: Steps to Reproduce 53 | description: | 54 | Provide a link to a live example, or an unambiguous set of steps to 55 | reproduce this bug. Include code to reproduce, if relevant 56 | placeholder: | 57 | 1. 58 | 2. 59 | 3. 60 | value: "-" 61 | 62 | - type: markdown 63 | attributes: 64 | value: | 65 | # Your Environment 66 | 67 | - type: input 68 | id: trollstore-version 69 | attributes: 70 | label: TrollStore Version 71 | description: like TrollInstaller2 72 | validations: 73 | required: true 74 | 75 | - type: input 76 | id: ios-version 77 | attributes: 78 | label: iOS/iPadOS version 79 | description: like iOS 15.1 80 | validations: 81 | required: true 82 | 83 | - type: input 84 | id: idevice-model 85 | attributes: 86 | label: iDevice Model 87 | description: like iPhone 11 88 | validations: 89 | required: true 90 | 91 | - type: input 92 | id: other-env 93 | attributes: 94 | label: Other info of your environment 95 | description: like macOS 12.6, the newest THEOS... 96 | 97 | -------------------------------------------------------------------------------- /RootHelper/unarchive.m: -------------------------------------------------------------------------------- 1 | #import "unarchive.h" 2 | 3 | #include 4 | #include 5 | 6 | static int 7 | copy_data(struct archive *ar, struct archive *aw) 8 | { 9 | int r; 10 | const void *buff; 11 | size_t size; 12 | la_int64_t offset; 13 | 14 | for (;;) { 15 | r = archive_read_data_block(ar, &buff, &size, &offset); 16 | if (r == ARCHIVE_EOF) 17 | return (ARCHIVE_OK); 18 | if (r < ARCHIVE_OK) 19 | return (r); 20 | r = archive_write_data_block(aw, buff, size, offset); 21 | if (r < ARCHIVE_OK) { 22 | fprintf(stderr, "%s\n", archive_error_string(aw)); 23 | return (r); 24 | } 25 | } 26 | } 27 | 28 | int extract(NSString* fileToExtract, NSString* extractionPath) 29 | { 30 | struct archive *a; 31 | struct archive *ext; 32 | struct archive_entry *entry; 33 | int flags; 34 | int r; 35 | 36 | /* Select which attributes we want to restore. */ 37 | flags = ARCHIVE_EXTRACT_TIME; 38 | flags |= ARCHIVE_EXTRACT_PERM; 39 | flags |= ARCHIVE_EXTRACT_ACL; 40 | flags |= ARCHIVE_EXTRACT_FFLAGS; 41 | 42 | a = archive_read_new(); 43 | archive_read_support_format_all(a); 44 | archive_read_support_filter_all(a); 45 | ext = archive_write_disk_new(); 46 | archive_write_disk_set_options(ext, flags); 47 | archive_write_disk_set_standard_lookup(ext); 48 | if ((r = archive_read_open_filename(a, fileToExtract.fileSystemRepresentation, 10240))) 49 | return 1; 50 | for (;;) 51 | { 52 | r = archive_read_next_header(a, &entry); 53 | if (r == ARCHIVE_EOF) 54 | break; 55 | if (r < ARCHIVE_OK) 56 | fprintf(stderr, "%s\n", archive_error_string(a)); 57 | if (r < ARCHIVE_WARN) 58 | return 1; 59 | 60 | NSString* currentFile = [NSString stringWithUTF8String:archive_entry_pathname(entry)]; 61 | NSString* fullOutputPath = [extractionPath stringByAppendingPathComponent:currentFile]; 62 | //printf("extracting %@ to %@\n", currentFile, fullOutputPath); 63 | archive_entry_set_pathname(entry, fullOutputPath.fileSystemRepresentation); 64 | 65 | r = archive_write_header(ext, entry); 66 | if (r < ARCHIVE_OK) 67 | fprintf(stderr, "%s\n", archive_error_string(ext)); 68 | else if (archive_entry_size(entry) > 0) { 69 | r = copy_data(a, ext); 70 | if (r < ARCHIVE_OK) 71 | fprintf(stderr, "%s\n", archive_error_string(ext)); 72 | if (r < ARCHIVE_WARN) 73 | return 1; 74 | } 75 | r = archive_write_finish_entry(ext); 76 | if (r < ARCHIVE_OK) 77 | fprintf(stderr, "%s\n", archive_error_string(ext)); 78 | if (r < ARCHIVE_WARN) 79 | return 1; 80 | } 81 | archive_read_close(a); 82 | archive_read_free(a); 83 | archive_write_close(ext); 84 | archive_write_free(ext); 85 | 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /TrollHelper/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "TSHAppDelegateNoScene.h" 3 | #import "TSHAppDelegateWithScene.h" 4 | #import "TSHSceneDelegate.h" 5 | #import 6 | #import 7 | 8 | BOOL sceneDelegateFix(void) 9 | { 10 | NSString* sceneDelegateClassName = nil; 11 | 12 | NSDictionary* UIApplicationSceneManifest = [NSBundle.mainBundle objectForInfoDictionaryKey:@"UIApplicationSceneManifest"]; 13 | if(UIApplicationSceneManifest && [UIApplicationSceneManifest isKindOfClass:NSDictionary.class]) 14 | { 15 | NSDictionary* UISceneConfiguration = UIApplicationSceneManifest[@"UISceneConfigurations"]; 16 | if(UISceneConfiguration && [UISceneConfiguration isKindOfClass:NSDictionary.class]) 17 | { 18 | NSArray* UIWindowSceneSessionRoleApplication = UISceneConfiguration[@"UIWindowSceneSessionRoleApplication"]; 19 | if(UIWindowSceneSessionRoleApplication && [UIWindowSceneSessionRoleApplication isKindOfClass:NSArray.class]) 20 | { 21 | NSDictionary* sceneToUse = nil; 22 | if(UIWindowSceneSessionRoleApplication.count > 1) 23 | { 24 | for(NSDictionary* scene in UIWindowSceneSessionRoleApplication) 25 | { 26 | if([scene isKindOfClass:NSDictionary.class]) 27 | { 28 | NSString* UISceneConfigurationName = scene[@"UISceneConfigurationName"]; 29 | if([UISceneConfigurationName isKindOfClass:NSString.class]) 30 | { 31 | if([UISceneConfigurationName isEqualToString:@"Default Configuration"]) 32 | { 33 | sceneToUse = scene; 34 | break; 35 | } 36 | } 37 | } 38 | } 39 | 40 | if(!sceneToUse) 41 | { 42 | sceneToUse = UIWindowSceneSessionRoleApplication.firstObject; 43 | } 44 | } 45 | else 46 | { 47 | sceneToUse = UIWindowSceneSessionRoleApplication.firstObject; 48 | } 49 | 50 | if(sceneToUse && [sceneToUse isKindOfClass:NSDictionary.class]) 51 | { 52 | sceneDelegateClassName = sceneToUse[@"UISceneDelegateClassName"]; 53 | } 54 | } 55 | } 56 | } 57 | 58 | if(sceneDelegateClassName && [sceneDelegateClassName isKindOfClass:NSString.class]) 59 | { 60 | Class newClass = objc_allocateClassPair([TSHSceneDelegate class], sceneDelegateClassName.UTF8String, 0); 61 | objc_registerClassPair(newClass); 62 | return YES; 63 | } 64 | 65 | return NO; 66 | } 67 | 68 | int main(int argc, char *argv[], char *envp[]) { 69 | @autoreleasepool { 70 | #ifdef EMBEDDED_ROOT_HELPER 71 | extern int rootHelperMain(int argc, char *argv[], char *envp[]); 72 | if(getuid() == 0) 73 | { 74 | // I got this idea while taking a dump 75 | // Don't judge 76 | return rootHelperMain(argc, argv, envp); 77 | } 78 | #endif 79 | 80 | chineseWifiFixup(); 81 | if(sceneDelegateFix()) 82 | { 83 | return UIApplicationMain(argc, argv, nil, NSStringFromClass(TSHAppDelegateWithScene.class)); 84 | } 85 | else 86 | { 87 | return UIApplicationMain(argc, argv, nil, NSStringFromClass(TSHAppDelegateNoScene.class)); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Shared/TSUtil.h: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | #import "CoreServices.h" 3 | 4 | #define TrollStoreErrorDomain @"TrollStoreErrorDomain" 5 | 6 | extern void chineseWifiFixup(void); 7 | extern NSString* safe_getExecutablePath(); 8 | extern NSString* rootHelperPath(void); 9 | extern NSString* getNSStringFromFile(int fd); 10 | extern void printMultilineNSString(NSString* stringToPrint); 11 | extern int spawnRoot(NSString* path, NSArray* args, NSString** stdOut, NSString** stdErr); 12 | extern void killall(NSString* processName, BOOL softly); 13 | extern void respring(void); 14 | extern void fetchLatestTrollStoreVersion(void (^completionHandler)(NSString* latestVersion)); 15 | extern void fetchLatestLdidVersion(void (^completionHandler)(NSString* latestVersion)); 16 | 17 | extern NSArray* trollStoreInstalledAppBundlePaths(); 18 | extern NSArray* trollStoreInstalledAppContainerPaths(); 19 | extern NSString* trollStorePath(); 20 | extern NSString* trollStoreAppPath(); 21 | 22 | extern BOOL isRemovableSystemApp(NSString* appId); 23 | 24 | #import 25 | 26 | @interface UIAlertController (Private) 27 | @property (setter=_setAttributedTitle:,getter=_attributedTitle,nonatomic,copy) NSAttributedString* attributedTitle; 28 | @property (setter=_setAttributedMessage:,getter=_attributedMessage,nonatomic,copy) NSAttributedString* attributedMessage; 29 | @property (nonatomic,retain) UIImage* image; 30 | @end 31 | 32 | typedef enum 33 | { 34 | PERSISTENCE_HELPER_TYPE_USER = 1 << 0, 35 | PERSISTENCE_HELPER_TYPE_SYSTEM = 1 << 1, 36 | PERSISTENCE_HELPER_TYPE_ALL = PERSISTENCE_HELPER_TYPE_USER | PERSISTENCE_HELPER_TYPE_SYSTEM 37 | } PERSISTENCE_HELPER_TYPE; 38 | 39 | extern LSApplicationProxy* findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE allowedTypes); 40 | 41 | typedef struct __SecCode const *SecStaticCodeRef; 42 | 43 | typedef CF_OPTIONS(uint32_t, SecCSFlags) { 44 | kSecCSDefaultFlags = 0 45 | }; 46 | #define kSecCSRequirementInformation 1 << 2 47 | #define kSecCSSigningInformation 1 << 1 48 | 49 | OSStatus SecStaticCodeCreateWithPathAndAttributes(CFURLRef path, SecCSFlags flags, CFDictionaryRef attributes, SecStaticCodeRef *staticCode); 50 | OSStatus SecCodeCopySigningInformation(SecStaticCodeRef code, SecCSFlags flags, CFDictionaryRef *information); 51 | CFDataRef SecCertificateCopyExtensionValue(SecCertificateRef certificate, CFTypeRef extensionOID, bool *isCritical); 52 | void SecPolicySetOptionsValue(SecPolicyRef policy, CFStringRef key, CFTypeRef value); 53 | 54 | extern CFStringRef kSecCodeInfoEntitlementsDict; 55 | extern CFStringRef kSecCodeInfoCertificates; 56 | extern CFStringRef kSecPolicyAppleiPhoneApplicationSigning; 57 | extern CFStringRef kSecPolicyAppleiPhoneProfileApplicationSigning; 58 | extern CFStringRef kSecPolicyLeafMarkerOid; 59 | 60 | extern SecStaticCodeRef getStaticCodeRef(NSString *binaryPath); 61 | extern NSDictionary* dumpEntitlements(SecStaticCodeRef codeRef); 62 | extern NSDictionary* dumpEntitlementsFromBinaryAtPath(NSString *binaryPath); 63 | extern NSDictionary* dumpEntitlementsFromBinaryData(NSData* binaryData); -------------------------------------------------------------------------------- /Shared/CoreServices.h: -------------------------------------------------------------------------------- 1 | extern NSString *LSInstallTypeKey; 2 | 3 | @interface LSBundleProxy 4 | @property (nonatomic,readonly) NSString * bundleIdentifier; 5 | @property (nonatomic) NSURL* dataContainerURL; 6 | @property (nonatomic,readonly) NSURL* bundleContainerURL; 7 | -(NSString*)localizedName; 8 | @end 9 | 10 | @interface LSApplicationProxy : LSBundleProxy 11 | + (instancetype)applicationProxyForIdentifier:(NSString*)identifier; 12 | + (instancetype)applicationProxyForBundleURL:(NSURL*)bundleURL; 13 | @property NSURL* bundleURL; 14 | @property NSString* bundleType; 15 | @property NSString* canonicalExecutablePath; 16 | @property (nonatomic,readonly) NSDictionary* groupContainerURLs; 17 | @property (nonatomic,readonly) NSArray* plugInKitPlugins; 18 | @property (getter=isInstalled,nonatomic,readonly) BOOL installed; 19 | @property (getter=isPlaceholder,nonatomic,readonly) BOOL placeholder; 20 | @property (getter=isRestricted,nonatomic,readonly) BOOL restricted; 21 | @property (nonatomic,readonly) NSSet* claimedURLSchemes; 22 | @property (nonatomic,readonly) NSString* applicationType; 23 | @end 24 | 25 | @interface LSApplicationWorkspace : NSObject 26 | + (instancetype)defaultWorkspace; 27 | - (BOOL)registerApplicationDictionary:(NSDictionary*)dict; 28 | - (BOOL)unregisterApplication:(id)arg1; 29 | - (BOOL)_LSPrivateRebuildApplicationDatabasesForSystemApps:(BOOL)arg1 internal:(BOOL)arg2 user:(BOOL)arg3; 30 | - (BOOL)openApplicationWithBundleID:(NSString *)arg1 ; 31 | - (void)enumerateApplicationsOfType:(NSUInteger)type block:(void (^)(LSApplicationProxy*))block; 32 | - (BOOL)installApplication:(NSURL*)appPackageURL withOptions:(NSDictionary*)options error:(NSError**)error; 33 | - (BOOL)uninstallApplication:(NSString*)appId withOptions:(NSDictionary*)options; 34 | - (void)addObserver:(id)arg1; 35 | - (void)removeObserver:(id)arg1; 36 | @end 37 | 38 | @protocol LSApplicationWorkspaceObserverProtocol 39 | @optional 40 | -(void)applicationsDidInstall:(id)arg1; 41 | -(void)applicationsDidUninstall:(id)arg1; 42 | @end 43 | 44 | @interface LSEnumerator : NSEnumerator 45 | @property (nonatomic,copy) NSPredicate * predicate; 46 | + (instancetype)enumeratorForApplicationProxiesWithOptions:(NSUInteger)options; 47 | @end 48 | 49 | @interface LSPlugInKitProxy : LSBundleProxy 50 | @property (nonatomic,readonly) NSString* pluginIdentifier; 51 | @property (nonatomic,readonly) NSDictionary * pluginKitDictionary; 52 | + (instancetype)pluginKitProxyForIdentifier:(NSString*)arg1; 53 | @end 54 | 55 | @interface MCMContainer : NSObject 56 | + (id)containerWithIdentifier:(id)arg1 createIfNecessary:(BOOL)arg2 existed:(BOOL*)arg3 error:(id*)arg4; 57 | @property (nonatomic,readonly) NSURL * url; 58 | @end 59 | 60 | @interface MCMDataContainer : MCMContainer 61 | @end 62 | 63 | @interface MCMAppDataContainer : MCMDataContainer 64 | @end 65 | 66 | @interface MCMAppContainer : MCMContainer 67 | @end 68 | 69 | @interface MCMPluginKitPluginDataContainer : MCMDataContainer 70 | @end 71 | 72 | @interface MCMSystemDataContainer : MCMContainer 73 | @end 74 | 75 | @interface MCMSharedDataContainer : MCMContainer 76 | @end -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TOPTARGETS := all clean 2 | 3 | $(TOPTARGETS): pre_build make_roothelper make_trollstore make_trollhelper make_trollhelper_package assemble_trollstore make_trollhelper_embedded build_installer15 build_installer64e 4 | 5 | pre_build: 6 | @rm -rf ./_build 2>/dev/null || true 7 | @mkdir -p ./_build 8 | 9 | make_roothelper: 10 | @$(MAKE) -C ./RootHelper FINALPACKAGE=1 $(MAKECMDGOALS) 11 | 12 | make_trollstore: 13 | @$(MAKE) -C ./TrollStore FINALPACKAGE=1 $(MAKECMDGOALS) 14 | 15 | make_trollhelper: 16 | @$(MAKE) -C ./TrollStore FINALPACKAGE=1 $(MAKECMDGOALS) 17 | 18 | ifneq ($(MAKECMDGOALS),clean) 19 | 20 | make_trollhelper_package: 21 | @$(MAKE) clean -C ./TrollHelper 22 | @cp ./RootHelper/.theos/obj/trollstorehelper ./TrollHelper/Resources/trollstorehelper 23 | @$(MAKE) -C ./TrollHelper FINALPACKAGE=1 package $(MAKECMDGOALS) 24 | @rm ./TrollHelper/Resources/trollstorehelper 25 | 26 | make_trollhelper_embedded: 27 | @$(MAKE) clean -C ./TrollHelper 28 | @$(MAKE) -C ./TrollHelper FINALPACKAGE=1 EMBEDDED_ROOT_HELPER=1 $(MAKECMDGOALS) 29 | 30 | assemble_trollstore: 31 | @cp cert.p12 ./TrollStore/.theos/obj/TrollStore.app/cert.p12 32 | @cp ./RootHelper/.theos/obj/trollstorehelper ./TrollStore/.theos/obj/TrollStore.app/trollstorehelper 33 | @cp ./TrollHelper/.theos/obj/TrollStorePersistenceHelper.app/TrollStorePersistenceHelper ./TrollStore/.theos/obj/TrollStore.app/PersistenceHelper 34 | @export COPYFILE_DISABLE=1 35 | @tar -czvf ./_build/TrollStore.tar -C ./TrollStore/.theos/obj TrollStore.app 36 | 37 | build_installer15: 38 | @mkdir -p ./_build/tmp15 39 | @unzip ./Victim/InstallerVictim.ipa -d ./_build/tmp15 40 | @cp ./TrollHelper/.theos/obj/TrollStorePersistenceHelper.app/TrollStorePersistenceHelper ./_build/TrollStorePersistenceHelperToInject 41 | @pwnify set-cpusubtype ./_build/TrollStorePersistenceHelperToInject 1 42 | @ldid -s -K./Victim/victim.p12 ./_build/TrollStorePersistenceHelperToInject 43 | APP_PATH=$$(find ./_build/tmp15/Payload -name "*" -depth 1) ; \ 44 | APP_NAME=$$(basename $$APP_PATH) ; \ 45 | BINARY_NAME=$$(echo "$$APP_NAME" | cut -f 1 -d '.') ; \ 46 | echo $$BINARY_NAME ; \ 47 | pwnify pwn ./_build/tmp15/Payload/$$APP_NAME/$$BINARY_NAME ./_build/TrollStorePersistenceHelperToInject 48 | @pushd ./_build/tmp15 ; \ 49 | zip -vrD ../../_build/TrollHelper_iOS15.ipa * ; \ 50 | popd 51 | @rm ./_build/TrollStorePersistenceHelperToInject 52 | @rm -rf ./_build/tmp15 53 | 54 | build_installer64e: 55 | @mkdir -p ./_build/tmp64e 56 | @unzip ./Victim/InstallerVictim.ipa -d ./_build/tmp64e 57 | APP_PATH=$$(find ./_build/tmp64e/Payload -name "*" -depth 1) ; \ 58 | APP_NAME=$$(basename $$APP_PATH) ; \ 59 | BINARY_NAME=$$(echo "$$APP_NAME" | cut -f 1 -d '.') ; \ 60 | echo $$BINARY_NAME ; \ 61 | pwnify pwn64e ./_build/tmp64e/Payload/$$APP_NAME/$$BINARY_NAME ./TrollHelper/.theos/obj/TrollStorePersistenceHelper.app/TrollStorePersistenceHelper 62 | @pushd ./_build/tmp64e ; \ 63 | zip -vrD ../../_build/TrollHelper_arm64e.ipa * ; \ 64 | popd 65 | @rm -rf ./_build/tmp64e 66 | endif 67 | 68 | .PHONY: $(TOPTARGETS) pre_build assemble_trollstore make_trollhelper_package make_trollhelper_embedded build_installer15 build_installer64e -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: TrollStore 3 | Upstream-Contact: opa334 4 | Source: https://github.com/opa334/TrollStore 5 | 6 | Files: * 7 | Copyright: 2022 Lars Fröder 8 | License: MIT 9 | 10 | Files: RootHelper/uicache.m 11 | Copyright: Copyright (c) 2019 CoolStar, 12 | Modified work Copyright (c) 2020-2022 Procursus Team 13 | Modified work Copyright (c) 2022 Lars Fröder 14 | License: BSD-4-Clause 15 | 16 | License: BSD-4-Clause 17 | Redistribution and use in source and binary forms, with or without 18 | modification, are permitted provided that the following conditions 19 | are met: 20 | 1. Redistributions of source code must retain the above copyright 21 | notice, this list of conditions and the following disclaimer. 22 | 2. Redistributions in binary form must reproduce the above copyright 23 | notice, this list of conditions and the following disclaimer in the 24 | documentation and/or other materials provided with the distribution. 25 | 3. All advertising materials mentioning features or use of this software 26 | must display the following acknowledgement: 27 | This product includes software developed by CoolStar. 28 | 4. Neither the name of the copyright holder nor the names of its contributors 29 | may be used to endorse or promote products derived from this software 30 | without specific prior written permission. 31 | . 32 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 33 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 34 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 35 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE HOLDERS OR 36 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 37 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 38 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 39 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 40 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 41 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 42 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 43 | 44 | License: MIT 45 | Permission is hereby granted, free of charge, to any person obtaining a 46 | copy of this software and associated documentation files (the "Software"), 47 | to deal in the Software without restriction, including without limitation 48 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 49 | and/or sell copies of the Software, and to permit persons to whom the 50 | Software is furnished to do so, subject to the following conditions: 51 | 52 | The above copyright notice and this permission notice shall be included 53 | in all copies or substantial portions of the Software. 54 | 55 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 56 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 57 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 58 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 59 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 60 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 61 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /TrollHelper/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleExecutable 6 | TrollStorePersistenceHelper 7 | CFBundleDisplayName 8 | TrollHelper 9 | CFBundleIcons 10 | 11 | CFBundlePrimaryIcon 12 | 13 | CFBundleIconFiles 14 | 15 | AppIcon29x29 16 | AppIcon40x40 17 | AppIcon57x57 18 | AppIcon60x60 19 | 20 | UIPrerenderedIcon 21 | 22 | 23 | 24 | CFBundleIcons~ipad 25 | 26 | CFBundlePrimaryIcon 27 | 28 | CFBundleIconFiles 29 | 30 | AppIcon29x29 31 | AppIcon40x40 32 | AppIcon57x57 33 | AppIcon60x60 34 | AppIcon50x50 35 | AppIcon72x72 36 | AppIcon76x76 37 | 38 | UIPrerenderedIcon 39 | 40 | 41 | 42 | CFBundleIdentifier 43 | com.opa334.trollstorepersistencehelper 44 | CFBundleInfoDictionaryVersion 45 | 6.0 46 | CFBundlePackageType 47 | APPL 48 | CFBundleSignature 49 | ???? 50 | CFBundleSupportedPlatforms 51 | 52 | iPhoneOS 53 | 54 | CFBundleVersion 55 | 1.5.1 56 | LSRequiresIPhoneOS 57 | 58 | UIDeviceFamily 59 | 60 | 1 61 | 2 62 | 63 | UIRequiredDeviceCapabilities 64 | 65 | armv7 66 | 67 | UILaunchImageFile 68 | LaunchImage 69 | UILaunchImages 70 | 71 | 72 | UILaunchImageMinimumOSVersion 73 | 7.0 74 | UILaunchImageName 75 | LaunchImage 76 | UILaunchImageOrientation 77 | Portrait 78 | UILaunchImageSize 79 | {320, 480} 80 | 81 | 82 | UILaunchImageMinimumOSVersion 83 | 7.0 84 | UILaunchImageName 85 | LaunchImage-700-568h 86 | UILaunchImageOrientation 87 | Portrait 88 | UILaunchImageSize 89 | {320, 568} 90 | 91 | 92 | UILaunchImageMinimumOSVersion 93 | 7.0 94 | UILaunchImageName 95 | LaunchImage-Portrait 96 | UILaunchImageOrientation 97 | Portrait 98 | UILaunchImageSize 99 | {768, 1024} 100 | 101 | 102 | UILaunchImageMinimumOSVersion 103 | 7.0 104 | UILaunchImageName 105 | LaunchImage-Landscape 106 | UILaunchImageOrientation 107 | Landscape 108 | UILaunchImageSize 109 | {768, 1024} 110 | 111 | 112 | UILaunchImageMinimumOSVersion 113 | 8.0 114 | UILaunchImageName 115 | LaunchImage-800-667h 116 | UILaunchImageOrientation 117 | Portrait 118 | UILaunchImageSize 119 | {375, 667} 120 | 121 | 122 | UILaunchImageMinimumOSVersion 123 | 8.0 124 | UILaunchImageName 125 | LaunchImage-800-Portrait-736h 126 | UILaunchImageOrientation 127 | Portrait 128 | UILaunchImageSize 129 | {414, 736} 130 | 131 | 132 | UILaunchImageMinimumOSVersion 133 | 8.0 134 | UILaunchImageName 135 | LaunchImage-800-Landscape-736h 136 | UILaunchImageOrientation 137 | Landscape 138 | UILaunchImageSize 139 | {414, 736} 140 | 141 | 142 | UISupportedInterfaceOrientations 143 | 144 | UIInterfaceOrientationPortrait 145 | UIInterfaceOrientationLandscapeLeft 146 | UIInterfaceOrientationLandscapeRight 147 | 148 | UISupportedInterfaceOrientations~ipad 149 | 150 | UIInterfaceOrientationPortrait 151 | UIInterfaceOrientationPortraitUpsideDown 152 | UIInterfaceOrientationLandscapeLeft 153 | UIInterfaceOrientationLandscapeRight 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /TrollStore/TSSceneDelegate.m: -------------------------------------------------------------------------------- 1 | #import "TSSceneDelegate.h" 2 | #import "TSRootViewController.h" 3 | #import "TSUtil.h" 4 | #import "TSInstallationController.h" 5 | #import 6 | 7 | @implementation TSSceneDelegate 8 | 9 | - (void)handleURLContexts:(NSSet*)URLContexts scene:(UIWindowScene*)scene 10 | { 11 | for(UIOpenURLContext* context in URLContexts) 12 | { 13 | NSURL* url = context.URL; 14 | 15 | if(url) 16 | { 17 | if([url isFileURL]) 18 | { 19 | [url startAccessingSecurityScopedResource]; 20 | void (^doneBlock)(BOOL) = ^(BOOL shouldExit) 21 | { 22 | [url stopAccessingSecurityScopedResource]; 23 | [[NSFileManager defaultManager] removeItemAtURL:url error:nil]; 24 | 25 | if(shouldExit) 26 | { 27 | NSLog(@"Respring + Exit"); 28 | respring(); 29 | exit(0); 30 | } 31 | }; 32 | 33 | if ([url.pathExtension.lowercaseString isEqualToString:@"ipa"] || [url.pathExtension.lowercaseString isEqualToString:@"tipa"]) 34 | { 35 | [TSInstallationController presentInstallationAlertIfEnabledForFile:url.path isRemoteInstall:NO completion:^(BOOL success, NSError* error){ 36 | doneBlock(NO); 37 | }]; 38 | } 39 | else if([url.pathExtension.lowercaseString isEqualToString:@"tar"]) 40 | { 41 | // Update TrollStore itself 42 | NSLog(@"Updating TrollStore..."); 43 | int ret = spawnRoot(rootHelperPath(), @[@"install-trollstore", url.path], nil, nil); 44 | doneBlock(ret == 0); 45 | NSLog(@"Updated TrollStore!"); 46 | } 47 | } 48 | else if([url.scheme isEqualToString:@"apple-magnifier"]) 49 | { 50 | NSURLComponents* components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; 51 | if([components.host isEqualToString:@"install"]) 52 | { 53 | NSString* URLStringToInstall; 54 | 55 | for(NSURLQueryItem* queryItem in components.queryItems) 56 | { 57 | if([queryItem.name isEqualToString:@"url"]) 58 | { 59 | URLStringToInstall = queryItem.value; 60 | break; 61 | } 62 | } 63 | 64 | if(URLStringToInstall && [URLStringToInstall isKindOfClass:NSString.class]) 65 | { 66 | NSURL* URLToInstall = [NSURL URLWithString:URLStringToInstall]; 67 | [TSInstallationController handleAppInstallFromRemoteURL:URLToInstall completion:nil]; 68 | } 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | // We want to auto install ldid if either it doesn't exist 76 | // or if it's the one from an old TrollStore version that's no longer supported 77 | - (void)handleLdidCheck 78 | { 79 | NSString* tsAppPath = [NSBundle mainBundle].bundlePath; 80 | 81 | NSString* ldidPath = [tsAppPath stringByAppendingPathComponent:@"ldid"]; 82 | NSString* ldidVersionPath = [tsAppPath stringByAppendingPathComponent:@"ldid.version"]; 83 | 84 | if(![[NSFileManager defaultManager] fileExistsAtPath:ldidPath] || ![[NSFileManager defaultManager] fileExistsAtPath:ldidVersionPath]) 85 | { 86 | [TSInstallationController installLdid]; 87 | } 88 | } 89 | 90 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { 91 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 92 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 93 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 94 | 95 | UIWindowScene* windowScene = (UIWindowScene*)scene; 96 | _window = [[UIWindow alloc] initWithWindowScene:windowScene]; 97 | _rootViewController = [[TSRootViewController alloc] init]; 98 | _window.rootViewController = _rootViewController; 99 | [_window makeKeyAndVisible]; 100 | 101 | if(connectionOptions.URLContexts.count) 102 | { 103 | [self handleURLContexts:connectionOptions.URLContexts scene:(UIWindowScene*)scene]; 104 | } 105 | else 106 | { 107 | [self handleLdidCheck]; 108 | } 109 | } 110 | 111 | 112 | - (void)sceneDidDisconnect:(UIScene *)scene { 113 | // Called as the scene is being released by the system. 114 | // This occurs shortly after the scene enters the background, or when its session is discarded. 115 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 116 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 117 | } 118 | 119 | 120 | - (void)sceneDidBecomeActive:(UIScene *)scene { 121 | // Called when the scene has moved from an inactive state to an active state. 122 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 123 | } 124 | 125 | 126 | - (void)sceneWillResignActive:(UIScene *)scene { 127 | // Called when the scene will move from an active state to an inactive state. 128 | // This may occur due to temporary interruptions (ex. an incoming phone call). 129 | } 130 | 131 | 132 | - (void)sceneWillEnterForeground:(UIScene *)scene { 133 | // Called as the scene transitions from the background to the foreground. 134 | // Use this method to undo the changes made on entering the background. 135 | } 136 | 137 | 138 | - (void)sceneDidEnterBackground:(UIScene *)scene { 139 | // Called as the scene transitions from the foreground to the background. 140 | // Use this method to save data, release shared resources, and store enough scene-specific state information 141 | // to restore the scene back to its current state. 142 | } 143 | 144 | - (void)scene:(UIScene *)scene openURLContexts:(NSSet *)URLContexts 145 | { 146 | [self handleURLContexts:URLContexts scene:(UIWindowScene*)scene]; 147 | } 148 | 149 | @end -------------------------------------------------------------------------------- /TrollStore/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleExecutable 6 | TrollStore 7 | CFBundleIcons 8 | 9 | CFBundlePrimaryIcon 10 | 11 | CFBundleIconFiles 12 | 13 | AppIcon29x29 14 | AppIcon40x40 15 | AppIcon57x57 16 | AppIcon60x60 17 | 18 | UIPrerenderedIcon 19 | 20 | 21 | 22 | CFBundleIcons~ipad 23 | 24 | CFBundlePrimaryIcon 25 | 26 | CFBundleIconFiles 27 | 28 | AppIcon29x29 29 | AppIcon40x40 30 | AppIcon57x57 31 | AppIcon60x60 32 | AppIcon50x50 33 | AppIcon72x72 34 | AppIcon76x76 35 | 36 | UIPrerenderedIcon 37 | 38 | 39 | 40 | CFBundleIdentifier 41 | com.opa334.TrollStore 42 | CFBundleInfoDictionaryVersion 43 | 6.0 44 | CFBundlePackageType 45 | APPL 46 | CFBundleSignature 47 | ???? 48 | CFBundleSupportedPlatforms 49 | 50 | iPhoneOS 51 | 52 | CFBundleVersion 53 | 1.5.1 54 | LSRequiresIPhoneOS 55 | 56 | UIDeviceFamily 57 | 58 | 1 59 | 2 60 | 61 | UIRequiredDeviceCapabilities 62 | 63 | armv7 64 | 65 | UILaunchStoryboardName 66 | LaunchScreen 67 | UISupportedInterfaceOrientations 68 | 69 | UIInterfaceOrientationPortrait 70 | UIInterfaceOrientationLandscapeLeft 71 | UIInterfaceOrientationLandscapeRight 72 | 73 | UISupportedInterfaceOrientations~ipad 74 | 75 | UIInterfaceOrientationPortrait 76 | UIInterfaceOrientationPortraitUpsideDown 77 | UIInterfaceOrientationLandscapeLeft 78 | UIInterfaceOrientationLandscapeRight 79 | 80 | UIApplicationSceneManifest 81 | 82 | UIApplicationSupportsMultipleScenes 83 | 84 | UISceneConfigurations 85 | 86 | UIWindowSceneSessionRoleApplication 87 | 88 | 89 | UISceneConfigurationName 90 | Default Configuration 91 | UISceneDelegateClassName 92 | TSSceneDelegate 93 | 94 | 95 | 96 | 97 | UTImportedTypeDeclarations 98 | 99 | 100 | UTTypeConformsTo 101 | 102 | public.data 103 | 104 | UTTypeDescription 105 | iOS App 106 | UTTypeIconFiles 107 | 108 | UTTypeIdentifier 109 | com.apple.itunes.ipa 110 | UTTypeTagSpecification 111 | 112 | public.filename-extension 113 | 114 | ipa 115 | 116 | public.mime-type 117 | 118 | 119 | 120 | 121 | CFBundleDocumentTypes 122 | 123 | 124 | CFBundleTypeName 125 | iOS App 126 | LSHandlerRank 127 | Default 128 | LSItemContentTypes 129 | 130 | com.apple.itunes.ipa 131 | 132 | 133 | 134 | CFBundleTypeName 135 | TrollStore Update 136 | LSHandlerRank 137 | Default 138 | LSItemContentTypes 139 | 140 | public.tar-archive 141 | 142 | 143 | 144 | CFBundleTypeName 145 | AirDrop friendly iOS app 146 | CFBundleTypeRole 147 | Viewer 148 | LSHandlerRank 149 | Owner 150 | LSItemContentTypes 151 | 152 | com.opa334.trollstore.tipa 153 | 154 | 155 | 156 | UTExportedTypeDeclarations 157 | 158 | 159 | UTTypeIdentifier 160 | com.opa334.trollstore.tipa 161 | UTTypeDescription 162 | AirDrop friendly iOS app 163 | UTTypeConformsTo 164 | 165 | public.data 166 | 167 | UTTypeTagSpecification 168 | 169 | public.filename-extension 170 | 171 | tipa 172 | 173 | public.mime-type 174 | application/trollstore-ipa 175 | 176 | 177 | 178 | CFBundleURLTypes 179 | 180 | 181 | CFBundleURLName 182 | com.apple.Magnifier 183 | CFBundleURLSchemes 184 | 185 | apple-magnifier 186 | 187 | 188 | 189 | LSSupportsOpeningDocumentsInPlace 190 | 191 | TSRootBinaries 192 | 193 | trollstorehelper 194 | ldid 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /TrollStore/TSSettingsAdvancedListController.m: -------------------------------------------------------------------------------- 1 | #import "TSSettingsAdvancedListController.h" 2 | #import 3 | 4 | extern NSUserDefaults* trollStoreUserDefaults(); 5 | @interface PSSpecifier () 6 | @property (nonatomic,retain) NSArray* values; 7 | @end 8 | 9 | @implementation TSSettingsAdvancedListController 10 | 11 | - (NSMutableArray*)specifiers 12 | { 13 | if(!_specifiers) 14 | { 15 | _specifiers = [NSMutableArray new]; 16 | 17 | PSSpecifier* installationMethodGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; 18 | //installationMethodGroupSpecifier.name = @"Installation"; 19 | [installationMethodGroupSpecifier setProperty:@"installd:\nInstalls applications by doing a placeholder installation through installd, fixing the permissions and then adding it to icon cache.\nAdvantage: Might be slightly more persistent then the custom method in terms of icon cache reloads.\nDisadvantage: Causes some small issues with certain applications for seemingly no reason (E.g. Watusi cannot save preferences when being installed using this method).\n\nCustom (Recommended):\nInstalls applications by manually creating a bundle using MobileContainerManager, copying the app into it and adding it to icon cache.\nAdvantage: No known issues (As opposed to the Watusi issue outlined in the installd method).\nDisadvantage: Might be slightly less persistent then the installd method in terms of icon cache reloads.\n\nNOTE: In cases where installd is selected but the placeholder installation fails, TrollStore automatically falls back to using the Custom method." forKey:@"footerText"]; 20 | [_specifiers addObject:installationMethodGroupSpecifier]; 21 | 22 | PSSpecifier* installationMethodSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Installation Method" 23 | target:self 24 | set:nil 25 | get:nil 26 | detail:nil 27 | cell:PSStaticTextCell 28 | edit:nil]; 29 | [installationMethodSpecifier setProperty:@YES forKey:@"enabled"]; 30 | installationMethodSpecifier.identifier = @"installationMethodLabel"; 31 | [_specifiers addObject:installationMethodSpecifier]; 32 | 33 | PSSpecifier* installationMethodSegmentSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Installation Method Segment" 34 | target:self 35 | set:@selector(setPreferenceValue:specifier:) 36 | get:@selector(readPreferenceValue:) 37 | detail:nil 38 | cell:PSSegmentCell 39 | edit:nil]; 40 | [installationMethodSegmentSpecifier setProperty:@YES forKey:@"enabled"]; 41 | installationMethodSegmentSpecifier.identifier = @"installationMethodSegment"; 42 | [installationMethodSegmentSpecifier setProperty:@"com.opa334.TrollStore" forKey:@"defaults"]; 43 | [installationMethodSegmentSpecifier setProperty:@"installationMethod" forKey:@"key"]; 44 | installationMethodSegmentSpecifier.values = @[@0, @1]; 45 | installationMethodSegmentSpecifier.titleDictionary = @{@0 : @"installd", @1 : @"Custom"}; 46 | [installationMethodSegmentSpecifier setProperty:@1 forKey:@"default"]; 47 | [_specifiers addObject:installationMethodSegmentSpecifier]; 48 | 49 | PSSpecifier* uninstallationMethodGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; 50 | //uninstallationMethodGroupSpecifier.name = @"Uninstallation"; 51 | [uninstallationMethodGroupSpecifier setProperty:@"installd (Recommended):\nUninstalls applications using the same API that SpringBoard uses when uninstalling them from the home screen.\n\nCustom:\nUninstalls applications by removing them from icon cache and then deleting their application and data bundles directly.\n\nNOTE: In cases where installd is selected but the stock uninstallation fails, TrollStore automatically falls back to using the Custom method." forKey:@"footerText"]; 52 | [_specifiers addObject:uninstallationMethodGroupSpecifier]; 53 | 54 | PSSpecifier* uninstallationMethodSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Uninstallation Method" 55 | target:self 56 | set:nil 57 | get:nil 58 | detail:nil 59 | cell:PSStaticTextCell 60 | edit:nil]; 61 | [uninstallationMethodSpecifier setProperty:@YES forKey:@"enabled"]; 62 | uninstallationMethodSpecifier.identifier = @"uninstallationMethodLabel"; 63 | [_specifiers addObject:uninstallationMethodSpecifier]; 64 | 65 | PSSpecifier* uninstallationMethodSegmentSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Installation Method Segment" 66 | target:self 67 | set:@selector(setPreferenceValue:specifier:) 68 | get:@selector(readPreferenceValue:) 69 | detail:nil 70 | cell:PSSegmentCell 71 | edit:nil]; 72 | [uninstallationMethodSegmentSpecifier setProperty:@YES forKey:@"enabled"]; 73 | uninstallationMethodSegmentSpecifier.identifier = @"uninstallationMethodSegment"; 74 | [uninstallationMethodSegmentSpecifier setProperty:@"com.opa334.TrollStore" forKey:@"defaults"]; 75 | [uninstallationMethodSegmentSpecifier setProperty:@"uninstallationMethod" forKey:@"key"]; 76 | uninstallationMethodSegmentSpecifier.values = @[@0, @1]; 77 | uninstallationMethodSegmentSpecifier.titleDictionary = @{@0 : @"installd", @1 : @"Custom"}; 78 | [uninstallationMethodSegmentSpecifier setProperty:@0 forKey:@"default"]; 79 | [_specifiers addObject:uninstallationMethodSegmentSpecifier]; 80 | } 81 | 82 | [(UINavigationItem *)self.navigationItem setTitle:@"Advanced"]; 83 | return _specifiers; 84 | } 85 | 86 | - (void)setPreferenceValue:(NSObject*)value specifier:(PSSpecifier*)specifier 87 | { 88 | NSUserDefaults* tsDefaults = trollStoreUserDefaults(); 89 | [tsDefaults setObject:value forKey:[specifier propertyForKey:@"key"]]; 90 | } 91 | 92 | - (NSObject*)readPreferenceValue:(PSSpecifier*)specifier 93 | { 94 | NSUserDefaults* tsDefaults = trollStoreUserDefaults(); 95 | NSObject* toReturn = [tsDefaults objectForKey:[specifier propertyForKey:@"key"]]; 96 | if(!toReturn) 97 | { 98 | toReturn = [specifier propertyForKey:@"default"]; 99 | } 100 | return toReturn; 101 | } 102 | 103 | @end -------------------------------------------------------------------------------- /TrollStore/TSApplicationsManager.m: -------------------------------------------------------------------------------- 1 | #import "TSApplicationsManager.h" 2 | #import 3 | extern NSUserDefaults* trollStoreUserDefaults(); 4 | 5 | @implementation TSApplicationsManager 6 | 7 | + (instancetype)sharedInstance 8 | { 9 | static TSApplicationsManager *sharedInstance = nil; 10 | static dispatch_once_t onceToken; 11 | dispatch_once(&onceToken, ^{ 12 | sharedInstance = [[TSApplicationsManager alloc] init]; 13 | }); 14 | return sharedInstance; 15 | } 16 | 17 | - (NSArray*)installedAppPaths 18 | { 19 | return trollStoreInstalledAppBundlePaths(); 20 | } 21 | 22 | - (NSError*)errorForCode:(int)code 23 | { 24 | NSString* errorDescription = @"Unknown Error"; 25 | switch(code) 26 | { 27 | // IPA install errors 28 | case 166: 29 | errorDescription = @"The IPA file does not exist or is not accessible."; 30 | break; 31 | case 167: 32 | errorDescription = @"The IPA file does not appear to contain an app."; 33 | break; 34 | case 168: 35 | errorDescription = @"Failed to extract IPA file."; 36 | break; 37 | case 169: 38 | errorDescription = @"Failed to extract update tar file."; 39 | break; 40 | // App install errors 41 | case 170: 42 | errorDescription = @"Failed to create container for app bundle."; 43 | break; 44 | case 171: 45 | errorDescription = @"A non-TrollStore app with the same identifier is already installed. If you are absolutely sure it is not, you can force install it."; 46 | break; 47 | case 172: 48 | errorDescription = @"The app does not contain an Info.plist file."; 49 | break; 50 | case 173: 51 | errorDescription = @"The app is not signed with a fake CoreTrust certificate and ldid is not installed. Install ldid in the settings tab and try again."; 52 | break; 53 | case 174: 54 | errorDescription = @"The app's main executable does not exist."; 55 | break; 56 | case 175: 57 | errorDescription = @"Failed to sign the app. ldid returned a non zero status code."; 58 | break; 59 | case 176: 60 | errorDescription = @"The app's Info.plist is missing required values."; 61 | break; 62 | case 177: 63 | errorDescription = @"Failed to mark app as TrollStore app."; 64 | break; 65 | case 178: 66 | errorDescription = @"Failed to copy app bundle."; 67 | break; 68 | case 179: 69 | errorDescription = @"The app you tried to install has the same identifier as a system app already installed on the device. The installation has been prevented to protect you from possible bootloops or other issues."; 70 | break; 71 | } 72 | 73 | NSError* error = [NSError errorWithDomain:TrollStoreErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : errorDescription}]; 74 | return error; 75 | } 76 | 77 | - (int)installIpa:(NSString*)pathToIpa force:(BOOL)force log:(NSString**)logOut 78 | { 79 | NSMutableArray* args = [NSMutableArray new]; 80 | [args addObject:@"install"]; 81 | if(force) 82 | { 83 | [args addObject:@"force"]; 84 | } 85 | NSNumber* installationMethodToUseNum = [trollStoreUserDefaults() objectForKey:@"installationMethod"]; 86 | int installationMethodToUse = installationMethodToUseNum ? installationMethodToUseNum.intValue : 1; 87 | if(installationMethodToUse == 1) 88 | { 89 | [args addObject:@"custom"]; 90 | } 91 | else 92 | { 93 | [args addObject:@"installd"]; 94 | } 95 | [args addObject:pathToIpa]; 96 | 97 | int ret = spawnRoot(rootHelperPath(), args, nil, logOut); 98 | [[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil]; 99 | return ret; 100 | } 101 | 102 | - (int)installIpa:(NSString*)pathToIpa 103 | { 104 | return [self installIpa:pathToIpa force:NO log:nil]; 105 | } 106 | 107 | - (int)uninstallApp:(NSString*)appId 108 | { 109 | if(!appId) return -200; 110 | 111 | NSMutableArray* args = [NSMutableArray new]; 112 | [args addObject:@"uninstall"]; 113 | 114 | NSNumber* uninstallationMethodToUseNum = [trollStoreUserDefaults() objectForKey:@"uninstallationMethod"]; 115 | int uninstallationMethodToUse = uninstallationMethodToUseNum ? uninstallationMethodToUseNum.intValue : 0; 116 | if(uninstallationMethodToUse == 1) 117 | { 118 | [args addObject:@"custom"]; 119 | } 120 | else 121 | { 122 | [args addObject:@"installd"]; 123 | } 124 | 125 | [args addObject:appId]; 126 | 127 | int ret = spawnRoot(rootHelperPath(), args, nil, nil); 128 | [[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil]; 129 | return ret; 130 | } 131 | 132 | - (int)uninstallAppByPath:(NSString*)path 133 | { 134 | if(!path) return -200; 135 | 136 | NSMutableArray* args = [NSMutableArray new]; 137 | [args addObject:@"uninstall-path"]; 138 | 139 | NSNumber* uninstallationMethodToUseNum = [trollStoreUserDefaults() objectForKey:@"uninstallationMethod"]; 140 | int uninstallationMethodToUse = uninstallationMethodToUseNum ? uninstallationMethodToUseNum.intValue : 0; 141 | if(uninstallationMethodToUse == 1) 142 | { 143 | [args addObject:@"custom"]; 144 | } 145 | else 146 | { 147 | [args addObject:@"installd"]; 148 | } 149 | 150 | [args addObject:path]; 151 | 152 | int ret = spawnRoot(rootHelperPath(), args, nil, nil); 153 | [[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil]; 154 | return ret; 155 | } 156 | 157 | - (BOOL)openApplicationWithBundleID:(NSString *)appId 158 | { 159 | return [[LSApplicationWorkspace defaultWorkspace] openApplicationWithBundleID:appId]; 160 | } 161 | 162 | - (int)changeAppRegistration:(NSString*)appPath toState:(NSString*)newState 163 | { 164 | if(!appPath || !newState) return -200; 165 | return spawnRoot(rootHelperPath(), @[@"modify-registration", appPath, newState], nil, nil); 166 | } 167 | 168 | @end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TrollStore 2 | 3 | TrollStore is a permasigned jailed app that can permanently install any IPA you open in it. 4 | 5 | It works because of an AMFI/CoreTrust bug where iOS does not verify whether or not a root certificate used to sign a binary is legit. 6 | 7 | ## Installing TrollStore 8 | 9 | ### Installation Guides 10 | 11 | | Version / Device | arm64 (A8 - A11) | arm64e (A12 - A15, M1) | 12 | | --- | --- | --- | 13 | | 13.7 and below | Not Supported (CT Bug only got introduced in 14.0) | Not Supported (CT Bug only got introduced in 14.0) | 14 | | 14.0 - 14.8.1 | [checkra1n + TrollHelper](./install_trollhelper.md) | [TrollHelperOTA (arm64e)](./install_trollhelperota_arm64e.md) | 15 | | 15.0 - 15.4.1 | [TrollHelperOTA (iOS 15+)](./install_trollhelperota_ios15.md) | [TrollHelperOTA (iOS 15+)](./install_trollhelperota_ios15.md) | 16 | | 15.5 beta 1 - 4 | [TrollHelperOTA (iOS 15+)](./install_trollhelperota_ios15.md) | [TrollHelperOTA (iOS 15+)](./install_trollhelperota_ios15.md) | 17 | | 15.5 (RC) | Not Supported (CT Bug fixed) | Not Supported (CT Bug fixed) | 18 | | 15.6 beta 1 - 5 | [SSH Ramdisk](./install_sshrd.md) | [TrollHelperOTA (arm64e)](./install_trollhelperota_arm64e.md) | 19 | | 15.6 (RC1/2) and above | Not Supported (CT Bug fixed) | Not Supported (CT Bug fixed) | 20 | 21 | This version table is final, TrollStore will never support anything other than the versions listed here. Do not bother asking, if you got a device on an unsupported version, it's best if you forget TrollStore even exists. 22 | 23 | ## Updating TrollStore 24 | 25 | When a new TrollStore update is available, a button to install it will appear at the top in the TrollStore settings. After tapping the button, TrollStore will automatically download the update, install it, and respring. 26 | 27 | Alternatively (if anything goes wrong), you can download the TrollStore.tar file under Releases and open it in TrollStore, TrollStore will install the update and respring. 28 | 29 | ## Uninstalling an app 30 | 31 | Apps installed from TrollStore can only be uninstalled from TrollStore itself, tap an app or swipe it to the right in the 'Apps' tab to delete it. 32 | 33 | ## Persistence Helper 34 | 35 | The CoreTrust bug used in TrollStore is only enough to install "System" apps, this is because FrontBoard has an additional security check (it calls libmis) every time before a user app is launched. Unfortunately it is not possible to install new "System" apps that stay through an icon cache reload. Therefore, when iOS reloads the icon cache, all TrollStore installed apps including TrollStore itself will revert back to "User" state and will no longer launch. 36 | 37 | The only way to work around this is to install a persistence helper into a system app, this helper can then be used to reregister TrollStore and its installed apps as "System" so that they become launchable again, an option for this is available in TrollStore settings. 38 | 39 | On jailbroken iOS 14 when TrollHelper is used for installation, it is located in /Applications and will persist as a "System" app through icon cache reloads, therefore TrollHelper is used as the persistence helper on iOS 14. 40 | 41 | ## URL Scheme 42 | 43 | As of version 1.3, TrollStore replaces the system URL scheme "apple-magnifier" (this is done so "jailbreak" detections can't detect TrollStore like they could if TrollStore had a unique URL scheme). This URL scheme can be used to install applications right from the browser, the format goes as follows: 44 | 45 | `apple-magnifier://install?url=` 46 | 47 | On devices that don't have TrollStore (1.3+) installed, this will just open the magnifier app. 48 | 49 | ## Features 50 | 51 | The binaries inside an IPA can have arbitrary entitlements, fakesign them with ldid and the entitlements you want (`ldid -S `) and TrollStore will preserve the entitlements when resigning them with the fake root certificate on installation. This gives you a lot of possibilities, some of which are explained below. 52 | 53 | ### Banned entitlements 54 | 55 | iOS 15 on A12+ has banned the following three entitlements related to running unsigned code, these are impossible to get without a PPL bypass, apps signed with them will crash on launch. 56 | 57 | `com.apple.private.cs.debugger` 58 | 59 | `dynamic-codesigning` 60 | 61 | `com.apple.private.skip-library-validation` 62 | 63 | ### Unsandboxing 64 | 65 | Your app can run unsandboxed using one of the following entitlements: 66 | 67 | ```xml 68 | com.apple.private.security.container-required 69 | 70 | ``` 71 | 72 | ```xml 73 | com.apple.private.security.no-container 74 | 75 | ``` 76 | 77 | ```xml 78 | com.apple.private.security.no-sandbox 79 | 80 | ``` 81 | 82 | The third one is recommended if you still want a sandbox container for your application. 83 | 84 | You might also need the platform-application entitlement in order for these to work properly: 85 | 86 | ```xml 87 | platform-application 88 | 89 | ``` 90 | 91 | Please note that the platform-application entitlement causes side effects such as some parts of the sandbox becoming tighter, so you may need additional private entitlements to circumvent that. (For example afterwards you need an exception entitlement for every single IOKit user client class you want to access). 92 | 93 | In order for an app with `com.apple.private.security.no-sandbox` and `platform-application` to be able to access it's own data container, you might need the additional entitlement: 94 | 95 | ```xml 96 | com.apple.private.security.storage.AppDataContainers 97 | 98 | ``` 99 | 100 | ### Root Helpers 101 | 102 | When your app is not sandboxed, you can spawn other binaries using posix_spawn, you can also spawn binaries as root with the following entitlement: 103 | 104 | ```xml 105 | com.apple.private.persona-mgmt 106 | 107 | ``` 108 | 109 | You can also add your own binaries into your app bundle. 110 | 111 | Afterwards you can use the [spawnRoot function in TSUtil.m](./Shared/TSUtil.m#L74) to spawn the binary as root. 112 | 113 | ### Things that are not possible using TrollStore 114 | 115 | - Getting proper platformization (`TF_PLATFORM` / `CS_PLATFORMIZED`) 116 | - Spawning a launch daemon (Would need `CS_PLATFORMIZED`) 117 | - Injecting a tweak into a system process (Would need `TF_PLATFORM`, a userland PAC bypass and a PMAP trust level bypass) 118 | 119 | ## Credits and Further Reading 120 | 121 | [@LinusHenze](https://twitter.com/LinusHenze/) - Found the CoreTrust bug that allows TrollStore to work. 122 | 123 | [Fugu15 Presentation](https://youtu.be/NIyKNjNNB5Q?t=3046) 124 | 125 | [Write-Up on the CoreTrust bug with more information](https://worthdoingbadly.com/coretrust/). 126 | -------------------------------------------------------------------------------- /Shared/TSListControllerShared.m: -------------------------------------------------------------------------------- 1 | #import "TSListControllerShared.h" 2 | #import "TSUtil.h" 3 | #import "TSPresentationDelegate.h" 4 | 5 | @implementation TSListControllerShared 6 | 7 | - (BOOL)isTrollStore 8 | { 9 | return YES; 10 | } 11 | 12 | - (NSString*)getTrollStoreVersion 13 | { 14 | if([self isTrollStore]) 15 | { 16 | return [NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleVersion"]; 17 | } 18 | else 19 | { 20 | NSString* trollStorePath = trollStoreAppPath(); 21 | if(!trollStorePath) return nil; 22 | 23 | NSBundle* trollStoreBundle = [NSBundle bundleWithPath:trollStorePath]; 24 | return [trollStoreBundle objectForInfoDictionaryKey:@"CFBundleVersion"]; 25 | } 26 | } 27 | 28 | - (void)downloadTrollStoreAndDo:(void (^)(NSString* localTrollStoreTarPath))doHandler 29 | { 30 | NSURL* trollStoreURL = [NSURL URLWithString:@"https://github.com/opa334/TrollStore/releases/latest/download/TrollStore.tar"]; 31 | NSURLRequest* trollStoreRequest = [NSURLRequest requestWithURL:trollStoreURL]; 32 | 33 | NSURLSessionDownloadTask* downloadTask = [NSURLSession.sharedSession downloadTaskWithRequest:trollStoreRequest completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) 34 | { 35 | if(error) 36 | { 37 | UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@"Error" message:[NSString stringWithFormat:@"Error downloading TrollStore: %@", error] preferredStyle:UIAlertControllerStyleAlert]; 38 | UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; 39 | [errorAlert addAction:closeAction]; 40 | 41 | dispatch_async(dispatch_get_main_queue(), ^ 42 | { 43 | [TSPresentationDelegate stopActivityWithCompletion:^ 44 | { 45 | [TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil]; 46 | }]; 47 | }); 48 | } 49 | else 50 | { 51 | NSString* tarTmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"TrollStore.tar"]; 52 | [[NSFileManager defaultManager] removeItemAtPath:tarTmpPath error:nil]; 53 | [[NSFileManager defaultManager] copyItemAtPath:location.path toPath:tarTmpPath error:nil]; 54 | 55 | doHandler(tarTmpPath); 56 | } 57 | }]; 58 | 59 | [downloadTask resume]; 60 | } 61 | 62 | - (void)_updateOrInstallTrollStore:(BOOL)update 63 | { 64 | if(update) 65 | { 66 | [TSPresentationDelegate startActivity:@"Updating TrollStore"]; 67 | } 68 | else 69 | { 70 | [TSPresentationDelegate startActivity:@"Installing TrollStore"]; 71 | } 72 | 73 | [self downloadTrollStoreAndDo:^(NSString* tmpTarPath) 74 | { 75 | int ret = spawnRoot(rootHelperPath(), @[@"install-trollstore", tmpTarPath], nil, nil); 76 | [[NSFileManager defaultManager] removeItemAtPath:tmpTarPath error:nil]; 77 | 78 | if(ret == 0) 79 | { 80 | respring(); 81 | 82 | if([self isTrollStore]) 83 | { 84 | exit(0); 85 | } 86 | else 87 | { 88 | dispatch_async(dispatch_get_main_queue(), ^ 89 | { 90 | [TSPresentationDelegate stopActivityWithCompletion:^ 91 | { 92 | [self reloadSpecifiers]; 93 | }]; 94 | }); 95 | } 96 | } 97 | else 98 | { 99 | dispatch_async(dispatch_get_main_queue(), ^ 100 | { 101 | [TSPresentationDelegate stopActivityWithCompletion:^ 102 | { 103 | UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@"Error" message:[NSString stringWithFormat:@"Error installing TrollStore: trollstorehelper returned %d", ret] preferredStyle:UIAlertControllerStyleAlert]; 104 | UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; 105 | [errorAlert addAction:closeAction]; 106 | [TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil]; 107 | }]; 108 | }); 109 | } 110 | }]; 111 | } 112 | 113 | - (void)installTrollStorePressed 114 | { 115 | [self _updateOrInstallTrollStore:NO]; 116 | } 117 | 118 | - (void)updateTrollStorePressed 119 | { 120 | [self _updateOrInstallTrollStore:YES]; 121 | } 122 | 123 | - (void)rebuildIconCachePressed 124 | { 125 | [TSPresentationDelegate startActivity:@"Rebuilding Icon Cache"]; 126 | 127 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ 128 | { 129 | spawnRoot(rootHelperPath(), @[@"refresh-all"], nil, nil); 130 | 131 | dispatch_async(dispatch_get_main_queue(), ^ 132 | { 133 | [TSPresentationDelegate stopActivityWithCompletion:nil]; 134 | }); 135 | }); 136 | } 137 | 138 | - (void)refreshAppRegistrationsPressed 139 | { 140 | [TSPresentationDelegate startActivity:@"Refreshing"]; 141 | 142 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ 143 | { 144 | spawnRoot(rootHelperPath(), @[@"refresh"], nil, nil); 145 | respring(); 146 | 147 | dispatch_async(dispatch_get_main_queue(), ^ 148 | { 149 | [TSPresentationDelegate stopActivityWithCompletion:nil]; 150 | }); 151 | }); 152 | } 153 | 154 | - (void)uninstallPersistenceHelperPressed 155 | { 156 | if([self isTrollStore]) 157 | { 158 | spawnRoot(rootHelperPath(), @[@"uninstall-persistence-helper"], nil, nil); 159 | [self reloadSpecifiers]; 160 | } 161 | else 162 | { 163 | UIAlertController* uninstallWarningAlert = [UIAlertController alertControllerWithTitle:@"Warning" message:@"Uninstalling the persistence helper will revert this app back to it's original state, you will however no longer be able to persistently refresh the TrollStore app registrations. Continue?" preferredStyle:UIAlertControllerStyleAlert]; 164 | 165 | UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; 166 | [uninstallWarningAlert addAction:cancelAction]; 167 | 168 | UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) 169 | { 170 | spawnRoot(rootHelperPath(), @[@"uninstall-persistence-helper"], nil, nil); 171 | exit(0); 172 | }]; 173 | [uninstallWarningAlert addAction:continueAction]; 174 | 175 | [TSPresentationDelegate presentViewController:uninstallWarningAlert animated:YES completion:nil]; 176 | } 177 | } 178 | 179 | - (void)handleUninstallation 180 | { 181 | if([self isTrollStore]) 182 | { 183 | exit(0); 184 | } 185 | else 186 | { 187 | [self reloadSpecifiers]; 188 | } 189 | } 190 | 191 | - (NSMutableArray*)argsForUninstallingTrollStore 192 | { 193 | return @[@"uninstall-trollstore"].mutableCopy; 194 | } 195 | 196 | - (void)uninstallTrollStorePressed 197 | { 198 | UIAlertController* uninstallAlert = [UIAlertController alertControllerWithTitle:@"Uninstall" message:@"You are about to uninstall TrollStore, do you want to preserve the apps installed by it?" preferredStyle:UIAlertControllerStyleAlert]; 199 | 200 | UIAlertAction* uninstallAllAction = [UIAlertAction actionWithTitle:@"Uninstall TrollStore, Uninstall Apps" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) 201 | { 202 | NSMutableArray* args = [self argsForUninstallingTrollStore]; 203 | spawnRoot(rootHelperPath(), args, nil, nil); 204 | [self handleUninstallation]; 205 | }]; 206 | [uninstallAlert addAction:uninstallAllAction]; 207 | 208 | UIAlertAction* preserveAppsAction = [UIAlertAction actionWithTitle:@"Uninstall TrollStore, Preserve Apps" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) 209 | { 210 | NSMutableArray* args = [self argsForUninstallingTrollStore]; 211 | [args addObject:@"preserve-apps"]; 212 | spawnRoot(rootHelperPath(), args, nil, nil); 213 | [self handleUninstallation]; 214 | }]; 215 | [uninstallAlert addAction:preserveAppsAction]; 216 | 217 | UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; 218 | [uninstallAlert addAction:cancelAction]; 219 | 220 | [TSPresentationDelegate presentViewController:uninstallAlert animated:YES completion:nil]; 221 | } 222 | 223 | @end -------------------------------------------------------------------------------- /TrollStore/TSInstallationController.m: -------------------------------------------------------------------------------- 1 | #import "TSInstallationController.h" 2 | 3 | #import "TSApplicationsManager.h" 4 | #import "TSAppInfo.h" 5 | #import 6 | #import 7 | 8 | extern NSUserDefaults* trollStoreUserDefaults(void); 9 | 10 | @implementation TSInstallationController 11 | 12 | + (void)handleAppInstallFromFile:(NSString*)pathToIPA forceInstall:(BOOL)force completion:(void (^)(BOOL, NSError*))completionBlock 13 | { 14 | dispatch_async(dispatch_get_main_queue(), ^ 15 | { 16 | [TSPresentationDelegate startActivity:@"Installing"]; 17 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ 18 | { 19 | // Install IPA 20 | NSString* log; 21 | int ret = [[TSApplicationsManager sharedInstance] installIpa:pathToIPA force:force log:&log]; 22 | 23 | NSError* error; 24 | if(ret != 0) 25 | { 26 | error = [[TSApplicationsManager sharedInstance] errorForCode:ret]; 27 | } 28 | 29 | NSLog(@"installed app! ret:%d, error: %@", ret, error); 30 | 31 | dispatch_async(dispatch_get_main_queue(), ^ 32 | { 33 | [TSPresentationDelegate stopActivityWithCompletion:^ 34 | { 35 | if(ret != 0) 36 | { 37 | UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Install Error %d", ret] message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert]; 38 | UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) 39 | { 40 | if(ret == 171) 41 | { 42 | if(completionBlock) completionBlock(NO, error); 43 | } 44 | }]; 45 | [errorAlert addAction:closeAction]; 46 | 47 | if(ret == 171) 48 | { 49 | UIAlertAction* forceInstallAction = [UIAlertAction actionWithTitle:@"Force Installation" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) 50 | { 51 | [self handleAppInstallFromFile:pathToIPA forceInstall:YES completion:completionBlock]; 52 | }]; 53 | [errorAlert addAction:forceInstallAction]; 54 | } 55 | else 56 | { 57 | UIAlertAction* copyLogAction = [UIAlertAction actionWithTitle:@"Copy Debug Log" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) 58 | { 59 | UIPasteboard* pasteboard = [UIPasteboard generalPasteboard]; 60 | pasteboard.string = log; 61 | }]; 62 | [errorAlert addAction:copyLogAction]; 63 | } 64 | 65 | [TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil]; 66 | } 67 | 68 | if(ret != 171) 69 | { 70 | if(completionBlock) completionBlock((BOOL)error, error); 71 | } 72 | }]; 73 | }); 74 | }); 75 | }); 76 | } 77 | 78 | + (void)presentInstallationAlertIfEnabledForFile:(NSString*)pathToIPA isRemoteInstall:(BOOL)remoteInstall completion:(void (^)(BOOL, NSError*))completionBlock 79 | { 80 | NSNumber* installAlertConfigurationNum = [trollStoreUserDefaults() objectForKey:@"installAlertConfiguration"]; 81 | NSUInteger installAlertConfiguration = 0; 82 | if(installAlertConfigurationNum) 83 | { 84 | installAlertConfiguration = installAlertConfigurationNum.unsignedIntegerValue; 85 | if(installAlertConfiguration > 2) 86 | { 87 | // broken pref? revert to 0 88 | installAlertConfiguration = 0; 89 | } 90 | } 91 | 92 | // Check if user disabled alert for this kind of install 93 | if(installAlertConfiguration > 0) 94 | { 95 | if(installAlertConfiguration == 2 || (installAlertConfiguration == 1 && !remoteInstall)) 96 | { 97 | [self handleAppInstallFromFile:pathToIPA completion:completionBlock]; 98 | return; 99 | } 100 | } 101 | 102 | TSAppInfo* appInfo = [[TSAppInfo alloc] initWithIPAPath:pathToIPA]; 103 | [appInfo loadInfoWithCompletion:^(NSError* error) 104 | { 105 | dispatch_async(dispatch_get_main_queue(), ^ 106 | { 107 | if(!error) 108 | { 109 | UIAlertController* installAlert = [UIAlertController alertControllerWithTitle:@"" message:@"" preferredStyle:UIAlertControllerStyleAlert]; 110 | installAlert.attributedTitle = [appInfo detailedInfoTitle]; 111 | installAlert.attributedMessage = [appInfo detailedInfoDescription]; 112 | UIAlertAction* installAction = [UIAlertAction actionWithTitle:@"Install" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) 113 | { 114 | [self handleAppInstallFromFile:pathToIPA completion:completionBlock]; 115 | }]; 116 | [installAlert addAction:installAction]; 117 | 118 | UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action) 119 | { 120 | if(completionBlock) completionBlock(NO, nil); 121 | }]; 122 | [installAlert addAction:cancelAction]; 123 | 124 | [TSPresentationDelegate presentViewController:installAlert animated:YES completion:nil]; 125 | } 126 | else 127 | { 128 | UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Parse Error %ld", error.code] message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert]; 129 | UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; 130 | [errorAlert addAction:closeAction]; 131 | 132 | [TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil]; 133 | } 134 | }); 135 | }]; 136 | } 137 | 138 | + (void)handleAppInstallFromFile:(NSString*)pathToIPA completion:(void (^)(BOOL, NSError*))completionBlock 139 | { 140 | [self handleAppInstallFromFile:pathToIPA forceInstall:NO completion:completionBlock]; 141 | } 142 | 143 | + (void)handleAppInstallFromRemoteURL:(NSURL*)remoteURL completion:(void (^)(BOOL, NSError*))completionBlock 144 | { 145 | NSURLRequest* downloadRequest = [NSURLRequest requestWithURL:remoteURL]; 146 | 147 | dispatch_async(dispatch_get_main_queue(), ^ 148 | { 149 | NSURLSessionDownloadTask* downloadTask = [NSURLSession.sharedSession downloadTaskWithRequest:downloadRequest completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) 150 | { 151 | dispatch_async(dispatch_get_main_queue(), ^ 152 | { 153 | [TSPresentationDelegate stopActivityWithCompletion:^ 154 | { 155 | if(error) 156 | { 157 | UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@"Error" message:[NSString stringWithFormat:@"Error downloading app: %@", error] preferredStyle:UIAlertControllerStyleAlert]; 158 | UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; 159 | [errorAlert addAction:closeAction]; 160 | 161 | [TSPresentationDelegate presentViewController:errorAlert animated:YES completion:^ 162 | { 163 | if(completionBlock) completionBlock(NO, error); 164 | }]; 165 | } 166 | else 167 | { 168 | NSString* tmpIpaPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp.ipa"]; 169 | [[NSFileManager defaultManager] removeItemAtPath:tmpIpaPath error:nil]; 170 | [[NSFileManager defaultManager] moveItemAtPath:location.path toPath:tmpIpaPath error:nil]; 171 | [self presentInstallationAlertIfEnabledForFile:tmpIpaPath isRemoteInstall:YES completion:^(BOOL success, NSError* error) 172 | { 173 | [[NSFileManager defaultManager] removeItemAtPath:tmpIpaPath error:nil]; 174 | if(completionBlock) completionBlock(success, error); 175 | }]; 176 | } 177 | }]; 178 | }); 179 | }]; 180 | 181 | [TSPresentationDelegate startActivity:@"Downloading" withCancelHandler:^ 182 | { 183 | [downloadTask cancel]; 184 | }]; 185 | 186 | [downloadTask resume]; 187 | }); 188 | } 189 | 190 | + (void)installLdid 191 | { 192 | fetchLatestLdidVersion(^(NSString* latestVersion) 193 | { 194 | if(!latestVersion) return; 195 | dispatch_async(dispatch_get_main_queue(), ^ 196 | { 197 | NSURL* ldidURL = [NSURL URLWithString:@"https://github.com/opa334/ldid/releases/latest/download/ldid"]; 198 | NSURLRequest* ldidRequest = [NSURLRequest requestWithURL:ldidURL]; 199 | 200 | [TSPresentationDelegate startActivity:@"Installing ldid"]; 201 | 202 | NSURLSessionDownloadTask* downloadTask = [NSURLSession.sharedSession downloadTaskWithRequest:ldidRequest completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) 203 | { 204 | if(error) 205 | { 206 | UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@"Error" message:[NSString stringWithFormat:@"Error downloading ldid: %@", error] preferredStyle:UIAlertControllerStyleAlert]; 207 | UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; 208 | [errorAlert addAction:closeAction]; 209 | 210 | dispatch_async(dispatch_get_main_queue(), ^ 211 | { 212 | [TSPresentationDelegate stopActivityWithCompletion:^ 213 | { 214 | [TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil]; 215 | }]; 216 | }); 217 | } 218 | else if(location) 219 | { 220 | spawnRoot(rootHelperPath(), @[@"install-ldid", location.path, latestVersion], nil, nil); 221 | dispatch_async(dispatch_get_main_queue(), ^ 222 | { 223 | [TSPresentationDelegate stopActivityWithCompletion:nil]; 224 | [[NSNotificationCenter defaultCenter] postNotificationName:@"TrollStoreReloadSettingsNotification" object:nil userInfo:nil]; 225 | }); 226 | } 227 | }]; 228 | 229 | [downloadTask resume]; 230 | }); 231 | }); 232 | } 233 | 234 | @end -------------------------------------------------------------------------------- /TrollHelper/TSHRootViewController.m: -------------------------------------------------------------------------------- 1 | #import "TSHRootViewController.h" 2 | #import 3 | #import 4 | 5 | @implementation TSHRootViewController 6 | 7 | - (BOOL)isTrollStore 8 | { 9 | return NO; 10 | } 11 | 12 | - (void)viewDidLoad 13 | { 14 | [super viewDidLoad]; 15 | TSPresentationDelegate.presentationViewController = self; 16 | 17 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadSpecifiers) name:UIApplicationWillEnterForegroundNotification object:nil]; 18 | 19 | fetchLatestTrollStoreVersion(^(NSString* latestVersion) 20 | { 21 | NSString* currentVersion = [self getTrollStoreVersion]; 22 | NSComparisonResult result = [currentVersion compare:latestVersion options:NSNumericSearch]; 23 | if(result == NSOrderedAscending) 24 | { 25 | _newerVersion = latestVersion; 26 | dispatch_async(dispatch_get_main_queue(), ^ 27 | { 28 | [self reloadSpecifiers]; 29 | }); 30 | } 31 | }); 32 | } 33 | 34 | - (NSMutableArray*)specifiers 35 | { 36 | if(!_specifiers) 37 | { 38 | _specifiers = [NSMutableArray new]; 39 | 40 | #ifdef EMBEDDED_ROOT_HELPER 41 | NSString* credits = @"Powered by Fugu15 CoreTrust & installd bugs, thanks to @LinusHenze\n\n© 2022 Lars Fröder (opa334)"; 42 | #else 43 | NSString* credits = @"Powered by Fugu15 CoreTrust bug, thanks to @LinusHenze\n\n© 2022 Lars Fröder (opa334)"; 44 | #endif 45 | 46 | PSSpecifier* infoGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; 47 | infoGroupSpecifier.name = @"Info"; 48 | [_specifiers addObject:infoGroupSpecifier]; 49 | 50 | PSSpecifier* infoSpecifier = [PSSpecifier preferenceSpecifierNamed:@"TrollStore" 51 | target:self 52 | set:nil 53 | get:@selector(getTrollStoreInfoString) 54 | detail:nil 55 | cell:PSTitleValueCell 56 | edit:nil]; 57 | infoSpecifier.identifier = @"info"; 58 | [infoSpecifier setProperty:@YES forKey:@"enabled"]; 59 | 60 | [_specifiers addObject:infoSpecifier]; 61 | 62 | BOOL isInstalled = trollStoreAppPath(); 63 | 64 | if(_newerVersion && isInstalled) 65 | { 66 | // Update TrollStore 67 | PSSpecifier* updateTrollStoreSpecifier = [PSSpecifier preferenceSpecifierNamed:[NSString stringWithFormat:@"Update TrollStore to %@", _newerVersion] 68 | target:self 69 | set:nil 70 | get:nil 71 | detail:nil 72 | cell:PSButtonCell 73 | edit:nil]; 74 | updateTrollStoreSpecifier.identifier = @"updateTrollStore"; 75 | [updateTrollStoreSpecifier setProperty:@YES forKey:@"enabled"]; 76 | updateTrollStoreSpecifier.buttonAction = @selector(updateTrollStorePressed); 77 | [_specifiers addObject:updateTrollStoreSpecifier]; 78 | } 79 | 80 | PSSpecifier* lastGroupSpecifier; 81 | 82 | PSSpecifier* utilitiesGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; 83 | [_specifiers addObject:utilitiesGroupSpecifier]; 84 | 85 | lastGroupSpecifier = utilitiesGroupSpecifier; 86 | 87 | if(isInstalled || trollStoreInstalledAppContainerPaths().count) 88 | { 89 | PSSpecifier* refreshAppRegistrationsSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Refresh App Registrations" 90 | target:self 91 | set:nil 92 | get:nil 93 | detail:nil 94 | cell:PSButtonCell 95 | edit:nil]; 96 | refreshAppRegistrationsSpecifier.identifier = @"refreshAppRegistrations"; 97 | [refreshAppRegistrationsSpecifier setProperty:@YES forKey:@"enabled"]; 98 | refreshAppRegistrationsSpecifier.buttonAction = @selector(refreshAppRegistrationsPressed); 99 | [_specifiers addObject:refreshAppRegistrationsSpecifier]; 100 | } 101 | if(isInstalled) 102 | { 103 | PSSpecifier* uninstallTrollStoreSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Uninstall TrollStore" 104 | target:self 105 | set:nil 106 | get:nil 107 | detail:nil 108 | cell:PSButtonCell 109 | edit:nil]; 110 | uninstallTrollStoreSpecifier.identifier = @"uninstallTrollStore"; 111 | [uninstallTrollStoreSpecifier setProperty:@YES forKey:@"enabled"]; 112 | [uninstallTrollStoreSpecifier setProperty:NSClassFromString(@"PSDeleteButtonCell") forKey:@"cellClass"]; 113 | uninstallTrollStoreSpecifier.buttonAction = @selector(uninstallTrollStorePressed); 114 | [_specifiers addObject:uninstallTrollStoreSpecifier]; 115 | } 116 | else 117 | { 118 | PSSpecifier* installTrollStoreSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Install TrollStore" 119 | target:self 120 | set:nil 121 | get:nil 122 | detail:nil 123 | cell:PSButtonCell 124 | edit:nil]; 125 | installTrollStoreSpecifier.identifier = @"installTrollStore"; 126 | [installTrollStoreSpecifier setProperty:@YES forKey:@"enabled"]; 127 | installTrollStoreSpecifier.buttonAction = @selector(installTrollStorePressed); 128 | [_specifiers addObject:installTrollStoreSpecifier]; 129 | } 130 | 131 | NSString* backupPath = [safe_getExecutablePath() stringByAppendingString:@"_TROLLSTORE_BACKUP"]; 132 | if([[NSFileManager defaultManager] fileExistsAtPath:backupPath]) 133 | { 134 | PSSpecifier* uninstallHelperGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; 135 | [_specifiers addObject:uninstallHelperGroupSpecifier]; 136 | lastGroupSpecifier = uninstallHelperGroupSpecifier; 137 | 138 | PSSpecifier* uninstallPersistenceHelperSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Uninstall Persistence Helper" 139 | target:self 140 | set:nil 141 | get:nil 142 | detail:nil 143 | cell:PSButtonCell 144 | edit:nil]; 145 | uninstallPersistenceHelperSpecifier.identifier = @"uninstallPersistenceHelper"; 146 | [uninstallPersistenceHelperSpecifier setProperty:@YES forKey:@"enabled"]; 147 | [uninstallPersistenceHelperSpecifier setProperty:NSClassFromString(@"PSDeleteButtonCell") forKey:@"cellClass"]; 148 | uninstallPersistenceHelperSpecifier.buttonAction = @selector(uninstallPersistenceHelperPressed); 149 | [_specifiers addObject:uninstallPersistenceHelperSpecifier]; 150 | } 151 | 152 | #ifdef EMBEDDED_ROOT_HELPER 153 | LSApplicationProxy* persistenceHelperProxy = findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE_ALL); 154 | BOOL isRegistered = [persistenceHelperProxy.bundleIdentifier isEqualToString:NSBundle.mainBundle.bundleIdentifier]; 155 | 156 | if((isRegistered || !persistenceHelperProxy) && ![[NSFileManager defaultManager] fileExistsAtPath:@"/Applications/TrollStorePersistenceHelper.app"]) 157 | { 158 | PSSpecifier* registerUnregisterGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; 159 | lastGroupSpecifier = nil; 160 | 161 | NSString* bottomText; 162 | PSSpecifier* registerUnregisterSpecifier; 163 | 164 | if(isRegistered) 165 | { 166 | bottomText = @"This app is registered as the TrollStore persistence helper and can be used to fix TrollStore app registrations in case they revert back to \"User\" state and the apps say they're unavailable."; 167 | registerUnregisterSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Unregister Persistence Helper" 168 | target:self 169 | set:nil 170 | get:nil 171 | detail:nil 172 | cell:PSButtonCell 173 | edit:nil]; 174 | registerUnregisterSpecifier.identifier = @"registerUnregisterSpecifier"; 175 | [registerUnregisterSpecifier setProperty:@YES forKey:@"enabled"]; 176 | [registerUnregisterSpecifier setProperty:NSClassFromString(@"PSDeleteButtonCell") forKey:@"cellClass"]; 177 | registerUnregisterSpecifier.buttonAction = @selector(unregisterPersistenceHelperPressed); 178 | } 179 | else if(!persistenceHelperProxy) 180 | { 181 | bottomText = @"If you want to use this app as the TrollStore persistence helper, you can register it here."; 182 | registerUnregisterSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Register Persistence Helper" 183 | target:self 184 | set:nil 185 | get:nil 186 | detail:nil 187 | cell:PSButtonCell 188 | edit:nil]; 189 | registerUnregisterSpecifier.identifier = @"registerUnregisterSpecifier"; 190 | [registerUnregisterSpecifier setProperty:@YES forKey:@"enabled"]; 191 | registerUnregisterSpecifier.buttonAction = @selector(registerPersistenceHelperPressed); 192 | } 193 | 194 | [registerUnregisterGroupSpecifier setProperty:[NSString stringWithFormat:@"%@\n\n%@", bottomText, credits] forKey:@"footerText"]; 195 | lastGroupSpecifier = nil; 196 | 197 | [_specifiers addObject:registerUnregisterGroupSpecifier]; 198 | [_specifiers addObject:registerUnregisterSpecifier]; 199 | } 200 | #endif 201 | 202 | if(lastGroupSpecifier) 203 | { 204 | [lastGroupSpecifier setProperty:credits forKey:@"footerText"]; 205 | } 206 | } 207 | 208 | [(UINavigationItem *)self.navigationItem setTitle:@"TrollStore Helper"]; 209 | return _specifiers; 210 | } 211 | 212 | - (NSString*)getTrollStoreInfoString 213 | { 214 | NSString* version = [self getTrollStoreVersion]; 215 | if(!version) 216 | { 217 | return @"Not Installed"; 218 | } 219 | else 220 | { 221 | return [NSString stringWithFormat:@"Installed, %@", version]; 222 | } 223 | } 224 | 225 | - (void)handleUninstallation 226 | { 227 | _newerVersion = nil; 228 | [super handleUninstallation]; 229 | } 230 | 231 | - (void)registerPersistenceHelperPressed 232 | { 233 | int ret = spawnRoot(rootHelperPath(), @[@"register-user-persistence-helper", NSBundle.mainBundle.bundleIdentifier], nil, nil); 234 | NSLog(@"registerPersistenceHelperPressed -> %d", ret); 235 | if(ret == 0) 236 | { 237 | [self reloadSpecifiers]; 238 | } 239 | } 240 | 241 | - (void)unregisterPersistenceHelperPressed 242 | { 243 | int ret = spawnRoot(rootHelperPath(), @[@"uninstall-persistence-helper"], nil, nil); 244 | if(ret == 0) 245 | { 246 | [self reloadSpecifiers]; 247 | } 248 | } 249 | 250 | @end 251 | -------------------------------------------------------------------------------- /RootHelper/uicache.m: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | @import CoreServices; 3 | #import "CoreServices.h" 4 | #import 5 | #import "dlfcn.h" 6 | #import 7 | #import 8 | 9 | // uicache on steroids 10 | 11 | extern NSSet* immutableAppBundleIdentifiers(void); 12 | extern NSDictionary* dumpEntitlementsFromBinaryAtPath(NSString* binaryPath); 13 | 14 | NSDictionary* constructGroupsContainersForEntitlements(NSDictionary* entitlements, BOOL systemGroups) 15 | { 16 | if(!entitlements) return nil; 17 | 18 | NSString* entitlementForGroups; 19 | Class mcmClass; 20 | if(systemGroups) 21 | { 22 | entitlementForGroups = @"com.apple.security.system-groups"; 23 | mcmClass = [MCMSystemDataContainer class]; 24 | } 25 | else 26 | { 27 | entitlementForGroups = @"com.apple.security.application-groups"; 28 | mcmClass = [MCMSharedDataContainer class]; 29 | } 30 | 31 | NSArray* groupIDs = entitlements[entitlementForGroups]; 32 | if(groupIDs && [groupIDs isKindOfClass:[NSArray class]]) 33 | { 34 | NSMutableDictionary* groupContainers = [NSMutableDictionary new]; 35 | 36 | for(NSString* groupID in groupIDs) 37 | { 38 | MCMContainer* container = [mcmClass containerWithIdentifier:groupID createIfNecessary:YES existed:nil error:nil]; 39 | if(container.url) 40 | { 41 | groupContainers[groupID] = container.url.path; 42 | } 43 | } 44 | 45 | return groupContainers.copy; 46 | } 47 | 48 | return nil; 49 | } 50 | 51 | BOOL constructContainerizationForEntitlements(NSDictionary* entitlements) 52 | { 53 | NSNumber* noContainer = entitlements[@"com.apple.private.security.no-container"]; 54 | if(noContainer && [noContainer isKindOfClass:[NSNumber class]]) 55 | { 56 | if(noContainer.boolValue) 57 | { 58 | return NO; 59 | } 60 | } 61 | 62 | NSNumber* containerRequired = entitlements[@"com.apple.private.security.container-required"]; 63 | if(containerRequired && [containerRequired isKindOfClass:[NSNumber class]]) 64 | { 65 | if(!containerRequired.boolValue) 66 | { 67 | return NO; 68 | } 69 | } 70 | 71 | return YES; 72 | } 73 | 74 | NSString* constructTeamIdentifierForEntitlements(NSDictionary* entitlements) 75 | { 76 | NSString* teamIdentifier = entitlements[@"com.apple.developer.team-identifier"]; 77 | if(teamIdentifier && [teamIdentifier isKindOfClass:[NSString class]]) 78 | { 79 | return teamIdentifier; 80 | } 81 | return nil; 82 | } 83 | 84 | NSDictionary* constructEnvironmentVariablesForContainerPath(NSString* containerPath) 85 | { 86 | NSString* tmpDir = [containerPath stringByAppendingPathComponent:@"tmp"]; 87 | return @{ 88 | @"CFFIXED_USER_HOME" : containerPath, 89 | @"HOME" : containerPath, 90 | @"TMPDIR" : tmpDir 91 | }; 92 | } 93 | 94 | void registerPath(NSString* path, BOOL unregister, BOOL system) 95 | { 96 | if(!path) return; 97 | 98 | LSApplicationWorkspace* workspace = [LSApplicationWorkspace defaultWorkspace]; 99 | if(unregister && ![[NSFileManager defaultManager] fileExistsAtPath:path]) 100 | { 101 | LSApplicationProxy* app = [LSApplicationProxy applicationProxyForIdentifier:path]; 102 | if(app.bundleURL) 103 | { 104 | path = [app bundleURL].path; 105 | } 106 | } 107 | 108 | path = [path stringByResolvingSymlinksInPath]; 109 | 110 | NSDictionary* appInfoPlist = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingPathComponent:@"Info.plist"]]; 111 | NSString* appBundleID = [appInfoPlist objectForKey:@"CFBundleIdentifier"]; 112 | 113 | if([immutableAppBundleIdentifiers() containsObject:appBundleID.lowercaseString]) return; 114 | 115 | if(appBundleID && !unregister) 116 | { 117 | MCMContainer* appContainer = [NSClassFromString(@"MCMAppDataContainer") containerWithIdentifier:appBundleID createIfNecessary:YES existed:nil error:nil]; 118 | NSString* containerPath = [appContainer url].path; 119 | 120 | NSMutableDictionary* dictToRegister = [NSMutableDictionary dictionary]; 121 | 122 | // Add entitlements 123 | 124 | NSString* appExecutablePath = [path stringByAppendingPathComponent:appInfoPlist[@"CFBundleExecutable"]]; 125 | NSDictionary* entitlements = dumpEntitlementsFromBinaryAtPath(appExecutablePath); 126 | if(entitlements) 127 | { 128 | dictToRegister[@"Entitlements"] = entitlements; 129 | } 130 | 131 | // Misc 132 | 133 | dictToRegister[@"ApplicationType"] = system ? @"System" : @"User"; 134 | dictToRegister[@"CFBundleIdentifier"] = appBundleID; 135 | dictToRegister[@"CodeInfoIdentifier"] = appBundleID; 136 | dictToRegister[@"CompatibilityState"] = @0; 137 | if(containerPath) 138 | { 139 | dictToRegister[@"Container"] = containerPath; 140 | dictToRegister[@"EnvironmentVariables"] = constructEnvironmentVariablesForContainerPath(containerPath); 141 | } 142 | dictToRegister[@"IsDeletable"] = @(![appBundleID isEqualToString:@"com.opa334.TrollStore"] && kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_15_0); 143 | dictToRegister[@"Path"] = path; 144 | dictToRegister[@"IsContainerized"] = @(constructContainerizationForEntitlements(entitlements)); 145 | dictToRegister[@"SignerOrganization"] = @"Apple Inc."; 146 | dictToRegister[@"SignatureVersion"] = @132352; 147 | dictToRegister[@"SignerIdentity"] = @"Apple iPhone OS Application Signing"; 148 | dictToRegister[@"IsAdHocSigned"] = @YES; 149 | dictToRegister[@"LSInstallType"] = @1; 150 | dictToRegister[@"HasMIDBasedSINF"] = @0; 151 | dictToRegister[@"MissingSINF"] = @0; 152 | dictToRegister[@"FamilyID"] = @0; 153 | dictToRegister[@"IsOnDemandInstallCapable"] = @0; 154 | 155 | NSString* teamIdentifier = constructTeamIdentifierForEntitlements(entitlements); 156 | if(teamIdentifier) dictToRegister[@"TeamIdentifier"] = teamIdentifier; 157 | 158 | // Add group containers 159 | 160 | NSDictionary* appGroupContainers = constructGroupsContainersForEntitlements(entitlements, NO); 161 | NSDictionary* systemGroupContainers = constructGroupsContainersForEntitlements(entitlements, YES); 162 | NSMutableDictionary* groupContainers = [NSMutableDictionary new]; 163 | [groupContainers addEntriesFromDictionary:appGroupContainers]; 164 | [groupContainers addEntriesFromDictionary:systemGroupContainers]; 165 | if(groupContainers.count) 166 | { 167 | if(appGroupContainers.count) 168 | { 169 | dictToRegister[@"HasAppGroupContainers"] = @YES; 170 | } 171 | if(systemGroupContainers.count) 172 | { 173 | dictToRegister[@"HasSystemGroupContainers"] = @YES; 174 | } 175 | dictToRegister[@"GroupContainers"] = groupContainers.copy; 176 | } 177 | 178 | // Add plugins 179 | 180 | NSString* pluginsPath = [path stringByAppendingPathComponent:@"PlugIns"]; 181 | NSArray* plugins = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:pluginsPath error:nil]; 182 | 183 | NSMutableDictionary* bundlePlugins = [NSMutableDictionary dictionary]; 184 | for (NSString* pluginName in plugins) 185 | { 186 | NSString* pluginPath = [pluginsPath stringByAppendingPathComponent:pluginName]; 187 | 188 | NSDictionary* pluginInfoPlist = [NSDictionary dictionaryWithContentsOfFile:[pluginPath stringByAppendingPathComponent:@"Info.plist"]]; 189 | NSString* pluginBundleID = [pluginInfoPlist objectForKey:@"CFBundleIdentifier"]; 190 | 191 | if(!pluginBundleID) continue; 192 | MCMContainer* pluginContainer = [NSClassFromString(@"MCMPluginKitPluginDataContainer") containerWithIdentifier:pluginBundleID createIfNecessary:YES existed:nil error:nil]; 193 | NSString* pluginContainerPath = [pluginContainer url].path; 194 | 195 | NSMutableDictionary* pluginDict = [NSMutableDictionary dictionary]; 196 | 197 | // Add entitlements 198 | 199 | NSString* pluginExecutablePath = [pluginPath stringByAppendingPathComponent:pluginInfoPlist[@"CFBundleExecutable"]]; 200 | NSDictionary* pluginEntitlements = dumpEntitlementsFromBinaryAtPath(pluginExecutablePath); 201 | if(pluginEntitlements) 202 | { 203 | pluginDict[@"Entitlements"] = pluginEntitlements; 204 | } 205 | 206 | // Misc 207 | 208 | pluginDict[@"ApplicationType"] = @"PluginKitPlugin"; 209 | pluginDict[@"CFBundleIdentifier"] = pluginBundleID; 210 | pluginDict[@"CodeInfoIdentifier"] = pluginBundleID; 211 | pluginDict[@"CompatibilityState"] = @0; 212 | if(pluginContainerPath) 213 | { 214 | pluginDict[@"Container"] = pluginContainerPath; 215 | pluginDict[@"EnvironmentVariables"] = constructEnvironmentVariablesForContainerPath(pluginContainerPath); 216 | } 217 | pluginDict[@"Path"] = pluginPath; 218 | pluginDict[@"PluginOwnerBundleID"] = appBundleID; 219 | pluginDict[@"IsContainerized"] = @(constructContainerizationForEntitlements(pluginEntitlements)); 220 | pluginDict[@"SignerOrganization"] = @"Apple Inc."; 221 | pluginDict[@"SignatureVersion"] = @132352; 222 | pluginDict[@"SignerIdentity"] = @"Apple iPhone OS Application Signing"; 223 | 224 | NSString* pluginTeamIdentifier = constructTeamIdentifierForEntitlements(pluginEntitlements); 225 | if(pluginTeamIdentifier) pluginDict[@"TeamIdentifier"] = pluginTeamIdentifier; 226 | 227 | // Add plugin group containers 228 | 229 | NSDictionary* pluginAppGroupContainers = constructGroupsContainersForEntitlements(pluginEntitlements, NO); 230 | NSDictionary* pluginSystemGroupContainers = constructGroupsContainersForEntitlements(pluginEntitlements, YES); 231 | NSMutableDictionary* pluginGroupContainers = [NSMutableDictionary new]; 232 | [pluginGroupContainers addEntriesFromDictionary:pluginAppGroupContainers]; 233 | [pluginGroupContainers addEntriesFromDictionary:pluginSystemGroupContainers]; 234 | if(pluginGroupContainers.count) 235 | { 236 | if(pluginAppGroupContainers.count) 237 | { 238 | pluginDict[@"HasAppGroupContainers"] = @YES; 239 | } 240 | if(pluginSystemGroupContainers.count) 241 | { 242 | pluginDict[@"HasSystemGroupContainers"] = @YES; 243 | } 244 | pluginDict[@"GroupContainers"] = pluginGroupContainers.copy; 245 | } 246 | 247 | [bundlePlugins setObject:pluginDict forKey:pluginBundleID]; 248 | } 249 | [dictToRegister setObject:bundlePlugins forKey:@"_LSBundlePlugins"]; 250 | 251 | if(![workspace registerApplicationDictionary:dictToRegister]) 252 | { 253 | NSLog(@"Error: Unable to register %@", path); 254 | } 255 | } 256 | else 257 | { 258 | NSURL* url = [NSURL fileURLWithPath:path]; 259 | if(![workspace unregisterApplication:url]) 260 | { 261 | NSLog(@"Error: Unable to unregister %@", path); 262 | } 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /RootHelper/main.m-system: -------------------------------------------------------------------------------- 1 | #import 2 | #import "unarchive.h" 3 | @import Foundation; 4 | #import "uicache.h" 5 | #import 6 | #import 7 | #import 8 | #import "path.h" 9 | #import "CoreServices.h" 10 | #import 11 | 12 | #define kCFPreferencesNoContainer CFSTR("kCFPreferencesNoContainer") 13 | 14 | typedef CFPropertyListRef (*_CFPreferencesCopyValueWithContainerType)(CFStringRef key, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath); 15 | typedef void (*_CFPreferencesSetValueWithContainerType)(CFStringRef key, CFPropertyListRef value, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath); 16 | typedef Boolean (*_CFPreferencesSynchronizeWithContainerType)(CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath); 17 | typedef CFArrayRef (*_CFPreferencesCopyKeyListWithContainerType)(CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath); 18 | typedef CFDictionaryRef (*_CFPreferencesCopyMultipleWithContainerType)(CFArrayRef keysToFetch, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath); 19 | 20 | extern char*** _NSGetArgv(); 21 | NSString* safe_getExecutablePath() 22 | { 23 | char* executablePathC = **_NSGetArgv(); 24 | return [NSString stringWithUTF8String:executablePathC]; 25 | } 26 | 27 | NSDictionary* infoDictionaryForAppPath(NSString* appPath) 28 | { 29 | NSString* infoPlistPath = [appPath stringByAppendingPathComponent:@"Info.plist"]; 30 | return [NSDictionary dictionaryWithContentsOfFile:infoPlistPath]; 31 | } 32 | 33 | NSString* appIdForAppPath(NSString* appPath) 34 | { 35 | return infoDictionaryForAppPath(appPath)[@"CFBundleIdentifier"]; 36 | } 37 | 38 | NSString* appPathForAppId(NSString* appId, NSError** error) 39 | { 40 | NSString* appPath = [TROLLSTORE_APPLICATIONS_PATH stringByAppendingPathComponent:appId]; 41 | 42 | NSArray* items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:appPath error:error]; 43 | if(!items) return nil; 44 | 45 | for(NSString* item in items) 46 | { 47 | if([item.pathExtension isEqualToString:@"app"]) 48 | { 49 | return [appPath stringByAppendingPathComponent:item]; 50 | } 51 | } 52 | 53 | return nil; 54 | } 55 | 56 | static void dump_file_content(int fd) 57 | { 58 | ssize_t num_read; 59 | char line_buf[256]; 60 | int cur_pos = 0; 61 | 62 | for (;;) 63 | { 64 | char c; 65 | num_read = read(fd, &c, sizeof(c)); 66 | if(num_read <= 0) 67 | 68 | if(c == '\n' || cur_pos >= 255 || num_read <= 0) 69 | { 70 | line_buf[cur_pos] = '\n'; 71 | NSLog(@"%s", (char*)line_buf); 72 | if(c == '\n') cur_pos++; 73 | 74 | if(num_read > 0) 75 | { 76 | continue; 77 | } 78 | else 79 | { 80 | break; 81 | } 82 | } 83 | 84 | line_buf[cur_pos++] = c; 85 | } 86 | } 87 | 88 | BOOL signApp(NSString* appPath, NSError** error) 89 | { 90 | NSString* ldidPath = [[safe_getExecutablePath() stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"ldid"]; 91 | if(![[NSFileManager defaultManager] fileExistsAtPath:ldidPath]) 92 | { 93 | NSLog(@"WARNING: ldid not found, not signing application"); 94 | return NO; 95 | } 96 | 97 | NSString* certPath = [TROLLSTORE_MAIN_PATH stringByAppendingPathComponent:@"TrollStore.app/cert.p12"]; 98 | NSString* certArg = [@"-K" stringByAppendingPathComponent:certPath]; 99 | 100 | int out[2]; 101 | posix_spawn_file_actions_t action; 102 | posix_spawn_file_actions_init(&action); 103 | pipe(out); 104 | posix_spawn_file_actions_adddup2(&action, out[1], STDERR_FILENO); 105 | posix_spawn_file_actions_addclose(&action, out[0]); 106 | 107 | char* args[] = { "ldid", "-S", "-M", (char*)certArg.UTF8String, (char*)appPath.UTF8String, NULL }; 108 | 109 | NSLog(@"%@ ldid -S -M %@ %@", ldidPath, certArg, appPath); 110 | 111 | pid_t task_pid; 112 | int status = -200; 113 | int spawnError = posix_spawn(&task_pid, [ldidPath UTF8String], &action, NULL, args, NULL); 114 | 115 | if(spawnError != 0) 116 | { 117 | NSLog(@"posix_spawn error %d\n", spawnError); 118 | return spawnError; 119 | } 120 | 121 | waitpid(task_pid, &status, WEXITED); 122 | 123 | NSLog(@"ldid exited with status %d", status); 124 | 125 | waitpid(task_pid, &status, 0); 126 | 127 | NSLog(@"ldid exited with status %d", status); 128 | 129 | NSLog(@"ldid output:"); 130 | 131 | close(out[1]); 132 | dump_file_content(out[0]); 133 | 134 | NSLog(@"end ldid output:"); 135 | 136 | return status == 0; 137 | } 138 | 139 | BOOL installApp(NSString* appPath, NSString* appId, BOOL sign, NSError** error) 140 | { 141 | if(sign) 142 | { 143 | // if it fails to sign, we don't care 144 | signApp(appPath, error); 145 | } 146 | 147 | BOOL existed; 148 | NSError* mcmError; 149 | MCMAppContainer* appContainer = [objc_getClass("MCMAppContainer") containerWithIdentifier:appId createIfNecessary:YES existed:&existed error:&mcmError]; 150 | NSLog(@"installApp appContainer: %@, mcmError: %@", appContainer, mcmError); 151 | if(!appContainer || mcmError) 152 | { 153 | if(error) *error = mcmError; 154 | return NO; 155 | } 156 | 157 | //TODO: if TrollStore, preserve by moving it into appPath if needed ldid if needed 158 | 159 | NSURL* trollStoreMarkURL = [appContainer.url URLByAppendingPathComponent:@"_TrollStore"]; 160 | if(existed) 161 | { 162 | // trying to update an app not installed by TrollStore... bailing out 163 | if(![trollStoreMarkURL checkResourceIsReachableAndReturnError:nil]) 164 | { 165 | NSLog(@"installApp already installed and not a TrollStore app... bailing out"); 166 | return NO; 167 | } 168 | else 169 | { 170 | // update existing app... clean old app directory 171 | NSLog(@"installApp found existing TrollStore app, cleaning directory"); 172 | NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:appContainer.url includingPropertiesForKeys:nil options:0 errorHandler:nil]; 173 | NSURL* fileURL; 174 | while(fileURL = [enumerator nextObject]) 175 | { 176 | [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil]; 177 | } 178 | } 179 | } 180 | 181 | [[NSFileManager defaultManager] createFileAtPath:trollStoreMarkURL.path contents:[NSData data] attributes:nil]; 182 | 183 | NSString* newAppPath = [appContainer.url.path stringByAppendingPathComponent:appPath.lastPathComponent]; 184 | 185 | NSLog(@"installApp new app path: %@", newAppPath); 186 | 187 | BOOL suc = [[NSFileManager defaultManager] copyItemAtPath:appPath toPath:newAppPath error:error]; 188 | 189 | NSLog(@"installApp copied app? %d, adding to uicache now...", suc); 190 | 191 | registerPath(newAppPath, NO); 192 | 193 | return YES; 194 | } 195 | 196 | BOOL uninstallApp(NSString* appId, NSError** error) 197 | { 198 | NSString* appPath = appPathForAppId(appId, error); 199 | if(!appPath) return NO; 200 | 201 | registerPath(appPath, YES); 202 | 203 | return [[NSFileManager defaultManager] removeItemAtPath:[appPath stringByDeletingLastPathComponent] error:error]; 204 | } 205 | 206 | BOOL installIpa(NSString* ipaPath, NSError** error) 207 | { 208 | BOOL suc = NO; 209 | NSString* tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString]; 210 | 211 | suc = [[NSFileManager defaultManager] createDirectoryAtPath:tmpPath withIntermediateDirectories:NO attributes:nil error:error]; 212 | if(!suc) return NO; 213 | 214 | extract(ipaPath, tmpPath); 215 | 216 | NSString* tmpPayloadPath = [tmpPath stringByAppendingPathComponent:@"Payload"]; 217 | 218 | NSArray* items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:tmpPayloadPath error:error]; 219 | if(!items) return NO; 220 | 221 | NSString* tmpAppPath; 222 | for(NSString* item in items) 223 | { 224 | if([item.pathExtension isEqualToString:@"app"]) 225 | { 226 | tmpAppPath = [tmpPayloadPath stringByAppendingPathComponent:item]; 227 | break; 228 | } 229 | } 230 | if(!tmpAppPath) return NO; 231 | 232 | NSString* appId = appIdForAppPath(tmpAppPath); 233 | 234 | suc = installApp(tmpAppPath, appId, YES, error); 235 | 236 | [[NSFileManager defaultManager] removeItemAtPath:tmpAppPath error:nil]; 237 | 238 | return suc; 239 | } 240 | 241 | void uninstallAllApps(void) 242 | { 243 | NSArray* items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:TROLLSTORE_APPLICATIONS_PATH error:nil]; 244 | for(NSString* appId in items) 245 | { 246 | NSString* appPath = appPathForAppId(appId, nil); 247 | registerPath(appPath, YES); 248 | } 249 | 250 | [[NSFileManager defaultManager] removeItemAtPath:TROLLSTORE_ROOT_PATH error:nil]; 251 | } 252 | 253 | BOOL uninstallTrollStore(void) 254 | { 255 | NSString* trollStore = [TROLLSTORE_MAIN_PATH stringByAppendingPathComponent:@"TrollStore.app"]; 256 | if(![[NSFileManager defaultManager] fileExistsAtPath:trollStore]) return NO; 257 | 258 | registerPath(trollStore, YES); 259 | return [[NSFileManager defaultManager] removeItemAtPath:trollStore error:nil]; 260 | } 261 | 262 | BOOL installTrollStore(NSString* pathToTar) 263 | { 264 | _CFPreferencesSetValueWithContainerType _CFPreferencesSetValueWithContainer = (_CFPreferencesSetValueWithContainerType)dlsym(RTLD_DEFAULT, "_CFPreferencesSetValueWithContainer"); 265 | _CFPreferencesSynchronizeWithContainerType _CFPreferencesSynchronizeWithContainer = (_CFPreferencesSynchronizeWithContainerType)dlsym(RTLD_DEFAULT, "_CFPreferencesSynchronizeWithContainer"); 266 | _CFPreferencesSetValueWithContainer(CFSTR("SBShowNonDefaultSystemApps"), kCFBooleanTrue, CFSTR("com.apple.springboard"), CFSTR("mobile"), kCFPreferencesAnyHost, kCFPreferencesNoContainer); 267 | _CFPreferencesSynchronizeWithContainer(CFSTR("com.apple.springboard"), CFSTR("mobile"), kCFPreferencesAnyHost, kCFPreferencesNoContainer); 268 | 269 | if(![[NSFileManager defaultManager] fileExistsAtPath:pathToTar]) return NO; 270 | if(![pathToTar.pathExtension isEqualToString:@"tar"]) return NO; 271 | 272 | NSString* tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString]; 273 | BOOL suc = [[NSFileManager defaultManager] createDirectoryAtPath:tmpPath withIntermediateDirectories:NO attributes:nil error:nil]; 274 | if(!suc) return NO; 275 | 276 | extract(pathToTar, tmpPath); 277 | 278 | NSLog(@"installTrollStore extracted %@ to %@", pathToTar, tmpPath); 279 | 280 | NSString* tmpTrollStore = [tmpPath stringByAppendingPathComponent:@"TrollStore.app"]; 281 | if(![[NSFileManager defaultManager] fileExistsAtPath:tmpTrollStore]) return NO; 282 | 283 | NSLog(@"installTrollStore temp TrollStore path: %@", tmpTrollStore); 284 | 285 | NSString* tmpTrollStoreMain = [tmpTrollStore stringByAppendingPathComponent:@"TrollStore"]; 286 | NSString* tmpTrollStoreRootHelper = [tmpTrollStore stringByAppendingPathComponent:@"trollstorehelper"]; 287 | NSString* tmpTrollStoreLdid = [tmpTrollStore stringByAppendingPathComponent:@"ldid"]; 288 | 289 | // make executable 290 | chmod(tmpTrollStoreMain.UTF8String, 0755); 291 | chmod(tmpTrollStoreRootHelper.UTF8String, 0755); 292 | chmod(tmpTrollStoreLdid.UTF8String, 0755); 293 | 294 | // set owners 295 | chown(tmpTrollStoreMain.UTF8String, 33, 33); 296 | chown(tmpTrollStoreRootHelper.UTF8String, 0, 0); // set root helper binary owner to root 297 | chown(tmpTrollStoreLdid.UTF8String, 0, 0); 298 | 299 | NSLog(@"installTrollStore extracted and prepared TrollStore app, now installing..."); 300 | 301 | installApp(tmpTrollStore, @"com.apple.TrollStore", NO, nil); 302 | 303 | [[NSFileManager defaultManager] removeItemAtPath:tmpPath error:nil]; 304 | 305 | return YES; 306 | } 307 | 308 | int main(int argc, char *argv[], char *envp[]) { 309 | @autoreleasepool { 310 | if(argc <= 1) return -1; 311 | 312 | NSLog(@"trollstore helper go, uid: %d, gid: %d", getuid(), getgid()); 313 | NSLog(@"ok %d", argc); 314 | 315 | NSBundle* mcmBundle = [NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/MobileContainerManager.framework"]; 316 | [mcmBundle load]; 317 | 318 | BOOL suc = NO; 319 | NSError* error; 320 | 321 | NSString* cmd = [NSString stringWithUTF8String:argv[1]]; 322 | if([cmd isEqualToString:@"install"]) 323 | { 324 | if(argc <= 2) return -2; 325 | NSString* ipaPath = [NSString stringWithUTF8String:argv[2]]; 326 | suc = installIpa(ipaPath, &error); 327 | } else if([cmd isEqualToString:@"uninstall"]) 328 | { 329 | if(argc <= 2) return -2; 330 | NSString* appId = [NSString stringWithUTF8String:argv[2]]; 331 | suc = uninstallApp(appId, &error); 332 | } else if([cmd isEqualToString:@"install-trollstore"]) 333 | { 334 | if(argc <= 2) return -2; 335 | NSString* tsTar = [NSString stringWithUTF8String:argv[2]]; 336 | suc = installTrollStore(tsTar); 337 | NSLog(@"installed troll store? %d", suc); 338 | } else if([cmd isEqualToString:@"uninstall-trollstore"]) 339 | { 340 | uninstallTrollStore(); 341 | uninstallAllApps(); 342 | } 343 | 344 | if(!suc) 345 | { 346 | NSLog(@"error: %@", error); 347 | } 348 | 349 | return !suc; 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /Pwnify/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // pwnify-universal 4 | // 5 | // Created by Lars Fröder on 08.10.22. 6 | // 7 | 8 | #import 9 | 10 | #import 11 | #import 12 | #import 13 | 14 | #define ALIGN_DEFAULT 0xE 15 | 16 | uint32_t roundUp(int numToRound, int multiple) 17 | { 18 | if (multiple == 0) 19 | return numToRound; 20 | 21 | int remainder = numToRound % multiple; 22 | if (remainder == 0) 23 | return numToRound; 24 | 25 | return numToRound + multiple - remainder; 26 | } 27 | 28 | void expandFile(FILE* file, uint32_t size) 29 | { 30 | fseek(file, 0, SEEK_END); 31 | if(ftell(file) >= size) return; 32 | 33 | while(ftell(file) != size) 34 | { 35 | char c = 0; 36 | fwrite(&c, 1, 1, file); 37 | } 38 | } 39 | 40 | void copyData(FILE* sourceFile, FILE* targetFile, size_t size) 41 | { 42 | for(size_t i = 0; i < size; i++) 43 | { 44 | char b; 45 | fread(&b, 1, 1, sourceFile); 46 | fwrite(&b, 1, 1, targetFile); 47 | } 48 | } 49 | 50 | void enumerateArchs(NSString* binaryPath, void (^archEnumBlock)(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop)) 51 | { 52 | FILE* machoFile = fopen(binaryPath.fileSystemRepresentation, "rb"); 53 | if(!machoFile) return; 54 | 55 | struct mach_header header; 56 | fread(&header,sizeof(header),1,machoFile); 57 | 58 | if(header.magic == FAT_MAGIC || header.magic == FAT_CIGAM) 59 | { 60 | fseek(machoFile,0,SEEK_SET); 61 | struct fat_header fatHeader; 62 | fread(&fatHeader,sizeof(fatHeader),1,machoFile); 63 | 64 | for(int i = 0; i < OSSwapBigToHostInt32(fatHeader.nfat_arch); i++) 65 | { 66 | uint32_t archFileOffset = sizeof(fatHeader) + sizeof(struct fat_arch) * i; 67 | struct fat_arch fatArch; 68 | fseek(machoFile, archFileOffset,SEEK_SET); 69 | fread(&fatArch,sizeof(fatArch),1,machoFile); 70 | 71 | uint32_t sliceFileOffset = OSSwapBigToHostInt32(fatArch.offset); 72 | struct mach_header archHeader; 73 | fseek(machoFile, sliceFileOffset, SEEK_SET); 74 | fread(&archHeader,sizeof(archHeader),1,machoFile); 75 | 76 | BOOL stop = NO; 77 | archEnumBlock(&fatArch, archFileOffset, &archHeader, sliceFileOffset, machoFile, &stop); 78 | if(stop) break; 79 | } 80 | } 81 | else if(header.magic == MH_MAGIC_64 || header.magic == MH_CIGAM_64) 82 | { 83 | BOOL stop; 84 | archEnumBlock(NULL, 0, &header, 0, machoFile, &stop); 85 | } 86 | 87 | fclose(machoFile); 88 | } 89 | 90 | void printArchs(NSString* binaryPath) 91 | { 92 | __block int i = 0; 93 | enumerateArchs(binaryPath, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) { 94 | if(arch) 95 | { 96 | printf("%d. fatArch type: 0x%X, subtype: 0x%X, align:0x%X, size:0x%X, offset:0x%X\n| ", i, OSSwapBigToHostInt32(arch->cputype), OSSwapBigToHostInt32(arch->cpusubtype), OSSwapBigToHostInt32(arch->align), OSSwapBigToHostInt32(arch->size), OSSwapBigToHostInt32(arch->offset)); 97 | } 98 | printf("machHeader type: 0x%X, subtype: 0x%X\n", OSSwapLittleToHostInt32(machHeader->cputype), OSSwapLittleToHostInt32(machHeader->cpusubtype)); 99 | 100 | i++; 101 | }); 102 | } 103 | 104 | void pwnify(NSString* appStoreBinary, NSString* binaryToInject, BOOL preferArm64e) 105 | { 106 | NSString* tmpFilePath = [NSTemporaryDirectory() stringByAppendingString:[[NSUUID UUID] UUIDString]]; 107 | 108 | // Determine amount of slices in output 109 | __block int slicesCount = 1; 110 | enumerateArchs(appStoreBinary, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) { 111 | slicesCount++; 112 | }); 113 | 114 | // Allocate FAT data 115 | uint32_t fatDataSize = sizeof(struct fat_header) + slicesCount * sizeof(struct fat_arch); 116 | char* fatData = malloc(fatDataSize); 117 | 118 | // Construct new fat header 119 | struct fat_header fatHeader; 120 | fatHeader.magic = OSSwapHostToBigInt32(0xCAFEBABE); 121 | fatHeader.nfat_arch = OSSwapHostToBigInt32(slicesCount); 122 | memcpy(&fatData[0], &fatHeader, sizeof(fatHeader)); 123 | 124 | uint32_t align = pow(2, ALIGN_DEFAULT); 125 | __block uint32_t curOffset = align; 126 | __block uint32_t curArchIndex = 0; 127 | 128 | // Construct new fat arch data 129 | enumerateArchs(appStoreBinary, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) { 130 | struct fat_arch newArch; 131 | if(arch) 132 | { 133 | newArch.cputype = arch->cputype; 134 | 135 | if(OSSwapBigToHostInt32(arch->cputype) == 0x100000C) 136 | { 137 | newArch.cpusubtype = OSSwapHostToBigInt32(2); // SET app store binary in FAT header to 2, fixes arm64e 138 | } 139 | else 140 | { 141 | newArch.cpusubtype = arch->cpusubtype; 142 | } 143 | 144 | newArch.size = arch->size; 145 | } 146 | else 147 | { 148 | newArch.cputype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cputype)); 149 | 150 | if(OSSwapLittleToHostInt32(machHeader->cputype) == 0x100000C) 151 | { 152 | newArch.cpusubtype = OSSwapHostToBigInt32(2); // SET app store binary in FAT header to 2, fixes arm64e 153 | } 154 | else 155 | { 156 | newArch.cpusubtype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cpusubtype)); 157 | } 158 | 159 | newArch.size = OSSwapHostToBigInt32((uint32_t)[[[NSFileManager defaultManager] attributesOfItemAtPath:appStoreBinary error:nil] fileSize]); 160 | } 161 | 162 | newArch.align = OSSwapHostToBigInt32(ALIGN_DEFAULT); 163 | newArch.offset = OSSwapHostToBigInt32(curOffset); 164 | curOffset += roundUp(OSSwapBigToHostInt32(newArch.size), align); 165 | 166 | memcpy(&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex], &newArch, sizeof(newArch)); 167 | curArchIndex++; 168 | }); 169 | 170 | // Determine what slices our injection binary contains 171 | __block BOOL toInjectHasArm64e = NO; 172 | __block BOOL toInjectHasArm64 = NO; 173 | enumerateArchs(binaryToInject, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) { 174 | if(arch) 175 | { 176 | if(OSSwapBigToHostInt32(arch->cputype) == 0x100000C) 177 | { 178 | if (!((OSSwapBigToHostInt32(arch->cpusubtype) ^ 0x2) & 0xFFFFFF)) 179 | { 180 | toInjectHasArm64e = YES; 181 | } 182 | else if(!((OSSwapBigToHostInt32(arch->cpusubtype) ^ 0x1) & 0xFFFFFF)) 183 | { 184 | toInjectHasArm64 = YES; 185 | } 186 | } 187 | } 188 | else 189 | { 190 | if(OSSwapLittleToHostInt32(machHeader->cputype) == 0x100000C) 191 | { 192 | if (!((OSSwapLittleToHostInt32(machHeader->cpusubtype) ^ 0x2) & 0xFFFFFF)) 193 | { 194 | toInjectHasArm64e = YES; 195 | } 196 | else if(!((OSSwapLittleToHostInt32(machHeader->cpusubtype) ^ 0x1) & 0xFFFFFF)) 197 | { 198 | toInjectHasArm64 = YES; 199 | } 200 | } 201 | } 202 | }); 203 | 204 | if(!toInjectHasArm64 && !preferArm64e) 205 | { 206 | printf("ERROR: can't proceed injection because binary to inject has no arm64 slice\n"); 207 | return; 208 | } 209 | 210 | uint32_t subtypeToUse = 0x1; 211 | if(preferArm64e && toInjectHasArm64e) 212 | { 213 | subtypeToUse = 0x2; 214 | } 215 | 216 | enumerateArchs(binaryToInject, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) { 217 | struct fat_arch currentArch; 218 | if(arch) 219 | { 220 | currentArch.cputype = arch->cputype; 221 | currentArch.cpusubtype = arch->cpusubtype; 222 | currentArch.size = arch->size; 223 | } 224 | else 225 | { 226 | currentArch.cputype = OSSwapHostToBigInt(OSSwapLittleToHostInt32(machHeader->cputype)); 227 | currentArch.cpusubtype = OSSwapHostToBigInt(OSSwapLittleToHostInt32(machHeader->cpusubtype)); 228 | currentArch.size = OSSwapHostToBigInt((uint32_t)[[[NSFileManager defaultManager] attributesOfItemAtPath:binaryToInject error:nil] fileSize]); 229 | } 230 | 231 | if(OSSwapBigToHostInt32(currentArch.cputype) == 0x100000C) 232 | { 233 | if (!((OSSwapBigToHostInt32(currentArch.cpusubtype) ^ subtypeToUse) & 0xFFFFFF)) 234 | { 235 | currentArch.align = OSSwapHostToBigInt32(ALIGN_DEFAULT); 236 | currentArch.offset = OSSwapHostToBigInt32(curOffset); 237 | curOffset += roundUp(OSSwapBigToHostInt32(currentArch.size), align); 238 | memcpy(&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex], ¤tArch, sizeof(currentArch)); 239 | curArchIndex++; 240 | *stop = YES; 241 | } 242 | } 243 | }); 244 | 245 | // FAT Header constructed, now write to file and then write the slices themselves 246 | 247 | FILE* tmpFile = fopen(tmpFilePath.fileSystemRepresentation, "wb"); 248 | fwrite(&fatData[0], fatDataSize, 1, tmpFile); 249 | 250 | curArchIndex = 0; 251 | enumerateArchs(appStoreBinary, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) { 252 | struct fat_arch* toWriteArch = (struct fat_arch*)&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex]; 253 | 254 | expandFile(tmpFile, OSSwapBigToHostInt32(toWriteArch->offset)); 255 | 256 | uint32_t offset = 0; 257 | uint32_t size = 0; 258 | 259 | if(arch) 260 | { 261 | offset = OSSwapBigToHostInt32(arch->offset); 262 | size = OSSwapBigToHostInt32(arch->size); 263 | } 264 | else 265 | { 266 | size = OSSwapBigToHostInt32(toWriteArch->size); 267 | } 268 | 269 | FILE* appStoreBinaryFile = fopen(appStoreBinary.fileSystemRepresentation, "rb"); 270 | fseek(appStoreBinaryFile, offset, SEEK_SET); 271 | copyData(appStoreBinaryFile, tmpFile, size); 272 | fclose(appStoreBinaryFile); 273 | curArchIndex++; 274 | }); 275 | 276 | struct fat_arch* toWriteArch = (struct fat_arch*)&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex]; 277 | enumerateArchs(binaryToInject, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) { 278 | struct fat_arch currentArch; 279 | if(arch) 280 | { 281 | currentArch.cputype = arch->cputype; 282 | currentArch.cpusubtype = arch->cpusubtype; 283 | currentArch.size = arch->size; 284 | } 285 | else 286 | { 287 | currentArch.cputype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cputype)); 288 | currentArch.cpusubtype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cpusubtype)); 289 | currentArch.size = OSSwapHostToBigInt32((uint32_t)[[[NSFileManager defaultManager] attributesOfItemAtPath:binaryToInject error:nil] fileSize]); 290 | } 291 | 292 | if(OSSwapBigToHostInt32(currentArch.cputype) == 0x100000C) 293 | { 294 | if (!((OSSwapBigToHostInt32(currentArch.cpusubtype) ^ subtypeToUse) & 0xFFFFFF)) 295 | { 296 | expandFile(tmpFile, OSSwapBigToHostInt32(toWriteArch->offset)); 297 | 298 | uint32_t offset = 0; 299 | uint32_t size = 0; 300 | 301 | if(arch) 302 | { 303 | offset = OSSwapBigToHostInt32(arch->offset); 304 | size = OSSwapBigToHostInt32(arch->size); 305 | } 306 | else 307 | { 308 | size = OSSwapBigToHostInt32(toWriteArch->size); 309 | } 310 | 311 | FILE* binaryToInjectFile = fopen(binaryToInject.fileSystemRepresentation, "rb"); 312 | fseek(binaryToInjectFile, offset, SEEK_SET); 313 | copyData(binaryToInjectFile, tmpFile, size); 314 | fclose(binaryToInjectFile); 315 | *stop = YES; 316 | } 317 | } 318 | }); 319 | 320 | fclose(tmpFile); 321 | chmod(tmpFilePath.fileSystemRepresentation, 0755); 322 | 323 | [[NSFileManager defaultManager] removeItemAtPath:appStoreBinary error:nil]; 324 | [[NSFileManager defaultManager] moveItemAtPath:tmpFilePath toPath:appStoreBinary error:nil]; 325 | } 326 | 327 | void setCPUSubtype(NSString* binaryPath, uint32_t subtype) 328 | { 329 | FILE* binaryFile = fopen(binaryPath.fileSystemRepresentation, "rb+"); 330 | if(!binaryFile) 331 | { 332 | printf("ERROR: File not found\n"); 333 | return; 334 | } 335 | 336 | enumerateArchs(binaryPath, ^(struct fat_arch *arch, uint32_t archFileOffset, struct mach_header *machHeader, uint32_t sliceFileOffset, FILE *file, BOOL *stop) { 337 | 338 | if(arch) 339 | { 340 | if(OSSwapBigToHostInt(arch->cputype) == 0x100000C) 341 | { 342 | if(OSSwapBigToHostInt(arch->cpusubtype) == 0x0) 343 | { 344 | arch->cpusubtype = OSSwapHostToBigInt32(subtype); 345 | fseek(binaryFile, archFileOffset, SEEK_SET); 346 | fwrite(arch, sizeof(struct fat_arch), 1, binaryFile); 347 | } 348 | } 349 | } 350 | 351 | if(OSSwapLittleToHostInt32(machHeader->cputype) == 0x100000C) 352 | { 353 | if(OSSwapLittleToHostInt32(machHeader->cpusubtype) == 0x0) 354 | { 355 | machHeader->cpusubtype = OSSwapHostToLittleInt32(subtype); 356 | fseek(binaryFile, sliceFileOffset, SEEK_SET); 357 | fwrite(machHeader, sizeof(struct mach_header), 1, binaryFile); 358 | } 359 | } 360 | }); 361 | 362 | fclose(binaryFile); 363 | } 364 | 365 | void printUsageAndExit(void) 366 | { 367 | printf("Usage:\n\nPrint architectures of a binary:\npwnify print \n\nInject target slice into victim binary:\npwnify pwn(64e) \n\nModify cpusubtype of a non FAT binary:\npwnify set-cpusubtype \n"); 368 | exit(0); 369 | } 370 | 371 | int main(int argc, const char * argv[]) { 372 | @autoreleasepool { 373 | if(argc < 3) 374 | { 375 | printUsageAndExit(); 376 | } 377 | 378 | NSString* operation = [NSString stringWithUTF8String:argv[1]]; 379 | 380 | if([operation isEqualToString:@"print"]) 381 | { 382 | NSString* binaryToPrint = [NSString stringWithUTF8String:argv[2]]; 383 | printArchs(binaryToPrint); 384 | } 385 | else if([operation isEqualToString:@"pwn"]) 386 | { 387 | if(argc < 4) printUsageAndExit(); 388 | NSString* victimBinary = [NSString stringWithUTF8String:argv[2]]; 389 | NSString* targetBinary = [NSString stringWithUTF8String:argv[3]]; 390 | pwnify(victimBinary, targetBinary, NO); 391 | } 392 | else if([operation isEqualToString:@"pwn64e"]) 393 | { 394 | if(argc < 4) printUsageAndExit(); 395 | NSString* victimBinary = [NSString stringWithUTF8String:argv[2]]; 396 | NSString* targetBinary = [NSString stringWithUTF8String:argv[3]]; 397 | pwnify(victimBinary, targetBinary, YES); 398 | } 399 | else if([operation isEqualToString:@"set-cpusubtype"]) 400 | { 401 | if(argc < 4) printUsageAndExit(); 402 | NSString* binaryToModify = [NSString stringWithUTF8String:argv[2]]; 403 | NSString* subtypeToSet = [NSString stringWithUTF8String:argv[3]]; 404 | 405 | NSNumberFormatter* f = [[NSNumberFormatter alloc] init]; 406 | f.numberStyle = NSNumberFormatterDecimalStyle; 407 | NSNumber* subtypeToSetNum = [f numberFromString:subtypeToSet]; 408 | 409 | setCPUSubtype(binaryToModify, [subtypeToSetNum unsignedIntValue]); 410 | } 411 | else 412 | { 413 | printUsageAndExit(); 414 | } 415 | } 416 | return 0; 417 | } 418 | -------------------------------------------------------------------------------- /Shared/TSUtil.m: -------------------------------------------------------------------------------- 1 | #import "TSUtil.h" 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | @interface PSAppDataUsagePolicyCache : NSObject 8 | + (instancetype)sharedInstance; 9 | - (void)setUsagePoliciesForBundle:(NSString*)bundleId cellular:(BOOL)cellular wifi:(BOOL)wifi; 10 | @end 11 | 12 | #define POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE 1 13 | extern int posix_spawnattr_set_persona_np(const posix_spawnattr_t* __restrict, uid_t, uint32_t); 14 | extern int posix_spawnattr_set_persona_uid_np(const posix_spawnattr_t* __restrict, uid_t); 15 | extern int posix_spawnattr_set_persona_gid_np(const posix_spawnattr_t* __restrict, uid_t); 16 | 17 | void chineseWifiFixup(void) 18 | { 19 | NSBundle *bundle = [NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/SettingsCellular.framework"]; 20 | [bundle load]; 21 | 22 | PSAppDataUsagePolicyCache* policyCache = [NSClassFromString(@"PSAppDataUsagePolicyCache") sharedInstance]; 23 | if([policyCache respondsToSelector:@selector(setUsagePoliciesForBundle:cellular:wifi:)]) 24 | { 25 | [policyCache setUsagePoliciesForBundle:NSBundle.mainBundle.bundleIdentifier cellular:true wifi:true]; 26 | } 27 | } 28 | 29 | extern char*** _NSGetArgv(); 30 | NSString* safe_getExecutablePath() 31 | { 32 | char* executablePathC = **_NSGetArgv(); 33 | return [NSString stringWithUTF8String:executablePathC]; 34 | } 35 | 36 | #ifdef EMBEDDED_ROOT_HELPER 37 | NSString* rootHelperPath(void) 38 | { 39 | return safe_getExecutablePath(); 40 | } 41 | #else 42 | NSString* rootHelperPath(void) 43 | { 44 | return [[NSBundle mainBundle].bundlePath stringByAppendingPathComponent:@"trollstorehelper"]; 45 | } 46 | #endif 47 | 48 | int fd_is_valid(int fd) 49 | { 50 | return fcntl(fd, F_GETFD) != -1 || errno != EBADF; 51 | } 52 | 53 | NSString* getNSStringFromFile(int fd) 54 | { 55 | NSMutableString* ms = [NSMutableString new]; 56 | ssize_t num_read; 57 | char c; 58 | if(!fd_is_valid(fd)) return @""; 59 | while((num_read = read(fd, &c, sizeof(c)))) 60 | { 61 | [ms appendString:[NSString stringWithFormat:@"%c", c]]; 62 | if(c == '\n') break; 63 | } 64 | return ms.copy; 65 | } 66 | 67 | void printMultilineNSString(NSString* stringToPrint) 68 | { 69 | NSCharacterSet *separator = [NSCharacterSet newlineCharacterSet]; 70 | NSArray* lines = [stringToPrint componentsSeparatedByCharactersInSet:separator]; 71 | for(NSString* line in lines) 72 | { 73 | NSLog(@"%@", line); 74 | } 75 | } 76 | 77 | int spawnRoot(NSString* path, NSArray* args, NSString** stdOut, NSString** stdErr) 78 | { 79 | NSMutableArray* argsM = args.mutableCopy ?: [NSMutableArray new]; 80 | [argsM insertObject:path atIndex:0]; 81 | 82 | NSUInteger argCount = [argsM count]; 83 | char **argsC = (char **)malloc((argCount + 1) * sizeof(char*)); 84 | 85 | for (NSUInteger i = 0; i < argCount; i++) 86 | { 87 | argsC[i] = strdup([[argsM objectAtIndex:i] UTF8String]); 88 | } 89 | argsC[argCount] = NULL; 90 | 91 | posix_spawnattr_t attr; 92 | posix_spawnattr_init(&attr); 93 | 94 | posix_spawnattr_set_persona_np(&attr, 99, POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE); 95 | posix_spawnattr_set_persona_uid_np(&attr, 0); 96 | posix_spawnattr_set_persona_gid_np(&attr, 0); 97 | 98 | posix_spawn_file_actions_t action; 99 | posix_spawn_file_actions_init(&action); 100 | 101 | int outErr[2]; 102 | if(stdErr) 103 | { 104 | pipe(outErr); 105 | posix_spawn_file_actions_adddup2(&action, outErr[1], STDERR_FILENO); 106 | posix_spawn_file_actions_addclose(&action, outErr[0]); 107 | } 108 | 109 | int out[2]; 110 | if(stdOut) 111 | { 112 | pipe(out); 113 | posix_spawn_file_actions_adddup2(&action, out[1], STDOUT_FILENO); 114 | posix_spawn_file_actions_addclose(&action, out[0]); 115 | } 116 | 117 | pid_t task_pid; 118 | int status = -200; 119 | int spawnError = posix_spawn(&task_pid, [path UTF8String], &action, &attr, (char* const*)argsC, NULL); 120 | posix_spawnattr_destroy(&attr); 121 | for (NSUInteger i = 0; i < argCount; i++) 122 | { 123 | free(argsC[i]); 124 | } 125 | free(argsC); 126 | 127 | if(spawnError != 0) 128 | { 129 | NSLog(@"posix_spawn error %d\n", spawnError); 130 | return spawnError; 131 | } 132 | 133 | __block volatile BOOL _isRunning = YES; 134 | NSMutableString* outString = [NSMutableString new]; 135 | NSMutableString* errString = [NSMutableString new]; 136 | dispatch_semaphore_t sema = 0; 137 | dispatch_queue_t logQueue; 138 | if(stdOut || stdErr) 139 | { 140 | logQueue = dispatch_queue_create("com.opa334.TrollStore.LogCollector", NULL); 141 | sema = dispatch_semaphore_create(0); 142 | 143 | int outPipe = out[0]; 144 | int outErrPipe = outErr[0]; 145 | 146 | __block BOOL outEnabled = (BOOL)stdOut; 147 | __block BOOL errEnabled = (BOOL)stdErr; 148 | dispatch_async(logQueue, ^ 149 | { 150 | while(_isRunning) 151 | { 152 | @autoreleasepool 153 | { 154 | if(outEnabled) 155 | { 156 | [outString appendString:getNSStringFromFile(outPipe)]; 157 | } 158 | if(errEnabled) 159 | { 160 | [errString appendString:getNSStringFromFile(outErrPipe)]; 161 | } 162 | } 163 | } 164 | dispatch_semaphore_signal(sema); 165 | }); 166 | } 167 | 168 | do 169 | { 170 | if (waitpid(task_pid, &status, 0) != -1) { 171 | NSLog(@"Child status %d", WEXITSTATUS(status)); 172 | } else 173 | { 174 | perror("waitpid"); 175 | _isRunning = NO; 176 | return -222; 177 | } 178 | } while (!WIFEXITED(status) && !WIFSIGNALED(status)); 179 | 180 | _isRunning = NO; 181 | if(stdOut || stdErr) 182 | { 183 | if(stdOut) 184 | { 185 | close(out[1]); 186 | } 187 | if(stdErr) 188 | { 189 | close(outErr[1]); 190 | } 191 | 192 | // wait for logging queue to finish 193 | dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); 194 | 195 | if(stdOut) 196 | { 197 | *stdOut = outString.copy; 198 | } 199 | if(stdErr) 200 | { 201 | *stdErr = errString.copy; 202 | } 203 | } 204 | 205 | return WEXITSTATUS(status); 206 | } 207 | 208 | void enumerateProcessesUsingBlock(void (^enumerator)(pid_t pid, NSString* executablePath, BOOL* stop)) 209 | { 210 | static int maxArgumentSize = 0; 211 | if (maxArgumentSize == 0) { 212 | size_t size = sizeof(maxArgumentSize); 213 | if (sysctl((int[]){ CTL_KERN, KERN_ARGMAX }, 2, &maxArgumentSize, &size, NULL, 0) == -1) { 214 | perror("sysctl argument size"); 215 | maxArgumentSize = 4096; // Default 216 | } 217 | } 218 | int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL}; 219 | struct kinfo_proc *info; 220 | size_t length; 221 | int count; 222 | 223 | if (sysctl(mib, 3, NULL, &length, NULL, 0) < 0) 224 | return; 225 | if (!(info = malloc(length))) 226 | return; 227 | if (sysctl(mib, 3, info, &length, NULL, 0) < 0) { 228 | free(info); 229 | return; 230 | } 231 | count = length / sizeof(struct kinfo_proc); 232 | for (int i = 0; i < count; i++) { 233 | @autoreleasepool { 234 | pid_t pid = info[i].kp_proc.p_pid; 235 | if (pid == 0) { 236 | continue; 237 | } 238 | size_t size = maxArgumentSize; 239 | char* buffer = (char *)malloc(length); 240 | if (sysctl((int[]){ CTL_KERN, KERN_PROCARGS2, pid }, 3, buffer, &size, NULL, 0) == 0) { 241 | NSString* executablePath = [NSString stringWithCString:(buffer+sizeof(int)) encoding:NSUTF8StringEncoding]; 242 | 243 | BOOL stop = NO; 244 | enumerator(pid, executablePath, &stop); 245 | if(stop) 246 | { 247 | free(buffer); 248 | break; 249 | } 250 | } 251 | free(buffer); 252 | } 253 | } 254 | free(info); 255 | } 256 | 257 | void killall(NSString* processName, BOOL softly) 258 | { 259 | enumerateProcessesUsingBlock(^(pid_t pid, NSString* executablePath, BOOL* stop) 260 | { 261 | if([executablePath.lastPathComponent isEqualToString:processName]) 262 | { 263 | if(softly) 264 | { 265 | kill(pid, SIGTERM); 266 | } 267 | else 268 | { 269 | kill(pid, SIGKILL); 270 | } 271 | } 272 | }); 273 | } 274 | 275 | void respring(void) 276 | { 277 | killall(@"SpringBoard", YES); 278 | exit(0); 279 | } 280 | 281 | void github_fetchLatestVersion(NSString* repo, void (^completionHandler)(NSString* latestVersion)) 282 | { 283 | NSString* urlString = [NSString stringWithFormat:@"https://api.github.com/repos/%@/releases/latest", repo]; 284 | NSURL* githubLatestAPIURL = [NSURL URLWithString:urlString]; 285 | 286 | NSURLSessionDataTask* task = [NSURLSession.sharedSession dataTaskWithURL:githubLatestAPIURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) 287 | { 288 | if(!error) 289 | { 290 | if ([response isKindOfClass:[NSHTTPURLResponse class]]) 291 | { 292 | NSError *jsonError; 293 | NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; 294 | 295 | if (!jsonError) 296 | { 297 | completionHandler(jsonResponse[@"tag_name"]); 298 | } 299 | } 300 | } 301 | }]; 302 | 303 | [task resume]; 304 | } 305 | 306 | void fetchLatestTrollStoreVersion(void (^completionHandler)(NSString* latestVersion)) 307 | { 308 | github_fetchLatestVersion(@"opa334/TrollStore", completionHandler); 309 | } 310 | 311 | void fetchLatestLdidVersion(void (^completionHandler)(NSString* latestVersion)) 312 | { 313 | github_fetchLatestVersion(@"opa334/ldid", completionHandler); 314 | } 315 | 316 | NSArray* trollStoreInstalledAppContainerPaths() 317 | { 318 | NSMutableArray* appContainerPaths = [NSMutableArray new]; 319 | 320 | NSString* appContainersPath = @"/var/containers/Bundle/Application"; 321 | 322 | NSError* error; 323 | NSArray* containers = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:appContainersPath error:&error]; 324 | if(error) 325 | { 326 | NSLog(@"error getting app bundles paths %@", error); 327 | } 328 | if(!containers) return nil; 329 | 330 | for(NSString* container in containers) 331 | { 332 | NSString* containerPath = [appContainersPath stringByAppendingPathComponent:container]; 333 | BOOL isDirectory = NO; 334 | BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:containerPath isDirectory:&isDirectory]; 335 | if(exists && isDirectory) 336 | { 337 | NSString* trollStoreMark = [containerPath stringByAppendingPathComponent:@"_TrollStore"]; 338 | if([[NSFileManager defaultManager] fileExistsAtPath:trollStoreMark]) 339 | { 340 | NSString* trollStoreApp = [containerPath stringByAppendingPathComponent:@"TrollStore.app"]; 341 | if(![[NSFileManager defaultManager] fileExistsAtPath:trollStoreApp]) 342 | { 343 | [appContainerPaths addObject:containerPath]; 344 | } 345 | } 346 | } 347 | } 348 | 349 | return appContainerPaths.copy; 350 | } 351 | 352 | NSArray* trollStoreInstalledAppBundlePaths() 353 | { 354 | NSMutableArray* appPaths = [NSMutableArray new]; 355 | for(NSString* containerPath in trollStoreInstalledAppContainerPaths()) 356 | { 357 | NSArray* items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:containerPath error:nil]; 358 | if(!items) return nil; 359 | 360 | for(NSString* item in items) 361 | { 362 | if([item.pathExtension isEqualToString:@"app"]) 363 | { 364 | [appPaths addObject:[containerPath stringByAppendingPathComponent:item]]; 365 | } 366 | } 367 | } 368 | return appPaths.copy; 369 | } 370 | 371 | NSString* trollStorePath() 372 | { 373 | NSError* mcmError; 374 | MCMAppContainer* appContainer = [MCMAppContainer containerWithIdentifier:@"com.opa334.TrollStore" createIfNecessary:NO existed:NULL error:&mcmError]; 375 | if(!appContainer) return nil; 376 | return appContainer.url.path; 377 | } 378 | 379 | NSString* trollStoreAppPath() 380 | { 381 | return [trollStorePath() stringByAppendingPathComponent:@"TrollStore.app"]; 382 | } 383 | 384 | BOOL isRemovableSystemApp(NSString* appId) 385 | { 386 | return [[NSFileManager defaultManager] fileExistsAtPath:[@"/System/Library/AppSignatures" stringByAppendingPathComponent:appId]]; 387 | } 388 | 389 | LSApplicationProxy* findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE allowedTypes) 390 | { 391 | __block LSApplicationProxy* outProxy; 392 | 393 | void (^searchBlock)(LSApplicationProxy* appProxy) = ^(LSApplicationProxy* appProxy) 394 | { 395 | if(appProxy.installed && !appProxy.restricted) 396 | { 397 | if([appProxy.bundleURL.path hasPrefix:@"/private/var/containers"]) 398 | { 399 | NSURL* trollStorePersistenceMarkURL = [appProxy.bundleURL URLByAppendingPathComponent:@".TrollStorePersistenceHelper"]; 400 | if([trollStorePersistenceMarkURL checkResourceIsReachableAndReturnError:nil]) 401 | { 402 | outProxy = appProxy; 403 | } 404 | } 405 | } 406 | }; 407 | 408 | if(allowedTypes & PERSISTENCE_HELPER_TYPE_USER) 409 | { 410 | [[LSApplicationWorkspace defaultWorkspace] enumerateApplicationsOfType:0 block:searchBlock]; 411 | } 412 | if(allowedTypes & PERSISTENCE_HELPER_TYPE_SYSTEM) 413 | { 414 | [[LSApplicationWorkspace defaultWorkspace] enumerateApplicationsOfType:1 block:searchBlock]; 415 | } 416 | 417 | return outProxy; 418 | } 419 | 420 | SecStaticCodeRef getStaticCodeRef(NSString *binaryPath) 421 | { 422 | if(binaryPath == nil) 423 | { 424 | return NULL; 425 | } 426 | 427 | CFURLRef binaryURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (__bridge CFStringRef)binaryPath, kCFURLPOSIXPathStyle, false); 428 | if(binaryURL == NULL) 429 | { 430 | NSLog(@"[getStaticCodeRef] failed to get URL to binary %@", binaryPath); 431 | return NULL; 432 | } 433 | 434 | SecStaticCodeRef codeRef = NULL; 435 | OSStatus result; 436 | 437 | result = SecStaticCodeCreateWithPathAndAttributes(binaryURL, kSecCSDefaultFlags, NULL, &codeRef); 438 | 439 | CFRelease(binaryURL); 440 | 441 | if(result != errSecSuccess) 442 | { 443 | NSLog(@"[getStaticCodeRef] failed to create static code for binary %@", binaryPath); 444 | return NULL; 445 | } 446 | 447 | return codeRef; 448 | } 449 | 450 | NSDictionary* dumpEntitlements(SecStaticCodeRef codeRef) 451 | { 452 | if(codeRef == NULL) 453 | { 454 | NSLog(@"[dumpEntitlements] attempting to dump entitlements without a StaticCodeRef"); 455 | return nil; 456 | } 457 | 458 | CFDictionaryRef signingInfo = NULL; 459 | OSStatus result; 460 | 461 | result = SecCodeCopySigningInformation(codeRef, kSecCSRequirementInformation, &signingInfo); 462 | 463 | if(result != errSecSuccess) 464 | { 465 | NSLog(@"[dumpEntitlements] failed to copy signing info from static code"); 466 | return nil; 467 | } 468 | 469 | NSDictionary *entitlementsNSDict = nil; 470 | 471 | CFDictionaryRef entitlements = CFDictionaryGetValue(signingInfo, kSecCodeInfoEntitlementsDict); 472 | if(entitlements == NULL) 473 | { 474 | NSLog(@"[dumpEntitlements] no entitlements specified"); 475 | } 476 | else if(CFGetTypeID(entitlements) != CFDictionaryGetTypeID()) 477 | { 478 | NSLog(@"[dumpEntitlements] invalid entitlements"); 479 | } 480 | else 481 | { 482 | entitlementsNSDict = (__bridge NSDictionary *)(entitlements); 483 | NSLog(@"[dumpEntitlements] dumped %@", entitlementsNSDict); 484 | } 485 | 486 | CFRelease(signingInfo); 487 | return entitlementsNSDict; 488 | } 489 | 490 | NSDictionary* dumpEntitlementsFromBinaryAtPath(NSString *binaryPath) 491 | { 492 | // This function is intended for one-shot checks. Main-event functions should retain/release their own SecStaticCodeRefs 493 | 494 | if(binaryPath == nil) 495 | { 496 | return nil; 497 | } 498 | 499 | SecStaticCodeRef codeRef = getStaticCodeRef(binaryPath); 500 | if(codeRef == NULL) 501 | { 502 | return nil; 503 | } 504 | 505 | NSDictionary *entitlements = dumpEntitlements(codeRef); 506 | CFRelease(codeRef); 507 | 508 | return entitlements; 509 | } 510 | 511 | NSDictionary* dumpEntitlementsFromBinaryData(NSData* binaryData) 512 | { 513 | NSDictionary* entitlements; 514 | NSString* tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString]; 515 | NSURL* tmpURL = [NSURL fileURLWithPath:tmpPath]; 516 | if([binaryData writeToURL:tmpURL options:NSDataWritingAtomic error:nil]) 517 | { 518 | entitlements = dumpEntitlementsFromBinaryAtPath(tmpPath); 519 | [[NSFileManager defaultManager] removeItemAtURL:tmpURL error:nil]; 520 | } 521 | return entitlements; 522 | } -------------------------------------------------------------------------------- /TrollStore/TSAppTableViewController.m: -------------------------------------------------------------------------------- 1 | #import "TSAppTableViewController.h" 2 | 3 | #import "TSApplicationsManager.h" 4 | #import 5 | #import "TSInstallationController.h" 6 | #import "TSUtil.h" 7 | @import UniformTypeIdentifiers; 8 | 9 | #define ICON_FORMAT_IPAD 8 10 | #define ICON_FORMAT_IPHONE 10 11 | 12 | NSInteger iconFormatToUse(void) 13 | { 14 | if(UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) 15 | { 16 | return ICON_FORMAT_IPAD; 17 | } 18 | else 19 | { 20 | return ICON_FORMAT_IPHONE; 21 | } 22 | } 23 | 24 | UIImage* imageWithSize(UIImage* image, CGSize size) 25 | { 26 | if(CGSizeEqualToSize(image.size, size)) return image; 27 | UIGraphicsBeginImageContextWithOptions(size, NO, UIScreen.mainScreen.scale); 28 | CGRect imageRect = CGRectMake(0.0, 0.0, size.width, size.height); 29 | [image drawInRect:imageRect]; 30 | UIImage* outImage = UIGraphicsGetImageFromCurrentImageContext(); 31 | UIGraphicsEndImageContext(); 32 | return outImage; 33 | } 34 | 35 | @interface UIImage () 36 | + (UIImage *)_applicationIconImageForBundleIdentifier:(NSString *)id format:(NSInteger)format scale:(double)scale; 37 | @end 38 | 39 | @implementation TSAppTableViewController 40 | 41 | - (void)loadAppInfos 42 | { 43 | NSArray* appPaths = [[TSApplicationsManager sharedInstance] installedAppPaths]; 44 | NSMutableArray* appInfos = [NSMutableArray new]; 45 | 46 | for(NSString* appPath in appPaths) 47 | { 48 | TSAppInfo* appInfo = [[TSAppInfo alloc] initWithAppBundlePath:appPath]; 49 | [appInfo sync_loadBasicInfo]; 50 | [appInfos addObject:appInfo]; 51 | } 52 | 53 | if(_searchKey && ![_searchKey isEqualToString:@""]) 54 | { 55 | [appInfos enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(TSAppInfo* appInfo, NSUInteger idx, BOOL* stop) 56 | { 57 | NSString* appName = [appInfo displayName]; 58 | BOOL nameMatch = [appName rangeOfString:_searchKey options:NSCaseInsensitiveSearch range:NSMakeRange(0, [appName length]) locale:[NSLocale currentLocale]].location != NSNotFound; 59 | if(!nameMatch) 60 | { 61 | [appInfos removeObjectAtIndex:idx]; 62 | } 63 | }]; 64 | } 65 | 66 | [appInfos sortUsingComparator:^(TSAppInfo* appInfoA, TSAppInfo* appInfoB) 67 | { 68 | return [[appInfoA displayName] localizedStandardCompare:[appInfoB displayName]]; 69 | }]; 70 | 71 | _cachedAppInfos = appInfos.copy; 72 | } 73 | 74 | - (instancetype)init 75 | { 76 | self = [super init]; 77 | if(self) 78 | { 79 | [self loadAppInfos]; 80 | _placeholderIcon = [UIImage _applicationIconImageForBundleIdentifier:@"com.apple.WebSheet" format:iconFormatToUse() scale:[UIScreen mainScreen].scale]; 81 | _cachedIcons = [NSMutableDictionary new]; 82 | [[LSApplicationWorkspace defaultWorkspace] addObserver:self]; 83 | } 84 | return self; 85 | } 86 | 87 | - (void)dealloc 88 | { 89 | [[LSApplicationWorkspace defaultWorkspace] removeObserver:self]; 90 | } 91 | 92 | - (void)reloadTable 93 | { 94 | [self loadAppInfos]; 95 | dispatch_async(dispatch_get_main_queue(), ^ 96 | { 97 | [self.tableView reloadData]; 98 | }); 99 | } 100 | 101 | - (void)loadView 102 | { 103 | [super loadView]; 104 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadTable) name:@"ApplicationsChanged" object:nil]; 105 | } 106 | 107 | - (void)viewDidLoad { 108 | [super viewDidLoad]; 109 | 110 | self.tableView.allowsMultipleSelectionDuringEditing = NO; 111 | self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; 112 | 113 | [self _setUpNavigationBar]; 114 | [self _setUpSearchBar]; 115 | } 116 | 117 | - (void)_setUpNavigationBar 118 | { 119 | UIAction* installFromFileAction = [UIAction actionWithTitle:@"Install IPA File" image:[UIImage systemImageNamed:@"doc.badge.plus"] identifier:@"InstallIPAFile" handler:^(__kindof UIAction *action) 120 | { 121 | dispatch_async(dispatch_get_main_queue(), ^ 122 | { 123 | UTType* ipaType = [UTType typeWithFilenameExtension:@"ipa" conformingToType:UTTypeData]; 124 | UTType* tipaType = [UTType typeWithFilenameExtension:@"tipa" conformingToType:UTTypeData]; 125 | 126 | UIDocumentPickerViewController* documentPickerVC = [[UIDocumentPickerViewController alloc] initForOpeningContentTypes:@[ipaType, tipaType]]; 127 | documentPickerVC.allowsMultipleSelection = NO; 128 | documentPickerVC.delegate = self; 129 | 130 | [TSPresentationDelegate presentViewController:documentPickerVC animated:YES completion:nil]; 131 | }); 132 | }]; 133 | 134 | UIAction* installFromURLAction = [UIAction actionWithTitle:@"Install from URL" image:[UIImage systemImageNamed:@"link.badge.plus"] identifier:@"InstallFromURL" handler:^(__kindof UIAction *action) 135 | { 136 | dispatch_async(dispatch_get_main_queue(), ^ 137 | { 138 | UIAlertController* installURLController = [UIAlertController alertControllerWithTitle:@"Install from URL" message:@"" preferredStyle:UIAlertControllerStyleAlert]; 139 | 140 | [installURLController addTextFieldWithConfigurationHandler:^(UITextField *textField) { 141 | textField.placeholder = @"URL"; 142 | }]; 143 | 144 | UIAlertAction* installAction = [UIAlertAction actionWithTitle:@"Install" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) 145 | { 146 | NSString* URLString = installURLController.textFields.firstObject.text; 147 | NSURL* remoteURL = [NSURL URLWithString:URLString]; 148 | 149 | [TSInstallationController handleAppInstallFromRemoteURL:remoteURL completion:nil]; 150 | }]; 151 | [installURLController addAction:installAction]; 152 | 153 | UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; 154 | [installURLController addAction:cancelAction]; 155 | 156 | [TSPresentationDelegate presentViewController:installURLController animated:YES completion:nil]; 157 | }); 158 | }]; 159 | 160 | UIMenu* installMenu = [UIMenu menuWithChildren:@[installFromFileAction, installFromURLAction]]; 161 | 162 | UIBarButtonItem* installBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"plus"] menu:installMenu]; 163 | 164 | self.navigationItem.rightBarButtonItems = @[installBarButtonItem]; 165 | } 166 | 167 | - (void)_setUpSearchBar 168 | { 169 | _searchController = [[UISearchController alloc] initWithSearchResultsController:nil]; 170 | _searchController.searchResultsUpdater = self; 171 | _searchController.obscuresBackgroundDuringPresentation = NO; 172 | self.navigationItem.searchController = _searchController; 173 | self.navigationItem.hidesSearchBarWhenScrolling = YES; 174 | } 175 | 176 | - (void)updateSearchResultsForSearchController:(UISearchController *)searchController 177 | { 178 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 179 | _searchKey = searchController.searchBar.text; 180 | [self reloadTable]; 181 | }); 182 | } 183 | 184 | - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls 185 | { 186 | NSString* pathToIPA = urls.firstObject.path; 187 | [TSInstallationController presentInstallationAlertIfEnabledForFile:pathToIPA isRemoteInstall:NO completion:nil]; 188 | } 189 | 190 | - (void)openAppPressedForRowAtIndexPath:(NSIndexPath*)indexPath 191 | { 192 | TSApplicationsManager* appsManager = [TSApplicationsManager sharedInstance]; 193 | 194 | TSAppInfo* appInfo = _cachedAppInfos[indexPath.row]; 195 | NSString* appId = [appInfo bundleIdentifier]; 196 | BOOL didOpen = [appsManager openApplicationWithBundleID:appId]; 197 | 198 | // if we failed to open the app, show an alert 199 | if(!didOpen) 200 | { 201 | NSString* failMessage = @""; 202 | if([[appInfo registrationState] isEqualToString:@"User"]) 203 | { 204 | failMessage = @"This app was not able to launch because it has a \"User\" registration state, register it as \"System\" and try again."; 205 | } 206 | 207 | NSString* failTitle = [NSString stringWithFormat:@"Failed to open %@", appId]; 208 | UIAlertController* didFailController = [UIAlertController alertControllerWithTitle:failTitle message:failMessage preferredStyle:UIAlertControllerStyleAlert]; 209 | UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; 210 | 211 | [didFailController addAction:cancelAction]; 212 | [TSPresentationDelegate presentViewController:didFailController animated:YES completion:nil]; 213 | } 214 | } 215 | 216 | - (void)showDetailsPressedForRowAtIndexPath:(NSIndexPath*)indexPath 217 | { 218 | TSAppInfo* appInfo = _cachedAppInfos[indexPath.row]; 219 | 220 | [appInfo loadInfoWithCompletion:^(NSError* error) 221 | { 222 | dispatch_async(dispatch_get_main_queue(), ^ 223 | { 224 | if(!error) 225 | { 226 | UIAlertController* detailsAlert = [UIAlertController alertControllerWithTitle:@"" message:@"" preferredStyle:UIAlertControllerStyleAlert]; 227 | detailsAlert.attributedTitle = [appInfo detailedInfoTitle]; 228 | detailsAlert.attributedMessage = [appInfo detailedInfoDescription]; 229 | 230 | UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; 231 | [detailsAlert addAction:closeAction]; 232 | 233 | [TSPresentationDelegate presentViewController:detailsAlert animated:YES completion:nil]; 234 | } 235 | else 236 | { 237 | UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Parse Error %ld", error.code] message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert]; 238 | UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; 239 | [errorAlert addAction:closeAction]; 240 | 241 | [TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil]; 242 | } 243 | }); 244 | }]; 245 | } 246 | 247 | - (void)changeAppRegistrationForRowAtIndexPath:(NSIndexPath*)indexPath toState:(NSString*)newState 248 | { 249 | TSAppInfo* appInfo = _cachedAppInfos[indexPath.row]; 250 | 251 | if([newState isEqualToString:@"User"]) 252 | { 253 | NSString* title = [NSString stringWithFormat:@"Switching '%@' to \"User\" Registration", [appInfo displayName]]; 254 | UIAlertController* confirmationAlert = [UIAlertController alertControllerWithTitle:title message:@"Switching this app to a \"User\" registration will make it unlaunchable after the next respring because the bugs exploited in TrollStore only affect apps registered as \"System\".\nThe purpose of this option is to make the app temporarily show up in settings, so you can adjust the settings and then switch it back to a \"System\" registration (TrollStore installed apps do not show up in settings otherwise). Additionally, the \"User\" registration state is also useful to temporarily fix iTunes file sharing, which also doesn't work for TrollStore installed apps otherwise.\nWhen you're done making the changes you need and want the app to become launchable again, you will need to switch it back to \"System\" state in TrollStore." preferredStyle:UIAlertControllerStyleAlert]; 255 | 256 | UIAlertAction* switchToUserAction = [UIAlertAction actionWithTitle:@"Switch to \"User\"" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) 257 | { 258 | [[TSApplicationsManager sharedInstance] changeAppRegistration:[appInfo bundlePath] toState:newState]; 259 | [appInfo sync_loadBasicInfo]; 260 | }]; 261 | 262 | [confirmationAlert addAction:switchToUserAction]; 263 | 264 | UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; 265 | 266 | [confirmationAlert addAction:cancelAction]; 267 | 268 | [TSPresentationDelegate presentViewController:confirmationAlert animated:YES completion:nil]; 269 | } 270 | else 271 | { 272 | [[TSApplicationsManager sharedInstance] changeAppRegistration:[appInfo bundlePath] toState:newState]; 273 | [appInfo sync_loadBasicInfo]; 274 | 275 | NSString* title = [NSString stringWithFormat:@"Switched '%@' to \"System\" Registration", [appInfo displayName]]; 276 | 277 | UIAlertController* infoAlert = [UIAlertController alertControllerWithTitle:title message:@"The app has been switched to the \"System\" registration state and will become launchable again after a respring." preferredStyle:UIAlertControllerStyleAlert]; 278 | 279 | UIAlertAction* respringAction = [UIAlertAction actionWithTitle:@"Respring" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) 280 | { 281 | respring(); 282 | }]; 283 | 284 | [infoAlert addAction:respringAction]; 285 | 286 | UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; 287 | 288 | [infoAlert addAction:closeAction]; 289 | 290 | [TSPresentationDelegate presentViewController:infoAlert animated:YES completion:nil]; 291 | } 292 | } 293 | 294 | - (void)uninstallPressedForRowAtIndexPath:(NSIndexPath*)indexPath 295 | { 296 | TSApplicationsManager* appsManager = [TSApplicationsManager sharedInstance]; 297 | 298 | TSAppInfo* appInfo = _cachedAppInfos[indexPath.row]; 299 | 300 | NSString* appPath = [appInfo bundlePath]; 301 | NSString* appId = [appInfo bundleIdentifier]; 302 | NSString* appName = [appInfo displayName]; 303 | 304 | UIAlertController* confirmAlert = [UIAlertController alertControllerWithTitle:@"Confirm Uninstallation" message:[NSString stringWithFormat:@"Uninstalling the app '%@' will delete the app and all data associated to it.", appName] preferredStyle:UIAlertControllerStyleAlert]; 305 | 306 | UIAlertAction* uninstallAction = [UIAlertAction actionWithTitle:@"Uninstall" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) 307 | { 308 | if(appId) 309 | { 310 | [appsManager uninstallApp:appId]; 311 | } 312 | else 313 | { 314 | [appsManager uninstallAppByPath:appPath]; 315 | } 316 | }]; 317 | [confirmAlert addAction:uninstallAction]; 318 | 319 | UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; 320 | [confirmAlert addAction:cancelAction]; 321 | 322 | [TSPresentationDelegate presentViewController:confirmAlert animated:YES completion:nil]; 323 | } 324 | 325 | - (void)deselectRow 326 | { 327 | [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES]; 328 | } 329 | 330 | #pragma mark - Table view data source 331 | 332 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 333 | return 1; 334 | } 335 | 336 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 337 | return _cachedAppInfos.count; 338 | } 339 | 340 | - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection 341 | { 342 | [self reloadTable]; 343 | } 344 | 345 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 346 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ApplicationCell"]; 347 | if(!cell) { 348 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"ApplicationCell"]; 349 | } 350 | 351 | if(!indexPath || indexPath.row > (_cachedAppInfos.count - 1)) return cell; 352 | 353 | TSAppInfo* appInfo = _cachedAppInfos[indexPath.row]; 354 | NSString* appId = [appInfo bundleIdentifier]; 355 | NSString* appVersion = [appInfo versionString]; 356 | 357 | // Configure the cell... 358 | cell.textLabel.text = [appInfo displayName]; 359 | cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ • %@", appVersion, appId]; 360 | cell.imageView.layer.borderWidth = 1; 361 | cell.imageView.layer.borderColor = [UIColor.labelColor colorWithAlphaComponent:0.1].CGColor; 362 | cell.imageView.layer.cornerRadius = 13.5; 363 | cell.imageView.layer.masksToBounds = YES; 364 | cell.imageView.layer.cornerCurve = kCACornerCurveContinuous; 365 | 366 | if(appId) 367 | { 368 | UIImage* cachedIcon = _cachedIcons[appId]; 369 | if(cachedIcon) 370 | { 371 | cell.imageView.image = cachedIcon; 372 | } 373 | else 374 | { 375 | cell.imageView.image = _placeholderIcon; 376 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ 377 | { 378 | //usleep(1000 * 5000); // (test delay for debugging) 379 | UIImage* iconImage = imageWithSize([UIImage _applicationIconImageForBundleIdentifier:appId format:iconFormatToUse() scale:[UIScreen mainScreen].scale], _placeholderIcon.size); 380 | _cachedIcons[appId] = iconImage; 381 | dispatch_async(dispatch_get_main_queue(), ^{ 382 | if([tableView.indexPathsForVisibleRows containsObject:indexPath]) 383 | { 384 | cell.imageView.image = iconImage; 385 | [cell setNeedsLayout]; 386 | } 387 | }); 388 | }); 389 | } 390 | } 391 | else 392 | { 393 | cell.imageView.image = _placeholderIcon; 394 | } 395 | 396 | cell.preservesSuperviewLayoutMargins = NO; 397 | cell.separatorInset = UIEdgeInsetsZero; 398 | cell.layoutMargins = UIEdgeInsetsZero; 399 | 400 | return cell; 401 | } 402 | 403 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 404 | return 80.0f; 405 | } 406 | 407 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 408 | { 409 | if(editingStyle == UITableViewCellEditingStyleDelete) 410 | { 411 | [self uninstallPressedForRowAtIndexPath:indexPath]; 412 | } 413 | } 414 | 415 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 416 | { 417 | TSAppInfo* appInfo = _cachedAppInfos[indexPath.row]; 418 | 419 | NSString* appId = [appInfo bundleIdentifier]; 420 | NSString* appName = [appInfo displayName]; 421 | 422 | UIAlertController* appSelectAlert = [UIAlertController alertControllerWithTitle:appName?:@"" message:appId?:@"" preferredStyle:UIAlertControllerStyleActionSheet]; 423 | 424 | UIAlertAction* openAction = [UIAlertAction actionWithTitle:@"Open" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) 425 | { 426 | [self openAppPressedForRowAtIndexPath:indexPath]; 427 | [self deselectRow]; 428 | }]; 429 | [appSelectAlert addAction:openAction]; 430 | 431 | UIAlertAction* showDetailsAction = [UIAlertAction actionWithTitle:@"Show Details" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) 432 | { 433 | [self showDetailsPressedForRowAtIndexPath:indexPath]; 434 | [self deselectRow]; 435 | }]; 436 | [appSelectAlert addAction:showDetailsAction]; 437 | 438 | NSString* switchState; 439 | NSString* registrationState = [appInfo registrationState]; 440 | UIAlertActionStyle switchActionStyle = 0; 441 | if([registrationState isEqualToString:@"System"]) 442 | { 443 | switchState = @"User"; 444 | switchActionStyle = UIAlertActionStyleDestructive; 445 | } 446 | else if([registrationState isEqualToString:@"User"]) 447 | { 448 | switchState = @"System"; 449 | switchActionStyle = UIAlertActionStyleDefault; 450 | } 451 | 452 | UIAlertAction* switchRegistrationAction = [UIAlertAction actionWithTitle:[NSString stringWithFormat:@"Switch to \"%@\" Registration", switchState] style:switchActionStyle handler:^(UIAlertAction* action) 453 | { 454 | [self changeAppRegistrationForRowAtIndexPath:indexPath toState:switchState]; 455 | [self deselectRow]; 456 | }]; 457 | [appSelectAlert addAction:switchRegistrationAction]; 458 | 459 | UIAlertAction* uninstallAction = [UIAlertAction actionWithTitle:@"Uninstall App" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) 460 | { 461 | [self uninstallPressedForRowAtIndexPath:indexPath]; 462 | [self deselectRow]; 463 | }]; 464 | [appSelectAlert addAction:uninstallAction]; 465 | 466 | UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action) 467 | { 468 | [self deselectRow]; 469 | }]; 470 | [appSelectAlert addAction:cancelAction]; 471 | 472 | appSelectAlert.popoverPresentationController.sourceView = tableView; 473 | appSelectAlert.popoverPresentationController.sourceRect = [tableView rectForRowAtIndexPath:indexPath]; 474 | 475 | [TSPresentationDelegate presentViewController:appSelectAlert animated:YES completion:nil]; 476 | } 477 | 478 | - (void)applicationsDidInstall:(id)arg1 479 | { 480 | [self reloadTable]; 481 | } 482 | 483 | - (void)applicationsDidUninstall:(id)arg1 484 | { 485 | [self reloadTable]; 486 | } 487 | 488 | @end --------------------------------------------------------------------------------