CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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