(self, va_arg, addr=None, simfd=None)
return num_args - failed
implement scanf - extract formatted data from memory or a file according to the stored format
        specifiers and store them into the pointers extracted from `args`.

        :param va_arg:      A function which, given a type, returns the next argument of that type
        :param addr:        The address in the memory to extract data from, or...
        :param simfd:       A file descriptor to use for reading data from
        :return:            The number of arguments parsed
implement scanf - extract formatted data from memory or a file according to the stored format
        specifiers and store them into the pointers extracted from `args`.
implement scanf - extract formatted data from memory or a file according to the stored format specifiers and store them into the pointers extracted from args .
def interpret(self, va_arg, addr=None, simfd=None):
        """
        implement scanf - extract formatted data from memory or a file according to the stored format
        specifiers and store them into the pointers extracted from `args`.

        :param va_arg:      A function which, given a type, returns the next argument of that type
        :param addr:        The address in the memory to extract data from, or...
        :param simfd:       A file descriptor to use for reading data from
        :return:            The number of arguments parsed
        """
        num_args = 0
        if simfd is not None and isinstance(simfd.read_storage, SimPackets):
            for component in self.components:
                if type(component) is bytes:
                    sdata, _ = simfd.read_data(len(component), short_reads=False)
                    self.state.add_constraints(sdata == component)
                elif isinstance(component, claripy.Bits):
                    sdata, _ = simfd.read_data(len(component) // 8, short_reads=False)
                    self.state.add_constraints(sdata == component)
                elif component.spec_type == b's':
                    if component.length_spec is None:
                        sdata, slen = simfd.read_data(self.state.libc.buf_symbolic_bytes)
                    else:
                        sdata, slen = simfd.read_data(component.length_spec)
                    for byte in sdata.chop(8):
                        self.state.add_constraints(claripy.And(*[byte != char for char in self.SCANF_DELIMITERS]))
                    ptr = va_arg('char*')
                    self.state.memory.store(ptr, sdata, size=slen)
                    self.state.memory.store(ptr + slen, claripy.BVV(0, 8))
                    num_args += 1
                elif component.spec_type == b'c':
                    sdata, _ = simfd.read_data(1, short_reads=False)
                    self.state.memory.store(va_arg('char*'), sdata)
                    num_args += 1
                else:
                    bits = component.size * 8
                    if component.spec_type == b'x':
                        base = 16
                    elif component.spec_type == b'o':
                        base = 8
                    else:
                        base = 10

                    # here's the variable representing the result of the parsing
                    target_variable = self.state.solver.BVS('scanf_' + component.string.decode(), bits,
                            key=('api', 'scanf', num_args, component.string))
                    negative = claripy.SLT(target_variable, 0)

                    # how many digits does it take to represent this variable fully?
                    max_digits = int(math.ceil(math.log(2**bits, base)))

                    # how many digits does the format specify?
                    spec_digits = component.length_spec

                    # how many bits can we specify as input?
                    available_bits = float('inf') if spec_digits is None else spec_digits * math.log(base, 2)
                    not_enough_bits = available_bits < bits

                    # how many digits will we model this input as?
                    digits = max_digits if spec_digits is None else spec_digits

                    # constrain target variable range explicitly if it can't take on all possible values
                    if not_enough_bits:
                        self.state.add_constraints(self.state.solver.And(
                            self.state.solver.SLE(target_variable, (base**digits) - 1),
                            self.state.solver.SGE(target_variable, -(base**(digits - 1) - 1))))

                    # perform the parsing in reverse - constrain the input digits to be the string version of the input
                    # this only works because we're reading from a packet stream and therefore nobody has the ability
                    # to add other constraints to this data!
                    # this makes z3's job EXTREMELY easy
                    sdata, _ = simfd.read_data(digits, short_reads=False)
                    for i, digit in enumerate(reversed(sdata.chop(8))):
                        digit_value = (target_variable // (base**i)) % base
                        digit_ascii = digit_value + ord('0')
                        if base > 10:
                            digit_ascii = claripy.If(digit_value >= 10, digit_value + (-10 + ord('a')), digit_ascii)

                        # if there aren't enough bits, we can increase the range by accounting for the possibility that
                        # the first digit is a minus sign
                        if not_enough_bits:
                            if i == digits - 1:
                                neg_digit_ascii = ord('-')
                            else:
                                neg_digit_value = (-target_variable // (base**i)) % base
                                neg_digit_ascii = neg_digit_value + ord('0')
                                if base > 10:
                                    neg_digit_ascii = claripy.If(neg_digit_value >= 10, neg_digit_value + (-10 + ord('a')), neg_digit_ascii)

                            digit_ascii = claripy.If(negative, neg_digit_ascii, digit_ascii)

                        self.state.add_constraints(digit == digit_ascii[7:0])

                    # again, a cheap hack
                    self.state.memory.store(va_arg('void*'), target_variable, endness=self.state.arch.memory_endness)
                    num_args += 1

            return num_args

        if simfd is not None:
            region = simfd.read_storage
            addr = simfd._pos if hasattr(simfd, '_pos') else simfd._read_pos # XXX THIS IS BAD
        else:
            region = self.parser.state.memory

        bits = self.parser.state.arch.bits
        failed = self.parser.state.solver.BVV(0, 32)
        position = addr
        for component in self.components:
            if isinstance(component, bytes):
                # TODO we skip non-format-specifiers in format string interpretation for now
                # if the region doesn't match the concrete component, we need to return immediately
                pass
            else:
                fmt_spec = component
                try:
                    dest = va_arg('void*')
                except SimProcedureArgumentError:
                    dest = None
                if fmt_spec.spec_type == b's':
                    # set some limits for the find
                    max_str_len = self.parser.state.libc.max_str_len
                    max_sym_bytes = self.parser.state.libc.buf_symbolic_bytes

                    # has the length of the format been limited by the string itself?
                    if fmt_spec.length_spec is not None:
                        max_str_len = fmt_spec.length_spec
                        max_sym_bytes = fmt_spec.length_spec

                    # TODO: look for limits on other characters which scanf is sensitive to, '\x00', '\x20'
                    _, _, match_indices = region.find(position, self.parser.state.solver.BVV(b'\n'), max_str_len,
                                            max_symbolic_bytes=max_sym_bytes)

                    if not match_indices:
                        # if no newline is found, mm is position + max_strlen
                        mm = position + max_str_len
                        # we're just going to concretize the length, load will do this anyways
                        length = self.parser.state.solver.max_int(mm - position)
                    else:
                        # a newline is found, or a max length is specified with the specifier
                        length = max(match_indices)
                    src_str = region.load(position, length)

                    # TODO all of these should be delimiters we search for above
                    # add that the contents of the string cannot be any scanf %s string delimiters
                    for delimiter in set(FormatString.SCANF_DELIMITERS):
                        delim_bvv = self.parser.state.solver.BVV(delimiter)
                        for i in range(length):
                            self.parser.state.add_constraints(region.load(position + i, 1) != delim_bvv)

                    # write it out to the pointer
                    self.parser.state.memory.store(dest, src_str)
                    # store the terminating null byte
                    self.parser.state.memory.store(dest + length, self.parser.state.solver.BVV(0, 8))

                    position += length

                else:

                    # XXX: atoi only supports strings of one byte
                    if fmt_spec.spec_type in [b'd', b'i', b'u', b'x']:
                        base = 16 if fmt_spec.spec_type == b'x' else 10
                        status, i, num_bytes = self.parser._sim_atoi_inner(position, region, base=base, read_length=fmt_spec.length_spec)
                        # increase failed count if we were unable to parse it
                        failed = self.parser.state.solver.If(status, failed, failed + 1)
                        position += num_bytes
                    elif fmt_spec.spec_type == b'c':
                        i = region.load(position, 1)
                        i = i.zero_extend(bits - 8)
                        position += 1
                    else:
                        raise SimProcedureError("unsupported format spec '%s' in interpret" % fmt_spec.spec_type)

                    i = self.parser.state.solver.Extract(fmt_spec.size*8-1, 0, i)
                    self.parser.state.memory.store(dest, i, size=fmt_spec.size, endness=self.parser.state.arch.memory_endness)

                num_args += 1

        if simfd is not None:
            _, realsize = simfd.read_data(position - addr)
            self.state.add_constraints(realsize == position - addr)

        return num_args - failed
def interpret ( self , va_arg , addr = None , simfd = None ) : num_args = 0 if simfd is not None and isinstance ( simfd . read_storage , SimPackets ) : for component in self . components : if type ( component ) is bytes : sdata , _ = simfd . read_data ( len ( component ) , short_reads = False ) self . state . add_constraints ( sdata == component ) elif isinstance ( component , claripy . Bits ) : sdata , _ = simfd . read_data ( len ( component ) // 8 , short_reads = False ) self . state . add_constraints ( sdata == component ) elif component . spec_type == b's' : if component . length_spec is None : sdata , slen = simfd . read_data ( self . state . libc . buf_symbolic_bytes ) else : sdata , slen = simfd . read_data ( component . length_spec ) for byte in sdata . chop ( 8 ) : self . state . add_constraints ( claripy . And ( * [ byte != char for char in self . SCANF_DELIMITERS ] ) ) ptr = va_arg ( 'char*' ) self . state . memory . store ( ptr , sdata , size = slen ) self . state . memory . store ( ptr + slen , claripy . BVV ( 0 , 8 ) ) num_args += 1 elif component . spec_type == b'c' : sdata , _ = simfd . read_data ( 1 , short_reads = False ) self . state . memory . store ( va_arg ( 'char*' ) , sdata ) num_args += 1 else : bits = component . size * 8 if component . spec_type == b'x' : base = 16 elif component . spec_type == b'o' : base = 8 else : base = 10 # here's the variable representing the result of the parsing target_variable = self . state . solver . BVS ( 'scanf_' + component . string . decode ( ) , bits , key = ( 'api' , 'scanf' , num_args , component . string ) ) negative = claripy . SLT ( target_variable , 0 ) # how many digits does it take to represent this variable fully? max_digits = int ( math . ceil ( math . log ( 2 ** bits , base ) ) ) # how many digits does the format specify? spec_digits = component . length_spec # how many bits can we specify as input? available_bits = float ( 'inf' ) if spec_digits is None else spec_digits * math . log ( base , 2 ) not_enough_bits = available_bits < bits # how many digits will we model this input as? digits = max_digits if spec_digits is None else spec_digits # constrain target variable range explicitly if it can't take on all possible values if not_enough_bits : self . state . add_constraints ( self . state . solver . And ( self . state . solver . SLE ( target_variable , ( base ** digits ) - 1 ) , self . state . solver . SGE ( target_variable , - ( base ** ( digits - 1 ) - 1 ) ) ) ) # perform the parsing in reverse - constrain the input digits to be the string version of the input # this only works because we're reading from a packet stream and therefore nobody has the ability # to add other constraints to this data! # this makes z3's job EXTREMELY easy sdata , _ = simfd . read_data ( digits , short_reads = False ) for i , digit in enumerate ( reversed ( sdata . chop ( 8 ) ) ) : digit_value = ( target_variable // ( base ** i ) ) % base digit_ascii = digit_value + ord ( '0' ) if base > 10 : digit_ascii = claripy . If ( digit_value >= 10 , digit_value + ( - 10 + ord ( 'a' ) ) , digit_ascii ) # if there aren't enough bits, we can increase the range by accounting for the possibility that # the first digit is a minus sign if not_enough_bits : if i == digits - 1 : neg_digit_ascii = ord ( '-' ) else : neg_digit_value = ( - target_variable // ( base ** i ) ) % base neg_digit_ascii = neg_digit_value + ord ( '0' ) if base > 10 : neg_digit_ascii = claripy . If ( neg_digit_value >= 10 , neg_digit_value + ( - 10 + ord ( 'a' ) ) , neg_digit_ascii ) digit_ascii = claripy . If ( negative , neg_digit_ascii , digit_ascii ) self . state . add_constraints ( digit == digit_ascii [ 7 : 0 ] ) # again, a cheap hack self . state . memory . store ( va_arg ( 'void*' ) , target_variable , endness = self . state . arch . memory_endness ) num_args += 1 return num_args if simfd is not None : region = simfd . read_storage addr = simfd . _pos if hasattr ( simfd , '_pos' ) else simfd . _read_pos # XXX THIS IS BAD else : region = self . parser . state . memory bits = self . parser . state . arch . bits failed = self . parser . state . solver . BVV ( 0 , 32 ) position = addr for component in self . components : if isinstance ( component , bytes ) : # TODO we skip non-format-specifiers in format string interpretation for now # if the region doesn't match the concrete component, we need to return immediately pass else : fmt_spec = component try : dest = va_arg ( 'void*' ) except SimProcedureArgumentError : dest = None if fmt_spec . spec_type == b's' : # set some limits for the find max_str_len = self . parser . state . libc . max_str_len max_sym_bytes = self . parser . state . libc . buf_symbolic_bytes # has the length of the format been limited by the string itself? if fmt_spec . length_spec is not None : max_str_len = fmt_spec . length_spec max_sym_bytes = fmt_spec . length_spec # TODO: look for limits on other characters which scanf is sensitive to, '\x00', '\x20' _ , _ , match_indices = region . find ( position , self . parser . state . solver . BVV ( b'\n' ) , max_str_len , max_symbolic_bytes = max_sym_bytes ) if not match_indices : # if no newline is found, mm is position + max_strlen mm = position + max_str_len # we're just going to concretize the length, load will do this anyways length = self . parser . state . solver . max_int ( mm - position ) else : # a newline is found, or a max length is specified with the specifier length = max ( match_indices ) src_str = region . load ( position , length ) # TODO all of these should be delimiters we search for above # add that the contents of the string cannot be any scanf %s string delimiters for delimiter in set ( FormatString . SCANF_DELIMITERS ) : delim_bvv = self . parser . state . solver . BVV ( delimiter ) for i in range ( length ) : self . parser . state . add_constraints ( region . load ( position + i , 1 ) != delim_bvv ) # write it out to the pointer self . parser . state . memory . store ( dest , src_str ) # store the terminating null byte self . parser . state . memory . store ( dest + length , self . parser . state . solver . BVV ( 0 , 8 ) ) position += length else : # XXX: atoi only supports strings of one byte if fmt_spec . spec_type in [ b'd' , b'i' , b'u' , b'x' ] : base = 16 if fmt_spec . spec_type == b'x' else 10 status , i , num_bytes = self . parser . _sim_atoi_inner ( position , region , base = base , read_length = fmt_spec . length_spec ) # increase failed count if we were unable to parse it failed = self . parser . state . solver . If ( status , failed , failed + 1 ) position += num_bytes elif fmt_spec . spec_type == b'c' : i = region . load ( position , 1 ) i = i . zero_extend ( bits - 8 ) position += 1 else : raise SimProcedureError ( "unsupported format spec '%s' in interpret" % fmt_spec . spec_type ) i = self . parser . state . solver . Extract ( fmt_spec . size * 8 - 1 , 0 , i ) self . parser . state . memory . store ( dest , i , size = fmt_spec . size , endness = self . parser . state . arch . memory_endness ) num_args += 1 if simfd is not None : _ , realsize = simfd . read_data ( position - addr ) self . state . add_constraints ( realsize == position - addr ) return num_args - failed