kompiler 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/kompile +143 -28
- data/lib/kompiler/directives.rb +388 -3
- data/lib/kompiler/wrappers/elf_wrapper.rb +2 -2
- data/lib/kompiler/wrappers/macho_wrapper.rb +1274 -0
- data/lib/kompiler/wrappers/packed_bytes.rb +18 -4
- data/lib/kompiler/wrappers.rb +1 -0
- metadata +3 -2
@@ -0,0 +1,1274 @@
|
|
1
|
+
require 'kompiler/wrappers/packed_bytes'
|
2
|
+
require 'securerandom'
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
|
6
|
+
module Kompiler
|
7
|
+
|
8
|
+
module Wrappers
|
9
|
+
|
10
|
+
|
11
|
+
module MachO
|
12
|
+
|
13
|
+
MH_MAGIC_32 = 0xfeedface
|
14
|
+
MH_MAGIC_64 = 0xfeedfacf
|
15
|
+
|
16
|
+
LC_REQ_DYLD = 0x80000000
|
17
|
+
|
18
|
+
|
19
|
+
# Encodes an unsigned integer into ULEB128 byte sequence
|
20
|
+
def self.encode_uleb value
|
21
|
+
|
22
|
+
result_bytes = ""
|
23
|
+
|
24
|
+
loop do
|
25
|
+
value_bits = value & 0x7f # 7 lower bits
|
26
|
+
value >>= 7 # shift value by 7 bits
|
27
|
+
continue_bit = (value == 0) ? 0 : 1 # "More bytes to come" bit
|
28
|
+
final_byte = value_bits | (continue_bit << 7)
|
29
|
+
result_bytes << final_byte.chr
|
30
|
+
|
31
|
+
break if value == 0 # If nothing else to encode, exit the loop
|
32
|
+
end
|
33
|
+
|
34
|
+
return result_bytes
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
def self.wrap_obj code, symbols, arch_type: 64, cputype: 0, cpusubtype: 0
|
41
|
+
|
42
|
+
segments = [
|
43
|
+
{
|
44
|
+
name: "__TEXT",
|
45
|
+
vaddr: 0,
|
46
|
+
vsize: code.bytesize,
|
47
|
+
maxprot: 7,
|
48
|
+
initprot: 7,
|
49
|
+
flags: 0,
|
50
|
+
|
51
|
+
sections: [
|
52
|
+
{
|
53
|
+
name: "__text",
|
54
|
+
vaddr: 0,
|
55
|
+
align: 4,
|
56
|
+
flags: 0x80000400, # section contains some machine instructions
|
57
|
+
content: code,
|
58
|
+
},
|
59
|
+
]
|
60
|
+
}
|
61
|
+
]
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
output = MachO.build(segments: segments, symbols: symbols, prebuilt_lcs: prebuilt_lcs, arch_type: arch_type, cputype: cputype, cpusubtype: cpusubtype, filetype: 1, align_section_contents: true)
|
66
|
+
|
67
|
+
return output
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def self.wrap_exec_static code, symbols, arch_type: 64, cputype: 0, cpusubtype: 0, thread_state: nil, virtual_entry_address: 0x80000, codesign: false
|
73
|
+
|
74
|
+
raise "Thread state must be provided for building a static Mach-O executable" if thread_state == nil
|
75
|
+
|
76
|
+
segments = [
|
77
|
+
{
|
78
|
+
name: "__PAGEZERO",
|
79
|
+
vaddr: 0,
|
80
|
+
vsize: virtual_entry_address - 0x1000,
|
81
|
+
maxprot: 0,
|
82
|
+
initprot: 0,
|
83
|
+
flags: 0,
|
84
|
+
file_offset: 0,
|
85
|
+
filesize: 0,
|
86
|
+
sections: []
|
87
|
+
},
|
88
|
+
{
|
89
|
+
name: "__TEXT",
|
90
|
+
vaddr: virtual_entry_address - 0x1000,
|
91
|
+
maxprot: 7,
|
92
|
+
initprot: 7,
|
93
|
+
flags: 0,
|
94
|
+
file_offset: 0,
|
95
|
+
sections: [
|
96
|
+
{
|
97
|
+
name: "__text",
|
98
|
+
vaddr: virtual_entry_address,
|
99
|
+
align: 0x1000,
|
100
|
+
flags: 0x80000400, # section contains some machine instructions
|
101
|
+
content: code,
|
102
|
+
}
|
103
|
+
]
|
104
|
+
}
|
105
|
+
]
|
106
|
+
|
107
|
+
if codesign
|
108
|
+
segments << {
|
109
|
+
name: "__LINKEDIT",
|
110
|
+
vaddr: 0x200000000,
|
111
|
+
vsize: 0,
|
112
|
+
maxprot: 1,
|
113
|
+
initprot: 1,
|
114
|
+
flags: 0,
|
115
|
+
sections: []
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
uuid_lc = PackedBytes.new
|
121
|
+
uuid_lc.uint32 0x1b
|
122
|
+
uuid_lc.uint32 24
|
123
|
+
uuid_lc.bytes Random.urandom(16) # UUID
|
124
|
+
|
125
|
+
|
126
|
+
unix_thread_lc = PackedBytes.new
|
127
|
+
unix_thread_lc.uint32 0x5 # LC_UNIX_THREAD
|
128
|
+
unix_thread_lc.uint32 thread_state.bytesize + 8 # cmdsize
|
129
|
+
unix_thread_lc.add thread_state
|
130
|
+
|
131
|
+
|
132
|
+
|
133
|
+
prebuilt_lcs = [
|
134
|
+
{
|
135
|
+
bytes: uuid_lc.result,
|
136
|
+
},
|
137
|
+
{
|
138
|
+
bytes: unix_thread_lc.result,
|
139
|
+
},
|
140
|
+
]
|
141
|
+
|
142
|
+
flags = 1
|
143
|
+
|
144
|
+
out = MachO.build segments: segments, symbols: symbols, prebuilt_lcs: prebuilt_lcs, codesign: codesign, arch_type: arch_type, cputype: cputype, cpusubtype: cpusubtype, filetype: 2, flags: flags, align_section_contents: true
|
145
|
+
|
146
|
+
return out
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
|
154
|
+
|
155
|
+
# Wrap executable with dynamic linker
|
156
|
+
# Wraps a binary program in Mach-O format using dynamic link commands (especially used on ARM MacOS)
|
157
|
+
#
|
158
|
+
def self.wrap_exec_dylink code, symbols, arch_type: 64, cputype: 0, cpusubtype: 0, virtual_entry_address: 0x80000, codesign: false
|
159
|
+
|
160
|
+
pagezero_size = 0x100000000
|
161
|
+
|
162
|
+
text_segment_vaddr = pagezero_size
|
163
|
+
|
164
|
+
text_section_vaddr = text_segment_vaddr + 0x1000
|
165
|
+
|
166
|
+
virtual_entry_address = text_section_vaddr
|
167
|
+
|
168
|
+
linkedit_vaddr = pagezero_size * 3
|
169
|
+
|
170
|
+
|
171
|
+
|
172
|
+
n_local_sym = symbols.size
|
173
|
+
|
174
|
+
symbols.map! do
|
175
|
+
_1[:value] += text_section_vaddr
|
176
|
+
_1
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
linkedit = PackedBytes.new
|
181
|
+
|
182
|
+
|
183
|
+
symtab, sym_strtab = MachO.build_symtab(symbols, arch_type: arch_type).values
|
184
|
+
|
185
|
+
symtab_offset = linkedit.result.bytesize
|
186
|
+
linkedit.add symtab
|
187
|
+
sym_strtab_offset = linkedit.result.bytesize
|
188
|
+
linkedit.add sym_strtab
|
189
|
+
|
190
|
+
|
191
|
+
segments = [
|
192
|
+
{
|
193
|
+
name: "__PAGEZERO",
|
194
|
+
file_offset: 0,
|
195
|
+
filesize: 0,
|
196
|
+
vaddr: 0,
|
197
|
+
vsize: pagezero_size,
|
198
|
+
flags: 0,
|
199
|
+
maxprot: 0,
|
200
|
+
initprot: 0,
|
201
|
+
sections: [],
|
202
|
+
},
|
203
|
+
{
|
204
|
+
name: "__TEXT",
|
205
|
+
vaddr: text_segment_vaddr,
|
206
|
+
# vsize: 0x4000,
|
207
|
+
maxprot: 7,
|
208
|
+
initprot: 7,
|
209
|
+
flags: 0,
|
210
|
+
file_offset: 0,
|
211
|
+
# filesize: 8192,
|
212
|
+
|
213
|
+
sections: [
|
214
|
+
{
|
215
|
+
name: "__text",
|
216
|
+
vaddr: text_section_vaddr,
|
217
|
+
align: 0x1000,
|
218
|
+
flags: 0x80000400, # section contains some machine instructions
|
219
|
+
content: code,
|
220
|
+
},
|
221
|
+
]
|
222
|
+
},
|
223
|
+
{
|
224
|
+
name: "__LINKEDIT",
|
225
|
+
vaddr: linkedit_vaddr,
|
226
|
+
maxprot: 1,
|
227
|
+
initprot: 1,
|
228
|
+
flags: 0,
|
229
|
+
|
230
|
+
content: linkedit.result,
|
231
|
+
content_align: 0x4000,
|
232
|
+
}
|
233
|
+
]
|
234
|
+
|
235
|
+
entry_point_lc = PackedBytes.new
|
236
|
+
entry_point_lc.uint32 0x80000028 # LC_MAIN
|
237
|
+
entry_point_lc.uint32 24 # cmdsize
|
238
|
+
|
239
|
+
entry_point_lc.uint64 0x80000 # file offset address
|
240
|
+
entry_point_lc.uint64 0 # stack size
|
241
|
+
|
242
|
+
|
243
|
+
|
244
|
+
uuid_lc = PackedBytes.new
|
245
|
+
uuid_lc.uint32 0x1b
|
246
|
+
uuid_lc.uint32 24
|
247
|
+
uuid_lc.bytes Random.urandom(16) # UUID
|
248
|
+
|
249
|
+
|
250
|
+
|
251
|
+
load_dylinker_lc = PackedBytes.new
|
252
|
+
|
253
|
+
load_dylinker_lc.uint32 0xe
|
254
|
+
load_dylinker_lc.uint32 32
|
255
|
+
load_dylinker_lc.bytes "\f\x00\x00\x00/usr/lib/dyld\x00\x00\x00\x00\x00\x00\x00"
|
256
|
+
|
257
|
+
symtab_lc = PackedBytes.new
|
258
|
+
|
259
|
+
symtab_lc.uint32 0x2
|
260
|
+
symtab_lc.uint32 24
|
261
|
+
|
262
|
+
symtab_lc.uint32 0
|
263
|
+
symtab_lc.uint32 symbols.size
|
264
|
+
|
265
|
+
symtab_lc.uint32 0
|
266
|
+
symtab_lc.uint32 sym_strtab.bytesize
|
267
|
+
|
268
|
+
|
269
|
+
dyld_info_lc = PackedBytes.new
|
270
|
+
|
271
|
+
dyld_info_lc.uint32 0x22
|
272
|
+
dyld_info_lc.uint32 12 * 4
|
273
|
+
|
274
|
+
dyld_info_lc.uint32 0 # rebase_off
|
275
|
+
dyld_info_lc.uint32 0 # size
|
276
|
+
|
277
|
+
dyld_info_lc.uint32 0 # bind_off
|
278
|
+
dyld_info_lc.uint32 0 # size
|
279
|
+
|
280
|
+
dyld_info_lc.uint32 0 # weak_bind_off
|
281
|
+
dyld_info_lc.uint32 0 # size
|
282
|
+
|
283
|
+
dyld_info_lc.uint32 0 # lazy_bind_off
|
284
|
+
dyld_info_lc.uint32 0 # size
|
285
|
+
|
286
|
+
dyld_info_lc.uint32 0 # export_off
|
287
|
+
dyld_info_lc.uint32 0 # size
|
288
|
+
|
289
|
+
|
290
|
+
|
291
|
+
code_signature_lc = PackedBytes.new
|
292
|
+
|
293
|
+
code_signature_lc.uint32 0x1d # LC_CODE_SIGNATURE
|
294
|
+
code_signature_lc.uint32 16 # cmdsize
|
295
|
+
|
296
|
+
code_signature_lc.uint32 0 # offset
|
297
|
+
code_signature_lc.uint32 0 # size
|
298
|
+
|
299
|
+
|
300
|
+
|
301
|
+
prebuilt_lcs = [
|
302
|
+
{
|
303
|
+
bytes: symtab_lc.result,
|
304
|
+
relocations: [
|
305
|
+
{
|
306
|
+
content_index: 1,
|
307
|
+
bytefield_offset: 8,
|
308
|
+
bytefield_size: 4,
|
309
|
+
addend: symtab_offset,
|
310
|
+
},
|
311
|
+
{
|
312
|
+
content_index: 1,
|
313
|
+
bytefield_offset: 16,
|
314
|
+
bytefield_size: 4,
|
315
|
+
addend: sym_strtab_offset,
|
316
|
+
}
|
317
|
+
]
|
318
|
+
},
|
319
|
+
{
|
320
|
+
bytes: load_dylinker_lc.result,
|
321
|
+
},
|
322
|
+
{
|
323
|
+
bytes: uuid_lc.result
|
324
|
+
},
|
325
|
+
{
|
326
|
+
bytes: dyld_info_lc.result
|
327
|
+
},
|
328
|
+
{
|
329
|
+
bytes: entry_point_lc.result,
|
330
|
+
relocations: [
|
331
|
+
{
|
332
|
+
content_index: 0,
|
333
|
+
bytefield_offset: 8,
|
334
|
+
bytefield_size: 8
|
335
|
+
}
|
336
|
+
]
|
337
|
+
},
|
338
|
+
]
|
339
|
+
|
340
|
+
|
341
|
+
mh_flags = 0x200000 | 0x4 | 0x1 # MH_PIE | MH_DYLDLINK | MH_NOUNDEFS
|
342
|
+
|
343
|
+
|
344
|
+
output = MachO.build(segments: segments, symbols: nil, prebuilt_lcs: prebuilt_lcs, arch_type: arch_type, codesign: codesign, cputype: cputype, cpusubtype: cpusubtype, filetype: 2, align_section_contents: true, flags: mh_flags)
|
345
|
+
|
346
|
+
return output
|
347
|
+
|
348
|
+
end
|
349
|
+
|
350
|
+
|
351
|
+
def self.build_thread_state_x86_32(r: [0] * 7, esp: 0, ss: 0, eflags: 0, eip: 0, cs: 0, ds: 0, es: 0, fs: 0, gs: 0)
|
352
|
+
raise "r must be an array of 7 elements" if r.size != 7
|
353
|
+
|
354
|
+
thread_state = PackedBytes.new
|
355
|
+
|
356
|
+
thread_state.uint32 1 # flavor = x86_THREAD_STATE_32
|
357
|
+
thread_state.uint32 16 # count = 16 4-byte fields
|
358
|
+
|
359
|
+
r.each do |r_val|
|
360
|
+
thread_state.uint32 r_val
|
361
|
+
end
|
362
|
+
|
363
|
+
thread_state.uint32 esp
|
364
|
+
thread_state.uint32 ss
|
365
|
+
thread_state.uint32 eflags
|
366
|
+
thread_state.uint32 eip
|
367
|
+
thread_state.uint32 cs
|
368
|
+
thread_state.uint32 ds
|
369
|
+
thread_state.uint32 es
|
370
|
+
thread_state.uint32 fs
|
371
|
+
thread_state.uint32 gs
|
372
|
+
|
373
|
+
return thread_state.result
|
374
|
+
end
|
375
|
+
|
376
|
+
|
377
|
+
def self.build_thread_state_x86_64(r: [0] * 16, rip: 0, rflags: 0, cs: 0, fs: 0, gs: 0)
|
378
|
+
|
379
|
+
raise "r must be an array of 16 elements" if r.size != 16
|
380
|
+
|
381
|
+
thread_state = PackedBytes.new
|
382
|
+
|
383
|
+
thread_state.uint32 4 # flavor = x86_THREAD_STATE_64
|
384
|
+
thread_state.uint32 42 # count = 21 8-byte fields / sizeof(int) (4 bytes)
|
385
|
+
|
386
|
+
|
387
|
+
r.each do |r_val|
|
388
|
+
thread_state.uint64 r_val
|
389
|
+
end
|
390
|
+
|
391
|
+
thread_state.uint64 rip
|
392
|
+
|
393
|
+
thread_state.uint64 rflags
|
394
|
+
thread_state.uint64 cs
|
395
|
+
thread_state.uint64 fs
|
396
|
+
thread_state.uint64 gs
|
397
|
+
|
398
|
+
|
399
|
+
return thread_state.result
|
400
|
+
end
|
401
|
+
|
402
|
+
|
403
|
+
|
404
|
+
def self.build_thread_state_arm64(x: [0] * 29, fp: 0, lr: 0, sp: 0, pc: 0, cpsr: 0)
|
405
|
+
|
406
|
+
thread_state = PackedBytes.new
|
407
|
+
|
408
|
+
thread_state.uint32 6 # flavor = ARM_THREAD_STATE64
|
409
|
+
thread_state.uint32 33 * 2 + 2 # count = 33 8-byte fields + 2 4-byte fields / sizeof(int) (4 bytes)
|
410
|
+
|
411
|
+
raise "x must be an array of 29 values." if x.size != 29
|
412
|
+
|
413
|
+
x.each do |x_val|
|
414
|
+
thread_state.uint64 x_val
|
415
|
+
end
|
416
|
+
|
417
|
+
thread_state.uint64 fp
|
418
|
+
thread_state.uint64 lr
|
419
|
+
thread_state.uint64 sp
|
420
|
+
thread_state.uint64 pc
|
421
|
+
|
422
|
+
thread_state.uint32 cpsr
|
423
|
+
|
424
|
+
thread_state.uint32 0 # pad
|
425
|
+
|
426
|
+
return thread_state.result
|
427
|
+
end
|
428
|
+
|
429
|
+
|
430
|
+
|
431
|
+
def self.build_thread_state_arm32(r: [0] * 13, lr: 0, sp: 0, pc: 0, cpsr: 0)
|
432
|
+
|
433
|
+
thread_state = PackedBytes.new
|
434
|
+
|
435
|
+
thread_state.uint32 1 # flavor = ARM_THREAD_STATE
|
436
|
+
thread_state.uint32 17 # count = 17 4-byte fields / sizeof(int) (4 bytes)
|
437
|
+
|
438
|
+
raise "r must be an array of 13 values." if r.size != 13
|
439
|
+
|
440
|
+
r.each do |r_val|
|
441
|
+
thread_state.uint32 r_val
|
442
|
+
end
|
443
|
+
|
444
|
+
thread_state.uint32 lr
|
445
|
+
thread_state.uint32 sp
|
446
|
+
thread_state.uint32 pc
|
447
|
+
|
448
|
+
thread_state.uint32 cpsr
|
449
|
+
|
450
|
+
return thread_state.result
|
451
|
+
end
|
452
|
+
|
453
|
+
|
454
|
+
|
455
|
+
def self.build_thread_state arch: nil, entry_address: 0, stack_pointer: 0
|
456
|
+
raise "arch must be specified" if arch == nil
|
457
|
+
|
458
|
+
case arch
|
459
|
+
when "arm64"
|
460
|
+
return MachO.build_thread_state_arm64(pc: entry_address, sp: stack_pointer)
|
461
|
+
when "arm32"
|
462
|
+
return MachO.build_thread_state_arm32(pc: entry_address, sp: stack_pointer)
|
463
|
+
when "x86-64"
|
464
|
+
r = [0] * 16
|
465
|
+
r[7] = stack_pointer
|
466
|
+
return MachO.build_thread_state_x86_64(rip: entry_address, r: r)
|
467
|
+
when "x86-32"
|
468
|
+
r = [0] * 7
|
469
|
+
return MachO.build_thread_state_x86_32(r: r, eip: entry_address)
|
470
|
+
else
|
471
|
+
raise "Unkown architecture"
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
|
476
|
+
def self.build_macho_header arch_type: 64, cputype: 0, cpusubtype: 0, filetype: 0, ncmds: 0, sizeofcmds: 0, flags: 0
|
477
|
+
|
478
|
+
macho_header = PackedBytes.new
|
479
|
+
|
480
|
+
case arch_type
|
481
|
+
when 64
|
482
|
+
macho_header.uint32 MH_MAGIC_64
|
483
|
+
when 32
|
484
|
+
macho_header.uint32 MH_MAGIC_32
|
485
|
+
end
|
486
|
+
|
487
|
+
macho_header.uint32 cputype
|
488
|
+
macho_header.uint32 cpusubtype
|
489
|
+
|
490
|
+
macho_header.uint32 filetype
|
491
|
+
|
492
|
+
macho_header.uint32 ncmds
|
493
|
+
|
494
|
+
macho_header.uint32 sizeofcmds
|
495
|
+
|
496
|
+
macho_header.uint32 flags
|
497
|
+
|
498
|
+
if arch_type == 64
|
499
|
+
# Reserved field in 64-bit
|
500
|
+
macho_header.uint32 0
|
501
|
+
end
|
502
|
+
|
503
|
+
return macho_header
|
504
|
+
end
|
505
|
+
|
506
|
+
|
507
|
+
# Build a Mach-O file from the input segments, symbols and other pre-built load commands
|
508
|
+
#
|
509
|
+
# Arguments:
|
510
|
+
# segments - list of segments to include in the file (structure below)
|
511
|
+
# symbols - list of symbols to be included inside a single symtab (structure below)
|
512
|
+
# prebuilt_lcs - a list of other already built load commands (strings) to be included in the file
|
513
|
+
# arch_type - architecture type, either 32- or 64-bit (default is 64)
|
514
|
+
# codesign - specifies whether to create a code signature. If yes, a __LINKEDIT segment must be present. The code signature will be a code_directory struct with hashes of everything before it. A CODE_SIGNATURE load command will be added automatically.
|
515
|
+
# cputype - Mach-O header cputype field
|
516
|
+
# cpusubtype - Mach-O header cpusubtype field
|
517
|
+
# flags - Mach-O header flags field
|
518
|
+
# filetype - Mach-O file type
|
519
|
+
# align_section_contents - specifies whether to align the contents of each section to its alignment boundary (default is false)
|
520
|
+
#
|
521
|
+
# Segment structure:
|
522
|
+
# name - the segment's name
|
523
|
+
# vaddr - virtual load address of the segment
|
524
|
+
# vsize - virtual size in memory of the segment
|
525
|
+
# maxprot - Mach-O segment maxprot field
|
526
|
+
# initprot - Mach-O segment initprot field
|
527
|
+
# flags - Mach-O segment flags field
|
528
|
+
# sections - a list of segment's section (structure below)
|
529
|
+
#
|
530
|
+
# Segment section structure:
|
531
|
+
# name - the section's name
|
532
|
+
# vaddr - virtual load address of section
|
533
|
+
# vsize - virtual size in memory of the section
|
534
|
+
# align - section's byte alignment
|
535
|
+
# flags - section's flags
|
536
|
+
# content - section's content
|
537
|
+
#
|
538
|
+
# Symbol structure:
|
539
|
+
# name - symbol's name
|
540
|
+
# value - symbol's value
|
541
|
+
# type - symbol's type field value
|
542
|
+
# sect - symbol's sect field value
|
543
|
+
# desc - symbol's desc field value
|
544
|
+
#
|
545
|
+
# Prebuilt load command structure:
|
546
|
+
# bytes - the load command bytes
|
547
|
+
# contents - contents related to the load command
|
548
|
+
# contents_align - alignment boundary in the file for the contents
|
549
|
+
# relocations - a list of relocations to replace some load command's bytes based on the content's file offset
|
550
|
+
#
|
551
|
+
# Prebuilt load command relocation structure:
|
552
|
+
# content_index - index of the contents related to the relocation (contents of segments and symtab will appear last)
|
553
|
+
# bytefield_offset - offset of the to-be-replaced bytes in the load command
|
554
|
+
# bytefield_size - size of the bytefield to be replaced
|
555
|
+
# addend - value to add to the contents' file offset to replace the specified bytefield in the load command with
|
556
|
+
# Relocation logic:
|
557
|
+
# load_command[:bytes][lc_offset...(lc_offset + lc_size)] = int_to_bytes(contents[content_i][:file_offset] + addend, lc_size)
|
558
|
+
#
|
559
|
+
def self.build segments: [], symbols: [], prebuilt_lcs: [], arch_type: 64, cputype: 0, cpusubtype: 0, filetype: 0, align_section_contents: false, flags: 0, codesign: false
|
560
|
+
|
561
|
+
|
562
|
+
n_symtabs = 1
|
563
|
+
n_symtabs = 0 if symbols == nil
|
564
|
+
|
565
|
+
file_content = PackedBytes.new
|
566
|
+
|
567
|
+
case arch_type
|
568
|
+
when 32
|
569
|
+
flexval_size = 4
|
570
|
+
when 64
|
571
|
+
flexval_size = 8
|
572
|
+
end
|
573
|
+
|
574
|
+
segment_entry_size = 4 * 2 + 16 + flexval_size * 4 + 4 * 2 + 4 * 2
|
575
|
+
|
576
|
+
section_entry_size = 16 + 16 + flexval_size * 2 + 7 * 4
|
577
|
+
|
578
|
+
if arch_type == 64
|
579
|
+
section_entry_size += 4 # one more reserved field for 64-bit
|
580
|
+
end
|
581
|
+
|
582
|
+
symtab_entry_size = 4 * 6
|
583
|
+
|
584
|
+
code_signature_lc_size = 4 * 4
|
585
|
+
|
586
|
+
n_code_signature_lcs = codesign ? 1 : 0
|
587
|
+
|
588
|
+
n_sections = segments.map{(_1[:sections] || []).size}.sum
|
589
|
+
|
590
|
+
prebuilt_lcs_size = prebuilt_lcs.map{_1[:bytes].bytesize}.sum
|
591
|
+
|
592
|
+
cmds_size = segment_entry_size * segments.size + section_entry_size * n_sections + symtab_entry_size * n_symtabs + code_signature_lc_size * n_code_signature_lcs + prebuilt_lcs_size
|
593
|
+
|
594
|
+
|
595
|
+
n_cmds = segments.size + n_symtabs + n_code_signature_lcs + prebuilt_lcs.size # One segment (with possibly multiple sections), one symtab
|
596
|
+
|
597
|
+
macho_header = build_macho_header(
|
598
|
+
arch_type: arch_type,
|
599
|
+
cputype: cputype,
|
600
|
+
cpusubtype: cpusubtype,
|
601
|
+
filetype: filetype,
|
602
|
+
ncmds: n_cmds,
|
603
|
+
sizeofcmds: cmds_size,
|
604
|
+
flags: flags
|
605
|
+
)
|
606
|
+
|
607
|
+
|
608
|
+
file_content.add macho_header
|
609
|
+
|
610
|
+
|
611
|
+
|
612
|
+
# Calculate where all of the content can be placed
|
613
|
+
contents_offset = cmds_size + file_content.result.bytesize
|
614
|
+
|
615
|
+
|
616
|
+
contents = ""
|
617
|
+
|
618
|
+
|
619
|
+
prebuilt_lc_content_offsets = []
|
620
|
+
|
621
|
+
prebuilt_lcs.each do |lc|
|
622
|
+
if lc.keys.include? :contents
|
623
|
+
|
624
|
+
align = lc[:contents_align] || 1
|
625
|
+
|
626
|
+
file_offset = contents.bytesize + contents_offset
|
627
|
+
|
628
|
+
if file_offset % align != 0
|
629
|
+
pad = align - (file_offset % align)
|
630
|
+
contents << "\0" * pad
|
631
|
+
file_offset += pad
|
632
|
+
end
|
633
|
+
|
634
|
+
prebuilt_lc_content_offsets << file_offset
|
635
|
+
|
636
|
+
contents << lc[:contents]
|
637
|
+
else
|
638
|
+
prebuilt_lc_content_offsets << nil
|
639
|
+
end
|
640
|
+
end
|
641
|
+
|
642
|
+
|
643
|
+
|
644
|
+
contents_offsets = []
|
645
|
+
|
646
|
+
if codesign
|
647
|
+
# Ensure that the __LINKEDIT segment exists, and place it as the last segment to not interfere with other load commands and hash more of the file
|
648
|
+
|
649
|
+
linkedit_segments = segments.each_with_index.filter{|seg, idx| seg[:name] == "__LINKEDIT"}
|
650
|
+
if linkedit_segments.size == 0
|
651
|
+
raise "Segment __LINKEDIT must be present if codesign is true"
|
652
|
+
# segments << {name: "__LINKEDIT", vaddr}
|
653
|
+
else
|
654
|
+
linkedit_segment, linkedit_segment_idx = linkedit_segments[0]
|
655
|
+
|
656
|
+
# Make __LINKEDIT the last segment
|
657
|
+
segments[-1], segments[linkedit_segment_idx] = segments[linkedit_segment_idx], segments[-1]
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
|
662
|
+
|
663
|
+
|
664
|
+
|
665
|
+
segments.each do |segment|
|
666
|
+
|
667
|
+
raise "Segment name is larger than 16 characters" if segment[:name].bytesize > 16
|
668
|
+
|
669
|
+
raise "Segment can't contain both the :content and :sections key" if segment.keys.include?(:content) && segment.keys.include?(:sections) && segment[:sections].size > 0
|
670
|
+
|
671
|
+
if segment.keys.include? :content
|
672
|
+
|
673
|
+
segment[:sections] = []
|
674
|
+
|
675
|
+
alignment = segment[:content_align] || 1
|
676
|
+
|
677
|
+
file_offset = contents_offset + contents.bytesize
|
678
|
+
if file_offset % alignment != 0
|
679
|
+
pad = alignment - (file_offset % alignment)
|
680
|
+
contents << "\0" * pad
|
681
|
+
file_offset += pad
|
682
|
+
end
|
683
|
+
|
684
|
+
contents << segment[:content]
|
685
|
+
|
686
|
+
segment[:file_offset] ||= file_offset
|
687
|
+
segment[:filesize] ||= segment[:content].bytesize
|
688
|
+
|
689
|
+
segment[:vaddr] ||= 0
|
690
|
+
segment[:vsize] ||= segment[:filesize]
|
691
|
+
|
692
|
+
segment[:flags] ||= 0
|
693
|
+
|
694
|
+
segment[:maxprot] ||= 0
|
695
|
+
segment[:initprot] ||= 0
|
696
|
+
|
697
|
+
contents_offsets << segment[:file_offset]
|
698
|
+
|
699
|
+
next
|
700
|
+
end
|
701
|
+
|
702
|
+
|
703
|
+
start_offset = contents.bytesize + contents_offset
|
704
|
+
|
705
|
+
segment[:sections].each_with_index do |section, section_i|
|
706
|
+
|
707
|
+
raise "Section name \"#{section[:name]}\" is larger than 16 characters" if section[:name].bytesize > 16
|
708
|
+
|
709
|
+
section[:align] ||= 1
|
710
|
+
|
711
|
+
raise "Section alignment value cannot be 0" if section[:align] == 0
|
712
|
+
|
713
|
+
sec_offset = contents_offset + contents.bytesize
|
714
|
+
|
715
|
+
pad_amount = 0
|
716
|
+
if (sec_offset % section[:align] != 0) && align_section_contents
|
717
|
+
pad_amount = section[:align] - (sec_offset % section[:align])
|
718
|
+
end
|
719
|
+
|
720
|
+
sec_offset += pad_amount
|
721
|
+
contents << "\0" * pad_amount
|
722
|
+
|
723
|
+
section[:file_offset] = sec_offset
|
724
|
+
|
725
|
+
if section_i == 0
|
726
|
+
segment[:file_offset] ||= sec_offset
|
727
|
+
end
|
728
|
+
|
729
|
+
contents_offsets << section[:file_offset]
|
730
|
+
|
731
|
+
contents << section[:content]
|
732
|
+
|
733
|
+
section[:vaddr] ||= 0
|
734
|
+
section[:vsize] ||= section[:content].bytesize
|
735
|
+
end
|
736
|
+
|
737
|
+
end_offset = contents.bytesize + contents_offset
|
738
|
+
|
739
|
+
segment[:file_offset] ||= start_offset
|
740
|
+
segment[:filesize] ||= end_offset - segment[:file_offset]
|
741
|
+
|
742
|
+
segment[:vaddr] ||= 0
|
743
|
+
segment[:vsize] ||= segment[:filesize]
|
744
|
+
|
745
|
+
segment[:flags] ||= 0
|
746
|
+
|
747
|
+
segment[:maxprot] ||= 0
|
748
|
+
segment[:initprot] ||= 0
|
749
|
+
end
|
750
|
+
|
751
|
+
|
752
|
+
if codesign
|
753
|
+
|
754
|
+
# The __LINKEDIT segment is last (logic written previously)
|
755
|
+
linkedit_segment = segments.last
|
756
|
+
|
757
|
+
|
758
|
+
code_directory = PackedBytes.new endianess: :be
|
759
|
+
|
760
|
+
code_directory.uint32 0xfade0c02 # Magic
|
761
|
+
|
762
|
+
code_directory_length_offset = code_directory.result.bytesize
|
763
|
+
code_directory.uint32 0 # Length (will be replaced)
|
764
|
+
|
765
|
+
code_directory.uint32 0 # Version
|
766
|
+
|
767
|
+
code_directory.uint32 0 # Flags
|
768
|
+
|
769
|
+
code_directory_header_size = 96
|
770
|
+
|
771
|
+
hash_array_offset = code_directory_header_size
|
772
|
+
|
773
|
+
total_hashing_size = linkedit_segment[:file_offset] + linkedit_segment[:filesize]
|
774
|
+
|
775
|
+
hashing_pagesize = 0x1000
|
776
|
+
|
777
|
+
n_hashes = (total_hashing_size / hashing_pagesize.to_f).ceil
|
778
|
+
|
779
|
+
hash_size = 32
|
780
|
+
hash_type = 2 # SHA256
|
781
|
+
|
782
|
+
hash_array_size = hash_size * n_hashes
|
783
|
+
|
784
|
+
|
785
|
+
ident_offset = hash_array_offset + hash_array_size
|
786
|
+
|
787
|
+
|
788
|
+
code_directory.uint32 hash_array_offset # Hash offset
|
789
|
+
|
790
|
+
code_directory.uint32 ident_offset # Identifier string offset
|
791
|
+
|
792
|
+
code_directory.uint32 0 # nSpecialSlots
|
793
|
+
|
794
|
+
code_directory.uint32 n_hashes # nCodeSlots
|
795
|
+
|
796
|
+
code_directory.uint32 total_hashing_size # codeLimit
|
797
|
+
|
798
|
+
|
799
|
+
code_directory.uint8 hash_size
|
800
|
+
code_directory.uint8 hash_type
|
801
|
+
|
802
|
+
code_directory.uint8 0
|
803
|
+
hashing_pagesize_enc = Math.log2(hashing_pagesize)
|
804
|
+
raise "Code signing pagesize is not a power of two" if hashing_pagesize_enc.to_i != hashing_pagesize_enc
|
805
|
+
code_directory.uint8 hashing_pagesize_enc.to_i
|
806
|
+
|
807
|
+
code_directory.uint32 0 # spare2
|
808
|
+
|
809
|
+
code_directory.uint32 0 # scatterOffset (zero for absent)
|
810
|
+
code_directory.uint32 0 # teamIDOffset (zero for absent)
|
811
|
+
|
812
|
+
code_directory.uint32 0 # spare3
|
813
|
+
|
814
|
+
code_directory.uint64 total_hashing_size # codeLimit64
|
815
|
+
|
816
|
+
code_directory.uint64 0 # offset of executable segment
|
817
|
+
code_directory.uint64 0 # limit of executable segment
|
818
|
+
code_directory.uint64 0 # exec segment flags
|
819
|
+
|
820
|
+
code_directory.uint32 0 # runtime
|
821
|
+
code_directory.uint32 0xFFFF # pre-encrypt hash slots offset
|
822
|
+
|
823
|
+
|
824
|
+
|
825
|
+
hash_array_placeholder = "\0" * hash_array_size
|
826
|
+
|
827
|
+
|
828
|
+
code_directory.add hash_array_placeholder
|
829
|
+
|
830
|
+
ident_string = "program-#{SecureRandom.uuid.gsub('-', '')}\0"
|
831
|
+
|
832
|
+
code_directory.add ident_string
|
833
|
+
|
834
|
+
|
835
|
+
code_directory_bytes = code_directory.result
|
836
|
+
code_directory_bytes[code_directory_length_offset...(code_directory_length_offset + 4)] = [code_directory_bytes.bytesize].pack("L>")
|
837
|
+
|
838
|
+
blobs = [
|
839
|
+
{type: 0, content: code_directory.result}
|
840
|
+
]
|
841
|
+
|
842
|
+
# Embedded signature blob (superblob with magic = 0xfade0cc0)
|
843
|
+
superblob = MachO.build_superblob(magic: 0xfade0cc0, blobs: blobs)
|
844
|
+
|
845
|
+
|
846
|
+
# Calculate the offset of the hash array that will be replaced later
|
847
|
+
|
848
|
+
code_directory_offset = MachO.calc_superblob_offset(blobs, 0)
|
849
|
+
superblob_offset = linkedit_segment[:file_offset] + linkedit_segment[:filesize]
|
850
|
+
|
851
|
+
codesign_hash_array_offset = superblob_offset + code_directory_offset + hash_array_offset
|
852
|
+
|
853
|
+
contents << superblob
|
854
|
+
|
855
|
+
# Change the linkedit segment to include the embedded signature blob
|
856
|
+
linkedit_segment[:filesize] += superblob.bytesize
|
857
|
+
linkedit_segment[:vsize] += superblob.bytesize
|
858
|
+
|
859
|
+
# Add a code signature load command
|
860
|
+
code_signature_lc = PackedBytes.new
|
861
|
+
code_signature_lc.uint32 0x1d # LC_CODE_SIGNATURE
|
862
|
+
code_signature_lc.uint32 4 * 4 # cmdsize
|
863
|
+
|
864
|
+
code_signature_lc.uint32 superblob_offset
|
865
|
+
code_signature_lc.uint32 superblob.bytesize
|
866
|
+
|
867
|
+
|
868
|
+
prebuilt_lcs << {
|
869
|
+
bytes: code_signature_lc.result
|
870
|
+
}
|
871
|
+
|
872
|
+
end
|
873
|
+
|
874
|
+
|
875
|
+
|
876
|
+
symtab_offset = nil
|
877
|
+
|
878
|
+
|
879
|
+
if symbols != nil
|
880
|
+
|
881
|
+
symtab_contents = PackedBytes.new
|
882
|
+
|
883
|
+
sym_strtab_contents = "\0"
|
884
|
+
|
885
|
+
symbols.each do |symbol|
|
886
|
+
if symbol.keys.include? :name
|
887
|
+
symbol[:name_offset] = sym_strtab_contents.bytesize
|
888
|
+
sym_strtab_contents << symbol[:name] + "\0"
|
889
|
+
else
|
890
|
+
symbol[:name_offset] = 0
|
891
|
+
end
|
892
|
+
|
893
|
+
symtab_contents.uint32 symbol[:name_offset]
|
894
|
+
symtab_contents.uint8 symbol[:type]
|
895
|
+
symtab_contents.uint8 symbol[:sect]
|
896
|
+
symtab_contents.uint16 symbol[:desc]
|
897
|
+
symtab_contents.bytes symbol[:value], flexval_size
|
898
|
+
end
|
899
|
+
|
900
|
+
|
901
|
+
symtab_offset = contents.bytesize + contents_offset
|
902
|
+
symtab_align = 360
|
903
|
+
|
904
|
+
if symtab_offset % symtab_align != 0
|
905
|
+
pad = (symtab_align - (symtab_offset % symtab_align))
|
906
|
+
contents << "\0" * pad
|
907
|
+
symtab_offset += pad
|
908
|
+
end
|
909
|
+
|
910
|
+
# contents_offsets << symtab_offset
|
911
|
+
|
912
|
+
contents << symtab_contents.result
|
913
|
+
|
914
|
+
sym_strtab_offset = contents.bytesize + contents_offset
|
915
|
+
|
916
|
+
contents << sym_strtab_contents
|
917
|
+
|
918
|
+
end
|
919
|
+
|
920
|
+
|
921
|
+
|
922
|
+
contents_offsets << symtab_offset if symtab_offset != nil
|
923
|
+
|
924
|
+
contents_offsets += prebuilt_lc_content_offsets
|
925
|
+
|
926
|
+
|
927
|
+
|
928
|
+
prebuilt_lcs.each do |lc|
|
929
|
+
|
930
|
+
if lc.keys.include? :relocations
|
931
|
+
lc[:relocations].each do |reloc|
|
932
|
+
|
933
|
+
addr = contents_offsets[reloc[:content_index]]
|
934
|
+
|
935
|
+
raise "Load command contents at index #{reloc[:content_index]} is empty and doesn't have an offset" if addr == nil
|
936
|
+
|
937
|
+
write_val = addr + (reloc[:addend] || 0)
|
938
|
+
|
939
|
+
bytesize = reloc[:bytefield_size]
|
940
|
+
|
941
|
+
bits = (0...(bytesize * 8)).map{|bit_i| write_val[bit_i] }
|
942
|
+
bytes = (0...bytesize).map{|byte_i| bits[(byte_i*8)...(byte_i*8 + 8)] }.map{|byte_bits| byte_bits.each_with_index.map{|bit, bit_i| 2 ** bit_i * bit }.sum }
|
943
|
+
str_bytes = bytes.map(&:chr).join
|
944
|
+
|
945
|
+
lc[:bytes][reloc[:bytefield_offset]...(reloc[:bytefield_offset] + bytesize)] = str_bytes
|
946
|
+
end
|
947
|
+
end
|
948
|
+
|
949
|
+
end
|
950
|
+
|
951
|
+
|
952
|
+
load_commands = PackedBytes.new
|
953
|
+
|
954
|
+
|
955
|
+
segments.each do |segment|
|
956
|
+
|
957
|
+
case arch_type
|
958
|
+
when 64
|
959
|
+
load_commands.uint32 0x19 # LC_SEGMENT_64
|
960
|
+
when 32
|
961
|
+
load_commands.uint32 0x1 # LC_SEGMENT
|
962
|
+
end
|
963
|
+
|
964
|
+
load_commands.uint32 segment_entry_size + section_entry_size * segment[:sections].size
|
965
|
+
|
966
|
+
load_commands.bytes segment[:name] + "\0" * (16 - segment[:name].bytesize)
|
967
|
+
|
968
|
+
load_commands.bytes segment[:vaddr], flexval_size
|
969
|
+
load_commands.bytes segment[:vsize], flexval_size
|
970
|
+
|
971
|
+
load_commands.bytes segment[:file_offset], flexval_size
|
972
|
+
load_commands.bytes segment[:filesize], flexval_size
|
973
|
+
|
974
|
+
load_commands.uint32 segment[:maxprot]
|
975
|
+
load_commands.uint32 segment[:initprot]
|
976
|
+
|
977
|
+
load_commands.uint32 segment[:sections].size
|
978
|
+
|
979
|
+
load_commands.uint32 segment[:flags]
|
980
|
+
|
981
|
+
|
982
|
+
|
983
|
+
segment[:sections].each do |section|
|
984
|
+
|
985
|
+
load_commands.bytes section[:name] + "\0" * (16 - section[:name].bytesize)
|
986
|
+
load_commands.bytes segment[:name] + "\0" * (16 - segment[:name].bytesize)
|
987
|
+
|
988
|
+
load_commands.bytes section[:vaddr], flexval_size
|
989
|
+
load_commands.bytes section[:vsize], flexval_size
|
990
|
+
|
991
|
+
load_commands.uint32 section[:file_offset]
|
992
|
+
|
993
|
+
align_value = Math.log2 section[:align]
|
994
|
+
raise "Section alignment is not a power of 2" if align_value.to_i != align_value
|
995
|
+
|
996
|
+
load_commands.uint32 align_value
|
997
|
+
|
998
|
+
load_commands.uint32 0 # reloff
|
999
|
+
load_commands.uint32 0 # nreloc
|
1000
|
+
|
1001
|
+
load_commands.uint32 section[:flags]
|
1002
|
+
|
1003
|
+
load_commands.uint32 0 # reserved 1
|
1004
|
+
load_commands.uint32 0 # reserved 2
|
1005
|
+
|
1006
|
+
if arch_type == 64
|
1007
|
+
load_commands.uint32 0 # reserved 3 (only in 64-bit)
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
end
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
|
1014
|
+
|
1015
|
+
# Symtab load command
|
1016
|
+
|
1017
|
+
if symbols != nil
|
1018
|
+
load_commands.uint32 0x2 # LC_SYMTAB
|
1019
|
+
load_commands.uint32 symtab_entry_size
|
1020
|
+
|
1021
|
+
load_commands.uint32 symtab_offset
|
1022
|
+
load_commands.uint32 symbols.size
|
1023
|
+
|
1024
|
+
load_commands.uint32 sym_strtab_offset
|
1025
|
+
load_commands.uint32 sym_strtab_contents.bytesize
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
|
1029
|
+
prebuilt_lcs.each do |lc|
|
1030
|
+
load_commands.bytes lc[:bytes]
|
1031
|
+
end
|
1032
|
+
|
1033
|
+
|
1034
|
+
file_content.add load_commands
|
1035
|
+
|
1036
|
+
|
1037
|
+
file_content.add contents
|
1038
|
+
|
1039
|
+
|
1040
|
+
if codesign
|
1041
|
+
# Compute the hashes and replace them instead of the placeholder
|
1042
|
+
|
1043
|
+
out = file_content.result
|
1044
|
+
|
1045
|
+
hash_array_contents = ""
|
1046
|
+
|
1047
|
+
n_hashes.times do |hash_i|
|
1048
|
+
start_offset = hash_i * hashing_pagesize
|
1049
|
+
# Enf offset is either the end of the page or the code_limit field from code_directory
|
1050
|
+
end_offset = [start_offset + hashing_pagesize, total_hashing_size].min
|
1051
|
+
|
1052
|
+
hashed = Digest::SHA256.digest out[start_offset...end_offset]
|
1053
|
+
|
1054
|
+
hash_array_contents << hashed
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
|
1058
|
+
hash_array_start = codesign_hash_array_offset
|
1059
|
+
hash_array_end = hash_array_start + hash_array_contents.bytesize
|
1060
|
+
|
1061
|
+
out[hash_array_start...hash_array_end] = hash_array_contents
|
1062
|
+
|
1063
|
+
return out
|
1064
|
+
end
|
1065
|
+
|
1066
|
+
|
1067
|
+
return file_content.result
|
1068
|
+
end
|
1069
|
+
|
1070
|
+
|
1071
|
+
|
1072
|
+
# Builds a code signature SuperBlob structure
|
1073
|
+
#
|
1074
|
+
# Arguments:
|
1075
|
+
# magic - SuperBlob's magic field value (default is 0)
|
1076
|
+
# blobs - blobs included in the superblob (structure below)
|
1077
|
+
#
|
1078
|
+
# Blob structure:
|
1079
|
+
# type - blob's type
|
1080
|
+
# content - blob's content
|
1081
|
+
#
|
1082
|
+
def self.build_superblob magic: 0, blobs: []
|
1083
|
+
|
1084
|
+
superblob = PackedBytes.new endianess: :be
|
1085
|
+
|
1086
|
+
superblob.uint32 0xfade0cc0 # magic
|
1087
|
+
|
1088
|
+
superblob_length_offset = superblob.result.bytesize
|
1089
|
+
superblob.uint32 0 # length (will be replaced)
|
1090
|
+
|
1091
|
+
superblob.uint32 1 # Number of entries
|
1092
|
+
|
1093
|
+
|
1094
|
+
index_entry_size = 8
|
1095
|
+
index_entries_size = index_entry_size * blobs.size
|
1096
|
+
|
1097
|
+
superblob_header_size = 4 * 3
|
1098
|
+
|
1099
|
+
blobs_offset = superblob_header_size + index_entries_size
|
1100
|
+
|
1101
|
+
blob_contents = ""
|
1102
|
+
|
1103
|
+
blobs.each do |blob|
|
1104
|
+
blob[:offset] = blobs_offset + blob_contents.bytesize
|
1105
|
+
blob_contents << blob[:content]
|
1106
|
+
end
|
1107
|
+
|
1108
|
+
# Index entries
|
1109
|
+
blobs.each do |blob|
|
1110
|
+
superblob.uint32 blob[:type]
|
1111
|
+
superblob.uint32 blob[:offset]
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
# Blob contents write
|
1115
|
+
blobs.each do |blob|
|
1116
|
+
superblob.add blob[:content]
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
|
1120
|
+
out = superblob.result
|
1121
|
+
out[superblob_length_offset...(superblob_length_offset + 4)] = [out.bytesize].pack("L>")
|
1122
|
+
|
1123
|
+
out
|
1124
|
+
end
|
1125
|
+
|
1126
|
+
|
1127
|
+
# Calculates the offset of the specified blob from the beginning of the superblob
|
1128
|
+
def self.calc_superblob_offset blobs, index
|
1129
|
+
|
1130
|
+
superblob_header_size = 4 * 3
|
1131
|
+
|
1132
|
+
index_entry_size = 8
|
1133
|
+
|
1134
|
+
index_entries_size = index_entry_size * blobs.size
|
1135
|
+
|
1136
|
+
total_offset = superblob_header_size + index_entries_size
|
1137
|
+
|
1138
|
+
blobs[...index].each do |blob|
|
1139
|
+
total_offset += blob[:content].bytesize
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
total_offset
|
1143
|
+
end
|
1144
|
+
|
1145
|
+
|
1146
|
+
|
1147
|
+
|
1148
|
+
# Builds a symtab and a strtab based off input symbols
|
1149
|
+
def self.build_symtab symbols, arch_type: 64
|
1150
|
+
|
1151
|
+
case arch_type
|
1152
|
+
when 64
|
1153
|
+
flexval_size = 8
|
1154
|
+
when 32
|
1155
|
+
flexval_size = 4
|
1156
|
+
end
|
1157
|
+
|
1158
|
+
symtab_contents = PackedBytes.new
|
1159
|
+
|
1160
|
+
sym_strtab_contents = "\0"
|
1161
|
+
|
1162
|
+
symbols.each do |symbol|
|
1163
|
+
if symbol.keys.include? :name
|
1164
|
+
symbol[:name_offset] = sym_strtab_contents.bytesize
|
1165
|
+
sym_strtab_contents << symbol[:name] + "\0"
|
1166
|
+
else
|
1167
|
+
symbol[:name_offset] = 0
|
1168
|
+
end
|
1169
|
+
|
1170
|
+
symtab_contents.uint32 symbol[:name_offset]
|
1171
|
+
symtab_contents.uint8 symbol[:type]
|
1172
|
+
symtab_contents.uint8 symbol[:sect]
|
1173
|
+
symtab_contents.uint16 symbol[:desc]
|
1174
|
+
symtab_contents.bytes symbol[:value], flexval_size
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
|
1178
|
+
return {symtab: symtab_contents.result, strtab: sym_strtab_contents}
|
1179
|
+
end
|
1180
|
+
|
1181
|
+
|
1182
|
+
# Converts a hash of label name-address pairs into section symbols suitable for the MachO.build method
|
1183
|
+
#
|
1184
|
+
# Arguments:
|
1185
|
+
# labels - the hash of labels
|
1186
|
+
# section_index - index of the section the symbols belong to (default is 1)
|
1187
|
+
# external - specifies if the label is external or not (default is false)
|
1188
|
+
# private_external - specifies if the label is private external or not (default is false)
|
1189
|
+
# debug_entry - specifies if the label is a debug entry or not (default is false)
|
1190
|
+
def self.labels_to_symbols labels,
|
1191
|
+
out = []
|
1192
|
+
labels.each do |name, value|
|
1193
|
+
out << create_symbol(name: name, value: value, type: :sect, external: true, section_number: 1)
|
1194
|
+
end
|
1195
|
+
return out
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
|
1199
|
+
# Creates a symbol from input information suitable for the MachO.build method
|
1200
|
+
#
|
1201
|
+
# Arguments:
|
1202
|
+
# name - the symbol's name
|
1203
|
+
# value - the symbol's value
|
1204
|
+
# type - the symbol's type: :undef, :abs, :sect, :prebound, :indirect (default is :sect)
|
1205
|
+
# private_external - specifies whether the symbol is private external (default is false)
|
1206
|
+
# external - specifies whether the symbol is external (default is false)
|
1207
|
+
# debug_entry - specifies whether the symbol is a debugging entry (default is false)
|
1208
|
+
# options - options depending on the symbol's type
|
1209
|
+
#
|
1210
|
+
# Type == :sect options:
|
1211
|
+
# section_number - section number the symbol is defined in
|
1212
|
+
#
|
1213
|
+
def self.create_symbol name: nil, value: nil, type: :sect, private_external: false, external: false, debug_entry: false, **options
|
1214
|
+
|
1215
|
+
raise "No name provided for the symbol" if name == nil
|
1216
|
+
raise "No value provided for the symbol" if value == nil
|
1217
|
+
|
1218
|
+
symbol = {name: name, value: value}
|
1219
|
+
|
1220
|
+
type_encodings = {undef: 0x0, abs: 0x2, sect: 0xe, prebound: 0xc, indirect: 0xa}
|
1221
|
+
encoded_type = type_encodings[type]
|
1222
|
+
|
1223
|
+
raise "Unknown symbol type \"#{type}\" - must be :undef, :abs, :sect, :prebound, or :indirect." if encoded_type == nil
|
1224
|
+
|
1225
|
+
|
1226
|
+
pext_bit = 0x0
|
1227
|
+
if private_external == true
|
1228
|
+
pext_bit = 0x10
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
ext_bit = 0x0
|
1232
|
+
if external == true
|
1233
|
+
ext_bit = 0x1
|
1234
|
+
end
|
1235
|
+
|
1236
|
+
debug_entry_bit = 0x0
|
1237
|
+
if debug_entry == true
|
1238
|
+
debug_entry_bit = 0xe
|
1239
|
+
end
|
1240
|
+
|
1241
|
+
type_field = encoded_type | pext_bit | ext_bit | debug_entry_bit
|
1242
|
+
|
1243
|
+
symbol[:type] = type_field
|
1244
|
+
|
1245
|
+
|
1246
|
+
case type
|
1247
|
+
when :undef
|
1248
|
+
symbol[:sect] = 0
|
1249
|
+
when :abs
|
1250
|
+
symbol[:sect] = 0
|
1251
|
+
when :sect
|
1252
|
+
section_number = options[:section_number] || 0
|
1253
|
+
raise "Section number too high - maximum is 255." if section_number > 255
|
1254
|
+
symbol[:sect] = section_number
|
1255
|
+
when :prebound
|
1256
|
+
symbol[:sect] = 0
|
1257
|
+
when :indirect
|
1258
|
+
symbol[:sect] = 0
|
1259
|
+
end
|
1260
|
+
|
1261
|
+
symbol[:desc] = 0
|
1262
|
+
|
1263
|
+
return symbol
|
1264
|
+
end
|
1265
|
+
|
1266
|
+
|
1267
|
+
|
1268
|
+
end # Kompiler::Wrappers::MachO
|
1269
|
+
|
1270
|
+
end # Kompiler::Wrappers
|
1271
|
+
|
1272
|
+
end # Kompiler
|
1273
|
+
|
1274
|
+
|