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

1import logging 

2 

3import bitstring 

4 

5from pyvex.lifting.util import JumpKind, Type 

6from pyvex.lifting.util.instr_helper import Instruction, ParseError 

7from pyvex.lifting.util.lifter_helper import GymratLifter 

8 

9log = logging.getLogger(__name__) 

10 

11 

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. 

15 

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. 

18 

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

25 

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

32 

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

39 

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

46 

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

53 

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 

99 

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) 

111 

112 

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# 

122 

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) 

128 

129 

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# 

139 

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) 

145 

146 

147class Instruction_MSR(ARMInstruction): 

148 name = "MSR" 

149 bin_format = "cccc00i10d10xxxj1111ssssssssssss" 

150 # 11100011001000011111000010010001 

151 # 11100001011011111111000000000001 

152 

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 ) 

159 

160 

161class Instruction_MRS(ARMInstruction): 

162 name = "MRS" 

163 bin_format = "cccc00010s001111dddd000000000000" 

164 

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 ) 

171 

172 

173class Instruction_STM(ARMInstruction): 

174 name = "STM" 

175 bin_format = "cccc100pu1w0bbbbrrrrrrrrrrrrrrrr" 

176 

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 

182 

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 ) 

189 

190 

191class Instruction_LDM(ARMInstruction): 

192 name = "LDM" 

193 bin_format = "cccc100PU1W1bbbbrrrrrrrrrrrrrrrr" 

194 

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 

200 

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

206 

207 src_n = int(self.data["b"], 2) 

208 src = self.get(src_n, Type.int_32) 

209 

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) 

235 

236 

237class Instruction_STC(ARMInstruction): 

238 name = "STC" 

239 bin_format = "cccc110PUNW0nnnnddddppppOOOOOOOO" 

240 

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) 

244 

245 

246class Instruction_STC_THUMB(ARMInstruction): 

247 name = "STC" 

248 bin_format = "111c110PUNW0nnnnddddppppOOOOOOOO" 

249 

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) 

253 

254 

255class Instruction_LDC(ARMInstruction): 

256 name = "LDC" 

257 bin_format = "cccc110PUNW1nnnnddddppppOOOOOOOO" 

258 

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) 

264 

265 

266class Instruction_LDC_THUMB(ARMInstruction): 

267 name = "LDC" 

268 bin_format = "111c110PUNW1nnnnddddppppOOOOOOOO" 

269 

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) 

275 

276 

277class Instruction_CDP(Instruction): 

278 name = "CDP" 

279 bin_format = "cccc1110oooonnnnddddppppPPP0mmmm" 

280 # c = cond 

281 # d = CPd 

282 # O = Offset 

283 # p = CP# 

284 

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) 

289 

290 

291## 

292## Thumb! (ugh) 

293## 

294 

295 

296class ThumbInstruction(Instruction): # pylint: disable=abstract-method 

297 def mark_instruction_start(self): 

298 self.irsb_c.imark(self.addr - 1, self.bytewidth, 1) 

299 

300 

301class Instruction_tCPSID(ThumbInstruction): 

302 name = "CPSID" 

303 bin_format = "101101x0011x0010" 

304 

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) 

308 

309 

310class Instruction_tMSR(ThumbInstruction): 

311 name = "tMSR" 

312 bin_format = "10x0mmmmxxxxxxxx11110011100Rrrrr" 

313 

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) 

317 

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 ) 

338 

339 

340class Instruction_tMRS(ThumbInstruction): 

341 name = "tMRS" 

342 bin_format = "10x0mmmmxxxxxxxx11110011111Rrrrr" 

343 

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) 

347 

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 ) 

372 

373 

374class Instruction_tDMB(ThumbInstruction): 

375 name = "DMB" 

376 bin_format = "100011110101xxxx1111001110111111" 

377 

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) 

381 

382 

383class Instruction_WFI(ThumbInstruction): 

384 name = "WFI" 

385 bin_format = "10111111001a0000" 

386 # 1011111100110000 

387 

388 def compute_result(self): # pylint: disable=arguments-differ 

389 log.debug("[thumb] Ignoring WFI instruction at %#x.", self.addr) 

390 

391 

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 ] 

413 

414 def __init__(self, *args): 

415 super().__init__(*args) 

416 self.thumb: bool = False 

417 

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)