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