예제 #1
0
 def __init__(self, bytecode, callcode):
     # retrive instructions, basicblocks & functions statically
     self.callcode = callcode
     self.bytecode = bytecode
     disasm = EthereumDisassembler(binascii.hexlify(bytecode).decode())
     self.instructions = disasm.disassemble()
     self.reverse_instructions = {k: v for k, v in enumerate(self.instructions)}
     self.vm = EthereumVM()
예제 #2
0
    def __init__(self, bytecode=None, max_depth=20):
        EthereumEmulatorEngine.__init__(self,
                                        bytecode=bytecode,
                                        ssa=True,
                                        symbolic_exec=False,
                                        max_depth=20)
        if not bytecode and not instructions:
            raise Exception("No bytecode/instructions provided")
        if instructions:
            self.instructions = instructions
        else:
            # disassemble bytecode to instructions
            disasm = EthereumDisassembler(bytecode)
            self.instructions = disasm.disassemble()
        self.ssaoptimizer = SSAOptimizer()

        self.reverse_instructions = {
            k: v
            for k, v in enumerate(self.instructions)
        }

        # retrive basicblocks & functions statically
        self.cfg = EthereumCFG(instructions=self.instructions,
                               analysis='static')
        self.functions = self.cfg.functions
        self.basicblocks = self.cfg.basicblocks

        self.functions_start_instr = [f.start_instr for f in self.functions]
        self.current_function = self.functions[0]
        self.basicblock_per_instr = dict()
        self.current_basicblock = None

        # create dict with:
        # * key = instruction offset
        # * value = basicblock reference
        # easy to get the corresponding basicblock per instr now
        for bb in self.basicblocks:
            for intr in bb.instructions:
                self.basicblock_per_instr[intr.offset] = bb

        # connection between basicblocks
        self.edges = list()

        self.states = dict()
        self.states_total = 0
        self.max_depth = max_depth
        self.ssa_counter = 0

        logging.info("=======================================")
        logging.info("#         Ethereum SSA Engine         #")
        logging.info("=======================================")
        logging.info("[+] Functions detected : %s",
                     [f.prefered_name for f in self.functions])
예제 #3
0
    def __init__(self, bytecode, ssa=True, symbolic_exec=False, max_depth=20):

        self.ssa = ssa
        self.symbolic_exec = symbolic_exec

        # retrive instructions, basicblocks & functions statically
        disasm = EthereumDisassembler(bytecode)
        self.instructions = disasm.disassemble()
        self.reverse_instructions = {
            k: v
            for k, v in enumerate(self.instructions)
        }
        self.functions = enum_func_static(self.instructions)
        self.basicblocks = enum_blocks_static(self.instructions)

        self.simplify_ssa = EthereumSSASimplifier()

        self.functions_start_instr = [f.start_instr for f in self.functions]
        self.current_function = self.functions[0]
        self.basicblock_per_instr = dict()
        self.current_basicblock = None

        # create dict with:
        # * key = instruction offset
        # * value = basicblock reference
        # easy to get the corresponding basicblock per instr now
        for bb in self.basicblocks:
            for intr in bb.instructions:
                self.basicblock_per_instr[intr.offset] = bb

        # connection between basicblocks
        # will be generate dynamically by the Emulator
        self.edges = list()

        self.states = dict()
        self.states_total = 0
        self.max_depth = max_depth
        self.ssa_counter = 0

        logging.info("=======================================")
        logging.info("#      Ethereum Emulator Engine")
        logging.info("#      class: %s", self.__class__.__name__)
        logging.info("=======================================")
        for f in self.functions:
            logging.info("[+] Functions detected - %x: %s/%s", f.start_offset,
                         f.prefered_name, f.name)
예제 #4
0
    def __init__(self, bytecode=None, instructions=None, analysis='dynamic'):
        """ TODO """

        if not bytecode and not instructions:
            raise Exception("No bytecode/instructions provided")
        if instructions:
            self.instructions = instructions
        else:
            # disassemble bytecode to instructions
            disasm = EthereumDisassembler(bytecode)
            self.instructions = disasm.disassemble()
        self.analysis = analysis

        self.basicblocks = list()
        self.functions = list()
        self.edges = list()

        if self.analysis == 'dynamic':
            self.run_dynamic_analysis()
        elif self.analysis == 'static':
            self.run_static_analysis()
