def get(self):
        """ Dequeue a state with the max priority """

        # A shutdown has been requested
        if self.is_shutdown():
            return None

        # if not more states in the queue, let's wait for some forks
        while len(self._states) == 0:
            # if no worker is running, bail out
            if self.running == 0:
                return None
            # if a shutdown has been requested, bail out
            if self.is_shutdown():
                return None
            # if there ares actually some workers running, wait for state forks
            logger.debug("Waiting for available states")
            self._lock.wait()

        state_id = self._policy.choice(list(self._states))
        if state_id is None:
            return None
        del self._states[self._states.index(state_id)]
        return state_id


def fork(self, state, expression, policy='ALL', setstate=None):
        """
        Fork state on expression concretizations.
        Using policy build a list of solutions for expression.
        For the state on each solution setting the new state with setstate

        For example if expression is a Bool it may have 2 solutions. True or False.

                                 Parent
                            (expression = ??)

                   Child1                         Child2
            (expression = True)             (expression = True)
               setstate(True)                   setstate(False)

        The optional setstate() function is supposed to set the concrete value
        in the child state.

        """
        assert isinstance(expression, Expression)

        if setstate is None:
            setstate = lambda x, y: None

        # Find a set of solutions for expression
        solutions = state.concretize(expression, policy)

        if not solutions:
            raise ExecutorError("Forking on unfeasible constraint set")

        if len(solutions) == 1:
            setstate(state, solutions[0])
            return state

        logger.info("Forking. Policy: %s. Values: %s",
                    policy,
                    ', '.join(f'0x{sol:x}' for sol in solutions))

        self._publish('will_fork_state', state, expression, solutions, policy)

        # Build and enqueue a state for each solution
        children = []
        for new_value in solutions:
            with state as new_state:
                new_state.constrain(expression == new_value)

                # and set the PC of the new state to the concrete pc-dest
                #(or other register or memory address to concrete)
                setstate(new_state, new_value)

                self._publish('did_fork_state', new_state, expression, new_value, policy)

                # enqueue new_state
                state_id = self.enqueue(new_state)
                # maintain a list of children for logging purpose
                children.append(state_id)

        logger.info("Forking current state into states %r", children)
        return None

Fork state on expression concretizations . Using policy build a list of solutions for expression . For the state on each solution setting the new state with setstate


def run(self):
        """
        Entry point of the Executor; called by workers to start analysis.
        """
        # policy_order=self.policy_order
        # policy=self.policy
        current_state = None
        current_state_id = None

        with WithKeyboardInterruptAs(self.shutdown):
            # notify siblings we are about to start a run
            self._notify_start_run()

            logger.debug("Starting Manticore Symbolic Emulator Worker (pid %d).", os.getpid())
            solver = Z3Solver()
            while not self.is_shutdown():
                try:  # handle fatal errors: exceptions in Manticore
                    try:  # handle external (e.g. solver) errors, and executor control exceptions
                        # select a suitable state to analyze
                        if current_state is None:
                            with self._lock:
                                # notify siblings we are about to stop this run
                                self._notify_stop_run()
                                try:
                                    # Select a single state_id
                                    current_state_id = self.get()
                                    # load selected state from secondary storage
                                    if current_state_id is not None:
                                        self._publish('will_load_state', current_state_id)
                                        current_state = self._workspace.load_state(current_state_id)
                                        self.forward_events_from(current_state, True)
                                        self._publish('did_load_state', current_state, current_state_id)
                                        logger.info("load state %r", current_state_id)
                                    # notify siblings we have a state to play with
                                finally:
                                    self._notify_start_run()

                        # If current_state is still None. We are done.
                        if current_state is None:
                            logger.debug("No more states in the queue, byte bye!")
                            break

                        assert current_state is not None
                        assert current_state.constraints is current_state.platform.constraints

                        # Allows to terminate manticore worker on user request
                        while not self.is_shutdown():
                            if not current_state.execute():
                                break
                        else:
                            # Notify this worker is done
                            self._publish('will_terminate_state', current_state, current_state_id, TerminateState('Shutdown'))
                            current_state = None

                    # Handling Forking and terminating exceptions
                    except Concretize as e:
                        # expression
                        # policy
                        # setstate()
                        logger.debug("Generic state fork on condition")
                        current_state = self.fork(current_state, e.expression, e.policy, e.setstate)

                    except TerminateState as e:
                        # Notify this worker is done
                        self._publish('will_terminate_state', current_state, current_state_id, e)

                        logger.debug("Generic terminate state")
                        if e.testcase:
                            self._publish('internal_generate_testcase', current_state, message=str(e))
                        current_state = None

                    except SolverError as e:
                        # raise
                        import traceback
                        trace = traceback.format_exc()
                        logger.error("Exception: %s\n%s", str(e), trace)

                        # Notify this state is done
                        self._publish('will_terminate_state', current_state, current_state_id, e)

                        if solver.check(current_state.constraints):
                            self._publish('internal_generate_testcase', current_state, message="Solver failed" + str(e))
                        current_state = None

                except (Exception, AssertionError) as e:
                    # raise
                    import traceback
                    trace = traceback.format_exc()
                    logger.error("Exception: %s\n%s", str(e), trace)
                    # Notify this worker is done
                    self._publish('will_terminate_state', current_state, current_state_id, e)
                    current_state = None

            assert current_state is None or self.is_shutdown()

            # notify siblings we are about to stop this run
            self._notify_stop_run()

Entry point of the Executor ; called by workers to start analysis .


def linux(cls, path, argv=None, envp=None, entry_symbol=None, symbolic_files=None, concrete_start='', pure_symbolic=False, stdin_size=None, **kwargs):
        """
        Constructor for Linux binary analysis.

        :param str path: Path to binary to analyze
        :param argv: Arguments to provide to the binary
        :type argv: list[str]
        :param envp: Environment to provide to the binary
        :type envp: dict[str, str]
        :param entry_symbol: Entry symbol to resolve to start execution
        :type envp: str
        :param symbolic_files: Filenames to mark as having symbolic input
        :type symbolic_files: list[str]
        :param str concrete_start: Concrete stdin to use before symbolic input
        :param int stdin_size: symbolic stdin size to use
        :param kwargs: Forwarded to the Manticore constructor
        :return: Manticore instance, initialized with a Linux State
        :rtype: Manticore
        """
        if stdin_size is None:
            stdin_size = consts.stdin_size

        try:
            return cls(_make_linux(path, argv, envp, entry_symbol, symbolic_files, concrete_start, pure_symbolic, stdin_size), **kwargs)
        except elftools.common.exceptions.ELFError:
            raise Exception(f'Invalid binary: {path}')

def
linux
(
cls
,
path
,
argv
=
,
envp
=
,
entry_symbol
=
,
symbolic_files
=
,
concrete_start
=
''
,
pure_symbolic
=
False
,
stdin_size
=
,
*
*
kwargs
)
:
if
stdin_size
is
:
stdin_size
=
consts
.
stdin_size
try
:
return
cls
(
_make_linux
(
path
,
argv
,
envp
,
entry_symbol
,
symbolic_files
,
concrete_start
,
pure_symbolic
,
stdin_size
)
,
*
*
kwargs
)
except
elftools
.
common
.
exceptions
.
ELFError
:
raise
Exception
(
f'Invalid binary: {path}'
)


def decree(cls, path, concrete_start='', **kwargs):
        """
        Constructor for Decree binary analysis.

        :param str path: Path to binary to analyze
        :param str concrete_start: Concrete stdin to use before symbolic input
        :param kwargs: Forwarded to the Manticore constructor
        :return: Manticore instance, initialized with a Decree State
        :rtype: Manticore
        """
        try:
            return cls(_make_decree(path, concrete_start), **kwargs)
        except KeyError:  # FIXME(mark) magic parsing for DECREE should raise better error
            raise Exception(f'Invalid binary: {path}')

Constructor for Decree binary analysis .


def _hook_callback(self, state, pc, instruction):
        'Invoke all registered generic hooks'

        # Ignore symbolic pc.
        # TODO(yan): Should we ask the solver if any of the hooks are possible,
        # and execute those that are?

        if issymbolic(pc):
            return

        # Invoke all pc-specific hooks
        for cb in self._hooks.get(pc, []):
            cb(state)

        # Invoke all pc-agnostic hooks
        for cb in self._hooks.get(None, []):
            cb(state)


def resolve(self, symbol):
        """
        A helper method used to resolve a symbol name into a memory address when
        injecting hooks for analysis.

        :param symbol: function name to be resolved
        :type symbol: string

        :param line: if more functions present, optional line number can be included
        :type line: int or None
        """

        with open(self.binary_path, 'rb') as f:

            elffile = ELFFile(f)

            # iterate over sections and identify symbol table section
            for section in elffile.iter_sections():
                if not isinstance(section, SymbolTableSection):
                    continue

                # get list of symbols by name
                symbols = section.get_symbol_by_name(symbol)
                if not symbols:
                    continue

                # return first indexed memory address for the symbol,
                return symbols[0].entry['st_value']

            raise ValueError(f"The {self.binary_path} ELFfile does not contain symbol {symbol}")

A helper method used to resolve a symbol name into a memory address when injecting hooks for analysis .


def binary_arch(binary):
    """
    helper method for determining binary architecture

    :param binary: str for binary to introspect.
    :rtype bool: True for x86_64, False otherwise
    """

    with open(binary, 'rb') as f:
        elffile = ELFFile(f)
        if elffile['e_machine'] == 'EM_X86_64':
            return True
        else:
            return False

def binary_arch ( binary ) : with open ( binary , 'rb' ) as f : elffile = ELFFile ( f ) if elffile [ 'e_machine' ] == 'EM_X86_64' : return True else : return False


def binary_symbols(binary):
    """
    helper method for getting all binary symbols with SANDSHREW_ prepended.
    We do this in order to provide the symbols Manticore should hook on to
    perform main analysis.

    :param binary: str for binary to instrospect.
    :rtype list: list of symbols from binary
    """

    def substr_after(string, delim):
        return string.partition(delim)[2]

    with open(binary, 'rb') as f:
        elffile = ELFFile(f)

        for section in elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue

            symbols = [sym.name for sym in section.iter_symbols() if sym]
            return [substr_after(name, PREPEND_SYM) for name in symbols
                    if name.startswith(PREPEND_SYM)]

def binary_symbols ( binary ) : def substr_after ( string , delim ) : return string . partition ( delim ) [ 2 ] with open ( binary , 'rb' ) as f : elffile = ELFFile ( f ) for section in elffile . iter_sections ( ) : if not isinstance ( section , SymbolTableSection ) : continue symbols = [ sym . name for sym in section . iter_symbols ( ) if sym ] return [ substr_after ( name , PREPEND_SYM ) for name in symbols if name . startswith ( PREPEND_SYM ) ]

helper method for getting all binary symbols with SANDSHREW_ prepended . We do this in order to provide the symbols Manticore should hook on to perform main analysis .


