]> git.saurik.com Git - ldid.git/blob - ldid.cpp
3bf2f770eb76ee96bf239df14e4ce37fb2b89356
[ldid.git] / ldid.cpp
1 /* JocStrap - Java/Objective-C Bootstrap
2 * Copyright (C) 2007 Jay Freeman (saurik)
3 */
4
5 /*
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
17 * distribution.
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #include "minimal/stdlib.h"
39 #include "minimal/mapping.h"
40
41 #include "sha1.h"
42
43 #include <cstring>
44 #include <string>
45 #include <vector>
46
47 #include <sys/wait.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50
51 struct fat_header {
52 uint32_t magic;
53 uint32_t nfat_arch;
54 } _packed;
55
56 #define FAT_MAGIC 0xcafebabe
57 #define FAT_CIGAM 0xbebafeca
58
59 struct fat_arch {
60 uint32_t cputype;
61 uint32_t cpusubtype;
62 uint32_t offset;
63 uint32_t size;
64 uint32_t align;
65 } _packed;
66
67 struct mach_header {
68 uint32_t magic;
69 uint32_t cputype;
70 uint32_t cpusubtype;
71 uint32_t filetype;
72 uint32_t ncmds;
73 uint32_t sizeofcmds;
74 uint32_t flags;
75 } _packed;
76
77 #define MH_MAGIC 0xfeedface
78 #define MH_CIGAM 0xcefaedfe
79
80 #define MH_EXECUTE 0x2
81 #define MH_DYLIB 0x6
82 #define MH_BUNDLE 0x8
83 #define MH_DYLIB_STUB 0x9
84
85 struct load_command {
86 uint32_t cmd;
87 uint32_t cmdsize;
88 } _packed;
89
90 #define LC_REQ_DYLD 0x80000000
91
92 #define LC_LOAD_DYLIB 0x0c
93 #define LC_ID_DYLIB 0x0d
94 #define LC_UUID 0x1b
95 #define LC_CODE_SIGNATURE 0x1d
96 #define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD)
97
98 struct dylib {
99 uint32_t name;
100 uint32_t timestamp;
101 uint32_t current_version;
102 uint32_t compatibility_version;
103 } _packed;
104
105 struct dylib_command {
106 uint32_t cmd;
107 uint32_t cmdsize;
108 struct dylib dylib;
109 } _packed;
110
111 struct uuid_command {
112 uint32_t cmd;
113 uint32_t cmdsize;
114 uint8_t uuid[16];
115 } _packed;
116
117 struct linkedit_data_command {
118 uint32_t cmd;
119 uint32_t cmdsize;
120 uint32_t dataoff;
121 uint32_t datasize;
122 } _packed;
123
124 uint16_t Swap_(uint16_t value) {
125 return
126 ((value >> 8) & 0x00ff) |
127 ((value << 8) & 0xff00);
128 }
129
130 uint32_t Swap_(uint32_t value) {
131 value = ((value >> 8) & 0x00ff00ff) |
132 ((value << 8) & 0xff00ff00);
133 value = ((value >> 16) & 0x0000ffff) |
134 ((value << 16) & 0xffff0000);
135 return value;
136 }
137
138 int16_t Swap_(int16_t value) {
139 return Swap_(static_cast<uint16_t>(value));
140 }
141
142 int32_t Swap_(int32_t value) {
143 return Swap_(static_cast<uint32_t>(value));
144 }
145
146 uint16_t Swap(uint16_t value) {
147 return true ? Swap_(value) : value;
148 }
149
150 uint32_t Swap(uint32_t value) {
151 return true ? Swap_(value) : value;
152 }
153
154 int16_t Swap(int16_t value) {
155 return Swap(static_cast<uint16_t>(value));
156 }
157
158 int32_t Swap(int32_t value) {
159 return Swap(static_cast<uint32_t>(value));
160 }
161
162 class Framework {
163 private:
164 void *base_;
165 size_t size_;
166 mach_header *mach_header_;
167 bool swapped_;
168
169 public:
170 uint16_t Swap(uint16_t value) const {
171 return swapped_ ? Swap_(value) : value;
172 }
173
174 uint32_t Swap(uint32_t value) const {
175 return swapped_ ? Swap_(value) : value;
176 }
177
178 int16_t Swap(int16_t value) const {
179 return Swap(static_cast<uint16_t>(value));
180 }
181
182 int32_t Swap(int32_t value) const {
183 return Swap(static_cast<uint32_t>(value));
184 }
185
186 Framework(const char *framework_path) :
187 swapped_(false)
188 {
189 base_ = map(framework_path, 0, _not(size_t), &size_, false);
190 fat_header *fat_header = reinterpret_cast<struct fat_header *>(base_);
191
192 if (Swap(fat_header->magic) == FAT_CIGAM) {
193 swapped_ = !swapped_;
194 goto fat;
195 } else if (Swap(fat_header->magic) != FAT_MAGIC)
196 mach_header_ = (mach_header *) base_;
197 else fat: {
198 size_t fat_narch = Swap(fat_header->nfat_arch);
199 fat_arch *fat_arch = reinterpret_cast<struct fat_arch *>(fat_header + 1);
200 size_t arch;
201 for (arch = 0; arch != fat_narch; ++arch) {
202 uint32_t arch_offset = Swap(fat_arch->offset);
203 mach_header_ = (mach_header *) ((uint8_t *) base_ + arch_offset);
204 goto found;
205 ++fat_arch;
206 }
207
208 _assert(false);
209 }
210
211 found:
212 if (Swap(mach_header_->magic) == MH_CIGAM)
213 swapped_ = !swapped_;
214 else _assert(Swap(mach_header_->magic) == MH_MAGIC);
215
216 _assert(
217 Swap(mach_header_->filetype) == MH_EXECUTE ||
218 Swap(mach_header_->filetype) == MH_DYLIB ||
219 Swap(mach_header_->filetype) == MH_BUNDLE
220 );
221 }
222
223 struct mach_header *operator ->() const {
224 return mach_header_;
225 }
226
227 void *GetBase() {
228 return base_;
229 }
230
231 size_t GetSize() {
232 return size_;
233 }
234
235 std::vector<struct load_command *> GetLoadCommands() {
236 std::vector<struct load_command *> load_commands;
237
238 struct load_command *load_command = reinterpret_cast<struct load_command *>(mach_header_ + 1);
239 for (uint32_t cmd = 0; cmd != Swap(mach_header_->ncmds); ++cmd) {
240 load_commands.push_back(load_command);
241 load_command = (struct load_command *) ((uint8_t *) load_command + Swap(load_command->cmdsize));
242 }
243
244 return load_commands;
245 }
246 };
247
248 #define CSMAGIC_CODEDIRECTORY 0xfade0c02
249 #define CSMAGIC_EMBEDDED_SIGNATURE 0xfade0cc0
250 #define CSMAGIC_ENTITLEMENTS 0xfade7171
251
252 #define CSSLOT_CODEDIRECTORY 0
253 #define CSSLOT_REQUIREMENTS 2
254 #define CSSLOT_ENTITLEMENTS 5
255
256 struct BlobIndex {
257 uint32_t type;
258 uint32_t offset;
259 } _packed;
260
261 struct Blob {
262 uint32_t magic;
263 uint32_t length;
264 } _packed;
265
266 struct SuperBlob {
267 struct Blob blob;
268 uint32_t count;
269 struct BlobIndex index[];
270 } _packed;
271
272 struct CodeDirectory {
273 struct Blob blob;
274 uint32_t version;
275 uint32_t flags;
276 uint32_t hashOffset;
277 uint32_t identOffset;
278 uint32_t nSpecialSlots;
279 uint32_t nCodeSlots;
280 uint32_t codeLimit;
281 uint8_t hashSize;
282 uint8_t hashType;
283 uint8_t spare1;
284 uint8_t pageSize;
285 uint32_t spare2;
286 } _packed;
287
288 extern "C" uint32_t hash(uint8_t *k, uint32_t length, uint32_t initval);
289
290 #define CODESIGN_ALLOCATE "arm-apple-darwin9-codesign_allocate"
291
292 void sha1(uint8_t *hash, uint8_t *data, size_t size) {
293 SHA1Context context;
294 SHA1Reset(&context);
295 SHA1Input(&context, data, size);
296 SHA1Result(&context, hash);
297 }
298
299 int main(int argc, const char *argv[]) {
300 bool flag_R(false);
301 bool flag_t(false);
302 bool flag_p(false);
303 bool flag_u(false);
304
305 bool flag_T(false);
306 bool flag_S(false);
307
308 bool timeh(false);
309 uint32_t timev(0);
310
311 const void *xmld(NULL);
312 size_t xmls(0);
313
314 std::vector<std::string> files;
315
316 _assert(argc != 0);
317 for (int argi(1); argi != argc; ++argi)
318 if (argv[argi][0] != '-')
319 files.push_back(argv[argi]);
320 else switch (argv[argi][1]) {
321 case 'R': flag_R = true; break;
322 case 't': flag_t = true; break;
323 case 'u': flag_u = true; break;
324 case 'p': flag_p = true; break;
325
326 case 'S':
327 flag_S = true;
328 if (argv[argi][2] != '\0') {
329 const char *xml = argv[argi] + 2;
330 xmld = map(xml, 0, _not(size_t), &xmls, true);
331 }
332 break;
333
334 case 'T': {
335 flag_T = true;
336 if (argv[argi][2] == '-')
337 timeh = true;
338 else {
339 char *arge;
340 timev = strtoul(argv[argi] + 2, &arge, 0);
341 _assert(arge == argv[argi] + strlen(argv[argi]));
342 }
343 } break;
344
345 default:
346 goto usage;
347 break;
348 }
349
350 if (files.empty()) usage: {
351 exit(0);
352 }
353
354 size_t filei(0), filee(0);
355 _foreach (file, files) try {
356 const char *path(file->c_str());
357 const char *base = strrchr(path, '/');
358 char *temp(NULL), *dir;
359 mode_t mode = 0;
360
361 if (base != NULL)
362 dir = strndup(path, base++ - path + 1);
363 else {
364 dir = strdup("");
365 base = path;
366 }
367
368 if (flag_S) {
369 asprintf(&temp, "%s.%s.cs", dir, base);
370 const char *allocate = getenv("CODESIGN_ALLOCATE");
371 if (allocate == NULL)
372 allocate = "codesign_allocate";
373
374 size_t size = _not(size_t);
375 const char *arch; {
376 Framework framework(path);
377 _foreach (load_command, framework.GetLoadCommands()) {
378 uint32_t cmd(framework.Swap((*load_command)->cmd));
379 if (cmd == LC_CODE_SIGNATURE) {
380 struct linkedit_data_command *signature = reinterpret_cast<struct linkedit_data_command *>(*load_command);
381 size = framework.Swap(signature->dataoff);
382 _assert(size < framework.GetSize());
383 break;
384 }
385 }
386
387 if (size == _not(size_t))
388 size = framework.GetSize();
389
390 switch (framework->cputype) {
391 case 12: switch (framework->cpusubtype) {
392 case 0: arch = "arm"; break;
393 case 6: arch = "armv6"; break;
394 default: arch = NULL; break;
395 } break;
396
397 default: arch = NULL; break;
398 }
399 }
400
401 _assert(arch != NULL);
402
403 pid_t pid = fork();
404 _syscall(pid);
405 if (pid == 0) {
406 char *ssize;
407 asprintf(&ssize, "%u", (sizeof(struct SuperBlob) + 2 * sizeof(struct BlobIndex) + sizeof(struct CodeDirectory) + strlen(base) + 1 + (size + 0x1000 - 1) / 0x1000 * 0x14 + 0xc + (xmld == NULL ? 0 : 0x10 + xmls) + 15) / 16 * 16);
408 //printf("codesign_allocate -i %s -a %s %s -o %s\n", path, arch, ssize, temp);
409 execlp(allocate, allocate, "-i", path, "-a", arch, ssize, "-o", temp, NULL);
410 _assert(false);
411 }
412
413 int status;
414 _syscall(waitpid(pid, &status, 0));
415 _assert(WIFEXITED(status));
416 _assert(WEXITSTATUS(status) == 0);
417 }
418
419 Framework framework(temp == NULL ? path : temp);
420 struct linkedit_data_command *signature(NULL);
421
422 if (flag_p)
423 printf("path%zu='%s'\n", filei, file->c_str());
424
425 _foreach (load_command, framework.GetLoadCommands()) {
426 uint32_t cmd(framework.Swap((*load_command)->cmd));
427
428 if (flag_R && cmd == LC_REEXPORT_DYLIB)
429 (*load_command)->cmd = framework.Swap(LC_LOAD_DYLIB);
430 else if (cmd == LC_CODE_SIGNATURE)
431 signature = reinterpret_cast<struct linkedit_data_command *>(*load_command);
432 else if (cmd == LC_UUID) {
433 volatile struct uuid_command *uuid_command(reinterpret_cast<struct uuid_command *>(*load_command));
434
435 if (flag_u) {
436 printf("uuid%zu=%.2x%.2x%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x\n", filei,
437 uuid_command->uuid[ 0], uuid_command->uuid[ 1], uuid_command->uuid[ 2], uuid_command->uuid[ 3],
438 uuid_command->uuid[ 4], uuid_command->uuid[ 5], uuid_command->uuid[ 6], uuid_command->uuid[ 7],
439 uuid_command->uuid[ 8], uuid_command->uuid[ 9], uuid_command->uuid[10], uuid_command->uuid[11],
440 uuid_command->uuid[12], uuid_command->uuid[13], uuid_command->uuid[14], uuid_command->uuid[15]
441 );
442 }
443 } else if (cmd == LC_ID_DYLIB) {
444 volatile struct dylib_command *dylib_command(reinterpret_cast<struct dylib_command *>(*load_command));
445
446 if (flag_t)
447 printf("time%zu=0x%.8x\n", filei, framework.Swap(dylib_command->dylib.timestamp));
448
449 if (flag_T) {
450 uint32_t timed;
451
452 if (!timeh)
453 timed = timev;
454 else {
455 dylib_command->dylib.timestamp = 0;
456 timed = hash(reinterpret_cast<uint8_t *>(framework.GetBase()), framework.GetSize(), timev);
457 }
458
459 dylib_command->dylib.timestamp = framework.Swap(timed);
460 }
461 }
462 }
463
464 if (flag_S) {
465 _assert(signature != NULL);
466
467 uint32_t data = framework.Swap(signature->dataoff);
468 uint32_t size = framework.Swap(signature->datasize);
469
470 uint8_t *top = reinterpret_cast<uint8_t *>(framework.GetBase());
471 uint8_t *blob = top + data;
472 struct SuperBlob *super = reinterpret_cast<struct SuperBlob *>(blob);
473 super->blob.magic = Swap(CSMAGIC_EMBEDDED_SIGNATURE);
474
475 uint32_t count = xmld == NULL ? 2 : 3;
476 uint32_t offset = sizeof(struct SuperBlob) + count * sizeof(struct BlobIndex);
477
478 super->index[0].type = Swap(CSSLOT_CODEDIRECTORY);
479 super->index[0].offset = Swap(offset);
480
481 uint32_t begin = offset;
482 struct CodeDirectory *directory = reinterpret_cast<struct CodeDirectory *>(blob + begin);
483 offset += sizeof(struct CodeDirectory);
484
485 directory->blob.magic = Swap(CSMAGIC_CODEDIRECTORY);
486 directory->version = Swap(0x00020001);
487 directory->flags = Swap(0);
488 directory->codeLimit = Swap(data);
489 directory->hashSize = 0x14;
490 directory->hashType = 0x01;
491 directory->spare1 = 0x00;
492 directory->pageSize = 0x0c;
493 directory->spare2 = Swap(0);
494
495 directory->identOffset = Swap(offset - begin);
496 strcpy(reinterpret_cast<char *>(blob + offset), base);
497 offset += strlen(base) + 1;
498
499 uint8_t (*hashes)[20] = reinterpret_cast<uint8_t (*)[20]>(blob + offset);
500 uint32_t special = 0;
501
502 uint32_t pages = (data + 0x1000 - 1) / 0x1000;
503 directory->nSpecialSlots = Swap(special);
504 directory->nCodeSlots = Swap(pages);
505
506 if (pages != 1)
507 for (size_t i = 0; i != pages - 1; ++i)
508 sha1(hashes[special + i], top + 0x1000 * i, 0x1000);
509 if (pages != 0)
510 sha1(hashes[special + pages - 1], top + 0x1000 * (pages - 1), data % 0x1000);
511
512 directory->hashOffset = Swap(offset - begin);
513 offset += sizeof(*hashes) * (special + pages);
514 directory->blob.length = Swap(offset - begin);
515
516 super->index[1].type = Swap(CSSLOT_REQUIREMENTS);
517 super->index[1].offset = Swap(offset);
518
519 memcpy(blob + offset, "\xfa\xde\x0c\x01\x00\x00\x00\x0c\x00\x00\x00\x00", 0xc);
520 offset += 0xc;
521
522 if (xmld != NULL) {
523 super->index[2].type = Swap(CSSLOT_ENTITLEMENTS);
524 super->index[2].offset = Swap(offset);
525
526 uint32_t begin = offset;
527 struct Blob *entitlements = reinterpret_cast<struct Blob *>(blob + begin);
528 offset += sizeof(struct Blob);
529
530 memcpy(blob + offset, xmld, xmls);
531 offset += xmls;
532
533 entitlements->magic = Swap(CSMAGIC_ENTITLEMENTS);
534 entitlements->length = Swap(offset - begin);
535 }
536
537 super->count = Swap(count);
538 super->blob.length = Swap(offset);
539
540 if (offset > size) {
541 fprintf(stderr, "offset (%zu) > size (%zu)\n", offset, size);
542 _assert(false);
543 } //else fprintf(stderr, "offset (%zu) <= size (%zu)\n", offset, size);
544
545 memset(blob + offset, 0, size - offset);
546 }
547
548 if (temp) {
549 struct stat info;
550 _syscall(stat(path, &info));
551 _syscall(chown(temp, info.st_uid, info.st_gid));
552 _syscall(chmod(temp, info.st_mode));
553 _syscall(unlink(path));
554 _syscall(rename(temp, path));
555 free(temp);
556 }
557
558 free(dir);
559 ++filei;
560 } catch (const char *) {
561 ++filee;
562 ++filei;
563 }
564
565 return filee;
566 }