13 #include <mach-o/loader.h>
14 #include <mach-o/fat.h>
15 #include <CommonCrypto/CommonDigest.h>
17 #define DEBUG_ASSERT_PRODUCTION_CODE 0
18 #include <AssertMacros.h>
21 * Magic numbers used by Code Signing
24 CSMAGIC_REQUIREMENT
= 0xfade0c00, /* single Requirement blob */
25 CSMAGIC_REQUIREMENTS
= 0xfade0c01, /* Requirements vector (internal requirements) */
26 CSMAGIC_CODEDIRECTORY
= 0xfade0c02, /* CodeDirectory blob */
27 CSMAGIC_EMBEDDED_SIGNATURE
= 0xfade0cc0, /* embedded form of signature data */
28 CSMAGIC_DETACHED_SIGNATURE
= 0xfade0cc1, /* multi-arch collection of embedded signatures */
30 CSSLOT_CODEDIRECTORY
= 0, /* slot index for CodeDirectory */
35 * Structure of an embedded-signature SuperBlob
37 typedef struct __BlobIndex
{
38 uint32_t type
; /* type of entry */
39 uint32_t offset
; /* offset of entry */
42 typedef struct __SuperBlob
{
43 uint32_t magic
; /* magic number */
44 uint32_t length
; /* total length of SuperBlob */
45 uint32_t count
; /* number of index entries following */
46 CS_BlobIndex index
[]; /* (count) entries */
47 /* followed by Blobs in no particular order as indicated by offsets in index */
52 * C form of a CodeDirectory.
54 typedef struct __CodeDirectory
{
55 uint32_t magic
; /* magic number (CSMAGIC_CODEDIRECTORY) */
56 uint32_t length
; /* total length of CodeDirectory blob */
57 uint32_t version
; /* compatibility version */
58 uint32_t flags
; /* setup and mode flags */
59 uint32_t hashOffset
; /* offset of hash slot element at index zero */
60 uint32_t identOffset
; /* offset of identifier string */
61 uint32_t nSpecialSlots
; /* number of special hash slots */
62 uint32_t nCodeSlots
; /* number of ordinary (code) hash slots */
63 uint32_t codeLimit
; /* limit to main image signature range */
64 uint8_t hashSize
; /* size of each hash in bytes */
65 uint8_t hashType
; /* type of hash (cdHashType* constants) */
66 uint8_t spare1
; /* unused (must be zero) */
67 uint8_t pageSize
; /* log2(page size in bytes); 0 => infinite */
68 uint32_t spare2
; /* unused (must be zero) */
69 /* followed by dynamic content as located by offset fields above */
73 //assert(page < ntohl(cd->nCodeSlots));
74 //return base + ntohl(cd->hashOffset) + page * 20;
77 static CFMutableDictionaryRef
lc_code_sig(uint8_t *lc_code_signature
, size_t lc_code_signature_len
)
79 CFDataRef codedir
= NULL
;
80 CFDataRef message
= NULL
;
81 CFMutableDictionaryRef code_signature
=
82 CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
83 &kCFTypeDictionaryKeyCallBacks
,
84 &kCFTypeDictionaryValueCallBacks
);
85 require(code_signature
, out
);
87 CS_SuperBlob
*sb
= (CS_SuperBlob
*)lc_code_signature
;
88 require(ntohl(sb
->magic
) == CSMAGIC_EMBEDDED_SIGNATURE
, out
);
90 for (count
= 0; count
< ntohl(sb
->count
); count
++) {
91 //uint32_t type = ntohl(sb->index[count].type);
92 uint32_t offset
= ntohl(sb
->index
[count
].offset
);
93 uint8_t *bytes
= lc_code_signature
+ offset
;
94 //fprintf(stderr, "blob[%d]: (type: 0x%.08x, offset: %p)\n", count, type, (void*)offset);
95 uint32_t magic
= ntohl(*(uint32_t*)bytes
);
96 uint32_t length
= ntohl(*(uint32_t*)(bytes
+4));
97 //fprintf(stderr, " magic: 0x%.08x length: %d\n", magic, length);
99 case 0xfade0c01: //write_data("requirements", bytes, length);
101 case 0xfade0c02: //write_data("codedir", bytes, length);
102 codedir
= CFDataCreate(kCFAllocatorDefault
, bytes
, length
);
103 require(codedir
, out
);
104 CFDictionarySetValue(code_signature
, CFSTR("CodeDirectory"), codedir
);
106 uint8_t *cursor
= bytes
;
107 require_string(htonl(*(uint32_t*)(cursor
+8)) >= 0x20001, out
, "incompatible version");
108 require_string(htonl(*(uint32_t*)(cursor
+8)) <= 0x2F000, out
, "incompatible version");
109 uint32_t hash_offset
= htonl(*(uint32_t*)(cursor
+16));
110 require_string(htonl(*(uint32_t*)(cursor
+24)) >= 5, out
, "no entitlements slot yet");
111 require_string(*(cursor
+36) == 20, out
, "unexpected hash size");
112 require_string(*(cursor
+37) == 1, out
, "unexpected hash type");
113 message
= CFDataCreate(kCFAllocatorDefault
, cursor
+hash_offset
-5*20, 20);
114 require(message
, out
);
115 CFDictionarySetValue(code_signature
, CFSTR("EntitlementsCDHash"), message
);
118 case 0xfade0b01: //write_data("signed", lc_code_signature, bytes-lc_code_signature);
120 fprintf(stderr
, "Ad-hoc signed binary\n");
123 message
= CFDataCreate(kCFAllocatorDefault
, bytes
+8, length
-8);
124 require(message
, out
);
125 CFDictionarySetValue(code_signature
, CFSTR("SignedData"), message
);
131 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
132 CCDigest(kCCDigestSHA1
, bytes
, length
, digest
);
133 message
= CFDataCreate(kCFAllocatorDefault
, digest
, sizeof(digest
));
134 require(message
, out
);
135 CFDictionarySetValue(code_signature
, CFSTR("EntitlementsHash"), message
);
137 message
= CFDataCreate(kCFAllocatorDefault
, bytes
+8, length
-8);
138 require(message
, out
);
139 CFDictionarySetValue(code_signature
, CFSTR("Entitlements"), message
);
144 fprintf(stderr
, "Skipping block with magic: 0x%x\n", magic
);
148 return code_signature
;
150 if (code_signature
) CFRelease(code_signature
);
156 open_bundle(const char * path
, const char * mode
)
158 char full_path
[1024] = {};
159 CFStringRef path_cfstring
= NULL
;
160 CFURLRef path_url
= NULL
;
161 CFBundleRef bundle
= NULL
;
163 path_cfstring
= CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault
, path
);
164 require(path_cfstring
, out
);
165 path_url
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, path_cfstring
, kCFURLPOSIXPathStyle
, true);
166 require(path_url
, out
);
167 bundle
= CFBundleCreate(kCFAllocatorDefault
, path_url
);
168 require(bundle
, out
);
169 CFURLRef exec
= CFBundleCopyExecutableURL(bundle
);
171 require(CFURLGetFileSystemRepresentation(exec
, true, (uint8_t*)full_path
, sizeof(full_path
)), out
);
173 if (path_cfstring
) CFRelease(path_cfstring
);
174 if (path_url
) CFRelease(path_url
);
175 if (bundle
) CFRelease(bundle
);
177 return fopen(full_path
, "r");
181 open_bundle(const char *path
, const char *mode
)
183 char full_path
[1024] = {};
186 slash
= rindex(path
, '/');
187 if (!slash
) slash
= path
;
188 require(strlcpy(full_path
, path
, sizeof(full_path
)) < sizeof(full_path
), out
);
189 require(strlcat(full_path
, "/", sizeof(full_path
)), out
);
190 require(strlcat(full_path
, slash
, sizeof(full_path
)), out
);
191 require(dot
= rindex(full_path
, '.'), out
);
193 return fopen(full_path
, "r");
199 CFMutableDictionaryRef
load_code_signature(FILE *binary
, size_t slice_offset
)
201 bool signature_found
= false;
202 CFMutableDictionaryRef result
= NULL
;
204 struct load_command lc
;
205 struct linkedit_data_command le_lc
;
208 require(1 == fread(&cmd
.lc
, sizeof(cmd
.lc
), 1, binary
), out
);
209 if (cmd
.lc
.cmd
== LC_CODE_SIGNATURE
) {
210 require(1 == fread((uint8_t *)&cmd
.lc
+ sizeof(cmd
.lc
), sizeof(cmd
.le_lc
) - sizeof(cmd
.lc
), 1, binary
), out
);
211 require_noerr(fseek(binary
, slice_offset
+cmd
.le_lc
.dataoff
, SEEK_SET
), out
);
212 size_t length
= cmd
.le_lc
.datasize
;
213 uint8_t *data
= malloc(length
);
214 require(length
&& data
, out
);
215 require(1 == fread(data
, length
, 1, binary
), out
);
216 signature_found
= true;
217 result
= lc_code_sig(data
, length
);
221 require_noerr(fseek(binary
, cmd
.lc
.cmdsize
-sizeof(cmd
.lc
), SEEK_CUR
), out
);
222 } while(cmd
.lc
.cmd
|| cmd
.lc
.cmdsize
); /* count lc */
224 if (!signature_found
)
225 fprintf(stderr
, "No LC_CODE_SIGNATURE segment found\n");
229 CFArrayRef
load_code_signatures(const char *path
)
231 bool fully_parsed_binary
= false;
232 CFMutableDictionaryRef result
= NULL
;
233 CFMutableArrayRef results
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
235 FILE *binary
= open_bundle(path
, "r");
236 require(binary
, out
);
238 struct mach_header header
;
239 require(1 == fread(&header
, sizeof(header
), 1, binary
), out
);
240 if (header
.magic
== 0xfeedface) {
241 result
= load_code_signature(binary
, 0 /*non fat*/);
242 require(result
, out
);
243 CFArrayAppendValue(results
, result
);
247 struct fat_header fat
;
248 require(!fseek(binary
, 0L, SEEK_SET
), out
);
249 require(1 == fread(&fat
, sizeof(fat
), 1, binary
), out
);
250 require(htonl(fat
.magic
) == FAT_MAGIC
, out
);
251 uint32_t slice
, slices
= htonl(fat
.nfat_arch
);
252 struct fat_arch
*archs
= calloc(slices
, sizeof(struct fat_arch
));
253 require(slices
== fread(archs
, sizeof(struct fat_arch
), slices
, binary
), out
);
254 for (slice
= 0; slice
< slices
; slice
++) {
255 uint32_t slice_offset
= htonl(archs
[slice
].offset
);
256 require(!fseek(binary
, slice_offset
, SEEK_SET
), out
);
257 require(1 == fread(&header
, sizeof(header
), 1, binary
), out
);
258 require(header
.magic
== 0xfeedface, out
);
259 result
= load_code_signature(binary
, slice_offset
);
260 require(result
, out
);
261 CFArrayAppendValue(results
, result
);
265 fully_parsed_binary
= true;
267 if (!fully_parsed_binary
) {