def get_group(name: str) -> _Group:
    """
    Get a configuration variable group named |name|
    """
    global _groups

    if name in _groups:
        return _groups[name]

    group = _Group(name)
    _groups[name] = group

    return group

def get_group ( name : str ) -> _Group : global _groups if name in _groups : return _groups [ name ] group = _Group ( name ) _groups [ name ] = group return group


def save(f):
    """
    Save current config state to an yml file stream identified by |f|

    :param f: where to write the config file
    """
    global _groups

    c = {}
    for group_name, group in _groups.items():
        section = {var.name: var.value for var in group.updated_vars()}
        if not section:
            continue
        c[group_name] = section

    yaml.safe_dump(c, f, line_break=True)

def save ( f ) : global _groups c = { } for group_name , group in _groups . items ( ) : section = { var . name : var . value for var in group . updated_vars ( ) } if not section : continue c [ group_name ] = section yaml . safe_dump ( c , f , line_break = True )


def parse_config(f):
    """
    Load an yml-formatted configuration from file stream |f|

    :param file f: Where to read the config.
    """

    try:
        c = yaml.safe_load(f)
        for section_name, section in c.items():
            group = get_group(section_name)

            for key, val in section.items():
                group.update(key)
                setattr(group, key, val)
    # Any exception here should trigger the warning; from not being able to parse yaml
    # to reading poorly formatted values
    except Exception:
        raise ConfigError("Failed reading config file. Do you have a local [.]manticore.yml file?")

Load an yml - formatted configuration from file stream |f|


def load_overrides(path=None):
    """
    Load config overrides from the yml file at |path|, or from default paths. If a path
    is provided and it does not exist, raise an exception

    Default paths: ./mcore.yml, ./.mcore.yml, ./manticore.yml, ./.manticore.yml.
    """

    if path is not None:
        names = [path]
    else:
        possible_names = ['mcore.yml', 'manticore.yml']
        names = [os.path.join('.', ''.join(x)) for x in product(['', '.'], possible_names)]

    for name in names:
        try:
            with open(name, 'r') as yml_f:
                logger.info(f'Reading configuration from {name}')
                parse_config(yml_f)
            break
        except FileNotFoundError:
            pass
    else:
        if path is not None:
            raise FileNotFoundError(f"'{path}' not found for config overrides")

Load config overrides from the yml file at |path| or from default paths . If a path is provided and it does not exist raise an exception


def process_config_values(parser: argparse.ArgumentParser, args: argparse.Namespace):
    """
    Bring in provided config values to the args parser, and import entries to the config
    from all arguments that were actually passed on the command line

    :param parser: The arg parser
    :param args: The value that parser.parse_args returned
    """
    # First, load a local config file, if passed or look for one in pwd if it wasn't.
    load_overrides(args.config)

    # Get a list of defined config vals. If these are passed on the command line,
    # update them in their correct group, not in the cli group
    defined_vars = list(get_config_keys())

    command_line_args = vars(args)

    # Bring in the options keys into args
    config_cli_args = get_group('cli')

    # Place all command line args into the cli group (for saving in the workspace). If
    # the value is set on command line, then it takes precedence; otherwise we try to
    # read it from the config file's cli group.
    for k in command_line_args:
        default = parser.get_default(k)
        set_val = getattr(args, k)
        if default is not set_val:
            if k not in defined_vars:
                config_cli_args.update(k, value=set_val)
            else:
                # Update a var's native group
                group_name, key = k.split('.')
                group = get_group(group_name)
                setattr(group, key, set_val)
        else:
            if k in config_cli_args:
                setattr(args, k, getattr(config_cli_args, k))

Bring in provided config values to the args parser and import entries to the config from all arguments that were actually passed on the command line


def add(self, name: str, default=None, description: str=None):
        """
        Add a variable named |name| to this value group, optionally giving it a
        default value and a description.

        Variables must be added with this method before they can be set or read.
        Reading a variable replaces the variable that was defined previously, but
        updates the description if a new one is set.

        """
        if name in self._vars:
            raise ConfigError(f"{self.name}.{name} already defined.")

        if name == 'name':
            raise ConfigError("'name' is a reserved name for a group.")

        v = _Var(name, description=description, default=default)
        self._vars[name] = v

Add a variable named |name| to this value group optionally giving it a default value and a description .


def update(self, name: str, value=None, default=None, description: str=None):
        """
        Like add, but can tolerate existing values; also updates the value.

        Mostly used for setting fields from imported INI files and modified CLI flags.
        """
        if name in self._vars:
            description = description or self._vars[name].description
            default = default or self._vars[name].default
        elif name == 'name':
            raise ConfigError("'name' is a reserved name for a group.")

        v = _Var(name, description=description, default=default, defined=False)
        v.value = value
        self._vars[name] = v

Like add but can tolerate existing values ; also updates the value .


def get_description(self, name: str) -> str:
        """
        Return the description, or a help string of variable identified by |name|.
        """
        if name not in self._vars:
            raise ConfigError(f"{self.name}.{name} not defined.")

        return self._vars[name].description

def
get_description
(
self
,
name
:
str
)
->
str
:
if
name
not
in
self
.
_vars
:
raise
ConfigError
(
f"{self.name}.{name} not defined."
)
return
self
.
_vars
[
name
]
.
description

Return the description or a help string of variable identified by |name| .


def function_signature_for_name_and_inputs(name: str, inputs: Sequence[Mapping[str, Any]]) -> str:
        """Returns the function signature for the specified name and Solidity JSON metadata inputs array.

        The ABI specification defines the function signature as the function name followed by the parenthesised list of
        parameter types separated by single commas and no spaces.
        See https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector
        """
        return name + SolidityMetadata.tuple_signature_for_components(inputs)

def function_signature_for_name_and_inputs ( name : str , inputs : Sequence [ Mapping [ str , Any ] ] ) -> str : return name + SolidityMetadata . tuple_signature_for_components ( inputs )

Returns the function signature for the specified name and Solidity JSON metadata inputs array .


def get_constructor_arguments(self) -> str:
        """Returns the tuple type signature for the arguments of the contract constructor."""
        item = self._constructor_abi_item
        return '()' if item is None else self.tuple_signature_for_components(item['inputs'])

def get_constructor_arguments ( self ) -> str : item = self . _constructor_abi_item return '()' if item is None else self . tuple_signature_for_components ( item [ 'inputs' ] )

Returns the tuple type signature for the arguments of the contract constructor .


def get_source_for(self, asm_offset, runtime=True):
        """ Solidity source code snippet related to `asm_pos` evm bytecode offset.
            If runtime is False, initialization bytecode source map is used
        """
        srcmap = self.get_srcmap(runtime)

        try:
            beg, size, _, _ = srcmap[asm_offset]
        except KeyError:
            #asm_offset pointing outside the known bytecode
            return ''

        output = ''
        nl = self.source_code[:beg].count('\n') + 1
        snippet = self.source_code[beg:beg + size]
        for l in snippet.split('\n'):
            output += '    %s  %s\n' % (nl, l)
            nl += 1
        return output

Solidity source code snippet related to asm_pos evm bytecode offset . If runtime is False initialization bytecode source map is used


