Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/export/macho.cpp
9903 views
1
/**************************************************************************/
2
/* macho.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "macho.h"
32
33
#include "core/crypto/crypto_core.h"
34
35
uint32_t MachO::seg_align(uint64_t p_vmaddr, uint32_t p_min, uint32_t p_max) {
36
uint32_t salign = p_max;
37
if (p_vmaddr != 0) {
38
uint64_t seg_align = 1;
39
salign = 0;
40
while ((seg_align & p_vmaddr) == 0) {
41
seg_align = seg_align << 1;
42
salign++;
43
}
44
salign = CLAMP(salign, p_min, p_max);
45
}
46
return salign;
47
}
48
49
bool MachO::alloc_signature(uint64_t p_size) {
50
ERR_FAIL_COND_V_MSG(fa.is_null(), false, "MachO: File not opened.");
51
if (signature_offset != 0) {
52
// Nothing to do, already have signature load command.
53
return true;
54
}
55
if (lc_limit == 0 || lc_limit + 16 > exe_base) {
56
ERR_FAIL_V_MSG(false, "MachO: Can't allocate signature load command, please use \"codesign_allocate\" utility first.");
57
} else {
58
// Add signature load command.
59
signature_offset = lc_limit;
60
61
fa->seek(lc_limit);
62
LoadCommandHeader lc;
63
lc.cmd = LC_CODE_SIGNATURE;
64
lc.cmdsize = 16;
65
if (swap) {
66
lc.cmdsize = BSWAP32(lc.cmdsize);
67
}
68
fa->store_buffer((const uint8_t *)&lc, sizeof(LoadCommandHeader));
69
70
uint32_t lc_offset = fa->get_length() + PAD(fa->get_length(), 16);
71
uint32_t lc_size = 0;
72
if (swap) {
73
lc_offset = BSWAP32(lc_offset);
74
lc_size = BSWAP32(lc_size);
75
}
76
fa->store_32(lc_offset);
77
fa->store_32(lc_size);
78
79
// Write new command number.
80
fa->seek(0x10);
81
uint32_t ncmds = fa->get_32();
82
uint32_t cmdssize = fa->get_32();
83
if (swap) {
84
ncmds = BSWAP32(ncmds);
85
cmdssize = BSWAP32(cmdssize);
86
}
87
ncmds += 1;
88
cmdssize += 16;
89
if (swap) {
90
ncmds = BSWAP32(ncmds);
91
cmdssize = BSWAP32(cmdssize);
92
}
93
fa->seek(0x10);
94
fa->store_32(ncmds);
95
fa->store_32(cmdssize);
96
97
lc_limit = lc_limit + sizeof(LoadCommandHeader) + 8;
98
99
return true;
100
}
101
}
102
103
bool MachO::is_macho(const String &p_path) {
104
Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
105
ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("MachO: Can't open file: \"%s\".", p_path));
106
uint32_t magic = fb->get_32();
107
return (magic == 0xcefaedfe || magic == 0xfeedface || magic == 0xcffaedfe || magic == 0xfeedfacf);
108
}
109
110
uint32_t MachO::get_filetype(const String &p_path) {
111
Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::READ);
112
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, vformat("MachO: Can't open file: \"%s\".", p_path));
113
uint32_t magic = fa->get_32();
114
MachHeader mach_header;
115
116
// Read MachO header.
117
if (magic == 0xcefaedfe || magic == 0xfeedface) {
118
// Thin 32-bit binary.
119
fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader));
120
} else if (magic == 0xcffaedfe || magic == 0xfeedfacf) {
121
// Thin 64-bit binary.
122
fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader));
123
fa->get_32(); // Skip extra reserved field.
124
} else {
125
ERR_FAIL_V_MSG(0, vformat("MachO: File is not a valid MachO binary: \"%s\".", p_path));
126
}
127
return mach_header.filetype;
128
}
129
130
bool MachO::open_file(const String &p_path) {
131
fa = FileAccess::open(p_path, FileAccess::READ_WRITE);
132
ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("MachO: Can't open file: \"%s\".", p_path));
133
uint32_t magic = fa->get_32();
134
MachHeader mach_header;
135
136
// Read MachO header.
137
swap = (magic == 0xcffaedfe || magic == 0xcefaedfe);
138
if (magic == 0xcefaedfe || magic == 0xfeedface) {
139
// Thin 32-bit binary.
140
fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader));
141
} else if (magic == 0xcffaedfe || magic == 0xfeedfacf) {
142
// Thin 64-bit binary.
143
fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader));
144
fa->get_32(); // Skip extra reserved field.
145
} else {
146
ERR_FAIL_V_MSG(false, vformat("MachO: File is not a valid MachO binary: \"%s\".", p_path));
147
}
148
149
if (swap) {
150
mach_header.ncmds = BSWAP32(mach_header.ncmds);
151
mach_header.cpusubtype = BSWAP32(mach_header.cpusubtype);
152
mach_header.cputype = BSWAP32(mach_header.cputype);
153
}
154
cpusubtype = mach_header.cpusubtype;
155
cputype = mach_header.cputype;
156
align = 0;
157
exe_base = std::numeric_limits<uint64_t>::max();
158
exe_limit = 0;
159
lc_limit = 0;
160
link_edit_offset = 0;
161
signature_offset = 0;
162
163
// Read load commands.
164
for (uint32_t i = 0; i < mach_header.ncmds; i++) {
165
LoadCommandHeader lc;
166
fa->get_buffer((uint8_t *)&lc, sizeof(LoadCommandHeader));
167
if (swap) {
168
lc.cmd = BSWAP32(lc.cmd);
169
lc.cmdsize = BSWAP32(lc.cmdsize);
170
}
171
uint64_t ps = fa->get_position();
172
switch (lc.cmd) {
173
case LC_SEGMENT: {
174
LoadCommandSegment lc_seg;
175
fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment));
176
if (swap) {
177
lc_seg.nsects = BSWAP32(lc_seg.nsects);
178
lc_seg.vmaddr = BSWAP32(lc_seg.vmaddr);
179
lc_seg.vmsize = BSWAP32(lc_seg.vmsize);
180
}
181
align = MAX(align, seg_align(lc_seg.vmaddr, 2, 15));
182
if (String(lc_seg.segname) == "__TEXT") {
183
exe_limit = MAX(exe_limit, lc_seg.vmsize);
184
for (uint32_t j = 0; j < lc_seg.nsects; j++) {
185
Section lc_sect;
186
fa->get_buffer((uint8_t *)&lc_sect, sizeof(Section));
187
if (String(lc_sect.sectname) == "__text") {
188
if (swap) {
189
exe_base = MIN(exe_base, BSWAP32(lc_sect.offset));
190
} else {
191
exe_base = MIN(exe_base, lc_sect.offset);
192
}
193
}
194
if (swap) {
195
align = MAX(align, BSWAP32(lc_sect.align));
196
} else {
197
align = MAX(align, lc_sect.align);
198
}
199
}
200
} else if (String(lc_seg.segname) == "__LINKEDIT") {
201
link_edit_offset = ps - 8;
202
}
203
} break;
204
case LC_SEGMENT_64: {
205
LoadCommandSegment64 lc_seg;
206
fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment64));
207
if (swap) {
208
lc_seg.nsects = BSWAP32(lc_seg.nsects);
209
lc_seg.vmaddr = BSWAP64(lc_seg.vmaddr);
210
lc_seg.vmsize = BSWAP64(lc_seg.vmsize);
211
}
212
align = MAX(align, seg_align(lc_seg.vmaddr, 3, 15));
213
if (String(lc_seg.segname) == "__TEXT") {
214
exe_limit = MAX(exe_limit, lc_seg.vmsize);
215
for (uint32_t j = 0; j < lc_seg.nsects; j++) {
216
Section64 lc_sect;
217
fa->get_buffer((uint8_t *)&lc_sect, sizeof(Section64));
218
if (String(lc_sect.sectname) == "__text") {
219
if (swap) {
220
exe_base = MIN(exe_base, BSWAP32(lc_sect.offset));
221
} else {
222
exe_base = MIN(exe_base, lc_sect.offset);
223
}
224
if (swap) {
225
align = MAX(align, BSWAP32(lc_sect.align));
226
} else {
227
align = MAX(align, lc_sect.align);
228
}
229
}
230
}
231
} else if (String(lc_seg.segname) == "__LINKEDIT") {
232
link_edit_offset = ps - 8;
233
}
234
} break;
235
case LC_CODE_SIGNATURE: {
236
signature_offset = ps - 8;
237
} break;
238
default: {
239
} break;
240
}
241
fa->seek(ps + lc.cmdsize - 8);
242
lc_limit = ps + lc.cmdsize - 8;
243
}
244
245
if (exe_limit == 0 || lc_limit == 0) {
246
ERR_FAIL_V_MSG(false, vformat("MachO: No load commands or executable code found: \"%s\".", p_path));
247
}
248
249
return true;
250
}
251
252
uint64_t MachO::get_exe_base() {
253
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
254
return exe_base;
255
}
256
257
uint64_t MachO::get_exe_limit() {
258
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
259
return exe_limit;
260
}
261
262
int32_t MachO::get_align() {
263
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
264
return align;
265
}
266
267
uint32_t MachO::get_cputype() {
268
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
269
return cputype;
270
}
271
272
uint32_t MachO::get_cpusubtype() {
273
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
274
return cpusubtype;
275
}
276
277
uint64_t MachO::get_size() {
278
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
279
return fa->get_length();
280
}
281
282
uint64_t MachO::get_signature_offset() {
283
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
284
ERR_FAIL_COND_V_MSG(signature_offset == 0, 0, "MachO: No signature load command.");
285
286
fa->seek(signature_offset + 8);
287
if (swap) {
288
return BSWAP32(fa->get_32());
289
} else {
290
return fa->get_32();
291
}
292
}
293
294
uint64_t MachO::get_code_limit() {
295
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
296
297
if (signature_offset == 0) {
298
return fa->get_length() + PAD(fa->get_length(), 16);
299
} else {
300
return get_signature_offset();
301
}
302
}
303
304
uint64_t MachO::get_signature_size() {
305
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
306
ERR_FAIL_COND_V_MSG(signature_offset == 0, 0, "MachO: No signature load command.");
307
308
fa->seek(signature_offset + 12);
309
if (swap) {
310
return BSWAP32(fa->get_32());
311
} else {
312
return fa->get_32();
313
}
314
}
315
316
bool MachO::is_signed() {
317
ERR_FAIL_COND_V_MSG(fa.is_null(), false, "MachO: File not opened.");
318
if (signature_offset == 0) {
319
return false;
320
}
321
322
fa->seek(get_signature_offset());
323
uint32_t magic = BSWAP32(fa->get_32());
324
if (magic != 0xfade0cc0) {
325
return false; // No SuperBlob found.
326
}
327
fa->get_32(); // Skip size field, unused.
328
uint32_t count = BSWAP32(fa->get_32());
329
for (uint32_t i = 0; i < count; i++) {
330
uint32_t index_type = BSWAP32(fa->get_32());
331
uint32_t offset = BSWAP32(fa->get_32());
332
if (index_type == 0x00000000) { // CodeDirectory index type.
333
fa->seek(get_signature_offset() + offset + 12);
334
uint32_t flags = BSWAP32(fa->get_32());
335
if (flags & 0x20000) {
336
return false; // Found CD, linker-signed.
337
} else {
338
return true; // Found CD, not linker-signed.
339
}
340
}
341
}
342
return false; // No CD found.
343
}
344
345
PackedByteArray MachO::get_cdhash_sha1() {
346
ERR_FAIL_COND_V_MSG(fa.is_null(), PackedByteArray(), "MachO: File not opened.");
347
if (signature_offset == 0) {
348
return PackedByteArray();
349
}
350
351
fa->seek(get_signature_offset());
352
uint32_t magic = BSWAP32(fa->get_32());
353
if (magic != 0xfade0cc0) {
354
return PackedByteArray(); // No SuperBlob found.
355
}
356
fa->get_32(); // Skip size field, unused.
357
uint32_t count = BSWAP32(fa->get_32());
358
for (uint32_t i = 0; i < count; i++) {
359
fa->get_32(); // Index type, skip.
360
uint32_t offset = BSWAP32(fa->get_32());
361
uint64_t pos = fa->get_position();
362
363
fa->seek(get_signature_offset() + offset);
364
uint32_t cdmagic = BSWAP32(fa->get_32());
365
uint32_t cdsize = BSWAP32(fa->get_32());
366
if (cdmagic == 0xfade0c02) { // CodeDirectory.
367
fa->seek(get_signature_offset() + offset + 36);
368
uint8_t hash_size = fa->get_8();
369
uint8_t hash_type = fa->get_8();
370
if (hash_size == 0x14 && hash_type == 0x01) { /* SHA-1 */
371
PackedByteArray hash;
372
hash.resize(0x14);
373
374
fa->seek(get_signature_offset() + offset);
375
PackedByteArray blob;
376
blob.resize(cdsize);
377
fa->get_buffer(blob.ptrw(), cdsize);
378
379
CryptoCore::SHA1Context ctx;
380
ctx.start();
381
ctx.update(blob.ptr(), blob.size());
382
ctx.finish(hash.ptrw());
383
384
return hash;
385
}
386
}
387
fa->seek(pos);
388
}
389
return PackedByteArray();
390
}
391
392
PackedByteArray MachO::get_cdhash_sha256() {
393
ERR_FAIL_COND_V_MSG(fa.is_null(), PackedByteArray(), "MachO: File not opened.");
394
if (signature_offset == 0) {
395
return PackedByteArray();
396
}
397
398
fa->seek(get_signature_offset());
399
uint32_t magic = BSWAP32(fa->get_32());
400
if (magic != 0xfade0cc0) {
401
return PackedByteArray(); // No SuperBlob found.
402
}
403
fa->get_32(); // Skip size field, unused.
404
uint32_t count = BSWAP32(fa->get_32());
405
for (uint32_t i = 0; i < count; i++) {
406
fa->get_32(); // Index type, skip.
407
uint32_t offset = BSWAP32(fa->get_32());
408
uint64_t pos = fa->get_position();
409
410
fa->seek(get_signature_offset() + offset);
411
uint32_t cdmagic = BSWAP32(fa->get_32());
412
uint32_t cdsize = BSWAP32(fa->get_32());
413
if (cdmagic == 0xfade0c02) { // CodeDirectory.
414
fa->seek(get_signature_offset() + offset + 36);
415
uint8_t hash_size = fa->get_8();
416
uint8_t hash_type = fa->get_8();
417
if (hash_size == 0x20 && hash_type == 0x02) { /* SHA-256 */
418
PackedByteArray hash;
419
hash.resize(0x20);
420
421
fa->seek(get_signature_offset() + offset);
422
PackedByteArray blob;
423
blob.resize(cdsize);
424
fa->get_buffer(blob.ptrw(), cdsize);
425
426
CryptoCore::SHA256Context ctx;
427
ctx.start();
428
ctx.update(blob.ptr(), blob.size());
429
ctx.finish(hash.ptrw());
430
431
return hash;
432
}
433
}
434
fa->seek(pos);
435
}
436
return PackedByteArray();
437
}
438
439
PackedByteArray MachO::get_requirements() {
440
ERR_FAIL_COND_V_MSG(fa.is_null(), PackedByteArray(), "MachO: File not opened.");
441
if (signature_offset == 0) {
442
return PackedByteArray();
443
}
444
445
fa->seek(get_signature_offset());
446
uint32_t magic = BSWAP32(fa->get_32());
447
if (magic != 0xfade0cc0) {
448
return PackedByteArray(); // No SuperBlob found.
449
}
450
fa->get_32(); // Skip size field, unused.
451
uint32_t count = BSWAP32(fa->get_32());
452
for (uint32_t i = 0; i < count; i++) {
453
fa->get_32(); // Index type, skip.
454
uint32_t offset = BSWAP32(fa->get_32());
455
uint64_t pos = fa->get_position();
456
457
fa->seek(get_signature_offset() + offset);
458
uint32_t rqmagic = BSWAP32(fa->get_32());
459
uint32_t rqsize = BSWAP32(fa->get_32());
460
if (rqmagic == 0xfade0c01) { // Requirements.
461
PackedByteArray blob;
462
fa->seek(get_signature_offset() + offset);
463
blob.resize(rqsize);
464
fa->get_buffer(blob.ptrw(), rqsize);
465
return blob;
466
}
467
fa->seek(pos);
468
}
469
return PackedByteArray();
470
}
471
472
const Ref<FileAccess> MachO::get_file() const {
473
return fa;
474
}
475
476
Ref<FileAccess> MachO::get_file() {
477
return fa;
478
}
479
480
bool MachO::set_signature_size(uint64_t p_size) {
481
ERR_FAIL_COND_V_MSG(fa.is_null(), false, "MachO: File not opened.");
482
483
// Ensure signature load command exists.
484
ERR_FAIL_COND_V_MSG(link_edit_offset == 0, false, "MachO: No __LINKEDIT segment found.");
485
ERR_FAIL_COND_V_MSG(!alloc_signature(p_size), false, "MachO: Can't allocate signature load command.");
486
487
// Update signature load command.
488
uint64_t old_size = get_signature_size();
489
uint64_t new_size = p_size + PAD(p_size, 16384);
490
491
if (new_size <= old_size) {
492
fa->seek(get_signature_offset());
493
for (uint64_t i = 0; i < old_size; i++) {
494
fa->store_8(0x00);
495
}
496
return true;
497
}
498
499
fa->seek(signature_offset + 12);
500
if (swap) {
501
fa->store_32(BSWAP32(new_size));
502
} else {
503
fa->store_32(new_size);
504
}
505
506
uint64_t end = get_signature_offset() + new_size;
507
508
// Update "__LINKEDIT" segment.
509
LoadCommandHeader lc;
510
fa->seek(link_edit_offset);
511
fa->get_buffer((uint8_t *)&lc, sizeof(LoadCommandHeader));
512
if (swap) {
513
lc.cmd = BSWAP32(lc.cmd);
514
lc.cmdsize = BSWAP32(lc.cmdsize);
515
}
516
switch (lc.cmd) {
517
case LC_SEGMENT: {
518
LoadCommandSegment lc_seg;
519
fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment));
520
if (swap) {
521
lc_seg.vmsize = BSWAP32(lc_seg.vmsize);
522
lc_seg.filesize = BSWAP32(lc_seg.filesize);
523
lc_seg.fileoff = BSWAP32(lc_seg.fileoff);
524
}
525
526
lc_seg.vmsize = end - lc_seg.fileoff;
527
lc_seg.vmsize += PAD(lc_seg.vmsize, 4096);
528
lc_seg.filesize = end - lc_seg.fileoff;
529
530
if (swap) {
531
lc_seg.vmsize = BSWAP32(lc_seg.vmsize);
532
lc_seg.filesize = BSWAP32(lc_seg.filesize);
533
}
534
fa->seek(link_edit_offset + 8);
535
fa->store_buffer((const uint8_t *)&lc_seg, sizeof(LoadCommandSegment));
536
} break;
537
case LC_SEGMENT_64: {
538
LoadCommandSegment64 lc_seg;
539
fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment64));
540
if (swap) {
541
lc_seg.vmsize = BSWAP64(lc_seg.vmsize);
542
lc_seg.filesize = BSWAP64(lc_seg.filesize);
543
lc_seg.fileoff = BSWAP64(lc_seg.fileoff);
544
}
545
lc_seg.vmsize = end - lc_seg.fileoff;
546
lc_seg.vmsize += PAD(lc_seg.vmsize, 4096);
547
lc_seg.filesize = end - lc_seg.fileoff;
548
if (swap) {
549
lc_seg.vmsize = BSWAP64(lc_seg.vmsize);
550
lc_seg.filesize = BSWAP64(lc_seg.filesize);
551
}
552
fa->seek(link_edit_offset + 8);
553
fa->store_buffer((const uint8_t *)&lc_seg, sizeof(LoadCommandSegment64));
554
} break;
555
default: {
556
ERR_FAIL_V_MSG(false, "MachO: Invalid __LINKEDIT segment type.");
557
} break;
558
}
559
fa->seek(get_signature_offset());
560
for (uint64_t i = 0; i < new_size; i++) {
561
fa->store_8(0x00);
562
}
563
return true;
564
}
565
566