diff --git a/Helper/control b/Helper/control index 630a2bf..be35717 100644 --- a/Helper/control +++ b/Helper/control @@ -1,6 +1,6 @@ Package: com.opa334.trollstoreroothelper Name: trollstoreroothelper -Version: 1.0.5 +Version: 1.0.6 Architecture: iphoneos-arm Description: An awesome tool of some sort!! Maintainer: opa334 diff --git a/Helper/main.m b/Helper/main.m index 24d70f5..6228fa7 100644 --- a/Helper/main.m +++ b/Helper/main.m @@ -8,52 +8,10 @@ #import #import "CoreServices.h" #import "Shared.h" -#import -#import -#import -#import -#import -#import -#import -#import #import #import - -#ifdef __LP64__ -#define segment_command_universal segment_command_64 -#define mach_header_universal mach_header_64 -#define MH_MAGIC_UNIVERSAL MH_MAGIC_64 -#define MH_CIGAM_UNIVERSAL MH_CIGAM_64 -#else -#define segment_command_universal segment_command -#define mach_header_universal mach_header -#define MH_MAGIC_UNIVERSAL MH_MAGIC -#define MH_CIGAM_UNIVERSAL MH_CIGAM -#endif - -#define SWAP32(x) ((((x) & 0xff000000) >> 24) | (((x) & 0xff0000) >> 8) | (((x) & 0xff00) << 8) | (((x) & 0xff) << 24)) -uint32_t s32(uint32_t toSwap, BOOL shouldSwap) -{ - return shouldSwap ? SWAP32(toSwap) : toSwap; -} - -#define CPU_SUBTYPE_ARM64E_NEW_ABI 0x80000002 - -struct CSSuperBlob { - uint32_t magic; - uint32_t length; - uint32_t count; -}; - -struct CSBlob { - uint32_t type; - uint32_t offset; -}; - -#define CS_MAGIC_EMBEDDED_SIGNATURE 0xfade0cc0 -#define CS_MAGIC_EMBEDDED_SIGNATURE_REVERSED 0xc00cdefa -#define CS_MAGIC_EMBEDDED_ENTITLEMENTS 0xfade7171 +#import extern mach_msg_return_t SBReloadIconForIdentifier(mach_port_t machport, const char* identifier); @@ -65,6 +23,27 @@ extern NSString* BKSOpenApplicationOptionKeyActivateForEvent; extern void BKSTerminateApplicationForReasonAndReportWithDescription(NSString *bundleID, int reasonID, bool report, NSString *description); +typedef CF_OPTIONS(uint32_t, SecCSFlags) { + kSecCSDefaultFlags = 0 +}; + + +#define kSecCSRequirementInformation 1 << 2 +#define kSecCSSigningInformation 1 << 1 + +typedef struct __SecCode const *SecStaticCodeRef; + +extern CFStringRef kSecCodeInfoEntitlementsDict; +extern CFStringRef kSecCodeInfoCertificates; +extern CFStringRef kSecPolicyAppleiPhoneApplicationSigning; +extern CFStringRef kSecPolicyAppleiPhoneProfileApplicationSigning; +extern CFStringRef kSecPolicyLeafMarkerOid; + +OSStatus SecStaticCodeCreateWithPathAndAttributes(CFURLRef path, SecCSFlags flags, CFDictionaryRef attributes, SecStaticCodeRef *staticCode); +OSStatus SecCodeCopySigningInformation(SecStaticCodeRef code, SecCSFlags flags, CFDictionaryRef *information); +CFDataRef SecCertificateCopyExtensionValue(SecCertificateRef certificate, CFTypeRef extensionOID, bool *isCritical); +void SecPolicySetOptionsValue(SecPolicyRef policy, CFStringRef key, CFTypeRef value); + #define kCFPreferencesNoContainer CFSTR("kCFPreferencesNoContainer") typedef CFPropertyListRef (*_CFPreferencesCopyValueWithContainerType)(CFStringRef key, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath); @@ -221,124 +200,219 @@ int runLdid(NSArray* args, NSString** output, NSString** errorOutput) return WEXITSTATUS(status); } -NSDictionary* dumpEntitlements(NSString* binaryPath) +SecStaticCodeRef getStaticCodeRef(NSString *binaryPath) { - char* entitlementsData = NULL; - uint32_t entitlementsLength = 0; - - FILE* machoFile = fopen(binaryPath.UTF8String, "rb"); - struct mach_header_universal header; - fread(&header,sizeof(header),1,machoFile); - - uint32_t archOffset = 0; - - // Get arch offset if FAT binary - if(header.magic == FAT_MAGIC || header.magic == FAT_CIGAM) + if(binaryPath == nil) { - fseek(machoFile,0,SEEK_SET); - - struct fat_header fatHeader; - fread(&fatHeader,sizeof(fatHeader),1,machoFile); - - BOOL swpFat = fatHeader.magic == FAT_CIGAM; - - for(int i = 0; i < s32(fatHeader.nfat_arch, swpFat); i++) - { - struct fat_arch fatArch; - fseek(machoFile,sizeof(fatHeader) + sizeof(fatArch) * i,SEEK_SET); - fread(&fatArch,sizeof(fatArch),1,machoFile); - - if(s32(fatArch.cputype, swpFat) != CPU_TYPE_ARM64) - { - continue; - } - - archOffset = s32(fatArch.offset, swpFat); - break; - } + return NULL; } - - fseek(machoFile,archOffset,SEEK_SET); - fread(&header,sizeof(header),1,machoFile); - - if(header.magic == MH_MAGIC_UNIVERSAL || header.magic == MH_CIGAM_UNIVERSAL) + + CFURLRef binaryURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (__bridge CFStringRef)binaryPath, kCFURLPOSIXPathStyle, false); + if(binaryURL == NULL) { - BOOL swp = header.magic == MH_CIGAM_UNIVERSAL; - // This code is cursed, don't stare at it too long or it will stare back at you - uint32_t offset = archOffset + sizeof(header); - for(int c = 0; c < s32(header.ncmds, swp); c++) - { - fseek(machoFile,offset,SEEK_SET); - struct load_command cmd; - fread(&cmd,sizeof(cmd),1,machoFile); - uint32_t normalizedCmd = s32(cmd.cmd,swp); - if(normalizedCmd == LC_CODE_SIGNATURE) - { - struct linkedit_data_command codeSignCommand; - fseek(machoFile,offset,SEEK_SET); - fread(&codeSignCommand,sizeof(codeSignCommand),1,machoFile); - uint32_t codeSignCmdOffset = archOffset + s32(codeSignCommand.dataoff, swp); - fseek(machoFile, codeSignCmdOffset, SEEK_SET); - struct CSSuperBlob superBlob; - fread(&superBlob, sizeof(superBlob), 1, machoFile); - if(SWAP32(superBlob.magic) == CS_MAGIC_EMBEDDED_SIGNATURE) // YES starting here everything is swapped no matter if CIGAM or MAGIC... - { - uint32_t itemCount = SWAP32(superBlob.count); - for(int i = 0; i < itemCount; i++) - { - fseek(machoFile, codeSignCmdOffset + sizeof(superBlob) + i * sizeof(struct CSBlob),SEEK_SET); - struct CSBlob blob; - fread(&blob, sizeof(struct CSBlob), 1, machoFile); - fseek(machoFile, codeSignCmdOffset + SWAP32(blob.offset),SEEK_SET); - uint32_t blobMagic; - fread(&blobMagic, sizeof(uint32_t), 1, machoFile); - if(SWAP32(blobMagic) == CS_MAGIC_EMBEDDED_ENTITLEMENTS) - { - uint32_t entitlementsLengthTmp; - fread(&entitlementsLengthTmp, sizeof(uint32_t), 1, machoFile); - entitlementsLength = SWAP32(entitlementsLengthTmp); - entitlementsData = malloc(entitlementsLength - 8); - fread(&entitlementsData[0], entitlementsLength - 8, 1, machoFile); - break; - } - } - } - - break; - } - - offset += cmd.cmdsize; - } + NSLog(@"[getStaticCodeRef] failed to get URL to binary %@", binaryPath); + return NULL; } - - fclose(machoFile); - - NSData* entitlementsNSData = nil; - - if(entitlementsData) + + SecStaticCodeRef codeRef = NULL; + OSStatus result; + + result = SecStaticCodeCreateWithPathAndAttributes(binaryURL, kSecCSDefaultFlags, NULL, &codeRef); + + CFRelease(binaryURL); + + if(result != errSecSuccess) { - entitlementsNSData = [NSData dataWithBytes:entitlementsData length:entitlementsLength]; - free(entitlementsData); + NSLog(@"[getStaticCodeRef] failed to create static code for binary %@", binaryPath); + return NULL; } + + return codeRef; +} - if(entitlementsNSData) +NSDictionary* dumpEntitlements(SecStaticCodeRef codeRef) +{ + if(codeRef == NULL) { - NSDictionary* plist = [NSPropertyListSerialization propertyListWithData:entitlementsNSData options:NSPropertyListImmutable format:nil error:nil]; - NSLog(@"%@ dumped entitlements %@", binaryPath, plist); - return plist; + NSLog(@"[dumpEntitlements] attempting to dump entitlements without a StaticCodeRef"); + return nil; + } + + CFDictionaryRef signingInfo = NULL; + OSStatus result; + + result = SecCodeCopySigningInformation(codeRef, kSecCSRequirementInformation, &signingInfo); + + if(result != errSecSuccess) + { + NSLog(@"[dumpEntitlements] failed to copy signing info from static code"); + return nil; + } + + NSDictionary *entitlementsNSDict = nil; + + CFDictionaryRef entitlements = CFDictionaryGetValue(signingInfo, kSecCodeInfoEntitlementsDict); + if(entitlements == NULL) + { + NSLog(@"[dumpEntitlements] no entitlements specified"); + } + else if(CFGetTypeID(entitlements) != CFDictionaryGetTypeID()) + { + NSLog(@"[dumpEntitlements] invalid entitlements"); } else { - NSLog(@"Failed to dump entitlements of %@... This is bad", binaryPath); + entitlementsNSDict = (__bridge NSDictionary *)(entitlements); + NSLog(@"[dumpEntitlements] dumped %@", entitlementsNSDict); } - return nil; + CFRelease(signingInfo); + return entitlementsNSDict; +} + +NSDictionary* dumpEntitlementsFromBinaryAtPath(NSString *binaryPath) +{ + // This function is intended for one-shot checks. Main-event functions should retain/release their own SecStaticCodeRefs + + if(binaryPath == nil) + { + return nil; + } + + SecStaticCodeRef codeRef = getStaticCodeRef(binaryPath); + if(codeRef == NULL) + { + return nil; + } + + NSDictionary *entitlements = dumpEntitlements(codeRef); + CFRelease(codeRef); + + return entitlements; +} + +BOOL certificateHasDataForExtensionOID(SecCertificateRef certificate, CFStringRef oidString) +{ + if(certificate == NULL || oidString == NULL) + { + NSLog(@"[certificateHasDataForExtensionOID] attempted to check null certificate or OID"); + return NO; + } + + CFDataRef extensionData = SecCertificateCopyExtensionValue(certificate, oidString, NULL); + if(extensionData != NULL) + { + CFRelease(extensionData); + return YES; + } + + return NO; +} + +BOOL codeCertChainContainsFakeAppStoreExtensions(SecStaticCodeRef codeRef) +{ + if(codeRef == NULL) + { + NSLog(@"[codeCertChainContainsFakeAppStoreExtensions] attempted to check cert chain of null static code object"); + return NO; + } + + CFDictionaryRef signingInfo = NULL; + OSStatus result; + + result = SecCodeCopySigningInformation(codeRef, kSecCSSigningInformation, &signingInfo); + + if(result != errSecSuccess) + { + NSLog(@"[codeCertChainContainsFakeAppStoreExtensions] failed to copy signing info from static code"); + return NO; + } + + CFArrayRef certificates = CFDictionaryGetValue(signingInfo, kSecCodeInfoCertificates); + if(certificates == NULL || CFArrayGetCount(certificates) == 0) + { + return NO; + } + + // If we match the standard Apple policy, we are signed properly, but we haven't been deliberately signed with a custom root + + SecPolicyRef appleAppStorePolicy = SecPolicyCreateWithProperties(kSecPolicyAppleiPhoneApplicationSigning, NULL); + + SecTrustRef trust = NULL; + SecTrustCreateWithCertificates(certificates, appleAppStorePolicy, &trust); + + if(SecTrustEvaluateWithError(trust, nil)) + { + CFRelease(trust); + CFRelease(appleAppStorePolicy); + CFRelease(signingInfo); + + NSLog(@"[codeCertChainContainsFakeAppStoreExtensions] found certificate extension, but was issued by Apple (App Store)"); + return NO; + } + + // We haven't matched Apple, so keep going. Is the app profile signed? + + CFRelease(appleAppStorePolicy); + + SecPolicyRef appleProfileSignedPolicy = SecPolicyCreateWithProperties(kSecPolicyAppleiPhoneProfileApplicationSigning, NULL); + if(SecTrustSetPolicies(trust, appleProfileSignedPolicy) != errSecSuccess) + { + NSLog(@"[codeCertChainContainsFakeAppStoreExtensions] error replacing trust policy to check for profile-signed app"); + CFRelease(trust); + CFRelease(signingInfo); + return NO; + } + + if(SecTrustEvaluateWithError(trust, nil)) + { + CFRelease(trust); + CFRelease(appleProfileSignedPolicy); + CFRelease(signingInfo); + + NSLog(@"[codeCertChainContainsFakeAppStoreExtensions] found certificate extension, but was issued by Apple (profile-signed)"); + return NO; + } + + // Still haven't matched Apple. Are we using a custom root that would take the App Store fastpath? + CFRelease(appleProfileSignedPolicy); + + // Cert chain should be of length 3 + if(CFArrayGetCount(certificates) != 3) + { + CFRelease(signingInfo); + + NSLog(@"[codeCertChainContainsFakeAppStoreExtensions] certificate chain length != 3"); + return NO; + } + + // AppleCodeSigning only checks for the codeSigning EKU by default + SecPolicyRef customRootPolicy = SecPolicyCreateWithProperties(kSecPolicyAppleCodeSigning, NULL); + SecPolicySetOptionsValue(customRootPolicy, CFSTR("LeafMarkerOid"), CFSTR("1.2.840.113635.100.6.1.3")); + + if(SecTrustSetPolicies(trust, customRootPolicy) != errSecSuccess) + { + NSLog(@"[codeCertChainContainsFakeAppStoreExtensions] error replacing trust policy to check for custom root"); + CFRelease(trust); + CFRelease(signingInfo); + return NO; + } + + // Need to add our certificate chain to the anchor as it is expected to be a self-signed root + SecTrustSetAnchorCertificates(trust, certificates); + + BOOL evaluatesToCustomAnchor = SecTrustEvaluateWithError(trust, nil); + NSLog(@"[codeCertChainContainsFakeAppStoreExtensions] app signed with non-Apple certificate %@ using valid custom certificates", evaluatesToCustomAnchor ? @"IS" : @"is NOT"); + + CFRelease(trust); + CFRelease(customRootPolicy); + CFRelease(signingInfo); + + return evaluatesToCustomAnchor; } BOOL signApp(NSString* appPath, NSError** error) { - if(!isLdidInstalled()) return NO; - NSDictionary* appInfoDict = [NSDictionary dictionaryWithContentsOfFile:[appPath stringByAppendingPathComponent:@"Info.plist"]]; if(!appInfoDict) return NO; @@ -346,13 +420,45 @@ BOOL signApp(NSString* appPath, NSError** error) NSString* executablePath = [appPath stringByAppendingPathComponent:executable]; if(![[NSFileManager defaultManager] fileExistsAtPath:executablePath]) return NO; + + NSObject *tsBundleIsPreSigned = appInfoDict[@"TSBundlePreSigned"]; + if([tsBundleIsPreSigned isKindOfClass:[NSNumber class]]) + { + + // if TSBundlePreSigned = YES, this bundle has been externally signed so we can skip over signing it now + NSNumber *tsBundleIsPreSignedNum = (NSNumber *)tsBundleIsPreSigned; + if([tsBundleIsPreSignedNum boolValue] == YES) + { + NSLog(@"[signApp] taking fast path for app which declares it has already been signed (%@)", executablePath); + return YES; + } + } + + SecStaticCodeRef codeRef = getStaticCodeRef(executablePath); + if(codeRef != NULL) + { + if(codeCertChainContainsFakeAppStoreExtensions(codeRef)) + { + NSLog(@"[signApp] taking fast path for app signed using a custom root certificate (%@)", executablePath); + CFRelease(codeRef); + return YES; + } + } + else + { + NSLog(@"[signApp] failed to get static code, can't derive entitlements from %@, continuing anways...", executablePath); + } + + if(!isLdidInstalled()) return NO; NSString* certPath = [trollStoreAppPath() stringByAppendingPathComponent:@"cert.p12"]; NSString* certArg = [@"-K" stringByAppendingPathComponent:certPath]; NSString* errorOutput; int ldidRet; - NSDictionary* entitlements = dumpEntitlements(executablePath); + NSDictionary* entitlements = dumpEntitlements(codeRef); + CFRelease(codeRef); + if(!entitlements) { NSLog(@"app main binary has no entitlements, signing app with fallback entitlements..."); @@ -394,6 +500,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 int installApp(NSString* appPath, BOOL sign, BOOL force, NSError** error) { NSLog(@"[installApp force = %d]", force); @@ -405,8 +512,7 @@ int installApp(NSString* appPath, BOOL sign, BOOL force, NSError** error) if(sign) { - // if it fails to sign, we don't care - signApp(appPath, error); + if(!signApp(appPath, error)) return 173; } BOOL existed; diff --git a/Helper/uicache.m b/Helper/uicache.m index 1a2f8d9..6b5aa9a 100644 --- a/Helper/uicache.m +++ b/Helper/uicache.m @@ -6,7 +6,7 @@ // uicache on steroids -extern NSDictionary* dumpEntitlements(NSString* binaryPath); +extern NSDictionary* dumpEntitlementsFromBinaryAtPath(NSString* binaryPath); NSDictionary* constructGroupsContainersForEntitlements(NSDictionary* entitlements, BOOL systemGroups) { @@ -118,7 +118,7 @@ void registerPath(char* cPath, int unregister) // Add entitlements NSString* appExecutablePath = [path stringByAppendingPathComponent:appInfoPlist[@"CFBundleExecutable"]]; - NSDictionary* entitlements = dumpEntitlements(appExecutablePath); + NSDictionary* entitlements = dumpEntitlementsFromBinaryAtPath(appExecutablePath); if(entitlements) { dictToRegister[@"Entitlements"] = entitlements; @@ -139,6 +139,9 @@ void registerPath(char* cPath, int unregister) dictToRegister[@"IsDeletable"] = @0; dictToRegister[@"Path"] = path; dictToRegister[@"IsContainerized"] = @(constructContainerizationForEntitlements(entitlements)); + dictToRegister[@"SignerOrganization"] = @"Apple Inc."; + dictToRegister[@"SignatureVersion"] = @132352; + dictToRegister[@"SignerIdentity"] = @"Apple iPhone OS Application Signing"; NSString* teamIdentifier = constructTeamIdentifierForEntitlements(entitlements); if(teamIdentifier) dictToRegister[@"TeamIdentifier"] = teamIdentifier; @@ -185,7 +188,7 @@ void registerPath(char* cPath, int unregister) // Add entitlements NSString* pluginExecutablePath = [pluginPath stringByAppendingPathComponent:pluginInfoPlist[@"CFBundleExecutable"]]; - NSDictionary* pluginEntitlements = dumpEntitlements(pluginExecutablePath); + NSDictionary* pluginEntitlements = dumpEntitlementsFromBinaryAtPath(pluginExecutablePath); if(pluginEntitlements) { pluginDict[@"Entitlements"] = pluginEntitlements; @@ -206,6 +209,9 @@ void registerPath(char* cPath, int unregister) pluginDict[@"Path"] = pluginPath; pluginDict[@"PluginOwnerBundleID"] = appBundleID; pluginDict[@"IsContainerized"] = @(constructContainerizationForEntitlements(pluginEntitlements)); + pluginDict[@"SignerOrganization"] = @"Apple Inc."; + pluginDict[@"SignatureVersion"] = @132352; + pluginDict[@"SignerIdentity"] = @"Apple iPhone OS Application Signing"; NSString* pluginTeamIdentifier = constructTeamIdentifierForEntitlements(pluginEntitlements); if(pluginTeamIdentifier) pluginDict[@"TeamIdentifier"] = pluginTeamIdentifier; @@ -247,4 +253,4 @@ void registerPath(char* cPath, int unregister) NSLog(@"Error: Unable to unregister %@", path); } } -} \ No newline at end of file +} diff --git a/PersistenceHelper/Resources/Info.plist b/PersistenceHelper/Resources/Info.plist index c89c107..15e42bc 100644 --- a/PersistenceHelper/Resources/Info.plist +++ b/PersistenceHelper/Resources/Info.plist @@ -52,7 +52,7 @@ iPhoneOS CFBundleVersion - 1.0.5 + 1.0.7 LSRequiresIPhoneOS UIDeviceFamily diff --git a/PersistenceHelper/control b/PersistenceHelper/control index 980a5ec..eb49441 100644 --- a/PersistenceHelper/control +++ b/PersistenceHelper/control @@ -1,6 +1,6 @@ Package: com.opa334.trollstorehelper Name: TrollStore Helper -Version: 1.0.5 +Version: 1.0.7 Architecture: iphoneos-arm Description: Helper app to install and manage TrollStore! Maintainer: opa334 diff --git a/Store/Resources/Info.plist b/Store/Resources/Info.plist index 9684759..4ad618f 100644 --- a/Store/Resources/Info.plist +++ b/Store/Resources/Info.plist @@ -50,7 +50,7 @@ iPhoneOS CFBundleVersion - 1.0.5 + 1.0.7 LSRequiresIPhoneOS UIDeviceFamily diff --git a/Store/TSApplicationsManager.m b/Store/TSApplicationsManager.m index 57294c2..328c7f9 100644 --- a/Store/TSApplicationsManager.m +++ b/Store/TSApplicationsManager.m @@ -72,6 +72,9 @@ case 172: errorDescription = @"The app does not seem to contain an Info.plist"; break; + case 173: + errorDescription = @"The app is not signed with a fake CoreTrust certificate and ldid does not seem to be installed. Make sure ldid is installed in the settings tab and try again."; + break; } NSError* error = [NSError errorWithDomain:TrollStoreErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : errorDescription}]; diff --git a/Store/TSSettingsListController.m b/Store/TSSettingsListController.m index 99a3d83..2885116 100644 --- a/Store/TSSettingsListController.m +++ b/Store/TSSettingsListController.m @@ -192,7 +192,7 @@ } PSSpecifier* otherGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; - [otherGroupSpecifier setProperty:[NSString stringWithFormat:@"TrollStore %@\n\n© 2022 Lars Fröder (opa334)\n\nCredits:\n@LinusHenze: CoreTrust bug\n@zhuowei: CoreTrust bug writeup and cert\n@ProcursusTeam: uicache and ldid build\n@cstar_ow: uicache\n@saurik: ldid", getTrollStoreVersion()] forKey:@"footerText"]; + [otherGroupSpecifier setProperty:[NSString stringWithFormat:@"TrollStore %@\n\n© 2022 Lars Fröder (opa334)\n\nCredits:\n@LinusHenze: CoreTrust bug\n@zhuowei: CoreTrust bug writeup and cert\n@lunotech11: Some contributions\n@ProcursusTeam: uicache and ldid build\n@cstar_ow: uicache\n@saurik: ldid", getTrollStoreVersion()] forKey:@"footerText"]; [_specifiers addObject:otherGroupSpecifier]; // Uninstall TrollStore diff --git a/Store/control b/Store/control index 812c349..c215f6c 100644 --- a/Store/control +++ b/Store/control @@ -1,6 +1,6 @@ Package: com.opa334.trollstore Name: TrollStore -Version: 1.0.5 +Version: 1.0.7 Architecture: iphoneos-arm Description: An awesome application! Maintainer: opa334