def constructor_abi(self) -> Dict[str, Any]:
        """Returns a copy of the Solidity JSON ABI item for the contract constructor.

        The content of the returned dict is described at https://solidity.readthedocs.io/en/latest/abi-spec.html#json_
        """
        item = self._constructor_abi_item
        if item:
            return dict(item)
        return {'inputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'constructor'}

def constructor_abi ( self ) -> Dict [ str , Any ] : item = self . _constructor_abi_item if item : return dict ( item ) return { 'inputs' : [ ] , 'payable' : False , 'stateMutability' : 'nonpayable' , 'type' : 'constructor' }

Returns a copy of the Solidity JSON ABI item for the contract constructor .


def get_abi(self, hsh: bytes) -> Dict[str, Any]:
        """Returns a copy of the Solidity JSON ABI item for the function associated with the selector ``hsh``.

        If no normal contract function has the specified selector, a dict describing the default or non-default
        fallback function is returned.

        The content of the returned dict is described at https://solidity.readthedocs.io/en/latest/abi-spec.html#json_
        """
        if not isinstance(hsh, (bytes, bytearray)):
            raise TypeError('The selector argument must be a concrete byte array')
        sig = self._function_signatures_by_selector.get(hsh)
        if sig is not None:
            return dict(self._function_abi_items_by_signature[sig])
        item = self._fallback_function_abi_item
        if item is not None:
            return dict(item)
        # An item describing the default fallback function.
        return {'payable': False, 'stateMutability': 'nonpayable', 'type': 'fallback'}

Returns a copy of the Solidity JSON ABI item for the function associated with the selector hsh .


def get_func_argument_types(self, hsh: bytes):
        """Returns the tuple type signature for the arguments of the function associated with the selector ``hsh``.

        If no normal contract function has the specified selector,
        the empty tuple type signature ``'()'`` is returned.
        """
        if not isinstance(hsh, (bytes, bytearray)):
            raise TypeError('The selector argument must be a concrete byte array')
        sig = self._function_signatures_by_selector.get(hsh)
        return '()' if sig is None else sig[sig.find('('):]

Returns the tuple type signature for the arguments of the function associated with the selector hsh .


def get_func_return_types(self, hsh: bytes) -> str:
        """Returns the tuple type signature for the output values of the function
        associated with the selector ``hsh``.

        If no normal contract function has the specified selector,
        the empty tuple type signature ``'()'`` is returned.
        """
        if not isinstance(hsh, (bytes, bytearray)):
            raise TypeError('The selector argument must be a concrete byte array')
        abi = self.get_abi(hsh)
        outputs = abi.get('outputs')
        return '()' if outputs is None else SolidityMetadata.tuple_signature_for_components(outputs)

Returns the tuple type signature for the output values of the function associated with the selector hsh .


def get_func_signature(self, hsh: bytes) -> Optional[str]:
        """Returns the signature of the normal function with the selector ``hsh``,
        or ``None`` if no such function exists.

        This function returns ``None`` for any selector that will be dispatched to a fallback function.
        """
        if not isinstance(hsh, (bytes, bytearray)):
            raise TypeError('The selector argument must be a concrete byte array')
        return self._function_signatures_by_selector.get(hsh)

Returns the signature of the normal function with the selector hsh or None if no such function exists .


def map_memory_callback(self, address, size, perms, name, offset, result):
        """
        Catches did_map_memory and copies the mapping into Manticore
        """
        logger.info(' '.join(("Mapping Memory @",
                              hex(address) if type(address) is int else "0x??",
                              hr_size(size), "-",
                              perms, "-",
                              f"{name}:{hex(offset) if name else ''}", "->",
                              hex(result))))
        self._emu.mem_map(address, size, convert_permissions(perms))
        self.copy_memory(address, size)

def
map_memory_callback
(
self
,
address
,
size
,
perms
,
name
,
offset
,
result
)
:
logger
.
info
(
' '
.
join
(
(
"Mapping Memory @"
,
hex
(
address
)
if
type
(
address
)
is
int
else
"0x??"
,
hr_size
(
size
)
,
"-"
,
perms
,
"-"
,
f"{name}:{hex(offset) if name else ''}"
,
"->"
,
hex
(
result
)
)
)
)
self
.
_emu
.
mem_map
(
address
,
size
,
convert_permissions
(
perms
)
)
self
.
copy_memory
(
address
,
size
)


def unmap_memory_callback(self, start, size):
        """Unmap Unicorn maps when Manticore unmaps them"""
        logger.info(f"Unmapping memory from {hex(start)} to {hex(start + size)}")

        mask = (1 << 12) - 1
        if (start & mask) != 0:
            logger.error("Memory to be unmapped is not aligned to a page")

        if (size & mask) != 0:
            size = ((size >> 12) + 1) << 12
            logger.warning("Forcing unmap size to align to a page")

        self._emu.mem_unmap(start, size)


def protect_memory_callback(self, start, size, perms):
        """ Set memory protections in Unicorn correctly """
        logger.info(f"Changing permissions on {hex(start)}:{hex(start + size)} to {perms}")
        self._emu.mem_protect(start, size, convert_permissions(perms))


def _hook_syscall(self, uc, data):
        """
        Unicorn hook that transfers control to Manticore so it can execute the syscall
        """
        logger.debug(f"Stopping emulation at {hex(uc.reg_read(self._to_unicorn_id('RIP')))} to perform syscall")
        self.sync_unicorn_to_manticore()
        from ..native.cpu.abstractcpu import Syscall
        self._to_raise = Syscall()
        uc.emu_stop()


def _hook_write_mem(self, uc, access, address, size, value, data):
        """
        Captures memory written by Unicorn
        """
        self._mem_delta[address] = (value, size)
        return True

def _hook_write_mem ( self , uc , access , address , size , value , data ) : self . _mem_delta [ address ] = ( value , size ) return True


def emulate(self, instruction):
        """
        Wrapper that runs the _step function in a loop while handling exceptions
        """

        # The emulation might restart if Unicorn needs to bring in a memory map
        # or bring a value from Manticore state.
        while True:

            # Try emulation
            self._should_try_again = False
            self._to_raise = None

            self._step(instruction)

            if not self._should_try_again:
                break


def sync_unicorn_to_manticore(self):
        """
        Copy registers and written memory back into Manticore
        """
        self.write_backs_disabled = True
        for reg in self.registers:
            val = self._emu.reg_read(self._to_unicorn_id(reg))
            self._cpu.write_register(reg, val)
        if len(self._mem_delta) > 0:
            logger.debug(f"Syncing {len(self._mem_delta)} writes back into Manticore")
        for location in self._mem_delta:
            value, size = self._mem_delta[location]
            self._cpu.write_int(location, value, size * 8)
        self.write_backs_disabled = False
        self._mem_delta = {}


def write_back_memory(self, where, expr, size):
        """ Copy memory writes from Manticore back into Unicorn in real-time """
        if self.write_backs_disabled:
            return
        if type(expr) is bytes:
            self._emu.mem_write(where, expr)
        else:
            if issymbolic(expr):
                data = [Operators.CHR(Operators.EXTRACT(expr, offset, 8)) for offset in range(0, size, 8)]
                concrete_data = []
                for c in data:
                    if issymbolic(c):
                        c = chr(solver.get_value(self._cpu.memory.constraints, c))
                    concrete_data.append(c)
                data = concrete_data
            else:
                data = [Operators.CHR(Operators.EXTRACT(expr, offset, 8)) for offset in range(0, size, 8)]
            logger.debug(f"Writing back {hr_size(size // 8)} to {hex(where)}: {data}")
            # TODO - the extra encoding is to handle null bytes output as strings when we concretize. That's probably a bug.
            self._emu.mem_write(where, b''.join(b.encode('utf-8') if type(b) is str else b for b in data))

Copy memory writes from Manticore back into Unicorn in real - time


def write_back_register(self, reg, val):
        """ Sync register state from Manticore -> Unicorn"""
        if self.write_backs_disabled:
            return
        if issymbolic(val):
            logger.warning("Skipping Symbolic write-back")
            return
        if reg in self.flag_registers:
            self._emu.reg_write(self._to_unicorn_id('EFLAGS'), self._cpu.read_register('EFLAGS'))
            return
        self._emu.reg_write(self._to_unicorn_id(reg), val)

def
write_back_register
(
self
,
reg
,
val
)
:
if
self
.
write_backs_disabled
:
return
if
issymbolic
(
val
)
:
logger
.
warning
(
"Skipping Symbolic write-back"
)
return
if
reg
in
self
.
flag_registers
:
self
.
_emu
.
reg_write
(
self
.
_to_unicorn_id
(
'EFLAGS'
)
,
self
.
_cpu
.
read_register
(
'EFLAGS'
)
)
return
self
.
_emu
.
reg_write
(
self
.
_to_unicorn_id
(
reg
)
,
val
)

Sync register state from Manticore - > Unicorn


def update_segment(self, selector, base, size, perms):
        """ Only useful for setting FS right now. """
        logger.info("Updating selector %s to 0x%02x (%s bytes) (%s)", selector, base, size, perms)
        if selector == 99:
            self.set_fs(base)
        else:
            logger.error("No way to write segment: %d", selector)


def deprecated(message: str):
    """A decorator for marking functions as deprecated. """
    assert isinstance(message, str), "The deprecated decorator requires a message string argument."

    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            warnings.warn(f"`{func.__qualname__}` is deprecated. {message}",
                          category=ManticoreDeprecationWarning, stacklevel=2)
            return func(*args, **kwargs)

        return wrapper

    return decorator

A decorator for marking functions as deprecated .


def perm(lst, func):
    ''' Produce permutations of `lst`, where permutations are mutated by `func`. Used for flipping constraints. highly
    possible that returned constraints can be unsat this does it blindly, without any attention to the constraints
    themselves

    Considering lst as a list of constraints, e.g.

      [ C1, C2, C3 ]

    we'd like to consider scenarios of all possible permutations of flipped constraints, excluding the original list.
    So we'd like to generate:

      [ func(C1),      C2 ,       C3 ],
      [      C1 , func(C2),       C3 ],
      [ func(C1), func(C2),       C3 ],
      [      C1 ,      C2 ,  func(C3)],
      .. etc

    This is effectively treating the list of constraints as a bitmask of width len(lst) and counting up, skipping the
    0th element (unmodified array).

    The code below yields lists of constraints permuted as above by treating list indeces as bitmasks from 1 to
     2**len(lst) and applying func to all the set bit offsets.

    '''
    for i in range(1, 2**len(lst)):
        yield [func(item) if (1<<j)&i else item for (j, item) in enumerate(lst)]

def perm ( lst , func ) : for i in range ( 1 , 2 ** len ( lst ) ) : yield [ func ( item ) if ( 1 << j ) & i else item for ( j , item ) in enumerate ( lst ) ]

Produce permutations of lst where permutations are mutated by func . Used for flipping constraints . highly possible that returned constraints can be unsat this does it blindly without any attention to the constraints themselves


def input_from_cons(constupl, datas):
    ' solve bytes in |datas| based on '
    def make_chr(c):
        try:
            return chr(c)
        except Exception:
            return c
    newset = constraints_to_constraintset(constupl)

    ret = ''
    for data in datas:
        for c in data:
            ret += make_chr(solver.get_value(newset, c))
    return ret

def input_from_cons ( constupl , datas ) : def make_chr ( c ) : try : return chr ( c ) except Exception : return c newset = constraints_to_constraintset ( constupl ) ret = '' for data in datas : for c in data : ret += make_chr ( solver . get_value ( newset , c ) ) return ret


def symbolic_run_get_cons(trace):
    '''
    Execute a symbolic run that follows a concrete run; return constraints generated
    and the stdin data produced
    '''

    m2 = Manticore.linux(prog, workspace_url='mem:')
    f = Follower(trace)
    m2.verbosity(VERBOSITY)
    m2.register_plugin(f)

    def on_term_testcase(mcore, state, stateid, err):
        with m2.locked_context() as ctx:
            readdata = []
            for name, fd, data in state.platform.syscall_trace:
                if name in ('_receive', '_read') and fd == 0:
                    readdata.append(data)
            ctx['readdata'] = readdata
            ctx['constraints'] = list(state.constraints.constraints)

    m2.subscribe('will_terminate_state', on_term_testcase)

    m2.run()

    constraints = m2.context['constraints']
    datas = m2.context['readdata']

    return constraints, datas

def symbolic_run_get_cons ( trace ) : m2 = Manticore . linux ( prog , workspace_url = 'mem:' ) f = Follower ( trace ) m2 . verbosity ( VERBOSITY ) m2 . register_plugin ( f ) def on_term_testcase ( mcore , state , stateid , err ) : with m2 . locked_context ( ) as ctx : readdata = [ ] for name , fd , data in state . platform . syscall_trace : if name in ( '_receive' , '_read' ) and fd == 0 : readdata . append ( data ) ctx [ 'readdata' ] = readdata ctx [ 'constraints' ] = list ( state . constraints . constraints ) m2 . subscribe ( 'will_terminate_state' , on_term_testcase ) m2 . run ( ) constraints = m2 . context [ 'constraints' ] datas = m2 . context [ 'readdata' ] return constraints , datas

Execute a symbolic run that follows a concrete run ; return constraints generated and the stdin data produced


def empty_platform(cls, arch):
        """
        Create a platform without an ELF loaded.

        :param str arch: The architecture of the new platform
        :rtype: Linux
        """
        platform = cls(None)
        platform._init_cpu(arch)
        platform._init_std_fds()
        return platform

def empty_platform ( cls , arch ) : platform = cls ( None ) platform . _init_cpu ( arch ) platform . _init_std_fds ( ) return platform

Create a platform without an ELF loaded .


def _execve(self, program, argv, envp):
        """
        Load `program` and establish program state, such as stack and arguments.

        :param program str: The ELF binary to load
        :param argv list: argv array
        :param envp list: envp array
        """
        argv = [] if argv is None else argv
        envp = [] if envp is None else envp

        logger.debug(f"Loading {program} as a {self.arch} elf")

        self.load(program, envp)
        self._arch_specific_init()

        self._stack_top = self.current.STACK
        self.setup_stack([program] + argv, envp)

        nprocs = len(self.procs)
        nfiles = len(self.files)
        assert nprocs > 0
        self.running = list(range(nprocs))

        # Each process can wait for one timeout
        self.timers = [None] * nprocs
        # each fd has a waitlist
        self.rwait = [set() for _ in range(nfiles)]
        self.twait = [set() for _ in range(nfiles)]

        # Install event forwarders
        for proc in self.procs:
            self.forward_events_from(proc)

Load program and establish program state such as stack and arguments .


def _init_arm_kernel_helpers(self):
        """
        ARM kernel helpers

        https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt
        """

        page_data = bytearray(b'\xf1\xde\xfd\xe7' * 1024)

        # Extracted from a RPi2
        preamble = binascii.unhexlify(
            'ff0300ea' +
            '650400ea' +
            'f0ff9fe5' +
            '430400ea' +
            '220400ea' +
            '810400ea' +
            '000400ea' +
            '870400ea'
        )

        # XXX(yan): The following implementations of cmpxchg and cmpxchg64 were
        # handwritten to not use any exclusive instructions (e.g. ldrexd) or
        # locking. For actual implementations, refer to
        # arch/arm64/kernel/kuser32.S in the Linux source code.
        __kuser_cmpxchg64 = binascii.unhexlify(
            '30002de9' +  # push    {r4, r5}
            '08c09de5' +  # ldr     ip, [sp, #8]
            '30009ce8' +  # ldm     ip, {r4, r5}
            '010055e1' +  # cmp     r5, r1
            '00005401' +  # cmpeq   r4, r0
            '0100a013' +  # movne   r0, #1
            '0000a003' +  # moveq   r0, #0
            '0c008c08' +  # stmeq   ip, {r2, r3}
            '3000bde8' +  # pop     {r4, r5}
            '1eff2fe1'   # bx      lr
        )

        __kuser_dmb = binascii.unhexlify(
            '5bf07ff5' +  # dmb ish
            '1eff2fe1'   # bx lr
        )

        __kuser_cmpxchg = binascii.unhexlify(
            '003092e5' +  # ldr     r3, [r2]
            '000053e1' +  # cmp     r3, r0
            '0000a003' +  # moveq   r0, #0
            '00108205' +  # streq   r1, [r2]
            '0100a013' +  # movne   r0, #1
            '1eff2fe1'   # bx      lr
        )

        # Map a TLS segment
        self._arm_tls_memory = self.current.memory.mmap(None, 4, 'rw ')

        __kuser_get_tls = binascii.unhexlify(
            '04009FE5' +  # ldr r0, [pc, #4]
            '010090e8' +  # ldm r0, {r0}
            '1eff2fe1'   # bx lr
        ) + struct.pack('<I', self._arm_tls_memory)

        tls_area = b'\x00' * 12

        version = struct.pack('<I', 5)

        def update(address, code):
            page_data[address:address + len(code)] = code

        # Offsets from Documentation/arm/kernel_user_helpers.txt in Linux
        update(0x000, preamble)
        update(0xf60, __kuser_cmpxchg64)
        update(0xfa0, __kuser_dmb)
        update(0xfc0, __kuser_cmpxchg)
        update(0xfe0, __kuser_get_tls)
        update(0xff0, tls_area)
        update(0xffc, version)

        self.current.memory.mmap(0xffff0000, len(page_data), 'r x', page_data)


def _open(self, f):
        """
        Adds a file descriptor to the current file descriptor list

        :rtype: int
        :param f: the file descriptor to add.
        :return: the index of the file descriptor in the file descr. list
        """
        if None in self.files:
            fd = self.files.index(None)
            self.files[fd] = f
        else:
            fd = len(self.files)
            self.files.append(f)
        return fd

def _open ( self , f ) : if None in self . files : fd = self . files . index ( None ) self . files [ fd ] = f else : fd = len ( self . files ) self . files . append ( f ) return fd


def sys_openat(self, dirfd, buf, flags, mode):
        """
        Openat SystemCall - Similar to open system call except dirfd argument
        when path contained in buf is relative, dirfd is referred to set the relative path
        Special value AT_FDCWD set for dirfd to set path relative to current directory

        :param dirfd: directory file descriptor to refer in case of relative path at buf
        :param buf: address of zero-terminated pathname
        :param flags: file access bits
        :param mode: file permission mode
        """

        filename = self.current.read_string(buf)
        dirfd = ctypes.c_int32(dirfd).value

        if os.path.isabs(filename) or dirfd == self.FCNTL_FDCWD:
            return self.sys_open(buf, flags, mode)

        try:
            dir_entry = self._get_fd(dirfd)
        except FdError as e:
            logger.info("openat: Not valid file descriptor. Returning EBADF")
            return -e.err

        if not isinstance(dir_entry, Directory):
            logger.info("openat: Not directory descriptor. Returning ENOTDIR")
            return -errno.ENOTDIR

        dir_path = dir_entry.name

        filename = os.path.join(dir_path, filename)
        try:
            f = self._sys_open_get_file(filename, flags)
            logger.debug(f"Opening file {filename} for real fd {f.fileno()}")
        except IOError as e:
            logger.info(f"Could not open file {filename}. Reason: {e!s}")
            return -e.errno if e.errno is not None else -errno.EINVAL

        return self._open(f)

Openat SystemCall - Similar to open system call except dirfd argument when path contained in buf is relative dirfd is referred to set the relative path Special value AT_FDCWD set for dirfd to set path relative to current directory


def sys_rename(self, oldnamep, newnamep):
        """
        Rename filename `oldnamep` to `newnamep`.

        :param int oldnamep: pointer to oldname
        :param int newnamep: pointer to newname
        """
        oldname = self.current.read_string(oldnamep)
        newname = self.current.read_string(newnamep)

        ret = 0
        try:
            os.rename(oldname, newname)
        except OSError as e:
            ret = -e.errno

        return ret

def sys_rename ( self , oldnamep , newnamep ) : oldname = self . current . read_string ( oldnamep ) newname = self . current . read_string ( newnamep ) ret = 0 try : os . rename ( oldname , newname ) except OSError as e : ret = - e . errno return ret


def sys_fsync(self, fd):
        """
        Synchronize a file's in-core state with that on disk.
        """

        ret = 0
        try:
            self.files[fd].sync()
        except IndexError:
            ret = -errno.EBADF
        except FdError:
            ret = -errno.EINVAL

        return ret

def sys_fsync ( self , fd ) : ret = 0 try : self . files [ fd ] . sync ( ) except IndexError : ret = - errno . EBADF except FdError : ret = - errno . EINVAL return ret

Synchronize a file s in - core state with that on disk .


def sys_rt_sigaction(self, signum, act, oldact):
        """Wrapper for sys_sigaction"""
        return self.sys_sigaction(signum, act, oldact)

def sys_rt_sigaction ( self , signum , act , oldact ) : return self . sys_sigaction ( signum , act , oldact )


def sys_rt_sigprocmask(self, cpu, how, newset, oldset):
        """Wrapper for sys_sigprocmask"""
        return self.sys_sigprocmask(cpu, how, newset, oldset)

def sys_rt_sigprocmask ( self , cpu , how , newset , oldset ) : return self . sys_sigprocmask ( cpu , how , newset , oldset )


def sys_chroot(self, path):
        """
        An implementation of chroot that does perform some basic error checking,
        but does not actually chroot.

        :param path: Path to chroot
        """
        if path not in self.current.memory:
            return -errno.EFAULT

        path_s = self.current.read_string(path)
        if not os.path.exists(path_s):
            return -errno.ENOENT

        if not os.path.isdir(path_s):
            return -errno.ENOTDIR

        return -errno.EPERM

def sys_chroot ( self , path ) : if path not in self . current . memory : return - errno . EFAULT path_s = self . current . read_string ( path ) if not os . path . exists ( path_s ) : return - errno . ENOENT if not os . path . isdir ( path_s ) : return - errno . ENOTDIR return - errno . EPERM

An implementation of chroot that does perform some basic error checking but does not actually chroot .


def sys_mmap_pgoff(self, address, size, prot, flags, fd, offset):
        """Wrapper for mmap2"""
        return self.sys_mmap2(address, size, prot, flags, fd, offset)

def sys_mmap_pgoff ( self , address , size , prot , flags , fd , offset ) : return self . sys_mmap2 ( address , size , prot , flags , fd , offset )


def syscall(self):
        """
        Syscall dispatcher.
        """

        index = self._syscall_abi.syscall_number()

        try:
            table = getattr(linux_syscalls, self.current.machine)
            name = table.get(index, None)
            implementation = getattr(self, name)
        except (AttributeError, KeyError):
            if name is not None:
                raise SyscallNotImplemented(index, name)
            else:
                raise Exception(f"Bad syscall index, {index}")

        return self._syscall_abi.invoke(implementation)

def
syscall
(
self
)
:
index
=
self
.
_syscall_abi
.
syscall_number
(
)
try
:
table
=
getattr
(
linux_syscalls
,
self
.
current
.
machine
)
name
=
table
.
get
(
index
,
)
implementation
=
getattr
(
self
,
name
)
except
(
AttributeError
,
KeyError
)
:
if
name
is
not
:
raise
SyscallNotImplemented
(
index
,
name
)
else
:
raise
Exception
(
f"Bad syscall index, {index}"
)
return
self
.
_syscall_abi
.
invoke
(
implementation
)


def sched(self):
        """ Yield CPU.
            This will choose another process from the running list and change
            current running process. May give the same cpu if only one running
            process.
        """
        if len(self.procs) > 1:
            logger.debug("SCHED:")
            logger.debug(f"\tProcess: {self.procs!r}")
            logger.debug(f"\tRunning: {self.running!r}")
            logger.debug(f"\tRWait: {self.rwait!r}")
            logger.debug(f"\tTWait: {self.twait!r}")
            logger.debug(f"\tTimers: {self.timers!r}")
            logger.debug(f"\tCurrent clock: {self.clocks}")
            logger.debug(f"\tCurrent cpu: {self._current}")

        if len(self.running) == 0:
            logger.debug("None running checking if there is some process waiting for a timeout")
            if all([x is None for x in self.timers]):
                raise Deadlock()
            self.clocks = min(x for x in self.timers if x is not None) + 1
            self.check_timers()
            assert len(self.running) != 0, "DEADLOCK!"
            self._current = self.running[0]
            return
        next_index = (self.running.index(self._current) + 1) % len(self.running)
        next_running_idx = self.running[next_index]
        if len(self.procs) > 1:
            logger.debug(f"\tTransfer control from process {self._current} to {next_running_idx}")
        self._current = next_running_idx

Yield CPU . This will choose another process from the running list and change current running process . May give the same cpu if only one running process .


def wait(self, readfds, writefds, timeout):
        """ Wait for file descriptors or timeout.
            Adds the current process in the correspondent waiting list and
            yield the cpu to another running process.
        """
        logger.debug("WAIT:")
        logger.debug(f"\tProcess {self._current} is going to wait for [ {readfds!r} {writefds!r} {timeout!r} ]")
        logger.debug(f"\tProcess: {self.procs!r}")
        logger.debug(f"\tRunning: {self.running!r}")
        logger.debug(f"\tRWait: {self.rwait!r}")
        logger.debug(f"\tTWait: {self.twait!r}")
        logger.debug(f"\tTimers: {self.timers!r}")

        for fd in readfds:
            self.rwait[fd].add(self._current)
        for fd in writefds:
            self.twait[fd].add(self._current)
        if timeout is not None:
            self.timers[self._current] = self.clocks + timeout
        procid = self._current
        # self.sched()
        next_index = (self.running.index(procid) + 1) % len(self.running)
        self._current = self.running[next_index]
        logger.debug(f"\tTransfer control from process {procid} to {self._current}")
        logger.debug(f"\tREMOVING {procid!r} from {self.running!r}. Current: {self._current!r}")
        self.running.remove(procid)
        if self._current not in self.running:
            logger.debug("\tCurrent not running. Checking for timers...")
            self._current = None
            self.check_timers()

Wait for file descriptors or timeout . Adds the current process in the correspondent waiting list and yield the cpu to another running process .


def awake(self, procid):
        """ Remove procid from waitlists and reestablish it in the running list """
        logger.debug(f"Remove procid:{procid} from waitlists and reestablish it in the running list")
        for wait_list in self.rwait:
            if procid in wait_list:
                wait_list.remove(procid)
        for wait_list in self.twait:
            if procid in wait_list:
                wait_list.remove(procid)
        self.timers[procid] = None
        self.running.append(procid)
        if self._current is None:
            self._current = procid


def signal_receive(self, fd):
        """ Awake one process waiting to receive data on fd """
        connections = self.connections
        if connections(fd) and self.twait[connections(fd)]:
            procid = random.sample(self.twait[connections(fd)], 1)[0]
            self.awake(procid)

def signal_receive ( self , fd ) : connections = self . connections if connections ( fd ) and self . twait [ connections ( fd ) ] : procid = random . sample ( self . twait [ connections ( fd ) ] , 1 ) [ 0 ] self . awake ( procid )


def check_timers(self):
        """ Awake process if timer has expired """
        if self._current is None:
            # Advance the clocks. Go to future!!
            advance = min([self.clocks] + [x for x in self.timers if x is not None]) + 1
            logger.debug(f"Advancing the clock from {self.clocks} to {advance}")
            self.clocks = advance
        for procid in range(len(self.timers)):
            if self.timers[procid] is not None:
                if self.clocks > self.timers[procid]:
                    self.procs[procid].PC += self.procs[procid].instruction.size
                    self.awake(procid)


def _interp_total_size(interp):
        """
        Compute total load size of interpreter.

        :param ELFFile interp: interpreter ELF .so
        :return: total load size of interpreter, not aligned
        :rtype: int
        """
        load_segs = [x for x in interp.iter_segments() if x.header.p_type == 'PT_LOAD']
        last = load_segs[-1]
        return last.header.p_vaddr + last.header.p_memsz

def _interp_total_size ( interp ) : load_segs = [ x for x in interp . iter_segments ( ) if x . header . p_type == 'PT_LOAD' ] last = load_segs [ - 1 ] return last . header . p_vaddr + last . header . p_memsz

Compute total load size of interpreter .


def sys_openat(self, dirfd, buf, flags, mode):
        """
        A version of openat that includes a symbolic path and symbolic directory file descriptor

        :param dirfd: directory file descriptor
        :param buf: address of zero-terminated pathname
        :param flags: file access bits
        :param mode: file permission mode
        """

        if issymbolic(dirfd):
            logger.debug("Ask to read from a symbolic directory file descriptor!!")
            # Constrain to a valid fd and one past the end of fds
            self.constraints.add(dirfd >= 0)
            self.constraints.add(dirfd <= len(self.files))
            raise ConcretizeArgument(self, 0)

        if issymbolic(buf):
            logger.debug("Ask to read to a symbolic buffer")
            raise ConcretizeArgument(self, 1)

        return super().sys_openat(dirfd, buf, flags, mode)


def sys_allocate(self, cpu, length, isX, addr):
        """ allocate - allocate virtual memory

           The  allocate  system call creates a new allocation in the virtual address
           space of the calling process.  The length argument specifies the length of
           the allocation in bytes which will be rounded up to the hardware page size.

           The kernel chooses the address at which to create the allocation; the
           address of the new allocation is returned in *addr as the result of the call.

           All newly allocated memory is readable and writeable. In addition, the
           is_X argument is a boolean that allows newly allocated memory to be marked
           as executable (non-zero) or non-executable (zero).

           The allocate function is invoked through system call number 5.

           :param cpu: current CPU
           :param length: the length of the allocation in bytes
           :param isX: boolean that allows newly allocated memory to be marked as executable
           :param addr: the address of the new allocation is returned in *addr

           :return: On success, allocate returns zero and a pointer to the allocated area
                               is returned in *addr.  Otherwise, an error code is returned
                               and *addr is undefined.
                   EINVAL   length is zero.
                   EINVAL   length is too large.
                   EFAULT   addr points to an invalid address.
                   ENOMEM   No memory is available or the process' maximum number of allocations
                            would have been exceeded.
        """
        # TODO: check 4 bytes from addr
        if addr not in cpu.memory:
            logger.info("ALLOCATE: addr points to invalid address. Returning EFAULT")
            return Decree.CGC_EFAULT

        perms = ['rw ', 'rwx'][bool(isX)]
        try:
            result = cpu.memory.mmap(None, length, perms)
        except Exception as e:
            logger.info("ALLOCATE exception %s. Returning ENOMEM %r", str(e), length)
            return Decree.CGC_ENOMEM
        cpu.write_int(addr, result, 32)
        logger.info("ALLOCATE(%d, %s, 0x%08x) -> 0x%08x" % (length, perms, addr, result))
        self.syscall_trace.append(("_allocate", -1, length))
        return 0


def sys_random(self, cpu, buf, count, rnd_bytes):
        """ random - fill a buffer with random data

           The  random  system call populates the buffer referenced by buf with up to
           count bytes of random data. If count is zero, random returns 0 and optionally
           sets *rx_bytes to zero. If count is greater than SSIZE_MAX, the result is unspecified.

           :param cpu: current CPU
           :param buf: a memory buffer
           :param count: max number of bytes to receive
           :param rnd_bytes: if valid, points to the actual number of random bytes

           :return:  0        On success
                     EINVAL   count is invalid.
                     EFAULT   buf or rnd_bytes points to an invalid address.
        """

        ret = 0
        if count != 0:
            if count > Decree.CGC_SSIZE_MAX or count < 0:
                ret = Decree.CGC_EINVAL
            else:
                # TODO check count bytes from buf
                if buf not in cpu.memory or (buf + count) not in cpu.memory:
                    logger.info("RANDOM: buf points to invalid address. Returning EFAULT")
                    return Decree.CGC_EFAULT

                with open("/dev/urandom", "rb") as f:
                    data = f.read(count)

                self.syscall_trace.append(("_random", -1, data))
                cpu.write_bytes(buf, data)

        # TODO check 4 bytes from rx_bytes
        if rnd_bytes:
            if rnd_bytes not in cpu.memory:
                logger.info("RANDOM: Not valid rnd_bytes. Returning EFAULT")
                return Decree.CGC_EFAULT
            cpu.write_int(rnd_bytes, len(data), 32)

        logger.info("RANDOM(0x%08x, %d, 0x%08x) -> <%s>)" % (buf, count, rnd_bytes, repr(data[:10])))
        return ret


def sys_receive(self, cpu, fd, buf, count, rx_bytes):
        """ receive - receive bytes from a file descriptor

            The receive system call reads up to count bytes from file descriptor fd to the
            buffer pointed to by buf. If count is zero, receive returns 0 and optionally
            sets *rx_bytes to zero.

            :param cpu: current CPU.
            :param fd: a valid file descriptor
            :param buf: a memory buffer
            :param count: max number of bytes to receive
            :param rx_bytes: if valid, points to the actual number of bytes received
            :return: 0            Success
                     EBADF        fd is not a valid file descriptor or is not open
                     EFAULT       buf or rx_bytes points to an invalid address.
        """
        data = ''
        if count != 0:
            if not self._is_open(fd):
                logger.info("RECEIVE: Not valid file descriptor on receive. Returning EBADF")
                return Decree.CGC_EBADF

            # TODO check count bytes from buf
            if buf not in cpu.memory:  # or not  buf+count in cpu.memory:
                logger.info("RECEIVE: buf points to invalid address. Returning EFAULT")
                return Decree.CGC_EFAULT

            #import random
            #count = random.randint(1,count)
            if fd > 2 and self.files[fd].is_empty():
                cpu.PC -= cpu.instruction.size
                self.wait([fd], [], None)
                raise RestartSyscall()

            # get some potential delay
            # if random.randint(5) == 0 and count > 1:
            #    count = count/2

            # Read the data and put it in memory
            data = self.files[fd].receive(count)
            self.syscall_trace.append(("_receive", fd, data))
            cpu.write_bytes(buf, data)

            self.signal_receive(fd)

        # TODO check 4 bytes from rx_bytes
        if rx_bytes:
            if rx_bytes not in cpu.memory:
                logger.info("RECEIVE: Not valid file descriptor on receive. Returning EFAULT")
                return Decree.CGC_EFAULT
            cpu.write_int(rx_bytes, len(data), 32)

        logger.info("RECEIVE(%d, 0x%08x, %d, 0x%08x) -> <%s> (size:%d)" % (fd, buf, count, rx_bytes, repr(data)[:min(count, 10)], len(data)))
        return 0


def sys_deallocate(self, cpu, addr, size):
        """ deallocate - remove allocations
        The  deallocate  system call deletes the allocations for the specified
        address range, and causes further references to the addresses within the
        range to generate invalid memory accesses. The region is also
        automatically deallocated when the process is terminated.

        The address addr must be a multiple of the page size.  The length parameter
        specifies the size of the region to be deallocated in bytes.  All pages
        containing a part of the indicated range are deallocated, and subsequent
        references will terminate the process.  It is not an error if the indicated
        range does not contain any allocated pages.

        The deallocate function is invoked through system call number 6.

        :param cpu: current CPU
        :param addr: the starting address to unmap.
        :param size: the size of the portion to unmap.
        :return 0        On success
                EINVAL   addr is not page aligned.
                EINVAL   length is zero.
                EINVAL   any  part  of  the  region  being  deallocated  is outside the valid
                         address range of the process.

        :param cpu: current CPU.
        :return: C{0} on success.
        """
        logger.info("DEALLOCATE(0x%08x, %d)" % (addr, size))

        if addr & 0xfff != 0:
            logger.info("DEALLOCATE: addr is not page aligned")
            return Decree.CGC_EINVAL
        if size == 0:
            logger.info("DEALLOCATE:length is zero")
            return Decree.CGC_EINVAL
        # unlikely AND WRONG!!!
        # if addr > Decree.CGC_SSIZE_MAX or addr+size > Decree.CGC_SSIZE_MAX:
        #    logger.info("DEALLOCATE: part of the region being deallocated is outside the valid address range of the process")
        #    return Decree.CGC_EINVAL

        cpu.memory.munmap(addr, size)
        self.syscall_trace.append(("_deallocate", -1, size))
        return 0

deallocate - remove allocations The deallocate system call deletes the allocations for the specified address range and causes further references to the addresses within the range to generate invalid memory accesses . The region is also automatically deallocated when the process is terminated .


def sched(self):
        """ Yield CPU.
            This will choose another process from the RUNNNIG list and change
            current running process. May give the same cpu if only one running
            process.
        """
        if len(self.procs) > 1:
            logger.info("SCHED:")
            logger.info("\tProcess: %r", self.procs)
            logger.info("\tRunning: %r", self.running)
            logger.info("\tRWait: %r", self.rwait)
            logger.info("\tTWait: %r", self.twait)
            logger.info("\tTimers: %r", self.timers)
            logger.info("\tCurrent clock: %d", self.clocks)
            logger.info("\tCurrent cpu: %d", self._current)

        if len(self.running) == 0:
            logger.info("None running checking if there is some process waiting for a timeout")
            if all([x is None for x in self.timers]):
                raise Deadlock()
            self.clocks = min([x for x in self.timers if x is not None]) + 1
            self.check_timers()
            assert len(self.running) != 0, "DEADLOCK!"
            self._current = self.running[0]
            return
        next_index = (self.running.index(self._current) + 1) % len(self.running)
        next = self.running[next_index]
        if len(self.procs) > 1:
            logger.info("\tTransfer control from process %d to %d", self._current, next)
        self._current = next

Yield CPU . This will choose another process from the RUNNNIG list and change current running process . May give the same cpu if only one running process .


def wait(self, readfds, writefds, timeout):
        """ Wait for filedescriptors or timeout.
            Adds the current process to the corresponding waiting list and
            yields the cpu to another running process.
        """
        logger.info("WAIT:")
        logger.info("\tProcess %d is going to wait for [ %r %r %r ]", self._current, readfds, writefds, timeout)
        logger.info("\tProcess: %r", self.procs)
        logger.info("\tRunning: %r", self.running)
        logger.info("\tRWait: %r", self.rwait)
        logger.info("\tTWait: %r", self.twait)
        logger.info("\tTimers: %r", self.timers)

        for fd in readfds:
            self.rwait[fd].add(self._current)
        for fd in writefds:
            self.twait[fd].add(self._current)
        if timeout is not None:
            self.timers[self._current] = self.clocks + timeout
        else:
            self.timers[self._current] = None
        procid = self._current
        # self.sched()
        next_index = (self.running.index(procid) + 1) % len(self.running)
        self._current = self.running[next_index]
        logger.info("\tTransfer control from process %d to %d", procid, self._current)
        logger.info("\tREMOVING %r from %r. Current: %r", procid, self.running, self._current)
        self.running.remove(procid)
        if self._current not in self.running:
            logger.info("\tCurrent not running. Checking for timers...")
            self._current = None
            if all([x is None for x in self.timers]):
                raise Deadlock()
            self.check_timers()

Wait for filedescriptors or timeout . Adds the current process to the corresponding waiting list and yields the cpu to another running process .


def sys_receive(self, cpu, fd, buf, count, rx_bytes):
        """
        Symbolic version of Decree.sys_receive
        """
        if issymbolic(fd):
            logger.info("Ask to read from a symbolic file descriptor!!")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 0)

        if issymbolic(buf):
            logger.info("Ask to read to a symbolic buffer")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 1)

        if issymbolic(count):
            logger.info("Ask to read a symbolic number of bytes ")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 2)

        if issymbolic(rx_bytes):
            logger.info("Ask to return size to a symbolic address ")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 3)

        return super().sys_receive(cpu, fd, buf, count, rx_bytes)

