├── .DS_Store ├── .gitignore ├── .gitmodules ├── external ├── sha1.c └── sha1.h ├── include ├── Application.h ├── Cracker.h ├── Packager.h ├── applist.h ├── dump.h └── out.h ├── src ├── Application.mm ├── Cracker.mm ├── Packager.mm ├── applist.mm ├── dump.m ├── main.mm └── out.mm └── xcode ├── .idea ├── .name ├── dictionaries │ └── dildog.xml ├── encodings.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── scopes │ └── scope_settings.xml ├── vcs.xml ├── workspace.xml ├── xcode.iml └── xcode.xml ├── Clutch.xcodeproj └── project.pbxproj ├── Clutch ├── Package │ ├── DEBIAN │ │ ├── control │ │ └── control.txt │ └── usr │ │ └── bin │ │ └── 0xdeadfa11 └── PackageVersion.plist └── LatestBuild /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KJCracks/Clutch2/ee1ae239dd8ed2a6ba49a86c1950d75d3944054a/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Xcode ## 2 | ########### 3 | 4 | codesign.sh 5 | 6 | build/* 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | *.xcworkspace 16 | !default.xcworkspace 17 | xcuserdata 18 | profile 19 | *.moved-aside 20 | DerivedData 21 | 22 | # OS files # 23 | ############ 24 | .DS_Store 25 | .DS_Store? 26 | ._* 27 | .Spotlight-V100 28 | .Trashes 29 | ehthumbs.db 30 | Thumbs.db 31 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Libraries/ZipArchive"] 2 | path = Libraries/ZipArchive 3 | url = https://github.com/mattconnolly/ZipArchive.git 4 | -------------------------------------------------------------------------------- /external/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 | -------------------------------------------------------------------------------- /external/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 | -------------------------------------------------------------------------------- /include/Application.h: -------------------------------------------------------------------------------- 1 | // 2 | // Application.h 3 | // Clutch 4 | // 5 | // Created by Ninja on 03/01/2014. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface Application : NSObject 12 | { 13 | NSString *baseDirectory; // Full Path 14 | NSString *directory; // Name of application directory (after UUID bit) 15 | NSString *displayName; // Display name (found in Info.plist) 16 | NSString *binary; // The binary name (CFBundleExectuable) 17 | NSString *baseName; // Name of the application folder 18 | NSString *UUID; // The UUID Apple generated for application 19 | NSString *version; // Version of application (found in Info.plist) 20 | NSString *identifier; // The reverse-notion identifier 21 | NSDictionary *infoPlist; // Parsed Info.plist 22 | NSString *binaryPath; // Full path to binary 23 | 24 | } 25 | 26 | @property (nonatomic, retain) NSString *baseDirectory; 27 | @property (nonatomic, retain) NSString *directory; 28 | @property (nonatomic, retain) NSString *displayName; 29 | @property (nonatomic, retain) NSString *binary; 30 | @property (nonatomic, retain) NSString *baseName; 31 | @property (nonatomic, retain) NSString *UUID; 32 | @property (nonatomic, retain) NSString *version; 33 | @property (nonatomic, retain) NSString *identifier; 34 | @property (nonatomic, retain) NSDictionary *infoPlist; 35 | @property (nonatomic, retain) NSString *binaryPath; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /include/Cracker.h: -------------------------------------------------------------------------------- 1 | // 2 | // Cracker.h 3 | // Clutch 4 | // 5 | // Created by DilDog on 12/22/13. 6 | // 7 | // 8 | 9 | #import 10 | #import "Application.h" 11 | 12 | @interface Cracker : NSObject 13 | { 14 | NSString *_appDescription; 15 | NSString *_finaldir; 16 | NSString *_baselinedir; 17 | NSString *_workingdir; 18 | 19 | NSString *workingDirectory; 20 | NSMutableArray *headersToStrip; 21 | NSString *sinfPath; 22 | NSString *suppPath; 23 | NSString *supfPath; 24 | } 25 | 26 | -(id)init; 27 | -(BOOL)createFullCopyOfContents:(NSString *)outdir withAppBaseDir:(NSString *)appdir; 28 | -(BOOL)createPartialCopy:(NSString *)outdir withApplicationDir:(NSString *)appdir withMainExecutable:(NSString *)mainexe; 29 | -(BOOL)prepareFromInstalledApp:(NSDictionary *)appdict; 30 | -(BOOL)prepareFromSpecificExecutable:(NSString *)exepath returnDescription:(NSMutableString *)description; 31 | -(NSString *)getAppDescription; 32 | -(NSString *)getOutputFolder; 33 | 34 | // Objective-C method declarations 35 | - (BOOL)crackApplication:(Application *)application; // Cracks the application 36 | - (BOOL)preflightBinaryOfApplication:(Application *)application; // Does some preflight checks on the binary of given application 37 | - (BOOL)crackBinary:(Application *)application; // Cracks the binary 38 | - (BOOL)createWorkingDirectory; // Create the working directory for cracking & sets the path to (NSString *)workingDirectory 39 | 40 | - (BOOL)createCopyOfDirectory:(NSString *)applicationDirectory; // Create copy of all application files to /tmp/{UUID} 41 | - (BOOL)createCopyOfBinary:(NSString *)binaryPath; // Create copy of application binary to /tmp/{UUID} 42 | - (BOOL)removeTempFiles; 43 | 44 | // C method declarations 45 | void get_local_device_information(); 46 | 47 | // Properties 48 | @property (nonatomic, strong) NSString *workingDirectory; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /include/Packager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Packager.h 3 | // Clutch 4 | // 5 | // Created by DilDog on 12/22/13. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface Packager : NSObject 12 | { 13 | NSString *_outputPath; 14 | } 15 | 16 | 17 | -(id)init; 18 | -(void)dealloc; 19 | -(NSString *)getOutputPath; 20 | -(BOOL)packFromSource:(NSString *)inputpath withOverlay:(NSString *)overlaypath; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /include/applist.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface applist : NSObject 4 | 5 | + (NSArray *)listApplications; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /include/dump.h: -------------------------------------------------------------------------------- 1 | 2 | #import "sha1.h" 3 | #import "Cracker.h" 4 | #import 5 | #import 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define OVERDRIVE_DYLIB_PATH "@executable_path/overdrive.dylib" 12 | #define OVERDRIVE_DYLIB_CURRENT_VER 0x20000 13 | #define OVERDRIVE_DYLIB_COMPATIBILITY_VERSION 0x20000 14 | 15 | /*#define LC_CODE_SIGNATURE 0x1d 16 | #define LC_ENCRYPTION_INFO 0x21 17 | #define LC_SEGMENT 0x1 18 | 19 | #define MH_PIE 0x200000*/ 20 | 21 | #define CSSLOT_CODEDIRECTORY 0 22 | 23 | #define PT_TRACE_ME 0 24 | 25 | 26 | void sha1(uint8_t *hash, uint8_t *data, unsigned int size); 27 | 28 | #if defined __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | BOOL dump_binary_64(FILE *origin, FILE *target, uint32_t top, NSString *originPath, NSString *finalPath); 33 | BOOL dump_binary_32(FILE *origin, FILE *target, uint32_t top, NSString *originPath, NSString *finalPath); 34 | 35 | #if defined __cplusplus 36 | }; 37 | #endif 38 | 39 | 40 | typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data); 41 | 42 | 43 | struct BlobIndex { 44 | uint32_t type; 45 | uint32_t offset; 46 | }; 47 | 48 | struct Blob { 49 | uint32_t magic; 50 | uint32_t length; 51 | }; 52 | 53 | struct SuperBlob { 54 | struct Blob blob; 55 | uint32_t count; 56 | struct BlobIndex index[]; 57 | }; 58 | 59 | struct CodeDirectory { 60 | struct Blob blob; 61 | uint32_t version; 62 | uint32_t flags; 63 | uint32_t hashOffset; 64 | uint32_t identOffset; 65 | uint32_t nSpecialSlots; 66 | uint32_t nCodeSlots; 67 | uint32_t codeLimit; 68 | uint8_t hashSize; 69 | uint8_t hashType; 70 | uint8_t spare1; 71 | uint8_t pageSize; 72 | uint32_t spare2; 73 | }; -------------------------------------------------------------------------------- /include/out.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #define CLUTCH_DEBUG // flag to enable debug logging 4 | 5 | #define FILE_NAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) // shortened path of __FILE__ is there is one 6 | 7 | #ifdef CLUTCH_DEBUG 8 | # define DEV(M, ...) fprintf(stderr, "\033[0;32mDEBUG\033[0m | %s\t|\t" M "\n", FILE_NAME, [[NSString stringWithFormat:M, ##__VA_ARGS__] UTF8String]); 9 | # define NSLog(M, ...) fprintf(stderr, "\033[0;32mDEBUG\033[0m | %s:%d\t|\t%s\n", FILE_NAME, __LINE__, [[NSString stringWithFormat:M, ##__VA_ARGS__] UTF8String]); 10 | #else 11 | # define DEBUG(M, ...) 12 | # define NSLog(...) 13 | #endif 14 | 15 | #define ERROR(M, ...) fprintf(stderr, "\033[1;31mERROR\033[0m | %s:%d\t|\t%s\n", FILE_NAME, __LINE__, [[NSString stringWithFormat:M, ##__VA_ARGS__] UTF8String]); // prints error 16 | #define VERBOSE(M, ...) fprintf(stderr, "%s\n", [[NSString stringWithFormat:M, ##__VA_ARGS__] UTF8String]); // prints verbose 17 | #define PERCENT(x) progress_percent(x); 18 | 19 | #if defined __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | int determine_screen_width(); 24 | void progress_percent(int percent); 25 | void print_bar(); 26 | void stop_bar(); 27 | void pause_bar(); 28 | 29 | #if defined __cplusplus 30 | }; 31 | #endif 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Application.mm: -------------------------------------------------------------------------------- 1 | // 2 | // Application.m 3 | // Clutch 4 | // 5 | // Created by Ninja on 03/01/2014. 6 | // 7 | // 8 | 9 | #import "Application.h" 10 | 11 | @implementation Application 12 | 13 | 14 | - (NSString *)description 15 | { 16 | return [NSString stringWithFormat: @"{ Application: BaseDirectory = %@\nDirectory = %@\nDisplayName = %@\nBinary = %@\nBaseName = %@\nUUID = %@\nVersion = %@\nIdentifier = %@\nInfoPlist = %@", baseDirectory, directory, displayName, binary, baseDirectory, UUID, version, identifier, infoPlist]; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /src/Cracker.mm: -------------------------------------------------------------------------------- 1 | // 2 | // Cracker.m 3 | // Clutch 4 | // 5 | // Created by DilDog on 12/22/13. 6 | // 7 | // 8 | 9 | /* 10 | * Includes 11 | */ 12 | #import "Cracker.h" 13 | #import "Application.h" 14 | #import "out.h" 15 | #import "dump.h" 16 | 17 | #import 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | //#define FAT_CIGAM 0xbebafeca 29 | //#define MH_MAGIC 0xfeedface 30 | 31 | #define ARMV7 9 32 | #define ARMV7S 11 33 | #define ARM64 16777228 34 | 35 | #define ARMV7_SUBTYPE 0x9000000 36 | #define ARMV7S_SUBTYPE 0xb000000 37 | #define ARM64_SUBTYPE 0x1000000 38 | 39 | #define CPUTYPE_32 0xc000000 40 | #define CPUTYPE_64 0xc000001 41 | 42 | char header_buffer[4096]; 43 | uint32_t local_cputype; 44 | uint32_t local_cpusubtype; 45 | int overdrive_enabled; 46 | 47 | @implementation Cracker 48 | 49 | - (id)init 50 | { 51 | self = [super init]; 52 | if (self) 53 | { 54 | _appDescription = NULL; 55 | _finaldir = NULL; 56 | _baselinedir = NULL; 57 | _workingdir = NULL; 58 | get_local_device_information(); 59 | } 60 | return self; 61 | } 62 | 63 | -(void)dealloc 64 | { 65 | if(_appDescription) 66 | { 67 | [_appDescription release]; 68 | } 69 | if(_baselinedir) 70 | { 71 | [_baselinedir release]; 72 | } 73 | if(_finaldir) 74 | { 75 | [_finaldir release]; 76 | } 77 | if(_workingdir) 78 | { 79 | [_workingdir release]; 80 | } 81 | 82 | [super dealloc]; 83 | } 84 | 85 | void get_local_device_information() 86 | { 87 | host_basic_info_data_t hostinfo; 88 | mach_msg_type_number_t infocount; 89 | 90 | infocount = HOST_BASIC_INFO_COUNT; 91 | host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostinfo, &infocount); 92 | 93 | local_cputype = hostinfo.cpu_type; 94 | #ifdef __LP64__ 95 | local_cpusubtype = 0; // for some reason this is 1 if using hostinfo.cpu_subtype 96 | #else 97 | local_cpusubtype = hostinfo.cpu_subtype; 98 | #endif 99 | 100 | NSLog(@"Local CPUTYPE: %u", local_cputype); 101 | NSLog(@"Local CPUTSUBTYPE: %u",local_cpusubtype); 102 | NSLog(@"Endianess: %ld", CFByteOrderGetCurrent()); 103 | } 104 | 105 | 106 | static BOOL forceRemoveDirectory(NSString *dirpath) 107 | { 108 | BOOL isDir; 109 | NSFileManager *fileManager=[NSFileManager defaultManager]; 110 | if(![fileManager fileExistsAtPath:dirpath isDirectory:&isDir]) 111 | { 112 | if(![fileManager removeItemAtPath:dirpath error:NULL]) 113 | { 114 | ERROR(@"Failed to force remove directory."); 115 | return NO; 116 | } 117 | } 118 | 119 | return YES; 120 | } 121 | 122 | static BOOL forceCreateDirectory(NSString *dirpath) 123 | { 124 | BOOL isDir; 125 | NSFileManager *fileManager= [NSFileManager defaultManager]; 126 | if(![fileManager fileExistsAtPath:dirpath isDirectory:&isDir]) 127 | { 128 | if(![fileManager removeItemAtPath:dirpath error:NULL]) 129 | { 130 | ERROR(@"Failed to remove item at path: %@", dirpath); 131 | return NO; 132 | } 133 | } 134 | if(![fileManager createDirectoryAtPath:dirpath withIntermediateDirectories:YES attributes:nil error:NULL]) 135 | { 136 | ERROR(@"Failed to create directory at path: %@", dirpath); 137 | return NO; 138 | } 139 | 140 | return YES; 141 | } 142 | 143 | static BOOL copyFile(NSString *infile, NSString *outfile) 144 | { 145 | NSError *error; 146 | NSFileManager *fileManager= [NSFileManager defaultManager]; 147 | if(![fileManager createDirectoryAtPath:[outfile stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:NULL]) 148 | { 149 | ERROR(@"Failed to create directory at path: %@", [outfile stringByDeletingLastPathComponent]); 150 | return NO; 151 | } 152 | 153 | if ([fileManager fileExistsAtPath:outfile]) 154 | { 155 | [fileManager removeItemAtPath:outfile error:nil]; 156 | } 157 | 158 | if(![fileManager copyItemAtPath:infile toPath:outfile error:&error]) 159 | { 160 | ERROR(@"Failed to copy item: %@ to %@", infile, outfile); 161 | NSLog(@"Copy file error: %@", error.localizedDescription); 162 | return NO; 163 | } 164 | 165 | return YES; 166 | } 167 | 168 | // createPartialCopy 169 | // copies only the files required for cracking an application to a staging area 170 | 171 | -(BOOL)createPartialCopy:(NSString *)outdir withApplicationDir:(NSString *)appdir withMainExecutable:(NSString *)mainexe 172 | { 173 | // Create output directory 174 | if(!forceCreateDirectory(outdir)) 175 | { 176 | return NO; 177 | } 178 | 179 | // XXX: This, only if necessary: Get sandbox folder 180 | //NSString *topleveldir=[appdir stringByDeletingLastPathComponent]; 181 | //NSString *appdirprefix=[appdir lastPathComponent]; 182 | 183 | // Get top level .app folder 184 | NSString *topleveldir=[appdir copy]; 185 | 186 | // Files required for cracking 187 | NSMutableArray *files=[[NSMutableArray alloc] init]; 188 | [files addObject:@"_CodeSignature/CodeResources"]; 189 | [files addObject:[NSString stringWithFormat:@"SC_Info/%@.sinf", mainexe]]; 190 | [files addObject:[NSString stringWithFormat:@"SC_Info/%@.supp", mainexe]]; 191 | [files addObject:mainexe]; 192 | //XXX:[files addObject:[NSString stringWithFormat:@"%@/_CodeSignature/CodeResources", appdirprefix]]; 193 | //XXX:[files addObject:[NSString stringWithFormat:@"%@/SC_Info/%@.sinf", appdirprefix, mainexe]]; 194 | //XXX:[files addObject:[NSString stringWithFormat:@"%@/SC_Info/%@.supp", appdirprefix, mainexe]]; 195 | //XXX:[files addObject:[NSString stringWithFormat:@"%@/%@", appdirprefix, mainexe]]; 196 | //XXX:[files addObject:[NSString stringWithFormat:@"%@/Info.plist", appdirprefix]; 197 | //XXX:[files addObject:@"iTunesMetadata.plist"]; 198 | //XXX:[files addObject:@"iTunesArtwork"]; 199 | 200 | NSEnumerator *e = [files objectEnumerator]; 201 | NSString *file; 202 | while(file = [e nextObject]) 203 | { 204 | if(!copyFile([NSString stringWithFormat:@"%@/%@", topleveldir, file], 205 | [NSString stringWithFormat:@"%@/%@", outdir, file])) 206 | { 207 | forceRemoveDirectory(outdir); 208 | 209 | [topleveldir release]; 210 | [files release]; 211 | 212 | return NO; 213 | } 214 | } 215 | 216 | [topleveldir release]; 217 | [files release]; 218 | 219 | return YES; 220 | } 221 | 222 | // prepareFromInstalledApp 223 | // set up application cracking from an installed application 224 | 225 | -(BOOL)prepareFromInstalledApp:(NSDictionary *)appdict 226 | { 227 | // Create the app description 228 | _appDescription=[NSString stringWithFormat:@"%@: %@ (%@)", 229 | [appdict objectForKey:@"ApplicationIdentifier"], 230 | [appdict objectForKey:@"ApplicationDisplayName"], 231 | [appdict objectForKey:@"ApplicationVersion"]]; 232 | 233 | // Create full copy of application which we will modify to our needs 234 | // to form final IPA file 235 | NSUUID *finaluuid=[[NSUUID alloc] init]; 236 | _finaldir=[NSString stringWithFormat:@"%@/%@/Payload", 237 | NSTemporaryDirectory(), 238 | [finaluuid UUIDString]]; 239 | if(![self createFullCopyOfContents: _finaldir withAppBaseDir:[appdict objectForKey:@"ApplicationBaseDirectory"]]) 240 | { 241 | [_finaldir release]; 242 | _finaldir=NULL; 243 | [finaluuid release]; 244 | return NO; 245 | } 246 | 247 | [finaluuid release]; 248 | 249 | 250 | // Create executable baseline copy from which lipo copies are formed 251 | NSUUID *baselineuuid=[[NSUUID alloc] init]; 252 | _baselinedir=[NSString stringWithFormat:@"%@/%@", 253 | NSTemporaryDirectory(), 254 | [baselineuuid UUIDString]]; 255 | if(![self createPartialCopy: _baselinedir 256 | withApplicationDir:[appdict objectForKey:@"ApplicationDirectory"] 257 | withMainExecutable:[appdict objectForKey:@"ApplicationName"]]) 258 | { 259 | [_baselinedir release]; 260 | _baselinedir=NULL; 261 | [baselineuuid release]; 262 | return NO; 263 | } 264 | 265 | /* 266 | // Create working directory copy 267 | NSUUID *workinguuid=[[NSUUID alloc] init]; 268 | _workingdir=[NSString stringWithFormat:@"%@/%@", 269 | NSTemporaryDirectory(), 270 | [workinguuid UUIDString]]; 271 | if(![self createPartialCopy:_workingdir 272 | withApplicationDir:[appdict objectForKey:@"ApplicationDirectory"] 273 | withMainExecutable:[appdict objectForKey:@"ApplicationName"]]) 274 | { 275 | [[NSFileManager defaultManager] removeItemAtPath:_baselinedir error:nil]; 276 | 277 | [_baselinedir release]; 278 | _baselinedir=NULL; 279 | [_workingdir release]; 280 | _workingdir=NULL; 281 | [baselineuuid release]; 282 | [workinguuid release]; 283 | return NO; 284 | } 285 | 286 | [workinguuid release]; 287 | */ 288 | // Clean up 289 | [baselineuuid release]; 290 | 291 | return YES; 292 | } 293 | 294 | -(BOOL)prepareFromSpecificExecutable:(NSString *)exepath returnDescription:(NSMutableString *)description 295 | { 296 | // Create the app description 297 | _appDescription=[NSString stringWithFormat:@"%@",exepath]; 298 | 299 | return YES; 300 | } 301 | 302 | -(NSString *)getAppDescription 303 | { 304 | return _appDescription; 305 | } 306 | 307 | -(NSString *)getOutputFolder 308 | { 309 | return _finaldir; 310 | } 311 | 312 | - (BOOL)preflightBinaryOfApplication:(Application *)application 313 | { 314 | VERBOSE(@"Performing cracking preflight..."); 315 | 316 | NSString *binaryPath = application.binaryPath; 317 | NSString *finalBinaryPath = [workingDirectory stringByAppendingFormat:@"Payload/%@/%@", application.baseName, application.binary]; 318 | 319 | // We do this to hide that the application was modified incase anyone is watching 🙈 320 | struct stat binary_stat; 321 | stat([binaryPath UTF8String], &binary_stat); 322 | 323 | time_t binary_stat_atime = binary_stat.st_atime; 324 | time_t binary_stat_mtime = binary_stat.st_mtime; 325 | 326 | if (![self crackBinary:application]) 327 | { 328 | return NO; 329 | } 330 | 331 | struct utimbuf old_time; 332 | old_time.actime = binary_stat_atime; 333 | old_time.modtime = binary_stat_mtime; 334 | 335 | utime([binaryPath UTF8String], &old_time); 336 | utime([finalBinaryPath UTF8String], &old_time); 337 | 338 | return YES; 339 | 340 | } 341 | 342 | - (BOOL)crackBinary:(Application *)application 343 | { 344 | VERBOSE(@"Cracking..."); 345 | 346 | NSString *finalBinaryPath = [workingDirectory stringByAppendingFormat:@"Payload/%@/%@", application.baseName, application.binary]; 347 | 348 | if (!copyFile(application.binaryPath, finalBinaryPath)) 349 | { 350 | return NO; 351 | } 352 | 353 | // Open streams from both binaries 354 | FILE *oldBinary, *newBinary; 355 | oldBinary = fopen([application.binaryPath UTF8String], "r+"); 356 | newBinary = fopen([finalBinaryPath UTF8String], "r+"); 357 | 358 | // Read the Mach-O header 359 | fread(&header_buffer, sizeof(header_buffer), 1, oldBinary); 360 | 361 | struct fat_header *header = (struct fat_header *)(header_buffer); 362 | 363 | if (header->magic == FAT_CIGAM) 364 | { 365 | VERBOSE(@"Binary is a fat executable"); 366 | 367 | struct fat_arch *arch; 368 | struct fat_arch armv7, armv7s, arm64; 369 | 370 | arch = (struct fat_arch *) &header[1]; 371 | 372 | // Iterate through all archs in binary, detecting portions on the way 373 | for (int i = 0; i < CFSwapInt32(header->nfat_arch); i++) 374 | { 375 | 376 | if (arch->cputype == CPUTYPE_32) 377 | { 378 | NSLog(@"32-bit portion detected: %@", [self getPrettyArchName:arch->cpusubtype]); 379 | 380 | switch (arch->cpusubtype) 381 | { 382 | case ARMV7_SUBTYPE: 383 | { 384 | armv7 = *arch; 385 | break; 386 | } 387 | case ARMV7S_SUBTYPE: 388 | { 389 | armv7s = *arch; 390 | break; 391 | } 392 | default: 393 | { 394 | NSLog(@"Unknown 32-bit portion: %@", [self getPrettyArchName:arch->cpusubtype]); 395 | } 396 | } 397 | } 398 | else if (arch->cpusubtype == CPUTYPE_64) 399 | { 400 | switch (arch->cpusubtype) 401 | { 402 | case ARM64_SUBTYPE: 403 | { 404 | arm64 = *arch; 405 | break; 406 | } 407 | default: 408 | { 409 | NSLog(@"Unknown 64-bit portion: %@", [self getPrettyArchName:arch->cpusubtype]); 410 | break; 411 | } 412 | } 413 | } 414 | 415 | headersToStrip = [[NSMutableArray alloc] init]; 416 | 417 | // Apply physical restriction filter 418 | if ((local_cputype == CPUTYPE_32) && (CFSwapInt32(arch->cpusubtype) > local_cpusubtype)) 419 | { 420 | NSLog(@"Can't crack arch %u on %u.", arch->cpusubtype, local_cpusubtype); 421 | [headersToStrip addObject:[NSNumber numberWithUnsignedInt:arch->cpusubtype]]; 422 | } 423 | else if (arch->cputype == CPUTYPE_64) 424 | { 425 | if ((local_cpusubtype == CPUTYPE_64) && (arch->cpusubtype > local_cpusubtype)) 426 | { 427 | NSLog(@"Can't crack arch %u on %u.", arch->cpusubtype, local_cpusubtype); 428 | [headersToStrip addObject:[NSNumber numberWithUnsignedInt:arch->cpusubtype]]; 429 | } 430 | else if (local_cpusubtype == CPUTYPE_32) 431 | { 432 | NSLog(@"Can't crack 64-bit arch on this device."); 433 | [headersToStrip addObject:[NSNumber numberWithUnsignedInt:arch->cpusubtype]]; 434 | } 435 | } 436 | 437 | arch++; 438 | } 439 | 440 | arch = (struct fat_arch *) &header[1]; // reset arch increment 441 | 442 | VERBOSE(@"Attempting to dump architectures..."); 443 | 444 | // Iterate through architectures and attempt to dump them 445 | for (int i = 0; i < CFSwapInt32(header->nfat_arch); i++) 446 | { 447 | 448 | // Check if the arch is correct for local_cpusubtype 449 | // Swap the arch if it's able to be cracked 450 | NSLog(@"local_cputsubtype: %d", local_cpusubtype); 451 | NSLog(@"arch subtype: %d", arch->cpusubtype); 452 | if (local_cpusubtype != arch->cpusubtype) 453 | { 454 | // Check if we can crack this arch on this device 455 | if ([headersToStrip containsObject:[NSNumber numberWithUnsignedInt:arch->cpusubtype]]) 456 | { 457 | VERBOSE(@"Cannot crack this architecture on this device.") 458 | arch++; 459 | 460 | continue; 461 | } 462 | 463 | VERBOSE(@"Cracking %@ portion.", [self getPrettyArchName:arch->cpusubtype]); 464 | 465 | NSString *archPath = [self swapArchitectureOfApplication:application toArchitecture:arch->cpusubtype]; 466 | 467 | if (archPath == nil) 468 | { 469 | NSLog(@"Failed to swap architectures."); 470 | 471 | fclose(newBinary); 472 | fclose(oldBinary); 473 | 474 | [self removeTempFiles]; 475 | 476 | return nil; 477 | } 478 | 479 | FILE *swapped_binary = fopen([archPath UTF8String], "r+"); 480 | 481 | if (arch->cputype == CPUTYPE_32) 482 | { 483 | NSLog(@"32-bit dumping."); 484 | // Crack 32-bit arch 485 | if (!dump_binary_32(swapped_binary, newBinary, arch->offset, archPath, finalBinaryPath)) 486 | { 487 | stop_bar(); 488 | ERROR(@"Could not crack architecture."); 489 | 490 | fclose(newBinary); 491 | fclose(oldBinary); 492 | 493 | [self removeTempFiles]; 494 | 495 | return nil; 496 | } 497 | else 498 | { 499 | NSLog(@"Cracked arch: %@", [self getPrettyArchName:arch->cpusubtype]); 500 | } 501 | } 502 | else if (arch->cputype == CPUTYPE_64) 503 | { 504 | 505 | if (!dump_binary_64(swapped_binary, newBinary, arch->offset, archPath, finalBinaryPath)) 506 | { 507 | stop_bar(); 508 | ERROR(@"Could not crack architecture."); 509 | 510 | fclose(newBinary); 511 | fclose(oldBinary); 512 | 513 | [self removeTempFiles]; 514 | 515 | return nil; 516 | } 517 | else 518 | { 519 | NSLog(@"Cracked arch: %@", [self getPrettyArchName:arch->cpusubtype]); 520 | } 521 | 522 | } 523 | } 524 | else 525 | { 526 | NSLog(@"No need swap-swap attacks needed."); 527 | 528 | if (arch->cpusubtype == CPUTYPE_32) 529 | { 530 | // Crack 32-bit arch 531 | if (!dump_binary_32(oldBinary, newBinary, arch->offset, application.binaryPath, finalBinaryPath)) 532 | { 533 | stop_bar(); 534 | ERROR(@"Could not crack architecture."); 535 | 536 | fclose(newBinary); 537 | fclose(oldBinary); 538 | 539 | [self removeTempFiles]; 540 | 541 | return nil; 542 | } 543 | else 544 | { 545 | NSLog(@"Cracked arch: %@", [self getPrettyArchName:arch->cpusubtype]); 546 | } 547 | } 548 | else if (arch->cpusubtype == CPUTYPE_64) 549 | { 550 | // Crack 64-bit arch 551 | if (!dump_binary_64(oldBinary, newBinary, arch->offset, application.binaryPath, finalBinaryPath)) 552 | { 553 | stop_bar(); 554 | ERROR(@"Could not crack architecture."); 555 | 556 | fclose(newBinary); 557 | fclose(oldBinary); 558 | 559 | [self removeTempFiles]; 560 | 561 | return nil; 562 | } 563 | else 564 | { 565 | NSLog(@"Cracked arch: %@", [self getPrettyArchName:arch->cpusubtype]); 566 | } 567 | } 568 | } 569 | } // end of for loop 570 | } 571 | /*else 572 | { 573 | VERBOSE(@"Binary is a thin exectuable."); 574 | 575 | struct mach_header *mach_header = (struct mach_header*)(header_buffer); 576 | 577 | NSLog(@"Binary arch: %@", [self getPrettyArchName:mach_header->cpusubtype]); 578 | 579 | 580 | }*/ 581 | 582 | // All architectures have been cracked, now we need to strip the headers of architectures we cannot crack on this device 583 | if ([headersToStrip count] > 0) 584 | { 585 | for (int i = 0; i < [headersToStrip count]; i++) 586 | { 587 | NSLog(@"Header to strip: %@", headersToStrip[i]); 588 | } 589 | } 590 | 591 | NSLog(@"End of func: crackBinary:"); 592 | 593 | return YES; 594 | } 595 | 596 | - (NSString *)getPrettyArchName:(uint32_t)cpusubtype 597 | { 598 | switch (cpusubtype) 599 | { 600 | case ARMV7_SUBTYPE: 601 | return @"armv7"; 602 | break; 603 | case ARMV7S_SUBTYPE: 604 | return @"armv7s"; 605 | break; 606 | case ARM64_SUBTYPE: 607 | return @"arm64"; 608 | break; 609 | default: 610 | return @"unknown"; 611 | break; 612 | } 613 | 614 | return nil; 615 | } 616 | 617 | - (BOOL)crackApplication:(Application *)application 618 | { 619 | // Create our working directory 620 | if (![self createWorkingDirectory]){ 621 | return NO; 622 | } 623 | 624 | VERBOSE(@"Performing initial anaylsis..."); 625 | 626 | // We used to open Info.plist here and add 'Apple iPhone OS Application Signing' for 'SignerIdentity' but this 627 | // is no longer needed (we used to do modifications to the timestamps of Info.plist as people used to check if 628 | // Info.plist had been tampered with. 629 | 630 | BOOL success = [self preflightBinaryOfApplication:application]; 631 | 632 | if (!success) 633 | { 634 | ERROR(@"Failed to crack binary."); 635 | 636 | return NO; 637 | } 638 | 639 | return YES; 640 | } 641 | 642 | - (NSString *)swapArchitectureOfApplication:(Application *)application toArchitecture:(uint32_t)arch_to_swap_to 643 | { 644 | char buffer[4096]; // sizeof(fat_header) 645 | 646 | if (local_cpusubtype == arch_to_swap_to) 647 | { 648 | NSLog(@"Dev logic error. No need to swap to the arch the device runs. Hurr."); 649 | 650 | return nil; 651 | } 652 | 653 | NSString *tempSwapBinaryPath = [workingDirectory stringByAppendingFormat:@"%@_lwork", [self getPrettyArchName:arch_to_swap_to]]; 654 | 655 | if (!copyFile(application.binaryPath, tempSwapBinaryPath)) 656 | { 657 | [self removeTempFiles]; 658 | 659 | return nil; 660 | } 661 | 662 | FILE *swap_binary = fopen([tempSwapBinaryPath UTF8String], "r+"); 663 | 664 | fseek(swap_binary, 0, SEEK_SET); 665 | fread(&buffer, sizeof(buffer), 1, swap_binary); 666 | 667 | struct fat_header *swap_fat_header = (struct fat_header *)(buffer); 668 | struct fat_arch *arch = (struct fat_arch *)&swap_fat_header[1]; 669 | 670 | uint32_t swap_cputype, largest_cpusubtype = 0; 671 | 672 | for (int i = 0; i < CFSwapInt32(swap_fat_header->nfat_arch); i++) 673 | { 674 | if (arch->cpusubtype == arch_to_swap_to) 675 | { 676 | NSLog(@"Found our arch to swap: %@", [self getPrettyArchName:arch->cpusubtype]); 677 | 678 | swap_cputype = arch->cputype; 679 | 680 | //NSLog(@"swap_cputype: %u (%@)\tArch cputype: %u (%@)", swap_cputype, [self getPrettyArchName:swap_cputype], arch->cputype) 681 | } 682 | 683 | if (arch->cpusubtype > largest_cpusubtype) 684 | { 685 | largest_cpusubtype = arch->cpusubtype; 686 | } 687 | 688 | arch++; 689 | } 690 | 691 | NSLog(@"Halfway!"); 692 | 693 | arch = (struct fat_arch *)&swap_fat_header[1]; // reset arch increment 694 | 695 | for (int i = 0; CFSwapInt32(swap_fat_header->nfat_arch); i++) 696 | { 697 | NSLog(@"Top of loop"); 698 | if (arch->cpusubtype == largest_cpusubtype) 699 | { 700 | if (swap_cputype != arch->cputype) 701 | { 702 | //NSLog(@"Swap: %u, Arch:%u", swap_cputype, arch->cputype); 703 | NSLog(@"cputypes to swap are incompatible."); 704 | 705 | return nil; 706 | } 707 | 708 | NSLog(@"Replaced %@'s cpusubtype to %@.", [self getPrettyArchName:arch->cpusubtype], [self getPrettyArchName:arch_to_swap_to]); 709 | arch->cpusubtype = arch_to_swap_to; 710 | } 711 | else if (arch->cpusubtype == arch_to_swap_to) 712 | { 713 | NSLog(@"Replaced %@'s subtype to %@.", [self getPrettyArchName:arch->cpusubtype], [self getPrettyArchName:largest_cpusubtype]); 714 | arch->cpusubtype = largest_cpusubtype; 715 | } 716 | 717 | if (i == CFSwapInt32(swap_fat_header->nfat_arch)) 718 | { 719 | break; 720 | } 721 | else 722 | { 723 | arch++; // this causes a segfault by itself lol. 724 | } 725 | } 726 | 727 | if (![self copySCInfoKeysForApplication:application]) 728 | { 729 | return nil; 730 | } 731 | 732 | fseek(swap_binary, 0, SEEK_SET); 733 | fwrite(buffer, sizeof(buffer), 1, swap_binary); 734 | fclose(swap_binary); 735 | 736 | VERBOSE(@"Swap: Wrote new arch information."); 737 | 738 | return tempSwapBinaryPath; 739 | } 740 | 741 | - (BOOL)copySCInfoKeysForApplication:(Application *)application 742 | { 743 | VERBOSE(@"Moving SC_Info keys..."); 744 | 745 | // Move SC_Info Keys 746 | NSArray *SCInfoFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"SC_Info/" error:nil]; 747 | 748 | for (int i = 0; i < [SCInfoFiles count]; i++) 749 | { 750 | if ([SCInfoFiles[i] rangeOfString:@".sinf"].location != NSNotFound) 751 | { 752 | sinfPath = [application.directory stringByAppendingFormat:@"SC_Info/%@", SCInfoFiles[i]]; 753 | 754 | if (!copyFile(sinfPath, [workingDirectory stringByAppendingFormat:@"SC_Info/"])) 755 | { 756 | NSLog(@"Error moving sinf file."); 757 | 758 | return NO; 759 | } 760 | 761 | NSLog(@"Sinf: %@", sinfPath); 762 | } 763 | else if ([SCInfoFiles[i] rangeOfString:@".supp"].location != NSNotFound) 764 | { 765 | suppPath = [application.directory stringByAppendingFormat:@"SC_Info/%@", SCInfoFiles[i]]; 766 | 767 | if (!copyFile(suppPath, [workingDirectory stringByAppendingFormat:@"SC_Info/"])) 768 | { 769 | NSLog(@"Error moving supp file."); 770 | 771 | return NO; 772 | } 773 | 774 | NSLog(@"Supp: %@", suppPath); 775 | } 776 | else if ([SCInfoFiles[i] rangeOfString:@".supf"].location != NSNotFound) 777 | { 778 | supfPath = [application.directory stringByAppendingFormat:@"SC_Info/%@", SCInfoFiles[i]]; 779 | 780 | if (!copyFile(supfPath, [workingDirectory stringByAppendingFormat:@"SC_Info/"])) 781 | { 782 | NSLog(@"Error moving supf file."); 783 | 784 | return NO; 785 | } 786 | 787 | NSLog(@"Supf: %@", supfPath); 788 | } 789 | } 790 | 791 | return YES; 792 | } 793 | 794 | - (BOOL)removeTempFiles 795 | { 796 | if (!forceRemoveDirectory(workingDirectory)) 797 | { 798 | ERROR(@"Failed to remove working directory (you'll have to do this manually from /tmp or restart)"); 799 | 800 | return NO; 801 | } 802 | 803 | return YES; 804 | } 805 | 806 | - (BOOL)createWorkingDirectory 807 | { 808 | VERBOSE(@"Creating working directory..."); 809 | 810 | workingDirectory = [NSString stringWithFormat:@"/tmp/%@/", [[NSUUID UUID] UUIDString]]; 811 | 812 | if (![[NSFileManager defaultManager] createDirectoryAtPath:[workingDirectory stringByAppendingString:@"Payload/"] withIntermediateDirectories:YES attributes:@{@"NSFileOwnerAccountName": @"mobbile", 813 | @"NSFileGroupOwnerAccountName": @"mobile"} error:NULL]) 814 | { 815 | ERROR(@"Could not create working directory"); 816 | 817 | return NO; 818 | } 819 | 820 | return YES; 821 | } 822 | 823 | 824 | @end -------------------------------------------------------------------------------- /src/Packager.mm: -------------------------------------------------------------------------------- 1 | // 2 | // Packager.m 3 | // Clutch 4 | // 5 | // Created by DilDog on 12/22/13. 6 | // 7 | // 8 | 9 | #import "Packager.h" 10 | 11 | @implementation Packager 12 | 13 | 14 | - (id)init 15 | { 16 | self = [super init]; 17 | if (self) 18 | { 19 | _outputPath = NULL; 20 | } 21 | return self; 22 | } 23 | 24 | -(void)dealloc 25 | { 26 | if(_outputPath) 27 | { 28 | [_outputPath release]; 29 | } 30 | [super dealloc]; 31 | } 32 | 33 | 34 | -(NSString *)getOutputPath 35 | { 36 | return _outputPath; 37 | } 38 | 39 | -(BOOL)packFromSource:(NSString *)inputpath withOverlay:(NSString *)overlaypath 40 | { 41 | return YES; 42 | } 43 | 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /src/applist.mm: -------------------------------------------------------------------------------- 1 | // 2 | // Cracker.m 3 | // Clutch 4 | // 5 | // Created by on 12/22/13. 6 | // 7 | // 8 | 9 | #import "applist.h" 10 | #import "out.h" 11 | #import "Application.h" 12 | 13 | @implementation applist 14 | 15 | + (NSArray *)listApplications 16 | { 17 | // Prepare array to return application list 18 | NSMutableArray *returnArray = [[NSMutableArray alloc] init]; 19 | 20 | // Get base path for installed applications 21 | NSString *basePath = @"/var/mobile/Applications/"; 22 | 23 | // Get list of all applictions from file manager 24 | NSArray *apps = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:basePath error:NULL]; 25 | 26 | if ([apps count] == 0) { 27 | printf("No applications found\n"); 28 | 29 | [returnArray release]; 30 | return NULL; 31 | } 32 | 33 | // Iterate over all applications 34 | for (NSString *value in apps) 35 | { 36 | NSString *applicationDirectory = [basePath stringByAppendingFormat:@"%@/", value]; 37 | NSArray *sandboxPath = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:applicationDirectory error:NULL]; 38 | 39 | 40 | for (NSString *directory in sandboxPath) 41 | { 42 | if ([directory rangeOfString:@".app"].location == NSNotFound) // if the directory doesn't contain ".app" iterate loop 43 | { 44 | continue; 45 | } else 46 | { 47 | // We're in an application folder 48 | 49 | // Check if SC_Info exsists, these are only present on encrypted applications 50 | // Apple stock applications don't seem to have them (when did they start appearing 51 | // symlink'd to /mobile/Applications? 52 | NSString *subdirectory = [applicationDirectory stringByAppendingFormat:@"%@/", directory]; 53 | 54 | if ([[NSFileManager defaultManager] fileExistsAtPath:[subdirectory stringByAppendingString:@"/SC_Info/"]]) { 55 | NSDictionary *infoPlist = [[NSDictionary alloc] initWithContentsOfFile:[subdirectory stringByAppendingString:@"/Info.plist"]]; 56 | 57 | NSString *bundleDisplayName = infoPlist[@"CFBundleDisplayName"]; 58 | 59 | if (bundleDisplayName == nil) 60 | { 61 | NSLog(@"%@ CFBundleDisplayName not found", directory); 62 | 63 | // try CFBundleName 64 | bundleDisplayName = infoPlist[@"CFBundleName"]; 65 | NSLog(@"Using CFBundleName: %@", bundleDisplayName); 66 | } 67 | 68 | // no aleternative for CFBundleIdentifier 69 | NSString *bundleIdentifier = infoPlist[@"CFBundleIdentifier"]; 70 | 71 | NSString *bundleVersionString = infoPlist[@"CFBundleVersion"]; 72 | 73 | if (bundleVersionString == nil) 74 | { 75 | NSLog(@"%@ CFBundleVersion not found", directory); 76 | 77 | // try CFBundleShortVersionString 78 | bundleVersionString = infoPlist[@"CFBundleShortVersionString"]; 79 | } 80 | 81 | // this should always be in Info.plist 82 | NSString *binary = infoPlist[@"CFBundleExecutable"]; 83 | 84 | // default to the executable name 85 | if (bundleDisplayName == nil) 86 | { 87 | bundleDisplayName = binary; 88 | } 89 | 90 | // Create dictionary of useful keys from Info.plis 91 | // if and only if the SC_Info folder exsists, which indicates 92 | // if an applicaiton is encrypted. 93 | Application *app = [[Application alloc] init]; 94 | app.baseDirectory = applicationDirectory; 95 | app.directory = subdirectory; 96 | app.displayName = bundleDisplayName; 97 | app.binary = binary; 98 | app.baseName = directory; 99 | app.UUID = [[applicationDirectory lastPathComponent] stringByDeletingPathExtension]; 100 | app.version = bundleVersionString; 101 | app.identifier = bundleIdentifier; 102 | app.infoPlist = infoPlist; 103 | app.binaryPath = [applicationDirectory stringByAppendingFormat:@"%@/%@", directory, binary]; 104 | 105 | 106 | /*printf("\n"); 107 | NSLog(@"BaseDir %@", applicationDirectory); 108 | NSLog(@"Directory %@", subdirectory); 109 | NSLog(@"DispName: %@", bundleDisplayName); 110 | NSLog(@"Binary %@", binary); 111 | NSLog(@"BaseName %@", directory); 112 | NSLog(@"UUID %@", [[applicationDirectory lastPathComponent] stringByDeletingPathExtension]); 113 | NSLog(@"Version %@", bundleVersionString); 114 | NSLog(@"Ident %@", bundleIdentifier);*/ 115 | 116 | [returnArray addObject:app]; 117 | 118 | [app release]; 119 | [infoPlist release]; 120 | } 121 | } 122 | } 123 | } 124 | 125 | if ([returnArray count] == 0) 126 | { 127 | [returnArray release]; 128 | 129 | return NULL; 130 | } 131 | 132 | NSArray *sortedReturnArray = [returnArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { 133 | NSString *first = [(Application*)obj1 displayName]; 134 | NSString *second = [(Application *)obj2 displayName]; 135 | 136 | return [first compare:second]; 137 | }]; 138 | 139 | // return the array 140 | return sortedReturnArray; 141 | 142 | //return (NSArray *)[returnArray sortedArrayUsingFunction:alphabeticalSort context:NULL]; 143 | } 144 | 145 | @end 146 | -------------------------------------------------------------------------------- /src/dump.m: -------------------------------------------------------------------------------- 1 | #import "dump.h" 2 | #import "out.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int overdrive_enabled; 10 | 11 | extern kern_return_t mach_vm_region 12 | ( 13 | vm_map_t target_task, 14 | mach_vm_address_t *address, 15 | mach_vm_size_t *size, 16 | vm_region_flavor_t flavor, 17 | vm_region_info_t info, 18 | mach_msg_type_number_t *infoCnt, 19 | mach_port_t *object_name 20 | ); 21 | 22 | extern kern_return_t mach_vm_read_overwrite 23 | ( 24 | vm_map_t target_task, 25 | mach_vm_address_t address, 26 | mach_vm_size_t size, 27 | mach_vm_address_t data, 28 | mach_vm_size_t *outsize 29 | ); 30 | 31 | extern kern_return_t mach_vm_protect 32 | ( 33 | vm_map_t target_task, 34 | mach_vm_address_t address, 35 | mach_vm_size_t size, 36 | boolean_t set_maximum, 37 | vm_prot_t new_protection 38 | ); 39 | 40 | extern kern_return_t mach_vm_write 41 | ( 42 | vm_map_t target_task, 43 | mach_vm_address_t address, 44 | vm_offset_t data, 45 | mach_msg_type_number_t dataCnt 46 | ); 47 | 48 | #ifndef __LP64__ 49 | BOOL dump_binary_32(FILE *origin, FILE *target, uint32_t top, NSString *originPath, NSString* finalPath) { 50 | //#ifndef __LP64__ 51 | fseek(target, top, SEEK_SET); // go the top of the target 52 | // we're going to be going to this position a lot so let's save it 53 | fpos_t topPosition; 54 | fgetpos(target, &topPosition); 55 | 56 | struct linkedit_data_command ldid; // LC_CODE_SIGNATURE load header (for resign) 57 | struct encryption_info_command crypt; // LC_ENCRYPTION_INFO load header (for crypt*) 58 | struct mach_header mach; // generic mach header 59 | struct load_command l_cmd; // generic load command 60 | struct segment_command __text; // __TEXT segment 61 | 62 | struct SuperBlob *codesignblob; // codesign blob pointer 63 | struct CodeDirectory directory; // codesign directory index 64 | 65 | BOOL foundCrypt = FALSE; 66 | BOOL foundSignature = FALSE; 67 | BOOL foundStartText = FALSE; 68 | uint32_t __text_start = 0; 69 | uint32_t __text_size = 0; 70 | 71 | NSLog(@"32bit dumping, offset %u", top); 72 | VERBOSE(@"dumping binary: analyzing load commands"); 73 | 74 | fread(&mach, sizeof(struct mach_header), 1, target); // read mach header to get number of load commands 75 | 76 | for (int lc_index = 0; lc_index < mach.ncmds; lc_index++) { // iterate over each load command 77 | fread(&l_cmd, sizeof(struct load_command), 1, target); // read load command from binary 78 | //DEBUG("command %u", l_cmd.cmd); 79 | if (l_cmd.cmd == LC_ENCRYPTION_INFO) { // encryption info? 80 | fseek(target, -1 * sizeof(struct load_command), SEEK_CUR); 81 | fread(&crypt, sizeof(struct encryption_info_command), 1, target); 82 | foundCrypt = TRUE; // remember that it was found 83 | } else if (l_cmd.cmd == LC_CODE_SIGNATURE) { // code signature? 84 | fseek(target, -1 * sizeof(struct load_command), SEEK_CUR); 85 | fread(&ldid, sizeof(struct linkedit_data_command), 1, target); 86 | foundSignature = TRUE; // remember that it was found 87 | } else if (l_cmd.cmd == LC_SEGMENT) { 88 | // some applications, like Skype, have decided to start offsetting the executable image's 89 | // vm regions by substantial amounts for no apparant reason. this will find the vmaddr of 90 | // that segment (referenced later during dumping) 91 | fseek(target, -1 * sizeof(struct load_command), SEEK_CUR); 92 | fread(&__text, sizeof(struct segment_command), 1, target); 93 | 94 | if (strncmp(__text.segname, "__TEXT", 6) == 0) { 95 | foundStartText = TRUE; 96 | __text_start = __text.vmaddr; 97 | __text_size = __text.vmsize; 98 | } 99 | fseek(target, l_cmd.cmdsize - sizeof(struct segment_command), SEEK_CUR); 100 | } else { 101 | fseek(target, l_cmd.cmdsize - sizeof(struct load_command), SEEK_CUR); // seek over the load command 102 | } 103 | 104 | if (foundCrypt && foundSignature && foundStartText) 105 | break; 106 | } 107 | 108 | // we need to have found both of these 109 | if (!foundCrypt || !foundSignature || !foundStartText) { 110 | VERBOSE(@"dumping binary: some load commands were not found"); 111 | return FALSE; 112 | } 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 | int status; // status of the wait 118 | //vm_size_t local_size = 0; // amount of data moved into the buffer 119 | mach_vm_size_t local_size = 0; // amount of data moved into the buffer 120 | uint32_t begin; 121 | 122 | VERBOSE(@"dumping binary: obtaining ptrace handle"); 123 | 124 | // open handle to dylib loader 125 | void *handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW); 126 | // load ptrace library into handle 127 | ptrace_ptr_t ptrace = dlsym(handle, "ptrace"); 128 | // begin the forking process 129 | VERBOSE(@"dumping binary: forking to begin tracing"); 130 | 131 | if ((pid = fork()) == 0) { 132 | // it worked! the magic is in allowing the process to trace before execl. 133 | // the process will be incapable of preventing itself from tracing 134 | // execl stops the process before this is capable 135 | // PT_DENY_ATTACH was never meant to be good security, only a minor roadblock 136 | 137 | VERBOSE(@"dumping binary: successfully forked"); 138 | 139 | ptrace(PT_TRACE_ME, 0, 0, 0); // trace 140 | execl([originPath UTF8String], "", (char *) 0); // import binary memory into executable space 141 | 142 | exit(2); // exit with err code 2 in case we could not import (this should not happen) 143 | } else if (pid < 0) { 144 | printf("error: Couldn't fork, did you compile with proper entitlements?"); 145 | ERROR(@"error: Couldn't fork, did you compile with proper entitlements?"); 146 | return FALSE; // couldn't fork 147 | } else { 148 | // wait until the binary stops 149 | do { 150 | wait(&status); 151 | if (WIFEXITED( status )) 152 | return FALSE; 153 | } while (!WIFSTOPPED( status )); 154 | 155 | VERBOSE(@"dumping binary: obtaining mach port"); 156 | 157 | // open mach port to the other process 158 | if ((err = task_for_pid(mach_task_self(), pid, &port) != KERN_SUCCESS)) { 159 | ERROR(@"ERROR: Could not obtain mach port, did you sign with proper entitlements?"); 160 | kill(pid, SIGKILL); // kill the fork 161 | return FALSE; 162 | } 163 | 164 | VERBOSE(@"dumping binary: preparing code resign"); 165 | 166 | codesignblob = malloc(ldid.datasize); 167 | fseek(target, top + ldid.dataoff, SEEK_SET); // seek to the codesign blob 168 | fread(codesignblob, ldid.datasize, 1, target); // read the whole codesign blob 169 | uint32_t countBlobs = CFSwapInt32(codesignblob->count); // how many indexes? 170 | 171 | // iterate through each index 172 | for (uint32_t index = 0; index < countBlobs; index++) { 173 | if (CFSwapInt32(codesignblob->index[index].type) == CSSLOT_CODEDIRECTORY) { // is this the code directory? 174 | // we'll find the hash metadata in here 175 | begin = top + ldid.dataoff + CFSwapInt32(codesignblob->index[index].offset); // store the top of the codesign directory blob 176 | fseek(target, begin, SEEK_SET); // seek to the beginning of the blob 177 | fread(&directory, sizeof(struct CodeDirectory), 1, target); // read the blob 178 | break; // break (we don't need anything from this the superblob anymore) 179 | } 180 | } 181 | 182 | free(codesignblob); // free the codesign blob 183 | 184 | uint32_t pages = CFSwapInt32(directory.nCodeSlots); // get the amount of codeslots 185 | if (pages == 0) { 186 | kill(pid, SIGKILL); // kill the fork 187 | return FALSE; 188 | } 189 | 190 | void *checksum = malloc(pages * 20); // 160 bits for each hash (SHA1) 191 | uint8_t buf_d[0x1000]; // create a single page buffer 192 | uint8_t *buf = &buf_d[0]; // store the location of the buffer 193 | 194 | VERBOSE(@"dumping binary: preparing to dump"); 195 | 196 | // we should only have to write and perform checksums on data that changes 197 | uint32_t togo = crypt.cryptsize + crypt.cryptoff; 198 | uint32_t total = togo; 199 | uint32_t pages_d = 0; 200 | BOOL header = TRUE; 201 | 202 | // write the header 203 | fsetpos(target, &topPosition); 204 | 205 | // in iOS 4.3+, ASLR can be enabled by developers by setting the MH_PIE flag in 206 | // the mach header flags. this will randomly offset the location of the __TEXT 207 | // segment, making it slightly difficult to identify the location of the 208 | // decrypted pages. instead of disabling this flag in the original binary 209 | // (which is slow, requires resigning, and requires reverting to the original 210 | // binary after cracking) we instead manually identify the vm regions which 211 | // contain the header and subsequent decrypted executable code. 212 | 213 | if (mach.flags & MH_PIE) { 214 | VERBOSE(@"dumping binary: ASLR enabled, identifying dump location dynamically"); 215 | // perform checks on vm regions 216 | memory_object_name_t object; 217 | vm_region_basic_info_data_t info; 218 | mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; 219 | mach_vm_address_t region_start = 0; 220 | mach_vm_size_t region_size = 0; 221 | vm_region_flavor_t flavor = VM_REGION_BASIC_INFO; 222 | err = 0; 223 | 224 | while (err == KERN_SUCCESS) { 225 | err = mach_vm_region(port, ®ion_start, ®ion_size, flavor, (vm_region_info_t) &info, &info_count, &object); 226 | 227 | NSLog(@"32-bit Region Size: %llu %u", region_size, crypt.cryptsize); 228 | 229 | if ((uint32_t)region_size == crypt.cryptsize) { 230 | break; 231 | } 232 | __text_start = region_start; 233 | region_start += region_size; 234 | region_size = 0; 235 | } 236 | if (err != KERN_SUCCESS) { 237 | free(checksum); 238 | NSLog(@"32-bit mach_vm_error: %u", err); 239 | ERROR(@"ASLR is enabled and we could not identify the decrypted memory region.\n"); 240 | kill(pid, SIGKILL); 241 | return FALSE; 242 | } 243 | } 244 | 245 | 246 | uint32_t headerProgress = sizeof(struct mach_header); 247 | uint32_t i_lcmd = 0; 248 | 249 | // overdrive dylib load command size 250 | uint32_t overdrive_size = sizeof(OVERDRIVE_DYLIB_PATH) + sizeof(struct dylib_command); 251 | overdrive_size += sizeof(long) - (overdrive_size % sizeof(long)); // load commands like to be aligned by long 252 | 253 | VERBOSE(@"dumping binary: performing dump"); 254 | 255 | while (togo > 0) { 256 | // get a percentage for the progress bar 257 | //PERCENT((int)ceil((((double)total - togo) / (double)total) * 100)); 258 | PERCENT((int)ceil((((double)total - togo) / (double)total) * 100)); 259 | 260 | // move an entire page into memory (we have to move an entire page regardless of whether it's a resultant or not) 261 | /*if((err = vm_read_overwrite(port, (mach_vm_address_t) __text_start + (pages_d * 0x1000), (vm_size_t) 0x1000, (pointer_t) buf, &local_size)) != KERN_SUCCESS) { 262 | VERBOSE("dumping binary: failed to dump a page"); 263 | free(checksum); // free checksum table 264 | kill(pid, SIGKILL); // kill fork 265 | return FALSE; 266 | }*/ 267 | 268 | if ((err = mach_vm_read_overwrite(port, (mach_vm_address_t) __text_start + (pages_d * 0x1000), (vm_size_t) 0x1000, (pointer_t) buf, &local_size)) != KERN_SUCCESS) { 269 | 270 | VERBOSE(@"dumping binary: failed to dump a page (32)"); 271 | 272 | free(checksum); // free checksum table 273 | kill(pid, SIGKILL); // kill the fork 274 | 275 | return FALSE; 276 | } 277 | 278 | 279 | if (header) { 280 | // is this the first header page? 281 | if (i_lcmd == 0) { 282 | // is overdrive enabled? 283 | if (overdrive_enabled) { 284 | // prepare the mach header for the new load command (overdrive dylib) 285 | ((struct mach_header *)buf)->ncmds += 1; 286 | ((struct mach_header *)buf)->sizeofcmds += overdrive_size; 287 | VERBOSE(@"dumping binary: patched mach header (overdrive)"); 288 | } 289 | } 290 | // iterate over the header (or resume iteration) 291 | void *curloc = buf + headerProgress; 292 | for (;i_lcmdcmdsize; 304 | } 305 | 306 | if (l_cmd->cmd == LC_ENCRYPTION_INFO) { 307 | struct encryption_info_command *newcrypt = (struct encryption_info_command *) curloc; 308 | newcrypt->cryptid = 0; // change the cryptid to 0 309 | VERBOSE(@"dumping binary: patched cryptid"); 310 | } else if (l_cmd->cmd == LC_SEGMENT) { 311 | //printf("lc segemn yo\n"); 312 | struct segment_command *newseg = (struct segment_command *) curloc; 313 | if (newseg->fileoff == 0 && newseg->filesize > 0) { 314 | // is overdrive enabled? this is __TEXT 315 | if (overdrive_enabled) { 316 | // maxprot so that overdrive can change the __TEXT protection & 317 | // cryptid in realtime 318 | newseg->maxprot |= VM_PROT_ALL; 319 | VERBOSE(@"dumping binary: patched maxprot (overdrive)"); 320 | } 321 | } 322 | } 323 | curloc += lcmd_size; 324 | if ((void *)curloc >= (void *)buf + 0x1000) { 325 | //printf("skipped pass the haeder yo\n"); 326 | // we are currently extended past the header page 327 | // offset for the next round: 328 | headerProgress = (((void *)curloc - (void *)buf) % 0x1000); 329 | // prevent attaching overdrive dylib by skipping 330 | goto skipoverdrive; 331 | } 332 | } 333 | // is overdrive enabled? 334 | if (overdrive_enabled) { 335 | // add the overdrive dylib as long as we have room 336 | if ((int8_t*)(curloc + overdrive_size) < (int8_t*)(buf + 0x1000)) { 337 | VERBOSE(@"dumping binary: attaching overdrive DYLIB (overdrive)"); 338 | struct dylib_command *overdrive_dyld = (struct dylib_command *) curloc; 339 | overdrive_dyld->cmd = LC_LOAD_DYLIB; 340 | overdrive_dyld->cmdsize = overdrive_size; 341 | overdrive_dyld->dylib.compatibility_version = OVERDRIVE_DYLIB_COMPATIBILITY_VERSION; 342 | overdrive_dyld->dylib.current_version = OVERDRIVE_DYLIB_CURRENT_VER; 343 | overdrive_dyld->dylib.timestamp = 2; 344 | overdrive_dyld->dylib.name.offset = sizeof(struct dylib_command); 345 | #ifndef __LP64__ 346 | overdrive_dyld->dylib.name.ptr = (char *) sizeof(struct dylib_command); 347 | #endif 348 | char *p = (char *) overdrive_dyld + overdrive_dyld->dylib.name.offset; 349 | strncpy(p, OVERDRIVE_DYLIB_PATH, sizeof(OVERDRIVE_DYLIB_PATH)); 350 | } 351 | } 352 | header = FALSE; 353 | } 354 | skipoverdrive: 355 | //printf("attemtping to write to binary\n"); 356 | fwrite(buf, 0x1000, 1, target); // write the new data to the target 357 | sha1(checksum + (20 * pages_d), buf, 0x1000); // perform checksum on the page 358 | //printf("doing checksum yo\n"); 359 | togo -= 0x1000; // remove a page from the togo 360 | //printf("togo yo %u\n", togo); 361 | pages_d += 1; // increase the amount of completed pages 362 | } 363 | 364 | VERBOSE(@"dumping binary: writing new checksum"); 365 | 366 | // nice! now let's write the new checksum data 367 | fseek(target, begin + CFSwapInt32(directory.hashOffset), SEEK_SET); // go to the hash offset 368 | fwrite(checksum, 20*pages_d, 1, target); // write the hashes (ONLY for the amount of pages modified) 369 | 370 | free(checksum); // free checksum table from memory 371 | kill(pid, SIGKILL); // kill the fork 372 | } 373 | 374 | stop_bar(); 375 | 376 | return TRUE; 377 | //#endif 378 | /*#ifdef __LP64__ 379 | printf("\nWe can't crack 32bit portions when running in 64bit mode!\n\n"); 380 | VERBOSE("string %@", [NSString stringWithFormat:@"./Clutch_32 -32 \"%@\" \"%@\" %u", originPath, finalPath, top]); 381 | int retVal = system([[NSString stringWithFormat:@"./Clutch_32 -32 \"%@\" \"%@\" %u", originPath, finalPath, top] UTF8String]); 382 | if (retVal != 0) { 383 | printf("error: could not dump 32bit portion"); 384 | return FALSE; 385 | } 386 | return TRUE; 387 | #endif*/ 388 | } 389 | 390 | #else 391 | 392 | BOOL dump_binary_64(FILE *origin, FILE *target, uint32_t top, NSString *originPath, NSString* finalPath) { 393 | fseek(target, top, SEEK_SET); // go the top of the target 394 | 395 | // we're going to be going to this position a lot so let's save it 396 | fpos_t topPosition; 397 | fgetpos(target, &topPosition); 398 | 399 | struct linkedit_data_command ldid; // LC_CODE_SIGNATURE load header (for resign) 400 | struct encryption_info_command_64 crypt; // LC_ENCRYPTION_INFO load header (for crypt*) 401 | struct mach_header_64 mach; // generic mach header 402 | struct load_command l_cmd; // generic load command 403 | struct segment_command_64 __text; // __TEXT segment 404 | 405 | struct SuperBlob *codesignblob; // codesign blob pointer 406 | struct CodeDirectory directory; // codesign directory index 407 | 408 | BOOL foundCrypt = FALSE; 409 | BOOL foundSignature = FALSE; 410 | BOOL foundStartText = FALSE; 411 | uint64_t __text_start = 0; 412 | uint64_t __text_size = 0; 413 | VERBOSE(@"dumping binary: analyzing load commands"); 414 | 415 | fread(&mach, sizeof(struct mach_header_64), 1, target); // read mach header to get number of load commands 416 | for (int lc_index = 0; lc_index < mach.ncmds; lc_index++) { // iterate over each load command 417 | fread(&l_cmd, sizeof(struct load_command), 1, target); // read load command from binary 418 | //DEBUG("command: %u", CFSwapInt32(l_cmd.cmd)); 419 | if (l_cmd.cmd == LC_ENCRYPTION_INFO_64) { // encryption info? 420 | fseek(target, -1 * sizeof(struct load_command), SEEK_CUR); 421 | fread(&crypt, sizeof(struct encryption_info_command_64), 1, target); 422 | VERBOSE(@"found cryptid"); 423 | foundCrypt = TRUE; // remember that it was found 424 | } else if (l_cmd.cmd == LC_CODE_SIGNATURE) { // code signature? 425 | fseek(target, -1 * sizeof(struct load_command), SEEK_CUR); 426 | fread(&ldid, sizeof(struct linkedit_data_command), 1, target); 427 | VERBOSE(@"found code signature"); 428 | foundSignature = TRUE; // remember that it was found 429 | } else if (l_cmd.cmd == LC_SEGMENT_64) { 430 | // some applications, like Skype, have decided to start offsetting the executable image's 431 | // vm regions by substantial amounts for no apparant reason. this will find the vmaddr of 432 | // that segment (referenced later during dumping) 433 | fseek(target, -1 * sizeof(struct load_command), SEEK_CUR); 434 | fread(&__text, sizeof(struct segment_command_64), 1, target); 435 | if (strncmp(__text.segname, "__TEXT", 6) == 0) { 436 | foundStartText = TRUE; 437 | VERBOSE(@"found start text"); 438 | __text_start = __text.vmaddr; 439 | __text_size = __text.vmsize; 440 | 441 | } 442 | fseek(target, l_cmd.cmdsize - sizeof(struct segment_command_64), SEEK_CUR); 443 | } else { 444 | fseek(target, l_cmd.cmdsize - sizeof(struct load_command), SEEK_CUR); // seek over the load command 445 | } 446 | if (foundCrypt && foundSignature && foundStartText) 447 | break; 448 | } 449 | 450 | 451 | // we need to have found both of these 452 | if (!foundCrypt || !foundSignature || !foundStartText) { 453 | VERBOSE(@"dumping binary: some load commands were not found"); 454 | return FALSE; 455 | } 456 | 457 | pid_t pid; // store the process ID of the fork 458 | mach_port_t port; // mach port used for moving virtual memory 459 | kern_return_t err; // any kernel return codes 460 | int status; // status of the wait 461 | mach_vm_size_t local_size = 0; // amount of data moved into the buffer 462 | uint32_t begin; 463 | 464 | VERBOSE(@"dumping binary: obtaining ptrace handle"); 465 | 466 | // open handle to dylib loader 467 | void *handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW); 468 | // load ptrace library into handle 469 | ptrace_ptr_t ptrace = dlsym(handle, "ptrace"); 470 | // begin the forking process 471 | VERBOSE(@"dumping binary: forking to begin tracing"); 472 | 473 | if ((pid = fork()) == 0) { 474 | // it worked! the magic is in allowing the process to trace before execl. 475 | // the process will be incapable of preventing itself from tracing 476 | // execl stops the process before this is capable 477 | // PT_DENY_ATTACH was never meant to be good security, only a minor roadblock 478 | 479 | ptrace(PT_TRACE_ME, 0, 0, 0); // trace 480 | execl([originPath UTF8String], "", (char *) 0); // import binary memory into executable space 481 | 482 | exit(2); // exit with err code 2 in case we could not import (this should not happen) 483 | } else if (pid < 0) { 484 | ERROR(@"Couldn't fork, did you compile with proper entitlements?"); 485 | return FALSE; // couldn't fork 486 | } else { 487 | // wait until the binary stops 488 | do { 489 | wait(&status); 490 | if (WIFEXITED( status )) 491 | return FALSE; 492 | } while (!WIFSTOPPED( status )); 493 | 494 | VERBOSE(@"dumping binary: obtaining mach port"); 495 | 496 | // open mach port to the other process 497 | if ((err = task_for_pid(mach_task_self(), pid, &port) != KERN_SUCCESS)) { 498 | ERROR(@"Could not obtain mach port, did you sign with proper entitlements?"); 499 | kill(pid, SIGKILL); // kill the fork 500 | return FALSE; 501 | } 502 | 503 | VERBOSE(@"dumping binary: preparing code resign"); 504 | 505 | codesignblob = malloc(ldid.datasize); 506 | fseek(target, top + ldid.dataoff, SEEK_SET); // seek to the codesign blob 507 | fread(codesignblob, ldid.datasize, 1, target); // read the whole codesign blob 508 | uint32_t countBlobs = CFSwapInt32(codesignblob->count); // how many indexes? 509 | 510 | // iterate through each index 511 | for (uint32_t index = 0; index < countBlobs; index++) { 512 | if (CFSwapInt32(codesignblob->index[index].type) == CSSLOT_CODEDIRECTORY) { // is this the code directory? 513 | // we'll find the hash metadata in here 514 | begin = top + ldid.dataoff + CFSwapInt32(codesignblob->index[index].offset); // store the top of the codesign directory blob 515 | fseek(target, begin, SEEK_SET); // seek to the beginning of the blob 516 | fread(&directory, sizeof(struct CodeDirectory), 1, target); // read the blob 517 | break; // break (we don't need anything from this the superblob anymore) 518 | } 519 | } 520 | 521 | free(codesignblob); // free the codesign blob 522 | 523 | uint32_t pages = CFSwapInt32(directory.nCodeSlots); // get the amount of codeslots 524 | if (pages == 0) { 525 | kill(pid, SIGKILL); // kill the fork 526 | return FALSE; 527 | } 528 | 529 | void *checksum = malloc(pages * 20); // 160 bits for each hash (SHA1) 530 | uint8_t buf_d[0x1000]; // create a single page buffer 531 | uint8_t *buf = &buf_d[0]; // store the location of the buffer 532 | 533 | VERBOSE(@"dumping binary: preparing to dump"); 534 | 535 | // we should only have to write and perform checksums on data that changes 536 | uint32_t togo = crypt.cryptsize + crypt.cryptoff; 537 | uint32_t total = togo; 538 | uint32_t pages_d = 0; 539 | BOOL header = TRUE; 540 | 541 | // write the header 542 | fsetpos(target, &topPosition); 543 | 544 | // in iOS 4.3+, ASLR can be enabled by developers by setting the MH_PIE flag in 545 | // the mach header flags. this will randomly offset the location of the __TEXT 546 | // segment, making it slightly difficult to identify the location of the 547 | // decrypted pages. instead of disabling this flag in the original binary 548 | // (which is slow, requires resigning, and requires reverting to the original 549 | // binary after cracking) we instead manually identify the vm regions which 550 | // contain the header and subsequent decrypted executable code. 551 | if (mach.flags & MH_PIE) { 552 | VERBOSE(@"dumping binary: ASLR enabled, identifying dump location dynamically"); 553 | // perform checks on vm regions 554 | memory_object_name_t object; 555 | vm_region_basic_info_data_t info; 556 | //mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; 557 | mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; 558 | mach_vm_address_t region_start = 0; 559 | mach_vm_size_t region_size = 0; 560 | vm_region_flavor_t flavor = VM_REGION_BASIC_INFO; 561 | err = 0; 562 | 563 | while (err == KERN_SUCCESS) { 564 | err = mach_vm_region(port, ®ion_start, ®ion_size, flavor, (vm_region_info_t) &info, &info_count, &object); 565 | NSLog(@"64-bit Region Size: %llu %u", region_size, crypt.cryptsize); 566 | 567 | if (region_size == crypt.cryptsize) { 568 | break; 569 | } 570 | __text_start = region_start; 571 | region_start += region_size; 572 | region_size = 0; 573 | } 574 | if (err != KERN_SUCCESS) { 575 | NSLog(@"mach_vm_error: %u", err); 576 | free(checksum); 577 | kill(pid, SIGKILL); 578 | ERROR(@"ASLR is enabled and we could not identify the decrypted memory region.\n"); 579 | return FALSE; 580 | 581 | } 582 | } 583 | 584 | uint32_t headerProgress = sizeof(struct mach_header); 585 | uint32_t i_lcmd = 0; 586 | 587 | // overdrive dylib load command size 588 | uint32_t overdrive_size = sizeof(OVERDRIVE_DYLIB_PATH) + sizeof(struct dylib_command); 589 | overdrive_size += sizeof(long) - (overdrive_size % sizeof(long)); // load commands like to be aligned by long 590 | 591 | VERBOSE(@"dumping binary: performing dump"); 592 | 593 | while (togo > 0) { 594 | // get a percentage for the progress bar 595 | PERCENT((int)ceil((((double)total - togo) / (double)total) * 100)); 596 | // move an entire page into memory (we have to move an entire page regardless of whether it's a resultant or not) 597 | if((err = mach_vm_read_overwrite(port, (mach_vm_address_t) __text_start + (pages_d * 0x1000), (vm_size_t) 0x1000, (pointer_t) buf, &local_size)) != KERN_SUCCESS) { 598 | NSLog(@"dum_error: %u", err); 599 | VERBOSE(@"dumping binary: failed to dump a page"); 600 | free(checksum); // free checksum table 601 | kill(pid, SIGKILL); // kill fork 602 | return FALSE; 603 | } 604 | 605 | if (header) { 606 | // is this the first header page? 607 | if (i_lcmd == 0) { 608 | // is overdrive enabled? 609 | if (overdrive_enabled) { 610 | // prepare the mach header for the new load command (overdrive dylib) 611 | ((struct mach_header *)buf)->ncmds += 1; 612 | ((struct mach_header *)buf)->sizeofcmds += overdrive_size; 613 | VERBOSE(@"dumping binary: patched mach header (overdrive)"); 614 | } 615 | } 616 | // iterate over the header (or resume iteration) 617 | void *curloc = buf + headerProgress; 618 | for (;i_lcmdcmdsize; 630 | } 631 | 632 | if (l_cmd->cmd == LC_ENCRYPTION_INFO) { 633 | struct encryption_info_command *newcrypt = (struct encryption_info_command *) curloc; 634 | newcrypt->cryptid = 0; // change the cryptid to 0 635 | VERBOSE(@"dumping binary: patched cryptid"); 636 | } else if (l_cmd->cmd == LC_SEGMENT) { 637 | //printf("lc segemn yo\n"); 638 | struct segment_command *newseg = (struct segment_command *) curloc; 639 | if (newseg->fileoff == 0 && newseg->filesize > 0) { 640 | // is overdrive enabled? this is __TEXT 641 | if (overdrive_enabled) { 642 | // maxprot so that overdrive can change the __TEXT protection & 643 | // cryptid in realtime 644 | newseg->maxprot |= VM_PROT_ALL; 645 | VERBOSE(@"dumping binary: patched maxprot (overdrive)"); 646 | } 647 | } 648 | } 649 | curloc += lcmd_size; 650 | if ((void *)curloc >= (void *)buf + 0x1000) { 651 | //printf("skipped pass the haeder yo\n"); 652 | // we are currently extended past the header page 653 | // offset for the next round: 654 | headerProgress = (((void *)curloc - (void *)buf) % 0x1000); 655 | // prevent attaching overdrive dylib by skipping 656 | goto skipoverdrive; 657 | } 658 | } 659 | // is overdrive enabled? 660 | if (overdrive_enabled) { 661 | // add the overdrive dylib as long as we have room 662 | if ((int8_t*)(curloc + overdrive_size) < (int8_t*)(buf + 0x1000)) { 663 | VERBOSE(@"dumping binary: attaching overdrive DYLIB (overdrive)"); 664 | struct dylib_command *overdrive_dyld = (struct dylib_command *) curloc; 665 | overdrive_dyld->cmd = LC_LOAD_DYLIB; 666 | overdrive_dyld->cmdsize = overdrive_size; 667 | overdrive_dyld->dylib.compatibility_version = OVERDRIVE_DYLIB_COMPATIBILITY_VERSION; 668 | overdrive_dyld->dylib.current_version = OVERDRIVE_DYLIB_CURRENT_VER; 669 | overdrive_dyld->dylib.timestamp = 2; 670 | overdrive_dyld->dylib.name.offset = sizeof(struct dylib_command); 671 | #ifndef __LP64__ 672 | overdrive_dyld->dylib.name.ptr = (char *) sizeof(struct dylib_command); 673 | #endif 674 | char *p = (char *) overdrive_dyld + overdrive_dyld->dylib.name.offset; 675 | strncpy(p, OVERDRIVE_DYLIB_PATH, sizeof(OVERDRIVE_DYLIB_PATH)); 676 | } 677 | } 678 | header = FALSE; 679 | } 680 | skipoverdrive: 681 | //printf("attemtping to write to binary\n"); 682 | fwrite(buf, 0x1000, 1, target); // write the new data to the target 683 | sha1(checksum + (20 * pages_d), buf, 0x1000); // perform checksum on the page 684 | //printf("doing checksum yo\n"); 685 | togo -= 0x1000; // remove a page from the togo 686 | //printf("togo yo %u\n", togo); 687 | pages_d += 1; // increase the amount of completed pages 688 | } 689 | 690 | VERBOSE(@"dumping binary: writing new checksum"); 691 | 692 | // nice! now let's write the new checksum data 693 | fseek(target, begin + CFSwapInt32(directory.hashOffset), SEEK_SET); // go to the hash offset 694 | fwrite(checksum, 20*pages_d, 1, target); // write the hashes (ONLY for the amount of pages modified) 695 | 696 | free(checksum); // free checksum table from memory 697 | kill(pid, SIGKILL); // kill the fork 698 | } 699 | stop_bar(); 700 | return TRUE; 701 | } 702 | 703 | #endif 704 | 705 | void sha1(uint8_t *hash, uint8_t *data, unsigned int size) { 706 | SHA1Context context; 707 | SHA1Reset(&context); 708 | SHA1Input(&context, data, size); 709 | SHA1Result(&context, hash); 710 | } -------------------------------------------------------------------------------- /src/main.mm: -------------------------------------------------------------------------------- 1 | /* 2 | ___ _ _ _ 3 | / __\ |_ _| |_ ___| |__ 4 | / / | | | | | __/ __| '_ \ 5 | / /___| | |_| | || (__| | | | 6 | \____/|_|\__,_|\__\___|_| |_| 7 | 8 | -------------------------------- 9 | High-Speed iOS Decryption System 10 | -------------------------------- 11 | 12 | Authors: 13 | 14 | dissident - The original creator of Clutch (pre 1.2.6) 15 | Nighthawk - Code contributor (pre 1.2.6) 16 | Rastignac - Inspiration and genius 17 | TheSexyPenguin - Inspiration 18 | dildog - Refactoring and code cleanup (2.0) 19 | 20 | */ 21 | 22 | /* 23 | * Includes 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #import "Foundation/Foundation.h" 32 | 33 | #import "applist.h" 34 | #import "Cracker.h" 35 | #import "Packager.h" 36 | #import "Application.h" 37 | 38 | /* 39 | * Configuration 40 | */ 41 | 42 | #define CLUTCH_TITLE "Clutch" 43 | #define CLUTCH_VERSION "v2.0" 44 | #define CLUTCH_RELEASE "ALPHA 2" 45 | 46 | /* 47 | * Prototypes 48 | */ 49 | 50 | void print_failures(NSArray *failures, NSArray *successes); 51 | int iterate_crack(NSArray *apps, NSMutableArray *successes, NSMutableArray *failures); 52 | int cmd_version(void); 53 | int cmd_help(void); 54 | int cmd_crack_all(void); 55 | int cmd_crack_updated(void); 56 | int cmd_flush_cache(void); 57 | int cmd_crack_exe(NSString *path); 58 | int cmd_list_applications(void); 59 | int cmd_crack_application(Application *application); 60 | 61 | /* 62 | * Commands 63 | */ 64 | 65 | 66 | int cmd_version(void) 67 | { 68 | printf("%s %s (%s)\n",CLUTCH_TITLE, CLUTCH_VERSION,CLUTCH_RELEASE); 69 | 70 | return 0; 71 | } 72 | 73 | int cmd_help(void) 74 | { 75 | cmd_version(); 76 | 77 | printf("-----------------------------\n"); 78 | //printf("-c Runs configuration utility\n"); 79 | printf("-x Crack specific executable\n"); 80 | printf("-a Cracks all applications\n"); 81 | printf("-u Cracks updated applications\n"); 82 | printf("-f Flush/clear cache\n"); 83 | printf("-v Shows version\n"); 84 | printf("-h,-? Shows this help\n"); 85 | printf("\n"); 86 | 87 | return 0; 88 | } 89 | 90 | // print_failures() 91 | // prints the list of things that succeeded and things that failed 92 | void print_failures(NSArray *successes, NSArray *failures) 93 | { 94 | if(successes && [successes count]>0) 95 | { 96 | printf("Success:\n"); 97 | 98 | NSEnumerator *e = [successes objectEnumerator]; 99 | while(NSString *app = [e nextObject]) 100 | { 101 | printf("%s\n",[app UTF8String]); 102 | } 103 | } 104 | if(failures && [failures count]>0) 105 | { 106 | printf("Failure:\n"); 107 | 108 | NSEnumerator *e = [failures objectEnumerator]; 109 | while(NSString *app = [e nextObject]) 110 | { 111 | printf("%s\n",[app UTF8String]); 112 | } 113 | } 114 | } 115 | 116 | 117 | // iterate_crack() 118 | // iterates over all of the apps in the NSArray list, 119 | // prepares the app, and cracks it. 120 | // returns a list of successes and failures 121 | 122 | int iterate_crack(NSArray *apps, NSMutableArray *successes, NSMutableArray *failures) 123 | { 124 | // Iterate over all applications 125 | NSEnumerator *e = [apps objectEnumerator]; 126 | while(Application *app = [e nextObject]) 127 | { 128 | // Prepare this application from the installed app 129 | Cracker *cracker=[[Cracker alloc] init]; 130 | 131 | NSMutableString *description=[[NSMutableString alloc] init]; 132 | [cracker prepareFromInstalledApp:app returnDescription:description]; 133 | 134 | 135 | if([cracker execute]) 136 | { 137 | [successes addObject:description]; 138 | } 139 | else 140 | { 141 | [failures addObject:description]; 142 | } 143 | 144 | // Repackage IPA file 145 | Packager *packager=[[Packager alloc] init]; 146 | [packager pack_from_source:app.baseDirectory 147 | with_overlay:[cracker getOutputFolder]]; 148 | } 149 | return 0; 150 | } 151 | 152 | int cmd_crack_all(void) 153 | { 154 | // Get list of all applications 155 | //NSArray *all_applications = get_application_list(FALSE, FALSE); 156 | NSArray *all_applications = [applist listApplications]; 157 | 158 | // Create list for failures and successes 159 | NSMutableArray *failures = [[NSMutableArray alloc] init]; 160 | NSMutableArray *successes = [[NSMutableArray alloc] init]; 161 | 162 | // Iterate over all applications 163 | int ret = iterate_crack(all_applications, successes, failures); 164 | 165 | // Print failures and success status 166 | print_failures(successes,failures); 167 | 168 | [failures release]; 169 | [successes release]; 170 | 171 | return ret; 172 | } 173 | 174 | int cmd_crack_updated(void) 175 | { 176 | // Get list of updated applications 177 | NSArray *update_applications = [applist listApplications]; 178 | 179 | // Create list for failures and successes 180 | NSMutableArray *failures=[[NSMutableArray alloc] init]; 181 | NSMutableArray *successes=[[NSMutableArray alloc] init]; 182 | 183 | // Iterate over all applications 184 | int ret = iterate_crack(update_applications, successes, failures); 185 | 186 | // Print failures and success status 187 | print_failures(successes,failures); 188 | 189 | [failures release]; 190 | [successes release]; 191 | 192 | return ret; 193 | } 194 | 195 | 196 | int cmd_crack_exe(NSString *path) 197 | { 198 | // Create list for failures and successes 199 | NSMutableArray *failures = [[NSMutableArray alloc] init]; 200 | NSMutableArray *successes = [[NSMutableArray alloc] init]; 201 | 202 | // Prepare this application from the installed app 203 | Cracker *cracker = [[Cracker alloc] init]; 204 | 205 | NSMutableString *description=[[NSMutableString alloc] init]; 206 | [cracker prepareFromSpecificExecutable:path returnDescription:description]; 207 | 208 | int ret = 0; 209 | if([cracker execute]) 210 | { 211 | [successes addObject:description]; 212 | [description release]; 213 | ret = 0; 214 | } 215 | else 216 | { 217 | [failures addObject:description]; 218 | [description release]; 219 | ret = 1; 220 | } 221 | 222 | // Repackage IPA file 223 | Packager *packager=[[Packager alloc] init]; 224 | [packager packFromSource:[path stringByDeletingLastPathComponent] 225 | withOverlay:[cracker getOutputFolder]]; 226 | 227 | // Print failures and success status 228 | print_failures(successes,failures); 229 | 230 | [cracker release]; 231 | [packager release]; 232 | [failures release]; 233 | [successes release]; 234 | 235 | return ret; 236 | } 237 | 238 | int cmd_crack_application(Application *application) 239 | { 240 | NSMutableArray *successes = [[NSMutableArray alloc] init]; 241 | NSMutableArray *failures = [[NSMutableArray alloc] init]; 242 | 243 | Cracker *cracker = [[Cracker alloc] init]; 244 | 245 | int ret = 0; 246 | 247 | if (![cracker crackApplication:application]) 248 | { 249 | [failures addObject:application.displayName]; 250 | 251 | ret = 1; 252 | } 253 | else 254 | { 255 | [successes addObject:application.displayName]; 256 | 257 | ret = 0; 258 | } 259 | 260 | print_failures(successes, failures); 261 | 262 | [cracker release]; 263 | [successes release]; 264 | [failures release]; 265 | 266 | return ret; 267 | } 268 | 269 | 270 | int cmd_flush_cache(void) 271 | { 272 | return 0; 273 | } 274 | 275 | int cmd_list_applications(void) 276 | { 277 | NSArray *list = [applist listApplications]; 278 | NSEnumerator *e = [list objectEnumerator]; 279 | Application *application; 280 | int index = 1; 281 | 282 | printf("\n"); 283 | 284 | while (application = [e nextObject]) 285 | { 286 | printf("%d) \033[1;3%dm%s\033[0m \n", index, 5 + ((index + 1) % 2), [application.displayName UTF8String]); 287 | index++; 288 | } 289 | 290 | printf("\n"); 291 | 292 | return 0; 293 | } 294 | 295 | /* 296 | * Main Function 297 | */ 298 | 299 | int main(int argc, const char *argv[]) 300 | { 301 | // Prepare command line options 302 | int ret=0; 303 | 304 | printf("\n"); 305 | 306 | // check that we are root 307 | if (getuid() != 0) 308 | { 309 | printf("You need to be root to use Clutch.\n"); 310 | 311 | return 1; 312 | } 313 | 314 | // this line gives me 315 | NSArray *arguments = [[NSProcessInfo processInfo] arguments]; 316 | 317 | int cnt = (int)[arguments count]; 318 | 319 | for(int idx = 0; idx < cnt; idx++) 320 | { 321 | // Process each command line option 322 | NSString *arg = [arguments objectAtIndex:idx]; 323 | 324 | if([arg isEqualToString:@"/usr/bin/Clutch"] && [arguments count] == 1) 325 | { 326 | // show help & list applications 327 | cmd_help(); 328 | cmd_list_applications(); 329 | } 330 | else if ([arg isEqualToString:@"-a"] || [arg isEqualToString:@"-all"]) 331 | { 332 | // Crack all applications 333 | ret = cmd_crack_all(); 334 | } 335 | else if([arg isEqualToString:@"-u"] || [arg isEqualToString:@"-updates"]) 336 | { 337 | // Crack updated applications 338 | ret = cmd_crack_updated(); 339 | } 340 | else if([arg isEqualToString:@"-f"] || [arg isEqualToString:@"-flush"]) 341 | { 342 | // Flush caches 343 | ret = cmd_flush_cache(); 344 | } 345 | else if([arg isEqualToString:@"-v"] || [arg isEqualToString:@"-version"]) 346 | { 347 | // Display version string 348 | ret = cmd_version(); 349 | } 350 | else if([arg isEqualToString:@"-x"] || [arg isEqualToString:@"-executable"]) 351 | { 352 | // Crack specific executable 353 | 354 | // Get path argument 355 | NSString *path = [arguments objectAtIndex:idx + 1]; 356 | if(path == nil) 357 | { 358 | printf("-x requires a 'path' argument"); 359 | return 1; 360 | } 361 | 362 | ret = cmd_crack_exe(path); 363 | } 364 | else if([arg isEqualToString:@"-h"] || [arg isEqualToString:@"-?"] || [arg isEqualToString:@"-help"]) 365 | { 366 | // Display help 367 | ret = cmd_help(); 368 | } 369 | else 370 | { 371 | NSArray *list = [applist listApplications]; 372 | int index = [arg intValue]; 373 | 374 | if (index == 0) 375 | { 376 | continue; 377 | } else 378 | { 379 | NSLog(@"index: %d", index); 380 | 381 | cmd_crack_application(list[index - 1]); 382 | 383 | return 0; 384 | } 385 | 386 | // Unknown command line option 387 | printf ("unknown option '%s'\n", [arg UTF8String]); 388 | return 1; 389 | } 390 | } 391 | 392 | return ret; 393 | } 394 | 395 | -------------------------------------------------------------------------------- /src/out.mm: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // out.mm 4 | // Clutch 5 | // 6 | // Created by Ninja on 04/01/2014. 7 | // 8 | // 9 | 10 | #import "out.h" 11 | #import 12 | #import 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | int determine_screen_width() 20 | { 21 | int fd; 22 | struct winsize wsz; 23 | 24 | fd = fileno(stderr); 25 | 26 | if (ioctl(fd, TIOCGWINSZ, &wsz) < 0) 27 | { 28 | return 0; 29 | } 30 | 31 | return wsz.ws_col; 32 | } 33 | 34 | int bar_mode = 0; // 0 = inactive, 1 = active 35 | int bar_percent = -1; // negative is no bar 36 | char bar_msg[200]; // msg buffer 37 | 38 | void progress_percent(int percent) { 39 | if ((bar_percent < percent - 5) || (percent == 100) || (percent < 0)) { 40 | bar_percent = percent; 41 | print_bar(); 42 | } 43 | } 44 | 45 | void print_bar() 46 | { 47 | if (bar_mode == 1) 48 | { 49 | printf("\033[0G\033[J"); 50 | } 51 | 52 | bar_mode = 1; 53 | 54 | int width = determine_screen_width(); 55 | 56 | if (bar_percent < 0) 57 | { 58 | if (strlen(bar_msg) > (width - 5)) 59 | { 60 | strncpy(bar_msg + width - 5, "...", 4); 61 | } 62 | 63 | printf("%s", bar_msg); 64 | fflush(stdout); 65 | } 66 | else 67 | { 68 | int pstart = floor(width / 2); 69 | int pwidth = width - pstart; 70 | int barwidth = ceil((pwidth - 7) * (((double)bar_percent) / 100)); 71 | int spacewidth = (pwidth - 7) - barwidth; 72 | 73 | if (strlen(bar_msg) > (pstart - 5)) { 74 | strncpy(bar_msg + pstart - 5, "...", 4); 75 | } 76 | 77 | printf("%s [", bar_msg); 78 | 79 | for (int i = 0; i < barwidth; i++) { 80 | printf("="); 81 | } 82 | 83 | for (int i = 0; i < spacewidth; i++) { 84 | printf(" "); 85 | } 86 | 87 | printf("] %d%%", bar_percent); 88 | 89 | fflush(stdout); 90 | } 91 | } 92 | 93 | void pause_bar(void) { 94 | if (bar_mode == 1) { 95 | printf("\033[0G\033[J"); 96 | fflush(stdout); 97 | } 98 | 99 | bar_mode = 0; 100 | } 101 | 102 | void stop_bar(void) { 103 | if (bar_mode == 1) { 104 | printf("\033[0G\033[J"); 105 | fflush(stdout); 106 | bar_mode = 0; 107 | bar_percent = -1; 108 | } 109 | } -------------------------------------------------------------------------------- /xcode/.idea/.name: -------------------------------------------------------------------------------- 1 | Clutch -------------------------------------------------------------------------------- /xcode/.idea/dictionaries/dildog.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /xcode/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /xcode/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /xcode/.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /xcode/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /xcode/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /xcode/.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /xcode/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /xcode/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 58 | 59 | 60 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | Spelling 73 | 74 | 75 | 76 | 77 | SpellCheckingInspection 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 117 | 118 | 121 | 122 | 125 | 126 | 127 | 128 | 131 | 132 | 135 | 136 | 139 | 140 | 141 | 142 | 145 | 146 | 149 | 150 | 153 | 154 | 157 | 158 | 159 | 160 | 163 | 164 | 167 | 168 | 171 | 172 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 226 | 227 | 228 | 229 | 1387903215426 230 | 1387903215426 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 260 | 261 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | -------------------------------------------------------------------------------- /xcode/.idea/xcode.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /xcode/.idea/xcode.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /xcode/Clutch.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 43A8C399186769F6004C3DBA /* applist.mm in Sources */ = {isa = PBXBuildFile; fileRef = 43A8C398186769F6004C3DBA /* applist.mm */; }; 11 | 43A8C39F186797A1004C3DBA /* Cracker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 43A8C39E186797A1004C3DBA /* Cracker.mm */; }; 12 | 43A8C3A21867980C004C3DBA /* Packager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 43A8C3A11867980C004C3DBA /* Packager.mm */; }; 13 | 43AD3B62186740BA00B3EF9B /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 43AD3B61186740BA00B3EF9B /* main.mm */; }; 14 | E994B8A51876F957002732C4 /* Application.mm in Sources */ = {isa = PBXBuildFile; fileRef = E994B8A41876F957002732C4 /* Application.mm */; }; 15 | E994B8AC1878326D002732C4 /* dump.m in Sources */ = {isa = PBXBuildFile; fileRef = E994B8AB1878326D002732C4 /* dump.m */; }; 16 | E994B8B01878330C002732C4 /* sha1.c in Sources */ = {isa = PBXBuildFile; fileRef = E994B8AE1878330C002732C4 /* sha1.c */; }; 17 | E994B8B418799366002732C4 /* out.mm in Sources */ = {isa = PBXBuildFile; fileRef = E994B8B3187839EB002732C4 /* out.mm */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 43A60AAB1867383D00F44BA4 /* Clutch */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Clutch; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 43A60AB01867383D00F44BA4 /* control.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = control.txt; path = Package/DEBIAN/control.txt; sourceTree = ""; }; 23 | 43A60AB11867383D00F44BA4 /* control */ = {isa = PBXFileReference; lastKnownFileType = text; name = control; path = Package/DEBIAN/control; sourceTree = ""; }; 24 | 43A60AB31867383D00F44BA4 /* PackageVersion.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = PackageVersion.plist; sourceTree = ""; }; 25 | 43A60AB61867383D00F44BA4 /* 0xdeadfa11 */ = {isa = PBXFileReference; lastKnownFileType = text; name = 0xdeadfa11; path = Package/usr/bin/0xdeadfa11; sourceTree = ""; }; 26 | 43A8C398186769F6004C3DBA /* applist.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = applist.mm; path = ../../src/applist.mm; sourceTree = ""; }; 27 | 43A8C39C18676A80004C3DBA /* applist.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = applist.h; path = ../../include/applist.h; sourceTree = ""; }; 28 | 43A8C39E186797A1004C3DBA /* Cracker.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Cracker.mm; path = ../../src/Cracker.mm; sourceTree = ""; }; 29 | 43A8C3A11867980C004C3DBA /* Packager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Packager.mm; path = ../../src/Packager.mm; sourceTree = ""; }; 30 | 43A8C3A31867988C004C3DBA /* Cracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Cracker.h; path = ../../include/Cracker.h; sourceTree = ""; }; 31 | 43A8C3A41867988C004C3DBA /* Packager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Packager.h; path = ../../include/Packager.h; sourceTree = ""; }; 32 | 43AD3B61186740BA00B3EF9B /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = main.mm; path = ../../src/main.mm; sourceTree = ""; }; 33 | E994B8A41876F957002732C4 /* Application.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Application.mm; path = ../../src/Application.mm; sourceTree = ""; }; 34 | E994B8A718777B17002732C4 /* Application.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Application.h; path = ../../include/Application.h; sourceTree = ""; }; 35 | E994B8AB1878326D002732C4 /* dump.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = dump.m; path = ../../src/dump.m; sourceTree = ""; }; 36 | E994B8AD18783274002732C4 /* dump.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = dump.h; path = ../../include/dump.h; sourceTree = ""; }; 37 | E994B8AE1878330C002732C4 /* sha1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sha1.c; path = ../../external/sha1.c; sourceTree = ""; }; 38 | E994B8AF1878330C002732C4 /* sha1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sha1.h; path = ../../external/sha1.h; sourceTree = ""; }; 39 | E994B8B218783730002732C4 /* out.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = out.h; path = ../../include/out.h; sourceTree = ""; }; 40 | E994B8B3187839EB002732C4 /* out.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = out.mm; path = ../../src/out.mm; sourceTree = ""; }; 41 | /* End PBXFileReference section */ 42 | 43 | /* Begin PBXFrameworksBuildPhase section */ 44 | 43A60AA81867383D00F44BA4 /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | /* End PBXFrameworksBuildPhase section */ 52 | 53 | /* Begin PBXGroup section */ 54 | 43A60AA21867383D00F44BA4 = { 55 | isa = PBXGroup; 56 | children = ( 57 | 43A60AAD1867383D00F44BA4 /* Clutch */, 58 | 43A60AAC1867383D00F44BA4 /* Products */, 59 | ); 60 | sourceTree = ""; 61 | }; 62 | 43A60AAC1867383D00F44BA4 /* Products */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 43A60AAB1867383D00F44BA4 /* Clutch */, 66 | ); 67 | name = Products; 68 | sourceTree = ""; 69 | }; 70 | 43A60AAD1867383D00F44BA4 /* Clutch */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | E994B8B118783310002732C4 /* external */, 74 | 43A8C39B18676A5C004C3DBA /* include */, 75 | 43A8C39A18676A58004C3DBA /* src */, 76 | 43A60AAE1867383D00F44BA4 /* Package */, 77 | 43A60AB21867383D00F44BA4 /* Supporting Files */, 78 | ); 79 | path = Clutch; 80 | sourceTree = ""; 81 | }; 82 | 43A60AAE1867383D00F44BA4 /* Package */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 43A60AAF1867383D00F44BA4 /* DEBIAN */, 86 | 43A60AB41867383D00F44BA4 /* usr */, 87 | ); 88 | name = Package; 89 | sourceTree = ""; 90 | }; 91 | 43A60AAF1867383D00F44BA4 /* DEBIAN */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 43A60AB01867383D00F44BA4 /* control.txt */, 95 | 43A60AB11867383D00F44BA4 /* control */, 96 | ); 97 | name = DEBIAN; 98 | sourceTree = ""; 99 | }; 100 | 43A60AB21867383D00F44BA4 /* Supporting Files */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 43A60AB31867383D00F44BA4 /* PackageVersion.plist */, 104 | ); 105 | name = "Supporting Files"; 106 | sourceTree = ""; 107 | }; 108 | 43A60AB41867383D00F44BA4 /* usr */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 43A60AB51867383D00F44BA4 /* bin */, 112 | ); 113 | name = usr; 114 | sourceTree = ""; 115 | }; 116 | 43A60AB51867383D00F44BA4 /* bin */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 43A60AB61867383D00F44BA4 /* 0xdeadfa11 */, 120 | ); 121 | name = bin; 122 | sourceTree = ""; 123 | }; 124 | 43A8C39A18676A58004C3DBA /* src */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 43A8C39E186797A1004C3DBA /* Cracker.mm */, 128 | 43A8C3A11867980C004C3DBA /* Packager.mm */, 129 | 43A8C398186769F6004C3DBA /* applist.mm */, 130 | E994B8A41876F957002732C4 /* Application.mm */, 131 | E994B8AB1878326D002732C4 /* dump.m */, 132 | 43AD3B61186740BA00B3EF9B /* main.mm */, 133 | E994B8B3187839EB002732C4 /* out.mm */, 134 | ); 135 | name = src; 136 | sourceTree = ""; 137 | }; 138 | 43A8C39B18676A5C004C3DBA /* include */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | 43A8C3A31867988C004C3DBA /* Cracker.h */, 142 | 43A8C3A41867988C004C3DBA /* Packager.h */, 143 | 43A8C39C18676A80004C3DBA /* applist.h */, 144 | E994B8A718777B17002732C4 /* Application.h */, 145 | E994B8AD18783274002732C4 /* dump.h */, 146 | E994B8B218783730002732C4 /* out.h */, 147 | ); 148 | name = include; 149 | sourceTree = ""; 150 | }; 151 | E994B8B118783310002732C4 /* external */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | E994B8AE1878330C002732C4 /* sha1.c */, 155 | E994B8AF1878330C002732C4 /* sha1.h */, 156 | ); 157 | name = external; 158 | sourceTree = ""; 159 | }; 160 | /* End PBXGroup section */ 161 | 162 | /* Begin PBXNativeTarget section */ 163 | 43A60AAA1867383D00F44BA4 /* Clutch */ = { 164 | isa = PBXNativeTarget; 165 | buildConfigurationList = 43A60ABB1867383D00F44BA4 /* Build configuration list for PBXNativeTarget "Clutch" */; 166 | buildPhases = ( 167 | 43A60AA71867383D00F44BA4 /* Sources */, 168 | 43A60AA81867383D00F44BA4 /* Frameworks */, 169 | 43A60AA91867383D00F44BA4 /* ShellScript */, 170 | ); 171 | buildRules = ( 172 | ); 173 | dependencies = ( 174 | ); 175 | name = Clutch; 176 | productName = Clutch; 177 | productReference = 43A60AAB1867383D00F44BA4 /* Clutch */; 178 | productType = "com.apple.product-type.tool"; 179 | }; 180 | /* End PBXNativeTarget section */ 181 | 182 | /* Begin PBXProject section */ 183 | 43A60AA31867383D00F44BA4 /* Project object */ = { 184 | isa = PBXProject; 185 | attributes = { 186 | LastUpgradeCheck = 0500; 187 | }; 188 | buildConfigurationList = 43A60AA61867383D00F44BA4 /* Build configuration list for PBXProject "Clutch" */; 189 | compatibilityVersion = "Xcode 3.2"; 190 | developmentRegion = English; 191 | hasScannedForEncodings = 0; 192 | knownRegions = ( 193 | en, 194 | ); 195 | mainGroup = 43A60AA21867383D00F44BA4; 196 | productRefGroup = 43A60AAC1867383D00F44BA4 /* Products */; 197 | projectDirPath = ""; 198 | projectRoot = ""; 199 | targets = ( 200 | 43A60AAA1867383D00F44BA4 /* Clutch */, 201 | ); 202 | }; 203 | /* End PBXProject section */ 204 | 205 | /* Begin PBXShellScriptBuildPhase section */ 206 | 43A60AA91867383D00F44BA4 /* ShellScript */ = { 207 | isa = PBXShellScriptBuildPhase; 208 | buildActionMask = 2147483647; 209 | files = ( 210 | ); 211 | inputPaths = ( 212 | ); 213 | outputPaths = ( 214 | ); 215 | runOnlyForDeploymentPostprocessing = 0; 216 | shellPath = /bin/sh; 217 | shellScript = "/opt/iOSOpenDev/bin/iosod --xcbp"; 218 | }; 219 | /* End PBXShellScriptBuildPhase section */ 220 | 221 | /* Begin PBXSourcesBuildPhase section */ 222 | 43A60AA71867383D00F44BA4 /* Sources */ = { 223 | isa = PBXSourcesBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | E994B8B418799366002732C4 /* out.mm in Sources */, 227 | 43AD3B62186740BA00B3EF9B /* main.mm in Sources */, 228 | E994B8AC1878326D002732C4 /* dump.m in Sources */, 229 | 43A8C39F186797A1004C3DBA /* Cracker.mm in Sources */, 230 | 43A8C399186769F6004C3DBA /* applist.mm in Sources */, 231 | E994B8B01878330C002732C4 /* sha1.c in Sources */, 232 | E994B8A51876F957002732C4 /* Application.mm in Sources */, 233 | 43A8C3A21867980C004C3DBA /* Packager.mm in Sources */, 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | }; 237 | /* End PBXSourcesBuildPhase section */ 238 | 239 | /* Begin XCBuildConfiguration section */ 240 | 43A60AB91867383D00F44BA4 /* Debug */ = { 241 | isa = XCBuildConfiguration; 242 | buildSettings = { 243 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 244 | CLANG_ENABLE_MODULES = YES; 245 | COPY_PHASE_STRIP = NO; 246 | EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES = "*.nib *.lproj *.gch (*) .DS_Store CVS .svn .git .hg *.xcodeproj *.xcode *.pbproj *.pbxproj"; 247 | FRAMEWORK_SEARCH_PATHS = ( 248 | "$(iOSOpenDevPath)/frameworks/**", 249 | "$(SDKROOT)/System/Library/PrivateFrameworks", 250 | ); 251 | GCC_C_LANGUAGE_STANDARD = gnu99; 252 | GCC_DYNAMIC_NO_PIC = NO; 253 | GCC_OPTIMIZATION_LEVEL = 0; 254 | GCC_PREPROCESSOR_DEFINITIONS = ( 255 | "DEBUG=1", 256 | "$(inherited)", 257 | ); 258 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 259 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 260 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 261 | GCC_WARN_UNUSED_VARIABLE = YES; 262 | HEADER_SEARCH_PATHS = ( 263 | "$(iOSOpenDevPath)/include/**", 264 | ../include, 265 | ); 266 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 267 | LIBRARY_SEARCH_PATHS = "$(iOSOpenDevPath)/lib/**"; 268 | ONLY_ACTIVE_ARCH = NO; 269 | SDKROOT = iphoneos; 270 | TARGETED_DEVICE_FAMILY = "1,2"; 271 | VALIDATE_PRODUCT = NO; 272 | iOSOpenDevPath = /opt/iOSOpenDev; 273 | }; 274 | name = Debug; 275 | }; 276 | 43A60ABA1867383D00F44BA4 /* Release */ = { 277 | isa = XCBuildConfiguration; 278 | buildSettings = { 279 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 280 | CLANG_ENABLE_MODULES = YES; 281 | COPY_PHASE_STRIP = YES; 282 | EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES = "*.nib *.lproj *.gch (*) .DS_Store CVS .svn .git .hg *.xcodeproj *.xcode *.pbproj *.pbxproj"; 283 | FRAMEWORK_SEARCH_PATHS = ( 284 | "$(iOSOpenDevPath)/frameworks/**", 285 | "$(SDKROOT)/System/Library/PrivateFrameworks", 286 | ); 287 | GCC_C_LANGUAGE_STANDARD = gnu99; 288 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 289 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 290 | GCC_WARN_UNUSED_VARIABLE = YES; 291 | HEADER_SEARCH_PATHS = ( 292 | "$(iOSOpenDevPath)/include/**", 293 | ../include, 294 | ); 295 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 296 | LIBRARY_SEARCH_PATHS = "$(iOSOpenDevPath)/lib/**"; 297 | ONLY_ACTIVE_ARCH = NO; 298 | SDKROOT = iphoneos; 299 | TARGETED_DEVICE_FAMILY = "1,2"; 300 | VALIDATE_PRODUCT = YES; 301 | iOSOpenDevPath = /opt/iOSOpenDev; 302 | }; 303 | name = Release; 304 | }; 305 | 43A60ABC1867383D00F44BA4 /* Debug */ = { 306 | isa = XCBuildConfiguration; 307 | buildSettings = { 308 | CODE_SIGN_IDENTITY = ""; 309 | INSTALL_PATH = /usr/bin; 310 | PRODUCT_NAME = "$(TARGET_NAME)"; 311 | iOSOpenDevBuildPackageOnAnyBuild = NO; 312 | iOSOpenDevCopyOnBuild = NO; 313 | iOSOpenDevDevice = ""; 314 | iOSOpenDevInstallOnAnyBuild = NO; 315 | iOSOpenDevInstallOnProfiling = YES; 316 | iOSOpenDevRespringOnInstall = YES; 317 | iOSOpenDevUsePackageVersionPList = YES; 318 | }; 319 | name = Debug; 320 | }; 321 | 43A60ABD1867383D00F44BA4 /* Release */ = { 322 | isa = XCBuildConfiguration; 323 | buildSettings = { 324 | CODE_SIGN_IDENTITY = ""; 325 | INSTALL_PATH = /usr/bin; 326 | PRODUCT_NAME = "$(TARGET_NAME)"; 327 | iOSOpenDevBuildPackageOnAnyBuild = NO; 328 | iOSOpenDevCopyOnBuild = NO; 329 | iOSOpenDevDevice = ""; 330 | iOSOpenDevInstallOnAnyBuild = NO; 331 | iOSOpenDevInstallOnProfiling = YES; 332 | iOSOpenDevRespringOnInstall = YES; 333 | iOSOpenDevUsePackageVersionPList = YES; 334 | }; 335 | name = Release; 336 | }; 337 | /* End XCBuildConfiguration section */ 338 | 339 | /* Begin XCConfigurationList section */ 340 | 43A60AA61867383D00F44BA4 /* Build configuration list for PBXProject "Clutch" */ = { 341 | isa = XCConfigurationList; 342 | buildConfigurations = ( 343 | 43A60AB91867383D00F44BA4 /* Debug */, 344 | 43A60ABA1867383D00F44BA4 /* Release */, 345 | ); 346 | defaultConfigurationIsVisible = 0; 347 | defaultConfigurationName = Release; 348 | }; 349 | 43A60ABB1867383D00F44BA4 /* Build configuration list for PBXNativeTarget "Clutch" */ = { 350 | isa = XCConfigurationList; 351 | buildConfigurations = ( 352 | 43A60ABC1867383D00F44BA4 /* Debug */, 353 | 43A60ABD1867383D00F44BA4 /* Release */, 354 | ); 355 | defaultConfigurationIsVisible = 0; 356 | defaultConfigurationName = Release; 357 | }; 358 | /* End XCConfigurationList section */ 359 | }; 360 | rootObject = 43A60AA31867383D00F44BA4 /* Project object */; 361 | } 362 | -------------------------------------------------------------------------------- /xcode/Clutch/Package/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: com.kjcracks.Clutch 2 | Name: Clutch 3 | Version: 0.1-1 4 | Description: 5 | Section: System 6 | Depends: firmware (>= 5.0) 7 | Conflicts: 8 | Replaces: 9 | Priority: optional 10 | Architecture: iphoneos-arm 11 | Author: DilDog 12 | dev: 13 | Homepage: 14 | Depiction: 15 | Maintainer: 16 | Icon: 17 | 18 | -------------------------------------------------------------------------------- /xcode/Clutch/Package/DEBIAN/control.txt: -------------------------------------------------------------------------------- 1 | 2 | The control file in the same directory as this file is the Debian control file that is needed 3 | to build the project's Debian package that's used by APT repositories like Cydia. 4 | 5 | For more on... 6 | Debian packages, see http://www.debian.org/doc/manuals/debian-faq/ch-pkg_basics.en.html. 7 | Debian control files, see http://www.debian.org/doc/debian-policy/ch-controlfields.html. 8 | Cydia packages, see http://www.saurik.com/id/7. 9 | 10 | Note: This file (control.txt) was created only to provide an explaination about the Debian control file. This file can now be deleted. 11 | -------------------------------------------------------------------------------- /xcode/Clutch/Package/usr/bin/0xdeadfa11: -------------------------------------------------------------------------------- 1 | 2 | This file was created only to help build the project's group structure, and it can now be deleted. 3 | 4 | Why 0xdeadfa11? See http://developer.apple.com/library/ios/#technotes/tn2151/_index.html. 5 | -------------------------------------------------------------------------------- /xcode/Clutch/PackageVersion.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BugFix 6 | 7 | Major 8 | 1 9 | Minor 10 | 0 11 | PackageRevision 12 | 1 13 | Stage 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /xcode/LatestBuild: -------------------------------------------------------------------------------- 1 | /Users/thomas/Library/Developer/Xcode/DerivedData/Clutch-crxnjtemsuuijldpybhnislkiayi/Build/Products/Debug-iphoneos --------------------------------------------------------------------------------