├── .gitignore ├── Makefile ├── README.md ├── Tweak.xm ├── appstoretroller.plist ├── appstoretrollerKiller ├── Makefile ├── TSUtil.h ├── TSUtil.m ├── entitlements.plist └── main.m ├── appstoretrollerPrefs ├── ATPRootListController.h ├── ATPRootListController.m ├── Makefile ├── Resources │ ├── Info.plist │ ├── Root.plist │ ├── icon.png │ ├── icon@2x.png │ └── icon@3x.png └── layout │ └── Library │ └── PreferenceLoader │ └── Preferences │ └── appstoretrollerPrefs.plist └── layout └── DEBIAN ├── control ├── postinst └── postrm /.gitignore: -------------------------------------------------------------------------------- 1 | .theos/ 2 | packages/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET := iphone:clang:16.5:14.5 2 | INSTALL_TARGET_PROCESSES = SpringBoard 3 | ARCHS = arm64 arm64e 4 | # THEOS_PACKAGE_SCHEME = rootless 5 | 6 | THEOS_DEVICE_IP = 127.0.0.1 7 | THEOS_DEVICE_PORT = 2222 8 | 9 | include $(THEOS)/makefiles/common.mk 10 | 11 | TWEAK_NAME = appstoretroller 12 | 13 | appstoretroller_FILES = Tweak.xm 14 | appstoretroller_CFLAGS = -fobjc-arc -Wno-deprecated-declarations 15 | 16 | include $(THEOS_MAKE_PATH)/tweak.mk 17 | SUBPROJECTS += appstoretrollerPrefs 18 | SUBPROJECTS += appstoretrollerKiller 19 | include $(THEOS_MAKE_PATH)/aggregate.mk 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AppStore Troller 2 | 3 | Very simple tweak to let you purchase an app to your Apple ID that requires a newer iOS version than the one you're running on your device. Afterwards, you can install the last compatible version onto your device, if it exists. 4 | -------------------------------------------------------------------------------- /Tweak.xm: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | static NSString *iosVersion = nil; 6 | static BOOL updatesEnabled = NO; 7 | 8 | %group appstoredHooks 9 | 10 | %hook NSMutableURLRequest 11 | 12 | - (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field 13 | { 14 | if (iosVersion != nil) { 15 | if (updatesEnabled == YES) { 16 | if ([field isEqualToString:@"User-Agent"]) { 17 | // NSLog(@"the spoofed ios is: iOS/%@ ", iosVersion); 18 | value = [value stringByReplacingOccurrencesOfString:@"iOS/.*? " withString:[NSString stringWithFormat:@"iOS/%@ ", iosVersion] options:NSRegularExpressionSearch range:NSMakeRange(0, [value length])]; 19 | } 20 | } else { 21 | if ([[self.URL absoluteString] containsString:@"WebObjects/MZBuy.woa/wa/buyProduct"]) { 22 | if ([field isEqualToString:@"User-Agent"]) { 23 | // NSLog(@"the spoofed ios is: iOS/%@ ", iosVersion); 24 | value = [value stringByReplacingOccurrencesOfString:@"iOS/.*? " withString:[NSString stringWithFormat:@"iOS/%@ ", iosVersion] options:NSRegularExpressionSearch range:NSMakeRange(0, [value length])]; 25 | } 26 | } 27 | } 28 | } 29 | %orig(value, field); 30 | } 31 | 32 | %end 33 | 34 | %end 35 | 36 | %group installdHooks 37 | 38 | %hook MIBundle 39 | 40 | -(BOOL)_isMinimumOSVersion:(id)arg1 applicableToOSVersion:(id)arg2 requiredOS:(unsigned long long)arg3 error:(id*)arg4 41 | { 42 | // NSLog(@"arg1: %@ arg2: %@ arg3: %llu", arg1, arg2, arg3); 43 | if (iosVersion != nil) { 44 | return %orig(arg1, iosVersion, arg3, arg4); 45 | } else { 46 | return %orig(arg1, arg2, arg3, arg4); 47 | } 48 | } 49 | 50 | %end 51 | 52 | %end 53 | 54 | %ctor { 55 | NSProcessInfo *processInfo = [NSProcessInfo processInfo]; 56 | 57 | NSString *currentProcessName = [processInfo processName]; 58 | 59 | [[NSFileManager defaultManager] setAttributes:@{NSFilePosixPermissions: @(0644)} ofItemAtPath:ROOT_PATH_NS(@"/var/mobile/Library/Preferences/dev.mineek.appstoretroller.plist") error:nil]; 60 | NSDictionary *prefs = [NSDictionary dictionaryWithContentsOfFile:ROOT_PATH_NS(@"/var/mobile/Library/Preferences/dev.mineek.appstoretroller.plist")]; 61 | updatesEnabled = [[prefs objectForKey:@"updatesEnabled"] boolValue]; 62 | if (![[prefs objectForKey:@"enabled"] boolValue]) { 63 | // NSLog(@"[appstoretroller] Not enabled."); 64 | return; 65 | } 66 | iosVersion = [prefs objectForKey:@"iOSVersion"]; 67 | 68 | if ([currentProcessName isEqualToString:@"appstored"]) { 69 | %init(appstoredHooks); 70 | } else if ([currentProcessName isEqualToString:@"installd"]) { 71 | %init(installdHooks); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /appstoretroller.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Filter 6 | 7 | Executables 8 | 9 | appstored 10 | installd 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /appstoretrollerKiller/Makefile: -------------------------------------------------------------------------------- 1 | ARCHS = arm64 arm64e 2 | TARGET := iphone:clang:16.5:14.5 3 | 4 | include $(THEOS)/makefiles/common.mk 5 | 6 | TOOL_NAME = appstoretrollerKiller 7 | 8 | appstoretrollerKiller_FILES = main.m TSUtil.m 9 | appstoretrollerKiller_CFLAGS = -fobjc-arc 10 | appstoretrollerKiller_CODESIGN_FLAGS = -Sentitlements.plist 11 | appstoretrollerKiller_INSTALL_PATH = /usr/local/bin 12 | 13 | include $(THEOS_MAKE_PATH)/tool.mk 14 | -------------------------------------------------------------------------------- /appstoretrollerKiller/TSUtil.h: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | 3 | extern int spawnRoot(NSString* path, NSArray* args, NSString** stdOut, NSString** stdErr); 4 | extern void killall(NSString* processName, BOOL softly); 5 | -------------------------------------------------------------------------------- /appstoretrollerKiller/TSUtil.m: -------------------------------------------------------------------------------- 1 | #import "TSUtil.h" 2 | 3 | #import 4 | #import 5 | #import 6 | #import 7 | #import 8 | 9 | #define SIGABRT 6 10 | #define OS_REASON_SIGNAL 2 11 | #define OS_REASON_DYLD 6 12 | #define DYLD_EXIT_REASON_OTHER 9 13 | 14 | void abort_with_payload(uint32_t reason_namespace, uint64_t reason_code, 15 | void *payload, uint32_t payload_size, 16 | const char *reason_string, uint64_t reason_flags) 17 | __attribute__((noreturn, cold)); 18 | 19 | #define ASSERT(e) (__builtin_expect(!(e), 0) ?\ 20 | ((void)fprintf(stderr, "%s:%d: failed ASSERTion `%s'\n", __FILE_NAME__, __LINE__, #e),\ 21 | abort_with_payload(OS_REASON_DYLD,DYLD_EXIT_REASON_OTHER,NULL,0, #e, 0)) : (void)0) 22 | 23 | 24 | extern char **environ; 25 | 26 | #define POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE 1 27 | 28 | enum { 29 | PERSONA_INVALID = 0, 30 | PERSONA_GUEST = 1, 31 | PERSONA_MANAGED = 2, 32 | PERSONA_PRIV = 3, 33 | PERSONA_SYSTEM = 4, 34 | PERSONA_DEFAULT = 5, 35 | PERSONA_SYSTEM_PROXY = 6, 36 | PERSONA_SYS_EXT = 7, 37 | PERSONA_ENTERPRISE = 8, 38 | 39 | PERSONA_TYPE_MAX = PERSONA_ENTERPRISE, 40 | }; 41 | 42 | #define PERSONA_INFO_V1 1 43 | #define PERSONA_INFO_V2 2 44 | 45 | struct kpersona_info { 46 | /* v1 fields */ 47 | uint32_t persona_info_version; 48 | 49 | uid_t persona_id; 50 | int persona_type; 51 | gid_t persona_gid; /* unused */ 52 | uint32_t persona_ngroups; /* unused */ 53 | gid_t persona_groups[NGROUPS]; /* unused */ 54 | uid_t persona_gmuid; /* unused */ 55 | char persona_name[MAXLOGNAME + 1]; 56 | 57 | /* v2 fields */ 58 | uid_t persona_uid; 59 | } __attribute__((packed)); 60 | 61 | extern int kpersona_find_by_type(int persona_type, uid_t *id, size_t *idlen); 62 | extern int kpersona_getpath(uid_t id, char path[MAXPATHLEN]); 63 | extern int kpersona_pidinfo(pid_t id, struct kpersona_info *info); 64 | extern int kpersona_info(uid_t id, struct kpersona_info *info); 65 | extern int kpersona_find(const char *name, uid_t uid, uid_t *id, size_t *idlen); 66 | extern int kpersona_alloc(struct kpersona_info *info, uid_t *id); 67 | 68 | 69 | int available_persona_id() 70 | { 71 | struct kpersona_info info={PERSONA_INFO_V1}; 72 | ASSERT(kpersona_pidinfo(getpid(), &info) == 0); 73 | 74 | int current_persona_id = info.persona_id; 75 | 76 | for(int t=1; t<=PERSONA_TYPE_MAX; t++) 77 | { 78 | uid_t personas[128]={0}; 79 | size_t npersonas = 128; 80 | 81 | if(kpersona_find_by_type(t, personas, &npersonas) <= 0) 82 | continue; 83 | 84 | for(int i=0; i 2 | 3 | 4 | 5 | platform-application 6 | 7 | com.apple.private.security.container-required 8 | 9 | com.apple.private.persona-mgmt 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /appstoretrollerKiller/main.m: -------------------------------------------------------------------------------- 1 | #include 2 | #import 3 | #import 4 | #import "TSUtil.h" 5 | 6 | int main(int argc, char *argv[], char *envp[]) { 7 | @autoreleasepool { 8 | if (getuid() == 501) { 9 | if (argc > 1 && strcmp(argv[1], "--child") == 0) { 10 | exit(1); 11 | } 12 | 13 | spawnRoot(ROOT_PATH_NS(@"/usr/local/bin/appstoretrollerKiller"), @[ @"", @"--child" ], nil, nil); 14 | exit(0); 15 | } 16 | killall(@"appstored", NO); 17 | killall(@"installd", YES); 18 | killall(@"AppStore", YES); 19 | exit(0); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /appstoretrollerPrefs/ATPRootListController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface ATPRootListController : PSListController 4 | 5 | @end 6 | 7 | @interface NSTask : NSObject 8 | @property (copy) NSArray *arguments; 9 | @property (copy) NSString *launchPath; 10 | - (id)init; 11 | - (void)waitUntilExit; 12 | - (void)launch; 13 | @end 14 | -------------------------------------------------------------------------------- /appstoretrollerPrefs/ATPRootListController.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "ATPRootListController.h" 3 | 4 | @implementation ATPRootListController 5 | 6 | - (NSArray *)specifiers { 7 | if (!_specifiers) { 8 | _specifiers = [self loadSpecifiersFromPlistName:@"Root" target:self]; 9 | } 10 | 11 | return _specifiers; 12 | } 13 | 14 | - (void)viewDidLoad { 15 | [super viewDidLoad]; 16 | 17 | UIBarButtonItem *respringButton = [[UIBarButtonItem alloc] initWithTitle:@"Apply" style:UIBarButtonItemStylePlain target:self action:@selector(respring)]; 18 | self.navigationItem.rightBarButtonItem = respringButton; 19 | } 20 | 21 | - (void)respring { 22 | for (UIWindow *window in [UIApplication sharedApplication].windows) { 23 | if (window.isKeyWindow) { 24 | [window endEditing:YES]; 25 | break; 26 | } 27 | } 28 | 29 | if (access(THEOS_PACKAGE_INSTALL_PREFIX "/var/mobile/Library/Preferences/dev.mineek.appstoretroller.plist", F_OK)) { 30 | NSString *prefsPath = @THEOS_PACKAGE_INSTALL_PREFIX "/var/mobile/Library/Preferences/dev.mineek.appstoretroller.plist"; 31 | NSDictionary *placeholder = @{}; 32 | 33 | NSData *data = [NSPropertyListSerialization dataWithPropertyList:placeholder 34 | format:NSPropertyListBinaryFormat_v1_0 35 | options:0 36 | error:nil]; 37 | [data writeToFile:prefsPath atomically:YES]; 38 | } 39 | 40 | NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"dev.mineek.appstoretroller"]; 41 | 42 | NSString *prefsPath = @THEOS_PACKAGE_INSTALL_PREFIX "/var/mobile/Library/Preferences/dev.mineek.appstoretroller.plist"; 43 | BOOL enabled = [defaults boolForKey:@"enabled"]; 44 | BOOL updatesEnabled = [defaults boolForKey:@"updatesEnabled"]; 45 | NSString *iOSVersion = [defaults stringForKey:@"iOSVersion"]; 46 | 47 | if (!iOSVersion) { 48 | [defaults setBool:NO forKey:@"enabled"]; 49 | enabled = NO; 50 | } 51 | 52 | if (updatesEnabled == NO) { 53 | [defaults setBool:NO forKey:@"updatesEnabled"]; 54 | } 55 | 56 | NSMutableDictionary *prefs = [NSMutableDictionary dictionaryWithDictionary:@{ 57 | @"enabled": @(enabled), 58 | @"updatesEnabled": @(updatesEnabled) 59 | }]; 60 | 61 | if (iOSVersion) { 62 | prefs[@"iOSVersion"] = iOSVersion; 63 | } 64 | 65 | NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:prefs 66 | format:NSPropertyListBinaryFormat_v1_0 67 | options:0 68 | error:nil]; 69 | 70 | [plistData writeToFile:prefsPath atomically:YES]; 71 | 72 | NSTask *t = [[NSTask alloc] init]; 73 | [t setLaunchPath:@THEOS_PACKAGE_INSTALL_PREFIX "/usr/local/bin/appstoretrollerKiller"]; 74 | [t launch]; 75 | } 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /appstoretrollerPrefs/Makefile: -------------------------------------------------------------------------------- 1 | TARGET := iphone:clang:16.5:14.5 2 | ARCHS = arm64 arm64e 3 | # THEOS_PACKAGE_SCHEME = rootless 4 | 5 | include $(THEOS)/makefiles/common.mk 6 | 7 | BUNDLE_NAME = appstoretrollerPrefs 8 | 9 | appstoretrollerPrefs_FILES = ATPRootListController.m 10 | appstoretrollerPrefs_FRAMEWORKS = UIKit 11 | appstoretrollerPrefs_PRIVATE_FRAMEWORKS = Preferences 12 | appstoretrollerPrefs_INSTALL_PATH = /Library/PreferenceBundles 13 | appstoretrollerPrefs_CFLAGS = -fobjc-arc -w 14 | 15 | include $(THEOS_MAKE_PATH)/bundle.mk 16 | -------------------------------------------------------------------------------- /appstoretrollerPrefs/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | appstoretrollerPrefs 9 | CFBundleIdentifier 10 | dev.mineek.appstoretrollerprefs 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1.0 21 | NSPrincipalClass 22 | ATPRootListController 23 | 24 | 25 | -------------------------------------------------------------------------------- /appstoretrollerPrefs/Resources/Root.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | cell 9 | PSGroupCell 10 | footerText 11 | Enable the tweak, spoof your version to a REASONABLE version (or the app will likely crash after download), apply, and profit. DISCLAIMER: ENABLING THE UPDATE OPTION MIGHT MAKE YOUR DEVICE SHOW AN ABSURD AMOUNT OF UPDATES DEPENDING ON THE VERSION YOU SPOOFED TO. 12 | headerText 13 | AppStore Troller 14 | 15 | 16 | cell 17 | PSSwitchCell 18 | defaults 19 | dev.mineek.appstoretroller 20 | key 21 | enabled 22 | label 23 | Enabled 24 | default 25 | 26 | 27 | 28 | cell 29 | PSSwitchCell 30 | defaults 31 | dev.mineek.appstoretroller 32 | key 33 | updatesEnabled 34 | label 35 | Enabled for Updates (DISCLAIMER) 36 | default 37 | 38 | 39 | 40 | cell 41 | PSEditTextCell 42 | key 43 | iOSVersion 44 | label 45 | iOS Version to Spoof 46 | defaults 47 | dev.mineek.appstoretroller 48 | 49 | 50 | title 51 | AppStore Troller 52 | 53 | 54 | -------------------------------------------------------------------------------- /appstoretrollerPrefs/Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verygenericname/appstoretroller/84e3acfc2a801e536db8a914945489d6431fed4a/appstoretrollerPrefs/Resources/icon.png -------------------------------------------------------------------------------- /appstoretrollerPrefs/Resources/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verygenericname/appstoretroller/84e3acfc2a801e536db8a914945489d6431fed4a/appstoretrollerPrefs/Resources/icon@2x.png -------------------------------------------------------------------------------- /appstoretrollerPrefs/Resources/icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verygenericname/appstoretroller/84e3acfc2a801e536db8a914945489d6431fed4a/appstoretrollerPrefs/Resources/icon@3x.png -------------------------------------------------------------------------------- /appstoretrollerPrefs/layout/Library/PreferenceLoader/Preferences/appstoretrollerPrefs.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | entry 6 | 7 | bundle 8 | appstoretrollerPrefs 9 | cell 10 | PSLinkCell 11 | detail 12 | ATPRootListController 13 | icon 14 | icon.png 15 | isController 16 | 17 | label 18 | AppStore Troller 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /layout/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: dev.mineek.appstoretroller 2 | Name: appstoretroller 3 | Version: 0.1.1 4 | Architecture: iphoneos-arm 5 | Description: Modified version of mineeks tweak that lets you specify the minimum version and bypass installd version check 6 | Maintainer: Mineek 7 | Author: Mineek 8 | Section: Tweaks 9 | Depends: mobilesubstrate (>= 0.9.5000) 10 | -------------------------------------------------------------------------------- /layout/DEBIAN/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | killall -9 appstored | true 4 | killall AppStore installd | true 5 | -------------------------------------------------------------------------------- /layout/DEBIAN/postrm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | killall -9 appstored | true 4 | killall AppStore installd | true 5 | --------------------------------------------------------------------------------