Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version
venvpy/
# celery beat schedule file
celerybeat-schedule

# dotenv
.env

# virtualenv
.venv
venv/
ENV/

# Spyder project settings
.spyderproject

# Rope project settings
.ropeproject

# macOS Finder files
.DS_Store

# PyCharm files
.idea/
7 changes: 1 addition & 6 deletions pyevmasm/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@

# try importing python 3 module first
try:
from pyevmasm.evmasm3 import *
except:
from evmasm2 import *
from .evmasm import EVMAsm
75 changes: 22 additions & 53 deletions pyevmasm/evmasm2.py → pyevmasm/evmasm.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,6 @@
import collections

class memoized(object):
'''Decorator. Caches a function's return value each time it is called.
If called later with the same arguments, the cached value is returned
(not reevaluated).
'''

def __init__(self, func):
self.func = func
self.cache = {}

def __call__(self, *args, **kwargs):
key = args + tuple(sorted(kwargs.items()))
if not isinstance(key, collections.Hashable):
# uncacheable. a list, for instance.
# better to not cache than blow up.
return self.func(*args, **kwargs)
if key in self.cache:
return self.cache[key]
else:
value = self.func(*args, **kwargs)
self.cache[key] = value
return value

def __repr__(self):
'''Return the function's docstring.'''
return self.func.__doc__

def __get__(self, obj, objtype):
'''Support instance methods.'''
return functools.partial(self.__call__, obj)
from builtins import map, next, chr, range, object
from binascii import hexlify, unhexlify
from .util import memoized


class EVMAsm(object):
Expand All @@ -38,7 +9,7 @@ class EVMAsm(object):

Example use::

>>> from manticore.platforms.evm import EVMAsm
>>> from evmasm import EVMAsm
>>> EVMAsm.disassemble_one('\\x60\\x10')
Instruction(0x60, 'PUSH', 1, 0, 1, 0, 'Place 1 byte item on stack.', 16, 0)
>>> EVMAsm.assemble_one('PUSH1 0x10')
Expand Down Expand Up @@ -221,7 +192,7 @@ def bytes(self):
''' Encoded instruction '''
bytes = []
bytes.append(chr(self._opcode))
for offset in reversed(xrange(self.operand_size)):
for offset in reversed(range(self.operand_size)):
c = (self.operand >> offset * 8) & 0xff
bytes.append(chr(c))
return ''.join(bytes)
Expand Down Expand Up @@ -470,7 +441,7 @@ def is_arithmetic(self):
def _get_reverse_table():
''' Build an internal table used in the assembler '''
reverse_table = {}
for (opcode, (name, immediate_operand_size, pops, pushes, gas, description)) in EVMAsm._table.items():
for (opcode, (name, immediate_operand_size, pops, pushes, gas, description)) in list(EVMAsm._table.items()):
mnemonic = name
if name == 'PUSH':
mnemonic = '%s%d' % (name, (opcode & 0x1f) + 1)
Expand All @@ -485,7 +456,7 @@ def assemble_one(assembler, pc=0):
''' Assemble one EVM instruction from its textual representation.

:param assembler: assembler code for one instruction
:param pc: program counter of the instruction (optional)
:param pc: program counter of the instruction(optional)
:return: An Instruction object

Example use::
Expand All @@ -506,17 +477,15 @@ def assemble_one(assembler, pc=0):
operand = None

return EVMAsm.Instruction(opcode, name, operand_size, pops, pushes, gas, description, operand=operand, pc=pc)
except Exception as e:
print "Exception", repr(e)

except BaseException:
raise Exception("Something wrong at pc %d" % pc)

@staticmethod
def assemble_all(assembler, pc=0):
''' Assemble a sequence of textual representation of EVM instructions

:param assembler: assembler code for any number of instructions
:param pc: program counter of the first instruction (optional)
:param pc: program counter of the first instruction(optional)
:return: An generator of Instruction objects

Example use::
Expand Down Expand Up @@ -550,7 +519,7 @@ def disassemble_one(bytecode, pc=0):