Symbolic version of Decree . sys_receive


def sys_transmit(self, cpu, fd, buf, count, tx_bytes):
        """
        Symbolic version of Decree.sys_transmit
        """
        if issymbolic(fd):
            logger.info("Ask to write to a symbolic file descriptor!!")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 0)

        if issymbolic(buf):
            logger.info("Ask to write to a symbolic buffer")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 1)

        if issymbolic(count):
            logger.info("Ask to write a symbolic number of bytes ")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 2)

        if issymbolic(tx_bytes):
            logger.info("Ask to return size to a symbolic address ")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 3)

        return super().sys_transmit(cpu, fd, buf, count, tx_bytes)

Symbolic version of Decree . sys_transmit


def sync(f):
    """ Synchronization decorator. """

    def new_function(self, *args, **kw):
        self._lock.acquire()
        try:
            return f(self, *args, **kw)
        finally:
            self._lock.release()
    return new_function

def sync ( f ) : def new_function ( self , * args , * * kw ) : self . _lock . acquire ( ) try : return f ( self , * args , * * kw ) finally : self . _lock . release ( ) return new_function


def save_value(self, key, value):
        """
        Save an arbitrary, serializable `value` under `key`.

        :param str key: A string identifier under which to store the value.
        :param value: A serializable value
        :return:
        """
        with self.save_stream(key) as s:
            s.write(value)

