168 lines
5.8 KiB
Python
Executable File
168 lines
5.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# BSD 3-Clause License
|
|
#
|
|
# Copyright (c) 2020, The Ohio State Univerisity. All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions are met:
|
|
#
|
|
# * Redistributions of source code must retain the above copyright notice, this
|
|
# list of conditions and the following disclaimer.
|
|
#
|
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
|
# this list of conditions and the following disclaimer in the documentation
|
|
# and/or other materials provided with the distribution.
|
|
#
|
|
# * Neither the name of the copyright holder nor the names of its
|
|
# contributors may be used to endorse or promote products derived from
|
|
# this software without specific prior written permission.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
# Standard lib imports
|
|
import argparse
|
|
import logging
|
|
import sys
|
|
from os.path import abspath, dirname, join
|
|
|
|
# Prepend .. to $PATH so the project modules can be imported below
|
|
|
|
# 用于将以太坊虚拟机(EVM)字节码反汇编为三地址代码进行程序分析
|
|
|
|
|
|
src_path = join(dirname(abspath(__file__)), "..")
|
|
sys.path.insert(0, src_path)
|
|
|
|
# Local project imports
|
|
import src.exporter as exporter
|
|
import src.tac_efg as tac_efg
|
|
import src.settings as settings
|
|
|
|
# Version string to display with -v
|
|
VERSION = """\
|
|
+------------------------------+
|
|
| TxSpector Detector v0.0.1 |
|
|
| (c) The Ohio State University |
|
|
+------------------------------+\
|
|
"""
|
|
|
|
|
|
# Define a version() function in case we want dynamic version strings later
|
|
def version():
|
|
return VERSION
|
|
|
|
|
|
# Configure argparse
|
|
parser = argparse.ArgumentParser(
|
|
description="An EVM bytecode disassembly that generates"
|
|
"three-address code for program analysis.")
|
|
|
|
parser.add_argument("-t",
|
|
"--tsv",
|
|
nargs="?",
|
|
const="",
|
|
metavar="DIR",
|
|
default=None,
|
|
help="generate tab-separated .facts files for Souffle "
|
|
"and write files to the specified directory, which "
|
|
"will be recursively created if it does not exist "
|
|
"(current working directory by default).")
|
|
|
|
parser.add_argument("-o",
|
|
"--opcodes",
|
|
nargs="*",
|
|
default=[],
|
|
metavar="OPCODE",
|
|
help="If producing tsv output, also include relations "
|
|
"encoding all occurrences of the specified "
|
|
"list of opcodes. Opcode X will be stored in "
|
|
"op_X.facts.")
|
|
|
|
parser.add_argument("-v",
|
|
"--verbose",
|
|
action="store_true",
|
|
help="emit verbose debug output to stderr.")
|
|
|
|
parser.add_argument("-vv",
|
|
"--prolix",
|
|
action="store_true",
|
|
help="higher verbosity level, including extra debug "
|
|
"messages from dataflow analysis and elsewhere.")
|
|
|
|
parser.add_argument("-n",
|
|
"--no_out",
|
|
action="store_true",
|
|
help="do not output decompiled graph.")
|
|
|
|
parser.add_argument("-V",
|
|
"--version",
|
|
action="store_true",
|
|
help="show program's version number and exit.")
|
|
|
|
parser.add_argument("infile",
|
|
nargs="?",
|
|
type=argparse.FileType("r"),
|
|
default=sys.stdin,
|
|
help="file from which decompiler input should be read "
|
|
"(stdin by default).")
|
|
|
|
parser.add_argument("outfile",
|
|
nargs="?",
|
|
type=argparse.FileType("w"),
|
|
default=sys.stdout,
|
|
help="file to which decompiler output should be written "
|
|
"(stdout by default).")
|
|
|
|
# Parse the arguments.
|
|
args = parser.parse_args()
|
|
|
|
# Set up logger, with appropriate log level depending on verbosity.
|
|
log_level = logging.WARNING
|
|
if args.prolix:
|
|
log_level = logging.DEBUG
|
|
elif args.verbose:
|
|
log_level = logging.INFO
|
|
logging.basicConfig(format='%(levelname)s: %(message)s', level=log_level)
|
|
|
|
# Handle --version
|
|
if args.version:
|
|
sys.exit(1)
|
|
|
|
# Always show version for log_level >= LOW
|
|
logging.info("\n" + version())
|
|
|
|
# Build TAC EFG from input file
|
|
try:
|
|
logging.info("Reading from '%s'.", args.infile.name)
|
|
efg = tac_efg.TACGraph.from_opcode(args.infile)
|
|
logging.info("Initial EFG generation completed.")
|
|
|
|
# Catch a Control-C and exit with UNIX failure status 1
|
|
except KeyboardInterrupt:
|
|
logging.critical("\nInterrupted by user")
|
|
sys.exit(1)
|
|
|
|
# Generate output using the requested exporter(s)
|
|
# blocks, textual version of efg
|
|
if not args.no_out:
|
|
logging.info("Writing string output.")
|
|
print(exporter.EFGStringExporter(efg).export(), file=args.outfile)
|
|
|
|
# Generate facts file
|
|
if args.tsv is not None:
|
|
logging.info("Writing TSV output.")
|
|
exporter.EFGTsvExporter(efg).export(output_dir=args.tsv,
|
|
out_opcodes=args.opcodes)
|
|
|
|
|