├── .gitignore ├── AppSyncUnified-FrontBoard ├── AppSyncUnified-FrontBoard.plist ├── AppSyncUnified-FrontBoard.x └── Makefile ├── AppSyncUnified-installd ├── AppSyncUnified-installd.plist ├── AppSyncUnified-installd.x ├── Makefile ├── cdhash.h ├── cdhash.m ├── cs_blobs.h ├── dump.cpp └── dump.h ├── LICENSE ├── Makefile ├── README.md ├── appinst ├── Makefile ├── README.md ├── appinst.m ├── appinst_entitlements.plist ├── changelog-inline.html ├── control ├── karentools-build-config ├── redditpost.md ├── transitional │ └── nodelete-com.linusyang.appinst │ │ └── DEBIAN │ │ └── control ├── zip.h └── zipconf.h ├── asu_inject ├── Makefile └── asu_inject.c ├── changelog-inline.html ├── control ├── entitlements.plist ├── karentools-build-config ├── layout └── Library │ └── LaunchDaemons │ └── ai.akemi.asu_inject.plist ├── pkg-actions ├── Makefile └── pkg-actions.m ├── redditpost.md └── transitional ├── nodelete-net.angelxwind.appsync50plus └── DEBIAN │ └── control ├── nodelete-net.angelxwind.appsync60plus └── DEBIAN │ └── control ├── nodelete-net.angelxwind.appsync70plus └── DEBIAN │ └── control └── nodelete-net.angelxwind.appsyncunified └── DEBIAN └── control /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | .theos/* 3 | .theos-rootless/* 4 | debs/* 5 | *.deb 6 | *.dylib 7 | **/.theos/* 8 | **/.theos-rootless/* 9 | */.theos/* 10 | */.theos-rootless/* 11 | *.sublime-workspace 12 | _/* 13 | */obj/* 14 | **/obj/* -------------------------------------------------------------------------------- /AppSyncUnified-FrontBoard/AppSyncUnified-FrontBoard.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Filter 6 | 7 | Bundles 8 | 9 | com.apple.FrontBoard 10 | com.apple.FrontBoardServices 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /AppSyncUnified-FrontBoard/AppSyncUnified-FrontBoard.x: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #ifdef DEBUG 4 | #define LOG(LogContents, ...) NSLog((@"[AppSync Unified] [dylib-FrontBoard] [%s] [L%d] " LogContents), __FUNCTION__, __LINE__, ##__VA_ARGS__) 5 | #else 6 | #define LOG(...) 7 | #endif 8 | 9 | // Located in iOS 14.x and above's FrontBoardServices.framework 10 | %hook FBSSignatureValidationService 11 | -(NSUInteger) trustStateForApplication:(id)application { 12 | LOG(@"Original response for FBSSignatureValidationService trustStateForApplication: application == %@, retval == %lu", application, (unsigned long)%orig(application)); 13 | // Returns 8 for a trusted, valid app. 14 | // Returns 4 when showing the 「"アプリ"はもう利用できません」 message. 15 | return 8; 16 | } 17 | %end 18 | 19 | // Located in iOS 9.3.x 〜 iOS 13.x's FrontBoard.framework 20 | %hook FBApplicationTrustData 21 | -(NSUInteger) trustStateWithTrustRequiredReasons:(NSUInteger *)reasons { 22 | LOG(@"Original response for FBApplicationTrustData trustStateWithTrustRequiredReasons: reasons == %lu, retval == %lu", (unsigned long)reasons, (unsigned long)%orig(reasons)); 23 | // Returns 2 for a trusted, valid app. 24 | return 2; 25 | } 26 | 27 | -(NSUInteger) trustState { 28 | LOG(@"Original response for FBApplicationTrustData trustState: retval == %lu", (unsigned long)%orig()); 29 | return 2; 30 | } 31 | %end 32 | 33 | %ctor { 34 | LOG(@"kCFCoreFoundationVersionNumber = %f", kCFCoreFoundationVersionNumber); 35 | } 36 | -------------------------------------------------------------------------------- /AppSyncUnified-FrontBoard/Makefile: -------------------------------------------------------------------------------- 1 | include $(THEOS)/makefiles/common.mk 2 | 3 | TWEAK_NAME = AppSyncUnified-FrontBoard 4 | 5 | AppSyncUnified-FrontBoard_FILES = AppSyncUnified-FrontBoard.x 6 | AppSyncUnified-FrontBoard_CFLAGS = -fobjc-arc 7 | 8 | include $(THEOS_MAKE_PATH)/tweak.mk 9 | -------------------------------------------------------------------------------- /AppSyncUnified-installd/AppSyncUnified-installd.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Filter 6 | 7 | Executables 8 | 9 | installd 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /AppSyncUnified-installd/AppSyncUnified-installd.x: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | #import "cdhash.h" 6 | #import "dump.h" 7 | 8 | // Minimal Cydia Substrate header 9 | typedef const void *MSImageRef; 10 | MSImageRef MSGetImageByName(const char *file); 11 | void *MSFindSymbol(MSImageRef image, const char *name); 12 | void MSHookFunction(void *symbol, void *replace, void **result); 13 | 14 | #define DPKG_PATH ROOT_PATH("/var/lib/dpkg/info/ai.akemi.appsyncunified.list") 15 | 16 | // Undefine LOG here since my version of cdhash.h defined it earlier 17 | #undef LOG 18 | #ifdef DEBUG 19 | #define LOG(LogContents, ...) NSLog((@"[AppSync Unified] [dylib-installd] [%s] [L%d] " LogContents), __FUNCTION__, __LINE__, ##__VA_ARGS__) 20 | #else 21 | #define LOG(...) 22 | #endif 23 | 24 | #define DECL_FUNC(name, ret, ...) \ 25 | static ret (*original_ ## name)(__VA_ARGS__); \ 26 | ret custom_ ## name(__VA_ARGS__) 27 | #define HOOK_FUNC(name, image) do { \ 28 | void *_ ## name = MSFindSymbol(image, "_" #name); \ 29 | if (_ ## name == NULL) { \ 30 | LOG(@"Failed to load symbol: " #name "."); \ 31 | return; \ 32 | } \ 33 | MSHookFunction(_ ## name, (void *) custom_ ## name, (void **) &original_ ## name); \ 34 | } while(0) 35 | #define LOAD_IMAGE(image, path) do { \ 36 | image = MSGetImageByName(path); \ 37 | if (image == NULL) { \ 38 | LOG(@"Failed to load image: " #image "."); \ 39 | return; \ 40 | } \ 41 | } while (0) 42 | 43 | static const uint8_t kSecSigningCertificate[] = {0x30, 0x82, 0x03, 0x83, 0x30, 0x82, 0x02, 0x6B, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x1E, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x79, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0A, 0x41, 0x70, 0x70, 0x6C, 0x65, 0x20, 0x49, 0x6E, 0x63, 0x2E, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x1D, 0x41, 0x70, 0x70, 0x6C, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x31, 0x2D, 0x30, 0x2B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x41, 0x70, 0x70, 0x6C, 0x65, 0x20, 0x69, 0x50, 0x68, 0x6F, 0x6E, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1E, 0x17, 0x0D, 0x30, 0x38, 0x30, 0x35, 0x32, 0x31, 0x30, 0x32, 0x30, 0x34, 0x31, 0x35, 0x5A, 0x17, 0x0D, 0x32, 0x30, 0x30, 0x35, 0x32, 0x31, 0x30, 0x32, 0x30, 0x34, 0x31, 0x35, 0x5A, 0x30, 0x50, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0A, 0x41, 0x70, 0x70, 0x6C, 0x65, 0x20, 0x49, 0x6E, 0x63, 0x2E, 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x41, 0x70, 0x70, 0x6C, 0x65, 0x20, 0x69, 0x50, 0x68, 0x6F, 0x6E, 0x65, 0x20, 0x4F, 0x53, 0x20, 0x41, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x53, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x30, 0x81, 0x9F, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8D, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xB1, 0x1D, 0x55, 0x38, 0xAE, 0xEF, 0xF6, 0x30, 0xA5, 0x9B, 0x65, 0xAE, 0x79, 0x36, 0x01, 0x4D, 0x48, 0x02, 0x6E, 0x71, 0xB8, 0x67, 0xD2, 0xF8, 0x53, 0xF5, 0xD8, 0xB9, 0x27, 0xBD, 0xAD, 0x4B, 0xF7, 0x44, 0xF3, 0x5D, 0xD6, 0x83, 0x62, 0x31, 0x71, 0x20, 0x1D, 0xBE, 0x02, 0x91, 0x11, 0x42, 0xED, 0xD9, 0xCC, 0x29, 0xD8, 0x31, 0xE8, 0x60, 0x07, 0x1B, 0x07, 0x97, 0x74, 0x7F, 0xFA, 0x1D, 0x89, 0xDE, 0x85, 0x4B, 0xD5, 0x1F, 0xA4, 0xFE, 0x28, 0x2D, 0xD3, 0x29, 0x6E, 0xD4, 0x3F, 0xEB, 0x10, 0x99, 0x33, 0x11, 0x8C, 0xD4, 0xD4, 0x32, 0x15, 0xEE, 0xDF, 0xB3, 0x58, 0x2C, 0x29, 0x6C, 0x79, 0x48, 0x41, 0xAE, 0x0C, 0xDF, 0xE6, 0x8A, 0x2C, 0x2B, 0xA5, 0xE9, 0x1E, 0xD8, 0xB6, 0x71, 0xA2, 0xAB, 0x11, 0x28, 0x48, 0x72, 0xC5, 0xE3, 0x35, 0xA5, 0x0C, 0xDF, 0xE7, 0xAC, 0x44, 0x87, 0x02, 0x03, 0x01, 0x00, 0x01, 0xA3, 0x81, 0xC2, 0x30, 0x81, 0xBF, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x0C, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01, 0x01, 0xFF, 0x04, 0x02, 0x30, 0x00, 0x30, 0x16, 0x06, 0x03, 0x55, 0x1D, 0x25, 0x01, 0x01, 0xFF, 0x04, 0x0C, 0x30, 0x0A, 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03, 0x30, 0x10, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x63, 0x64, 0x06, 0x01, 0x03, 0x04, 0x02, 0x05, 0x00, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0x29, 0x74, 0x91, 0xAC, 0x21, 0xD9, 0xCD, 0xA4, 0xBD, 0x78, 0xF0, 0x8A, 0x46, 0xF9, 0x0A, 0xB4, 0x6E, 0x06, 0xAC, 0x09, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x1D, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xE7, 0x34, 0x2A, 0x2E, 0x22, 0xDE, 0x39, 0x60, 0x6B, 0xB4, 0x94, 0xCE, 0x77, 0x83, 0x61, 0x2F, 0x31, 0xA0, 0x7C, 0x35, 0x30, 0x38, 0x06, 0x03, 0x55, 0x1D, 0x1F, 0x04, 0x31, 0x30, 0x2F, 0x30, 0x2D, 0xA0, 0x2B, 0xA0, 0x29, 0x86, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 0x61, 0x70, 0x70, 0x6C, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x61, 0x70, 0x70, 0x6C, 0x65, 0x63, 0x61, 0x2F, 0x69, 0x70, 0x68, 0x6F, 0x6E, 0x65, 0x2E, 0x63, 0x72, 0x6C, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x8C, 0xEC, 0xB5, 0x3E, 0x50, 0x80, 0xCC, 0x0D, 0xF5, 0x1D, 0x2A, 0x24, 0x38, 0x1D, 0x60, 0xED, 0x32, 0x8E, 0xB2, 0x78, 0xBB, 0x73, 0x97, 0xF5, 0x90, 0x61, 0x4C, 0x35, 0xF2, 0x95, 0xDA, 0xB7, 0x97, 0xD3, 0x75, 0x4C, 0x05, 0xBE, 0xEE, 0xE3, 0xD1, 0x66, 0xF2, 0x36, 0xE8, 0xF1, 0xAD, 0x60, 0xDF, 0x92, 0x48, 0x6C, 0xD1, 0xC3, 0x95, 0x57, 0x22, 0x1F, 0xDC, 0x74, 0x3B, 0x36, 0xD6, 0xC9, 0x49, 0x43, 0xD0, 0x74, 0x9B, 0x74, 0xF3, 0xFD, 0xC8, 0x8E, 0x07, 0x79, 0x7B, 0x5C, 0xE0, 0x4B, 0x74, 0xB2, 0xBE, 0x05, 0xFE, 0x43, 0x68, 0xA2, 0x30, 0x04, 0xCC, 0x5B, 0x4B, 0x78, 0xB3, 0x08, 0x26, 0x3B, 0x28, 0x47, 0xCE, 0xF6, 0x59, 0xAB, 0xCC, 0x10, 0xE1, 0xBB, 0x55, 0x3C, 0x67, 0x55, 0x73, 0x98, 0xF2, 0x6E, 0xFE, 0x51, 0x80, 0xE7, 0x71, 0x54, 0xAF, 0x88, 0xE8, 0xDB, 0xE9, 0x73, 0xA9, 0x66, 0x17, 0x79, 0x70, 0x1B, 0x1C, 0xAB, 0x24, 0x74, 0x08, 0x20, 0x46, 0xC5, 0x99, 0x30, 0x3E, 0x13, 0x9A, 0x60, 0x9F, 0x08, 0x5B, 0xCC, 0x01, 0x26, 0xFA, 0x93, 0x6B, 0x72, 0xC7, 0xB6, 0xEC, 0x7E, 0x3B, 0x77, 0xE3, 0xEB, 0x85, 0x53, 0x82, 0x4B, 0xF7, 0x11, 0xF7, 0x5F, 0x7F, 0x1D, 0xDA, 0xA7, 0xFE, 0x24, 0xF5, 0x41, 0x7D, 0x10, 0xF1, 0xBF, 0xA6, 0x90, 0x86, 0xC8, 0x59, 0x98, 0xAF, 0x41, 0xFA, 0x91, 0x24, 0x7C, 0x2C, 0x38, 0x40, 0x97, 0xA2, 0xE8, 0x4F, 0x7A, 0xCD, 0x1A, 0xAD, 0x6F, 0xC0, 0x12, 0x1D, 0xA7, 0x59, 0xE5, 0xF5, 0x27, 0xF2, 0x00, 0x5C, 0xF0, 0xB6, 0x8F, 0x0E, 0xFB, 0xCE, 0x69, 0xAA, 0x1F, 0x21, 0x6A, 0xD8, 0xC7, 0x79, 0x1B, 0x4F, 0x1A, 0xB2, 0xC6, 0xC5, 0x9C, 0xEF, 0x11, 0x3E, 0x7B, 0xB1, 0xB7, 0x7E, 0xE8, 0x8C, 0xE0, 0xD1, 0xFE, 0x6D, 0x32}; 44 | #define kSecSigningCertificateLength sizeof(kSecSigningCertificate) 45 | #define kSecSubjectCStr "Apple iPhone OS Application Signing" 46 | 47 | static void copyIdentifierAndEntitlements(NSString *path, NSString **identifier, NSDictionary **info) { 48 | if (path == nil || identifier == NULL || info == NULL) { 49 | LOG(@"copyIdentifierAndEntitlements: All arguments are NULL, returning."); 50 | return; 51 | } 52 | 53 | LOG(@"Bundle path == %@", path); 54 | NSBundle *bundle = [NSBundle bundleWithPath:path]; 55 | 56 | NSString *bundleIdentifier = [bundle bundleIdentifier]; 57 | if (bundleIdentifier != nil) { 58 | *identifier = [[NSString alloc] initWithString:bundleIdentifier]; 59 | LOG(@"Bundle ID == %@", bundleIdentifier); 60 | } 61 | 62 | NSString *executablePath = [bundle executablePath]; 63 | NSArray *paths = [executablePath pathComponents]; 64 | if (paths.count > 0 && [paths.lastObject isEqualToString:@"Cydia"]) { 65 | NSMutableArray *newPaths = [NSMutableArray arrayWithArray:paths]; 66 | newPaths[newPaths.count - 1] = @"MobileCydia"; 67 | executablePath = [NSString pathWithComponents:newPaths]; 68 | } 69 | LOG(@"Bundle executable path == %@", executablePath); 70 | 71 | NSMutableData *data = [NSMutableData data]; 72 | int ret = copyEntitlementDataFromFile(executablePath.UTF8String, (CFMutableDataRef) data); 73 | if (ret == kCopyEntSuccess) { 74 | NSError *error; 75 | NSDictionary *plist = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListImmutable format:NULL error:&error]; 76 | NSMutableDictionary *mutableInfo = [[NSMutableDictionary alloc] initWithDictionary:plist]; 77 | if ([mutableInfo objectForKey:@"application-identifier"] == nil) { 78 | if ([mutableInfo objectForKey:@"com.apple.developer.team-identifier"] != nil) { 79 | [mutableInfo setObject:[NSString stringWithFormat:@"%@.%@", [mutableInfo objectForKey:@"com.apple.developer.team-identifier"], bundleIdentifier] forKey:@"application-identifier"]; 80 | } else { 81 | [mutableInfo setObject:bundleIdentifier forKey:@"application-identifier"]; 82 | } 83 | } 84 | *info = [mutableInfo copy]; 85 | } else { 86 | LOG(@"Failed to fetch entitlements: %@", (NSString *) entErrorString(ret)); 87 | } 88 | } 89 | 90 | DECL_FUNC(SecCertificateCreateWithData, SecCertificateRef, CFAllocatorRef allocator, CFDataRef data) { 91 | SecCertificateRef result = original_SecCertificateCreateWithData(allocator, data); 92 | LOG(@"Original SecCertificateRef == %@", (NSData *)result); 93 | if (result == NULL) { 94 | CFDataRef dataRef = CFDataCreate(NULL, kSecSigningCertificate, kSecSigningCertificateLength); 95 | LOG(@"Generated SecCertificateRef == %@", (NSData *)dataRef); 96 | if (data != NULL && CFEqual(dataRef, data)) { 97 | result = (SecCertificateRef)dataRef; 98 | } else { 99 | CFRelease(dataRef); 100 | } 101 | } 102 | return result; 103 | } 104 | 105 | DECL_FUNC(SecCertificateCopySubjectSummary, CFStringRef, SecCertificateRef certificate) { 106 | if (CFGetTypeID(certificate) == CFDataGetTypeID()) { 107 | return CFStringCreateWithCString(NULL, kSecSubjectCStr, kCFStringEncodingUTF8); 108 | } 109 | CFStringRef result = original_SecCertificateCopySubjectSummary(certificate); 110 | return result; 111 | } 112 | 113 | static uintptr_t ASU_MISValidateSignatureAndCopyInfo(NSString *path, NSDictionary **info) { 114 | if (access(DPKG_PATH, F_OK) == -1) { 115 | printf("You seem to have installed AppSync Unified from an APT repository that is not cydia.akemi.ai.\n"); 116 | printf("Please make sure that you download AppSync Unified from the official repository to ensure proper operation.\n"); 117 | } 118 | 119 | if (info == NULL) { 120 | LOG(@"Signing information is NULL."); 121 | } else if (*info == nil) { 122 | LOG(@"Signing information is nil, generating and returning fake signing information…"); 123 | if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) { 124 | static dispatch_once_t onceToken; 125 | dispatch_once(&onceToken, ^{ 126 | MSImageRef imageSec; 127 | LOG(@"Loading and injecting into Security.framework…"); 128 | LOAD_IMAGE(imageSec, "/System/Library/Frameworks/Security.framework/Security"); 129 | LOG(@"Hooking SecCertificateCreateWithData…"); 130 | HOOK_FUNC(SecCertificateCreateWithData, imageSec); 131 | LOG(@"Hooking SecCertificateCopySubjectSummary…"); 132 | HOOK_FUNC(SecCertificateCopySubjectSummary, imageSec); 133 | }); 134 | 135 | NSMutableDictionary *fakeInfo = [[NSMutableDictionary alloc] init]; 136 | NSDictionary *entitlements = nil; 137 | NSString *identifier = nil; 138 | 139 | copyIdentifierAndEntitlements(path, &identifier, &entitlements); 140 | 141 | if (entitlements != nil) { 142 | [fakeInfo setObject:entitlements forKey:@"Entitlements"]; 143 | if ([[fakeInfo objectForKey:@"Entitlements"] objectForKey:@"com.apple.developer.team-identifier"] != nil) { 144 | [fakeInfo setObject:[[fakeInfo objectForKey:@"Entitlements"] objectForKey:@"com.apple.developer.team-identifier"] forKey:@"TeamID"]; 145 | } 146 | [entitlements release]; 147 | } 148 | if (identifier != nil) { 149 | [fakeInfo setObject:identifier forKey:@"SigningID"]; 150 | [identifier release]; 151 | } 152 | 153 | amfid_cdhash_t computedCdHash = {}; 154 | 155 | NSDictionary *appBundleInfoPlist = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/Info.plist", path]]; 156 | NSString *binaryPath = [NSString stringWithFormat:@"%@/%@", path, [appBundleInfoPlist objectForKey:@"CFBundleExecutable"]]; 157 | 158 | if (find_cdhash([binaryPath UTF8String], &computedCdHash)) { 159 | [fakeInfo setObject:[NSData dataWithBytes:computedCdHash length:sizeof(computedCdHash)] forKey:@"CdHash"]; 160 | } 161 | 162 | [fakeInfo setObject:[NSData dataWithBytes:kSecSigningCertificate length:kSecSigningCertificateLength] forKey:@"SignerCertificate"]; 163 | [fakeInfo setObject:[NSDate date] forKey:@"SigningTime"]; 164 | [fakeInfo setObject:[NSNumber numberWithBool:NO] forKey:@"ValidatedByProfile"]; 165 | [fakeInfo setObject:[NSNumber numberWithBool:NO] forKey:@"ValidatedByUniversalProfile"]; 166 | [fakeInfo setObject:[NSNumber numberWithBool:NO] forKey:@"ValidatedByLocalProfile"]; 167 | [fakeInfo setObject:[NSNumber numberWithInt:0x20100] forKey:@"SignatureVersion"]; 168 | 169 | // AppSync Unified treats all fakesigned apps as App Store apps. 170 | // SignerType 2 == App Store app 171 | // SignerType 3 == Developer-signed app 172 | [fakeInfo setObject:[NSNumber numberWithInt:2] forKey:@"SignerType"]; 173 | 174 | *info = fakeInfo; 175 | LOG(@"Generated fake signing information == %@", *info); 176 | } 177 | } else { 178 | LOG(@"Original signing information is valid, proceeding without generating fake signing information…"); 179 | LOG(@"Original signing information == %@", *info); 180 | } 181 | return 0; 182 | } 183 | 184 | // iOS 14 changed the name of the symbol MISValidateSignatureAndCopyInfo to MISValidateSignatureAndCopyInfoWithProgress 185 | DECL_FUNC(MISValidateSignatureAndCopyInfoWithProgress, uintptr_t, NSString *path, NSDictionary *options, NSDictionary **info, uintptr_t d) { 186 | original_MISValidateSignatureAndCopyInfoWithProgress(path, options, info, d); 187 | return ASU_MISValidateSignatureAndCopyInfo(path, info); 188 | } 189 | 190 | DECL_FUNC(MISValidateSignatureAndCopyInfo, uintptr_t, NSString *path, NSDictionary *options, NSDictionary **info) { 191 | original_MISValidateSignatureAndCopyInfo(path, options, info); 192 | return ASU_MISValidateSignatureAndCopyInfo(path, info); 193 | } 194 | 195 | %ctor { 196 | @autoreleasepool { 197 | LOG(@"kCFCoreFoundationVersionNumber = %f", kCFCoreFoundationVersionNumber); 198 | MSImageRef image; 199 | LOG(@"Loading and injecting into libmis.dylib…"); 200 | LOAD_IMAGE(image, "/usr/lib/libmis.dylib"); 201 | do { 202 | void *_MISValidateSignatureAndCopyInfoWithProgress = MSFindSymbol(image, "_MISValidateSignatureAndCopyInfoWithProgress"); 203 | if (_MISValidateSignatureAndCopyInfoWithProgress == NULL) { 204 | // I contemplated adding a version check here, but it's unnecessary with the way I implemented this logic. 205 | // It's cleaner this way anyway — juuuuust in case there exists some bizarre version of iOS 13 or whatever that uses MISValidateSignatureAndCopyInfoWithProgress for some reason wwww 206 | LOG(@"Failed to find symbol: MISValidateSignatureAndCopyInfoWithProgress."); 207 | LOG(@"Now attempting to find MISValidateSignatureAndCopyInfo instead."); 208 | void *_MISValidateSignatureAndCopyInfo = MSFindSymbol(image, "_MISValidateSignatureAndCopyInfo"); 209 | if (_MISValidateSignatureAndCopyInfo == NULL) { 210 | LOG(@"Failed to find symbol: MISValidateSignatureAndCopyInfo."); 211 | return; 212 | } else { 213 | LOG(@"Hooking MISValidateSignatureAndCopyInfo…"); 214 | MSHookFunction(_MISValidateSignatureAndCopyInfo, (void *)custom_MISValidateSignatureAndCopyInfo, (void **)&original_MISValidateSignatureAndCopyInfo); 215 | } 216 | } else { 217 | LOG(@"Hooking MISValidateSignatureAndCopyInfoWithProgress…"); 218 | MSHookFunction(_MISValidateSignatureAndCopyInfoWithProgress, (void *)custom_MISValidateSignatureAndCopyInfoWithProgress, (void **)&original_MISValidateSignatureAndCopyInfoWithProgress); 219 | } 220 | } while(0); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /AppSyncUnified-installd/Makefile: -------------------------------------------------------------------------------- 1 | include $(THEOS)/makefiles/common.mk 2 | 3 | TWEAK_NAME = AppSyncUnified-installd 4 | 5 | AppSyncUnified-installd_FILES = AppSyncUnified-installd.x dump.cpp cdhash.m 6 | AppSyncUnified-installd_CFLAGS += -fvisibility=hidden -Wno-unused-private-field 7 | AppSyncUnified-installd_LIBRARIES = substrate 8 | 9 | include $(THEOS_MAKE_PATH)/tweak.mk 10 | -------------------------------------------------------------------------------- /AppSyncUnified-installd/cdhash.h: -------------------------------------------------------------------------------- 1 | // Heavily modified version of: https://github.com/bazad/blanket/blob/master/amfidupe/cdhash.h 2 | 3 | #ifndef BLANKET__AMFID__CDHASH_H_ 4 | #define BLANKET__AMFID__CDHASH_H_ 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "cs_blobs.h" 13 | 14 | /* 15 | * compute_cdhash 16 | * 17 | * Description: 18 | * Compute the cdhash of a Mach-O file. 19 | * 20 | * Parameters: 21 | * file The contents of the Mach-O file. 22 | * size The size of the Mach-O file. 23 | * cdhash out On return, contains the cdhash of the file. Must be 24 | * CS_CDHASH_LEN bytes. 25 | */ 26 | typedef uint8_t amfid_cdhash_t[CS_CDHASH_LEN]; 27 | 28 | bool compute_cdhash(const void *file, size_t size, void *cdhash); 29 | bool find_cdhash(const char *path, amfid_cdhash_t *cdhash); 30 | 31 | #ifdef DEBUG 32 | #define LOG(LogContents, ...) NSLog((@"[AppSync Unified] [cdhash] [%s] [L%d] " LogContents), __FUNCTION__, __LINE__, ##__VA_ARGS__) 33 | #else 34 | #define LOG(...) 35 | #endif 36 | #define DEBUG_TRACE(level, fmt, ...) LOG(@ fmt, ##__VA_ARGS__); 37 | #define INFO(fmt, ...) LOG(@ fmt, ##__VA_ARGS__) 38 | #define WARNING(fmt, ...) LOG(@ fmt, ##__VA_ARGS__) 39 | #define ERROR(fmt, ...) LOG(@ fmt, ##__VA_ARGS__) 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /AppSyncUnified-installd/cdhash.m: -------------------------------------------------------------------------------- 1 | // Heavily modified version of: https://github.com/bazad/blanket/blob/master/amfidupe/cdhash.c 2 | 3 | /* 4 | * Cdhash computation 5 | * ------------------ 6 | * 7 | * The amfid patch needs to be able to compute the cdhash of a binary. 8 | * This code is heavily based on the implementation in Ian Beer's triple_fetch project [1] and on 9 | * the source of XNU [2]. 10 | * 11 | * [1]: https://bugs.chromium.org/p/project-zero/issues/detail?id=1247 12 | * [2]: https://opensource.apple.com/source/xnu/xnu-4570.41.2/bsd/kern/ubc_subr.c.auto.html 13 | * 14 | */ 15 | #include "cdhash.h" 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #ifndef __LP64__ 27 | #define mach_header_host mach_header 28 | #else 29 | #define mach_header_host mach_header_64 30 | #endif 31 | 32 | // Check whether the file looks like a Mach-O file. 33 | static bool 34 | macho_identify(const struct mach_header_host *mh, size_t size) { 35 | // Check the file size and magic. 36 | if (size < 0x1000 || mh->magic != MH_MAGIC_64) { 37 | return false; 38 | } 39 | return true; 40 | } 41 | 42 | // Perform some basic validation on the Mach-O header. This is NOT enough to be sure that the 43 | // Mach-O is safe! 44 | static bool 45 | macho_validate(const struct mach_header_host *mh, size_t size) { 46 | if (!macho_identify(mh, size)) { 47 | return false; 48 | } 49 | // Check that the load commands fit in the file. 50 | if (mh->sizeofcmds > size) { 51 | return false; 52 | } 53 | // Check that each load command fits in the header. 54 | const uint8_t *lc_p = (const uint8_t *)(mh + 1); 55 | const uint8_t *lc_end = lc_p + mh->sizeofcmds; 56 | while (lc_p < lc_end) { 57 | const struct load_command *lc = (const struct load_command *)lc_p; 58 | if (lc->cmdsize >= 0x80000000) { 59 | return false; 60 | } 61 | const uint8_t *lc_next = lc_p + lc->cmdsize; 62 | if (lc_next > lc_end) { 63 | return false; 64 | } 65 | lc_p = lc_next; 66 | } 67 | return true; 68 | } 69 | 70 | // Get the next load command in a Mach-O file. 71 | static const void * 72 | macho_next_load_command(const struct mach_header_host *mh, size_t size, const void *lc) { 73 | const struct load_command *next = lc; 74 | if (next == NULL) { 75 | next = (const struct load_command *)(mh + 1); 76 | } else { 77 | next = (const struct load_command *)((uint8_t *)next + next->cmdsize); 78 | } 79 | if ((uintptr_t)next >= (uintptr_t)(mh + 1) + mh->sizeofcmds) { 80 | next = NULL; 81 | } 82 | return next; 83 | } 84 | 85 | // Find the next load command in a Mach-O file matching the given type. 86 | static const void * 87 | macho_find_load_command(const struct mach_header_host *mh, size_t size, 88 | uint32_t command, const void *lc) { 89 | const struct load_command *loadcmd = lc; 90 | for (;;) { 91 | loadcmd = macho_next_load_command(mh, size, loadcmd); 92 | if (loadcmd == NULL || loadcmd->cmd == command) { 93 | return loadcmd; 94 | } 95 | } 96 | } 97 | 98 | // Validate a CS_CodeDirectory and return its true length. 99 | static size_t 100 | cs_codedirectory_validate(CS_CodeDirectory *cd, size_t size) { 101 | // Make sure we at least have a CS_CodeDirectory. There's an end_earliest parameter, but 102 | // XNU doesn't seem to use it in cs_validate_codedirectory(). 103 | if (size < sizeof(*cd)) { 104 | ERROR("CS_CodeDirectory is too small"); 105 | return 0; 106 | } 107 | // Validate the magic. 108 | uint32_t magic = ntohl(cd->magic); 109 | if (magic != CSMAGIC_CODEDIRECTORY) { 110 | ERROR("CS_CodeDirectory has incorrect magic"); 111 | return 0; 112 | } 113 | // Validate the length. 114 | uint32_t length = ntohl(cd->length); 115 | if (length > size) { 116 | ERROR("CS_CodeDirectory has invalid length"); 117 | return 0; 118 | } 119 | return length; 120 | } 121 | 122 | // Validate a CS_SuperBlob and return its true length. 123 | static size_t 124 | cs_superblob_validate(CS_SuperBlob *sb, size_t size) { 125 | // Make sure we at least have a CS_SuperBlob. 126 | if (size < sizeof(*sb)) { 127 | ERROR("CS_SuperBlob is too small"); 128 | return 0; 129 | } 130 | // Validate the magic. 131 | uint32_t magic = ntohl(sb->magic); 132 | if (magic != CSMAGIC_EMBEDDED_SIGNATURE) { 133 | ERROR("CS_SuperBlob has incorrect magic"); 134 | return 0; 135 | } 136 | // Validate the length. 137 | uint32_t length = ntohl(sb->length); 138 | if (length > size) { 139 | ERROR("CS_SuperBlob has invalid length"); 140 | return 0; 141 | } 142 | uint32_t count = ntohl(sb->count); 143 | // Validate the count. 144 | CS_BlobIndex *index = &sb->index[count]; 145 | if (count >= 0x10000 || (uintptr_t)index > (uintptr_t)sb + size) { 146 | ERROR("CS_SuperBlob has invalid count"); 147 | return 0; 148 | } 149 | return length; 150 | } 151 | 152 | // Compute the cdhash of a code directory using SHA1. 153 | static void 154 | cdhash_sha1(CS_CodeDirectory *cd, size_t length, void *cdhash) { 155 | uint8_t digest[CC_SHA1_DIGEST_LENGTH]; 156 | CC_SHA1(cd, (CC_LONG) length, digest); 157 | memcpy(cdhash, digest, CS_CDHASH_LEN); 158 | } 159 | 160 | // Compute the cdhash of a code directory using SHA256. 161 | static void 162 | cdhash_sha256(CS_CodeDirectory *cd, size_t length, void *cdhash) { 163 | uint8_t digest[CC_SHA256_DIGEST_LENGTH]; 164 | CC_SHA256(cd, (CC_LONG) length, digest); 165 | memcpy(cdhash, digest, CS_CDHASH_LEN); 166 | } 167 | 168 | // Compute the cdhash from a CS_CodeDirectory. 169 | static bool 170 | cs_codedirectory_cdhash(CS_CodeDirectory *cd, size_t size, void *cdhash) { 171 | size_t length = ntohl(cd->length); 172 | switch (cd->hashType) { 173 | case CS_HASHTYPE_SHA1: 174 | DEBUG_TRACE(2, "Using SHA1"); 175 | cdhash_sha1(cd, length, cdhash); 176 | return true; 177 | case CS_HASHTYPE_SHA256: 178 | DEBUG_TRACE(2, "Using SHA256"); 179 | cdhash_sha256(cd, length, cdhash); 180 | return true; 181 | } 182 | ERROR("Unsupported hash type %d", cd->hashType); 183 | return false; 184 | } 185 | 186 | // Get the rank of a code directory. 187 | static unsigned 188 | cs_codedirectory_rank(CS_CodeDirectory *cd) { 189 | // The supported hash types, ranked from least to most preferred. From XNU's 190 | // bsd/kern/ubc_subr.c. 191 | static uint32_t ranked_hash_types[] = { 192 | CS_HASHTYPE_SHA1, 193 | CS_HASHTYPE_SHA256_TRUNCATED, 194 | CS_HASHTYPE_SHA256, 195 | CS_HASHTYPE_SHA384, 196 | }; 197 | // Define the rank of the code directory as its index in the array plus one. 198 | for (unsigned i = 0; i < sizeof(ranked_hash_types) / sizeof(ranked_hash_types[0]); i++) { 199 | if (ranked_hash_types[i] == cd->hashType) { 200 | return (i + 1); 201 | } 202 | } 203 | return 0; 204 | } 205 | 206 | // Compute the cdhash from a CS_SuperBlob. 207 | static bool 208 | cs_superblob_cdhash(CS_SuperBlob *sb, size_t size, void *cdhash) { 209 | // Iterate through each index searching for the best code directory. 210 | CS_CodeDirectory *best_cd = NULL; 211 | unsigned best_cd_rank = 0; 212 | size_t best_cd_size = 0; 213 | uint32_t count = ntohl(sb->count); 214 | for (size_t i = 0; i < count; i++) { 215 | CS_BlobIndex *index = &sb->index[i]; 216 | uint32_t type = ntohl(index->type); 217 | uint32_t offset = ntohl(index->offset); 218 | // Validate the offset. 219 | if (offset > size) { 220 | ERROR("CS_SuperBlob has out-of-bounds CS_BlobIndex"); 221 | return false; 222 | } 223 | // Look for a code directory. 224 | if (type == CSSLOT_CODEDIRECTORY || 225 | (CSSLOT_ALTERNATE_CODEDIRECTORIES <= type && type < CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT)) { 226 | CS_CodeDirectory *cd = (CS_CodeDirectory *)((uint8_t *)sb + offset); 227 | size_t cd_size = cs_codedirectory_validate(cd, size - offset); 228 | if (cd_size == 0) { 229 | return false; 230 | } 231 | DEBUG_TRACE(2, "CS_CodeDirectory { hashType = %u }", cd->hashType); 232 | // Rank the code directory to see if it's better than our previous best. 233 | unsigned cd_rank = cs_codedirectory_rank(cd); 234 | if (cd_rank > best_cd_rank) { 235 | best_cd = cd; 236 | best_cd_rank = cd_rank; 237 | best_cd_size = cd_size; 238 | } 239 | } 240 | } 241 | // If we didn't find a code directory, error. 242 | if (best_cd == NULL) { 243 | ERROR("CS_SuperBlob does not have a code directory"); 244 | return false; 245 | } 246 | // Hash the code directory. 247 | return cs_codedirectory_cdhash(best_cd, best_cd_size, cdhash); 248 | } 249 | 250 | // Compute the cdhash from a csblob. 251 | static bool 252 | csblob_cdhash(CS_GenericBlob *blob, size_t size, void *cdhash) { 253 | // Make sure we at least have a CS_GenericBlob. 254 | if (size < sizeof(*blob)) { 255 | ERROR("CSBlob is too small"); 256 | return false; 257 | } 258 | uint32_t magic = ntohl(blob->magic); 259 | uint32_t length = ntohl(blob->length); 260 | DEBUG_TRACE(2, "CS_GenericBlob { %08x, %u }, size = %zu", magic, length, size); 261 | // Make sure the length is sensible. 262 | if (length > size) { 263 | ERROR("CSBlob has invalid length"); 264 | return false; 265 | } 266 | // Handle the blob. 267 | bool ok; 268 | switch (magic) { 269 | case CSMAGIC_EMBEDDED_SIGNATURE: 270 | ok = cs_superblob_validate((CS_SuperBlob *)blob, length); 271 | if (!ok) { 272 | return false; 273 | } 274 | return cs_superblob_cdhash((CS_SuperBlob *)blob, length, cdhash); 275 | case CSMAGIC_CODEDIRECTORY: 276 | ok = cs_codedirectory_validate((CS_CodeDirectory *)blob, length); 277 | if (!ok) { 278 | return false; 279 | } 280 | return cs_codedirectory_cdhash((CS_CodeDirectory *)blob, length, cdhash); 281 | } 282 | ERROR("Unrecognized CSBlob magic 0x%08x", magic); 283 | return false; 284 | } 285 | 286 | // Compute the cdhash for a Mach-O file. 287 | static bool 288 | compute_cdhash_macho(const struct mach_header_host *mh, size_t size, void *cdhash) { 289 | // Find the code signature command. 290 | const struct linkedit_data_command *cs_cmd = 291 | macho_find_load_command(mh, size, LC_CODE_SIGNATURE, NULL); 292 | if (cs_cmd == NULL) { 293 | ERROR("No code signature"); 294 | return false; 295 | } 296 | // Check that the code signature is in-bounds. 297 | const uint8_t *cs_data = (const uint8_t *)mh + cs_cmd->dataoff; 298 | const uint8_t *cs_end = cs_data + cs_cmd->datasize; 299 | if (!((uint8_t *)mh < cs_data && cs_data < cs_end && cs_end <= (uint8_t *)mh + size)) { 300 | ERROR("Invalid code signature"); 301 | return false; 302 | } 303 | // Check that the code signature data looks correct. 304 | return csblob_cdhash((CS_GenericBlob *)cs_data, cs_end - cs_data, cdhash); 305 | } 306 | 307 | bool 308 | compute_cdhash(const void *file, size_t size, void *cdhash) { 309 | // Try to compute the cdhash for a Mach-O file. 310 | const struct mach_header_host *mh = file; 311 | 312 | uint32_t fileOffset = 0; 313 | uint32_t magic = *(uint32_t*)file; 314 | if (ntohl(magic) == FAT_MAGIC) { 315 | INFO("Found a fat header!\n"); 316 | 317 | // Get the cputype and cpusubtype of the mach_portal binary 318 | struct mach_header_host *mainMachHeader = (struct mach_header_host *)_dyld_get_image_header(0); 319 | cpu_type_t mainCpuType = mainMachHeader->cputype & ~CPU_ARCH_MASK; 320 | cpu_type_t mainCpuSubType = mainMachHeader->cpusubtype & ~CPU_SUBTYPE_MASK; 321 | 322 | struct fat_header *fatHeader = (struct fat_header *)file; 323 | struct fat_arch *fatArch = (struct fat_arch *)(file + sizeof(struct fat_header)); 324 | for (int i = 0; i < ntohl(fatHeader->nfat_arch); i++, fatArch++) { 325 | cpu_type_t cpuType = ntohl(fatArch->cputype) & ~CPU_ARCH_MASK; 326 | cpu_subtype_t cpuSubType = ntohl(fatArch->cpusubtype) & ~CPU_SUBTYPE_MASK; 327 | if (cpuType == mainCpuType && cpuSubType == mainCpuSubType) { 328 | fileOffset = ntohl(fatArch->offset); 329 | INFO("arm64 arch offset is %u\n", fileOffset); 330 | fatHeader++; 331 | break; 332 | } 333 | } 334 | 335 | if (fileOffset == 0) { 336 | ERROR("Arch not found in fat header…\n"); 337 | return false; 338 | } 339 | 340 | mh = (const struct mach_header_host*)((uintptr_t)file + fileOffset); 341 | } 342 | 343 | if (macho_identify(mh, size)) { 344 | if (!macho_validate(mh, size)) { 345 | ERROR("Bad Mach-O file"); 346 | return false; 347 | } 348 | return compute_cdhash_macho(mh, size, cdhash); 349 | } 350 | // What is it? 351 | ERROR("Unrecognized file format"); 352 | return false; 353 | } 354 | 355 | bool 356 | find_cdhash(const char *path, amfid_cdhash_t *cdhash) { 357 | bool success = false; 358 | size_t fileoff = 0; 359 | 360 | int fd; 361 | fd = open(path, O_RDONLY); 362 | if (fd < 0) { 363 | ERROR("Could not open \"%s\"", path); 364 | goto fail_0; 365 | } 366 | // Get the size of the file. 367 | struct stat st; 368 | int err = fstat(fd, &st); 369 | if (err != 0) { 370 | ERROR("Could not get the size of \"%s\"", path); 371 | goto fail_1; 372 | } 373 | size_t size = st.st_size; 374 | assert(fileoff < size); 375 | // Map the file into memory. 376 | DEBUG_TRACE(2, "Mapping %s size %zu offset %zu", path, size, fileoff); 377 | size -= fileoff; 378 | uint8_t *file = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, fileoff); 379 | if (file == MAP_FAILED) { 380 | ERROR("Could not map \"%s\"", path); 381 | goto fail_1; 382 | } 383 | DEBUG_TRACE(3, "file[0] = %llx", *(uint64_t *)file); 384 | // Compute the cdhash. 385 | success = compute_cdhash(file, size, cdhash); 386 | 387 | munmap(file, size); 388 | fail_1: 389 | close(fd); 390 | fail_0: 391 | return success; 392 | } 393 | -------------------------------------------------------------------------------- /AppSyncUnified-installd/cs_blobs.h: -------------------------------------------------------------------------------- 1 | // Unmodified copy of: https://opensource.apple.com/source/xnu/xnu-7195.81.3/osfmk/kern/cs_blobs.h 2 | 3 | /* 4 | * Copyright (c) 2017 Apple Computer, Inc. All rights reserved. 5 | * 6 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 7 | * 8 | * This file contains Original Code and/or Modifications of Original Code 9 | * as defined in and that are subject to the Apple Public Source License 10 | * Version 2.0 (the 'License'). You may not use this file except in 11 | * compliance with the License. The rights granted to you under the License 12 | * may not be used to create, or enable the creation or redistribution of, 13 | * unlawful or unlicensed copies of an Apple operating system, or to 14 | * circumvent, violate, or enable the circumvention or violation of, any 15 | * terms of an Apple operating system software license agreement. 16 | * 17 | * Please obtain a copy of the License at 18 | * http://www.opensource.apple.com/apsl/ and read it before using this file. 19 | * 20 | * The Original Code and all software distributed under the License are 21 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 22 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 23 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 25 | * Please see the License for the specific language governing rights and 26 | * limitations under the License. 27 | * 28 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 29 | */ 30 | 31 | #ifndef _KERN_CODESIGN_H_ 32 | #define _KERN_CODESIGN_H_ 33 | 34 | #include 35 | 36 | /* code signing attributes of a process */ 37 | #define CS_VALID 0x00000001 /* dynamically valid */ 38 | #define CS_ADHOC 0x00000002 /* ad hoc signed */ 39 | #define CS_GET_TASK_ALLOW 0x00000004 /* has get-task-allow entitlement */ 40 | #define CS_INSTALLER 0x00000008 /* has installer entitlement */ 41 | 42 | #define CS_FORCED_LV 0x00000010 /* Library Validation required by Hardened System Policy */ 43 | #define CS_INVALID_ALLOWED 0x00000020 /* (macOS Only) Page invalidation allowed by task port policy */ 44 | 45 | #define CS_HARD 0x00000100 /* don't load invalid pages */ 46 | #define CS_KILL 0x00000200 /* kill process if it becomes invalid */ 47 | #define CS_CHECK_EXPIRATION 0x00000400 /* force expiration checking */ 48 | #define CS_RESTRICT 0x00000800 /* tell dyld to treat restricted */ 49 | 50 | #define CS_ENFORCEMENT 0x00001000 /* require enforcement */ 51 | #define CS_REQUIRE_LV 0x00002000 /* require library validation */ 52 | #define CS_ENTITLEMENTS_VALIDATED 0x00004000 /* code signature permits restricted entitlements */ 53 | #define CS_NVRAM_UNRESTRICTED 0x00008000 /* has com.apple.rootless.restricted-nvram-variables.heritable entitlement */ 54 | 55 | #define CS_RUNTIME 0x00010000 /* Apply hardened runtime policies */ 56 | #define CS_LINKER_SIGNED 0x00020000 /* Automatically signed by the linker */ 57 | 58 | #define CS_ALLOWED_MACHO (CS_ADHOC | CS_HARD | CS_KILL | CS_CHECK_EXPIRATION | \ 59 | CS_RESTRICT | CS_ENFORCEMENT | CS_REQUIRE_LV | CS_RUNTIME | CS_LINKER_SIGNED) 60 | 61 | #define CS_EXEC_SET_HARD 0x00100000 /* set CS_HARD on any exec'ed process */ 62 | #define CS_EXEC_SET_KILL 0x00200000 /* set CS_KILL on any exec'ed process */ 63 | #define CS_EXEC_SET_ENFORCEMENT 0x00400000 /* set CS_ENFORCEMENT on any exec'ed process */ 64 | #define CS_EXEC_INHERIT_SIP 0x00800000 /* set CS_INSTALLER on any exec'ed process */ 65 | 66 | #define CS_KILLED 0x01000000 /* was killed by kernel for invalidity */ 67 | #define CS_DYLD_PLATFORM 0x02000000 /* dyld used to load this is a platform binary */ 68 | #define CS_PLATFORM_BINARY 0x04000000 /* this is a platform binary */ 69 | #define CS_PLATFORM_PATH 0x08000000 /* platform binary by the fact of path (osx only) */ 70 | 71 | #define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */ 72 | #define CS_SIGNED 0x20000000 /* process has a signature (may have gone invalid) */ 73 | #define CS_DEV_CODE 0x40000000 /* code is dev signed, cannot be loaded into prod signed code (will go away with rdar://problem/28322552) */ 74 | #define CS_DATAVAULT_CONTROLLER 0x80000000 /* has Data Vault controller entitlement */ 75 | 76 | #define CS_ENTITLEMENT_FLAGS (CS_GET_TASK_ALLOW | CS_INSTALLER | CS_DATAVAULT_CONTROLLER | CS_NVRAM_UNRESTRICTED) 77 | 78 | /* executable segment flags */ 79 | 80 | #define CS_EXECSEG_MAIN_BINARY 0x1 /* executable segment denotes main binary */ 81 | #define CS_EXECSEG_ALLOW_UNSIGNED 0x10 /* allow unsigned pages (for debugging) */ 82 | #define CS_EXECSEG_DEBUGGER 0x20 /* main binary is debugger */ 83 | #define CS_EXECSEG_JIT 0x40 /* JIT enabled */ 84 | #define CS_EXECSEG_SKIP_LV 0x80 /* OBSOLETE: skip library validation */ 85 | #define CS_EXECSEG_CAN_LOAD_CDHASH 0x100 /* can bless cdhash for execution */ 86 | #define CS_EXECSEG_CAN_EXEC_CDHASH 0x200 /* can execute blessed cdhash */ 87 | 88 | /* 89 | * Magic numbers used by Code Signing 90 | */ 91 | enum { 92 | CSMAGIC_REQUIREMENT = 0xfade0c00, /* single Requirement blob */ 93 | CSMAGIC_REQUIREMENTS = 0xfade0c01, /* Requirements vector (internal requirements) */ 94 | CSMAGIC_CODEDIRECTORY = 0xfade0c02, /* CodeDirectory blob */ 95 | CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, /* embedded form of signature data */ 96 | CSMAGIC_EMBEDDED_SIGNATURE_OLD = 0xfade0b02, /* XXX */ 97 | CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171, /* embedded entitlements */ 98 | CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, /* multi-arch collection of embedded signatures */ 99 | CSMAGIC_BLOBWRAPPER = 0xfade0b01, /* CMS Signature, among other things */ 100 | 101 | CS_SUPPORTSSCATTER = 0x20100, 102 | CS_SUPPORTSTEAMID = 0x20200, 103 | CS_SUPPORTSCODELIMIT64 = 0x20300, 104 | CS_SUPPORTSEXECSEG = 0x20400, 105 | CS_SUPPORTSRUNTIME = 0x20500, 106 | CS_SUPPORTSLINKAGE = 0x20600, 107 | 108 | CSSLOT_CODEDIRECTORY = 0, /* slot index for CodeDirectory */ 109 | CSSLOT_INFOSLOT = 1, 110 | CSSLOT_REQUIREMENTS = 2, 111 | CSSLOT_RESOURCEDIR = 3, 112 | CSSLOT_APPLICATION = 4, 113 | CSSLOT_ENTITLEMENTS = 5, 114 | 115 | CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000, /* first alternate CodeDirectory, if any */ 116 | CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5, /* max number of alternate CD slots */ 117 | CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT = CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX, /* one past the last */ 118 | 119 | CSSLOT_SIGNATURESLOT = 0x10000, /* CMS Signature */ 120 | CSSLOT_IDENTIFICATIONSLOT = 0x10001, 121 | CSSLOT_TICKETSLOT = 0x10002, 122 | 123 | CSTYPE_INDEX_REQUIREMENTS = 0x00000002, /* compat with amfi */ 124 | CSTYPE_INDEX_ENTITLEMENTS = 0x00000005, /* compat with amfi */ 125 | 126 | CS_HASHTYPE_SHA1 = 1, 127 | CS_HASHTYPE_SHA256 = 2, 128 | CS_HASHTYPE_SHA256_TRUNCATED = 3, 129 | CS_HASHTYPE_SHA384 = 4, 130 | 131 | CS_SHA1_LEN = 20, 132 | CS_SHA256_LEN = 32, 133 | CS_SHA256_TRUNCATED_LEN = 20, 134 | 135 | CS_CDHASH_LEN = 20, /* always - larger hashes are truncated */ 136 | CS_HASH_MAX_SIZE = 48, /* max size of the hash we'll support */ 137 | 138 | /* 139 | * Currently only to support Legacy VPN plugins, and Mac App Store 140 | * but intended to replace all the various platform code, dev code etc. bits. 141 | */ 142 | CS_SIGNER_TYPE_UNKNOWN = 0, 143 | CS_SIGNER_TYPE_LEGACYVPN = 5, 144 | CS_SIGNER_TYPE_MAC_APP_STORE = 6, 145 | 146 | CS_SUPPL_SIGNER_TYPE_UNKNOWN = 0, 147 | CS_SUPPL_SIGNER_TYPE_TRUSTCACHE = 7, 148 | CS_SUPPL_SIGNER_TYPE_LOCAL = 8, 149 | }; 150 | 151 | #define KERNEL_HAVE_CS_CODEDIRECTORY 1 152 | #define KERNEL_CS_CODEDIRECTORY_HAVE_PLATFORM 1 153 | 154 | /* 155 | * C form of a CodeDirectory. 156 | */ 157 | typedef struct __CodeDirectory { 158 | uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */ 159 | uint32_t length; /* total length of CodeDirectory blob */ 160 | uint32_t version; /* compatibility version */ 161 | uint32_t flags; /* setup and mode flags */ 162 | uint32_t hashOffset; /* offset of hash slot element at index zero */ 163 | uint32_t identOffset; /* offset of identifier string */ 164 | uint32_t nSpecialSlots; /* number of special hash slots */ 165 | uint32_t nCodeSlots; /* number of ordinary (code) hash slots */ 166 | uint32_t codeLimit; /* limit to main image signature range */ 167 | uint8_t hashSize; /* size of each hash in bytes */ 168 | uint8_t hashType; /* type of hash (cdHashType* constants) */ 169 | uint8_t platform; /* platform identifier; zero if not platform binary */ 170 | uint8_t pageSize; /* log2(page size in bytes); 0 => infinite */ 171 | uint32_t spare2; /* unused (must be zero) */ 172 | 173 | char end_earliest[0]; 174 | 175 | /* Version 0x20100 */ 176 | uint32_t scatterOffset; /* offset of optional scatter vector */ 177 | char end_withScatter[0]; 178 | 179 | /* Version 0x20200 */ 180 | uint32_t teamOffset; /* offset of optional team identifier */ 181 | char end_withTeam[0]; 182 | 183 | /* Version 0x20300 */ 184 | uint32_t spare3; /* unused (must be zero) */ 185 | uint64_t codeLimit64; /* limit to main image signature range, 64 bits */ 186 | char end_withCodeLimit64[0]; 187 | 188 | /* Version 0x20400 */ 189 | uint64_t execSegBase; /* offset of executable segment */ 190 | uint64_t execSegLimit; /* limit of executable segment */ 191 | uint64_t execSegFlags; /* executable segment flags */ 192 | char end_withExecSeg[0]; 193 | /* Version 0x20500 */ 194 | uint32_t runtime; 195 | uint32_t preEncryptOffset; 196 | char end_withPreEncryptOffset[0]; 197 | 198 | /* Version 0x20600 */ 199 | uint8_t linkageHashType; 200 | uint8_t linkageTruncated; 201 | uint16_t spare4; 202 | uint32_t linkageOffset; 203 | uint32_t linkageSize; 204 | char end_withLinkage[0]; 205 | 206 | 207 | /* followed by dynamic content as located by offset fields above */ 208 | } CS_CodeDirectory 209 | __attribute__ ((aligned(1))); 210 | 211 | /* 212 | * Structure of an embedded-signature SuperBlob 213 | */ 214 | 215 | typedef struct __BlobIndex { 216 | uint32_t type; /* type of entry */ 217 | uint32_t offset; /* offset of entry */ 218 | } CS_BlobIndex 219 | __attribute__ ((aligned(1))); 220 | 221 | typedef struct __SC_SuperBlob { 222 | uint32_t magic; /* magic number */ 223 | uint32_t length; /* total length of SuperBlob */ 224 | uint32_t count; /* number of index entries following */ 225 | CS_BlobIndex index[]; /* (count) entries */ 226 | /* followed by Blobs in no particular order as indicated by offsets in index */ 227 | } CS_SuperBlob 228 | __attribute__ ((aligned(1))); 229 | 230 | #define KERNEL_HAVE_CS_GENERICBLOB 1 231 | typedef struct __SC_GenericBlob { 232 | uint32_t magic; /* magic number */ 233 | uint32_t length; /* total length of blob */ 234 | char data[]; 235 | } CS_GenericBlob 236 | __attribute__ ((aligned(1))); 237 | 238 | typedef struct __SC_Scatter { 239 | uint32_t count; // number of pages; zero for sentinel (only) 240 | uint32_t base; // first page number 241 | uint64_t targetOffset; // offset in target 242 | uint64_t spare; // reserved 243 | } SC_Scatter 244 | __attribute__ ((aligned(1))); 245 | 246 | 247 | #endif /* _KERN_CODESIGN_H */ -------------------------------------------------------------------------------- /AppSyncUnified-installd/dump.cpp: -------------------------------------------------------------------------------- 1 | /* ldid - (Mach-O) Link-Loader Identity Editor 2 | * Copyright (C) 2007-2012 Jay Freeman (saurik) 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | **/ 17 | 18 | #include "dump.h" 19 | #include 20 | #include 21 | #include 22 | 23 | #define _packed \ 24 | __attribute__((packed)) 25 | 26 | #define _not(type) \ 27 | ((type) ~ (type) 0) 28 | 29 | #define FAT_MAGIC 0xcafebabe 30 | #define FAT_CIGAM 0xbebafeca 31 | #define MH_MAGIC 0xfeedface 32 | #define MH_CIGAM 0xcefaedfe 33 | #define MH_MAGIC_64 0xfeedfacf 34 | #define MH_CIGAM_64 0xcffaedfe 35 | #define MH_EXECUTE 0x2 36 | #define MH_DYLIB 0x6 37 | #define MH_BUNDLE 0x8 38 | #define LC_CODE_SIGNATURE uint32_t(0x1d) 39 | #define CSSLOT_ENTITLEMENTS uint32_t(5) 40 | 41 | template 42 | struct Iterator_ { 43 | typedef typename Type_::const_iterator Result; 44 | }; 45 | 46 | #define _foreach(item, list) \ 47 | for (bool _stop(true); _stop; ) \ 48 | for (const __typeof__(list) &_list = (list); _stop; _stop = false) \ 49 | for (Iterator_<__typeof__(list)>::Result _item = _list.begin(); _item != _list.end(); ++_item) \ 50 | for (bool _suck(true); _suck; _suck = false) \ 51 | for (const __typeof__(*_item) &item = *_item; _suck; _suck = false) 52 | 53 | struct fat_header { 54 | uint32_t magic; 55 | uint32_t nfat_arch; 56 | } _packed; 57 | 58 | struct fat_arch { 59 | uint32_t cputype; 60 | uint32_t cpusubtype; 61 | uint32_t offset; 62 | uint32_t size; 63 | uint32_t align; 64 | } _packed; 65 | 66 | struct mach_header { 67 | uint32_t magic; 68 | uint32_t cputype; 69 | uint32_t cpusubtype; 70 | uint32_t filetype; 71 | uint32_t ncmds; 72 | uint32_t sizeofcmds; 73 | uint32_t flags; 74 | } _packed; 75 | 76 | struct segment_command { 77 | uint32_t cmd; 78 | uint32_t cmdsize; 79 | char segname[16]; 80 | uint32_t vmaddr; 81 | uint32_t vmsize; 82 | uint32_t fileoff; 83 | uint32_t filesize; 84 | uint32_t maxprot; 85 | uint32_t initprot; 86 | uint32_t nsects; 87 | uint32_t flags; 88 | } _packed; 89 | 90 | struct segment_command_64 { 91 | uint32_t cmd; 92 | uint32_t cmdsize; 93 | char segname[16]; 94 | uint64_t vmaddr; 95 | uint64_t vmsize; 96 | uint64_t fileoff; 97 | uint64_t filesize; 98 | uint32_t maxprot; 99 | uint32_t initprot; 100 | uint32_t nsects; 101 | uint32_t flags; 102 | } _packed; 103 | 104 | struct section { 105 | char sectname[16]; 106 | char segname[16]; 107 | uint32_t addr; 108 | uint32_t size; 109 | uint32_t offset; 110 | uint32_t align; 111 | uint32_t reloff; 112 | uint32_t nreloc; 113 | uint32_t flags; 114 | uint32_t reserved1; 115 | uint32_t reserved2; 116 | } _packed; 117 | 118 | struct section_64 { 119 | char sectname[16]; 120 | char segname[16]; 121 | uint64_t addr; 122 | uint64_t size; 123 | uint32_t offset; 124 | uint32_t align; 125 | uint32_t reloff; 126 | uint32_t nreloc; 127 | uint32_t flags; 128 | uint32_t reserved1; 129 | uint32_t reserved2; 130 | } _packed; 131 | 132 | struct linkedit_data_command { 133 | uint32_t cmd; 134 | uint32_t cmdsize; 135 | uint32_t dataoff; 136 | uint32_t datasize; 137 | } _packed; 138 | 139 | struct load_command { 140 | uint32_t cmd; 141 | uint32_t cmdsize; 142 | } _packed; 143 | 144 | struct BlobIndex { 145 | uint32_t type; 146 | uint32_t offset; 147 | } _packed; 148 | 149 | struct Blob { 150 | uint32_t magic; 151 | uint32_t length; 152 | } _packed; 153 | 154 | struct SuperBlob { 155 | struct Blob blob; 156 | uint32_t count; 157 | struct BlobIndex index[]; 158 | } _packed; 159 | 160 | static uint32_t Swap_(uint32_t value) { 161 | value = ((value >> 8) & 0x00ff00ff) | 162 | ((value << 8) & 0xff00ff00); 163 | value = ((value >> 16) & 0x0000ffff) | 164 | ((value << 16) & 0xffff0000); 165 | return value; 166 | } 167 | 168 | static bool little_(true); 169 | 170 | static uint32_t Swap(uint32_t value) { 171 | return little_ ? Swap_(value) : value; 172 | } 173 | 174 | template 175 | class Pointer; 176 | 177 | class Data { 178 | private: 179 | void *base_; 180 | size_t size_; 181 | 182 | protected: 183 | bool swapped_; 184 | 185 | public: 186 | Data(void *base, size_t size) : 187 | base_(base), 188 | size_(size), 189 | swapped_(false) 190 | { 191 | } 192 | 193 | uint32_t Swap(uint32_t value) const { 194 | return swapped_ ? Swap_(value) : value; 195 | } 196 | 197 | int32_t Swap(int32_t value) const { 198 | return Swap(static_cast(value)); 199 | } 200 | 201 | void *GetBase() const { 202 | return base_; 203 | } 204 | 205 | size_t GetSize() const { 206 | return size_; 207 | } 208 | }; 209 | 210 | class MachHeader : 211 | public Data 212 | { 213 | private: 214 | bool bits64_; 215 | 216 | struct mach_header *mach_header_; 217 | struct load_command *load_command_; 218 | 219 | public: 220 | MachHeader(void *base, size_t size) : 221 | Data(base, size) 222 | { 223 | mach_header_ = (mach_header *) base; 224 | 225 | switch (Swap(mach_header_->magic)) { 226 | case MH_CIGAM: 227 | swapped_ = !swapped_; 228 | case MH_MAGIC: 229 | bits64_ = false; 230 | break; 231 | 232 | case MH_CIGAM_64: 233 | swapped_ = !swapped_; 234 | case MH_MAGIC_64: 235 | bits64_ = true; 236 | break; 237 | 238 | default: 239 | throw "unknown Mach-O magic"; 240 | } 241 | 242 | void *post = mach_header_ + 1; 243 | if (bits64_) 244 | post = (uint32_t *) post + 1; 245 | load_command_ = (struct load_command *) post; 246 | 247 | if (!(Swap(mach_header_->filetype) == MH_EXECUTE || 248 | Swap(mach_header_->filetype) == MH_DYLIB || 249 | Swap(mach_header_->filetype) == MH_BUNDLE)) { 250 | throw "bad Mach-O file type"; 251 | } 252 | } 253 | 254 | struct mach_header *operator ->() const { 255 | return mach_header_; 256 | } 257 | 258 | std::vector GetLoadCommands() const { 259 | std::vector load_commands; 260 | 261 | struct load_command *load_command = load_command_; 262 | for (uint32_t cmd = 0; cmd != Swap(mach_header_->ncmds); ++cmd) { 263 | load_commands.push_back(load_command); 264 | load_command = (struct load_command *) ((uint8_t *) load_command + Swap(load_command->cmdsize)); 265 | } 266 | 267 | return load_commands; 268 | } 269 | }; 270 | 271 | class FatMachHeader : 272 | public MachHeader 273 | { 274 | private: 275 | fat_arch *fat_arch_; 276 | 277 | public: 278 | FatMachHeader(void *base, size_t size, fat_arch *fat_arch) : 279 | MachHeader(base, size), 280 | fat_arch_(fat_arch) 281 | { 282 | } 283 | }; 284 | 285 | class FatHeader : 286 | public Data 287 | { 288 | private: 289 | fat_header *fat_header_; 290 | std::vector mach_headers_; 291 | 292 | public: 293 | FatHeader(void *base, size_t size) : 294 | Data(base, size) 295 | { 296 | fat_header_ = reinterpret_cast(base); 297 | 298 | if (Swap(fat_header_->magic) == FAT_CIGAM) { 299 | swapped_ = !swapped_; 300 | goto fat; 301 | } else if (Swap(fat_header_->magic) != FAT_MAGIC) { 302 | fat_header_ = NULL; 303 | mach_headers_.push_back(FatMachHeader(base, size, NULL)); 304 | } else fat: { 305 | size_t fat_narch = Swap(fat_header_->nfat_arch); 306 | fat_arch *fat_arch = reinterpret_cast(fat_header_ + 1); 307 | size_t arch; 308 | for (arch = 0; arch != fat_narch; ++arch) { 309 | uint32_t arch_offset = Swap(fat_arch->offset); 310 | uint32_t arch_size = Swap(fat_arch->size); 311 | mach_headers_.push_back(FatMachHeader((uint8_t *) base + arch_offset, arch_size, fat_arch)); 312 | ++fat_arch; 313 | } 314 | } 315 | } 316 | 317 | std::vector &GetMachHeaders() { 318 | return mach_headers_; 319 | } 320 | 321 | bool IsFat() const { 322 | return fat_header_ != NULL; 323 | } 324 | 325 | struct fat_header *operator ->() const { 326 | return fat_header_; 327 | } 328 | }; 329 | 330 | static void *map(const char *path, size_t offset, size_t size, size_t *psize) { 331 | int fd; 332 | void *base = NULL; 333 | 334 | fd = open(path, O_RDONLY); 335 | if (fd == -1) { 336 | return NULL; 337 | } 338 | 339 | if (size == _not(size_t)) { 340 | struct stat stat; 341 | if (fstat(fd, &stat) == -1) { 342 | goto fail; 343 | } 344 | size = stat.st_size; 345 | } 346 | 347 | if (psize != NULL) 348 | *psize = size; 349 | 350 | base = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, offset); 351 | if (base == MAP_FAILED) { 352 | base = NULL; 353 | } 354 | 355 | fail: 356 | close(fd); 357 | return base; 358 | } 359 | 360 | #ifdef DEBUG 361 | CFStringRef entErrorString(int code) { 362 | static CFStringRef strings[] = { 363 | CFSTR("success"), 364 | CFSTR("argument is null"), 365 | CFSTR("file read error"), 366 | CFSTR("malformed Mach-O file"), 367 | CFSTR("unknown error") 368 | }; 369 | if (code >= kCopyEntSuccess && code < kCopyEntUnknown) { 370 | return strings[code]; 371 | } 372 | return strings[kCopyEntUnknown]; 373 | } 374 | #endif 375 | 376 | int copyEntitlementDataFromFile(const char *path, CFMutableDataRef output) { 377 | if (path == NULL || output == NULL) { 378 | return kCopyEntArgumentNull; 379 | } 380 | 381 | static union { 382 | uint16_t word; 383 | uint8_t byte[2]; 384 | } endian = {1}; 385 | 386 | little_ = endian.byte[0]; 387 | 388 | size_t size; 389 | void *base(map(path, 0, _not(size_t), &size)); 390 | if (base == NULL) { 391 | return kCopyEntMapFail; 392 | } 393 | 394 | int result = kCopyEntUnknown; 395 | try { 396 | FatHeader fat_header(base, size); 397 | struct linkedit_data_command *signature(NULL); 398 | 399 | _foreach (mach_header, fat_header.GetMachHeaders()) { 400 | _foreach (load_command, mach_header.GetLoadCommands()) { 401 | uint32_t cmd(mach_header.Swap(load_command->cmd)); 402 | if (cmd == LC_CODE_SIGNATURE) { 403 | signature = reinterpret_cast(load_command); 404 | if (signature != NULL) { 405 | uint32_t data = mach_header.Swap(signature->dataoff); 406 | 407 | uint8_t *top = reinterpret_cast(mach_header.GetBase()); 408 | uint8_t *blob = top + data; 409 | struct SuperBlob *super = reinterpret_cast(blob); 410 | 411 | if ((super->count) > 0) { 412 | for (size_t index(0); index != Swap(super->count); ++index) { 413 | if (Swap(super->index[index].type) == CSSLOT_ENTITLEMENTS) { 414 | uint32_t begin = Swap(super->index[index].offset); 415 | struct Blob *entitlements = reinterpret_cast(blob + begin); 416 | CFDataAppendBytes(output, (const uint8_t *) (entitlements + 1), Swap(entitlements->length) - sizeof(struct Blob)); 417 | } 418 | } 419 | } 420 | 421 | result = kCopyEntSuccess; 422 | goto cleanup; 423 | } 424 | } 425 | } 426 | } 427 | 428 | } catch (const char *exception) { 429 | #ifdef DEBUG 430 | if (exception != NULL) { 431 | CFLog(kCFLogLevelWarning, CFSTR("failed to process binary: %s"), exception); 432 | } 433 | #endif 434 | result = kCopyEntMachO; 435 | } 436 | 437 | cleanup: 438 | munmap(base, size); 439 | return result; 440 | } 441 | 442 | #ifdef DUMP_TEST 443 | int main(int argc, const char *argv[]) { 444 | if (argc < 2) { 445 | fprintf(stderr, "%s: missing arguments\n", argv[0]); 446 | return 1; 447 | } 448 | CFMutableDataRef data = CFDataCreateMutable(NULL, 0); 449 | int ret = copyEntitlementDataFromFile(argv[1], data); 450 | if (ret == kCopyEntSuccess) { 451 | CFStringRef str = CFStringCreateFromExternalRepresentation(NULL, data, 452 | kCFStringEncodingUTF8); 453 | CFShow(str); 454 | CFRelease(str); 455 | } else { 456 | #ifdef DEBUG 457 | CFLog(kCFLogLevelError, CFSTR("failed to dump (%@)"), entErrorString(ret)); 458 | #else 459 | fprintf(stderr, "%s: failed to dump (error %d)\n", argv[0], ret); 460 | #endif 461 | } 462 | CFRelease(data); 463 | return 0; 464 | } 465 | #endif 466 | -------------------------------------------------------------------------------- /AppSyncUnified-installd/dump.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | int copyEntitlementDataFromFile(const char *path, CFMutableDataRef output); 8 | 9 | enum { 10 | kCopyEntSuccess = 0, 11 | kCopyEntArgumentNull = 1, 12 | kCopyEntMapFail = 2, 13 | kCopyEntMachO = 3, 14 | kCopyEntUnknown = 4 15 | }; 16 | 17 | #ifdef DEBUG 18 | #include 19 | CFStringRef entErrorString(int code); 20 | #endif 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014-2024 Karen/あけみ, Linus Yang 2 | 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program. If not, see . 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export TARGET = iphone:clang:latest:5.0 2 | export ARCHS = armv7 armv7s arm64 arm64e 3 | export DEBUG = 0 4 | 5 | THEOS_PACKAGE_DIR_NAME = debs 6 | PACKAGE_VERSION = $(THEOS_PACKAGE_BASE_VERSION) 7 | 8 | include $(THEOS)/makefiles/common.mk 9 | 10 | SUBPROJECTS += AppSyncUnified-installd 11 | SUBPROJECTS += AppSyncUnified-FrontBoard 12 | SUBPROJECTS += pkg-actions 13 | SUBPROJECTS += asu_inject 14 | 15 | include $(THEOS_MAKE_PATH)/aggregate.mk 16 | 17 | package:: 18 | ifndef THEOS_PACKAGE_SCHEME 19 | @$(_THEOS_PLATFORM_DPKG_DEB) -b -Zgzip "transitional/nodelete-net.angelxwind.appsyncunified" "$(THEOS_PACKAGE_DIR_NAME)/nodelete-net.angelxwind.appsyncunified.deb" 20 | @$(_THEOS_PLATFORM_DPKG_DEB) -b -Zgzip "transitional/nodelete-net.angelxwind.appsync70plus" "$(THEOS_PACKAGE_DIR_NAME)/nodelete-net.angelxwind.appsync70plus.deb" 21 | @$(_THEOS_PLATFORM_DPKG_DEB) -b -Zgzip "transitional/nodelete-net.angelxwind.appsync60plus" "$(THEOS_PACKAGE_DIR_NAME)/nodelete-net.angelxwind.appsync60plus.deb" 22 | @$(_THEOS_PLATFORM_DPKG_DEB) -b -Zgzip "transitional/nodelete-net.angelxwind.appsync50plus" "$(THEOS_PACKAGE_DIR_NAME)/nodelete-net.angelxwind.appsync50plus.deb" 23 | endif 24 | 25 | after-install:: 26 | install.exec "killall backboardd; exit 0" # backboardd doesn't exist on iOS 5, but that's fine since… FrontBoard also doesn't exist on iOS 5. ;P 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AppSync Unified 2 | ###### Unified AppSync dynamic library for iOS 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, and 18. 3 | 4 | ## What is AppSync Unified? 5 | 6 | AppSync Unified is a tweak that allows users to freely install ad-hoc signed, fakesigned, unsigned, or expired IPA app packages on their iOS devices that iOS would otherwise consider invalid. 7 | 8 | Some popular use cases include: 9 | 10 | * Installing freely-distributed apps that are unavailable from the App Store without having to re-sign the apps in question every 7 days (if the user does not have a subscription to the Apple Developer Program) 11 | * Assisting in the development of iOS applications with Xcode 12 | * Cloning or downgrading already-installed apps 13 | 14 | --- 15 | 16 | ## How do I install AppSync Unified on my jailbroken iOS device? 17 | 18 | AppSync Unified is available from **Karen/あけみ's Repo: https://cydia.akemi.ai/** ([Tap here on your device to automatically add the repo!](https://cydia.akemi.ai/add.php)) 19 | 20 | If you do not see AppSync Unified listed in Karen/あけみ's Repo, then that just means you have another repository added that is also hosting a copy of AppSync Unified under the same package ID. 21 | 22 | **_Please_ only ever install the official, unmodified release from Karen/あけみ's Repo for your own safety, and to ensure proper operation!** Third-party modified versions from other repositories can and _have_ broken various users' iOS installations in the past. 23 | 24 | By installing third-party modified versions of _any system tweak_ like AppSync Unified, you are putting the security and stability of your iOS device and your personal data at risk. 25 | 26 | --- 27 | 28 | ## Help! I installed AppSync Unified, but it doesn't seem to be working after I resprung from Cydia/Zebra/Sileo/etc.! 29 | 30 | If AppSync Unified is not working after installation, please reboot your device or perform a userspace reboot (`launchctl reboot userspace`, `ldrestart`, etc.) to activate it. You will only need to do this ONCE. 31 | 32 | This issue appears to be caused by what …seems like a Cydia Substrate/Substitute bug(?) that's resurfaced from years ago, and occurs _really_ rarely, so it's an absolute nightmare of a bug. It's especially frustrating for me since I'm such a perfectionist when it comes to software development, too ww (🍍˃̶͈̀ロ˂̶͈́)੭ꠥ⁾⁾ 33 | 34 | **For the curious developers among you:** AppSync Unified's `postinst` binary (see [pkg-actions.m](pkg-actions/pkg-actions.m)) restarts `installd` via `launchctl` — for some reason though, it seems like Cydia Substrate and/or Substitute doesn't always inject the dylib properly into `installd` when it is reloaded via `launchctl` in this way. 35 | 36 | I tried _really_ hard to determine the cause of this, but I really have no idea what could be causing this. The dylib has _long_ since been written to the filesystem by the time `postinst` was _executed_, let alone when `launchctl` was even called by `posix_spawn`. 37 | 38 | I guess for now, all I can do is inform people about the bug and how to resolve it. ⊂⌒~⊃。Д。🍍)⊃ 39 | 40 | Hopefully I'll be able to properly resolve this in time. 41 | 42 | --- 43 | 44 | ## Regarding piracy… 45 | 46 | **Please do NOT use AppSync Unified for piracy.** 47 | 48 | AppSync Unified is a development tool designed for app developers first and foremost, alongside other valid legal uses that I support — a few of which are outlined above. 49 | 50 | **Software piracy is illegal.** Please support the developers of the iOS software you use, whether they be app developers on the App Store or tweak developers on Chariz/Dynastic/etc. 51 | 52 | They're just trying to make a living too, much like you and I. 53 | 54 | --- 55 | 56 | ## How do I build AppSync Unified? 57 | 58 | First, make sure you have [Theos](https://github.com/theos/theos) installed. If you don't, [please refer to the official documentation](https://theos.dev/docs/installation) on how to set up Theos on your operating system of choice. 59 | 60 | Once you've confirmed that you have Theos installed, open up Terminal and run the following commands: 61 | 62 | ```shell 63 | git clone https://github.com/akemin-dayo/AppSync.git 64 | cd AppSync/ 65 | make 66 | make package 67 | ``` 68 | 69 | And you should have a freshly built *.deb package file of AppSync Unified! 70 | 71 | --- 72 | 73 | ## Do I need to do anything in order to use Xcode with AppSync Unified? 74 | 75 | No, not unless you're using Xcode 6 or below. 76 | 77 | As long as you're using a … _reasonably_ modern version of Xcode (7 or above, really), just use Xcode with your iOS device as you would normally! 78 | 79 | That being said, if for some reason you _are_ using Xcode 6 or below, please follow this old tutorial I wrote up all the way back in 2014: [Tutorial: How to use AppSync Unified for development with Xcode 6 or below](https://akemi.ai/?page/how2asu) 80 | 81 | --- 82 | 83 | ## I'm a developer — do you have a rough high-level explanation as to how this all works? 84 | 85 | As of AppSync Unified 90.0, ASU is split into two separate dynamic libraries — `AppSyncUnified-installd` and `AppSyncUnified-FrontBoard`. 86 | 87 | ### `AppSyncUnified-installd` 88 | 89 | `AppSyncUnified-installd` injects into — you guessed it — `installd`, which is where the vast majority of ASU's functionality resides in. 90 | 91 | AppSync Unified utilises Cydia Substrate's dynamic hooking function `MSHookFunction()` (which is also shimmed appropriately for systems that use Substitute as their code injection platform instead) to bypass `installd`'s signature checks. For iOS 13 and below, the main function being modified is `MISValidateSignatureAndCopyInfo()`, while on iOS 14 and above, it is `MISValidateSignatureAndCopyInfoWithProgress()`. 92 | 93 | Using `MSFindSymbol()`, AppSync Unified determines which function is present on the system it's currently running on and appropriately hooks the correct one. 94 | 95 | When iOS makes a request to install an app, one of the two functions mentioned above will be called, and AppSync Unified's injected `ASU_MISValidateSignatureAndCopyInfo()` function will take over. 96 | 97 | If the app in question has valid signing information, AppSync Unified will not make any modifications to it, and simply pass the information along to the original Apple-implemented function, letting the app installation process carry on as if the system was not modified at all. 98 | 99 | On the other hand, if the app contains invalid signing information, AppSync Unified will generate the appropriate signing information required and pass it along to the system. As of AppSync Unified 90.0, this process also includes code directory hash value (`cdhash`) computation. 100 | 101 | AppSync Unified also hooks two other functions — `SecCertificateCreateWithData()` and `SecCertificateCopySubjectSummary()`. The modifications work in pretty much the same way as above — if the original certificate is valid, don't touch anything at all. If it isn't valid, then just… present a valid certificate to iOS. 102 | 103 | ### `AppSyncUnified-FrontBoard` 104 | 105 | AppSync Unified 90.0 introduced a second dynamic library that injects into the `FrontBoard` and `FrontBoardServices` private frameworks. This is done in order to bypass a set of signature verifications that are performed at app runtime, generally used for timed app expirations. 106 | 107 | That being said, the modifications are actually… incredibly simple. 108 | 109 | On iOS 9.3.x to iOS 13, AppSync Unified hooks the Objective-C methods `-(NSUInteger) trustStateWithTrustRequiredReasons:(NSUInteger *)reasons` and `-(NSUInteger) trustState` found in the class `FBApplicationTrustData`, and forces both methods to always return the value used to signal to iOS that the app that the user is attempting to launch is valid and trusted. 110 | 111 | On iOS 14 and above, it's… basically the same thing, but targetting a different method — `-(NSUInteger) trustStateForApplication:(id)application` found in the class `FBSSignatureValidationService` — since Apple moved the relevant functionality from `FrontBoard` to `FrontBoardServices`. 112 | 113 | … 114 | 115 | And that's about it! This explanation is obviously _incredibly_ simplified (the best explanation is just to read the code!), but hopefully it gives you a grasp on how everything works. c: 116 | 117 | --- 118 | 119 | ## License 120 | 121 | Licensed under [GPLv3](http://www.gnu.org/copyleft/gpl.html). 122 | -------------------------------------------------------------------------------- /appinst/Makefile: -------------------------------------------------------------------------------- 1 | export TARGET = iphone:clang:latest:5.0 2 | export ARCHS = armv7 armv7s arm64 3 | export DEBUG = 0 4 | 5 | THEOS_PACKAGE_DIR_NAME = debs 6 | PACKAGE_VERSION = $(THEOS_PACKAGE_BASE_VERSION) 7 | 8 | include $(THEOS)/makefiles/common.mk 9 | 10 | TOOL_NAME = appinst 11 | appinst_FILES = appinst.m 12 | appinst_CCFLAGS += -std=c++11 -stdlib=libc++ -fobjc-arc -include ./zip.h -I . -fvisibility=hidden -Wno-unused-property-ivar 13 | appinst_CFLAGS += -fobjc-arc -include ./zip.h -I . -fvisibility=hidden -Wno-unused-property-ivar 14 | appinst_FRAMEWORKS = Foundation ImageIO CoreGraphics 15 | appinst_PRIVATE_FRAMEWORKS = MobileCoreServices 16 | appinst_LDFLAGS += $(THEOS)/lib/libzip.a 17 | appinst_LIBRARIES = z 18 | appinst_INSTALL_PATH = /usr/bin 19 | appinst_CODESIGN_FLAGS = -Sappinst_entitlements.plist 20 | 21 | include $(THEOS_MAKE_PATH)/tool.mk 22 | 23 | package:: 24 | ifndef THEOS_PACKAGE_SCHEME 25 | @$(_THEOS_PLATFORM_DPKG_DEB) -b -Zgzip "transitional/nodelete-com.linusyang.appinst" "$(THEOS_PACKAGE_DIR_NAME)/nodelete-com.linusyang.appinst.deb" 26 | endif 27 | -------------------------------------------------------------------------------- /appinst/README.md: -------------------------------------------------------------------------------- 1 | # appinst (App Installer) 2 | ###### A command-line IPA app installer for iOS 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, and 18. 3 | 4 | ## What is appinst? 5 | 6 | appinst is a command-line utility that allows you to install IPA packages. 7 | 8 | AppSync Unified is required as a dependency in order to install ad-hoc signed, fakesigned, unsigned, or expired apps. 9 | 10 | **Usage:** `appinst ` 11 | 12 | --- 13 | 14 | ## How do I install appinst on my jailbroken iOS device? 15 | 16 | appinst is available from **Karen/あけみ's Repo: https://cydia.akemi.ai/** ([Tap here on your device to automatically add the repo!](https://cydia.akemi.ai/add.php)) 17 | 18 | If you do not see appinst listed in Karen/あけみ's Repo, then that just means you have another repository added that is also hosting a copy of appinst under the same package ID. 19 | 20 | **_Please_ only ever install the official, unmodified release from Karen/あけみ's Repo for your own safety, and to ensure proper operation!** 21 | 22 | --- 23 | 24 | ## How do I build appinst? 25 | 26 | First, make sure you have [Theos](https://github.com/theos/theos) installed. If you don't, [please refer to the official documentation](https://theos.dev/docs/installation) on how to set up Theos on your operating system of choice. 27 | 28 | Once you've confirmed that you have Theos installed, you'll need to acquire a copy of `libzip.a` to statically link against. 29 | 30 | To do this, download the `libzip` package available from the BigBoss repository. The latest version as of the writing of this documentation is 0.11.2, and [the URL to the deb file can be found here](http://apt.thebigboss.org/repofiles/cydia/debs2.0/libzip_0.11.2.deb). 31 | 32 | Extract the deb using whatever method you like. ([`dpkg -x`](https://formulae.brew.sh/formula/dpkg), [`unar`](https://formulae.brew.sh/formula/unar) ([also available without using Homebrew](https://theunarchiver.com/command-line)), [The Unarchiver](https://theunarchiver.com/), etc.) 33 | 34 | Locate the `libzip.a` file, and copy it to `$THEOS/libs/`. 35 | 36 | After you've done that, open up Terminal and run the following commands: 37 | 38 | ```shell 39 | git clone https://github.com/akemin-dayo/AppSync.git 40 | cd AppSync/appinst/ 41 | make 42 | make package 43 | ``` 44 | 45 | And you should have a freshly built *.deb package file of appinst! 46 | 47 | ### … Wait, why are you _statically_ linking against libzip, instead of dynamically linking it and listing `libzip` as an APT dependency? 48 | 49 | Ah. Yeah. About that. 50 | 51 | It turns out that the different packages of `libzip` that are found on BigBoss (arm64, armv7s, armv7, armv6), Bingner/Elucubratus (arm64 only), and Procursus (arm64 only) all install to different locations and are all under different names. 52 | 53 | This pretty much makes it impossible to support, so statically linking against `libzip` is the only option. 54 | 55 | --- 56 | 57 | ## License 58 | 59 | Licensed under [GPLv3](http://www.gnu.org/copyleft/gpl.html). 60 | -------------------------------------------------------------------------------- /appinst/appinst.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | #import "zip.h" 6 | 7 | #ifdef DEBUG 8 | #define LOG(LogContents, ...) NSLog((@"[appinst] [%s] [L%d] " LogContents), __FUNCTION__, __LINE__, ##__VA_ARGS__) 9 | #else 10 | #define LOG(...) 11 | #endif 12 | #define kIdentifierKey @"CFBundleIdentifier" 13 | #define kAppType @"User" 14 | #define kAppTypeKey @"ApplicationType" 15 | #define kRandomLength 32 16 | 17 | #define DPKG_PATH ROOT_PATH("/var/lib/dpkg/info/ai.akemi.appinst.list") 18 | 19 | static const NSString *kRandomAlphanumeric = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 20 | 21 | typedef enum { 22 | AppInstExitCodeSuccess = 0x0, 23 | AppInstExitCodeInject, 24 | AppInstExitCodeZip, 25 | AppInstExitCodeMalformed, 26 | AppInstExitCodeFileSystem, 27 | AppInstExitCodeRuntime, 28 | AppInstExitCodeUnknown 29 | } AppInstExitCode; 30 | 31 | // MobileInstallation for iOS 5〜7 32 | typedef void (*MobileInstallationCallback)(CFDictionaryRef information); 33 | typedef int (*MobileInstallationInstall)(CFStringRef path, CFDictionaryRef parameters, MobileInstallationCallback callback, CFStringRef backpath); 34 | #define MI_PATH "/System/Library/PrivateFrameworks/MobileInstallation.framework/MobileInstallation" 35 | 36 | void mobileInstallationStatusCallback(CFDictionaryRef information) { 37 | NSDictionary *installInfo = (__bridge NSDictionary *)information; 38 | NSNumber *percentComplete = [installInfo objectForKey:@"PercentComplete"]; 39 | NSString *installStatus = [installInfo objectForKey:@"Status"]; 40 | 41 | if (installStatus) { 42 | // Use NSRegularExpression to split up the Apple-provided PascalCase status string into individual words with spaces 43 | NSRegularExpression *pascalCaseSplitterRegex = [NSRegularExpression regularExpressionWithPattern:@"([a-z])([A-Z])" options:0 error:nil]; 44 | installStatus = [pascalCaseSplitterRegex stringByReplacingMatchesInString:installStatus options:0 range:NSMakeRange(0, [installStatus length]) withTemplate:@"$1 $2"]; 45 | 46 | // Capitalise only the first character in the resulting string 47 | // TODO: Figure out a better/cleaner way to do this. This was simply the first method that came to my head after thinking about it for all of like, 30 seconds. 48 | installStatus = [NSString stringWithFormat:@"%@%@", [[installStatus substringToIndex:1] uppercaseString], [[installStatus substringWithRange:NSMakeRange(1, [installStatus length] - 1)] lowercaseString]]; 49 | 50 | // Print status 51 | // Yes, I went through all this extra effort just so the user can look at some pretty strings. No, there is (probably) nothing wrong with me. ;P 52 | printf("%ld%% - %s…\n", (long)[percentComplete integerValue], [installStatus UTF8String]); 53 | } 54 | } 55 | 56 | // LSApplicationWorkspace for iOS 8 and above 57 | @interface LSApplicationWorkspace : NSObject 58 | + (id)defaultWorkspace; 59 | - (BOOL)installApplication:(NSURL *)path withOptions:(NSDictionary *)options error:(NSError **)error; 60 | - (BOOL)uninstallApplication:(NSString *)identifier withOptions:(NSDictionary *)options; 61 | @end 62 | 63 | bool doesProcessAtPIDExist(pid_t pid) { 64 | // kill() returns 0 when the process exists, and -1 if the process does not. 65 | // TODO: This currently does not take into account a possible edge-case where a user can launch one instance of appinst as root, and another appinst instance as a non-privileged user. 66 | // In such a case, if the non-privileged appinst attempts to kill(), it would return -1 due to failing the permission check, therefore resulting in a false positive. 67 | return (kill(pid, 0) == 0); 68 | } 69 | 70 | bool isSafeToDeleteAppInstTemporaryDirectory(NSString *workPath) { 71 | // There is no point in running multiple instances of appinst, as app installation on iOS can only happen one app at a time. 72 | // … That being said, some people may still try to do so anyway — iOS /does/ gracefully handle such a state, and will simply wait for an existing install session lock to release before proceeding. 73 | // However, appinst's temporary directory self-cleanup code prior to appinst 1.2 could potentially result in a slight issue if the user tries to run multiple appinst instances. 74 | // If you launch two appinst instances in quick enough succession, both will fail to install due to their temporary IPA copies having been deleted by each other. 75 | // ※ If you don't do it quickly, then nothing will really happen, because the file handle would have already been opened by MobileInstallation / LSApplicationWorkSpace, and the deletion wouldn't really take effect until the file handle was closed. 76 | // But in the interest of making appinst as robust as I possibly can, here's some code to handle this potential edge-case. 77 | 78 | // Build a list of all PID files in the temporary directory 79 | NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:workPath error:nil]; 80 | NSArray *pidFiles = [dirContents filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF ENDSWITH '.pid'"]]; 81 | for (NSString *pidFile in pidFiles) { 82 | // Read the PID file contents and assign it to a pid_t 83 | NSString *pidFilePath = [workPath stringByAppendingPathComponent:pidFile]; 84 | pid_t pidToCheck = [[NSString stringWithContentsOfFile:pidFilePath encoding:NSUTF8StringEncoding error:nil] intValue]; 85 | if (pidToCheck == 0) { 86 | // If the resulting pid_t ends up as 0, something went horribly wrong while parsing the contents of the PID file. 87 | // We'll just treat this failed state as if there are other active instances of appinst, just in case. 88 | printf("Failed to read the PID from %s! Proceeding as if there are other active instances of appinst…", [pidFilePath UTF8String]); 89 | return false; 90 | } 91 | if (doesProcessAtPIDExist(pidToCheck)) { 92 | // If the PID exists, this means that there is another appinst instance in an active install session. 93 | // This also takes into account PID files left over by an appinst that crashed or was otherwise interrupted, and therefore didn't get to clean up after itself 94 | printf("Another instance of appinst seems to be in an active install session. Proceeding without deleting the temporary directory…\n"); 95 | return false; 96 | } 97 | } 98 | return true; 99 | } 100 | 101 | int main(int argc, const char *argv[]) { 102 | @autoreleasepool { 103 | printf("appinst (App Installer)\n"); 104 | printf("Copyright (C) 2014-2024 Karen/あけみ\n"); 105 | printf("** PLEASE DO NOT USE APPINST FOR PIRACY **\n"); 106 | if (access(DPKG_PATH, F_OK) == -1) { 107 | printf("You seem to have installed appinst from an APT repository that is not cydia.akemi.ai.\n"); 108 | printf("Please make sure that you download AppSync Unified from the official repository to ensure proper operation.\n"); 109 | } 110 | 111 | // Construct our temporary directory path 112 | NSFileManager *fileManager = [NSFileManager defaultManager]; 113 | NSString *workPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"appinst"]; 114 | 115 | // If there was a leftover temporary directory from a previous run, clean it up 116 | if ([fileManager fileExistsAtPath:workPath] && isSafeToDeleteAppInstTemporaryDirectory(workPath)) { 117 | if (![fileManager removeItemAtPath:workPath error:nil]) { 118 | // This theoretically should never happen, now that appinst sets 0777 directory permissions for its temporary directory as of version 1.2. 119 | // That, and the temporary directory is also different as of 1.2, too, so even if an older version of appinst was run as root, it should not affect appinst 1.2. 120 | printf("Failed to delete leftover temporary directory at %s, continuing anyway.\n", [workPath UTF8String]); 121 | printf("This can happen if the previous temporary directory was created by the root user.\n"); 122 | } else { 123 | printf("Deleting leftover temporary directory…\n"); 124 | } 125 | } 126 | 127 | // Print usage information if the number of arguments was incorrect 128 | if (argc != 2) { 129 | printf("Usage: appinst \n"); 130 | return AppInstExitCodeUnknown; 131 | } 132 | 133 | // Check if the user-specified file path exists 134 | NSString *filePath = [NSString stringWithUTF8String:argv[1]]; 135 | if (![fileManager fileExistsAtPath:filePath]) { 136 | // If the first argument is -h or --help, print usage information 137 | if ([filePath isEqualToString:@"-h"] || [filePath isEqualToString:@"--help"]) { 138 | printf("Usage: appinst \n"); 139 | return AppInstExitCodeUnknown; 140 | } 141 | printf("The file \"%s\" could not be found. Perhaps you made a typo?\n", [filePath UTF8String]); 142 | return AppInstExitCodeFileSystem; 143 | } 144 | 145 | // Resolve app identifier 146 | NSString *appIdentifier = nil; 147 | int err = 0; 148 | zip_t *archive = zip_open(argv[1], 0, &err); 149 | if (err) { 150 | printf("Unable to read the specified IPA file.\n"); 151 | return AppInstExitCodeZip; 152 | } 153 | zip_int64_t num_entries = zip_get_num_entries(archive, 0); 154 | for (zip_uint64_t i = 0; i < num_entries; ++i) { 155 | const char* name = zip_get_name(archive, i, 0); 156 | if (!name) { 157 | printf("Unable to read the specified IPA file.\n"); 158 | zip_close(archive); 159 | return AppInstExitCodeZip; 160 | } 161 | NSString *fileName = [NSString stringWithUTF8String:name]; 162 | NSArray *components = [fileName pathComponents]; 163 | NSUInteger count = components.count; 164 | NSString *firstComponent = [components objectAtIndex:0]; 165 | if ([firstComponent isEqualToString:@"/"]) { 166 | firstComponent = [components objectAtIndex:1]; 167 | count -= 1; 168 | } 169 | if (count == 3 && [firstComponent isEqualToString:@"Payload"] && 170 | [components.lastObject isEqualToString:@"Info.plist"]) { 171 | zip_stat_t st; 172 | zip_stat_init(&st); 173 | zip_stat_index(archive, i, 0, &st); 174 | 175 | void *buffer = malloc(st.size); 176 | if (!buffer) { 177 | printf("Unable to read the specified IPA file.\n"); 178 | zip_close(archive); 179 | return AppInstExitCodeZip; 180 | } 181 | 182 | zip_file_t *file_in_zip = zip_fopen_index(archive, i, 0); 183 | if (!file_in_zip) { 184 | printf("Unable to read the specified IPA file.\n"); 185 | zip_close(archive); 186 | return AppInstExitCodeZip; 187 | } 188 | 189 | zip_fread(file_in_zip, buffer, st.size); 190 | zip_fclose(file_in_zip); 191 | 192 | NSData *fileData = [NSData dataWithBytesNoCopy:buffer length:st.size freeWhenDone:YES]; 193 | if (fileData == nil) { 194 | printf("Unable to read the specified IPA file.\n"); 195 | return AppInstExitCodeZip; 196 | } 197 | NSError *error; 198 | NSPropertyListFormat format; 199 | NSDictionary * dict = (NSDictionary *) [NSPropertyListSerialization propertyListWithData:fileData 200 | options:NSPropertyListImmutable format:&format error:&error]; 201 | if (dict == nil) { 202 | printf("The specified IPA file contains a malformed Info.plist.\n"); 203 | return AppInstExitCodeMalformed; 204 | } 205 | appIdentifier = [dict objectForKey:kIdentifierKey]; 206 | break; 207 | } 208 | } 209 | 210 | zip_close(archive); 211 | 212 | if (appIdentifier == nil) { 213 | printf("Failed to resolve app identifier for the specified IPA file.\n"); 214 | return AppInstExitCodeMalformed; 215 | } 216 | 217 | // Begin copying the IPA to a temporary directory 218 | // First, we need to set the permissions of the temporary directory itself to 0777, to avoid running into permission issues if the user runs appinst as root. 219 | NSDictionary *workPathDirectoryPermissions = [NSDictionary dictionaryWithObject:@0777 forKey:NSFilePosixPermissions]; 220 | if (![fileManager createDirectoryAtPath:workPath withIntermediateDirectories:YES attributes:workPathDirectoryPermissions error:nil]) { 221 | printf("Failed to create temporary directory.\n"); 222 | return AppInstExitCodeFileSystem; 223 | } 224 | 225 | // Generate a random string which will be used as a reasonably unique session ID 226 | NSMutableString *sessionID = [NSMutableString stringWithCapacity:kRandomLength]; 227 | for (int i = 0; i < kRandomLength; i++) { 228 | [sessionID appendFormat: @"%C", [kRandomAlphanumeric characterAtIndex:arc4random_uniform([kRandomAlphanumeric length])]]; 229 | } 230 | 231 | // Write the current appinst PID to a file corresponding to the session ID 232 | // This is only used in isSafeToDeleteAppInstTemporaryDirectory() — see the comments in that function for more information. 233 | pid_t currentPID = getpid(); 234 | printf("Initialising appinst installation session ID %s (PID %d)…\n", [sessionID UTF8String], currentPID); 235 | NSString *pidFilePath = [workPath stringByAppendingPathComponent:[NSString stringWithFormat:@"appinst-session-%@.pid", sessionID]]; 236 | if (![[NSString stringWithFormat:@"%d", currentPID] writeToFile:pidFilePath atomically:YES encoding:NSUTF8StringEncoding error:nil]) { 237 | // If we fail to write the PID, just ignore it and continue on. It's very unlikely that users will even run into the rare issue that this code is a fix for, anyway. 238 | printf("Failed to write PID file to %s, continuing anyway.\n", [pidFilePath UTF8String]); 239 | } 240 | 241 | // Copy the user-specified IPA to the temporary directory 242 | // The reason why we do this is because MobileInstallation / LSApplicationWorkSpace will actually delete the IPA once it's finished extracting. 243 | NSString *installName = [NSString stringWithFormat:@"appinst-session-%@.ipa", sessionID]; 244 | NSString *installPath = [workPath stringByAppendingPathComponent:installName]; 245 | if ([fileManager fileExistsAtPath:installPath]) { 246 | // It is extremely unlikely (almost impossible) for a session ID collision to occur, but if it does, we'll delete the conflicting IPA. 247 | if (![fileManager removeItemAtPath:installPath error:nil]) { 248 | // … It's also possible (but even /more/ unlikely) that this will fail. 249 | // If this somehow happens, just instruct the user to try again. That will give them a different, non-conflicting session ID. 250 | printf("Failed to delete conflicting leftover temporary files from a previous appinst session at %s. Please try running appinst again.\n", [installPath UTF8String]); 251 | return AppInstExitCodeFileSystem; 252 | } 253 | } 254 | if (![fileManager copyItemAtPath:filePath toPath:installPath error:nil]) { 255 | printf("Failed to copy the specified IPA to the temporary directory. Do you have enough free disk space?\n"); 256 | return AppInstExitCodeFileSystem; 257 | } 258 | 259 | // Call system APIs to actually install the app 260 | printf("Installing \"%s\"…\n", [appIdentifier UTF8String]); 261 | BOOL isInstalled = false; 262 | if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) { 263 | // Use LSApplicationWorkspace on iOS 8 and above 264 | Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace"); 265 | if (LSApplicationWorkspace_class == nil) { 266 | printf("Failed to get class: LSApplicationWorkspace\n"); 267 | return AppInstExitCodeRuntime; 268 | } 269 | 270 | LSApplicationWorkspace *workspace = [LSApplicationWorkspace_class performSelector:@selector(defaultWorkspace)]; 271 | if (workspace == nil) { 272 | printf("Failed to get the default workspace.\n"); 273 | return AppInstExitCodeRuntime; 274 | } 275 | 276 | // Install app 277 | NSDictionary *options = [NSDictionary dictionaryWithObject:appIdentifier forKey:kIdentifierKey]; 278 | NSError *error; 279 | @try { 280 | if ([workspace installApplication:[NSURL fileURLWithPath:installPath] withOptions:options error:&error]) { 281 | isInstalled = YES; 282 | } 283 | } @catch (NSException *exception) { 284 | printf("An exception occurred while attempting to install the app!\n"); 285 | printf("NSException info: %s\n", [[NSString stringWithFormat:@"%@", exception] UTF8String]); 286 | } 287 | if (error) { 288 | printf("An error occurred while attempting to install the app!\n"); 289 | printf("NSError info: %s\n", [[NSString stringWithFormat:@"%@", error] UTF8String]); 290 | } 291 | } else { 292 | // Use MobileInstallationInstall on iOS 5〜7 293 | void *image = dlopen(MI_PATH, RTLD_LAZY); 294 | if (image == NULL) { 295 | printf("Failed to retrieve MobileInstallation.\n"); 296 | return AppInstExitCodeRuntime; 297 | } 298 | 299 | MobileInstallationInstall installHandle = (MobileInstallationInstall) dlsym(image, "MobileInstallationInstall"); 300 | if (installHandle == NULL) { 301 | printf("Failed to retrieve the MobileInstallationInstall function.\n"); 302 | return AppInstExitCodeRuntime; 303 | } 304 | 305 | // Install app 306 | NSDictionary *options = [NSDictionary dictionaryWithObject:kAppType forKey:kAppTypeKey]; 307 | if (installHandle((__bridge CFStringRef) installPath, (__bridge CFDictionaryRef) options, &mobileInstallationStatusCallback, (__bridge CFStringRef) installPath) == 0) { 308 | isInstalled = YES; 309 | } 310 | } 311 | 312 | // Clean up appinst PID file for current session ID 313 | if ([fileManager fileExistsAtPath:pidFilePath] && [fileManager isDeletableFileAtPath:pidFilePath]) { 314 | printf("Cleaning up appinst session ID %s (PID %d)…\n", [sessionID UTF8String], currentPID); 315 | [fileManager removeItemAtPath:pidFilePath error:nil]; 316 | } 317 | 318 | // Clean up temporary copied IPA 319 | if ([fileManager fileExistsAtPath:installPath] && [fileManager isDeletableFileAtPath:installPath]) { 320 | printf("Cleaning up temporary files…\n"); 321 | [fileManager removeItemAtPath:installPath error:nil]; 322 | } 323 | 324 | // Clean up temporary directory 325 | if ([fileManager fileExistsAtPath:workPath] && [fileManager isDeletableFileAtPath:workPath] && isSafeToDeleteAppInstTemporaryDirectory(workPath)) { 326 | printf("Deleting temporary directory…\n"); 327 | [fileManager removeItemAtPath:workPath error:nil]; 328 | } 329 | 330 | // Print final results 331 | if (isInstalled) { 332 | printf("Successfully installed \"%s\"!\n", [appIdentifier UTF8String]); 333 | return AppInstExitCodeSuccess; 334 | } 335 | printf("Failed to install \"%s\".\n", [appIdentifier UTF8String]); 336 | return AppInstExitCodeUnknown; 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /appinst/appinst_entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | application-identifier 6 | ai.akemi.appinst 7 | com.apple.private.mobileinstall.allowedSPI 8 | 9 | Install 10 | InstallForLaunchServices 11 | 12 | com.apple.security.exception.mach-lookup.global-name 13 | 14 | com.apple.mobile.installd 15 | 16 | platform-application 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /appinst/changelog-inline.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |
6 | 7 |
8 |
9 |
10 |
11 |
12 |
13 | 14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | 29 |
30 |
31 |
32 |
33 | 34 |
35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 | 43 |
44 |
45 |
46 | 47 |
48 |
49 |
50 | 51 |
52 |
53 |
54 | 55 |
56 |
57 |
58 | 59 |
60 |
61 |
62 |
-------------------------------------------------------------------------------- /appinst/control: -------------------------------------------------------------------------------- 1 | Package: ai.akemi.appinst 2 | Name: appinst (App Installer) 3 | Priority: standard 4 | Pre-Depends: firmware (>= 5.0) 5 | Depends: ai.akemi.appsyncunified (>= 5.1) 6 | Version: 2.1.1 7 | Architecture: iphoneos-arm 8 | Description: A command-line IPA app installer. 9 | Maintainer: Karen/あけみ 10 | Author: Karen/あけみ, Linus Yang 11 | Section: Utilities 12 | Homepage: https://github.com/akemin-dayo/AppSync 13 | Tag: role::enduser, purpose::console 14 | Conflicts: com.sull.appinstaller 15 | Provides: com.linusyang.appinst 16 | Replaces: com.linusyang.appinst 17 | Depiction: https://cydia.akemi.ai/?page/ai.akemi.appinst 18 | -------------------------------------------------------------------------------- /appinst/karentools-build-config: -------------------------------------------------------------------------------- 1 | projectType="iOS-theos" 2 | bundleIdentifier="ai.akemi.appinst" 3 | miniOSVersion="5.0" 4 | maxiOSVersion="latest" 5 | rootlessSupport=1 6 | -------------------------------------------------------------------------------- /appinst/redditpost.md: -------------------------------------------------------------------------------- 1 | ### Repo URL: https://cydia.akemi.ai/ ([Tap here on your device to automatically add the repo!](https://cydia.akemi.ai/add.php)) 2 | 3 | #### Support me with [Patreon](https://patreon.com/akemin_dayo), [PayPal (`karen@akemi.ai`)](https://paypal.me/akemindayo), or [Cryptocurrency](https://akemi.ai/?page/links#crypto) 4 | 5 | (A full list of all available donation methods can be found [here](https://akemi.ai/?page/links#donate).) 6 | 7 | Any support is _greatly_ appreciated, but donations are *not* and will *never* be necessary to use my software! 8 | 9 | --- 10 | 11 | # Changelog for 2.1.1 ([full changelog](https://cydia.akemi.ai/?page/ai.akemi.appinst-changelog)) 12 | 13 | * Removed Dopamine warning message, as it is no longer necessary due to Évelyne fixing her ElleKit code injection platform for AppSync Unified. For more information, please see [GitHub issue #174](https://github.com/akemin-dayo/AppSync/issues/174). 14 | 15 | --- 16 | 17 | # `appinst`, a command-line IPA app installer for iOS 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, and 18. 18 | 19 | appinst is a command-line utility that allows you to install IPA packages. 20 | 21 | [AppSync Unified](https://cydia.akemi.ai/?page/ai.akemi.appsyncunified) is required as a dependency in order to install ad-hoc signed, fakesigned, unsigned, or expired apps. 22 | 23 | **Usage:** `appinst ` 24 | -------------------------------------------------------------------------------- /appinst/transitional/nodelete-com.linusyang.appinst/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Architecture: iphoneos-arm 2 | Author: Karen/あけみ 3 | Depends: ai.akemi.appinst 4 | Description: Transitional package for appinst (App Installer). 5 | Maintainer: Karen/あけみ 6 | Homepage: http://cydia.akemi.ai/ 7 | Name: appinst (App Installer) Transitional Package 8 | Package: com.linusyang.appinst 9 | Section: Tweaks 10 | Version: 9999.0 11 | Depiction: https://cydia.akemi.ai/?page/ai.akemi.appinst-transitional 12 | Tag: cydia::obsolete 13 | -------------------------------------------------------------------------------- /appinst/zip.h: -------------------------------------------------------------------------------- 1 | #ifndef _HAD_ZIP_H 2 | #define _HAD_ZIP_H 3 | 4 | /* 5 | zip.h -- exported declarations. 6 | Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner 7 | 8 | This file is part of libzip, a library to manipulate ZIP archives. 9 | The authors can be contacted at 10 | 11 | Redistribution and use in source and binary forms, with or without 12 | modification, are permitted provided that the following conditions 13 | are met: 14 | 1. Redistributions of source code must retain the above copyright 15 | notice, this list of conditions and the following disclaimer. 16 | 2. Redistributions in binary form must reproduce the above copyright 17 | notice, this list of conditions and the following disclaimer in 18 | the documentation and/or other materials provided with the 19 | distribution. 20 | 3. The names of the authors may not be used to endorse or promote 21 | products derived from this software without specific prior 22 | written permission. 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS 25 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 26 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY 28 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 30 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 32 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 33 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 34 | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #if 0 41 | } /* fix autoindent */ 42 | #endif 43 | #endif 44 | 45 | #include 46 | 47 | #ifndef ZIP_EXTERN 48 | #ifndef ZIP_STATIC 49 | #ifdef _WIN32 50 | #define ZIP_EXTERN __declspec(dllimport) 51 | #elif defined(__GNUC__) && __GNUC__ >= 4 52 | #define ZIP_EXTERN __attribute__((visibility("default"))) 53 | #else 54 | #define ZIP_EXTERN 55 | #endif 56 | #else 57 | #define ZIP_EXTERN 58 | #endif 59 | #endif 60 | 61 | #include 62 | #include 63 | #include 64 | 65 | /* flags for zip_open */ 66 | 67 | #define ZIP_CREATE 1 68 | #define ZIP_EXCL 2 69 | #define ZIP_CHECKCONS 4 70 | #define ZIP_TRUNCATE 8 71 | #define ZIP_RDONLY 16 72 | 73 | 74 | /* flags for zip_name_locate, zip_fopen, zip_stat, ... */ 75 | 76 | #define ZIP_FL_NOCASE 1u /* ignore case on name lookup */ 77 | #define ZIP_FL_NODIR 2u /* ignore directory component */ 78 | #define ZIP_FL_COMPRESSED 4u /* read compressed data */ 79 | #define ZIP_FL_UNCHANGED 8u /* use original data, ignoring changes */ 80 | #define ZIP_FL_RECOMPRESS 16u /* force recompression of data */ 81 | #define ZIP_FL_ENCRYPTED 32u /* read encrypted data (implies ZIP_FL_COMPRESSED) */ 82 | #define ZIP_FL_ENC_GUESS 0u /* guess string encoding (is default) */ 83 | #define ZIP_FL_ENC_RAW 64u /* get unmodified string */ 84 | #define ZIP_FL_ENC_STRICT 128u /* follow specification strictly */ 85 | #define ZIP_FL_LOCAL 256u /* in local header */ 86 | #define ZIP_FL_CENTRAL 512u /* in central directory */ 87 | /* 1024u reserved for internal use */ 88 | #define ZIP_FL_ENC_UTF_8 2048u /* string is UTF-8 encoded */ 89 | #define ZIP_FL_ENC_CP437 4096u /* string is CP437 encoded */ 90 | #define ZIP_FL_OVERWRITE 8192u /* zip_file_add: if file with name exists, overwrite (replace) it */ 91 | 92 | /* archive global flags flags */ 93 | 94 | #define ZIP_AFL_RDONLY 2u /* read only -- cannot be cleared */ 95 | 96 | 97 | /* create a new extra field */ 98 | 99 | #define ZIP_EXTRA_FIELD_ALL ZIP_UINT16_MAX 100 | #define ZIP_EXTRA_FIELD_NEW ZIP_UINT16_MAX 101 | 102 | 103 | /* libzip error codes */ 104 | 105 | #define ZIP_ER_OK 0 /* N No error */ 106 | #define ZIP_ER_MULTIDISK 1 /* N Multi-disk zip archives not supported */ 107 | #define ZIP_ER_RENAME 2 /* S Renaming temporary file failed */ 108 | #define ZIP_ER_CLOSE 3 /* S Closing zip archive failed */ 109 | #define ZIP_ER_SEEK 4 /* S Seek error */ 110 | #define ZIP_ER_READ 5 /* S Read error */ 111 | #define ZIP_ER_WRITE 6 /* S Write error */ 112 | #define ZIP_ER_CRC 7 /* N CRC error */ 113 | #define ZIP_ER_ZIPCLOSED 8 /* N Containing zip archive was closed */ 114 | #define ZIP_ER_NOENT 9 /* N No such file */ 115 | #define ZIP_ER_EXISTS 10 /* N File already exists */ 116 | #define ZIP_ER_OPEN 11 /* S Can't open file */ 117 | #define ZIP_ER_TMPOPEN 12 /* S Failure to create temporary file */ 118 | #define ZIP_ER_ZLIB 13 /* Z Zlib error */ 119 | #define ZIP_ER_MEMORY 14 /* N Malloc failure */ 120 | #define ZIP_ER_CHANGED 15 /* N Entry has been changed */ 121 | #define ZIP_ER_COMPNOTSUPP 16 /* N Compression method not supported */ 122 | #define ZIP_ER_EOF 17 /* N Premature end of file */ 123 | #define ZIP_ER_INVAL 18 /* N Invalid argument */ 124 | #define ZIP_ER_NOZIP 19 /* N Not a zip archive */ 125 | #define ZIP_ER_INTERNAL 20 /* N Internal error */ 126 | #define ZIP_ER_INCONS 21 /* N Zip archive inconsistent */ 127 | #define ZIP_ER_REMOVE 22 /* S Can't remove file */ 128 | #define ZIP_ER_DELETED 23 /* N Entry has been deleted */ 129 | #define ZIP_ER_ENCRNOTSUPP 24 /* N Encryption method not supported */ 130 | #define ZIP_ER_RDONLY 25 /* N Read-only archive */ 131 | #define ZIP_ER_NOPASSWD 26 /* N No password provided */ 132 | #define ZIP_ER_WRONGPASSWD 27 /* N Wrong password provided */ 133 | #define ZIP_ER_OPNOTSUPP 28 /* N Operation not supported */ 134 | #define ZIP_ER_INUSE 29 /* N Resource still in use */ 135 | #define ZIP_ER_TELL 30 /* S Tell error */ 136 | #define ZIP_ER_COMPRESSED_DATA 31 /* N Compressed data invalid */ 137 | #define ZIP_ER_CANCELLED 32 /* N Operation cancelled */ 138 | 139 | /* type of system error value */ 140 | 141 | #define ZIP_ET_NONE 0 /* sys_err unused */ 142 | #define ZIP_ET_SYS 1 /* sys_err is errno */ 143 | #define ZIP_ET_ZLIB 2 /* sys_err is zlib error code */ 144 | 145 | /* compression methods */ 146 | 147 | #define ZIP_CM_DEFAULT -1 /* better of deflate or store */ 148 | #define ZIP_CM_STORE 0 /* stored (uncompressed) */ 149 | #define ZIP_CM_SHRINK 1 /* shrunk */ 150 | #define ZIP_CM_REDUCE_1 2 /* reduced with factor 1 */ 151 | #define ZIP_CM_REDUCE_2 3 /* reduced with factor 2 */ 152 | #define ZIP_CM_REDUCE_3 4 /* reduced with factor 3 */ 153 | #define ZIP_CM_REDUCE_4 5 /* reduced with factor 4 */ 154 | #define ZIP_CM_IMPLODE 6 /* imploded */ 155 | /* 7 - Reserved for Tokenizing compression algorithm */ 156 | #define ZIP_CM_DEFLATE 8 /* deflated */ 157 | #define ZIP_CM_DEFLATE64 9 /* deflate64 */ 158 | #define ZIP_CM_PKWARE_IMPLODE 10 /* PKWARE imploding */ 159 | /* 11 - Reserved by PKWARE */ 160 | #define ZIP_CM_BZIP2 12 /* compressed using BZIP2 algorithm */ 161 | /* 13 - Reserved by PKWARE */ 162 | #define ZIP_CM_LZMA 14 /* LZMA (EFS) */ 163 | /* 15-17 - Reserved by PKWARE */ 164 | #define ZIP_CM_TERSE 18 /* compressed using IBM TERSE (new) */ 165 | #define ZIP_CM_LZ77 19 /* IBM LZ77 z Architecture (PFS) */ 166 | #define ZIP_CM_LZMA2 33 167 | #define ZIP_CM_XZ 95 /* XZ compressed data */ 168 | #define ZIP_CM_JPEG 96 /* Compressed Jpeg data */ 169 | #define ZIP_CM_WAVPACK 97 /* WavPack compressed data */ 170 | #define ZIP_CM_PPMD 98 /* PPMd version I, Rev 1 */ 171 | 172 | /* encryption methods */ 173 | 174 | #define ZIP_EM_NONE 0 /* not encrypted */ 175 | #define ZIP_EM_TRAD_PKWARE 1 /* traditional PKWARE encryption */ 176 | #if 0 /* Strong Encryption Header not parsed yet */ 177 | #define ZIP_EM_DES 0x6601 /* strong encryption: DES */ 178 | #define ZIP_EM_RC2_OLD 0x6602 /* strong encryption: RC2, version < 5.2 */ 179 | #define ZIP_EM_3DES_168 0x6603 180 | #define ZIP_EM_3DES_112 0x6609 181 | #define ZIP_EM_PKZIP_AES_128 0x660e 182 | #define ZIP_EM_PKZIP_AES_192 0x660f 183 | #define ZIP_EM_PKZIP_AES_256 0x6610 184 | #define ZIP_EM_RC2 0x6702 /* strong encryption: RC2, version >= 5.2 */ 185 | #define ZIP_EM_RC4 0x6801 186 | #endif 187 | #define ZIP_EM_AES_128 0x0101 /* Winzip AES encryption */ 188 | #define ZIP_EM_AES_192 0x0102 189 | #define ZIP_EM_AES_256 0x0103 190 | #define ZIP_EM_UNKNOWN 0xffff /* unknown algorithm */ 191 | 192 | #define ZIP_OPSYS_DOS 0x00u 193 | #define ZIP_OPSYS_AMIGA 0x01u 194 | #define ZIP_OPSYS_OPENVMS 0x02u 195 | #define ZIP_OPSYS_UNIX 0x03u 196 | #define ZIP_OPSYS_VM_CMS 0x04u 197 | #define ZIP_OPSYS_ATARI_ST 0x05u 198 | #define ZIP_OPSYS_OS_2 0x06u 199 | #define ZIP_OPSYS_MACINTOSH 0x07u 200 | #define ZIP_OPSYS_Z_SYSTEM 0x08u 201 | #define ZIP_OPSYS_CPM 0x09u 202 | #define ZIP_OPSYS_WINDOWS_NTFS 0x0au 203 | #define ZIP_OPSYS_MVS 0x0bu 204 | #define ZIP_OPSYS_VSE 0x0cu 205 | #define ZIP_OPSYS_ACORN_RISC 0x0du 206 | #define ZIP_OPSYS_VFAT 0x0eu 207 | #define ZIP_OPSYS_ALTERNATE_MVS 0x0fu 208 | #define ZIP_OPSYS_BEOS 0x10u 209 | #define ZIP_OPSYS_TANDEM 0x11u 210 | #define ZIP_OPSYS_OS_400 0x12u 211 | #define ZIP_OPSYS_OS_X 0x13u 212 | 213 | #define ZIP_OPSYS_DEFAULT ZIP_OPSYS_UNIX 214 | 215 | 216 | enum zip_source_cmd { 217 | ZIP_SOURCE_OPEN, /* prepare for reading */ 218 | ZIP_SOURCE_READ, /* read data */ 219 | ZIP_SOURCE_CLOSE, /* reading is done */ 220 | ZIP_SOURCE_STAT, /* get meta information */ 221 | ZIP_SOURCE_ERROR, /* get error information */ 222 | ZIP_SOURCE_FREE, /* cleanup and free resources */ 223 | ZIP_SOURCE_SEEK, /* set position for reading */ 224 | ZIP_SOURCE_TELL, /* get read position */ 225 | ZIP_SOURCE_BEGIN_WRITE, /* prepare for writing */ 226 | ZIP_SOURCE_COMMIT_WRITE, /* writing is done */ 227 | ZIP_SOURCE_ROLLBACK_WRITE, /* discard written changes */ 228 | ZIP_SOURCE_WRITE, /* write data */ 229 | ZIP_SOURCE_SEEK_WRITE, /* set position for writing */ 230 | ZIP_SOURCE_TELL_WRITE, /* get write position */ 231 | ZIP_SOURCE_SUPPORTS, /* check whether source supports command */ 232 | ZIP_SOURCE_REMOVE, /* remove file */ 233 | ZIP_SOURCE_RESERVED_1, /* previously used internally */ 234 | ZIP_SOURCE_BEGIN_WRITE_CLONING, /* like ZIP_SOURCE_BEGIN_WRITE, but keep part of original file */ 235 | ZIP_SOURCE_ACCEPT_EMPTY, /* whether empty files are valid archives */ 236 | ZIP_SOURCE_GET_FILE_ATTRIBUTES /* get additional file attributes */ 237 | }; 238 | typedef enum zip_source_cmd zip_source_cmd_t; 239 | 240 | #define ZIP_SOURCE_MAKE_COMMAND_BITMASK(cmd) (((zip_int64_t)1) << (cmd)) 241 | 242 | /* clang-format off */ 243 | 244 | #define ZIP_SOURCE_SUPPORTS_READABLE (ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_OPEN) \ 245 | | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_READ) \ 246 | | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_CLOSE) \ 247 | | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_STAT) \ 248 | | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ERROR) \ 249 | | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_FREE)) 250 | 251 | #define ZIP_SOURCE_SUPPORTS_SEEKABLE (ZIP_SOURCE_SUPPORTS_READABLE \ 252 | | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK) \ 253 | | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_TELL) \ 254 | | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SUPPORTS)) 255 | 256 | #define ZIP_SOURCE_SUPPORTS_WRITABLE (ZIP_SOURCE_SUPPORTS_SEEKABLE \ 257 | | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE) \ 258 | | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_COMMIT_WRITE) \ 259 | | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ROLLBACK_WRITE) \ 260 | | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_WRITE) \ 261 | | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK_WRITE) \ 262 | | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_TELL_WRITE) \ 263 | | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_REMOVE)) 264 | 265 | /* clang-format on */ 266 | 267 | /* for use by sources */ 268 | struct zip_source_args_seek { 269 | zip_int64_t offset; 270 | int whence; 271 | }; 272 | 273 | typedef struct zip_source_args_seek zip_source_args_seek_t; 274 | #define ZIP_SOURCE_GET_ARGS(type, data, len, error) ((len) < sizeof(type) ? zip_error_set((error), ZIP_ER_INVAL, 0), (type *)NULL : (type *)(data)) 275 | 276 | 277 | /* error information */ 278 | /* use zip_error_*() to access */ 279 | struct zip_error { 280 | int zip_err; /* libzip error code (ZIP_ER_*) */ 281 | int sys_err; /* copy of errno (E*) or zlib error code */ 282 | char *_Nullable str; /* string representation or NULL */ 283 | }; 284 | 285 | #define ZIP_STAT_NAME 0x0001u 286 | #define ZIP_STAT_INDEX 0x0002u 287 | #define ZIP_STAT_SIZE 0x0004u 288 | #define ZIP_STAT_COMP_SIZE 0x0008u 289 | #define ZIP_STAT_MTIME 0x0010u 290 | #define ZIP_STAT_CRC 0x0020u 291 | #define ZIP_STAT_COMP_METHOD 0x0040u 292 | #define ZIP_STAT_ENCRYPTION_METHOD 0x0080u 293 | #define ZIP_STAT_FLAGS 0x0100u 294 | 295 | struct zip_stat { 296 | zip_uint64_t valid; /* which fields have valid values */ 297 | const char *_Nullable name; /* name of the file */ 298 | zip_uint64_t index; /* index within archive */ 299 | zip_uint64_t size; /* size of file (uncompressed) */ 300 | zip_uint64_t comp_size; /* size of file (compressed) */ 301 | time_t mtime; /* modification time */ 302 | zip_uint32_t crc; /* crc of file data */ 303 | zip_uint16_t comp_method; /* compression method used */ 304 | zip_uint16_t encryption_method; /* encryption method used */ 305 | zip_uint32_t flags; /* reserved for future use */ 306 | }; 307 | 308 | struct zip_buffer_fragment { 309 | zip_uint8_t *_Nonnull data; 310 | zip_uint64_t length; 311 | }; 312 | 313 | struct zip_file_attributes { 314 | zip_uint64_t valid; /* which fields have valid values */ 315 | zip_uint8_t version; /* version of this struct, currently 1 */ 316 | zip_uint8_t host_system; /* host system on which file was created */ 317 | zip_uint8_t ascii; /* flag whether file is ASCII text */ 318 | zip_uint8_t version_needed; /* minimum version needed to extract file */ 319 | zip_uint32_t external_file_attributes; /* external file attributes (host-system specific) */ 320 | zip_uint16_t general_purpose_bit_flags; /* general purpose big flags, only some bits are honored */ 321 | zip_uint16_t general_purpose_bit_mask; /* which bits in general_purpose_bit_flags are valid */ 322 | }; 323 | 324 | #define ZIP_FILE_ATTRIBUTES_HOST_SYSTEM 0x0001u 325 | #define ZIP_FILE_ATTRIBUTES_ASCII 0x0002u 326 | #define ZIP_FILE_ATTRIBUTES_VERSION_NEEDED 0x0004u 327 | #define ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES 0x0008u 328 | #define ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS 0x0010u 329 | 330 | struct zip; 331 | struct zip_file; 332 | struct zip_source; 333 | 334 | typedef struct zip zip_t; 335 | typedef struct zip_error zip_error_t; 336 | typedef struct zip_file zip_file_t; 337 | typedef struct zip_file_attributes zip_file_attributes_t; 338 | typedef struct zip_source zip_source_t; 339 | typedef struct zip_stat zip_stat_t; 340 | typedef struct zip_buffer_fragment zip_buffer_fragment_t; 341 | 342 | typedef zip_uint32_t zip_flags_t; 343 | 344 | typedef zip_int64_t (*zip_source_callback)(void *_Nullable, void *_Nullable, zip_uint64_t, zip_source_cmd_t); 345 | typedef void (*zip_progress_callback)(zip_t *_Nonnull, double, void *_Nullable); 346 | typedef int (*zip_cancel_callback)(zip_t *_Nonnull, void *_Nullable); 347 | 348 | #ifndef ZIP_DISABLE_DEPRECATED 349 | typedef void (*zip_progress_callback_t)(double); 350 | ZIP_EXTERN void zip_register_progress_callback(zip_t *_Nonnull, zip_progress_callback_t _Nullable); /* use zip_register_progress_callback_with_state */ 351 | 352 | ZIP_EXTERN zip_int64_t zip_add(zip_t *_Nonnull, const char *_Nonnull, zip_source_t *_Nonnull); /* use zip_file_add */ 353 | ZIP_EXTERN zip_int64_t zip_add_dir(zip_t *_Nonnull, const char *_Nonnull); /* use zip_dir_add */ 354 | ZIP_EXTERN const char *_Nullable zip_get_file_comment(zip_t *_Nonnull, zip_uint64_t, int *_Nullable, int); /* use zip_file_get_comment */ 355 | ZIP_EXTERN int zip_get_num_files(zip_t *_Nonnull); /* use zip_get_num_entries instead */ 356 | ZIP_EXTERN int zip_rename(zip_t *_Nonnull, zip_uint64_t, const char *_Nonnull); /* use zip_file_rename */ 357 | ZIP_EXTERN int zip_replace(zip_t *_Nonnull, zip_uint64_t, zip_source_t *_Nonnull); /* use zip_file_replace */ 358 | ZIP_EXTERN int zip_set_file_comment(zip_t *_Nonnull, zip_uint64_t, const char *_Nullable, int); /* use zip_file_set_comment */ 359 | ZIP_EXTERN int zip_error_get_sys_type(int); /* use zip_error_system_type */ 360 | ZIP_EXTERN void zip_error_get(zip_t *_Nonnull, int *_Nullable, int *_Nullable); /* use zip_get_error, zip_error_code_zip / zip_error_code_system */ 361 | ZIP_EXTERN int zip_error_to_str(char *_Nonnull, zip_uint64_t, int, int); /* use zip_error_init_with_code / zip_error_strerror */ 362 | ZIP_EXTERN void zip_file_error_get(zip_file_t *_Nonnull, int *_Nullable, int *_Nullable); /* use zip_file_get_error, zip_error_code_zip / zip_error_code_system */ 363 | #endif 364 | 365 | ZIP_EXTERN int zip_close(zip_t *_Nonnull); 366 | ZIP_EXTERN int zip_delete(zip_t *_Nonnull, zip_uint64_t); 367 | ZIP_EXTERN zip_int64_t zip_dir_add(zip_t *_Nonnull, const char *_Nonnull, zip_flags_t); 368 | ZIP_EXTERN void zip_discard(zip_t *_Nonnull); 369 | 370 | ZIP_EXTERN zip_error_t *_Nonnull zip_get_error(zip_t *_Nonnull); 371 | ZIP_EXTERN void zip_error_clear(zip_t *_Nonnull); 372 | ZIP_EXTERN int zip_error_code_zip(const zip_error_t *_Nonnull); 373 | ZIP_EXTERN int zip_error_code_system(const zip_error_t *_Nonnull); 374 | ZIP_EXTERN void zip_error_fini(zip_error_t *_Nonnull); 375 | ZIP_EXTERN void zip_error_init(zip_error_t *_Nonnull); 376 | ZIP_EXTERN void zip_error_init_with_code(zip_error_t *_Nonnull, int); 377 | ZIP_EXTERN void zip_error_set(zip_error_t *_Nullable, int, int); 378 | ZIP_EXTERN const char *_Nonnull zip_error_strerror(zip_error_t *_Nonnull); 379 | ZIP_EXTERN int zip_error_system_type(const zip_error_t *_Nonnull); 380 | ZIP_EXTERN zip_int64_t zip_error_to_data(const zip_error_t *_Nonnull, void *_Nonnull, zip_uint64_t); 381 | 382 | ZIP_EXTERN int zip_fclose(zip_file_t *_Nonnull); 383 | ZIP_EXTERN zip_t *_Nullable zip_fdopen(int, int, int *_Nullable); 384 | ZIP_EXTERN zip_int64_t zip_file_add(zip_t *_Nonnull, const char *_Nonnull, zip_source_t *_Nonnull, zip_flags_t); 385 | ZIP_EXTERN void zip_file_attributes_init(zip_file_attributes_t *_Nonnull); 386 | ZIP_EXTERN void zip_file_error_clear(zip_file_t *_Nonnull); 387 | ZIP_EXTERN int zip_file_extra_field_delete(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_flags_t); 388 | ZIP_EXTERN int zip_file_extra_field_delete_by_id(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_uint16_t, zip_flags_t); 389 | ZIP_EXTERN int zip_file_extra_field_set(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_uint16_t, const zip_uint8_t *_Nullable, zip_uint16_t, zip_flags_t); 390 | ZIP_EXTERN zip_int16_t zip_file_extra_fields_count(zip_t *_Nonnull, zip_uint64_t, zip_flags_t); 391 | ZIP_EXTERN zip_int16_t zip_file_extra_fields_count_by_id(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_flags_t); 392 | ZIP_EXTERN const zip_uint8_t *_Nullable zip_file_extra_field_get(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_uint16_t *_Nullable, zip_uint16_t *_Nullable, zip_flags_t); 393 | ZIP_EXTERN const zip_uint8_t *_Nullable zip_file_extra_field_get_by_id(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_uint16_t, zip_uint16_t *_Nullable, zip_flags_t); 394 | ZIP_EXTERN const char *_Nullable zip_file_get_comment(zip_t *_Nonnull, zip_uint64_t, zip_uint32_t *_Nullable, zip_flags_t); 395 | ZIP_EXTERN zip_error_t *_Nonnull zip_file_get_error(zip_file_t *_Nonnull); 396 | ZIP_EXTERN int zip_file_get_external_attributes(zip_t *_Nonnull, zip_uint64_t, zip_flags_t, zip_uint8_t *_Nullable, zip_uint32_t *_Nullable); 397 | ZIP_EXTERN int zip_file_rename(zip_t *_Nonnull, zip_uint64_t, const char *_Nonnull, zip_flags_t); 398 | ZIP_EXTERN int zip_file_replace(zip_t *_Nonnull, zip_uint64_t, zip_source_t *_Nonnull, zip_flags_t); 399 | ZIP_EXTERN int zip_file_set_comment(zip_t *_Nonnull, zip_uint64_t, const char *_Nullable, zip_uint16_t, zip_flags_t); 400 | ZIP_EXTERN int zip_file_set_dostime(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_uint16_t, zip_flags_t); 401 | ZIP_EXTERN int zip_file_set_encryption(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, const char *_Nullable); 402 | ZIP_EXTERN int zip_file_set_external_attributes(zip_t *_Nonnull, zip_uint64_t, zip_flags_t, zip_uint8_t, zip_uint32_t); 403 | ZIP_EXTERN int zip_file_set_mtime(zip_t *_Nonnull, zip_uint64_t, time_t, zip_flags_t); 404 | ZIP_EXTERN const char *_Nonnull zip_file_strerror(zip_file_t *_Nonnull); 405 | ZIP_EXTERN zip_file_t *_Nullable zip_fopen(zip_t *_Nonnull, const char *_Nonnull, zip_flags_t); 406 | ZIP_EXTERN zip_file_t *_Nullable zip_fopen_encrypted(zip_t *_Nonnull, const char *_Nonnull, zip_flags_t, const char *_Nullable); 407 | ZIP_EXTERN zip_file_t *_Nullable zip_fopen_index(zip_t *_Nonnull, zip_uint64_t, zip_flags_t); 408 | ZIP_EXTERN zip_file_t *_Nullable zip_fopen_index_encrypted(zip_t *_Nonnull, zip_uint64_t, zip_flags_t, const char *_Nullable); 409 | ZIP_EXTERN zip_int64_t zip_fread(zip_file_t *_Nonnull, void *_Nonnull, zip_uint64_t); 410 | ZIP_EXTERN zip_int8_t zip_fseek(zip_file_t *_Nonnull, zip_int64_t, int); 411 | ZIP_EXTERN zip_int64_t zip_ftell(zip_file_t *_Nonnull); 412 | ZIP_EXTERN const char *_Nullable zip_get_archive_comment(zip_t *_Nonnull, int *_Nullable, zip_flags_t); 413 | ZIP_EXTERN int zip_get_archive_flag(zip_t *_Nonnull, zip_flags_t, zip_flags_t); 414 | ZIP_EXTERN const char *_Nullable zip_get_name(zip_t *_Nonnull, zip_uint64_t, zip_flags_t); 415 | ZIP_EXTERN zip_int64_t zip_get_num_entries(zip_t *_Nonnull, zip_flags_t); 416 | ZIP_EXTERN const char *_Nonnull zip_libzip_version(void); 417 | ZIP_EXTERN zip_int64_t zip_name_locate(zip_t *_Nonnull, const char *_Nonnull, zip_flags_t); 418 | ZIP_EXTERN zip_t *_Nullable zip_open(const char *_Nonnull, int, int *_Nullable); 419 | ZIP_EXTERN zip_t *_Nullable zip_open_from_source(zip_source_t *_Nonnull, int, zip_error_t *_Nullable); 420 | ZIP_EXTERN int zip_register_progress_callback_with_state(zip_t *_Nonnull, double, zip_progress_callback _Nullable, void (*_Nullable)(void *_Nullable), void *_Nullable); 421 | ZIP_EXTERN int zip_register_cancel_callback_with_state(zip_t *_Nonnull, zip_cancel_callback _Nullable, void (*_Nullable)(void *_Nullable), void *_Nullable); 422 | ZIP_EXTERN int zip_set_archive_comment(zip_t *_Nonnull, const char *_Nullable, zip_uint16_t); 423 | ZIP_EXTERN int zip_set_archive_flag(zip_t *_Nonnull, zip_flags_t, int); 424 | ZIP_EXTERN int zip_set_default_password(zip_t *_Nonnull, const char *_Nullable); 425 | ZIP_EXTERN int zip_set_file_compression(zip_t *_Nonnull, zip_uint64_t, zip_int32_t, zip_uint32_t); 426 | ZIP_EXTERN int zip_source_begin_write(zip_source_t *_Nonnull); 427 | ZIP_EXTERN int zip_source_begin_write_cloning(zip_source_t *_Nonnull, zip_uint64_t); 428 | ZIP_EXTERN zip_source_t *_Nullable zip_source_buffer(zip_t *_Nonnull, const void *_Nullable, zip_uint64_t, int); 429 | ZIP_EXTERN zip_source_t *_Nullable zip_source_buffer_create(const void *_Nullable, zip_uint64_t, int, zip_error_t *_Nullable); 430 | ZIP_EXTERN zip_source_t *_Nullable zip_source_buffer_fragment(zip_t *_Nonnull, const zip_buffer_fragment_t *_Nonnull, zip_uint64_t, int); 431 | ZIP_EXTERN zip_source_t *_Nullable zip_source_buffer_fragment_create(const zip_buffer_fragment_t *_Nullable, zip_uint64_t, int, zip_error_t *_Nullable); 432 | ZIP_EXTERN int zip_source_close(zip_source_t *_Nonnull); 433 | ZIP_EXTERN int zip_source_commit_write(zip_source_t *_Nonnull); 434 | ZIP_EXTERN zip_error_t *_Nonnull zip_source_error(zip_source_t *_Nonnull); 435 | ZIP_EXTERN zip_source_t *_Nullable zip_source_file(zip_t *_Nonnull, const char *_Nonnull, zip_uint64_t, zip_int64_t); 436 | ZIP_EXTERN zip_source_t *_Nullable zip_source_file_create(const char *_Nonnull, zip_uint64_t, zip_int64_t, zip_error_t *_Nullable); 437 | ZIP_EXTERN zip_source_t *_Nullable zip_source_filep(zip_t *_Nonnull, FILE *_Nonnull, zip_uint64_t, zip_int64_t); 438 | ZIP_EXTERN zip_source_t *_Nullable zip_source_filep_create(FILE *_Nonnull, zip_uint64_t, zip_int64_t, zip_error_t *_Nullable); 439 | ZIP_EXTERN void zip_source_free(zip_source_t *_Nullable); 440 | ZIP_EXTERN zip_source_t *_Nullable zip_source_function(zip_t *_Nonnull, zip_source_callback _Nonnull, void *_Nullable); 441 | ZIP_EXTERN zip_source_t *_Nullable zip_source_function_create(zip_source_callback _Nonnull, void *_Nullable, zip_error_t *_Nullable); 442 | ZIP_EXTERN int zip_source_get_file_attributes(zip_source_t *_Nonnull, zip_file_attributes_t *_Nonnull); 443 | ZIP_EXTERN int zip_source_is_deleted(zip_source_t *_Nonnull); 444 | ZIP_EXTERN void zip_source_keep(zip_source_t *_Nonnull); 445 | ZIP_EXTERN zip_int64_t zip_source_make_command_bitmap(zip_source_cmd_t, ...); 446 | ZIP_EXTERN int zip_source_open(zip_source_t *_Nonnull); 447 | ZIP_EXTERN zip_int64_t zip_source_read(zip_source_t *_Nonnull, void *_Nonnull, zip_uint64_t); 448 | ZIP_EXTERN void zip_source_rollback_write(zip_source_t *_Nonnull); 449 | ZIP_EXTERN int zip_source_seek(zip_source_t *_Nonnull, zip_int64_t, int); 450 | ZIP_EXTERN zip_int64_t zip_source_seek_compute_offset(zip_uint64_t, zip_uint64_t, void *_Nonnull, zip_uint64_t, zip_error_t *_Nullable); 451 | ZIP_EXTERN int zip_source_seek_write(zip_source_t *_Nonnull, zip_int64_t, int); 452 | ZIP_EXTERN int zip_source_stat(zip_source_t *_Nonnull, zip_stat_t *_Nonnull); 453 | ZIP_EXTERN zip_int64_t zip_source_tell(zip_source_t *_Nonnull); 454 | ZIP_EXTERN zip_int64_t zip_source_tell_write(zip_source_t *_Nonnull); 455 | #ifdef _WIN32 456 | ZIP_EXTERN zip_source_t *zip_source_win32a(zip_t *, const char *, zip_uint64_t, zip_int64_t); 457 | ZIP_EXTERN zip_source_t *zip_source_win32a_create(const char *, zip_uint64_t, zip_int64_t, zip_error_t *); 458 | ZIP_EXTERN zip_source_t *zip_source_win32handle(zip_t *, void *, zip_uint64_t, zip_int64_t); 459 | ZIP_EXTERN zip_source_t *zip_source_win32handle_create(void *, zip_uint64_t, zip_int64_t, zip_error_t *); 460 | ZIP_EXTERN zip_source_t *zip_source_win32w(zip_t *, const wchar_t *, zip_uint64_t, zip_int64_t); 461 | ZIP_EXTERN zip_source_t *zip_source_win32w_create(const wchar_t *, zip_uint64_t, zip_int64_t, zip_error_t *); 462 | #endif 463 | ZIP_EXTERN zip_int64_t zip_source_write(zip_source_t *_Nonnull, const void *_Nullable, zip_uint64_t); 464 | ZIP_EXTERN zip_source_t *_Nullable zip_source_zip(zip_t *_Nonnull, zip_t *_Nonnull, zip_uint64_t, zip_flags_t, zip_uint64_t, zip_int64_t); 465 | ZIP_EXTERN int zip_stat(zip_t *_Nonnull, const char *_Nonnull, zip_flags_t, zip_stat_t *_Nonnull); 466 | ZIP_EXTERN int zip_stat_index(zip_t *_Nonnull, zip_uint64_t, zip_flags_t, zip_stat_t *_Nonnull); 467 | ZIP_EXTERN void zip_stat_init(zip_stat_t *_Nonnull); 468 | ZIP_EXTERN const char *_Nonnull zip_strerror(zip_t *_Nonnull); 469 | ZIP_EXTERN int zip_unchange(zip_t *_Nonnull, zip_uint64_t); 470 | ZIP_EXTERN int zip_unchange_all(zip_t *_Nonnull); 471 | ZIP_EXTERN int zip_unchange_archive(zip_t *_Nonnull); 472 | ZIP_EXTERN int zip_compression_method_supported(zip_int32_t method, int compress); 473 | ZIP_EXTERN int zip_encryption_method_supported(zip_uint16_t method, int encode); 474 | 475 | #ifdef __cplusplus 476 | } 477 | #endif 478 | 479 | #endif /* _HAD_ZIP_H */ -------------------------------------------------------------------------------- /appinst/zipconf.h: -------------------------------------------------------------------------------- 1 | #ifndef _HAD_ZIPCONF_H 2 | #define _HAD_ZIPCONF_H 3 | 4 | /* 5 | zipconf.h -- platform specific include file 6 | 7 | This file was generated automatically by CMake 8 | based on ../cmake-zipconf.h.in. 9 | */ 10 | 11 | #define LIBZIP_VERSION "1.7.3" 12 | #define LIBZIP_VERSION_MAJOR 1 13 | #define LIBZIP_VERSION_MINOR 7 14 | #define LIBZIP_VERSION_MICRO 3 15 | 16 | /* #undef ZIP_STATIC */ 17 | 18 | 19 | 20 | #if !defined(__STDC_FORMAT_MACROS) 21 | #define __STDC_FORMAT_MACROS 1 22 | #endif 23 | #include 24 | 25 | typedef int8_t zip_int8_t; 26 | typedef uint8_t zip_uint8_t; 27 | typedef int16_t zip_int16_t; 28 | typedef uint16_t zip_uint16_t; 29 | typedef int32_t zip_int32_t; 30 | typedef uint32_t zip_uint32_t; 31 | typedef int64_t zip_int64_t; 32 | typedef uint64_t zip_uint64_t; 33 | 34 | #define ZIP_INT8_MIN (-ZIP_INT8_MAX-1) 35 | #define ZIP_INT8_MAX 0x7f 36 | #define ZIP_UINT8_MAX 0xff 37 | 38 | #define ZIP_INT16_MIN (-ZIP_INT16_MAX-1) 39 | #define ZIP_INT16_MAX 0x7fff 40 | #define ZIP_UINT16_MAX 0xffff 41 | 42 | #define ZIP_INT32_MIN (-ZIP_INT32_MAX-1L) 43 | #define ZIP_INT32_MAX 0x7fffffffL 44 | #define ZIP_UINT32_MAX 0xffffffffLU 45 | 46 | #define ZIP_INT64_MIN (-ZIP_INT64_MAX-1LL) 47 | #define ZIP_INT64_MAX 0x7fffffffffffffffLL 48 | #define ZIP_UINT64_MAX 0xffffffffffffffffULL 49 | 50 | #endif /* zipconf.h */ -------------------------------------------------------------------------------- /asu_inject/Makefile: -------------------------------------------------------------------------------- 1 | ARCHS = armv7 armv7s arm64 2 | 3 | include $(THEOS)/makefiles/common.mk 4 | 5 | TOOL_NAME = asu_inject 6 | 7 | asu_inject_FILES = asu_inject.c 8 | asu_inject_CFLAGS += -fvisibility=hidden 9 | asu_inject_INSTALL_PATH = /usr/bin 10 | asu_inject_CODESIGN_FLAGS = -S../entitlements.plist 11 | 12 | include $(THEOS_MAKE_PATH)/tool.mk 13 | -------------------------------------------------------------------------------- /asu_inject/asu_inject.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define DPKG_PATH ROOT_PATH("/var/lib/dpkg/info/ai.akemi.appsyncunified.list") 15 | 16 | extern char ***_NSGetEnviron(void); 17 | extern int proc_listallpids(void *, int); 18 | extern int proc_pidpath(int, void *, uint32_t); 19 | 20 | static const char *cynject_path = ROOT_PATH("/usr/bin/cynject"); 21 | static const char *inject_criticald_path = ROOT_PATH("/electra/inject_criticald"); 22 | static const char *dylib_path = ROOT_PATH("/Library/MobileSubstrate/DynamicLibraries/AppSyncUnified-installd.dylib"); 23 | static const char *dispatch_queue_name = NULL; 24 | static const char *process_name = "installd"; 25 | static int process_buffer_size = 4096; 26 | static pid_t process_pid = -1; 27 | 28 | static boolean_t find_process(const char *name, pid_t *ppid_ret) { 29 | pid_t *pid_buffer; 30 | char path_buffer[MAXPATHLEN]; 31 | int count, i, ret; 32 | boolean_t res = FALSE; 33 | 34 | pid_buffer = (pid_t *)calloc(1, process_buffer_size); 35 | assert(pid_buffer != NULL); 36 | 37 | count = proc_listallpids(pid_buffer, process_buffer_size); 38 | if (count) { 39 | for (i = 0; i < count; i++) { 40 | pid_t ppid = pid_buffer[i]; 41 | 42 | ret = proc_pidpath(ppid, (void *)path_buffer, sizeof(path_buffer)); 43 | if (ret < 0) { 44 | printf("(%s:%d) proc_pidinfo() call failed.\n", __FILE__, __LINE__); 45 | continue; 46 | } 47 | 48 | if (strstr(path_buffer, name)) { 49 | res = TRUE; 50 | *ppid_ret = ppid; 51 | break; 52 | } 53 | } 54 | } 55 | 56 | free(pid_buffer); 57 | return res; 58 | } 59 | 60 | static const char *determine_suitable_injector() { 61 | // asu_inject should not be necessary on anything that's not 32-bit iOS 9.3.x, where cynject is pretty much guaranteed to be available. 62 | // That being said, I might as well add support for inject_criticald to asu_inject just for… uh, futureproofing purposes? 63 | // In general, asu_inject should not be necessary at all. It being required at all on any iOS version / jailbreak is merely a workaround for an old bug that will probably never be fixed. 64 | 65 | // ※ TODO: ElleKit appears to lack a suitable analogue to cynject. 66 | if (access(inject_criticald_path, X_OK) == 0) { 67 | return inject_criticald_path; 68 | } 69 | 70 | return cynject_path; 71 | } 72 | 73 | static void inject_dylib(const char *name, pid_t pid, const char *dylib) { 74 | char **argv; 75 | char pid_buf[32]; 76 | int res; 77 | pid_t child; 78 | 79 | argv = calloc(4, sizeof(char *)); 80 | assert(argv != NULL); 81 | 82 | snprintf(pid_buf, sizeof(pid_buf), "%d", pid); 83 | 84 | argv[0] = (char *)name; 85 | argv[1] = (char *)pid_buf; 86 | argv[2] = (char *)dylib; 87 | argv[3] = NULL; 88 | 89 | printf("(%s:%d) calling \"%s %s %s\"\n", __FILE__, __LINE__, argv[0], argv[1], argv[2]); 90 | 91 | res = posix_spawn(&child, argv[0], NULL, NULL, argv, (char * const *)_NSGetEnviron()); 92 | assert(res == 0); 93 | 94 | return; 95 | } 96 | 97 | int main(int argc, char *argv[]) { 98 | printf("asu_inject for AppSync Unified\n"); 99 | printf("Copyright (C) 2014-2024 Karen/あけみ\n"); 100 | if (access(DPKG_PATH, F_OK) == -1) { 101 | printf("You seem to have installed AppSync Unified from an APT repository that is not cydia.akemi.ai.\n"); 102 | printf("Please make sure that you download AppSync Unified from the official repository to ensure proper operation.\n"); 103 | } 104 | 105 | if (geteuid() != 0) { 106 | printf("FATAL: asu_inject must be run as root.\n"); 107 | return 1; 108 | } 109 | 110 | if ((access(cynject_path, X_OK) == -1) && (access(inject_criticald_path, X_OK) == -1)) { 111 | printf("FATAL: Unable to locate any suitable injectors! (%s, %s)\n", cynject_path, inject_criticald_path); 112 | printf("If you are certain that they exist on your filesystem, please make sure their filesystem permissions are set correctly and that they are executable.\n"); 113 | return 1; 114 | } 115 | 116 | printf("Creating queue…\n"); 117 | dispatch_queue_t queue = dispatch_queue_create(dispatch_queue_name, 0); 118 | 119 | printf("Finding installd PID…\n"); 120 | dispatch_async(queue, ^{ 121 | while (!find_process(process_name, &process_pid)); 122 | }); 123 | 124 | printf("Waiting for queue to come back…\n"); 125 | dispatch_sync(queue, ^{}); 126 | 127 | printf("installd's PID is %d\n", process_pid); 128 | 129 | printf("Injecting AppSyncUnified-installd.dylib into installd…\n"); 130 | inject_dylib(determine_suitable_injector(), process_pid, dylib_path); 131 | 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /changelog-inline.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |
6 |
7 | 8 |
9 |
10 |
11 |
12 |
13 | 14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 23 |
24 |
25 |
26 |
27 | 28 |
29 |
30 |
31 |
32 | 33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 41 |
42 |
43 |
44 |
45 | 46 |
47 |
48 |
49 |
50 | 51 |
52 |
53 |
54 |
55 | 56 |
57 |
58 |
59 |
60 | 61 |
62 |
63 |
64 |
65 | 66 |
67 |
68 |
69 |
70 | 71 |
72 |
73 |
74 |
75 | 76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | 86 |
87 |
88 |
89 |
90 | 91 |
92 |
93 |
94 |
95 | 96 |
97 |
98 |
99 |
100 | 101 |
102 |
103 |
104 |
105 | 106 |
107 |
108 |
109 |
110 | 111 |
112 |
113 |
114 |
115 |
116 | 117 |
118 |
119 |
120 |
121 |
122 | 123 |
124 |
125 |
126 |
127 | 128 |
129 |
130 |
131 |
132 |
133 | 134 |
135 |
136 |
137 |
138 |
139 | 140 |
141 |
142 |
143 |
144 |
145 | 146 |
147 |
148 |
149 | 150 |
151 |
152 |
153 |
154 | 155 |
156 |
157 |
158 | 159 |
160 |
161 |
162 | 163 |
164 |
165 |
166 | 167 |
168 |
169 |
170 |
171 | 172 |
173 |
174 |
175 | 176 |
177 |
178 |
179 | 180 |
181 |
182 |
183 | 184 |
185 |
186 |
187 |
188 | 189 |
190 |
191 |
192 |
193 | 194 |
195 |
196 |
197 | 198 |
199 |
200 |
201 |
202 | 203 |
204 |
205 |
206 |
207 | 208 |
209 |
210 |
211 | 212 |
213 |
214 |
215 | 216 |
217 |
218 |
219 | 220 |
221 |
222 |
223 |
224 | 225 |
226 |
227 |
228 | 229 |
230 |
231 |
232 | 233 |
234 |
235 |
236 | 237 |
238 |
239 |
240 |
241 | 242 |
243 |
244 |
245 | 246 |
247 |
248 |
249 | 250 |
251 |
252 |
253 | 254 |
255 |
256 |
257 | 258 |
259 |
260 |
261 |
-------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Conflicts: us.hackulo.appsync50, us.hackulo.appsync50plus, com.hackyouriphone.appsync7, com.teiron.ppsync, appsync50plus2.25pp, com.linusyang.appsync, com.sull.appsyncunified, com.sull.appsyncunifiedbeta, com.xsellize.appsync50, com.xsellize.appsync50plus, appsync50plus.178, appsync50plus2.178, cydia.net.angelxwind.appsyncunified, com.ifucker.appsync, com.sexyphonetech.appsyncunified, appsyncunifiedofficial, org.digbick.asufixed, cn.mingmei.asu623517234615234813, technology.goated.appsync, technology.goated.appsyncunified, vaginalfisting.appsynctool, science.xnu.substitute, akemi.appsink, akemi.appsyncunifed, akemi.appsyncunifed2, akemi.appsyncunifedpatched, appsyncpatched, appsyncrootless, weaponized.autism.technology.appsyncunified, ellekit (< 1.1) 2 | Depends: firmware (>= 5.0), mobilesubstrate (>= 0.9.5100), firmware (<= 18.2) 3 | Depiction: https://cydia.akemi.ai/?page/ai.akemi.appsyncunified 4 | Maintainer: Karen/あけみ 5 | Homepage: https://cydia.akemi.ai/ 6 | Provides: appsync, net.angelxwind.appsyncunified 7 | Replaces: us.hackulo.appsync50, net.angelxwind.appsyncunified 8 | Package: ai.akemi.appsyncunified 9 | Name: AppSync Unified 10 | Version: 116.0 11 | Architecture: iphoneos-arm 12 | Description: Enables the ability to install unsigned/fakesigned iOS applications. 13 | Section: Tweaks 14 | Author: Karen/あけみ, Linus Yang 15 | -------------------------------------------------------------------------------- /entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | platform-application 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /karentools-build-config: -------------------------------------------------------------------------------- 1 | projectType="iOS-theos" 2 | bundleIdentifier="ai.akemi.appsyncunified" 3 | miniOSVersion="5.0" 4 | maxiOSVersion="latest" 5 | rootlessSupport=1 6 | -------------------------------------------------------------------------------- /layout/Library/LaunchDaemons/ai.akemi.asu_inject.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | RunAtLoad 6 | 7 | KeepAlive 8 | 9 | Label 10 | ai.akemi.asu_inject 11 | ProgramArguments 12 | 13 | /usr/bin/asu_inject 14 | 15 | StandardErrorPath 16 | /dev/null 17 | StandardOutPath 18 | /dev/null 19 | 20 | 21 | -------------------------------------------------------------------------------- /pkg-actions/Makefile: -------------------------------------------------------------------------------- 1 | ARCHS = armv7 armv7s arm64 2 | 3 | include $(THEOS)/makefiles/common.mk 4 | 5 | TOOL_NAME = postinst prerm 6 | 7 | postinst_FILES = pkg-actions.m 8 | postinst_CFLAGS += -fobjc-arc -fvisibility=hidden -DPOSTINST 9 | postinst_FRAMEWORKS = CoreFoundation 10 | postinst_INSTALL_PATH = /DEBIAN 11 | postinst_CODESIGN_FLAGS = -S../entitlements.plist 12 | 13 | prerm_FILES = pkg-actions.m 14 | prerm_CFLAGS += -fobjc-arc -fvisibility=hidden 15 | prerm_FRAMEWORKS = CoreFoundation 16 | prerm_INSTALL_PATH = /DEBIAN 17 | prerm_CODESIGN_FLAGS = -S../entitlements.plist 18 | 19 | include $(THEOS_MAKE_PATH)/tool.mk 20 | -------------------------------------------------------------------------------- /pkg-actions/pkg-actions.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef DEBUG 11 | #define LOG(LogContents, ...) NSLog((@"[AppSync Unified] [pkg-actions] [%s] [L%d] " LogContents), __FUNCTION__, __LINE__, ##__VA_ARGS__) 12 | #else 13 | #define LOG(...) 14 | #endif 15 | 16 | #define DPKG_PATH ROOT_PATH("/var/lib/dpkg/info/ai.akemi.appsyncunified.list") 17 | 18 | #define L_LAUNCHDAEMON_PATH "/Library/LaunchDaemons" 19 | #define SL_LAUNCHDAEMON_PATH "/System" L_LAUNCHDAEMON_PATH 20 | 21 | #define INSTALLD_PLIST_PATH_L ROOT_PATH(L_LAUNCHDAEMON_PATH) "/com.apple.mobile.installd.plist" 22 | #define INSTALLD_PLIST_PATH_SL SL_LAUNCHDAEMON_PATH "/com.apple.mobile.installd.plist" 23 | 24 | #define ASU_INJECT_PLIST_PATH ROOT_PATH(L_LAUNCHDAEMON_PATH) "/ai.akemi.asu_inject.plist" 25 | #define ASU_INJECT_PLIST_PATH_OLD ROOT_PATH(L_LAUNCHDAEMON_PATH) "/net.angelxwind.asu_inject.plist" 26 | 27 | typedef struct __CFUserNotification *CFUserNotificationRef; 28 | FOUNDATION_EXTERN CFUserNotificationRef CFUserNotificationCreate(CFAllocatorRef allocator, CFTimeInterval timeout, CFOptionFlags flags, SInt32 *error, CFDictionaryRef dictionary); 29 | FOUNDATION_EXTERN SInt32 CFUserNotificationReceiveResponse(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags *responseFlags); 30 | 31 | static int run_posix_spawn(const char *args[]) { 32 | pid_t pid; 33 | int status; 34 | posix_spawn(&pid, args[0], NULL, NULL, (char* const*)args, NULL); 35 | waitpid(pid, &status, 0); 36 | return status; 37 | } 38 | 39 | static const char *determine_launchctl_path() { 40 | // launchctl is shipped as part of the jailbreak bootstrap, not iOS itself. 41 | // Some jailbreak bootstraps put launchctl inside /sbin instead of /bin for some reason, which is where it normally resides (at least on macOS). 42 | if (access(ROOT_PATH("/bin/launchctl"), X_OK) == -1) { 43 | return ROOT_PATH("/sbin/launchctl"); 44 | } 45 | return ROOT_PATH("/bin/launchctl"); 46 | } 47 | 48 | static int run_launchctl(const char *path, const char *cmd, bool is_installd) { 49 | LOG("run_launchctl() %s %s\n", cmd, path); 50 | const char *args[] = { determine_launchctl_path(), cmd, path, NULL }; 51 | return run_posix_spawn(args); 52 | } 53 | 54 | int main(int argc, const char **argv) { 55 | @autoreleasepool { 56 | #ifdef POSTINST 57 | LOG("Running postinst…\n"); 58 | #else 59 | LOG("Running prerm…\n"); 60 | #endif 61 | printf("AppSync Unified\n"); 62 | printf("Copyright (C) 2014-2024 Karen/あけみ\n"); 63 | printf("** PLEASE DO NOT USE APPSYNC UNIFIED FOR PIRACY **\n"); 64 | if (access(DPKG_PATH, F_OK) == -1) { 65 | printf("You seem to have installed AppSync Unified from an APT repository that is not cydia.akemi.ai.\n"); 66 | printf("Please make sure that you download AppSync Unified from the official repository to ensure proper operation.\n"); 67 | } 68 | if (geteuid() != 0) { 69 | printf("FATAL: This binary must be run as root. (… Actually, how are you even using dpkg without being root?)\n"); 70 | return 1; 71 | } 72 | 73 | if ((kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) && (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_10_0)) { 74 | #ifdef POSTINST 75 | if (access(INSTALLD_PLIST_PATH_L, F_OK) == -1) { 76 | // NOTE: I thought about removing the symlinked installd plist in the prerm, but decided against it as such an operation has a non-zero chance of somehow going horribly wrong in some kind of edge case. 77 | printf("This device appears to be running iOS 8 or 9. Creating a symbolic link to the installd LaunchDaemon…\n"); 78 | symlink(INSTALLD_PLIST_PATH_SL, INSTALLD_PLIST_PATH_L); 79 | } 80 | #endif 81 | printf("Unloading and stopping the symlinked installd LaunchDaemon…\n"); 82 | run_launchctl(INSTALLD_PLIST_PATH_L, "unload", true); 83 | printf("Reloading and starting the symlinked installd LaunchDaemon…\n"); 84 | run_launchctl(INSTALLD_PLIST_PATH_L, "load", true); 85 | } 86 | 87 | printf("Unloading and stopping the installd LaunchDaemon…\n"); 88 | run_launchctl(INSTALLD_PLIST_PATH_SL, "unload", true); 89 | printf("Reloading and starting the installd LaunchDaemon…\n"); 90 | run_launchctl(INSTALLD_PLIST_PATH_SL, "load", true); 91 | 92 | if (access(ASU_INJECT_PLIST_PATH_OLD, F_OK) != -1) { 93 | // This case should never happen, but I'm adding this check here just in case someone somehow has their system in such a weird state. 94 | printf("Found an old version of the asu_inject LaunchDaemon, unloading and removing it…\n"); 95 | run_launchctl(ASU_INJECT_PLIST_PATH_OLD, "unload", false); 96 | unlink(ASU_INJECT_PLIST_PATH_OLD); 97 | } 98 | 99 | #ifdef __LP64__ 100 | printf("Removing the asu_inject LaunchDaemon, as it's not required on this system.\n"); 101 | unlink(ASU_INJECT_PLIST_PATH); 102 | #else 103 | if ((kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_9_3) && (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_10_0)) { 104 | printf("This device is /probably/ running the Phœnix jailbreak (detected iOS 9.3.x and a 32-bit CPU architecture).\n"); 105 | printf("Due to a bug in Phœnix, the asu_inject LaunchDaemon (which launches /usr/bin/asu_inject once upon boot) is required in order to properly inject AppSync Unified into installd.\n"); 106 | // This path lookup does not need to be rootless-aware for obvious reasons, but we might as well do this just in case someone decides to release a rootless jailbreak for older iOS versions (… whyever anyone would ever want to do that). 107 | if (access(ROOT_PATH("/usr/bin/cynject"), X_OK) != -1) { 108 | printf("Found an executable copy of cynject on this device!\n"); 109 | chown(ASU_INJECT_PLIST_PATH, 0, 0); 110 | chmod(ASU_INJECT_PLIST_PATH, 0644); 111 | printf("Unloading and stopping the asu_inject LaunchDaemon…\n"); 112 | run_launchctl(ASU_INJECT_PLIST_PATH, "unload", false); 113 | #ifdef POSTINST 114 | printf("Reloading and starting the asu_inject LaunchDaemon…\n"); 115 | run_launchctl(ASU_INJECT_PLIST_PATH, "load", false); 116 | #endif 117 | } else { 118 | // Just in case. 119 | printf("Unable to find an executable copy of cynject on this device.\n"); 120 | printf("Removing the asu_inject LaunchDaemon…\n"); 121 | unlink(ASU_INJECT_PLIST_PATH); 122 | } 123 | } 124 | #endif 125 | 126 | printf("****** AppSync Unified installation complete! ******\n"); 127 | 128 | #ifdef POSTINST 129 | // The CFUserNotification will only be shown if: 130 | // ・The user is installing AppSync Unified using an APT frontend that supports the CYDIA environment variable (Cydia, Zebra, Sileo, etc.) 131 | // ・The file /ai.akemi.appsyncunified.no-postinst-notification does not exist in the rootFS or the rootless prefix path. 132 | // ※ This was originally used for KarenTools automated testing, but you can still use it to permanently silence the notification when using APT frontends. 133 | if (getenv("CYDIA") != NULL && access(ROOT_PATH("/ai.akemi.appsyncunified.no-postinst-notification"), F_OK) == -1) { 134 | // TODO: For some reason, this notification doesn't appear on my iOS 10 device. It's a minor bug though, so I'll allow it for now. 135 | // … Even if that makes my perfectionist self scream in intense, agonising pain (🍍˃̶͈̀ロ˂̶͈́)੭ꠥ⁾⁾ 136 | 137 | // Construct the notification 138 | CFUserNotificationRef postinstNotification = CFUserNotificationCreate(kCFAllocatorDefault, 0, 0, NULL, (__bridge CFDictionaryRef)[[NSDictionary alloc] initWithObjectsAndKeys: 139 | [NSString stringWithFormat:@"%@ %@", (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_6_0) ? @"⚠️" : @"⚠", @"IMPORTANT NOTE 🍍"], @"AlertHeader", 140 | @"If AppSync Unified is not working after installation, please reboot your device or perform a userspace reboot (launchctl reboot userspace, ldrestart, etc.) to activate it. You will only need to do this ONCE.", @"AlertMessage", 141 | @"Okay, I understand! (🍍•̀ω•́)୨✨", @"DefaultButtonTitle", nil]); 142 | 143 | // Display the notification using CFUserNotificationCreateRunLoopSource so we don't block the main thread 144 | CFUserNotificationCreateRunLoopSource(kCFAllocatorDefault, postinstNotification, NULL, 0); 145 | } 146 | printf("※ IMPORTANT NOTE: If AppSync Unified is not working after installation, please reboot your device or perform a userspace reboot (launchctl reboot userspace, ldrestart, etc.) to activate it. You will only need to do this ONCE.\n"); 147 | #endif 148 | } 149 | return 0; 150 | } 151 | -------------------------------------------------------------------------------- /redditpost.md: -------------------------------------------------------------------------------- 1 | ### Repo URL: https://cydia.akemi.ai/ ([Tap here on your device to automatically add the repo!](https://cydia.akemi.ai/add.php)) 2 | 3 | #### Support me with [Patreon](https://patreon.com/akemin_dayo), [PayPal (`karen@akemi.ai`)](https://paypal.me/akemindayo), or [Cryptocurrency](https://akemi.ai/?page/links#crypto) 4 | 5 | (A full list of all available donation methods can be found [here](https://akemi.ai/?page/links#donate).) 6 | 7 | Any support is _greatly_ appreciated, but donations are *not* and will *never* be necessary to use my software! 8 | 9 | --- 10 | 11 | # Changelog for 116.0 ([full changelog](https://cydia.akemi.ai/?page/ai.akemi.appsyncunified-changelog)) 12 | 13 | * Added support for all iOS versions up to iOS 18.2. 14 | * No other changes were made to AppSync Unified's functionality or code. 15 | 16 | --- 17 | 18 | # Unified AppSync dynamic library for iOS 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, and 18. Open-source on [GitHub](https://github.com/akemin-dayo/AppSync)! 19 | 20 | AppSync Unified is a tweak that allows users to freely install ad-hoc signed, fakesigned, or unsigned IPA app packages on their iOS devices that iOS would otherwise consider invalid. 21 | 22 | Some popular use cases include: 23 | 24 | * Installing freely-distributed apps that are unavailable from the App Store without having to re-sign the apps in question every 7 days (if the user does not have a subscription to the Apple Developer Program) 25 | * Assisting in the development of iOS applications with Xcode 26 | * Cloning or downgrading already-installed apps 27 | 28 | --- 29 | 30 | # Help! I installed AppSync Unified, but it doesn't seem to be working after I resprung from Cydia/Zebra/Sileo/etc.! 31 | 32 | If AppSync Unified is not working after installation, please reboot your device or perform a userspace reboot (`launchctl reboot userspace`, `ldrestart`, etc.) to activate it. You will only need to do this ONCE. 33 | 34 | This issue appears to be caused by what …seems like a Cydia Substrate/Substitute bug(?) that's resurfaced from years ago, and occurs _really_ rarely, so it's an absolute nightmare of a bug. It's especially frustrating for me since I'm such a perfectionist when it comes to software development, too ww (🍍˃̶͈̀ロ˂̶͈́)੭ꠥ⁾⁾ 35 | 36 | **For the curious developers among you:** AppSync Unified's `postinst` binary (see [pkg-actions.m](https://github.com/akemin-dayo/AppSync/blob/master/pkg-actions/pkg-actions.m)) restarts `installd` via `launchctl` — for some reason though, it seems like Cydia Substrate and/or Substitute doesn't always inject the dylib properly into `installd` when it is reloaded via `launchctl` in this way. 37 | 38 | I tried _really_ hard to determine the cause of this, but I really have no idea what could be causing this. The dylib has _long_ since been written to the filesystem by the time `postinst` was _executed_, let alone when `launchctl` was even called by `posix_spawn`. 39 | 40 | I guess for now, all I can do is inform people about the bug and how to resolve it. ⊂⌒~⊃。Д。🍍)⊃ 41 | 42 | Hopefully I'll be able to properly resolve this in time. 43 | 44 | --- 45 | 46 | # Regarding piracy… 47 | 48 | **Please do NOT use AppSync Unified for piracy.** 49 | 50 | AppSync Unified is a development tool designed for app developers first and foremost, alongside other valid legal uses that I support — a few of which are outlined above. 51 | 52 | **Software piracy is illegal.** Please support the developers of the iOS software you use, whether they be app developers on the App Store or tweak developers on Chariz/Dynastic/etc. 53 | 54 | They're just trying to make a living too, much like you and I. 55 | -------------------------------------------------------------------------------- /transitional/nodelete-net.angelxwind.appsync50plus/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Architecture: iphoneos-arm 2 | Author: Karen/あけみ 3 | Depends: ai.akemi.appsyncunified 4 | Description: Transitional package for AppSync Unified. 5 | Maintainer: Karen/あけみ 6 | Homepage: http://cydia.akemi.ai/ 7 | Name: AppSync Unified Transitional Package (50plus) 8 | Package: net.angelxwind.appsync50plus 9 | Section: Tweaks 10 | Version: 9999.0 11 | Depiction: https://cydia.akemi.ai/?page/ai.akemi.appsyncunified-transitional-legacy 12 | Tag: cydia::obsolete 13 | -------------------------------------------------------------------------------- /transitional/nodelete-net.angelxwind.appsync60plus/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Architecture: iphoneos-arm 2 | Author: Karen/あけみ 3 | Depends: ai.akemi.appsyncunified 4 | Description: Transitional package for AppSync Unified. 5 | Maintainer: Karen/あけみ 6 | Homepage: http://cydia.akemi.ai/ 7 | Name: AppSync Unified Transitional Package (60plus) 8 | Package: net.angelxwind.appsync60plus 9 | Section: Tweaks 10 | Version: 9999.0 11 | Depiction: https://cydia.akemi.ai/?page/ai.akemi.appsyncunified-transitional-legacy 12 | Tag: cydia::obsolete 13 | -------------------------------------------------------------------------------- /transitional/nodelete-net.angelxwind.appsync70plus/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Architecture: iphoneos-arm 2 | Author: Karen/あけみ 3 | Depends: ai.akemi.appsyncunified 4 | Description: Transitional package for AppSync Unified. 5 | Maintainer: Karen/あけみ 6 | Homepage: http://cydia.akemi.ai/ 7 | Name: AppSync Unified Transitional Package (70plus) 8 | Package: net.angelxwind.appsync70plus 9 | Section: Tweaks 10 | Version: 9999.0 11 | Depiction: https://cydia.akemi.ai/?page/ai.akemi.appsyncunified-transitional-legacy 12 | Tag: cydia::obsolete 13 | -------------------------------------------------------------------------------- /transitional/nodelete-net.angelxwind.appsyncunified/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Architecture: iphoneos-arm 2 | Author: Karen/あけみ 3 | Depends: ai.akemi.appsyncunified 4 | Description: Transitional package for AppSync Unified. 5 | Maintainer: Karen/あけみ 6 | Homepage: http://cydia.akemi.ai/ 7 | Name: AppSync Unified Transitional Package 8 | Package: net.angelxwind.appsyncunified 9 | Section: Tweaks 10 | Version: 9999.0 11 | Depiction: https://cydia.akemi.ai/?page/ai.akemi.appsyncunified-transitional 12 | Tag: cydia::obsolete 13 | --------------------------------------------------------------------------------