def save_value ( self , key , value ) : with self . save_stream ( key ) as s : s . write ( value )

Save an arbitrary serializable value under key .


def load_value(self, key, binary=False):
        """
        Load an arbitrary value identified by `key`.

        :param str key: The key that identifies the value
        :return: The loaded value
        """
        with self.load_stream(key, binary=binary) as s:
            return s.read()

def load_value ( self , key , binary = False ) : with self . load_stream ( key , binary = binary ) as s : return s . read ( )

Load an arbitrary value identified by key .


def save_stream(self, key, binary=False):
        """
        Return a managed file-like object into which the calling code can write
        arbitrary data.

        :param key:
        :return: A managed stream-like object
        """
        s = io.BytesIO() if binary else io.StringIO()
        yield s
        self.save_value(key, s.getvalue())

def save_stream ( self , key , binary = False ) : s = io . BytesIO ( ) if binary else io . StringIO ( ) yield s self . save_value ( key , s . getvalue ( ) )

Return a managed file - like object into which the calling code can write arbitrary data .


def load_stream(self, key, binary=False):
        """
        Return a managed file-like object from which the calling code can read
        previously-serialized data.

        :param key:
        :return: A managed stream-like object
        """
        value = self.load_value(key, binary=binary)
        yield io.BytesIO(value) if binary else io.StringIO(value)

