root / trunk / util / ldid.cpp

Revision 476, 20.3 kB (checked in by saurik, 2 months ago)

Added ldid -e.

Line 
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/string.h"
40#include "minimal/mapping.h"
41
42#include "sha1.h"
43
44#include <cstring>
45#include <string>
46#include <vector>
47
48#include <sys/wait.h>
49#include <sys/types.h>
50#include <sys/stat.h>
51
52struct fat_header {
53    uint32_t magic;
54    uint32_t nfat_arch;
55} _packed;
56
57#define FAT_MAGIC 0xcafebabe
58#define FAT_CIGAM 0xbebafeca
59
60struct fat_arch {
61    uint32_t cputype;
62    uint32_t cpusubtype;
63    uint32_t offset;
64    uint32_t size;
65    uint32_t align;
66} _packed;
67
68struct mach_header {
69    uint32_t magic;
70    uint32_t cputype;
71    uint32_t cpusubtype;
72    uint32_t filetype;
73    uint32_t ncmds;
74    uint32_t sizeofcmds;
75    uint32_t flags;
76} _packed;
77
78#define MH_MAGIC 0xfeedface
79#define MH_CIGAM 0xcefaedfe
80
81#define MH_EXECUTE    0x2
82#define MH_DYLIB      0x6
83#define MH_BUNDLE     0x8
84#define MH_DYLIB_STUB 0x9
85
86struct load_command {
87    uint32_t cmd;
88    uint32_t cmdsize;
89} _packed;
90
91#define LC_REQ_DYLD  0x80000000
92
93#define LC_LOAD_DYLIB      0x0c
94#define LC_ID_DYLIB        0x0d
95#define LC_UUID            0x1b
96#define LC_CODE_SIGNATURE  0x1d
97#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD)
98
99struct dylib {
100    uint32_t name;
101    uint32_t timestamp;
102    uint32_t current_version;
103    uint32_t compatibility_version;
104} _packed;
105
106struct dylib_command {
107    uint32_t cmd;
108    uint32_t cmdsize;
109    struct dylib dylib;
110} _packed;
111
112struct uuid_command {
113    uint32_t cmd;
114    uint32_t cmdsize;
115    uint8_t uuid[16];
116} _packed;
117
118struct linkedit_data_command {
119    uint32_t cmd;
120    uint32_t cmdsize;
121    uint32_t dataoff;
122    uint32_t datasize;
123} _packed;
124
125uint16_t Swap_(uint16_t value) {
126    return
127        ((value >>  8) & 0x00ff) |
128        ((value <<  8) & 0xff00);
129}
130
131uint32_t Swap_(uint32_t value) {
132    value = ((value >>  8) & 0x00ff00ff) |
133            ((value <<  8) & 0xff00ff00);
134    value = ((value >> 16) & 0x0000ffff) |
135            ((value << 16) & 0xffff0000);
136    return value;
137}
138
139int16_t Swap_(int16_t value) {
140    return Swap_(static_cast<uint16_t>(value));
141}
142
143int32_t Swap_(int32_t value) {
144    return Swap_(static_cast<uint32_t>(value));
145}
146
147uint16_t Swap(uint16_t value) {
148    return true ? Swap_(value) : value;
149}
150
151uint32_t Swap(uint32_t value) {
152    return true ? Swap_(value) : value;
153}
154
155int16_t Swap(int16_t value) {
156    return Swap(static_cast<uint16_t>(value));
157}
158
159int32_t Swap(int32_t value) {
160    return Swap(static_cast<uint32_t>(value));
161}
162
163class Framework {
164  private:
165    void *base_;
166    size_t size_;
167    mach_header *mach_header_;
168    bool swapped_;
169
170  public:
171    uint16_t Swap(uint16_t value) const {
172        return swapped_ ? Swap_(value) : value;
173    }
174
175    uint32_t Swap(uint32_t value) const {
176        return swapped_ ? Swap_(value) : value;
177    }
178
179    int16_t Swap(int16_t value) const {
180        return Swap(static_cast<uint16_t>(value));
181    }
182
183    int32_t Swap(int32_t value) const {
184        return Swap(static_cast<uint32_t>(value));
185    }
186
187    Framework(const char *framework_path) :
188        swapped_(false)
189    {
190        base_ = map(framework_path, 0, _not(size_t), &size_, false);
191        fat_header *fat_header = reinterpret_cast<struct fat_header *>(base_);
192
193        if (Swap(fat_header->magic) == FAT_CIGAM) {
194            swapped_ = !swapped_;
195            goto fat;
196        } else if (Swap(fat_header->magic) != FAT_MAGIC)
197            mach_header_ = (mach_header *) base_;
198        else fat: {
199            size_t fat_narch = Swap(fat_header->nfat_arch);
200            fat_arch *fat_arch = reinterpret_cast<struct fat_arch *>(fat_header + 1);
201            size_t arch;
202            for (arch = 0; arch != fat_narch; ++arch) {
203                uint32_t arch_offset = Swap(fat_arch->offset);
204                mach_header_ = (mach_header *) ((uint8_t *) base_ + arch_offset);
205                goto found;
206                ++fat_arch;
207            }
208
209            _assert(false);
210        }
211
212      found:
213        if (Swap(mach_header_->magic) == MH_CIGAM)
214            swapped_ = !swapped_;
215        else _assert(Swap(mach_header_->magic) == MH_MAGIC);
216
217        _assert(
218            Swap(mach_header_->filetype) == MH_EXECUTE ||
219            Swap(mach_header_->filetype) == MH_DYLIB ||
220            Swap(mach_header_->filetype) == MH_BUNDLE
221        );
222    }
223
224    struct mach_header *operator ->() const {
225        return mach_header_;
226    }
227
228    void *GetBase() {
229        return base_;
230    }
231
232    size_t GetSize() {
233        return size_;
234    }
235
236    std::vector<struct load_command *> GetLoadCommands() {
237        std::vector<struct load_command *> load_commands;
238
239        struct load_command *load_command = reinterpret_cast<struct load_command *>(mach_header_ + 1);
240        for (uint32_t cmd = 0; cmd != Swap(mach_header_->ncmds); ++cmd) {
241            load_commands.push_back(load_command);
242            load_command = (struct load_command *) ((uint8_t *) load_command + Swap(load_command->cmdsize));
243        }
244
245        return load_commands;
246    }
247};
248
249#define CSMAGIC_CODEDIRECTORY      0xfade0c02
250#define CSMAGIC_EMBEDDED_SIGNATURE 0xfade0cc0
251#define CSMAGIC_ENTITLEMENTS       0xfade7171
252
253#define CSSLOT_CODEDIRECTORY 0
254#define CSSLOT_REQUIREMENTS  2
255#define CSSLOT_ENTITLEMENTS  5
256
257struct BlobIndex {
258    uint32_t type;
259    uint32_t offset;
260} _packed;
261
262struct Blob {
263    uint32_t magic;
264    uint32_t length;
265} _packed;
266
267struct SuperBlob {
268    struct Blob blob;
269    uint32_t count;
270    struct BlobIndex index[];
271} _packed;
272
273struct CodeDirectory {
274    struct Blob blob;
275    uint32_t version;
276    uint32_t flags;
277    uint32_t hashOffset;
278    uint32_t identOffset;
279    uint32_t nSpecialSlots;
280    uint32_t nCodeSlots;
281    uint32_t codeLimit;
282    uint8_t hashSize;
283    uint8_t hashType;
284    uint8_t spare1;
285    uint8_t pageSize;
286    uint32_t spare2;
287} _packed;
288
289extern "C" uint32_t hash(uint8_t *k, uint32_t length, uint32_t initval);
290
291#define CODESIGN_ALLOCATE "arm-apple-darwin9-codesign_allocate"
292
293void sha1(uint8_t *hash, uint8_t *data, size_t size) {
294    SHA1Context context;
295    SHA1Reset(&context);
296    SHA1Input(&context, data, size);
297    SHA1Result(&context, hash);
298}
299
300int main(int argc, const char *argv[]) {
301    bool flag_R(false);
302    bool flag_t(false);
303    bool flag_p(false);
304    bool flag_u(false);
305    bool flag_e(false);
306
307    bool flag_T(false);
308
309    bool flag_S(false);
310    bool flag_s(false);
311
312    bool timeh(false);
313    uint32_t timev(0);
314
315    const void *xmld(NULL);
316    size_t xmls(0);
317
318    std::vector<std::string> files;
319
320    if (argc == 1) {
321        fprintf(stderr, "usage: %s -S[entitlements.xml] <binary>\n", argv[0]);
322        fprintf(stderr, "   %s -e MobileSafari\n", argv[0]);
323        fprintf(stderr, "   %s -S cat\n", argv[0]);
324        fprintf(stderr, "   %s -Stfp.xml gdb\n", argv[0]);
325        exit(0);
326    }
327
328    for (int argi(1); argi != argc; ++argi)
329        if (argv[argi][0] != '-')
330            files.push_back(argv[argi]);
331        else switch (argv[argi][1]) {
332            case 'R': flag_R = true; break;
333            case 't': flag_t = true; break;
334            case 'u': flag_u = true; break;
335            case 'p': flag_p = true; break;
336            case 'e': flag_e = true; break;
337
338            case 's':
339                _assert(!flag_S);
340                flag_s = true;
341            break;
342
343            case 'S':
344                _assert(!flag_s);
345                flag_S = true;
346                if (argv[argi][2] != '\0') {
347                    const char *xml = argv[argi] + 2;
348                    xmld = map(xml, 0, _not(size_t), &xmls, true);
349                }
350            break;
351
352            case 'T': {
353                flag_T = true;
354                if (argv[argi][2] == '-')
355                    timeh = true;
356                else {
357                    char *arge;
358                    timev = strtoul(argv[argi] + 2, &arge, 0);
359                    _assert(arge == argv[argi] + strlen(argv[argi]));
360                }
361            } break;
362
363            default:
364                goto usage;
365            break;
366        }
367
368    if (files.empty()) usage: {
369        exit(0);
370    }
371
372    size_t filei(0), filee(0);
373    _foreach (file, files) try {
374        const char *path(file->c_str());
375        const char *base = strrchr(path, '/');
376        char *temp(NULL), *dir;
377
378        if (base != NULL)
379            dir = strndup_(path, base++ - path + 1);
380        else {
381            dir = strdup("");
382            base = path;
383        }
384
385        if (flag_S) {
386            asprintf(&temp, "%s.%s.cs", dir, base);
387            const char *allocate = getenv("CODESIGN_ALLOCATE");
388            if (allocate == NULL)
389                allocate = "codesign_allocate";
390
391            size_t size = _not(size_t);
392            const char *arch; {
393                Framework framework(path);
394                _foreach (load_command, framework.GetLoadCommands()) {
395                    uint32_t cmd(framework.Swap((*load_command)->cmd));
396                    if (cmd == LC_CODE_SIGNATURE) {
397                        struct linkedit_data_command *signature = reinterpret_cast<struct linkedit_data_command *>(*load_command);
398                        size = framework.Swap(signature->dataoff);
399                        _assert(size < framework.GetSize());
400                        break;
401                    }
402                }
403
404                if (size == _not(size_t))
405                    size = framework.GetSize();
406
407                switch (framework->cputype) {
408                    case 12: switch (framework->cpusubtype) {
409                        case 0: arch = "arm"; break;
410                        case 6: arch = "armv6"; break;
411                        default: arch = NULL; break;
412                    } break;
413
414                    default: arch = NULL; break;
415                }
416            }
417
418            _assert(arch != NULL);
419
420            pid_t pid = fork();
421            _syscall(pid);
422            if (pid == 0) {
423                char *ssize;
424                asprintf(&ssize, "%u", (sizeof(struct SuperBlob) + 2 * sizeof(struct BlobIndex) + sizeof(struct CodeDirectory) + strlen(base) + 1 + ((xmld == NULL ? CSSLOT_REQUIREMENTS : CSSLOT_ENTITLEMENTS) + (size + 0x1000 - 1) / 0x1000) * 0x14 + 0xc + (xmld == NULL ? 0 : 0x10 + xmls) + 15) / 16 * 16);
425                //printf("%s -i %s -a %s %s -o %s\n", allocate, path, arch, ssize, temp);
426                execlp(allocate, allocate, "-i", path, "-a", arch, ssize, "-o", temp, NULL);
427                _assert(false);
428            }
429
430            int status;
431            _syscall(waitpid(pid, &status, 0));
432            _assert(WIFEXITED(status));
433            _assert(WEXITSTATUS(status) == 0);
434        }
435
436        Framework framework(temp == NULL ? path : temp);
437        struct linkedit_data_command *signature(NULL);
438
439        if (flag_p)
440            printf("path%zu='%s'\n", filei, file->c_str());
441
442        _foreach (load_command, framework.GetLoadCommands()) {
443            uint32_t cmd(framework.Swap((*load_command)->cmd));
444
445            if (flag_R && cmd == LC_REEXPORT_DYLIB)
446                (*load_command)->cmd = framework.Swap(LC_LOAD_DYLIB);
447            else if (cmd == LC_CODE_SIGNATURE)
448                signature = reinterpret_cast<struct linkedit_data_command *>(*load_command);
449            else if (cmd == LC_UUID) {
450                volatile struct uuid_command *uuid_command(reinterpret_cast<struct uuid_command *>(*load_command));
451
452                if (flag_u) {
453                    printf("uuid%zu=%.2x%.2x%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x\n", filei,
454                        uuid_command->uuid[ 0], uuid_command->uuid[ 1], uuid_command->uuid[ 2], uuid_command->uuid[ 3],
455                        uuid_command->uuid[ 4], uuid_command->uuid[ 5], uuid_command->uuid[ 6], uuid_command->uuid[ 7],
456                        uuid_command->uuid[ 8], uuid_command->uuid[ 9], uuid_command->uuid[10], uuid_command->uuid[11],
457                        uuid_command->uuid[12], uuid_command->uuid[13], uuid_command->uuid[14], uuid_command->uuid[15]
458                    );
459                }
460            } else if (cmd == LC_ID_DYLIB) {
461                volatile struct dylib_command *dylib_command(reinterpret_cast<struct dylib_command *>(*load_command));
462
463                if (flag_t)
464                    printf("time%zu=0x%.8x\n", filei, framework.Swap(dylib_command->dylib.timestamp));
465
466                if (flag_T) {
467                    uint32_t timed;
468
469                    if (!timeh)
470                        timed = timev;
471                    else {
472                        dylib_command->dylib.timestamp = 0;
473                        timed = hash(reinterpret_cast<uint8_t *>(framework.GetBase()), framework.GetSize(), timev);
474                    }
475
476                    dylib_command->dylib.timestamp = framework.Swap(timed);
477                }
478            }
479        }
480
481        if (flag_e) {
482            _assert(signature != NULL);
483
484            uint32_t data = framework.Swap(signature->dataoff);
485            uint32_t size = framework.Swap(signature->datasize);
486
487            uint8_t *top = reinterpret_cast<uint8_t *>(framework.GetBase());
488            uint8_t *blob = top + data;
489            struct SuperBlob *super = reinterpret_cast<struct SuperBlob *>(blob);
490
491            for (size_t index(0); index != Swap(super->count); ++index)
492                if (Swap(super->index[index].type) == CSSLOT_ENTITLEMENTS) {
493                    uint32_t begin = Swap(super->index[index].offset);
494                    struct Blob *entitlements = reinterpret_cast<struct Blob *>(blob + begin);
495                    fwrite(entitlements + 1, 1, Swap(entitlements->length) - sizeof(struct Blob), stdout);
496                }
497        }
498
499        if (flag_s) {
500            _assert(signature != NULL);
501
502            uint32_t data = framework.Swap(signature->dataoff);
503            uint32_t size = framework.Swap(signature->datasize);
504
505            uint8_t *top = reinterpret_cast<uint8_t *>(framework.GetBase());
506            uint8_t *blob = top + data;
507            struct SuperBlob *super = reinterpret_cast<struct SuperBlob *>(blob);
508
509            for (size_t index(0); index != Swap(super->count); ++index)
510                if (Swap(super->index[index].type) == CSSLOT_CODEDIRECTORY) {
511                    uint32_t begin = Swap(super->index[index].offset);
512                    struct CodeDirectory *directory = reinterpret_cast<struct CodeDirectory *>(blob + begin);
513
514                    uint8_t (*hashes)[20] = reinterpret_cast<uint8_t (*)[20]>(blob + begin + Swap(directory->hashOffset));
515                    uint32_t pages = Swap(directory->nCodeSlots);
516
517                    if (pages != 1)
518                        for (size_t i = 0; i != pages - 1; ++i)
519                            sha1(hashes[i], top + 0x1000 * i, 0x1000);
520                    if (pages != 0)
521                        sha1(hashes[pages - 1], top + 0x1000 * (pages - 1), ((data - 1) % 0x1000) + 1);
522                }
523        }
524
525        if (flag_S) {
526            _assert(signature != NULL);
527
528            uint32_t data = framework.Swap(signature->dataoff);
529            uint32_t size = framework.Swap(signature->datasize);
530
531            uint8_t *top = reinterpret_cast<uint8_t *>(framework.GetBase());
532            uint8_t *blob = top + data;
533            struct SuperBlob *super = reinterpret_cast<struct SuperBlob *>(blob);
534            super->blob.magic = Swap(CSMAGIC_EMBEDDED_SIGNATURE);
535
536            uint32_t count = xmld == NULL ? 2 : 3;
537            uint32_t offset = sizeof(struct SuperBlob) + count * sizeof(struct BlobIndex);
538
539            super->index[0].type = Swap(CSSLOT_CODEDIRECTORY);
540            super->index[0].offset = Swap(offset);
541
542            uint32_t begin = offset;
543            struct CodeDirectory *directory = reinterpret_cast<struct CodeDirectory *>(blob + begin);
544            offset += sizeof(struct CodeDirectory);
545
546            directory->blob.magic = Swap(CSMAGIC_CODEDIRECTORY);
547            directory->version = Swap(0x00020001);
548            directory->flags = Swap(0);
549            directory->codeLimit = Swap(data);
550            directory->hashSize = 0x14;
551            directory->hashType = 0x01;
552            directory->spare1 = 0x00;
553            directory->pageSize = 0x0c;
554            directory->spare2 = Swap(0);
555
556            directory->identOffset = Swap(offset - begin);
557            strcpy(reinterpret_cast<char *>(blob + offset), base);
558            offset += strlen(base) + 1;
559
560            uint32_t special = xmld == NULL ? CSSLOT_REQUIREMENTS : CSSLOT_ENTITLEMENTS;
561            directory->nSpecialSlots = Swap(special);
562
563            uint8_t (*hashes)[20] = reinterpret_cast<uint8_t (*)[20]>(blob + offset);
564            memset(hashes, 0, sizeof(*hashes) * special);
565
566            offset += sizeof(*hashes) * special;
567            hashes += special;
568
569            uint32_t pages = (data + 0x1000 - 1) / 0x1000;
570            directory->nCodeSlots = Swap(pages);
571
572            if (pages != 1)
573                for (size_t i = 0; i != pages - 1; ++i)
574                    sha1(hashes[i], top + 0x1000 * i, 0x1000);
575            if (pages != 0)
576                sha1(hashes[pages - 1], top + 0x1000 * (pages - 1), ((data - 1) % 0x1000) + 1);
577
578            directory->hashOffset = Swap(offset - begin);
579            offset += sizeof(*hashes) * pages;
580            directory->blob.length = Swap(offset - begin);
581
582            super->index[1].type = Swap(CSSLOT_REQUIREMENTS);
583            super->index[1].offset = Swap(offset);
584
585            memcpy(blob + offset, "\xfa\xde\x0c\x01\x00\x00\x00\x0c\x00\x00\x00\x00", 0xc);
586            offset += 0xc;
587
588            if (xmld != NULL) {
589                super->index[2].type = Swap(CSSLOT_ENTITLEMENTS);
590                super->index[2].offset = Swap(offset);
591
592                uint32_t begin = offset;
593                struct Blob *entitlements = reinterpret_cast<struct Blob *>(blob + begin);
594                offset += sizeof(struct Blob);
595
596                memcpy(blob + offset, xmld, xmls);
597                offset += xmls;
598
599                entitlements->magic = Swap(CSMAGIC_ENTITLEMENTS);
600                entitlements->length = Swap(offset - begin);
601            }
602
603            for (size_t index(0); index != count; ++index) {
604                uint32_t type = Swap(super->index[index].type);
605                if (type != 0 && type <= special) {
606                    uint32_t offset = Swap(super->index[index].offset);
607                    struct Blob *local = (struct Blob *) (blob + offset);
608                    sha1((uint8_t *) (hashes - type), (uint8_t *) local, Swap(local->length));
609                }
610            }
611
612            super->count = Swap(count);
613            super->blob.length = Swap(offset);
614
615            if (offset > size) {
616                fprintf(stderr, "offset (%u) > size (%u)\n", offset, size);
617                _assert(false);
618            } //else fprintf(stderr, "offset (%zu) <= size (%zu)\n", offset, size);
619
620            memset(blob + offset, 0, size - offset);
621        }
622
623        if (temp) {
624            struct stat info;
625            _syscall(stat(path, &info));
626            _syscall(chown(temp, info.st_uid, info.st_gid));
627            _syscall(chmod(temp, info.st_mode));
628            _syscall(unlink(path));
629            _syscall(rename(temp, path));
630            free(temp);
631        }
632
633        free(dir);
634        ++filei;
635    } catch (const char *) {
636        ++filee;
637        ++filei;
638    }
639
640    return filee;
641}
Note: See TracBrowser for help on using the browser.