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