Readd ldid for use on iOS 14

This commit is contained in:
opa334 2023-11-28 01:04:40 +01:00
parent b79c9c1ad5
commit c6ea42cf5a
14 changed files with 418 additions and 14 deletions

View File

@ -1,6 +1,7 @@
#include <Foundation/Foundation.h>
#include <Security/Security.h>
#include <TargetConditionals.h>
#include <dlfcn.h>
#ifdef __cplusplus
extern "C" {
@ -64,12 +65,27 @@ typedef struct CF_BRIDGED_TYPE(id) __SecCodeSigner* SecCodeSignerRef SPI_AVAILAB
typedef struct __SecCodeSigner* SecCodeSignerRef SPI_AVAILABLE(macos(10.5), ios(15.0), macCatalyst(13.0));
#endif
extern const CFStringRef kSecCodeSignerEntitlements SPI_AVAILABLE(macos(10.5), ios(15.0), macCatalyst(13.0));
extern const CFStringRef kSecCodeSignerIdentifier SPI_AVAILABLE(macos(10.5), ios(15.0), macCatalyst(13.0));
extern const CFStringRef kSecCodeSignerIdentity SPI_AVAILABLE(macos(10.5), ios(15.0), macCatalyst(13.0));
extern const CFStringRef kSecCodeSignerPreserveMetadata SPI_AVAILABLE(macos(10.5), ios(15.0), macCatalyst(13.0));
extern const CFStringRef kSecCodeSignerRequirements SPI_AVAILABLE(macos(10.5), ios(15.0), macCatalyst(13.0));
extern const CFStringRef kSecCodeSignerResourceRules SPI_AVAILABLE(macos(10.5), ios(15.0), macCatalyst(13.0));
const CFStringRef kSecCodeSignerApplicationData = CFSTR("application-specific");
const CFStringRef kSecCodeSignerDetached = CFSTR("detached");
const CFStringRef kSecCodeSignerDigestAlgorithm = CFSTR("digest-algorithm");
const CFStringRef kSecCodeSignerDryRun = CFSTR("dryrun");
const CFStringRef kSecCodeSignerEntitlements = CFSTR("entitlements");
const CFStringRef kSecCodeSignerFlags = CFSTR("flags");
const CFStringRef kSecCodeSignerIdentifier = CFSTR("identifier");
const CFStringRef kSecCodeSignerIdentifierPrefix = CFSTR("identifier-prefix");
const CFStringRef kSecCodeSignerIdentity = CFSTR("signer");
const CFStringRef kSecCodeSignerPageSize = CFSTR("pagesize");
const CFStringRef kSecCodeSignerRequirements = CFSTR("requirements");
const CFStringRef kSecCodeSignerResourceRules = CFSTR("resource-rules");
const CFStringRef kSecCodeSignerSDKRoot = CFSTR("sdkroot");
const CFStringRef kSecCodeSignerSigningTime = CFSTR("signing-time");
const CFStringRef kSecCodeSignerRequireTimestamp = CFSTR("timestamp-required");
const CFStringRef kSecCodeSignerTimestampServer = CFSTR("timestamp-url");
const CFStringRef kSecCodeSignerTimestampAuthentication = CFSTR("timestamp-authentication");
const CFStringRef kSecCodeSignerTimestampOmitCertificates = CFSTR("timestamp-omit-certificates");
const CFStringRef kSecCodeSignerPreserveMetadata = CFSTR("preserve-metadata");
const CFStringRef kSecCodeSignerTeamIdentifier = CFSTR("teamidentifier");
const CFStringRef kSecCodeSignerPlatformIdentifier = CFSTR("platform-identifier");
#ifdef BRIDGED_SECCODESIGNER
OSStatus SecCodeSignerCreate(CFDictionaryRef parameters, SecCSFlags flags, SecCodeSignerRef* __nonnull CF_RETURNS_RETAINED signer)
@ -91,6 +107,13 @@ extern const CFStringRef kSecCodeInfoResourceDirectory; /* Internal */
int codesign_sign_adhoc(const char *path, bool preserveMetadata, NSDictionary *customEntitlements)
{
// We need to do this shit because iOS 14 does not have the symbol
OSStatus (*__SecCodeSignerCreate)(CFDictionaryRef parameters, SecCSFlags flags, SecCodeSignerRef *signerRef) = dlsym(RTLD_DEFAULT, "SecCodeSignerCreate");
OSStatus (*__SecCodeSignerAddSignatureWithErrors)(SecCodeSignerRef signerRef, SecStaticCodeRef codeRef, SecCSFlags flags, CFErrorRef *errors) = dlsym(RTLD_DEFAULT, "SecCodeSignerAddSignatureWithErrors");
// if this is not found, all bets are off
if (!__SecCodeSignerCreate) return 404;
if (!__SecCodeSignerAddSignatureWithErrors) return 404;
NSString *filePath = [NSString stringWithUTF8String:path];
OSStatus status = 0;
int retval = 200;
@ -127,13 +150,13 @@ int codesign_sign_adhoc(const char *path, bool preserveMetadata, NSDictionary *c
}
SecCodeSignerRef signerRef;
status = SecCodeSignerCreate((__bridge CFDictionaryRef)parameters, kSecCSDefaultFlags, &signerRef);
status = __SecCodeSignerCreate((__bridge CFDictionaryRef)parameters, kSecCSDefaultFlags, &signerRef);
if (status == 0) {
SecStaticCodeRef code;
status = SecStaticCodeCreateWithPathAndAttributes((__bridge CFURLRef)[NSURL fileURLWithPath:filePath], kSecCSDefaultFlags, (__bridge CFDictionaryRef)@{}, &code);
if (status == 0) {
CFErrorRef errors;
status = SecCodeSignerAddSignatureWithErrors(signerRef, code, kSecCSDefaultFlags, &errors);
status = __SecCodeSignerAddSignatureWithErrors(signerRef, code, kSecCSDefaultFlags, &errors);
if (status == 0) {
CFDictionaryRef newSigningInformation;
// Difference from codesign: added kSecCSSigningInformation, kSecCSRequirementInformation, kSecCSInternalInformation

View File

@ -240,6 +240,105 @@ void setTSURLSchemeState(BOOL newState, NSString* customAppPath)
}
}
void installLdid(NSString* ldidToCopyPath, NSString* ldidVersion)
{
if(![[NSFileManager defaultManager] fileExistsAtPath:ldidToCopyPath]) return;
NSString* ldidPath = [trollStoreAppPath() stringByAppendingPathComponent:@"ldid"];
NSString* ldidVersionPath = [trollStoreAppPath() stringByAppendingPathComponent:@"ldid.version"];
if([[NSFileManager defaultManager] fileExistsAtPath:ldidPath])
{
[[NSFileManager defaultManager] removeItemAtPath:ldidPath error:nil];
}
[[NSFileManager defaultManager] copyItemAtPath:ldidToCopyPath toPath:ldidPath error:nil];
NSData* ldidVersionData = [ldidVersion dataUsingEncoding:NSUTF8StringEncoding];
[ldidVersionData writeToFile:ldidVersionPath atomically:YES];
chmod(ldidPath.fileSystemRepresentation, 0755);
chmod(ldidVersionPath.fileSystemRepresentation, 0644);
}
BOOL isLdidInstalled(void)
{
NSString* ldidPath = [trollStoreAppPath() stringByAppendingPathComponent:@"ldid"];
return [[NSFileManager defaultManager] fileExistsAtPath:ldidPath];
}
int runLdid(NSArray* args, NSString** output, NSString** errorOutput)
{
NSString* ldidPath = [trollStoreAppPath() stringByAppendingPathComponent:@"ldid"];
NSMutableArray* argsM = args.mutableCopy ?: [NSMutableArray new];
[argsM insertObject:ldidPath.lastPathComponent atIndex:0];
NSUInteger argCount = [argsM count];
char **argsC = (char **)malloc((argCount + 1) * sizeof(char*));
for (NSUInteger i = 0; i < argCount; i++)
{
argsC[i] = strdup([[argsM objectAtIndex:i] UTF8String]);
}
argsC[argCount] = NULL;
posix_spawn_file_actions_t action;
posix_spawn_file_actions_init(&action);
int outErr[2];
pipe(outErr);
posix_spawn_file_actions_adddup2(&action, outErr[1], STDERR_FILENO);
posix_spawn_file_actions_addclose(&action, outErr[0]);
int out[2];
pipe(out);
posix_spawn_file_actions_adddup2(&action, out[1], STDOUT_FILENO);
posix_spawn_file_actions_addclose(&action, out[0]);
pid_t task_pid;
int status = -200;
int spawnError = posix_spawn(&task_pid, [ldidPath fileSystemRepresentation], &action, NULL, (char* const*)argsC, NULL);
for (NSUInteger i = 0; i < argCount; i++)
{
free(argsC[i]);
}
free(argsC);
if(spawnError != 0)
{
NSLog(@"posix_spawn error %d\n", spawnError);
return spawnError;
}
do
{
if (waitpid(task_pid, &status, 0) != -1) {
//printf("Child status %dn", WEXITSTATUS(status));
} else
{
perror("waitpid");
return -222;
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
close(outErr[1]);
close(out[1]);
NSString* ldidOutput = getNSStringFromFile(out[0]);
if(output)
{
*output = ldidOutput;
}
NSString* ldidErrorOutput = getNSStringFromFile(outErr[0]);
if(errorOutput)
{
*errorOutput = ldidErrorOutput;
}
return WEXITSTATUS(status);
}
BOOL certificateHasDataForExtensionOID(SecCertificateRef certificate, CFStringRef oidString)
{
if(certificate == NULL || oidString == NULL)
@ -377,6 +476,52 @@ int signApp(NSString* appPath)
return -1;
}
#else
int signAdhoc(NSString *filePath, NSDictionary *entitlements)
{
if (@available(iOS 15, *)) {
return codesign_sign_adhoc(filePath.fileSystemRepresentation, true, entitlements);
}
// If iOS 14 is so great, how come there is no iOS 14 2?????
else {
if(!isLdidInstalled()) return 173;
NSString *entitlementsPath = nil;
NSString *signArg = @"-s";
NSString* errorOutput;
if(entitlements)
{
NSData *entitlementsXML = [NSPropertyListSerialization dataWithPropertyList:entitlements format:NSPropertyListXMLFormat_v1_0 options:0 error:nil];
if (entitlementsXML) {
entitlementsPath = [[NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString] stringByAppendingPathExtension:@"plist"];
[entitlementsXML writeToFile:entitlementsPath atomically:NO];
signArg = [@"-S" stringByAppendingString:entitlementsPath];
}
}
int ldidRet = runLdid(@[signArg, filePath], nil, &errorOutput);
if (entitlementsPath) {
[[NSFileManager defaultManager] removeItemAtPath:entitlementsPath error:nil];
}
NSLog(@"ldid exited with status %d", ldidRet);
NSLog(@"- ldid error output start -");
printMultilineNSString(errorOutput);
NSLog(@"- ldid error output end -");
if(ldidRet == 0)
{
return 0;
}
else
{
return 175;
}
}
}
int signApp(NSString* appPath)
{
NSDictionary* appInfoDict = infoDictionaryForAppPath(appPath);
@ -460,7 +605,7 @@ int signApp(NSString* appPath)
}
// First attempt ad hoc signing
int r = codesign_sign_adhoc(tmpPath.fileSystemRepresentation, true, entitlementsToUse);
int r = signAdhoc(tmpPath, entitlementsToUse);
if (r != 0) {
NSLog(@"[%@] Adhoc signing failed with error code %d, continuing anyways...\n", filePath, r);
}
@ -545,6 +690,7 @@ void applyPatchesToInfoDictionary(NSString* appPath)
// 170: failed to create container for app bundle
// 171: a non trollstore app with the same identifier is already installled
// 172: no info.plist found in app
// 173: app is not signed and cannot be signed because ldid not installed or didn't work
// 174:
int installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate, BOOL useInstalldMethod)
{
@ -892,6 +1038,41 @@ int installTrollStore(NSString* pathToTar)
NSString* tmpTrollStorePath = [tmpPayloadPath stringByAppendingPathComponent:@"TrollStore.app"];
if(![[NSFileManager defaultManager] fileExistsAtPath:tmpTrollStorePath]) return 1;
if (@available(iOS 15, *)) {} else {
// Transfer existing ldid installation if it exists
// But only if the to-be-installed version of TrollStore is 1.5.0 or above
// This is to make it possible to downgrade to older versions still
NSString* toInstallInfoPlistPath = [tmpTrollStorePath stringByAppendingPathComponent:@"Info.plist"];
if(![[NSFileManager defaultManager] fileExistsAtPath:toInstallInfoPlistPath]) return 1;
NSDictionary* toInstallInfoDict = [NSDictionary dictionaryWithContentsOfFile:toInstallInfoPlistPath];
NSString* toInstallVersion = toInstallInfoDict[@"CFBundleVersion"];
NSComparisonResult result = [@"1.5.0" compare:toInstallVersion options:NSNumericSearch];
if(result != NSOrderedDescending)
{
NSString* existingLdidPath = [trollStoreAppPath() stringByAppendingPathComponent:@"ldid"];
NSString* existingLdidVersionPath = [trollStoreAppPath() stringByAppendingPathComponent:@"ldid.version"];
if([[NSFileManager defaultManager] fileExistsAtPath:existingLdidPath])
{
NSString* tmpLdidPath = [tmpTrollStorePath stringByAppendingPathComponent:@"ldid"];
if(![[NSFileManager defaultManager] fileExistsAtPath:tmpLdidPath])
{
[[NSFileManager defaultManager] copyItemAtPath:existingLdidPath toPath:tmpLdidPath error:nil];
}
}
if([[NSFileManager defaultManager] fileExistsAtPath:existingLdidVersionPath])
{
NSString* tmpLdidVersionPath = [tmpTrollStorePath stringByAppendingPathComponent:@"ldid.version"];
if(![[NSFileManager defaultManager] fileExistsAtPath:tmpLdidVersionPath])
{
[[NSFileManager defaultManager] copyItemAtPath:existingLdidVersionPath toPath:tmpLdidVersionPath error:nil];
}
}
}
}
// Merge existing URL scheme settings value
if(!getTSURLSchemeState(nil))
{
@ -1171,6 +1352,15 @@ int MAIN_NAME(int argc, char *argv[], char *envp[])
}
uninstallTrollStore(YES);
}
else if([cmd isEqualToString:@"install-ldid"])
{
if (@available(iOS 15, *)) {} else {
if(args.count < 3) return -3;
NSString* ldidPath = args[1];
NSString* ldidVersion = args[2];
installLdid(ldidPath, ldidVersion);
}
}
else if([cmd isEqualToString:@"refresh"])
{
refreshAppRegistrations(YES);

View File

@ -12,6 +12,7 @@ extern int spawnRoot(NSString* path, NSArray* args, NSString** stdOut, NSString*
extern void killall(NSString* processName, BOOL softly);
extern void respring(void);
extern void fetchLatestTrollStoreVersion(void (^completionHandler)(NSString* latestVersion));
extern void fetchLatestLdidVersion(void (^completionHandler)(NSString* latestVersion));
extern NSArray* trollStoreInstalledAppBundlePaths();
extern NSArray* trollStoreInstalledAppContainerPaths();

View File

@ -310,6 +310,11 @@ void fetchLatestTrollStoreVersion(void (^completionHandler)(NSString* latestVers
github_fetchLatestVersion(@"opa334/TrollStore", completionHandler);
}
void fetchLatestLdidVersion(void (^completionHandler)(NSString* latestVersion))
{
github_fetchLatestVersion(@"opa334/ldid", completionHandler);
}
NSArray* trollStoreInstalledAppContainerPaths()
{
NSMutableArray* appContainerPaths = [NSMutableArray new];

View File

@ -191,6 +191,7 @@
<key>TSRootBinaries</key>
<array>
<string>trollstorehelper</string>
<string>ldid</string>
</array>
</dict>
</plist>

View File

@ -47,11 +47,20 @@ extern NSUserDefaults* trollStoreUserDefaults();
case 172:
errorDescription = @"The app does not contain an Info.plist file.";
break;
case 173:
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.";
break;
case 174:
errorDescription = @"The app's main executable does not exist.";
break;
case 175:
errorDescription = @"Failed to sign the app.";
case 175: {
if (@available(iOS 15, *)) {
errorDescription = @"Failed to sign the app.";
}
else {
errorDescription = @"Failed to sign the app. ldid returned a non zero status code.";
}
}
break;
case 176:
errorDescription = @"The app's Info.plist is missing required values.";

View File

@ -9,4 +9,6 @@
+ (void)handleAppInstallFromRemoteURL:(NSURL*)remoteURL completion:(void (^)(BOOL, NSError*))completion;
+ (void)installLdid;
@end

View File

@ -187,4 +187,48 @@ extern NSUserDefaults* trollStoreUserDefaults(void);
});
}
+ (void)installLdid
{
fetchLatestLdidVersion(^(NSString* latestVersion)
{
if(!latestVersion) return;
dispatch_async(dispatch_get_main_queue(), ^
{
NSURL* ldidURL = [NSURL URLWithString:@"https://github.com/opa334/ldid/releases/latest/download/ldid"];
NSURLRequest* ldidRequest = [NSURLRequest requestWithURL:ldidURL];
[TSPresentationDelegate startActivity:@"Installing ldid"];
NSURLSessionDownloadTask* downloadTask = [NSURLSession.sharedSession downloadTaskWithRequest:ldidRequest completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error)
{
if(error)
{
UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@"Error" message:[NSString stringWithFormat:@"Error downloading ldid: %@", error] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil];
[errorAlert addAction:closeAction];
dispatch_async(dispatch_get_main_queue(), ^
{
[TSPresentationDelegate stopActivityWithCompletion:^
{
[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];
}];
});
}
else if(location)
{
spawnRoot(rootHelperPath(), @[@"install-ldid", location.path, latestVersion], nil, nil);
dispatch_async(dispatch_get_main_queue(), ^
{
[TSPresentationDelegate stopActivityWithCompletion:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:@"TrollStoreReloadSettingsNotification" object:nil userInfo:nil];
});
}
}];
[downloadTask resume];
});
});
}
@end

View File

@ -72,6 +72,23 @@
}
}
// We want to auto install ldid if either it doesn't exist
// or if it's the one from an old TrollStore version that's no longer supported
- (void)handleLdidCheck
{
if (@available(iOS 15, *)) {} else {
NSString* tsAppPath = [NSBundle mainBundle].bundlePath;
NSString* ldidPath = [tsAppPath stringByAppendingPathComponent:@"ldid"];
NSString* ldidVersionPath = [tsAppPath stringByAppendingPathComponent:@"ldid.version"];
if(![[NSFileManager defaultManager] fileExistsAtPath:ldidPath] || ![[NSFileManager defaultManager] fileExistsAtPath:ldidVersionPath])
{
[TSInstallationController installLdid];
}
}
}
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
@ -87,6 +104,10 @@
{
[self handleURLContexts:connectionOptions.URLContexts scene:(UIWindowScene*)scene];
}
else
{
[self handleLdidCheck];
}
}

