def __iter__(self): terminated = False index = 0 constraint = bl.Constant(True) while not terminated: read_address = self.address + bv.Constant(self.address.size, index) byte = self.state.read(read_address, 8) if self.state.solver.check(byte.can_be_nonzero()): if byte.symbolic: if not constraint.symbolic: constraint = (byte != 0) else: # not sure that I've implemented &= in smt constraint = constraint & (byte != 0) # this might look silly, but it actually makes the # output smt formulae substantially smaller... returned_constraint = bl.Symbol( unique_name('string_length')) self.state.solver.add(returned_constraint == constraint) yield (byte, returned_constraint) else: yield (byte, constraint) else: terminated = True yield (bv.Constant(8, 0), constraint) index += 1
def _load_registers(self, state): context = self._t.getRegisterContext(self._t.getCurrentThread()) state.ip = context.getProgramCounter() for reg in [ 'rax', 'rbx', 'rcx', 'rdx', 'rsi', 'rdi', 'rbp', 'rsp', 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15' ]: state.registers[reg] = bv.Constant(64, context.getRegisterByName(reg)) for reg in ['cf', 'pf', 'af', 'zf', 'sf', 'df', 'of']: state.registers[reg] = bv.Constant( 8, context.getRegisterByName(reg.upper())) # vdb doesn't appear to read xmm registers for reg in [ 'xmm0', 'xmm1', 'xmm2', 'xmm3', 'xmm4', 'xmm5', 'xmm6', 'xmm7', 'xmm8', 'xmm9', 'xmm10', 'xmm11', 'xmm12', 'xmm13', 'xmm14', 'xmm15' ]: state.registers[reg] = \ bv.Constant(128, context.getRegisterByName('x' + reg[1:]) & 0xffffffffffffffffffffffffffffffff) state.registers['fsbase'] = bv.Constant(64, self._get_fsbase()) state.registers['gsbase'] = bv.Constant(64, self._get_gsbase())
def append(self, c): if isinstance(c, str): c = bv.Constant(8, ord(c)) write_address = self.address + bv.Constant(self.address.size, self.index) self.index += 1 self.state.write(write_address, c)
def op_bisz(i, s): a = operand_value(s, i.input0) dst = i.output result = bv.if_then_else( a == bv.Constant(a.size, 0), bv.Constant(dst.size, 1), bv.Constant(dst.size, 0)) s.registers[dst.name] = result return [s]
def ret(self, value=None): return_address = bv.Constant(64, self.state.ip) # set return value (if set) if value is not None: if isinstance(value, int): self.state.registers['rax'] = bv.Constant(64, value) elif isinstance(value, str): self.state.registers['rax'] = bv.Symbol(64, unique_name(value)) else: self.state.registers['rax'] = value print 'returning to {}'.format(return_address) return self.state.branch(return_address)
def ret(self, value=None): # load return address, adjust stack pointer esp = self.state.registers['esp'] self.state.registers['esp'] = esp + bv.Constant(32, 4) return_address = self.state.read(esp, 32) # set return value (if set) if value is not None: if isinstance(value, int): self.state.registers['eax'] = bv.Constant(32, value) elif isinstance(value, str): self.state.registers['eax'] = bv.Symbol(32, unique_name(value)) else: self.state.registers['eax'] = value return self.state.branch(return_address)
def fetch_instruction(s, x86_64=False): ip = s.ip s.trace.append(ip) if ip not in _translation_cache: bs = [] for i in range(0, 128): try: bs.append(s.memory.read_byte(s, ip + i)) except IndexError(): break bs = ''.join(map(lambda x: chr(x.value), bs)) for i in x86.translate(bs, ip, x86_64): _translation_cache[i.address] = i if ip not in _translation_cache: raise InvalidExecution(s, ip) i = _translation_cache[ip] s.ip += i.size if x86_64: s.registers['rip'] = bv.Constant(64, s.ip) return i
def minimum(state, value): if not value.symbolic: return value absolute_max = -1 & mask(value.size) lower = 0 upper = absolute_max while (upper - lower) > 1: mid = (upper + lower) // 2 if state.solver.check(value < bv.Constant(value.size, mid)): upper = mid else: lower = mid return bv.Constant(value.size, lower)
def ret(self, value=None): # load return address, adjust stack pointer rsp = self.state.registers['rsp'] self.state.registers['rsp'] = rsp + bv.Constant(64, 8) return_address = self.state.read(rsp, 64) # set return value (if set) if value is not None: if isinstance(value, int) or isinstance(value, long): self.state.registers['rax'] = bv.Constant(64, value) elif isinstance(value, str): self.state.registers['rax'] = bv.Symbol(64, unique_name(value)) else: self.state.registers['rax'] = value return self.state.branch(return_address)
def sys_read(s, cc): f = cc(s) fd = f.params[0] buf = f.params[1] size = f.params[2] s.log.syscall(f, 'sys_read(fd={}, ptr={}, size={})', fd, buf, size) output = OutputBuffer(s, buf) if fd.symbolic: raise ValueError('wtf') if fd.value > len(s.files): return f.ret(value=0) else: file = s.files[fd.value] offset = file['offset'] output = OutputBuffer(s, buf) real_fd = None if file['path'] not in ['stdin', 'stdout', 'stderr']: real_fd = open(file['path'], 'rb') if size.symbolic: raise NotImplementedError() elif real_fd is None: for i in xrange(0, size.value): b = bv.Symbol(8, 'file_{}_{:x}'.format(fd.value, offset)) output.append(b) file['bytes'][offset] = b offset += 1 else: real_fd.seek(offset, 0) for i in range(0, size.value): byte = real_fd.read(1) if len(byte) == 1: if byte == '#': b = bv.Symbol(8, 'file_{}_{:x}'.format(fd.value, offset)) else: b = bv.Constant(8, ord(byte)) output.append(b) file['bytes'][offset] = b offset += 1 else: break file['offset'] = offset if real_fd is not None: real_fd.close() return f.ret(value=size)
def operand_value(s, o): output = o if isinstance(o, reil.ImmediateOperand): output = bv.Constant(o.size, o.value) elif isinstance(o, reil.RegisterOperand): output = s.registers[o.name] if output.size > o.size: output = output.extract(o.size) elif output.size < o.size: output = output.zero_extend_to(o.size) return output
def sys_write(s, cc): f = cc(s) fd = f.params[0] buf = f.params[1] size = f.params[2] s.log.syscall(f, 'sys_write(fd={}, buf={}, size={})', fd, buf, size) o = DummyOutputBuffer() if size.symbolic: raise NotImplementedError() else: for i in range(0, size.value): o.append(s.read(buf + bv.Constant(buf.size, i), 8)) output_string = o.string.strip('\r').strip('\n') s.log.output(output_string) return f.ret(value=bv.Constant(size.size, o.index))
def gets(s, cc): f = cc(s) buf = f.params[0] s.log.function_call(f, 'gets(buf={})', buf) output = OutputBuffer(s, buf) # TODO: this needs to use the file for stdin instead of this nonsense for i in xrange(0, 0x100000): byte = bv.Symbol(8, unique_name('stdin_{0}'.format(i))) s.files[0]['bytes'].append(byte) output.append(byte) s.files[0]['bytes'].append(bv.Constant(8, 0x0a)) output.append(bv.Constant(8, 0)) return f.ret(value=buf)
def _load_registers(self, state): context = self._t.getRegisterContext(self._t.getCurrentThread()) state.ip = context.getProgramCounter() for reg in ['eax', 'ebx', 'ecx', 'edx', 'esi', 'edi', 'ebp', 'esp']: state.registers[reg] = bv.Constant(32, context.getRegisterByName(reg)) for reg in ['cf', 'pf', 'af', 'zf', 'sf', 'df', 'of']: state.registers[reg] = bv.Constant( 8, context.getRegisterByName(reg.upper())) # vdb doesn't appear to read xmm registers... for reg in [ 'xmm0', 'xmm1', 'xmm2', 'xmm3', 'xmm4', 'xmm5', 'xmm6', 'xmm7' ]: state.registers[reg] = bv.Constant(128, context.getRegisterByName(reg)) state.registers['gsbase'] = bv.Constant(32, self._get_gsbase())
def read_byte(self, state, address): try: return self._cache[address] except KeyError: for b, l, d in self._pages: if b <= address < l: value = bv.Constant(8, ord(d[address - b])) self._cache[address] = value return value state.throw(InvalidRead(state, address))
def fread(s, cc): f = cc(s) buf = f.params[0] size = f.params[1] count = f.params[2] stream = f.params[3] s.log.function_call(f, 'fread(ptr={}, size={}, count={}, stream={})', buf, size, count, stream) if stream.symbolic: raise ValueError('wtf') if stream.value > len(s.files): return f.ret(value=0) else: file = s.files[stream.value] offset = file['offset'] output = OutputBuffer(s, buf) fd = None if file['path'] not in ['stdin', 'stdout', 'stderr']: fd = open(file['path'], 'rb') if size.symbolic or count.symbolic: raise NotImplementedError() elif fd is None: for i in xrange(0, size.value * count.value): output.append(bv.Symbol(8, 'file_{}_{:x}'.format(stream.value, offset))) offset += 1 else: fd.seek(offset, 0) for i in range(0, size.value * count.value): byte = fd.read(1) if len(byte) == 1: if byte == '#': output.append(bv.Symbol(8, 'file_{}_{:x}'.format(stream.value, offset))) else: output.append(bv.Constant(8, ord(byte))) offset += 1 else: break file['offset'] = offset if fd is not None: fd.close() return f.ret(value=output.index)
def _parse_model(self, results, expr=None): output = dict() expressions = list(self.roots()) if expr is not None: self._cache(expr) expressions.append(expr) symbols = set() for e in expressions: symbols.update(e.symbols()) for bl_match in self.bl_re.findall(results): name = bl_match[0] if bl_match[1] == 'true': value = True else: value = False output[name] = bl.Constant(value) for bv_match in self.bv_re.findall(results): name = bv_match[0] size = int(bv_match[1]) value = int('0x' + bv_match[2], 16) output[name] = bv.Constant(size, value) for symbol in symbols: if symbol.name not in output: if isinstance(symbol, bv.Expression): output[symbol.name] = bv.Constant(symbol.size, 0x2323232323232323) else: output[symbol.name] = bl.Constant(True) return output
def run(states): while not exit_event.is_set(): active_threads.acquire() while available_states.acquire(blocking=False): try: s = states.pop() ns = emulator.single_step(s, x86_64) for n in ns: if culling_function is not None: if culling_function(n): continue if scoring_function is not None: n.score = scoring_function(n) states.append(n) states.sort(key=lambda x: x.score) else: states.append(n) available_states.release() except StateException, v: v.state.log.vulnerability(v) v.state.log.debug('saving vuln state {}'.format( v.state.id)) serialisation.save('vuln_state_{}'.format(v.state.id), v) data = '' if isinstance(v, ArbitraryRead): v.state.solver.add(v.address == bv.Constant( v.address.size, 0xc01db33f)) s = v.state m = v.state.solver.model() for i in range(0, 0x4000): name = 'ttf_{:x}'.format(i) if name in m: data += chr(m[name].value) else: data += '#' print colored(data, 'white', 'on_red', attrs=['bold']) print data.encode('hex') with open('font_{}.ttf'.format(v.state.id), 'wb') as tmp: tmp.write(data) active_threads.release() time.sleep(1.0)
def calloc(s, cc): f = cc(s) size = f.params[0] count = f.params[1] if size.symbolic or count.symbolic: raise NotImplementedError() else: ptr = s.memory.allocate(s, size.value * count.value) zero = bv.Constant(8, 0) for i in xrange(0, size.value * count.value): s.memory.write_byte(s, ptr + i, zero) s.log.function_call(f, 'calloc(size={}, count={}) [{:x}]', size, count, ptr) return f.ret(value=ptr)
def malloc(s, cc): f = cc(s) size = f.params[0] ss = [] sizes = [] if size.symbolic: min_size = minimum(s, size) max_size = maximum(s, size) sizes.append(min_size.value) if min_size.value != max_size.value: sizes.append(max_size.value) else: sizes.append(size.value) # TODO: decide if this is worthwhile # insert malloc failure sizes.append(None) ss = [] total_sizes = len(sizes) while len(sizes) > 0: size_ = sizes.pop() if total_sizes > 1: s_ = s.fork() else: s_ = s if size_ is None: ptr = 0 s_.log.function_call(f, 'malloc(<FAIL>) [NULL]') else: s_.solver.add(size == bv.Constant(size.size, size_)) ptr = s_.memory.allocate(s_, size_) s_.log.function_call(f, 'malloc(size={:x}) [{:x}]', size_, ptr) f_ = cc(s_) ss += f_.ret(value=ptr) return ss
def memset(s, cc): f = cc(s) dst = f.params[0] val = f.params[1].resize(8) count = f.params[2] if dst.symbolic: if arbitrary(s, dst): raise ArbitraryWrite(s, dst, val) counts = [] if count.symbolic: min_count = minimum(s, count) max_count = maximum(s, count) counts.append(min_count.value) if min_count.value != max_count.value: counts.append(max_count.value) else: counts.append(count.value) ss = [] total_counts = len(counts) while len(counts) > 0: count_ = counts.pop() if total_counts > 1: s_ = s.fork() else: s_ = s s_.solver.add(count == bv.Constant(count.size, count_)) s_.log.function_call(f, 'memset(dst={}, val={}, count={:x})', dst, val, count_) s_.memory.bulk_set(s_, dst.value, count_, val) f_ = cc(s_) ss += f_.ret(value=dst) return ss
def op_bsh(i, s): a = operand_value(s, i.input0) b = operand_value(s, i.input1) dst = i.output operation_size = max(a.size, b.size, dst.size) a = a.resize(operation_size) b = b.resize(operation_size) # TODO: support symbolic shifts if b.value >= 0: result = a << b else: result = a >> bv.Constant(b.size, abs(b.value)) result = result.resize(dst.size) s.registers[dst.name] = result return [s]
def sys_mmap(s, cc): f = cc(s) addr = f.params[0] length = f.params[1] prot = f.params[2] flags = f.params[3] fd = f.params[4] offset = f.params[5] s.log.syscall( f, 'sys_mmap(addr={}, length={}, prot={}, flags={}, fd={}, offset={})', addr, length, prot, flags, fd, offset) if length.symbolic: raise NotImplementedError() ptr = bv.Constant(addr.size, s.memory.allocate(s, length.value)) return f.ret(value=ptr)
def realloc(s, cc): f = cc(s) ptr = f.params[0] size = f.params[1] if ptr.symbolic: raise NotImplementedError() sizes = [] if size.symbolic: min_size = minimum(s, size) max_size = maximum(s, size) sizes.append(min_size.value) if min_size.value != max_size.value: sizes.append(max_size.value) else: sizes.append(size.value) ss = [] total_sizes = len(sizes) while len(sizes) > 0: size_ = sizes.pop() if total_sizes > 1: s_ = s.fork() else: s_ = s s_.solver.add(size == bv.Constant(size.size, size_)) ptr_ = s_.memory.reallocate(s_, ptr.value, size_) s_.log.function_call(f, 'realloc(ptr={}, size={:x}) [{:x}]', ptr, size_, ptr_) f_ = cc(s_) ss += f_.ret(value=ptr_) return ss
def return_address(self): esp = self.state.registers['esp'] return self.state.read(esp + bv.Constant(32, 12), 32)
def __getitem__(self, index): return self.state.read(self.address + bv.Constant(32, (4 * index)), 32)
def arbitrary(state, value): if state.solver.check(value == bv.Constant(value.size, 0xc01db33f)): return True else: return False
def __getitem__(self, index): esp = self.state.registers['esp'] param_address = esp + bv.Constant(32, 4 + (4 * index)) return self.state.read(param_address, 32)
def va_args(self, index): esp = self.state.registers['esp'] address = esp + bv.Constant(32, 4 + (4 * index)) return self.VaArgs(self.state, address)
def return_address(self): return bv.Constant(64, self.state.ip)