Use 10 (not 6) bits in source version fields B-E.
[class-dump.git] / deprotect.m
blobe10921b0e292c67e2e889f20309dcd4e1f047f26
1 // -*- mode: ObjC -*-
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.
6 #include <stdio.h>
7 #include <libc.h>
8 #include <unistd.h>
9 #include <getopt.h>
10 #include <stdlib.h>
11 #include <sysexits.h>
12 #include <mach-o/arch.h>
14 #import "CDClassDump.h"
15 #import "CDMachOFile.h"
16 #import "CDFatFile.h"
17 #import "CDLoadCommand.h"
18 #import "CDLCSegment.h"
20 void print_usage(void)
22     fprintf(stderr,
23             "deprotect %s\n"
24             "Usage: deprotect [options] <input file> <output file>\n"
25             "\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"
28             ,
29             CLASS_DUMP_VERSION
30        );
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;
40             
41             if (segment.isProtected) {
42                 hasProtectedSegments = YES;
43                 NSRange segmentRange = NSMakeRange([segment fileoff], [segment filesize]);
44                 NSUInteger flagOffset;
45                 
46                 NSData *decryptedData = [segment decryptedData];
47                 NSCParameterAssert([decryptedData length] == segmentRange.length);
48                 
49                 [mdata replaceBytesInRange:segmentRange withBytes:[decryptedData bytes]];
50                 if (segment.machOFile.uses64BitABI) {
51                     flagOffset = [segment commandOffset] + offsetof(struct segment_command_64, flags);
52                 } else {
53                     flagOffset = [segment commandOffset] + offsetof(struct segment_command, flags);
54                 }
55                 
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]);
60                     exit(EX_SOFTWARE);
61                 }
62                 flags &= ~SG_PROTECTED_VERSION_1;
63                 
64                 if (file.byteOrder == CDByteOrder_BigEndian) {
65                     OSWriteBigInt32([mdata mutableBytes], flagOffset, flags);
66                 } else {
67                     OSWriteLittleInt32([mdata mutableBytes], flagOffset, flags);
68                 }
69             }
70         }
71     }
72     
73     if (hasProtectedSegments) {
74         [mdata writeToFile:path atomically:NO];
75     }
76     
77     return hasProtectedSegments;
80 int main(int argc, char *argv[])
82     @autoreleasepool {
83         if (argc == 1) {
84             print_usage();
85             exit(EX_OK);
86         }
87         
88         int ch;
89         BOOL errorFlag = NO;
90         CDArch targetArch = {CPU_TYPE_ANY, CPU_TYPE_ANY};
91         
92         struct option longopts[] = {
93             { "arch", required_argument, NULL, 'a' },
94             { NULL,   0,                 NULL, 0 },
95         };
96         
97         while ( (ch = getopt_long(argc, argv, "a:", longopts, NULL)) != -1) {
98             switch (ch) {
99                 case 'a': {
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);
104                         errorFlag = YES;
105                     }
106                     break;
107                 }
108                 case '?':
109                 default:
110                     errorFlag = YES;
111                     break;
112             }
113         }
114         
115         argc -= optind;
116         argv += optind;
118         if (errorFlag || argc < 2) {
119             print_usage();
120             exit(EX_USAGE);
121         }
122         
123         {
124             NSString *inputFile = [NSString stringWithFileSystemRepresentation:argv[0]];
125             NSString *outputFile = [NSString stringWithFileSystemRepresentation:argv[1]];
126             
127             CDFile *file = [CDFile fileWithContentsOfFile:inputFile searchPathState:nil];
128             if (file == nil) {
129                 fprintf(stderr, "Error: input file is neither a Mach-O file nor a fat archive.\n");
130                 exit(EX_DATAERR);
131             }
132             
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");
140                         exit(EX_SOFTWARE);
141                     }
142                 }
143                 thinFile = [(CDFatFile *)file machOFileWithArch:targetArch];
144                 if (!thinFile) {
145                     const NXArchInfo *arhcInfo = NXGetArchInfoFromCpuType(targetArch.cputype, targetArch.cpusubtype);
146                     fprintf(stderr, "Error: input file does not contain the '%s' arch.\n", arhcInfo->name);
147                     exit(EX_DATAERR);
148                 }
149             } else {
150                 fprintf(stderr, "Internal Error: file is neither a CDFatFile nor a CDMachOFile instance.\n");
151                 exit(EX_SOFTWARE);
152             }
153             
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);
158                 exit(EX_DATAERR);
159             }
160         }
161     }
163     return 0;