def load_stream ( self , key , binary = False ) : value = self . load_value ( key , binary = binary ) yield io . BytesIO ( value ) if binary else io . StringIO ( value )

Return a managed file - like object from which the calling code can read previously - serialized data .


def save_state(self, state, key):
        """
        Save a state to storage.

        :param manticore.core.StateBase state:
        :param str key:
        :return:
        """
        with self.save_stream(key, binary=True) as f:
            self._serializer.serialize(state, f)

def save_state ( self , state , key ) : with self . save_stream ( key , binary = True ) as f : self . _serializer . serialize ( state , f )


def load_state(self, key, delete=True):
        """
        Load a state from storage.

        :param key: key that identifies state
        :rtype: manticore.core.StateBase
        """
        with self.load_stream(key, binary=True) as f:
            state = self._serializer.deserialize(f)
            if delete:
                self.rm(key)
            return state

def load_state ( self , key , delete = True ) : with self . load_stream ( key , binary = True ) as f : state = self . _serializer . deserialize ( f ) if delete : self . rm ( key ) return state


def save_stream(self, key, binary=False):
        """
        Yield a file object representing `key`

        :param str key: The file to save to
        :param bool binary: Whether we should treat it as binary
        :return:
        """
        mode = 'wb' if binary else 'w'
        with open(os.path.join(self.uri, key), mode) as f:
            yield f

def save_stream ( self , key , binary = False ) : mode = 'wb' if binary else 'w' with open ( os . path . join ( self . uri , key ) , mode ) as f : yield f


def rm(self, key):
        """
        Remove file identified by `key`.

        :param str key: The file to delete
        """
        path = os.path.join(self.uri, key)
        os.remove(path)

def rm ( self , key ) : path = os . path . join ( self . uri , key ) os . remove ( path )


def ls(self, glob_str):
        """
        Return just the filenames that match `glob_str` inside the store directory.

        :param str glob_str: A glob string, i.e. 'state_*'
        :return: list of matched keys
        """
        path = os.path.join(self.uri, glob_str)
        return [os.path.split(s)[1] for s in glob.glob(path)]

def ls ( self , glob_str ) : path = os . path . join ( self . uri , glob_str ) return [ os . path . split ( s ) [ 1 ] for s in glob . glob ( path ) ]

Return just the filenames that match glob_str inside the store directory .


def _get_id(self):
        """
        Get a unique state id.

        :rtype: int
        """
        id_ = self._last_id.value
        self._last_id.value += 1
        return id_

def _get_id ( self ) : id_ = self . _last_id . value self . _last_id . value += 1 return id_

Get a unique state id.

        :rtype: int


def load_state(self, state_id, delete=True):
        """
        Load a state from storage identified by `state_id`.

        :param state_id: The state reference of what to load
        :return: The deserialized state
        :rtype: State
        """
        return self._store.load_state(f'{self._prefix}{state_id:08x}{self._suffix}', delete=delete)

Load a state from storage identified by state_id .


def save_state(self, state, state_id=None):
        """
        Save a state to storage, return identifier.

        :param state: The state to save
        :param int state_id: If not None force the state id potentially overwriting old states
        :return: New state id
        :rtype: int
        """
        assert isinstance(state, StateBase)
        if state_id is None:
            state_id = self._get_id()
        else:
            self.rm_state(state_id)

        self._store.save_state(state, f'{self._prefix}{state_id:08x}{self._suffix}')
        return state_id

Save a state to storage return identifier .


def _named_stream(self, name, binary=False):
        """
        Create an indexed output stream i.e. 'test_00000001.name'

        :param name: Identifier for the stream
        :return: A context-managed stream-like object
        """
        with self._store.save_stream(self._named_key(name), binary=binary) as s:
            yield s

def _named_stream ( self , name , binary = False ) : with self . _store . save_stream ( self . _named_key ( name ) , binary = binary ) as s : yield s

Create an indexed output stream i . e . test_00000001 . name


def cmp_regs(cpu, should_print=False):
    """
    Compare registers from a remote gdb session to current mcore.

    :param manticore.core.cpu Cpu: Current cpu
    :param bool should_print: Whether to print values to stdout
    :return: Whether or not any differences were detected
    :rtype: bool
    """
    differing = False
    gdb_regs = gdb.getCanonicalRegisters()
    for name in sorted(gdb_regs):
        vg = gdb_regs[name]
        if name.endswith('psr'):
            name = 'apsr'
        v = cpu.read_register(name.upper())
        if should_print:
            logger.debug(f'{name} gdb:{vg:x} mcore:{v:x}')
        if vg != v:
            if should_print:
                logger.warning('^^ unequal')
            differing = True
    if differing:
        logger.debug(qemu.correspond(None))
    return differing

def
cmp_regs
(
cpu
,
should_print
=
False
)
:
differing
=
False
gdb_regs
=
gdb
.
getCanonicalRegisters
(
)
for
name
in
sorted
(
gdb_regs
)
:
vg
=
gdb_regs
[
name
]
if
name
.
endswith
(
'psr'
)
:
name
=
'apsr'
v
=
cpu
.
read_register
(
name
.
upper
(
)
)
if
should_print
:
logger
.
debug
(
f'{name} gdb:{vg:x} mcore:{v:x}'
)
if
vg
!=
v
:
if
should_print
:
logger
.
warning
(
'^^ unequal'
)
differing
=
True
if
differing
:
logger
.
debug
(
qemu
.
correspond
(
)
)
return
differing

