Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pyvex/lifting/util/lifter_helper.py: 86%
98 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.const import vex_int_class
6from pyvex.errors import LiftingException
7from pyvex.lifting.lifter import Lifter
9from .vex_helper import IRSBCustomizer, JumpKind
11log = logging.getLogger(__name__)
14def is_empty(bitstrm):
15 try:
16 bitstrm.peek(1)
17 return False
18 except bitstring.ReadError:
19 return True
22class ParseError(Exception):
23 pass
26class GymratLifter(Lifter):
27 """
28 This is a base class for lifters that use Gymrat.
29 For most architectures, all you need to do is subclass this, and set the property "instructions"
30 to be a list of classes that define each instruction.
31 By default, a lifter will decode instructions by attempting to instantiate every class until one works.
32 This will use an IRSBCustomizer, which will, if it succeeds, add the appropriate VEX instructions to a pyvex IRSB.
33 pyvex, when lifting a block of code for this architecture, will call the method "lift", which will produce the IRSB
34 of the lifted code.
35 """
37 __slots__ = (
38 "bitstrm",
39 "errors",
40 "thedata",
41 )
43 REQUIRE_DATA_PY = True
44 instrs = None
46 def __init__(self, *args):
47 super().__init__(*args)
48 self.bitstrm = None
49 self.errors = None
50 self.thedata = None
52 def create_bitstrm(self):
53 self.bitstrm = bitstring.ConstBitStream(bytes=self.thedata)
55 def _decode_next_instruction(self, addr):
56 # Try every instruction until one works
57 for possible_instr in self.instrs:
58 try:
59 log.debug("Trying %s", possible_instr.name)
60 return possible_instr(self.bitstrm, self.irsb.arch, addr)
61 # a ParserError signals that this instruction did not match
62 # we need to try other instructions, so we ignore this error
63 except ParseError:
64 pass # l.exception(repr(possible_instr))
65 # if we are out of input, ignore.
66 # there may be other, shorter instructions that still match,
67 # so we continue with the loop
68 except bitstring.ReadError:
69 pass
71 # If no instruction matches, log an error
72 errorstr = "Unknown instruction at bit position %d" % self.bitstrm.bitpos
73 log.debug(errorstr)
74 log.debug("Address: %#08x" % addr)
76 def decode(self):
77 try:
78 self.create_bitstrm()
79 count = 0
80 disas = []
81 addr = self.irsb.addr
82 log.debug("Starting block at address: " + hex(addr))
83 bytepos = self.bitstrm.bytepos
85 while not is_empty(self.bitstrm):
86 instr = self._decode_next_instruction(addr)
87 if not instr:
88 break
89 disas.append(instr)
90 log.debug("Matched " + instr.name)
91 addr += self.bitstrm.bytepos - bytepos
92 bytepos = self.bitstrm.bytepos
93 count += 1
94 return disas
95 except Exception as e:
96 self.errors = str(e)
97 log.exception(f"Error decoding block at offset {bytepos:#x} (address {addr:#x}):")
98 raise
100 def _lift(self, disassemble=False, dump_irsb=False):
101 self.thedata = (
102 self.data[: self.max_bytes]
103 if isinstance(self.data, (bytes, bytearray, memoryview))
104 else self.data[: self.max_bytes].encode()
105 )
106 log.debug(repr(self.thedata))
107 instructions = self.decode()
109 if disassemble:
110 return [instr.disassemble() for instr in instructions]
111 self.irsb.jumpkind = JumpKind.Invalid
112 irsb_c = IRSBCustomizer(self.irsb)
113 log.debug("Decoding complete.")
114 for i, instr in enumerate(instructions[: self.max_inst]):
115 log.debug("Lifting instruction %s", instr.name)
116 instr(irsb_c, instructions[:i], instructions[i + 1 :])
117 if irsb_c.irsb.jumpkind != JumpKind.Invalid:
118 break
119 if (i + 1) == self.max_inst: # if we are on our last iteration
120 instr.jump(None, irsb_c.irsb.addr + irsb_c.irsb.size)
121 break
122 else:
123 if len(irsb_c.irsb.statements) == 0:
124 raise LiftingException("Could not decode any instructions")
125 irsb_c.irsb.jumpkind = JumpKind.NoDecode
126 dst = irsb_c.irsb.addr + irsb_c.irsb.size
127 dst_ty = vex_int_class(irsb_c.irsb.arch.bits).type
128 irsb_c.irsb.next = irsb_c.mkconst(dst, dst_ty)
129 log.debug(self.irsb._pp_str())
130 if dump_irsb:
131 self.irsb.pp()
132 return self.irsb
134 def pp_disas(self):
135 disasstr = ""
136 insts = self.disassemble()
137 for addr, name, args in insts:
138 args_str = ",".join(str(a) for a in args)
139 disasstr += f"{addr:0#8x}:\t{name} {args_str}\n"
140 print(disasstr)
142 def error(self):
143 return self.errors
145 def disassemble(self):
146 return self.lift(disassemble=True)