def _search_value(self): if self._arm: armins = self._string_to_insn(self._insbytes) if not self._arm64: if not self._armthumb: # ARM instructions if armins & 0x0C000000 == 0x04000000: # LDR thoughtval = armins & 0xFFF if thoughtval != self.value: raise ValueNotFoundError imm12 = self._imm(12) self.patch_value_expression = imm12.zero_extend( self.bits - 12) self.patch_bytes_expression = claripy.Concat( BVV(armins >> 12, 20), imm12) self._test_values = (1, 0xfff) elif armins & 0x0E000000 == 0x02000000: # Data processing w/ immediate shiftval = (armins & 0xF00) >> 7 thoughtval = armins & 0xFF thoughtval = ror(thoughtval, shiftval, 32) if thoughtval != self.value: raise ValueNotFoundError shift = self._imm(4, 'shift') imm8 = self._imm(8) self.patch_value_expression = claripy.RotateRight( imm8.zero_extend(32 - 8), shift.zero_extend(32 - 4) * 2) self.patch_bytes_expression = claripy.Concat( BVV(armins >> 12, 20), shift, imm8) self._test_values = (1, 0xff, 0xff000000) elif armins & 0x0E400090 == 0x00400090: # LDRH thoughtval = (armins & 0xF) | ((armins & 0xF00) >> 4) if thoughtval != self.value: raise ValueNotFoundError hinib = self._imm(4, 'hinib') lonib = self._imm(4, 'lonib') self.patch_value_expression = claripy.Concat( hinib, lonib).zero_extend(self.bits - 8) self.patch_bytes_expression = claripy.Concat( BVV(armins >> 12, 20), hinib, BVV((armins >> 4) & 0xF, 4), lonib) self._test_values = (1, 0xff) elif armins & 0x0E000000 == 0x0C000000: # Coprocessor data transfer # i.e. FLD/FST thoughtval = armins & 0xFF thoughtval *= 4 if thoughtval != self.value: raise ValueNotFoundError imm8 = self._imm(8) self.patch_value_expression = imm8.zero_extend( self.bits - 8) << 2 self.patch_bytes_expression = claripy.Concat( BVV(armins >> 8, 24), imm8) self._test_values = (4, 0x3fc) else: raise ValueNotFoundError else: # THUMB instructions # https://ece.uwaterloo.ca/~ece222/ARM/ARM7-TDMI-manual-pt3.pdf if self._inslen == 2: # 16 bit instructions if armins & 0xF000 in (0x9000, 0xA000): # SP-relative LDR/STR, also SP-addiition # page 26, 28 # unsigned offsets only, 10 bit imm stored w/o last two bits thoughtval = armins & 0xFF thoughtval *= 4 if thoughtval != self.value: raise ValueNotFoundError imm8 = self._imm(8) self.patch_value_expression = imm8.zero_extend( self.bits - 8) << 2 self.patch_bytes_expression = claripy.Concat( BVV(armins >> 8, 8), imm8) self._test_values = (4, 0x3fc) elif armins & 0xFF00 == 0xB000: # Add/sub offset to SP # page 30 # uses sign bit, 9 bit imm stored w/o last two bits thoughtval = armins & 0x7F thoughtval *= 4 if thoughtval != self.value: raise ValueNotFoundError imm7 = self._imm(7) self.patch_value_expression = imm7.zero_extend( self.bits - 7) << 2 self.patch_bytes_expression = claripy.Concat( BVV(armins >> 7, 9), imm7) self._test_values = (4, 0x1fc) elif armins & 0xFC00 == 0x1C00: # ADD/SUB (immediate format) # page 7 # uses sign bit, 3 bit immediate thoughtval = (armins & 0x01C0) >> 6 if thoughtval != self.value: raise ValueNotFoundError imm3 = self._imm(3) self.patch_value_expression = imm3.zero_extend( self.bits - 3) self.patch_bytes_expression = claripy.Concat( BVV(armins >> 9, 7), imm3, BVV(armins & 0x3F, 6)) self._test_values = (1, 7) elif armins & 0xE000 == 0x2000: # Move/Compare/Add/Subtract immediate # page 9 # Unsigned 8 bit immediate thoughtval = armins & 0xFF if thoughtval != self.value: raise ValueNotFoundError imm8 = self._imm(8) self.patch_value_expression = imm8.zero_extend( self.bits - 8) self.patch_bytes_expression = claripy.Concat( BVV(armins >> 8, 8), imm8) self._test_values = (1, 0xff) elif armins & 0xF000 == 0x6000: # Register-relative LDR/STR # page 22 # unsigned 7 bit imm stored w/o last two bits thoughtval = ((armins >> 6) & 0x1F) << 2 if thoughtval != self.value: raise ValueNotFoundError imm5 = self._imm(5) self.patch_value_expression = imm5.zero_extend( self.bits - 5) << 2 self.patch_bytes_expression = claripy.Concat( BVV(armins >> 11, 5), imm5, BVV(armins & 0x3F, 6)) self._test_values = (4, 0x7c) elif armins & 0xF000 == 0x7000: # Register-relative LDRB/STRB # page 22 # unsigned 5 bit imm thoughtval = (armins >> 6) & 0x1F if thoughtval != self.value: raise ValueNotFoundError imm5 = self._imm(5) self.patch_value_expression = imm5.zero_extend( self.bits - 5) self.patch_bytes_expression = claripy.Concat( BVV(armins >> 11, 5), imm5, BVV(armins & 0x3F, 6)) self._test_values = (1, 0x1f) else: raise ValueNotFoundError elif self._inslen == 4: # 32 bit instructions # http://read.pudn.com/downloads159/doc/709030/Thumb-2SupplementReferenceManual.pdf if armins & 0xFE1F0000 == 0xF81F0000 or \ armins & 0xFE800000 == 0xF8800000: # Load/Store # page 66, formats 1-2 # imm12 with designated sign bit thoughtval = armins & 0xFFF if thoughtval != self.value: raise ValueNotFoundError imm12 = self._imm(12) self.patch_value_expression = imm12.zero_extend( self.bits - 12) self.patch_bytes_expression = claripy.Concat( BVV(armins >> 12, 20), imm12) self._test_values = (1, 0xfff) elif armins & 0xFE800900 == 0xF8000800: # Load/Store # page 66, formats 3-4 # imm8 with designated sign bit thoughtval = armins & 0xFF if thoughtval != self.value: raise ValueNotFoundError imm8 = self._imm(8) self.patch_value_expression = imm8.zero_extend( self.bits - 8) self.patch_bytes_expression = claripy.Concat( BVV(armins >> 8, 24), imm8) self._test_values = (1, 0xff) elif armins & 0xFE800900 == 0xF8000900: # Load/Store # page 66, formats 5-6 # imm8, sign extended thoughtval = armins & 0x7F if armins & 0x80 == 0x80: thoughtval = (thoughtval ^ 0x7F) + 1 if thoughtval != self.value: raise ValueNotFoundError imm8 = self._imm(8) self.patch_value_expression = imm8.sign_extend( self.bits - 8) self.patch_bytes_expression = claripy.Concat( BVV(armins >> 8, 24), imm8) self._test_values = (-0x80, 0x7f) elif armins & 0xFB408000 == 0xF2000000: # Add/Sub # page 53, format 2 # 12 bit immediate split into 3 bitfields thoughtval = armins & 0xFF thoughtval |= (armins & 0x7000) >> 4 thoughtval |= (armins & 0x04000000) >> 15 if thoughtval != self.value: raise ValueNotFoundError imm8 = self._imm(8) imm3 = self._imm(3) imm1 = self._imm(1) self.patch_value_expression = claripy.Concat( imm1, imm3, imm8).zero_extend(self.bits - 12) self.patch_bytes_expression = claripy.Concat( BVV(armins >> 27, 5), imm1, BVV((armins & 0x03FF8000) >> 15, 11), imm3, BVV((armins & 0xF00) >> 8, 4), imm8) self._test_values = (1, 0xfff) elif armins & 0xFB408000 == 0xF2400000: # Move # page 53, format 3 # 16 bit immediate split into 4 bitfields thoughtval = armins & 0xFF thoughtval |= (armins & 0x7000) >> 4 thoughtval |= (armins & 0x04000000) >> 15 thoughtval |= (armins & 0xF0000) >> 4 if thoughtval != self.value: raise ValueNotFoundError imm8 = self._imm(8) imm3 = self._imm(3) imm1 = self._imm(1) imm4 = self._imm(1) self.patch_value_expression = claripy.Concat( imm4, imm1, imm3, imm8).zero_extend(self.bits - 12) self.patch_bytes_expression = claripy.Concat( BVV(armins >> 27, 5), imm1, BVV((armins & 0x03F00000) >> 20, 6), imm4, BVV((armins & 0x00008000) >> 15, 1), imm3, BVV((armins & 0xF00) >> 8, 4), imm8) self._test_values = (1, 0xffff) elif armins & 0xFA008000 == 0xF0000000: # Data processing, modified 12 bit imm, aka EVIL # page 53 # wow. just. wow. imm12 = armins & 0xFF imm12 |= (armins & 0x7000) >> 4 imm12 |= (armins & 0x04000000) >> 15 # decoding algorithm from page 93 if imm12 & 0xC00 == 0: if imm12 & 0x300 == 0: thoughtval = imm12 elif imm12 & 0x300 == 0x100: thoughtval = imm12 & 0xFF thoughtval |= thoughtval << 16 elif imm12 & 0x300 == 0x200: thoughtval = (imm12 & 0xFF) << 8 thoughtval |= thoughtval << 16 elif imm12 & 0x300 == 0x300: thoughtval = imm12 & 0xFF thoughtval |= thoughtval << 8 thoughtval |= thoughtval << 16 else: thoughtval = ror(0x80 | (imm12 & 0x7F), imm12 >> 7, 32) if thoughtval != self.value: raise ValueNotFoundError imm12 = self._imm(12) ITE = claripy.If CAT = claripy.Concat ROR = claripy.RotateRight imm8 = imm12[7:0] imm7 = imm12[6:0] imm3 = imm12[10:8] imm1 = imm12[11] zero = BVV(0, 8) bit = BVV(1, 1) monster = ITE( imm12[11:10] == 0, ITE( imm12[9] == 0, ITE(imm12[8] == 0, imm12[7:0].zero_extend(32 - 8), CAT(zero, imm8, zero, imm8)), ITE(imm12[8] == 0, CAT(imm8, zero, imm8, zero), CAT(imm8, imm8, imm8, imm8))), ROR( CAT(bit, imm7).zero_extend(32 - 8), imm12[11:7].zero_extend(32 - 5))) self.patch_value_expression = monster self.patch_bytes_expression = claripy.Concat( BVV(armins >> 27, 5), imm1, BVV((armins & 0x03FF8000) >> 15, 11), imm3, BVV((armins & 0xF00) >> 8, 4), imm8) self._test_values = (0xff00ff00, 0x00ff00ff, 0xffffffff, 0xff, 0xff000000) else: raise ValueNotFoundError else: raise FidgetUnsupportedError( "You found a THUMB instruction longer than 32 bits??" ) else: self.bit_length = 64 # aarch64 instructions # can't find a reference doc?????? I'm pulling these from VEX, guest_arm64_toIR.c if armins & 0x7f800000 in (0x28800000, 0x29800000, 0x29000000): # LDP/SDP # line 4791 # 7 bit immediate signed offset, scaled by load size (from MSB) shift = 3 if armins & 0x80000000 else 2 simm7 = (armins & 0x3f8000) >> 15 simm7 = resign_int(simm7, 7) simm7 <<= shift if simm7 != self.value: raise ValueNotFoundError imm7 = self._imm(7) self.patch_value_expression = imm7.sign_extend(self.bits - 7) << shift self.patch_bytes_expression = claripy.Concat( BVV((armins & 0xffc00000) >> 22, 10), imm7, BVV(armins & 0x7fff, 15)) self._test_values = (-0x40 << shift, 0x3f << shift) elif (armins & 0x3f800000 == 0x39000000) or \ (armins & 0x3f800000 == 0x39800000 and \ ((armins >> 30) | ((armins >> 22) & 1)) in (4, 2, 3, 0, 1)): # LDR/STR, LDRS # line 4639, 5008 # 12 bit immediate unsigned offset, scaled by load size (from 2 MSB) shift = (armins & 0xc0000000) >> 30 offs = (armins & 0x3ffc00) >> 10 offs <<= shift if offs != self.value: raise ValueNotFoundError imm12 = self._imm(12) self.patch_value_expression = imm12.zero_extend( self.bits - 12) << shift self.patch_bytes_expression = claripy.Concat( BVV((armins & 0xffc00000) >> 22, 10), imm12, BVV(armins & 0x3ff, 10)) self._test_values = (1 << shift, 0xfff << shift) elif armins & 0x1f000000 == 0x11000000: # ADD/SUB imm # line 2403 # 12 bit shifted unsigned immediate if not armins & 0x80000000: self.bit_length = 32 shift = (armins >> 22) & 3 imm12 = (armins >> 10) & 0xfff imm12 <<= 12 * shift if imm12 != self.value: raise ValueNotFoundError shift = self._imm(1, 'shift') imm12 = self._imm(12) shift_full = shift.zero_extend(self.bits - 1) * 12 self.patch_value_expression = imm12.zero_extend( self.bits - 12) << shift_full self.patch_bytes_expression = claripy.Concat( BVV(armins >> 24, 8), BVV(0, 1), shift, imm12, BVV(armins & 0x3ff, 10)) self._test_values = (1, 0xfff, 0xfff000) elif armins & 0x3fa00000 == 0x38000000: # LDUR/STUR # Line 4680 # 9 bit signed immediate offset imm9 = (armins >> 12) & 0x1ff imm9 = resign_int(imm9, 9) if imm9 != self.value: raise ValueNotFoundError imm9 = self._imm(9) self.patch_value_expression = imm9.sign_extend(self.bits - 9) self.patch_bytes_expression = claripy.Concat( BVV(armins >> 21, 11), imm9, BVV(armins & 0xfff, 12)) self._test_values = (-0x100, 0xff) else: raise ValueNotFoundError if not self.sanity_check(): raise ValueNotFoundError else: insn = self._string_to_insn(self._insbytes) insn = BVV(insn, self._inslen * 8) for word_size in (64, 32, 16, 8): if word_size > self.bits: continue for bit_offset in xrange(0, insn.length - word_size + 1, 8): result = insn[bit_offset + word_size - 1:bit_offset] result = result.sign_extend(self.bits - word_size) if claripy.is_true(result == self.value): imm = self._imm(word_size) self.patch_value_expression = imm.sign_extend( self.bits - word_size) if bit_offset + word_size >= insn.length: acc = imm else: acc = claripy.Concat( insn[insn.length - 1:bit_offset + word_size], imm) if bit_offset != 0: acc = claripy.Concat(acc, insn[bit_offset - 1:0]) if self._project.arch.memory_endness == 'Iend_LE': self.patch_bytes_offset = bit_offset / 8 else: self.patch_bytes_offset = self._inslen - ( bit_offset + word_size) / 8 self.patch_bytes_size = word_size / 8 self.patch_bytes_expression = acc self._test_values = (-(1 << word_size) >> 1, ((1 << word_size) >> 1) - 1) if self.sanity_check(): break # found if self._project.arch.name == 'PPC64': # On PPC64, the lowest two bits of immediate values can be used for other things # Mask those out result = (result & ~3).sign_extend(self.bits - word_size) if not claripy.is_true(result == self.value): continue imm = self._imm(word_size - 2) self.patch_value_expression = claripy.Concat( imm, BVV(0, 2)).sign_extend(self.bits - word_size) if bit_offset + word_size >= insn.length: acc = imm else: acc = claripy.Concat( insn[insn.length - 1:bit_offset + word_size], imm) acc = claripy.Concat(acc, insn[bit_offset + 1:0]) if self._project.arch.memory_endness == 'Iend_LE': self.patch_bytes_offset = bit_offset / 8 else: self.patch_bytes_offset = self._inslen - ( bit_offset + word_size) / 8 self.patch_bytes_size = word_size / 8 self.patch_bytes_expression = acc self._test_values = (-(1 << word_size) >> 1, ((1 << word_size) >> 1) - 4) if self.sanity_check(): break # found else: # inner loop did not break: not found continue # inner loop broke: found break else: # outer loop did not break: inner loop did not break: not found raise ValueNotFoundError # outer loop broke: inner loop broke: found return
def exec_branch(self, state): # pylint:disable=invalid-name """Execute forward from a state, queuing new states if needed.""" logger.debug("Constraints: %s", state.solver.constraints) def solution(variable): """Returns the solution. There must be one or we fail.""" solutions = state.solver.eval(variable, 2) if len(solutions) > 1: raise MultipleSolutionsError( "Multiple solutions for %s (%#x)" % (variable, self.code[state.pc])) solution = solutions[0] return solution if isinstance(solution, numbers.Number) else solution.value self.code.pc = state.pc while True: if state.pc >= len(self.code): return True op = self.code[state.pc] self.code.pc += 1 self.coverage[state.pc] += 1 logger.debug("NEW STEP") logger.debug("Memory: %s", state.memory) logger.debug("Stack: %s", state.stack) logger.debug("PC: %i, %#x", state.pc, op) assert self.code.pc == state.pc + 1 assert isinstance(op, numbers.Number) assert all( isinstance(i, claripy.ast.base.BV) for i in state.stack), "The stack musty only contains claripy BV's" # Trivial operations first if not self.code.is_valid_opcode(state.pc): raise utils.CodeError("Trying to execute PUSH data") elif op == 254: # INVALID opcode raise utils.CodeError("designed INVALID opcode") elif op == opcode_values.JUMPDEST: pass elif op == opcode_values.ADD: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name state.stack_push(s0 + s1) elif op == opcode_values.SUB: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name state.stack_push(s0 - s1) elif op == opcode_values.MUL: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name state.stack_push(s0 * s1) elif op == opcode_values.DIV: # We need to use claripy.LShR instead of a division if possible, # because the solver is bad dealing with divisions, better # with shifts. And we need shifts to handle the solidity ABI # for function selection. s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name try: s1 = solution(s1) # pylint:disable=invalid-name except MultipleSolutionsError: state.stack_push(claripy.If(s1 == 0, BVV_0, s0 / s1)) else: if s1 == 0: state.stack_push(BVV_0) elif s1 == 1: state.stack_push(s0) elif s1 & (s1 - 1) == 0: exp = int(math.log(s1, 2)) state.stack_push(s0.LShR(exp)) else: state.stack_push(s0 / s1) elif op == opcode_values.SDIV: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name try: s1 = solution(s1) except MultipleSolutionsError: state.stack_push(claripy.If(s1 == 0, BVV_0, s0.SDiv(s1))) else: state.stack_push(BVV_0 if s1 == 0 else s0.SDiv(s1)) elif op == opcode_values.MOD: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name try: s1 = solution(s1) except MultipleSolutionsError: state.stack_push(claripy.If(s1 == 0, BVV_0, s0 % s1)) else: state.stack_push(BVV_0 if s1 == 0 else s0 % s1) elif op == opcode_values.SMOD: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name try: s1 = solution(s1) except MultipleSolutionsError: state.stack_push(claripy.If(s1 == 0, BVV_0, s0.SMod(s1))) else: state.stack_push(BVV_0 if s1 == 0 else s0.SMod(s1)) elif op == opcode_values.ADDMOD: s0, s1, s2 = state.stack_pop(), state.stack_pop( ), state.stack_pop() try: s2 = solution(s2) except MultipleSolutionsError: state.stack_push(claripy.If(s2 == 0, BVV_0, (s0 + s1) % s2)) else: state.stack_push(BVV_0 if s2 == 0 else (s0 + s1) % s2) elif op == opcode_values.MULMOD: s0, s1, s2 = state.stack_pop(), state.stack_pop( ), state.stack_pop() try: s2 = solution(s2) except MultipleSolutionsError: state.stack_push(claripy.If(s2 == 0, BVV_0, (s0 * s1) % s2)) else: state.stack_push(BVV_0 if s2 == 0 else (s0 * s1) % s2) elif op == opcode_values.SHL: shift, value = state.stack_pop(), state.stack_pop() state.stack_push(value << shift) elif op == opcode_values.SHR: shift, value = state.stack_pop(), state.stack_pop() state.stack_push(value.LShR(shift)) elif op == opcode_values.SAR: shift, value = state.stack_pop(), state.stack_pop() state.stack_push(claripy.RotateRight(value, shift)) elif op == opcode_values.EXP: base, exponent = state.stack_pop(), state.stack_pop() base_sol = solution(base) if base_sol == 2: state.stack_push(1 << exponent) else: try: exponent_sol = solution(exponent) except MultipleSolutionsError: state.stack_push(exponent) # restore stack state.stack_push(base) self.add_for_fuzzing(state, exponent, EXP_EXPONENT_FUZZ) return False else: state.stack_push( claripy.BVV(base_sol**exponent_sol, 256)) elif op == opcode_values.LT: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name state.stack_push(bool_to_bv(claripy.ULT(s0, s1))) elif op == opcode_values.GT: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name state.stack_push(bool_to_bv(claripy.UGT(s0, s1))) elif op == opcode_values.SLT: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name state.stack_push(bool_to_bv(claripy.SLT(s0, s1))) elif op == opcode_values.SGT: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name state.stack_push(bool_to_bv(claripy.SGT(s0, s1))) elif op == opcode_values.SIGNEXTEND: # TODO: Use Claripy's SignExt that should do exactly that. s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name # s0 is the number of bits. s1 the number we want to extend. s0 = solution(s0) if s0 <= 31: sign_bit = 1 << (s0 * 8 + 7) state.stack_push( claripy.If( s1 & sign_bit == 0, s1 & (sign_bit - 1), s1 | ((1 << 256) - sign_bit), )) else: state.stack_push(s1) elif op == opcode_values.EQ: s0, s1 = state.stack_pop(), state.stack_pop() state.stack_push(bool_to_bv(s0 == s1)) elif op == opcode_values.ISZERO: state.stack_push(bool_to_bv(state.stack_pop() == BVV_0)) elif op == opcode_values.AND: s0, s1 = state.stack_pop(), state.stack_pop() state.stack_push(s0 & s1) elif op == opcode_values.OR: s0, s1 = state.stack_pop(), state.stack_pop() state.stack_push(s0 | s1) elif op == opcode_values.XOR: s0, s1 = state.stack_pop(), state.stack_pop() state.stack_push(s0 ^ s1) elif op == opcode_values.NOT: state.stack_push(~state.stack_pop()) elif op == opcode_values.BYTE: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name state.stack_push( s1.LShR(claripy.If(s0 > 31, 32, 31 - s0) * 8) & 0xFF) elif op == opcode_values.PC: state.stack_push(bvv(state.pc)) elif op == opcode_values.GAS: state.stack_push(state.env.gas) elif op == opcode_values.ADDRESS: state.stack_push(state.env.address) elif op == opcode_values.BALANCE: addr = solution(state.stack_pop()) if addr != solution(state.env.address): raise utils.InterpreterError( state, "Can only query balance of the current contract for now" ) state.stack_push(state.env.balance) elif op == opcode_values.ORIGIN: state.stack_push(state.env.origin) elif op == opcode_values.CALLER: state.stack_push(state.env.caller) elif op == opcode_values.CALLVALUE: state.stack_push(state.env.value) elif op == opcode_values.BLOCKHASH: block_num = state.stack_pop() if block_num not in state.env.block_hashes: state.env.block_hashes[block_num] = claripy.BVS( "blockhash[%s]" % block_num, 256) state.stack_push(state.env.block_hashes[block_num]) elif op == opcode_values.TIMESTAMP: state.stack_push(state.env.block_timestamp) elif op == opcode_values.NUMBER: state.stack_push(state.env.block_number) elif op == opcode_values.COINBASE: state.stack_push(state.env.coinbase) elif op == opcode_values.DIFFICULTY: state.stack_push(state.env.difficulty) elif op == opcode_values.POP: state.stack_pop() elif op == opcode_values.JUMP: addr = solution(state.stack_pop()) if addr >= len(self.code ) or self.code[addr] != opcode_values.JUMPDEST: raise utils.CodeError("Invalid jump (%i)" % addr) state.pc = addr self.add_branch(state) return False elif op == opcode_values.JUMPI: addr, condition = solution( state.stack_pop()), state.stack_pop() state_false = state.copy() state.solver.add(condition != BVV_0) state_false.solver.add(condition == BVV_0) state_false.pc += 1 self.add_branch(state_false) state.pc = addr if (state.pc >= len(self.code) or self.code[state.pc] != opcode_values.JUMPDEST): raise utils.CodeError("Invalid jump (%i)" % (state.pc - 1)) self.add_branch(state) return False elif opcode_values.PUSH1 <= op <= opcode_values.PUSH32: pushnum = op - opcode_values.PUSH1 + 1 raw_value = self.code.read(pushnum) state.pc += pushnum state.stack_push( bvv(int.from_bytes(raw_value, byteorder="big"))) elif opcode_values.DUP1 <= op <= opcode_values.DUP16: depth = op - opcode_values.DUP1 + 1 state.stack_push(state.stack[-depth]) elif opcode_values.SWAP1 <= op <= opcode_values.SWAP16: depth = op - opcode_values.SWAP1 + 1 temp = state.stack[-depth - 1] state.stack[-depth - 1] = state.stack[-1] state.stack[-1] = temp elif opcode_values.LOG0 <= op <= opcode_values.LOG4: depth = op - opcode_values.LOG0 mstart, msz = (state.stack_pop(), state.stack_pop()) topics = [state.stack_pop() for x in range(depth)] elif op == opcode_values.SHA3: start, length = solution(state.stack_pop()), solution( state.stack_pop()) memory = state.memory.read(start, length) state.stack_push(Sha3(memory)) elif op == opcode_values.STOP: return True elif op == opcode_values.RETURN: return True elif op == opcode_values.CALLDATALOAD: index = state.stack_pop() try: index_sol = solution(index) except MultipleSolutionsError: state.stack_push(index) # restore the stack self.add_for_fuzzing(state, index, CALLDATALOAD_INDEX_FUZZ) return False state.stack_push(state.env.calldata.read(index_sol, 32)) elif op == opcode_values.CALLDATASIZE: state.stack_push(state.env.calldata_size) elif op == opcode_values.CALLDATACOPY: old_state = state.copy() mstart, dstart, size = ( state.stack_pop(), state.stack_pop(), state.stack_pop(), ) mstart, dstart = solution(mstart), solution(dstart) try: size = solution(size) except MultipleSolutionsError: self.add_for_fuzzing(old_state, size, CALLDATACOPY_SIZE_FUZZ) return False state.memory.copy_from(state.env.calldata, mstart, dstart, size) elif op == opcode_values.CODESIZE: state.stack_push(bvv(len(self.code))) elif op == opcode_values.EXTCODESIZE: addr = state.stack_pop() if (addr == state.env.address).is_true(): state.stack_push(bvv(len(self.code))) else: # TODO: Improve that... It's clearly not constraining enough. state.stack_push(claripy.BVS("EXTCODESIZE[%s]" % addr, 256)) elif op == opcode_values.EXTCODECOPY: old_state = state.copy() addr = state.stack_pop() mem_start = solution(state.stack_pop()) code_start = solution(state.stack_pop()) size = state.stack_pop() try: size = solution(size) except MultipleSolutionsError: # TODO: Fuzz. # self.add_for_fuzzing(old_state, size, []) # return False raise state.memory.write( mem_start, size, claripy.BVS("EXTCODE[%s from %s]" % (addr, code_start), size * 8), ) elif op == opcode_values.CODECOPY: mem_start, code_start, size = [ solution(state.stack_pop()) for _ in range(3) ] for i in range(size): if code_start + i < len(state.env.code): state.memory.write( mem_start + i, 1, claripy.BVV(state.env.code[code_start + i], 8), ) else: state.memory.write(mem_start + i, 1, claripy.BVV(0, 8)) elif op == opcode_values.MLOAD: index = solution(state.stack_pop()) state.stack_push(state.memory.read(index, 32)) elif op == opcode_values.MSTORE: index, value = solution(state.stack_pop()), state.stack_pop() state.memory.write(index, 32, value) elif op == opcode_values.MSTORE8: index, value = solution(state.stack_pop()), state.stack_pop() state.memory.write(index, 1, value[7:0]) elif op == opcode_values.MSIZE: state.stack_push(bvv(state.memory.size())) elif op == opcode_values.SLOAD: state.pc += 1 key = state.stack_pop() for w_key, w_value in state.storage_written.items(): read_written = [w_key == key] if state.solver.satisfiable( extra_constraints=read_written): new_state = state.copy() new_state.solver.add(read_written) new_state.stack_push(w_value) self.add_branch(new_state) state.solver.add(w_key != key) if state.solver.satisfiable(): assert key not in state.storage_written if key not in state.storage_read: state.storage_read[key] = claripy.BVS( "storage[%s]" % key, 256) state.stack_push(state.storage_read[key]) self.add_branch(state) return elif op == opcode_values.SSTORE: state.pc += 1 key = state.stack_pop() value = state.stack_pop() for w_key, w_value in state.storage_written.items(): read_written = [w_key == key] if state.solver.satisfiable( extra_constraints=read_written): new_state = state.copy() new_state.solver.add(read_written) new_state.storage_written[w_key] = value self.add_branch(new_state) state.solver.add(w_key != key) if state.solver.satisfiable(): assert key not in state.storage_written state.storage_written[key] = value self.add_branch(state) return elif op == opcode_values.CALL: state.pc += 1 # pylint:disable=unused-variable gas, to_, value, meminstart, meminsz, memoutstart, memoutsz = ( state.stack_pop() for _ in range(7)) # First possibility: the call fails # (always possible with a call stack big enough) state_fail = state.copy() state_fail.stack_push(BVV_0) self.add_branch(state_fail) # Second possibility: success. state.calls.append((memoutsz, memoutstart, meminsz, meminstart, value, to_, gas)) memoutsz = solution(memoutsz) if memoutsz != 0: # If we expect some output, let's constraint the call to # be to a contract that we do control. Otherwise it could # return anything... state.solver.add(to_[159:0] == utils.DEFAULT_CALLER[159:0]) memoutstart = solution(memoutstart) state.memory.write( memoutstart, memoutsz, claripy.BVS("CALL_RETURN[%s]" % to_, memoutsz * 8), ) state.stack_push(BVV_1) self.add_branch(state) return False elif op == opcode_values.DELEGATECALL: state.pc += 1 # pylint:disable=unused-variable gas, to_, meminstart, meminsz, memoutstart, memoutsz = ( state.stack_pop() for _ in range(6)) # First possibility: the call fails # (always possible with a call stack big enough) state_fail = state.copy() state_fail.stack_push(BVV_0) self.add_branch(state_fail) # If the call is to a specific contract we don't control, # don't assume it could return anything, or even be successful. # So we say we need to be able to call an arbitrary contract. state.solver.add(to_[159:0] == utils.DEFAULT_CALLER[159:0]) # Second possibility: success. state.calls.append( (memoutsz, memoutstart, meminsz, meminstart, to_, gas)) memoutsz = solution(memoutsz) if memoutsz != 0: memoutstart = solution(memoutstart) state.memory.write( memoutstart, memoutsz, claripy.BVS("DELEGATECALL_RETURN[%s]" % to_, memoutsz * 8), ) state.stack_push(BVV_1) self.add_branch(state) return False elif op == opcode_values.RETURNDATASIZE: state.stack_push(claripy.BVS("RETURNDATASIZE", 256)) elif op == opcode_values.RETURNDATACOPY: old_state = state.copy() mem_start_position = solution(state.stack_pop()) returndata_start_position = solution(state.stack_pop()) size = state.stack_pop() try: size = solution(size) except MultipleSolutionsError: self.add_for_fuzzing(old_state, size, RETURNDATACOPY_SIZE_FUZZ) return False state.memory.write(mem_start_position, size, claripy.BVS("RETURNDATACOPY", size * 8)) elif op == opcode_values.SELFDESTRUCT: state.selfdestruct_to = state.stack[-1] return True elif op == opcode_values.REVERT: return False else: raise utils.InterpreterError(state, "Unknown opcode %#x" % op) state.pc += 1