3 // This file is part of class-dump, a utility for examining the Objective-C segment of Mach-O files.
4 // Copyright (C) 1997-2019 Steve Nygard.
12 #include <mach-o/arch.h>
14 #import "CDClassDump.h"
15 #import "CDMachOFile.h"
17 #import "CDLoadCommand.h"
18 #import "CDLCSegment.h"
20 void print_usage(void)
24 "Usage: deprotect [options] <input file> <output file>\n"
26 " where options are:\n"
27 " --arch <arch> choose a specific architecture from a universal binary (ppc, ppc64, i386, x86_64, armv6, armv7, armv7s, arm64)\n"
33 BOOL saveDeprotectedFileToPath(CDMachOFile *file, NSString *path)
35 BOOL hasProtectedSegments = NO;
36 NSMutableData *mdata = [[NSMutableData alloc] initWithData:file.data];
37 for (CDLoadCommand *command in file.loadCommands) {
38 if ([command isKindOfClass:[CDLCSegment class]]) {
39 CDLCSegment *segment = (CDLCSegment *)command;
41 if (segment.isProtected) {
42 hasProtectedSegments = YES;
43 NSRange segmentRange = NSMakeRange([segment fileoff], [segment filesize]);
44 NSUInteger flagOffset;
46 NSData *decryptedData = [segment decryptedData];
47 NSCParameterAssert([decryptedData length] == segmentRange.length);
49 [mdata replaceBytesInRange:segmentRange withBytes:[decryptedData bytes]];
50 if (segment.machOFile.uses64BitABI) {
51 flagOffset = [segment commandOffset] + offsetof(struct segment_command_64, flags);
53 flagOffset = [segment commandOffset] + offsetof(struct segment_command, flags);
56 CDMachOFileDataCursor *cursor = [[CDMachOFileDataCursor alloc] initWithFile:file offset:flagOffset];
57 uint32_t flags = [cursor readInt32];
58 if (flags != [segment flags]) {
59 fprintf(stderr, "Internal Error: flags (0x%x) does not match segment flags (0x%x).\n", flags, [segment flags]);
62 flags &= ~SG_PROTECTED_VERSION_1;
64 if (file.byteOrder == CDByteOrder_BigEndian) {
65 OSWriteBigInt32([mdata mutableBytes], flagOffset, flags);
67 OSWriteLittleInt32([mdata mutableBytes], flagOffset, flags);
73 if (hasProtectedSegments) {
74 [mdata writeToFile:path atomically:NO];
77 return hasProtectedSegments;
80 int main(int argc, char *argv[])
90 CDArch targetArch = {CPU_TYPE_ANY, CPU_TYPE_ANY};
92 struct option longopts[] = {
93 { "arch", required_argument, NULL, 'a' },
97 while ( (ch = getopt_long(argc, argv, "a:", longopts, NULL)) != -1) {
100 NSString *name = [NSString stringWithUTF8String:optarg];
101 targetArch = CDArchFromName(name);
102 if (targetArch.cputype == CPU_TYPE_ANY) {
103 fprintf(stderr, "Error: Unknown arch %s\n\n", optarg);
118 if (errorFlag || argc < 2) {
124 NSString *inputFile = [NSString stringWithFileSystemRepresentation:argv[0]];
125 NSString *outputFile = [NSString stringWithFileSystemRepresentation:argv[1]];
127 CDFile *file = [CDFile fileWithContentsOfFile:inputFile searchPathState:nil];
129 fprintf(stderr, "Error: input file is neither a Mach-O file nor a fat archive.\n");
133 CDMachOFile *thinFile = nil;
134 if ([file isKindOfClass:[CDMachOFile class]]) {
135 thinFile = (CDMachOFile *)file;
136 } else if ([file isKindOfClass:[CDFatFile class]]) {
137 if (targetArch.cputype == CPU_TYPE_ANY) {
138 if ([file bestMatchForLocalArch:&targetArch] == NO) {
139 fprintf(stderr, "Internal Error: Couldn't get local architecture.\n");
143 thinFile = [(CDFatFile *)file machOFileWithArch:targetArch];
145 const NXArchInfo *arhcInfo = NXGetArchInfoFromCpuType(targetArch.cputype, targetArch.cpusubtype);
146 fprintf(stderr, "Error: input file does not contain the '%s' arch.\n", arhcInfo->name);
150 fprintf(stderr, "Internal Error: file is neither a CDFatFile nor a CDMachOFile instance.\n");
154 BOOL hasProtectedSegments = saveDeprotectedFileToPath(thinFile, outputFile);
155 if (!hasProtectedSegments) {
156 const NXArchInfo *arhcInfo = NXGetArchInfoFromCpuType(targetArch.cputype, targetArch.cpusubtype);
157 fprintf(stderr, "Error: input file (%s arch) is not protected.\n", arhcInfo->name);