:param bytecode: the bytecode stream
:type bytecode: bytearray or str
:param pc: program counter of the instruction (optional)
:param pc: program counter of the instruction(optional)
:type bytecode: iterator/sequence/str
:return: an Instruction object

Expand All @@ -559,11 +528,11 @@ def disassemble_one(bytecode, pc=0):
>>> print EVMAsm.disassemble_one('\x60\x10')

'''
if isinstance(bytecode, str):
bytecode = bytearray(bytecode)
if isinstance(bytecode, (str, bytes)):
bytecode = bytearray(bytecode.encode())
bytecode = iter(bytecode)
opcode = next(bytecode)
assert isinstance(opcode, (int, long))
assert isinstance(opcode, int)

invalid = ('INVALID', 0, 0, 0, 0, 'Unknown opcode')
name, operand_size, pops, pushes, gas, description = EVMAsm._table.get(opcode, invalid)
Expand All @@ -578,7 +547,7 @@ def disassemble_all(bytecode, pc=0):
''' Decode all instructions in bytecode

:param bytecode: an evm bytecode (binary)
:param pc: program counter of the first instruction (optional)
:param pc: program counter of the first instruction(optional)
:type bytecode: iterator/sequence/str
:return: An generator of Instruction objects

Expand All @@ -603,7 +572,7 @@ def disassemble_all(bytecode, pc=0):
'''

if isinstance(bytecode, str):
bytecode = bytearray(bytecode)
bytecode = bytearray(bytecode.encode())
bytecode = iter(bytecode)
while True:
instr = EVMAsm.disassemble_one(bytecode, pc=pc)
Expand All @@ -615,7 +584,7 @@ def disassemble(bytecode, pc=0):
''' Disassemble an EVM bytecode

:param bytecode: binary representation of an evm bytecode (hexadecimal)
:param pc: program counter of the first instruction (optional)
:param pc: program counter of the first instruction(optional)
:type bytecode: str
:return: the text representation of the aseembler code

Expand All @@ -637,7 +606,7 @@ def assemble(asmcode, pc=0):
''' Assemble an EVM program

:param asmcode: an evm assembler program
:param pc: program counter of the first instruction (optional)
:param pc: program counter of the first instruction(optional)
:type asmcode: str
:return: the hex representation of the bytecode

Expand All @@ -653,14 +622,14 @@ def assemble(asmcode, pc=0):
...
"\x60\x60\x60\x40\x52\x60\x02\x61\x01\x00"
'''
return ''.join(map(lambda x: x.bytes, EVMAsm.assemble_all(asmcode, pc=pc)))
return ''.join([x.bytes for x in EVMAsm.assemble_all(asmcode, pc=pc)])

@staticmethod
def disassemble_hex(bytecode, pc=0):
''' Disassemble an EVM bytecode

:param bytecode: canonical representation of an evm bytecode (hexadecimal)
:param pc: program counter of the first instruction (optional)
:param pc: program counter of the first instruction(optional)
:type bytecode: str
:return: the text representation of the aseembler code

Expand All @@ -677,15 +646,15 @@ def disassemble_hex(bytecode, pc=0):
'''
if bytecode.startswith('0x'):
bytecode = bytecode[2:]
bytecode = bytecode.decode('hex')
bytecode = unhexlify(bytecode.encode())
return EVMAsm.disassemble(bytecode, pc=pc)

@staticmethod
def assemble_hex(asmcode, pc=0):
''' Assemble an EVM program

:param asmcode: an evm assembler program
:param pc: program counter of the first instruction (optional)
:param pc: program counter of the first instruction(optional)
:type asmcode: str
:return: the hex representation of the bytecode

Expand All @@ -701,4 +670,4 @@ def assemble_hex(asmcode, pc=0):
...
"0x6060604052600261010"
'''
return '0x' + EVMAsm.assemble(asmcode, pc=pc).encode('hex')
return '0x' + hexlify(EVMAsm.assemble(asmcode, pc=pc).encode()).decode()
Loading