Merge pull request #2 from b-mueller/master

merge from upstream
pull/50/head
step21 7 years ago committed by GitHub
commit 7b2ee2f936
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 54
      README.md
  2. 85
      examples/wallet.sol
  3. 357
      examples/walletlibrary.sol
  4. 216
      myth
  5. 51
      mythril/disassembler/callgraph.py
  6. 96
      mythril/disassembler/disassembly.py
  7. 8
      mythril/ether/contractstorage.py
  8. 28
      mythril/ether/ethcontract.py
  9. 5
      mythril/ether/evm.py
  10. 63
      mythril/ether/util.py
  11. 2
      mythril/exceptions.py
  12. 184
      mythril/ipc/client.py
  13. 39
      mythril/rpc/client.py
  14. 0
      mythril/support/__init__.py
  15. 43
      mythril/support/loader.py
  16. 4
      requirements.txt
  17. 6
      setup.py
  18. BIN
      static/callgraph6.png
  19. BIN
      static/callgraph7.png
  20. 82
      static/mythril.html
  21. 19
      tests/cmline_test.py
  22. 15
      tests/disassembler_test.py
  23. 4
      tests/ethcontract_test.py
  24. 2
      tests/evm_test.py
  25. 1
      tests/ipc_test.py
  26. 2
      tests/rpc_test.py
  27. 19
      tests/svm_test.py
  28. 15
      tests/testdata/metacoin.sol
  29. 2
      tests/util_test.py