Compare registers from a remote gdb session to current mcore .


def sync_svc(state):
    """
    Mirror some service calls in manticore. Happens after qemu executed a SVC
    instruction, but before manticore did.
    """
    syscall = state.cpu.R7 # Grab idx from manticore since qemu could have exited
    name = linux_syscalls.armv7[syscall]

    logger.debug(f"Syncing syscall: {name}")

    try:
        # Make sure mmap returns the same address
        if 'mmap' in name:
            returned = gdb.getR('R0')
            logger.debug(f"Syncing mmap ({returned:x})")
            state.cpu.write_register('R0', returned)
        if 'exit' in name:
            return
    except ValueError:
        for reg in state.cpu.canonical_registers:
            print(f'{reg}: {state.cpu.read_register(reg):x}')
        raise

Mirror some service calls in manticore . Happens after qemu executed a SVC instruction but before manticore did .


def visit(self, node, use_fixed_point=False):
        """
        The entry point of the visitor.
        The exploration algorithm is a DFS post-order traversal
        The implementation used two stacks instead of a recursion
        The final result is store in self.result

        :param node: Node to explore
        :type node: Expression
        :param use_fixed_point: if True, it runs _methods until a fixed point is found
        :type use_fixed_point: Bool
        """
        cache = self._cache
        visited = set()
        stack = []
        stack.append(node)
        while stack:
            node = stack.pop()
            if node in cache:
                self.push(cache[node])
            elif isinstance(node, Operation):
                if node in visited:
                    operands = [self.pop() for _ in range(len(node.operands))]
                    value = self._method(node, *operands)

                    visited.remove(node)
                    self.push(value)
                    cache[node] = value
                else:
                    visited.add(node)
                    stack.append(node)
                    stack.extend(node.operands)
            else:
                self.push(self._method(node))

        if use_fixed_point:
            old_value = None
            new_value = self.pop()
            while old_value is not new_value:
                self.visit(new_value)
                old_value = new_value
                new_value = self.pop()
            self.push(new_value)

def visit ( self , node , use_fixed_point = False ) : cache = self . _cache visited = set ( ) stack = [ ] stack . append ( node ) while stack : node = stack . pop ( ) if node in cache : self . push ( cache [ node ] ) elif isinstance ( node , Operation ) : if node in visited : operands = [ self . pop ( ) for _ in range ( len ( node . operands ) ) ] value = self . _method ( node , * operands ) visited . remove ( node ) self . push ( value ) cache [ node ] = value else : visited . add ( node ) stack . append ( node ) stack . extend ( node . operands ) else : self . push ( self . _method ( node ) ) if use_fixed_point : old_value = None new_value = self . pop ( ) while old_value is not new_value : self . visit ( new_value ) old_value = new_value new_value = self . pop ( ) self . push ( new_value )

The entry point of the visitor . The exploration algorithm is a DFS post - order traversal The implementation used two stacks instead of a recursion The final result is store in self . result


def _method(self, expression, *args):
        """
        Overload Visitor._method because we want to stop to iterate over the
        visit_ functions as soon as a valid visit_ function is found
        """
        assert expression.__class__.__mro__[-1] is object
        for cls in expression.__class__.__mro__:
            sort = cls.__name__
            methodname = 'visit_%s' % sort
            method = getattr(self, methodname, None)
            if method is not None:
                method(expression, *args)
                return
        return

def _method ( self , expression , * args ) : assert expression . __class__ . __mro__ [ - 1 ] is object for cls in expression . __class__ . __mro__ : sort = cls . __name__ methodname = 'visit_%s' % sort method = getattr ( self , methodname , None ) if method is not None : method ( expression , * args ) return return

Overload Visitor . _method because we want to stop to iterate over the visit_ functions as soon as a valid visit_ function is found


def visit_BitVecAdd(self, expression, *operands):
        """ a + 0  ==> a
            0 + a  ==> a
        """
        left = expression.operands[0]
        right = expression.operands[1]
        if isinstance(right, BitVecConstant):
            if right.value == 0:
                return left
        if isinstance(left, BitVecConstant):
            if left.value == 0:
                return right

def visit_BitVecAdd ( self , expression , * operands ) : left = expression . operands [ 0 ] right = expression . operands [ 1 ] if isinstance ( right , BitVecConstant ) : if right . value == 0 : return left if isinstance ( left , BitVecConstant ) : if left . value == 0 : return right


def visit_BitVecOr(self, expression, *operands):
        """ a | 0 => a
            0 | a => a
            0xffffffff & a => 0xffffffff
            a & 0xffffffff => 0xffffffff

        """
        left = expression.operands[0]
        right = expression.operands[1]
        if isinstance(right, BitVecConstant):
            if right.value == 0:
                return left
            elif right.value == left.mask:
                return right
            elif isinstance(left, BitVecOr):
                left_left = left.operands[0]
                left_right = left.operands[1]
                if isinstance(right, Constant):
                    return BitVecOr(left_left, (left_right | right), taint=expression.taint)
        elif isinstance(left, BitVecConstant):
            return BitVecOr(right, left, taint=expression.taint)

def visit_BitVecOr ( self , expression , * operands ) : left = expression . operands [ 0 ] right = expression . operands [ 1 ] if isinstance ( right , BitVecConstant ) : if right . value == 0 : return left elif right . value == left . mask : return right elif isinstance ( left , BitVecOr ) : left_left = left . operands [ 0 ] left_right = left . operands [ 1 ] if isinstance ( right , Constant ) : return BitVecOr ( left_left , ( left_right | right ) , taint = expression . taint ) elif isinstance ( left , BitVecConstant ) : return BitVecOr ( right , left , taint = expression . taint )

a | 0 = > a 0 | a = > a 0xffffffff & a = > 0xffffffff a & 0xffffffff = > 0xffffffff


def _type_size(ty):
        """ Calculate `static` type size """
        if ty[0] in ('int', 'uint', 'bytesM', 'function'):
            return 32
        elif ty[0] in ('tuple'):
            result = 0
            for ty_i in ty[1]:
                result += ABI._type_size(ty_i)
            return result
        elif ty[0] in ('array'):
            rep = ty[1]
            result = 32  # offset link
            return result
        elif ty[0] in ('bytes', 'string'):
            result = 32  # offset link
            return result
        raise ValueError

def
_type_size
(
ty
)
:
if
ty
[
0
]
in
(
'int'
,
'uint'
,
'bytesM'
,
'function'
)
:
return
32
elif
ty
[
0
]
in
(
'tuple'
)
:
result
=
0
for
ty_i
in
ty
[
1
]
:
result
+=
ABI
.
_type_size
(
ty_i
)
return
result
elif
ty
[
0
]
in
(
'array'
)
:
rep
=
ty
[
1
]
result
=
32
# offset link
return
result
elif
ty
[
0
]
in
(
'bytes'
,
'string'
)
:
result
=
32
# offset link
return
result
raise
ValueError


def function_call(type_spec, *args):
        """
        Build transaction data from function signature and arguments
        """
        m = re.match(r"(?P<name>[a-zA-Z_][a-zA-Z_0-9]*)(?P<type>\(.*\))", type_spec)
        if not m:
            raise EthereumError("Function signature expected")

        ABI._check_and_warn_num_args(type_spec, *args)

        result = ABI.function_selector(type_spec)  # Funcid
        result += ABI.serialize(m.group('type'), *args)
        return result


def function_selector(method_name_and_signature):
        """
        Makes a function hash id from a method signature
        """
        s = sha3.keccak_256()
        s.update(method_name_and_signature.encode())
        return bytes(s.digest()[:4])

def function_selector ( method_name_and_signature ) : s = sha3 . keccak_256 ( ) s . update ( method_name_and_signature . encode ( ) ) return bytes ( s . digest ( ) [ : 4 ] )


def _serialize_uint(value, size=32, padding=0):
        """
        Translates a python integral or a BitVec into a 32 byte string, MSB first
        """
        if size <= 0 or size > 32:
            raise ValueError

        from .account import EVMAccount  # because of circular import
        if not isinstance(value, (int, BitVec, EVMAccount)):
            raise ValueError
        if issymbolic(value):
            # FIXME This temporary array variable should be obtained from a specific constraint store
            bytes = ArrayVariable(index_bits=256, index_max=32, value_bits=8, name='temp{}'.format(uuid.uuid1()))
            if value.size <= size * 8:
                value = Operators.ZEXTEND(value, size * 8)
            else:
                # automatically truncate, e.g. if they passed a BitVec(256) for an `address` argument (160 bits)
                value = Operators.EXTRACT(value, 0, size * 8)
            bytes = ArrayProxy(bytes.write_BE(padding, value, size))
        else:
            value = int(value)
            bytes = bytearray()
            for _ in range(padding):
                bytes.append(0)
            for position in reversed(range(size)):
                bytes.append(Operators.EXTRACT(value, position * 8, 8))
        assert len(bytes) == size + padding
        return bytes

Translates a python integral or a BitVec into a 32 byte string MSB first


def _serialize_int(value, size=32, padding=0):
        """
        Translates a signed python integral or a BitVec into a 32 byte string, MSB first
        """
        if size <= 0 or size > 32:
            raise ValueError
        if not isinstance(value, (int, BitVec)):
            raise ValueError
        if issymbolic(value):
            buf = ArrayVariable(index_bits=256, index_max=32, value_bits=8, name='temp{}'.format(uuid.uuid1()))
            value = Operators.SEXTEND(value, value.size, size * 8)
            buf = ArrayProxy(buf.write_BE(padding, value, size))
        else:
            value = int(value)
            buf = bytearray()
            for _ in range(padding):
                buf.append(0)

            for position in reversed(range(size)):
                buf.append(Operators.EXTRACT(value, position * 8, 8))
        return buf

def _serialize_int ( value , size = 32 , padding = 0 ) : if size <= 0 or size > 32 : raise ValueError if not isinstance ( value , ( int , BitVec ) ) : raise ValueError if issymbolic ( value ) : buf = ArrayVariable ( index_bits = 256 , index_max = 32 , value_bits = 8 , name = 'temp{}' . format ( uuid . uuid1 ( ) ) ) value = Operators . SEXTEND ( value , value . size , size * 8 ) buf = ArrayProxy ( buf . write_BE ( padding , value , size ) ) else : value = int ( value ) buf = bytearray ( ) for _ in range ( padding ) : buf . append ( 0 ) for position in reversed ( range ( size ) ) : buf . append ( Operators . EXTRACT ( value , position * 8 , 8 ) ) return buf

Translates a signed python integral or a BitVec into a 32 byte string MSB first


