From 5659a84c57f8ee24ae43342b171df6269ee571ff Mon Sep 17 00:00:00 2001 From: Luke Noble Date: Sun, 4 Sep 2022 19:48:20 +0100 Subject: [PATCH 1/8] Info.plist fast path for apps already CT-signed Apps declaring TSBundlePreSigned = YES in their Info.plist will not be signed by TrollStore --- Helper/main.m | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Helper/main.m b/Helper/main.m index 24d70f5..c1e2105 100644 --- a/Helper/main.m +++ b/Helper/main.m @@ -346,6 +346,19 @@ 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 CT-signed (%@)", executablePath); + return YES; + } + } NSString* certPath = [trollStoreAppPath() stringByAppendingPathComponent:@"cert.p12"]; NSString* certArg = [@"-K" stringByAppendingPathComponent:certPath]; From d909eee3fe16cec3a4050925cbe8f22a63effd9b Mon Sep 17 00:00:00 2001 From: Luke Noble Date: Sun, 4 Sep 2022 21:18:59 +0100 Subject: [PATCH 2/8] Handle apps already signed with a custom root --- Helper/main.m | 368 +++++++++++++++++++++++++++-------------------- Helper/uicache.m | 8 +- 2 files changed, 220 insertions(+), 156 deletions(-) diff --git a/Helper/main.m b/Helper/main.m index c1e2105..e12712d 100644 --- a/Helper/main.m +++ b/Helper/main.m @@ -8,53 +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); @interface SBSHomeScreenService : NSObject @@ -65,6 +22,26 @@ 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 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); + #define kCFPreferencesNoContainer CFSTR("kCFPreferencesNoContainer") typedef CFPropertyListRef (*_CFPreferencesCopyValueWithContainerType)(CFStringRef key, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath); @@ -221,118 +198,189 @@ 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; + + if(binaryPath == nil) + { + return NULL; + } + + CFURLRef binaryURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (__bridge CFStringRef)binaryPath, kCFURLPOSIXPathStyle, false); + if(binaryURL == NULL) + { + NSLog(@"[getStaticCodeRef] failed to get URL to binary %@", binaryPath); + return NULL; + } + + SecStaticCodeRef codeRef = NULL; + OSStatus result; + + result = SecStaticCodeCreateWithPathAndAttributes(binaryURL, kSecCSDefaultFlags, NULL, &codeRef); + + CFRelease(binaryURL); + + if(result != errSecSuccess) + { + NSLog(@"[getStaticCodeRef] failed to create static code for binary %@", binaryPath); + return NULL; + } + + return codeRef; +} - FILE* machoFile = fopen(binaryPath.UTF8String, "rb"); - struct mach_header_universal header; - fread(&header,sizeof(header),1,machoFile); +NSDictionary* dumpEntitlements(SecStaticCodeRef codeRef) +{ + + if(codeRef == NULL) + { + 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 + { + entitlementsNSDict = (__bridge NSDictionary *)(entitlements); + NSLog(@"[dumpEntitlements] dumped %@", entitlementsNSDict); + } + + CFRelease(signingInfo); + return entitlementsNSDict; +} - uint32_t archOffset = 0; +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; +} - // Get arch offset if FAT binary - if(header.magic == FAT_MAGIC || header.magic == FAT_CIGAM) - { - fseek(machoFile,0,SEEK_SET); +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; +} - struct fat_header fatHeader; - fread(&fatHeader,sizeof(fatHeader),1,machoFile); +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); - 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; - } - } - - fseek(machoFile,archOffset,SEEK_SET); - fread(&header,sizeof(header),1,machoFile); - - if(header.magic == MH_MAGIC_UNIVERSAL || header.magic == MH_CIGAM_UNIVERSAL) - { - 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; - } - } - - fclose(machoFile); - - NSData* entitlementsNSData = nil; - - if(entitlementsData) - { - entitlementsNSData = [NSData dataWithBytes:entitlementsData length:entitlementsLength]; - free(entitlementsData); - } - - if(entitlementsNSData) - { - NSDictionary* plist = [NSPropertyListSerialization propertyListWithData:entitlementsNSData options:NSPropertyListImmutable format:nil error:nil]; - NSLog(@"%@ dumped entitlements %@", binaryPath, plist); - return plist; - } - else - { - NSLog(@"Failed to dump entitlements of %@... This is bad", binaryPath); - } - - return nil; + // If we match the standard Apple policy, we are signed properly, but we haven't been deliberately signed with a custom root + + SecPolicyRef genuineApplePolicy = SecPolicyCreateWithProperties(kSecPolicyAppleiPhoneApplicationSigning, NULL); + SecTrustRef genuineAppleTrust = NULL; + SecTrustCreateWithCertificates(certificates, genuineApplePolicy, &genuineAppleTrust); + + if(SecTrustEvaluateWithError(genuineAppleTrust, nil)) + { + CFRelease(genuineAppleTrust); + CFRelease(genuineApplePolicy); + CFRelease(signingInfo); + + NSLog(@"[codeCertChainContainsFakeAppStoreExtensions] found certificate extension, but was issued by Apple"); + return NO; + } + + // We haven't matched Apple, so keep going. Are we using a custom root that would take the App Store fastpath? + CFRelease(genuineAppleTrust); + CFRelease(genuineApplePolicy); + + // Cert chain should be of length 3 + if(CFArrayGetCount(certificates) != 3) + { + CFRelease(signingInfo); + + NSLog(@"[codeCertChainContainsFakeAppStoreExtensions] certificate chain length != 3"); + return NO; + } + + const void *keys[1] = { kSecPolicyLeafMarkerOid }; + const void *values[1] = { CFSTR("1.2.840.113635.100.6.1.3") }; + CFDictionaryRef customPolicyOptions = CFDictionaryCreate(NULL, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + // AppleCodeSigning only checks for the codeSigning EKU by default + SecPolicyRef customRootPolicy = SecPolicyCreateWithProperties(kSecPolicyAppleCodeSigning, customPolicyOptions); + SecTrustRef customRootTrust = NULL; + + // Need to add our certificate chain to the anchor as it is expected to be a self-signed root + SecTrustCreateWithCertificates(certificates, customRootPolicy, &customRootTrust); + SecTrustSetAnchorCertificates(customRootTrust, certificates); + + BOOL evaluatesToCustomAnchor = SecTrustEvaluateWithError(customRootTrust, nil); + + CFRelease(customRootTrust); + CFRelease(customRootPolicy); + CFRelease(customPolicyOptions); + + CFRelease(signingInfo); + return evaluatesToCustomAnchor; } BOOL signApp(NSString* appPath, NSError** error) @@ -355,17 +403,33 @@ BOOL signApp(NSString* appPath, NSError** error) NSNumber *tsBundleIsPreSignedNum = (NSNumber *)tsBundleIsPreSigned; if([tsBundleIsPreSignedNum boolValue] == YES) { - NSLog(@"[signApp] taking fast path for app which declares it has already been CT-signed (%@)", executablePath); + NSLog(@"[signApp] taking fast path for app which declares it has already been signed (%@)", executablePath); return YES; } } + + SecStaticCodeRef codeRef = getStaticCodeRef(executablePath); + if(codeRef == NULL) + { + NSLog(@"[signApp] failed to get static code, can't derive entitlements from %@", executablePath); + return NO; + } + + if(codeCertChainContainsFakeAppStoreExtensions(codeRef)) + { + NSLog(@"[signApp] taking fast path for app signed using a custom root certificate (%@)", executablePath); + CFRelease(codeRef); + return YES; + } 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..."); diff --git a/Helper/uicache.m b/Helper/uicache.m index 1a2f8d9..97bfeb1 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; @@ -185,7 +185,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; @@ -247,4 +247,4 @@ void registerPath(char* cPath, int unregister) NSLog(@"Error: Unable to unregister %@", path); } } -} \ No newline at end of file +} From fa642e8d06fcbaa9c178a2d8c2a7f8e34813f3ad Mon Sep 17 00:00:00 2001 From: opa334 Date: Mon, 5 Sep 2022 14:48:06 +0200 Subject: [PATCH 3/8] Fix app plugins --- Helper/uicache.m | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Helper/uicache.m b/Helper/uicache.m index 1a2f8d9..85f22e9 100644 --- a/Helper/uicache.m +++ b/Helper/uicache.m @@ -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; @@ -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; From 4f870c1a822ec1437eb3df8b454468bce9878722 Mon Sep 17 00:00:00 2001 From: opa334 Date: Mon, 5 Sep 2022 15:09:31 +0200 Subject: [PATCH 4/8] Revert "Merge pull request #63 from luken11/signing-fast-path" This reverts commit a0dc58a4c0465609dbdb340913e780347daa98ea, reversing changes made to fa642e8d06fcbaa9c178a2d8c2a7f8e34813f3ad. --- Helper/main.m | 379 +++++++++++++++++++---------------------------- Helper/uicache.m | 8 +- 2 files changed, 155 insertions(+), 232 deletions(-) diff --git a/Helper/main.m b/Helper/main.m index e12712d..24d70f5 100644 --- a/Helper/main.m +++ b/Helper/main.m @@ -8,10 +8,53 @@ #import #import "CoreServices.h" #import "Shared.h" +#import +#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 + extern mach_msg_return_t SBReloadIconForIdentifier(mach_port_t machport, const char* identifier); @interface SBSHomeScreenService : NSObject @@ -22,26 +65,6 @@ 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 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); - #define kCFPreferencesNoContainer CFSTR("kCFPreferencesNoContainer") typedef CFPropertyListRef (*_CFPreferencesCopyValueWithContainerType)(CFStringRef key, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath); @@ -198,189 +221,118 @@ int runLdid(NSArray* args, NSString** output, NSString** errorOutput) return WEXITSTATUS(status); } -SecStaticCodeRef getStaticCodeRef(NSString *binaryPath) +NSDictionary* dumpEntitlements(NSString* binaryPath) { - - if(binaryPath == nil) - { - return NULL; - } - - CFURLRef binaryURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (__bridge CFStringRef)binaryPath, kCFURLPOSIXPathStyle, false); - if(binaryURL == NULL) - { - NSLog(@"[getStaticCodeRef] failed to get URL to binary %@", binaryPath); - return NULL; - } - - SecStaticCodeRef codeRef = NULL; - OSStatus result; - - result = SecStaticCodeCreateWithPathAndAttributes(binaryURL, kSecCSDefaultFlags, NULL, &codeRef); - - CFRelease(binaryURL); - - if(result != errSecSuccess) - { - NSLog(@"[getStaticCodeRef] failed to create static code for binary %@", binaryPath); - return NULL; - } - - return codeRef; -} + char* entitlementsData = NULL; + uint32_t entitlementsLength = 0; -NSDictionary* dumpEntitlements(SecStaticCodeRef codeRef) -{ - - if(codeRef == NULL) - { - 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 - { - entitlementsNSDict = (__bridge NSDictionary *)(entitlements); - NSLog(@"[dumpEntitlements] dumped %@", entitlementsNSDict); - } - - CFRelease(signingInfo); - return entitlementsNSDict; -} + FILE* machoFile = fopen(binaryPath.UTF8String, "rb"); + struct mach_header_universal header; + fread(&header,sizeof(header),1,machoFile); -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; -} + uint32_t archOffset = 0; -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; -} + // Get arch offset if FAT binary + if(header.magic == FAT_MAGIC || header.magic == FAT_CIGAM) + { + fseek(machoFile,0,SEEK_SET); -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); + struct fat_header fatHeader; + fread(&fatHeader,sizeof(fatHeader),1,machoFile); - // If we match the standard Apple policy, we are signed properly, but we haven't been deliberately signed with a custom root - - SecPolicyRef genuineApplePolicy = SecPolicyCreateWithProperties(kSecPolicyAppleiPhoneApplicationSigning, NULL); - SecTrustRef genuineAppleTrust = NULL; - SecTrustCreateWithCertificates(certificates, genuineApplePolicy, &genuineAppleTrust); - - if(SecTrustEvaluateWithError(genuineAppleTrust, nil)) - { - CFRelease(genuineAppleTrust); - CFRelease(genuineApplePolicy); - CFRelease(signingInfo); - - NSLog(@"[codeCertChainContainsFakeAppStoreExtensions] found certificate extension, but was issued by Apple"); - return NO; - } - - // We haven't matched Apple, so keep going. Are we using a custom root that would take the App Store fastpath? - CFRelease(genuineAppleTrust); - CFRelease(genuineApplePolicy); - - // Cert chain should be of length 3 - if(CFArrayGetCount(certificates) != 3) - { - CFRelease(signingInfo); - - NSLog(@"[codeCertChainContainsFakeAppStoreExtensions] certificate chain length != 3"); - return NO; - } - - const void *keys[1] = { kSecPolicyLeafMarkerOid }; - const void *values[1] = { CFSTR("1.2.840.113635.100.6.1.3") }; - CFDictionaryRef customPolicyOptions = CFDictionaryCreate(NULL, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - - // AppleCodeSigning only checks for the codeSigning EKU by default - SecPolicyRef customRootPolicy = SecPolicyCreateWithProperties(kSecPolicyAppleCodeSigning, customPolicyOptions); - SecTrustRef customRootTrust = NULL; - - // Need to add our certificate chain to the anchor as it is expected to be a self-signed root - SecTrustCreateWithCertificates(certificates, customRootPolicy, &customRootTrust); - SecTrustSetAnchorCertificates(customRootTrust, certificates); - - BOOL evaluatesToCustomAnchor = SecTrustEvaluateWithError(customRootTrust, nil); - - CFRelease(customRootTrust); - CFRelease(customRootPolicy); - CFRelease(customPolicyOptions); - - CFRelease(signingInfo); - return evaluatesToCustomAnchor; + 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; + } + } + + fseek(machoFile,archOffset,SEEK_SET); + fread(&header,sizeof(header),1,machoFile); + + if(header.magic == MH_MAGIC_UNIVERSAL || header.magic == MH_CIGAM_UNIVERSAL) + { + 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; + } + } + + fclose(machoFile); + + NSData* entitlementsNSData = nil; + + if(entitlementsData) + { + entitlementsNSData = [NSData dataWithBytes:entitlementsData length:entitlementsLength]; + free(entitlementsData); + } + + if(entitlementsNSData) + { + NSDictionary* plist = [NSPropertyListSerialization propertyListWithData:entitlementsNSData options:NSPropertyListImmutable format:nil error:nil]; + NSLog(@"%@ dumped entitlements %@", binaryPath, plist); + return plist; + } + else + { + NSLog(@"Failed to dump entitlements of %@... This is bad", binaryPath); + } + + return nil; } BOOL signApp(NSString* appPath, NSError** error) @@ -394,42 +346,13 @@ 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) - { - NSLog(@"[signApp] failed to get static code, can't derive entitlements from %@", executablePath); - return NO; - } - - if(codeCertChainContainsFakeAppStoreExtensions(codeRef)) - { - NSLog(@"[signApp] taking fast path for app signed using a custom root certificate (%@)", executablePath); - CFRelease(codeRef); - return YES; - } NSString* certPath = [trollStoreAppPath() stringByAppendingPathComponent:@"cert.p12"]; NSString* certArg = [@"-K" stringByAppendingPathComponent:certPath]; NSString* errorOutput; int ldidRet; - NSDictionary* entitlements = dumpEntitlements(codeRef); - CFRelease(codeRef); - + NSDictionary* entitlements = dumpEntitlements(executablePath); if(!entitlements) { NSLog(@"app main binary has no entitlements, signing app with fallback entitlements..."); diff --git a/Helper/uicache.m b/Helper/uicache.m index f88ac1b..85f22e9 100644 --- a/Helper/uicache.m +++ b/Helper/uicache.m @@ -6,7 +6,7 @@ // uicache on steroids -extern NSDictionary* dumpEntitlementsFromBinaryAtPath(NSString* binaryPath); +extern NSDictionary* dumpEntitlements(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 = dumpEntitlementsFromBinaryAtPath(appExecutablePath); + NSDictionary* entitlements = dumpEntitlements(appExecutablePath); if(entitlements) { dictToRegister[@"Entitlements"] = entitlements; @@ -188,7 +188,7 @@ void registerPath(char* cPath, int unregister) // Add entitlements NSString* pluginExecutablePath = [pluginPath stringByAppendingPathComponent:pluginInfoPlist[@"CFBundleExecutable"]]; - NSDictionary* pluginEntitlements = dumpEntitlementsFromBinaryAtPath(pluginExecutablePath); + NSDictionary* pluginEntitlements = dumpEntitlements(pluginExecutablePath); if(pluginEntitlements) { pluginDict[@"Entitlements"] = pluginEntitlements; @@ -253,4 +253,4 @@ void registerPath(char* cPath, int unregister) NSLog(@"Error: Unable to unregister %@", path); } } -} +} \ No newline at end of file From f864807df7ed06c4116702891f78dbdabc80f1da Mon Sep 17 00:00:00 2001 From: opa334 Date: Mon, 5 Sep 2022 15:15:41 +0200 Subject: [PATCH 5/8] 1.0.6 --- Helper/control | 2 +- PersistenceHelper/Resources/Info.plist | 2 +- PersistenceHelper/control | 2 +- Store/Resources/Info.plist | 2 +- Store/control | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) 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/PersistenceHelper/Resources/Info.plist b/PersistenceHelper/Resources/Info.plist index c89c107..4953dbf 100644 --- a/PersistenceHelper/Resources/Info.plist +++ b/PersistenceHelper/Resources/Info.plist @@ -52,7 +52,7 @@ iPhoneOS CFBundleVersion - 1.0.5 + 1.0.6 LSRequiresIPhoneOS UIDeviceFamily diff --git a/PersistenceHelper/control b/PersistenceHelper/control index 980a5ec..f7a86f8 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.6 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..da5e09f 100644 --- a/Store/Resources/Info.plist +++ b/Store/Resources/Info.plist @@ -50,7 +50,7 @@ iPhoneOS CFBundleVersion - 1.0.5 + 1.0.6 LSRequiresIPhoneOS UIDeviceFamily diff --git a/Store/control b/Store/control index 812c349..6b0fccc 100644 --- a/Store/control +++ b/Store/control @@ -1,6 +1,6 @@ Package: com.opa334.trollstore Name: TrollStore -Version: 1.0.5 +Version: 1.0.6 Architecture: iphoneos-arm Description: An awesome application! Maintainer: opa334 From 0a5a8aefbed07ae32670c9e3b26d1c40093f8aaf Mon Sep 17 00:00:00 2001 From: Luke Noble Date: Mon, 5 Sep 2022 20:39:17 +0100 Subject: [PATCH 6/8] Improve existing certificate checks for fast-path --- Helper/main.m | 405 +++++++++++++++++++++++++++++------------------ Helper/uicache.m | 8 +- 2 files changed, 259 insertions(+), 154 deletions(-) diff --git a/Helper/main.m b/Helper/main.m index 24d70f5..481ad4f 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,118 +200,215 @@ 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; + + if(binaryPath == nil) + { + return NULL; + } + + CFURLRef binaryURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (__bridge CFStringRef)binaryPath, kCFURLPOSIXPathStyle, false); + if(binaryURL == NULL) + { + NSLog(@"[getStaticCodeRef] failed to get URL to binary %@", binaryPath); + return NULL; + } + + SecStaticCodeRef codeRef = NULL; + OSStatus result; + + result = SecStaticCodeCreateWithPathAndAttributes(binaryURL, kSecCSDefaultFlags, NULL, &codeRef); + + CFRelease(binaryURL); + + if(result != errSecSuccess) + { + NSLog(@"[getStaticCodeRef] failed to create static code for binary %@", binaryPath); + return NULL; + } + + return codeRef; +} - FILE* machoFile = fopen(binaryPath.UTF8String, "rb"); - struct mach_header_universal header; - fread(&header,sizeof(header),1,machoFile); +NSDictionary* dumpEntitlements(SecStaticCodeRef codeRef) +{ + + if(codeRef == NULL) + { + 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 + { + entitlementsNSDict = (__bridge NSDictionary *)(entitlements); + NSLog(@"[dumpEntitlements] dumped %@", entitlementsNSDict); + } + + CFRelease(signingInfo); + return entitlementsNSDict; +} - uint32_t archOffset = 0; +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; +} - // Get arch offset if FAT binary - if(header.magic == FAT_MAGIC || header.magic == FAT_CIGAM) - { - fseek(machoFile,0,SEEK_SET); +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; +} - struct fat_header fatHeader; - fread(&fatHeader,sizeof(fatHeader),1,machoFile); +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); - 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; - } - } - - fseek(machoFile,archOffset,SEEK_SET); - fread(&header,sizeof(header),1,machoFile); - - if(header.magic == MH_MAGIC_UNIVERSAL || header.magic == MH_CIGAM_UNIVERSAL) - { - 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; - } - } - - fclose(machoFile); - - NSData* entitlementsNSData = nil; - - if(entitlementsData) - { - entitlementsNSData = [NSData dataWithBytes:entitlementsData length:entitlementsLength]; - free(entitlementsData); - } - - if(entitlementsNSData) - { - NSDictionary* plist = [NSPropertyListSerialization propertyListWithData:entitlementsNSData options:NSPropertyListImmutable format:nil error:nil]; - NSLog(@"%@ dumped entitlements %@", binaryPath, plist); - return plist; - } - else - { - NSLog(@"Failed to dump entitlements of %@... This is bad", binaryPath); - } - - return nil; + // 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) @@ -346,13 +422,42 @@ 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) + { + NSLog(@"[signApp] failed to get static code, can't derive entitlements from %@", executablePath); + return NO; + } + + if(codeCertChainContainsFakeAppStoreExtensions(codeRef)) + { + NSLog(@"[signApp] taking fast path for app signed using a custom root certificate (%@)", executablePath); + CFRelease(codeRef); + return YES; + } 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..."); diff --git a/Helper/uicache.m b/Helper/uicache.m index 85f22e9..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; @@ -188,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; @@ -253,4 +253,4 @@ void registerPath(char* cPath, int unregister) NSLog(@"Error: Unable to unregister %@", path); } } -} \ No newline at end of file +} From 4d2bfbf667a4108484448f43254e6b51575f73db Mon Sep 17 00:00:00 2001 From: opa334 Date: Tue, 6 Sep 2022 01:04:10 +0200 Subject: [PATCH 7/8] 1.0.7 --- Helper/main.m | 47 +++++++++++++------------- PersistenceHelper/Resources/Info.plist | 2 +- PersistenceHelper/control | 2 +- Store/Resources/Info.plist | 2 +- Store/TSApplicationsManager.m | 3 ++ Store/TSSettingsListController.m | 2 +- Store/control | 2 +- 7 files changed, 32 insertions(+), 28 deletions(-) diff --git a/Helper/main.m b/Helper/main.m index 481ad4f..f9c9d8e 100644 --- a/Helper/main.m +++ b/Helper/main.m @@ -202,7 +202,6 @@ int runLdid(NSArray* args, NSString** output, NSString** errorOutput) SecStaticCodeRef getStaticCodeRef(NSString *binaryPath) { - if(binaryPath == nil) { return NULL; @@ -233,7 +232,6 @@ SecStaticCodeRef getStaticCodeRef(NSString *binaryPath) NSDictionary* dumpEntitlements(SecStaticCodeRef codeRef) { - if(codeRef == NULL) { NSLog(@"[dumpEntitlements] attempting to dump entitlements without a StaticCodeRef"); @@ -295,7 +293,6 @@ NSDictionary* dumpEntitlementsFromBinaryAtPath(NSString *binaryPath) BOOL certificateHasDataForExtensionOID(SecCertificateRef certificate, CFStringRef oidString) { - if(certificate == NULL || oidString == NULL) { NSLog(@"[certificateHasDataForExtensionOID] attempted to check null certificate or OID"); @@ -314,7 +311,6 @@ BOOL certificateHasDataForExtensionOID(SecCertificateRef certificate, CFStringRe BOOL codeCertChainContainsFakeAppStoreExtensions(SecStaticCodeRef codeRef) { - if(codeRef == NULL) { NSLog(@"[codeCertChainContainsFakeAppStoreExtensions] attempted to check cert chain of null static code object"); @@ -325,7 +321,7 @@ BOOL codeCertChainContainsFakeAppStoreExtensions(SecStaticCodeRef codeRef) OSStatus result; result = SecCodeCopySigningInformation(codeRef, kSecCSSigningInformation, &signingInfo); - + if(result != errSecSuccess) { NSLog(@"[codeCertChainContainsFakeAppStoreExtensions] failed to copy signing info from static code"); @@ -333,14 +329,18 @@ BOOL codeCertChainContainsFakeAppStoreExtensions(SecStaticCodeRef codeRef) } 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); @@ -350,7 +350,7 @@ BOOL codeCertChainContainsFakeAppStoreExtensions(SecStaticCodeRef codeRef) 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); @@ -397,7 +397,7 @@ BOOL codeCertChainContainsFakeAppStoreExtensions(SecStaticCodeRef codeRef) 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); @@ -413,8 +413,6 @@ BOOL codeCertChainContainsFakeAppStoreExtensions(SecStaticCodeRef codeRef) BOOL signApp(NSString* appPath, NSError** error) { - if(!isLdidInstalled()) return NO; - NSDictionary* appInfoDict = [NSDictionary dictionaryWithContentsOfFile:[appPath stringByAppendingPathComponent:@"Info.plist"]]; if(!appInfoDict) return NO; @@ -437,18 +435,21 @@ BOOL signApp(NSString* appPath, NSError** error) } SecStaticCodeRef codeRef = getStaticCodeRef(executablePath); - if(codeRef == NULL) + if(codeRef != NULL) { - NSLog(@"[signApp] failed to get static code, can't derive entitlements from %@", executablePath); - return NO; - } - - if(codeCertChainContainsFakeAppStoreExtensions(codeRef)) - { - NSLog(@"[signApp] taking fast path for app signed using a custom root certificate (%@)", executablePath); - CFRelease(codeRef); - return YES; + 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]; @@ -499,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); @@ -510,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/PersistenceHelper/Resources/Info.plist b/PersistenceHelper/Resources/Info.plist index 4953dbf..15e42bc 100644 --- a/PersistenceHelper/Resources/Info.plist +++ b/PersistenceHelper/Resources/Info.plist @@ -52,7 +52,7 @@ iPhoneOS CFBundleVersion - 1.0.6 + 1.0.7 LSRequiresIPhoneOS UIDeviceFamily diff --git a/PersistenceHelper/control b/PersistenceHelper/control index f7a86f8..eb49441 100644 --- a/PersistenceHelper/control +++ b/PersistenceHelper/control @@ -1,6 +1,6 @@ Package: com.opa334.trollstorehelper Name: TrollStore Helper -Version: 1.0.6 +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 da5e09f..4ad618f 100644 --- a/Store/Resources/Info.plist +++ b/Store/Resources/Info.plist @@ -50,7 +50,7 @@ iPhoneOS CFBundleVersion - 1.0.6 + 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 6b0fccc..c215f6c 100644 --- a/Store/control +++ b/Store/control @@ -1,6 +1,6 @@ Package: com.opa334.trollstore Name: TrollStore -Version: 1.0.6 +Version: 1.0.7 Architecture: iphoneos-arm Description: An awesome application! Maintainer: opa334 From 7d0f525f51e71f31aa3e965534f1793b3611f533 Mon Sep 17 00:00:00 2001 From: opa334 Date: Tue, 6 Sep 2022 01:04:45 +0200 Subject: [PATCH 8/8] Fix intendation --- Helper/main.m | 406 +++++++++++++++++++++++++------------------------- 1 file changed, 203 insertions(+), 203 deletions(-) diff --git a/Helper/main.m b/Helper/main.m index f9c9d8e..6228fa7 100644 --- a/Helper/main.m +++ b/Helper/main.m @@ -24,7 +24,7 @@ extern NSString* BKSOpenApplicationOptionKeyActivateForEvent; extern void BKSTerminateApplicationForReasonAndReportWithDescription(NSString *bundleID, int reasonID, bool report, NSString *description); typedef CF_OPTIONS(uint32_t, SecCSFlags) { - kSecCSDefaultFlags = 0 + kSecCSDefaultFlags = 0 }; @@ -202,213 +202,213 @@ int runLdid(NSArray* args, NSString** output, NSString** errorOutput) SecStaticCodeRef getStaticCodeRef(NSString *binaryPath) { - if(binaryPath == nil) - { - return NULL; - } - - CFURLRef binaryURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (__bridge CFStringRef)binaryPath, kCFURLPOSIXPathStyle, false); - if(binaryURL == NULL) - { - NSLog(@"[getStaticCodeRef] failed to get URL to binary %@", binaryPath); - return NULL; - } - - SecStaticCodeRef codeRef = NULL; - OSStatus result; - - result = SecStaticCodeCreateWithPathAndAttributes(binaryURL, kSecCSDefaultFlags, NULL, &codeRef); - - CFRelease(binaryURL); - - if(result != errSecSuccess) - { - NSLog(@"[getStaticCodeRef] failed to create static code for binary %@", binaryPath); - return NULL; - } - - return codeRef; + if(binaryPath == nil) + { + return NULL; + } + + CFURLRef binaryURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (__bridge CFStringRef)binaryPath, kCFURLPOSIXPathStyle, false); + if(binaryURL == NULL) + { + NSLog(@"[getStaticCodeRef] failed to get URL to binary %@", binaryPath); + return NULL; + } + + SecStaticCodeRef codeRef = NULL; + OSStatus result; + + result = SecStaticCodeCreateWithPathAndAttributes(binaryURL, kSecCSDefaultFlags, NULL, &codeRef); + + CFRelease(binaryURL); + + if(result != errSecSuccess) + { + NSLog(@"[getStaticCodeRef] failed to create static code for binary %@", binaryPath); + return NULL; + } + + return codeRef; } NSDictionary* dumpEntitlements(SecStaticCodeRef codeRef) { - if(codeRef == NULL) - { - 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 - { - entitlementsNSDict = (__bridge NSDictionary *)(entitlements); - NSLog(@"[dumpEntitlements] dumped %@", entitlementsNSDict); - } - - CFRelease(signingInfo); - return entitlementsNSDict; + if(codeRef == NULL) + { + 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 + { + entitlementsNSDict = (__bridge NSDictionary *)(entitlements); + NSLog(@"[dumpEntitlements] dumped %@", entitlementsNSDict); + } + + 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; + // 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; + 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; + 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); + 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(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); + // 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); + 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; - } + 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; - } + // 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; + // 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) @@ -420,33 +420,33 @@ 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) - { + + 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); + NSLog(@"[signApp] failed to get static code, can't derive entitlements from %@, continuing anways...", executablePath); } if(!isLdidInstalled()) return NO; @@ -456,9 +456,9 @@ BOOL signApp(NSString* appPath, NSError** error) NSString* errorOutput; int ldidRet; - NSDictionary* entitlements = dumpEntitlements(codeRef); - CFRelease(codeRef); - + NSDictionary* entitlements = dumpEntitlements(codeRef); + CFRelease(codeRef); + if(!entitlements) { NSLog(@"app main binary has no entitlements, signing app with fallback entitlements...");