Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pyvex/lifting/gym/arm_spotter.py: 99%
222 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:15 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:15 +0000
1import logging
3import bitstring
5from pyvex.lifting.util import JumpKind, Type
6from pyvex.lifting.util.instr_helper import Instruction, ParseError
7from pyvex.lifting.util.lifter_helper import GymratLifter
9log = logging.getLogger(__name__)
12class ARMInstruction(Instruction): # pylint: disable=abstract-method
13 # NOTE: WARNING: There is no CPSR in VEX's ARM implementation
14 # You must use straight nasty hacks instead.
16 # NOTE 2: Something is goofy w/r/t archinfo and VEX; cc_op3 is used in ccalls, but there's
17 # no cc_op3 in archinfo, angr itself uses cc_depn instead. We do the same.
19 def match_instruction(self, data, bitstrm):
20 """
21 ARM Instructions are pretty dense, so let's do what we can to weed them out
22 """
23 if "c" not in data or data["c"] == "1111":
24 raise ParseError("Invalid ARM Instruction")
26 def get_N(self):
27 cc_op = self.get("cc_op", Type.int_32)
28 cc_dep1 = self.get("cc_dep1", Type.int_32)
29 cc_dep2 = self.get("cc_dep2", Type.int_32)
30 cc_depn = self.get("cc_ndep", Type.int_32)
31 return self.ccall(Type.int_32, "armg_calculate_flag_n", [cc_op, cc_dep1, cc_dep2, cc_depn])
33 def get_C(self):
34 cc_op = self.get("cc_op", Type.int_32)
35 cc_dep1 = self.get("cc_dep1", Type.int_32)
36 cc_dep2 = self.get("cc_dep2", Type.int_32)
37 cc_depn = self.get("cc_ndep", Type.int_32)
38 return self.ccall(Type.int_32, "armg_calculate_flag_c", [cc_op, cc_dep1, cc_dep2, cc_depn])
40 def get_V(self):
41 cc_op = self.get("cc_op", Type.int_32)
42 cc_dep1 = self.get("cc_dep1", Type.int_32)
43 cc_dep2 = self.get("cc_dep2", Type.int_32)
44 cc_depn = self.get("cc_ndep", Type.int_32)
45 return self.ccall(Type.int_32, "armg_calculate_flag_v", [cc_op, cc_dep1, cc_dep2, cc_depn])
47 def get_Z(self):
48 cc_op = self.get("cc_op", Type.int_32)
49 cc_dep1 = self.get("cc_dep1", Type.int_32)
50 cc_dep2 = self.get("cc_dep2", Type.int_32)
51 cc_depn = self.get("cc_ndep", Type.int_32)
52 return self.ccall(Type.int_32, "armg_calculate_flag_z", [cc_op.rdt, cc_dep1.rdt, cc_dep2.rdt, cc_depn.rdt])
54 def evaluate_condition(self):
55 # condition codes should be in 'c'
56 cond = self.data["c"]
57 if cond == "0000":
58 # equal, z set
59 return self.get_Z() == 1
60 elif cond == "0001":
61 # not equal, Z clear
62 return self.get_Z() == 0
63 elif cond == "0010":
64 # Carry, C set
65 return self.get_C() == 1
66 elif cond == "0011":
67 # Carry Clear, C clear
68 return self.get_C() == 0
69 elif cond == "0100":
70 # MI / neagative / N set
71 return self.get_N() == 1
72 elif cond == "0101":
73 # PL / plus / positive / N clear
74 return self.get_N() == 0
75 elif cond == "0110":
76 # VS / V set / Overflow
77 return self.get_V() == 1
78 elif cond == "0111":
79 # VC / V Clear / no overflow
80 return self.get_V() == 0
81 elif cond == "1000":
82 # Hi / unsigned higher / C set, Z clear
83 return (self.get_C() == 1) & (self.get_Z() == 0)
84 elif cond == "1001":
85 # LS / C clear, Z set
86 return (self.get_C() == 0) & (self.get_Z() == 1)
87 elif cond == "1011":
88 # LT / Less than / N != V
89 return self.get_N() != self.get_V()
90 elif cond == "1100":
91 # GT / greater than / Z clear and (n == v)
92 return (self.get_Z() == 1) & (self.get_N() != self.get_V())
93 elif cond == "1101":
94 # LE / less than or equal to / Z set OR (N != V)
95 return (self.get_Z() == 1) | (self.get_N() != self.get_V())
96 else:
97 # No condition
98 return None
100 def _load_le_instr(self, bitstream: bitstring.ConstBitStream, numbits: int) -> str:
101 # THUMB mode instructions swap endianness every two bytes!
102 if (self.addr & 1) == 1 and numbits > 16:
103 chunk = ""
104 oldpos = bitstream.pos
105 for _ in range(0, numbits, 16):
106 chunk += bitstring.Bits(uint=bitstream.peek("uintle:%d" % 16), length=16).bin
107 bitstream.pos += 16
108 bitstream.pos = oldpos
109 return chunk
110 return super()._load_le_instr(bitstream, numbits)
113class Instruction_MRC(ARMInstruction):
114 name = "MRC"
115 bin_format = "cccc1110CCC1nnnnddddppppOOOOOOOO"
116 # 11101110000100010001111100010000
117 # c = cond
118 # C = Coprocessor operation mode
119 # d = CPd
120 # O = Offset
121 # p = CP#
123 def compute_result(self): # pylint: disable=arguments-differ
124 # TODO at least look at the conditionals
125 # TODO Clobber the dst reg of MCR
126 # TODO maybe treat coproc regs as simple storage (even though they are very much not)
127 log.debug("Ignoring MRC instruction at %#x.", self.addr)
130class Instruction_MCR(ARMInstruction):
131 name = "MCR"
132 bin_format = "cccc1110CCC0nnnnddddppppOOOOOOOO"
133 # 11101110000000010000111100010000
134 # c = cond
135 # C = Coprocessor operation mode
136 # d = CPd
137 # O = Offset
138 # p = CP#
140 def compute_result(self): # pylint: disable=arguments-differ
141 # TODO at least look at the conditionals
142 # TODO Clobber the dst reg of MCR
143 # TODO maybe treat coproc regs as simple storage (even though they are very much not)
144 log.debug("Ignoring MCR instruction at %#x.", self.addr)
147class Instruction_MSR(ARMInstruction):
148 name = "MSR"
149 bin_format = "cccc00i10d10xxxj1111ssssssssssss"
150 # 11100011001000011111000010010001
151 # 11100001011011111111000000000001
153 def compute_result(self): # pylint: disable=arguments-differ
154 log.debug(
155 "Ignoring MSR instruction at %#x. VEX cannot support this instruction. "
156 "See pyvex/lifting/gym/arm_spotter.py",
157 self.addr,
158 )
161class Instruction_MRS(ARMInstruction):
162 name = "MRS"
163 bin_format = "cccc00010s001111dddd000000000000"
165 def compute_result(self): # pylint: disable=arguments-differ
166 log.debug(
167 "Ignoring MRS instruction at %#x. VEX cannot support this instruction. "
168 "See pyvex/lifting/gym/arm_spotter.py",
169 self.addr,
170 )
173class Instruction_STM(ARMInstruction):
174 name = "STM"
175 bin_format = "cccc100pu1w0bbbbrrrrrrrrrrrrrrrr"
177 def match_instruction(self, data, bitstrm):
178 # If we don't push anything, that's not real
179 if int(data["r"]) == 0:
180 raise ParseError("Invalid STM instruction")
181 return True
183 def compute_result(self): # pylint: disable=arguments-differ
184 log.warning(
185 "Ignoring STMxx ^ instruction at %#x. This mode is not implemented by VEX! "
186 "See pyvex/lifting/gym/arm_spotter.py",
187 self.addr,
188 )
191class Instruction_LDM(ARMInstruction):
192 name = "LDM"
193 bin_format = "cccc100PU1W1bbbbrrrrrrrrrrrrrrrr"
195 def match_instruction(self, data, bitstrm):
196 # If we don't push anything, that's not real
197 if int(data["r"]) == 0:
198 raise ParseError("Invalid LDM instruction")
199 return True
201 def compute_result(self): # pylint: disable=arguments-differ
202 # test if PC will be set. If so, the jumpkind of this block should be Ijk_Ret
203 log.warning("Spotting an LDM instruction at %#x. This is not fully tested. Prepare for errors.", self.addr)
204 # l.warning(repr(self.rawbits))
205 # l.warning(repr(self.data))
207 src_n = int(self.data["b"], 2)
208 src = self.get(src_n, Type.int_32)
210 for reg_num, bit in enumerate(self.data["r"]):
211 reg_num = 15 - reg_num
212 if bit == "1":
213 if self.data["P"] == "1":
214 if self.data["U"] == "0":
215 src += 4
216 else:
217 src -= 4
218 val = self.load(src, Type.int_32)
219 self.put(val, reg_num)
220 if self.data["P"] == "0":
221 if self.data["U"] == "0":
222 src += 4
223 else:
224 src -= 4
225 # If we touch PC, we're doing a RET!
226 if reg_num == 15 and bit == "1":
227 cond = self.evaluate_condition()
228 if cond is not None:
229 self.jump(cond, val, JumpKind.Ret)
230 else:
231 self.jump(None, val, JumpKind.Ret)
232 # Write-back
233 if self.data["W"] == "1":
234 self.put(src, src_n)
237class Instruction_STC(ARMInstruction):
238 name = "STC"
239 bin_format = "cccc110PUNW0nnnnddddppppOOOOOOOO"
241 def compute_result(self): # pylint: disable=arguments-differ
242 # TODO At least look at the conditionals
243 log.debug("Ignoring STC instruction at %#x.", self.addr)
246class Instruction_STC_THUMB(ARMInstruction):
247 name = "STC"
248 bin_format = "111c110PUNW0nnnnddddppppOOOOOOOO"
250 def compute_result(self): # pylint: disable=arguments-differ
251 # TODO At least look at the conditionals
252 log.debug("Ignoring STC instruction at %#x.", self.addr)
255class Instruction_LDC(ARMInstruction):
256 name = "LDC"
257 bin_format = "cccc110PUNW1nnnnddddppppOOOOOOOO"
259 def compute_result(self): # pylint: disable=arguments-differ
260 # TODO At least look at the conditionals
261 # TODO Clobber the dest reg of LDC
262 # TODO Maybe clobber the dst reg of CDP, if we're really adventurous
263 log.debug("Ignoring LDC instruction at %#x.", self.addr)
266class Instruction_LDC_THUMB(ARMInstruction):
267 name = "LDC"
268 bin_format = "111c110PUNW1nnnnddddppppOOOOOOOO"
270 def compute_result(self): # pylint: disable=arguments-differ
271 # TODO At least look at the conditionals
272 # TODO Clobber the dest reg of LDC
273 # TODO Maybe clobber the dst reg of CDP, if we're really adventurous
274 log.debug("Ignoring LDC instruction at %#x.", self.addr)
277class Instruction_CDP(Instruction):
278 name = "CDP"
279 bin_format = "cccc1110oooonnnnddddppppPPP0mmmm"
280 # c = cond
281 # d = CPd
282 # O = Offset
283 # p = CP#
285 def compute_result(self): # pylint: disable=arguments-differ
286 # TODO At least look at the conditionals
287 # TODO Maybe clobber the dst reg of CDP, if we're really adventurous
288 log.debug("Ignoring CDP instruction at %#x.", self.addr)
291##
292## Thumb! (ugh)
293##
296class ThumbInstruction(Instruction): # pylint: disable=abstract-method
297 def mark_instruction_start(self):
298 self.irsb_c.imark(self.addr - 1, self.bytewidth, 1)
301class Instruction_tCPSID(ThumbInstruction):
302 name = "CPSID"
303 bin_format = "101101x0011x0010"
305 def compute_result(self): # pylint: disable=arguments-differ
306 # TODO haha lol yeah right
307 log.debug("[thumb] Ignoring CPS instruction at %#x.", self.addr)
310class Instruction_tMSR(ThumbInstruction):
311 name = "tMSR"
312 bin_format = "10x0mmmmxxxxxxxx11110011100Rrrrr"
314 def compute_result(self): # pylint: disable=arguments-differ
315 dest_spec_reg = int(self.data["x"], 2)
316 src_reg = int(self.data["r"], 2)
318 # If 0, do not write the SPSR
319 if self.data["R"] == "0":
320 if dest_spec_reg == 8: # msp
321 src = self.get(src_reg, Type.int_32)
322 self.put(src, "sp")
323 elif dest_spec_reg == 16: # primask
324 src = self.get(src_reg, Type.int_32)
325 self.put(src, "primask")
326 else:
327 log.warning(
328 "[thumb] FIXME: tMSR at %#x is writing into an unsupported special register %#x. "
329 "Ignoring the instruction.",
330 self.addr,
331 dest_spec_reg,
332 )
333 else:
334 log.warning("[thumb] tMSR at %#x is writing SPSR. Ignoring the instruction. FixMe.", self.addr)
335 log.warning(
336 "[thumb] Spotting an tMSR instruction at %#x. This is not fully tested. Prepare for errors.", self.addr
337 )
340class Instruction_tMRS(ThumbInstruction):
341 name = "tMRS"
342 bin_format = "10x0mmmmxxxxxxxx11110011111Rrrrr"
344 def compute_result(self): # pylint: disable=arguments-differ
345 spec_reg = int(self.data["x"], 2)
346 dest_reg = int(self.data["m"], 2)
348 # Reading from CPSR
349 if self.data["R"] == "0":
350 # See special registers constants here:
351 # https://github.com/aquynh/capstone/blob/45bec1a691e455b864f7e4d394711a467e5493dc/arch/ARM/ARMInstPrinter.c#L1654
352 if spec_reg == 8:
353 # We move the SP and call it a day.
354 src = self.get("sp", Type.int_32)
355 self.put(src, dest_reg)
356 elif spec_reg == 16:
357 src = self.get("primask", Type.int_32)
358 self.put(src, dest_reg)
359 else:
360 log.warning(
361 "[thumb] FIXME: tMRS at %#x is using the unsupported special register %#x. "
362 "Ignoring the instruction.",
363 self.addr,
364 spec_reg,
365 )
366 else:
367 log.warning("[thumb] tMRS at %#x is reading from SPSR. Ignoring the instruction. FixMe.", self.addr)
368 log.debug("[thumb] Ignoring tMRS instruction at %#x.", self.addr)
369 log.warning(
370 "[thumb] Spotting an tMRS instruction at %#x. This is not fully tested. Prepare for errors.", self.addr
371 )
374class Instruction_tDMB(ThumbInstruction):
375 name = "DMB"
376 bin_format = "100011110101xxxx1111001110111111"
378 def compute_result(self): # pylint: disable=arguments-differ
379 # TODO haha lol yeah right
380 log.debug("[thumb] Ignoring DMB instruction at %#x.", self.addr)
383class Instruction_WFI(ThumbInstruction):
384 name = "WFI"
385 bin_format = "10111111001a0000"
386 # 1011111100110000
388 def compute_result(self): # pylint: disable=arguments-differ
389 log.debug("[thumb] Ignoring WFI instruction at %#x.", self.addr)
392class ARMSpotter(GymratLifter):
393 arm_instrs = [
394 Instruction_MRC,
395 Instruction_MCR,
396 Instruction_MSR,
397 Instruction_MRS,
398 Instruction_STM,
399 Instruction_LDM,
400 Instruction_STC,
401 Instruction_LDC,
402 Instruction_CDP,
403 ]
404 thumb_instrs = [
405 Instruction_tCPSID,
406 Instruction_tMSR,
407 Instruction_tMRS,
408 Instruction_WFI,
409 Instruction_tDMB,
410 Instruction_STC_THUMB,
411 Instruction_LDC_THUMB,
412 ]
414 def __init__(self, *args):
415 super().__init__(*args)
416 self.thumb: bool = False
418 def _lift(self, disassemble=False, dump_irsb=False):
419 if self.irsb.addr & 1:
420 # Thumb!
421 self.instrs = self.thumb_instrs
422 self.thumb = True
423 else:
424 self.instrs = self.arm_instrs
425 self.thumb = False
426 super()._lift(disassemble, dump_irsb)