-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathNECromancer.py
373 lines (290 loc) · 11.8 KB
/
NECromancer.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
366
367
368
369
370
371
372
373
from ida_lines import COLOR_INSN, COLOR_MACRO
from ida_idp import CUSTOM_INSN_ITYPE, IDP_Hooks, ph_get_regnames, ph_get_id, PLFM_NEC_V850X
from ida_bytes import get_bytes
from ida_idaapi import plugin_t, PLUGIN_PROC, PLUGIN_HIDE, PLUGIN_SKIP, PLUGIN_KEEP
from ida_ua import o_displ, o_reg, o_imm, dt_dword, OOF_ADDR
from struct import unpack
###############################################################################
#
#
# _____ _____ _____
# | | | __| |___ ___ _____ ___ ___ ___ ___ ___
# | | | | __| --| _| . | | .'| | _| -_| _|
# |_|___|_____|_____|_| |___|_|_|_|__,|_|_|___|___|_|
# ____________________________________________________________________________
#
# NECromancer - a NEC V850X instruction extender plugin for IDA Pro
# -----------------------------------------------------------------
#
# This plugin extends the V850E1 IDA processor module by adding support
# for certain V850E2 instructions on a per need basis. Rather than modifying
# the source code of the V850E1 IDA processor module, this script has been
# developed as an exercise in writing a processor module extension in
# IDAPython, particularly for version 7 of IDA and onwards.
#
###############################################################################
# ----------------------------------------------------------------------------
#
# necromancer: noun | nec*ro*man*cer | a supposed practice of magic involving
# communication with the diseased
#
# history and changelog:
# ----------------------
# 2017.01.31 - initial version
# support for "divq", "divqu", shl (reg1, reg2, reg3),
# shr (reg1, reg2, reg3) and "sar" instructions
# 2017.02.01 - support for LD.HU (disp23[reg1], reg3), "feret" and "eiret"
# instructions
# 2017.02.02 - support for ST.H (reg3, disp23[reg1]) instruction,
# bugfixes, cleanup
# 2017.02.03 - support for sign extending 23bit displacement values,
# "sch1l", "sch1r", "caxi" and "fetrap" instructions
# 2017.08.20 - IDA 7 compatibility
# 2017.09.03 - Full IDA 7 compatibility (not requiring compatibility layer)
# 2017.12.03 - Bugfixes (with thanks to https://github.com/Quorth)
# 2018.05.08 - Fixed decoding of fetrap instruction
#
#
# based on V850E2S User's Manual: Architecture, available at:
# https://www.renesas.com/en-eu/doc/products/mpumcu/doc/v850/r01us0037ej0100_v850e2.pdf
#
# ------------------------------------------------------------------------------
__author__ = "Dennis Elser"
DEBUG_PLUGIN = True
NEWINSN_COLOR = COLOR_MACRO if DEBUG_PLUGIN else COLOR_INSN
# from V850 processor module
N850F_USEBRACKETS = 0x01
N850F_OUTSIGNED = 0x02
class NewInstructions:
(NN_divq,
NN_divqu,
NN_sar,
NN_shl,
NN_shr,
NN_feret,
NN_eiret,
NN_ld_hu,
NN_st_h,
NN_sch1l,
NN_sch1r,
NN_caxi,
NN_fetrap) = range(CUSTOM_INSN_ITYPE, CUSTOM_INSN_ITYPE+13)
lst = {NN_divq:"divq",
NN_divqu:"divqu",
NN_sar:"sar",
NN_shl:"shl",
NN_shr:"shr",
NN_feret:"feret",
NN_eiret:"eiret",
NN_ld_hu:"ld.hu",
NN_st_h:"st.h",
NN_sch1l:"sch1l",
NN_sch1r:"sch1r",
NN_caxi:"caxi",
NN_fetrap:"fetrap"}
#--------------------------------------------------------------------------
class v850_idp_hook_t(IDP_Hooks):
def __init__(self):
IDP_Hooks.__init__(self)
def parse_r1(self, w):
return w & 0x1F
def parse_r2(self, w):
return (w & 0xF800) >> 11
def parse_r3(self, w):
return self.parse_r2(w)
def sign_extend(self, disp, nbits):
val = disp
if val & (1 << (nbits-1)):
val |= ~((1 << nbits)-1)
return val
def decode_instruction(self, insn):
buf = get_bytes(insn.ea, 2)
hw1 = unpack("<H", buf)[0]
op = (hw1 & 0x7E0) >> 5 # take bit5->bit10
# Format I
if op == 2 and (hw1 >> 11) == 0 and (hw1 & 0x1F) != 0:
# TODO add vector4 parsing
insn.itype = NewInstructions.NN_fetrap
insn.size = 2
return True
# Format XIV
elif op == 0x3D and ((hw1 & 0xFFE0) >> 5) == 0x3D:
buf = get_bytes(insn.ea+2, 2)
hw2 = unpack("<H", buf)[0]
subop = hw2 & 0x1F
if subop == 0x07: # ld.hu
insn.itype = NewInstructions.NN_ld_hu
insn.Op1.type = o_displ
insn.Op2.type = o_reg
insn.Op1.specflag1 = N850F_USEBRACKETS | N850F_OUTSIGNED
insn.Op1.reg = self.parse_r1(hw1)
buf = get_bytes(insn.ea+4, 2)
hw3 = unpack("<H", buf)[0]
insn.Op1.addr = self.sign_extend(((hw3 << 6) | ((hw2 & 0x7E0) >> 5)) << 1, 23)
insn.Op1.dtyp = dt_dword
insn.Op2.reg = self.parse_r2(hw2)
insn.Op2.dtyp = dt_dword
insn.size = 6
return True
elif subop == 0xD: # st.h
insn.itype = NewInstructions.NN_st_h
insn.Op1.type = o_reg
insn.Op2.type = o_displ
insn.Op2.specflag1 = N850F_USEBRACKETS | N850F_OUTSIGNED
insn.Op2.reg = self.parse_r1(hw1)
buf = get_bytes(insn.ea+4, 2)
hw3 = unpack("<H", buf)[0]
insn.Op2.addr = self.sign_extend(((hw3 << 6) | ((hw2 & 0x7E0) >> 5)) << 1, 23)
insn.Op2.dtyp = dt_dword
insn.Op1.reg = self.parse_r2(hw2)
insn.Op1.dtyp = dt_dword
insn.size = 6
return True
# Format II
elif op == 0x15: # sar imm5, reg2
insn.itype = NewInstructions.NN_sar
insn.Op1.type = o_imm
insn.Op2.type = o_reg
insn.Op1.value = hw1 & 0x1F
insn.Op2.reg = self.parse_r2(hw1)
insn.size = 2
return True
# Format IX, X, XI
elif op == 0x3F:
buf = get_bytes(insn.ea+2, 2)
hw2 = unpack("<H", buf)[0]
subop = hw2 & 0x7FF
if hw1 & 0x7FF == 0x7E0:
if hw1 == 0x7E0:
if hw2 == 0x14A: # feret
insn.itype = NewInstructions.NN_feret
insn.size = 4
return insn.size
elif hw2 == 0x0148: # eiret
insn.itype = NewInstructions.NN_eiret
insn.size = 4
return True
elif subop == 0x366: # sch1l reg2, reg3
insn.itype = NewInstructions.NN_sch1l
insn.Op1.type = o_reg
insn.Op2.type = o_reg
insn.Op1.reg = self.parse_r2(hw1)
insn.Op2.reg = self.parse_r3(hw2)
insn.size = 4
return True
elif subop == 0x362: # sch1r reg2, reg3
insn.itype = NewInstructions.NN_sch1r
insn.Op1.type = o_reg
insn.Op2.type = o_reg
insn.Op1.reg = self.parse_r2(hw1)
insn.Op2.reg = self.parse_r3(hw2)
insn.size = 4
return True
insn_handled = False
if subop == hw2 == 0xA0: # sar reg1, reg2
insn.itype = NewInstructions.NN_sar
insn.Op1.type = o_reg
insn.Op2.type = o_reg
insn.Op1.reg = self.parse_r1(hw1)
insn.Op2.reg = self.parse_r2(hw1)
insn.size = 4
return True
elif subop == 0xEE: # caxi [reg1], reg2, reg3
insn.itype = NewInstructions.NN_caxi
insn.Op1.type = o_displ
insn.Op1.addr = 0
insn.Op2.type = o_reg
insn.Op3.type = o_reg
insn.Op1.reg = self.parse_r1(hw1)
insn.Op2.reg = self.parse_r2(hw1)
insn.Op2.reg = self.parse_r3(hw2)
insn.size = 4
return True
elif subop == 0x2FC: # divq reg1, reg2, reg3
insn.itype = NewInstructions.NN_divq
insn.size = 4
insn_handled = True
elif subop == 0x2FE: # divqu reg1, reg2, reg3
insn.itype = NewInstructions.NN_divqu
insn.size = 4
insn_handled = True
elif subop == 0xA2: # sar reg1, reg2, reg3
insn.itype = NewInstructions.NN_sar
insn.size = 4
insn_handled = True
elif subop == 0xC2: # shl reg1, reg2, reg3
insn.itype = NewInstructions.NN_shl
insn.size = 4
insn_handled = True
elif subop == 0x82: # shr reg1, reg2, reg3
insn.itype = NewInstructions.NN_shr
insn.size = 4
insn_handled = True
if insn_handled:
insn.Op1.type = o_reg
insn.Op2.type = o_reg
insn.Op3.type = o_reg
insn.Op1.reg = self.parse_r1(hw1)
insn.Op2.reg = self.parse_r2(hw1)
insn.Op3.reg = self.parse_r3(hw2)
return True
return False
def ev_ana_insn(self, insn):
if insn.ea & 1:
return False
return self.decode_instruction(insn)
def ev_out_mnem(self, outctx):
insntype = outctx.insn.itype
global NEWINSN_COLOR
if (insntype >= CUSTOM_INSN_ITYPE) and (insntype in NewInstructions.lst):
mnem = NewInstructions.lst[insntype]
outctx.out_tagon(NEWINSN_COLOR)
outctx.out_line(mnem)
outctx.out_tagoff(NEWINSN_COLOR)
# TODO: how can MNEM_width be determined programatically?
MNEM_WIDTH = 8
width = max(1, MNEM_WIDTH - len(mnem))
outctx.out_line(' ' * width)
return True
return False
def ev_emu_insn(self, insn):
if insn.itype in [NewInstructions.NN_eiret, NewInstructions.NN_feret]:
return True
return False
def ev_out_operand(self, outctx, op):
insn = outctx.insn
if insn.itype in [NewInstructions.NN_ld_hu, NewInstructions.NN_st_h]:
if op.type == o_displ:
outctx.out_value(op, OOF_ADDR)
brackets = insn.ops[op.n].specflag1 & N850F_USEBRACKETS
if brackets:
outctx.out_symbol('[')
outctx.out_register(ph_get_regnames()[op.reg])
if brackets:
outctx.out_symbol(']')
return True
return False
#--------------------------------------------------------------------------
class NECromancer_t(plugin_t):
flags = PLUGIN_PROC | PLUGIN_HIDE
comment = ""
wanted_hotkey = ""
help = "Adds support for additional V850X instructions"
wanted_name = "NECromancer"
def __init__(self):
self.prochook = None
def init(self):
if ph_get_id() != PLFM_NEC_V850X:
return PLUGIN_SKIP
self.prochook = v850_idp_hook_t()
self.prochook.hook()
print "%s intialized." % NECromancer_t.wanted_name
return PLUGIN_KEEP
def run(self, arg):
pass
def term(self):
if self.prochook:
self.prochook.unhook()
#--------------------------------------------------------------------------
def PLUGIN_ENTRY():
return NECromancer_t()