├── .gitignore ├── .travis.yml ├── Clutch.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── dev.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ └── Clutch.xcscheme └── xcuserdata │ └── dev.xcuserdatad │ └── xcschemes │ ├── xcodewtf.xcscheme │ └── xcschememanagement.plist ├── Clutch ├── ARM64Dumper.h ├── ARM64Dumper.m ├── ARMDumper.h ├── ARMDumper.m ├── ASLRDisabler.h ├── ASLRDisabler.m ├── Application.h ├── Application.m ├── ApplicationsManager.h ├── ApplicationsManager.m ├── Binary.h ├── Binary.m ├── BinaryDumpProtocol.h ├── BundleDumpOperation.h ├── BundleDumpOperation.m ├── Clutch-Prefix.pch ├── Clutch.entitlements ├── ClutchBundle.h ├── ClutchBundle.m ├── ClutchCommands.h ├── ClutchCommands.m ├── ClutchPrint.h ├── ClutchPrint.m ├── Device.h ├── Device.m ├── Dumper.h ├── Dumper.m ├── Extension.h ├── Extension.m ├── FBApplicationInfo.h ├── FinalizeDumpOperation.h ├── FinalizeDumpOperation.m ├── Framework.h ├── Framework.m ├── Framework64Dumper.h ├── Framework64Dumper.m ├── FrameworkDumper.h ├── FrameworkDumper.m ├── FrameworkLoader.h ├── FrameworkLoader.m ├── Info.plist ├── LSApplicationWorkspace.h ├── MiniZip │ ├── crypt.h │ ├── ioapi.c │ ├── ioapi.h │ ├── unzip.c │ ├── unzip.h │ ├── zip.c │ └── zip.h ├── NSBundle+Clutch.h ├── NSBundle+Clutch.m ├── NSData+Reading.h ├── NSData+Reading.m ├── NSFileHandle+Private.h ├── NSFileHandle+Private.m ├── NSTask.h ├── SCInfoBuilder.h ├── SCInfoBuilder.m ├── ZipArchive.h ├── ZipArchive.m ├── ZipOperation.h ├── ZipOperation.m ├── mach_vm.h ├── main.m ├── move_and_sign.sh ├── optool-defines.h ├── optool-headers.h ├── optool-headers.m ├── optool-operations.h ├── optool-operations.m ├── optool.h ├── scinfo.h ├── scinfo.m ├── sha1.c └── sha1.h ├── LSApplicationProxy.h └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | build/ 3 | *.xcuserstate 4 | project.xcworkspace/ 5 | xcuserdata 6 | # OS junk 7 | .DS_Store 8 | .DS_Store? 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | script: xcodebuild clean build 3 | -------------------------------------------------------------------------------- /Clutch.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Clutch.xcodeproj/project.xcworkspace/xcuserdata/dev.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iOS-Reverse-Engineering-Dev/Clutch/4b7d0d5d5ac638c33c6ea43a9c7edc3ef3080f58/Clutch.xcodeproj/project.xcworkspace/xcuserdata/dev.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Clutch.xcodeproj/xcshareddata/xcschemes/Clutch.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 78 | 79 | 80 | 81 | 82 | 83 | 89 | 90 | 96 | 97 | 98 | 99 | 101 | 102 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /Clutch.xcodeproj/xcuserdata/dev.xcuserdatad/xcschemes/xcodewtf.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 39 | 40 | 41 | 42 | 48 | 49 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Clutch.xcodeproj/xcuserdata/dev.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Clutch.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | ClutchAnalyse.xcscheme 13 | 14 | orderHint 15 | 2 16 | 17 | MiniZip.xcscheme 18 | 19 | orderHint 20 | 3 21 | 22 | [DEBUG] Clutch.xcscheme_^#shared#^_ 23 | 24 | orderHint 25 | 0 26 | 27 | xcodewtf.xcscheme 28 | 29 | orderHint 30 | 1 31 | 32 | 33 | SuppressBuildableAutocreation 34 | 35 | 266FD1651E1D7C6200935807 36 | 37 | primary 38 | 39 | 40 | 26AB70781E1DC67500BDAE34 41 | 42 | primary 43 | 44 | 45 | 329B80701C5B6745007DD817 46 | 47 | primary 48 | 49 | 50 | 32BE3C241C5B8C450049ECAD 51 | 52 | primary 53 | 54 | 55 | D7C6DCC81E2920A700B5AFEE 56 | 57 | primary 58 | 59 | 60 | D7C6DCD81E29224600B5AFEE 61 | 62 | primary 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Clutch/ARM64Dumper.h: -------------------------------------------------------------------------------- 1 | // 2 | // ARM64Dumper.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 22.03.15. 6 | // 7 | // 8 | 9 | #import "Dumper.h" 10 | 11 | @interface ARM64Dumper : Dumper 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Clutch/ARM64Dumper.m: -------------------------------------------------------------------------------- 1 | // 2 | // ARM64Dumper.m 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 22.03.15. 6 | // 7 | // 8 | 9 | #import "ARM64Dumper.h" 10 | #import 11 | #import "Device.h" 12 | #import 13 | #import 14 | #import 15 | #import "ClutchPrint.h" 16 | 17 | @implementation ARM64Dumper 18 | 19 | - (cpu_type_t)supportedCPUType 20 | { 21 | return CPU_TYPE_ARM64; 22 | } 23 | 24 | - (BOOL)dumpBinary { 25 | __block BOOL dumpResult; 26 | NSString *binaryDumpPath = [_originalBinary.workingPath stringByAppendingPathComponent:_originalBinary.binaryPath.lastPathComponent]; 27 | 28 | NSFileHandle *newFileHandle = [[NSFileHandle alloc]initWithFileDescriptor:fileno(fopen(binaryDumpPath.UTF8String, "r+"))]; 29 | 30 | NSString* swappedBinaryPath = _originalBinary.binaryPath, *newSinf = _originalBinary.sinfPath, *newSupp = _originalBinary.suppPath, *newSupf = _originalBinary.supfPath; // default values if we dont need to swap archs 31 | 32 | //check if cpusubtype matches 33 | if ((_thinHeader.header.cpusubtype != [Device cpu_subtype]) && (_originalBinary.hasMultipleARMSlices || (_originalBinary.hasARM64Slice && ([Device cpu_type]==CPU_TYPE_ARM64)))) { 34 | 35 | NSString* suffix = [NSString stringWithFormat:@"_%@", [Dumper readableArchFromHeader:_thinHeader]]; 36 | 37 | swappedBinaryPath = [_originalBinary.binaryPath stringByAppendingString:suffix]; 38 | newSinf = [_originalBinary.sinfPath.stringByDeletingPathExtension stringByAppendingString:[suffix stringByAppendingPathExtension:_originalBinary.sinfPath.pathExtension]]; 39 | newSupp = [_originalBinary.suppPath.stringByDeletingPathExtension stringByAppendingString:[suffix stringByAppendingPathExtension:_originalBinary.suppPath.pathExtension]]; 40 | 41 | [self swapArch]; 42 | 43 | } 44 | 45 | 46 | //actual dumping 47 | 48 | [newFileHandle seekToFileOffset:_thinHeader.offset + _thinHeader.size]; 49 | 50 | struct linkedit_data_command ldid; // LC_CODE_SIGNATURE load header (for resign) 51 | struct encryption_info_command_64 crypt; // LC_ENCRYPTION_INFO load header (for crypt*) 52 | struct segment_command_64 __text; // __TEXT segment 53 | 54 | struct super_blob *codesignblob; // codesign blob pointer 55 | struct code_directory directory; // codesign directory index 56 | 57 | directory.nCodeSlots = 0; 58 | BOOL foundCrypt = NO, foundSignature = NO, foundStartText = NO; 59 | crypt.cryptid = crypt.cryptoff = crypt.cryptsize = 0; 60 | 61 | uint64_t __text_start = 0; 62 | 63 | [[ClutchPrint sharedInstance] printDeveloper: @"64bit dumping: arch %@ offset %u", [Dumper readableArchFromHeader:_thinHeader], _thinHeader.offset]; 64 | 65 | for (int i = 0; i < _thinHeader.header.ncmds; i++) { 66 | 67 | uint32_t cmd = [newFileHandle intAtOffset:newFileHandle.offsetInFile]; 68 | uint32_t size = [newFileHandle intAtOffset:newFileHandle.offsetInFile+sizeof(uint32_t)]; 69 | 70 | switch (cmd) { 71 | case LC_CODE_SIGNATURE: { 72 | [newFileHandle getBytes:&ldid inRange:NSMakeRange((NSUInteger)(newFileHandle.offsetInFile),sizeof(struct linkedit_data_command))]; 73 | foundSignature = YES; 74 | 75 | [[ClutchPrint sharedInstance] printDeveloper: @"FOUND CODE SIGNATURE: dataoff %u | datasize %u",ldid.dataoff,ldid.datasize]; 76 | 77 | break; 78 | } 79 | case LC_ENCRYPTION_INFO_64: { 80 | [newFileHandle getBytes:&crypt inRange:NSMakeRange((NSUInteger)(newFileHandle.offsetInFile),sizeof(struct encryption_info_command_64))]; 81 | foundCrypt = YES; 82 | 83 | [[ClutchPrint sharedInstance] printDeveloper: @"FOUND ENCRYPTION INFO: cryptoff %u | cryptsize %u | cryptid %u",crypt.cryptoff,crypt.cryptsize,crypt.cryptid]; 84 | 85 | break; 86 | } 87 | case LC_SEGMENT_64: 88 | { 89 | [newFileHandle getBytes:&__text inRange:NSMakeRange((NSUInteger)(newFileHandle.offsetInFile),sizeof(struct segment_command_64))]; 90 | 91 | if (strncmp(__text.segname, "__TEXT", 6) == 0) { 92 | foundStartText = YES; 93 | [[ClutchPrint sharedInstance] printDeveloper: @"FOUND %s SEGMENT",__text.segname]; 94 | __text_start = __text.vmaddr; 95 | } 96 | break; 97 | } 98 | } 99 | 100 | [newFileHandle seekToFileOffset:newFileHandle.offsetInFile + size]; 101 | 102 | if (foundCrypt && foundSignature && foundStartText) 103 | break; 104 | } 105 | 106 | // we need to have all of these 107 | if (!foundCrypt || !foundSignature || !foundStartText) { 108 | [[ClutchPrint sharedInstance] printDeveloper: @"dumping binary: some load commands were not found %@ %@ %@",foundCrypt?@"YES":@"NO",foundSignature?@"YES":@"NO",foundStartText?@"YES":@"NO"]; 109 | return NO; 110 | } 111 | 112 | [[ClutchPrint sharedInstance] printDeveloper: @"found all required load commands for %@ %@",_originalBinary,[Dumper readableArchFromHeader:_thinHeader]]; 113 | 114 | pid_t pid; // store the process ID of the fork 115 | mach_port_t port; // mach port used for moving virtual memory 116 | kern_return_t err; // any kernel return codes 117 | NSUInteger begin = 0; 118 | 119 | pid = [self posix_spawn:swappedBinaryPath disableASLR:self.shouldDisableASLR]; 120 | 121 | if ((err = task_for_pid(mach_task_self(), pid, &port) != KERN_SUCCESS)) { 122 | [[ClutchPrint sharedInstance] printError:@"Could not obtain mach port, either the process is dead (codesign error?) or entitlements were not properly signed!"]; 123 | goto gotofail; 124 | } 125 | 126 | codesignblob = malloc(ldid.datasize); 127 | 128 | 129 | //seek to ldid offset 130 | 131 | [newFileHandle seekToFileOffset:_thinHeader.offset + ldid.dataoff]; 132 | [newFileHandle getBytes:codesignblob inRange:NSMakeRange((NSUInteger)(newFileHandle.offsetInFile), ldid.datasize)]; 133 | 134 | uint32_t countBlobs = CFSwapInt32(codesignblob->count); // how many indexes? 135 | 136 | 137 | for (uint32_t index = 0; index < countBlobs; index++) { // is this the code directory? 138 | if (CFSwapInt32(codesignblob->index[index].type) == CSSLOT_CODEDIRECTORY) { 139 | // we'll find the hash metadata in here 140 | [[ClutchPrint sharedInstance] printDeveloper: @"%u %u %u", _thinHeader.offset, ldid.dataoff, codesignblob->index[index].offset]; 141 | begin = _thinHeader.offset + ldid.dataoff + CFSwapInt32(codesignblob->index[index].offset); // store the top of the codesign directory blob 142 | [newFileHandle getBytes:&directory inRange:NSMakeRange(begin, sizeof(struct code_directory))]; //read the blob from its beginning 143 | [[ClutchPrint sharedInstance] printDeveloper: @"Found CSSLOT_CODEDIRECTORY"]; 144 | break; //break (we don't need anything from this the superblob anymore) 145 | } 146 | } 147 | 148 | free(codesignblob); 149 | 150 | uint32_t pages = CFSwapInt32(directory.nCodeSlots); // get the amount of codeslots 151 | 152 | [[ClutchPrint sharedInstance] printDeveloper: @"Codesign Pages %u", pages]; 153 | 154 | if (pages == 0) { 155 | [[ClutchPrint sharedInstance] printColor:ClutchPrinterColorPurple format:@"pages == 0"]; 156 | goto gotofail; 157 | } 158 | 159 | [newFileHandle seekToFileOffset:_thinHeader.offset]; 160 | 161 | if ((_thinHeader.header.flags & MH_PIE) && !self.shouldDisableASLR) 162 | { 163 | mach_vm_address_t main_address = [ASLRDisabler slideForPID:pid]; 164 | if(main_address == -1) { 165 | [[ClutchPrint sharedInstance] printColor:ClutchPrinterColorPurple format:@"Failed to find address of header!"]; 166 | goto gotofail; 167 | } 168 | 169 | [[ClutchPrint sharedInstance] printColor:ClutchPrinterColorPink format:@"ASLR slide: 0x%llx", main_address]; 170 | __text_start = main_address; 171 | } 172 | 173 | 174 | { 175 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); 176 | 177 | dispatch_sync(queue, ^{ 178 | dumpResult = [self _dumpToFileHandle:newFileHandle withDumpSize:(crypt.cryptsize + crypt.cryptoff) pages:pages fromPort:port pid:pid aslrSlide:__text_start codeSignature_hashOffset:CFSwapInt32(directory.hashOffset) codesign_begin:(uint32_t)begin]; 179 | }); 180 | 181 | } 182 | [[ClutchPrint sharedInstance] printDeveloper:@"done dumping"]; 183 | 184 | //done dumping, let's wait for pid 185 | 186 | _kill(pid); 187 | [newFileHandle closeFile]; 188 | if (![swappedBinaryPath isEqualToString:_originalBinary.binaryPath]) 189 | [[NSFileManager defaultManager]removeItemAtPath:swappedBinaryPath error:nil]; 190 | if (![newSinf isEqualToString:_originalBinary.sinfPath]) 191 | [[NSFileManager defaultManager]removeItemAtPath:newSinf error:nil]; 192 | if (![newSupp isEqualToString:_originalBinary.suppPath]) 193 | [[NSFileManager defaultManager]removeItemAtPath:newSupp error:nil]; 194 | if (![newSupf isEqualToString:_originalBinary.supfPath]) 195 | [[NSFileManager defaultManager]removeItemAtPath:newSupf error:nil]; 196 | 197 | return dumpResult; 198 | 199 | gotofail: 200 | 201 | _kill(pid); 202 | [newFileHandle closeFile]; 203 | if (![swappedBinaryPath isEqualToString:_originalBinary.binaryPath]) 204 | [[NSFileManager defaultManager]removeItemAtPath:swappedBinaryPath error:nil]; 205 | if (![newSinf isEqualToString:_originalBinary.sinfPath]) 206 | [[NSFileManager defaultManager]removeItemAtPath:newSinf error:nil]; 207 | if (![newSupp isEqualToString:_originalBinary.suppPath]) 208 | [[NSFileManager defaultManager]removeItemAtPath:newSupp error:nil]; 209 | if (![newSupf isEqualToString:_originalBinary.supfPath]) 210 | [[NSFileManager defaultManager]removeItemAtPath:newSupf error:nil]; 211 | 212 | return NO; 213 | } 214 | 215 | 216 | @end 217 | -------------------------------------------------------------------------------- /Clutch/ARMDumper.h: -------------------------------------------------------------------------------- 1 | // 2 | // ARMDumper.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 22.03.15. 6 | // 7 | // 8 | 9 | #import "Dumper.h" 10 | 11 | @interface ARMDumper : Dumper 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Clutch/ARMDumper.m: -------------------------------------------------------------------------------- 1 | // 2 | // ARMDumper.m 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 22.03.15. 6 | // 7 | // 8 | 9 | #import "ARMDumper.h" 10 | #import 11 | #import "Device.h" 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import "ClutchPrint.h" 18 | 19 | @implementation ARMDumper 20 | 21 | - (cpu_type_t)supportedCPUType 22 | { 23 | return CPU_TYPE_ARM; 24 | } 25 | 26 | 27 | - (BOOL)dumpBinary { 28 | __block BOOL dumpResult; 29 | NSString *binaryDumpPath = [_originalBinary.workingPath stringByAppendingPathComponent:_originalBinary.binaryPath.lastPathComponent]; 30 | 31 | NSFileHandle *newFileHandle = [[NSFileHandle alloc]initWithFileDescriptor:fileno(fopen(binaryDumpPath.UTF8String, "r+"))]; 32 | 33 | NSString* swappedBinaryPath = _originalBinary.binaryPath, *newSinf = _originalBinary.sinfPath, *newSupp = _originalBinary.suppPath; // default values if we dont need to swap archs 34 | 35 | //check if cpusubtype matches 36 | if ((_thinHeader.header.cpusubtype != [Device cpu_subtype]) && (_originalBinary.hasMultipleARMSlices || (_originalBinary.hasARM64Slice && ([Device cpu_type]==CPU_TYPE_ARM64)))) { 37 | 38 | NSString* suffix = [NSString stringWithFormat:@"_%@", [Dumper readableArchFromHeader:_thinHeader]]; 39 | 40 | swappedBinaryPath = [_originalBinary.binaryPath stringByAppendingString:suffix]; 41 | newSinf = [_originalBinary.sinfPath.stringByDeletingPathExtension stringByAppendingString:[suffix stringByAppendingPathExtension:_originalBinary.sinfPath.pathExtension]]; 42 | newSupp = [_originalBinary.suppPath.stringByDeletingPathExtension stringByAppendingString:[suffix stringByAppendingPathExtension:_originalBinary.suppPath.pathExtension]]; 43 | 44 | [self swapArch]; 45 | 46 | } 47 | 48 | //actual dumping 49 | 50 | [newFileHandle seekToFileOffset:_thinHeader.offset + _thinHeader.size]; 51 | 52 | struct linkedit_data_command ldid; // LC_CODE_SIGNATURE load header (for resign) 53 | struct encryption_info_command crypt; // LC_ENCRYPTION_INFO load header (for crypt*) 54 | struct segment_command __text; // __TEXT segment 55 | crypt.cryptsize = crypt.cryptoff = crypt.cryptid = 0; 56 | 57 | struct super_blob *codesignblob; // codesign blob pointer 58 | struct code_directory directory; // codesign directory index 59 | directory.nCodeSlots = 0; 60 | 61 | BOOL foundCrypt = NO, foundSignature = NO, foundStartText = NO; 62 | 63 | uint64_t __text_start = 0; 64 | 65 | [[ClutchPrint sharedInstance] printDeveloper: @"32bit Dumping: arch %@ offset %u", [Dumper readableArchFromHeader:_thinHeader], _thinHeader.offset]; 66 | 67 | for (int i = 0; i < _thinHeader.header.ncmds; i++) { 68 | 69 | uint32_t cmd = [newFileHandle intAtOffset:newFileHandle.offsetInFile]; 70 | uint32_t size = [newFileHandle intAtOffset:newFileHandle.offsetInFile+sizeof(uint32_t)]; 71 | 72 | switch (cmd) { 73 | case LC_CODE_SIGNATURE: { 74 | [newFileHandle getBytes:&ldid inRange:NSMakeRange((NSUInteger)(newFileHandle.offsetInFile),sizeof(struct linkedit_data_command))]; 75 | foundSignature = YES; 76 | 77 | [[ClutchPrint sharedInstance] printDeveloper: @"FOUND CODE SIGNATURE: dataoff %u | datasize %u",ldid.dataoff,ldid.datasize]; 78 | 79 | break; 80 | } 81 | case LC_ENCRYPTION_INFO: { 82 | [newFileHandle getBytes:&crypt inRange:NSMakeRange((NSUInteger)(newFileHandle.offsetInFile),sizeof(struct encryption_info_command))]; 83 | foundCrypt = YES; 84 | 85 | [[ClutchPrint sharedInstance] printDeveloper: @"FOUND ENCRYPTION INFO: cryptoff %u | cryptsize %u | cryptid %u",crypt.cryptoff,crypt.cryptsize,crypt.cryptid]; 86 | 87 | break; 88 | } 89 | case LC_SEGMENT: 90 | { 91 | [newFileHandle getBytes:&__text inRange:NSMakeRange((NSUInteger)(newFileHandle.offsetInFile),sizeof(struct segment_command))]; 92 | 93 | if (strncmp(__text.segname, "__TEXT", 6) == 0) { 94 | foundStartText = YES; 95 | [[ClutchPrint sharedInstance] printDeveloper: @"FOUND %s SEGMENT",__text.segname]; 96 | __text_start = __text.vmaddr; 97 | } 98 | break; 99 | } 100 | } 101 | 102 | [newFileHandle seekToFileOffset:newFileHandle.offsetInFile + size]; 103 | 104 | if (foundCrypt && foundSignature && foundStartText) 105 | break; 106 | } 107 | 108 | [[ClutchPrint sharedInstance] printDeveloper: @"binary path %@", swappedBinaryPath]; 109 | 110 | // we need to have all of these 111 | if (!foundCrypt || !foundSignature || !foundStartText) { 112 | [[ClutchPrint sharedInstance] printError:@"dumping binary: some load commands were not found %@ %@ %@",foundCrypt?@"YES":@"NO",foundSignature?@"YES":@"NO",foundStartText?@"YES":@"NO"]; 113 | return NO; 114 | } 115 | 116 | [[ClutchPrint sharedInstance] printDeveloper: @"found all required load commands for %@ %@",_originalBinary,[Dumper readableArchFromHeader:_thinHeader]]; 117 | 118 | pid_t pid; // store the process ID of the fork 119 | mach_port_t port; // mach port used for moving virtual memory 120 | kern_return_t err; // any kernel return codes 121 | NSUInteger begin = 0; 122 | 123 | pid = [self posix_spawn:swappedBinaryPath disableASLR:self.shouldDisableASLR]; 124 | 125 | if ((err = task_for_pid(mach_task_self(), pid, &port) != KERN_SUCCESS)) { 126 | [[ClutchPrint sharedInstance] printError:@"Could not obtain mach port, either the process is dead (codesign error?) or entitlements were not properly signed!?"]; 127 | goto gotofail; 128 | } 129 | 130 | [newFileHandle seekToFileOffset:_thinHeader.offset + ldid.dataoff]; 131 | 132 | codesignblob = malloc(ldid.datasize); 133 | 134 | 135 | //seek to ldid offset 136 | 137 | [newFileHandle seekToFileOffset:_thinHeader.offset + ldid.dataoff]; 138 | [newFileHandle getBytes:codesignblob inRange:NSMakeRange((NSUInteger)(newFileHandle.offsetInFile), ldid.datasize)]; 139 | 140 | uint32_t countBlobs = CFSwapInt32(codesignblob->count); // how many indexes? 141 | 142 | for (uint32_t index = 0; index < countBlobs; index++) { // is this the code directory? 143 | if (CFSwapInt32(codesignblob->index[index].type) == CSSLOT_CODEDIRECTORY) { 144 | // we'll find the hash metadata in here 145 | [[ClutchPrint sharedInstance] printDeveloper: @"%u %u %u", _thinHeader.offset, ldid.dataoff, codesignblob->index[index].offset]; 146 | begin = _thinHeader.offset + ldid.dataoff + CFSwapInt32(codesignblob->index[index].offset); // store the top of the codesign directory blob 147 | [newFileHandle getBytes:&directory inRange:NSMakeRange(begin, sizeof(struct code_directory))]; //read the blob from its beginning 148 | [[ClutchPrint sharedInstance] printDeveloper: @"Found CSSLOT_CODEDIRECTORY"]; 149 | break; //break (we don't need anything from this the superblob anymore) 150 | } 151 | } 152 | 153 | free(codesignblob); 154 | 155 | uint32_t pages = CFSwapInt32(directory.nCodeSlots); // get the amount of codeslots 156 | 157 | if (pages == 0) { 158 | [[ClutchPrint sharedInstance] printColor:ClutchPrinterColorPurple format:@"pages == 0"]; 159 | goto gotofail; 160 | } 161 | 162 | [newFileHandle seekToFileOffset:_thinHeader.offset]; 163 | 164 | if ((_thinHeader.header.flags & MH_PIE) && !self.shouldDisableASLR) 165 | { 166 | mach_vm_address_t main_address = [ASLRDisabler slideForPID:pid]; 167 | if(main_address == -1) { 168 | [[ClutchPrint sharedInstance] printError:@"Failed to find address of header!"]; 169 | goto gotofail; 170 | } 171 | 172 | [[ClutchPrint sharedInstance] printColor:ClutchPrinterColorPurple format:@"ASLR slide: 0x%llx", main_address]; 173 | __text_start = main_address; 174 | } 175 | 176 | { 177 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); 178 | 179 | dispatch_sync(queue, ^{ 180 | dumpResult = [self _dumpToFileHandle:newFileHandle withDumpSize:(crypt.cryptsize + crypt.cryptoff) pages:pages fromPort:port pid:pid aslrSlide:__text_start codeSignature_hashOffset:CFSwapInt32(directory.hashOffset) codesign_begin:(uint32_t)begin]; 181 | }); 182 | 183 | } 184 | 185 | [[ClutchPrint sharedInstance] printDeveloper:@"done dumping"]; 186 | if (![swappedBinaryPath isEqualToString:_originalBinary.binaryPath]) 187 | [[NSFileManager defaultManager]removeItemAtPath:swappedBinaryPath error:nil]; 188 | if (![newSinf isEqualToString:_originalBinary.sinfPath]) 189 | [[NSFileManager defaultManager]removeItemAtPath:newSinf error:nil]; 190 | if (![newSupp isEqualToString:_originalBinary.suppPath]) 191 | [[NSFileManager defaultManager]removeItemAtPath:newSupp error:nil]; 192 | [newFileHandle closeFile]; 193 | _kill(pid); 194 | 195 | return dumpResult; 196 | 197 | gotofail: 198 | 199 | _kill(pid); 200 | [newFileHandle closeFile]; 201 | if (![swappedBinaryPath isEqualToString:_originalBinary.binaryPath]) 202 | [[NSFileManager defaultManager]removeItemAtPath:swappedBinaryPath error:nil]; 203 | if (![newSinf isEqualToString:_originalBinary.sinfPath]) 204 | [[NSFileManager defaultManager]removeItemAtPath:newSinf error:nil]; 205 | if (![newSupp isEqualToString:_originalBinary.suppPath]) 206 | [[NSFileManager defaultManager]removeItemAtPath:newSupp error:nil]; 207 | 208 | return NO; 209 | } 210 | 211 | @end 212 | -------------------------------------------------------------------------------- /Clutch/ASLRDisabler.h: -------------------------------------------------------------------------------- 1 | // 2 | // ASLR.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 14.02.15. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface ASLRDisabler : NSObject 13 | 14 | + (mach_vm_address_t)slideForPID:(pid_t)pid; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Clutch/ASLRDisabler.m: -------------------------------------------------------------------------------- 1 | // 2 | // ASLR.m 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 14.02.15. 6 | // 7 | // 8 | 9 | #import "ASLRDisabler.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import "mach_vm.h" 16 | #import "ClutchPrint.h" 17 | 18 | @import MachO.loader; 19 | 20 | @implementation ASLRDisabler 21 | 22 | + (mach_vm_address_t)slideForPID:(pid_t)pid { 23 | vm_map_t targetTask = 0; 24 | kern_return_t kr = 0; 25 | if (task_for_pid(mach_task_self(), pid, &targetTask)) 26 | { 27 | [[ClutchPrint sharedInstance] printError:@"Can't execute task_for_pid! Do you have the right permissions/entitlements?"]; 28 | return -1; 29 | } 30 | 31 | vm_address_t iter = 0; 32 | while (1) 33 | { 34 | struct mach_header mh = {0}; 35 | vm_address_t addr = iter; 36 | vm_size_t lsize = 0; 37 | uint32_t depth; 38 | mach_vm_size_t bytes_read = 0; 39 | struct vm_region_submap_info_64 info; 40 | mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64; 41 | if (vm_region_recurse_64(targetTask, &addr, &lsize, &depth, (vm_region_info_t)&info, &count)) 42 | { 43 | break; 44 | } 45 | kr = mach_vm_read_overwrite(targetTask, (mach_vm_address_t)addr, (mach_vm_size_t)sizeof(struct mach_header), (mach_vm_address_t)&mh, &bytes_read); 46 | if (kr == KERN_SUCCESS && bytes_read == sizeof(struct mach_header)) 47 | { 48 | /* only one image with MH_EXECUTE filetype */ 49 | if ((mh.magic == MH_MAGIC || mh.magic == MH_MAGIC_64) && mh.filetype == MH_EXECUTE) 50 | { 51 | #if DEBUG 52 | [[ClutchPrint sharedInstance] printDeveloper:@"Found main binary mach-o image @ %p!", (void*)addr]; 53 | #endif 54 | return addr; 55 | break; 56 | } 57 | } 58 | iter = addr + lsize; 59 | } 60 | 61 | return -1; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Clutch/Application.h: -------------------------------------------------------------------------------- 1 | // 2 | // Application.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 09.02.2015. 6 | // 7 | // 8 | 9 | #import 10 | #import "ClutchBundle.h" 11 | #import "Extension.h" 12 | #import "Framework.h" 13 | 14 | @interface Application : ClutchBundle 15 | 16 | @property (readonly) BOOL hasAppleWatchApp; // YES if contains watchOS 2 compatible application 17 | @property (readonly) BOOL isAppleWatchApp; // only for Apple Watch apps that support watchOS 2 or newer (armv7k) 18 | 19 | @property (readonly) NSArray *extensions; 20 | @property (readonly) NSArray *frameworks; 21 | @property (readonly) NSArray *watchOSApps; 22 | 23 | - (BOOL)dumpToDirectoryURL:(NSURL *)directoryURL onlyBinaries:(BOOL)yrn; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Clutch/ApplicationsManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // ApplicationsManager.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 09.02.15. 6 | // 7 | // 8 | 9 | #import 10 | #import "Application.h" 11 | 12 | @interface ApplicationsManager : NSObject 13 | 14 | - (instancetype)init; 15 | 16 | @property (NS_NONATOMIC_IOSONLY, readonly, copy) NSDictionary *installedApps; 17 | 18 | -(NSDictionary*)_allCachedApplications; 19 | 20 | @end -------------------------------------------------------------------------------- /Clutch/ApplicationsManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // ApplicationsManager.m 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 09.02.15. 6 | // 7 | // 8 | 9 | #define applistCachePath @"applist-cache.plist" 10 | #define dumpedAppPath @"/etc/dumped.clutch" 11 | 12 | #import 13 | #import "ApplicationsManager.h" 14 | #import "FBApplicationInfo.h" 15 | #import "LSApplicationProxy.h" 16 | #import "LSApplicationWorkspace.h" 17 | 18 | typedef NSDictionary* (*MobileInstallationLookup)(NSDictionary *options); 19 | 20 | @interface ApplicationsManager () 21 | { 22 | NSMutableArray* _cachedApps; 23 | } 24 | 25 | @end 26 | 27 | 28 | @implementation ApplicationsManager 29 | 30 | - (instancetype)init 31 | { 32 | if ((self = [super init])) 33 | { 34 | if ([[NSFileManager defaultManager] fileExistsAtPath:applistCachePath]) 35 | { 36 | _cachedApps = [[NSMutableArray alloc] initWithContentsOfFile:applistCachePath]; 37 | } 38 | else 39 | { 40 | _cachedApps = [NSMutableArray new]; 41 | } 42 | } 43 | 44 | return self; 45 | } 46 | 47 | 48 | - (NSDictionary *)listApplicationsForiOS7AndLower 49 | { 50 | MobileInstallationLookup mobileInstallationLookup; 51 | void * MIHandle; 52 | 53 | NSMutableDictionary *returnValue = [NSMutableDictionary new]; 54 | MIHandle = dlopen("/System/Library/PrivateFrameworks/MobileInstallation.framework/MobileInstallation", RTLD_NOW); 55 | mobileInstallationLookup = NULL; 56 | 57 | if (MIHandle) 58 | { 59 | mobileInstallationLookup = dlsym(MIHandle,"MobileInstallationLookup"); 60 | if (mobileInstallationLookup) 61 | { 62 | 63 | NSDictionary *installedApps; 64 | NSDictionary* options = @{@"ApplicationType":@"User", 65 | @"ReturnAttributes":@[@"CFBundleShortVersionString", 66 | @"CFBundleVersion", 67 | @"Path", 68 | @"CFBundleDisplayName", 69 | @"CFBundleExecutable", 70 | @"MinimumOSVersion"]}; 71 | 72 | installedApps = mobileInstallationLookup(options); 73 | 74 | 75 | for (NSString *bundleID in installedApps.allKeys) 76 | { 77 | NSDictionary *appI = installedApps[bundleID]; 78 | NSURL *bundleURL = [NSURL fileURLWithPath:appI[@"Path"]]; 79 | NSString *scinfo = [bundleURL.path stringByAppendingPathComponent:@"SC_Info"]; 80 | 81 | BOOL isDirectory; 82 | BOOL purchased = [[NSFileManager defaultManager]fileExistsAtPath:scinfo isDirectory:&isDirectory]; 83 | 84 | if (purchased && isDirectory) 85 | { 86 | NSString *name = appI[@"CFBundleDisplayName"]; 87 | if (name == nil) 88 | { 89 | name = appI[@"CFBundleExecutable"]; 90 | } 91 | 92 | NSDictionary *bundleInfo = @{@"BundleContainer":bundleURL.URLByDeletingLastPathComponent, 93 | @"BundleURL":bundleURL, 94 | @"DisplayName": name, 95 | @"BundleIdentifier": bundleID}; 96 | Application *app = [[Application alloc] initWithBundleInfo:bundleInfo]; 97 | returnValue[bundleID] = app; 98 | 99 | [self cacheBundle:bundleInfo]; 100 | } 101 | } 102 | } 103 | } 104 | 105 | [self writeToCache]; 106 | 107 | return returnValue; 108 | } 109 | 110 | - (NSDictionary *)listApplicationsForiOS8AndHigher 111 | { 112 | NSMutableDictionary *returnValue = [NSMutableDictionary new]; 113 | LSApplicationWorkspace *applicationWorkspace = [LSApplicationWorkspace defaultWorkspace]; 114 | 115 | NSArray *proxies = [applicationWorkspace allApplications]; 116 | NSDictionary *bundleInfo = nil; 117 | 118 | for (FBApplicationInfo *proxy in proxies) 119 | { 120 | NSString *appType = [proxy performSelector:@selector(applicationType)]; 121 | 122 | if ([appType isEqualToString:@"User"] && proxy.bundleContainerURL && proxy.bundleURL) 123 | { 124 | NSString *scinfo = [proxy.bundleURL.path stringByAppendingPathComponent:@"SC_Info"]; 125 | 126 | BOOL isDirectory; 127 | BOOL purchased = [[NSFileManager defaultManager] fileExistsAtPath:scinfo isDirectory:&isDirectory]; 128 | 129 | if (purchased && isDirectory) 130 | { 131 | NSString *itemName = ((LSApplicationProxy*)proxy).itemName; 132 | 133 | if (!itemName) 134 | { 135 | itemName = ((LSApplicationProxy*)proxy).localizedName; 136 | } 137 | 138 | bundleInfo = @{ 139 | @"BundleContainer":proxy.bundleContainerURL, 140 | @"BundleURL":proxy.bundleURL, 141 | @"DisplayName": itemName, 142 | @"BundleIdentifier": proxy.bundleIdentifier 143 | }; 144 | 145 | Application *app = [[Application alloc] initWithBundleInfo:bundleInfo]; 146 | returnValue[proxy.bundleIdentifier] = app; 147 | 148 | [self cacheBundle:bundleInfo]; 149 | } 150 | } 151 | } 152 | 153 | [self writeToCache]; 154 | 155 | return returnValue.copy; 156 | } 157 | 158 | - (void)writeToCache 159 | { 160 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); 161 | dispatch_async(queue, ^{ 162 | [_cachedApps writeToFile:applistCachePath atomically:YES]; 163 | }); 164 | } 165 | 166 | 167 | - (NSDictionary *)_allApplications 168 | { 169 | NSDictionary *returnValue; 170 | if (SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(NSFoundationVersionNumber_iOS_7_0)) 171 | { 172 | returnValue = [self listApplicationsForiOS7AndLower]; 173 | } 174 | else 175 | { 176 | returnValue = [self listApplicationsForiOS8AndHigher]; 177 | } 178 | 179 | return returnValue.copy; 180 | } 181 | 182 | 183 | - (NSDictionary *)installedApps 184 | { 185 | return [self _allApplications]; 186 | } 187 | 188 | 189 | -(NSDictionary*)_allCachedApplications 190 | { 191 | if ([_cachedApps count] < 1) 192 | { 193 | return [self _allApplications]; 194 | } 195 | 196 | NSMutableDictionary* returnValue = [NSMutableDictionary new]; 197 | for (NSDictionary* bundleInfo in _cachedApps) 198 | { 199 | Application *app =[[Application alloc]initWithBundleInfo:bundleInfo]; 200 | returnValue[bundleInfo[@"BundleIdentifier"]] = app; 201 | } 202 | 203 | return returnValue; 204 | } 205 | 206 | 207 | -(void)cacheBundle:(NSDictionary*) bundle 208 | { 209 | [_cachedApps addObject:bundle]; 210 | } 211 | 212 | 213 | - (NSArray *)dumpedApps 214 | { 215 | NSString *dumpedPath = @""; 216 | NSArray *array = [[NSArray alloc] initWithArray:[[NSFileManager defaultManager] contentsOfDirectoryAtPath:dumpedPath error:nil]]; 217 | 218 | NSMutableArray *paths = [NSMutableArray new]; 219 | 220 | for (int i = 0; i < array.count; i++) 221 | { 222 | if (![[array[i] pathExtension] caseInsensitiveCompare:@"ipa"]) 223 | { 224 | [paths addObject:array[i]]; 225 | } 226 | } 227 | 228 | return paths; 229 | } 230 | 231 | @end -------------------------------------------------------------------------------- /Clutch/Binary.h: -------------------------------------------------------------------------------- 1 | // 2 | // Binary.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 10.02.15. 6 | // 7 | // 8 | 9 | #import 10 | #import "BundleDumpOperation.h" 11 | 12 | @class ClutchBundle; 13 | 14 | @interface Binary : NSObject 15 | 16 | @property (readonly) BOOL hasRestrictedSegment; 17 | 18 | @property (readonly) BundleDumpOperation *dumpOperation; 19 | @property (readonly) NSString *workingPath; 20 | @property (readonly) NSString *binaryPath; 21 | @property (readonly) NSString *sinfPath; 22 | @property (readonly) NSString *supfPath; 23 | @property (readonly) NSString *suppPath; 24 | @property (readonly) NSString* frameworksPath; 25 | 26 | @property (readonly) BOOL isFAT; 27 | @property (readonly) BOOL hasARMSlice; 28 | @property (readonly) BOOL hasARM64Slice; 29 | @property (readonly) BOOL hasMultipleARMSlices; 30 | @property (readonly) BOOL hasMultipleARM64Slices; 31 | 32 | - (instancetype)initWithBundle:(ClutchBundle *)bundle; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Clutch/Binary.m: -------------------------------------------------------------------------------- 1 | // 2 | // Binary.m 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 10.02.15. 6 | // 7 | // 8 | 9 | #import "Binary.h" 10 | #import "ClutchBundle.h" 11 | 12 | #include 13 | #import "optool.h" 14 | #import "NSFileHandle+Private.h" 15 | #import "ClutchPrint.h" 16 | 17 | @interface Binary () 18 | { 19 | ClutchBundle *_bundle; 20 | BOOL _isFAT; 21 | BOOL _m32; 22 | BOOL _m64; 23 | } 24 | @end 25 | 26 | @implementation Binary 27 | 28 | - (NSString *)workingPath 29 | { 30 | return [_bundle.workingPath stringByAppendingPathComponent:_bundle.bundleIdentifier]; 31 | } 32 | 33 | - (instancetype)initWithBundle:(ClutchBundle *)path 34 | { 35 | if (self = [super init]) { 36 | 37 | _bundle = path; 38 | 39 | [[ClutchPrint sharedInstance] printDeveloper:@"######## bundle URL %@", _bundle.bundleContainerURL]; 40 | if ([[_bundle.bundleContainerURL path] hasSuffix:@"Frameworks"]) { 41 | _frameworksPath = [_bundle.bundleContainerURL path]; 42 | } 43 | 44 | // perm. fix 45 | 46 | NSDictionary *ownershipInfo = @{NSFileOwnerAccountName:@"mobile", NSFileGroupOwnerAccountName:@"mobile"}; 47 | 48 | [[NSFileManager defaultManager] setAttributes:ownershipInfo ofItemAtPath:self.binaryPath error:nil]; 49 | 50 | _sinfPath = [_bundle pathForResource:_bundle.executablePath.lastPathComponent ofType:@"sinf" inDirectory:@"SC_Info"]; 51 | _supfPath = [_bundle pathForResource:_bundle.executablePath.lastPathComponent ofType:@"supf" inDirectory:@"SC_Info"]; 52 | _suppPath = [_bundle pathForResource:_bundle.executablePath.lastPathComponent ofType:@"supp" inDirectory:@"SC_Info"]; 53 | 54 | _dumpOperation = [[BundleDumpOperation alloc]initWithBundle:_bundle]; 55 | 56 | NSFileHandle *tmpHandle = [[NSFileHandle alloc]initWithFileDescriptor:fileno(fopen(_bundle.executablePath.UTF8String, "r+")) closeOnDealloc:YES]; 57 | 58 | NSData *headersData = tmpHandle.availableData; 59 | 60 | thin_header headers[4]; 61 | uint32_t numHeaders = 0; 62 | 63 | headersFromBinary(headers, headersData, &numHeaders); 64 | 65 | int m32=0,m64=0; 66 | for (int i= 0; i 1; 81 | _m64 = m64 > 1; 82 | _isFAT = numHeaders > 1; 83 | 84 | _hasRestrictedSegment = NO; 85 | 86 | struct thin_header macho = headers[0]; 87 | 88 | unsigned long long size = [tmpHandle seekToEndOfFile]; 89 | 90 | [tmpHandle seekToFileOffset:macho.offset + macho.size]; 91 | 92 | for (int i = 0; i < macho.header.ncmds; i++) { 93 | if (tmpHandle.offsetInFile >= size || 94 | tmpHandle.offsetInFile > macho.header.sizeofcmds + macho.size + macho.offset) 95 | break; 96 | 97 | uint32_t cmd = [tmpHandle intAtOffset:tmpHandle.offsetInFile]; 98 | uint32_t size = [tmpHandle intAtOffset:tmpHandle.offsetInFile + sizeof(uint32_t)]; 99 | 100 | struct segment_command * command; 101 | 102 | command = malloc(sizeof(struct segment_command)); 103 | 104 | [tmpHandle getBytes:command inRange:NSMakeRange((NSUInteger)(tmpHandle.offsetInFile),sizeof(struct segment_command))]; 105 | 106 | if (((cmd == LC_SEGMENT) || (cmd == LC_SEGMENT_64)) && (strcmp(command->segname, "__RESTRICT") == 0)) { 107 | _hasRestrictedSegment = YES; 108 | break; 109 | } else 110 | [tmpHandle seekToFileOffset:tmpHandle.offsetInFile + size]; 111 | 112 | free(command); 113 | 114 | } 115 | 116 | [tmpHandle closeFile]; 117 | 118 | } 119 | 120 | return self; 121 | } 122 | 123 | - (NSString *)binaryPath 124 | { 125 | NSString *path = [_bundle.executablePath copy]; 126 | 127 | if ([path hasPrefix:@"/var/mobile"]) { 128 | path = [@"/private" stringByAppendingString:path]; 129 | } 130 | 131 | return path; 132 | } 133 | 134 | - (BOOL)isFAT 135 | { 136 | return _isFAT; 137 | } 138 | 139 | - (BOOL)hasARMSlice 140 | { 141 | return [_bundle.executableArchitectures containsObject:@CPU_TYPE_ARM]; 142 | } 143 | 144 | - (BOOL)hasARM64Slice 145 | { 146 | return [_bundle.executableArchitectures containsObject:@CPU_TYPE_ARM64]; 147 | } 148 | 149 | - (BOOL)hasMultipleARM64Slices 150 | { 151 | return _m64; 152 | } 153 | 154 | - (BOOL)hasMultipleARMSlices 155 | { 156 | return _m32; 157 | } 158 | 159 | - (NSString *)description { 160 | return [NSString stringWithFormat:@"<%@>",_bundle.executablePath.lastPathComponent]; 161 | } 162 | 163 | @end 164 | 165 | 166 | -------------------------------------------------------------------------------- /Clutch/BinaryDumpProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // BinaryDumpProtocol.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 22.03.15. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | #import "optool.h" 12 | #import "NSFileHandle+Private.h" 13 | 14 | typedef NS_ENUM(NSUInteger, ArchCompatibility) { 15 | ArchCompatibilityCompatible, 16 | //ArchCompatibilityStrip, 17 | ArchCompatibilitySwap, 18 | ArchCompatibilityNotCompatible, 19 | }; 20 | 21 | typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data); 22 | void sha1(uint8_t *hash, uint8_t *data, size_t size); 23 | 24 | #define CSSLOT_CODEDIRECTORY 0 25 | 26 | #define PT_TRACE_ME 0 27 | 28 | struct blob_index { 29 | unsigned int type; 30 | unsigned int offset; 31 | }; 32 | 33 | struct super_blob { 34 | unsigned int magic; 35 | unsigned int length; 36 | unsigned int count; 37 | struct blob_index index[]; 38 | }; 39 | 40 | struct code_directory { 41 | unsigned int magic; 42 | unsigned int length; 43 | unsigned int version; 44 | unsigned int flags; 45 | unsigned int hashOffset; 46 | unsigned int identOffset; 47 | unsigned int nSpecialSlots; 48 | unsigned int nCodeSlots; /* number of ordinary (code) hash slots */ 49 | unsigned int codeLimit; 50 | unsigned char hashSize; 51 | unsigned char hashType; 52 | unsigned char spare1; 53 | unsigned char pageSize; 54 | unsigned int spare2; 55 | }; 56 | 57 | @protocol BinaryDumpProtocol 58 | 59 | - (cpu_type_t)supportedCPUType; 60 | 61 | - (BOOL)dumpBinary; 62 | 63 | @end 64 | 65 | @protocol FrameworkBinaryDumpProtocol 66 | 67 | - (cpu_type_t)supportedCPUType; 68 | 69 | - (BOOL)dumpBinary; 70 | 71 | @end -------------------------------------------------------------------------------- /Clutch/BundleDumpOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // BundleDumpOperation.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 11.02.15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @class ClutchBundle; 12 | 13 | @interface BundleDumpOperation : NSOperation 14 | @property (assign) BOOL failed; 15 | 16 | - (instancetype)initWithBundle:(ClutchBundle *)application; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Clutch/Clutch-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'Clutch' target in the 'Clutch' project 3 | // 4 | 5 | 6 | /* credits: some guy on stackoverflow, sorry! 7 | 8 | #define KNRM "\x1B[0m" 9 | #define KRED "\x1B[31m" 10 | #define KGRN "\x1B[32m" 11 | #define KYEL "\x1B[33m" 12 | #define KBLU "\x1B[34m" 13 | #define KMAG "\x1B[35m" 14 | #define KCYN "\x1B[36m" 15 | #define KWHT "\x1B[37m" 16 | #define RESET "\033[0m" 17 | 18 | */ 19 | 20 | #ifdef __OBJC__ 21 | #import 22 | #endif 23 | 24 | #include 25 | int diff_ms(struct timeval t1, struct timeval t2); 26 | 27 | 28 | 29 | #define CLUTCH_VERSION_ @"2.0.4" 30 | 31 | #ifdef DEBUG 32 | #define CLUTCH_VERSION [NSString stringWithFormat:@"%@ DEBUG",CLUTCH_VERSION_] 33 | #else 34 | #define CLUTCH_VERSION CLUTCH_VERSION_ 35 | #endif 36 | 37 | 38 | //# define SUCCESS(M, ...) fprintf(stderr, "\033[1;35m%s\033[0m\n",[[NSString stringWithFormat:M, ##__VA_ARGS__] UTF8String]); 39 | //# define SUCCESS_OUT(M, ...) fprintf(stdout, "\033[1;35m%s\033[0m\n",[[NSString stringWithFormat:M, ##__VA_ARGS__] UTF8String]); 40 | 41 | # define FILE_NAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) // shortened path of __FILE__ is there is one 42 | 43 | /*#ifdef DEBUG 44 | 45 | # define ERROR(M, ...) fprintf(stderr, "\033[1;31mERROR\033[0m | %s:%s [Line %d] | %s\n", FILE_NAME, __PRETTY_FUNCTION__, __LINE__, [[NSString stringWithFormat:M, ##__VA_ARGS__] UTF8String]); 46 | # define NSLog(M, ...) fprintf(stderr, "\033[0;33mDEBUG\033[0m | %s:%s [Line %d] | %s\n", FILE_NAME, __PRETTY_FUNCTION__, __LINE__, [[NSString stringWithFormat:M, ##__VA_ARGS__] UTF8String]); 47 | # define VERBOSE(M, ...) fprintf(stderr, "\033[0;33mVERBOSE\033[0m | %s:%s [Line %d] | %s\n", FILE_NAME, __PRETTY_FUNCTION__, __LINE__, [[NSString stringWithFormat:M, ##__VA_ARGS__] UTF8String]); 48 | 49 | #else 50 | # define NSLog(...) 51 | 52 | # define ERROR(M, ...) fprintf(stderr, "\033[1;31mERROR\033[0m | %s\n",[[NSString stringWithFormat:M, ##__VA_ARGS__] UTF8String]); 53 | # define VERBOSE(M, ...) fprintf(stderr, "\033[0;33m%s\033[0m\n",[[NSString stringWithFormat:M, ##__VA_ARGS__] UTF8String]); 54 | #endif 55 | */ 56 | 57 | 58 | #define SYSTEM_VERSION_EQUAL_TO(_v) ( floor(NSFoundationVersionNumber) == _v ? YES : NO ) 59 | #define SYSTEM_VERSION_GREATER_THAN(_v) ( floor(NSFoundationVersionNumber) > _v ? YES : NO ) 60 | #define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(_v) ( floor(NSFoundationVersionNumber) >= _v ? YES : NO ) 61 | #define SYSTEM_VERSION_LESS_THAN(_v) ( floor(NSFoundationVersionNumber) < _v ? YES : NO ) 62 | #define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(_v) ( floor(NSFoundationVersionNumber) <= _v ? YES : NO ) 63 | -------------------------------------------------------------------------------- /Clutch/Clutch.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | get-task-allow 6 | 7 | task_for_pid-allow 8 | 9 | com.apple.backboardd.debugapplications 10 | 11 | com.apple.springboard.debugapplications 12 | 13 | run-unsigned-code 14 | 15 | com.apple.private.librarian.can-get-application-info 16 | 17 | com.apple.private.mobileinstall.allowedSPI 18 | 19 | Lookup 20 | CopyInstalledAppsForLaunchServices 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Clutch/ClutchBundle.h: -------------------------------------------------------------------------------- 1 | // 2 | // ClutchBundle.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 10.02.15. 6 | // 7 | // 8 | 9 | #import 10 | #import "Binary.h" 11 | 12 | @class Application; 13 | 14 | @interface ClutchBundle : NSBundle 15 | { 16 | @public 17 | NSOperationQueue *_dumpQueue; 18 | } 19 | 20 | @property ClutchBundle *parentBundle; 21 | @property (readonly) NSString *workingPath; 22 | @property (readonly) NSString *zipFilename; 23 | @property (readonly) NSString *zipPrefix; 24 | @property (readonly) NSURL *enumURL; 25 | @property (readonly) NSURL *bundleContainerURL; 26 | @property (readonly) Binary *executable; 27 | 28 | @property (readonly) NSString* displayName; 29 | 30 | - (instancetype)initWithBundleInfo:(NSDictionary *)info; 31 | - (void)dumpToDirectoryURL:(NSURL *)directoryURL; 32 | - (void)prepareForDump; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Clutch/ClutchBundle.m: -------------------------------------------------------------------------------- 1 | // 2 | // ClutchBundle.m 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 10.02.15. 6 | // 7 | // 8 | 9 | #import "ClutchBundle.h" 10 | #import "optool.h" 11 | #import "ClutchPrint.h" 12 | 13 | @interface ClutchBundle () 14 | 15 | @end 16 | 17 | @implementation ClutchBundle 18 | 19 | - (instancetype)initWithBundleInfo:(NSDictionary *)info 20 | { 21 | if (self = [super initWithURL:info[@"BundleURL"]]) { 22 | _bundleContainerURL = [info[@"BundleContainer"] copy]; 23 | _displayName = [info[@"DisplayName"] copy]; 24 | _dumpQueue = [NSOperationQueue new]; 25 | } 26 | 27 | return self; 28 | } 29 | 30 | - (void)prepareForDump { 31 | 32 | _executable = [[Binary alloc]initWithBundle:self]; 33 | 34 | [[ClutchPrint sharedInstance] printVerbose:@"Preparing to dump %@", _executable]; 35 | [[ClutchPrint sharedInstance] printVerbose:@"Path: %@", self.executable.binaryPath]; 36 | 37 | NSDictionary *ownershipInfo = @{NSFileOwnerAccountName:@"mobile", NSFileGroupOwnerAccountName:@"mobile"}; 38 | 39 | [[NSFileManager defaultManager] setAttributes:ownershipInfo ofItemAtPath:self.executable.binaryPath error:nil]; 40 | 41 | } 42 | 43 | - (void)dumpToDirectoryURL:(NSURL *)directoryURL 44 | { 45 | if (_dumpQueue.operationCount) 46 | [_dumpQueue cancelAllOperations]; 47 | } 48 | 49 | - (NSString *)debugDescription { 50 | return [NSString stringWithFormat:@"<%@: %p, bundleIdentifier: %@, bundleURL: %@>",NSStringFromClass([self class]),self,self.bundleIdentifier,self.bundleURL]; 51 | } 52 | 53 | - (NSString *)description { 54 | return [NSString stringWithFormat:@"<%@ bundleID: %@>",self.bundlePath.lastPathComponent.stringByDeletingPathExtension,self.bundleIdentifier]; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Clutch/ClutchCommands.h: -------------------------------------------------------------------------------- 1 | // 2 | // ClutchCommands.h 3 | // Clutch 4 | // 5 | // Created by dev on 10/01/2017. 6 | // 7 | // 8 | 9 | #import 10 | 11 | typedef NS_OPTIONS(NSInteger, ClutchCommandFlag) { 12 | ClutchCommandFlagNone = 0, 13 | ClutchCommandFlagInvisible = 1 << 0, // don't print to help 14 | ClutchCommandFlagArgumentRequired = 1 << 1, // requires args 15 | ClutchCommandFlagNoArguments = 1 << 2, // will not take args 16 | ClutchCommandFlagOptional = 1 << 3, // can be optionally added to any other command (i.e. --verbose) 17 | }; 18 | 19 | typedef NS_ENUM(NSUInteger, ClutchCommandOption) { 20 | ClutchCommandOptionNone, 21 | ClutchCommandOptionFrameworkDump, 22 | ClutchCommandOptionBinaryDump, 23 | ClutchCommandOptionDump, 24 | ClutchCommandOptionPrintInstalled, 25 | ClutchCommandOptionClean, 26 | ClutchCommandOptionVersion, 27 | ClutchCommandOptionHelp, 28 | ClutchCommandOptionNoColor, 29 | ClutchCommandOptionVerbose 30 | }; 31 | 32 | @interface ClutchCommand : NSObject 33 | 34 | @property (nonatomic) ClutchCommandOption option; 35 | @property (nonatomic) NSString *shortOption; 36 | @property (nonatomic) NSString *longOption; 37 | @property (nonatomic) NSString *commandDescription; 38 | @property (nonatomic) ClutchCommandFlag flag; 39 | 40 | - (instancetype)initWithCommandOption:(ClutchCommandOption)commandOption shortOption:(NSString *)shortOption longOption:(NSString *)longOption commandDescription:(NSString *)commandDescription flag:(ClutchCommandFlag)flag; 41 | 42 | @end 43 | 44 | @interface ClutchCommands : NSObject 45 | 46 | @property (nonatomic, retain) NSArray *allCommands; 47 | @property (nonatomic) NSArray *commands; 48 | @property (nonatomic) NSString *helpString; 49 | @property (nonatomic) NSArray *values; 50 | 51 | - (instancetype)initWithArguments:(NSArray *)arguments; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /Clutch/ClutchCommands.m: -------------------------------------------------------------------------------- 1 | // 2 | // ClutchCommands.m 3 | // Clutch 4 | // 5 | // Created by dev on 10/01/2017. 6 | // 7 | // 8 | 9 | #import "ClutchCommands.h" 10 | #import "ClutchPrint.h" 11 | 12 | @implementation ClutchCommand 13 | 14 | - (instancetype)initWithCommandOption:(ClutchCommandOption)commandOption shortOption:(NSString *)shortOption longOption:(NSString *)longOption commandDescription:(NSString *)commandDescription flag:(ClutchCommandFlag)flag 15 | { 16 | self = [super self]; 17 | 18 | if (self) 19 | { 20 | self.option = commandOption; 21 | self.shortOption = shortOption; 22 | self.longOption = longOption; 23 | self.commandDescription = commandDescription; 24 | self.flag = flag; 25 | } 26 | 27 | return self; 28 | } 29 | 30 | @end 31 | 32 | @implementation ClutchCommands 33 | 34 | - (instancetype)initWithArguments:(NSArray *)arguments 35 | { 36 | self = [super self]; 37 | 38 | if (self) 39 | { 40 | self.allCommands = [self buildCommands]; 41 | self.commands = [self parseCommandWithArguments:arguments]; 42 | self.helpString = [self buildHelpString]; 43 | } 44 | 45 | return self; 46 | } 47 | 48 | - (NSArray *)parseCommandWithArguments:(NSArray *)arguments 49 | { 50 | NSMutableArray *returnCommands = [NSMutableArray new]; 51 | NSMutableArray *returnValues = [NSMutableArray new]; 52 | 53 | BOOL commandFound = NO; 54 | 55 | for (NSString *argument in arguments) 56 | { 57 | if ([argument isEqualToString:arguments[0]]) 58 | { 59 | continue; 60 | } 61 | else if ([argument isEqualToString:@"--no-color"]) // Optionals 62 | { 63 | [returnCommands insertObject:self.allCommands[8] atIndex:0]; 64 | } 65 | else if ([argument isEqualToString:@"--verbose"]) 66 | { 67 | [returnCommands insertObject:self.allCommands[9] atIndex:0]; 68 | } 69 | else if ([argument hasPrefix:@"-"]) 70 | { 71 | // is a flag 72 | for (ClutchCommand *command in self.allCommands) 73 | { 74 | if ([argument isEqualToString:command.shortOption] || [argument isEqualToString:command.longOption]) 75 | { 76 | if (commandFound == NO) 77 | { 78 | commandFound = YES; 79 | [returnCommands addObject:command]; 80 | break; 81 | } 82 | else 83 | { 84 | [[ClutchPrint sharedInstance] print:@"Ignoring incorrectly chained command and values: %@.", argument]; 85 | // ignore 2nd command in chained commands like -b foo -d bar 86 | } 87 | } 88 | } 89 | } 90 | else 91 | { 92 | // is a value 93 | [returnValues addObject:argument]; 94 | } 95 | } 96 | 97 | if (returnCommands.count < 1) 98 | { 99 | return @[self.allCommands[0]]; 100 | } 101 | 102 | self.values = (NSArray *)returnValues; 103 | 104 | return (NSArray *)returnCommands; 105 | } 106 | 107 | - (NSArray *)buildCommands 108 | { 109 | ClutchCommand *none = [[ClutchCommand alloc] initWithCommandOption:ClutchCommandOptionNone shortOption:nil longOption:nil commandDescription:@"None command" flag:(ClutchCommandFlagInvisible | ClutchCommandFlagNoArguments)]; 110 | ClutchCommand *framework = [[ClutchCommand alloc] initWithCommandOption:ClutchCommandOptionFrameworkDump shortOption:@"-f" longOption:@"--fmwk-dump" commandDescription:@"Only dump binary files from specified bundleID" flag:(ClutchCommandFlagArgumentRequired|ClutchCommandFlagInvisible)]; 111 | ClutchCommand *binary = [[ClutchCommand alloc] initWithCommandOption:ClutchCommandOptionBinaryDump shortOption:@"-b" longOption:@"--binary-dump" commandDescription:@"Only dump binary files from specified bundleID" flag:ClutchCommandFlagArgumentRequired]; 112 | ClutchCommand *dump = [[ClutchCommand alloc] initWithCommandOption:ClutchCommandOptionDump shortOption:@"-d" longOption:@"--dump" commandDescription:@"Dump specified bundleID into .ipa file" flag:ClutchCommandFlagArgumentRequired]; 113 | ClutchCommand *printInstalled = [[ClutchCommand alloc] initWithCommandOption:ClutchCommandOptionPrintInstalled shortOption:@"-i" longOption:@"--print-installed" commandDescription:@"Prints installed applications" flag:ClutchCommandFlagNoArguments]; 114 | ClutchCommand *clean = [[ClutchCommand alloc] initWithCommandOption:ClutchCommandOptionClean shortOption:nil longOption:@"--clean" commandDescription:@"Clean /var/tmp/clutch directory" flag:ClutchCommandFlagNoArguments]; 115 | ClutchCommand *version = [[ClutchCommand alloc] initWithCommandOption:ClutchCommandOptionVersion shortOption:nil longOption:@"--version" commandDescription:@"Display version and exit" flag:ClutchCommandFlagNoArguments]; 116 | ClutchCommand *help = [[ClutchCommand alloc] initWithCommandOption:ClutchCommandOptionHelp shortOption:@"-?" longOption:@"--help" commandDescription:@"Displays this help and exit" flag:ClutchCommandFlagNoArguments]; 117 | ClutchCommand *noColor = [[ClutchCommand alloc] initWithCommandOption:ClutchCommandOptionNoColor shortOption:@"-n" longOption:@"--no-color" commandDescription:@"Prints with colors disabled" flag:ClutchCommandFlagOptional]; 118 | ClutchCommand *verbose = [[ClutchCommand alloc] initWithCommandOption:ClutchCommandOptionVerbose shortOption:@"-v" longOption:@"--verbose" commandDescription:@"Print verbose messages" flag:ClutchCommandFlagOptional]; 119 | 120 | return @[none, framework, binary, dump, printInstalled, clean, version, help, noColor, verbose]; 121 | } 122 | 123 | - (ClutchCommand *)parseCommandString:(NSString *)commandString 124 | { 125 | for (ClutchCommand *command in self.commands) 126 | { 127 | if ([commandString isEqualToString:command.shortOption] || [commandString isEqualToString:command.longOption]) 128 | { 129 | return command; 130 | } 131 | } 132 | 133 | return self.commands[0]; // return ClutchCommand None 134 | } 135 | 136 | - (NSString *)buildHelpString 137 | { 138 | NSMutableString *helpString = [NSMutableString stringWithFormat:@"Usage: %@ [OPTIONS]\n", [NSProcessInfo processInfo].processName]; 139 | 140 | for (ClutchCommand *command in self.allCommands) 141 | { 142 | BOOL isInvisible = (command.flag & ClutchCommandFlagInvisible); 143 | 144 | if (!isInvisible) 145 | { 146 | [helpString appendFormat:@"%-2s %-30s%@\n", command.shortOption.UTF8String ? command.shortOption.UTF8String : " ", command.longOption.UTF8String, command.commandDescription]; 147 | } 148 | } 149 | 150 | return (NSString *)helpString; 151 | } 152 | 153 | @end 154 | -------------------------------------------------------------------------------- /Clutch/ClutchPrint.h: -------------------------------------------------------------------------------- 1 | // 2 | // ClutchPrint.h 3 | // Clutch 4 | // 5 | // Created by dev on 15/02/2016. 6 | // 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, ClutchPrinterVerboseLevel) { 12 | ClutchPrinterVerboseLevelNone = 0, 13 | ClutchPrinterVerboseLevelUser = 1, 14 | ClutchPrinterVerboseLevelDeveloper = 2, 15 | ClutchPrinterVerboseLevelFull = 3 16 | }; 17 | 18 | typedef NS_ENUM(NSInteger, ClutchPrinterColorLevel) { 19 | ClutchPrinterColorLevelNone = 0, 20 | ClutchPrinterColorLevelFormatOnly = 1, 21 | ClutchPrinterColorLevelFull = 2, 22 | }; 23 | 24 | typedef NS_ENUM(NSInteger, ClutchPrinterColor) 25 | { 26 | ClutchPrinterColorNone = 0, 27 | ClutchPrinterColorRed = 1, 28 | ClutchPrinterColorPurple = 2, 29 | ClutchPrinterColorPink = 3 30 | }; 31 | 32 | 33 | /*#define ClutchPrint(f, ...) [[ClutchPrint sharedInstance] printWithFormat:[NSString stringWithFormat:(f), ##__VA_ARGS__]] 34 | #define ClutchPrintDeveloper(f, ...) [[ClutchPrint sharedInstance] printDeveloperWithFileArguments:@[[[NSString stringWithUTF8String:__FILE__] lastPathComponent], [NSNumber numberWithInt:__LINE__], [NSString stringWithUTF8String:__PRETTY_FUNCTION__]] format:[NSString stringWithFormat:(f), #__VA_ARGS__]] 35 | #define ClutchPrintVerbose(f, ...) [[ClutchPrint sharedInstance] printVerboseWithFormat:[NSString stringWithFormat:(f), ##__VA_ARGS__]] 36 | #define ClutchPrintError(f, ...) [[ClutchPrint sharedInstance] printError:[NSString stringWithFormat:(f), ##__VA_ARGS__]] 37 | #define ClutchPrintColor(color, f, ...)[[ClutchPrint sharedInstance] printColoredStringWithColor:color message:[NSString stringWithFormat:(f), ##__VA_ARGS__]] 38 | */ 39 | 40 | @interface ClutchPrint : NSObject 41 | - (instancetype)initWithColorLevel:(ClutchPrinterColorLevel)colorLevel verboseLevel:(ClutchPrinterVerboseLevel)verboseLevel; 42 | 43 | + (instancetype)sharedInstance; 44 | 45 | - (void)print:(NSString *)format, ...; 46 | - (void)printDeveloper:(NSString *)format, ...; 47 | - (void)printVerbose:(NSString *)format, ...; 48 | - (void)printError:(NSString *)format, ...; 49 | - (void)printColor:(ClutchPrinterColor)color format:(NSString *)format, ...; 50 | 51 | - (void)setColorLevel:(ClutchPrinterColorLevel)colorLev; 52 | - (void)setVerboseLevel:(ClutchPrinterVerboseLevel)verboseLev; 53 | 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /Clutch/ClutchPrint.m: -------------------------------------------------------------------------------- 1 | // 2 | // ClutchPrint.m 3 | // Clutch 4 | // 5 | // Created by dev on 15/02/2016. 6 | // 7 | // 8 | 9 | #import "ClutchPrint.h" 10 | 11 | @interface ClutchPrint () 12 | { 13 | ClutchPrinterColorLevel colorLevel; 14 | ClutchPrinterVerboseLevel verboseLevel; 15 | } 16 | 17 | @end 18 | 19 | @implementation ClutchPrint 20 | 21 | + (instancetype)sharedInstance 22 | { 23 | static dispatch_once_t pred; 24 | static id shared = nil; 25 | 26 | dispatch_once(&pred, ^{ 27 | shared = [self new]; 28 | }); 29 | 30 | return shared; 31 | } 32 | 33 | - (void)setVerboseLevel:(ClutchPrinterVerboseLevel)verboseLev 34 | { 35 | verboseLevel = verboseLev; 36 | } 37 | 38 | - (void)setColorLevel:(ClutchPrinterColorLevel)colorLev 39 | { 40 | colorLevel = colorLev; 41 | } 42 | 43 | - (instancetype)initWithColorLevel:(ClutchPrinterColorLevel)colorLev verboseLevel:(ClutchPrinterVerboseLevel)verboseLev 44 | { 45 | if (self = [super init]) 46 | { 47 | verboseLevel = verboseLev; 48 | colorLevel = colorLev; 49 | } 50 | 51 | return self; 52 | } 53 | 54 | - (void)print:(NSString *)format, ... 55 | { 56 | if (format != nil) 57 | { 58 | va_list args; 59 | va_start(args, format); 60 | NSString *formatString = [[NSString alloc] initWithFormat:format arguments:args]; 61 | NSString *printString = [NSString stringWithFormat:@"%@\n", formatString]; 62 | printf("%s", printString.UTF8String); 63 | va_end(args); 64 | } 65 | } 66 | 67 | - (void)printDeveloper:(NSString *)format, ... 68 | { 69 | #ifdef DEBUG 70 | if (verboseLevel == ClutchPrinterVerboseLevelDeveloper || verboseLevel == ClutchPrinterVerboseLevelFull) 71 | { 72 | if (format != nil) 73 | { 74 | NSString *stackSymobolsString = [NSThread callStackSymbols][1]; 75 | NSMutableArray *stackSymbols = [NSMutableArray arrayWithArray:[stackSymobolsString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" -[]+?.,"]]]; 76 | [stackSymbols removeObject:@""]; 77 | 78 | va_list args; 79 | va_start(args, format); 80 | NSString *formatString = [[NSString alloc] initWithFormat:format arguments:args]; 81 | NSString *printString = [NSString stringWithFormat:@"%@ | %@\n", stackSymbols[3], formatString]; 82 | printf("%s", printString.UTF8String); 83 | va_end(args); 84 | } 85 | } 86 | #endif 87 | } 88 | 89 | - (void)printError:(NSString *)format, ... 90 | { 91 | if (format != nil) 92 | { 93 | va_list args; 94 | va_start(args, format); 95 | 96 | NSString *formatString = [[NSString alloc] initWithFormat:format arguments:args]; 97 | NSString *printString = [NSString stringWithFormat:@"Error: %@\n", formatString]; 98 | 99 | [self printColor:ClutchPrinterColorRed format:@"%@", printString]; 100 | va_end(args); 101 | } 102 | } 103 | 104 | - (void)printColor:(ClutchPrinterColor)color format:(NSString *)format, ... 105 | { 106 | if (format != nil) 107 | { 108 | va_list args; 109 | va_start(args, format); 110 | NSString *formatString = [[NSString alloc] initWithFormat:format arguments:args]; 111 | NSString *printString; 112 | if (colorLevel == ClutchPrinterColorLevelNone) 113 | { 114 | printString = [NSString stringWithFormat:@"%@\n", formatString]; 115 | } 116 | else if (colorLevel == ClutchPrinterColorLevelFull) 117 | { 118 | NSString *colorString; 119 | switch (color) 120 | { 121 | case ClutchPrinterColorPink: 122 | colorString = @"\033[1;35m"; 123 | break; 124 | case ClutchPrinterColorRed: 125 | colorString = @"\033[1;31m"; 126 | break; 127 | case ClutchPrinterColorPurple: 128 | colorString = @"\033[0;34m"; 129 | break; 130 | default: 131 | colorString = @""; 132 | break; 133 | } 134 | 135 | printString = [NSString stringWithFormat:@"%@%@\033[0m\n", colorString, formatString]; 136 | } 137 | 138 | 139 | printf("%s", printString.UTF8String); 140 | va_end(args); 141 | } 142 | } 143 | 144 | - (void)printVerbose:(NSString *)format, ... 145 | { 146 | if (verboseLevel == ClutchPrinterVerboseLevelDeveloper || verboseLevel == ClutchPrinterVerboseLevelFull) 147 | { 148 | if (format != nil) 149 | { 150 | va_list args; 151 | va_start(args, format); 152 | 153 | NSString *formatString =[[NSString alloc] initWithFormat:format arguments:args]; 154 | NSString *printString = [NSString stringWithFormat:@"%@\n", formatString]; 155 | 156 | printf("%s", printString.UTF8String); 157 | va_end(args); 158 | } 159 | } 160 | } 161 | 162 | 163 | @end 164 | -------------------------------------------------------------------------------- /Clutch/Device.h: -------------------------------------------------------------------------------- 1 | // 2 | // Device.h 3 | // Clutch 4 | // 5 | // Created by Zorro on 14/11/13. 6 | // Copyright (c) 2013 AppAddict. All rights reserved. 7 | // 8 | // Re-tailored for Clutch 9 | 10 | #import 11 | #include 12 | #include 13 | #import 14 | 15 | #import "BinaryDumpProtocol.h" 16 | 17 | @interface Device : NSObject 18 | 19 | + (cpu_type_t)cpu_type; 20 | + (cpu_subtype_t)cpu_subtype; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Clutch/Device.m: -------------------------------------------------------------------------------- 1 | // 2 | // Device.m 3 | // Clutch 4 | // 5 | // Created by Zorro on 14/11/13. 6 | // Copyright (c) 2013 AppAddict. All rights reserved. 7 | // 8 | // Re-tailored for Clutch 9 | 10 | #import "Device.h" 11 | #import 12 | #import 13 | #import "NSData+Reading.h" 14 | 15 | @import MachO.loader; 16 | 17 | @implementation Device 18 | 19 | + (cpu_type_t)cpu_type 20 | { 21 | const struct mach_header *header = _dyld_get_image_header(0); 22 | cpu_type_t local_cpu_type = header->cputype; 23 | 24 | return local_cpu_type; 25 | } 26 | 27 | + (cpu_subtype_t)cpu_subtype 28 | { 29 | const struct mach_header *header = _dyld_get_image_header(0); 30 | cpu_subtype_t local_cpu_subtype = header->cpusubtype; 31 | 32 | return local_cpu_subtype; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Clutch/Dumper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Dumper.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 22.03.15. 6 | // 7 | // 8 | 9 | #import "BinaryDumpProtocol.h" 10 | #import "Binary.h" 11 | #import "ASLRDisabler.h" 12 | #import "mach_vm.h" 13 | #import "ClutchBundle.h" 14 | #import "ClutchPrint.h" 15 | 16 | void *safe_trim(void *p, size_t n); 17 | 18 | @interface Dumper : NSObject 19 | { 20 | Binary *_originalBinary; 21 | thin_header _thinHeader; 22 | } 23 | void exit_with_errno (int err, const char *prefix); 24 | void _kill(pid_t pid); 25 | 26 | @property (readonly) BOOL isASLRProtected; 27 | @property NSFileHandle *originalFileHandle; 28 | @property BOOL shouldDisableASLR; 29 | 30 | 31 | + (NSString *)readableArchFromHeader:(thin_header)macho; 32 | + (NSString *)readableArchFromMachHeader:(struct mach_header)header; 33 | - (pid_t)posix_spawn:(NSString *)binaryPath disableASLR:(BOOL)yrn; 34 | - (pid_t)posix_spawn:(NSString *)binaryPath disableASLR:(BOOL)yrn suspend:(BOOL) suspend; 35 | - (instancetype)initWithHeader:(thin_header)macho originalBinary:(Binary *)binary; 36 | - (ArchCompatibility)compatibilityMode; 37 | 38 | - (void)swapArch; 39 | 40 | - (BOOL)_dumpToFileHandle:(NSFileHandle *)fileHandle withDumpSize:(uint32_t)togo pages:(uint32_t)pages fromPort:(mach_port_t)port pid:(pid_t)pid aslrSlide:(mach_vm_address_t)__text_start codeSignature_hashOffset:(uint32_t)hashOffset codesign_begin:(uint32_t)begin; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Clutch/Extension.h: -------------------------------------------------------------------------------- 1 | // 2 | // Extension.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 10.02.15. 6 | // 7 | // 8 | 9 | #import 10 | #import "ClutchBundle.h" 11 | 12 | @class Application; 13 | 14 | NS_CLASS_AVAILABLE_IOS(8_0) 15 | @interface Extension : ClutchBundle 16 | 17 | @property (readonly) BOOL isWatchKitExtension; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Clutch/Extension.m: -------------------------------------------------------------------------------- 1 | // 2 | // Extension.m 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 10.02.15. 6 | // 7 | // 8 | 9 | #import "Extension.h" 10 | 11 | @implementation Extension 12 | 13 | - (instancetype)initWithBundleInfo:(NSDictionary *)info 14 | { 15 | if (self = [super initWithBundleInfo:info]) { 16 | 17 | 18 | } 19 | return self; 20 | } 21 | 22 | - (BOOL)isWatchKitExtension { 23 | return [self.infoDictionary[@"NSExtension"][@"NSExtensionPointIdentifier"]isEqualToString:@"com.apple.watchkit"]; 24 | } 25 | 26 | - (NSString *)zipFilename 27 | { 28 | return self.parentBundle.zipFilename; 29 | } 30 | 31 | - (NSString *)zipPrefix 32 | { 33 | return [@"Payload" stringByAppendingPathComponent:[self.bundleContainerURL.path stringByReplacingOccurrencesOfString:self.parentBundle.bundleContainerURL.path withString:@""]]; 34 | } 35 | 36 | - (NSURL *)enumURL 37 | { 38 | return self.bundleURL; 39 | } 40 | 41 | - (NSString *)workingPath 42 | { 43 | return self.parentBundle.workingPath; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Clutch/FBApplicationInfo.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface FBBundleInfo : NSObject 4 | { 5 | id _proxy; 6 | NSString *_displayName; 7 | NSString *_bundleIdentifier; 8 | NSString *_bundleVersion; 9 | NSString *_bundleType; 10 | NSURL *_bundleURL; 11 | } 12 | 13 | @property(retain, nonatomic) NSURL *bundleURL; // @synthesize bundleURL=_bundleURL; 14 | @property(copy, nonatomic) NSString *bundleType; // @synthesize bundleType=_bundleType; 15 | @property(copy, nonatomic) NSString *bundleVersion; // @synthesize bundleVersion=_bundleVersion; 16 | @property(copy, nonatomic) NSString *bundleIdentifier; // @synthesize bundleIdentifier=_bundleIdentifier; 17 | @property(copy, nonatomic) NSString *displayName; // @synthesize displayName=_displayName; 18 | @property(readonly, retain, nonatomic, getter=_proxy) id proxy; // @synthesize proxy=_proxy; 19 | 20 | - (id)initWithApplicationProxy:(id)arg1; 21 | 22 | @end 23 | 24 | @interface FBApplicationInfo : FBBundleInfo 25 | { 26 | NSURL *_executableURL; 27 | NSURL *_bundleContainerURL; 28 | NSURL *_dataContainerURL; 29 | NSURL *_sandboxURL; 30 | double _lastModifiedDate; 31 | NSString *_preferenceDomain; 32 | NSString *_signerIdentity; 33 | NSDictionary *_environmentVariables; 34 | NSDictionary *_entitlements; 35 | _Bool _provisioningProfileValidated; 36 | NSString *_sdkVersion; 37 | NSArray *_customMachServices; 38 | unsigned long long _type; 39 | NSArray *_requiredCapabilities; 40 | NSArray *_tags; 41 | NSArray *_deviceFamilies; 42 | _Bool _enabled; 43 | _Bool _newsstand; 44 | _Bool _restricted; 45 | _Bool _beta; 46 | NSSet *_backgroundModes; 47 | NSSet *_supportedInterfaceOrientations; 48 | _Bool _exitsOnSuspend; 49 | _Bool _requiresPersistentWiFi; 50 | float _minimumBrightnessLevel; 51 | NSArray *_externalAccessoryProtocols; 52 | long long _ratingRank; 53 | NSArray *_folderNames; 54 | NSString *_fallbackFolderName; 55 | _Bool _installing; 56 | _Bool _uninstalling; 57 | NSObject *_workQueue; 58 | } 59 | 60 | @property(nonatomic, getter=_isUninstalling, setter=_setUninstalling:) _Bool uninstalling; // @synthesize uninstalling=_uninstalling; 61 | @property(nonatomic, getter=_isInstalling, setter=_setInstalling:) _Bool installing; // @synthesize installing=_installing; 62 | @property(readonly, nonatomic) long long ratingRank; // @synthesize ratingRank=_ratingRank; 63 | @property(readonly, retain, nonatomic) NSArray *externalAccessoryProtocols; // @synthesize externalAccessoryProtocols=_externalAccessoryProtocols; 64 | @property(readonly, nonatomic) float minimumBrightnessLevel; // @synthesize minimumBrightnessLevel=_minimumBrightnessLevel; 65 | @property(readonly, nonatomic) _Bool requiresPersistentWiFi; // @synthesize requiresPersistentWiFi=_requiresPersistentWiFi; 66 | @property(readonly, nonatomic, getter=isExitsOnSuspend) _Bool exitsOnSuspend; // @synthesize exitsOnSuspend=_exitsOnSuspend; 67 | @property(readonly, nonatomic, getter=isBeta) _Bool beta; // @synthesize beta=_beta; 68 | @property(readonly, nonatomic, getter=isRestricted) _Bool restricted; // @synthesize restricted=_restricted; 69 | @property(readonly, nonatomic, getter=isNewsstand) _Bool newsstand; // @synthesize newsstand=_newsstand; 70 | @property(readonly, nonatomic, getter=isEnabled) _Bool enabled; // @synthesize enabled=_enabled; 71 | @property(readonly, retain, nonatomic) NSArray *tags; // @synthesize tags=_tags; 72 | @property(readonly, retain, nonatomic) NSArray *deviceFamilies; // @synthesize deviceFamilies=_deviceFamilies; 73 | @property(readonly, retain, nonatomic) NSArray *requiredCapabilities; // @synthesize requiredCapabilities=_requiredCapabilities; 74 | @property(readonly, nonatomic) unsigned long long type; // @synthesize type=_type; 75 | @property(readonly, retain, nonatomic) NSArray *customMachServices; // @synthesize customMachServices=_customMachServices; 76 | @property(readonly, copy, nonatomic) NSString *sdkVersion; // @synthesize sdkVersion=_sdkVersion; 77 | @property(readonly, nonatomic, getter=isProvisioningProfileValidated) _Bool provisioningProfileValidated; // @synthesize provisioningProfileValidated=_provisioningProfileValidated; 78 | @property(readonly, retain, nonatomic) NSDictionary *entitlements; // @synthesize entitlements=_entitlements; 79 | @property(readonly, retain, nonatomic) NSDictionary *environmentVariables; // @synthesize environmentVariables=_environmentVariables; 80 | @property(readonly, copy, nonatomic) NSString *signerIdentity; // @synthesize signerIdentity=_signerIdentity; 81 | @property(readonly, copy, nonatomic) NSString *preferenceDomain; // @synthesize preferenceDomain=_preferenceDomain; 82 | @property(readonly, nonatomic) double lastModifiedDate; // @synthesize lastModifiedDate=_lastModifiedDate; 83 | @property(readonly, retain, nonatomic) NSURL *sandboxURL; // @synthesize sandboxURL=_sandboxURL; 84 | @property(readonly, retain, nonatomic) NSURL *dataContainerURL; // @synthesize dataContainerURL=_dataContainerURL; 85 | @property(readonly, retain, nonatomic) NSURL *bundleContainerURL; // @synthesize bundleContainerURL=_bundleContainerURL; 86 | @property(readonly, retain, nonatomic) NSURL *executableURL; // @synthesize executableURL=_executableURL; 87 | - (id)_localizedGenreFromDictionary:(id)arg1; 88 | - (id)_localizedGenreNameForID:(long long)arg1; 89 | - (void)_cacheFolderNamesForSystemApp:(id)arg1; 90 | - (id)_configureEnvironment:(id)arg1; 91 | - (long long)_computeRatingRank; 92 | - (id)_copyiTunesMetadata; 93 | - (void)_buildDefaultsFromInfoPlist:(id)arg1; 94 | - (id)_computeSupportedInterfaceOrientations:(id)arg1; 95 | - (void)_acceptApplicationSignatureIdentity; 96 | - (id)_preferenceDomain; 97 | - (double)_lastModifiedDateForPath:(id)arg1; 98 | - (unsigned long long)_applicationType:(id)arg1; 99 | - (id)description; 100 | - (_Bool)builtOnOrAfterSDKVersion:(id)arg1; 101 | - (void)acceptApplicationSignatureIdentity; 102 | - (_Bool)supportsInterfaceOrientation:(long long)arg1; 103 | - (_Bool)supportsBackgroundMode:(id)arg1; 104 | @property(readonly, retain, nonatomic) NSString *fallbackFolderName; // @synthesize fallbackFolderName=_fallbackFolderName; 105 | @property(readonly, retain, nonatomic) NSArray *folderNames; // @synthesize folderNames=_folderNames; 106 | @property(readonly, nonatomic) long long signatureState; // @dynamic signatureState; 107 | - (void)dealloc; 108 | - (id)initWithApplicationProxy:(id)arg1; 109 | 110 | @end -------------------------------------------------------------------------------- /Clutch/FinalizeDumpOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // FinalizeDumpOperation.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 12.02.15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @class Application; 12 | 13 | @interface FinalizeDumpOperation : NSOperation 14 | 15 | @property (assign) BOOL onlyBinaries; 16 | @property (assign) NSInteger expectedBinariesCount; 17 | 18 | - (instancetype)initWithApplication:(Application *)application; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Clutch/FinalizeDumpOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // FinalizeDumpOperation.m 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 12.02.15. 6 | // 7 | // 8 | 9 | #import "FinalizeDumpOperation.h" 10 | #import "ZipArchive.h" 11 | #import "Application.h" 12 | #import "ZipOperation.h" 13 | #import 14 | #import "ClutchPrint.h" 15 | 16 | int diff_ms(struct timeval, struct timeval); 17 | extern struct timeval gStart; 18 | 19 | @interface FinalizeDumpOperation () 20 | { 21 | Application *_application; 22 | BOOL _executing, _finished; 23 | ZipArchive *_archive; 24 | } 25 | @end 26 | 27 | 28 | @implementation FinalizeDumpOperation 29 | 30 | - (instancetype)initWithApplication:(Application *)application { 31 | self = [super init]; 32 | if (self) { 33 | _executing = NO; 34 | _finished = NO; 35 | _application = application; 36 | } 37 | return self; 38 | } 39 | 40 | - (BOOL)isAsynchronous { 41 | return YES; 42 | } 43 | 44 | - (BOOL)isConcurrent { 45 | return YES; 46 | } 47 | 48 | - (BOOL)isExecuting { 49 | return _executing; 50 | } 51 | 52 | - (BOOL)isFinished { 53 | return _finished; 54 | } 55 | 56 | - (void)start { 57 | // Always check for cancellation before launching the task. 58 | if ([self isCancelled]) 59 | { 60 | // Must move the operation to the finished state if it is canceled. 61 | [self willChangeValueForKey:@"isFinished"]; 62 | _finished = YES; 63 | [self didChangeValueForKey:@"isFinished"]; 64 | return; 65 | } 66 | 67 | NSString __weak *bundleIdentifier = _application.bundleIdentifier; 68 | self.completionBlock = ^{ 69 | struct timeval end; 70 | gettimeofday(&end, NULL); 71 | int dif = diff_ms(end, gStart); 72 | float sec = ((dif + 500.0f) / 1000.0f); 73 | [[ClutchPrint sharedInstance] printColor:ClutchPrinterColorPink format:@"Finished dumping %@ in %0.1f seconds", bundleIdentifier, sec]; 74 | }; 75 | 76 | // If the operation is not canceled, begin executing the task. 77 | [self willChangeValueForKey:@"isExecuting"]; 78 | [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil]; 79 | _executing = YES; 80 | [self didChangeValueForKey:@"isExecuting"]; 81 | } 82 | 83 | - (void)main { 84 | @try { 85 | 86 | if (_onlyBinaries) { 87 | 88 | NSDirectoryEnumerator *dirEnumerator = [NSFileManager.defaultManager enumeratorAtURL:[NSURL fileURLWithPath:_application.workingPath] includingPropertiesForKeys:@[NSURLNameKey,NSURLIsDirectoryKey] options:0 errorHandler:^BOOL(NSURL *url, NSError *error) { 89 | return YES; 90 | }]; 91 | 92 | NSMutableArray *plists = [NSMutableArray new]; 93 | 94 | for (NSURL *theURL in dirEnumerator) 95 | { 96 | NSNumber *isDirectory; 97 | [theURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil]; 98 | 99 | if ([theURL.lastPathComponent isEqualToString:@"filesToAdd.plist"]) { 100 | 101 | NSDictionary *dict = [NSDictionary dictionaryWithContentsOfURL:theURL]; 102 | 103 | if (dict) 104 | [plists addObject:theURL.path]; 105 | 106 | [[NSFileManager defaultManager] removeItemAtURL:theURL error:nil]; 107 | } 108 | } 109 | 110 | __block BOOL status = plists.count == self.expectedBinariesCount; 111 | 112 | if (status) { 113 | [[ClutchPrint sharedInstance] printColor:ClutchPrinterColorPink format:@"Finished dumping %@ to %@", _application.bundleIdentifier, _application.workingPath]; 114 | } 115 | else { 116 | [[ClutchPrint sharedInstance] printError:@"Failed to dump %@ :(", _application.bundleIdentifier]; 117 | return; 118 | } 119 | 120 | [self completeOperation]; 121 | 122 | return; 123 | } 124 | 125 | NSString *_zipFilename = _application.zipFilename; 126 | 127 | if (_application.parentBundle) { 128 | [[ClutchPrint sharedInstance] printDeveloper:@"Zipping %@",_application.bundleURL.lastPathComponent]; 129 | } 130 | 131 | if (_archive == nil) { 132 | _archive = [[ZipArchive alloc] init]; 133 | [_archive CreateZipFile2:[_application.workingPath stringByAppendingPathComponent:_zipFilename] append:YES]; 134 | } 135 | 136 | NSDirectoryEnumerator *dirEnumerator = [NSFileManager.defaultManager enumeratorAtURL:[NSURL fileURLWithPath:_application.workingPath] includingPropertiesForKeys:@[NSURLNameKey,NSURLIsDirectoryKey] options:0 errorHandler:^BOOL(NSURL *url, NSError *error) { 137 | return YES; 138 | }]; 139 | 140 | NSMutableArray *plists = [NSMutableArray new]; 141 | 142 | for (NSURL *theURL in dirEnumerator) 143 | { 144 | NSNumber *isDirectory; 145 | [theURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil]; 146 | 147 | if ([theURL.lastPathComponent isEqualToString:@"filesToAdd.plist"]) { 148 | 149 | NSDictionary *dict = [NSDictionary dictionaryWithContentsOfURL:theURL]; 150 | 151 | if (dict) 152 | { 153 | for (NSString *key in dict.allKeys) { 154 | NSString *zipPath = dict[key]; 155 | [_archive addFileToZip:key newname:zipPath]; 156 | #if PRINT_ZIP_LOGS 157 | [[ClutchPrint sharedInstance] print:@"Added %@",zipPath]; 158 | #endif 159 | } 160 | 161 | [plists addObject:theURL.path]; 162 | } 163 | } 164 | } 165 | 166 | [_archive CloseZipFile2]; 167 | 168 | // cleanup 169 | 170 | #ifndef DEBUG 171 | for (NSString *path in plists) 172 | [[NSFileManager defaultManager]removeItemAtPath:path.stringByDeletingLastPathComponent error:nil]; 173 | #endif 174 | 175 | __block BOOL status = plists.count == self.expectedBinariesCount; 176 | 177 | NSString *_ipaPath = [@"/private/var/mobile/Documents/Dumped" stringByAppendingPathComponent:_zipFilename]; 178 | 179 | if (!status) { 180 | // remove .ipa if failed 181 | [[NSFileManager defaultManager]removeItemAtPath:[_application.workingPath stringByAppendingPathComponent:_zipFilename] error:nil]; 182 | }else { 183 | [[NSFileManager defaultManager] createDirectoryAtPath:@"/private/var/mobile/Documents/Dumped" withIntermediateDirectories:YES attributes:nil error:nil]; 184 | 185 | NSURL *ipaSrcURL = [NSURL fileURLWithPath:[_application.workingPath stringByAppendingPathComponent:_zipFilename]]; 186 | NSError *anError; 187 | if ([[NSFileManager defaultManager] fileExistsAtPath:_ipaPath]) { 188 | for (int i = 2; i < 999; ++i) { 189 | NSFileManager *fileMgr = [NSFileManager defaultManager]; 190 | NSString *newName = 191 | [_ipaPath.lastPathComponent.stringByDeletingPathExtension 192 | stringByAppendingFormat:@"-%i.%@", i, _ipaPath.pathExtension]; 193 | NSString *currentFile = [_ipaPath.stringByDeletingLastPathComponent 194 | stringByAppendingPathComponent:newName]; 195 | BOOL fileExists = [fileMgr fileExistsAtPath:currentFile]; 196 | if (!fileExists) { 197 | _ipaPath = currentFile; 198 | if (![[NSFileManager defaultManager] 199 | moveItemAtURL:ipaSrcURL 200 | toURL:[NSURL fileURLWithPath:currentFile] 201 | error:&anError]) { 202 | [[ClutchPrint sharedInstance] printDeveloper:@"Failed to move from %@ to %@ with error %@", ipaSrcURL, 203 | [NSURL fileURLWithPath:currentFile], anError]; 204 | } 205 | break; 206 | } 207 | } 208 | } else { 209 | if (![[NSFileManager defaultManager] 210 | moveItemAtURL:ipaSrcURL 211 | toURL:[NSURL fileURLWithPath:_ipaPath] 212 | error:&anError]) { 213 | [[ClutchPrint sharedInstance] printDeveloper:@"Failed to move from %@ to %@ with error %@", ipaSrcURL, 214 | [NSURL fileURLWithPath:_ipaPath], anError]; 215 | } 216 | } 217 | } 218 | 219 | [[ClutchPrint sharedInstance] print:@"%@: %@",status?@"DONE":@"FAILED",status?_ipaPath:_application]; 220 | 221 | // Do the main work of the operation here. 222 | [self completeOperation]; 223 | } 224 | @catch(...) { 225 | // Do not rethrow exceptions. 226 | } 227 | } 228 | 229 | - (void)completeOperation { 230 | [self willChangeValueForKey:@"isFinished"]; 231 | [self willChangeValueForKey:@"isExecuting"]; 232 | _executing = NO; 233 | _finished = YES; 234 | [self didChangeValueForKey:@"isExecuting"]; 235 | [self didChangeValueForKey:@"isFinished"]; 236 | } 237 | 238 | -(NSUInteger)hash { 239 | return 4201234; 240 | } 241 | 242 | @end 243 | -------------------------------------------------------------------------------- /Clutch/Framework.h: -------------------------------------------------------------------------------- 1 | // 2 | // Framework.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 10.02.15. 6 | // 7 | // 8 | 9 | #import 10 | #import "ClutchBundle.h" 11 | 12 | @class Application; 13 | 14 | NS_CLASS_AVAILABLE_IOS(8_0) 15 | @interface Framework : ClutchBundle 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Clutch/Framework.m: -------------------------------------------------------------------------------- 1 | // 2 | // Framework.m 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 10.02.15. 6 | // 7 | // 8 | 9 | #import "Framework.h" 10 | #import "Device.h" 11 | 12 | @implementation Framework 13 | 14 | - (instancetype)initWithBundleInfo:(NSDictionary *)info 15 | { 16 | if (self = [super initWithBundleInfo:info]) { 17 | 18 | 19 | } 20 | return self; 21 | } 22 | 23 | - (void)prepareForDump 24 | { 25 | [super prepareForDump]; 26 | } 27 | 28 | - (NSString *)zipFilename 29 | { 30 | return self.parentBundle.zipFilename; 31 | } 32 | 33 | - (NSString *)zipPrefix 34 | { 35 | return [@"Payload" stringByAppendingPathComponent:[self.bundleContainerURL.path stringByReplacingOccurrencesOfString:self.parentBundle.bundleContainerURL.path withString:@""]]; 36 | } 37 | 38 | - (NSURL *)enumURL 39 | { 40 | return self.bundleURL; 41 | } 42 | 43 | - (NSString *)workingPath 44 | { 45 | return self.parentBundle.workingPath; 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /Clutch/Framework64Dumper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Framework64Dumper.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 02.04.15. 6 | // 7 | // 8 | 9 | #import "Dumper.h" 10 | 11 | @interface Framework64Dumper : Dumper 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Clutch/FrameworkDumper.h: -------------------------------------------------------------------------------- 1 | // 2 | // FrameworkDumper.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 02.04.15. 6 | // 7 | // 8 | 9 | #import "Dumper.h" 10 | 11 | @interface FrameworkDumper : Dumper 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Clutch/FrameworkDumper.m: -------------------------------------------------------------------------------- 1 | // 2 | // FrameworkDumper.m 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 02.04.15. 6 | // 7 | // 8 | 9 | #import "FrameworkDumper.h" 10 | #import "Device.h" 11 | #import 12 | #import "ClutchPrint.h" 13 | 14 | @implementation FrameworkDumper 15 | 16 | - (cpu_type_t)supportedCPUType 17 | { 18 | return CPU_TYPE_ARM; 19 | } 20 | 21 | - (BOOL)dumpBinary 22 | { 23 | 24 | ClutchBundle *bundle = [_originalBinary valueForKey:@"_bundle"]; 25 | 26 | NSString *binaryDumpPath = [_originalBinary.workingPath stringByAppendingPathComponent:_originalBinary.binaryPath.lastPathComponent]; 27 | 28 | 29 | NSString* swappedBinaryPath = _originalBinary.binaryPath, *newSinf = _originalBinary.sinfPath, *newSupp = _originalBinary.suppPath; // default values if we dont need to swap archs 30 | 31 | //check if cpusubtype matches 32 | if ((_thinHeader.header.cpusubtype != [Device cpu_subtype]) && (_originalBinary.hasMultipleARMSlices || (_originalBinary.hasARM64Slice && ([Device cpu_type]==CPU_TYPE_ARM64)))) { 33 | 34 | NSString* suffix = [NSString stringWithFormat:@"_%@", [Dumper readableArchFromHeader:_thinHeader]]; 35 | 36 | swappedBinaryPath = [_originalBinary.binaryPath stringByAppendingString:suffix]; 37 | newSinf = [_originalBinary.sinfPath.stringByDeletingPathExtension stringByAppendingString:[suffix stringByAppendingPathExtension:_originalBinary.sinfPath.pathExtension]]; 38 | newSupp = [_originalBinary.suppPath.stringByDeletingPathExtension stringByAppendingString:[suffix stringByAppendingPathExtension:_originalBinary.suppPath.pathExtension]]; 39 | 40 | 41 | [self swapArch]; 42 | 43 | } 44 | 45 | NSFileHandle *newFileHandle = [[NSFileHandle alloc]initWithFileDescriptor:fileno(fopen(binaryDumpPath.UTF8String, "r+"))]; 46 | 47 | [newFileHandle seekToFileOffset:_thinHeader.offset + _thinHeader.size]; 48 | 49 | struct linkedit_data_command ldid; // LC_CODE_SIGNATURE load header (for resign) 50 | struct encryption_info_command crypt; // LC_ENCRYPTION_INFO load header (for crypt*) 51 | struct segment_command __text; // __TEXT segment 52 | 53 | struct super_blob *codesignblob; // codesign blob pointer 54 | struct code_directory directory; // codesign directory index 55 | 56 | BOOL foundCrypt = NO, foundSignature = NO, foundStartText = NO; 57 | directory.nCodeSlots = directory.hashOffset = 0; 58 | 59 | [[ClutchPrint sharedInstance] printDeveloper: @"32bit dumping: arch %@ offset %u", [Dumper readableArchFromHeader:_thinHeader], _thinHeader.offset]; 60 | uint32_t cryptlc_offset = 0; 61 | 62 | for (int i = 0; i < _thinHeader.header.ncmds; i++) { 63 | 64 | uint32_t cmd = [newFileHandle intAtOffset:newFileHandle.offsetInFile]; 65 | uint32_t size = [newFileHandle intAtOffset:newFileHandle.offsetInFile+sizeof(uint32_t)]; 66 | 67 | switch (cmd) { 68 | case LC_CODE_SIGNATURE: { 69 | [newFileHandle getBytes:&ldid inRange:NSMakeRange((NSUInteger)(newFileHandle.offsetInFile),sizeof(struct linkedit_data_command))]; 70 | foundSignature = YES; 71 | 72 | [[ClutchPrint sharedInstance] printDeveloper: @"FOUND CODE SIGNATURE: dataoff %u | datasize %u",ldid.dataoff,ldid.datasize]; 73 | 74 | break; 75 | } 76 | case LC_ENCRYPTION_INFO: { 77 | cryptlc_offset = (uint32_t)(newFileHandle.offsetInFile); 78 | [newFileHandle getBytes:&crypt inRange:NSMakeRange((NSUInteger)(newFileHandle.offsetInFile),sizeof(struct encryption_info_command))]; 79 | foundCrypt = YES; 80 | 81 | [[ClutchPrint sharedInstance] printDeveloper: @"FOUND ENCRYPTION INFO: cryptoff %u | cryptsize %u | cryptid %u",crypt.cryptoff,crypt.cryptsize,crypt.cryptid]; 82 | 83 | break; 84 | } 85 | case LC_SEGMENT: 86 | { 87 | [newFileHandle getBytes:&__text inRange:NSMakeRange((NSUInteger)(newFileHandle.offsetInFile),sizeof(struct segment_command))]; 88 | 89 | if (strncmp(__text.segname, "__TEXT", 6) == 0) { 90 | foundStartText = YES; 91 | [[ClutchPrint sharedInstance] printDeveloper: @"FOUND %s SEGMENT",__text.segname]; 92 | } 93 | break; 94 | } 95 | } 96 | 97 | [newFileHandle seekToFileOffset:newFileHandle.offsetInFile + size]; 98 | 99 | if (foundCrypt && foundSignature && foundStartText) 100 | break; 101 | } 102 | 103 | // we need to have all of these 104 | if (!foundCrypt || !foundSignature || !foundStartText) { 105 | [[ClutchPrint sharedInstance] printDeveloper: @"dumping binary: some load commands were not found %@ %@ %@",foundCrypt?@"YES":@"NO",foundSignature?@"YES":@"NO",foundStartText?@"YES":@"NO"]; 106 | return NO; 107 | } 108 | 109 | [[ClutchPrint sharedInstance] printDeveloper: @"starting to ldid"]; 110 | 111 | NSUInteger begin = 0; 112 | 113 | //seek to ldid offset 114 | 115 | codesignblob = malloc(ldid.datasize); 116 | 117 | [newFileHandle seekToFileOffset:_thinHeader.offset + ldid.dataoff]; 118 | [newFileHandle getBytes:codesignblob inRange:NSMakeRange((NSUInteger)(newFileHandle.offsetInFile), ldid.datasize)]; 119 | 120 | [[ClutchPrint sharedInstance] printDeveloper: @"hello it's me"]; 121 | 122 | uint32_t countBlobs = CFSwapInt32(codesignblob->count); // how many indexes? 123 | 124 | 125 | 126 | for (uint32_t index = 0; index < countBlobs; index++) { // is this the code directory? 127 | if (CFSwapInt32(codesignblob->index[index].type) == CSSLOT_CODEDIRECTORY) { 128 | // we'll find the hash metadata in here 129 | [[ClutchPrint sharedInstance] printDeveloper: @"%u %u %u", _thinHeader.offset, ldid.dataoff, codesignblob->index[index].offset]; 130 | begin = _thinHeader.offset + ldid.dataoff + CFSwapInt32(codesignblob->index[index].offset); // store the top of the codesign directory blob 131 | [newFileHandle getBytes:&directory inRange:NSMakeRange(begin, sizeof(struct code_directory))]; //read the blob from its beginning 132 | [[ClutchPrint sharedInstance] printDeveloper: @"Found CSSLOT_CODEDIRECTORY"]; 133 | break; //break (we don't need anything from this the superblob anymore) 134 | } 135 | } 136 | free(codesignblob); 137 | 138 | uint32_t pages = CFSwapInt32(directory.nCodeSlots); // get the amount of codeslots 139 | 140 | if (pages == 0) { 141 | [[ClutchPrint sharedInstance] printColor:ClutchPrinterColorPurple format:@"pages == 0"]; 142 | return NO; 143 | } 144 | 145 | [newFileHandle closeFile]; 146 | 147 | 148 | [[ClutchPrint sharedInstance] printDeveloper: @"hello from the other side"]; 149 | 150 | extern char **environ; 151 | posix_spawnattr_t attr; 152 | 153 | pid_t pid; 154 | 155 | 156 | 157 | NSUUID* workingUUID = [NSUUID new]; 158 | NSString* workingPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[@"clutch" stringByAppendingPathComponent:workingUUID.UUIDString]]; 159 | 160 | while ([[NSFileManager defaultManager] fileExistsAtPath:workingPath]) { 161 | workingUUID = [NSUUID new]; 162 | workingPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[@"clutch" stringByAppendingPathComponent:workingUUID.UUIDString]]; 163 | } 164 | 165 | [[NSFileManager defaultManager] createDirectoryAtPath:workingPath withIntermediateDirectories:YES attributes:nil error:nil]; 166 | 167 | 168 | 169 | if (![[NSFileManager defaultManager] copyItemAtPath:[NSProcessInfo processInfo].arguments[0] toPath:[workingPath stringByAppendingPathComponent:@"clutch"] error:nil]) { 170 | [[ClutchPrint sharedInstance] printError:@"Failed to copy clutch to %@", workingPath]; 171 | return NO; 172 | } 173 | 174 | if (_originalBinary.frameworksPath == nil) { 175 | [[ClutchPrint sharedInstance] printError:@"Could not find Frameworks path to create symbolic link to"]; 176 | return NO; 177 | } 178 | 179 | [[NSFileManager defaultManager] createSymbolicLinkAtPath:[workingPath stringByAppendingPathComponent:@"Frameworks"] withDestinationPath:_originalBinary.frameworksPath error:nil]; 180 | 181 | const char *argv[] = {[[workingPath stringByAppendingPathComponent:@"clutch"] UTF8String], 182 | "-f", 183 | swappedBinaryPath.UTF8String, 184 | binaryDumpPath.UTF8String, 185 | [NSString stringWithFormat:@"%u",pages].UTF8String, 186 | [NSString stringWithFormat:@"%u",_thinHeader.header.ncmds].UTF8String, 187 | [NSString stringWithFormat:@"%u",_thinHeader.offset].UTF8String, 188 | bundle.parentBundle.bundleIdentifier.UTF8String, 189 | [NSString stringWithFormat:@"%u",CFSwapInt32(directory.hashOffset)].UTF8String, 190 | [NSString stringWithFormat:@"%u",(unsigned int)begin].UTF8String, 191 | [NSString stringWithFormat:@"%u", crypt.cryptoff].UTF8String, 192 | [NSString stringWithFormat:@"%u", crypt.cryptsize].UTF8String, 193 | [NSString stringWithFormat:@"%u", cryptlc_offset].UTF8String, 194 | NULL}; 195 | 196 | [[ClutchPrint sharedInstance] printDeveloper: @"i must have called a thousand times!"]; 197 | 198 | [[ClutchPrint sharedInstance] printDeveloper: @"hello potato posix_spawn %@", [[NSString alloc] initWithUTF8String:argv[0]]]; 199 | 200 | 201 | posix_spawnattr_init (&attr); 202 | 203 | size_t ocount = 0; 204 | 205 | cpu_type_t cpu_type = CPU_TYPE_ARM; 206 | 207 | 208 | posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount); 209 | 210 | 211 | short flags = POSIX_SPAWN_START_SUSPENDED; 212 | // Set the flags we just made into our posix spawn attributes 213 | exit_with_errno (posix_spawnattr_setflags (&attr, flags), "::posix_spawnattr_setflags (&attr, flags) error: "); 214 | 215 | int dumpResult = posix_spawnp(&pid, argv[0], NULL, &attr, (char* const*)argv, environ); 216 | __block NSUInteger finalDumpResult = 9999; //it shouldn't be 9999 217 | 218 | if (dumpResult == 0) { 219 | [[ClutchPrint sharedInstance] printDeveloper: @"Child pid: %i", pid]; 220 | 221 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); 222 | 223 | dispatch_sync(queue, ^{ 224 | kill(pid, SIGCONT); 225 | int dumpResult = 0; 226 | if (waitpid(pid, &dumpResult, 0) != -1) { 227 | [[ClutchPrint sharedInstance] printDeveloper: @"Child exited with status %u", dumpResult]; 228 | finalDumpResult = dumpResult; 229 | } else { 230 | perror("waitpid"); 231 | } 232 | }); 233 | 234 | } else { 235 | [[ClutchPrint sharedInstance] printDeveloper: @"posix_spawn: %s", strerror(dumpResult)]; 236 | } 237 | 238 | 239 | if (![swappedBinaryPath isEqualToString:_originalBinary.binaryPath]) 240 | [[NSFileManager defaultManager]removeItemAtPath:swappedBinaryPath error:nil]; 241 | if (![newSinf isEqualToString:_originalBinary.sinfPath]) 242 | [[NSFileManager defaultManager]removeItemAtPath:newSinf error:nil]; 243 | if (![newSupp isEqualToString:_originalBinary.suppPath]) 244 | [[NSFileManager defaultManager]removeItemAtPath:newSupp error:nil]; 245 | 246 | [[NSFileManager defaultManager] removeItemAtPath:workingPath error:nil]; 247 | 248 | 249 | 250 | if (finalDumpResult == 0) 251 | return YES; 252 | 253 | return NO; 254 | } 255 | 256 | @end 257 | -------------------------------------------------------------------------------- /Clutch/FrameworkLoader.h: -------------------------------------------------------------------------------- 1 | // 2 | // FrameworkLoader.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 06.04.15. 6 | // 7 | // 8 | 9 | #import "Dumper.h" 10 | 11 | @interface FrameworkLoader : Dumper 12 | 13 | @property (assign) uint32_t ncmds; 14 | @property (assign) uint32_t offset; 15 | @property (assign) uint32_t pages; 16 | @property (assign) uint32_t dumpSize; 17 | @property (assign) uint32_t hashOffset; 18 | @property (assign) uint32_t cryptoff; 19 | @property (assign) uint32_t cryptsize; 20 | @property (assign) uint32_t cryptlc_offset; 21 | @property (assign) uint32_t codesign_begin; 22 | @property (assign) BOOL arm64; 23 | @property (nonatomic) NSString *binPath; 24 | @property (nonatomic) NSString *dumpPath; 25 | @property (nonatomic) NSString *bID; 26 | 27 | - (cpu_type_t)supportedCPUType; 28 | 29 | - (BOOL)dumpBinary; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Clutch/FrameworkLoader.m: -------------------------------------------------------------------------------- 1 | // 2 | // FrameworkLoader.m 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 06.04.15. 6 | // 7 | // 8 | 9 | 10 | #import "FrameworkLoader.h" 11 | #import "Device.h" 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | #import 19 | #import 20 | #import "NSBundle+Clutch.h" 21 | #import "ClutchPrint.h" 22 | 23 | @import ObjectiveC.runtime; 24 | 25 | @interface FrameworkLoader () 26 | { 27 | uint32_t _dyldImageIndex; 28 | } 29 | @end 30 | 31 | @implementation FrameworkLoader 32 | 33 | - (cpu_type_t)supportedCPUType 34 | { 35 | return CPU_TYPE_ARM | CPU_TYPE_ARM64; 36 | } 37 | 38 | - (BOOL)dumpBinary { 39 | 40 | 41 | 42 | NSString *binaryDumpPath = self.dumpPath; 43 | 44 | NSString* swappedBinaryPath = self.binPath; // default values if we dont need to swap archs 45 | 46 | NSDictionary *_infoPlist = [NSDictionary dictionaryWithContentsOfFile:[self.binPath.stringByDeletingLastPathComponent stringByAppendingPathComponent:@"Info.plist"]]; 47 | 48 | [NSBundle mainBundle].clutchBID = self.bID;//_infoPlist[@"CFBundleIdentifier"]; 49 | 50 | _originalBinary = (Binary*)[NSString stringWithFormat:@"<%@>", _infoPlist[@"CFBundleExecutable"]]; 51 | 52 | 53 | NSFileHandle *newFileHandle = [[NSFileHandle alloc]initWithFileDescriptor:fileno(fopen(binaryDumpPath.UTF8String, "r+"))]; 54 | 55 | [newFileHandle seekToFileOffset:self.offset]; 56 | 57 | void *handle = dlopen(swappedBinaryPath.UTF8String, RTLD_LAZY); 58 | 59 | if (!handle) { 60 | [[ClutchPrint sharedInstance] printError:@"Failed to dlopen %@ %s", swappedBinaryPath, dlerror()]; 61 | return NO; 62 | } 63 | 64 | uint32_t imageCount = _dyld_image_count(); 65 | uint32_t dyldIndex = -1; 66 | for (uint32_t idx = 0; idx < imageCount; idx++) { 67 | NSString *dyldPath = [NSString stringWithUTF8String:_dyld_get_image_name(idx)]; 68 | if ([swappedBinaryPath.lastPathComponent isEqualToString:dyldPath.lastPathComponent]) { 69 | dyldIndex = idx; 70 | break; 71 | } 72 | } 73 | 74 | if (dyldIndex == -1) { 75 | dlclose(handle); 76 | return NO; 77 | } 78 | 79 | _dyldImageIndex = dyldIndex; 80 | 81 | intptr_t dyldPointer = _dyld_get_image_vmaddr_slide(dyldIndex); 82 | 83 | [[ClutchPrint sharedInstance] printDeveloper: @"dyld offset %u", dyldPointer]; 84 | 85 | BOOL dumpResult; 86 | 87 | //[self _dumpToFileHandle:newFileHandle withEncryptionInfoCommand:self.encryptionInfoCommand pages:self.pages fromPort:mach_task_self() pid:[NSProcessInfo processInfo].processIdentifier aslrSlide:dyldPointer]; 88 | 89 | dumpResult = [self _dumpToFileHandle:newFileHandle withDumpSize:self.dumpSize pages:self.pages fromPort:mach_task_self() pid:[NSProcessInfo processInfo].processIdentifier aslrSlide:dyldPointer codeSignature_hashOffset:self.hashOffset codesign_begin:self.codesign_begin]; 90 | 91 | dlclose(handle); 92 | 93 | return dumpResult; 94 | } 95 | 96 | - (BOOL)_dumpToFileHandle:(NSFileHandle *)fileHandle withDumpSize:(uint32_t)togo pages:(uint32_t)pages fromPort:(mach_port_t)port pid:(pid_t)pid aslrSlide:(mach_vm_address_t)__text_start codeSignature_hashOffset:(uint32_t)hashOffset codesign_begin:(uint32_t)begin 97 | { 98 | 99 | [[ClutchPrint sharedInstance] printDeveloper: @"Using Framework Dumper, pages %u", pages]; 100 | void *checksum = malloc(pages * 20); // 160 bits for each hash (SHA1) 101 | 102 | const struct mach_header *image_header = _dyld_get_image_header(_dyldImageIndex); 103 | 104 | [[ClutchPrint sharedInstance] printColor:ClutchPrinterColorPurple format:@"Dumping %@ %@", _originalBinary, [Dumper readableArchFromMachHeader:*image_header]]; 105 | 106 | uint32_t pages_d = 0; 107 | 108 | uint8_t* buf = malloc(0x1000); 109 | 110 | [fileHandle seekToFileOffset:self.offset]; 111 | 112 | 113 | while (togo > 0) { 114 | memcpy(buf, (unsigned char*)image_header + (pages_d * 0x1000), 0x1000); 115 | [fileHandle writeData:[NSData dataWithBytes:buf length:0x1000]]; 116 | sha1(checksum + (20 * pages_d), buf, 0x1000); // perform checksum on the page 117 | togo -= 0x1000; // remove a page from the togo 118 | pages_d += 1; // increase the amount of completed pages 119 | } 120 | free(buf); 121 | 122 | //nice! now let's write the new checksum data 123 | [[ClutchPrint sharedInstance] printDeveloper:@"Writing new checksum"]; 124 | 125 | [fileHandle seekToFileOffset:(begin + hashOffset)]; 126 | 127 | NSData* trimmed_checksum = [[NSData dataWithBytes:checksum length:pages*20] subdataWithRange:NSMakeRange(0, 20*pages_d)]; 128 | free(checksum); 129 | [fileHandle writeData:trimmed_checksum]; 130 | 131 | [[ClutchPrint sharedInstance] printDeveloper: @"Done writing checksum"]; 132 | 133 | [[ClutchPrint sharedInstance] printDeveloper: @"Patching cryptid"]; 134 | 135 | NSData* data; 136 | 137 | if (image_header->cputype == CPU_TYPE_ARM64) { 138 | struct encryption_info_command_64 crypt; 139 | 140 | [fileHandle getBytes:&crypt atOffset:self.cryptlc_offset length:sizeof(struct encryption_info_command_64)]; 141 | 142 | [[ClutchPrint sharedInstance] printDeveloper:@"current cryptid %u", crypt.cryptid]; 143 | crypt.cryptid = 0; 144 | [fileHandle seekToFileOffset:self.cryptlc_offset]; 145 | 146 | data = [NSData dataWithBytes:&crypt length:sizeof(struct encryption_info_command_64)]; 147 | 148 | } 149 | else { 150 | struct encryption_info_command crypt; 151 | [fileHandle getBytes:&crypt atOffset:self.cryptlc_offset length:sizeof(struct encryption_info_command)]; 152 | [[ClutchPrint sharedInstance] printDeveloper:@"current cryptid %u", crypt.cryptid]; 153 | crypt.cryptid = 0; 154 | [fileHandle seekToFileOffset:self.cryptlc_offset]; 155 | data = [NSData dataWithBytes:&crypt length:sizeof(struct encryption_info_command)]; 156 | } 157 | 158 | [fileHandle writeData:data]; 159 | return YES; 160 | } 161 | 162 | 163 | @end 164 | -------------------------------------------------------------------------------- /Clutch/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 2.0.4 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIRequiredDeviceCapabilities 24 | 25 | armv7 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Clutch/LSApplicationWorkspace.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This header is generated by classdump-dyld 0.7 3 | * on Sunday, November 22, 2015 at 10:24:36 PM Eastern Standard Time 4 | * Operating System: Version 9.0.2 (Build 13A452) 5 | * Image Source: /System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices 6 | * classdump-dyld is licensed under GPLv3, Copyright © 2013 by Elias Limneos. 7 | */ 8 | 9 | 10 | @interface LSApplicationWorkspace : NSObject 11 | +(id)defaultWorkspace; 12 | -(void)_sf_openURL:(id)arg1 withOptions:(id)arg2 completionHandler:(/*^block*/id)arg3 ; 13 | -(id)operationToOpenResource:(id)arg1 usingApplication:(id)arg2 userInfo:(id)arg3 ; 14 | -(BOOL)establishConnection; 15 | -(id)remoteObserver; 16 | -(id)pluginsWithIdentifiers:(id)arg1 protocols:(id)arg2 version:(id)arg3 applyFilter:(/*^block*/id)arg4 ; 17 | -(id)operationToOpenResource:(id)arg1 usingApplication:(id)arg2 uniqueDocumentIdentifier:(id)arg3 userInfo:(id)arg4 ; 18 | -(BOOL)installApplication:(id)arg1 withOptions:(id)arg2 error:(id*)arg3 usingBlock:(/*^block*/id)arg4 ; 19 | -(id)installProgressForApplication:(id)arg1 withPhase:(unsigned long long)arg2 ; 20 | -(BOOL)registerApplicationDictionary:(id)arg1 withObserverNotification:(int)arg2 ; 21 | -(BOOL)installPhaseFinishedForProgress:(id)arg1 ; 22 | -(unsigned long long)getInstallTypeForOptions:(id)arg1 andApp:(id)arg2 ; 23 | -(id)installBundle:(id)arg1 withOptions:(id)arg2 usingBlock:(/*^block*/id)arg3 forApp:(id)arg4 withError:(id*)arg5 outInstallProgress:(id*)arg6 ; 24 | -(BOOL)uninstallApplication:(id)arg1 withOptions:(id)arg2 usingBlock:(/*^block*/id)arg3 ; 25 | -(BOOL)registerBundleWithInfo:(id)arg1 options:(id)arg2 type:(unsigned long long)arg3 progress:(id)arg4 ; 26 | -(void)clearCreatedProgressForBundleID:(id)arg1 ; 27 | -(void)removeInstallProgressForBundleID:(id)arg1 ; 28 | -(void)getKnowledgeUUID:(id*)arg1 andSequenceNumber:(id*)arg2 ; 29 | -(id)delegateProxy; 30 | -(id)directionsApplications; 31 | -(id)applicationsWithAudioComponents; 32 | -(id)applicationsWithSettingsBundle; 33 | -(id)applicationsWithVPNPlugins; 34 | -(id)applicationsWithExternalAccessoryProtocols; 35 | -(id)applicationForUserActivityType:(id)arg1 ; 36 | -(id)applicationForUserActivityDomainName:(id)arg1 ; 37 | -(id)pluginsWithIdentifiers:(id)arg1 protocols:(id)arg2 version:(id)arg3 withFilter:(/*^block*/id)arg4 ; 38 | -(id)pluginsWithIdentifiers:(id)arg1 protocols:(id)arg2 version:(id)arg3 ; 39 | -(void)openUserActivity:(id)arg1 withApplicationProxy:(id)arg2 completionHandler:(/*^block*/id)arg3 ; 40 | -(id)installedVPNPlugins; 41 | -(id)unrestrictedApplications; 42 | -(id)publicURLSchemes; 43 | -(BOOL)getClaimedActivityTypes:(id*)arg1 domains:(id*)arg2 ; 44 | -(BOOL)installApplication:(id)arg1 withOptions:(id)arg2 ; 45 | -(BOOL)installApplication:(id)arg1 withOptions:(id)arg2 error:(id*)arg3 ; 46 | -(BOOL)downgradeApplicationToPlaceholder:(id)arg1 withOptions:(id)arg2 error:(id*)arg3 ; 47 | -(BOOL)registerApplicationDictionary:(id)arg1 ; 48 | -(BOOL)registerApplication:(id)arg1 ; 49 | -(BOOL)unregisterApplication:(id)arg1 ; 50 | -(BOOL)registerPlugin:(id)arg1 ; 51 | -(BOOL)unregisterPlugin:(id)arg1 ; 52 | -(BOOL)updateSINFWithData:(id)arg1 forApplication:(id)arg2 options:(id)arg3 error:(id*)arg4 ; 53 | -(BOOL)invalidateIconCache:(id)arg1 ; 54 | -(void)_clearCachedAdvertisingIdentifier; 55 | -(id)deviceIdentifierForAdvertising; 56 | -(id)installProgressForBundleID:(id)arg1 makeSynchronous:(unsigned char)arg2 ; 57 | -(BOOL)_LSPrivateRebuildApplicationDatabasesForSystemApps:(BOOL)arg1 internal:(BOOL)arg2 user:(BOOL)arg3 ; 58 | -(id)applicationForOpeningResource:(id)arg1 ; 59 | -(void)addObserver:(id)arg1 ; 60 | -(BOOL)isApplicationAvailableToOpenURL:(id)arg1 error:(id*)arg2 ; 61 | -(id)applicationsAvailableForHandlingURLScheme:(id)arg1 ; 62 | -(id)privateURLSchemes; 63 | -(id)URLOverrideForURL:(id)arg1 ; 64 | -(BOOL)openURL:(id)arg1 ; 65 | -(void)removeObserver:(id)arg1 ; 66 | -(id)operationToOpenResource:(id)arg1 usingApplication:(id)arg2 uniqueDocumentIdentifier:(id)arg3 userInfo:(id)arg4 delegate:(id)arg5 ; 67 | -(id)deviceIdentifierForVendor; 68 | -(id)applicationsAvailableForOpeningDocument:(id)arg1 ; 69 | -(id)operationToOpenResource:(id)arg1 usingApplication:(id)arg2 uniqueDocumentIdentifier:(id)arg3 sourceIsManaged:(BOOL)arg4 userInfo:(id)arg5 delegate:(id)arg6 ; 70 | -(BOOL)openURL:(id)arg1 withOptions:(id)arg2 ; 71 | -(BOOL)openSensitiveURL:(id)arg1 withOptions:(id)arg2 ; 72 | -(id)allInstalledApplications; 73 | -(id)applicationsOfType:(unsigned long long)arg1 ; 74 | -(void)enumerateBundlesOfType:(unsigned long long)arg1 usingBlock:(/*^block*/id)arg2 ; 75 | -(BOOL)uninstallApplication:(id)arg1 withOptions:(id)arg2 ; 76 | -(id)placeholderApplications; 77 | -(BOOL)applicationIsInstalled:(id)arg1 ; 78 | -(id)installedPlugins; 79 | -(void)_LSClearSchemaCaches; 80 | -(void)clearAdvertisingIdentifier; 81 | -(id)applicationsWithUIBackgroundModes; 82 | -(id)allApplications; 83 | -(BOOL)openApplicationWithBundleID:(id)arg1 ; 84 | -(BOOL)openSensitiveURL:(id)arg1 withOptions:(id)arg2 error:(id*)arg3 ; 85 | -(BOOL)openURL:(id)arg1 withOptions:(id)arg2 error:(id*)arg3 ; 86 | @end 87 | -------------------------------------------------------------------------------- /Clutch/MiniZip/crypt.h: -------------------------------------------------------------------------------- 1 | /* crypt.h -- base code for crypt/uncrypt ZIPfile 2 | 3 | 4 | Version 1.01e, February 12th, 2005 5 | 6 | Copyright (C) 1998-2005 Gilles Vollant 7 | 8 | This code is a modified version of crypting code in Infozip distribution 9 | 10 | The encryption/decryption parts of this source code (as opposed to the 11 | non-echoing password parts) were originally written in Europe. The 12 | whole source package can be freely distributed, including from the USA. 13 | (Prior to January 2000, re-export from the US was a violation of US law.) 14 | 15 | This encryption code is a direct transcription of the algorithm from 16 | Roger Schlafly, described by Phil Katz in the file appnote.txt. This 17 | file (appnote.txt) is distributed with the PKZIP program (even in the 18 | version without encryption capabilities). 19 | 20 | If you don't need crypting in your application, just define symbols 21 | NOCRYPT and NOUNCRYPT. 22 | 23 | This code support the "Traditional PKWARE Encryption". 24 | 25 | The new AES encryption added on Zip format by Winzip (see the page 26 | http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong 27 | Encryption is not supported. 28 | */ 29 | 30 | #define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) 31 | 32 | /*********************************************************************** 33 | * Return the next byte in the pseudo-random sequence 34 | */ 35 | static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) 36 | { 37 | unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an 38 | * unpredictable manner on 16-bit systems; not a problem 39 | * with any known compiler so far, though */ 40 | 41 | temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; 42 | return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); 43 | } 44 | 45 | /*********************************************************************** 46 | * Update the encryption keys with the next byte of plain text 47 | */ 48 | static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) 49 | { 50 | (*(pkeys+0)) = CRC32((*(pkeys+0)), c); 51 | (*(pkeys+1)) += (*(pkeys+0)) & 0xff; 52 | (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; 53 | { 54 | register int keyshift = (int)((*(pkeys+1)) >> 24); 55 | (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); 56 | } 57 | return c; 58 | } 59 | 60 | 61 | /*********************************************************************** 62 | * Initialize the encryption keys and the random header according to 63 | * the given password. 64 | */ 65 | static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) 66 | { 67 | *(pkeys+0) = 305419896L; 68 | *(pkeys+1) = 591751049L; 69 | *(pkeys+2) = 878082192L; 70 | while (*passwd != '\0') { 71 | update_keys(pkeys,pcrc_32_tab,(int)*passwd); 72 | passwd++; 73 | } 74 | } 75 | 76 | #define zdecode(pkeys,pcrc_32_tab,c) \ 77 | (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) 78 | 79 | #define zencode(pkeys,pcrc_32_tab,c,t) \ 80 | (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) 81 | 82 | #ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED 83 | 84 | #define RAND_HEAD_LEN 12 85 | /* "last resort" source for second part of crypt seed pattern */ 86 | # ifndef ZCR_SEED2 87 | # define ZCR_SEED2 3141592654UL /* use PI as default pattern */ 88 | # endif 89 | 90 | static int crypthead(const char* passwd, /* password string */ 91 | unsigned char* buf, /* where to write header */ 92 | int bufSize, 93 | unsigned long* pkeys, 94 | const unsigned long* pcrc_32_tab, 95 | unsigned long crcForCrypting) 96 | { 97 | int n; /* index in random header */ 98 | int t; /* temporary */ 99 | int c; /* random byte */ 100 | unsigned char header[RAND_HEAD_LEN-2]; /* random header */ 101 | static unsigned calls = 0; /* ensure different random header each time */ 102 | 103 | if (bufSize> 7) & 0xff; 118 | header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); 119 | } 120 | /* Encrypt random header (last two bytes is high word of crc) */ 121 | init_keys(passwd, pkeys, pcrc_32_tab); 122 | for (n = 0; n < RAND_HEAD_LEN-2; n++) 123 | { 124 | buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); 125 | } 126 | buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); 127 | buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); 128 | return n; 129 | } 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /Clutch/MiniZip/ioapi.h: -------------------------------------------------------------------------------- 1 | /* ioapi.h -- IO base function header for compress/uncompress .zip 2 | part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) 3 | 4 | Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) 5 | 6 | Modifications for Zip64 support 7 | Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) 8 | 9 | For more info read MiniZip_info.txt 10 | 11 | Changes 12 | 13 | Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) 14 | Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. 15 | More if/def section may be needed to support other platforms 16 | Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. 17 | (but you should use iowin32.c for windows instead) 18 | 19 | */ 20 | 21 | #ifndef _ZLIBIOAPI64_H 22 | #define _ZLIBIOAPI64_H 23 | 24 | #if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) 25 | /* Linux needs this to support file operation on files larger then 4+GB 26 | But might need better if/def to select just the platforms that needs them. */ 27 | #ifndef __USE_FILE_OFFSET64 28 | #define __USE_FILE_OFFSET64 29 | #endif 30 | #ifndef __USE_LARGEFILE64 31 | #define __USE_LARGEFILE64 32 | #endif 33 | #ifndef _LARGEFILE64_SOURCE 34 | #define _LARGEFILE64_SOURCE 35 | #endif 36 | #ifndef _FILE_OFFSET_BIT 37 | #define _FILE_OFFSET_BIT 64 38 | #endif 39 | #endif 40 | 41 | #import 42 | #import 43 | #import "zlib.h" 44 | 45 | #if defined(USE_FILE32API) 46 | #define fopen64 fopen 47 | #define ftello64 ftell 48 | #define fseeko64 fseek 49 | #else 50 | #ifdef __FreeBSD__ 51 | #define fopen64 fopen 52 | #define ftello64 ftello 53 | #define fseeko64 fseeko 54 | #endif 55 | #ifdef _MSC_VER 56 | #define fopen64 fopen 57 | #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) 58 | #define ftello64 _ftelli64 59 | #define fseeko64 _fseeki64 60 | #else // old MSC 61 | #define ftello64 ftell 62 | #define fseeko64 fseek 63 | #endif 64 | #endif 65 | #endif 66 | 67 | /* a type choosen by DEFINE */ 68 | #ifdef HAVE_64BIT_INT_CUSTOM 69 | typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; 70 | #else 71 | #ifdef HAS_STDINT_H 72 | #import "stdint.h" 73 | typedef uint64_t ZPOS64_T; 74 | #else 75 | 76 | #if defined(_MSC_VER) || defined(__BORLANDC__) 77 | typedef unsigned __int64 ZPOS64_T; 78 | #else 79 | typedef unsigned long long int ZPOS64_T; 80 | #endif 81 | #endif 82 | #endif 83 | 84 | #ifdef __cplusplus 85 | extern "C" { 86 | #endif 87 | 88 | #define ZLIB_FILEFUNC_SEEK_CUR (1) 89 | #define ZLIB_FILEFUNC_SEEK_END (2) 90 | #define ZLIB_FILEFUNC_SEEK_SET (0) 91 | 92 | #define ZLIB_FILEFUNC_MODE_READ (1) 93 | #define ZLIB_FILEFUNC_MODE_WRITE (2) 94 | #define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) 95 | 96 | #define ZLIB_FILEFUNC_MODE_EXISTING (4) 97 | #define ZLIB_FILEFUNC_MODE_CREATE (8) 98 | 99 | #ifndef ZCALLBACK 100 | #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || \ 101 | defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) 102 | #define ZCALLBACK CALLBACK 103 | #else 104 | #define ZCALLBACK 105 | #endif 106 | #endif 107 | 108 | typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); 109 | typedef voidpf (ZCALLBACK *opendisk_file_func) OF((voidpf opaque, voidpf stream, int number_disk, int mode)); 110 | typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); 111 | typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); 112 | typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); 113 | typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); 114 | 115 | typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); 116 | typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); 117 | 118 | /* here is the "old" 32 bits structure structure */ 119 | typedef struct zlib_filefunc_def_s 120 | { 121 | open_file_func zopen_file; 122 | opendisk_file_func zopendisk_file; 123 | read_file_func zread_file; 124 | write_file_func zwrite_file; 125 | tell_file_func ztell_file; 126 | seek_file_func zseek_file; 127 | close_file_func zclose_file; 128 | testerror_file_func zerror_file; 129 | voidpf opaque; 130 | } zlib_filefunc_def; 131 | 132 | typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); 133 | typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); 134 | typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); 135 | typedef voidpf (ZCALLBACK *opendisk64_file_func)OF((voidpf opaque, voidpf stream, int number_disk, int mode)); 136 | 137 | typedef struct zlib_filefunc64_def_s 138 | { 139 | open64_file_func zopen64_file; 140 | opendisk64_file_func zopendisk64_file; 141 | read_file_func zread_file; 142 | write_file_func zwrite_file; 143 | tell64_file_func ztell64_file; 144 | seek64_file_func zseek64_file; 145 | close_file_func zclose_file; 146 | testerror_file_func zerror_file; 147 | voidpf opaque; 148 | } zlib_filefunc64_def; 149 | 150 | void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); 151 | void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); 152 | 153 | /* now internal definition, only for zip.c and unzip.h */ 154 | typedef struct zlib_filefunc64_32_def_s 155 | { 156 | zlib_filefunc64_def zfile_func64; 157 | open_file_func zopen32_file; 158 | opendisk_file_func zopendisk32_file; 159 | tell_file_func ztell32_file; 160 | seek_file_func zseek32_file; 161 | } zlib_filefunc64_32_def; 162 | 163 | #define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) 164 | #define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) 165 | //#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) 166 | //#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) 167 | #define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) 168 | #define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) 169 | 170 | voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); 171 | voidpf call_zopendisk64 OF((const zlib_filefunc64_32_def* pfilefunc, voidpf filestream, int number_disk, int mode)); 172 | long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); 173 | ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); 174 | 175 | void fill_zlib_filefunc64_32_def_from_filefunc32 OF((zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32)); 176 | 177 | #define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) 178 | #define ZOPENDISK64(filefunc,filestream,diskn,mode) (call_zopendisk64((&(filefunc)),(filestream),(diskn),(mode))) 179 | #define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) 180 | #define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) 181 | 182 | #ifdef __cplusplus 183 | } 184 | #endif 185 | 186 | #endif 187 | -------------------------------------------------------------------------------- /Clutch/MiniZip/zip.h: -------------------------------------------------------------------------------- 1 | /* zip.h -- IO on .zip files using zlib 2 | Version 1.1, February 14h, 2010 3 | part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) 4 | 5 | Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) 6 | 7 | Modifications for Zip64 support 8 | Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) 9 | 10 | For more info read MiniZip_info.txt 11 | 12 | --------------------------------------------------------------------------- 13 | 14 | Condition of use and distribution are the same than zlib : 15 | 16 | This software is provided 'as-is', without any express or implied 17 | warranty. In no event will the authors be held liable for any damages 18 | arising from the use of this software. 19 | 20 | Permission is granted to anyone to use this software for any purpose, 21 | including commercial applications, and to alter it and redistribute it 22 | freely, subject to the following restrictions: 23 | 24 | 1. The origin of this software must not be misrepresented; you must not 25 | claim that you wrote the original software. If you use this software 26 | in a product, an acknowledgment in the product documentation would be 27 | appreciated but is not required. 28 | 2. Altered source versions must be plainly marked as such, and must not be 29 | misrepresented as being the original software. 30 | 3. This notice may not be removed or altered from any source distribution. 31 | 32 | --------------------------------------------------------------------------- 33 | */ 34 | 35 | #ifndef _zip12_H 36 | #define _zip12_H 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | 42 | #ifndef _ZLIB_H 43 | #import "zlib.h" 44 | #endif 45 | 46 | #ifndef _ZLIBIOAPI_H 47 | #import "ioapi.h" 48 | #endif 49 | 50 | #ifdef HAVE_BZIP2 51 | #import "bzlib.h" 52 | #endif 53 | 54 | #define Z_BZIP2ED 12 55 | 56 | #if defined(STRICTZIP) || defined(STRICTZIPUNZIP) 57 | /* like the STRICT of WIN32, we define a pointer that cannot be converted 58 | from (void*) without cast */ 59 | typedef struct TagzipFile__ { int unused; } zipFile__; 60 | typedef zipFile__ *zipFile; 61 | #else 62 | typedef voidp zipFile; 63 | #endif 64 | 65 | #define ZIP_OK (0) 66 | #define ZIP_EOF (0) 67 | #define ZIP_ERRNO (Z_ERRNO) 68 | #define ZIP_PARAMERROR (-102) 69 | #define ZIP_BADZIPFILE (-103) 70 | #define ZIP_INTERNALERROR (-104) 71 | 72 | #ifndef DEF_MEM_LEVEL 73 | # if MAX_MEM_LEVEL >= 8 74 | # define DEF_MEM_LEVEL 8 75 | # else 76 | # define DEF_MEM_LEVEL MAX_MEM_LEVEL 77 | # endif 78 | #endif 79 | /* default memLevel */ 80 | 81 | /* tm_zip contain date/time info */ 82 | typedef struct tm_zip_s 83 | { 84 | uInt tm_sec; /* seconds after the minute - [0,59] */ 85 | uInt tm_min; /* minutes after the hour - [0,59] */ 86 | uInt tm_hour; /* hours since midnight - [0,23] */ 87 | uInt tm_mday; /* day of the month - [1,31] */ 88 | uInt tm_mon; /* months since January - [0,11] */ 89 | uInt tm_year; /* years - [1980..2044] */ 90 | } tm_zip; 91 | 92 | typedef struct 93 | { 94 | tm_zip tmz_date; /* date in understandable format */ 95 | uLong dosDate; /* if dos_date == 0, tmu_date is used */ 96 | uLong internal_fa; /* internal file attributes 2 bytes */ 97 | uLong external_fa; /* external file attributes 4 bytes */ 98 | } zip_fileinfo; 99 | 100 | typedef const char* zipcharpc; 101 | 102 | #define APPEND_STATUS_CREATE (0) 103 | #define APPEND_STATUS_CREATEAFTER (1) 104 | #define APPEND_STATUS_ADDINZIP (2) 105 | 106 | /***************************************************************************/ 107 | /* Writing a zip file */ 108 | 109 | extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); 110 | extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); 111 | /* Create a zipfile. 112 | 113 | pathname should contain the full pathname (by example, on a Windows XP computer 114 | "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". 115 | 116 | return NULL if zipfile cannot be opened 117 | return zipFile handle if no error 118 | 119 | If the file pathname exist and append == APPEND_STATUS_CREATEAFTER, the zip 120 | will be created at the end of the file. (useful if the file contain a self extractor code) 121 | If the file pathname exist and append == APPEND_STATUS_ADDINZIP, we will add files in existing 122 | zip (be sure you don't add file that doesn't exist) 123 | 124 | NOTE: There is no delete function into a zipfile. If you want delete file into a zipfile, 125 | you must open a zipfile, and create another. Of course, you can use RAW reading and writing to copy 126 | the file you did not want delete. */ 127 | 128 | extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, int append, zipcharpc* globalcomment, 129 | zlib_filefunc_def* pzlib_filefunc_def)); 130 | 131 | extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, int append, zipcharpc* globalcomment, 132 | zlib_filefunc64_def* pzlib_filefunc_def)); 133 | 134 | extern zipFile ZEXPORT zipOpen3 OF((const char *pathname, int append, ZPOS64_T disk_size, 135 | zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc_def)); 136 | /* Same as zipOpen2 but allows specification of spanned zip size */ 137 | 138 | extern zipFile ZEXPORT zipOpen3_64 OF((const void *pathname, int append, ZPOS64_T disk_size, 139 | zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def)); 140 | 141 | extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, 142 | const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, 143 | uInt size_extrafield_global, const char* comment, int method, int level)); 144 | /* Open a file in the ZIP for writing. 145 | 146 | filename : the filename in zip (if NULL, '-' without quote will be used 147 | *zipfi contain supplemental information 148 | extrafield_local buffer to store the local header extra field data, can be NULL 149 | size_extrafield_local size of extrafield_local buffer 150 | extrafield_global buffer to store the global header extra field data, can be NULL 151 | size_extrafield_global size of extrafield_local buffer 152 | comment buffer for comment string 153 | method contain the compression method (0 for store, Z_DEFLATED for deflate) 154 | level contain the level of compression (can be Z_DEFAULT_COMPRESSION) 155 | zip64 is set to 1 if a zip64 extended information block should be added to the local file header. 156 | this MUST be '1' if the uncompressed size is >= 0xffffffff. */ 157 | 158 | extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, 159 | const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, 160 | uInt size_extrafield_global, const char* comment, int method, int level, int zip64)); 161 | /* Same as zipOpenNewFileInZip with zip64 support */ 162 | 163 | extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, 164 | const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, 165 | uInt size_extrafield_global, const char* comment, int method, int level, int raw)); 166 | /* Same as zipOpenNewFileInZip, except if raw=1, we write raw file */ 167 | 168 | extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, 169 | const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, 170 | uInt size_extrafield_global, const char* comment, int method, int level, int raw, int zip64)); 171 | /* Same as zipOpenNewFileInZip3 with zip64 support */ 172 | 173 | extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, 174 | const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, 175 | uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, 176 | int strategy, const char* password, uLong crcForCrypting)); 177 | /* Same as zipOpenNewFileInZip2, except 178 | windowBits, memLevel, strategy : see parameter strategy in deflateInit2 179 | password : crypting password (NULL for no crypting) 180 | crcForCrypting : crc of file to compress (needed for crypting) */ 181 | 182 | extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, 183 | const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, 184 | uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, 185 | int strategy, const char* password, uLong crcForCrypting, int zip64)); 186 | /* Same as zipOpenNewFileInZip3 with zip64 support */ 187 | 188 | extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, 189 | const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, 190 | uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, 191 | int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase)); 192 | /* Same as zipOpenNewFileInZip3 except versionMadeBy & flag fields */ 193 | 194 | extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, 195 | const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, 196 | uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, 197 | int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase, int zip64)); 198 | /* Same as zipOpenNewFileInZip4 with zip64 support */ 199 | 200 | extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, const void* buf, unsigned len)); 201 | /* Write data in the zipfile */ 202 | 203 | extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); 204 | /* Close the current file in the zipfile */ 205 | 206 | extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, uLong uncompressed_size, uLong crc32)); 207 | extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, ZPOS64_T uncompressed_size, uLong crc32)); 208 | /* Close the current file in the zipfile, for file opened with parameter raw=1 in zipOpenNewFileInZip2 209 | uncompressed_size and crc32 are value for the uncompressed size */ 210 | 211 | extern int ZEXPORT zipClose OF((zipFile file, const char* global_comment)); 212 | /* Close the zipfile */ 213 | 214 | /***************************************************************************/ 215 | 216 | #ifdef __cplusplus 217 | } 218 | #endif 219 | 220 | #endif /* _zip64_H */ 221 | -------------------------------------------------------------------------------- /Clutch/NSBundle+Clutch.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSBundle+Clutch.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 20.04.15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface NSBundle (Clutch) 12 | 13 | @property NSString *clutchBID; 14 | 15 | - (NSString *)bundleIdentifier; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Clutch/NSBundle+Clutch.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSBundle+Clutch.m 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 20.04.15. 6 | // 7 | // 8 | 9 | #import "NSBundle+Clutch.h" 10 | 11 | @import ObjectiveC.runtime; 12 | 13 | @implementation NSBundle (Clutch) 14 | 15 | static NSString* _bID; 16 | 17 | - (NSString *)clutchBID 18 | { 19 | NSString *value = objc_getAssociatedObject(self, &_bID); 20 | return value; 21 | } 22 | 23 | - (void)setClutchBID:(NSString *)clutchBID 24 | { 25 | [self willChangeValueForKey:@"clutchBID"]; 26 | objc_setAssociatedObject(self, &_bID, clutchBID, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 27 | [self didChangeValueForKey:@"clutchBID"]; 28 | } 29 | 30 | - (NSString *)bundleIdentifier { 31 | 32 | if ([self.bundlePath isEqualToString:[NSBundle mainBundle].bundlePath]) { 33 | 34 | return self.clutchBID; 35 | } 36 | 37 | return self.infoDictionary[@"CFBundleIdentifier"]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Clutch/NSData+Reading.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+Reading.h 3 | // optool 4 | // Copyright (c) 2014, Alex Zielenski 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // * Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 13 | // * Redistributions in binary form must reproduce the above copyright notice, 14 | // this list of conditions and the following disclaimer in the documentation 15 | // and/or other materials provided with the distribution. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | #import 29 | 30 | @interface NSData (Reading) 31 | 32 | - (NSUInteger)currentOffset; 33 | - (void)setCurrentOffset:(NSUInteger)offset; 34 | 35 | - (uint8_t)nextByte; 36 | - (uint8_t)byteAtOffset:(NSUInteger)offset; 37 | 38 | - (uint16_t)nextShort; 39 | - (uint16_t)shortAtOffset:(NSUInteger)offset; 40 | 41 | - (uint32_t)nextInt; 42 | - (uint32_t)intAtOffset:(NSUInteger)offset; 43 | 44 | - (uint64_t)nextLong; 45 | - (uint64_t)longAtOffset:(NSUInteger)offset; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /Clutch/NSData+Reading.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+Reading.m 3 | // optool 4 | // Copyright (c) 2014, Alex Zielenski 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // * Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 13 | // * Redistributions in binary form must reproduce the above copyright notice, 14 | // this list of conditions and the following disclaimer in the documentation 15 | // and/or other materials provided with the distribution. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | #import "NSData+Reading.h" 29 | #import 30 | 31 | @implementation NSData (Reading) 32 | 33 | static char OFFSET; 34 | - (NSUInteger)currentOffset 35 | { 36 | NSNumber *value = objc_getAssociatedObject(self, &OFFSET); 37 | return value.unsignedIntegerValue; 38 | } 39 | 40 | - (void)setCurrentOffset:(NSUInteger)offset 41 | { 42 | [self willChangeValueForKey:@"currentOffset"]; 43 | objc_setAssociatedObject(self, &OFFSET, @(offset), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 44 | [self didChangeValueForKey:@"currentOffset"]; 45 | } 46 | 47 | - (uint8_t)nextByte 48 | { 49 | uint8_t nextByte = [self byteAtOffset:self.currentOffset]; 50 | self.currentOffset += sizeof(uint8_t); 51 | return nextByte; 52 | } 53 | 54 | - (uint8_t)byteAtOffset:(NSUInteger)offset 55 | { 56 | uint8_t result; 57 | [self getBytes:&result range:NSMakeRange(offset, sizeof(result))]; 58 | return result; 59 | } 60 | 61 | - (uint16_t)nextShort 62 | { 63 | uint16_t nextShort = [self shortAtOffset:self.currentOffset]; 64 | self.currentOffset += sizeof(uint16_t); 65 | return nextShort; 66 | } 67 | 68 | - (uint16_t)shortAtOffset:(NSUInteger)offset 69 | { 70 | uint16_t result; 71 | [self getBytes:&result range:NSMakeRange(offset, sizeof(result))]; 72 | return result; 73 | } 74 | 75 | - (uint32_t)nextInt 76 | { 77 | uint32_t nextInt = [self intAtOffset:self.currentOffset]; 78 | self.currentOffset += sizeof(uint32_t); 79 | return nextInt; 80 | } 81 | 82 | - (uint32_t)intAtOffset:(NSUInteger)offset 83 | { 84 | uint32_t result; 85 | [self getBytes:&result range:NSMakeRange(offset, sizeof(result))]; 86 | return result; 87 | } 88 | 89 | - (uint64_t)nextLong 90 | { 91 | uint64_t nextLong = [self longAtOffset:self.currentOffset]; 92 | self.currentOffset += sizeof(uint64_t); 93 | return nextLong; 94 | } 95 | 96 | - (uint64_t)longAtOffset:(NSUInteger)offset; 97 | { 98 | uint64_t result; 99 | [self getBytes:&result range:NSMakeRange(offset, sizeof(result))]; 100 | return result; 101 | } 102 | 103 | @end 104 | 105 | @implementation NSMutableData (ByteAdditions) 106 | 107 | - (void)appendByte:(uint8_t)value 108 | { 109 | [self appendBytes:&value length:sizeof(value)]; 110 | } 111 | 112 | - (void)appendShort:(uint16_t)value 113 | { 114 | uint16_t swap = CFSwapInt16HostToLittle(value); 115 | [self appendBytes:&swap length:sizeof(swap)]; 116 | } 117 | 118 | - (void)appendInt:(uint32_t)value 119 | { 120 | uint32_t swap = CFSwapInt32HostToLittle(value); 121 | [self appendBytes:&swap length:sizeof(swap)]; 122 | } 123 | 124 | - (void)appendLong:(uint64_t)value; 125 | { 126 | uint64_t swap = CFSwapInt64HostToLittle(value); 127 | [self appendBytes:&swap length:sizeof(swap)]; 128 | } 129 | 130 | @end 131 | -------------------------------------------------------------------------------- /Clutch/NSFileHandle+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSFileHandle+Private.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 01.04.15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface NSFileHandle (Private) 12 | 13 | - (uint32_t)intAtOffset:(unsigned long long)offset; 14 | - (void)replaceBytesInRange:(NSRange)range withBytes:(const void *)bytes; 15 | - (void)getBytes:(void*)result atOffset:(NSUInteger)offset length:(NSUInteger)length; 16 | - (void)getBytes:(void*)result inRange:(NSRange)range; 17 | //- (bool)hk_readValue:(void*)arg1 ofSize:(unsigned long long)arg2; 18 | //- (bool)hk_writeValue:(const void*)arg1 size:(unsigned long long)arg2; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Clutch/NSFileHandle+Private.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSFileHandle+Private.m 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 01.04.15. 6 | // 7 | // 8 | 9 | #import "NSFileHandle+Private.h" 10 | 11 | @implementation NSFileHandle (Private) 12 | 13 | - (void)replaceBytesInRange:(NSRange)range withBytes:(const void *)bytes 14 | { 15 | unsigned long long oldOffset = self.offsetInFile; 16 | 17 | [self seekToFileOffset:range.location]; 18 | 19 | [self writeData:[NSData dataWithBytes:bytes length:range.length]]; 20 | 21 | [self seekToFileOffset:oldOffset]; 22 | } 23 | 24 | - (void)getBytes:(void *)result inRange:(NSRange)range 25 | { 26 | unsigned long long oldOffset = self.offsetInFile; 27 | 28 | [self seekToFileOffset:range.location]; 29 | 30 | NSData *data = [self readDataOfLength:range.length]; 31 | 32 | [data getBytes:result length:range.length]; 33 | 34 | [self seekToFileOffset:oldOffset]; 35 | } 36 | 37 | - (void)getBytes:(void*)result atOffset:(NSUInteger)offset length:(NSUInteger)length 38 | { 39 | unsigned long long oldOffset = self.offsetInFile; 40 | 41 | [self seekToFileOffset:offset]; 42 | 43 | NSData *data = [self readDataOfLength:length]; 44 | 45 | [data getBytes:result length:length]; 46 | 47 | [self seekToFileOffset:oldOffset]; 48 | } 49 | 50 | - (const void *)bytesAtOffset:(NSUInteger)offset length:(NSUInteger)size 51 | { 52 | unsigned long long oldOffset = self.offsetInFile; 53 | 54 | [self seekToFileOffset:offset]; 55 | 56 | const void * result; 57 | 58 | NSData *data = [self readDataOfLength:size]; 59 | 60 | [data getBytes:&result length:size]; 61 | 62 | [self seekToFileOffset:oldOffset]; 63 | 64 | return result; 65 | } 66 | 67 | - (uint32_t)intAtOffset:(unsigned long long)offset 68 | { 69 | unsigned long long oldOffset = self.offsetInFile; 70 | 71 | [self seekToFileOffset:offset]; 72 | 73 | uint32_t result; 74 | 75 | NSData *data = [self readDataOfLength:sizeof(result)]; 76 | 77 | [data getBytes:&result length:sizeof(result)]; 78 | 79 | [self seekToFileOffset:oldOffset]; 80 | 81 | return result; 82 | } 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /Clutch/NSTask.h: -------------------------------------------------------------------------------- 1 | /* NSTask.h 2 | Copyright (c) 1996-2007, Apple Inc. All rights reserved. 3 | */ 4 | 5 | #import 6 | 7 | @class NSString, NSArray, NSDictionary; 8 | 9 | @interface NSTask : NSObject 10 | 11 | // Create an NSTask which can be run at a later time 12 | // An NSTask can only be run once. Subsequent attempts to 13 | // run an NSTask will raise. 14 | // Upon task death a notification will be sent 15 | // { Name = NSTaskDidTerminateNotification; object = task; } 16 | // 17 | 18 | - (instancetype)init; 19 | 20 | // set parameters 21 | // these methods can only be done before a launch 22 | // if not set, use current 23 | // if not set, use current 24 | 25 | // set standard I/O channels; may be either an NSFileHandle or an NSPipe 26 | - (void)setStandardInput:(id)input; 27 | - (void)setStandardOutput:(id)output; 28 | - (void)setStandardError:(id)error; 29 | 30 | // get parameters 31 | @property (NS_NONATOMIC_IOSONLY, copy) NSString *launchPath; 32 | @property (NS_NONATOMIC_IOSONLY, copy) NSArray *arguments; 33 | @property (NS_NONATOMIC_IOSONLY, copy) NSDictionary *environment; 34 | @property (NS_NONATOMIC_IOSONLY, copy) NSString *currentDirectoryPath; 35 | 36 | // get standard I/O channels; could be either an NSFileHandle or an NSPipe 37 | - (id)standardInput; 38 | - (id)standardOutput; 39 | - (id)standardError; 40 | 41 | // actions 42 | - (void)launch; 43 | 44 | - (void)interrupt; // Not always possible. Sends SIGINT. 45 | - (void)terminate; // Not always possible. Sends SIGTERM. 46 | 47 | @property (NS_NONATOMIC_IOSONLY, readonly) BOOL suspend; 48 | @property (NS_NONATOMIC_IOSONLY, readonly) BOOL resume; 49 | 50 | // status 51 | @property (NS_NONATOMIC_IOSONLY, readonly) int processIdentifier; 52 | @property (NS_NONATOMIC_IOSONLY, getter=isRunning, readonly) BOOL running; 53 | 54 | @property (NS_NONATOMIC_IOSONLY, readonly) int terminationStatus; 55 | 56 | @end 57 | 58 | @interface NSTask (NSTaskConveniences) 59 | 60 | + (NSTask *)launchedTaskWithLaunchPath:(NSString *)path arguments:(NSArray *)arguments; 61 | // convenience; create and launch 62 | 63 | - (void)waitUntilExit; 64 | // poll the runLoop in defaultMode until task completes 65 | 66 | @end 67 | 68 | FOUNDATION_EXPORT NSString * const NSTaskDidTerminateNotification; 69 | 70 | -------------------------------------------------------------------------------- /Clutch/SCInfoBuilder.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCInfoBuilder.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 03.04.15. 6 | // 7 | // 8 | 9 | #import 10 | #import "ClutchBundle.h" 11 | 12 | struct sinf_atom { 13 | uint32_t size; // size of entire atom structure 14 | uint32_t name; // name (usually an ASCII value) 15 | uint8_t blob[]; // (size - 8) byte long data blob 16 | }; 17 | 18 | struct sinf_kval { 19 | uint32_t name; // name of structure 20 | uint32_t val; // value of structure 21 | }; 22 | 23 | @interface SCInfoBuilder : NSObject 24 | 25 | + (NSData *)sinfForBundle:(ClutchBundle *)bundle; 26 | + (NSDictionary *)parseOriginaleSinfForBundle:(ClutchBundle *)bundle; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Clutch/SCInfoBuilder.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCInfoBuilder.m 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 03.04.15. 6 | // 7 | // 8 | 9 | #import "SCInfoBuilder.h" 10 | #import "NSData+Reading.h" 11 | #import "scinfo.h" 12 | 13 | @implementation SCInfoBuilder 14 | 15 | + (NSData * _Nullable)sinfForBundle:(ClutchBundle *)bundle { 16 | return nil; 17 | } 18 | 19 | + (NSDictionary *)parseBlob:(NSData *)blobData { 20 | 21 | NSMutableDictionary *_blob = [NSMutableDictionary new]; 22 | 23 | NSDictionary *kvalues = @{@"frma":@"string", 24 | @"schm":@"data", 25 | @"user":@"number", 26 | @"key ":@"data", 27 | @"iviv":@"data", 28 | @"veID":@"number", 29 | @"plat":@"number", 30 | @"aver":@"number", 31 | @"tran":@"number", 32 | @"song":@"number", 33 | @"tool":@"number", 34 | @"medi":@"number", 35 | @"mode":@"number", 36 | @"name":@"string", 37 | @"priv":@"data", 38 | @"sign":@"data"}; 39 | 40 | blobData.currentOffset = 0; 41 | 42 | while (1) { 43 | 44 | if (blobData.currentOffset >= blobData.length) { 45 | break; 46 | } 47 | 48 | uint32_t _realSize = CFSwapInt32(blobData.nextInt); 49 | uint32_t _blobSize = _realSize - sizeof(struct sinf_kval); 50 | uint32_t _blobName = blobData.nextInt; 51 | 52 | NSString *name = [NSString stringWithCString:(const char *)&_blobName encoding:NSASCIIStringEncoding]; 53 | 54 | if (name.length > 4) { 55 | name = [name substringWithRange:NSMakeRange(0, 4)]; 56 | } 57 | 58 | if ([kvalues.allKeys containsObject:name] && name.length) { 59 | 60 | NSData *valData = [blobData subdataWithRange:NSMakeRange(blobData.currentOffset, _blobSize)]; 61 | 62 | if ([kvalues[name]isEqualToString:@"string"]) { 63 | NSString *val = [[NSString alloc]initWithData:valData encoding:NSASCIIStringEncoding]; 64 | _blob[name] = val; 65 | }else if ([kvalues[name]isEqualToString:@"data"]) { 66 | _blob[name] = valData; 67 | }else if ([kvalues[name]isEqualToString:@"number"]) { 68 | uint32_t integer; 69 | [valData getBytes:&integer length:sizeof(integer)]; 70 | _blob[name] = [NSNumber numberWithInt:CFSwapInt32(integer)]; 71 | } 72 | 73 | }else if (name.length && [name isEqualToString:@"schi"]) { 74 | _blob[name] = [self parseBlob:[blobData subdataWithRange:NSMakeRange(blobData.currentOffset, _blobSize)]]; 75 | }else if (name.length && [name isEqualToString:@"righ"]) { 76 | 77 | NSMutableDictionary *_righ = [NSMutableDictionary new]; 78 | 79 | int count = _blobSize / sizeof(struct sinf_kval); 80 | 81 | for (int i = 0; i < count; i++) { 82 | 83 | uint32_t kName = blobData.nextInt; 84 | uint32_t kValue = blobData.nextInt; 85 | 86 | NSString *name = [NSString stringWithCString:(const char *)&kName encoding:NSASCIIStringEncoding]; 87 | 88 | if (name.length > 4) { 89 | name = [name substringWithRange:NSMakeRange(0, 4)]; 90 | } 91 | 92 | if ([kvalues[name]isEqualToString:@"number"]&&name.length) { 93 | 94 | _righ[name] = [NSNumber numberWithUnsignedInt:CFSwapInt32(kValue)]; 95 | } 96 | 97 | } 98 | 99 | _blob[name] = _righ.copy; 100 | continue; 101 | } 102 | 103 | blobData.currentOffset += _blobSize; 104 | } 105 | 106 | return _blob.copy; 107 | } 108 | 109 | + (NSDictionary *)parseOriginaleSinfForBundle:(ClutchBundle *)bundle { 110 | 111 | NSMutableDictionary *_sinfDict = [NSMutableDictionary new]; 112 | 113 | Binary *executable = bundle.executable; 114 | 115 | NSData *sinfData = [NSData dataWithContentsOfFile:executable.sinfPath]; 116 | 117 | uint32_t realSize = CFSwapInt32([sinfData intAtOffset:0]); 118 | 119 | uint32_t blobSize = realSize - sizeof(struct sinf_kval); 120 | 121 | struct sinf_atom sinf; 122 | 123 | [sinfData getBytes:&sinf length:sizeof(sinf)]; 124 | 125 | _sinfDict[@"size"] = [NSNumber numberWithInt:realSize]; 126 | _sinfDict[@"name"] = [NSString stringWithCString:(const char *)&sinf.name encoding:NSASCIIStringEncoding]; 127 | _sinfDict[@"blob"] = [self parseBlob:[sinfData subdataWithRange:NSMakeRange(sizeof(struct sinf_kval), blobSize)]]; 128 | 129 | return _sinfDict.copy; 130 | } 131 | 132 | @end 133 | -------------------------------------------------------------------------------- /Clutch/ZipArchive.h: -------------------------------------------------------------------------------- 1 | /** 2 | // @header ZipArchive.h 3 | // 4 | // An objective C wrapper for minizip and libz for creating and exanding ZIP files. 5 | // 6 | // @author Created by aish on 08-9-11. 7 | // acsolu@gmail.com 8 | // @copyright Copyright 2008 Inc. All rights reserved. 9 | // 10 | */ 11 | 12 | 13 | typedef NS_ENUM(NSInteger, ZipArchiveCompression) { 14 | ZipArchiveCompressionDefault = -1, 15 | ZipArchiveCompressionNone = 0, 16 | ZipArchiveCompressionSpeed = 1, 17 | ZipArchiveCompressionBest = 9, 18 | }; 19 | 20 | 21 | /** 22 | a block that is called from UnzipFileTo:overwrite:withProgressBlock: where the percentage of 23 | files processed (as an integer from 0 to 100), the number of files processed so far and the 24 | total number of files in the archive is called after each file is processed. 25 | */ 26 | typedef void(^ZipArchiveProgressUpdateBlock)(int percentage, int filesProcessed, unsigned long numFiles); 27 | 28 | /** 29 | @protocol 30 | @discussion methods for a delegate to receive error notifications and control overwriting of files 31 | */ 32 | 33 | @protocol ZipArchiveDelegate 34 | @optional 35 | 36 | /** 37 | @brief Delegate method to be notified of errors 38 | 39 | ZipArchive calls this selector on the delegate when errors are encountered. 40 | 41 | @param msg a string describing the error. 42 | */ 43 | 44 | -(void) ErrorMessage:(NSString*) msg; 45 | 46 | /** 47 | @brief Delegate method to determine if a file should be replaced 48 | 49 | When an zip file is being expanded and a file is about to be replaced, this selector 50 | is called on the delegate to notify that file is about to be replaced. The delegate method 51 | should return YES to overwrite the file, or NO to skip it. 52 | 53 | @param file - path to the file to be overwritten. 54 | @result a BOOL - YES to replace, NO to skip 55 | */ 56 | 57 | -(BOOL) OverWriteOperation:(NSString*) file; 58 | 59 | @end 60 | 61 | /** 62 | @class 63 | @brief An object that can create zip files and expand existing ones. 64 | This class provides methods to create a zip file (optionally with a password) and 65 | add files to that zip archive. 66 | 67 | It also provides methods to expand an existing archive file (optionally with a password), 68 | and extract the files. 69 | */ 70 | 71 | @interface ZipArchive : NSObject { 72 | @private 73 | void* _zipFile; 74 | void* _unzFile; 75 | 76 | unsigned long _numFiles; 77 | NSString* _password; 78 | id _delegate; 79 | ZipArchiveProgressUpdateBlock _progressBlock; 80 | 81 | NSArray* _unzippedFiles; 82 | 83 | NSFileManager* _fileManager; 84 | NSStringEncoding _stringEncoding; 85 | } 86 | 87 | /** a delegate object conforming to ZipArchiveDelegate protocol */ 88 | @property (nonatomic, retain) id delegate; 89 | @property (nonatomic, readonly) unsigned long numFiles; 90 | @property (nonatomic, copy) ZipArchiveProgressUpdateBlock progressBlock; 91 | 92 | @property (nonatomic, assign) ZipArchiveCompression compression; 93 | 94 | /** 95 | @brief String encoding to be used when interpreting file names in the zip file. 96 | */ 97 | @property (nonatomic, assign) NSStringEncoding stringEncoding; 98 | 99 | /** an array of files that were successfully expanded. Available after calling UnzipFileTo:overWrite: */ 100 | @property (nonatomic, readonly) NSArray* unzippedFiles; 101 | 102 | -(id) initWithFileManager:(NSFileManager*) fileManager; 103 | 104 | -(BOOL) CreateZipFile2:(NSString*) zipFile; 105 | -(BOOL) CreateZipFile2:(NSString*) zipFile append:(BOOL)isAppend; 106 | -(BOOL) CreateZipFile2:(NSString*) zipFile Password:(NSString*) password; 107 | -(BOOL) CreateZipFile2:(NSString*) zipFile Password:(NSString*) password append:(BOOL)isAppend; 108 | -(BOOL) addFileToZip:(NSString*) file newname:(NSString*) newname; 109 | -(BOOL) addDataToZip:(NSData*) data fileAttributes:(NSDictionary *)attr newname:(NSString*) newname; 110 | -(BOOL) CloseZipFile2; 111 | 112 | -(BOOL) UnzipOpenFile:(NSString*) zipFile; 113 | -(BOOL) UnzipOpenFile:(NSString*) zipFile Password:(NSString*) password; 114 | -(BOOL) UnzipFileTo:(NSString*) path overWrite:(BOOL) overwrite; 115 | -(NSDictionary *)UnzipFileToMemory;//To avoid memory issue, only use this method for small zip files. 116 | -(BOOL) UnzipCloseFile; 117 | 118 | // List the contents of the zip archive. must be called after UnzipOpenFile. 119 | // If zip file was appended with `CreateZipFile2:append:` or ``CreateZipFile2:Password:append:`, 120 | // `getZipFileContents` result won't be updated until re-unzip-open after close write handle (`CloseZipFile2` then `UnzipCloseFile` then (`UnzipOpenFile:` or `UnzipOpenFile:Password`) get called). 121 | -(NSArray*) getZipFileContents; 122 | 123 | @end 124 | -------------------------------------------------------------------------------- /Clutch/ZipOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZipOperation.h 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 11.02.15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | #ifdef DEBUG 12 | #define PRINT_ZIP_LOGS 0 13 | #endif 14 | 15 | @class ClutchBundle; 16 | 17 | @interface ZipOperation : NSOperation 18 | 19 | - (instancetype)initWithApplication:(ClutchBundle *)clutchBundle; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Clutch/ZipOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZipOperation.m 3 | // Clutch 4 | // 5 | // Created by Anton Titkov on 11.02.15. 6 | // 7 | // 8 | 9 | #import "ZipOperation.h" 10 | #import "ZipArchive.h" 11 | #import "ClutchBundle.h" 12 | #import "ClutchPrint.h" 13 | 14 | @interface ZipOperation () 15 | { 16 | ClutchBundle *_application; 17 | BOOL _executing, _finished; 18 | ZipArchive *_archive; 19 | } 20 | @end 21 | 22 | 23 | @implementation ZipOperation 24 | 25 | - (id)initWithApplication:(ClutchBundle *)application { 26 | self = [super init]; 27 | if (self) { 28 | _executing = NO; 29 | _finished = NO; 30 | _application = application; 31 | } 32 | return self; 33 | } 34 | 35 | - (BOOL)isAsynchronous { 36 | return YES; 37 | } 38 | 39 | - (BOOL)isConcurrent { 40 | return YES; 41 | } 42 | 43 | - (BOOL)isExecuting { 44 | return _executing; 45 | } 46 | 47 | - (BOOL)isFinished { 48 | return _finished; 49 | } 50 | 51 | - (void)start { 52 | // Always check for cancellation before launching the task. 53 | if ([self isCancelled]) 54 | { 55 | // Must move the operation to the finished state if it is canceled. 56 | [self willChangeValueForKey:@"isFinished"]; 57 | _finished = YES; 58 | [self didChangeValueForKey:@"isFinished"]; 59 | return; 60 | } 61 | 62 | self.completionBlock = ^{ 63 | 64 | }; 65 | 66 | // If the operation is not canceled, begin executing the task. 67 | [self willChangeValueForKey:@"isExecuting"]; 68 | [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil]; 69 | _executing = YES; 70 | [self didChangeValueForKey:@"isExecuting"]; 71 | } 72 | 73 | - (void)main { 74 | @try { 75 | 76 | NSString *_zipFilename = _application.zipFilename, *_localPrefix = _application.zipPrefix; 77 | 78 | [[ClutchPrint sharedInstance] print:@"Zipping %@",_application.bundleURL.lastPathComponent]; 79 | 80 | if (_archive == nil) { 81 | _archive = [[ZipArchive alloc] init]; 82 | [_archive CreateZipFile2:[_application.workingPath stringByAppendingPathComponent:_zipFilename] append:(_application.parentBundle != nil)]; 83 | } 84 | 85 | if (!_application.parentBundle && [[NSFileManager defaultManager]fileExistsAtPath:[_application.bundleContainerURL URLByAppendingPathComponent:@"iTunesArtwork" isDirectory:NO].path]) { 86 | [_archive addFileToZip:[_application.bundleContainerURL URLByAppendingPathComponent:@"iTunesArtwork" isDirectory:NO].path newname:@"iTunesArtwork"]; 87 | } 88 | 89 | if (!_application.parentBundle && [[NSFileManager defaultManager]fileExistsAtPath:[_application.bundleContainerURL URLByAppendingPathComponent:@"iTunesMetadata.plist" isDirectory:NO].path]) { 90 | 91 | // skip iTunesMetadata 92 | // [_archive addFileToZip:[_application.bundleContainerURL URLByAppendingPathComponent:@"iTunesMetadata.plist" isDirectory:NO].path newname:@"iTunesMetadata.plist"]; 93 | } 94 | 95 | NSDirectoryEnumerator *dirEnumerator = [NSFileManager.defaultManager enumeratorAtURL:_application.bundleURL includingPropertiesForKeys:@[NSURLNameKey,NSURLIsDirectoryKey] options:0 errorHandler:^BOOL(NSURL *url, NSError *error) { 96 | return YES; 97 | }]; 98 | 99 | for (NSURL *theURL in dirEnumerator) 100 | { 101 | NSNumber *isDirectory; 102 | [theURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil]; 103 | 104 | NSString *_localPath = [theURL.path stringByReplacingOccurrencesOfString:_application.bundleContainerURL.path withString:@""]; 105 | 106 | NSArray *_pathComponents = _localPath.pathComponents; 107 | 108 | if (_pathComponents.count > 2) { 109 | if ([_pathComponents[2] isEqualToString:@"SC_Info"]||[_pathComponents[2] isEqualToString:@"Watch"]||[_pathComponents[2] isEqualToString:@"Frameworks"]||[_pathComponents[2] isEqualToString:@"PlugIns"]) { 110 | if ([_localPath.lastPathComponent hasPrefix:@"libswift"] && ![_localPath.pathExtension caseInsensitiveCompare:@"dylib"]) { 111 | [_archive addFileToZip:theURL.path newname:[_localPrefix stringByAppendingPathComponent:_localPath]]; 112 | #if PRINT_ZIP_LOGS 113 | [[ClutchPrint sharedInstance] print:@"Added %@",[_localPrefix stringByAppendingPathComponent:_localPath]]; 114 | #endif 115 | }else { 116 | #if PRINT_ZIP_LOGS 117 | [[ClutchPrint sharedInstance] print:@"Skipping %@",[_localPrefix stringByAppendingPathComponent:_localPath]]; 118 | #endif 119 | } 120 | } 121 | else if (![isDirectory boolValue] && ![_pathComponents[2] isEqualToString:_application.executablePath.lastPathComponent]) { 122 | [_archive addFileToZip:theURL.path newname:[_localPrefix stringByAppendingPathComponent:_localPath]]; 123 | #if PRINT_ZIP_LOGS 124 | [[ClutchPrint sharedInstance] print:@"Added %@",[_localPrefix stringByAppendingPathComponent:_localPath]]; 125 | #endif 126 | } 127 | else { 128 | #if PRINT_ZIP_LOGS 129 | [[ClutchPrint sharedInstance] print:@"Skipping %@",[_localPrefix stringByAppendingPathComponent:_localPath]]; 130 | #endif 131 | } 132 | } 133 | else { 134 | #if PRINT_ZIP_LOGS 135 | [[ClutchPrint sharedInstance] print:@"Skipping %@",_localPath]; 136 | #endif 137 | } 138 | 139 | } 140 | 141 | [_archive CloseZipFile2]; 142 | 143 | // Do the main work of the operation here. 144 | [self completeOperation]; 145 | } 146 | @catch(...) { 147 | // Do not rethrow exceptions. 148 | } 149 | } 150 | 151 | - (void)completeOperation { 152 | [self willChangeValueForKey:@"isFinished"]; 153 | [self willChangeValueForKey:@"isExecuting"]; 154 | _executing = NO; 155 | _finished = YES; 156 | [self didChangeValueForKey:@"isExecuting"]; 157 | [self didChangeValueForKey:@"isFinished"]; 158 | } 159 | 160 | @end 161 | -------------------------------------------------------------------------------- /Clutch/move_and_sign.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd "$PROJECT_DIR" 4 | ! [ -d build ] && mkdir build 5 | cp "$BUILT_PRODUCTS_DIR/$EXECUTABLE_PATH" "build/" 6 | codesign -fs- --entitlements "$CODE_SIGN_ENTITLEMENTS" "build/$EXECUTABLE_NAME" 7 | -------------------------------------------------------------------------------- /Clutch/optool-defines.h: -------------------------------------------------------------------------------- 1 | // 2 | // defines.h 3 | // optool 4 | // Copyright (c) 2014, Alex Zielenski 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // * Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 13 | // * Redistributions in binary form must reproduce the above copyright notice, 14 | // this list of conditions and the following disclaimer in the documentation 15 | // and/or other materials provided with the distribution. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | 29 | #import 30 | 31 | #ifndef CPU_TYPE_ARM64 32 | #define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) 33 | #endif 34 | 35 | #define CPU(CPUTYPE) ({ \ 36 | const char *c = ""; \ 37 | if (CPUTYPE == CPU_TYPE_I386) \ 38 | c = "x86"; \ 39 | if (CPUTYPE == CPU_TYPE_X86_64) \ 40 | c = "x86_64"; \ 41 | if (CPUTYPE == CPU_TYPE_ARM) \ 42 | c = "arm"; \ 43 | if (CPUTYPE == CPU_TYPE_ARM64) \ 44 | c = "arm64"; \ 45 | c; \ 46 | }) 47 | 48 | #define LC(LOADCOMMAND) ({ \ 49 | const char *c = ""; \ 50 | if (LOADCOMMAND == LC_REEXPORT_DYLIB) \ 51 | c = "LC_REEXPORT_DYLIB";\ 52 | else if (LOADCOMMAND == LC_LOAD_WEAK_DYLIB) \ 53 | c = "LC_LOAD_WEAK_DYLIB";\ 54 | else if (LOADCOMMAND == LC_LOAD_UPWARD_DYLIB) \ 55 | c = "LC_LOAD_UPWARD_DYLIB";\ 56 | else if (LOADCOMMAND == LC_LOAD_DYLIB) \ 57 | c = "LC_LOAD_DYLIB";\ 58 | c;\ 59 | }) 60 | 61 | #define COMMAND(str) ({ \ 62 | uint32_t cmd = -1; \ 63 | if ([str isEqualToString: @"reexport"]) \ 64 | cmd = LC_REEXPORT_DYLIB; \ 65 | else if ([str isEqualToString: @"weak"]) \ 66 | cmd = LC_LOAD_WEAK_DYLIB; \ 67 | else if ([str isEqualToString: @"upward"]) \ 68 | cmd = LC_LOAD_UPWARD_DYLIB; \ 69 | else if ([str isEqualToString: @"load"]) \ 70 | cmd = LC_LOAD_DYLIB; \ 71 | cmd; \ 72 | }) 73 | 74 | // we pass around this header which includes some extra information 75 | // and a 32-bit header which we used for both 32-bit and 64-bit files 76 | // since the 64-bit just adds an extra field to the end which we don't need 77 | 78 | typedef struct thin_header { 79 | uint32_t offset; 80 | uint32_t size; 81 | struct mach_header header; 82 | } thin_header; 83 | 84 | typedef NS_ENUM(int, OPError) { 85 | OPErrorNone = 0, 86 | OPErrorRead = 1, // failed to read target path 87 | OPErrorIncompatibleBinary = 2, // couldn't find x86 or x86_64 architecture in binary 88 | OPErrorStripFailure = 3, // failed to strip codesignature 89 | OPErrorWriteFailure = 4, // failed to write data to final output path 90 | OPErrorNoBackup = 5, // no backup to restore 91 | OPErrorRemovalFailure = 6, // failed to remove executable during restore 92 | OPErrorMoveFailure = 7, // failed to move backup to correct location 93 | OPErrorNoEntries = 8, // cant remove dylib entries because they dont exist 94 | OPErrorInsertFailure = 9, // failed to insert load command 95 | OPErrorInvalidLoadCommand = 10, // user provided an unnacceptable load command string 96 | OPErrorResignFailure = 11, // codesign failed for some reason 97 | OPErrorBackupFailure = 12, // failed to write backup 98 | OPErrorInvalidArguments = 13 // bad arguments 99 | }; -------------------------------------------------------------------------------- /Clutch/optool-headers.h: -------------------------------------------------------------------------------- 1 | // 2 | // headers.h 3 | // optool 4 | // Copyright (c) 2014, Alex Zielenski 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // * Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 13 | // * Redistributions in binary form must reproduce the above copyright notice, 14 | // this list of conditions and the following disclaimer in the documentation 15 | // and/or other materials provided with the distribution. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | #import 29 | #import "optool-defines.h" 30 | 31 | thin_header headerAtOffset(NSData *binary, uint32_t offset); 32 | thin_header *headersFromBinary(thin_header *headers, NSData *binary, uint32_t *amount); -------------------------------------------------------------------------------- /Clutch/optool-headers.m: -------------------------------------------------------------------------------- 1 | // 2 | // headers.m 3 | // optool 4 | // Copyright (c) 2014, Alex Zielenski 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // * Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 13 | // * Redistributions in binary form must reproduce the above copyright notice, 14 | // this list of conditions and the following disclaimer in the documentation 15 | // and/or other materials provided with the distribution. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | #import "optool-headers.h" 29 | #import 30 | #import 31 | #import "NSData+Reading.h" 32 | 33 | thin_header headerAtOffset(NSData *binary, uint32_t offset) { 34 | thin_header macho; 35 | macho.offset = offset; 36 | macho.header = *(struct mach_header *)(binary.bytes + offset); 37 | if (macho.header.magic == MH_MAGIC || macho.header.magic == MH_CIGAM) { 38 | macho.size = sizeof(struct mach_header); 39 | } else { 40 | macho.size = sizeof(struct mach_header_64); 41 | } 42 | if (macho.header.cputype != CPU_TYPE_X86_64 && macho.header.cputype != CPU_TYPE_I386 && macho.header.cputype != CPU_TYPE_ARM && macho.header.cputype != CPU_TYPE_ARM64){ 43 | macho.size = 0; 44 | } 45 | 46 | return macho; 47 | } 48 | 49 | thin_header *headersFromBinary(thin_header *headers, NSData *binary, uint32_t *amount) { 50 | // In a MachO/FAT binary the first 4 bytes is a magic number 51 | // which gives details about the type of binary it is 52 | // CIGAM and co. mean the target binary has a byte order 53 | // in reverse relation to the host machine so we have to swap the bytes 54 | uint32_t magic = [binary intAtOffset:0]; 55 | bool shouldSwap = magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == FAT_CIGAM; 56 | #define SWAP(NUM) (shouldSwap ? CFSwapInt32(NUM) : NUM) 57 | 58 | uint32_t numArchs = 0; 59 | 60 | // a FAT file is basically a collection of thin MachO binaries 61 | if (magic == FAT_CIGAM || magic == FAT_MAGIC) { 62 | 63 | // WE GOT A FAT ONE 64 | struct fat_header fat = *(struct fat_header *)binary.bytes; 65 | fat.nfat_arch = SWAP(fat.nfat_arch); 66 | int offset = sizeof(struct fat_header); 67 | 68 | // Loop through the architectures within the FAT binary to find 69 | // a thin macho header that we can work with (x86 or x86_64) 70 | for (int i = 0; i < fat.nfat_arch; i++) { 71 | struct fat_arch arch; 72 | arch = *(struct fat_arch *)([binary bytes] + offset); 73 | arch.cputype = SWAP(arch.cputype); 74 | arch.offset = SWAP(arch.offset); 75 | 76 | thin_header macho = headerAtOffset(binary, arch.offset); 77 | if (macho.size > 0) { 78 | 79 | headers[numArchs] = macho; 80 | numArchs++; 81 | } 82 | 83 | offset += sizeof(struct fat_arch); 84 | } 85 | // The binary is thin, meaning it contains only one architecture 86 | } else if (magic == MH_MAGIC || magic == MH_MAGIC_64) { 87 | thin_header macho = headerAtOffset(binary, 0); 88 | if (macho.size > 0) { 89 | 90 | numArchs++; 91 | headers[0] = macho; 92 | } 93 | 94 | } 95 | 96 | *amount = numArchs; 97 | 98 | return headers; 99 | } 100 | -------------------------------------------------------------------------------- /Clutch/optool-operations.h: -------------------------------------------------------------------------------- 1 | // 2 | // operations.h 3 | // optool 4 | // Copyright (c) 2014, Alex Zielenski 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // * Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 13 | // * Redistributions in binary form must reproduce the above copyright notice, 14 | // this list of conditions and the following disclaimer in the documentation 15 | // and/or other materials provided with the distribution. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | 29 | #import 30 | #import "optool-defines.h" 31 | 32 | BOOL stripCodeSignatureFromBinary(NSMutableData *binary, thin_header macho, BOOL soft); 33 | BOOL removeLoadEntryFromBinary(NSMutableData *binary, thin_header macho, NSString *payload); 34 | BOOL binaryHasLoadCommandForDylib(NSMutableData *binary, NSString *dylib, uint32_t *lastOffset, thin_header macho); 35 | BOOL insertLoadEntryIntoBinary(NSString *dylibPath, NSMutableData *binary, thin_header macho, uint32_t type); 36 | BOOL removeASLRFromBinary(NSMutableData *binary, thin_header macho); 37 | 38 | // Clutch 2 39 | BOOL insertRPATHIntoBinary(NSString *dylibPath, NSMutableData *binary, thin_header macho); 40 | BOOL removeRPATHFromBinary(NSMutableData *binary, thin_header macho); 41 | BOOL binaryHasRPATH(NSMutableData *binary, NSString *dylib, uint32_t *lastOffset, thin_header macho); -------------------------------------------------------------------------------- /Clutch/optool.h: -------------------------------------------------------------------------------- 1 | 2 | #import "optool-headers.h" 3 | #import "optool-operations.h" -------------------------------------------------------------------------------- /Clutch/scinfo.h: -------------------------------------------------------------------------------- 1 | /* 2 | Generates SC_Info keys (.sinf and .supp) 3 | see http://hackulo.us/wiki/SC_Info 4 | */ 5 | 6 | void *create_atom(char *name, int len, void *content); 7 | void *coalesced_atom(int amount, uint32_t name, ...); 8 | void *combine_atoms(char *name, int amount, ...); 9 | void *generate_sinf(int appid, char *person_name, int vendorID); 10 | void *generate_supp(uint32_t *suppsize); -------------------------------------------------------------------------------- /Clutch/scinfo.m: -------------------------------------------------------------------------------- 1 | /* 2 | Generates SC_Info keys (.sinf and .supp) 3 | see https://archive.fo/PNfJ5 4 | */ 5 | 6 | #import "scinfo.h" 7 | 8 | // create a SINF atom 9 | void *create_atom(char *name, int len, void *content) 10 | { 11 | uint32_t atomsize = len + 8; 12 | void *buf = malloc(atomsize); 13 | 14 | atomsize = CFSwapInt32(atomsize); 15 | 16 | memcpy(buf, &atomsize, 4); // copy atomsize 17 | memcpy(buf+4, name, 4); // copy atom name 18 | memcpy(buf+8, content, len); // copy atom content 19 | 20 | return buf; 21 | } 22 | 23 | void *coalesced_atom(int amount, uint32_t name, ...) 24 | { 25 | va_list vl; 26 | va_start(vl, name); 27 | uint32_t atomsize = 8 + amount * 8; // 8 bytes per field, 8 bytes for atom header 28 | 29 | amount = amount * 2; 30 | 31 | void *buf = malloc(atomsize); 32 | 33 | atomsize = CFSwapInt32(atomsize); 34 | 35 | memcpy(buf, &atomsize, 4); // copy atom size 36 | memcpy(buf + 4, &name, 4); // copy name 37 | 38 | uint32_t *curpos = (uint32_t *) buf + 2; 39 | 40 | for (int i=0;i107) 158 | { 159 | fakepriv[i] = 0; 160 | } 161 | else 162 | { 163 | fakepriv[i] = arc4random(); 164 | } 165 | } 166 | 167 | void *priv = create_atom("priv", 440, fakepriv); 168 | free(fakepriv); 169 | 170 | void *schi = combine_atoms("schi", 6, user, key, fakeiviv, fakerigh, fakename, priv); 171 | free(user); 172 | free(key); 173 | free(fakeiviv); 174 | free(fakerigh); 175 | free(fakename); 176 | free(priv); 177 | 178 | // sinf.frma = "game" 179 | void *frma = create_atom("frma", 4, "game"); 180 | 181 | // sinf.schm = "\x00000000itun\x00000000" 182 | void *schm = create_atom("schm", 12, "\x00\x00\x00\x00itun\x00\x00\x00\x00"); 183 | 184 | uint32_t *sign = malloc(128); 185 | 186 | for (int i=0;i<32;i++) 187 | { 188 | sign[i] = arc4random(); 189 | } 190 | 191 | void *fakesign = create_atom("sign", 128, sign); 192 | free(sign); 193 | 194 | void *sinf = combine_atoms("sinf", 4, frma, schm, schi, fakesign); 195 | free(frma); 196 | free(schm); 197 | free(schi); 198 | free(fakesign); 199 | 200 | return sinf; 201 | } 202 | -------------------------------------------------------------------------------- /Clutch/sha1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * sha1.c 3 | * 4 | * Description: 5 | * This file implements the Secure Hashing Algorithm 1 as 6 | * defined in FIPS PUB 180-1 published April 17, 1995. 7 | * 8 | * The SHA-1, produces a 160-bit message digest for a given 9 | * data stream. It should take about 2**n steps to find a 10 | * message with the same digest as a given message and 11 | * 2**(n/2) to find any two messages with the same digest, 12 | * when n is the digest size in bits. Therefore, this 13 | * algorithm can serve as a means of providing a 14 | * "fingerprint" for a message. 15 | * 16 | * Portability Issues: 17 | * SHA-1 is defined in terms of 32-bit "words". This code 18 | * uses (included via "sha1.h" to define 32 and 8 19 | * bit unsigned integer types. If your C compiler does not 20 | * support 32 bit unsigned integers, this code is not 21 | * appropriate. 22 | * 23 | * Caveats: 24 | * SHA-1 is designed to work with messages less than 2^64 bits 25 | * long. Although SHA-1 allows a message digest to be generated 26 | * for messages of any number of bits less than 2^64, this 27 | * implementation only works with messages with a length that is 28 | * a multiple of the size of an 8-bit character. 29 | * 30 | */ 31 | 32 | #include "sha1.h" 33 | 34 | /* 35 | * Define the SHA1 circular left shift macro 36 | */ 37 | #define SHA1CircularShift(bits,word) \ 38 | (((word) << (bits)) | ((word) >> (32-(bits)))) 39 | 40 | /* Local Function Prototyptes */ 41 | void SHA1PadMessage(SHA1Context *); 42 | void SHA1ProcessMessageBlock(SHA1Context *); 43 | 44 | /* 45 | * SHA1Reset 46 | * 47 | * Description: 48 | * This function will initialize the SHA1Context in preparation 49 | * for computing a new SHA1 message digest. 50 | * 51 | * Parameters: 52 | * context: [in/out] 53 | * The context to reset. 54 | * 55 | * Returns: 56 | * sha Error Code. 57 | * 58 | */ 59 | int SHA1Reset(SHA1Context *context) 60 | { 61 | if (!context) 62 | { 63 | return shaNull; 64 | } 65 | 66 | context->Length_Low = 0; 67 | context->Length_High = 0; 68 | context->Message_Block_Index = 0; 69 | 70 | context->Intermediate_Hash[0] = 0x67452301; 71 | context->Intermediate_Hash[1] = 0xEFCDAB89; 72 | context->Intermediate_Hash[2] = 0x98BADCFE; 73 | context->Intermediate_Hash[3] = 0x10325476; 74 | context->Intermediate_Hash[4] = 0xC3D2E1F0; 75 | 76 | context->Computed = 0; 77 | context->Corrupted = 0; 78 | 79 | return shaSuccess; 80 | } 81 | 82 | /* 83 | * SHA1Result 84 | * 85 | * Description: 86 | * This function will return the 160-bit message digest into the 87 | * Message_Digest array provided by the caller. 88 | * NOTE: The first octet of hash is stored in the 0th element, 89 | * the last octet of hash in the 19th element. 90 | * 91 | * Parameters: 92 | * context: [in/out] 93 | * The context to use to calculate the SHA-1 hash. 94 | * Message_Digest: [out] 95 | * Where the digest is returned. 96 | * 97 | * Returns: 98 | * sha Error Code. 99 | * 100 | */ 101 | int SHA1Result( SHA1Context *context, 102 | uint8_t Message_Digest[SHA1HashSize]) 103 | { 104 | int i; 105 | 106 | if (!context || !Message_Digest) 107 | { 108 | return shaNull; 109 | } 110 | 111 | if (context->Corrupted) 112 | { 113 | return context->Corrupted; 114 | } 115 | 116 | if (!context->Computed) 117 | { 118 | SHA1PadMessage(context); 119 | for(i=0; i<64; ++i) 120 | { 121 | /* message may be sensitive, clear it out */ 122 | context->Message_Block[i] = 0; 123 | } 124 | context->Length_Low = 0; /* and clear length */ 125 | context->Length_High = 0; 126 | context->Computed = 1; 127 | 128 | } 129 | 130 | for(i = 0; i < SHA1HashSize; ++i) 131 | { 132 | Message_Digest[i] = context->Intermediate_Hash[i>>2] 133 | >> 8 * ( 3 - ( i & 0x03 ) ); 134 | } 135 | 136 | return shaSuccess; 137 | } 138 | 139 | /* 140 | * SHA1Input 141 | * 142 | * Description: 143 | * This function accepts an array of octets as the next portion 144 | * of the message. 145 | * 146 | * Parameters: 147 | * context: [in/out] 148 | * The SHA context to update 149 | * message_array: [in] 150 | * An array of characters representing the next portion of 151 | * the message. 152 | * length: [in] 153 | * The length of the message in message_array 154 | * 155 | * Returns: 156 | * sha Error Code. 157 | * 158 | */ 159 | int SHA1Input( SHA1Context *context, 160 | const uint8_t *message_array, 161 | unsigned length) 162 | { 163 | if (!length) 164 | { 165 | return shaSuccess; 166 | } 167 | 168 | if (!context || !message_array) 169 | { 170 | return shaNull; 171 | } 172 | 173 | if (context->Computed) 174 | { 175 | context->Corrupted = shaStateError; 176 | 177 | return shaStateError; 178 | } 179 | 180 | if (context->Corrupted) 181 | { 182 | return context->Corrupted; 183 | } 184 | while(length-- && !context->Corrupted) 185 | { 186 | context->Message_Block[context->Message_Block_Index++] = 187 | (*message_array & 0xFF); 188 | 189 | context->Length_Low += 8; 190 | if (context->Length_Low == 0) 191 | { 192 | context->Length_High++; 193 | if (context->Length_High == 0) 194 | { 195 | /* Message is too long */ 196 | context->Corrupted = 1; 197 | } 198 | } 199 | 200 | if (context->Message_Block_Index == 64) 201 | { 202 | SHA1ProcessMessageBlock(context); 203 | } 204 | 205 | message_array++; 206 | } 207 | 208 | return shaSuccess; 209 | } 210 | 211 | /* 212 | * SHA1ProcessMessageBlock 213 | * 214 | * Description: 215 | * This function will process the next 512 bits of the message 216 | * stored in the Message_Block array. 217 | * 218 | * Parameters: 219 | * None. 220 | * 221 | * Returns: 222 | * Nothing. 223 | * 224 | * Comments: 225 | 226 | * Many of the variable names in this code, especially the 227 | * single character names, were used because those were the 228 | * names used in the publication. 229 | * 230 | * 231 | */ 232 | void SHA1ProcessMessageBlock(SHA1Context *context) 233 | { 234 | const uint32_t K[] = { /* Constants defined in SHA-1 */ 235 | 0x5A827999, 236 | 0x6ED9EBA1, 237 | 0x8F1BBCDC, 238 | 0xCA62C1D6 239 | }; 240 | int t; /* Loop counter */ 241 | uint32_t temp; /* Temporary word value */ 242 | uint32_t W[80]; /* Word sequence */ 243 | uint32_t A, B, C, D, E; /* Word buffers */ 244 | 245 | /* 246 | * Initialize the first 16 words in the array W 247 | */ 248 | for(t = 0; t < 16; t++) 249 | { 250 | W[t] = context->Message_Block[t * 4] << 24; 251 | W[t] |= context->Message_Block[t * 4 + 1] << 16; 252 | W[t] |= context->Message_Block[t * 4 + 2] << 8; 253 | W[t] |= context->Message_Block[t * 4 + 3]; 254 | } 255 | 256 | for(t = 16; t < 80; t++) 257 | { 258 | W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); 259 | } 260 | 261 | A = context->Intermediate_Hash[0]; 262 | B = context->Intermediate_Hash[1]; 263 | C = context->Intermediate_Hash[2]; 264 | D = context->Intermediate_Hash[3]; 265 | E = context->Intermediate_Hash[4]; 266 | 267 | for(t = 0; t < 20; t++) 268 | { 269 | temp = SHA1CircularShift(5,A) + 270 | ((B & C) | ((~B) & D)) + E + W[t] + K[0]; 271 | E = D; 272 | D = C; 273 | C = SHA1CircularShift(30,B); 274 | 275 | B = A; 276 | A = temp; 277 | } 278 | 279 | for(t = 20; t < 40; t++) 280 | { 281 | temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; 282 | E = D; 283 | D = C; 284 | C = SHA1CircularShift(30,B); 285 | B = A; 286 | A = temp; 287 | } 288 | 289 | for(t = 40; t < 60; t++) 290 | { 291 | temp = SHA1CircularShift(5,A) + 292 | ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; 293 | E = D; 294 | D = C; 295 | C = SHA1CircularShift(30,B); 296 | B = A; 297 | A = temp; 298 | } 299 | 300 | for(t = 60; t < 80; t++) 301 | { 302 | temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; 303 | E = D; 304 | D = C; 305 | C = SHA1CircularShift(30,B); 306 | B = A; 307 | A = temp; 308 | } 309 | 310 | context->Intermediate_Hash[0] += A; 311 | context->Intermediate_Hash[1] += B; 312 | context->Intermediate_Hash[2] += C; 313 | context->Intermediate_Hash[3] += D; 314 | context->Intermediate_Hash[4] += E; 315 | 316 | context->Message_Block_Index = 0; 317 | } 318 | 319 | /* 320 | * SHA1PadMessage 321 | * 322 | 323 | * Description: 324 | * According to the standard, the message must be padded to an even 325 | * 512 bits. The first padding bit must be a '1'. The last 64 326 | * bits represent the length of the original message. All bits in 327 | * between should be 0. This function will pad the message 328 | * according to those rules by filling the Message_Block array 329 | * accordingly. It will also call the ProcessMessageBlock function 330 | * provided appropriately. When it returns, it can be assumed that 331 | * the message digest has been computed. 332 | * 333 | * Parameters: 334 | * context: [in/out] 335 | * The context to pad 336 | * ProcessMessageBlock: [in] 337 | * The appropriate SHA*ProcessMessageBlock function 338 | * Returns: 339 | * Nothing. 340 | * 341 | */ 342 | 343 | void SHA1PadMessage(SHA1Context *context) 344 | { 345 | /* 346 | * Check to see if the current message block is too small to hold 347 | * the initial padding bits and length. If so, we will pad the 348 | * block, process it, and then continue padding into a second 349 | * block. 350 | */ 351 | if (context->Message_Block_Index > 55) 352 | { 353 | context->Message_Block[context->Message_Block_Index++] = 0x80; 354 | while(context->Message_Block_Index < 64) 355 | { 356 | context->Message_Block[context->Message_Block_Index++] = 0; 357 | } 358 | 359 | SHA1ProcessMessageBlock(context); 360 | 361 | while(context->Message_Block_Index < 56) 362 | { 363 | context->Message_Block[context->Message_Block_Index++] = 0; 364 | } 365 | } 366 | else 367 | { 368 | context->Message_Block[context->Message_Block_Index++] = 0x80; 369 | while(context->Message_Block_Index < 56) 370 | { 371 | 372 | context->Message_Block[context->Message_Block_Index++] = 0; 373 | } 374 | } 375 | 376 | /* 377 | * Store the message length as the last 8 octets 378 | */ 379 | context->Message_Block[56] = context->Length_High >> 24; 380 | context->Message_Block[57] = context->Length_High >> 16; 381 | context->Message_Block[58] = context->Length_High >> 8; 382 | context->Message_Block[59] = context->Length_High; 383 | context->Message_Block[60] = context->Length_Low >> 24; 384 | context->Message_Block[61] = context->Length_Low >> 16; 385 | context->Message_Block[62] = context->Length_Low >> 8; 386 | context->Message_Block[63] = context->Length_Low; 387 | 388 | SHA1ProcessMessageBlock(context); 389 | } 390 | -------------------------------------------------------------------------------- /Clutch/sha1.h: -------------------------------------------------------------------------------- 1 | /* 2 | * sha1.h 3 | * 4 | * Description: 5 | * This is the header file for code which implements the Secure 6 | * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published 7 | * April 17, 1995. 8 | * 9 | * Many of the variable names in this code, especially the 10 | * single character names, were used because those were the names 11 | * used in the publication. 12 | * 13 | * Please read the file sha1.c for more information. 14 | * 15 | */ 16 | 17 | #ifndef _SHA1_H_ 18 | #define _SHA1_H_ 19 | 20 | #include 21 | 22 | #ifndef _SHA_enum_ 23 | #define _SHA_enum_ 24 | enum 25 | { 26 | shaSuccess = 0, 27 | shaNull, /* Null pointer parameter */ 28 | shaInputTooLong, /* input data too long */ 29 | shaStateError /* called Input after Result */ 30 | }; 31 | #endif 32 | #define SHA1HashSize 20 33 | 34 | /* 35 | * This structure will hold context information for the SHA-1 36 | * hashing operation 37 | */ 38 | typedef struct SHA1Context 39 | { 40 | uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */ 41 | 42 | uint32_t Length_Low; /* Message length in bits */ 43 | uint32_t Length_High; /* Message length in bits */ 44 | 45 | /* Index into message block array */ 46 | int_least16_t Message_Block_Index; 47 | uint8_t Message_Block[64]; /* 512-bit message blocks */ 48 | 49 | int Computed; /* Is the digest computed? */ 50 | int Corrupted; /* Is the message digest corrupted? */ 51 | } SHA1Context; 52 | 53 | /* 54 | * Function Prototypes 55 | */ 56 | 57 | int SHA1Reset( SHA1Context *); 58 | int SHA1Input( SHA1Context *, 59 | const uint8_t *, 60 | unsigned int); 61 | int SHA1Result( SHA1Context *, 62 | uint8_t Message_Digest[SHA1HashSize]); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /LSApplicationProxy.h: -------------------------------------------------------------------------------- 1 | @interface LSApplicationProxy : NSObject { 2 | NSArray *_UIBackgroundModes; 3 | NSArray *_VPNPlugins; 4 | NSArray *_appTags; 5 | NSString *_applicationDSID; 6 | NSArray *_audioComponents; 7 | NSArray *_deviceFamily; 8 | NSUUID *_deviceIdentifierForVendor; 9 | NSArray *_directionsModes; 10 | NSNumber *_dynamicDiskUsage; 11 | NSArray *_externalAccessoryProtocols; 12 | unsigned int _flags; 13 | NSDictionary *_groupContainers; 14 | NSArray *_groupIdentifiers; 15 | unsigned int _installType; 16 | BOOL _isContainerized; 17 | NSNumber *_itemID; 18 | NSString *_itemName; 19 | NSString *_minimumSystemVersion; 20 | long _modTime; 21 | unsigned int _originalInstallType; 22 | NSArray *_plugInKitPlugins; 23 | NSArray *_pluginUUIDs; 24 | NSArray *_privateDocumentIconNames; 25 | LSApplicationProxy *_privateDocumentTypeOwner; 26 | NSArray *_requiredDeviceCapabilities; 27 | NSString *_sdkVersion; 28 | NSString *_shortVersionString; 29 | NSString *_sourceAppIdentifier; 30 | NSNumber *_staticDiskUsage; 31 | NSString *_storeCohortMetadata; 32 | NSNumber *_storeFront; 33 | NSString *_teamID; 34 | NSString *_vendorName; 35 | } 36 | 37 | @property (nonatomic, readonly) NSArray *UIBackgroundModes; 38 | @property (nonatomic, readonly) NSArray *VPNPlugins; 39 | @property (nonatomic, readonly) NSArray *appTags; 40 | @property (nonatomic, readonly) NSString *applicationDSID; 41 | @property (nonatomic, readonly) NSString *applicationIdentifier; 42 | @property (nonatomic, readonly) NSString *applicationType; 43 | @property (nonatomic, readonly) NSArray *audioComponents; 44 | @property (nonatomic, readonly) NSString *bundleVersion; 45 | @property (nonatomic, readonly) NSArray *deviceFamily; 46 | @property (nonatomic, readonly) NSUUID *deviceIdentifierForVendor; 47 | @property (nonatomic, readonly) NSArray *directionsModes; 48 | @property (nonatomic, readonly) NSNumber *dynamicDiskUsage; 49 | @property (nonatomic, readonly) NSArray *externalAccessoryProtocols; 50 | @property (nonatomic, readonly) BOOL fileSharingEnabled; 51 | @property (nonatomic, readonly) NSDictionary *groupContainers; 52 | @property (nonatomic, readonly) NSArray *groupIdentifiers; 53 | @property (nonatomic, readonly) BOOL hasSettingsBundle; 54 | @property (nonatomic, readonly) BOOL iconIsPrerendered; 55 | @property (nonatomic, readonly) NSProgress *installProgress; 56 | @property (nonatomic, readonly) unsigned int installType; 57 | @property (nonatomic, readonly) BOOL isAppUpdate; 58 | @property (nonatomic, readonly) BOOL isBetaApp; 59 | @property (nonatomic, readonly) BOOL isContainerized; 60 | @property (nonatomic, readonly) BOOL isInstalled; 61 | @property (nonatomic, readonly) BOOL isNewsstandApp; 62 | @property (nonatomic, readonly) BOOL isPlaceholder; 63 | @property (nonatomic, readonly) BOOL isPurchasedReDownload; 64 | @property (nonatomic, readonly) BOOL isRestricted; 65 | @property (nonatomic, readonly) BOOL isWatchKitApp; 66 | @property (nonatomic, readonly) NSNumber *itemID; 67 | @property (nonatomic, readonly) NSString *itemName; 68 | @property (nonatomic, readonly) NSString *minimumSystemVersion; 69 | @property (nonatomic, readonly) unsigned int originalInstallType; 70 | @property (nonatomic, readonly) NSArray *plugInKitPlugins; 71 | @property (nonatomic, readonly) BOOL profileValidated; 72 | @property (nonatomic, readonly) NSArray *requiredDeviceCapabilities; 73 | @property (nonatomic, readonly) NSString *roleIdentifier; 74 | @property (nonatomic, readonly) NSString *sdkVersion; 75 | @property (nonatomic, readonly) NSString *shortVersionString; 76 | @property (nonatomic, readonly) NSString *sourceAppIdentifier; 77 | @property (nonatomic, readonly) NSNumber *staticDiskUsage; 78 | @property (nonatomic, readonly) NSString *storeCohortMetadata; 79 | @property (nonatomic, readonly) NSNumber *storeFront; 80 | @property (nonatomic, readonly) BOOL supportsAudiobooks; 81 | @property (nonatomic, readonly) BOOL supportsExternallyPlayableContent; 82 | @property (nonatomic, readonly) NSString *teamID; 83 | @property (nonatomic, readonly) NSString *vendorName; 84 | 85 | // Image: /System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices 86 | 87 | + (id)applicationProxyForBundleURL:(id)arg1; 88 | + (id)applicationProxyForIdentifier:(id)arg1; 89 | + (id)applicationProxyForIdentifier:(id)arg1 placeholder:(BOOL)arg2; 90 | + (id)applicationProxyForIdentifier:(id)arg1 roleIdentifier:(id)arg2; 91 | + (id)applicationProxyForItemID:(id)arg1; 92 | + (id)applicationProxyWithBundleUnitID:(unsigned long)arg1; 93 | + (BOOL)supportsSecureCoding; 94 | 95 | - (id)UIBackgroundModes; 96 | - (id)VPNPlugins; 97 | - (id)_initWithBundleUnit:(unsigned long)arg1 applicationIdentifier:(id)arg2; 98 | - (id)appStoreReceiptURL; 99 | - (id)appTags; 100 | - (id)applicationDSID; 101 | - (id)applicationIdentifier; 102 | - (id)applicationType; 103 | - (id)audioComponents; 104 | - (long)bundleModTime; 105 | - (void)dealloc; 106 | - (id)description; 107 | - (id)deviceFamily; 108 | - (id)deviceIdentifierForVendor; 109 | - (id)directionsModes; 110 | - (id)dynamicDiskUsage; 111 | - (void)encodeWithCoder:(id)arg1; 112 | - (id)externalAccessoryProtocols; 113 | - (BOOL)fileSharingEnabled; 114 | - (id)groupContainers; 115 | - (id)groupIdentifiers; 116 | - (BOOL)hasSettingsBundle; 117 | - (unsigned int)hash; 118 | - (id)iconDataForVariant:(int)arg1; 119 | - (BOOL)iconIsPrerendered; 120 | - (id)iconStyleDomain; 121 | - (id)initWithCoder:(id)arg1; 122 | - (id)installProgress; 123 | - (id)installProgressSync; 124 | - (unsigned int)installType; 125 | - (BOOL)isAppUpdate; 126 | - (BOOL)isBetaApp; 127 | - (BOOL)isContainerized; 128 | - (BOOL)isEqual:(id)arg1; 129 | - (BOOL)isInstalled; 130 | - (BOOL)isNewsstandApp; 131 | - (BOOL)isPlaceholder; 132 | - (BOOL)isPurchasedReDownload; 133 | - (BOOL)isRestricted; 134 | - (BOOL)isWatchKitApp; 135 | - (id)itemID; 136 | - (id)itemName; 137 | - (id)localizedName; 138 | - (id)localizedShortName; 139 | - (id)machOUUIDs; 140 | - (id)minimumSystemVersion; 141 | - (unsigned int)originalInstallType; 142 | - (id)plugInKitPlugins; 143 | - (void)populateNotificationData; 144 | - (BOOL)privateDocumentIconAllowOverride; 145 | - (id)privateDocumentIconNames; 146 | - (id)privateDocumentTypeOwner; 147 | - (BOOL)profileValidated; 148 | - (id)requiredDeviceCapabilities; 149 | - (id)resourcesDirectoryURL; 150 | - (id)roleIdentifier; 151 | - (id)sdkVersion; 152 | - (void)setPrivateDocumentIconAllowOverride:(BOOL)arg1; 153 | - (void)setPrivateDocumentIconNames:(id)arg1; 154 | - (void)setPrivateDocumentTypeOwner:(id)arg1; 155 | - (id)shortVersionString; 156 | - (id)sourceAppIdentifier; 157 | - (id)staticDiskUsage; 158 | - (id)storeCohortMetadata; 159 | - (id)storeFront; 160 | - (BOOL)supportsAudiobooks; 161 | - (BOOL)supportsExternallyPlayableContent; 162 | - (id)teamID; 163 | - (id)userActivityStringForAdvertisementData:(id)arg1; 164 | - (id)vendorName; 165 | 166 | @end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clutch 2 | 3 | *Clutch* is a high-speed iOS decryption tool. Clutch supports the iPhone, iPod Touch, and iPad as well as all iOS version, architecture types, and most binaries. **Clutch is meant only for educational purposes and security research.** 4 | 5 | Clutch requires a jailbroken iOS device with version 8.0 or greater. 6 | 7 | # Usage 8 | 9 | ``` 10 | Clutch [OPTIONS] 11 | -b --binary-dump Only dump binary files from specified bundleID 12 | -d --dump Dump specified bundleID into .ipa file 13 | -i --print-installed Print installed application 14 | --clean Clean /var/tmp/clutch directory 15 | --version Display version and exit 16 | -? --help Display this help and exit 17 | ``` 18 | 19 | Clutch may encounter `Segmentation Fault: 11` when dumping apps with a large number of frameworks. Increase your device's maximum number of open file descriptors with `ulimit -n 512` (default is 256). 20 | 21 | 22 | # Building 23 | 24 | ## Requirements 25 | 26 | * Xcode (install from [App Store](https://itunes.apple.com/us/app/xcode/id497799835?mt=12) or from [Apple's developer site](http://adcdownload.apple.com/Developer_Tools/Xcode_8.2.1/Xcode_8.2.1.xip)) 27 | * Xcode command line tools: `xcode-select --install` (or from [Apple's developer site](http://adcdownload.apple.com/Developer_Tools/Command_Line_Tools_macOS_10.12_for_Xcode_8.2/Command_Line_Tools_macOS_10.12_for_Xcode_8.2.dmg)) 28 | 29 | ## Disable SDK code signing requirement 30 | 31 | ```sh 32 | killall Xcode 33 | cp /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/SDKSettings.plist ~/ 34 | /usr/libexec/PlistBuddy -c "Set :DefaultProperties:CODE_SIGNING_REQUIRED NO" /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/SDKSettings.plist 35 | /usr/libexec/PlistBuddy -c "Set :DefaultProperties:AD_HOC_CODE_SIGNING_ALLOWED YES" /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/SDKSettings.plist 36 | ``` 37 | 38 | Note that if you update Xcode you may need to run these commands again. 39 | 40 | ## Compiling 41 | 42 | ```sh 43 | xcodebuild clean build 44 | ``` 45 | 46 | ## Installation 47 | 48 | After building, a copy of the binary named `Clutch` is placed in the build directory. Copy this to your device: 49 | 50 | ```sh 51 | scp ./build/Clutch root@:/usr/bin/Clutch 52 | ``` 53 | 54 | If you are using [iproxy](http://iphonedevwiki.net/index.php/SSH_Over_USB), use this line (replace `2222` with a different port if necessary): 55 | 56 | ```sh 57 | scp -P 2222 ./build/Clutch root@localhost:/usr/bin/Clutch 58 | ``` 59 | 60 | When you SSH into your device, run `Clutch`. 61 | 62 | # Licenses 63 | 64 | Clutch uses the following libraries under their respective licenses. 65 | 66 | * [optool](https://github.com/alexzielenski/optool) by Alex Zielenski 67 | * [ZipArchive](https://github.com/mattconnolly/ZipArchive/) by Matt Connolly, Edward Patel, et al. 68 | * [MiniZip](http://www.winimage.com/zLibDll/minizip.html) by Gilles Vollant and Mathias Svensson. 69 | 70 | # Thanks 71 | 72 | Clutch would not be what it is without these people: 73 | 74 | * dissident - The original creator of Clutch (pre 1.2.6) 75 | * Nighthawk - Code contributor (pre 1.2.6) 76 | * Rastignac - Inspiration and genius 77 | * TheSexyPenguin - Inspiration 78 | 79 | # Contributors 80 | 81 | * [iT0ny](https://github.com/iT0ny) 82 | * [ttwj](https://github.com/ttwj) 83 | * [NinjaLikesCheez](https://github.com/NinjaLikesCheez) 84 | * [Tatsh](https://github.com/Tatsh) 85 | * [C0deH4cker](https://github.com/C0deH4cker) 86 | * [DoubleDoughnut](https://github.com/DoubleDoughnut) 87 | * [iD70my](https://github.com/iD70my) 88 | * [OdNairy](https://github.com/OdNairy) 89 | 90 | # Copyright 91 | 92 | © [Kim Jong-Cracks](http://cracksby.kim) 1819-2017 93 | --------------------------------------------------------------------------------