예제 #5
0
def main() -> None:
    parser = argparse.ArgumentParser(
        description=
        'Security Analysis tool for WebAssembly module and Blockchain Smart Contracts (BTC/ETH/NEO/EOS)'
    )

    inputs = parser.add_argument_group('Input arguments')
    inputs.add_argument(
        '-r',
        '--raw',
        help='hex-encoded bytecode string ("ABcdeF09..." or "0xABcdeF09...")',
        metavar='BYTECODE')
    inputs.add_argument('-f',
                        '--file',
                        type=argparse.FileType('r'),
                        help='file containing hex-encoded bytecode string',
                        metavar='BYTECODEFILE')
    inputs.add_argument('-a',
                        '--address',
                        help='pull contract from the blockchain',
                        metavar='CONTRACT_ADDRESS')

    features = parser.add_argument_group('Features')
    features.add_argument(
        '-e',
        '--explore',
        action='store_true',
        help='client to retrieve information from blockchains')
    features.add_argument('-d',
                          '--disassemble',
                          action='store_true',
                          help='print text disassembly ')
    features.add_argument('-g',
                          '--cfg',
                          action='store_true',
                          help='generate the control flow graph (CFG)')
    features.add_argument('-c',
                          '--call',
                          action='store_true',
                          help='generate the call flow graph')
    features.add_argument('-s',
                          '--ssa',
                          action='store_true',
                          help='generate the CFG with SSA representation')

    graph = parser.add_argument_group('Graph options')
    graph.add_argument(
        '--onlystatic',
        action='store_true',
        help=
        'generate the CFG without stack emulation (fastest but less accurate)')
    graph.add_argument('--simplify',
                       action='store_true',
                       help='generate a simplify CFG')
    graph.add_argument('--functions',
                       action='store_true',
                       help='create subgraph for each function')
    graph.add_argument(
        '--onlyfunction',
        type=str,
        nargs="*",
        default=[],
        help='only generate the CFG for this list of function name')
    #graph.add_argument('--visualize',
    #                   help='direcly open the CFG file')
    #graph.add_argument('--format',
    #                   choices=['pdf', 'png', 'dot'],
    #                   default='pdf',
    #                   help='direcly open the CFG file')

    explorer = parser.add_argument_group('Explorer options')
    explorer.add_argument('--code',
                          help='get contract code',
                          metavar='CONTRACT_ADDRESS')
    explorer.add_argument('--tx', help='get transaction information')
    #explorer.add_argument('--txdecode', action='store_true',
    #                      help='return important transaction information')
    explorer.add_argument(
        '--blockid',
        type=int,
        help='get block information using given block number',
        metavar='BLOCK_NUMBER')
    explorer.add_argument('--blockhash',
                          help='get block information using given block hash',
                          metavar='BLOCK_HASH')
    explorer.add_argument(
        '--infura',
        help='Infura network choice',
        choices=['mainnet', 'ropsten', 'infuranet', 'kovan', 'rinkeby'],
        default='mainnet')
    explorer.add_argument('--rpc',
                          help='custom RPC settings',
                          metavar='HOST:PORT')
    explorer.add_argument('--rpctls',
                          action='store_false',
                          help='RPC connection over TLS')

    args = parser.parse_args()

    octo_bytecode = None
    octo_explorer = None
    octo_disasm = None
    octo_cfg = None

    # Explorer
    if args.explore or args.address:
        # user choose some explorer options
        if args.rpc:
            from octopus.platforms.ETH.explorer import EthereumExplorerRPC
            # parsing RPC HOST & PORT
            host, port = args.rpc.split(':')
            octo_explorer = EthereumExplorerRPC(host=host,
                                                port=port,
                                                tls=args.rpctls)
        else:
            from octopus.platforms.ETH.explorer import EthereumInfuraExplorer
            from octopus.platforms.ETH.explorer import (INFURA_MAINNET,
                                                        INFURA_ROPSTEN,
                                                        INFURA_INFURANET,
                                                        INFURA_KOVAN,
                                                        INFURA_RINKEBY)
            if args.infura == 'mainnet':
                network = INFURA_MAINNET
            if args.infura == 'ropsten':
                network = INFURA_ROPSTEN
            if args.infura == 'infuranet':
                network = INFURA_INFURANET
            if args.infura == 'kovan':
                network = INFURA_KOVAN
            if args.infura == 'rinkeby':
                network = INFURA_RINKEBY

            octo_explorer = EthereumInfuraExplorer(network=network)

        # process explorer related commands
        if args.code:
            print(octo_explorer.eth_getCode(args.code))
        elif args.tx:
            print(octo_explorer.get_transaction(args.tx))
        # elif args.txdecode:
        #    pass
        elif args.blockid:
            print(octo_explorer.get_block_by_number(args.blockid))
        elif args.blockhash:
            print(octo_explorer.get_block_by_hash(args.blockhash))

    # process input code
    if args.raw:
        octo_bytecode = args.raw
    elif args.file:
        octo_bytecode = ''.join(
            [l.strip() for l in args.file if len(l.strip()) > 0])
    elif args.address:
        octo_bytecode = octo_explorer.eth_getCode(args.address)

    # Disassembly
    if args.disassemble:
        from octopus.platforms.ETH.disassembler import EthereumDisassembler

        # TODO add other r_format support
        octo_disasm = EthereumDisassembler()
        print(octo_disasm.disassemble(octo_bytecode, r_format='text'))

    # Control Flow Analysis
    elif args.cfg or args.ssa:
        from octopus.platforms.ETH.cfg import EthereumCFG
        from octopus.analysis.graph import CFGGraph

        if args.onlystatic and not args.ssa:
            octo_cfg = EthereumCFG(octo_bytecode, evm_analysis='static')
        else:
            octo_cfg = EthereumCFG(octo_bytecode)

        octo_graph = CFGGraph(octo_cfg)

        if args.functions or args.onlyfunction:
            octo_graph.view_functions(only_func_name=args.onlyfunction,
                                      simplify=args.simplify,
                                      ssa=args.ssa)
        else:
            octo_graph.view(simplify=args.simplify, ssa=args.ssa)

    # Call Flow Analysis
    elif args.call:
        error_print('Call Flow Analysis not yet supported')
    elif args.ssa:
        # already done
        pass
    elif args.explore:
        # already done
        pass
    else:
        parser.print_help()