View File

@ -4,5 +4,6 @@
{
PSSpecifier* _installPersistenceHelperSpecifier;
NSString* _newerVersion;
NSString* _newerLdidVersion;
}
@end

View File

@ -33,6 +33,28 @@ extern NSUserDefaults* trollStoreUserDefaults(void);
});
}
});
if (@available(iOS 15, *)) {} else {
fetchLatestLdidVersion(^(NSString* latestVersion)
{
NSString* ldidVersionPath = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"ldid.version"];
NSString* ldidVersion = nil;
NSData* ldidVersionData = [NSData dataWithContentsOfFile:ldidVersionPath];
if(ldidVersionData)
{
ldidVersion = [[NSString alloc] initWithData:ldidVersionData encoding:NSUTF8StringEncoding];
}
if(![latestVersion isEqualToString:ldidVersion])
{
_newerLdidVersion = latestVersion;
dispatch_async(dispatch_get_main_queue(), ^
{
[self reloadSpecifiers];
});
}
});
}
}
- (NSMutableArray*)specifiers
@ -91,6 +113,84 @@ extern NSUserDefaults* trollStoreUserDefaults(void);
[_specifiers addObject:rebuildIconCacheSpecifier];
if (@available(iOS 15, *)) { }
else {
NSString* ldidPath = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"ldid"];
NSString* ldidVersionPath = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"ldid.version"];
BOOL ldidInstalled = [[NSFileManager defaultManager] fileExistsAtPath:ldidPath];
NSString* ldidVersion = nil;
NSData* ldidVersionData = [NSData dataWithContentsOfFile:ldidVersionPath];
if(ldidVersionData)
{
ldidVersion = [[NSString alloc] initWithData:ldidVersionData encoding:NSUTF8StringEncoding];
}
PSSpecifier* signingGroupSpecifier = [PSSpecifier emptyGroupSpecifier];
signingGroupSpecifier.name = @"Signing";
if(ldidInstalled)
{
[signingGroupSpecifier setProperty:@"ldid is installed and allows TrollStore to install unsigned IPA files." forKey:@"footerText"];
}
else
{
[signingGroupSpecifier setProperty:@"In order for TrollStore to be able to install unsigned IPAs, ldid has to be installed using this button. It can't be directly included in TrollStore because of licensing issues." forKey:@"footerText"];
}
[_specifiers addObject:signingGroupSpecifier];
if(ldidInstalled)
{
NSString* installedTitle = @"ldid: Installed";
if(ldidVersion)
{
installedTitle = [NSString stringWithFormat:@"%@ (%@)", installedTitle, ldidVersion];
}
PSSpecifier* ldidInstalledSpecifier = [PSSpecifier preferenceSpecifierNamed:installedTitle
target:self
set:nil
get:nil
detail:nil
cell:PSStaticTextCell
edit:nil];
[ldidInstalledSpecifier setProperty:@NO forKey:@"enabled"];
ldidInstalledSpecifier.identifier = @"ldidInstalled";
[_specifiers addObject:ldidInstalledSpecifier];
if(_newerLdidVersion && ![_newerLdidVersion isEqualToString:ldidVersion])
{
NSString* updateTitle = [NSString stringWithFormat:@"Update to %@", _newerLdidVersion];
PSSpecifier* ldidUpdateSpecifier = [PSSpecifier preferenceSpecifierNamed:updateTitle
target:self
set:nil
get:nil
detail:nil
cell:PSButtonCell
edit:nil];
ldidUpdateSpecifier.identifier = @"updateLdid";
[ldidUpdateSpecifier setProperty:@YES forKey:@"enabled"];
ldidUpdateSpecifier.buttonAction = @selector(installOrUpdateLdidPressed);
[_specifiers addObject:ldidUpdateSpecifier];
}
}
else
{
PSSpecifier* installLdidSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Install ldid"
target:self
set:nil
get:nil
detail:nil
cell:PSButtonCell
edit:nil];
installLdidSpecifier.identifier = @"installLdid";
[installLdidSpecifier setProperty:@YES forKey:@"enabled"];
installLdidSpecifier.buttonAction = @selector(installOrUpdateLdidPressed);
[_specifiers addObject:installLdidSpecifier];
}
}
PSSpecifier* persistenceGroupSpecifier = [PSSpecifier emptyGroupSpecifier];
persistenceGroupSpecifier.name = @"Persistence";
[_specifiers addObject:persistenceGroupSpecifier];
@ -265,6 +365,11 @@ extern NSUserDefaults* trollStoreUserDefaults(void);
respring();
}
- (void)installOrUpdateLdidPressed
{
[TSInstallationController installLdid];
}
- (void)installPersistenceHelperPressed
{
NSMutableArray* appCandidates = [NSMutableArray new];

View File

@ -14,7 +14,9 @@
4. Wait a few seconds, your device should respring and TrollStore will be installed.
5. Done, you can now share IPA files with TrollStore and they will be permanently installed on your device.
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.
6. Done, you can now share IPA files with TrollStore and they will be permanently installed on your device.
## Unjailbreaking while retaining TrollStore

View File

@ -20,6 +20,6 @@
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.
8. Open the TrollStore app and 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).
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).
9. Done, you can now share IPA files with TrollStore and they will be permanently installed on your device.

View File

@ -20,6 +20,6 @@
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.
8. Open the TrollStore app and 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).
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).
9. Done, you can now share IPA files with TrollStore and they will be permanently installed on your device.