]> git.saurik.com Git - ldid.git/blame - ldid.cpp
Added a #define for a DYLIB_STUB, not that I need to use it, apparently.
[ldid.git] / ldid.cpp
CommitLineData
a362a82f
JF
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
fdb119ef
JF
41#include "sha1.h"
42
a362a82f
JF
43#include <cstring>
44#include <string>
45#include <vector>
46
fdb119ef 47#include <sys/wait.h>
31ad673b
JF
48#include <sys/types.h>
49#include <sys/stat.h>
fdb119ef 50
a362a82f
JF
51struct fat_header {
52 uint32_t magic;
53 uint32_t nfat_arch;
54};
55
56#define FAT_MAGIC 0xcafebabe
57#define FAT_CIGAM 0xbebafeca
58
59struct fat_arch {
60 uint32_t cputype;
61 uint32_t cpusubtype;
62 uint32_t offset;
63 uint32_t size;
64 uint32_t align;
65};
66
67struct 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};
76
fdb119ef 77#define MH_MAGIC 0xfeedface
a362a82f
JF
78#define MH_CIGAM 0xcefaedfe
79
efd6cd4a
JF
80#define MH_EXECUTE 0x2
81#define MH_DYLIB 0x6
82#define MH_BUNDLE 0x8
83#define MH_DYLIB_STUB 0x9
a362a82f
JF
84
85struct load_command {
86 uint32_t cmd;
87 uint32_t cmdsize;
88};
89
90#define LC_REQ_DYLD 0x80000000
91
fdb119ef
JF
92#define LC_LOAD_DYLIB 0x0c
93#define LC_ID_DYLIB 0x0d
94#define LC_UUID 0x1b
95#define LC_CODE_SIGNATURE 0x1d
a362a82f
JF
96#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD)
97
98struct dylib {
99 uint32_t name;
100 uint32_t timestamp;
101 uint32_t current_version;
102 uint32_t compatibility_version;
103};
104
105struct dylib_command {
106 uint32_t cmd;
107 uint32_t cmdsize;
108 struct dylib dylib;
109};
110
111struct uuid_command {
112 uint32_t cmd;
113 uint32_t cmdsize;
114 uint8_t uuid[16];
115};
116
fdb119ef
JF
117struct linkedit_data_command {
118 uint32_t cmd;
119 uint32_t cmdsize;
120 uint32_t dataoff;
121 uint32_t datasize;
122};
123
124uint16_t Swap_(uint16_t value) {
125 return
126 ((value >> 8) & 0x00ff) |
127 ((value << 8) & 0xff00);
128}
129
130uint32_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
138int16_t Swap_(int16_t value) {
139 return Swap_(static_cast<uint16_t>(value));
140}
141
142int32_t Swap_(int32_t value) {
143 return Swap_(static_cast<uint32_t>(value));
144}
145
146uint16_t Swap(uint16_t value) {
147 return true ? Swap_(value) : value;
148}
149
150uint32_t Swap(uint32_t value) {
151 return true ? Swap_(value) : value;
152}
153
154int16_t Swap(int16_t value) {
155 return Swap(static_cast<uint16_t>(value));
156}
157
158int32_t Swap(int32_t value) {
159 return Swap(static_cast<uint32_t>(value));
160}
161
a362a82f
JF
162class Framework {
163 private:
164 void *base_;
165 size_t size_;
166 mach_header *mach_header_;
167 bool swapped_;
168
169 public:
fdb119ef
JF
170 uint16_t Swap(uint16_t value) const {
171 return swapped_ ? Swap_(value) : value;
a362a82f
JF
172 }
173
fdb119ef
JF
174 uint32_t Swap(uint32_t value) const {
175 return swapped_ ? Swap_(value) : value;
a362a82f
JF
176 }
177
fdb119ef
JF
178 int16_t Swap(int16_t value) const {
179 return Swap(static_cast<uint16_t>(value));
a362a82f
JF
180 }
181
fdb119ef
JF
182 int32_t Swap(int32_t value) const {
183 return Swap(static_cast<uint32_t>(value));
a362a82f
JF
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
afbb7c8e
JF
223 struct mach_header *operator ->() const {
224 return mach_header_;
225 }
226
a362a82f
JF
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
fdb119ef
JF
248#define CSMAGIC_CODEDIRECTORY 0xfade0c02
249#define CSMAGIC_EMBEDDED_SIGNATURE 0xfade0cc0
250#define CSSLOT_CODEDIRECTORY 0
afbb7c8e 251#define CSSLOT_REQUIREMENTS 2
fdb119ef
JF
252
253struct BlobIndex {
254 uint32_t type;
255 uint32_t offset;
256};
257
258struct SuperBlob {
259 uint32_t magic;
260 uint32_t length;
261 uint32_t count;
262 struct BlobIndex index[];
263};
264
265struct CodeDirectory {
266 uint32_t magic;
267 uint32_t length;
268 uint32_t version;
269 uint32_t flags;
270 uint32_t hashOffset;
271 uint32_t identOffset;
272 uint32_t nSpecialSlots;
273 uint32_t nCodeSlots;
274 uint32_t codeLimit;
275 uint8_t hashSize;
276 uint8_t hashType;
277 uint8_t spare1;
278 uint8_t pageSize;
279 uint32_t spare2;
280};
281
a362a82f
JF
282extern "C" uint32_t hash(uint8_t *k, uint32_t length, uint32_t initval);
283
fdb119ef
JF
284#define CODESIGN_ALLOCATE "arm-apple-darwin9-codesign_allocate"
285
286void sha1(uint8_t *hash, uint8_t *data, size_t size) {
287 SHA1Context context;
288 SHA1Reset(&context);
289 SHA1Input(&context, data, size);
290 SHA1Result(&context, hash);
291}
292
a362a82f
JF
293int main(int argc, const char *argv[]) {
294 bool flag_R(false);
295 bool flag_t(false);
296 bool flag_p(false);
297 bool flag_u(false);
298
299 bool flag_T(false);
fdb119ef 300 bool flag_S(false);
a362a82f
JF
301
302 bool timeh(false);
303 uint32_t timev(0);
304
305 std::vector<std::string> files;
306
307 _assert(argc != 0);
308 for (int argi(1); argi != argc; ++argi)
309 if (argv[argi][0] != '-')
310 files.push_back(argv[argi]);
311 else switch (argv[argi][1]) {
312 case 'R': flag_R = true; break;
313 case 't': flag_t = true; break;
314 case 'u': flag_u = true; break;
315 case 'p': flag_p = true; break;
fdb119ef 316 case 'S': flag_S = true; break;
a362a82f
JF
317
318 case 'T': {
319 flag_T = true;
320 if (argv[argi][2] == '-')
321 timeh = true;
322 else {
323 char *arge;
324 timev = strtoul(argv[argi] + 2, &arge, 0);
325 _assert(arge == argv[argi] + strlen(argv[argi]));
326 }
327 } break;
328
329 default:
330 goto usage;
331 break;
332 }
333
334 if (files.empty()) usage: {
335 exit(0);
336 }
337
338 size_t filei(0), filee(0);
339 _foreach (file, files) try {
fdb119ef
JF
340 const char *path(file->c_str());
341 const char *base = strrchr(path, '/');
342 char *temp(NULL), *dir;
31ad673b 343 mode_t mode = 0;
fdb119ef
JF
344
345 if (base != NULL)
346 dir = strndup(path, base++ - path + 1);
347 else {
348 dir = strdup("");
349 base = path;
350 }
351
352 if (flag_S) {
353 asprintf(&temp, "%s.%s.cs", dir, base);
354 const char *allocate = getenv("CODESIGN_ALLOCATE");
355 if (allocate == NULL)
356 allocate = "codesign_allocate";
357
afbb7c8e
JF
358 size_t size = _not(size_t);
359 const char *arch; {
fdb119ef 360 Framework framework(path);
afbb7c8e
JF
361 _foreach (load_command, framework.GetLoadCommands()) {
362 uint32_t cmd(framework.Swap((*load_command)->cmd));
363 if (cmd == LC_CODE_SIGNATURE) {
364 struct linkedit_data_command *signature = reinterpret_cast<struct linkedit_data_command *>(*load_command);
365 size = framework.Swap(signature->dataoff);
366 _assert(size < framework.GetSize());
367 break;
368 }
369 }
370
371 if (size == _not(size_t))
372 size = framework.GetSize();
373
374 switch (framework->cputype) {
375 case 12: switch (framework->cpusubtype) {
376 case 0: arch = "arm"; break;
377 case 6: arch = "armv6"; break;
378 default: arch = NULL; break;
379 } break;
380
381 default: arch = NULL; break;
382 }
fdb119ef
JF
383 }
384
afbb7c8e
JF
385 _assert(arch != NULL);
386
fdb119ef
JF
387 pid_t pid = fork();
388 _syscall(pid);
389 if (pid == 0) {
390 char *ssize;
afbb7c8e
JF
391 asprintf(&ssize, "%u", (sizeof(struct SuperBlob) + 2 * sizeof(struct BlobIndex) + sizeof(struct CodeDirectory) + strlen(base) + 1 + (size + 0x1000 - 1) / 0x1000 * 0x14 + 0xc + 15) / 16 * 16);
392 execlp(allocate, allocate, "-i", path, "-a", arch, ssize, "-o", temp, NULL);
fdb119ef
JF
393 _assert(false);
394 }
395
396 int status;
397 _syscall(waitpid(pid, &status, 0));
398 _assert(WIFEXITED(status));
399 _assert(WEXITSTATUS(status) == 0);
400 }
401
402 Framework framework(temp == NULL ? path : temp);
403 struct linkedit_data_command *signature(NULL);
a362a82f
JF
404
405 if (flag_p)
406 printf("path%zu='%s'\n", filei, file->c_str());
407
408 _foreach (load_command, framework.GetLoadCommands()) {
409 uint32_t cmd(framework.Swap((*load_command)->cmd));
410
411 if (flag_R && cmd == LC_REEXPORT_DYLIB)
412 (*load_command)->cmd = framework.Swap(LC_LOAD_DYLIB);
fdb119ef
JF
413 else if (cmd == LC_CODE_SIGNATURE)
414 signature = reinterpret_cast<struct linkedit_data_command *>(*load_command);
a362a82f
JF
415 else if (cmd == LC_UUID) {
416 volatile struct uuid_command *uuid_command(reinterpret_cast<struct uuid_command *>(*load_command));
417
418 if (flag_u) {
419 printf("uuid%zu=%.2x%.2x%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x\n", filei,
420 uuid_command->uuid[ 0], uuid_command->uuid[ 1], uuid_command->uuid[ 2], uuid_command->uuid[ 3],
421 uuid_command->uuid[ 4], uuid_command->uuid[ 5], uuid_command->uuid[ 6], uuid_command->uuid[ 7],
422 uuid_command->uuid[ 8], uuid_command->uuid[ 9], uuid_command->uuid[10], uuid_command->uuid[11],
423 uuid_command->uuid[12], uuid_command->uuid[13], uuid_command->uuid[14], uuid_command->uuid[15]
424 );
425 }
426 } else if (cmd == LC_ID_DYLIB) {
427 volatile struct dylib_command *dylib_command(reinterpret_cast<struct dylib_command *>(*load_command));
428
429 if (flag_t)
430 printf("time%zu=0x%.8x\n", filei, framework.Swap(dylib_command->dylib.timestamp));
431
432 if (flag_T) {
433 uint32_t timed;
434
435 if (!timeh)
436 timed = timev;
437 else {
438 dylib_command->dylib.timestamp = 0;
439 timed = hash(reinterpret_cast<uint8_t *>(framework.GetBase()), framework.GetSize(), timev);
440 }
441
442 dylib_command->dylib.timestamp = framework.Swap(timed);
443 }
444 }
445 }
446
fdb119ef
JF
447 if (flag_S) {
448 _assert(signature != NULL);
449
450 uint32_t data = framework.Swap(signature->dataoff);
451 uint32_t size = framework.Swap(signature->datasize);
452
453 uint8_t *top = reinterpret_cast<uint8_t *>(framework.GetBase());
454 uint8_t *blob = top + data;
455 struct SuperBlob *super = reinterpret_cast<struct SuperBlob *>(blob);
456 super->magic = Swap(CSMAGIC_EMBEDDED_SIGNATURE);
457
afbb7c8e 458 uint32_t count = 2;
fdb119ef
JF
459 uint32_t offset = sizeof(struct SuperBlob) + count * sizeof(struct BlobIndex);
460
461 super->index[0].type = Swap(CSSLOT_CODEDIRECTORY);
462 super->index[0].offset = Swap(offset);
463
464 uint32_t begin = offset;
465 struct CodeDirectory *directory = reinterpret_cast<struct CodeDirectory *>(blob + begin);
466 offset += sizeof(struct CodeDirectory);
467
468 directory->magic = Swap(CSMAGIC_CODEDIRECTORY);
469 directory->version = Swap(0x00020001);
470 directory->flags = Swap(0);
471 directory->codeLimit = Swap(data);
472 directory->hashSize = 0x14;
473 directory->hashType = 0x01;
474 directory->spare1 = 0x00;
475 directory->pageSize = 0x0c;
476 directory->spare2 = Swap(0);
477
478 directory->identOffset = Swap(offset - begin);
479 strcpy(reinterpret_cast<char *>(blob + offset), base);
480 offset += strlen(base) + 1;
481
482 uint8_t (*hashes)[20] = reinterpret_cast<uint8_t (*)[20]>(blob + offset);
483 uint32_t special = 0;
484
485 uint32_t pages = (data + 0x1000 - 1) / 0x1000;
486 directory->nSpecialSlots = Swap(special);
487 directory->nCodeSlots = Swap(pages);
488
489 if (pages != 1)
490 for (size_t i = 0; i != pages - 1; ++i)
491 sha1(hashes[special + i], top + 0x1000 * i, 0x1000);
492 if (pages != 0)
493 sha1(hashes[special + pages - 1], top + 0x1000 * (pages - 1), data % 0x1000);
494
495 directory->hashOffset = Swap(offset - begin);
496 offset += sizeof(*hashes) * (special + pages);
497 directory->length = Swap(offset - begin);
498
afbb7c8e
JF
499 super->index[1].type = Swap(CSSLOT_REQUIREMENTS);
500 super->index[1].offset = Swap(offset);
501
502 memcpy(blob + offset, "\xfa\xde\x0c\x01\x00\x00\x00\x0c\x00\x00\x00\x00", 0xc);
503 offset += 0xc;
504
fdb119ef
JF
505 super->count = Swap(count);
506 super->length = Swap(offset);
507
afbb7c8e 508 _assert(offset <= size);
fdb119ef
JF
509 memset(blob + offset, 0, size - offset);
510 }
511
512 if (temp) {
31ad673b
JF
513 struct stat info;
514 _syscall(stat(path, &info));
515 _syscall(chown(temp, info.st_uid, info.st_gid));
516 _syscall(chmod(temp, info.st_mode));
fdb119ef
JF
517 _syscall(unlink(path));
518 _syscall(rename(temp, path));
519 free(temp);
520 }
521
522 free(dir);
a362a82f
JF
523 ++filei;
524 } catch (const char *) {
525 ++filee;
526 ++filei;
527 }
528
529 return filee;
530}