-
Notifications
You must be signed in to change notification settings - Fork 10
/
enc.py
365 lines (306 loc) · 11.7 KB
/
enc.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
import sys, binascii, assemble, distorm3
from distorm3 import OPERAND_REGISTER, OPERAND_IMMEDIATE, OPERAND_MEMORY, \
OPERAND_ABSOLUTE_ADDRESS
def enc_apply(hex):
dec = distorm3.Decompose(0, hex.decode('hex'), distorm3.Decode32Bits)
return Enc(dec[0]).encode()
class Enc:
_labels = {}
_ff_flag = {8: 0xff, 16: 0xffff, 32: 0xffffffff}
def __init__(self, dis):
if not dis: return
self.dis = dis
self.reg_index1 = None
self.reg_index2 = None
self.xmm_index1 = None
self.xmm_index2 = None
self.xmm_reg1 = None
self.xmm_reg2 = None
self.lines = []
if len(dis.operands) > 0:
self.type1 = dis.operands[0].type
if self.type1 == OPERAND_REGISTER:
self.reg_index1 = dis.operands[0].index & 7
self.xmm_index1 = dis.operands[0].index & 3
self.xmm_reg1 = self._xmm_gpr_index(self.reg_index1)
self.size1 = self.dis.operands[0].size
elif self.type1 == OPERAND_IMMEDIATE:
self.imm1 = self.dis.operands[0].value
elif self.type1 == OPERAND_ABSOLUTE_ADDRESS:
self.type1 = OPERAND_MEMORY
if len(dis.operands) > 1:
self.type2 = dis.operands[1].type
if self.type2 == OPERAND_REGISTER:
self.reg_index2 = dis.operands[1].index & 7
self.xmm_index2 = dis.operands[1].index & 3
self.xmm_reg2 = self._xmm_gpr_index(self.reg_index2)
self.size2 = self.dis.operands[1].size
elif self.type2 == OPERAND_IMMEDIATE:
self.imm2 = self.dis.operands[1].value
elif self.type2 == OPERAND_ABSOLUTE_ADDRESS:
self.type2 = OPERAND_MEMORY
# reset labels dict
def reset_labels(self):
Enc._labels = {}
def encode(self):
func = getattr(self, '_encode_' + self.dis.mnemonic.lower(), None)
if not func:
raise Exception('Cannot encode %s' % self.dis.mnemonic.lower())
func()
return self.lines
# find register for gpr
def _xmm_gpr_index(self, gpr_index):
return 'xmm%d' % (6 + gpr_index / 4)
# construct a 16byte xmm value
def _m128(self, val):
if str(val) not in Enc._labels:
self.lines.append('jmp m128_%d_end' % len(Enc._labels))
self.lines.append('align 16, db 0')
self.lines.append('m128_%d: ' % len(Enc._labels) + \
'dd 0x%08x, 0x%08x, 0x%08x, 0x%08x' % tuple(val))
self.lines.append('m128_%d_end:' % len(Enc._labels))
Enc._labels[str(val)] = 'dqword [m128_%d]' % len(Enc._labels)
return Enc._labels[str(val)]
# construct a 16byte xmm value from 4 dwords
def _m128_flag4(self, index, yes=0, no=0):
val = [no for i in range(4)]
val[index] = yes
return self._m128(val)
# construct a 16byte xmm value from 8 words
def _m128_flag8(self, index, yes=0, no=0):
val = [no for i in range(8)]
val[index * 2] = yes
val = [(val[i] + (val[i+1] << 16)) for i in xrange(0, 8, 2)]
return self._m128(val)
# construct a 16byte xmm value from 16 bytes
def _m128_flag16(self, index, yes=0, no=0):
val = [no for i in range(16)]
val[index * 4] = yes
return self._m128([reduce(lambda x, y: x * 256 + y, val[i:i+4][::-1]) \
for i in xrange(0, 16, 4)])
def _m128_flagsize(self, index, yes=0, no=0, size=32):
if size == 32: return self._m128_flag4(index, yes, no)
elif size == 16: return self._m128_flag8(index, yes, no)
elif size == 8: return self._m128_flag16(index, yes, no)
raise Exception('dafuq')
# calculate the flag for pshufd instruction
def _flag_pshufd(self, index, value, flags=[0,0,0,0]):
flags[index & 3] = value & 3
return reduce(lambda x, y: x * 4 + y, flags)
# read a 8bit, 16bit or 32bit integer from a memory address
# optionally give it a special position
def _read_mem(self, reg, addr, size=32, position=0):
self.lines.append('movss xmm%d, [0x%x]' % (reg, addr))
if size != 32:
self.lines.append('pand xmm%d, %s' % (reg, \
self._m128([self._ff_flag[size], 0, 0, 0])))
if position != 0:
self.lines.append('pshufd xmm%d, xmm%d, %d' % (reg, reg, \
self._flag_pshufd(position, 0)))
# write a 8bit, 16bit or 32bit value to an address
def _write_mem(self, addr, value, tmp_reg=3, size=32, position=0):
if size != 32:
self._read_mem(tmp_reg, addr, position=position)
self.lines.append('pand xmm%d, %s' % (tmp_reg, \
self._m128_flag4(position, -self._ff_flag[size], \
self._ff_flag[32])))
else:
self.lines.append('pxor xmm%d, xmm%d' % (tmp_reg, tmp_reg))
self.lines.append('pxor xmm%d, %s' % (tmp_reg, \
self._m128_flag4(position, yes=value)))
if position != 0:
self.lines.append('pshufd xmm%d, xmm%d, %d' % (tmp_reg, tmp_reg, \
self._flag_pshufd(0, position)))
self.lines.append('movss [0x%x], xmm%d' % tmp_reg)
# read a [8, 16, 32] bit "emulated" gpr to the
# given xmm register's low 32bits
def _read_emugpr_xmm(self, gpr, xmm=0, size=32):
# TODO: 8/16bit support
self.lines.append('pshufd xmm%d, %s, %d' % (xmm, \
self._xmm_gpr_index(gpr), self._flag_pshufd(3, gpr & 3)))
if size == 8:
self.lines.append('pand xmm%d, %s' % (xmm, \
self._m128_flag16(0, yes=self._ff_flag[size])))
elif size == 16:
self.lines.append('pand xmm%d, %s' % (xmm, \
self._m128_flag8(0, yes=self._ff_flag[size])))
elif size == 32:
self.lines.append('pand xmm%d, %s' % (xmm, self._m128_flag4(0, \
yes=self._ff_flag[size])))
# write a [8, 16, 32] bit "emulated" gpr to the
# given xmm register's low 32bits
def _write_emugpr_xmm(self, gpr, xmm=0, size=32):
# TODO: 8/16bit support
# zero the register out
self.lines.append('pand %s, %s' % (self._xmm_gpr_index(gpr), \
self._m128_flagsize(gpr & 3, no=self._ff_flag[size], \
size=size)))
# make sure the value is in the correct dword
if gpr & 3: self.lines.append('pshufd xmm%d, xmm%d, %d' % (xmm, xmm, \
self._flag_pshufd(gpr & 3, 0)))
# zero everything out for the source operand
self.lines.append('pand xmm%d, %s' % (xmm, \
self._m128_flag4(gpr & 3, yes=self._ff_flag[size])))
# write the new value
self.lines.append('por %s, xmm%d' % (self._xmm_gpr_index(gpr), xmm))
#print '\n'.join(self.lines)
def _read_memory_xmm(self, addr, xmm=0, size=32):
# TODO: 8/16bit support
self.lines.append('movd xmm%d, dword ptr [0x%08x]' % (xmm, addr))
def _write_memory_xmm(self, addr, xmm=0, size=32):
# TODO: 8/16bit support
self.lines.append('movd dword ptr [0x%08x], xmm%d' % (addr, xmm))
def _read_expr_mem(self, operand, xmm=0, size=32):
# TODO: 8/16bit support
self.lines.append('movapd xmm%d, %s' % (xmm, \
self._m128([operand.disp, 0, 0, 0])))
if operand.base != None:
self._read_emugpr_xmm(operand.base & 7, 2, size)
self.lines.append('paddd xmm%d, xmm2' % xmm)
if operand.index != 0:
self._read_emugpr_xmm(operand.index & 7, 2, size)
if operand.scale != 1:
# there must be a better way to do this,
# but I can't come up with it atm.
conv = {2: 1, 4: 2, 8: 3}
self.lines.append('pslld xmm2, %d' % conv[operand.scale])
self.lines.append('paddd xmm%d, xmm2' % xmm)
def _write_expr_mem(self, operand, xmm=0, size=32):
# TODO: 8/16bit support
self._read_expr_mem(operand, xmm=1, size=size)
self.lines.append('movd eax, xmm1')
self.lines.append('movd dword [eax], xmm%d' % xmm)
def _read_value_xmm(self, operand, xmm=0):
op = self.dis.operands[operand]
if op.type == OPERAND_REGISTER:
self._read_emugpr_xmm(op.index & 7, xmm=xmm, size=op.size)
elif op.type == OPERAND_IMMEDIATE:
self.lines.append('movapd xmm%d, %s' % (xmm, \
self._m128([op.value, 0, 0, 0])))
elif op.type == OPERAND_ABSOLUTE_ADDRESS:
self._read_memory_xmm(op.disp, xmm=xmm, size=op.size)
elif op.type == OPERAND_MEMORY:
self._read_expr_mem(op, xmm=xmm)
self.lines.append('movd eax, xmm%d' % xmm)
self.lines.append('movd xmm%d, dword [eax]' % xmm)
if op.size and op.size != 32:
self.lines.append('pand xmm%d, %s' % (xmm, \
self._m128([self._ff_flag[op.size], 0, 0, 0])))
def _write_value_xmm(self, operand, xmm=0):
op = self.dis.operands[operand]
if op.type == OPERAND_REGISTER:
self._write_emugpr_xmm(op.index & 7, xmm=xmm, size=op.size)
elif op.type == OPERAND_IMMEDIATE:
raise Exception('dafuq')
elif op.type == OPERAND_ABSOLUTE_ADDRESS:
self._write_memory_xmm(op.disp, xmm=xmm, size=op.size)
elif op.type == OPERAND_MEMORY:
self._write_expr_mem(op, xmm=xmm, size=op.size)
def _encode_nop(self):
# do nothing
self
def _encode_xor(self):
self._read_value_xmm(0)
self._read_value_xmm(1, 1)
self.lines.append('pxor xmm0, xmm1')
self._write_value_xmm(0)
def _encode_or(self):
self._read_value_xmm(0)
self._read_value_xmm(1, 1)
self.lines.append('por xmm0, xmm1')
self._write_value_xmm(0)
def _encode_and(self):
self._read_value_xmm(0)
self._read_value_xmm(1, 1)
self.lines.append('pand xmm0, xmm1')
self._write_value_xmm(0)
def _encode_mov(self):
self._read_value_xmm(1)
self._write_value_xmm(0)
def _encode_movzx(self):
self._read_value_xmm(1)
self._write_value_xmm(0)
def _encode_lea(self):
self._read_expr_mem(self.dis.operands[1])
self._write_value_xmm(0)
def _encode_shl(self):
self._read_value_xmm(0)
self._read_value_xmm(1, 1)
self.lines.append('pslld xmm0, xmm1')
self._write_value_xmm(0)
def _encode_shr(self):
self._read_value_xmm(0)
self._read_value_xmm(1, 1)
self.lines.append('psrld xmm0, xmm1')
self._write_value_xmm(0)
def _encode_add(self):
self._read_value_xmm(0)
self._read_value_xmm(1, 1)
self.lines.append('paddd xmm0, xmm1')
self._write_value_xmm(0)
def _encode_sub(self):
self._read_value_xmm(0)
self._read_value_xmm(1, 1)
self.lines.append('psubd xmm0, xmm1')
self._write_value_xmm(0)
def _encode_push(self):
# esp is the first dword in the xmm7 register
self.lines.append('psubd xmm7, %s' % self._m128([4, 0, 0, 0]))
self._read_value_xmm(0)
self._read_emugpr_xmm(assemble.ESP, 1)
self.lines.append('movd eax, xmm1')
self.lines.append('movd dword [eax], xmm0')
def _encode_pop(self):
self._read_value_xmm(0)
self._read_emugpr_xmm(assemble.ESP, 1)
self.lines.append('movd eax, xmm1')
self.lines.append('movd xmm0, dword [eax]')
self._write_value_xmm(0)
# esp is the first dword in the xmm7 register
self.lines.append('paddd xmm7, %s' % self._m128([4, 0, 0, 0]))
def _encode_inc(self):
self._read_value_xmm(0)
self.lines.append('paddd xmm0, %s' % self._m128([1, 0, 0, 0]))
self._write_value_xmm(0)
def _encode_dec(self):
self._read_value_xmm(0)
self.lines.append('psubd xmm0, %s' % self._m128([1, 0, 0, 0]))
self._write_value_xmm(0)
def _encode_mul(self):
self._read_emugpr_xmm(assemble.EAX)
self._read_value_xmm(0, 1)
self.lines.append('pmuludq xmm0, xmm1')
self.lines.append('pshufd xmm1, xmm0, %d' % self._flag_pshufd(3, 1))
self._write_emugpr_xmm(assemble.EAX, 0)
self._write_emugpr_xmm(assemble.EDX, 1)
def _encode_not(self):
self._read_value_xmm(0)
self.lines.append('pxor xmm0, %s' % self._m128([0xffffffff, 0, 0, 0]))
self._write_value_xmm(0)
def _encode_neg(self):
self._read_value_xmm(0)
self.lines.append('pxor xmm1, xmm1')
self.lines.append('psubd xmm1, xmm0')
self._write_value_xmm(0, 1)
def _encode_xchg(self):
self._read_value_xmm(0)
self._read_value_xmm(1, 1)
self._write_value_xmm(1, 0)
self._write_value_xmm(0, 1)
def _encode_leave(self):
# leave = mov esp, ebp ; pop ebp
self.lines += enc_apply('8be5')
self.lines += enc_apply('5d')
def _encode_ret(self):
# ret = pop eip
# we encode as pop eax ; jmp eax
self._read_emugpr_xmm(assemble.ESP)
self.lines.append('movd eax, xmm0')
# esp is the first dword in the xmm7 register
self.lines.append('paddd xmm7, %s' % self._m128([4, 0, 0, 0]))
# jump to the address
self.lines.append('jmp dword [eax]')
if __name__ == '__main__':
lines = sys.stdin.readlines()
code = assemble.assemble(lines)
print binascii.hexlify(code)