def _deserialize_uint(data, nbytes=32, padding=0, offset=0):
        """
        Read a `nbytes` bytes long big endian unsigned integer from `data` starting at `offset`

        :param data: sliceable buffer; symbolic buffer of Eth ABI encoded data
        :param nbytes: number of bytes to read starting from least significant byte
        :rtype: int or Expression
        """
        assert isinstance(data, (bytearray, Array))
        value = ABI._readBE(data, nbytes, padding=True, offset=offset)
        value = Operators.ZEXTEND(value, (nbytes + padding) * 8)
        return value

def _deserialize_uint ( data , nbytes = 32 , padding = 0 , offset = 0 ) : assert isinstance ( data , ( bytearray , Array ) ) value = ABI . _readBE ( data , nbytes , padding = True , offset = offset ) value = Operators . ZEXTEND ( value , ( nbytes + padding ) * 8 ) return value

Read a nbytes bytes long big endian unsigned integer from data starting at offset


def _deserialize_int(data, nbytes=32, padding=0):
        """
        Read a `nbytes` bytes long big endian signed integer from `data` starting at `offset`

        :param data: sliceable buffer; symbolic buffer of Eth ABI encoded data
        :param nbytes: number of bytes to read starting from least significant byte
        :rtype: int or Expression
        """
        assert isinstance(data, (bytearray, Array))
        value = ABI._readBE(data, nbytes, padding=True)
        value = Operators.SEXTEND(value, nbytes * 8, (nbytes + padding) * 8)
        if not issymbolic(value):
            # sign bit on
            if value & (1 << (nbytes * 8 - 1)):
                value = -(((~value) + 1) & ((1 << (nbytes * 8)) - 1))
        return value

def
_deserialize_int
(
data
,
nbytes
=
32
,
padding
=
0
)
:
assert
isinstance
(
data
,
(
bytearray
,
Array
)
)
value
=
ABI
.
_readBE
(
data
,
nbytes
,
padding
=
True
)
value
=
Operators
.
SEXTEND
(
value
,
nbytes
*
8
,
(
nbytes
+
padding
)
*
8
)
if
not
issymbolic
(
value
)
:
# sign bit on
if
value
&
(
1
<<
(
nbytes
*
8
1
)
)
:
value
=
(
(
(
~
value
)
+
1
)
&
(
(
1
<<
(
nbytes
*
8
)
)
1
)
)
return
value

Read a nbytes bytes long big endian signed integer from data starting at offset


def concretized_args(**policies):
    """
    Make sure an EVM instruction has all of its arguments concretized according to
    provided policies.

    Example decoration:

        @concretized_args(size='ONE', address='')
        def LOG(self, address, size, *topics):
            ...

    The above will make sure that the |size| parameter to LOG is Concretized when symbolic
    according to the 'ONE' policy and concretize |address| with the default policy.

    :param policies: A kwargs list of argument names and their respective policies.
                         Provide None or '' as policy to use default.
    :return: A function decorator
    """
    def concretizer(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            spec = inspect.getfullargspec(func)
            for arg, policy in policies.items():
                assert arg in spec.args, "Concretizer argument not found in wrapped function."
                # index is 0-indexed, but ConcretizeArgument is 1-indexed. However, this is correct
                # since implementation method is always a bound method (self is param 0)
                index = spec.args.index(arg)
                if not issymbolic(args[index]):
                    continue
                if not policy:
                    policy = 'SAMPLED'

                if policy == "ACCOUNTS":
                    value = args[index]
                    world = args[0].world
                    #special handler for EVM only policy
                    cond = world._constraint_to_accounts(value, ty='both', include_zero=True)
                    world.constraints.add(cond)
                    policy = 'ALL'
                raise ConcretizeArgument(index, policy=policy)
            return func(*args, **kwargs)
        wrapper.__signature__ = inspect.signature(func)
        return wrapper
    return concretizer

Make sure an EVM instruction has all of its arguments concretized according to provided policies .


def _get_memfee(self, address, size=1):
        """
        This calculates the amount of extra gas needed for accessing to
        previously unused memory.

        :param address: base memory offset
        :param size: size of the memory access
        """
        if not issymbolic(size) and size == 0:
            return 0

        address = self.safe_add(address, size)
        allocated = self.allocated
        GMEMORY = 3
        GQUADRATICMEMDENOM = 512  # 1 gas per 512 quadwords
        old_size = Operators.ZEXTEND(Operators.UDIV(self.safe_add(allocated, 31), 32), 512)
        new_size = Operators.ZEXTEND(Operators.UDIV(self.safe_add(address, 31), 32), 512)

        old_totalfee = self.safe_mul(old_size, GMEMORY) + Operators.UDIV(self.safe_mul(old_size, old_size), GQUADRATICMEMDENOM)
        new_totalfee = self.safe_mul(new_size, GMEMORY) + Operators.UDIV(self.safe_mul(new_size, new_size), GQUADRATICMEMDENOM)
        memfee = new_totalfee - old_totalfee
        flag = Operators.UGT(new_totalfee, old_totalfee)
        return Operators.ITEBV(512, size == 0, 0, Operators.ITEBV(512, flag, memfee, 0))

def
_get_memfee
(
self
,
address
,
size
=
1
)
:
if
not
issymbolic
(
size
)
and
size
==
0
:
return
0
address
=
self
.
safe_add
(
address
,
size
)
allocated
=
self
.
allocated
GMEMORY
=
3
GQUADRATICMEMDENOM
=
512
# 1 gas per 512 quadwords
old_size
=
Operators
.
ZEXTEND
(
Operators
.
UDIV
(
self
.
safe_add
(
allocated
,
31
)
,
32
)
,
512
)
new_size
=
Operators
.
ZEXTEND
(
Operators
.
UDIV
(
self
.
safe_add
(
address
,
31
)
,
32
)
,
512
)
old_totalfee
=
self
.
safe_mul
(
old_size
,
GMEMORY
)
+
Operators
.
UDIV
(
self
.
safe_mul
(
old_size
,
old_size
)
,
GQUADRATICMEMDENOM
)
new_totalfee
=
self
.
safe_mul
(
new_size
,
GMEMORY
)
+
Operators
.
UDIV
(
self
.
safe_mul
(
new_size
,
new_size
)
,
GQUADRATICMEMDENOM
)
memfee
=
new_totalfee
old_totalfee
flag
=
Operators
.
UGT
(
new_totalfee
,
old_totalfee
)
return
Operators
.
ITEBV
(
512
,
size
==
0
,
0
,
Operators
.
ITEBV
(
512
,
flag
,
memfee
,
0
)
)

This calculates the amount of extra gas needed for accessing to previously unused memory .


def read_code(self, address, size=1):
        """
        Read size byte from bytecode.
        If less than size bytes are available result will be pad with \x00
        """
        assert address < len(self.bytecode)
        value = self.bytecode[address:address + size]
        if len(value) < size:
            value += '\x00' * (size - len(value))  # pad with null (spec)
        return value

def
read_code
(
self
,
address
,
size
=
1
)
:
assert
address
<
len
(
self
.
bytecode
)
value
=
self
.
bytecode
[
address
:
address
+
size
]
if
len
(
value
)
<
size
:
value
+=
'\x00'
*
(
size
len
(
value
)
)
# pad with null (spec)
return
value

Read size byte from bytecode . If less than size bytes are available result will be pad with \ x00


def instruction(self):
        """
        Current instruction pointed by self.pc
        """
        # FIXME check if pc points to invalid instruction
        # if self.pc >= len(self.bytecode):
        #    return InvalidOpcode('Code out of range')
        # if self.pc in self.invalid:
        #    raise InvalidOpcode('Opcode inside a PUSH immediate')
        try:
            _decoding_cache = getattr(self, '_decoding_cache')
        except Exception:
            _decoding_cache = self._decoding_cache = {}

        pc = self.pc
        if isinstance(pc, Constant):
            pc = pc.value

        if pc in _decoding_cache:
            return _decoding_cache[pc]

        def getcode():
            bytecode = self.bytecode
            for pc_i in range(pc, len(bytecode)):
                yield simplify(bytecode[pc_i]).value
            while True:
                yield 0
        instruction = EVMAsm.disassemble_one(getcode(), pc=pc, fork=DEFAULT_FORK)
        _decoding_cache[pc] = instruction
        return instruction

Current instruction pointed by self . pc


def _push(self, value):
        """
        Push into the stack

              ITEM0
              ITEM1
              ITEM2
        sp->  {empty}
        """
        assert isinstance(value, int) or isinstance(value, BitVec) and value.size == 256
        if len(self.stack) >= 1024:
            raise StackOverflow()

        if isinstance(value, int):
            value = value & TT256M1

        value = simplify(value)
        if isinstance(value, Constant) and not value.taint:
            value = value.value
        self.stack.append(value)

def _push ( self , value ) : assert isinstance ( value , int ) or isinstance ( value , BitVec ) and value . size == 256 if len ( self . stack ) >= 1024 : raise StackOverflow ( ) if isinstance ( value , int ) : value = value & TT256M1 value = simplify ( value ) if isinstance ( value , Constant ) and not value . taint : value = value . value self . stack . append ( value )


def _top(self, n=0):
        """Read a value from the top of the stack without removing it"""
        if len(self.stack) - n < 0:
            raise StackUnderflow()
        return self.stack[n - 1]

def _top ( self , n = 0 ) : if len ( self . stack ) - n < 0 : raise StackUnderflow ( ) return self . stack [ n - 1 ]


def _rollback(self):
        """Revert the stack, gas, pc and memory allocation so it looks like before executing the instruction"""
        last_pc, last_gas, last_instruction, last_arguments, fee, allocated = self._checkpoint_data
        self._push_arguments(last_arguments)
        self._gas = last_gas
        self._pc = last_pc
        self._allocated = allocated
        self._checkpoint_data = None

def _rollback ( self ) : last_pc , last_gas , last_instruction , last_arguments , fee , allocated = self . _checkpoint_data self . _push_arguments ( last_arguments ) self . _gas = last_gas self . _pc = last_pc self . _allocated = allocated self . _checkpoint_data = None

Revert the stack gas pc and memory allocation so it looks like before executing the instruction


def _store(self, offset, value, size=1):
        """Stores value in memory as a big endian"""
        self.memory.write_BE(offset, value, size)
        for i in range(size):
            self._publish('did_evm_write_memory', offset + i, Operators.EXTRACT(value, (size - i - 1) * 8, 8))

def _store ( self , offset , value , size = 1 ) : self . memory . write_BE ( offset , value , size ) for i in range ( size ) : self . _publish ( 'did_evm_write_memory' , offset + i , Operators . EXTRACT ( value , ( size - i - 1 ) * 8 , 8 ) )


def DIV(self, a, b):
        """Integer division operation"""
        try:
            result = Operators.UDIV(a, b)
        except ZeroDivisionError:
            result = 0
        return Operators.ITEBV(256, b == 0, 0, result)

def DIV ( self , a , b ) : try : result = Operators . UDIV ( a , b ) except ZeroDivisionError : result = 0 return Operators . ITEBV ( 256 , b == 0 , 0 , result )