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

1import logging 

2 

3import bitstring 

4 

5from pyvex.const import vex_int_class 

6from pyvex.errors import LiftingException 

7from pyvex.lifting.lifter import Lifter 

8 

9from .vex_helper import IRSBCustomizer, JumpKind 

10 

11log = logging.getLogger(__name__) 

12 

13 

14def is_empty(bitstrm): 

15 try: 

16 bitstrm.peek(1) 

17 return False 

18 except bitstring.ReadError: 

19 return True 

20 

21 

22class ParseError(Exception): 

23 pass 

24 

25 

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 """ 

36 

37 __slots__ = ( 

38 "bitstrm", 

39 "errors", 

40 "thedata", 

41 ) 

42 

43 REQUIRE_DATA_PY = True 

44 instrs = None 

45 

46 def __init__(self, *args): 

47 super().__init__(*args) 

48 self.bitstrm = None 

49 self.errors = None 

50 self.thedata = None 

51 

52 def create_bitstrm(self): 

53 self.bitstrm = bitstring.ConstBitStream(bytes=self.thedata) 

54 

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 

70 

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) 

75 

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 

84 

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 

99 

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() 

108 

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 

133 

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) 

141 

142 def error(self): 

143 return self.errors 

144 

145 def disassemble(self): 

146 return self.lift(disassemble=True)