@ -4,6 +4,17 @@
Mythril is a reverse engineering and bug hunting framework for the Ethereum blockchain.
* [Installation and setup](#installation-and-setup)
* [Command line usage](#command-line-usage)
+ [Input formats](#input-formats)
- [Working with on-chain contracts](#working-with-on-chain-contracts)
- [Working with Solidity files](#working-with-solidity-files)
+ [Disassembler](#disassembler)
+ [Control flow graph](#control-flow-graph)
+ [Contract search](#contract-search)
- [Searching from the command line](#searching-from-the-command-line)
- [Finding cross-references](#finding-cross-references)
## Installation and setup
Install from Pypi:
@ -22,15 +33,35 @@ $ python setup.py install
Note that Mythril requires Python 3.5 to work.
You also need a [go-ethereum](https://github.com/ethereum/go-ethereum) node that is synced with the network (note that Mythril uses non-standard RPC APIs only supported by go-ethereum, so other clients likely won't work). Start the node as follows:
## Command line usage
The Mythril command line tool (aptly named `myth`) allows you to conveniently access most of Mythril's functionality.
### Input formats
Mythril can handle various sources and input formats, including bytecode, addresses of contracts on the blockchain, and Solidity source code files.
#### Working with on-chain contracts
To pull contracts from the blockchain you need an Ethereum node that is synced with the network. By default, Mythril will query a local node via RPC. Alternatively, you can connect to a remote service such as [INFURA](https://infura.io):
```
$ myth --rpchost=mainnet.infura.io/{API-KEY} --rpcport=443 --rpctls=True (... etc ...)
```
The recommended way is to use [go-ethereum](https://github.com/ethereum/go-ethereum). Start your local node as follows:
```bash
$ geth --rpc --rpcapi eth,debug --syncmode fast
```
## Command line usage
#### Working with Solidity files
In order to work with Solidity source code files, the [solc command line compiler](http://solidity.readthedocs.io/en/develop/using-the-compiler.html) needs to be installed and in path. You can then provide the source file(s) as positional arguments, e.g.:
The Mythril command line tool (aptly named `myth`) allows you to conveniently access some of Mythril's functionality.
```bash
$ myth -g ./graph.html myContract.sol
```
### Disassembler
@ -62,9 +93,9 @@ Mythril integrates the LASER symbolic virtual machine. Right now, this is mainly
$ myth -g ./graph.html -a "0xFa52274DD61E1643d2205169732f29114BC240b3"
```
![callgraph](https://raw.githubusercontent.com/b-mueller/mythril/master/static/callgraph6.png "Call graph")
![callgraph](https://raw.githubusercontent.com/b-mueller/mythril/master/static/callgraph7.png "Call graph")
The "bounce" effect, while awesome (and thus enabled by default), sometimes messes up the graph layout. If that happens, disable the effect with the `--disable-physics` flag.
~~The "bounce" effect, while awesome (and thus enabled by default), sometimes messes up the graph layout.~~ Try adding the `--enable-physics` flag for a very entertaining "bounce" effect that unfortunately completely destroys usability.
### Contract search
@ -77,8 +108,6 @@ Processing block 4323000, 3 individual contracts in database
(...)
```
Mythril retrieves contract data over RPC by default. You can switch to IPC using the `--ipc` flag.
The default behavior is to only sync contracts with a non-zero balance. You can disable this behavior with the `--sync-all` flag, but be aware that this will result in a huge (as in: dozens of GB) database.
#### Searching from the command line
@ -99,19 +128,10 @@ It is often useful to find other contracts referenced by a particular contract.
$ myth --search "code#DELEGATECALL#"
Matched contract with code hash 07459966443977122e639cbf7804c446
Address: 0x76799f77587738bfeef09452df215b63d2cfb08a, balance: 1000000000000000
$ myth --xrefs 07459966443977122e639cbf7804c446
$ myth --xrefs -a 0x76799f77587738bfeef09452df215b63d2cfb08a
5b9e8728e316bbeb692d22daaab74f6cbf2c4691
```
## Issues
The database sync is currently not very efficient.
- Using PyEthereum: I encountered issues syncing PyEthereum with Homestead. Also, PyEthApp only supports Python 2.7, which causes issues with other important packages.
- Accessing the Go-Ethereum LevelDB: This would be a great option. However, PyEthereum database code seems unable to deal with Go-Ethereum's LevelDB. It would take quite a bit of effort to figure this out.
I'm writing this in my spare time, so contributors would be highly welcome!
## Credit
- JSON RPC library is adapted from [ethjsonrpc](https://github.com/ConsenSys/ethjsonrpc) (it doesn't seem to be maintained anymore, and I needed to make some changes to it).

@ -0,0 +1,85 @@
//sol Wallet
// Multi-sig, daily-limited account proxy/wallet.
// @authors:
// Gav Wood <g@ethdev.com>
// inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a
// single, or, crucially, each of a number of, designated owners.
// usage:
// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
// interior is executed.
pragma solidity ^0.4.9;
contract WalletEvents {
// EVENTS
// this contract only has six types of events: it can accept a confirmation, in which case
// we record owner and operation (hash) alongside it.
event Confirmation(address owner, bytes32 operation);
event Revoke(address owner, bytes32 operation);
// some others are in the case of an owner changing.
event OwnerChanged(address oldOwner, address newOwner);
event OwnerAdded(address newOwner);
event OwnerRemoved(address oldOwner);
// the last one is emitted if the required signatures change
event RequirementChanged(uint newRequirement);
// Funds has arrived into the wallet (record how much).
event Deposit(address _from, uint value);
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
event SingleTransact(address owner, uint value, address to, bytes data, address created);
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data, address created);
// Confirmation still needed for a transaction.
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
}
contract Wallet is WalletEvents {
// METHODS
// gets called when no other function matches
function() payable {
// just being sent some cash?
if (msg.value > 0)
Deposit(msg.sender, msg.value);
else if (msg.data.length > 0)
_walletLibrary.delegatecall(msg.data);
}
// Gets an owner by 0-indexed position (using numOwners as the count)
function getOwner(uint ownerIndex) constant returns (address) {
return address(m_owners[ownerIndex + 1]);
}
// As return statement unavailable in fallback, explicit the method here
function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool) {
return _walletLibrary.delegatecall(msg.data);
}
function isOwner(address _addr) constant returns (bool) {
return _walletLibrary.delegatecall(msg.data);
}
// FIELDS
address constant _walletLibrary = 0x1111111111111111111111111111111111111111;
// the number of owners that must confirm the same operation before it is run.
uint public m_required;
// pointer used to find a free slot in m_owners
uint public m_numOwners;
uint public m_dailyLimit;
uint public m_spentToday;
uint public m_lastDay;
// list of owners
uint[256] m_owners;
}

@ -0,0 +1,357 @@
pragma solidity ^0.4.9;
contract WalletLibrary {
// EVENTS
// this contract only has six types of events: it can accept a confirmation, in which case
// we record owner and operation (hash) alongside it.
event Confirmation(address owner, bytes32 operation);
event Revoke(address owner, bytes32 operation);
// some others are in the case of an owner changing.
event OwnerChanged(address oldOwner, address newOwner);
event OwnerAdded(address newOwner);
event OwnerRemoved(address oldOwner);
// the last one is emitted if the required signatures change
event RequirementChanged(uint newRequirement);
// Funds has arrived into the wallet (record how much).
event Deposit(address _from, uint value);
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
event SingleTransact(address owner, uint value, address to, bytes data, address created);
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data, address created);
// Confirmation still needed for a transaction.
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
// TYPES
// struct for the status of a pending operation.
struct PendingState {
uint yetNeeded;
uint ownersDone;
uint index;
}
// Transaction structure to remember details of transaction lest it need be saved for a later call.
struct Transaction {
address to;
uint value;
bytes data;
}
// MODIFIERS
// simple single-sig function modifier.
modifier onlyowner {
if (isOwner(msg.sender))
_;
}
// multi-sig function modifier: the operation must have an intrinsic hash in order
// that later attempts can be realised as the same underlying operation and
// thus count as confirmations.
modifier onlymanyowners(bytes32 _operation) {
if (confirmAndCheck(_operation))
_;
}
// METHODS
// gets called when no other function matches
function() payable {
// just being sent some cash?
if (msg.value > 0)
Deposit(msg.sender, msg.value);
}
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
// as well as the selection of addresses capable of confirming them.
function initMultiowned(address[] _owners, uint _required) {
m_numOwners = _owners.length + 1;
m_owners[1] = uint(msg.sender);
m_ownerIndex[uint(msg.sender)] = 1;
for (uint i = 0; i < _owners.length; ++i)
{
m_owners[2 + i] = uint(_owners[i]);
m_ownerIndex[uint(_owners[i])] = 2 + i;
}
m_required = _required;
}
// Revokes a prior confirmation of the given operation
function revoke(bytes32 _operation) external {
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
// make sure they're an owner
if (ownerIndex == 0) return;
uint ownerIndexBit = 2**ownerIndex;
var pending = m_pending[_operation];
if (pending.ownersDone & ownerIndexBit > 0) {
pending.yetNeeded++;
pending.ownersDone -= ownerIndexBit;
Revoke(msg.sender, _operation);
}
}
// Replaces an owner `_from` with another `_to`.
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external {
if (isOwner(_to)) return;
uint ownerIndex = m_ownerIndex[uint(_from)];
if (ownerIndex == 0) return;
clearPending();
m_owners[ownerIndex] = uint(_to);
m_ownerIndex[uint(_from)] = 0;
m_ownerIndex[uint(_to)] = ownerIndex;
OwnerChanged(_from, _to);
}
function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
if (isOwner(_owner)) return;
clearPending();
if (m_numOwners >= c_maxOwners)
reorganizeOwners();
if (m_numOwners >= c_maxOwners)
return;
m_numOwners++;
m_owners[m_numOwners] = uint(_owner);
m_ownerIndex[uint(_owner)] = m_numOwners;
OwnerAdded(_owner);
}
function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
uint ownerIndex = m_ownerIndex[uint(_owner)];
if (ownerIndex == 0) return;
if (m_required > m_numOwners - 1) return;
m_owners[ownerIndex] = 0;
m_ownerIndex[uint(_owner)] = 0;
clearPending();
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
OwnerRemoved(_owner);
}
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
if (_newRequired > m_numOwners) return;
m_required = _newRequired;
clearPending();
RequirementChanged(_newRequired);
}
// Gets an owner by 0-indexed position (using numOwners as the count)
function getOwner(uint ownerIndex) external constant returns (address) {
return address(m_owners[ownerIndex + 1]);
}
function isOwner(address _addr) constant returns (bool) {
return m_ownerIndex[uint(_addr)] > 0;
}
function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool) {
var pending = m_pending[_operation];
uint ownerIndex = m_ownerIndex[uint(_owner)];
// make sure they're an owner
if (ownerIndex == 0) return false;
// determine the bit to set for this owner.
uint ownerIndexBit = 2**ownerIndex;
return !(pending.ownersDone & ownerIndexBit == 0);
}
// constructor - stores initial daily limit and records the present day's index.
function initDaylimit(uint _limit) {
m_dailyLimit = _limit;
m_lastDay = today();
}
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
m_dailyLimit = _newLimit;
}
// resets the amount already spent today. needs many of the owners to confirm.
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
m_spentToday = 0;
}
// constructor - just pass on the owner array to the multiowned and
// the limit to daylimit
function initWallet(address[] _owners, uint _required, uint _daylimit) {
initDaylimit(_daylimit);
initMultiowned(_owners, _required);
}
// kills the contract sending everything to `_to`.
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
suicide(_to);
}
// Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
// and _data arguments). They still get the option of using them if they want, anyways.
function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 o_hash) {
// first, take the opportunity to check that we're under the daily limit.
if ((_data.length == 0 && underLimit(_value)) || m_required == 1) {
// yes - just execute the call.
address created;
if (_to == 0) {
created = create(_value, _data);
} else {
if (!_to.call.value(_value)(_data))
throw;
}
SingleTransact(msg.sender, _value, _to, _data, created);
} else {
// determine our operation hash.
o_hash = sha3(msg.data, block.number);
// store if it's new
if (m_txs[o_hash].to == 0 && m_txs[o_hash].value == 0 && m_txs[o_hash].data.length == 0) {
m_txs[o_hash].to = _to;
m_txs[o_hash].value = _value;
m_txs[o_hash].data = _data;
}
if (!confirm(o_hash)) {
ConfirmationNeeded(o_hash, msg.sender, _value, _to, _data);
}
}
}
function create(uint _value, bytes _code) internal returns (address o_addr) {
assembly {
o_addr := create(_value, add(_code, 0x20), mload(_code))
// jumpi(invalidJumpLabel, iszero(extcodesize(o_addr)))
}
}
// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
// to determine the body of the transaction from the hash provided.
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool o_success) {
if (m_txs[_h].to != 0 || m_txs[_h].value != 0 || m_txs[_h].data.length != 0) {
address created;
if (m_txs[_h].to == 0) {
created = create(m_txs[_h].value, m_txs[_h].data);
} else {
if (!m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data))
throw;
}
MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data, created);
delete m_txs[_h];
return true;
}
}
// INTERNAL METHODS
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
// determine what index the present sender is:
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
// make sure they're an owner
if (ownerIndex == 0) return;
var pending = m_pending[_operation];
// if we're not yet working on this operation, switch over and reset the confirmation status.
if (pending.yetNeeded == 0) {
// reset count of confirmations needed.
pending.yetNeeded = m_required;
// reset which owners have confirmed (none) - set our bitmap to 0.
pending.ownersDone = 0;
pending.index = m_pendingIndex.length++;
m_pendingIndex[pending.index] = _operation;
}
// determine the bit to set for this owner.
uint ownerIndexBit = 2**ownerIndex;
// make sure we (the message sender) haven't confirmed this operation previously.
if (pending.ownersDone & ownerIndexBit == 0) {
Confirmation(msg.sender, _operation);
// ok - check if count is enough to go ahead.
if (pending.yetNeeded <= 1) {
// enough confirmations: reset and run interior.
delete m_pendingIndex[m_pending[_operation].index];
delete m_pending[_operation];
return true;
}
else
{
// not enough: record that this owner in particular confirmed.
pending.yetNeeded--;
pending.ownersDone |= ownerIndexBit;
}
}
}
function reorganizeOwners() private {
uint free = 1;
while (free < m_numOwners)
{
while (free < m_numOwners && m_owners[free] != 0) free++;
while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
{
m_owners[free] = m_owners[m_numOwners];
m_ownerIndex[m_owners[free]] = free;
m_owners[m_numOwners] = 0;
}
}
}
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
// returns true. otherwise just returns false.
function underLimit(uint _value) internal onlyowner returns (bool) {
// reset the spend limit if we're on a different day to last time.
if (today() > m_lastDay) {
m_spentToday = 0;
m_lastDay = today();
}
// check to see if there's enough left - if so, subtract and return true.
// overflow protection // dailyLimit check
if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
m_spentToday += _value;
return true;
}
return false;
}
// determines today's index.
function today() private constant returns (uint) { return now / 1 days; }
function clearPending() internal {
uint length = m_pendingIndex.length;
for (uint i = 0; i < length; ++i) {
delete m_txs[m_pendingIndex[i]];
if (m_pendingIndex[i] != 0)
delete m_pending[m_pendingIndex[i]];
}
delete m_pendingIndex;
}
// FIELDS
address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;
// the number of owners that must confirm the same operation before it is run.
uint public m_required;
// pointer used to find a free slot in m_owners
uint public m_numOwners;
uint public m_dailyLimit;
uint public m_spentToday;
uint public m_lastDay;
// list of owners
uint[256] m_owners;
uint constant c_maxOwners = 250;
// index on the list of owners to allow reverse lookup
mapping(uint => uint) m_ownerIndex;
// the ongoing operations.
mapping(bytes32 => PendingState) m_pending;
bytes32[] m_pendingIndex;
// pending transactions we have at present.
mapping (bytes32 => Transaction) m_txs;
}

216
myth

@ -4,16 +4,19 @@
http://www.github.com/b-mueller/mythril
"""
from mythril.ether import evm,util
from mythril.disassembler.disassembly import Disassembly
from mythril.ether import evm, util
from mythril.disassembler.callgraph import generate_callgraph
from mythril.ether.contractstorage import get_persistent_storage
from mythril.ether.ethcontract import ETHContract
from mythril.ether.util import compile_solidity
from mythril.rpc.client import EthJsonRpc
from mythril.ipc.client import EthIpc
from mythril.support.loader import DynLoader
from mythril.exceptions import CompilerError
from ethereum import utils
from laser.ethereum import laserfree
from laser.ethereum import svm, laserfree
from pathlib import Path
import logging
import binascii
import sys
import argparse
import os
@ -21,7 +24,7 @@ import re
def searchCallback(code_hash, code, addresses, balances):
print("Matched contract with code hash " + code_hash )
print("Matched contract with code hash " + code_hash)
for i in range(0, len(addresses)):
print("Address: " + addresses[i] + ", balance: " + str(balances[i]))
@ -33,119 +36,154 @@ def exitWithError(message):
parser = argparse.ArgumentParser(description='Bug hunting on the Ethereum blockchain')
parser.add_argument("solidity_file", nargs='*')
commands = parser.add_argument_group('commands')
commands.add_argument('-d', '--disassemble', action='store_true', help='disassemble, specify input with -c or -a')
commands.add_argument('-t', '--trace', action='store_true', help='trace, use with -c or -a and --data (optional)')
commands.add_argument('-g', '--graph', help='generate a call graph', metavar='OUTPUT_FILE')
commands.add_argument('-l', '--fire-lasers', action='store_true', help='detect vulnerabilities, use with -c or -a')
commands.add_argument('-s', '--search', help='search the contract database')
commands.add_argument('--xrefs', help='get xrefs from contract in database', metavar='CONTRACT_HASH')
commands.add_argument('-d', '--disassemble', action='store_true', help='disassemble')
commands.add_argument('-g', '--graph', help='generate a control flow graph', metavar='OUTPUT_FILE')
commands.add_argument('-x', '--fire-lasers', action='store_true', help='detect vulnerabilities')
commands.add_argument('-t', '--trace', action='store_true', help='trace contract, use with --data (optional)')
commands.add_argument('-s', '--search', help='search the contract database', metavar='EXPRESSION')
commands.add_argument('--xrefs', action='store_true', help='get xrefs from a contract')
commands.add_argument('--hash', help='calculate function signature hash', metavar='SIGNATURE')
commands.add_argument('--init-db', action='store_true', help='initialize the contract database')
inputs = parser.add_argument_group('input arguments')
inputs.add_argument('-c', '--code', help='hex-encoded bytecode string ("6060604052...")', metavar='BYTECODE')
inputs.add_argument('-a', '--address', help='contract address')
inputs.add_argument('-a', '--address', help='pull contract from the blockchain', metavar='CONTRACT_ADDRESS')
inputs.add_argument('-l', '--dynld', action='store_true', help='auto-load dependencies (experimental)')
inputs.add_argument('--data', help='message call input data for tracing')
options = parser.add_argument_group('options')
options.add_argument('--sync-all', action='store_true', help='Also sync contracts with zero balance')
options.add_argument('--infura-mainnet', action='store_true', help='Use Infura Node service, equivalent to: --rpchost=mainnet.infura.io --rpcport=443 --rpctls="True"')
options.add_argument('--infura-rinkeby', action='store_true', help='Use Infura Node service, equivalent to: --rpchost=rinkeby.infura.io --rpcport=443 --rpctls="True"')
options.add_argument('--infura-kovan', action='store_true', help='Use Infura Node service, equivalent to: --rpchost=kovan.infura.io --rpcport=443 --rpctls="True"')
options.add_argument('--infura-ropsten', action='store_true', help='Use Infura Node service, equivalent to: --rpchost=ropsten.infura.io --rpcport=443 --rpctls="True"')
options.add_argument('--rpchost', default='127.0.0.1', help='RPC host')
options.add_argument('--rpcport', type=int, default=8545, help='RPC port')
options.add_argument('--rpctls', type=bool, default=False, help='RPC port')
options.add_argument('--ipc', help='use IPC interface instead of RPC', action='store_true')
options.add_argument('--disable-physics', action='store_true', help='disable graph physics simulation')
options.add_argument('--enable-physics', type=bool, default=False, help='enable graph physics simulation')
options.add_argument('-v', type=int, help='log level (0-2)', metavar='LOG_LEVEL')
# Get config values
try:
db_dir = os.environ['DB_DIR']
except KeyError:
db_dir = None
try:
solc_binary = os.environ['SOLC']
except KeyError:
solc_binary = 'solc'
# Parse cmdline args
args = parser.parse_args()
if not (args.search or args.init_db or args.hash or args.disassemble or args.graph or args.xrefs or args.fire_lasers or args.trace):
parser.print_help()
sys.exit()
if (args.v):
if (0 <= args.v < 3):
logging.basicConfig(level=[logging.NOTSET, logging.INFO, logging.DEBUG][args.v])
if (args.disassemble or args.graph or args.fire_lasers):
elif (args.hash):
print("0x" + utils.sha3(args.hash)[:4].hex())
sys.exit()
# Database search ops
if args.search or args.init_db:
if (args.code):
encoded_bytecode = args.code
elif (args.address):
contract_storage = get_persistent_storage(db_dir)
if (args.search):
try:
contract_storage.search(args.search, searchCallback)
except SyntaxError:
exitWithError("Syntax error in search expression.")
elif (args.init_db):
contract_storage.initialize(args.rpchost, args.rpcport, args.rpctls, args.sync_all, args.ipc)
sys.exit()
# Establish RPC/IPC connection if necessary
if (args.address or len(args.solidity_file)):
if args.ipc:
try:
eth = EthIpc()
encoded_bytecode = eth.eth_getCode(args.address)
except Exception as e:
exitWithError("Exception loading bytecode via IPC: " + str(e))
exitWithError("Error establishing IPC connection: " + str(e))
else:
try:
eth = EthJsonRpc(args.rpchost, args.rpcport)
encoded_bytecode = eth.eth_getCode(args.address)
if args.infura_mainnet:
eth = EthJsonRpc('mainnet.infura.io', 443, True)
elif args.infura_rinkeby:
eth = EthJsonRpc('rinkeby.infura.io', 443, True)
elif args.infura_kovan:
eth = EthJsonRpc('kovan.infura.io', 443, True)
elif args.infura_ropsten:
eth = EthJsonRpc('ropsten.infura.io', 443, True)
else:
eth = EthJsonRpc(args.rpchost, args.rpcport, args.rpctls)
except Exception as e:
exitWithError("Exception loading bytecode via RPC: " + str(e))
else:
exitWithError("No input bytecode. Please provide the code via -c BYTECODE or -a address")
try:
disassembly = Disassembly(encoded_bytecode)
except binascii.Error:
exitWithError("Disassembler: Invalid code string.")
exitWithError("Error establishing RPC connection: " + str(e))
if (args.disassemble):
# Load / compile input contracts
easm_text = disassembly.get_easm()
sys.stdout.write(easm_text)
contracts = []
elif (args.graph):
if (args.code):
contracts.append(ETHContract(args.code, name="MAIN", address = util.get_indexed_address(0)))
elif (args.address):
contracts.append(ETHContract(eth.eth_getCode(args.address), name=args.address, address = args.address))
elif (len(args.solidity_file)):
index = 0
if (args.disable_physics):
physics = False
else:
physics = True
for file in args.solidity_file:
html = generate_callgraph(disassembly, physics)
file = file.replace("~", str(Path.home())) # Expand user path
try:
with open(args.graph, "w") as f:
f.write(html)
except Exception as e:
print("Error saving graph: " + str(e))
name, bytecode = compile_solidity(solc_binary, file)
except CompilerError as e:
exitWithError(e)
# Max. 16 contracts supported!
elif (args.fire_lasers):
contract = ETHContract(bytecode, name = name, address = util.get_indexed_address(index))
index += 1
laserfree.fire(disassembly)
contracts.append(contract)
logging.info(contract.name + " at " + contract.address)
elif (args.trace):
else:
exitWithError("No input bytecode. Please provide EVM code via -c BYTECODE, -a ADDRESS, or -i SOLIDITY_FILES")
if (args.code):
encoded_bytecode = args.code
# Commands
elif (args.address):
if args.ipc:
eth = EthIpc()
encoded_bytecode = eth.eth_getCode(args.address)
if (args.disassemble):
else:
eth = EthJsonRpc(args.rpchost, args.rpcport)
encoded_bytecode = eth.eth_getCode(args.address)
easm_text = contracts[0].get_easm()
sys.stdout.write(easm_text)
else:
exitWithError("Disassembler: Provide the input bytecode via -c BYTECODE or --id ID")
elif (args.trace):
if (args.data):
trace = evm.trace(encoded_bytecode, args.data)
trace = evm.trace(contracts[0].code, args.data)
else:
trace = evm.trace(encoded_bytecode)
trace = evm.trace(contracts[0].code)
for i in trace:
if (re.match(r'^PUSH.*', i['op'])):
@ -153,36 +191,44 @@ elif (args.trace):
else:
print(str(i['pc']) + " " + i['op'] + ";\tSTACK: " + i['stack'])
else:
contract_storage = get_persistent_storage(db_dir)
if (args.search):
elif (args.xrefs):
try:
contract_storage.search(args.search, searchCallback)
except SyntaxError:
exitWithError("Syntax error in search expression.")
print("\n".join(contracts[0].get_xrefs()))
elif (args.xrefs):
elif (args.graph) or (args.fire_lasers):
try:
contract_hash = util.safe_decode(args.xrefs)
except binascii.Error:
exitWithError("Invalid contract hash.")
# Convert to LASER SVM format
try:
contract = contract_storage.get_contract_by_hash(contract_hash)
print("\n".join(contract.get_xrefs()))
except KeyError:
exitWithError("Contract not found in the database.")
modules = {}
elif (args.init_db):
if args.ipc:
contract_storage.initialize(args.rpchost, args.rpcport, args.sync_all, args.ipc)
else:
contract_storage.initialize(args.rpchost, args.rpcport, args.sync_all, args.ipc)
for contract in contracts:
modules[contract.address] = contract.as_dict()
if (args.dynld):
loader = DynLoader(eth)
_svm = svm.SVM(modules, dynamic_loader=loader)
else:
_svm = svm.SVM(modules)
if (args.graph):
_svm.simplify_model = True
if args.enable_physics is not None:
physics = True
html = generate_callgraph(_svm, contracts[0].address, args.enable_physics)
try:
with open(args.graph, "w") as f:
f.write(html)
except Exception as e:
elif (args.hash):
print("0x" + utils.sha3(args.hash)[:4].hex())
print("Error saving graph: " + str(e))
else:
parser.print_help()
laserfree.fire(modules, contracts[0].address)
else:
parser.print_help()

@ -1,4 +1,3 @@
from laser.ethereum import svm
from z3 import Z3Exception, simplify
import re
@ -7,11 +6,11 @@ graph_html = '''<html>
<head>
<style type="text/css">
#mynetwork {
background-color: #000000;
background-color: #232625;
}
body {
background-color: #000000;
background-color: #232625;
color: #ffffff;
}
</style>
@ -49,14 +48,6 @@ graph_html = '''<html>
align: 'left',
color: '#FFFFFF',
},
color: {
border: '#26996f',
background: '#1f7e5b',
highlight: {
border: '#26996f',
background: '#28a16f'
}
}
},
edges:{
font: {
@ -95,18 +86,28 @@ var gph = new vis.Network(container, data, options);
'''
def serialize(_svm):
colors = [
"{border: '#26996f', background: '#2f7e5b', highlight: {border: '#26996f', background: '#28a16f'}}",
"{border: '#9e42b3', background: '#842899', highlight: {border: '#9e42b3', background: '#933da6'}}",
"{border: '#b82323', background: '#991d1d', highlight: {border: '#b82323', background: '#a61f1f'}}",
"{border: '#4753bf', background: '#3b46a1', highlight: {border: '#4753bf', background: '#424db3'}}",
]
def serialize(_svm, color_map):
nodes = []
edges = []
for n in _svm.nodes:
for node_key in _svm.nodes:
code = _svm.nodes[n].as_dict()['code']
code = _svm.nodes[node_key].as_dict()['code']
code = re.sub("([0-9a-f]{8})[0-9a-f]+", lambda m: m.group(1) + "(...)", code)
nodes.append("{id: " + str(_svm.nodes[n].as_dict()['id']) + ", size: 150, 'label': '" + code + "'}")
color = color_map[_svm.nodes[node_key].as_dict()['module_name']]
nodes.append("{id: '" + node_key + "', color: " + color + ", size: 150, 'label': '" + code + "'}")
for edge in _svm.edges:
@ -118,23 +119,29 @@ def serialize(_svm):
label = str(simplify(edge.condition)).replace("\n", "")
except Z3Exception:
label = str(edge.condition).replace("\n", "")
label = re.sub("[^_]([[\d]{2}\d+)", lambda m: hex(int(m.group(1))), label)
label = re.sub("([^_])([\d]{2}\d+)", lambda m: m.group(1) + hex(int(m.group(2))), label)
code = re.sub("([0-9a-f]{8})[0-9a-f]+", lambda m: m.group(1) + "(...)", code)
edges.append("{from: " + str(edge.as_dict()['from']) + ', to: ' + str(edge.as_dict()['to']) + ", 'arrows': 'to', 'label': '" + label + "', 'smooth': {'type': 'cubicBezier'}}")
edges.append("{from: '" + str(edge.as_dict()['from']) + "', to: '" + str(edge.as_dict()['to']) + "', 'arrows': 'to', 'label': '" + label + "', 'smooth': {'type': 'cubicBezier'}}")
return "var nodes = [\n" + ",\n".join(nodes) + "\n];\nvar edges = [\n" + ",\n".join(edges) + "\n];"
def generate_callgraph(disassembly, physics):
def generate_callgraph(svm, main_address, physics):
svm.sym_exec(main_address)
i = 0
_svm = svm.SVM(disassembly)
color_map = {}
_svm.sym_exec()
for k in svm.modules:
color_map[svm.modules[k]['name']] = colors[i]
i += 1
html = graph_html.replace("[JS]", serialize(_svm))
html = graph_html.replace("[JS]", serialize(svm, color_map))
html = html.replace("[ENABLE_PHYSICS]", str(physics).lower())
return html

@ -3,32 +3,10 @@ import os
import json
class Block:
def __init__(self, id, code_index, funcname):
self.id = id
self.code_index = code_index
self.funcname = funcname
self.instruction_list = []
def update_length(self, num_instructions):
self.length = num_instructions
self.start_addr = self.instruction_list[0]['address']
self.end_addr = self.instruction_list[-1]['address']
def get_easm(self):
easm = str(self.instruction_list[0]['address']) + " " + self.funcname + "\n"
easm += asm.instruction_list_to_easm(self.instruction_list[1:])
return easm
class Disassembly:
def __init__(self, code):
self.instruction_list = asm.disassemble(util.safe_decode(code))
self.blocks = []
self.xrefs = []
self.func_to_addr = {}
self.addr_to_func = {}
@ -59,80 +37,8 @@ class Disassembly:
except:
continue
# Parse instructions into basic blocks
current_block = Block(0, 0, "PROLOGUE")
index = 0
blocklen = 0
blocknumber = 1
for instruction in self.instruction_list:
if (instruction['opcode'] == "JUMPDEST"):
try:
func_name = "- FUNCTION " + self.addr_to_func[instruction['address']] + " -"
except KeyError:
func_name = "- JUMPDEST_UNK -"
current_block.update_length(blocklen)
self.blocks.append(current_block)
current_block = Block(blocknumber, index, func_name)
blocklen = 0
blocknumber += 1
current_block.instruction_list.append(instruction)
blocklen += 1
index += 1
# Add the last block
current_block.update_length(blocklen)
self.blocks.append(current_block)
# Resolve cross-references
for block in self.blocks:
jmp_indices = asm.find_opcode_sequence(["JUMP"], block.instruction_list)
jmp_indices += asm.find_opcode_sequence(["JUMPI"], block.instruction_list)
for i in jmp_indices:
try:
dest_hex = block.instruction_list[i - 1]['argument']
dest = int(dest_hex[2:], 16)
except:
continue
j = 0
try:
while(self.blocks[j].end_addr < dest):
j += 1
except IndexError:
continue
if not (block.id, self.blocks[j].id) in self.xrefs:
self.xrefs.append((block.id, self.blocks[j].id))
# if the last instruction isn't an unconditional jump or halt, also add a reference to the following block
try:
if (block.id < len(self.blocks)) and (block.instruction_list[block.length - 1]['opcode'] not in ['JUMP', 'STOP', 'THROW', 'REVERT', 'INVALID']):
if not (block.id, self.blocks[j].id) in self.xrefs:
self.xrefs.append((block.id, block.id + 1))
except UnboundLocalError:
# quickfix
continue
def get_easm(self):
easm = asm.instruction_list_to_easm(self.instruction_list[0:self.blocks[0].length])
for block in self.blocks[1:]:
easm += block.get_easm()
return easm
return asm.instruction_list_to_easm(self.instruction_list)

@ -47,11 +47,11 @@ class ContractStorage(persistent.Persistent):
return self.contracts[contract_hash]
def initialize(self, rpchost, rpcport, sync_all, ipc):
def initialize(self, rpchost, rpcport, rpctls, sync_all, ipc):
if ipc:
eth = EthIpc()
else:
eth = EthJsonRpc(rpchost, rpcport)
eth = EthJsonRpc(rpchost, rpcport, rpctls)
if self.last_block:
blockNum = self.last_block
@ -105,6 +105,10 @@ class ContractStorage(persistent.Persistent):
self.last_block = blockNum
blockNum -= 1
# If we've finished initializing the database, start over from the end of the chain if we want to initialize again
self.last_block = 0
transaction.commit()
def search(self, expression, callback_func):

@ -1,20 +1,32 @@
from mythril.ether import asm, util
import re
import persistent
from mythril.disassembler.disassembly import Disassembly
from ethereum import utils
import persistent
import re
class ETHContract(persistent.Persistent):
def __init__(self, code, creation_code = ""):
def __init__(self, code, creation_code="", name="", address=""):
self.code = code
self.creation_code = creation_code
self.name = name
self.address = address
def as_dict(self):
return {
'address': self.address,
'name': self.name,
'code': self.code,
'creation_code': self.creation_code,
'disassembly': self.get_disassembly()
}
def get_xrefs(self):
instruction_list = asm.disassemble(util.safe_decode(self.code))
instruction_list = Disassembly(self.code).instruction_list
xrefs = []
@ -30,14 +42,14 @@ class ETHContract(persistent.Persistent):
return xrefs
def get_instruction_list(self):
def get_disassembly(self):
return asm.disassemble(util.safe_decode(self.code))
return Disassembly(self.code)
def get_easm(self):
return asm.instruction_list_to_easm(asm.disassemble(util.safe_decode(self.code)))
return Disassembly(self.code).get_easm()
def matches_expression(self, expression):

@ -7,7 +7,6 @@ from io import StringIO
import re
def trace(code, calldata = ""):
logHandlers = ['eth.vm.op', 'eth.vm.op.stack', 'eth.vm.op.memory', 'eth.vm.op.storage']
@ -57,7 +56,7 @@ def trace(code, calldata = ""):
if (len(stackitems)):
for i in range(0, len(stackitems) - 1):
stack += hex(int(stackitems[i])) + ", "
stack += hex(int(stackitems[i])) + ", "
stack += hex(int(stackitems[-1]))
@ -74,5 +73,3 @@ def trace(code, calldata = ""):
trace.append({'pc': pc, 'op': op, 'stack': stack})
return trace

@ -1,67 +1,52 @@
from mythril.rpc.client import EthJsonRpc
from mythril.ipc.client import EthIpc
from ethereum.abi import encode_abi, encode_int
from ethereum.utils import zpad
from ethereum.abi import method_id
from mythril.exceptions import CompilerError
import subprocess
from subprocess import Popen, PIPE
import binascii
import os
import re
def safe_decode(hex_encoded_string):
# print(type(hex_encoded_string))
def safe_decode(hex_encoded_string):
if (hex_encoded_string.startswith("0x")):
return bytes.fromhex(hex_encoded_string[2:])
# return codecs.decode(, 'hex_codec')
else:
return bytes.fromhex(hex_encoded_string)
# return codecs.decode(hex_encoded_string, 'hex_codec')
def bytecode_from_blockchain(creation_tx_hash, ipc, rpc_host='127.0.0.1', rpc_port=8545):
"""Load bytecode from a local node via
creation_tx_hash = ID of transaction that created the contract.
"""
if ipc:
pass
else:
eth = EthJsonRpc(rpc_host, rpc_port)
trace = eth.traceTransaction(creation_tx_hash)
def compile_solidity(solc_binary, file):
if trace['returnValue']:
try:
p = Popen(["solc", "--bin-runtime", file], stdout=PIPE, stderr=PIPE)
stdout, stderr = p.communicate()
except FileNotFoundError:
raise CompilerError("Compiler not found. Make sure that solc is installed and in PATH, or set the SOLC environment variable.")
return trace['returnValue']
out = stdout.decode("UTF-8")
raise RuntimeError("Transaction trace didn't return any bytecode")
if out == "":
err = "Error compiling input file. Solc returned:\n" + stderr.decode("UTF-8")
raise CompilerError(err)
# out = out.replace("[\n\s]", "")
def fire_lasers(disassembly):
return laserfree.analysis(disassembly)
m = re.search(r":(.*?) =======\nBinary of the runtime part: \n([0-9a-f]+)\n", out)
return [m.group(1), m.group(2)]
def encode_calldata(func_name, arg_types, args):
mid = method_id(func_name, arg_types)
function_selector = zpad(encode_int(mid), 4)
args = encode_abi(arg_types, args)
return "0x" + function_selector.hex() + args.hex()
def raw_bytes_to_file(filename, bytestring):
with open(filename, 'wb') as f:
f.write(bytestring)
def file_to_raw_bytes(filename):
with open(filename, 'rb') as f:
data = f.read()
return data
def get_random_address():
return binascii.b2a_hex(os.urandom(20)).decode('UTF-8')
def string_to_file(filename, string):
with open(filename, 'w') as f:
f.write(string)
def get_indexed_address(index):
return "0x" + (hex(index)[2:] * 40)
def file_to_string(filename):
with open(filename, 'r') as f:
data = f.read()
return data

@ -0,0 +1,2 @@
class CompilerError(Exception):
pass

@ -75,11 +75,12 @@ class EthIpc(object):
def call(self, address, sig, args, result_types):
'''
Call a contract function on the RPC server, without sending a
Call a contract function on the IPC server, without sending a
transaction (useful for reading data)
'''
data = self._encode_function(sig, args)
data_hex = data.encode('hex')
# could be made to use web3py directly, but instead uses eth_call which is adapted
response = self.eth_call(to_address=address, data=data_hex)
return decode_abi(result_types, response[2:].decode('hex'))
@ -103,7 +104,6 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#web3_clientversion
http://web3py.readthedocs.io/en/latest/web3.version.html#web3.version.Version.node
'''
return self.web3.version.node
@ -130,8 +130,8 @@ class EthIpc(object):
def net_listening(self):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#net_listening
ONLY indirectly available
TESTED
Only indirectly available
'''
return self.web3net.listening()
@ -139,7 +139,7 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#net_peercount
ONLY indirectly available
TESTED
'''
return self.web3.net.peerCount()
@ -181,7 +181,7 @@ class EthIpc(object):
http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=syncing#web3.eth.Eth.hashrate
'''
return hex_to_dec(web3.eth.hashrate)
return web3.eth.hashrate
def eth_gasPrice(self):
'''
@ -189,7 +189,7 @@ class EthIpc(object):
http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.gasPrice
'''
return hex_to_dec(web3.eth.gasPrice)
return web3.eth.gasPrice
def eth_accounts(self):
'''
@ -205,7 +205,7 @@ class EthIpc(object):
http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.blockNumber
'''
return hex_to_dec(self.web3.eth.blockNumber)
return self.web3.eth.blockNumber
def eth_getBalance(self, address=None, block=BLOCK_TAG_LATEST):
'''
@ -256,8 +256,9 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getunclecountbyblockhash
http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getUncle
(not implemented with convenience functions)
'''
return self.web3.manager.request_blocking('eth_getUncleCountByBlockHash', [block_hash])
return self.web3.eth.getUncleCount(block_hash)
def eth_getUncleCountByBlockNumber(self, block=BLOCK_TAG_LATEST):
@ -267,13 +268,13 @@ class EthIpc(object):
'''
block = validate_block(block)
self.web3.manager.request_blocking('debug_getBlockRlp', [number])
return self.web3.eth.getUncleCount(block)
def eth_getCode(self, address, default_block=BLOCK_TAG_LATEST):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getcode
http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getCode
NEEDS TESTING
'''
if isinstance(default_block, str):
if default_block not in BLOCK_TAGS:
@ -285,7 +286,6 @@ class EthIpc(object):
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.sign
either data= hexstr= or text= probably not needed now but if used should be differentiated
NEEDS TESTING
'''
return self.web3.eth.sign(address, data)
@ -294,7 +294,6 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sendtransaction
http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=syncing#web3.eth.Eth.sendTransaction
NEEDS TESTING
'''
params = {}
params['from'] = from_address or self.eth_coinbase()
@ -316,7 +315,6 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sendrawtransaction
http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=syncing#web3.eth.Eth.sendRawTransaction
NEEDS TESTING
'''
return self.web3.eth.sendRawTransaction(data)
@ -326,7 +324,6 @@ class EthIpc(object):
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_call
http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=syncing#web3.eth.Eth.call
NEEDS TESTING
'''
if isinstance(default_block, str):
if default_block not in BLOCK_TAGS:
@ -350,7 +347,6 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_estimategas
http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.estimateGas
NEEDS TESTING
'''
if isinstance(default_block, str):
if default_block not in BLOCK_TAGS:
@ -374,7 +370,6 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblockbyhash
http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getBlock
TESTED
'''
return self.web3.eth.getBlock(block_identifier=block_hash, full_transactions=tx_objects)
@ -382,7 +377,6 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblockbynumber
http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getBlock
TESTED
'''
block = validate_block(block)
return self.web3.eth.getBlock(block_identifier=block, full_transactions=tx_objects)
@ -391,7 +385,6 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionbyhash
http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getTransaction
TESTED
'''
return self.web3.eth.getTransactionByHash(tx_hash)
@ -399,7 +392,6 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionbyblockhashandindex
http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.getTransactionFromBlock
TESTED
'''
return self.web3.eth.getTransactionFromBlock(block_identifier=block_hash, transaction_index=index)
@ -408,7 +400,6 @@ class EthIpc(object):
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionbyblocknumberandindex
http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.getTransactionFromBlock
TESTED
'''
block = validate_block(block)
return self.web3.eth.getTransactionFromBlock(block_identifier=block, transaction_index=index)
@ -417,64 +408,60 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionreceipt
http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=gasprice#web3.eth.Eth.getTransactionReceipt
TESTED
'''
return self.web3.eth.getTransactionReceipt(tx_hash)
def eth_getUncleByBlockHashAndIndex(self, block_hash, index=0):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getunclebyblockhashandindex
NOT IMPLEMENTED
TESTED
Indirectly accessible
self.web3.manager.request_blocking('rpc/ipc function', [params])
'''
return "Not implemented"
return self.web3.manager.request_blocking('eth_getUncleByBlockHashAndIndex', [block_hash, web3.toHex(index)])
def eth_getUncleByBlockNumberAndIndex(self, block=BLOCK_TAG_LATEST, index=0):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getunclebyblocknumberandindex
NOT IMPLEMENTED
TESTED
Indirectly accessible
'''
block = validate_block(block)
return "Not implemented"
return self.web3.manager.request_blocking('eth_getUncleByBlockNumberAndIndex', [block, web3.toHex(index)])
def eth_getCompilers(self):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getcompilers
Does not seem to be implemented
TESTED
Indirectly implemented
'''
return "Not implemented"
return self.web3.manager.request_blocking('eth_getCompilers')
def eth_compileSolidity(self, code):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_compilesolidity
Implemented?
TESTED
Indirectly implemented
'''
return self.web3.eth.compileSolidity(code)
return self.web3.manager.request_blocking('eth_compileSolidity', [code])
def eth_compileLLL(self, code):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_compilelll
Implemented?
N/A
Indirectly accessible
'''
return self.web3.eth.compileLLL(code)
return self.web3.manager.request_blocking('eth_compileLLL', [code])
def eth_compileSerpent(self, code):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_compileserpent
Implemented?
N/A
Indirectly implemented
'''
return self.web3.eth.compileSerpent(code)
return self.web3.manager.request_blocking('eth_compileSerpent', [code])
def eth_newFilter(self, from_block=BLOCK_TAG_LATEST, to_block=BLOCK_TAG_LATEST, address=None, topics=None):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter
http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.filter
NEEDS TESTING
'''
filter_params = {
'fromBlock': from_block,
@ -488,7 +475,6 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newblockfilter
http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.filter
TESTED
'''
return self.web3.eth.newFilter('latest')
@ -496,7 +482,6 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newpendingtransactionfilter
http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.filter
TESTED
'''
return self.web3.eth.newFilter('pending')
@ -504,7 +489,6 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_uninstallfilter
http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.uninstallFilter
NEEDS TESTING
'''
return self.web3.eth.uninstallFilter(filter_id)
@ -512,7 +496,6 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges
http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.getFilterChanges
NEEDS TESTING
'''
return self.web3.eth.getFilterChanges(filter_id)
@ -520,7 +503,6 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterlogs
http://web3py.readthedocs.io/en/latest/web3.eth.html#web3.eth.Eth.getFilterLogs
NEEDS TESTING
'''
return self.web3.eth.getFilterLogs(filter_id)
@ -528,7 +510,6 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs
http://web3py.readthedocs.io/en/latest/filters.html?highlight=getLogs#web3.utils.filters.LogFilter.get
NEEDS TESTING
'''
return self.filter_object.get()
@ -536,164 +517,117 @@ class EthIpc(object):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getwork
http://web3py.readthedocs.io/en/latest/releases.html?highlight=getWork#id15
TESTED
'''
return self.web3.eth.getWork()
def eth_submitWork(self, nonce, header, mix_digest):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_submitwork
Not sure if implemented
NEEDS TESTING
Implemented indirectly
'''
return self.web3.eth.submitWork(nonce, header, mix_digest)
return self.web3.manager.request_blocking('eth_submitWork', [nonce, header, mix_digest])
def eth_submitHashrate(self, hash_rate, client_id):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_submithashrate
Not sure if implemented
TESTED
'''
return self.web3.eth.submitHashrate(hash_rate, client_id)
def db_putString(self, db_name, key, value):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#db_putstring
Not implemented I think
TESTED
'''
warnings.warn('deprecated', DeprecationWarning)
return self.web3.db.putString(db_name, key, value)
def db_getString(self, db_name, key):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#db_getstring
Not implemented I think
TESTED
'''
warnings.warn('deprecated', DeprecationWarning)
return self.web3.db.getString(db_name, key)
def db_putHex(self, db_name, key, value):
Implemented indirectly
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#db_puthex
TESTED
'''
if not value.startswith('0x'):
value = '0x{}'.format(value)
warnings.warn('deprecated', DeprecationWarning)
return self.web3.db.putHex(db_name, key, value)
def db_getHex(self, db_name, key):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#db_gethex
TESTED
'''
warnings.warn('deprecated', DeprecationWarning)
return self.web3.db.getHex(db_name, key)
return self.web3.manager.request_blocking('eth_submitHashrate', [hash_rate, client_id])
def shh_version(self):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_version
http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.version
N/A
'''
return self.web3.shh.version()
def shh_post(self, topics, payload, priority, ttl, from_=None, to=None):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_post
NEEDS TESTING
http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.post
# only topics and payload are necessary according to web3.py
'''
whisper_object = {
'from': from_,
'to': to,
'topics': topics,
'payload': payload,
'priority': hex(priority),
'ttl': hex(ttl),
'topics': web3.toHex(topics),
'payload': web3.toHex(payload),
'priority': web3.toHex(priority),
'ttl': web3.toHex(ttl)
}
return self._call('shh_post', [whisper_object])
return self.web3.shh.post(whisper_object)
def shh_newIdentity(self):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_newidentity
N/A
http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.newIdentity
'''
return self._call('shh_newIdentity')
return self.web3.shh.newIdentity()
def shh_hasIdentity(self, address):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_hasidentity
NEEDS TESTING
http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.hasIdentity
'''
return self._call('shh_hasIdentity', [address])
return self.web3.shh.hasIdentity(address)
def shh_newGroup(self):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_newgroup
N/A
http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.newGroup
'''
return self._call('shh_newGroup')
return self.web3.shh.newGroup()
def shh_addToGroup(self):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_addtogroup
NEEDS TESTING
http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.addToGroup
'''
return self._call('shh_addToGroup')
return self.web3.shh.addToGroup()
def shh_newFilter(self, to, topics):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_newfilter
NEEDS TESTING
http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.filter
# to is optional
'''
_filter = {
'to': to,
'topics': topics,
'to': to
}
return self._call('shh_newFilter', [_filter])
return self.web3.shh.newFilter(_filter)
def shh_uninstallFilter(self, filter_id):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_uninstallfilter
NEEDS TESTING
http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.uninstallFilter
'''
return self._call('shh_uninstallFilter', [filter_id])
return self.web3.shh.uninstallFilter(filter_id)
def shh_getFilterChanges(self, filter_id):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_getfilterchanges
http://web3py.readthedocs.io/en/latest/web3.eth.html?highlight=syncing#web3.eth.Eth.getFilterChanges
NEEDS TESTING
'''
filt = self.web3.eth.filter()
return self._call('shh_getFilterChanges', [filter_id])
return self.web3.shh.getFilterChanges(filter_id)
def shh_getMessages(self, filter_id):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_getmessages
NEEDS TESTING
http://web3py.readthedocs.io/en/latest/web3.shh.html#web3.shh.Shh.getMessages
'''
return self._call('shh_getMessages', [filter_id])
return self.web3.shh.getMessages(filter_id)
def getBlockRlp(self, number=0):
return self._call('debug_getBlockRlp', [number])
#not accessible with convenience functions
return self.web3.manager.request_blocking('debug_getBlockRlp', [number])
def traceTransaction(self, txHash):
#atm not directly accessible, so something like this is needed
#https://github.com/pipermerriam/web3.py/issues/308
#web3.manager.request_blocking('debug_traceTransaction', ["TX_ID_AS_HEX_STRING"])
return self.web3.manager.request_blocking('debug_traceTransaction', [txHash])

@ -1,5 +1,4 @@
import json
import warnings
import requests
from requests.adapters import HTTPAdapter
@ -587,44 +586,6 @@ class EthJsonRpc(object):
'''
return self._call('eth_submitHashrate', [hex(hash_rate), client_id])
def db_putString(self, db_name, key, value):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#db_putstring
TESTED
'''
warnings.warn('deprecated', DeprecationWarning)
return self._call('db_putString', [db_name, key, value])
def db_getString(self, db_name, key):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#db_getstring
TESTED
'''
warnings.warn('deprecated', DeprecationWarning)
return self._call('db_getString', [db_name, key])
def db_putHex(self, db_name, key, value):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#db_puthex
TESTED
'''
if not value.startswith('0x'):
value = '0x{}'.format(value)
warnings.warn('deprecated', DeprecationWarning)
return self._call('db_putHex', [db_name, key, value])
def db_getHex(self, db_name, key):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#db_gethex
TESTED
'''
warnings.warn('deprecated', DeprecationWarning)
return self._call('db_getHex', [db_name, key])
def shh_version(self):
'''
https://github.com/ethereum/wiki/wiki/JSON-RPC#shh_version

@ -0,0 +1,43 @@
from mythril.ether.ethcontract import ETHContract
import logging
import re
class DynLoader:
def __init__(self, eth):
self.eth = eth
def dynld(self, contract_address, dependency_address):
logging.info("Dynld at contract " + contract_address + ": " + dependency_address)
# Hack-ish
m = re.match(r'(0x[0-9a-fA-F]{40})', dependency_address)
if (m):
dependency_address = m.group(1)
else:
m = re.search(r'storage_(\d+)', dependency_address)
if (m):
idx = int(m.group(1))
logging.info("Dynamic contract address at storage index " + str(idx))
dependency_address = "0x" + self.eth.eth_getStorageAt(contract_address, position=idx, block='latest')[26:]
else:
logging.info("Unable to resolve address.")
return None
logging.info("Dependency address: " + dependency_address)
code = self.eth.eth_getCode(dependency_address)
if (code == "0x"):
return None
else:
contract = ETHContract(self.eth.eth_getCode(dependency_address), name=dependency_address, address=dependency_address)
return contract.as_dict()

@ -2,4 +2,6 @@ ethereum>=2.0.4
ZODB>=5.3.0
z3-solver>=4.5
web3
laser-ethereum>=0.1.4
laser-ethereum==0.2.4
requests
BTrees

@ -219,7 +219,7 @@ security community.
setup(
name='mythril',
version='0.5.6',
version='0.7.6',
description='A reversing and bug hunting framework for the Ethereum blockchain',
long_description=long_description,
@ -256,7 +256,9 @@ setup(
'web3',
'ZODB>=5.3.0',
'z3-solver>=4.5',
'laser-ethereum>=0.1.6'
'laser-ethereum==0.2.4',
'requests',
'BTrees'
],
python_requires='>=3.5',

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

@ -2,11 +2,11 @@
<head>
<style type="text/css">
#mynetwork {
background-color: #000000;
background-color: #232625;
}
body {
background-color: #000000;
background-color: #232625;
color: #ffffff;
}
</style>
@ -68,43 +68,61 @@
},
physics:{
enabled: true,
enabled: false,
}
}
var nodes = [
{id: 0, size: 150, 'label': '0 PUSH1 0x60\n2 PUSH1 0x40\n4 MSTORE\n5 PUSH1 0x04\n7 CALLDATASIZE\n8 LT\n9 PUSH2 0x0057\n12 JUMPI\n13 PUSH1 0x00\n15 CALLDATALOAD\n16 PUSH29 0x01000000(...)\n46 SWAP1\n47 DIV\n48 PUSH4 0xffffffff\n53 AND\n54 DUP1\n55 PUSH4 0x3ccfd60b\n60 EQ\n61 PUSH2 0x005c\n64 JUMPI\n65 DUP1\n66 PUSH4 0xf8b2cb4f\n71 EQ\n72 PUSH2 0x0071\n75 JUMPI\n76 DUP1\n77 PUSH4 0xfcddd056\n82 EQ\n83 PUSH2 0x00be\n86 JUMPI\n87 JUMPDEST\n88 PUSH1 0x00\n90 DUP1\n91 REVERT\n'},
{id: 198, size: 150, 'label': '198 JUMPDEST\n199 STOP\n'},
{id: 113, size: 150, 'label': '113 getBalance(address)\n114 CALLVALUE\n115 ISZERO\n116 PUSH2 0x007c\n119 JUMPI\n120 PUSH1 0x00\n122 DUP1\n123 REVERT\n'},
{id: 168, size: 150, 'label': '168 JUMPDEST\n169 PUSH1 0x40\n171 MLOAD\n172 DUP1\n173 DUP3\n174 DUP2\n175 MSTORE\n176 PUSH1 0x20\n178 ADD\n179 SWAP2\n180 POP\n181 POP\n182 PUSH1 0x40\n184 MLOAD\n185 DUP1\n186 SWAP2\n187 SUB\n188 SWAP1\n189 RETURN\n'},
{id: 92, size: 150, 'label': '92 withdraw()\n93 CALLVALUE\n94 ISZERO\n95 PUSH2 0x0067\n98 JUMPI\n99 PUSH1 0x00\n101 DUP1\n102 REVERT\n'},
{id: 103, size: 150, 'label': '103 JUMPDEST\n104 PUSH2 0x006f\n107 PUSH2 0x00c8\n110 JUMP\n'},
{id: 190, size: 150, 'label': '190 _function_0xfcddd056\n191 PUSH2 0x00c6\n194 PUSH2 0x021c\n197 JUMP\n'},
{id: 398, size: 150, 'label': '398 JUMPDEST\n399 PUSH1 0x00\n401 DUP1\n402 PUSH1 0x00\n404 CALLER\n405 PUSH20 0xffffffff(...)\n426 AND\n427 PUSH20 0xffffffff(...)\n448 AND\n449 DUP2\n450 MSTORE\n451 PUSH1 0x20\n453 ADD\n454 SWAP1\n455 DUP2\n456 MSTORE\n457 PUSH1 0x20\n459 ADD\n460 PUSH1 0x00\n462 SHA3\n463 DUP2\n464 SWAP1\n465 SSTORE\n466 POP\n467 JUMP\n'},
{id: 111, size: 150, 'label': '111 JUMPDEST\n112 STOP\n'},
{id: 200, size: 150, 'label': '200 JUMPDEST\n201 PUSH1 0x00\n203 DUP1\n204 PUSH1 0x00\n206 CALLER\n207 PUSH20 0xffffffff(...)\n228 AND\n229 PUSH20 0xffffffff(...)\n250 AND\n251 DUP2\n252 MSTORE\n253 PUSH1 0x20\n255 ADD\n256 SWAP1\n257 DUP2\n258 MSTORE\n259 PUSH1 0x20\n261 ADD\n262 PUSH1 0x00\n264 SHA3\n265 SLOAD\n266 GT\n267 ISZERO\n268 ISZERO\n269 PUSH2 0x0115\n272 JUMPI\n273 PUSH1 0x00\n275 DUP1\n276 REVERT\n'},
{id: 468, size: 150, 'label': '468 JUMPDEST\n469 PUSH1 0x00\n471 DUP1\n472 PUSH1 0x00\n474 DUP4\n475 PUSH20 0xffffffff(...)\n496 AND\n497 PUSH20 0xffffffff(...)\n518 AND\n519 DUP2\n520 MSTORE\n521 PUSH1 0x20\n523 ADD\n524 SWAP1\n525 DUP2\n526 MSTORE\n527 PUSH1 0x20\n529 ADD\n530 PUSH1 0x00\n532 SHA3\n533 SLOAD\n534 SWAP1\n535 POP\n536 SWAP2\n537 SWAP1\n538 POP\n539 JUMP\n'},
{id: 277, size: 150, 'label': '277 JUMPDEST\n278 CALLER\n279 PUSH20 0xffffffff(...)\n300 AND\n301 PUSH1 0x00\n303 DUP1\n304 CALLER\n305 PUSH20 0xffffffff(...)\n326 AND\n327 PUSH20 0xffffffff(...)\n348 AND\n349 DUP2\n350 MSTORE\n351 PUSH1 0x20\n353 ADD\n354 SWAP1\n355 DUP2\n356 MSTORE\n357 PUSH1 0x20\n359 ADD\n360 PUSH1 0x00\n362 SHA3\n363 SLOAD\n364 PUSH1 0x40\n366 MLOAD\n367 PUSH1 0x00\n369 PUSH1 0x40\n371 MLOAD\n372 DUP1\n373 DUP4\n374 SUB\n375 DUP2\n376 DUP6\n377 DUP8\n378 PUSH2 0x8796\n381 GAS\n382 SUB\n383 CALL\n384 SWAP3\n385 POP\n386 POP\n387 POP\n388 ISZERO\n389 ISZERO\n390 PUSH2 0x018e\n393 JUMPI\n394 PUSH1 0x00\n396 DUP1\n397 REVERT\n'},
{id: 87, size: 150, 'label': '87 JUMPDEST\n88 PUSH1 0x00\n90 DUP1\n91 REVERT\n'},
{id: 124, size: 150, 'label': '124 JUMPDEST\n125 PUSH2 0x00a8\n128 PUSH1 0x04\n130 DUP1\n131 DUP1\n132 CALLDATALOAD\n133 PUSH20 0xffffffff(...)\n154 AND\n155 SWAP1\n156 PUSH1 0x20\n158 ADD\n159 SWAP1\n160 SWAP2\n161 SWAP1\n162 POP\n163 POP\n164 PUSH2 0x01d4\n167 JUMP\n'},
{id: 540, size: 150, 'label': '540 JUMPDEST\n541 CALLVALUE\n542 PUSH1 0x00\n544 DUP1\n545 CALLER\n546 PUSH20 0xffffffff(...)\n567 AND\n568 PUSH20 0xffffffff(...)\n589 AND\n590 DUP2\n591 MSTORE\n592 PUSH1 0x20\n594 ADD\n595 SWAP1\n596 DUP2\n597 MSTORE\n598 PUSH1 0x20\n600 ADD\n601 PUSH1 0x00\n603 SHA3\n604 PUSH1 0x00\n606 DUP3\n607 DUP3\n608 SLOAD\n609 ADD\n610 SWAP3\n611 POP\n612 POP\n613 DUP2\n614 SWAP1\n615 SSTORE\n616 POP\n617 JUMP\n'}
{id: 256, size: 150, 'label': '256 JUMPDEST\n257 PUSH1 0x40\n259 MLOAD\n260 DUP1\n261 DUP3\n262 DUP2\n263 MSTORE\n264 PUSH1 0x20\n266 ADD\n267 SWAP2\n268 POP\n269 POP\n270 PUSH1 0x40\n272 MLOAD\n273 DUP1\n274 SWAP2\n275 SUB\n276 SWAP1\n277 RETURN\n'},
{id: 0, size: 150, 'label': '0 PUSH1 0x60\n2 PUSH1 0x40\n4 MSTORE\n5 PUSH1 0x04\n7 CALLDATASIZE\n8 LT\n9 PUSH2 0x006d\n12 JUMPI\n13 PUSH1 0x00\n15 CALLDATALOAD\n16 PUSH29 0x01000000(...)\n46 SWAP1\n47 DIV\n48 PUSH4 0xffffffff\n53 AND\n54 DUP1\n55 PUSH4 0x1803334e\n60 EQ\n61 PUSH2 0x0072\n64 JUMPI\n65 DUP1\n66 PUSH4 0x2e1a7d4d\n71 EQ\n72 PUSH2 0x0087\n75 JUMPI\n76 DUP1\n77 PUSH4 0x41c0e1b5\n82 EQ\n83 PUSH2 0x00aa\n86 JUMPI\n87 DUP1\n88 PUSH4 0x69774c2d\n93 EQ\n94 PUSH2 0x00bf\n97 JUMPI\n98 DUP1\n99 PUSH4 0xf8b2cb4f\n104 EQ\n105 PUSH2 0x00c9\n108 JUMPI\n109 JUMPDEST\n110 PUSH1 0x00\n112 DUP1\n113 REVERT\n'},
{id: 770, size: 150, 'label': '770 JUMPDEST\n771 PUSH1 0x00\n773 DUP1\n774 PUSH1 0x00\n776 DUP4\n777 PUSH20 0xffffffff(...)\n798 AND\n799 PUSH20 0xffffffff(...)\n820 AND\n821 DUP2\n822 MSTORE\n823 PUSH1 0x20\n825 ADD\n826 SWAP1\n827 DUP2\n828 MSTORE\n829 PUSH1 0x20\n831 ADD\n832 PUSH1 0x00\n834 SHA3\n835 SLOAD\n836 SWAP1\n837 POP\n838 SWAP2\n839 SWAP1\n840 POP\n841 JUMP\n'},
{id: 133, size: 150, 'label': '133 JUMPDEST\n134 STOP\n'},
{id: 135, size: 150, 'label': '135 withdraw(uint256)\n136 CALLVALUE\n137 ISZERO\n138 PUSH2 0x0092\n141 JUMPI\n142 PUSH1 0x00\n144 DUP1\n145 REVERT\n'},
{id: 201, size: 150, 'label': '201 getBalance(address)\n202 CALLVALUE\n203 ISZERO\n204 PUSH2 0x00d4\n207 JUMPI\n208 PUSH1 0x00\n210 DUP1\n211 REVERT\n'},
{id: 653, size: 150, 'label': '653 JUMPDEST\n654 CALLER\n655 PUSH20 0xffffffff(...)\n676 AND\n677 SUICIDE\n'},
{id: 146, size: 150, 'label': '146 JUMPDEST\n147 PUSH2 0x00a8\n150 PUSH1 0x04\n152 DUP1\n153 DUP1\n154 CALLDATALOAD\n155 SWAP1\n156 PUSH1 0x20\n158 ADD\n159 SWAP1\n160 SWAP2\n161 SWAP1\n162 POP\n163 POP\n164 PUSH2 0x0159\n167 JUMP\n'},
{id: 212, size: 150, 'label': '212 JUMPDEST\n213 PUSH2 0x0100\n216 PUSH1 0x04\n218 DUP1\n219 DUP1\n220 CALLDATALOAD\n221 PUSH20 0xffffffff(...)\n242 AND\n243 SWAP1\n244 PUSH1 0x20\n246 ADD\n247 SWAP1\n248 SWAP2\n249 SWAP1\n250 POP\n251 POP\n252 PUSH2 0x0302\n255 JUMP\n'},
{id: 278, size: 150, 'label': '278 JUMPDEST\n279 CALLER\n280 PUSH1 0x01\n282 PUSH1 0x00\n284 PUSH2 0x0100\n287 EXP\n288 DUP2\n289 SLOAD\n290 DUP2\n291 PUSH20 0xffffffff(...)\n312 MUL\n313 NOT\n314 AND\n315 SWAP1\n316 DUP4\n317 PUSH20 0xffffffff(...)\n338 AND\n339 MUL\n340 OR\n341 SWAP1\n342 SSTORE\n343 POP\n344 JUMP\n'},
{id: 345, size: 150, 'label': '345 JUMPDEST\n346 PUSH1 0x00\n348 DUP1\n349 PUSH1 0x00\n351 CALLER\n352 PUSH20 0xffffffff(...)\n373 AND\n374 PUSH20 0xffffffff(...)\n395 AND\n396 DUP2\n397 MSTORE\n398 PUSH1 0x20\n400 ADD\n401 SWAP1\n402 DUP2\n403 MSTORE\n404 PUSH1 0x20\n406 ADD\n407 PUSH1 0x00\n409 SHA3\n410 SLOAD\n411 EQ\n412 ISZERO\n413 PUSH2 0x01a5\n416 JUMPI\n417 PUSH1 0x00\n419 DUP1\n420 REVERT\n'},
{id: 421, size: 150, 'label': '421 JUMPDEST\n422 DUP1\n423 PUSH1 0x00\n425 DUP1\n426 CALLER\n427 PUSH20 0xffffffff(...)\n448 AND\n449 PUSH20 0xffffffff(...)\n470 AND\n471 DUP2\n472 MSTORE\n473 PUSH1 0x20\n475 ADD\n476 SWAP1\n477 DUP2\n478 MSTORE\n479 PUSH1 0x20\n481 ADD\n482 PUSH1 0x00\n484 SHA3\n485 PUSH1 0x00\n487 DUP3\n488 DUP3\n489 SLOAD\n490 SUB\n491 SWAP3\n492 POP\n493 POP\n494 DUP2\n495 SWAP1\n496 SSTORE\n497 POP\n498 CALLER\n499 PUSH20 0xffffffff(...)\n520 AND\n521 PUSH2 0x08fc\n524 DUP3\n525 SWAP1\n526 DUP2\n527 ISZERO\n528 MUL\n529 SWAP1\n530 PUSH1 0x40\n532 MLOAD\n533 PUSH1 0x00\n535 PUSH1 0x40\n537 MLOAD\n538 DUP1\n539 DUP4\n540 SUB\n541 DUP2\n542 DUP6\n543 DUP9\n544 DUP9\n545 CALL\n546 SWAP4\n547 POP\n548 POP\n549 POP\n550 POP\n551 ISZERO\n552 ISZERO\n553 PUSH2 0x0231\n556 JUMPI\n557 PUSH1 0x00\n559 DUP1\n560 REVERT\n'},
{id: 678, size: 150, 'label': '678 JUMPDEST\n679 PUSH1 0x00\n681 CALLVALUE\n682 EQ\n683 ISZERO\n684 PUSH2 0x02b4\n687 JUMPI\n688 PUSH1 0x00\n690 DUP1\n691 REVERT\n'},
{id: 168, size: 150, 'label': '168 JUMPDEST\n169 STOP\n'},
{id: 170, size: 150, 'label': '170 kill()\n171 CALLVALUE\n172 ISZERO\n173 PUSH2 0x00b5\n176 JUMPI\n177 PUSH1 0x00\n179 DUP1\n180 REVERT\n'},
{id: 199, size: 150, 'label': '199 JUMPDEST\n200 STOP\n'},
{id: 109, size: 150, 'label': '109 JUMPDEST\n110 PUSH1 0x00\n112 DUP1\n113 REVERT\n'},
{id: 561, size: 150, 'label': '561 JUMPDEST\n562 POP\n563 JUMP\n'},
{id: 114, size: 150, 'label': '114 _function_0x1803334e\n115 CALLVALUE\n116 ISZERO\n117 PUSH2 0x007d\n120 JUMPI\n121 PUSH1 0x00\n123 DUP1\n124 REVERT\n'},
{id: 692, size: 150, 'label': '692 JUMPDEST\n693 CALLVALUE\n694 PUSH1 0x00\n696 DUP1\n697 CALLER\n698 PUSH20 0xffffffff(...)\n719 AND\n720 PUSH20 0xffffffff(...)\n741 AND\n742 DUP2\n743 MSTORE\n744 PUSH1 0x20\n746 ADD\n747 SWAP1\n748 DUP2\n749 MSTORE\n750 PUSH1 0x20\n752 ADD\n753 PUSH1 0x00\n755 SHA3\n756 PUSH1 0x00\n758 DUP3\n759 DUP3\n760 SLOAD\n761 ADD\n762 SWAP3\n763 POP\n764 POP\n765 DUP2\n766 SWAP1\n767 SSTORE\n768 POP\n769 JUMP\n'},
{id: 181, size: 150, 'label': '181 JUMPDEST\n182 PUSH2 0x00bd\n185 PUSH2 0x0234\n188 JUMP\n'},
{id: 564, size: 150, 'label': '564 JUMPDEST\n565 PUSH1 0x01\n567 PUSH1 0x00\n569 SWAP1\n570 SLOAD\n571 SWAP1\n572 PUSH2 0x0100\n575 EXP\n576 SWAP1\n577 DIV\n578 PUSH20 0xffffffff(...)\n599 AND\n600 PUSH20 0xffffffff(...)\n621 AND\n622 CALLER\n623 PUSH20 0xffffffff(...)\n644 AND\n645 EQ\n646 ISZERO\n647 ISZERO\n648 PUSH2 0x028d\n651 JUMPI\n652 INVALID\n'},
{id: 125, size: 150, 'label': '125 JUMPDEST\n126 PUSH2 0x0085\n129 PUSH2 0x0116\n132 JUMP\n'},
{id: 191, size: 150, 'label': '191 _function_0x69774c2d\n192 PUSH2 0x00c7\n195 PUSH2 0x02a6\n198 JUMP\n'}
];
var edges = [
{from: 0, to: 87, 'arrows': 'to', 'label': 'Not(ULE(4, calldatasize))', 'smooth': {'type': 'cubicBezier'}},
{from: 398, to: 111, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 277, to: 398, 'arrows': 'to', 'label': 'Not(retval_384_115 == 0)', 'smooth': {'type': 'cubicBezier'}},
{from: 200, to: 277, 'arrows': 'to', 'label': 'Not(storage_sha_hash == 0)', 'smooth': {'type': 'cubicBezier'}},
{from: 103, to: 200, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 92, to: 103, 'arrows': 'to', 'label': 'callvalue == 0', 'smooth': {'type': 'cubicBezier'}},
{from: 0, to: 92, 'arrows': 'to', 'label': 'Extract0xff,0xe0, calldata_0) ==0x3ccfd60b', 'smooth': {'type': 'cubicBezier'}},
{from: 468, to: 168, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 124, to: 468, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 113, to: 124, 'arrows': 'to', 'label': 'callvalue == 0', 'smooth': {'type': 'cubicBezier'}},
{from: 0, to: 113, 'arrows': 'to', 'label': 'Extract0xff,0xe0, calldata_0) ==0xf8b2cb4f', 'smooth': {'type': 'cubicBezier'}},
{from: 540, to: 198, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 190, to: 540, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 0, to: 190, 'arrows': 'to', 'label': 'Extract0xff,0xe0, calldata_0) ==0xfcddd056', 'smooth': {'type': 'cubicBezier'}}
{from: 0, to: 109, 'arrows': 'to', 'label': 'Not(ULE(4, calldatasize))', 'smooth': {'type': 'cubicBezier'}},
{from: 278, to: 133, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 125, to: 278, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 114, to: 125, 'arrows': 'to', 'label': 'callvalue == 0', 'smooth': {'type': 'cubicBezier'}},
{from: 0, to: 114, 'arrows': 'to', 'label': 'Extract(0xff, 0xe0, calldata_0) == 0x1803334e', 'smooth': {'type': 'cubicBezier'}},
{from: 561, to: 168, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 421, to: 561, 'arrows': 'to', 'label': 'Not(retval_546_333 == 0)', 'smooth': {'type': 'cubicBezier'}},
{from: 345, to: 421, 'arrows': 'to', 'label': 'Not(storage_98ea2e05 == 0)', 'smooth': {'type': 'cubicBezier'}},
{from: 146, to: 345, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 135, to: 146, 'arrows': 'to', 'label': 'callvalue == 0', 'smooth': {'type': 'cubicBezier'}},
{from: 0, to: 135, 'arrows': 'to', 'label': 'Extract(0xff, 0xe0, calldata_0) == 0x2e1a7d4d', 'smooth': {'type': 'cubicBezier'}},
{from: 564, to: 653, 'arrows': 'to', 'label': 'Extract(0x9f, 0, caller) == Extract(0xa7, 8, storage_1)', 'smooth': {'type': 'cubicBezier'}},
{from: 181, to: 564, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 170, to: 181, 'arrows': 'to', 'label': 'callvalue == 0', 'smooth': {'type': 'cubicBezier'}},
{from: 0, to: 170, 'arrows': 'to', 'label': 'Extract(0xff, 0xe0, calldata_0) == 0x41c0e1b5', 'smooth': {'type': 'cubicBezier'}},
{from: 692, to: 199, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 678, to: 692, 'arrows': 'to', 'label': 'Not(callvalue == 0)', 'smooth': {'type': 'cubicBezier'}},
{from: 191, to: 678, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 0, to: 191, 'arrows': 'to', 'label': 'Extract(0xff, 0xe0, calldata_0) == 0x69774c2d', 'smooth': {'type': 'cubicBezier'}},
{from: 770, to: 256, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 212, to: 770, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 201, to: 212, 'arrows': 'to', 'label': 'callvalue == 0', 'smooth': {'type': 'cubicBezier'}},
{from: 0, to: 201, 'arrows': 'to', 'label': 'Extract(0xff, 0xe0, calldata_0) == 0xf8b2cb4f', 'smooth': {'type': 'cubicBezier'}}
];
</script>

@ -0,0 +1,19 @@
import unittest
import os
from subprocess import check_output
class CommandLineToolTestCase(unittest.TestCase):
def runTest(self):
script_path = os.path.dirname(os.path.realpath(__file__))
myth = os.path.join(script_path, '..', 'myth')
out = check_output([myth,'-d','-c', '0x5050']).decode("UTF-8")
self.assertEqual('0 POP\n1 POP\n', out)
out = check_output([myth,'-d', os.path.join(script_path,'testdata','metacoin.sol')]).decode("UTF-8")
self.assertIn('0 PUSH1 0x60\n2 PUSH1 0x40', out)

File diff suppressed because one or more lines are too long

@ -14,9 +14,9 @@ class Getinstruction_listTestCase(ETHContractTestCase):
contract = ETHContract(self.code, self.creation_code)
instruction_list = contract.get_instruction_list()
disassembly = contract.get_disassembly()
self.assertEqual(len(instruction_list), 71, 'Error disassembling code using ETHContract.get_instruction_list()')
self.assertEqual(len(disassembly.instruction_list), 71, 'Error disassembling code using ETHContract.get_instruction_list()')
class GetEASMTestCase(ETHContractTestCase):

@ -1,2 +0,0 @@
import unittest

@ -1 +0,0 @@
import unittest

@ -1,2 +0,0 @@
import unittest

@ -0,0 +1,19 @@
import unittest
from mythril.disassembler.callgraph import generate_callgraph
from mythril.disassembler.disassembly import Disassembly
from laser.ethereum import svm
class SVMTestCase(unittest.TestCase):
def runTest(self):
modules = {}
modules['0x0000000000000000000000000000000000000000'] = {'name': 'metaCoin', 'address': '0x0000000000000000000000000000000000000000', 'creation_code': '', 'code': '60606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806327e235e314610051578063412664ae1461009e575b600080fd5b341561005c57600080fd5b610088600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506100f8565b6040518082815260200191505060405180910390f35b34156100a957600080fd5b6100de600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610110565b604051808215151515815260200191505060405180910390f35b60006020528060005260406000206000915090505481565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561016157600090506101fe565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550600090505b929150505600a165627a7a72305820fd4fa106da498514e90965a45ffecc1da53a0cd8bb7a7135910f8612245a46370029'}
modules['0x0000000000000000000000000000000000000000']['disassembly'] = Disassembly(modules['0x0000000000000000000000000000000000000000']['code'])
_svm = svm.SVM(modules)
html = generate_callgraph(_svm, '0x0000000000000000000000000000000000000000', False)
self.assertTrue("var nodes = [\n{id: \'metaCoin:" in html)

@ -0,0 +1,15 @@
pragma solidity ^0.4.17;
contract metaCoin {
mapping (address => uint) public balances;
function metaCoin() public {
balances[msg.sender] = 10000;
}
function sendToken(address receiver, uint amount) public returns(bool successful){
if (balances[msg.sender] < amount) return false;
balances[msg.sender] -= amount;
balances[receiver] += amount;
return false;
}
}

@ -1,2 +0,0 @@
import unittest
Loading…
Cancel
Save